746 lines
23 KiB
PHP
746 lines
23 KiB
PHP
<?php
|
||
/**
|
||
* Plugin Name: Elementor Addon
|
||
* Description: Simple hello world widgets for Elementor.
|
||
* Version: 1.0.0
|
||
* Author: Elementor Developer
|
||
* Author URI: https://developers.elementor.com/
|
||
* Text Domain: elementor-addon
|
||
*
|
||
* Requires Plugins: elementor
|
||
* Elementor tested up to: 3.21.0
|
||
* Elementor Pro tested up to: 3.21.0
|
||
*/
|
||
|
||
if ( ! defined( 'ABSPATH' ) ) {
|
||
exit;
|
||
}
|
||
|
||
/**
|
||
* Formatuje kwotÄ™ do postaci 1 000 000,00.
|
||
*
|
||
* @param mixed $price Surowa wartość ceny.
|
||
* @return string
|
||
*/
|
||
function elementor_addon_format_price( $price ) {
|
||
if ( '' === $price || null === $price ) {
|
||
return '';
|
||
}
|
||
|
||
$value = wp_strip_all_tags( (string) $price );
|
||
$value = str_replace( [ "\xc2\xa0", ' ', 'zł', 'zl' ], '', $value );
|
||
$value = preg_replace( '/[^\d,.\-]/u', '', $value );
|
||
|
||
if ( '' === $value || null === $value ) {
|
||
return '';
|
||
}
|
||
|
||
$last_comma = strrpos( $value, ',' );
|
||
$last_dot = strrpos( $value, '.' );
|
||
|
||
if ( false !== $last_comma && false !== $last_dot ) {
|
||
if ( $last_comma > $last_dot ) {
|
||
$value = str_replace( '.', '', $value );
|
||
$value = str_replace( ',', '.', $value );
|
||
} else {
|
||
$value = str_replace( ',', '', $value );
|
||
}
|
||
} elseif ( false !== $last_comma ) {
|
||
$value = str_replace( '.', '', $value );
|
||
$value = str_replace( ',', '.', $value );
|
||
} else {
|
||
$parts = explode( '.', $value );
|
||
if ( count( $parts ) > 2 ) {
|
||
$decimal = array_pop( $parts );
|
||
$value = implode( '', $parts ) . '.' . $decimal;
|
||
}
|
||
}
|
||
|
||
if ( ! is_numeric( $value ) ) {
|
||
return trim( (string) $price );
|
||
}
|
||
|
||
return number_format_i18n( (float) $value, 0 );
|
||
}
|
||
|
||
/**
|
||
* Register widget files
|
||
*/
|
||
function register_hello_world_widget( $widgets_manager ) {
|
||
require_once( __DIR__ . '/widgets/apartaments.php' );
|
||
require_once( __DIR__ . '/widgets/parking-spots.php' );
|
||
|
||
$widgets_manager->register( new \Elementor_Apartaments() );
|
||
$widgets_manager->register( new \Elementor_Parking_Spots() );
|
||
}
|
||
add_action( 'elementor/widgets/register', 'register_hello_world_widget' );
|
||
|
||
/**
|
||
* Register scripts/styles only.
|
||
* Do not enqueue them globally.
|
||
*/
|
||
function elementor_addon_register_assets() {
|
||
|
||
// Swiper CSS
|
||
wp_register_style(
|
||
'elementor-addon-swiper',
|
||
plugins_url( 'plugins/swiper/swiper-bundle.min.css', __FILE__ ),
|
||
[],
|
||
'11.0.0'
|
||
);
|
||
|
||
// Fancybox CSS
|
||
wp_register_style(
|
||
'elementor-addon-fancybox',
|
||
'https://cdn.jsdelivr.net/npm/@fancyapps/ui@5.0/dist/fancybox/fancybox.css',
|
||
[],
|
||
'5.0'
|
||
);
|
||
|
||
// Widget CSS
|
||
wp_register_style(
|
||
'elementor-addon-main-css',
|
||
plugins_url( 'assets/css/main.css', __FILE__ ),
|
||
[ 'elementor-addon-swiper', 'elementor-addon-fancybox' ],
|
||
'1.0.0'
|
||
);
|
||
|
||
// Swiper JS
|
||
wp_register_script(
|
||
'elementor-addon-swiper',
|
||
plugins_url( 'plugins/swiper/swiper-bundle.min.js', __FILE__ ),
|
||
[],
|
||
'11.0.0',
|
||
true
|
||
);
|
||
|
||
// Fancybox JS
|
||
wp_register_script(
|
||
'elementor-addon-fancybox',
|
||
'https://cdn.jsdelivr.net/npm/@fancyapps/ui@5.0/dist/fancybox/fancybox.umd.js',
|
||
[],
|
||
'5.0',
|
||
true
|
||
);
|
||
|
||
// Widget JS
|
||
wp_register_script(
|
||
'elementor-addon-main-js',
|
||
plugins_url( 'assets/js/main.js', __FILE__ ),
|
||
[ 'jquery', 'elementor-addon-swiper', 'elementor-addon-fancybox' ],
|
||
'1.0.0',
|
||
true
|
||
);
|
||
}
|
||
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 );
|
||
|
||
/**
|
||
* Włącza wsparcie "Atrybuty wpisu" (menu_order) dla CPT apartamenty,
|
||
* aby można było ręcznie ustawiać kolejność.
|
||
*/
|
||
function elementor_addon_enable_apartamenty_menu_order( $args, $post_type ) {
|
||
if ( 'apartamenty' !== $post_type ) {
|
||
return $args;
|
||
}
|
||
|
||
$supports = $args['supports'] ?? [];
|
||
if ( true === $supports || false === $supports ) {
|
||
$supports = [ 'title', 'editor' ];
|
||
}
|
||
if ( ! is_array( $supports ) ) {
|
||
$supports = (array) $supports;
|
||
}
|
||
if ( ! in_array( 'page-attributes', $supports, true ) ) {
|
||
$supports[] = 'page-attributes';
|
||
}
|
||
|
||
$args['supports'] = $supports;
|
||
|
||
return $args;
|
||
}
|
||
add_filter( 'register_post_type_args', 'elementor_addon_enable_apartamenty_menu_order', 20, 2 );
|
||
|
||
// ===========================================================
|
||
// 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 );
|
||
}
|
||
|
||
/**
|
||
* Tworzy tabelÄ™ wp_parking_price_history dla miejsc postojowych.
|
||
*/
|
||
function elementor_addon_create_parking_price_history_table() {
|
||
global $wpdb;
|
||
|
||
$table_name = $wpdb->prefix . 'parking_price_history';
|
||
$charset_collate = $wpdb->get_charset_collate();
|
||
|
||
$sql = "CREATE TABLE {$table_name} (
|
||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||
parking_type VARCHAR(50) NOT NULL,
|
||
price VARCHAR(50) NOT NULL DEFAULT '',
|
||
price_m2 VARCHAR(50) NOT NULL DEFAULT '',
|
||
recorded_at DATE NOT NULL,
|
||
PRIMARY KEY (id),
|
||
UNIQUE KEY unique_daily (parking_type, recorded_at),
|
||
KEY idx_type (parking_type)
|
||
) ENGINE=InnoDB {$charset_collate};";
|
||
|
||
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
|
||
dbDelta( $sql );
|
||
}
|
||
|
||
/**
|
||
* Tworzy obie tabele przy aktywacji pluginu.
|
||
*/
|
||
function elementor_addon_create_tables() {
|
||
elementor_addon_create_price_history_table();
|
||
elementor_addon_create_parking_price_history_table();
|
||
update_option( 'elementor_addon_db_version', '1.1' );
|
||
}
|
||
register_activation_hook( __FILE__, 'elementor_addon_create_tables' );
|
||
|
||
/**
|
||
* Sprawdza wersję DB przy każdym init i tworzy tabele jeśli brakuje.
|
||
*/
|
||
function elementor_addon_maybe_update_db() {
|
||
if ( get_option( 'elementor_addon_db_version' ) !== '1.1' ) {
|
||
elementor_addon_create_tables();
|
||
}
|
||
}
|
||
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
|
||
)
|
||
);
|
||
}
|
||
|
||
// Miejsca postojowe - zapis cen z ACF options (pola w grupach)
|
||
$parking_table = $wpdb->prefix . 'parking_price_history';
|
||
$parking_groups = [
|
||
'zwykle' => 'miejsce_postojowe_zwykle',
|
||
'rodzinne' => 'miejsce_postojowe_rodzinne',
|
||
];
|
||
|
||
foreach ( $parking_groups as $type => $group_name ) {
|
||
$group = get_field( $group_name, 'option' ) ?: [];
|
||
$price = $group[ $group_name . '_cena' ] ?? '';
|
||
$price_m2 = $group[ $group_name . '_cena_m2' ] ?? '';
|
||
|
||
if ( $price || $price_m2 ) {
|
||
$wpdb->query(
|
||
$wpdb->prepare(
|
||
"INSERT IGNORE INTO {$parking_table} (parking_type, price, price_m2, recorded_at)
|
||
VALUES (%s, %s, %s, %s)",
|
||
$type,
|
||
(string) $price,
|
||
(string) $price_m2,
|
||
$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' );
|
||
|
||
/**
|
||
* Zwraca historię cen dla miejsca postojowego jako JSON.
|
||
* Parametr: parking_type ('zwykle' lub 'rodzinne')
|
||
*/
|
||
function elementor_addon_get_parking_price_history_ajax() {
|
||
global $wpdb;
|
||
|
||
if ( ! check_ajax_referer( 'apartamenty_price_history_nonce', 'nonce', false ) ) {
|
||
wp_send_json_error( [ 'message' => 'Invalid nonce' ] );
|
||
die();
|
||
}
|
||
|
||
$parking_type = sanitize_text_field( $_POST['parking_type'] ?? '' );
|
||
if ( ! in_array( $parking_type, [ 'zwykle', 'rodzinne' ], true ) ) {
|
||
wp_send_json_error( [ 'message' => 'Invalid parking_type' ] );
|
||
die();
|
||
}
|
||
|
||
$labels = [
|
||
'zwykle' => 'Miejsce postojowe pojedyncze',
|
||
'rodzinne' => 'Miejsce postojowe rodzinne',
|
||
];
|
||
|
||
$group_names = [
|
||
'zwykle' => 'miejsce_postojowe_zwykle',
|
||
'rodzinne' => 'miejsce_postojowe_rodzinne',
|
||
];
|
||
|
||
$table_name = $wpdb->prefix . 'parking_price_history';
|
||
|
||
$history = $wpdb->get_results(
|
||
$wpdb->prepare(
|
||
"SELECT recorded_at, price, price_m2
|
||
FROM {$table_name}
|
||
WHERE parking_type = %s
|
||
ORDER BY recorded_at DESC
|
||
LIMIT 50",
|
||
$parking_type
|
||
)
|
||
);
|
||
|
||
$group_name = $group_names[ $parking_type ];
|
||
$group = get_field( $group_name, 'option' ) ?: [];
|
||
|
||
wp_send_json_success( [
|
||
'title' => $labels[ $parking_type ],
|
||
'price' => $group[ $group_name . '_cena' ] ?? '',
|
||
'price_m2' => $group[ $group_name . '_cena_m2' ] ?? '',
|
||
'history' => $history,
|
||
] );
|
||
}
|
||
add_action( 'wp_ajax_parking_get_price_history', 'elementor_addon_get_parking_price_history_ajax' );
|
||
add_action( 'wp_ajax_nopriv_parking_get_price_history', 'elementor_addon_get_parking_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' => [
|
||
'menu_order' => 'ASC',
|
||
'title' => '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";
|
||
}
|
||
}
|
||
|
||
// Miejsca postojowe
|
||
$parking_table = $wpdb->prefix . 'parking_price_history';
|
||
$parking_configs = [
|
||
'zwykle' => [
|
||
'label' => 'Miejsce postojowe zwykłe',
|
||
'group_name' => 'miejsce_postojowe_zwykle',
|
||
],
|
||
'rodzinne' => [
|
||
'label' => 'Miejsce postojowe rodzinne',
|
||
'group_name' => 'miejsce_postojowe_rodzinne',
|
||
],
|
||
];
|
||
|
||
$xml .= "\t<miejsca_postojowe>\n";
|
||
|
||
foreach ( $parking_configs as $type => $cfg ) {
|
||
$group = get_field( $cfg['group_name'], 'option' ) ?: [];
|
||
$price = $group[ $cfg['group_name'] . '_cena' ] ?? '';
|
||
$price_m2 = $group[ $cfg['group_name'] . '_cena_m2' ] ?? '';
|
||
|
||
$p_history = $wpdb->get_results(
|
||
$wpdb->prepare(
|
||
"SELECT recorded_at, price, price_m2
|
||
FROM {$parking_table}
|
||
WHERE parking_type = %s
|
||
ORDER BY recorded_at DESC",
|
||
$type
|
||
)
|
||
);
|
||
|
||
$last_update = ! empty( $p_history ) ? $p_history[0]->recorded_at : date( 'Y-m-d' );
|
||
|
||
$xml .= "\t\t" . '<miejsce_postojowe typ="' . $x( $type ) . '">' . "\n";
|
||
$xml .= "\t\t\t<nazwa>" . $x( $cfg['label'] ) . "</nazwa>\n";
|
||
$xml .= "\t\t\t<cena_brutto>" . $x( $price ) . "</cena_brutto>\n";
|
||
$xml .= "\t\t\t<cena_za_m2>" . $x( $price_m2 ) . "</cena_za_m2>\n";
|
||
$xml .= "\t\t\t<data_aktualizacji>" . $x( $last_update ) . "</data_aktualizacji>\n";
|
||
$xml .= "\t\t\t<historia_cen>\n";
|
||
|
||
foreach ( $p_history as $row ) {
|
||
$xml .= "\t\t\t\t" . '<zmiana data="' . $x( $row->recorded_at ) . '">' . "\n";
|
||
$xml .= "\t\t\t\t\t<cena_brutto>" . $x( $row->price ) . "</cena_brutto>\n";
|
||
$xml .= "\t\t\t\t\t<cena_za_m2>" . $x( $row->price_m2 ) . "</cena_za_m2>\n";
|
||
$xml .= "\t\t\t\t</zmiana>\n";
|
||
}
|
||
|
||
$xml .= "\t\t\t</historia_cen>\n";
|
||
$xml .= "\t\t</miejsce_postojowe>\n";
|
||
}
|
||
|
||
$xml .= "\t</miejsca_postojowe>\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
|
||
}
|