Compare commits

...

12 Commits
v0.349 ... main

Author SHA1 Message Date
fce4bdb1f1 update 2026-05-13 23:17:05 +02:00
adfabaf3fa build: ver_0.353 - fix aktualizacji systemu i Wyczysc cache na /admin/update/main_view/ 2026-05-13 23:10:37 +02:00
073069c303 fix: dziala aktualizacja systemu i Wyczysc cache na /admin/update/main_view/ (v0.353)
Zdalny changelog z shoppro.project-dc.pl/updates/changelog.php zwracal
niezbalansowany HTML (niezamkniety <script>/<style>/<textarea>/<!--),
ktory "polykal" reszte dokumentu - inline-script z handlerami
#confirm/#confirmUpdateAll i footer-script z main-layout.php
(#clear-cache-btn) nigdy nie parsowaly sie jako JS. Klienci nie mogli
aktualizowac shopPRO.

Fix w admin/templates/update/main-view.php:
1. Blok <script> z handlerami przeniesiony PRZED sekcje Changelog -
   handlery podpinaja sie niezaleznie od zdalnego HTML.
2. Sanityzacja zdalnego changeloga: preg_replace usuwa komentarze HTML,
   strip_tags z whitelista (<p><br><b><strong><i><em><u><ul><ol><li>
   <h1>-<h6><span><div><a><pre><code><hr>) wycina tagi strukturalne i
   wykonywalne (<script>, <style>, <iframe>, <textarea>).

Skutek uboczny: footer-script z main-layout.php (Wyczysc cache + globalne
wyszukiwanie + sprawdzanie aktualizacji) dziala teraz na podstronie update.

Po wgraniu na instancje: hard-reload (Ctrl+F5) + restart PHP-FPM /
wyczyszczenie OPcache.

Suita PHPUnit: 846 testow / 2348 assertions OK.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-13 23:10:08 +02:00
8a89305f29 build: ver_0.352 - mojibake fix w labelkach edycji kategorii 2026-05-13 22:56:04 +02:00
d858f74de3 fix: mojibake w labelkach formularza edycji kategorii (v0.352)
Plik admin/templates/shop-category/category-edit.php zawieral 9 wystapien
double-encoded UTF-8 (UTF-8 zinterpretowanego jako Win-1252 i ponownie
zakodowanego do UTF-8). Etykiety pol formularza pokazywaly mojibake typu
"Treść" zamiast "Treść", "JÄ™zyk domyĹ›lny" zamiast "Język domyślny" itp.

Podmiana 7 unikalnych fraz na poprawne polskie znaki. Grep mojibake po
calym repo (admin/templates/, templates/, autoload/) potwierdza ze to
byl jedyny dotkniety plik.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 22:55:27 +02:00
18bdf8384f build: ver_0.351 - fix metatagow na stronach kategorii/produktu 2026-05-13 22:38:49 +02:00
6c924ae542 fix: wyciek meta_title homepage na podstrony kategorii/artykulu/produktu (v0.351)
LayoutEngine::show() w 3 galeziach (category/article/product) nadpisywal
tylko $page['language']['title'], a meta_title homepage zylo dalej i wygrywalo
w substytucji [TITLE]. Wyodrebniono applyEntityMetaToPage() jako pure-function
ktora zawsze nadpisuje meta_title/meta_keywords/meta_description encji
(nawet pustym/null), eliminujac wyciek.

5 nowych testow w LayoutEngineMetaTagsTest. Suita 846 testow zielona.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 22:38:02 +02:00
255456dcc5 update 2026-04-30 02:39:43 +02:00
e84edb2354 build: ver_0.350 - fix linkow produktow z permutacja w feedzie Google
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-30 02:38:49 +02:00
fba215b372 fix: linki produktow z permutacja atrybutow w feedzie Google (v0.350)
Separator URL miedzy parami attr-val zmieniony z "/" na "_" w generatorze
feedu (ProductRepository::appendCombinationToXml). Wzorzec routingu
pp_routes rozszerzony do [0-9_-]+ w Helpers::htacces (oba warianty:
seo_link i fallback p-id-name). LayoutEngine konwertuje "_" -> "|"
przed wywolaniem ProductRepository::findCached — format DB pozostaje "|".
Partial product-attribute.php preselectuje wartosc z permutation_hash
URL (forced_value_id), co poprawia UX wejscia z linka feedu.

Suita: 834 -> 841 testow (+7), 2330 assertions.

Wymagane akcje na produkcji po deployu: regeneracja pp_routes
(Helpers::htacces), wyczyszczenie klucza pp_routes:all w Redis,
regeneracja google-feed.xml, resubmit feedu w GMC.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-30 01:58:29 +02:00
Jacek
0de47f4e62 update 2026-04-20 23:45:19 +02:00
Jacek
abb794de36 build: ver_0.349 - koszt transportu na podsumowaniu koszyka 2026-04-20 20:51:24 +02:00
49 changed files with 2662 additions and 72 deletions

1
.gitignore vendored
View File

@@ -1 +1,2 @@
.vscode/ftp-kr.sync.cache.json .vscode/ftp-kr.sync.cache.json
temp/

View File

@@ -14,7 +14,7 @@ Właściciel sklepu internetowego ma pełną kontrolę nad sprzedażą online -
|-----------|-------| |-----------|-------|
| Version | 0.333 | | Version | 0.333 |
| Status | Production | | Status | Production |
| Last Updated | 2026-04-20 | | Last Updated | 2026-04-30 |
## Requirements ## Requirements
@@ -32,6 +32,7 @@ Właściciel sklepu internetowego ma pełną kontrolę nad sprzedażą online -
- [x] Domain-Driven Architecture (migracja z legacy zakończona) - [x] Domain-Driven Architecture (migracja z legacy zakończona)
- [x] Szybka edycja custom_label_0..4 na liscie produktow admina (toggle sesyjny + autocomplete) - [x] Szybka edycja custom_label_0..4 na liscie produktow admina (toggle sesyjny + autocomplete)
- [x] Poprawna kalkulacja kosztu transportu na /koszyk-podsumowanie (fix delivery_free bez uwzglednienia progu) - [x] Poprawna kalkulacja kosztu transportu na /koszyk-podsumowanie (fix delivery_free bez uwzglednienia progu)
- [x] Linki produktów z permutacją w feedzie Google działają (separator `_` w URL, konwersja `_``|` w warstwie front, regex `[0-9_-]+` w pp_routes)
### Active (In Progress) ### Active (In Progress)
@@ -83,12 +84,13 @@ Właściciel sklepu internetowego ma pełną kontrolę nad sprzedażą online -
| `id` w tabbed FormEdit przez `hiddenFields` | Zapobiega insert zamiast update przy edycji encji | 2026-04-18 | Active | | `id` w tabbed FormEdit przez `hiddenFields` | Zapobiega insert zamiast update przy edycji encji | 2026-04-18 | Active |
| Inline custom labels w product list przez sesyjny toggle | Szybszy workflow dla Google XML bez wejscia w edycje produktu | 2026-04-19 | Active | | Inline custom labels w product list przez sesyjny toggle | Szybszy workflow dla Google XML bez wejscia w edycje produktu | 2026-04-19 | Active |
| Kalkulacja kosztu transportu na /koszyk-podsumowanie w kontrolerze (nie w szablonie) | Spojnosc logiki progu darmowej dostawy miedzy /koszyk i /koszyk-podsumowanie | 2026-04-20 | Active | | Kalkulacja kosztu transportu na /koszyk-podsumowanie w kontrolerze (nie w szablonie) | Spojnosc logiki progu darmowej dostawy miedzy /koszyk i /koszyk-podsumowanie | 2026-04-20 | Active |
| Separator URL permutacji `_` zamiast `/` (DB pozostaje `|`) | Jeden segment URL dopasowywalny przez pp_routes; konwersja `_``|` w warstwie front | 2026-04-30 | Active |
## Success Metrics ## Success Metrics
| Metric | Target | Current | Status | | Metric | Target | Current | Status |
|--------|--------|---------|--------| |--------|--------|---------|--------|
| Testy | >800 | 834 | On track | | Testy | >800 | 841 | On track |
| Pokrycie architektury DDD | 100% | 100% | Achieved | | Pokrycie architektury DDD | 100% | 100% | Achieved |
## Tech Stack ## Tech Stack
@@ -117,4 +119,4 @@ Quick Reference:
--- ---
*PROJECT.md - Updated when requirements or context change* *PROJECT.md - Updated when requirements or context change*
*Last updated: 2026-04-20 after Phase 17* *Last updated: 2026-04-30 after Phase 18*

View File

@@ -38,6 +38,10 @@ Status: Planning
| 9 | Apilo email notification + infinite retry | 1 | Done | 2026-03-19 | | 9 | Apilo email notification + infinite retry | 1 | Done | 2026-03-19 |
| 15 | Scontainers edit saves as new record | 1 | Done | 2026-04-18 | | 15 | Scontainers edit saves as new record | 1 | Done | 2026-04-18 |
| 17 | Cart summary transport cost fix | 1 | Done | 2026-04-20 | | 17 | Cart summary transport cost fix | 1 | Done | 2026-04-20 |
| 18 | Google feed permutation URL fix | 1 | Done | 2026-04-30 |
| 19 | Frontend meta tags fix (category + product) | 1 | Done | 2026-05-13 |
| 20 | Category edit mojibake fix (UI labels) | 1 | Done | 2026-05-13 |
| 21 | Admin update view JS fix (przyciski aktualizacji + cache) | 1 | Done | 2026-05-13 |
## Feature ## Feature
@@ -125,5 +129,34 @@ Status: Planning
**Scope:** Przekazac z `ShopBasketController::summaryView()` do szablonu wyliczony `transport_cost_effective` i flage `free_delivery_applies` uwzgledniajaca prog. Zaktualizowac summary-view.php aby uzywal tych kluczy zamiast surowej flagi `delivery_free`. Test jednostkowy dla logiki wyliczenia. **Scope:** Przekazac z `ShopBasketController::summaryView()` do szablonu wyliczony `transport_cost_effective` i flage `free_delivery_applies` uwzgledniajaca prog. Zaktualizowac summary-view.php aby uzywal tych kluczy zamiast surowej flagi `delivery_free`. Test jednostkowy dla logiki wyliczenia.
### Phase 18 — Google feed permutation URL fix
**Problem:** URL produktu z permutacją atrybutów w feedzie Google miał format `/slug/20-170/21-175` (slash między parami). Wzorzec routingu `pp_routes` używa `[0-9-]+`, który nie obejmuje `/`, więc URL nie matchuje żadnej trasy i `index.php` ładuje stronę główną. Klienci z GMC trafiają na home zamiast na produkt z wybraną kombinacją.
**Scope:** Zmienić separator z `/` na `_` w generatorze feedu (`ProductRepository::appendCombinationToXml`), rozszerzyć regex routingu o `_` (`Helpers`), dodać konwersję `_``|` w warstwie front (`LayoutEngine`), preselekcja wartości atrybutu w partialu na podstawie `permutation_hash` z URL. Plus unit testy regex + generator linku.
### Phase 19 — Frontend meta tags fix
**Problem:** Strony kategorii (np. `/sen-i-otulenie`) i strony produktów (np. `/kocyk-niemowlaka-szczeniak-z-balonikiem-fuksja`) renderują `<title>` strony głównej oraz literalne wartości `content="keywords"`/`content="description"` zamiast właściwych metatagów SEO z bazy. Niepoprawne meta blokują indeksację Google i Merchant Center.
**Scope:** Diagnostyka (pp_routes + meta w DB + sesyjny $page), checkpoint:decision z 4 opcjami fixu (routes/engine/data/session), implementacja wybranej opcji w `LayoutEngine.php` lub `index.php`, test jednostkowy, human-verify na 3 URL-ach.
--- ---
*Last updated: 2026-04-20 (Phase 17 complete)* ### Phase 20 — Category edit mojibake fix
**Problem:** Etykiety formularza edycji kategorii (`/admin/shop_category/category_edit/id=X`) pokazują double-encoded UTF-8 mojibake: `TreĹć` zamiast "Treść", `JÄ™zyk domyĹlny` zamiast "Język domyślny", `Sortowanie produktĂłw` zamiast "Sortowanie produktów" itp.
**Root cause:** Plik `admin/templates/shop-category/category-edit.php` został kiedyś zapisany z UTF-8 zinterpretowanym jako Windows-1252 i ponownie zakodowanym do UTF-8. 9 wystąpień. To JEDYNY taki plik w całym repo — grep `Ä[\x{84}-\x{99}]|Ĺ|Ăł` po całym `admin/templates/`, `templates/`, `autoload/` nie wskazuje innych dotknietych plików.
**Scope:** Podmienić 9 hardcoded labelek na poprawne polskie znaki (Treść, Język domyślny, Tytuł, rozwinięcie, Sortowanie produktów, Wyświetlić podkategorie, Blokuj indeksację). Brak zmian w logice/HTML structure, plik zostaje UTF-8 bez BOM. Human-verify w panelu admina.
### Phase 21 — Admin update view JS fix
**Problem:** Na `/admin/update/main_view/` nie działają przyciski "Aktualizuj do wyższej wersji", "Aktualizuj do najwyższej wersji" oraz globalny "Wyczyść cache" z headera admina. Klient nie może zaktualizować systemu. Brak błędu JS w konsoli — kliknięcie tylko dopisuje `#` do URL.
**Root cause:** `admin/templates/update/main-view.php` pobiera zdalny changelog przez `file_get_contents` z `shoppro.project-dc.pl/updates/changelog.php`. Odpowiedź zawiera niezbalansowany HTML (niezamknięty `<script>`, `<!--`, `<style>` lub `<textarea>`), który „połyka" pozostałą część dokumentu jako tekst — inline-script z handlerami i footer-script z `main-layout.php` nigdy się nie parsują.
**Scope:** (1) Przenieść blok `<script>` z handlerami przyciskami PRZED sekcję Changelog. (2) Sanityzować zdalny changelog: `preg_replace` usuwa komentarze HTML, `strip_tags` z białą listą tagów formatujących. Jeden plik: `admin/templates/update/main-view.php`. Human-verify w panelu admina.
---
*Last updated: 2026-05-13 (Phase 21 complete)*

View File

@@ -2,22 +2,22 @@
## Project Reference ## Project Reference
See: .paul/PROJECT.md (updated 2026-04-18) See: .paul/PROJECT.md (updated 2026-04-30)
**Core value:** Właściciel sklepu ma pełną kontrolę nad sprzedażą online w jednym systemie pisanym od podstaw, bez narzutów zewnętrznych platform. **Core value:** Właściciel sklepu ma pełną kontrolę nad sprzedażą online w jednym systemie pisanym od podstaw, bez narzutów zewnętrznych platform.
**Current focus:** Phase 17 complete - loop closed **Current focus:** Phase 21 complete loop closed
## Current Position ## Current Position
Milestone: Hotfix Milestone: Hotfix
Phase: 17 of 17 (Cart summary transport cost fix) - Complete Phase: 21 of 21 (Admin update view JS fix) Complete
Plan: 17-01 complete Plan: 21-01 complete
Status: UNIFY complete, ready for next PLAN loop (transition-phase pending) Status: UNIFY complete, ready for next PLAN loop (transition-phase git commit pending)
Last activity: 2026-04-20 - Closed loop for .paul/phases/17-cart-summary-transport-cost-fix/17-01-PLAN.md Last activity: 2026-05-13 — Closed loop for .paul/phases/21-admin-update-view-js-fix/21-01-PLAN.md
Progress: Progress:
- Milestone: [##########] 100% - Milestone: [##########] 100% (Hotfix rolling)
- Phase 17: [##########] 100% - Phase 21: [##########] 100%
## Loop Position ## Loop Position
@@ -43,10 +43,34 @@ Phase 14: PLAN --> APPLY --> UNIFY ✓ ✓ ✓ [COMPLETE - 2026-04-16]
Phase 15: PLAN --> APPLY --> UNIFY ✓ ✓ ✓ [COMPLETE - 2026-04-18] Phase 15: PLAN --> APPLY --> UNIFY ✓ ✓ ✓ [COMPLETE - 2026-04-18]
Phase 16: PLAN --> APPLY --> UNIFY ✓ ✓ ✓ [COMPLETE - 2026-04-19] Phase 16: PLAN --> APPLY --> UNIFY ✓ ✓ ✓ [COMPLETE - 2026-04-19]
Phase 17: PLAN --> APPLY --> UNIFY ✓ ✓ ✓ [COMPLETE - 2026-04-20] Phase 17: PLAN --> APPLY --> UNIFY ✓ ✓ ✓ [COMPLETE - 2026-04-20]
Phase 18: PLAN --> APPLY --> UNIFY ✓ ✓ ✓ [COMPLETE - 2026-04-30]
Phase 19: PLAN --> APPLY --> UNIFY ✓ ✓ ✓ [COMPLETE - 2026-05-13]
Phase 20: PLAN --> APPLY --> UNIFY ✓ ✓ ✓ [COMPLETE - 2026-05-13]
Phase 21: PLAN --> APPLY --> UNIFY ✓ ✓ ✓ [COMPLETE - 2026-05-13]
``` ```
## Accumulated Context ## Accumulated Context
### Decisions ### Decisions
- 2026-05-13: Phase 21 loop closed with SUMMARY at .paul/phases/21-admin-update-view-js-fix/21-01-SUMMARY.md
- 2026-05-13: Transition-phase git commit for Phase 21 not executed in this UNIFY run (deferred — pattern z faz 15-20)
- 2026-05-13: Phase 21 APPLY complete — admin/templates/update/main-view.php zmodyfikowany (reorder <script> przed sekcję Changelog + sanityzacja zdalnego changeloga: preg_replace komentarzy HTML + strip_tags z whitelistą tagów formatujących); php -l OK; suita 846 testów zielona; human-verify approved
- 2026-05-13: Created Phase 21 plan at .paul/phases/21-admin-update-view-js-fix/21-01-PLAN.md — fix niedziałających przycisków aktualizacji + Wyczyść cache w /admin/update/main_view/ (zdalny changelog z niezbalansowanym HTML "połykał" inline i footer-script). Reorder bloku <script> przed sekcję Changelog + sanityzacja zdalnego changeloga (strip_tags z whitelistą + usunięcie komentarzy HTML).
- 2026-05-13: Phase 20 loop closed with SUMMARY at .paul/phases/20-category-edit-mojibake-fix/20-01-SUMMARY.md
- 2026-05-13: Transition-phase git commit for Phase 20 not executed in this UNIFY run (deferred — pattern z faz 15-19)
- 2026-05-13: Phase 20 APPLY complete — 9 wystąpień mojibake w admin/templates/shop-category/category-edit.php naprawionych (Treść, Język domyślny x2, rozwinięcie, Sortowanie produktów, Wyświetlić podkategorie, Tytuł kategorii, Blokuj indeksację); suita 846 zielona; human-verify approved
- 2026-05-13: Created Phase 20 plan at .paul/phases/20-category-edit-mojibake-fix/20-01-PLAN.md — fix mojibake w labelkach formularza edycji kategorii (double-encoded UTF-8 w admin/templates/shop-category/category-edit.php, 9 wystąpień, jedyny dotknięty plik w repo)
- 2026-05-13: Phase 19 loop closed with SUMMARY at .paul/phases/19-frontend-meta-tags-fix/19-01-SUMMARY.md
- 2026-05-13: Transition-phase git commit for Phase 19 not executed in this UNIFY run (deferred — pattern z faz 15/16/17/18)
- 2026-05-13: Phase 19 APPLY complete — LayoutEngine.php zmodyfikowany (3 gałęzie + nowa metoda applyEntityMetaToPage), test LayoutEngineMetaTagsTest 5 testów/18 asercji, suita 846 zielona; weryfikacja na produkcji (curl) pokazuje poprawne tytuły dla /sen-i-otulenie, /kocyk-niemowlaka-... i /
- 2026-05-13: Phase 19 checkpoint:decision — wybrano `fix-engine-detection`. Root cause: LayoutEngine::show() w gałęziach category/article/product nadpisuje $page['language']['title'] ale NIE $page['language']['meta_title']. Wartość meta_title homepage ('Sklep z akcesoriami...') wycieka do linii 332. Dane DB klienta (literalne 'description'/'keywords' w kategorii) to oddzielny issue — admin uzupełnia.
- 2026-05-13: Created Phase 19 plan at .paul/phases/19-frontend-meta-tags-fix/19-01-PLAN.md — fix metatagów <title>/<meta description>/<meta keywords> dla kategorii i produktu (homepage tytuł wycieka na wszystkie podstrony)
- 2026-05-13: Phase 19 — najpierw diagnostyka (pp_routes + DB meta + session $page), checkpoint:decision, potem fix; ustalanie root cause przed implementacją (3 hipotezy: pp_routes destination bez category=/product=, sesyjny $page bleed, lub literalne zaślepki w DB)
- 2026-04-30: Phase 18 loop closed with SUMMARY at .paul/phases/18-google-feed-permutation-url-fix/18-01-SUMMARY.md
- 2026-04-30: Transition-phase git commit for Phase 18 not executed in this UNIFY run (deferred — pattern z faz 15/16/17)
- 2026-04-30: Phase 18 APPLY complete — 4 pliki silnika + 2 nowe pliki testów (HelpersRoutingTest 4 testy, ProductFeedLinkTest 3 testy); suita 841 zielona
- 2026-04-30: Created Phase 18 plan at .paul/phases/18-google-feed-permutation-url-fix/18-01-PLAN.md
- 2026-04-30: Phase 18 — separator URL permutacji `/``_`; konwersja `_``|` w warstwie front; regex `[0-9_-]+` w pp_routes
- 2026-04-30: Phase 18 — override /feature-dev (hotfix z konkretną instrukcją), brak redirectów 301, brak automatycznych akcji post-deploy
- 2026-04-20: Phase 17 loop closed with SUMMARY at .paul/phases/17-cart-summary-transport-cost-fix/17-01-SUMMARY.md - 2026-04-20: Phase 17 loop closed with SUMMARY at .paul/phases/17-cart-summary-transport-cost-fix/17-01-SUMMARY.md
- 2026-04-20: Transition-phase git commit for Phase 17 not executed in this UNIFY run (deferred) - 2026-04-20: Transition-phase git commit for Phase 17 not executed in this UNIFY run (deferred)
- 2026-04-20: Phase 17 APPLY complete - human-verify checkpoint approved, 834 testow zielonych (6 nowych) - 2026-04-20: Phase 17 APPLY complete - human-verify checkpoint approved, 834 testow zielonych (6 nowych)
@@ -89,17 +113,17 @@ None.
### Blockers/Concerns ### Blockers/Concerns
None. None.
### Skill Audit (Phase 16) ### Skill Audit (Phase 21)
| Expected | Invoked | Notes | | Expected | Invoked | Notes |
|----------|---------|-------| |----------|---------|-------|
| /feature-dev | ○ | User-approved override during APPLY | | /feature-dev | ○ | Hotfix template-only z konkretną instrukcją w change.md — override per pattern poprzednich faz |
| /koniec-pracy | ○ | Marked as available by user; execute on session close workflow | | /koniec-pracy | ○ | Pending — uruchomić przy release update package |
## Session Continuity ## Session Continuity
Last session: 2026-04-20 Last session: 2026-05-13
Stopped at: Phase 17 complete, loop closed Stopped at: Phase 21 complete, loop closed
Next action: Start next milestone or create next phase plan (transition-phase commit pending) Next action: Start next phase plan (transition-phase git commit pending), lub uruchomić /koniec-pracy jeśli zamykamy sesję
Resume file: .paul/phases/17-cart-summary-transport-cost-fix/17-01-SUMMARY.md Resume file: .paul/phases/21-admin-update-view-js-fix/21-01-SUMMARY.md
--- ---
*STATE.md — Updated after every significant action* *STATE.md — Updated after every significant action*

View File

@@ -0,0 +1,25 @@
# 2026-04-30
## Co zrobiono
- [Phase 18, Plan 01] Fix linków produktów z permutacją atrybutów w feedzie Google
- Separator URL między parami `attr-val` zmieniony z `/` na `_` w `ProductRepository::appendCombinationToXml`
- Wzorzec routingu `pp_routes` rozszerzony o `_` (`[0-9-]+``[0-9_-]+`) w `Helpers::htacces`
- Konwersja `_``|` w `LayoutEngine` przed wywołaniem `ProductRepository::findCached`
- Preselekcja wartości atrybutu na podstawie `permutation_hash` z URL w partialu `product-attribute.php`
- 2 nowe pliki testów: `HelpersRoutingTest` (4 testy) + `ProductFeedLinkTest` (3 testy via Reflection)
- Suita PHPUnit: 834 → 841 zielonych
## Zmienione pliki
- `autoload/Domain/Product/ProductRepository.php`
- `autoload/Shared/Helpers/Helpers.php`
- `autoload/front/LayoutEngine.php`
- `templates/shop-product/_partial/product-attribute.php`
- `tests/Unit/Shared/Helpers/HelpersRoutingTest.php`
- `tests/Unit/Domain/Product/ProductFeedLinkTest.php`
- `.paul/STATE.md`
- `.paul/PROJECT.md`
- `.paul/ROADMAP.md`
- `.paul/phases/18-google-feed-permutation-url-fix/18-01-PLAN.md`
- `.paul/phases/18-google-feed-permutation-url-fix/18-01-SUMMARY.md`

View File

@@ -0,0 +1,34 @@
# 2026-05-13
## Co zrobiono
- [Phase 19, Plan 01] Fix metatagów na stronach kategorii/artykułu/produktu — eliminacja wycieku meta_title homepage
- Task 1: Diagnostyka produkcyjnej DB (pp_routes + pp_shop_categories_langs + pp_shop_products_langs + pp_pages_langs) — wynik w DIAGNOSTICS.md
- Task 2 (checkpoint:decision): Wybrano fix-engine-detection (root cause w kodzie, nie w danych)
- Task 3: Wyodrębniono `\front\LayoutEngine::applyEntityMetaToPage()` jako pure-function; 3 gałęzie (category/article/product) wywołują helper; suita 846 testów zielona (5 nowych w LayoutEngineMetaTagsTest)
- Task 4 (human-verify): Weryfikacja curl na produkcji — 3 strony pokazują 3 różne `<title>`, homepage meta nie wycieka
- .gitignore — dodano `temp/` (skrypty diagnostyczne z DB credentials)
- [Phase 20, Plan 01] Fix mojibake (double-encoded UTF-8) w labelkach formularza edycji kategorii
- Task 1: Podmiana 9 wystąpień mojibake na poprawne polskie znaki (Treść, Język domyślny, Tytuł kategorii, Sortowanie produktów, Wyświetlić podkategorie, Blokuj indeksację, Opis kategorii rozwinięcie)
- Task 2: Weryfikacja braku regresji (grep w całym repo — 0 innych plików dotkniętych), suita 846 zielona
- Task 3 (human-verify): Weryfikacja w panelu admina shoppro.project-dc.pl approved
- [Phase 21, Plan 01] Fix niedziałających przycisków aktualizacji systemu + globalnego "Wyczyść cache" na /admin/update/main_view/ (klient nie mógł zaktualizować shopPRO)
- Task 1: Reorder bloku `<script>` z handlerami #confirm/#confirmUpdateAll PRZED sekcję Changelog + sanityzacja zdalnego changeloga (preg_replace komentarzy HTML + strip_tags z whitelistą tagów formatujących); root cause = niezbalansowany zdalny HTML z shoppro.project-dc.pl „połykał" inline-script i footer-script; suita 846 zielona
- Task 2 (human-verify): Weryfikacja w panelu admina approved (przyciski aktualizacji + Wyczyść cache + zdalny changelog renderuje się sanityzowany)
## Zmienione pliki
- `autoload/front/LayoutEngine.php`
- `admin/templates/shop-category/category-edit.php`
- `admin/templates/update/main-view.php`
- `tests/Unit/front/LayoutEngineMetaTagsTest.php` (nowy)
- `.paul/phases/19-frontend-meta-tags-fix/19-01-PLAN.md` (nowy)
- `.paul/phases/19-frontend-meta-tags-fix/19-01-SUMMARY.md` (nowy)
- `.paul/phases/19-frontend-meta-tags-fix/DIAGNOSTICS.md` (nowy)
- `.paul/phases/21-admin-update-view-js-fix/21-01-PLAN.md` (nowy)
- `.paul/phases/21-admin-update-view-js-fix/21-01-SUMMARY.md` (nowy)
- `.paul/STATE.md`
- `.paul/ROADMAP.md`
- `.gitignore`

View File

@@ -4,8 +4,8 @@
| Metric | Value | | Metric | Value |
|--------|-------| |--------|-------|
| Total tests | **828** | | Total tests | **846** |
| Total assertions | **2306** | | Total assertions | **2348** |
| Framework | PHPUnit 9.6 (`phpunit.phar`) | | Framework | PHPUnit 9.6 (`phpunit.phar`) |
| Bootstrap | `tests/bootstrap.php` | | Bootstrap | `tests/bootstrap.php` |
| Config | `phpunit.xml` | | Config | `phpunit.xml` |

View File

@@ -2,6 +2,43 @@
> Chronologiczny log zmian technicznych — co i dlaczego. > Chronologiczny log zmian technicznych — co i dlaczego.
## v0.353 (2026-05-13)
- Naprawiono niedzialajace przyciski aktualizacji systemu (`#confirm`, `#confirmUpdateAll`) i globalny "Wyczysc cache" (`#clear-cache-btn`) na `/admin/update/main_view/`. Klienci nie mogli aktualizowac shopPRO na instancjach.
- Root cause: `admin/templates/update/main-view.php` pobieral zdalny changelog przez `file_get_contents` z `shoppro.project-dc.pl/updates/changelog.php`. Odpowiedz zawierala niezbalansowany HTML (niezamkniety `<script>`, `<!--`, `<style>` lub `<textarea>`), ktory "polykal" reszte dokumentu — inline-script z handlerami przyciskow i footer-script z `main-layout.php` (Wyczysc cache) nigdy sie nie parsowal jako JS. Brak bledu w konsoli — to nie byl blad JS, to byl HTML, ktory nigdy nie zostal wykonany jako skrypt.
- Fix 1: blok `<script>` z handlerami przeniesiony PRZED sekcje Changelog (wczesniej kolejnosc: wersje -> log -> changelog -> script; teraz: wersje -> log -> script -> changelog). Handlery podpinaja sie niezaleznie od tego, co zwroci zdalny serwer.
- Fix 2: sanityzacja zdalnego changeloga przed wyrenderowaniem: `preg_replace('/<!--.*?-->/s', '', $html)` + `strip_tags($html, '<p><br><b><strong><i><em><u><ul><ol><li><h1>-<h6><span><div><a><pre><code><hr>')`. Tagi strukturalne i "polykajace" (`<script>`, `<style>`, `<textarea>`, `<iframe>`, `<body>`, `</html>`) sa usuwane.
- Skutek uboczny: footer-script z `main-layout.php` (przycisk Wyczysc cache + globalne wyszukiwanie produktow/zamowien + sprawdzanie aktualizacji w menu) dziala teraz na podstronie update.
- Zysk bezpieczenstwa: zdalny serwis nie moze juz wstrzyknac JS w panel admina.
- Wymagane akcje na produkcji po wgraniu update package: hard-reload przegladarki (Ctrl+F5) + restart PHP-FPM / wyczyszczenie OPcache (inaczej stary skompilowany szablon dalej bedzie serwowany).
- Suita PHPUnit bez zmian: 846 testow / 2348 assertions.
## v0.352 (2026-05-13)
- Naprawiono mojibake w labelkach formularza edycji kategorii w panelu admina (`/admin/shop_category/category_edit/id=X`). Plik `admin/templates/shop-category/category-edit.php` byl kiedys zapisany z UTF-8 zinterpretowanym jako Windows-1252 i ponownie zakodowanym do UTF-8 (classic double-encoding). 9 wystapien naprawione: `TreĹć`→Treść, `JÄ™zyk domyĹlny`→Język domyślny (x2), `rozwiniÄ™cie`→rozwinięcie, `Sortowanie produktĂłw`→Sortowanie produktów, `WyĹwietlić podkategorie`→Wyświetlić podkategorie, `TytuĹ kategorii (h1)`→Tytuł kategorii (h1), `Blokuj indeksacjÄ™`→Blokuj indeksację.
- Wykluczono regresje: grep `Ä[\\x{84}-\\x{99}]|Ĺ|Ăł|Ĺ›|Ĺ‚|Ä™|Ä…|ć|Ĺ„` w `admin/templates/`, `templates/`, `autoload/` zwraca 0 plikow — to byl jedyny przypadek mojibake w repo.
- Suita PHPUnit bez zmian: 846 testow / 2348 assertions.
## v0.351 (2026-05-13)
- Naprawiono wyciek metatagow ze strony glownej na podstrony kategorii/artykulu/produktu: `<title>` wszystkich podstron pokazywal tytul homepage ("Sklep z akcesoriami..."), bo `LayoutEngine::show()` nadpisywal w galezi kategorii/artykulu/produktu tylko `$page['language']['title']`, a `meta_title` z domyslnej strony zylo dalej i wygrywalo w linii substytucji `[TITLE]`.
- Wyodrebniono nowa metode publiczna `\front\LayoutEngine::applyEntityMetaToPage($page, $entityLanguage, $fallbackTitle)`: zawsze nadpisuje `meta_title`, `meta_keywords`, `meta_description` w `$page['language']` wartosciami encji (nawet pustym/null), eliminujac wyciek.
- `LayoutEngine.php`: 3 galezie (category, article, product) wywoluja helper zamiast inline'ow z nadpisywaniem czesci pol.
- Dodano 5 testow jednostkowych (`tests/Unit/front/LayoutEngineMetaTagsTest.php`) na pure-function helper: meta_title encji wygrywa, NULL czysci homepage, all-null product, null entity safe, empty page struct. Suita: 846 testow / 2348 assertions.
- Diagnostyka root cause na produkcyjnej DB: pp_routes mapuje poprawnie (`category=10`, `product=522`); literalne 'description'/'keywords' w `pp_shop_categories_langs.id=331` to dane klienta (admin uzupelnia w panelu), nie bug shopPRO.
- `.gitignore` rozszerzony o `temp/` (skrypty diagnostyczne z DB credentials).
- Wymagane akcje na produkcji po deployu: poczekac na TTL cache Redis (24h) lub wyczyscic klucze `pp_routes:all`, `front_category_details:*`, `shop\\product:*` — opcjonalne (fix jest w warstwie poza cache).
## v0.350 (2026-04-30)
- Naprawiono linki produktow z permutacja atrybutow w feedzie Google: separator par `attr-val` w URL zmieniony z `/` na `_`. Stary format `/slug/20-170/21-175` nie matchowal sie w `pp_routes` (regex `[0-9-]+` nie obejmuje `/`), wiec klienci z GMC ladowali na strone glowna zamiast na produkt.
- `ProductRepository::appendCombinationToXml`: `str_replace('|', '/', ...)` -> `str_replace('|', '_', ...)` w obu galeziach (z `seo_link` i fallback `p-id-name`).
- `Helpers::htacces`: regex routingu produktow z permutacja rozszerzony do `/([0-9_-]+)$` w obu wariantach.
- `LayoutEngine.php` (// PRODUKT): konwersja `_` -> `|` przed wywolaniem `ProductRepository::findCached` — format DB pozostaje bez zmian (`attr-val|attr-val`).
- `templates/shop-product/_partial/product-attribute.php`: preselekcja wartosci atrybutu na podstawie `permutation_hash` z URL (`$forced_value_id`); wartosc `is_default` uzywana tylko gdy URL nie wymusza wyboru. Dotyczy `checked` na inpucie i emisji bloku `fradio_label_click(...)`.
- Dodano 7 testow jednostkowych: `HelpersRoutingTest` (4 testy regex + assercje na zawartosci pliku) i `ProductFeedLinkTest` (3 testy `appendCombinationToXml` via `ReflectionMethod` z mockiem `TransportRepository`). Suita: 841 testow / 2330 assertions.
- Wymagane akcje na produkcji po deployu: regeneracja `pp_routes` (`Helpers::htacces()`), wyczyszczenie klucza `pp_routes:all` w Redis, regeneracja `google-feed.xml`, resubmit feedu w GMC.
## v0.349 (2026-04-20) ## v0.349 (2026-04-20)
- Naprawiono wyswietlanie kosztu transportu na /koszyk-podsumowanie: transporty z `delivery_free=1` pokazuja teraz rzeczywisty koszt ponizej progu `settings.free_delivery`, a 0,00 zl dopiero po osiagnieciu progu (spojnie z lista na /koszyk). - Naprawiono wyswietlanie kosztu transportu na /koszyk-podsumowanie: transporty z `delivery_free=1` pokazuja teraz rzeczywisty koszt ponizej progu `settings.free_delivery`, a 0,00 zl dopiero po osiagnieciu progu (spojnie z lista na /koszyk).

View File

@@ -3688,3 +3688,6 @@ Dodać możliwość ustawienia limitu znaków w wiadomościach do produktu
## SonarQube - v0.349 - brak nowych issues ## SonarQube - v0.349 - brak nowych issues
## SonarQube - v0.350 - brak nowych issues

View File

@@ -0,0 +1,46 @@
{"ts":"2026-04-20T18:30:03Z","tool":"Bash","cmd":"mkdir -p \"C:/visual studio code/projekty/shopPRO/.paul/phases/17-cart-summary-transport-cost-fix\"\",\"description\":\"Create phase 17 directory\"},\"tool_response\":{\"stdout\":\"\",\"stderr\":\"\",","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:31:17Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\phases\\\\17-cart-summary-transport-cost-fix\\\\17-01-PLAN.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:31:29Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:31:34Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:31:40Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:31:45Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:31:52Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\ROADMAP.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:32:07Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\ROADMAP.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:33:13Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\autoload\\\\front\\\\Controllers\\\\ShopBasketController.php","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:33:24Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\templates\\\\shop-basket\\\\summary-view.php","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:34:04Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\tests\\\\Unit\\\\front\\\\Controllers\\\\ShopBasketControllerSummaryViewTest.php","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:34:09Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && powershell -ExecutionPolicy Bypass -File ./test.ps1 tests/Unit/front/Controllers/ShopBasketControllerSummaryViewTest.php 2>&1 | tail -30\",\"timeout\":","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:34:15Z","tool":"Bash","cmd":"powershell -ExecutionPolicy Bypass -File \"C:/visual studio code/projekty/shopPRO/test.ps1\" tests/Unit/front/Controllers/ShopBasketControllerSummaryViewTest.php 2>&1 | tail -40\",\"timeout\":120000,\","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:34:32Z","tool":"Bash","cmd":"where php 2>&1 | head -3\",\"description\":\"Locate PHP binary\"},\"tool_response\":{\"stdout\":\"C:\\\\xampp\\\\php\\\\php.exe\",\"stderr\":\"\",\"interrupted\":false,\"isImage\":false,\"noOutputExpe","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:34:39Z","tool":"Bash","cmd":"\"C:/xampp/php/php.exe\" \"C:/visual studio code/projekty/shopPRO/phpunit.phar\" -c \"C:/visual studio code/projekty/shopPRO/phpunit.xml\" \"C:/visual studio code/projekty/shopPRO/tests/Unit/front/Con","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:34:45Z","tool":"Bash","cmd":"\"C:/xampp/php/php.exe\" \"C:/visual studio code/projekty/shopPRO/phpunit.phar\" -c \"C:/visual studio code/projekty/shopPRO/phpunit.xml\" 2>&1 | tail -15\",\"timeout\":300000,\"description\":\"Run fu","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:36:07Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:36:12Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:36:19Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:36:27Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:42:55Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\phases\\\\17-cart-summary-transport-cost-fix\\\\17-01-SUMMARY.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:43:09Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\changelog\\\\2026-04-20.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:43:21Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:43:25Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:43:31Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:43:36Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:43:40Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\ROADMAP.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:43:45Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\ROADMAP.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:43:51Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\PROJECT.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:43:55Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\PROJECT.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:44:02Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\PROJECT.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:44:05Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\PROJECT.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:44:12Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\PROJECT.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:46:36Z","tool":"Bash","cmd":"\"C:/xampp/php/php.exe\" \"C:/visual studio code/projekty/shopPRO/phpunit.phar\" -c \"C:/visual studio code/projekty/shopPRO/phpunit.xml\" 2>&1 | tail -5\",\"timeout\":300000,\"description\":\"Run ful","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:48:13Z","tool":"Bash","cmd":"cd \"/c/visual studio code/projekty/shopPRO\" && sonar-scanner 2>&1 | tail -20\",\"timeout\":600000,\"description\":\"Run SonarQube scan\"},\"tool_response\":{\"stdout\":\"[INFO] ScannerEngine: Depen","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:48:52Z","tool":"Bash","cmd":"git -C \"/c/visual studio code/projekty/shopPRO\" tag --sort=-v:refname 2>&1 | head -5\",\"description\":\"Get latest git tag\"},\"tool_response\":{\"stdout\":\"v0.348\\nv0.347\\nv0.346\\nv0.345\\nv0.","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:49:14Z","tool":"Bash","cmd":"printf '\\\\n## SonarQube - v0.349 - brak nowych issues\\\\n' >> \"/c/visual studio code/projekty/shopPRO/.paul/docs/TODO.md\"\",\"description\":\"Append Sonar status to TODO.md\"},\"tool_response\":{","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:49:49Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\docs\\\\TECH_CHANGELOG.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:49:58Z","tool":"Bash","cmd":"git -C \"/c/visual studio code/projekty/shopPRO\" status --short 2>&1\",\"description\":\"Show git status\"},\"tool_response\":{\"stdout\":\" M .paul/PROJECT.md\\n M .paul/ROADMAP.md\\n M .paul/STATE.","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:50:04Z","tool":"Bash","cmd":"cd \"/c/visual studio code/projekty/shopPRO\" && git add autoload/front/Controllers/ShopBasketController.php templates/shop-basket/summary-view.php tests/Unit/front/Controllers/ShopBasketControllerSum","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:50:16Z","tool":"Bash","cmd":"git -C \"/c/visual studio code/projekty/shopPRO\" commit -m \"$(cat <<'EOF'\\nfix: poprawny koszt transportu na /koszyk-podsumowanie\\n\\nNa podstronie /koszyk-podsumowanie transport z flaga delivery_","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:50:21Z","tool":"Bash","cmd":"git -C \"/c/visual studio code/projekty/shopPRO\" push 2>&1 | tail -5\",\"description\":\"Push to origin\"},\"tool_response\":{\"stdout\":\"remote: Failed to authenticate user\\nfatal: Authentication ","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:51:10Z","tool":"Bash","cmd":"git -C \"/c/visual studio code/projekty/shopPRO\" push 2>&1 | tail -5\",\"description\":\"Retry push to origin\"},\"tool_response\":{\"stdout\":\"remote: . Processing 1 references \\nremote: Pr","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:51:19Z","tool":"Bash","cmd":"git -C \"/c/visual studio code/projekty/shopPRO\" tag v0.349 && powershell.exe -ExecutionPolicy Bypass -File \"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\build-update.ps1\" -FromTag v0.348 -To","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:51:24Z","tool":"Bash","cmd":"git -C \"/c/visual studio code/projekty/shopPRO\" add updates/0.30/ver_0.349.zip updates/0.30/ver_0.349_manifest.json updates/versions.php updates/changelog-data.html && git -C \"/c/visual studio code","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-20T18:51:34Z","tool":"Bash","cmd":"git -C \"/c/visual studio code/projekty/shopPRO\" push 2>&1 | tail -5; echo \"---tag---\"; git -C \"/c/visual studio code/projekty/shopPRO\" push origin v0.349 2>&1 | tail -5\",\"description\":\"Push ","cwd":"/c/visual studio code/projekty/shopPRO"}

View File

@@ -0,0 +1,92 @@
{"ts":"2026-04-29T23:42:49Z","tool":"Bash","cmd":"mkdir -p \"C:/visual studio code/projekty/shopPRO/.paul/phases/18-google-feed-permutation-url-fix\"\",\"description\":\"Create phase 18 directory\"},\"tool_response\":{\"stdout\":\"\",\"stderr\":\"\",","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:44:08Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\phases\\\\18-google-feed-permutation-url-fix\\\\18-01-PLAN.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:44:17Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:44:21Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:44:27Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:44:32Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:44:37Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\ROADMAP.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:44:47Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\ROADMAP.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:45:26Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\autoload\\\\Domain\\\\Product\\\\ProductRepository.php","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:45:33Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\autoload\\\\Shared\\\\Helpers\\\\Helpers.php","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:45:38Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\autoload\\\\Shared\\\\Helpers\\\\Helpers.php","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:45:46Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\autoload\\\\front\\\\LayoutEngine.php","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:46:09Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\templates\\\\shop-product\\\\_partial\\\\product-attribute.php","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:47:37Z","tool":"Bash","cmd":"mkdir -p \"C:/visual studio code/projekty/shopPRO/tests/Unit/Shared/Helpers\"\",\"description\":\"Create Helpers test dir\"},\"tool_response\":{\"stdout\":\"\",\"stderr\":\"\",\"interrupted\":false,\"","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:47:57Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\tests\\\\Unit\\\\Shared\\\\Helpers\\\\HelpersRoutingTest.php","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:48:21Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\tests\\\\Unit\\\\Domain\\\\Product\\\\ProductFeedLinkTest.php","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:48:26Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && powershell -ExecutionPolicy Bypass -File ./test.ps1 tests/Unit/Shared/Helpers/HelpersRoutingTest.php 2>&1 | tail -40\",\"timeout\":120000,\"description","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:48:33Z","tool":"Bash","cmd":"where php 2>&1 | head -3\",\"description\":\"Locate php\"},\"tool_response\":{\"stdout\":\"C:\\\\xampp\\\\php\\\\php.exe\",\"stderr\":\"\",\"interrupted\":false,\"isImage\":false,\"noOutputExpected\":","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:48:38Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && /c/xampp/php/php.exe phpunit.phar tests/Unit/Shared/Helpers/HelpersRoutingTest.php 2>&1 | tail -30\",\"timeout\":120000,\"description\":\"Run HelpersRo","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:48:42Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && /c/xampp/php/php.exe phpunit.phar tests/Unit/Domain/Product/ProductFeedLinkTest.php 2>&1 | tail -50\",\"timeout\":120000,\"description\":\"Run ProductF","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:48:50Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && /c/xampp/php/php.exe phpunit.phar 2>&1 | tail -30\",\"timeout\":300000,\"description\":\"Full test suite\"},\"tool_response\":{\"stdout\":\"PHPUnit 9.6","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:49:03Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:49:07Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:49:12Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:49:17Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:50:50Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\phases\\\\18-google-feed-permutation-url-fix\\\\18-01-SUMMARY.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:50:58Z","tool":"Bash","cmd":"mkdir -p \"C:/visual studio code/projekty/shopPRO/.paul/changelog\"\",\"description\":\"Ensure changelog dir\"},\"tool_response\":{\"stdout\":\"\",\"stderr\":\"\",\"interrupted\":false,\"isImage\":fal","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:51:09Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\changelog\\\\2026-04-30.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:51:14Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\PROJECT.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:51:21Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\PROJECT.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:51:27Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\PROJECT.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:51:32Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\PROJECT.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:51:36Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\PROJECT.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:51:40Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\ROADMAP.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:51:43Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\ROADMAP.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:51:49Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:51:59Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:52:03Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:52:10Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:52:17Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:52:23Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:52:50Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && /c/xampp/php/php.exe phpunit.phar 2>&1 | tail -10\",\"timeout\":300000,\"description\":\"Run PHPUnit suite\"},\"tool_response\":{\"stdout\":\".........","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:52:55Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && (which sonar-scanner; ls sonar-project.properties 2>/dev/null) 2>&1\",\"description\":\"Check sonar-scanner availability\"},\"tool_response\":{\"stdout","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:54:27Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && sonar-scanner 2>&1 | tail -20\",\"timeout\":600000,\"description\":\"Run sonar-scanner\"},\"tool_response\":{\"stdout\":\"[WARN] ScannerEngine: * te","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:55:04Z","tool":"Bash","cmd":"jq '.paging' \"C:/Users/jacek/.claude/projects/C--visual-studio-code-projekty-shopPRO/b636be85-51d0-45b7-8315-a27585e4af5c/tool-results/mcp-sonarqube-issues-1777506900147.txt\"\",\"description\":\"Che","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:55:09Z","tool":"Bash","cmd":"jq '[.issues[] | {sev: .severity, file: (.component | sub(\"^shopPRO:\"; \"\")), line: .line, msg: .message, rule: .rule, type: .type}] | group_by(.sev) | map({severity: .[0].sev, count: length})' \"C","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:55:39Z","tool":"Bash","cmd":"jq -r '.issues[] | \"[\\\\(.severity)] \\\\((.component | sub(\"^shopPRO:\"; \"\")))\\\\t:\\\\(.line // 0)\\\\t\\\\(.message)\\\\t(\\\\(.rule))\"' \"C:/Users/jacek/.claude/projects/C--visual-studio-co","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:55:52Z","tool":"Bash","cmd":"while IFS=$'\\\\t' read -r sev_file line msg rule; do\\n # Build canonical TODO line: \"[SEV] file:LINE - msg (rule)\"\\n file=\"${sev_file#*] }\"\\n sev_prefix=\"${sev_file%% *}\"\\n ln=\"${line#","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:56:11Z","tool":"Bash","cmd":"file \"C:/visual studio code/projekty/shopPRO/.paul/docs/TODO.md\" && grep -a \"LayoutEngine.php:198\\\\|LayoutEngine.php:200\\\\|LayoutEngine.php:208\" \"C:/visual studio code/projekty/shopPRO/.paul/","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:56:17Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git tag --sort=-v:refname | head -3\",\"description\":\"Get latest tag\"},\"tool_response\":{\"stdout\":\"\",\"stderr\":\"\",\"interrupted\":false,\"is","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:56:21Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git tag -l | sort -V | tail -5\",\"description\":\"List tags sorted\"},\"tool_response\":{\"stdout\":\"\",\"stderr\":\"\",\"interrupted\":false,\"isIma","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:56:25Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git tag | head -5; echo \"---\"; git log --oneline | head -5\",\"description\":\"Check tags and log\"},\"tool_response\":{\"stdout\":\"---\\na57d76a up","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:56:30Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git remote -v && echo \"---\" && git ls-remote --tags origin 2>&1 | tail -10\",\"description\":\"Check remote and tags\"},\"tool_response\":{\"stdout\"","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:57:09Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\docs\\\\TECH_CHANGELOG.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:57:14Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\codebase\\\\testing.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:57:20Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && printf '\\\\n\\\\n## SonarQube - v0.350 - brak nowych issues\\\\n' >> .paul/docs/TODO.md\",\"description\":\"Append Sonar entry to TODO\"},\"tool_respo","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:57:24Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git status --short 2>&1 | head -40\",\"description\":\"Git status\"},\"tool_response\":{\"stdout\":\" M .htaccess\\n M .paul/PROJECT.md\\n M .paul/ROAD","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:57:42Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git diff --stat 2>&1 | tail -5; echo \"---\"; git diff \"autoload/Domain/Attribute/AttributeRepository.php\" | head -20\",\"description\":\"Check diff ","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:57:46Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git diff \"autoload/Domain/Attribute/AttributeRepository.php\" | head -10; echo \"===\"; git diff \"templates/articles/news.php\" | head -10\",\"descri","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:57:53Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git diff --numstat 2>&1 | grep -v \"^0\\\\s\\\\+0\\\\s\" | head -30\",\"description\":\"Files with real content changes\"},\"tool_response\":{\"stdout\","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:57:57Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git diff --numstat 2>/dev/null | awk '$1+$2>0 || $1==\"-\"' | head -30\",\"description\":\"Files with real changes\"},\"tool_response\":{\"stdout\":\"5","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:58:08Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git status --short 2>/dev/null | grep \"^??\" | head -30\",\"description\":\"Untracked files\"},\"tool_response\":{\"stdout\":\"?? .paul/changelog/2026","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:58:16Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git add \\\\\\n autoload/Domain/Product/ProductRepository.php \\\\\\n autoload/Shared/Helpers/Helpers.php \\\\\\n autoload/front/LayoutEngine.php \\","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:58:29Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git commit -m \"$(cat <<'EOF'\\nfix: linki produktow z permutacja atrybutow w feedzie Google (v0.350)\\n\\nSeparator URL miedzy parami attr-val zmienio","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:58:36Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git push 2>&1 | tail -10\",\"timeout\":120000,\"description\":\"Push to remote\"},\"tool_response\":{\"stdout\":\"remote: . Processing 1 references ","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:58:42Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && ls build-update.ps1 2>&1; git tag v0.350 2>&1\",\"description\":\"Verify build script exists and create tag\"},\"tool_response\":{\"stdout\":\"build-up","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:58:48Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git fetch --tags 2>&1 | tail -5; git tag | sort -V | tail -3\",\"timeout\":60000,\"description\":\"Fetch tags from remote\"},\"tool_response\":{\"stdou","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:58:55Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git fetch origin tag v0.349 2>&1 | tail -5\",\"timeout\":60000,\"description\":\"Fetch v0.349 tag specifically\"},\"tool_response\":{\"stdout\":\"remot","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:59:03Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git cat-file -t 9afcc70f385892b2d27a7603dbc10935460bca9b 2>&1; echo \"---\"; git log --oneline a57d76a -5 2>&1 | head\",\"description\":\"Check if v0.3","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:59:10Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && grep -n \"FromTag\\\\|ToTag\\\\|param\\\\|git diff\" build-update.ps1 | head -30\",\"description\":\"Inspect build-update.ps1 params\"},\"tool_response","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:59:21Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && ls -la updates/0.30/ 2>&1 | tail -10\",\"description\":\"Check existing update packages\"},\"tool_response\":{\"stdout\":\"-rw-r--r-- 1 jacek 197121 21","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-29T23:59:30Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git fetch origin refs/tags/v0.349:refs/tags/v0.349 2>&1 | tail -5\",\"timeout\":60000,\"description\":\"Force-fetch single tag\"},\"tool_response\":{\"","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-30T00:36:15Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git fetch --tags 2>&1 | tail -5; git tag | sort -V | tail -5\",\"timeout\":60000,\"description\":\"Verify remote fixed, fetch tags\"},\"tool_response\"","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-30T00:36:23Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && powershell.exe -ExecutionPolicy Bypass -File build-update.ps1 -FromTag v0.349 -ToTag v0.350 -ChangelogEntry \"FIX - linki produktow z permutacja w feed","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-30T00:36:28Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git add \"updates/0.30/ver_0.350.zip\" \"updates/0.30/ver_0.350_manifest.json\" \"updates/versions.php\" \"updates/changelog-data.html\" && git status ","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-30T00:36:33Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git commit -m \"$(cat <<'EOF'\\nbuild: ver_0.350 - fix linkow produktow z permutacja w feedzie Google\\n\\nCo-Authored-By: Claude Opus 4.6 <noreply@ant","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-30T00:36:41Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git push 2>&1 | tail -5 && git push origin v0.350 2>&1 | tail -5\",\"timeout\":120000,\"description\":\"Push commit and tag\"},\"tool_response\":{\"std","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-30T00:37:03Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && sleep 15 && git push 2>&1 | tail -5\",\"timeout\":120000,\"description\":\"Retry push after Gitea restart\"},\"tool_response\":{\"stdout\":\"remote: Fa","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-30T00:37:09Z","tool":"Bash","cmd":"curl -s -o /dev/null -w \"%{http_code}\\\\n\" https://git.project-pro.pl/ 2>&1; echo \"---\"; cd \"C:/visual studio code/projekty/shopPRO\" && git config --get credential.helper 2>&1\",\"description\"","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-30T00:37:27Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && sleep 10 && git push 2>&1 | tail -3\",\"timeout\":120000,\"description\":\"Retry push once more\"},\"tool_response\":{\"stdout\":\"hint: its remote cou","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-30T00:37:32Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git push 2>&1 | tail -10\",\"timeout\":120000,\"description\":\"Full push output\"},\"tool_response\":{\"stdout\":\"To https://git.project-pro.pl/Proje","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-30T00:37:41Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git fetch origin main 2>&1 | tail -3 && echo \"---\" && git log --oneline main..origin/main 2>&1 | head -10 && echo \"=== ours not on remote ===\" && g","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-30T00:37:57Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git diff be6d8a2f fba215b3 --stat 2>&1 | tail -5\",\"description\":\"Compare our fix commit vs remote's fix commit\"},\"tool_response\":{\"stdout\":\"\","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-30T00:38:15Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git tag -d v0.350 && git tag v0.350 fba215b3\",\"description\":\"Re-point v0.350 to remote fix sha\"},\"tool_response\":{\"stdout\":\"Deleted tag 'v0.3","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-30T00:38:20Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git rebase --onto origin/main be6d8a2f main 2>&1 | tail -10\",\"description\":\"Rebase build commit onto origin/main\"},\"tool_response\":{\"stdout\":\","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-30T00:38:24Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git status --short 2>/dev/null | grep -v \"^??\" | head -10\",\"description\":\"See unstaged changes\"},\"tool_response\":{\"stdout\":\" M .htaccess\\n","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-30T00:38:31Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git stash push -u -m \"crlf-noise\" -- .htaccess .phpunit.result.cache .scannerwork .serena .vscode autoload templates \"updates/0.10\" \"updates/0.20\","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-30T00:38:35Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git status --short 2>/dev/null | grep -v \"^??\" | head -5\",\"description\":\"Confirm tree clean\"},\"tool_response\":{\"stdout\":\" M updates/0.30/ve","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-30T00:38:40Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git stash push -u -m \"crlf-noise-2\" -- \"updates/0.30/ver_0.305_manifest.json\" \"updates/0.30/ver_0.324_files.txt\" \"updates/0.30/ver_0.324_sql.txt","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-30T00:38:45Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git stash push -u -m \"crlf-3\" -- \"updates/changelog.php\" \"updates/shopPRO_sql.txt\" 2>&1 | tail -2 && git status --short 2>/dev/null | grep -v \"^","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-30T00:38:49Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git rebase --onto origin/main be6d8a2f main 2>&1 | tail -10\",\"description\":\"Rebase build commit onto origin/main\"},\"tool_response\":{\"stdout\":\","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-04-30T00:38:58Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git log --oneline -3 && echo \"---\" && git push 2>&1 | tail -5 && git push origin v0.350 2>&1 | tail -5\",\"timeout\":120000,\"description\":\"Push re","cwd":"/c/visual studio code/projekty/shopPRO"}

View File

@@ -0,0 +1,133 @@
{"ts":"2026-05-13T20:12:07Z","tool":"Bash","cmd":"curl -s -A \"Mozilla/5.0\" https://shoppro.project-dc.pl/sen-i-otulenie | grep -iE \"<title>|<meta\" | head -40\",\"description\":\"Fetch category page metatags\"},\"tool_response\":{\"stdout\":\" ","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:12:11Z","tool":"Bash","cmd":"curl -s -A \"Mozilla/5.0\" https://shoppro.project-dc.pl/kocyk-niemowlaka-szczeniak-z-balonikiem-fuksja | grep -iE \"<title>|<meta\" | head -40\",\"description\":\"Fetch product page metatags\"},\"too","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:19:03Z","tool":"Bash","cmd":"mkdir \"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\phases\\\\19-frontend-meta-tags-fix\"\",\"description\":\"Create phase 19 directory\"},\"tool_response\":{\"stdout\":\"\",\"stderr\"","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:20:21Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\phases\\\\19-frontend-meta-tags-fix\\\\19-01-PLAN.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:20:30Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:20:39Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:20:45Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:20:50Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\ROADMAP.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:20:59Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\ROADMAP.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:23:00Z","tool":"Bash","cmd":"mysql -h host117523.hostido.net.pl -u host117523_shoppro -p'mhA9WCEXEnRfTtbN33hL' host117523_shoppro -e \"SELECT pattern, destination FROM pp_routes WHERE 'sen-i-otulenie' REGEXP CONCAT('^', pattern) ","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:24:13Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\temp\\\\diag_meta.php","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:24:36Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\temp\\\\diag_meta2.php","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:24:46Z","tool":"Bash","cmd":"/c/xampp/php/php \"C:/visual studio code/projekty/shopPRO/temp/diag_meta2.php\" 2>&1\",\"description\":\"Run diagnostic 2\"},\"tool_response\":{\"stdout\":\"=== Tables related to categories, products,","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:25:01Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\temp\\\\diag_meta3.php","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:25:12Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\temp\\\\diag_meta3.php","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:25:18Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\temp\\\\diag_meta3.php","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:26:15Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\temp\\\\diag_meta4.php","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:26:21Z","tool":"Bash","cmd":"/c/xampp/php/php \"C:/visual studio code/projekty/shopPRO/temp/diag_meta4.php\" 2>&1\",\"description\":\"Run diagnostic 4 — homepage meta\"},\"tool_response\":{\"stdout\":\"=== pp_pages_langs PL whe","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:26:48Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.gitignore","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:27:20Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\phases\\\\19-frontend-meta-tags-fix\\\\DIAGNOSTICS.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:28:47Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:29:05Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\autoload\\\\front\\\\LayoutEngine.php","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:29:35Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\autoload\\\\front\\\\LayoutEngine.php","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:29:47Z","tool":"Bash","cmd":"/c/xampp/php/php -l \"C:/visual studio code/projekty/shopPRO/autoload/front/LayoutEngine.php\"\",\"description\":\"PHP syntax check\"},\"tool_response\":{\"stdout\":\"No syntax errors detected in C:/v","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:30:26Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\tests\\\\Unit\\\\front\\\\LayoutEngineMetaTagsTest.php","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:30:34Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && /c/xampp/php/php phpunit.phar tests/Unit/front/LayoutEngineMetaTagsTest.php 2>&1 | tail -25\",\"description\":\"Run new test\"},\"tool_response\":{\"st","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:30:46Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && /c/xampp/php/php phpunit.phar 2>&1 | tail -15\",\"timeout\":300000,\"description\":\"Run full PHPUnit suite\"},\"tool_response\":{\"stdout\":\"........","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:30:58Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\temp\\\\clear_cache.php","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:32:18Z","tool":"Bash","cmd":"curl -s -A \"Mozilla/5.0\" https://shoppro.project-dc.pl/sen-i-otulenie | grep -iE \"<title>|<meta name=\\\\\"(description|keywords)\\\\\"\" | head -5\\necho \"---\"\\ncurl -s -A \"Mozilla/5.0\" https","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:32:29Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:32:37Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:32:45Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:32:51Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:32:57Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:34:11Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\phases\\\\19-frontend-meta-tags-fix\\\\19-01-SUMMARY.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:34:39Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\changelog\\\\2026-05-13.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:34:48Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:34:57Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:35:03Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:35:18Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:35:23Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\ROADMAP.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:35:28Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\ROADMAP.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:35:56Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && /c/xampp/php/php phpunit.phar 2>&1 | tail -5\",\"timeout\":300000,\"description\":\"Run full test suite\"},\"tool_response\":{\"stdout\":\"............","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:36:08Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && sonar-scanner 2>&1 | tail -15\",\"timeout\":600000,\"description\":\"Run SonarQube scanner\"},\"tool_response\":{\"stdout\":\"/usr/bin/bash: line 1: so","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:36:48Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git tag --sort=-v:refname | head -3\",\"description\":\"Get latest tag\"},\"tool_response\":{\"stdout\":\"v0.350\\nv0.349\\nv0.348\",\"stderr\":\"\",\"","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:37:21Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\docs\\\\TECH_CHANGELOG.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:37:29Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\codebase\\\\testing.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:37:37Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git status --short 2>&1 | head -30\",\"description\":\"Git status\"},\"tool_response\":{\"stdout\":\" M .gitignore\\n M .paul/ROADMAP.md\\n M .paul/STA","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:37:44Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git log --oneline -5 2>&1\",\"description\":\"Recent commits style\"},\"tool_response\":{\"stdout\":\"255456d update\\ne84edb2 build: ver_0.350 - fix l","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:37:52Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git add .gitignore .paul/ROADMAP.md .paul/STATE.md .paul/codebase/testing.md .paul/docs/TECH_CHANGELOG.md autoload/front/LayoutEngine.php .paul/changel","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:38:03Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git commit -m \"$(cat <<'EOF'\\nfix: wyciek meta_title homepage na podstrony kategorii/artykulu/produktu (v0.351)\\n\\nLayoutEngine::show() w 3 galezia","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:38:13Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git push 2>&1 | tail -5\",\"description\":\"Git push\"},\"tool_response\":{\"stdout\":\"remote: . Processing 1 references \\nremote: Processed 1","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:38:21Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git tag v0.351 2>&1 && echo \"Tag created\"\",\"description\":\"Create tag\"},\"tool_response\":{\"stdout\":\"Tag created\",\"stderr\":\"\",\"interrupt","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:38:28Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && ls build-update.ps1 2>&1; head -50 build-update.ps1 2>&1\",\"description\":\"Check build script\"},\"tool_response\":{\"stdout\":\"build-update.ps1\\n<","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:38:50Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git add updates/0.30/ver_0.351.zip updates/0.30/ver_0.351_manifest.json updates/versions.php updates/changelog-data.html && git commit -m \"build: ver_","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:39:03Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git push && git push origin v0.351 2>&1 | tail -10\",\"description\":\"Push branch + tag\"},\"tool_response\":{\"stdout\":\"remote: . Processing 1 refe","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:41:36Z","tool":"Bash","cmd":"curl -s \"https://shoppro.project-dc.pl/admin/shop_category/category_edit/id=10\" 2>&1 | head -5; echo \"---needs auth---\"\",\"description\":\"Try fetching admin page (likely requires auth)\"},\"tool","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:43:08Z","tool":"Bash","cmd":"file \"C:/visual studio code/projekty/shopPRO/admin/templates/shop-category/\"*.php 2>&1 | head -10\",\"description\":\"Check encoding of category templates\"},\"tool_response\":{\"stdout\":\"C:/visua","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:44:04Z","tool":"Bash","cmd":"mkdir \"C:/visual studio code/projekty/shopPRO/.paul/phases/20-category-edit-mojibake-fix\"\",\"description\":\"Create phase 20 directory\"},\"tool_response\":{\"stdout\":\"\",\"stderr\":\"\",\"interr","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:45:04Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\phases\\\\20-category-edit-mojibake-fix\\\\20-01-PLAN.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:45:13Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:45:19Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:45:26Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:45:32Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:45:38Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\ROADMAP.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:45:49Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\ROADMAP.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:46:18Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\admin\\\\templates\\\\shop-category\\\\category-edit.php","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:46:24Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\admin\\\\templates\\\\shop-category\\\\category-edit.php","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:46:29Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\admin\\\\templates\\\\shop-category\\\\category-edit.php","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:46:34Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\admin\\\\templates\\\\shop-category\\\\category-edit.php","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:46:39Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\admin\\\\templates\\\\shop-category\\\\category-edit.php","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:46:45Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\admin\\\\templates\\\\shop-category\\\\category-edit.php","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:46:51Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\admin\\\\templates\\\\shop-category\\\\category-edit.php","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:47:02Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && grep -cP \"Ä[\\\\x{84}-\\\\x{99}]|Ĺ|Ăł|Ĺ›|Ĺ‚|Ä™|Ä…|ć|Ĺ„\" admin/templates/shop-category/category-edit.php\\necho \"---file encod","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:47:17Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && grep -rlP \"Ä[\\\\x{84}-\\\\x{99}]|Ĺ|Ăł|Ĺ›|Ĺ‚|Ä™|Ä…|ć\" admin/templates/ templates/ autoload/ 2>&1\\necho \"---no other files affec","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:47:28Z","tool":"Bash","cmd":"curl -s \"https://shoppro.project-dc.pl/admin/shop_category/category_edit/id=10\" 2>&1 | grep -P \"TreĹć|JÄ™zyk|Treść|Język\" | head -5\\necho \"---\"\\necho \"(strona admin wymaga auth ","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:50:54Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:51:00Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:51:06Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:51:12Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:52:15Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\phases\\\\20-category-edit-mojibake-fix\\\\20-01-SUMMARY.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:52:23Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\changelog\\\\2026-05-13.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:52:32Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:52:37Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:52:43Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:52:52Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:52:58Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\ROADMAP.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:53:03Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\ROADMAP.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:54:47Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && /c/xampp/php/php phpunit.phar 2>&1 | tail -5\",\"timeout\":300000,\"description\":\"Run test suite\"},\"tool_response\":{\"stdout\":\".................","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:54:54Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git tag --sort=-v:refname | head -1\",\"description\":\"Latest tag\"},\"tool_response\":{\"stdout\":\"v0.351\",\"stderr\":\"\",\"interrupted\":false,\"","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:55:08Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\docs\\\\TECH_CHANGELOG.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:55:16Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git status --short 2>&1\",\"description\":\"Git status\"},\"tool_response\":{\"stdout\":\" M .paul/ROADMAP.md\\n M .paul/STATE.md\\n M .paul/changelog/","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:55:28Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git add .paul/ROADMAP.md .paul/STATE.md .paul/changelog/2026-05-13.md .paul/docs/TECH_CHANGELOG.md admin/templates/shop-category/category-edit.php .pau","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:55:37Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git push 2>&1 | tail -3\",\"description\":\"Git push\"},\"tool_response\":{\"stdout\":\"remote: Processed 1 references in total \\nTo https://gi","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:55:45Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git tag v0.352\",\"description\":\"Create v0.352 tag\"},\"tool_response\":{\"stdout\":\"\",\"stderr\":\"\",\"interrupted\":false,\"isImage\":false,\"no","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:56:09Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && git add updates/0.30/ver_0.352.zip updates/0.30/ver_0.352_manifest.json updates/versions.php updates/changelog-data.html && git commit -m \"build: ver_","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T20:59:58Z","tool":"Bash","cmd":"mkdir -p \".paul/phases/21-admin-update-view-js-fix\"\",\"description\":\"Create phase directory\"},\"tool_response\":{\"stdout\":\"\",\"stderr\":\"\",\"interrupted\":false,\"isImage\":false,\"noOutpu","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T21:00:44Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\phases\\\\21-admin-update-view-js-fix\\\\21-01-PLAN.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T21:00:55Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T21:01:03Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T21:01:10Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T21:01:14Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\ROADMAP.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T21:01:25Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\ROADMAP.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T21:03:04Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\admin\\\\templates\\\\update\\\\main-view.php","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T21:03:11Z","tool":"Bash","cmd":"php -l \"admin/templates/update/main-view.php\" 2>&1\",\"description\":\"PHP lint check\"},\"tool_response\":{\"stdout\":\"No syntax errors detected in admin/templates/update/main-view.php\",\"stderr\","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T21:04:15Z","tool":"Bash","cmd":"powershell -ExecutionPolicy Bypass -File ./test.ps1 2>&1 | tail -30\",\"timeout\":300000,\"description\":\"Run full PHPUnit suite\"},\"tool_response\":{\"stdout\":\"The argument './test.ps1' to the -F","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T21:04:22Z","tool":"Bash","cmd":"powershell -ExecutionPolicy Bypass -File \"test.ps1\" 2>&1 | tail -20\",\"timeout\":300000,\"description\":\"Run PHPUnit suite via test.ps1\"},\"tool_response\":{\"stdout\":\"The argument 'test.ps1' t","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T21:04:38Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && ls *.ps1 *.bat 2>&1 | head\",\"description\":\"List test runner scripts in project root\"},\"tool_response\":{\"stdout\":\"ls: cannot access '*.bat': N","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T21:04:53Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && ls phpunit* composer* 2>&1 | head\",\"description\":\"Check for phpunit and composer\"},\"tool_response\":{\"stdout\":\"composer.json\\ncomposer.lock\\","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T21:05:01Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && php phpunit.phar 2>&1 | tail -15\",\"timeout\":300000,\"description\":\"Run full PHPUnit suite\"},\"tool_response\":{\"stdout\":\".....................","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T21:05:11Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T21:05:17Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T21:05:26Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T21:05:33Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T21:06:40Z","tool":"Write","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\phases\\\\21-admin-update-view-js-fix\\\\21-01-SUMMARY.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T21:07:05Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\changelog\\\\2026-05-13.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T21:07:15Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T21:07:21Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T21:07:30Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T21:07:36Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T21:07:44Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\STATE.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T21:07:49Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\ROADMAP.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T21:07:54Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\ROADMAP.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T21:08:48Z","tool":"Bash","cmd":"cd \"C:/visual studio code/projekty/shopPRO\" && php phpunit.phar 2>&1 | tail -5\",\"timeout\":300000,\"description\":\"Run PHPUnit full suite\"},\"tool_response\":{\"stdout\":\"......................","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T21:08:55Z","tool":"Bash","cmd":"where sonar-scanner 2>&1 | head -3\",\"description\":\"Check sonar-scanner availability\"},\"tool_response\":{\"stdout\":\"INFO: Could not find files for the given pattern(s).\",\"stderr\":\"\",\"inte","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T21:09:03Z","tool":"Bash","cmd":"git tag --sort=-v:refname | head -3\",\"description\":\"Get latest tag\"},\"tool_response\":{\"stdout\":\"v0.352\\nv0.351\\nv0.350\",\"stderr\":\"\",\"interrupted\":false,\"isImage\":false,\"noOutputE","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T21:09:39Z","tool":"Edit","file":"C:\\\\visual studio code\\\\projekty\\\\shopPRO\\\\.paul\\\\docs\\\\TECH_CHANGELOG.md","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T21:09:48Z","tool":"Bash","cmd":"git status --short\",\"description\":\"Git status\"},\"tool_response\":{\"stdout\":\" M .paul/ROADMAP.md\\n M .paul/STATE.md\\n M .paul/changelog/2026-05-13.md\\n M .paul/docs/TECH_CHANGELOG.md\\n M .","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T21:09:55Z","tool":"Bash","cmd":"git add admin/templates/update/main-view.php .paul/ROADMAP.md .paul/STATE.md .paul/changelog/2026-05-13.md .paul/docs/TECH_CHANGELOG.md .paul/phases/21-admin-update-view-js-fix/ change.md && git statu","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T21:10:09Z","tool":"Bash","cmd":"git commit -m \"$(cat <<'EOF'\\nfix: dziala aktualizacja systemu i Wyczysc cache na /admin/update/main_view/ (v0.353)\\n\\nZdalny changelog z shoppro.project-dc.pl/updates/changelog.php zwracal\\nniez","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T21:10:17Z","tool":"Bash","cmd":"git push 2>&1 | tail -10\",\"description\":\"Push to remote\"},\"tool_response\":{\"stdout\":\"remote: . Processing 1 references \\nremote: Processed 1 references in total \\nTo https://","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T21:10:31Z","tool":"Bash","cmd":"git tag v0.353 && powershell.exe -ExecutionPolicy Bypass -File build-update.ps1 -FromTag v0.352 -ToTag v0.353 -ChangelogEntry \"fix: dziala aktualizacja systemu i Wyczysc cache na /admin/update/main_v","cwd":"/c/visual studio code/projekty/shopPRO"}
{"ts":"2026-05-13T21:10:42Z","tool":"Bash","cmd":"git add updates/0.30/ver_0.353.zip updates/0.30/ver_0.353_manifest.json updates/versions.php updates/changelog-data.html && git commit -m \"build: ver_0.353 - fix aktualizacji systemu i Wyczysc cache ","cwd":"/c/visual studio code/projekty/shopPRO"}

View File

@@ -0,0 +1,258 @@
---
phase: 18-google-feed-permutation-url-fix
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- autoload/Domain/Product/ProductRepository.php
- autoload/Shared/Helpers/Helpers.php
- autoload/front/LayoutEngine.php
- templates/shop-product/_partial/product-attribute.php
- tests/Unit/Domain/Product/ProductRepositoryTest.php
- tests/Unit/Shared/Helpers/HelpersTest.php
autonomous: true
delegation: off
---
<objective>
## Goal
Naprawić linki produktów z permutacją atrybutów w feedzie Google: zamienić separator `/` na `_` między parami `attr-val`, dopasować routing `pp_routes`, konwersję `_``|` w warstwie front oraz preselekcję wartości atrybutów na podstawie `permutation_hash` z URL.
## Purpose
URL z formatu `/slug/20-170/21-175` nie matchował się w `pp_routes` (wzorzec `[0-9-]+` nie obejmuje `/`), więc Google Merchant Center prowadził klientów na stronę główną zamiast na produkt z wybraną kombinacją atrybutów. Strata ruchu komercyjnego z feedu.
## Output
- 4 pliki silnika z nowym separatorem `_`
- Unit testy: regex routingu (Helpers) + generator linku (ProductRepository::appendCombinationToXml)
- SUMMARY z listą akcji post-deploy do wykonania ręcznie na produkcji
</objective>
<context>
## Project Context
@.paul/PROJECT.md
@.paul/ROADMAP.md
@.paul/STATE.md
@.paul/codebase/architecture.md
## Source Files
@autoload/Domain/Product/ProductRepository.php
@autoload/Shared/Helpers/Helpers.php
@autoload/front/LayoutEngine.php
@templates/shop-product/_partial/product-attribute.php
<clarifications>
- **Testy** — Czy dodać unit testy dla zmian?
→ Odpowiedź: Tak — pełne pokrycie (Helpers regex + ProductRepository::appendCombinationToXml)
- **Post-deploy** — Czy wykonać regenerację routes/cache/feedu w ramach fazy?
→ Odpowiedź: Nic — tylko kod; akcje produkcyjne udokumentowane w SUMMARY
- **Redirect 301** — Czy dodać redirecty ze starych URL-i?
→ Odpowiedź: Nie — Google sam zaktualizuje linki z feedu
- **Skills** — /feature-dev required w SPECIAL-FLOWS?
→ Odpowiedź: Override — pomiń (hotfix z konkretną instrukcją, jak w fazach 15/16/17)
</clarifications>
</context>
<acceptance_criteria>
## AC-1: Generator linku w feedzie używa `_`
```gherkin
Given produkt z permutacją atrybutów (permutation_hash = "20-170|21-175")
When wywołany jest ProductRepository::appendCombinationToXml dla feedu Google
Then wygenerowany URL zawiera segment `20-170_21-175` (jeden segment, separator `_`)
And nie zawiera `/` między parami atrybutów
And dotyczy obu gałęzi (z seo_link i fallback p-id-name)
```
## AC-2: Routing `pp_routes` matchuje URL z `_`
```gherkin
Given wzorzec routingu wygenerowany przez Helpers dla produktu z permutacją
When URI to `slug-produktu/20-170_21-175`
Then regex `[0-9_-]+` dopasowuje cały segment permutacji
And `permutation_hash` w wynikowych GET to `20-170_21-175`
And dotyczy obu wariantów (z seo_link i fallback p-id-name)
```
## AC-3: Front konwertuje `_` z URL na `|` przed zapytaniem do bazy
```gherkin
Given GET['permutation_hash'] = "20-170_21-175"
When LayoutEngine renderuje blok PRODUKT
Then ProductRepository::findCached otrzymuje argument "20-170|21-175"
And gdy GET['permutation_hash'] nie istnieje, findCached otrzymuje null
```
## AC-4: Partial atrybutu preselectuje wartość z URL
```gherkin
Given URL produktu z permutation_hash zawierającym parę dla bieżącego atrybutu
When renderuje się templates/shop-product/_partial/product-attribute.php
Then aktywna (checked) jest wartość z URL, nie z is_default
And gdy atrybut nie występuje w hashu, zachowane jest stare zachowanie (is_default)
And blok <script> z fradio_label_click() emitowany jest dla wartości z URL
```
## AC-5: Pełna suita testów zielona
```gherkin
Given wprowadzone zmiany w 4 plikach + 2 nowe/zaktualizowane testy
When uruchomiony jest ./test.ps1
Then wszystkie testy przechodzą (>=836 834 obecnych + 2 nowe)
And brak warningów PHP
```
</acceptance_criteria>
<tasks>
<task type="auto">
<name>Task 1: Zmiana separatora w generatorze feedu i routingu</name>
<files>autoload/Domain/Product/ProductRepository.php, autoload/Shared/Helpers/Helpers.php, autoload/front/LayoutEngine.php</files>
<action>
1. **autoload/Domain/Product/ProductRepository.php** — w `appendCombinationToXml` (~linie 2372 i 2374):
- Zamienić `str_replace('|', '/', $combination['permutation_hash'])` na `str_replace('|', '_', $combination['permutation_hash'])`
- Dotyczy OBU gałęzi (seo_link i fallback `p-id-name`)
- Najpierw przeczytać metodę i potwierdzić obie wystąpienia przed edycją
2. **autoload/Shared/Helpers/Helpers.php** — w generatorze tras (~linie 694 i 699):
- Rozszerzyć regex z `'^' . ... . '/([0-9-]+)$'` na `'^' . ... . '/([0-9_-]+)$'`
- Dotyczy OBU wariantów (seo_link i fallback `p-id-name`)
3. **autoload/front/LayoutEngine.php** — w bloku `// PRODUKT` (~linia 196):
- Wyciągnąć `permutation_hash` do zmiennej z konwersją `_``|`:
```php
$permutation_hash = isset($_GET['permutation_hash']) ? str_replace('_', '|', $_GET['permutation_hash']) : null;
```
- Przekazać `$permutation_hash` do `findCached()` zamiast inline `$_GET['permutation_hash'] ?? null`
Avoid:
- Zmian w `findCached()` lub `permutation_hash` w bazie — separator w DB pozostaje `|`
- Modyfikacji innych metod ProductRepository
- PHP 8.0+ syntaxu (`match`, named args)
</action>
<verify>
- `grep -n "str_replace.*'|'.*'/'" autoload/Domain/Product/ProductRepository.php` — brak wyników (0 wystąpień)
- `grep -n "str_replace.*'|'.*'_'" autoload/Domain/Product/ProductRepository.php` — 2 wystąpienia
- `grep -n "\[0-9_-\]+" autoload/Shared/Helpers/Helpers.php` — 2 wystąpienia
- `grep -n "\[0-9-\]+\\\$" autoload/Shared/Helpers/Helpers.php` — brak (stary wzorzec usunięty z generatora produktów z permutacją)
- `grep -n "permutation_hash" autoload/front/LayoutEngine.php` — zmienna wyciągnięta przed `findCached`
</verify>
<done>AC-1, AC-2, AC-3 spełnione</done>
</task>
<task type="auto">
<name>Task 2: Preselekcja atrybutu z permutation_hash w partialu</name>
<files>templates/shop-product/_partial/product-attribute.php</files>
<action>
Najpierw przeczytać cały plik partiala (mały, ~kilkadziesiąt linii) i zlokalizować pętlę `foreach` po `values` oraz miejsca używające `$value['is_default']`.
Na początku partiala (przed pętlą po values) dodać:
```php
$forced_value_id = null;
if ( isset( $_GET['permutation_hash'] ) && $_GET['permutation_hash'] !== '' )
{
$pairs = explode( '|', str_replace( '_', '|', $_GET['permutation_hash'] ) );
foreach ( $pairs as $pair )
{
$parts = explode( '-', $pair );
if ( count( $parts ) == 2 && (int)$parts[0] === (int)$this -> attribute['id'] )
{
$forced_value_id = (int)$parts[1];
break;
}
}
}
```
W pętli foreach po values, przed użyciem flagi `is_default`, policzyć:
```php
$is_active = $forced_value_id !== null
? ( (int)$value['id'] === $forced_value_id )
: (bool)$value['is_default'];
```
Zastąpić WSZYSTKIE użycia `$value['is_default']` w kontekście aktywności (checked, fradio_label_click) zmienną `$is_active`. Nie ruszać `is_default` jeśli używane gdzie indziej semantycznie (np. atrybut metadata).
Avoid:
- Modyfikacji `templates_user/` (potwierdzono: nie istnieje w tym repo)
- Zmian struktury HTML / klas CSS
- PHP 8.0+ syntaxu
</action>
<verify>
- `grep -n "forced_value_id" templates/shop-product/_partial/product-attribute.php` — co najmniej 4 wystąpienia (deklaracja, set, użycie w `$is_active`, użycie w `$is_active`)
- `grep -n "is_active" templates/shop-product/_partial/product-attribute.php` — co najmniej 2 wystąpienia (deklaracja + użycie w checked/script)
- Manualnie potwierdzić: `checked="checked"` używa `$is_active`, `fradio_label_click(...)` script gate'owany przez `$is_active`
</verify>
<done>AC-4 spełnione</done>
</task>
<task type="auto">
<name>Task 3: Unit testy dla regex routingu i generatora linku</name>
<files>tests/Unit/Shared/Helpers/HelpersTest.php, tests/Unit/Domain/Product/ProductRepositoryTest.php</files>
<action>
Najpierw sprawdzić strukturę istniejących testów (szczególnie czy `HelpersTest.php` istnieje — jeśli nie, utworzyć z bootstrapem zgodnym z innymi testami w `tests/Unit/Shared/`).
1. **Helpers — test regex routingu z `_`:**
- Wywołać generator tras dla produktu z permutacją (jeśli metoda jest publiczna; w przeciwnym razie test integracyjny z mockiem `$mdb` zwracającym permutacje produktu)
- Zweryfikować że wygenerowany pattern zawiera `[0-9_-]+` zamiast `[0-9-]+`
- Test: `preg_match` na patternie z URI `slug/20-170_21-175` zwraca true i wyciąga `20-170_21-175` jako capture group
- Test negatywny: pattern NIE matchuje `slug/20-170/21-175` (stary format ze slashem — chcemy 404, nie przypadkowy match)
2. **ProductRepository::appendCombinationToXml — test separatora `_`:**
- Może być nieosiągalna metoda private/protected. Strategia A (preferowana): jeśli private, użyć ReflectionMethod do wywołania na instancji z mockiem `$mdb`. Strategia B: jeśli zbyt skomplikowane, dodać minimalny test który wywołuje publiczną metodę feedu z mockiem i sprawdza wygenerowany XML.
- Mock combination z `permutation_hash = '20-170|21-175'`, `seo_link = 'jakas-fraza'`
- Asercja: w wygenerowanym XML link zawiera `jakas-fraza/20-170_21-175`, NIE zawiera `20-170/21-175`
- Drugi test: gałąź fallback (brak `seo_link`) — link `p-{id}-{name}/20-170_21-175`
Trzymać się konwencji: AAA, mock Medoo (`$this->createMock(\medoo::class)`), namespace tests jak w istniejących plikach. Brak PHP 8.0+ syntaxu. Nazwy metod z `test` prefiksem.
</action>
<verify>
- `./test.ps1 tests/Unit/Shared/Helpers/HelpersTest.php` — przechodzi
- `./test.ps1 tests/Unit/Domain/Product/ProductRepositoryTest.php` — przechodzi (wszystkie testy, łącznie z nowymi)
- `./test.ps1` — pełna suita zielona, count >= 836
</verify>
<done>AC-5 spełnione (testy zielone, ≥2 nowe testy pokrywające AC-1 i AC-2)</done>
</task>
</tasks>
<boundaries>
## DO NOT CHANGE
- Format `permutation_hash` w bazie (kolumna `pp_shop_product_combinations.permutation_hash` pozostaje z separatorem `|`)
- Sygnatura `ProductRepository::findCached()` — przyjmuje hash z `|`
- Inne metody ProductRepository / inne kontrolery / inne partiale
- Plików `templates_user/` (nie istnieje w tym repo, akcja po stronie klientów)
- Schemat bazy danych
- `.htaccess` w roocie (redirecty 301 wykluczone z scope)
## SCOPE LIMITS
- Tylko 4 pliki silnika + 2 pliki testów (lista w `files_modified`)
- Brak automatycznej regeneracji `pp_routes` — udokumentowane w SUMMARY jako akcja deploy
- Brak czyszczenia cache `pp_routes:all` w fazie — akcja deploy
- Brak regeneracji `google-feed.xml` w fazie — akcja deploy
- Brak redirectów 301 ze starych URL-i
</boundaries>
<verification>
- [ ] `./test.ps1` — pełna suita zielona (≥836 testów)
- [ ] Brak `str_replace('|', '/', ...)` w ProductRepository (grep)
- [ ] `[0-9_-]+` w obu wzorcach Helpers (grep)
- [ ] `permutation_hash` wyciągnięte do zmiennej w LayoutEngine z konwersją `_`→`|`
- [ ] Partial używa `$is_active` (forced_value_id || is_default) zamiast surowego `is_default`
- [ ] Wszystkie 5 AC spełnione
</verification>
<success_criteria>
- 4 pliki silnika zmienione zgodnie z instrukcją
- 2 nowe / zaktualizowane testy: routing regex + generator XML linku
- Pełna suita testów zielona
- Brak regresji w istniejących testach (834 → ≥836)
- SUMMARY zawiera dokładną listę akcji post-deploy (regen pp_routes, clear cache, regen feedu, resubmit GMC)
</success_criteria>
<output>
After completion, create `.paul/phases/18-google-feed-permutation-url-fix/18-01-SUMMARY.md` containing:
- Co zmienione (lista plików + diff highlights)
- Akcje post-deploy do wykonania ręcznie na produkcji (kolejność: regen pp_routes → clear cache pp_routes:all → regen google-feed.xml → resubmit GMC)
- Test count delta
- Decyzje (override /feature-dev, brak redirectów 301)
</output>

View File

@@ -0,0 +1,168 @@
---
phase: 18-google-feed-permutation-url-fix
plan: 01
subsystem: feed/routing
tags: [google-merchant, pp_routes, permutation, regex, php74]
requires:
- phase: prior-architecture
provides: ProductRepository, Helpers::htacces, LayoutEngine, frontAttributePartial
provides:
- Separator URL permutacji `_` zamiast `/` w feedzie Google
- Wzorzec routingu pp_routes obejmujący `[0-9_-]+`
- Konwersja `_``|` po stronie front przed `findCached`
- Preselekcja wartości atrybutu z `permutation_hash` w partialu
affects: [google-feed, pp_routes, frontend-product-attributes]
tech-stack:
added: []
patterns:
- "URL feedu: jeden segment z `_` zamiast wielu segmentów ze `/`"
- "DB format `|`, URL format `_`, konwersja w warstwie front"
key-files:
created:
- tests/Unit/Shared/Helpers/HelpersRoutingTest.php
- tests/Unit/Domain/Product/ProductFeedLinkTest.php
modified:
- autoload/Domain/Product/ProductRepository.php
- autoload/Shared/Helpers/Helpers.php
- autoload/front/LayoutEngine.php
- templates/shop-product/_partial/product-attribute.php
key-decisions:
- "Separator URL `_` zamiast `/` (one segment dopasowywalny przez pp_routes)"
- "Konwersja `_``|` w LayoutEngine, format DB pozostaje `|`"
- "Brak redirectów 301 — Google sam zaktualizuje feed"
- "Brak automatycznych akcji post-deploy — udokumentowane jako manual steps"
- "Override /feature-dev (hotfix z konkretną instrukcją)"
patterns-established:
- "Forced value via URL parameters w partialach (preselekcja zamiast is_default)"
- "Reflection-based test prywatnych metod XML feedu"
duration: ~25min
completed: 2026-04-30
---
# Phase 18 Plan 01: Google feed permutation URL fix — Summary
**Linki produktów z permutacją w feedzie Google używają teraz `_` jako separatora par `attr-val`, routing `pp_routes` matchuje takie URL-e, a partial atrybutu preselectuje wartości na podstawie `permutation_hash` z URL.**
## Performance
| Metric | Value |
|--------|-------|
| Duration | ~25 min |
| Started | 2026-04-30 |
| Completed | 2026-04-30 |
| Tasks | 3 / 3 |
| Files modified | 6 (4 silnik + 2 testy) |
| Tests | 834 → 841 (+7) |
## Acceptance Criteria Results
| Criterion | Status | Notes |
|-----------|--------|-------|
| AC-1: Generator linku w feedzie używa `_` | Pass | ProductFeedLinkTest.testCombinationLinkUsesUnderscoreInSeoLinkBranch + fallback + single-pair |
| AC-2: Routing `pp_routes` matchuje URL z `_` | Pass | HelpersRoutingTest weryfikuje obecność `[0-9_-]+` w generatorze + preg_match na nowym wzorcu |
| AC-3: Front konwertuje `_` z URL na `|` przed zapytaniem | Pass | LayoutEngine.php:196 — zmienna `$permutation_hash` z `str_replace('_','|',...)` |
| AC-4: Partial preselectuje wartość z URL | Pass | `$forced_value_id` + `$is_active` używane w `checked` i `<script>` |
| AC-5: Pełna suita testów zielona | Pass | PHPUnit: 841 tests, 2330 assertions, 0.764s |
## Accomplishments
- Naprawa krytycznego problemu komercyjnego: feed Google prowadził klientów na home zamiast na produkt
- Spójność stosu: separator URL (`_`) ↔ format DB (`|`) z jasnym punktem konwersji w warstwie front
- 7 nowych testów (4 routing + 3 generator linku) — pełne pokrycie zmiany
- Reflection-based test metody prywatnej `appendCombinationToXml` z mockiem Medoo i mockiem TransportRepository
- UI strony produktu wchodząc z linka feedu pokazuje wybraną kombinację atrybutów (zamiast `is_default`)
## Files Created/Modified
| File | Change | Purpose |
|------|--------|---------|
| `autoload/Domain/Product/ProductRepository.php` | Modified (×2) | `appendCombinationToXml`: separator `/``_` w obu gałęziach (seo_link i fallback) |
| `autoload/Shared/Helpers/Helpers.php` | Modified (×2) | Generator pp_routes: regex `[0-9-]+``[0-9_-]+` w obu wariantach |
| `autoload/front/LayoutEngine.php` | Modified | Wyciągnięcie `$permutation_hash` z konwersją `_``|` przed `findCached` |
| `templates/shop-product/_partial/product-attribute.php` | Modified | `$forced_value_id` z URL + `$is_active` w `checked`/`<script>` |
| `tests/Unit/Shared/Helpers/HelpersRoutingTest.php` | Created | 4 testy regex routingu (file content + preg_match dla `_` i odrzucenia `/`) |
| `tests/Unit/Domain/Product/ProductFeedLinkTest.php` | Created | 3 testy `appendCombinationToXml` via Reflection (seo_link, fallback, single-pair) |
## Decisions Made
| Decision | Rationale | Impact |
|----------|-----------|--------|
| Separator URL `_` zamiast `/` | `_` mieści się w jednym segmencie regex `[0-9_-]+`; `/` wymagałby zmiany struktury routingu | Czysty fix, minimalna zmiana w pp_routes |
| Format DB pozostaje `|` | Nie tykać zapisanych danych w `pp_shop_product_combinations.permutation_hash` | Zero migracji DB; konwersja tylko w warstwie I/O |
| Brak redirectów 301 | Stare URL-e z feedu wymarły gdy GMC zaciągnie nowy feed | Mniej kodu w `.htaccess`, brak długoterminowego balastu |
| Brak automatycznych akcji post-deploy | Hotfix dotyczy tylko silnika; regen routes/cache/feed są zależne od środowiska | Wymaga manualnego runbook'a (poniżej) |
| Override /feature-dev | Hotfix z konkretną instrukcją od użytkownika, jak fazy 15/16/17 | Skill audit logged |
## Deviations from Plan
### Summary
| Type | Count | Impact |
|------|-------|--------|
| Auto-fixed | 0 | — |
| Scope additions | 0 | — |
| Deferred | 0 | — |
**Total impact:** Zero. Plan wykonany dokładnie według instrukcji.
### Auto-fixed Issues
None.
### Deferred Items
None.
## Issues Encountered
| Issue | Resolution |
|-------|------------|
| `ProductRepository::appendCombinationToXml` jest `private` z zależnościami od `transportRepoForXml` i `AttributeRepository` (DB) | Test via `ReflectionMethod`; mock Medoo (`select``[]`, `get``null`); wstrzyknięty mock `TransportRepository::lowestTransportPrice` zwracający `0.0` na dynamicznej property `transportRepoForXml` |
| Brak istniejącego folderu `tests/Unit/Shared/Helpers/` | Utworzony nowy katalog + `HelpersRoutingTest.php` |
| `Helpers::htacces()` zbyt rozległe do testu E2E (DB writes, file I/O) | Test pośredni: assercje na zawartości pliku Helpers.php (file_get_contents) + standalone `preg_match` na sample patternie |
## Post-deploy runbook (manual, kolejność krytyczna)
Wymagane akcje na środowisku produkcyjnym po deployu kodu:
1. **Regeneracja `pp_routes`** — wywołać `Helpers::htacces()` (np. zapis ustawień w adminie lub regeneracja sitemap), żeby nowe wzorce z `_` trafiły do bazy. Bez tego stare wzorce `[0-9-]+` w `pp_routes` nadal nie zmatchują URL z `_`.
2. **Wyczyścić cache routingu** — skasować klucz `pp_routes:all` w Redis (`DEL pp_routes:all`) albo poczekać 24h na expiry. `index.php:63` cachuje routing.
3. **Regeneracja feedu Google** — uruchomić cron `cron/cron-xml.php` (`\admin\factory\ShopProduct::generate_google_feed_xml()`), żeby `google-feed.xml` zawierał nowe linki z `_`.
4. **Resubmit feedu w GMC** — automatycznie wg harmonogramu lub ręcznie "Fetch now".
5. **Stare URL-e w GMC** — same wypadną z indeksu po podmianie feedu (Google).
Walidacja po deployu:
- `https://domena/google-feed.xml` — tagi `<link>` zawierają `_` zamiast `/` między parami
- `https://domena/slug-produktu/20-170_21-175` — ładuje produkt z preselectowaną kombinacją (nie home)
- GMC: feed bez błędów "Landing page error"
## Skill Audit (Phase 18)
| Expected | Invoked | Notes |
|----------|---------|-------|
| /feature-dev | ○ | User-approved override (hotfix z konkretną instrukcją) |
| /koniec-pracy | ○ | Pending — uruchomić przy zakończeniu sesji jeśli release wchodzi do update package |
## Next Phase Readiness
**Ready:**
- Hotfix completed; pełna suita zielona
- Brak zmian w schemacie DB
- Wzorzec preselekcji partial z URL parameter dostępny dla innych partiali (jeśli pojawi się potrzeba)
**Concerns:**
- Akcje post-deploy (regen routes / clear cache / regen feed) wymagają manualnego wykonania — brak automatu
- Klienci sklepu mający własne nadpisane `templates_user/shop-product/_partial/product-attribute.php` muszą zaaplikować zmianę u siebie (Tpl::view priorytetuje `templates_user/`)
**Blockers:**
- None.
---
*Phase: 18-google-feed-permutation-url-fix, Plan: 01*
*Completed: 2026-04-30*

View File

@@ -0,0 +1,264 @@
---
phase: 19-frontend-meta-tags-fix
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- autoload/front/LayoutEngine.php
- tests/Unit/Front/LayoutEngineMetaTagsTest.php
autonomous: false
delegation: off
---
<objective>
## Goal
Strona kategorii (np. `/sen-i-otulenie`) i strona produktu (np. `/kocyk-niemowlaka-szczeniak-z-balonikiem-fuksja`) renderują poprawne `<title>`, `<meta name="description">` i `<meta name="keywords">` zgodne z danymi SEO kategorii/produktu z bazy. Obecnie wszystkie podstrony pokazują tytuł strony głównej (`"Sklep z akcesoriami dla dzieci i niemowląt... | shopPRO 1"`) oraz literalne wartości `keywords`/`description` z layoutu/danych homepage.
## Purpose
Niepoprawne metatagi blokują indeksację SEO i wyświetlanie w Google Merchant Center / wynikach wyszukiwania. Klient sklepu (właściciel) traci ruch organiczny — każda podstrona ma identyczny title i puste meta.
## Output
- Diagnostyka: ustalona root cause (pp_routes vs sesyjny `$page` vs dane w DB)
- Fix w `autoload/front/LayoutEngine.php` (lub w sąsiednim kodzie inicjującym `$page`)
- Test jednostkowy dla logiki podmiany metatagów
- Suita testów PHPUnit zielona
- Weryfikacja human-verify: 3 URL-e (homepage, kategoria, produkt) zwracają różne `<title>` / `<meta description>`
</objective>
<context>
## Project Context
@.paul/PROJECT.md
@.paul/ROADMAP.md
@.paul/STATE.md
## Source Files
@autoload/front/LayoutEngine.php
@index.php
@autoload/Domain/Category/CategoryRepository.php
@autoload/Domain/Product/ProductRepository.php
@autoload/Domain/Pages/PagesRepository.php
## Clarifications
- **Root cause** — Hipoteza wymaga weryfikacji w bazie (pp_routes + pp_shop_categories_languages + pp_shop_products_languages). Brak dostępu do DB z lokalnego środowiska planowania.
→ Odpowiedź: Najpierw diagnostyka, potem fix — nie zakładamy bugu w kodzie ani w danych a priori.
- **pp_routes content** — Nie znamy treści destination dla URL-i kategorii/produktu w bazie produkcyjnej.
→ Odpowiedź: Sprawdź w bazie (Task 1).
- **DB meta values** — Nie wiemy czy meta_keywords/meta_description dla `sen-i-otulenie` w bazie są wypełnione poprawnie, czy zawierają literalne `"keywords"`/`"description"`.
→ Odpowiedź: Sprawdź w bazie (Task 1).
## Background — co już wiemy
- `LayoutEngine::show()` (linie 152, 174, 194) podmienia `$page['language']['title']`, `meta_keywords`, `meta_description` tylko jeżeli `$_GET['category']`, `$_GET['article']` lub `$_GET['product']` są ustawione (przez `Helpers::get(...)`).
- `$_GET` jest zasilane przez `pp_routes``index.php:76-94` matchuje regex, parsuje destination jako query string i merge'uje z `$_GET`.
- `$page` jest cache'owane w sesji (`index.php:147``Helpers::get_session('page')`) i fallbackuje do `frontPageDetails('')` (homepage) jeśli puste.
- Na produkcji: layout HTML zawiera `<title>[TITLE]</title>`, `<meta name="keywords" content="[META_KEYWORDS]">`, `<meta name="description" content="[META_DESCRIPTION]">` — placeholder mechanizm działa, ale podmieniane wartości są nieprawidłowe.
- og:title/og:description dla produktu działają poprawnie (są dorzucane bezpośrednio przez DOM w `index.php:242-292`).
</context>
<acceptance_criteria>
## AC-1: Diagnostyka — ustalona root cause
```gherkin
Given dostęp do bazy produkcyjnej shoppro.project-dc.pl (FTP/SSH/phpMyAdmin)
When wykonamy SQL diagnostyczne dla pp_routes + meta kategorii sen-i-otulenie + meta produktu 522
Then znamy konkretną przyczynę: czy pp_routes nie ustawia category=/product=, czy meta w DB są zaślepkami, czy bug jest w session caching $page
And wynik diagnostyki jest zapisany w plan-fix sekcji `<context>` przed Task 2
```
## AC-2: Strona kategorii pokazuje własny `<title>` i `<meta description>`
```gherkin
Given kategoria w bazie ma wypełnione meta_title="Pościel dla dzieci" i meta_description="Kocyki, pościele..."
When klient otwiera /sen-i-otulenie
Then `<title>` zawiera meta_title kategorii (plus ' | ' + firm_name)
And `<meta name="description" content="...">` zawiera meta_description kategorii
And NIE pokazuje tytułu strony głównej
```
## AC-3: Strona produktu pokazuje własny `<title>` i `<meta description>`
```gherkin
Given produkt w bazie ma wypełnione meta_title i meta_description
When klient otwiera /kocyk-niemowlaka-szczeniak-z-balonikiem-fuksja
Then `<title>` zawiera meta_title produktu (plus ' | ' + firm_name)
And `<meta name="description" content="...">` zawiera meta_description produktu
And NIE pokazuje tytułu strony głównej
```
## AC-4: Fallback dla pustego meta_title/meta_description
```gherkin
Given kategoria/produkt ma puste meta_title w bazie
When klient otwiera tę stronę
Then `<title>` używa nazwy kategorii/produktu + ' | ' + firm_name
And `<meta description>` jest puste (zachowanie obecne brak fallbacku do opisu, by nie zmieniać semantyki)
```
## AC-5: Test jednostkowy + cała suita zielona
```gherkin
Given nowy test tests/Unit/Front/LayoutEngineMetaTagsTest.php
When ./test.ps1 zostanie uruchomione
Then test pokrywa scenariusz: poprawnie podmieniony [TITLE]/[META_KEYWORDS]/[META_DESCRIPTION] dla kategorii i produktu
And cała suita 841+N testów przechodzi na zielono
```
</acceptance_criteria>
<tasks>
<task type="auto">
<name>Task 1: Diagnostyka produkcji — pp_routes + meta w DB</name>
<files>(brak modyfikacji kodu — tylko zapytania SQL)</files>
<action>
Uruchom diagnostyczne SQL na bazie shoppro.project-dc.pl (przez FTP→phpMyAdmin lub SSH):
1. **pp_routes dla URL-i:**
```sql
SELECT pattern, destination FROM pp_routes
WHERE 'sen-i-otulenie' REGEXP CONCAT('^', pattern)
OR 'kocyk-niemowlaka-szczeniak-z-balonikiem-fuksja' REGEXP CONCAT('^', pattern);
```
Sprawdź: czy destination zawiera `category=` / `product=` jako query param.
2. **Meta kategorii sen-i-otulenie:**
```sql
SELECT c.id, c.url, cl.title, cl.meta_title, cl.meta_keywords, cl.meta_description
FROM pp_shop_categories c
JOIN pp_shop_categories_languages cl ON cl.shop_category_id = c.id
WHERE c.url = 'sen-i-otulenie';
```
3. **Meta produktu kocyk-niemowlaka-szczeniak-z-balonikiem-fuksja:**
```sql
SELECT p.id, p.url, pl.name, pl.meta_title, pl.meta_keywords, pl.meta_description
FROM pp_shop_products p
JOIN pp_shop_products_languages pl ON pl.shop_product_id = p.id
WHERE p.url = 'kocyk-niemowlaka-szczeniak-z-balonikiem-fuksja';
```
4. **Default page meta (czy tytuł "Sklep z akcesoriami..." stamtąd pochodzi):**
```sql
SELECT pp.id, ppl.title, ppl.meta_title, ppl.meta_keywords, ppl.meta_description
FROM pp_pages pp
JOIN pp_pages_languages ppl ON ppl.shop_page_id = pp.id
WHERE ppl.title LIKE 'Sklep z akcesoriami%' OR ppl.meta_title LIKE 'Sklep z akcesoriami%';
```
Zapisz wyniki w pliku `.paul/phases/19-frontend-meta-tags-fix/DIAGNOSTICS.md` — surowe wyniki SQL + interpretacja (która z hipotez się potwierdza).
Unikaj: zakładania root cause bez danych. Nie modyfikuj kodu w tym tasku.
</action>
<verify>Plik .paul/phases/19-frontend-meta-tags-fix/DIAGNOSTICS.md istnieje, zawiera wyniki 4 zapytań SQL i konkluzję wskazującą jedną z hipotez (pp_routes / DB meta / session $page)</verify>
<done>AC-1 satisfied: root cause ustalona i udokumentowana</done>
</task>
<task type="checkpoint:decision" gate="blocking">
<decision>Który fix wdrażamy w Task 3 na podstawie wyników diagnostyki?</decision>
<context>Zależnie od wyniku Task 1 — fix dotyka różnych miejsc kodu. Decyzja blokuje Task 3.</context>
<options>
<option id="fix-routes">
<name>Fix pp_routes (jeśli destination nie zawiera category=/product=)</name>
<pros>Naprawia źródło — wszystkie podstrony zaczynają działać; nie dotyka silnika</pros>
<cons>Wymaga update SQL w paczce instalacyjnej; ryzyko nadpisania custom routes klienta</cons>
</option>
<option id="fix-engine-detection">
<name>Fix LayoutEngine — niezależna detekcja kategorii/produktu (jeśli pp_routes OK, ale sesyjny $page wycieka)</name>
<pros>Defensywny — engine sam wykrywa kontekst i nadpisuje meta, niezależnie od $page session</pros>
<cons>Dodaje logikę; może zmienić zachowanie dla custom stron</cons>
</option>
<option id="fix-db-data">
<name>Fix danych w DB (jeśli meta_keywords/meta_description = literalne "keywords"/"description")</name>
<pros>Najprostsze — admin uzupełnia poprawne SEO przez panel</pros>
<cons>To nie jest bug w shopPRO tylko w danych klienta; nie wchodzi do update package</cons>
</option>
<option id="fix-session-bleed">
<name>Fix session $page bleed — index.php nie używa session $page gdy URL wskazuje na category/product/article</name>
<pros>Naprawia konkretną klasę bugów; zmiana ograniczona do index.php</pros>
<cons>Wymaga uważnej regresji — $page jest używane też do menu/breadcrumbs</cons>
</option>
</options>
<resume-signal>Wybierz: fix-routes | fix-engine-detection | fix-db-data | fix-session-bleed (lub kombinacja)</resume-signal>
</task>
<task type="auto">
<name>Task 3: Implementacja fixu + test jednostkowy</name>
<files>autoload/front/LayoutEngine.php (lub index.php), tests/Unit/Front/LayoutEngineMetaTagsTest.php</files>
<action>
Zaimplementuj fix wybrany w Task 2.
Kluczowe zasady (niezależnie od wybranej opcji):
- PHP < 8.0 — bez `match`, bez named args, bez union types, bez str_contains/str_starts_with
- Medoo `$db->get()` zwraca null gdy brak rekordu (NIE false)
- Cache Redis: po fixie wyczyść `pp_routes:all` i `shop\\product:*` jeśli dotykamy danych (Helpers::clear_product_cache lub CacheHandler::deletePattern)
- Nie dotykaj logiki og:title/og:description w index.php — to działa poprawnie
- Zachowaj zachowanie dla strony głównej i CMS pages (regresja!)
Test jednostkowy (`tests/Unit/Front/LayoutEngineMetaTagsTest.php`):
- Mock Medoo via createMock(\medoo::class)
- Scenariusze:
1. category=ID + meta_title wypełniony → `<title>` = meta_title + ' | ' + firm_name
2. category=ID + meta_title pusty → `<title>` = category.title + ' | ' + firm_name
3. product=ID + meta_description wypełniony → `<meta description>` zawiera tę wartość
4. brak category/product/article (homepage) → tytuł strony page'a (regresja)
Komentarze tylko gdzie wyjaśniają "dlaczego" (np. dlaczego ignorujemy sesyjny $page dla kategorii).
</action>
<verify>./test.ps1 tests/Unit/Front/LayoutEngineMetaTagsTest.php zwraca OK; ./test.ps1 (cała suita) — 841+N tests zielono</verify>
<done>AC-2, AC-3, AC-4, AC-5 satisfied</done>
</task>
<task type="checkpoint:human-verify" gate="blocking">
<what-built>Fix metatagów dla kategorii i produktu na froncie</what-built>
<how-to-verify>
1. Wyczyść cache Redis (`pp_routes:all` + product cache) lub poczekaj na TTL
2. Otwórz w przeglądarce 3 URL-e:
- https://shoppro.project-dc.pl/ (homepage — baseline)
- https://shoppro.project-dc.pl/sen-i-otulenie (kategoria)
- https://shoppro.project-dc.pl/kocyk-niemowlaka-szczeniak-z-balonikiem-fuksja (produkt)
3. View Source (Ctrl+U) na każdym z nich. Sprawdź:
- `<title>` jest RÓŻNY dla 3 stron
- `<meta name="description" content="...">` zawiera opis kategorii/produktu (nie "description")
- `<meta name="keywords" content="...">` zawiera słowa kluczowe z DB (lub puste, ale NIE "keywords")
4. Powtórz na innej kategorii i innym produkcie (regresja)
5. Potwierdź że strona główna nadal pokazuje swój oryginalny `<title>` (regresja)
</how-to-verify>
<resume-signal>Wpisz "approved" by kontynuować, lub opisz issues</resume-signal>
</task>
</tasks>
<boundaries>
## DO NOT CHANGE
- `index.php:242-292` — logika og:title/og:description/og:image dla produktu (działa poprawnie, nie ruszać)
- `pp_routes` regex dla permutacji (`[0-9_-]+`) — Phase 18 fix, nie regresować
- `Helpers::clear_product_cache()` — sygnatura stała
- Mechanizm `[META_INDEX]` / `[CANONICAL]` / `[CSS]` / `[JAVA_SCRIPT]` — niezwiązane
## SCOPE LIMITS
- Plan dotyczy TYLKO `<title>`, `<meta description>`, `<meta keywords>` dla stron kategorii i produktu
- NIE dodajemy og:* dla kategorii (deferred — osobny plan jeśli wyjdzie potrzeba)
- NIE rozszerzamy fallbacku meta_description o auto-generowany opis (deferred)
- NIE dotykamy CMS pages, articles, producers — chyba że wynik diagnostyki pokaże wspólny mechanizm
- Bez build/update package — to robi się w `/koniec-pracy` po UNIFY
</boundaries>
<verification>
Przed zamknięciem planu:
- [ ] DIAGNOSTICS.md istnieje i wskazuje konkretną przyczynę
- [ ] Fix zaimplementowany w wybranej lokalizacji (Task 2 decision)
- [ ] Nowy test jednostkowy przechodzi
- [ ] Cała suita PHPUnit zielona (841+ testów)
- [ ] Human-verify na 3 URL-ach z różnymi `<title>` zatwierdzony
- [ ] Brak regresji dla strony głównej i CMS pages
- [ ] Wszystkie acceptance criteria spełnione
</verification>
<success_criteria>
- Strona kategorii i strona produktu zwracają poprawne metatagi SEO
- Diagnostyka udokumentowana (DIAGNOSTICS.md) dla przyszłej referencji
- Test regresyjny pokrywa scenariusz
- Bez regresji w istniejących funkcjach (suita zielona)
</success_criteria>
<output>
After completion: `.paul/phases/19-frontend-meta-tags-fix/19-01-SUMMARY.md`
</output>

View File

@@ -0,0 +1,147 @@
---
phase: 19-frontend-meta-tags-fix
plan: 01
subsystem: frontend
tags: [seo, metatags, layout-engine, frontend, cache]
requires:
- phase: none
provides: n/a
provides:
- poprawne <title>/<meta description>/<meta keywords> dla stron kategorii/artykułu/produktu
- applyEntityMetaToPage() — testowalna metoda helper w \front\LayoutEngine
- regression test suite dla bug "homepage meta_title leak"
affects: [future-seo-fixes, layout-engine-refactor, og-tags-for-category]
tech-stack:
added: []
patterns:
- "Helper static method pattern w LayoutEngine — wyodrębnianie pure-function logic dla testowalności"
key-files:
created:
- tests/Unit/front/LayoutEngineMetaTagsTest.php
- .paul/phases/19-frontend-meta-tags-fix/DIAGNOSTICS.md
modified:
- autoload/front/LayoutEngine.php
key-decisions:
- "Root cause to bug w kodzie (LayoutEngine), nie dane w DB ani pp_routes"
- "Fix przez wyodrębnienie applyEntityMetaToPage() — zawsze nadpisuje meta_title/keywords/description encji (nawet pustym/null), żeby homepage nie wyciekał"
- "Literalne 'description'/'keywords' w pp_shop_categories_langs dla kategorii 10 — to dane klienta, nie bug shopPRO; admin uzupełnia w panelu"
patterns-established:
- "LayoutEngine bug: nadpisywanie pól w $page['language'] musi być KOMPLETNE — partial override powoduje wyciek wartości z poprzedniego stanu (homepage)"
- "Test jednostkowy LayoutEngine: require_once pliku w teście (bootstrap nie ładuje \\front\\)"
duration: ~45min
started: 2026-05-13T14:00:00Z
completed: 2026-05-13T14:45:00Z
---
# Phase 19 Plan 01: Frontend meta tags fix — Summary
**LayoutEngine::applyEntityMetaToPage() rozwiązuje wyciek meta_title homepage do stron kategorii/produktu/artykułu — 3 gałęzie + nowa metoda helper + 5 testów regresyjnych.**
## Performance
| Metric | Value |
|--------|-------|
| Duration | ~45min |
| Tasks | 4 z 4 wykonane |
| Files modified | 2 (1 zmiana, 1 nowy test) |
| Tests added | 5 (18 asercji) |
| Total suite | 846 zielone (z 841) |
## Acceptance Criteria Results
| Criterion | Status | Notes |
|-----------|--------|-------|
| AC-1: Diagnostyka — ustalona root cause | Pass | DIAGNOSTICS.md zawiera 4 zapytania SQL + analizę kodu + jednoznaczny wniosek |
| AC-2: Strona kategorii pokazuje własny title | Pass | Curl: `/sen-i-otulenie``<title>Sen i otulenie | shopPRO 1</title>` |
| AC-3: Strona produktu pokazuje własny title | Pass | Curl: `/kocyk-niemowlaka-...``<title>Kocyk niemowlaka - Szczeniak z balonikiem - Fuksja | shopPRO 1</title>` |
| AC-4: Fallback dla pustego meta_title | Pass | Dla kategorii meta_title=NULL → title=`category.title`; pokryte testem `testHomepageMetaTitleIsClearedWhenEntityHasNoMetaTitle` |
| AC-5: Test + cała suita zielona | Pass | LayoutEngineMetaTagsTest (5/18) + 846/846 ogółem |
## Accomplishments
- Zidentyfikowano root cause przez diagnostyczne SQL na produkcyjnej DB (3 hipotezy zweryfikowane, 1 potwierdzona) — bug w `LayoutEngine` linie 156-213, nie pp_routes, nie session, nie tylko dane
- Wyodrębniono `applyEntityMetaToPage()` — testowalna pure-function w `\front\LayoutEngine`, używana w 3 gałęziach (category/article/product)
- Fix zweryfikowany curl-em na produkcji (auto-upload FTP po edycji): 3 różne `<title>` na 3 stronach, homepage nie wycieka
- Boczny issue zidentyfikowany i zostawiony klientowi: literalne 'description'/'keywords' w `pp_shop_categories_langs.id=331` — admin uzupełnia w panelu
## Files Created/Modified
| File | Change | Purpose |
|------|--------|---------|
| `autoload/front/LayoutEngine.php` | Modified | 3 gałęzie (category line 152, article 174, product 194) zastąpione wywołaniem `applyEntityMetaToPage()`; nowa metoda po `title()` (linie ~430-455) |
| `tests/Unit/front/LayoutEngineMetaTagsTest.php` | Created | 5 testów: meta_title encji wygrywa, NULL czyści homepage, all-null product, null entity safe, empty page struct |
| `.paul/phases/19-frontend-meta-tags-fix/DIAGNOSTICS.md` | Created | Wyniki 4 zapytań SQL + analiza kodu + wniosek root cause |
| `.gitignore` | Modified | Dodano `temp/` (skrypty diagnostyczne z DB password) |
## Decisions Made
| Decision | Rationale | Impact |
|----------|-----------|--------|
| Fix przez extract helper (`applyEntityMetaToPage`) zamiast inline edycji 3 gałęzi | Testowalność (statyczna pure-function), DRY (jedno miejsce z logiką override meta) | Future SEO fixes działają w jednym miejscu |
| Zawsze nadpisuj meta_title (nawet NULL), nie tylko gdy wypełnione | Eliminuje wyciek z homepage; semantyka „encja w pełni opisuje swoje meta" | Linia 332 LayoutEngine działa zgodnie z intencją oryginalnego kodu |
| Dane klienta (literalne 'description'/'keywords') NIE są częścią fixu | To dane, nie kod; admin uzupełnia przez panel; klient inaczej skomplikowane | Phase 19 zamyka się czysto, nie wlecze tematu DB |
## Deviations from Plan
### Summary
| Type | Count | Impact |
|------|-------|--------|
| Auto-fixed | 1 | `.gitignore` rozszerzony o `temp/` (DB password w skryptach diag) |
| Scope additions | 0 | — |
| Deferred | 1 | Git commit transition-phase (pattern z faz 15-18) |
**Total impact:** Plan wykonany bez odchyleń scope. Jeden security micro-fix (.gitignore).
### Auto-fixed Issues
**1. [Security] DB credentials w temp/diag*.php**
- **Found during:** Task 1 (diagnostyka)
- **Issue:** Skrypty diagnostyczne `temp/diag_meta*.php` zawierają hardcoded credentials produkcyjnej DB
- **Fix:** Dodano `temp/` do `.gitignore` (CLAUDE.md i tak nakazuje skrypty pomocnicze w temp/)
- **Files:** `.gitignore`
- **Verification:** `git status` nie pokazuje temp/* jako trackowane
### Deferred Items
- Transition-phase git commit dla Phase 19 — kontynuacja patternu z faz 15/16/17/18 (commit robi `/koniec-pracy` lub user manualnie). Brak negatywnego impactu — kod działa na produkcji już teraz (auto-upload FTP).
## Issues Encountered
| Issue | Resolution |
|-------|------------|
| Pierwsza wersja diag SQL używała `pp_shop_categories_languages` (nie istnieje) | Sprawdzono `SHOW TABLES` — tabele to `*_langs` (skrót). Zaktualizowano zapytania |
| Pierwsza wersja używała `shop_category_id` (nie istnieje) | Kolumna to `category_id`. Sprawdzono `SHOW COLUMNS` |
| LayoutEngine niedostępny w bootstrap testów | `require_once` w teście (bootstrap PSR-4 nie ładuje `\front\` namespace) |
## Next Phase Readiness
**Ready:**
- Codebase z `applyEntityMetaToPage()` — gotowa do reuse w przyszłych fixach SEO (np. og:title dla kategorii)
- Test pattern dla `\front\LayoutEngine` ustalony (require_once + asercje na pure-function)
**Concerns:**
- LayoutEngine::show() nadal jest 400+ linijowym monolitem ze statycznymi globalami — dalsze ekstrakcje wskazane, ale poza scope tej fazy
- Klienci z istniejącymi instalacjami muszą uzupełnić meta_title/keywords/description przez panel admina (lub zaakceptować że podstrony mają brak meta — co teraz przynajmniej NIE jest niepoprawnym tytułem homepage)
**Blockers:**
- None.
## Skill Audit (Phase 19)
| Expected | Invoked | Notes |
|----------|---------|-------|
| /feature-dev | ○ | Hotfix z konkretną instrukcją — override per pattern Phase 15-18 |
| /koniec-pracy | ○ | Pending — przy zamknięciu sesji jeśli release wchodzi do update package |
---
*Phase: 19-frontend-meta-tags-fix, Plan: 01*
*Completed: 2026-05-13*

View File

@@ -0,0 +1,129 @@
# Phase 19 — DIAGNOSTICS
**Data:** 2026-05-13
**Środowisko:** shoppro.project-dc.pl (produkcja) — DB `host117523_shoppro` na `host117523.hostido.net.pl`
## Wynik diagnostyczny: ROOT CAUSE w kodzie (LayoutEngine.php)
`$page['language']['meta_title']` z domyślnej strony (homepage id=6) **nigdy nie jest nadpisywany** w gałęziach kategorii/artykułu/produktu w `LayoutEngine::show()`. Linia 332 priorytetuje `meta_title` nad `title`, więc tytuł homepage wycieka na wszystkie podstrony.
Dodatkowo: meta_description/meta_keywords dla podstron też nie zachowują się dobrze, bo w DB klient ma literalne zaślepki ("description", "keywords") albo NULL — ale to drugorzędny problem względem bugu w kodzie.
---
## 1) pp_routes — działa poprawnie
```
URL: sen-i-otulenie
MATCH pattern='^sen-i-otulenie$' dest='index.php?category=10&lang=pl'
URL: kocyk-niemowlaka-szczeniak-z-balonikiem-fuksja
MATCH pattern='^kocyk-niemowlaka-szczeniak-z-balonikiem-fuksja$' dest='index.php?product=522'
```
Hipoteza "pp_routes nie ustawia category=/product=" — **OBALONA**.
## 2) Meta kategorii 10 (sen-i-otulenie), lang=pl
```
id = 331
category_id = 10
lang_id = 'pl'
title = 'Sen i otulenie'
meta_title = NULL
meta_description = 'description' ← LITERALNA ZAŚLEPKA w DB
meta_keywords = 'keywords' ← LITERALNA ZAŚLEPKA w DB
seo_link = 'sen-i-otulenie'
category_title = 'Sen i otulenie'
```
## 3) Meta produktu 522 (kocyk-niemowlaka...), lang=pl
```
id = 4040
product_id = 522
lang_id = 'pl'
name = 'Kocyk niemowlaka - Szczeniak z balonikiem - Fuksja'
meta_title = NULL
meta_description = NULL
meta_keywords = NULL
seo_link = 'kocyk-niemowlaka-szczeniak-z-balonikiem-fuksja'
```
## 4) Default page (homepage) — id=6, start=1, lang=pl
```
id = 6
start = 1
title = 'Home'
meta_title = 'Sklep z akcesoriami dla dzieci i niemowląt, kocyki minky, poduszki, ubranka'
meta_keywords = '' (puste)
meta_description = 'Marianek to sklep internetowy, w którym znajdziecie Państwo artykuły dla dzieci i niemowląt...'
```
To jest źródło "wyciekającego" tytułu na podstronach.
---
## Analiza kodu (autoload/front/LayoutEngine.php)
### Gałąź kategorii (linie 152-168)
```php
if ( \Shared\Helpers\Helpers::get( 'category' ) ) {
$category = $categoryRepo->frontCategoryDetails(...);
if ( $category['language']['meta_title'] )
$page['language']['title'] = $category['language']['meta_title']; // ← przypisuje do TITLE, nie meta_title
else
$page['language']['title'] = $category['language']['title'];
$page['show_title'] = true;
$page['language']['meta_keywords'] = $category['language']['meta_keywords']; // OK
$page['language']['meta_description'] = $category['language']['meta_description']; // OK
// BRAK: $page['language']['meta_title'] = $category['language']['meta_title'];
}
```
### Gałąź produktu (linie 194-213) — identyczny bug
### Gałąź artykułu (linie 174-189) — identyczny bug
### Substytucja [TITLE] (linia 332)
```php
$html = str_replace( '[TITLE]',
$page['language']['meta_title']
? $page['language']['meta_title'] . ' | ' . $settings['firm_name']
: $page['language']['title'] . ' | ' . $settings['firm_name'],
$html );
```
`meta_title` z homepage żyje dalej w `$page['language']` (bo nie został zresetowany w gałęzi kategorii/produktu) → wygrywa nad title kategorii/produktu.
---
## Wpływ na obserwowane zachowanie
| URL | Obserwowane `<title>` | Powód |
|-----|----------------------|-------|
| /sen-i-otulenie | "Sklep z akcesoriami... \| shopPRO 1" | meta_title homepage wycieka |
| /kocyk-niemowlaka-... | "Sklep z akcesoriami... \| shopPRO 1" | meta_title homepage wycieka |
| URL | Obserwowane `<meta description>` | Powód |
|-----|---------------------------------|-------|
| /sen-i-otulenie | "description" | meta_description kategorii (literalna zaślepka) — POPRAWNE nadpisanie, ale dane w DB są wadliwe |
| /kocyk-niemowlaka-... | "" (puste) | meta_description produktu = NULL — POPRAWNE nadpisanie |
| URL | Obserwowane `<meta keywords>` | Powód |
|-----|------------------------------|-------|
| /sen-i-otulenie | "keywords" | meta_keywords kategorii (literalna zaślepka) — POPRAWNE nadpisanie, ale dane w DB są wadliwe |
| /kocyk-niemowlaka-... | "" (puste) | meta_keywords produktu = NULL — POPRAWNE nadpisanie |
---
## Wniosek
**Bug w kodzie**: `LayoutEngine::show()` w 3 gałęziach (category/article/product) zapisuje meta_title kategorii do `title`, ale nie nadpisuje `$page['language']['meta_title']`. Wartość z homepage zostaje i wygrywa.
**Dane klienta**: oddzielny issue — meta_keywords/meta_description dla kategorii to literalne zaślepki "keywords"/"description", produkt ma NULL. To NIE jest bug shopPRO — admin musi wypełnić panel.
**Rekomendacja fixu**: opcja `fix-engine-detection` z planu — naprawić gałęzie kategorii/artykułu/produktu, by zawsze nadpisywały `meta_title` (nawet pustym/NULL), oraz uprościć logikę title żeby była symetryczna.

View File

@@ -0,0 +1,197 @@
---
phase: 20-category-edit-mojibake-fix
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- admin/templates/shop-category/category-edit.php
autonomous: false
delegation: off
---
<objective>
## Goal
Etykiety formularza edycji kategorii (`/admin/shop_category/category_edit/id=X`) pokazują poprawne polskie znaki: "Treść", "Język domyślny", "Tytuł kategorii (h1)", "Wyświetlić podkategorie", "Sortowanie produktów", "Blokuj indeksację", "Opis kategorii (rozwinięcie)" — zamiast mojibake `TreĹć`, `JÄ™zyk domyĹlny`, itp.
## Purpose
Mojibake w etykietach UI panelu admina utrudnia korzystanie z formularza. Administrator musi się domyślać znaczenia pól. Naprawia profesjonalny wygląd panelu i poprawia UX uzupełniania kategorii (m.in. SEO).
## Output
- `admin/templates/shop-category/category-edit.php` z poprawnymi polskimi znakami (UTF-8) we wszystkich hardcoded stringach
- Weryfikacja wizualna w panelu admina
## Root cause
Plik został kiedyś zapisany z UTF-8 zinterpretowanym jako Windows-1252 i ponownie zakodowanym do UTF-8 (classic double-encoding mojibake). `file` raportuje "UTF-8 text" bo bajty są poprawnym UTF-8 — tyle że ich znaczenie po dekodowaniu UTF-8 to bezsensowne kombinacje znaków łacińskich (Ä, Ĺ, Ă itp.).
Grep `Ä[\x{84}-\x{99}]|Ĺ|Ăł` wskazał, że **tylko ten jeden plik** w całym repo (`admin/templates/`, `templates/`, `autoload/`) jest dotknięty — pozostałe templates mają poprawne UTF-8.
</objective>
<context>
## Project Context
@.paul/PROJECT.md
@.paul/STATE.md
## Source Files
@admin/templates/shop-category/category-edit.php
## Clarifications
- **Objaw** — Mojibake (`Ä€„` zamiast `ą`) w labelkach formularza edycji kategorii
→ Odpowiedź: potwierdzone — mojibake (double-encoded UTF-8)
- **Pola** — Nie wiem, sprawdź
→ Odpowiedź (po inspekcji): WSZYSTKIE hardcoded labelki w pliku `category-edit.php` (9 wystąpień), żadne inne pliki templates nie dotknięte
- **Migracja danych** — chodzi o labelki, nie zapisywane dane
→ Odpowiedź: plan dotyczy tylko hardcoded stringów w pliku PHP. Dane w bazie (mieszane HTML entities w additional_text) — oddzielny issue, NIE w tym planie
## Wykryte miejsca mojibake (grep)
```
Line 10: TreĹć → Treść
Line 20: JÄ™zyk domyĹlny → Język domyślny
Line 48: Opis kategorii (rozwiniÄ™cie) → Opis kategorii (rozwinięcie)
Line 87: Sortowanie produktĂłw → Sortowanie produktów
Line 96: WyĹwietlić podkategorie → Wyświetlić podkategorie
Line 107: JÄ™zyk domyĹlny → Język domyślny (duplikat)
Line 127: TytuĹ kategorii (h1) → Tytuł kategorii (h1)
Line 159: Blokuj indeksacjÄ™ → Blokuj indeksację
```
Mapowanie sekwencji (do podmiany):
- `ć``ć`, `Ä…``ą`, `Ä™``ę`, `Ĺ‚``ł`, `Ĺ›``ś`, `Ĺ„``ń`, `Ĺş``ź`, `Ĺľ``ż`
- `Ć``Ć`, `Ä„``Ą`, `Ę``Ę`, `Ĺ``Ł`, `Ĺš``Ś`, `Ĺƒ``Ń`, `Ĺ¹``Ź`, `Ĺ˝``Ż`
- `Ăł``ó`, `Ă“``Ó`
</context>
<acceptance_criteria>
## AC-1: Brak mojibake w pliku
```gherkin
Given plik admin/templates/shop-category/category-edit.php po fixie
When uruchomię grep regex `Ä[\x{84}-\x{99}]|Ĺ|Ăł|Ĺ|Ĺ|Ä|Ä|Ä`
Then nie ma żadnego dopasowania (0 linii)
And plik dalej jest UTF-8 (bez BOM) sprawdzalne przez `file`
```
## AC-2: Labelki formularza renderują się z polskimi znakami
```gherkin
Given administrator otwiera /admin/shop_category/category_edit/id=10 po deployu
When sprawdza nazwy tabów i etykiet pól
Then widzi "Treść", "Ustawienia", "SEO" jako nazwy tabów
And widzi "Język domyślny", "Tytuł kategorii (h1)", "Sortowanie produktów",
"Wyświetlić podkategorie", "Blokuj indeksację", "Opis kategorii (rozwinięcie)"
jako etykiety pól
And NIE widzi `Ä`, `Ĺ`, `Ă` w żadnym widocznym miejscu na tej stronie
```
## AC-3: Brak regresji w innych szablonach
```gherkin
Given fix dotyczy tylko jednego pliku
When uruchomię grep mojibake w admin/templates/ + templates/ + autoload/
Then żaden inny plik nie zawiera mojibake (tak jak przed fixem)
And cała suita PHPUnit (846 testów) pozostaje zielona
```
</acceptance_criteria>
<tasks>
<task type="auto">
<name>Task 1: Podmień double-encoded UTF-8 na właściwe polskie znaki</name>
<files>admin/templates/shop-category/category-edit.php</files>
<action>
Wykonaj globalne podstawienia w pliku z mojibake → UTF-8 polskie znaki:
Podstawienia całych słów (najbezpieczniejsze — unikamy fałszywych alarmów z `Ä` w innym kontekście):
"TreĹć" → "Treść"
"JÄ™zyk domyĹlny" → "Język domyślny"
"rozwiniÄ™cie" → "rozwinięcie"
"Sortowanie produktĂłw" → "Sortowanie produktów"
"WyĹwietlić podkategorie" → "Wyświetlić podkategorie"
"TytuĹ kategorii (h1)" → "Tytuł kategorii (h1)"
"Blokuj indeksacjÄ™" → "Blokuj indeksację"
Po wszystkich podstawieniach zweryfikuj greppem że żadne mojibake już nie zostało.
Avoid: globalne sekwencyjne `str_replace` typu `ć``ć` bez kontekstu — mogłoby przypadkiem złamać poprawne fragmenty w przyszłości (na razie nie ma ryzyka, ale lepiej trzymać się konkretnych słów).
Avoid: zmiana encodingu pliku (musi zostać UTF-8 bez BOM, CRLF — jak teraz).
Avoid: jakichkolwiek zmian poza podmianami stringów (struktura HTML, logika, imports — bez zmian).
</action>
<verify>
grep -P "Ä[\x{84}-\x{99}]|Ĺ|Ăł|Ĺ›|Ĺ‚|Ä™|Ä…|ć|Ĺ„" admin/templates/shop-category/category-edit.php
→ 0 dopasowań
file admin/templates/shop-category/category-edit.php → UTF-8 text (bez BOM)
</verify>
<done>AC-1 satisfied: plik bez mojibake, dalej UTF-8</done>
</task>
<task type="auto">
<name>Task 2: Weryfikacja braku regresji + odpalenie suity testów</name>
<files>(brak modyfikacji — weryfikacja)</files>
<action>
1. Powtórz globalny grep mojibake w admin/templates/, templates/, autoload/ — powinno być 0 plików (poza tym fixowanym, który po Task 1 też ma 0)
2. Uruchom suitę PHPUnit: `php phpunit.phar`
3. Sprawdź że nie ma żadnych nowych failów (oczekiwane: 846 OK)
</action>
<verify>
grep -rl -P "Ä[\x{84}-\x{99}]|Ĺ" admin/templates/ templates/ autoload/ — brak wyniku
php phpunit.phar — exit code 0, 846 tests OK
</verify>
<done>AC-3 satisfied: brak regresji, suita zielona</done>
</task>
<task type="checkpoint:human-verify" gate="blocking">
<what-built>
Plik admin/templates/shop-category/category-edit.php z polskimi znakami w UTF-8
(auto-upload FTP wysyła zmianę na shoppro.project-dc.pl po zapisie pliku)
</what-built>
<how-to-verify>
1. Otwórz https://shoppro.project-dc.pl/admin/shop_category/category_edit/id=10 (zaloguj się jeśli trzeba)
2. Sprawdź nazwy 3 tabów: "Treść", "Ustawienia", "SEO" — powinny być po polsku, bez mojibake
3. W tabie "Treść" sprawdź:
- tooltip ikony gwiazdki: "Język domyślny"
- etykiety pól: "Nazwa kategorii", "Opis kategorii", "Opis kategorii (rozwinięcie)", "Dodatkowy tekst (nad produktami)"
4. W tabie "Ustawienia" sprawdź: "Aktywna", "Sortowanie produktów", "Wyświetlić podkategorie"
5. W tabie "SEO" sprawdź: "Tytuł kategorii (h1)", "Blokuj indeksację"
6. Otwórz inną kategorię (np. id=11) — to samo dla regresji
</how-to-verify>
<resume-signal>Wpisz "approved" by kontynuować, lub opisz issues</resume-signal>
</task>
</tasks>
<boundaries>
## DO NOT CHANGE
- Struktura HTML pliku (tagi, klasy CSS, JS scripts)
- Logika PHP (if/foreach, wywołania Tpl)
- Inne pliki w admin/templates/shop-category/ — nie są dotknięte mojibake
- Encoding pliku (musi zostać UTF-8 bez BOM, jak obecnie)
- Dane w bazie (HTML entities w `additional_text` to oddzielny issue)
## SCOPE LIMITS
- Plan dotyczy TYLKO pliku `admin/templates/shop-category/category-edit.php`
- NIE skanujemy wszystkich szablonów admina pod kątem mojibake (już sprawdzone — pozostałe OK)
- NIE migrujemy danych w DB (admin uzupełnia ręcznie jeśli ma encje)
- Bez build update package — to robi `/koniec-pracy` po UNIFY
</boundaries>
<verification>
Przed zamknięciem planu:
- [ ] grep mojibake → 0 wyników w category-edit.php
- [ ] file potwierdza UTF-8 bez BOM
- [ ] Suita PHPUnit 846 zielona
- [ ] Human-verify na 2 kategoriach (id=10 i jeszcze jednej) zatwierdzony
- [ ] AC-1, AC-2, AC-3 spełnione
</verification>
<success_criteria>
- 9 wystąpień mojibake naprawionych w 1 pliku
- Brak regresji w innych szablonach
- Suita testów zielona
- Wizualna weryfikacja w panelu admina OK
</success_criteria>
<output>
After completion: `.paul/phases/20-category-edit-mojibake-fix/20-01-SUMMARY.md`
</output>

View File

@@ -0,0 +1,121 @@
---
phase: 20-category-edit-mojibake-fix
plan: 01
subsystem: admin-ui
tags: [encoding, utf8, mojibake, admin-templates, polish-chars]
requires:
- phase: none
provides: n/a
provides:
- poprawne polskie znaki w labelkach formularza edycji kategorii
- pewność że pozostałe pliki admin/templates/, templates/, autoload/ są wolne od double-encoded UTF-8 mojibake
affects: []
tech-stack:
added: []
patterns:
- "Detekcja double-encoded UTF-8: grep `Ä[\\x{84}-\\x{99}]|Ĺ|Ăł|Ĺ›|Ĺ‚|Ä™|Ä…|ć|Ĺ„` znajduje pliki z mojibake"
key-files:
modified:
- admin/templates/shop-category/category-edit.php
key-decisions:
- "Fix tylko 1 plik (9 wystąpień) — żaden inny szablon w repo nie ma mojibake"
- "Podmiana całych słów zamiast generyczna `ć``ć` — unika fałszywych alarmów na przyszłość"
- "Brak migracji danych w DB (te są oddzielnym tematem — HTML entities w additional_text)"
patterns-established:
- "Workflow detekcji mojibake: jeden grep regex pokrywa najczęstsze sekwencje Win-1252→UTF-8 double-encoding"
duration: ~10min
started: 2026-05-13T16:30:00Z
completed: 2026-05-13T16:40:00Z
---
# Phase 20 Plan 01: Category edit mojibake fix — Summary
**9 wystąpień double-encoded UTF-8 w `admin/templates/shop-category/category-edit.php` naprawione na poprawne polskie znaki; pozostałe szablony zweryfikowane jako wolne od mojibake.**
## Performance
| Metric | Value |
|--------|-------|
| Duration | ~10min |
| Tasks | 3 z 3 wykonane |
| Files modified | 1 |
| Mojibake fixed | 9 wystąpień (7 unikalnych fraz) |
| Total suite | 846 zielone (bez zmian) |
## Acceptance Criteria Results
| Criterion | Status | Notes |
|-----------|--------|-------|
| AC-1: Brak mojibake w pliku | Pass | grep mojibake → 0 wyników; file → UTF-8 bez BOM |
| AC-2: Labelki renderują się z polskimi znakami | Pass | Human-verify approved przez użytkownika |
| AC-3: Brak regresji w innych szablonach | Pass | grep w admin/templates/+templates/+autoload/ → 0 plików; suita 846 OK |
## Accomplishments
- Wykryto i naprawiono mojibake w jedynym dotkniętym pliku repo
- Etykiety formularza edycji kategorii w panelu admina są czytelne po polsku ("Treść", "Język domyślny", "Sortowanie produktów", "Tytuł kategorii (h1)", "Blokuj indeksację", "Wyświetlić podkategorie", "Opis kategorii (rozwinięcie)")
- Auto-upload FTP wysłał plik na produkcję; weryfikacja w przeglądarce potwierdzona
## Files Created/Modified
| File | Change | Purpose |
|------|--------|---------|
| `admin/templates/shop-category/category-edit.php` | Modified | Podmiana 9 wystąpień double-encoded UTF-8 na poprawne polskie znaki (7 unikalnych fraz: TreĹć, JÄ™zyk domyĹlny x2, rozwiniÄ™cie, Sortowanie produktĂłw, WyĹwietlić podkategorie, TytuĹ kategorii, Blokuj indeksacjÄ™) |
## Decisions Made
| Decision | Rationale | Impact |
|----------|-----------|--------|
| Podmiana całych słów zamiast `ć``ć` itd. | Bezpieczniej — nie zostawia ryzyka uszkodzenia poprawnych fragmentów przy hipotetycznym przyszłym pojawieniu się tych bajtów w innym kontekście | Edits są precyzyjne, łatwe do code-review |
| Fix tylko ten jeden plik | grep pokazał że to jedyny plik z mojibake w całym repo | Scope minimalny, brak ryzyka regresji |
## Deviations from Plan
### Summary
| Type | Count | Impact |
|------|-------|--------|
| Auto-fixed | 0 | — |
| Scope additions | 0 | — |
| Deferred | 1 | Git commit transition-phase (pattern z faz 15-19) |
**Total impact:** Plan wykonany dokładnie według planu.
### Deferred Items
- Transition-phase git commit dla Phase 20 — pattern z faz 15-19 (commit zrobi `/koniec-pracy` lub user manualnie). Brak negatywnego impactu — auto-upload FTP już ma plik na produkcji.
## Issues Encountered
None.
## Next Phase Readiness
**Ready:**
- Pattern detekcji mojibake (regex) udokumentowany — przydatny przy przyszłych audytach encoding
- Plik category-edit.php czysty UTF-8
**Concerns:**
- Inne pola w bazie (`additional_text` kategorii) mają mieszane HTML entities z polskimi znakami — oddzielny issue, nie blokuje
- Brak automatycznego sprawdzania encoding w CI — przyszły fix mógłby wprowadzić nowy mojibake bez wykrycia
**Blockers:** None.
## Skill Audit (Phase 20)
| Expected | Invoked | Notes |
|----------|---------|-------|
| /feature-dev | ○ | Triviany fix labelek — override per pattern poprzednich faz |
| /koniec-pracy | ○ | Pending — uruchomić przy release update package |
---
*Phase: 20-category-edit-mojibake-fix, Plan: 01*
*Completed: 2026-05-13*

View File

@@ -0,0 +1,173 @@
---
phase: 21-admin-update-view-js-fix
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- admin/templates/update/main-view.php
autonomous: false
delegation: off
---
<objective>
## Goal
Naprawić niedziałające przyciski na `/admin/update/main_view/`: "Aktualizuj do wyższej wersji" (`#confirm`), "Aktualizuj do najwyższej wersji" (`#confirmUpdateAll`) oraz globalny "Wyczyść cache" (`#clear-cache-btn`) z headera admina.
## Purpose
Na instancjach klientów shopPRO aktualizacja wersji nie była możliwa — zdalny changelog z `shoppro.project-dc.pl/updates/changelog.php` wstrzykiwał niezbalansowany HTML (niezamknięty `<script>`/`<style>`/`<textarea>`/`<!--`), który „połykał" pozostałą część dokumentu jako tekst i blokował parsowanie inline-scriptu z handlerami oraz footer-scriptu z `main-layout.php`. Bez tej naprawy klienci nie mogą aktualizować systemu.
## Output
Zmodyfikowany plik `admin/templates/update/main-view.php`:
1. Blok `<script>` z handlerami przeniesiony PRZED sekcję Changelog.
2. Zdalny changelog sanityzowany przed wyrenderowaniem (usunięcie komentarzy HTML + `strip_tags` z białą listą).
</objective>
<context>
## Project Context
@.paul/PROJECT.md
@.paul/ROADMAP.md
@.paul/STATE.md
## Source Files
@admin/templates/update/main-view.php
@change.md
<clarifications>
- **Scope** — Wystarczy fix zgodny z change.md (jeden plik, dwa kroki)?
→ Odpowiedź: brak pytań — change.md zawiera kompletny opis problemu, przyczyny i konkretne fragmenty kodu do wdrożenia. Plan = mechaniczne wdrożenie change.md.
</clarifications>
</context>
<acceptance_criteria>
## AC-1: Handlery JS działają niezależnie od treści zdalnego changeloga
```gherkin
Given administrator jest na /admin/update/main_view/ z wersją starszą niż najnowsza
And zdalny serwer shoppro.project-dc.pl zwraca dowolny HTML (nawet niezbalansowany)
When admin klika "Aktualizuj do wyższej wersji" lub "Aktualizuj do najwyższej wersji"
Then otwiera się dialog `$.confirm` (potwierdzenie aktualizacji)
And po potwierdzeniu uruchamia się aktualizacja (redirect /admin/update/update/ lub AJAX /admin/update/updateAll/)
```
## AC-2: Globalny przycisk "Wyczyść cache" działa na podstronie update
```gherkin
Given administrator jest na /admin/update/main_view/
When klika "Wyczyść cache" w headerze admina (`#clear-cache-btn`)
Then footer-script z main-layout.php obsługuje kliknięcie (nie jest połknięty" przez nieparzysty tag changeloga)
```
## AC-3: Zdalny changelog nadal się wyświetla, ale nie może popsuć strony
```gherkin
Given zdalny changelog zawiera `<script>`, `<style>`, `<textarea>`, `<iframe>`, `<!--...-->` lub niezamknięte tagi strukturalne
When szablon main-view.php renderuje sekcję Changelog
Then niedozwolone tagi są usunięte przez `strip_tags` (whitelist: <p><br><b><strong><i><em><u><ul><ol><li><h1>-<h6><span><div><a><pre><code><hr>)
And komentarze HTML `<!--...-->` są usuwane
And treść tekstowa changeloga + dozwolone tagi formatujące są widoczne na stronie
```
</acceptance_criteria>
<tasks>
<task type="auto">
<name>Task 1: Reorder + sanitize w main-view.php</name>
<files>admin/templates/update/main-view.php</files>
<action>
Wdrożyć zmianę zgodnie z change.md:
1. **Przenieść blok `<script type="text/javascript">...</script>`** (linie 64-140 obecnej wersji) PRZED sekcję Changelog (przed `<div class="panel">` zawierający `<span class="panel-title">Changelog</span>`).
Nowa kolejność w szablonie:
- panel "Aktualizacja systemu" (wersje + przyciski)
- panel "Log ostatniej aktualizacji" (warunkowy)
- **`<script>` z handlerami** ← przeniesiony tutaj
- panel "Changelog" (zdalny `file_get_contents` + sanityzacja)
2. **Zastąpić linię z `file_get_contents`** (obecnie linia 60):
```php
<?= @file_get_contents( 'https://shoppro.project-dc.pl/updates/changelog.php?ver=' . $this->ver ); ?>
```
na blok sanityzacji:
```php
<?php
$changelog = (string) @file_get_contents( 'https://shoppro.project-dc.pl/updates/changelog.php?ver=' . $this->ver );
$changelog = preg_replace( '/<!--.*?-->/s', '', $changelog );
$changelog = strip_tags( $changelog, '<p><br><b><strong><i><em><u><ul><ol><li><h1><h2><h3><h4><h5><h6><span><div><a><pre><code><hr>' );
echo $changelog;
?>
```
Nie zmieniać:
- logiki JS w bloku `<script>` (handlery `#confirm`, `#confirmUpdateAll`, funkcja `updateAll`)
- struktury HTML paneli (klasy CSS, ID, atrybuty)
- tekstów polskich w szablonie
- składni krótkich tagów `<?` (kompatybilność PHP < 8.0 — szablon używa krótkich tagów, zachować konwencję)
Plik zostaje UTF-8 bez BOM.
Unikać: wprowadzania dodatkowych zależności, refaktoringu spoza zakresu change.md, zmiany URL zdalnego changeloga.
</action>
<verify>
1. `php -l admin/templates/update/main-view.php` → "No syntax errors"
2. Grep: `grep -n "strip_tags" admin/templates/update/main-view.php` → linia z whitelistą obecna
3. Grep: blok `<script type="text/javascript">` znajduje się przed `<span class="panel-title">Changelog</span>` w pliku (sprawdzić numery linii)
</verify>
<done>AC-3 satisfied (sanityzacja whitelist + usunięcie komentarzy HTML); strukturalna baza dla AC-1 i AC-2 (reorder przed changelogiem)</done>
</task>
<task type="checkpoint:human-verify" gate="blocking">
<what-built>
Reorder bloku `<script>` przed sekcję Changelog + sanityzacja zdalnego changeloga w `admin/templates/update/main-view.php`.
</what-built>
<how-to-verify>
1. Wgrać zmodyfikowany plik na instancję testową (lub lokalnie, jeśli setup pozwala).
2. Hard-reload (Ctrl+F5) w przeglądarce.
3. Restart PHP-FPM / wyczyścić OPcache (jeśli aktywny).
4. Wejść na `/admin/update/main_view/` jako admin (przy wersji starszej niż najnowsza, żeby przyciski się pokazały).
5. **AC-1:** Kliknąć "Aktualizuj do wyższej wersji" → dialog `$.confirm` musi się otworzyć. To samo dla "Aktualizuj do najwyższej wersji".
6. **AC-2:** Kliknąć "Wyczyść cache" w headerze admina → handler musi zadziałać (komunikat / akcja zgodna z `main-layout.php`).
7. **AC-3:** Sekcja Changelog nadal wyświetla treść ze zdalnego serwera (tekst + formatowanie), ale bez `<script>`/`<style>`/`<iframe>` (View Source).
8. Sprawdzić DevTools console — brak nowych błędów JS związanych z `main-view.php`.
</how-to-verify>
<resume-signal>Type "approved" to continue, or describe issues to fix</resume-signal>
</task>
</tasks>
<boundaries>
## DO NOT CHANGE
- Logikę JS handlerów aktualizacji (`#confirm`, `#confirmUpdateAll`, funkcja `updateAll`) — wyłącznie reorder bloku.
- `admin/templates/site/main-layout.php` (footer-script `#clear-cache-btn`) — naprawa jest skutkiem ubocznym reorderu w main-view.
- URL zdalnego changeloga (`https://shoppro.project-dc.pl/updates/changelog.php`).
- Logikę kontrolera `UpdateController` i endpointy `/admin/update/update/`, `/admin/update/updateAll/`.
- Inne szablony w `admin/templates/update/`.
## SCOPE LIMITS
- Tylko jeden plik: `admin/templates/update/main-view.php`.
- Brak testów PHPUnit (zmiana w warstwie szablonu, brak unit-testowalnej logiki).
- Brak zmian w bazie / migracjach.
- Brak release update package — to robi `/koniec-pracy` po UNIFY.
- Brak zmiany sposobu pobierania changeloga (np. cache, timeout, fallback) — to byłby osobny scope.
</boundaries>
<verification>
Before declaring plan complete:
- [ ] `php -l admin/templates/update/main-view.php` passes
- [ ] Blok `<script>` znajduje się przed sekcją Changelog (kolejność w pliku)
- [ ] Sekcja Changelog zawiera `strip_tags` z whitelistą formatowania
- [ ] Human-verify checkpoint approved (3 przyciski działają, changelog renderuje się sanityzowany)
- [ ] All acceptance criteria met
</verification>
<success_criteria>
- AC-1, AC-2, AC-3 spełnione i potwierdzone w human-verify
- Brak regresji innych funkcji panelu admina
- Pełna suita PHPUnit (`./test.ps1`) nadal zielona (846 testów — bez nowych testów w tym planie)
</success_criteria>
<output>
After completion, create `.paul/phases/21-admin-update-view-js-fix/21-01-SUMMARY.md`
</output>

View File

@@ -0,0 +1,124 @@
---
phase: 21-admin-update-view-js-fix
plan: 01
subsystem: admin
tags: [template, js, htmlsanitize, update, admin-panel]
requires:
- phase: none
provides: none
provides:
- Działające przyciski aktualizacji systemu (`#confirm`, `#confirmUpdateAll`) na /admin/update/main_view/
- Działający globalny przycisk "Wyczyść cache" (`#clear-cache-btn`) na tej podstronie
- Sanityzacja zdalnego changeloga (whitelist tagów formatujących + usunięcie komentarzy HTML)
affects: [admin update workflow, future remote-content includes]
tech-stack:
added: []
patterns:
- "Zdalne file_get_contents w szablonie sanityzować przez strip_tags z whitelistą + usunięcie komentarzy HTML; krytyczne <script> renderować PRZED takim includem"
key-files:
created: []
modified:
- admin/templates/update/main-view.php
key-decisions:
- "Reorder: blok <script> z handlerami PRZED sekcję Changelog (nie po) — odporność na uszkodzony zdalny HTML"
- "Whitelist strip_tags ogranicza zdalny changelog do tagów formatujących; <script>/<style>/<textarea>/<iframe> wykluczone"
patterns-established:
- "Sanityzacja zdalnego HTML w szablonach admina: preg_replace('/<!--.*?-->/s', '', $html) + strip_tags(whitelist)"
duration: ~15min
started: 2026-05-13T00:00:00Z
completed: 2026-05-13T00:00:00Z
---
# Phase 21 Plan 01: Admin update view JS fix — Summary
**Naprawiono niedziałające przyciski aktualizacji systemu i globalny "Wyczyść cache" na `/admin/update/main_view/` przez reorder bloku `<script>` przed sekcję Changelog + sanityzację zdalnego changeloga w `admin/templates/update/main-view.php`.**
## Performance
| Metric | Value |
|--------|-------|
| Duration | ~15min |
| Tasks | 1 auto + 1 checkpoint:human-verify |
| Files modified | 1 |
| Tests | 846 / 2348 asercji — OK |
## Acceptance Criteria Results
| Criterion | Status | Notes |
|-----------|--------|-------|
| AC-1: Handlery JS działają niezależnie od treści zdalnego changeloga | Pass | Blok `<script>` w linii 55, przed sekcją Changelog w linii 135 — handlery `#confirm`/`#confirmUpdateAll` podpinają się przed parsowaniem zdalnego HTML |
| AC-2: Globalny "Wyczyść cache" działa na podstronie update | Pass | Footer-script z `main-layout.php` (#clear-cache-btn) nie jest już „połykany" przez niezbalansowany tag changeloga — potwierdzone human-verify |
| AC-3: Zdalny changelog renderuje się sanityzowany | Pass | `preg_replace('/<!--.*?-->/s', '', ...)` usuwa komentarze HTML, `strip_tags` z whitelistą `<p><br><b><strong><i><em><u><ul><ol><li><h1>-<h6><span><div><a><pre><code><hr>` wycina niedozwolone tagi strukturalne i wykonywalne |
## Accomplishments
- Klient może aktualizować shopPRO na instancjach — root cause (niezbalansowany zdalny HTML „połykający" inline-script handlerów i footer-script `main-layout.php`) wyeliminowany przez reorder + sanityzację.
- Globalny "Wyczyść cache" w headerze admina działa nie tylko na podstronie update, ale i pośrednio wszędzie indziej (footer-script się teraz parsuje).
- Dodatkowy zysk bezpieczeństwa: zdalny `shoppro.project-dc.pl/updates/changelog.php` nie może już wstrzyknąć `<script>`/`<style>`/`<iframe>` w panel admina.
## Files Created/Modified
| File | Change | Purpose |
|------|--------|---------|
| `admin/templates/update/main-view.php` | Modified | Reorder bloku `<script>` przed sekcję Changelog + sanityzacja zdalnego changeloga (strip_tags whitelist + usunięcie komentarzy HTML) |
## Decisions Made
| Decision | Rationale | Impact |
|----------|-----------|--------|
| Reorder zamiast np. wyłączenia zdalnego changeloga | Zachowuje istniejącą funkcjonalność (admin widzi nowości), eliminuje root cause | Minimalna zmiana, ten sam UX |
| `strip_tags` z białą listą zamiast np. iframe sandbox | Najprostszy, zerowy narzut, działa na PHP 7.4, brak dodatkowych zależności | Zdalny content może być tylko tekstem z formatowaniem — wystarczy do changeloga |
| Whitelist obejmuje tagi formatujące + `<div>`/`<span>`/`<a>` | Zachowuje typową strukturę changeloga | Zdalny serwer nie może już strukturalnie popsuć strony |
## Deviations from Plan
### Summary
| Type | Count | Impact |
|------|-------|--------|
| Auto-fixed | 0 | — |
| Scope additions | 0 | — |
| Deferred | 0 | — |
**Total impact:** Plan wykonany dokładnie według change.md, bez odstępstw.
### Deferred Items
None.
## Issues Encountered
| Issue | Resolution |
|-------|------------|
| `./test.ps1` z CLAUDE.md nie istnieje w repo (zmiana w lokalnej konfiguracji) | Uruchomiono `php phpunit.phar` bezpośrednio — 846 testów zielonych |
## Skill Audit (Phase 21)
| Expected | Invoked | Notes |
|----------|---------|-------|
| /feature-dev | ○ | Hotfix template-only z konkretną instrukcją w change.md — override per pattern poprzednich faz |
| /koniec-pracy | ○ | Pending — uruchomić przy release update package |
Skill audit: not blocking (warning only).
## Next Phase Readiness
**Ready:**
- Repo gotowe do `/koniec-pracy` (release update package z nową wersją zawierającą fix `admin/templates/update/main-view.php`)
- Brak regresji testowej — 846 testów zielonych
**Concerns:**
- Po wgraniu update package na instancje klientów konieczny: hard-reload (Ctrl+F5) + restart PHP-FPM / wyczyszczenie OPcache (zgodnie z change.md sekcja "Po wgraniu")
**Blockers:**
- None
---
*Phase: 21-admin-update-view-js-fix, Plan: 01*
*Completed: 2026-05-13*

File diff suppressed because one or more lines are too long

View File

@@ -2,5 +2,5 @@ projectKey=shopPRO
serverUrl=https://sonar.project-pro.pl serverUrl=https://sonar.project-pro.pl
serverVersion=26.3.0.120487 serverVersion=26.3.0.120487
dashboardUrl=https://sonar.project-pro.pl/dashboard?id=shopPRO dashboardUrl=https://sonar.project-pro.pl/dashboard?id=shopPRO
ceTaskId=646324f5-0b31-4df4-a424-9aa2e5c89d4b ceTaskId=33e10a49-4790-400e-bf25-9b40eaed053e
ceTaskUrl=https://sonar.project-pro.pl/api/ce/task?id=646324f5-0b31-4df4-a424-9aa2e5c89d4b ceTaskUrl=https://sonar.project-pro.pl/api/ce/task?id=33e10a49-4790-400e-bf25-9b40eaed053e

View File

@@ -7,7 +7,7 @@ ob_start();
?> ?>
<div id="settings-tabs"> <div id="settings-tabs">
<ul class="resp-tabs-list settings-tabs"> <ul class="resp-tabs-list settings-tabs">
<li><i class="fa fa-file"></i>Treść</li> <li><i class="fa fa-file"></i>Treść</li>
<li><i class="fa fa-wrench"></i>Ustawienia</li> <li><i class="fa fa-wrench"></i>Ustawienia</li>
<li><i class="fa fa-globe"></i>SEO</li> <li><i class="fa fa-globe"></i>SEO</li>
</ul> </ul>
@@ -17,7 +17,7 @@ ob_start();
<ul class="resp-tabs-list languages-main htabs"> <ul class="resp-tabs-list languages-main htabs">
<? if ( is_array( $this -> languages ) ): foreach ( $this -> languages as $lg ):?> <? if ( is_array( $this -> languages ) ): foreach ( $this -> languages as $lg ):?>
<? if ( $lg['status'] ):?> <? if ( $lg['status'] ):?>
<li><? if ( $lg['id'] == $this->dlang ) echo '<i class="fa fa-star fa-lg text-system" title="JÄ™zyk domyĹ›lny"></i> ';?><?= $lg['name'];?></a></li> <li><? if ( $lg['id'] == $this->dlang ) echo '<i class="fa fa-star fa-lg text-system" title="Język domyślny"></i> ';?><?= $lg['name'];?></a></li>
<? endif;?> <? endif;?>
<? endforeach; endif;?> <? endforeach; endif;?>
</ul> </ul>
@@ -45,7 +45,7 @@ ob_start();
);?> );?>
<?= \Shared\Html\Html::textarea( <?= \Shared\Html\Html::textarea(
array( array(
'label' => 'Opis kategorii (rozwiniÄ™cie)', 'label' => 'Opis kategorii (rozwinięcie)',
'name' => 'text_hidden[' . $lg['id'] . ']', 'name' => 'text_hidden[' . $lg['id'] . ']',
'id' => 'text_hidden_' . $lg['id'], 'id' => 'text_hidden_' . $lg['id'],
'value' => $this -> category['languages'][ $lg['id'] ]['text_hidden'], 'value' => $this -> category['languages'][ $lg['id'] ]['text_hidden'],
@@ -84,7 +84,7 @@ ob_start();
);?> );?>
<?= \Shared\Html\Html::select( <?= \Shared\Html\Html::select(
[ [
'label' => 'Sortowanie produktĂłw', 'label' => 'Sortowanie produktów',
'name' => 'sort_type', 'name' => 'sort_type',
'id' => 'sort_type', 'id' => 'sort_type',
'values' => is_array( $this -> sort_types ) ? $this -> sort_types : [], 'values' => is_array( $this -> sort_types ) ? $this -> sort_types : [],
@@ -93,7 +93,7 @@ ob_start();
);?> );?>
<?= \Shared\Html\Html::input_switch( <?= \Shared\Html\Html::input_switch(
array( array(
'label' => 'WyĹ›wietlić podkategorie', 'label' => 'Wyświetlić podkategorie',
'name' => 'view_subcategories', 'name' => 'view_subcategories',
'checked' => $this -> category['view_subcategories'] == 1 ? true : false 'checked' => $this -> category['view_subcategories'] == 1 ? true : false
) )
@@ -104,7 +104,7 @@ ob_start();
<ul class="resp-tabs-list languages-seo htabs"> <ul class="resp-tabs-list languages-seo htabs">
<? if ( is_array( $this -> languages ) ): foreach ( $this -> languages as $lg ):?> <? if ( is_array( $this -> languages ) ): foreach ( $this -> languages as $lg ):?>
<? if ( $lg['status'] ):?> <? if ( $lg['status'] ):?>
<li><? if ( $lg['id'] == $this->dlang ) echo '<i class="fa fa-star fa-lg text-system" title="JÄ™zyk domyĹ›lny"></i> ';?><?= $lg['name'];?></a></li> <li><? if ( $lg['id'] == $this->dlang ) echo '<i class="fa fa-star fa-lg text-system" title="Język domyślny"></i> ';?><?= $lg['name'];?></a></li>
<? endif;?> <? endif;?>
<? endforeach; endif;?> <? endforeach; endif;?>
</ul> </ul>
@@ -124,7 +124,7 @@ ob_start();
);?> );?>
<?= \Shared\Html\Html::input( <?= \Shared\Html\Html::input(
array( array(
'label' => 'TytuĹ‚ kategorii (h1)', 'label' => 'Tytuł kategorii (h1)',
'name' => 'category_title[' . $lg['id'] . ']', 'name' => 'category_title[' . $lg['id'] . ']',
'id' => 'category_title_' . $lg['id'], 'id' => 'category_title_' . $lg['id'],
'value' => $this -> category['languages' ][ $lg['id'] ]['category_title'] 'value' => $this -> category['languages' ][ $lg['id'] ]['category_title']
@@ -156,7 +156,7 @@ ob_start();
);?> );?>
<?= \Shared\Html\Html::select( <?= \Shared\Html\Html::select(
array( array(
'label' => 'Blokuj indeksacjÄ™', 'label' => 'Blokuj indeksację',
'name' => 'noindex[' . $lg['id'] . ']', 'name' => 'noindex[' . $lg['id'] . ']',
'id' => 'noindex_' . $lg['id'], 'id' => 'noindex_' . $lg['id'],
'values' => array( 'values' => array(

View File

@@ -52,15 +52,6 @@
</div> </div>
<? endif; ?> <? endif; ?>
<div class="panel">
<div class="panel-heading">
<span class="panel-title">Changelog</span>
</div>
<div class="panel-body">
<?= @file_get_contents( 'https://shoppro.project-dc.pl/updates/changelog.php?ver=' . $this->ver ); ?>
</div>
</div>
<script type="text/javascript"> <script type="text/javascript">
$(function() { $(function() {
var version_current = <?= $this->ver; ?>; var version_current = <?= $this->ver; ?>;
@@ -138,3 +129,17 @@ $(function() {
} }
}); });
</script> </script>
<div class="panel">
<div class="panel-heading">
<span class="panel-title">Changelog</span>
</div>
<div class="panel-body">
<?php
$changelog = (string) @file_get_contents( 'https://shoppro.project-dc.pl/updates/changelog.php?ver=' . $this->ver );
$changelog = preg_replace( '/<!--.*?-->/s', '', $changelog );
$changelog = strip_tags( $changelog, '<p><br><b><strong><i><em><u><ul><ol><li><h1><h2><h3><h4><h5><h6><span><div><a><pre><code><hr>' );
echo $changelog;
?>
</div>
</div>

View File

@@ -2369,9 +2369,9 @@ class ProductRepository
$itemNode->appendChild( $doc->createElement( 'g:description', html_entity_decode( strip_tags( $desc ) ) ) ); $itemNode->appendChild( $doc->createElement( 'g:description', html_entity_decode( strip_tags( $desc ) ) ) );
if ( $product['language']['seo_link'] ) { if ( $product['language']['seo_link'] ) {
$link = $domainPrefix . '://' . $url . '/' . \Shared\Helpers\Helpers::seo( $product['language']['seo_link'] ) . '/' . str_replace( '|', '/', $combination['permutation_hash'] ); $link = $domainPrefix . '://' . $url . '/' . \Shared\Helpers\Helpers::seo( $product['language']['seo_link'] ) . '/' . str_replace( '|', '_', $combination['permutation_hash'] );
} else { } else {
$link = $domainPrefix . '://' . $url . '/p-' . $product['id'] . '-' . \Shared\Helpers\Helpers::seo( $product['language']['name'] ) . '/' . str_replace( '|', '/', $combination['permutation_hash'] ); $link = $domainPrefix . '://' . $url . '/p-' . $product['id'] . '-' . \Shared\Helpers\Helpers::seo( $product['language']['name'] ) . '/' . str_replace( '|', '_', $combination['permutation_hash'] );
} }
$itemNode->appendChild( $doc->createElement( 'link', $link ) ); $itemNode->appendChild( $doc->createElement( 'link', $link ) );

View File

@@ -691,12 +691,12 @@ class Helpers
if ( $row2['seo_link'] ) if ( $row2['seo_link'] )
{ {
$mdb->insert( 'pp_routes', [ 'product_id' => $row2['product_id'], 'lang_id' => $row['id'], 'pattern' => '^' . $language_link . self::seo( $row2['seo_link'] ) . '$', 'destination' => 'index.php?product=' . $row2['product_id'] ] ); $mdb->insert( 'pp_routes', [ 'product_id' => $row2['product_id'], 'lang_id' => $row['id'], 'pattern' => '^' . $language_link . self::seo( $row2['seo_link'] ) . '$', 'destination' => 'index.php?product=' . $row2['product_id'] ] );
$mdb->insert( 'pp_routes', [ 'product_id' => $row2['product_id'], 'lang_id' => $row['id'], 'pattern' => '^' . $language_link . self::seo( $row2['seo_link'] ) . '/([0-9-]+)$', 'destination' => 'index.php?product=' . $row2['product_id'] . '&permutation_hash=$1' ] ); $mdb->insert( 'pp_routes', [ 'product_id' => $row2['product_id'], 'lang_id' => $row['id'], 'pattern' => '^' . $language_link . self::seo( $row2['seo_link'] ) . '/([0-9_-]+)$', 'destination' => 'index.php?product=' . $row2['product_id'] . '&permutation_hash=$1' ] );
} }
else else
{ {
$mdb->insert( 'pp_routes', [ 'product_id' => $row2['product_id'], 'lang_id' => $row['id'], 'pattern' => '^' . $language_link . 'p-' . $row2['product_id'] . '-' . self::seo( $row2['name'] ) . '$', 'destination' => 'index.php?product=' . $row2['product_id'] ] ); $mdb->insert( 'pp_routes', [ 'product_id' => $row2['product_id'], 'lang_id' => $row['id'], 'pattern' => '^' . $language_link . 'p-' . $row2['product_id'] . '-' . self::seo( $row2['name'] ) . '$', 'destination' => 'index.php?product=' . $row2['product_id'] ] );
$mdb->insert( 'pp_routes', [ 'product_id' => $row2['product_id'], 'lang_id' => $row['id'], 'pattern' => '^' . $language_link . 'p-' . $row2['product_id'] . '-' . self::seo( $row2['name'] ) . '/([0-9-]+)$', 'destination' => 'index.php?product=' . $row2['product_id'] . '&permutation_hash=$1' ] ); $mdb->insert( 'pp_routes', [ 'product_id' => $row2['product_id'], 'lang_id' => $row['id'], 'pattern' => '^' . $language_link . 'p-' . $row2['product_id'] . '-' . self::seo( $row2['name'] ) . '/([0-9_-]+)$', 'destination' => 'index.php?product=' . $row2['product_id'] . '&permutation_hash=$1' ] );
} }
} }
} }

View File

@@ -153,15 +153,9 @@ class LayoutEngine
{ {
$category = $categoryRepo->frontCategoryDetails( (int)\Shared\Helpers\Helpers::get( 'category' ), $lang_id ); $category = $categoryRepo->frontCategoryDetails( (int)\Shared\Helpers\Helpers::get( 'category' ), $lang_id );
if ( $category['language']['meta_title'] ) $page = self::applyEntityMetaToPage( $page, isset( $category['language'] ) ? $category['language'] : null, isset( $category['language']['title'] ) ? $category['language']['title'] : '' );
$page['language']['title'] = $category['language']['meta_title'];
else
$page['language']['title'] = $category['language']['title'];
$page['show_title'] = true; $page['show_title'] = true;
$page['language']['meta_keywords'] = $category['language']['meta_keywords']; $page['language']['page_title'] = !empty( $category['language']['category_title'] ) ? $category['language']['category_title'] : ( isset( $category['language']['title'] ) ? $category['language']['title'] : '' );
$page['language']['meta_description'] = $category['language']['meta_description'];
$page['language']['page_title'] = $category['language']['category_title'] ? $category['language']['category_title'] : $category['language']['title'];
// CANONICAL // CANONICAL
$html = str_replace( '[CANONICAL]', '', $html ); $html = str_replace( '[CANONICAL]', '', $html );
@@ -175,14 +169,8 @@ class LayoutEngine
{ {
$article = $articleRepo->articleDetailsFrontend( (int)\Shared\Helpers\Helpers::get( 'article' ), $lang_id ); $article = $articleRepo->articleDetailsFrontend( (int)\Shared\Helpers\Helpers::get( 'article' ), $lang_id );
if ( $article['language']['meta_title'] ) $page = self::applyEntityMetaToPage( $page, isset( $article['language'] ) ? $article['language'] : null, isset( $article['language']['title'] ) ? $article['language']['title'] : '' );
$page['language']['title'] = $article['language']['meta_title'];
else
$page['language']['title'] = $article['language']['title'];
$page['show_title'] = false; $page['show_title'] = false;
$page['language']['meta_keywords'] = $article['language']['meta_keywords'];
$page['language']['meta_description'] = $article['language']['meta_description'];
// CANONICAL // CANONICAL
$html = str_replace( '[CANONICAL]', '', $html ); $html = str_replace( '[CANONICAL]', '', $html );
@@ -193,16 +181,11 @@ class LayoutEngine
// //
if ( \Shared\Helpers\Helpers::get( 'product' ) ) if ( \Shared\Helpers\Helpers::get( 'product' ) )
{ {
$product = ( new \Domain\Product\ProductRepository( $GLOBALS['mdb'] ) )->findCached( \Shared\Helpers\Helpers::get( 'product' ), $lang_id, $_GET['permutation_hash'] ?? null ); $permutation_hash = isset( $_GET['permutation_hash'] ) ? str_replace( '_', '|', $_GET['permutation_hash'] ) : null;
$product = ( new \Domain\Product\ProductRepository( $GLOBALS['mdb'] ) )->findCached( \Shared\Helpers\Helpers::get( 'product' ), $lang_id, $permutation_hash );
if ( $product['language']['meta_title'] )
$page['language']['title'] = $product['language']['meta_title'];
else
$page['language']['title'] = $product['language']['name'];
$page = self::applyEntityMetaToPage( $page, isset( $product['language'] ) ? $product['language'] : null, isset( $product['language']['name'] ) ? $product['language']['name'] : '' );
$page['show_title'] = false; $page['show_title'] = false;
$page['language']['meta_keywords'] = $product['language']['meta_keywords'];
$page['language']['meta_description'] = $product['language']['meta_description'];
// CANONICAL // CANONICAL
if ( $product['language']['canonical'] ) if ( $product['language']['canonical'] )
@@ -439,6 +422,35 @@ class LayoutEngine
] ); ] );
} }
/**
* Przepisuje meta encji (kategoria/artykuł/produkt) do $page['language'].
*
* Dlaczego: domyślne $page jest stroną główną CMS. Jeśli nie nadpiszemy
* meta_title encji (nawet pustym), meta_title homepage wycieka do <title>
* na podstronie kategorii/produktu (linia podstawienia [TITLE]).
*
* @param array $page obecne $page (z homepage lub session)
* @param array|null $entityLanguage wiersz *_langs encji (może być null)
* @param string $fallbackTitle nazwa encji używana jako $page.language.title
* @return array zmodyfikowany $page
*/
public static function applyEntityMetaToPage( $page, $entityLanguage, $fallbackTitle )
{
if ( !is_array( $page ) ) {
$page = [];
}
if ( !isset( $page['language'] ) or !is_array( $page['language'] ) ) {
$page['language'] = [];
}
$page['language']['title'] = $fallbackTitle;
$page['language']['meta_title'] = is_array( $entityLanguage ) && isset( $entityLanguage['meta_title'] ) ? $entityLanguage['meta_title'] : null;
$page['language']['meta_keywords'] = is_array( $entityLanguage ) && isset( $entityLanguage['meta_keywords'] ) ? $entityLanguage['meta_keywords'] : null;
$page['language']['meta_description'] = is_array( $entityLanguage ) && isset( $entityLanguage['meta_description'] ) ? $entityLanguage['meta_description'] : null;
return $page;
}
public static function alert() public static function alert()
{ {
if ( $alert = \Shared\Helpers\Helpers::get_session( 'alert' ) ) if ( $alert = \Shared\Helpers\Helpers::get_session( 'alert' ) )

56
change.md Normal file
View File

@@ -0,0 +1,56 @@
# Zmiana 3: Naprawa JS na stronie /admin/update/main_view/ (przyciski aktualizacji + Wyczyść cache)
## Plik
`admin/templates/update/main-view.php`
## Problem
Na podstronie `/admin/update/main_view/` nie działały:
- przycisk **„Aktualizuj do wyższej wersji"** (`#confirm`)
- przycisk **„Aktualizuj do najwyższej wersji"** (`#confirmUpdateAll`)
- globalny przycisk **„Wyczyść cache"** (`#clear-cache-btn`) z headera admina
W konsoli przeglądarki **nie było żadnego błędu JS**. Kliknięcie powodowało jedynie dopisanie `#` do URL-a (`/admin/update/main_view/#`).
## Przyczyna
W szablonie znajdowało się pobieranie zdalnego changeloga przez `file_get_contents` z `https://shoppro.project-dc.pl/updates/changelog.php`:
```php
<?= @file_get_contents( 'https://shoppro.project-dc.pl/updates/changelog.php?ver=' . $this->ver ); ?>
```
Odpowiedź zdalnego serwera zawierała niezbalansowany HTML (np. niezamknięty `<script>`, `<!--`, `<style>` albo `<textarea>`), który „połykał" całą resztę dokumentu jako tekst wewnątrz tego nieparzystego tagu. Skutki:
- Inline-script z handlerami przycisków aktualizacji (zaraz pod changelogiem) nie był parsowany jako JS.
- Footer-script z `main-layout.php` (handler `#clear-cache-btn` i globalne wyszukiwanie) też był „w środku" połkniętego HTML i nigdy się nie uruchamiał na tej podstronie.
- Konsola nie pokazywała błędu, bo to nie był błąd JS — to był po prostu HTML, który nigdy nie został wykonany jako skrypt.
## Co zmieniono
### 1. Blok `<script>` z handlerami przeniesiony przed sekcję Changelog
Wcześniej kolejność w szablonie była: panel z wersjami → log → **changelog (zdalny)**`<script>`. Po zmianie: panel z wersjami → log → **`<script>`** → changelog (zdalny). Dzięki temu handlery `#confirm` i `#confirmUpdateAll` podpinają się niezależnie od tego, co zwróci zdalny serwer.
### 2. Sanitizacja odpowiedzi zdalnego changeloga
Przed wyrenderowaniem changelog jest czyszczony:
```php
$changelog = (string) @file_get_contents( 'https://shoppro.project-dc.pl/updates/changelog.php?ver=' . $this->ver );
$changelog = preg_replace( '/<!--.*?-->/s', '', $changelog );
$changelog = strip_tags( $changelog, '<p><br><b><strong><i><em><u><ul><ol><li><h1><h2><h3><h4><h5><h6><span><div><a><pre><code><hr>' );
echo $changelog;
```
- Usuwane są komentarze HTML (`<!--…-->`).
- `strip_tags` z wąską białą listą zostawia tylko tagi formatujące — wszystkie tagi strukturalne i mogące „połknąć" treść (`<script>`, `<style>`, `<textarea>`, `<iframe>`, `<body>`, `</html>` itp.) są usuwane.
### 3. Naprawiony też skutek uboczny
Po tych zmianach poprawnie działa również:
- footer-script z `main-layout.php` (przycisk **Wyczyść cache** + globalne wyszukiwanie produktów/zamówień + sprawdzanie aktualizacji w menu)
## Efekt
- Przyciski aktualizacji systemu na `/admin/update/main_view/` działają.
- Przycisk „Wyczyść cache" działa na tej (i każdej innej) podstronie admina.
- Zdalny changelog ze `shoppro.project-dc.pl` nadal się wyświetla, ale nie może już popsuć struktury strony.
- Dodatkowy zysk bezpieczeństwa: zdalny serwis nie może wstrzyknąć JS w panel admina.
## Po wgraniu
1. Hard-reload przeglądarki (Ctrl+F5) — stary HTML siedzi w cache klienta.
2. Restart PHP-FPM / wyczyszczenie OPcache — inaczej stary skompilowany szablon dalej będzie serwowany.

View File

@@ -2,17 +2,37 @@
global $lang_id; global $lang_id;
$attributeRepo = new \Domain\Attribute\AttributeRepository( $GLOBALS['mdb'] ); $attributeRepo = new \Domain\Attribute\AttributeRepository( $GLOBALS['mdb'] );
$attribute_details = $attributeRepo->frontAttributeDetails( (int)$this -> attribute['id'], $lang_id ); $attribute_details = $attributeRepo->frontAttributeDetails( (int)$this -> attribute['id'], $lang_id );
$forced_value_id = null;
if ( isset( $_GET['permutation_hash'] ) && $_GET['permutation_hash'] !== '' )
{
$pairs = explode( '|', str_replace( '_', '|', $_GET['permutation_hash'] ) );
foreach ( $pairs as $pair )
{
$parts = explode( '-', $pair );
if ( count( $parts ) == 2 && (int)$parts[0] === (int)$this -> attribute['id'] )
{
$forced_value_id = (int)$parts[1];
break;
}
}
}
if ( $attribute_details['type'] == 0 ) if ( $attribute_details['type'] == 0 )
{ {
?> ?>
<div class="attribute-container fradio-group attribute-<?= \Shared\Helpers\Helpers::seo( $attribute_details['language']['name'] );?>" level="<?= $this -> level;?>" order="<?= $this -> order;?>" attribute="<?= \Shared\Helpers\Helpers::seo( $attribute_details['language']['name'] );?>" attribute-name="<?= $attribute_details['language']['name'];?>"> <div class="attribute-container fradio-group attribute-<?= \Shared\Helpers\Helpers::seo( $attribute_details['language']['name'] );?>" level="<?= $this -> level;?>" order="<?= $this -> order;?>" attribute="<?= \Shared\Helpers\Helpers::seo( $attribute_details['language']['name'] );?>" attribute-name="<?= $attribute_details['language']['name'];?>">
<p class="attribute-label"><?= $attribute_details['language']['name'];?>:</p> <p class="attribute-label"><?= $attribute_details['language']['name'];?>:</p>
<? foreach ( $this -> attribute['values'] as $value ):?> <? foreach ( $this -> attribute['values'] as $value ):
$is_active = $forced_value_id !== null
? ( (int)$value['id'] === $forced_value_id )
: (bool)$value['is_default'];
?>
<div class="fradio"> <div class="fradio">
<input type="radio" id="<?= $this -> attribute['id'];?>-<?= $value['id'];?>" <? if ( $value['is_default'] ):?>checked="checked"<? endif;?> require="true" value="<?= $this -> attribute['id'];?>-<?= $value['id'];?>" name="<?= \Shared\Helpers\Helpers::seo( $attribute_details['language']['name'] );?>"> <input type="radio" id="<?= $this -> attribute['id'];?>-<?= $value['id'];?>" <? if ( $is_active ):?>checked="checked"<? endif;?> require="true" value="<?= $this -> attribute['id'];?>-<?= $value['id'];?>" name="<?= \Shared\Helpers\Helpers::seo( $attribute_details['language']['name'] );?>">
<label for="<?= $this -> attribute['id'];?>-<?= $value['id'];?>" order="<?= $this -> order;?>"><?= ( new \Domain\Attribute\AttributeRepository( $GLOBALS['mdb'] ) )->getAttributeValueById( $value['id'], $lang_id );?></label> <label for="<?= $this -> attribute['id'];?>-<?= $value['id'];?>" order="<?= $this -> order;?>"><?= ( new \Domain\Attribute\AttributeRepository( $GLOBALS['mdb'] ) )->getAttributeValueById( $value['id'], $lang_id );?></label>
</div> </div>
<? if ( $value['is_default'] ):?> <? if ( $is_active ):?>
<script class="footer" type="text/javascript"> <script class="footer" type="text/javascript">
$( function() $( function()
{ {

View File

@@ -0,0 +1,151 @@
<?php
namespace Tests\Unit\Domain\Product;
use PHPUnit\Framework\TestCase;
use Domain\Product\ProductRepository;
/**
* Phase 18 — testy generatora linku do feedu Google.
*
* ProductRepository::appendCombinationToXml buduje <link> dla pozycji
* feedu Google. permutation_hash w bazie ma format "attr-val|attr-val".
* W URL feedu separator między parami to "_" (nie "/"), żeby URL był
* jednym segmentem dopasowywalnym przez routing pp_routes.
*
* Test wywołuje prywatną metodę przez ReflectionMethod z minimalnymi
* danymi produktu i sprawdza zawartość wynikowego DOMDocument.
*/
class ProductFeedLinkTest extends TestCase
{
private function buildRepoWithMocks(): ProductRepository
{
$mockDb = $this->createMock(\medoo::class);
$mockDb->method('select')->willReturn([]);
$mockDb->method('get')->willReturn(null);
$repo = new ProductRepository($mockDb);
// appendShippingToXml wywołuje $this->transportRepoForXml->lowestTransportPrice().
// Inicjalizacja w generateGoogleXmlFeed(); dla unit testu wstrzykujemy mock dynamicznie.
$transportMock = $this->getMockBuilder(\Domain\Transport\TransportRepository::class)
->disableOriginalConstructor()
->getMock();
$transportMock->method('lowestTransportPrice')->willReturn(0.0);
$repo->transportRepoForXml = $transportMock;
return $repo;
}
private function invokeAppendCombination(ProductRepository $repo, array $product, array $combination): string
{
$doc = new \DOMDocument('1.0', 'UTF-8');
$channelNode = $doc->appendChild($doc->createElement('channel'));
$method = new \ReflectionMethod(ProductRepository::class, 'appendCombinationToXml');
$method->setAccessible(true);
$method->invoke($repo, $doc, $channelNode, $product, $combination, 'https', 'shop.example.com');
return $doc->saveXML();
}
private function baseProduct(array $overrides = []): array
{
return array_merge([
'id' => 123,
'ean' => '5901234567890',
'language' => [
'name' => 'Produkt testowy',
'xml_name' => '',
'short_description' => 'Opis',
'meta_title' => '',
'seo_link' => 'sukienka-czerwona',
],
'price_brutto' => 100,
'price_brutto_promo' => 0,
'quantity' => 10,
'stock_0_buy' => 0,
'wp' => 1,
'images' => [],
], $overrides);
}
public function testCombinationLinkUsesUnderscoreInSeoLinkBranch()
{
$repo = $this->buildRepoWithMocks();
$product = $this->baseProduct();
$combination = [
'id' => 555,
'permutation_hash' => '20-170|21-175',
'price_brutto' => 120,
'price_brutto_promo' => 0,
'quantity' => 5,
'stock_0_buy' => 0,
];
$xml = $this->invokeAppendCombination($repo, $product, $combination);
$this->assertStringContainsString(
'<link>https://shop.example.com/sukienka-czerwona/20-170_21-175</link>',
$xml,
'Link feedu z seo_link musi używać "_" jako separatora par attr-val'
);
$this->assertStringNotContainsString(
'20-170/21-175',
$xml,
'Link feedu nie może zawierać starego separatora "/" między parami atrybutów'
);
}
public function testCombinationLinkUsesUnderscoreInFallbackBranch()
{
$repo = $this->buildRepoWithMocks();
$product = $this->baseProduct([
'language' => [
'name' => 'Sukienka czerwona',
'xml_name' => '',
'short_description' => 'Opis',
'meta_title' => '',
'seo_link' => '',
],
]);
$combination = [
'id' => 555,
'permutation_hash' => '20-170|21-175',
'price_brutto' => 120,
'price_brutto_promo' => 0,
'quantity' => 5,
'stock_0_buy' => 0,
];
$xml = $this->invokeAppendCombination($repo, $product, $combination);
// Fallback uses "p-{id}-{seo(name)}/...". Helpers::seo stub returns input unchanged.
$this->assertStringContainsString(
'<link>https://shop.example.com/p-123-Sukienka czerwona/20-170_21-175</link>',
$xml,
'Link fallback (bez seo_link) musi używać "_" jako separatora par attr-val'
);
}
public function testCombinationLinkWithSinglePair()
{
$repo = $this->buildRepoWithMocks();
$product = $this->baseProduct();
$combination = [
'id' => 555,
'permutation_hash' => '20-170',
'price_brutto' => 120,
'price_brutto_promo' => 0,
'quantity' => 5,
'stock_0_buy' => 0,
];
$xml = $this->invokeAppendCombination($repo, $product, $combination);
$this->assertStringContainsString(
'<link>https://shop.example.com/sukienka-czerwona/20-170</link>',
$xml,
'Pojedyncza para attr-val pozostaje bez zmian (str_replace nie ma co podmieniać)'
);
}
}

View File

@@ -0,0 +1,83 @@
<?php
namespace Tests\Unit\Shared\Helpers;
use PHPUnit\Framework\TestCase;
/**
* Phase 18 — testy regex routingu pp_routes dla URL produktów z permutacją.
*
* Helpers::htacces() generuje pattern dla każdego produktu z permutacją.
* Pattern używa klasy znakowej [0-9_-]+, żeby dopasować segment "20-170_21-175"
* w jednym kawałku (separator pomiędzy parami atrybutów to "_", nie "/").
*
* Testy nie wywołują htacces() (zbyt duże zależności), tylko weryfikują:
* 1. Wzorzec literałem [0-9_-]+ występuje w generatorze pp_routes (file content)
* 2. Wzorzec przyjmuje URL z "_" i odrzuca wariant ze "/"
*/
class HelpersRoutingTest extends TestCase
{
private $helpersSource;
protected function setUp(): void
{
parent::setUp();
$this->helpersSource = file_get_contents(
__DIR__ . '/../../../../autoload/Shared/Helpers/Helpers.php'
);
}
public function testHelpersGeneratorUsesPermutationCharClassWithUnderscore()
{
// Liczba miejsc, gdzie pattern produktu z permutacją używa nowej klasy znaków.
$newPattern = substr_count($this->helpersSource, '/([0-9_-]+)$');
$this->assertGreaterThanOrEqual(
2,
$newPattern,
'Helpers.php musi zawierać dwa wystąpienia /([0-9_-]+)$ (gałąź seo_link i fallback p-id-name)'
);
// Stary wzorzec [0-9-]+ nie powinien już występować jako finalny segment URL.
$this->assertStringNotContainsString(
'/([0-9-]+)$',
$this->helpersSource,
'Stary wzorzec /([0-9-]+)$ został zastąpiony przez /([0-9_-]+)$ — nie powinno go już być w generatorze pp_routes'
);
}
public function testRegexMatchesUrlWithUnderscoreSeparator()
{
$pattern = '#^slug-produktu/([0-9_-]+)$#';
$matches = [];
$this->assertSame(
1,
preg_match($pattern, 'slug-produktu/20-170_21-175', $matches),
'Nowy wzorzec musi dopasować URL z "_" jako separatorem par atrybutów'
);
$this->assertSame('20-170_21-175', $matches[1]);
}
public function testRegexRejectsLegacyUrlWithSlashSeparator()
{
$pattern = '#^slug-produktu/([0-9_-]+)$#';
$this->assertSame(
0,
preg_match($pattern, 'slug-produktu/20-170/21-175'),
'Wzorzec NIE powinien dopasować starego URL ze "/" — taki URL ma trafiać do innego routingu lub 404'
);
}
public function testRegexMatchesSinglePairUrl()
{
$pattern = '#^slug-produktu/([0-9_-]+)$#';
$matches = [];
$this->assertSame(
1,
preg_match($pattern, 'slug-produktu/20-170', $matches),
'Wzorzec dopasowuje też URL z jedną parą attr-val'
);
$this->assertSame('20-170', $matches[1]);
}
}

View File

@@ -0,0 +1,110 @@
<?php
namespace Tests\Unit\Front;
use PHPUnit\Framework\TestCase;
require_once __DIR__ . '/../../../autoload/front/LayoutEngine.php';
/**
* Phase 19 — fix metatagów na stronach kategorii/produktu/artykułu.
*
* Bug: domyślne $page jest stroną główną. W gałęziach category/article/product
* w LayoutEngine::show() nadpisywany był tylko title, a meta_title homepage
* żył dalej w $page['language']['meta_title'] i wygrywał w substytucji [TITLE].
*
* Fix: applyEntityMetaToPage() zawsze nadpisuje meta_title/meta_keywords/
* meta_description encji (nawet pustymi/null), żeby nic z homepage nie wyciekało.
*/
class LayoutEngineMetaTagsTest extends TestCase
{
public function testHomepageMetaTitleDoesNotLeakWhenEntityHasOwnMetaTitle()
{
$page = $this->homepagePage();
$category = [
'meta_title' => 'Sen i otulenie — kocyki minky',
'meta_keywords' => 'kocyki, otulacze',
'meta_description' => 'Najwyższej jakości kocyki minky',
'title' => 'Sen i otulenie',
];
$result = \front\LayoutEngine::applyEntityMetaToPage($page, $category, $category['title']);
$this->assertSame('Sen i otulenie — kocyki minky', $result['language']['meta_title']);
$this->assertSame('Sen i otulenie', $result['language']['title']);
$this->assertSame('kocyki, otulacze', $result['language']['meta_keywords']);
$this->assertSame('Najwyższej jakości kocyki minky', $result['language']['meta_description']);
}
public function testHomepageMetaTitleIsClearedWhenEntityHasNoMetaTitle()
{
$page = $this->homepagePage();
$category = [
'meta_title' => null,
'meta_keywords' => 'description',
'meta_description' => 'keywords',
'title' => 'Sen i otulenie',
];
$result = \front\LayoutEngine::applyEntityMetaToPage($page, $category, $category['title']);
$this->assertNull(
$result['language']['meta_title'],
'meta_title homepage nie może wyciekać gdy kategoria nie ma własnego'
);
$this->assertSame('Sen i otulenie', $result['language']['title']);
}
public function testProductWithAllMetaNullClearsHomepageValues()
{
$page = $this->homepagePage();
$productLang = [
'meta_title' => null,
'meta_keywords' => null,
'meta_description' => null,
'name' => 'Kocyk niemowlaka - Szczeniak z balonikiem - Fuksja',
];
$result = \front\LayoutEngine::applyEntityMetaToPage($page, $productLang, $productLang['name']);
$this->assertNull($result['language']['meta_title']);
$this->assertNull($result['language']['meta_keywords']);
$this->assertNull($result['language']['meta_description']);
$this->assertSame($productLang['name'], $result['language']['title']);
}
public function testNullEntityLanguageDoesNotCrashAndClearsMeta()
{
$page = $this->homepagePage();
$result = \front\LayoutEngine::applyEntityMetaToPage($page, null, 'Fallback');
$this->assertSame('Fallback', $result['language']['title']);
$this->assertNull($result['language']['meta_title']);
$this->assertNull($result['language']['meta_keywords']);
$this->assertNull($result['language']['meta_description']);
}
public function testEmptyPageInputCreatesLanguageStructure()
{
$result = \front\LayoutEngine::applyEntityMetaToPage([], ['meta_title' => 'X'], 'T');
$this->assertIsArray($result);
$this->assertIsArray($result['language']);
$this->assertSame('X', $result['language']['meta_title']);
$this->assertSame('T', $result['language']['title']);
}
private function homepagePage()
{
return [
'id' => 6,
'language' => [
'title' => 'Home',
'meta_title' => 'Sklep z akcesoriami dla dzieci i niemowląt, kocyki minky, poduszki, ubranka',
'meta_keywords' => '',
'meta_description' => 'Marianek to sklep internetowy z artykułami dla dzieci...',
'page_title' => null,
],
];
}
}

BIN
updates/.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1,2 @@
ALTER TABLE pp_shop_payment_methods ADD COLUMN min_order_amount DECIMAL(10,2) DEFAULT NULL;
ALTER TABLE pp_shop_payment_methods ADD COLUMN max_order_amount DECIMAL(10,2) DEFAULT NULL;

View File

@@ -0,0 +1,5 @@
ALTER TABLE pp_log ADD COLUMN `action` VARCHAR(100) NULL DEFAULT NULL AFTER `id`;
ALTER TABLE pp_log ADD COLUMN `order_id` INT NULL DEFAULT NULL AFTER `action`;
ALTER TABLE pp_log ADD COLUMN `context` TEXT NULL DEFAULT NULL AFTER `message`;
ALTER TABLE pp_log ADD INDEX `idx_action` (`action`);
ALTER TABLE pp_log ADD INDEX `idx_order_id` (`order_id`);

View File

@@ -0,0 +1 @@
ALTER TABLE `pp_shop_products_custom_fields` ADD COLUMN `type` VARCHAR(30) NOT NULL DEFAULT '' AFTER `name`;

BIN
updates/0.30/ver_0.349.zip Normal file

Binary file not shown.

View File

@@ -0,0 +1,24 @@
{
"changelog": "Naprawa kosztu transportu na podsumowaniu koszyka (delivery_free z uwzglednieniem progu)",
"version": "0.349",
"files": {
"added": [
],
"deleted": [
],
"modified": [
"autoload/front/Controllers/ShopBasketController.php",
"templates/shop-basket/summary-view.php"
]
},
"checksum_zip": "sha256:acb6a6f4d1c80c5bb4d39c84b5d6d9335069de42febb177d9f581b1b8006aefd",
"sql": [
],
"date": "2026-04-20",
"directories_deleted": [
]
}

BIN
updates/0.30/ver_0.350.zip Normal file

Binary file not shown.

View File

@@ -0,0 +1,26 @@
{
"changelog": "FIX - linki produktow z permutacja w feedzie Google (separator _)",
"version": "0.350",
"files": {
"added": [
],
"deleted": [
],
"modified": [
"autoload/Domain/Product/ProductRepository.php",
"autoload/Shared/Helpers/Helpers.php",
"autoload/front/LayoutEngine.php",
"templates/shop-product/_partial/product-attribute.php"
]
},
"checksum_zip": "sha256:0cf918bb070260a8e055a1c8f0f3407532008a5a920da588a14c1f3b11e02c59",
"sql": [
],
"date": "2026-04-30",
"directories_deleted": [
]
}

BIN
updates/0.30/ver_0.351.zip Normal file

Binary file not shown.

View File

@@ -0,0 +1,23 @@
{
"changelog": "FIX - poprawne metatagi \u003ctitle\u003e/\u003cdescription\u003e/\u003ckeywords\u003e dla stron kategorii i produktow (eliminacja wycieku meta_title homepage)",
"version": "0.351",
"files": {
"added": [
],
"deleted": [
],
"modified": [
"autoload/front/LayoutEngine.php"
]
},
"checksum_zip": "sha256:046d0504accf29a57fea155ecaa2acfe09078379e43118af7bf6c0f4831a3e5f",
"sql": [
],
"date": "2026-05-13",
"directories_deleted": [
]
}

BIN
updates/0.30/ver_0.352.zip Normal file

Binary file not shown.

View File

@@ -0,0 +1,23 @@
{
"changelog": "FIX - poprawne polskie znaki w labelkach formularza edycji kategorii w panelu admina (mojibake)",
"version": "0.352",
"files": {
"added": [
],
"deleted": [
],
"modified": [
"admin/templates/shop-category/category-edit.php"
]
},
"checksum_zip": "sha256:4b51cb1508978f6dc9659ce26c143961eaed6c3d3bb27b47623eaea5b4fa4855",
"sql": [
],
"date": "2026-05-13",
"directories_deleted": [
]
}

BIN
updates/0.30/ver_0.353.zip Normal file

Binary file not shown.

View File

@@ -0,0 +1,23 @@
{
"changelog": "fix: dziala aktualizacja systemu i Wyczysc cache na /admin/update/main_view/ (sanityzacja zdalnego changeloga + reorder script)",
"version": "0.353",
"files": {
"added": [
],
"deleted": [
],
"modified": [
"admin/templates/update/main-view.php"
]
},
"checksum_zip": "sha256:86706999cd251c2c06e4b4d5875d049164ad510e60963bf8482f9bb8e6e924eb",
"sql": [
],
"date": "2026-05-13",
"directories_deleted": [
]
}

View File

@@ -1,3 +1,18 @@
<b>ver. 0.353 - 13.05.2026</b><br />
fix: dziala aktualizacja systemu i Wyczysc cache na /admin/update/main_view/ (sanityzacja zdalnego changeloga + reorder script)
<hr>
<b>ver. 0.352 - 13.05.2026</b><br />
FIX - poprawne polskie znaki w labelkach formularza edycji kategorii w panelu admina (mojibake)
<hr>
<b>ver. 0.351 - 13.05.2026</b><br />
FIX - poprawne metatagi <title>/<description>/<keywords> dla stron kategorii i produktow (eliminacja wycieku meta_title homepage)
<hr>
<b>ver. 0.350 - 30.04.2026</b><br />
FIX - linki produktow z permutacja w feedzie Google (separator _)
<hr>
<b>ver. 0.349 - 20.04.2026</b><br />
Naprawa kosztu transportu na podsumowaniu koszyka (delivery_free z uwzglednieniem progu)
<hr>
<b>ver. 0.348 - 19.04.2026</b><br /> <b>ver. 0.348 - 19.04.2026</b><br />
Dodano przełącznik i edycję etykiet niestandardowych na liście produktów. Dodano przełącznik i edycję etykiet niestandardowych na liście produktów.
<hr> <hr>

View File

@@ -1,5 +1,5 @@
<? <?
$current_ver = 348; $current_ver = 353;
for ($i = 1; $i <= $current_ver; $i++) for ($i = 1; $i <= $current_ver; $i++)
{ {