---
phase: 02-jawnosc-cen
plan: 01
type: execute
wave: 2
depends_on: ["01-01"]
files_modified:
  - wp-content/plugins/elementor-addon/elementor-addon.php
autonomous: false
---

<objective>
## Goal
Zbudować publiczny XML endpoint z danymi cenowymi apartamentów oraz XML katalog zgodny z XSD portalu dane.gov.pl — gotowy do automatycznego zasilania Ministerstwa.

## Purpose
Ustawa o jawności cen zobowiązuje deweloperów do raportowania cen mieszkań do portalu rządowego dane.gov.pl. Deweloper musi podać Ministerstwu URL pliku XML, który portal będzie cyklicznie pobierał. Budujemy te endpointy po stronie WordPress, korzystając z istniejącej tabeli `wp_price_history` i pól ACF.

## Output
- `/ceny-mieszkan.xml` — plik z cenami wszystkich lokali + historia z DB
- `/ceny-mieszkan.md5` — hash MD5 dla powyższego
- `/dane-gov-pl.xml` — katalog XSD-compliant dla dane.gov.pl wskazujący na plik cen
- `/dane-gov-pl.md5` — hash MD5 dla katalogu
- Strona administracyjna z URL-ami do zgłoszenia do Ministerstwa
</objective>

<context>
## Project Context
@.paul/PROJECT.md
@.paul/ROADMAP.md
@.paul/STATE.md

## Prior Work
@.paul/phases/01-historia-cen/01-01-SUMMARY.md

## Source Files
@wp-content/plugins/elementor-addon/elementor-addon.php
</context>

<acceptance_criteria>

## AC-1: Endpoint cen mieszkań dostępny
```gherkin
Given WordPress jest uruchomiony z przepłukanymi regułami permalink
When GET /ceny-mieszkan.xml
Then odpowiedź HTTP 200 z Content-Type: application/xml,
     XML zawiera element <lokale> z listą <lokal> dla każdego posta apartamenty,
     każdy <lokal> ma: id, nazwa, typ, pietro, powierzchnia, cena_brutto, cena_za_m2, historia_cen
```

## AC-2: MD5 companions poprawne
```gherkin
Given pliki XML są serwowane przez WordPress
When GET /ceny-mieszkan.md5 lub /dane-gov-pl.md5
Then odpowiedź HTTP 200 z 32-znakowym lowercase hex stringiem (MD5 odpowiadającego XML)
```

## AC-3: Katalog dane.gov.pl zgodny z XSD
```gherkin
Given endpoint /dane-gov-pl.xml działa
When GET /dane-gov-pl.xml
Then XML zawiera element <datasets> z <dataset> opisującym inwestycję,
     <resource> wskazuje na home_url('/ceny-mieszkan.xml'),
     updateFrequency to "daily", kategoria to "REGI"
```

## AC-4: Strona administracyjna z URL-ami
```gherkin
Given admin jest zalogowany
When odwiedzi Narzędzia → Jawność Cen (wp-admin/tools.php?page=jawnosc-cen)
Then widzi oba URL-e do zgłoszenia (ceny-mieszkan.xml i dane-gov-pl.xml),
     przyciski "Kopiuj URL" i "Otwórz XML",
     informację o harmonogramie (codziennie)
```

</acceptance_criteria>

<tasks>

