?php
/** TylkoWynajem presentation layer. Marketplace accounts and billing are separate. */
defined( 'ABSPATH' ) || exit;
function tw_rental_styles() {
return array( 'blue' => 'Niebieski', 'coral' => 'Koralowy', 'graphite' => 'Grafit i żółty', 'green' => 'Zielony', 'purple' => 'Fioletowy' );
}
function tw_rental_sanitize_style( $value ) {
$styles = tw_rental_styles();
return isset( $styles[ $value ] ) ? $value : 'blue';
}
function tw_rental_customizer( $customizer ) {
$customizer->add_section( 'tw_rental', array( 'title' => 'TylkoWynajem — wygląd', 'priority' => 25 ) );
$customizer->add_setting( 'tw_default_style', array( 'default' => 'blue', 'sanitize_callback' => 'tw_rental_sanitize_style', 'transport' => 'refresh' ) );
$customizer->add_control( 'tw_default_style', array( 'label' => 'Domyślny styl portalu', 'description' => 'Styl dla nowych odwiedzających. Wybór klienta jest zapamiętywany na jego urządzeniu.', 'section' => 'tw_rental', 'type' => 'select', 'choices' => tw_rental_styles() ) );
}
add_action( 'customize_register', 'tw_rental_customizer' );
function tw_rental_assets() {
if ( ! is_front_page() ) { return; }
wp_dequeue_style( 'astra-theme-css' );
wp_dequeue_style( 'astra-addon-css' );
wp_dequeue_style( 'global-styles' );
$version = wp_get_theme()->get( 'Version' );
wp_enqueue_style( 'tw-rental', get_stylesheet_uri(), array(), $version );
wp_enqueue_script( 'tw-rental', get_stylesheet_directory_uri() . '/assets/app.js', array(), $version, true );
wp_localize_script( 'tw-rental', 'TWConfig', array(
'assetBase' => get_stylesheet_directory_uri() . '/assets/',
'defaultStyle' => tw_rental_sanitize_style( get_theme_mod( 'tw_default_style', 'blue' ) ),
) );
}
add_action( 'wp_enqueue_scripts', 'tw_rental_assets', 999 );
function tw_rental_front_title( $title ) {
return is_front_page() ? 'TylkoWynajem.pl — wypożyczalnie z całej Polski' : $title;
}
add_filter( 'pre_get_document_title', 'tw_rental_front_title' );
/* Partner catalog v1.1 */
function tw_catalog_partners() {
return array(
'fitness' => array('name'=>'FitnessNaWynos.pl','host'=>'fitnessnawynos.pl','region'=>'Kraków, Katowice · Małopolska i Śląsk','phone'=>'+48791796280','contact'=>'https://fitnessnawynos.pl/kontakt/','category'=>'Fitness','shipping'=>false),
'fizjo' => array('name'=>'FizjoNaWynos.pl','host'=>'fizjonawynos.pl','region'=>'Wysyłka w Polsce','phone'=>'+48791796280','contact'=>'https://fizjonawynos.pl/kontakt/','category'=>'Rehabilitacja','shipping'=>true),
'lampy' => array('name'=>'LampyNaPodczerwien.pl','host'=>'lampynapodczerwien.pl','region'=>'Wysyłka w Polsce','phone'=>'','contact'=>'https://lampynapodczerwien.pl/kontakt/','category'=>'Podczerwień','shipping'=>true),
'grampi' => array('name'=>'Grampi.pl','host'=>'grampi.pl','region'=>'Kwaczała · Kraków i Katowice','phone'=>'+48886667853','contact'=>'https://grampi.pl/kontakt/','category'=>'Gry i elektronika','shipping'=>true),
);
}
function tw_catalog_register() {
register_post_type('tw_listing',array('labels'=>array('name'=>'Oferty wynajmu','singular_name'=>'Oferta wynajmu','add_new_item'=>'Dodaj ofertę wynajmu','edit_item'=>'Edytuj ofertę'),'public'=>true,'show_in_rest'=>false,'menu_icon'=>'dashicons-store','supports'=>array('title','editor'),'rewrite'=>false,'has_archive'=>false,'capability_type'=>'post','map_meta_cap'=>true));
}
add_action('init','tw_catalog_register');
function tw_catalog_clean($text) { return trim(html_entity_decode(wp_strip_all_tags((string)$text),ENT_QUOTES,'UTF-8')); }
function tw_catalog_review_ids() { return array(147,141,129,153,332,320,266,264,604); }
function tw_catalog_category($partner,$product) {
if ($partner !== 'fitness') { $partners=tw_catalog_partners(); return $partners[$partner]['category']; }
$slugs=wp_list_pluck($product['categories'],'slug');
if (array_intersect($slugs,array('rehabilitacja','aparaty-do-drenazu-limfatycznego','presoterapia','masazery','rollmasazery'))) return 'Rehabilitacja';
if (in_array('lampy-na-podczerwien',$slugs,true)) return 'Podczerwień';
if (array_intersect($slugs,array('deski-sup-wypozyczalnia','sauny-finskie-mobilne','jacuzzi','rowery-elektryczne-na-wynajem','rowery-gravelowe-na-wynajem'))) return 'Rekreacja';
return 'Fitness';
}
function tw_catalog_price_variants($host,$id,$base) {
$map=tw_catalog_verified_prices(); $key=$host.':'.$id;
if (!isset($map[$key]) || !$map[$key]) return array();
$rows=$map[$key]; $min=min(wp_list_pluck($rows,'price'));
return abs($min-$base)<0.011 ? $rows : array();
}
function tw_catalog_build($key,$product) {
$partners=tw_catalog_partners(); $partner=$partners[$key];
$source=esc_url_raw($product['permalink']);
if (wp_parse_url($source,PHP_URL_HOST)!==($partner['host']) return new WP_Error('source','Nieprawidłowa domena karty produktu.');
$name=tw_catalog_clean($product['name']); $prices=$product['prices'];
$price=isset($prices['price']) && is_numeric($prices['price']) ? (float)$prices['price']/pow(10,(int)$prices['currency_minor_unit']) : null;
if (($prices['currency_code'] ?? '')!=='PLN') $price=null;
$variants=$price!==null ? tw_catalog_price_variants($partner['host'],$product['id'],$price) : array();
$unit='wariant wynajmu';
if ($key==='fizjo' && strpos($product['price_html'],'/ mies.')!==false) $unit='miesiąc';
if ($variants) { $low=$variants[0]; foreach($variants as $row) if($row['price']<$low['price']) $low=$row; $price=$low['price']; $unit=$low['label']; }
$category=tw_catalog_category($key,$product);
$image=isset($product['images'][0]['src']) ? esc_url_raw($product['images'][0]['src'],array('https')) : '';
$source_categories=array_map('tw_catalog_clean',wp_list_pluck($product['categories'],'name'));
return array('source_id'=>(int)$product['id'],'partner'=>$key,'supplier'=>$partner['name'],'name'=>$name,'category'=>$category,'source_categories'=>$source_categories,'price'=>$price,'unit'=>$unit,'variants'=>$variants,'currency'=>'PLN','city'=>$partner['region'],'shipping'=>$partner['shipping'],'delivery'=>true,'phone'=>$partner['phone'],'contact'=>$partner['contact'],'image'=>$image,'source'=>$source,'desc'=>'Oferta partnera '.$partner['name'].'. Termin, dostępność sprzętu, kaucję i koszt dostawy potwierdź bezpośrednio w wypożyczalni.','checked'=>current_time('Y-m-d'),'availability'=>'Do potwierdzenia');
}
function tw_catalog_import() {
if (!current_user_can('manage_options')) wp_send_json_error('Brak uprawnień.',403);
check_ajax_referer('tw_catalog_import','nonce');
$key=isset($_POST['partner']) ? sanitize_key(wp_unslash($_POST['partner'])) : '';
$page=isset($_POST['page']) ? absint($_POST['page']) : 1;
$partners=tw_catalog_partners();
if (!isset($partners[$key]) || $page<1 || $page>50) wp_send_json_error('Nieprawidłowy katalog.',400);
$url='https://'.$partners[$key]['host'].'/wp-json/wc/store/v1/products?per_page=100&page='.$page;
$response=wp_safe_remote_get($url,array('timeout'=>40,'redirection'=>2,'limit_response_size'=>8*1024*1024));
if (is_wp_error($response)) wp_send_json_error($response->get_error_message(),502);
if (wp_remote_retrieve_response_code($response)!==200) wp_send_json_error('Katalog zwrócił HTTP '.wp_remote_retrieve_response_code($response),502);
$products=json_decode(wp_remote_retrieve_body($response),true);
if (!is_array($products) || (isset($products['code']))) wp_send_json_error('Nieprawidłowe dane katalogu.',502);
$counts=array('created'=>0,'updated'=>0,'review'=>0,'skipped'=>0);
foreach($products as $product) {
if (empty($product['id']) || empty($product['name']) || empty($product['prices']) || !isset($product['categories'])) wp_send_json_error('Niepełny rekord produktu.',502);
$slugs=wp_list_pluck($product['categories'],'slug');
if ($key==='fitness' && (($product['type'] ?? '')==='external' || in_array('outlet-sprzetu-fitness',$slugs,true))) { $counts['skipped']++; continue; }
$data=tw_catalog_build($key,$product);
if (is_wp_error($data)) wp_send_json_error($data->get_error_message(),502);
$source_key=$key.':'.(int)$product['id'];
$ids=get_posts(array('post_type'=>'tw_listing','post_status'=>array('publish','draft','pending','private','future'),'meta_key'=>'_tw_source_key','meta_value'=>$source_key,'fields'=>'ids','numberposts'=>1));
$review=$key==='fizjo' && in_array((int)$product['id'],tw_catalog_review_ids(),true);
$post=array('post_type'=>'tw_listing','post_title'=>$data['name'],'post_content'=>$data['desc'],'post_status'=>$review?'draft':(!empty($ids)?get_post_status($ids[0]):'publish'));
if ($ids) $post['ID']=$ids[0];
$post_id=wp_insert_post(wp_slash($post),true);
if (is_wp_error($post_id)) wp_send_json_error($post_id->get_error_message(),500);
update_post_meta($post_id,'_tw_source_key',$source_key);
update_post_meta($post_id,'_tw_data',wp_slash($data));
if ($review) update_post_meta($post_id,'_tw_review','Sprawdź możliwość wynajmu: materiał zużywalny, implant lub akcesorium osobiste.');
$counts[$ids?'updated':'created']++; if($review) $counts['review']++;
}
$total=(int)wp_remote_retrieve_header($response,'x-wp-total');
$pages=max(1,(int)ceil($total/100));
update_option('tw_catalog_last_import',current_time('mysql'),false);
wp_send_json_success(array('partner'=>$partners[$key]['name'],'page'=>$page,'pages'=>$pages,'total'=>$total,'counts'=>$counts));
}
add_action('wp_ajax_tw_catalog_import','tw_catalog_import');
function tw_catalog_menu() { add_submenu_page('edit.php?post_type=tw_listing','Import partnerów','Import partnerów','manage_options','tw-catalog','tw_catalog_admin'); }
add_action('admin_menu','tw_catalog_menu');
function tw_catalog_admin() {
if (!current_user_can('manage_options')) return;
$counts=wp_count_posts('tw_listing');
echo '<div class="wrap"><h1>Katalog partnerów TylkoWynajem</h1><p>Opublikowane: <strong>'.(int)$counts->publish.'</strong> · Szkice do sprawdzenia: <strong>'.(int)$counts->draft.'</strong></p><p>Import czterech wskazanych partnerów. Ponowienie aktualizuje istniejące oferty po identyfikatorze źródłowym. Nie usuwa ofert. Zdjęcia są pobierane przez przeglądarkę ze stron partnerów. Ceny i dostępność wymagają potwierdzenia u partnera.</p><p>Pomijamy outlet i odnośniki sprzedażowe FitnessNaWynos. Dziewięć pozycji FizjoNaWynos wymagających sprawdzenia trafia do szkiców.</p><button class="button button-primary" id="tw-import">Importuj / odśwież 4 katalogi</button><pre id="tw-import-log" style="white-space:pre-wrap;padding:20px;background:#fff" aria-live="polite"></pre></div>';
$config=array('url'=>admin_url('admin-ajax.php'),'nonce'=>wp_create_nonce('tw_catalog_import'),'partners'=>array_keys(tw_catalog_partners()));
echo '<script>const twImport='.wp_json_encode($config).';document.getElementById("tw-import").onclick=async function(){this.disabled=true;const log=document.getElementById("tw-import-log");log.textContent="Import trwa. Pozostaw tę stronę otwartą.\\\\n";try{for(const partner of twImport.partners){let pages=1;for(let page=1;page<=pages;page++){const body=new URLSearchParams({action:"tw_catalog_import",nonce:twImport.nonce,partner,page});const response=await fetch(twImport.url,{method:"POST",credentials:"same-origin",body});const result=await response.json();if(!result.success)throw Error(typeof result.data==="string"?result.data:JSON.stringify(result.data));const d=result.data;pages=d.pages;if(pages>50)throw Error("Za duży katalog — sprawdź ręcznie.");log.textContent+=d.partner+" · strona "+page+"/"+pages+" · nowe "+d.counts.created+", aktualizacje "+d.counts.updated+", szkice "+d.counts.review+", pominięte "+d.counts.skipped+"\\\\n";}}log.textContent+="IMPORT ZAKOŃCZONY. Odśwież stronę, aby zobaczyć liczniki.";}catch(error){log.textContent+="BŁĄD: "+error.message+". Możesz bezpiecznie ponowić import.";}finally{this.disabled=false;}};</script>';
}
function tw_catalog_rows() {
$posts=get_posts(array('post_type'=>'tw_listing','post_status'=>'publish','numberposts'=>-1,'orderby'=>'ID','order'=>'ASC')); $rows=array();
foreach($posts as $post) { $d=get_post_meta($post->ID,'_tw_data',true); if(!is_array($d)) continue; $d['id']=$post->ID;$d['name']=get_the_title($post);$d['url']=get_permalink($post);$rows[]=$d; }
return $rows;
}
function tw_catalog_localize() {
if (!is_front_page()) return;
wp_localize_script('tw-rental','TWCatalog',array('items'=>tw_catalog_rows(),'partners'=>tw_catalog_partners()));
wp_add_inline_style('tw-rental','.product-image img.photo{width:100%;height:100%;object-fit:contain;background:#fff;padding:12px}.tw-partner-filter{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:18px}.tw-partner-filter .filter{font-size:12px}.tw-pagination{display:flex;align-items:center;justify-content:center;gap:15px;margin-top:28px}.card-body h3{min-height:2.8em}.card-meta{flex-wrap:wrap}.card-meta span{white-space:normal}.tw-single{max-width:980px;margin:30px auto;padding:24px}.tw-single img{max-height:380px;max-width:100%;object-fit:contain}.tw-single table{max-width:600px}.tw-contact{display:flex;gap:12px;flex-wrap:wrap}.tw-source{font-size:13px}.tw-ssr-links{padding:20px;display:flex;gap:12px;flex-wrap:wrap}');
}
add_action('wp_enqueue_scripts','tw_catalog_localize',1000);
function tw_catalog_money($price) { return number_format_i18n((float)$price,((float)$price==floor((float)$price))?0:2).' zł'; }
function tw_catalog_single($content) {
if (!is_singular('tw_listing') || !in_the_loop() || !is_main_query()) return $content;
$d=get_post_meta(get_the_ID(),'_tw_data',true);if(!is_array($d)) return $content;
$h='<section class="tw-single"><p><a href="'.esc_url(home_url('/#oferty')).'</a></p>';
if($d['image']) $h.='<img src="'.esc_url($d['image']).'" alt="'.esc_attr($d['name']).'" loading="lazy">';
$h='<p><strong>'.esc_html($d['supplier']).'</strong> · '.esc_html($d['city']).'</p><h2>'.($d['price']!==null?'Od '.esc_html(tw_catalog_money($d['price'])).' / '.esc_html($d['unit']):'Cena u partnera').'</h2>';
if($d['variants']) { $h='<table><caption>Cennik wariantów wynajmu</caption><thead><tr><th scope="col">Okres / wariant</th><th scope="col">Cena za cały okres</th></tr></thead><tbody>';foreach($d['variants'] as $v) $h.='<tr><td>'.esc_html($v['label']).'</td><td>'.esc_html(tw_catalog_money($v['price'])).'</td></tr>';$h.='</tbody></table>'; }
$h.='<p>'.esc_html($d['desc']).'</p><p>Dostępność: <strong>do potwierdzenia</strong>. Koszt transportu jest ustalany osobno.</p><div class="tw-contact"><a class="button" href="'.esc_url($d['source']).'" target="_blank" rel="noopener">Sprawdź ofertę u partnera ↗</a><a class="button" href="'.esc_url($d['contact']).'" target="_blank" rel="noopener">Kontakt z wypożyczalnią</a>';
if($d['phone']) $h.='<a class="button" href="tel:'.esc_attr($d['phone']).'">Zadzwoń: '.esc_html($d['phone']).'</a>';
$h.='</div><p class="tw-source">Źródło: <a href="'.esc_url($d['source']).'">'.esc_html($d['supplier']).'</a> · Aktualizacja danych: '.esc_html($d['checked']).'. Dostępność i ostateczne warunki potwierdza partner.</p></section>';
return $h;
}
add_filter('the_content','tw_catalog_single');
function tw_catalog_meta_box() { add_meta_box('tw-source','Dane importu','tw_catalog_meta','tw_listing','normal','default'); }
add_action('add_meta_boxes','tw_catalog_meta_box');
function tw_catalog_meta($post) { $d=get_post_meta($post->ID,'_tw_data',true);if(!is_array($d)) return;echo '<p>Partner: <strong>'.esc_html($d['supplier']).'</strong> · ID źródła: '.(int)$d['source_id'].'</p><p><a href="'.esc_url($d['source']).'" target="_blank" rel="noopener">Otwórz kartę źródłową</a></p>'; $review=get_post_meta($post->ID,'_tw_review',true);if($review) echo '<p><strong>'.esc_html($review).'</strong></p>'; }
function tw_catalog_verified_prices() { static $map=null; if($map===null) $map=json_decode('{"fitnessnawynos.pl:21540":[{"label":"1 miesiąc","price":249.0},{"label":"2 miesiące","price":479.0},{"label":"3 miesiące","price":699.0}],"fitnessnawynos.pl:21404":[{"label":"1 miesiąc","price":350.0},{"label":"2 miesiące","price":600.0},{"label":"3 miesiące","price":800.0}],"fitnessnawynos.pl:21365":[{"label":"1 miesiąc","price":399.0},{"label":"2 miesiące","price":749.0},{"label":"3 miesiące","price":999.0}],"fitnessnawynos.pl:21324":[{"label":"1 miesiąc","price":269.0},{"label":"2 miesiące","price":469.0},{"label":"3 miesiące","price":649.0}],"fitnessnawynos.pl:21102":[{"label":"1 dzień","price":249.99},{"label":"2 dni","price":449.99},{"label":"3 dni","price":599.99},{"label":"7 dni","price":999.99},{"label":"14 dni","price":1649.99},{"label":"30 dni","price":2499.99}],"fitnessnawynos.pl:21088":[{"label":"1 dzień","price":249.99},{"label":"2 dni","price":449.99},{"label":"3 dni","price":599.99},{"label":"7 dni","price":899.99},{"label":"14 dni","price":1499.99},{"label":"30 dni","price":2199.99}],"fitnessnawynos.pl:20794":[{"label":"2 tygodnie","price":299.99},{"label":"1 miesiąc","price":399.99},{"label":"2 miesiące","price":699.99},{"label":"3 miesiące","price":1099.99}],"fitnessnawynos.pl:20776":[{"label":"2 tygodnie","price":249.99},{"label":"1 miesiąc","price":399.99},{"label":"2 miesiące","price":699.99},{"label":"3 miesiące","price":949.99}],"fitnessnawynos.pl:20755":[{"label":"2 tygodnie","price":299.99},{"label":"1 miesiąc","price":449.99},{"label":"2 miesiące","price":899.99},{"label":"3 miesiące","price":1099.99}],"fitnessnawynos.pl:20677":[{"label":"tydzień","price":119.99},{"label":"2 tygodnie","price":229.99},{"label":"miesiąc","price":289.99}],"fitnessnawynos.pl:20566":[{"label":"1 miesiąc","price":300.0},{"label":"2 miesiące","price":500.0},{"label":"3 miesiące","price":650.0}],"fitnessnawynos.pl:20528":[{"label":"1 miesiąc","price":180.0},{"label":"2 miesiące","price":300.0},{"label":"3 miesiące","price":430.0}],"fitnessnawynos.pl:8493":[{"label":"1 miesiąc","price":160.0},{"label":"2 miesiące","price":300.0},{"label":"3 miesiące","price":450.0}],"fitnessnawynos.pl:8475":[{"label":"1 miesiąc","price":180.0},{"label":"2 miesiące","price":350.0},{"label":"3 miesiące","price":480.0}],"fitnessnawynos.pl:8460":[{"label":"1 miesiąc","price":300.0},{"label":"2 miesiące","price":550.0},{"label":"3 miesiące","price":790.0}],"fitnessnawynos.pl:8446":[{"label":"1 miesiąc","price":300.0},{"label":"2 miesiące","price":580.0},{"label":"3 miesiące","price":820.0}],"fitnessnawynos.pl:8386":[{"label":"1 dzień","price":499.99},{"label":"2 dni","price":699.99},{"label":"3 dni","price":799.99},{"label":"1 tydzień","price":999.99},{"label":"2 tygodnie","price":1499.99},{"label":"1 miesiąc","price":2299.99}],"fitnessnawynos.pl:8307":[{"label":"1 miesiąc","price":199.99},{"label":"2 miesiące","price":379.99},{"label":"3 miesiące","price":549.99}],"fitnessnawynos.pl:8288":[{"label":"2 tygodnie","price":79.99},{"label":"1 miesiąc","price":139.99},{"label":"2 miesiące","price":269.99},{"label":"3 miesiące","price":369.99}],"fitnessnawynos.pl:8272":[{"label":"tydzień","price":139.99},{"label":"2 tygodnie","price":249.99},{"label":"miesiąc","price":329.99}],"fitnessnawynos.pl:8178":[{"label":"1 miesiąc","price":140.0},{"label":"2 miesiące","price":260.0},{"label":"3 miesiące","price":370.0}],"fitnessnawynos.pl:8045":[{"label":"1 miesiąc","price":200.0},{"label":"2 miesiące","price":390.0},{"label":"3 miesiące","price":520.0}],"fitnessnawynos.pl:8029":[{"label":"1 miesiąc","price":180.0},{"label":"2 miesiące","price":350.0},{"label":"3 miesiące","price":470.0}],"fitnessnawynos.pl:8008":[{"label":"1 miesiąc","price":180.0},{"label":"2 miesiące","price":350.0},{"label":"3 miesiące","price":480.0}],"fitnessnawynos.pl:7995":[{"label":"1 miesiąc","price":180.0},{"label":"2 miesiące","price":350.0},{"label":"3 miesiące","price":480.0}],"lampynapodczerwien.pl:16357":[{"label":"1 tydzień","price":59.99},{"label":"2 tygodnie","price":99.99},{"label":"1 miesiąc","price":129.99},{"label":"2 miesiące","price":199.99},{"label":"3 miesiące","price":269.99}],"grampi.pl:7016":[{"label":"1 Dzień","price":130.0},{"label":"3Dni","price":160.0},{"label":"7Dni","price":250.0},{"label":"14Dni","price":350.0},{"label":"30Dni","price":500.0}]}',true); return $map; }
function tw_catalog_asset_version($src,$handle) { return $handle==='tw-rental' ? add_query_arg('catalog','1.1.0',$src) : $src; }
add_filter('script_loader_src','tw_catalog_asset_version',10,2);
Warning: Cannot modify header information - headers already sent by (output started at /home/platne/serwer143591/public_html/tylkowynajem.pl/wp-content/themes/tylkowynajem-astra/functions.php:1) in /home/platne/serwer143591/public_html/tylkowynajem.pl/wp-includes/sitemaps/class-wp-sitemaps-renderer.php on line 126
https://tylkowynajem.pl/wp-sitemap-posts-post-1.xmlhttps://tylkowynajem.pl/wp-sitemap-posts-page-1.xmlhttps://tylkowynajem.pl/wp-sitemap-taxonomies-category-1.xmlhttps://tylkowynajem.pl/wp-sitemap-users-1.xml