feat(v0.1): historia cen + jawnosc cen — milestone Initial Release
Historia cen: - Tabela wp_price_history z WP Cronem dziennym (snapshot cen) - AJAX endpoint apartamenty_get_price_history (zabezpieczony nonce) - Popup "Historia cen" w widgecie — vanilla JS, modal zgodny z projektem Jawnosc cen: - Endpointy /ceny-mieszkan.xml + /dane-gov-pl.xml (XSD-compliant) - Pliki MD5 dla obu XML - Strona admina: Narzedzia -> Jawnosc Cen z URL-ami do Ministerstwa - Transient cache 1h z inwalidacja przez cron Dokumentacja: docs/readme.md + docs/jawnosc-cen.md Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -275,3 +275,114 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Historia cen — popup overlay
|
||||
.price-history-overlay {
|
||||
display: none;
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 99999;
|
||||
background: rgba(25, 44, 68, 0.55);
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20px;
|
||||
|
||||
&.is-open {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
.price-history-modal {
|
||||
position: relative;
|
||||
background: #fff;
|
||||
border: 4px solid #192c44;
|
||||
padding: 32px 36px 28px;
|
||||
max-width: 560px;
|
||||
width: 100%;
|
||||
font-family: 'Barlow', sans-serif;
|
||||
color: #192c44;
|
||||
|
||||
@media (max-width: 600px) {
|
||||
padding: 24px 20px 20px;
|
||||
}
|
||||
|
||||
&__close {
|
||||
position: absolute;
|
||||
top: 14px;
|
||||
right: 14px;
|
||||
background: none;
|
||||
border: 2px solid #192c44;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
margin: 0 0 18px;
|
||||
padding-right: 40px;
|
||||
color: #192c44;
|
||||
}
|
||||
|
||||
&__current {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
&__row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: 18px;
|
||||
line-height: 1.5;
|
||||
color: #192c44;
|
||||
|
||||
&--bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
||||
&__val {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
&__table-wrap {
|
||||
border-top: 1px solid #192c44;
|
||||
padding-top: 12px;
|
||||
margin-top: 4px;
|
||||
max-height: 40vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
&__table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
|
||||
tr {
|
||||
border: none;
|
||||
background: transparent;
|
||||
|
||||
td {
|
||||
padding: 4px 0;
|
||||
font-size: 15px;
|
||||
color: #192c44;
|
||||
font-family: 'Barlow', sans-serif;
|
||||
font-weight: 400;
|
||||
border: none;
|
||||
background: transparent;
|
||||
|
||||
&:last-child {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
&:nth-child(2) {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,3 +18,109 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
hideClass: 'fancybox-fadeOut',
|
||||
});
|
||||
});
|
||||
|
||||
// Historia cen
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
var overlay = document.getElementById('price-history-overlay');
|
||||
var closeBtn = document.getElementById('price-history-close');
|
||||
var elTitle = document.getElementById('price-history-title');
|
||||
var elPrice = document.getElementById('price-history-price');
|
||||
var elPriceM2= document.getElementById('price-history-sqm');
|
||||
var elTbody = document.getElementById('price-history-tbody');
|
||||
|
||||
if (!overlay || !closeBtn || !elTitle || !elPrice || !elPriceM2 || !elTbody) {
|
||||
console.warn('[historia-cen] Brakuje elementów popupa w DOM:', {
|
||||
overlay: !!overlay, closeBtn: !!closeBtn,
|
||||
elTitle: !!elTitle, elPrice: !!elPrice,
|
||||
elPriceM2: !!elPriceM2, elTbody: !!elTbody
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
function openPopup() {
|
||||
overlay.setAttribute('aria-hidden', 'false');
|
||||
overlay.classList.add('is-open');
|
||||
document.body.style.overflow = 'hidden';
|
||||
}
|
||||
|
||||
function closePopup() {
|
||||
overlay.setAttribute('aria-hidden', 'true');
|
||||
overlay.classList.remove('is-open');
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
|
||||
// Zamknij przyciskiem X
|
||||
closeBtn.addEventListener('click', closePopup);
|
||||
|
||||
// Zamknij klikając na overlay (poza modalem)
|
||||
overlay.addEventListener('click', function (e) {
|
||||
if (e.target === overlay) closePopup();
|
||||
});
|
||||
|
||||
// Zamknij klawiszem Escape
|
||||
document.addEventListener('keydown', function (e) {
|
||||
if (e.key === 'Escape' && overlay.classList.contains('is-open')) closePopup();
|
||||
});
|
||||
|
||||
// Kliknięcie w przycisk Historia cen
|
||||
document.querySelectorAll('.btn-historia-cen').forEach(function (btn) {
|
||||
btn.addEventListener('click', function () {
|
||||
var postId = this.dataset.postId;
|
||||
if (!postId) return;
|
||||
|
||||
// Reset i pokaż "Ładowanie..."
|
||||
elTitle.textContent = 'Ładowanie...';
|
||||
elPrice.textContent = '';
|
||||
elPriceM2.textContent = '';
|
||||
elTbody.innerHTML = '';
|
||||
openPopup();
|
||||
|
||||
// Sprawdź dostępność danych globalnych (wp_localize_script)
|
||||
if (typeof apartamentsData === 'undefined') {
|
||||
elTitle.textContent = 'Błąd konfiguracji';
|
||||
return;
|
||||
}
|
||||
|
||||
// Buduj FormData
|
||||
var formData = new FormData();
|
||||
formData.append('action', 'apartamenty_get_price_history');
|
||||
formData.append('post_id', postId);
|
||||
formData.append('nonce', apartamentsData.nonce);
|
||||
|
||||
fetch(apartamentsData.ajaxUrl, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
credentials: 'same-origin',
|
||||
})
|
||||
.then(function (res) { return res.json(); })
|
||||
.then(function (json) {
|
||||
if (!json.success) {
|
||||
elTitle.textContent = 'Brak danych';
|
||||
return;
|
||||
}
|
||||
|
||||
var d = json.data;
|
||||
|
||||
elTitle.textContent = d.title || '';
|
||||
elPrice.textContent = d.price ? d.price + ' zł' : '—';
|
||||
elPriceM2.textContent = d.price_m2 ? d.price_m2 + ' zł' : '—';
|
||||
|
||||
if (!d.history || d.history.length === 0) {
|
||||
elTbody.innerHTML = '<tr><td colspan="3">Brak historii cen</td></tr>';
|
||||
return;
|
||||
}
|
||||
|
||||
elTbody.innerHTML = d.history.map(function (row) {
|
||||
return '<tr>' +
|
||||
'<td>' + (row.recorded_at || '') + '</td>' +
|
||||
'<td>' + (row.price_m2 ? row.price_m2 + ' zł/m²' : '—') + '</td>' +
|
||||
'<td>' + (row.price ? row.price + ' zł' : '—') + '</td>' +
|
||||
'</tr>';
|
||||
}).join('');
|
||||
})
|
||||
.catch(function () {
|
||||
elTitle.textContent = 'Błąd ładowania';
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -83,4 +83,423 @@ function elementor_addon_register_assets() {
|
||||
true
|
||||
);
|
||||
}
|
||||
add_action( 'wp_enqueue_scripts', 'elementor_addon_register_assets' );
|
||||
add_action( 'wp_enqueue_scripts', 'elementor_addon_register_assets' );
|
||||
|
||||
/**
|
||||
* Przekaż dane do JS (ajaxUrl + nonce) gdy skrypt jest enqueue'owany przez widget.
|
||||
*/
|
||||
function elementor_addon_localize_scripts() {
|
||||
wp_localize_script( 'elementor-addon-main-js', 'apartamentsData', [
|
||||
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
|
||||
'nonce' => wp_create_nonce( 'apartamenty_price_history_nonce' ),
|
||||
] );
|
||||
}
|
||||
add_action( 'wp_enqueue_scripts', 'elementor_addon_localize_scripts', 20 );
|
||||
|
||||
// ===========================================================
|
||||
// HISTORIA CEN — TABELA DB
|
||||
// ===========================================================
|
||||
|
||||
/**
|
||||
* Tworzy tabelę wp_price_history jeśli nie istnieje lub wersja DB jest stara.
|
||||
*/
|
||||
function elementor_addon_create_price_history_table() {
|
||||
global $wpdb;
|
||||
|
||||
$table_name = $wpdb->prefix . 'price_history';
|
||||
$charset_collate = $wpdb->get_charset_collate();
|
||||
|
||||
$sql = "CREATE TABLE {$table_name} (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
post_id BIGINT UNSIGNED NOT NULL,
|
||||
price VARCHAR(50) NOT NULL DEFAULT '',
|
||||
price_m2 VARCHAR(50) NOT NULL DEFAULT '',
|
||||
floor_space VARCHAR(50) NOT NULL DEFAULT '',
|
||||
recorded_at DATE NOT NULL,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY unique_daily (post_id, recorded_at),
|
||||
KEY idx_post_id (post_id)
|
||||
) ENGINE=InnoDB {$charset_collate};";
|
||||
|
||||
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
|
||||
dbDelta( $sql );
|
||||
|
||||
update_option( 'elementor_addon_db_version', '1.0' );
|
||||
}
|
||||
register_activation_hook( __FILE__, 'elementor_addon_create_price_history_table' );
|
||||
|
||||
/**
|
||||
* Sprawdza wersję DB przy każdym init i tworzy tabelę jeśli brakuje.
|
||||
*/
|
||||
function elementor_addon_maybe_update_db() {
|
||||
if ( get_option( 'elementor_addon_db_version' ) !== '1.0' ) {
|
||||
elementor_addon_create_price_history_table();
|
||||
}
|
||||
}
|
||||
add_action( 'init', 'elementor_addon_maybe_update_db' );
|
||||
|
||||
// ===========================================================
|
||||
// HISTORIA CEN — CRON DZIENNY
|
||||
// ===========================================================
|
||||
|
||||
/**
|
||||
* Rejestruje WP Cron jeśli jeszcze nie zaplanowany.
|
||||
*/
|
||||
function elementor_addon_schedule_cron() {
|
||||
if ( ! wp_next_scheduled( 'apartamenty_record_prices' ) ) {
|
||||
wp_schedule_event( time(), 'daily', 'apartamenty_record_prices' );
|
||||
}
|
||||
}
|
||||
add_action( 'wp', 'elementor_addon_schedule_cron' );
|
||||
|
||||
/**
|
||||
* Zapisuje aktualne ceny wszystkich apartamentów do tabeli historii.
|
||||
* Używa INSERT IGNORE — jeden rekord na apartament na dzień.
|
||||
*/
|
||||
function elementor_addon_record_prices() {
|
||||
global $wpdb;
|
||||
|
||||
delete_transient( 'apartamenty_price_xml_cache' );
|
||||
|
||||
$table_name = $wpdb->prefix . 'price_history';
|
||||
$today = current_time( 'Y-m-d' );
|
||||
|
||||
$apartaments = new WP_Query( [
|
||||
'post_type' => 'apartamenty',
|
||||
'post_status' => 'publish',
|
||||
'posts_per_page' => -1,
|
||||
'fields' => 'ids',
|
||||
] );
|
||||
|
||||
if ( empty( $apartaments->posts ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( $apartaments->posts as $post_id ) {
|
||||
$price = get_post_meta( $post_id, 'information_price', true );
|
||||
$price_m2 = get_post_meta( $post_id, 'information_price_m2', true );
|
||||
$floor_space = get_post_meta( $post_id, 'information_floor_space', true );
|
||||
|
||||
$wpdb->query(
|
||||
$wpdb->prepare(
|
||||
"INSERT IGNORE INTO {$table_name} (post_id, price, price_m2, floor_space, recorded_at)
|
||||
VALUES (%d, %s, %s, %s, %s)",
|
||||
$post_id,
|
||||
(string) $price,
|
||||
(string) $price_m2,
|
||||
(string) $floor_space,
|
||||
$today
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
add_action( 'apartamenty_record_prices', 'elementor_addon_record_prices' );
|
||||
|
||||
// ===========================================================
|
||||
// HISTORIA CEN — AJAX ENDPOINT
|
||||
// ===========================================================
|
||||
|
||||
/**
|
||||
* Zwraca historię cen dla apartamentu jako JSON.
|
||||
* Wymaga nonce: apartamenty_price_history_nonce
|
||||
*/
|
||||
function elementor_addon_get_price_history_ajax() {
|
||||
global $wpdb;
|
||||
|
||||
if ( ! check_ajax_referer( 'apartamenty_price_history_nonce', 'nonce', false ) ) {
|
||||
wp_send_json_error( [ 'message' => 'Invalid nonce' ] );
|
||||
die();
|
||||
}
|
||||
|
||||
$post_id = absint( $_POST['post_id'] ?? 0 );
|
||||
if ( ! $post_id ) {
|
||||
wp_send_json_error( [ 'message' => 'Invalid post_id' ] );
|
||||
die();
|
||||
}
|
||||
|
||||
$table_name = $wpdb->prefix . 'price_history';
|
||||
|
||||
$history = $wpdb->get_results(
|
||||
$wpdb->prepare(
|
||||
"SELECT recorded_at, price, price_m2, floor_space
|
||||
FROM {$table_name}
|
||||
WHERE post_id = %d
|
||||
ORDER BY recorded_at DESC
|
||||
LIMIT 50",
|
||||
$post_id
|
||||
)
|
||||
);
|
||||
|
||||
wp_send_json_success( [
|
||||
'title' => get_the_title( $post_id ),
|
||||
'price' => get_post_meta( $post_id, 'information_price', true ),
|
||||
'price_m2' => get_post_meta( $post_id, 'information_price_m2', true ),
|
||||
'floor_space' => get_post_meta( $post_id, 'information_floor_space', true ),
|
||||
'history' => $history,
|
||||
] );
|
||||
}
|
||||
add_action( 'wp_ajax_apartamenty_get_price_history', 'elementor_addon_get_price_history_ajax' );
|
||||
add_action( 'wp_ajax_nopriv_apartamenty_get_price_history', 'elementor_addon_get_price_history_ajax' );
|
||||
|
||||
// ===========================================================
|
||||
// JAWNOŚĆ CEN — XML ENDPOINTS
|
||||
// ===========================================================
|
||||
|
||||
/**
|
||||
* Rejestruje reguły przepisywania dla endpointów XML.
|
||||
*/
|
||||
function apartamenty_xml_rewrite_rules() {
|
||||
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' );
|
||||
}
|
||||
add_action( 'init', 'apartamenty_xml_rewrite_rules', 10 );
|
||||
|
||||
/**
|
||||
* Dodaje query vars dla endpointów XML.
|
||||
*/
|
||||
function apartamenty_xml_query_vars( $vars ) {
|
||||
$vars[] = 'apartamenty_xml';
|
||||
$vars[] = 'apartamenty_datagov';
|
||||
return $vars;
|
||||
}
|
||||
add_filter( 'query_vars', 'apartamenty_xml_query_vars' );
|
||||
|
||||
/**
|
||||
* Generuje XML z cenami wszystkich apartamentów.
|
||||
* Wynik cachowany w transiencie na 1 godzinę.
|
||||
*
|
||||
* @return string XML jako string
|
||||
*/
|
||||
function apartamenty_generate_price_xml() {
|
||||
$cached = get_transient( 'apartamenty_price_xml_cache' );
|
||||
if ( false !== $cached ) {
|
||||
return $cached;
|
||||
}
|
||||
|
||||
global $wpdb;
|
||||
$table_name = $wpdb->prefix . 'price_history';
|
||||
|
||||
$query = new WP_Query( [
|
||||
'post_type' => 'apartamenty',
|
||||
'post_status' => 'publish',
|
||||
'posts_per_page' => -1,
|
||||
'orderby' => 'title',
|
||||
'order' => 'ASC',
|
||||
] );
|
||||
|
||||
$x = function( $val ) {
|
||||
return htmlspecialchars( (string) $val, ENT_XML1, 'UTF-8' );
|
||||
};
|
||||
|
||||
$xml = '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
|
||||
$xml .= '<lokale inwestycja="Wyszyńskiego 12" generowany="' . $x( date( 'c' ) ) . '">' . "\n";
|
||||
|
||||
if ( $query->have_posts() ) {
|
||||
foreach ( $query->posts as $post ) {
|
||||
$post_id = $post->ID;
|
||||
|
||||
$type = get_post_meta( $post_id, 'information_type', true );
|
||||
$floor = get_post_meta( $post_id, 'information_floor', true );
|
||||
$floor_space = get_post_meta( $post_id, 'information_floor_space', true );
|
||||
$price = get_post_meta( $post_id, 'information_price', true );
|
||||
$price_m2 = get_post_meta( $post_id, 'information_price_m2', true );
|
||||
$status = get_post_meta( $post_id, 'information_status', true );
|
||||
|
||||
$history = $wpdb->get_results(
|
||||
$wpdb->prepare(
|
||||
"SELECT recorded_at, price, price_m2
|
||||
FROM {$table_name}
|
||||
WHERE post_id = %d
|
||||
ORDER BY recorded_at DESC",
|
||||
$post_id
|
||||
)
|
||||
);
|
||||
|
||||
$last_update = ! empty( $history ) ? $history[0]->recorded_at : date( 'Y-m-d' );
|
||||
|
||||
$xml .= "\t" . '<lokal id="' . absint( $post_id ) . '">' . "\n";
|
||||
$xml .= "\t\t<nazwa>" . $x( $post->post_title ) . "</nazwa>\n";
|
||||
$xml .= "\t\t<typ>" . $x( $type ) . "</typ>\n";
|
||||
$xml .= "\t\t<pietro>" . $x( $floor ) . "</pietro>\n";
|
||||
$xml .= "\t\t<powierzchnia>" . $x( $floor_space ) . "</powierzchnia>\n";
|
||||
$xml .= "\t\t<status>" . $x( $status ) . "</status>\n";
|
||||
$xml .= "\t\t<cena_brutto>" . $x( $price ) . "</cena_brutto>\n";
|
||||
$xml .= "\t\t<cena_za_m2>" . $x( $price_m2 ) . "</cena_za_m2>\n";
|
||||
$xml .= "\t\t<data_aktualizacji>" . $x( $last_update ) . "</data_aktualizacji>\n";
|
||||
$xml .= "\t\t<historia_cen>\n";
|
||||
|
||||
foreach ( $history as $row ) {
|
||||
$xml .= "\t\t\t" . '<zmiana data="' . $x( $row->recorded_at ) . '">' . "\n";
|
||||
$xml .= "\t\t\t\t<cena_brutto>" . $x( $row->price ) . "</cena_brutto>\n";
|
||||
$xml .= "\t\t\t\t<cena_za_m2>" . $x( $row->price_m2 ) . "</cena_za_m2>\n";
|
||||
$xml .= "\t\t\t</zmiana>\n";
|
||||
}
|
||||
|
||||
$xml .= "\t\t</historia_cen>\n";
|
||||
$xml .= "\t</lokal>\n";
|
||||
}
|
||||
}
|
||||
|
||||
$xml .= '</lokale>';
|
||||
|
||||
set_transient( 'apartamenty_price_xml_cache', $xml, HOUR_IN_SECONDS );
|
||||
|
||||
return $xml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generuje XML katalogu dane.gov.pl zgodny z XSD portalu.
|
||||
*
|
||||
* @return string XML jako string
|
||||
*/
|
||||
function apartamenty_generate_datagov_xml() {
|
||||
$resource_url = home_url( '/ceny-mieszkan.xml' );
|
||||
$last_update = date( 'Y-m-d\T00:00:00.000\Z' );
|
||||
|
||||
$x = function( $val ) {
|
||||
return htmlspecialchars( (string) $val, ENT_XML1, 'UTF-8' );
|
||||
};
|
||||
|
||||
$xml = '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
|
||||
$xml .= '<datasets xmlns:xsi="urn:otwarte-dane:harvester:1.0-rc1">' . "\n";
|
||||
$xml .= "\t<dataset status=\"published\">\n";
|
||||
$xml .= "\t\t<extIdent>wyszynskiego12-ceny-mieszkan-v1</extIdent>\n";
|
||||
$xml .= "\t\t<title>\n";
|
||||
$xml .= "\t\t\t<polish>Ceny mieszkań – Wyszyńskiego 12</polish>\n";
|
||||
$xml .= "\t\t</title>\n";
|
||||
$xml .= "\t\t<description>\n";
|
||||
$xml .= "\t\t\t<polish>Historia cen lokali mieszkalnych w inwestycji przy ul. Wyszyńskiego 12. Dane aktualizowane codziennie zgodnie z ustawą o jawności cen.</polish>\n";
|
||||
$xml .= "\t\t</description>\n";
|
||||
$xml .= "\t\t<updateFrequency>daily</updateFrequency>\n";
|
||||
$xml .= "\t\t<categories>REGI</categories>\n";
|
||||
$xml .= "\t\t<resources>\n";
|
||||
$xml .= "\t\t\t<resource status=\"published\">\n";
|
||||
$xml .= "\t\t\t\t<extIdent>wyszynskiego12-ceny-xml-v1</extIdent>\n";
|
||||
$xml .= "\t\t\t\t<url>" . $x( $resource_url ) . "</url>\n";
|
||||
$xml .= "\t\t\t\t<title>\n";
|
||||
$xml .= "\t\t\t\t\t<polish>Cennik lokali XML</polish>\n";
|
||||
$xml .= "\t\t\t\t</title>\n";
|
||||
$xml .= "\t\t\t\t<description>\n";
|
||||
$xml .= "\t\t\t\t\t<polish>Plik XML z aktualnym cennikiem i historią zmian cen wszystkich lokali</polish>\n";
|
||||
$xml .= "\t\t\t\t</description>\n";
|
||||
$xml .= "\t\t\t\t<availability>remote</availability>\n";
|
||||
$xml .= "\t\t\t\t<lastUpdateDate>" . $x( $last_update ) . "</lastUpdateDate>\n";
|
||||
$xml .= "\t\t\t</resource>\n";
|
||||
$xml .= "\t\t</resources>\n";
|
||||
$xml .= "\t\t<tags lang=\"pl\">\n";
|
||||
$xml .= "\t\t\t<tag>mieszkania</tag>\n";
|
||||
$xml .= "\t\t\t<tag>ceny</tag>\n";
|
||||
$xml .= "\t\t\t<tag>deweloper</tag>\n";
|
||||
$xml .= "\t\t\t<tag>jawność cen</tag>\n";
|
||||
$xml .= "\t\t\t<tag>historia cen</tag>\n";
|
||||
$xml .= "\t\t</tags>\n";
|
||||
$xml .= "\t</dataset>\n";
|
||||
$xml .= '</datasets>';
|
||||
|
||||
return $xml;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obsługuje żądania do endpointów XML — wysyła odpowiedź i kończy.
|
||||
*/
|
||||
function apartamenty_xml_template_redirect() {
|
||||
$xml_type = get_query_var( 'apartamenty_xml' );
|
||||
$datagov_type = get_query_var( 'apartamenty_datagov' );
|
||||
|
||||
if ( $xml_type ) {
|
||||
$content = apartamenty_generate_price_xml();
|
||||
if ( 'xml' === $xml_type ) {
|
||||
header( 'Content-Type: application/xml; charset=UTF-8' );
|
||||
echo $content;
|
||||
} else {
|
||||
header( 'Content-Type: text/plain; charset=UTF-8' );
|
||||
echo md5( $content );
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
if ( $datagov_type ) {
|
||||
$content = apartamenty_generate_datagov_xml();
|
||||
if ( 'xml' === $datagov_type ) {
|
||||
header( 'Content-Type: application/xml; charset=UTF-8' );
|
||||
echo $content;
|
||||
} else {
|
||||
header( 'Content-Type: text/plain; charset=UTF-8' );
|
||||
echo md5( $content );
|
||||
}
|
||||
exit;
|
||||
}
|
||||
}
|
||||
add_action( 'template_redirect', 'apartamenty_xml_template_redirect', 1 );
|
||||
|
||||
// ===========================================================
|
||||
// JAWNOŚĆ CEN — STRONA ADMINISTRACYJNA
|
||||
// ===========================================================
|
||||
|
||||
/**
|
||||
* Rejestruje stronę Jawność Cen w menu Narzędzia wp-admin.
|
||||
*/
|
||||
function apartamenty_jawnosc_cen_menu() {
|
||||
add_management_page(
|
||||
'Jawność Cen',
|
||||
'Jawność Cen',
|
||||
'manage_options',
|
||||
'jawnosc-cen',
|
||||
'apartamenty_jawnosc_cen_page'
|
||||
);
|
||||
}
|
||||
add_action( 'admin_menu', 'apartamenty_jawnosc_cen_menu' );
|
||||
|
||||
/**
|
||||
* Renderuje stronę administracyjną z URL-ami do zgłoszenia.
|
||||
*/
|
||||
function apartamenty_jawnosc_cen_page() {
|
||||
$url_ceny = esc_url( home_url( '/ceny-mieszkan.xml' ) );
|
||||
$url_datagov = esc_url( home_url( '/dane-gov-pl.xml' ) );
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1>Jawność Cen — Wyszyńskiego 12</h1>
|
||||
|
||||
<div class="notice notice-info">
|
||||
<p>Dane aktualizowane codziennie przez WP Cron. Zgłoś URL katalogu dane.gov.pl do administratora portalu: <strong>kontakt@dane.gov.pl</strong></p>
|
||||
</div>
|
||||
|
||||
<table class="widefat" style="max-width:800px; margin-top:20px;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Plik</th>
|
||||
<th>URL</th>
|
||||
<th>Akcje</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><strong>Plik cen (dane)</strong></td>
|
||||
<td><code><?php echo $url_ceny; ?></code></td>
|
||||
<td>
|
||||
<button class="button" onclick="copyUrl('<?php echo esc_js( $url_ceny ); ?>')">Kopiuj URL</button>
|
||||
<a href="<?php echo $url_ceny; ?>" target="_blank" class="button">Otwórz XML</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Katalog dane.gov.pl</strong><br><em>(zgłoś Ministerstwu)</em></td>
|
||||
<td><code><?php echo $url_datagov; ?></code></td>
|
||||
<td>
|
||||
<button class="button button-primary" onclick="copyUrl('<?php echo esc_js( $url_datagov ); ?>')">Kopiuj URL</button>
|
||||
<a href="<?php echo $url_datagov; ?>" target="_blank" class="button">Otwórz XML</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<script>
|
||||
function copyUrl(url) {
|
||||
navigator.clipboard.writeText(url).then(function() {
|
||||
alert('Skopiowano: ' + url);
|
||||
}).catch(function() {
|
||||
prompt('Skopiuj URL:', url);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
@@ -156,7 +156,8 @@ class Elementor_Apartaments extends \Elementor\Widget_Base {
|
||||
<?php endif; ?>
|
||||
<tr class="apartament-card__price-history">
|
||||
<td class="apartament-card__info_table-title"></td>
|
||||
<td class="apartament-card__info_table-value">
|
||||
<td class="apartament-card__info_table-value btn-historia-cen"
|
||||
data-post-id="<?php echo esc_attr( get_the_ID() ); ?>">
|
||||
HISTORIA CEN
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="12" height="7" viewBox="0 0 12 7" fill="none">
|
||||
@@ -214,6 +215,33 @@ class Elementor_Apartaments extends \Elementor\Widget_Base {
|
||||
<?php wp_reset_postdata(); ?>
|
||||
</div>
|
||||
|
||||
<?php // Popup historia cen — jeden globalny, wypełniany przez JS ?>
|
||||
<div class="price-history-overlay" id="price-history-overlay" aria-hidden="true">
|
||||
<div class="price-history-modal" role="dialog" aria-modal="true">
|
||||
<button class="price-history-modal__close" id="price-history-close" aria-label="Zamknij">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14" fill="none">
|
||||
<path d="M13 1L1 13M1 1L13 13" stroke="#192C44" stroke-width="2" stroke-linecap="round"/>
|
||||
</svg>
|
||||
</button>
|
||||
<h3 class="price-history-modal__title" id="price-history-title"></h3>
|
||||
<div class="price-history-modal__current">
|
||||
<div class="price-history-modal__row price-history-modal__row--bold">
|
||||
<span>Cena brutto:</span>
|
||||
<span class="price-history-modal__val" id="price-history-price"></span>
|
||||
</div>
|
||||
<div class="price-history-modal__row">
|
||||
<span>Cena m²:</span>
|
||||
<span class="price-history-modal__val" id="price-history-sqm"> </span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="price-history-modal__table-wrap">
|
||||
<table class="price-history-modal__table">
|
||||
<tbody id="price-history-tbody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user