<task type="auto">
  <name>Task 1: Rewrite rules + endpoint cen mieszkań (XML + MD5)</name>
  <files>wp-content/plugins/elementor-addon/elementor-addon.php</files>
  <action>
    Dodaj na końcu pliku (po istniejących hookach) następujące funkcje:

    **1. Rewrite rules (hook: init, priority 10):**
    ```
    add_rewrite_rule('^ceny-mieszkan\.(xml|md5)$', 'index.php?apartamenty_xml=$matches[1]', 'top');
    add_rewrite_rule('^dane-gov-pl\.(xml|md5)$',   'index.php?apartamenty_datagov=$matches[1]', 'top');
    ```
    Dodaj query vars: 'apartamenty_xml', 'apartamenty_datagov' przez filter 'query_vars'.

    **2. Generator XML cen (funkcja apartamenty_generate_price_xml()):**
    - Sprawdź transient 'apartamenty_price_xml_cache' (TTL 1h) — jeśli istnieje, zwróć go
    - WP_Query: post_type=apartamenty, posts_per_page=-1, orderby=title, order=ASC
    - Dla każdego postu:
      - Pobierz: post ID, post_title
      - get_post_meta flat: information_type, information_floor, information_floor_space, information_price, information_price_m2, information_status
      - Pobierz historię z wp_price_history: SELECT recorded_at, price, price_m2 WHERE post_id = %d ORDER BY recorded_at DESC
    - Buduj XML jako string (nie DOMDocument — nie ma pewności że rozszerzenie jest dostępne, użyj SimpleXMLElement lub czystego PHP string z esc_xml/htmlspecialchars):
    ```xml
    <?xml version="1.0" encoding="UTF-8"?>
    <lokale inwestycja="Wyszyńskiego 12" generowany="[date('c')]">
      <lokal id="[post_id]">
        <nazwa>[post_title]</nazwa>
        <typ>[information_type]</typ>
        <pietro>[information_floor]</pietro>
        <powierzchnia>[information_floor_space]</powierzchnia>
        <status>[information_status]</status>
        <cena_brutto>[information_price]</cena_brutto>
        <cena_za_m2>[information_price_m2]</cena_za_m2>
        <data_aktualizacji>[max recorded_at from history or today]</data_aktualizacji>
        <historia_cen>
          <zmiana data="[recorded_at]">
            <cena_brutto>[price]</cena_brutto>
            <cena_za_m2>[price_m2]</cena_za_m2>
          </zmiana>
          ...
        </historia_cen>
      </lokal>
    </lokale>
    ```
    - Zapisz do transientu i zwróć
    - Unika: nie używaj DOMDocument jeśli brak ext-dom, używaj SimpleXMLElement lub czystych stringów z htmlspecialchars(value, ENT_XML1, 'UTF-8')

    **3. Endpoint handler (hook: template_redirect, priority 1):**
    - get_query_var('apartamenty_xml') == 'xml' → header + echo apartamenty_generate_price_xml() + exit
    - get_query_var('apartamenty_xml') == 'md5' → header Content-Type text/plain + echo md5(apartamenty_generate_price_xml()) + exit

    **4. Inwalidacja cache w istniejącym cronie apartamenty_record_prices:**
    Dodaj na początku funkcji crona: delete_transient('apartamenty_price_xml_cache');

    Avoid: nie dodawaj flush_rewrite_rules() w runtime (tylko przy aktywacji); nie generuj XML przez DOMDocument bez sprawdzenia extension_loaded('dom').
  </action>
  <verify>
    1. Przejdź do Ustawienia → Bezpośrednie odnośniki → Zapisz (flush rewrite rules)
    2. Otwórz w przeglądarce: [site_url]/ceny-mieszkan.xml
    3. Sprawdź: Content-Type w nagłówkach = application/xml, XML jest poprawny (widoczna struktura w przeglądarce)
    4. Otwórz: [site_url]/ceny-mieszkan.md5
    5. Sprawdź: 32-znakowy lowercase hex string
  </verify>
  <done>AC-1 i AC-2 (część ceny) spełnione: endpoint XML cen dostępny, MD5 poprawny</done>
</task>

