276 lines
14 KiB
PHP
276 lines
14 KiB
PHP
<?php
|
||
/**
|
||
* Plugin Name: YogaSoul Eventi
|
||
* Description: Mostra gli eventi di YogiBook con card di prenotazione (lista d'attesa inclusa). Shortcode: [yogasoul_eventi], [yogasoul_eventi ids="12,15"], [yogasoul_eventi id="12"], [yogasoul_eventi limit="3"].
|
||
* Version: 1.0.0
|
||
* Author: YogaSoul
|
||
*/
|
||
|
||
if (!defined('ABSPATH')) {
|
||
exit;
|
||
}
|
||
|
||
/**
|
||
* Base dell'API YogiBook. Sovrascrivibile in wp-config.php con:
|
||
* define('YSE_API_BASE', 'https://yogibook.yogasoul.it/apiwp');
|
||
*/
|
||
if (!defined('YSE_API_BASE')) {
|
||
define('YSE_API_BASE', 'https://yogibook.yogasoul.it/apiwp');
|
||
}
|
||
|
||
/* Registra gli asset (accodati solo quando lo shortcode è in pagina).
|
||
Nessun Google Font: il widget eredita il carattere del tema. */
|
||
add_action('wp_enqueue_scripts', function () {
|
||
wp_register_style('yse-eventi', plugins_url('assets/yogasoul-eventi.css', __FILE__), [], '1.0.1');
|
||
wp_register_script('yse-eventi', plugins_url('assets/yogasoul-eventi.js', __FILE__), [], '1.0.0', true);
|
||
});
|
||
|
||
add_shortcode('yogasoul_eventi', 'yse_render_shortcode');
|
||
|
||
function yse_render_shortcode($atts)
|
||
{
|
||
$atts = shortcode_atts([
|
||
'ids' => '',
|
||
'id' => '',
|
||
'limit' => '',
|
||
], $atts, 'yogasoul_eventi');
|
||
|
||
wp_enqueue_style('yse-eventi');
|
||
wp_enqueue_script('yse-eventi');
|
||
|
||
// Costruisce l'URL dell'endpoint lista
|
||
$query = [];
|
||
if ($atts['id'] !== '') {
|
||
$query['id'] = (int) $atts['id'];
|
||
}
|
||
if ($atts['ids'] !== '') {
|
||
$clean = preg_replace('/[^0-9,]/', '', $atts['ids']);
|
||
if ($clean !== '') {
|
||
$query['ids'] = $clean;
|
||
}
|
||
}
|
||
if ($atts['limit'] !== '') {
|
||
$query['limit'] = (int) $atts['limit'];
|
||
}
|
||
$url = YSE_API_BASE . '/events.php' . (!empty($query) ? '?' . http_build_query($query) : '');
|
||
|
||
// Lettura server-side (niente CORS) + anti-cache
|
||
$resp = wp_remote_get($url . (strpos($url, '?') !== false ? '&' : '?') . '_=' . time(), [
|
||
'timeout' => 8,
|
||
'headers' => ['Cache-Control' => 'no-cache'],
|
||
]);
|
||
if (is_wp_error($resp) || (int) wp_remote_retrieve_response_code($resp) !== 200) {
|
||
return '<div class="yse-widget"><p class="yse-empty">Eventi non disponibili al momento.</p></div>';
|
||
}
|
||
$data = json_decode(wp_remote_retrieve_body($resp), true);
|
||
if (!is_array($data) || empty($data['ok'])) {
|
||
return '<div class="yse-widget"><p class="yse-empty">Eventi non disponibili al momento.</p></div>';
|
||
}
|
||
if (empty($data['events'])) {
|
||
return '<div class="yse-widget"><p class="yse-empty">Nessun evento in programma al momento.</p></div>';
|
||
}
|
||
|
||
$out = '<div class="yse-widget" data-api="' . esc_attr(YSE_API_BASE) . '">';
|
||
foreach ($data['events'] as $ev) {
|
||
$out .= yse_render_card($ev);
|
||
}
|
||
$out .= '</div>';
|
||
return $out;
|
||
}
|
||
|
||
/* Icona SVG inline (stroke = currentColor) */
|
||
function yse_icon($name)
|
||
{
|
||
$p = [
|
||
'user' => '<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/>',
|
||
'pin' => '<path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/>',
|
||
'clock' => '<circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/>',
|
||
'heart' => '<path d="M20.8 5.6a5 5 0 0 0-7-.1L12 7l-1.8-1.5a5 5 0 1 0-6.6 7.5L12 21l8.4-8a5 5 0 0 0 .4-7.4z"/>',
|
||
'bag' => '<path d="M6 7h12l1 13H5L6 7z"/><path d="M9 7a3 3 0 0 1 6 0"/>',
|
||
'info' => '<circle cx="12" cy="12" r="9"/><path d="M12 11v5"/><path d="M12 8h.01"/>',
|
||
'img' => '<rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="9" cy="9" r="2"/><path d="m21 15-5-5L5 21"/>',
|
||
'chev' => '<path d="m6 9 6 6 6-6"/>',
|
||
];
|
||
$inner = $p[$name] ?? '';
|
||
return '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7">' . $inner . '</svg>';
|
||
}
|
||
|
||
/* Rende una singola card evento */
|
||
function yse_render_card($ev)
|
||
{
|
||
$mesiAbbr = [1 => 'GEN', 2 => 'FEB', 3 => 'MAR', 4 => 'APR', 5 => 'MAG', 6 => 'GIU', 7 => 'LUG', 8 => 'AGO', 9 => 'SET', 10 => 'OTT', 11 => 'NOV', 12 => 'DIC'];
|
||
$mesi = [1 => 'gennaio', 2 => 'febbraio', 3 => 'marzo', 4 => 'aprile', 5 => 'maggio', 6 => 'giugno', 7 => 'luglio', 8 => 'agosto', 9 => 'settembre', 10 => 'ottobre', 11 => 'novembre', 12 => 'dicembre'];
|
||
|
||
$id = (int) $ev['id'];
|
||
$cost = (float) $ev['cost'];
|
||
$sym = (string) $ev['currency_symbol'];
|
||
$price = $cost > 0 ? $sym . number_format($cost, 2, ',', '.') : 'Gratuito';
|
||
$color = (isset($ev['color']) && preg_match('/^#[0-9a-fA-F]{6}$/', $ev['color'])) ? $ev['color'] : '#4CAF50';
|
||
|
||
$soldOut = !empty($ev['sold_out']);
|
||
$waitOn = !empty($ev['waitlist_enabled']);
|
||
$waitMode = $soldOut && $waitOn;
|
||
$hardFull = $soldOut && !$waitOn;
|
||
$maxPer = max(1, (int) $ev['max_per_booking']);
|
||
$remaining = (int) $ev['remaining'];
|
||
$maxQty = $soldOut ? ($waitMode ? $maxPer : 0) : max(1, min($maxPer, $remaining));
|
||
|
||
$startTs = strtotime($ev['starts_at']);
|
||
$dayNum = (int) date('j', $startTs);
|
||
$monAbbr = $mesiAbbr[(int) date('n', $startTs)];
|
||
$dateLong = $dayNum . ' ' . $mesi[(int) date('n', $startTs)] . ' ' . date('Y', $startTs);
|
||
$timeLabel = date('H:i', $startTs) . (!empty($ev['ends_at']) ? '–' . date('H:i', strtotime($ev['ends_at'])) : '');
|
||
|
||
$address = trim((string) ($ev['address'] ?? ''));
|
||
$organizer = trim((string) ($ev['organizer'] ?? ''));
|
||
$mapQuery = $address !== '' ? $address : ((isset($ev['lat'], $ev['lng']) && $ev['lat'] && $ev['lng']) ? ($ev['lat'] . ',' . $ev['lng']) : '');
|
||
|
||
$gallery = is_array($ev['gallery'] ?? null) ? $ev['gallery'] : [];
|
||
$hasDetails = trim((string) ($ev['description'] ?? '')) !== ''
|
||
|| trim((string) ($ev['suitable_for'] ?? '')) !== ''
|
||
|| trim((string) ($ev['what_to_bring'] ?? '')) !== ''
|
||
|| trim((string) ($ev['instructions'] ?? '')) !== ''
|
||
|| !empty($ev['cover_image']) || !empty($gallery) || $mapQuery !== '';
|
||
|
||
ob_start();
|
||
?>
|
||
<div class="yse-card" style="--yse-accent: <?php echo esc_attr($color); ?>;"
|
||
data-eid="<?php echo $id; ?>"
|
||
data-cost="<?php echo esc_attr($cost); ?>"
|
||
data-sym="<?php echo esc_attr($sym); ?>"
|
||
data-maxqty="<?php echo (int) $maxQty; ?>"
|
||
data-waitmode="<?php echo $waitMode ? 1 : 0; ?>">
|
||
|
||
<!-- HEADER COMPATTO -->
|
||
<div class="yse-chead">
|
||
<div class="yse-datechip">
|
||
<div class="yse-m"><?php echo esc_html($monAbbr); ?></div>
|
||
<div class="yse-d"><?php echo $dayNum; ?></div>
|
||
</div>
|
||
<?php if (!empty($ev['cover_image'])) : ?>
|
||
<img class="yse-thumb" src="<?php echo esc_url($ev['cover_image']); ?>" alt="<?php echo esc_attr($ev['title']); ?>">
|
||
<?php endif; ?>
|
||
<div class="yse-main">
|
||
<div class="yse-title-row">
|
||
<h3><?php echo esc_html($ev['title']); ?></h3>
|
||
<?php if ($hasDetails) : ?>
|
||
<button type="button" class="yse-toggle" aria-expanded="false">
|
||
<span class="yse-tlabel">Dettagli</span><?php echo yse_icon('chev'); ?>
|
||
</button>
|
||
<?php endif; ?>
|
||
</div>
|
||
<div class="yse-meta">
|
||
<span class="yse-item"><?php echo yse_icon('user'); ?> Capacità <strong><?php echo (int) $ev['occupied']; ?>/<?php echo (int) $ev['max_seats']; ?></strong></span>
|
||
<?php if ($address !== '') : ?>
|
||
<span class="yse-item"><?php echo yse_icon('pin'); ?> <?php echo esc_html($address); ?></span>
|
||
<?php endif; ?>
|
||
<span class="yse-item"><?php echo yse_icon('clock'); ?> <?php echo esc_html($dateLong); ?> · <?php echo esc_html($timeLabel); ?></span>
|
||
</div>
|
||
</div>
|
||
<div class="yse-price"><?php echo esc_html($price); ?></div>
|
||
</div>
|
||
|
||
<!-- DETTAGLI ESPANDIBILI -->
|
||
<?php if ($hasDetails) : ?>
|
||
<div class="yse-details">
|
||
<div class="yse-hero <?php echo empty($ev['cover_image']) ? 'yse-nocover' : ''; ?>">
|
||
<?php if (!empty($ev['cover_image'])) : ?>
|
||
<img src="<?php echo esc_url($ev['cover_image']); ?>" alt="<?php echo esc_attr($ev['title']); ?>">
|
||
<?php else : ?>
|
||
<div class="yse-emblem"><?php echo yse_icon('img'); ?></div>
|
||
<?php endif; ?>
|
||
</div>
|
||
<div class="yse-body">
|
||
<?php if (trim((string) ($ev['description'] ?? '')) !== '') : ?>
|
||
<div class="yse-desc"><?php echo wp_kses_post($ev['description']); ?></div>
|
||
<?php endif; ?>
|
||
|
||
<?php if (trim((string) ($ev['suitable_for'] ?? '')) !== '' || trim((string) ($ev['what_to_bring'] ?? '')) !== '' || trim((string) ($ev['instructions'] ?? '')) !== '') : ?>
|
||
<div class="yse-detail-grid">
|
||
<?php if (trim((string) ($ev['suitable_for'] ?? '')) !== '') : ?>
|
||
<div class="yse-detail"><div class="yse-dh"><?php echo yse_icon('heart'); ?> Per chi è adatto</div><div class="yse-dt"><?php echo nl2br(esc_html($ev['suitable_for'])); ?></div></div>
|
||
<?php endif; ?>
|
||
<?php if (trim((string) ($ev['what_to_bring'] ?? '')) !== '') : ?>
|
||
<div class="yse-detail"><div class="yse-dh"><?php echo yse_icon('bag'); ?> Cosa portare</div><div class="yse-dt"><?php echo nl2br(esc_html($ev['what_to_bring'])); ?></div></div>
|
||
<?php endif; ?>
|
||
<?php if (trim((string) ($ev['instructions'] ?? '')) !== '') : ?>
|
||
<div class="yse-detail"><div class="yse-dh"><?php echo yse_icon('info'); ?> Istruzioni</div><div class="yse-dt"><?php echo nl2br(esc_html($ev['instructions'])); ?></div></div>
|
||
<?php endif; ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<?php if (!empty($gallery)) : ?>
|
||
<div class="yse-section">
|
||
<h4><?php echo yse_icon('img'); ?> Galleria</h4>
|
||
<div class="yse-gallery">
|
||
<?php foreach ($gallery as $g) : ?>
|
||
<img src="<?php echo esc_url($g); ?>" alt="" data-full="<?php echo esc_url($g); ?>">
|
||
<?php endforeach; ?>
|
||
</div>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<?php if ($mapQuery !== '') : ?>
|
||
<div class="yse-section">
|
||
<h4><?php echo yse_icon('pin'); ?> Dove</h4>
|
||
<iframe class="yse-map" loading="lazy" src="https://www.google.com/maps?q=<?php echo rawurlencode($mapQuery); ?>&output=embed"></iframe>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<!-- BOOKING -->
|
||
<div class="yse-booking">
|
||
<?php if (!empty($ev['scheduled'])) :
|
||
$pt = !empty($ev['publish_at']) ? strtotime($ev['publish_at']) : 0;
|
||
$ptLabel = $pt ? ((int) date('j', $pt) . ' ' . $mesi[(int) date('n', $pt)] . ' ' . date('Y', $pt) . ' alle ' . date('H:i', $pt)) : '';
|
||
?>
|
||
<div class="yse-scheduled">Prenotazioni aperte dal <strong><?php echo esc_html($ptLabel); ?></strong></div>
|
||
<?php elseif ($hardFull) : ?>
|
||
<div class="yse-soldout">Evento esaurito — non ci sono più posti disponibili.</div>
|
||
<?php else : ?>
|
||
<?php if ($waitMode) :
|
||
$waitingCount = (int) ($ev['waiting'] ?? 0);
|
||
$waitingTxt = $waitingCount > 0
|
||
? ' Al momento ' . ($waitingCount === 1 ? "c'è 1 persona" : "ci sono {$waitingCount} persone") . ' in coda.'
|
||
: '';
|
||
?>
|
||
<div class="yse-wl-banner">Posti esauriti — puoi iscriverti alla <strong>lista d'attesa</strong>: ti avviseremo se si libera un posto.<?php echo esc_html($waitingTxt); ?></div>
|
||
<?php endif; ?>
|
||
<div class="yse-bcompact">
|
||
<div class="yse-btitle"><?php echo $waitMode ? "Lista d'attesa" : 'Prenota questo evento'; ?></div>
|
||
<div class="yse-bblock">
|
||
<div class="yse-lbl">Persone</div>
|
||
<div class="yse-stepper">
|
||
<button type="button" class="yse-minus">−</button>
|
||
<span class="yse-num">1</span>
|
||
<button type="button" class="yse-plus">+</button>
|
||
</div>
|
||
</div>
|
||
<div class="yse-bblock yse-total">
|
||
<div class="yse-lbl">Totale</div>
|
||
<div class="yse-val"><?php echo esc_html($price); ?></div>
|
||
</div>
|
||
<button type="button" class="yse-cta yse-open"><?php echo $waitMode ? "Lista d'attesa" : 'Prenota'; ?></button>
|
||
</div>
|
||
|
||
<div class="yse-bform">
|
||
<div class="yse-frow">
|
||
<div class="yse-field"><label>Nome *</label><input type="text" class="yse-name" placeholder="Il tuo nome"></div>
|
||
<div class="yse-field"><label>Cognome *</label><input type="text" class="yse-surname" placeholder="Il tuo cognome"></div>
|
||
</div>
|
||
<div class="yse-field"><label>Email *</label><input type="email" class="yse-email" placeholder="latua@email.it"></div>
|
||
<label class="yse-consent"><input type="checkbox" class="yse-c-privacy"> <span>Ho letto e accetto l'informativa privacy. *</span></label>
|
||
<label class="yse-consent"><input type="checkbox" class="yse-c-mkt"> <span>Desidero ricevere aggiornamenti e novità via email.</span></label>
|
||
<!-- honeypot anti-bot -->
|
||
<input type="text" class="yse-hp" name="website" tabindex="-1" autocomplete="off">
|
||
<button type="button" class="yse-cta yse-full yse-submit"><?php echo $waitMode ? "Iscriviti alla lista d'attesa" : 'Conferma prenotazione'; ?></button>
|
||
<div class="yse-error"></div>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
</div>
|
||
<?php
|
||
return ob_get_clean();
|
||
}
|