<task type="auto">
  <name>Task 2: Endpoint katalogu dane.gov.pl (XML + MD5)</name>
  <files>wp-content/plugins/elementor-addon/elementor-addon.php</files>
  <action>
    Dodaj funkcję apartamenty_generate_datagov_xml():

    Generuje XML zgodny z XSD https://www.dane.gov.pl/static/xml/otwarte_dane_latest.xsd:
    ```xml
    <?xml version="1.0" encoding="UTF-8"?>
    <datasets xmlns:xsi="urn:otwarte-dane:harvester:1.0-rc1">
      <dataset status="published">
        <extIdent>wyszynskiego12-ceny-mieszkan-v1</extIdent>
        <title>
          <polish>Ceny mieszkań – Wyszyńskiego 12</polish>
        </title>
        <description>
          <polish>Historia cen lokali mieszkalnych w inwestycji przy ul. Wyszyńskiego 12. Dane aktualizowane codziennie zgodnie z ustawą o jawności cen.</polish>
        </description>
        <updateFrequency>daily</updateFrequency>
        <categories>REGI</categories>
        <resources>
          <resource status="published">
            <extIdent>wyszynskiego12-ceny-xml-v1</extIdent>
            <url>[home_url('/ceny-mieszkan.xml')]</url>
            <title>
              <polish>Cennik lokali XML</polish>
            </title>
            <description>
              <polish>Plik XML z aktualnym cennikiem i historią zmian cen wszystkich lokali</polish>
            </description>
            <availability>remote</availability>
            <lastUpdateDate>[date('Y-m-d\T00:00:00.000\Z')]</lastUpdateDate>
          </resource>
        </resources>
        <tags lang="pl">
          <tag>mieszkania</tag>
          <tag>ceny</tag>
          <tag>deweloper</tag>
          <tag>jawność cen</tag>
          <tag>historia cen</tag>
        </tags>
      </dataset>
    </datasets>
    ```
    Użyj home_url() dla URL-a zasobu (nie hardcode).

    Uzupełnij template_redirect handler:
    - get_query_var('apartamenty_datagov') == 'xml' → header + echo apartamenty_generate_datagov_xml() + exit
    - get_query_var('apartamenty_datagov') == 'md5' → header text/plain + echo md5(apartamenty_generate_datagov_xml()) + exit

    Avoid: nie używaj hardcoded URL-a strony; nie pomijaj pola `categories` (wymagane przez XSD).
  </action>
  <verify>
    1. Otwórz: [site_url]/dane-gov-pl.xml
    2. Sprawdź: Content-Type application/xml, struktura XML poprawna, URL w <resource> wskazuje na /ceny-mieszkan.xml
    3. Otwórz: [site_url]/dane-gov-pl.md5
    4. Sprawdź: 32-znakowy lowercase hex string
  </verify>
  <done>AC-2 (komplet) i AC-3 spełnione: oba endpointy XML + oba MD5 działają</done>
</task>

<task type="auto">
  <name>Task 3: Strona administracyjna Jawność Cen</name>
  <files>wp-content/plugins/elementor-addon/elementor-addon.php</files>
  <action>
    Zarejestruj stronę w menu Narzędzia WP (hook: admin_menu):
    ```php
    add_management_page(
        'Jawność Cen',
        'Jawność Cen',
        'manage_options',
        'jawnosc-cen',
        'apartamenty_jawnosc_cen_page'
    );
    ```

    Funkcja apartamenty_jawnosc_cen_page():
    - Wyświetl dwa bloki (prostą tabelę HTML, bez CSS frameworków):
      - Blok 1: "Plik cen (dane)": URL = home_url('/ceny-mieszkan.xml'), [przycisk kopiuj JS], [link "Otwórz"]
      - Blok 2: "Katalog dane.gov.pl (zgłoś Ministerstwu)": URL = home_url('/dane-gov-pl.xml'), [przycisk kopiuj JS], [link "Otwórz"]
    - Informacja tekstowa: "Dane aktualizowane codziennie przez WP Cron. Zgłoś URL katalogu dane.gov.pl do administratora portalu: kontakt@dane.gov.pl"
    - Prosty `<script>` z funkcją copyToClipboard używającą navigator.clipboard.writeText
    - Użyj standardowych klas WP (.wrap, .notice) dla stylu

    Avoid: nie ładuj zewnętrznych skryptów/stylów; nie używaj nonce na tej stronie (tylko read-only display).
  </action>
  <verify>
    1. W wp-admin przejdź do Narzędzia → Jawność Cen
    2. Sprawdź: widoczne oba URL-e, przyciski kopiuj działają, linki "Otwórz" przekierowują poprawnie
  </verify>
  <done>AC-4 spełnione: strona administracyjna z URL-ami widoczna i funkcjonalna</done>
</task>

<task type="checkpoint:human-verify" gate="blocking">
  <what-built>
    Trzy endpointy publiczne i strona admina:
    - /ceny-mieszkan.xml — dane cen apartamentów (XML)
    - /ceny-mieszkan.md5 — hash MD5
    - /dane-gov-pl.xml — katalog dane.gov.pl
    - /dane-gov-pl.md5 — hash MD5
    - wp-admin: Narzędzia → Jawność Cen
  </what-built>
  <how-to-verify>
    1. W wp-admin: Ustawienia → Bezpośrednie odnośniki → kliknij "Zapisz" (flush rewrite rules)
    2. Otwórz [site_url]/ceny-mieszkan.xml — sprawdź że XML się wyświetla z apartamentami
    3. Otwórz [site_url]/ceny-mieszkan.md5 — sprawdź że widać 32-znakowy hex string
    4. Otwórz [site_url]/dane-gov-pl.xml — sprawdź że XML zawiera element <datasets> z URL do ceny-mieszkan.xml
    5. Otwórz [site_url]/dane-gov-pl.md5 — sprawdź 32-znakowy hex string
    6. Przejdź do Narzędzia → Jawność Cen — sprawdź że URL-e są widoczne
  </how-to-verify>
  <resume-signal>Wpisz "approved" żeby kontynuować, lub opisz problemy do naprawienia</resume-signal>
</task>

</tasks>

<boundaries>

## DO NOT CHANGE
- wp-content/plugins/elementor-addon/widgets/apartaments.php (widget bez zmian)
- wp-content/plugins/elementor-addon/assets/ (CSS/JS bez zmian)
- Tabela wp_price_history (schemat DB bez zmian)
- Istniejąca logika crona apartamenty_record_prices (tylko dodaj delete_transient na początku)
- Istniejący AJAX endpoint apartamenty_get_price_history

## SCOPE LIMITS
- Nie rejestruj dewelopera na dane.gov.pl (to ręczna czynność)
- Nie twórz osobnego pliku XML — wszystko w elementor-addon.php
- Nie buduj walidatora XSD po stronie PHP
- Nie dodawaj formularza do edycji danych o inwestycji (tylko statyczne dane w XML katalogu)
- Nie obsługuj wielu inwestycji — tylko jedna (Wyszyńskiego 12)

</boundaries>

<verification>
Przed uznaniem planu za ukończony:
- [ ] GET /ceny-mieszkan.xml zwraca HTTP 200 z Content-Type application/xml
- [ ] XML zawiera co najmniej jeden element <lokal> z polami cena_brutto i historia_cen
- [ ] GET /ceny-mieszkan.md5 zwraca 32-znakowy lowercase hex string
- [ ] GET /dane-gov-pl.xml zwraca HTTP 200 z Content-Type application/xml
- [ ] /dane-gov-pl.xml zawiera element <resource> z URL wskazującym na /ceny-mieszkan.xml
- [ ] GET /dane-gov-pl.md5 zwraca 32-znakowy lowercase hex string
- [ ] wp-admin: Narzędzia → Jawność Cen widoczne i pokazuje URL-e
- [ ] Brak PHP errors/warnings w wp-debug.log
</verification>

<success_criteria>
- Wszystkie 3 zadania ukończone
- Checkpoint human-verify przejdzie pomyślnie
- Deweloper może skopiować URL /dane-gov-pl.xml i zgłosić go do kontakt@dane.gov.pl
- Oba XML-e dostępne publicznie bez logowania
</success_criteria>

<output>
Po ukończeniu utwórz `.paul/phases/02-jawnosc-cen/02-01-SUMMARY.md`
</output>
