- Plugin: vc_breadcrumb_trail()/vc_breadcrumb_html(); tours, region, city and tag pages show Home / (Region) / Page in the banner instead of the generic 'Tour & Travel' label; the tour page uses the same helper - Theme: blog list shows Home / Blog; blog post title, breadcrumb and meta move into the banner like the other pages - Shared style: title's colour (white in photo banners, black on About), softer links, current page bold and shortened if long - also on phones - Plugin 1.9.6, theme 1.0.16 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
256 lines
15 KiB
PHP
256 lines
15 KiB
PHP
<?php
|
|
/**
|
|
* Single Viator tour — "Hop On Hop Off" design (ported from new front/tour.html).
|
|
* Itinerary and Reviews are not in the mockup; they reuse the design's own
|
|
* primitives (.notes, .tmeta) so they sit naturally alongside the rest.
|
|
*/
|
|
defined( 'ABSPATH' ) || exit;
|
|
|
|
get_header();
|
|
|
|
while ( have_posts() ) :
|
|
the_post();
|
|
$post_id = get_the_ID();
|
|
|
|
$rating = get_post_meta( $post_id, '_vc_rating', true );
|
|
$reviews = (int) get_post_meta( $post_id, '_vc_review_count', true );
|
|
$price = get_post_meta( $post_id, '_vc_price_from', true );
|
|
$currency = get_post_meta( $post_id, '_vc_currency', true );
|
|
$supplier = get_post_meta( $post_id, '_vc_supplier', true );
|
|
$duration_min = (int) get_post_meta( $post_id, '_vc_duration_minutes', true );
|
|
$max_trav = (int) get_post_meta( $post_id, '_vc_max_travelers', true );
|
|
$freecancel = (int) get_post_meta( $post_id, '_vc_free_cancellation', true );
|
|
$booking_url = get_post_meta( $post_id, '_vc_booking_url', true );
|
|
|
|
$dest_terms = get_the_terms( $post_id, 'viator_destination' );
|
|
$dest_names = ( $dest_terms && ! is_wp_error( $dest_terms ) ) ? wp_list_pluck( $dest_terms, 'name' ) : array();
|
|
$cat_terms = get_the_terms( $post_id, 'viator_tag' );
|
|
$cat_names = ( $cat_terms && ! is_wp_error( $cat_terms ) ) ? array_slice( wp_list_pluck( VC_Tags::public_terms( $cat_terms ), 'name' ), 0, 2 ) : array();
|
|
|
|
$gallery = vc_json_decode_meta( get_post_meta( $post_id, '_vc_gallery_image_urls', true ), array() );
|
|
$gallery = array_values( array_filter( (array) $gallery ) );
|
|
$primary = get_post_meta( $post_id, '_vc_primary_image_url', true );
|
|
if ( ! $primary && ! empty( $gallery ) ) {
|
|
$primary = $gallery[0];
|
|
}
|
|
// The featured image (sharp, full size) leads the gallery when set;
|
|
// Viator's own photos follow.
|
|
$lead = has_post_thumbnail( $post_id ) ? (string) get_the_post_thumbnail_url( $post_id, 'large' ) : $primary;
|
|
$all = array_values( array_unique( array_filter( array_merge( array( $lead, $primary ), $gallery ) ) ) );
|
|
|
|
$book_href = $booking_url ? $booking_url : '#';
|
|
$book_attrs = $booking_url ? ' target="_blank" rel="nofollow noopener"' : '';
|
|
?>
|
|
<div class="vc-app vc-single">
|
|
<section class="section tour tour--top">
|
|
<div class="container">
|
|
<?php echo vc_breadcrumb_html( vc_breadcrumb_trail(), get_the_title(), 'tour__crumbs' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
|
|
</div>
|
|
<div class="container tour__layout">
|
|
<div class="tour__main">
|
|
<?php if ( $all ) : ?>
|
|
<?php // .gallery: the lightbox (vc-design.js) opens on any of these images, hidden ones included. ?>
|
|
<div class="gallery tgal<?php echo count( $all ) < 3 ? ' tgal--few' : ''; ?>">
|
|
<?php foreach ( $all as $i => $url ) : ?>
|
|
<img class="<?php echo 0 === $i ? 'tgal__main' : ( $i < 3 ? 'tgal__thumb' : 'tgal__extra' ); ?>" src="<?php echo esc_url( $url ); ?>" alt="<?php echo esc_attr( get_the_title() ); ?>"<?php echo 0 === $i ? ' fetchpriority="high"' : ' loading="lazy"'; ?>>
|
|
<?php endforeach; ?>
|
|
<?php if ( count( $all ) > 3 ) : ?>
|
|
<button class="tgal__all" type="button"><?php echo vc_icon( 'grid' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?><?php echo esc_html( sprintf( __( 'All %d photos', 'viator-collector' ), count( $all ) ) ); ?></button>
|
|
<?php endif; ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<header class="tour-head">
|
|
<h1 class="tour-head__title"><?php the_title(); ?></h1>
|
|
<div class="tmeta">
|
|
<?php if ( '' !== (string) $rating ) : ?>
|
|
<span class="tmeta__item"><?php echo vc_icon( 'star', 'icon--12 tmeta__star' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?><b><?php echo esc_html( number_format_i18n( (float) $rating, 1 ) ); ?></b><i>/5</i><?php if ( $reviews ) : ?> <a class="tmeta__reviews" href="#reviews">(<?php echo esc_html( sprintf( _n( '%s review', '%s reviews', $reviews, 'viator-collector' ), number_format_i18n( $reviews ) ) ); ?>)</a><?php endif; ?></span>
|
|
<?php endif; ?>
|
|
<?php if ( ! empty( $dest_names ) ) : ?>
|
|
<span class="tmeta__rule" aria-hidden="true"></span>
|
|
<span class="tmeta__item"><?php echo vc_icon( 'location', 'tmeta__icon' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?><?php echo esc_html( implode( ', ', array_slice( $dest_names, 0, 2 ) ) ); ?></span>
|
|
<?php endif; ?>
|
|
<?php if ( $supplier ) : ?>
|
|
<span class="tmeta__rule" aria-hidden="true"></span>
|
|
<span class="tmeta__item"><?php echo vc_icon( 'person', 'tmeta__icon' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?><?php echo esc_html__( 'from', 'viator-collector' ) . ' ' . esc_html( $supplier ); ?></span>
|
|
<?php endif; ?>
|
|
</div>
|
|
</header>
|
|
|
|
<ul class="facts">
|
|
<?php if ( $duration_min ) : ?>
|
|
<li class="fact"><?php echo vc_icon( 'duration', 'fact__icon' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?><div class="fact__text"><span class="fact__k"><?php echo esc_html__( 'Duration', 'viator-collector' ); ?></span><span class="fact__v"><?php echo esc_html( vc_duration_human( $duration_min ) ); ?></span></div></li>
|
|
<?php endif; ?>
|
|
<?php if ( $max_trav ) : ?>
|
|
<li class="fact"><?php echo vc_icon( 'person', 'fact__icon' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?><div class="fact__text"><span class="fact__k"><?php echo esc_html__( 'Max Travelers', 'viator-collector' ); ?></span><span class="fact__v"><?php echo esc_html( sprintf( __( '%d people', 'viator-collector' ), $max_trav ) ); ?></span></div></li>
|
|
<?php endif; ?>
|
|
<?php if ( $freecancel ) : ?>
|
|
<li class="fact"><?php echo vc_icon( 'shield-check', 'fact__icon' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?><div class="fact__text"><span class="fact__k"><?php echo esc_html__( 'Free Cancellation', 'viator-collector' ); ?></span><span class="fact__v"><?php echo esc_html__( 'Up to 24 hrs', 'viator-collector' ); ?></span></div></li>
|
|
<?php endif; ?>
|
|
<?php if ( ! empty( $cat_names ) ) : ?>
|
|
<li class="fact"><?php echo vc_icon( 'grid', 'fact__icon' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?><div class="fact__text"><span class="fact__k"><?php echo esc_html__( 'Category', 'viator-collector' ); ?></span><span class="fact__v"><?php echo esc_html( implode( ', ', $cat_names ) ); ?></span></div></li>
|
|
<?php endif; ?>
|
|
</ul>
|
|
|
|
<?php if ( get_the_content() ) : ?>
|
|
<section class="tour__block">
|
|
<h3 class="tour__ct"><?php echo esc_html__( 'Overview', 'viator-collector' ); ?></h3>
|
|
<div class="tour__text"><?php the_content(); ?></div>
|
|
</section>
|
|
<?php endif; ?>
|
|
|
|
<?php
|
|
$inc = array_filter( array_map( 'vc_feature_text', (array) vc_json_decode_meta( get_post_meta( $post_id, '_vc_inclusions', true ), array() ) ) );
|
|
$exc = array_filter( array_map( 'vc_feature_text', (array) vc_json_decode_meta( get_post_meta( $post_id, '_vc_exclusions', true ), array() ) ) );
|
|
if ( $inc || $exc ) :
|
|
?>
|
|
<div class="lists">
|
|
<?php if ( $inc ) : ?>
|
|
<section class="ilist">
|
|
<h3 class="tour__ct"><?php echo esc_html__( "What's included", 'viator-collector' ); ?></h3>
|
|
<ul class="ilist__items">
|
|
<?php foreach ( $inc as $row ) : ?>
|
|
<li><span class="tick tick--yes" aria-hidden="true"><?php echo vc_icon( 'check', 'icon--12' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span><p><?php echo esc_html( $row ); ?></p></li>
|
|
<?php endforeach; ?>
|
|
</ul>
|
|
</section>
|
|
<?php endif; ?>
|
|
<?php if ( $exc ) : ?>
|
|
<section class="ilist">
|
|
<h3 class="tour__ct"><?php echo esc_html__( "What's Not included", 'viator-collector' ); ?></h3>
|
|
<ul class="ilist__items">
|
|
<?php foreach ( $exc as $row ) : ?>
|
|
<li><span class="tick tick--no" aria-hidden="true"><?php echo vc_icon( 'x', 'icon--12' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span><p><?php echo esc_html( $row ); ?></p></li>
|
|
<?php endforeach; ?>
|
|
</ul>
|
|
</section>
|
|
<?php endif; ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<?php
|
|
$steps = vc_itinerary_steps( vc_json_decode_meta( get_post_meta( $post_id, '_vc_itinerary', true ), array() ) );
|
|
if ( ! empty( $steps ) ) :
|
|
?>
|
|
<section class="tour__block">
|
|
<h3 class="tour__ct"><?php echo esc_html__( 'Itinerary', 'viator-collector' ); ?></h3>
|
|
<ul class="notes">
|
|
<?php foreach ( $steps as $step ) : ?>
|
|
<li class="note"><?php echo vc_icon( 'location', 'note__icon' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?><p><?php
|
|
$name = ! empty( $step['name'] ) ? '<strong>' . esc_html( $step['name'] ) . '</strong> ' : '';
|
|
echo $name . esc_html( ! empty( $step['description'] ) ? $step['description'] : '' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
|
?></p></li>
|
|
<?php endforeach; ?>
|
|
</ul>
|
|
</section>
|
|
<?php endif; ?>
|
|
|
|
<?php
|
|
$info = (array) vc_json_decode_meta( get_post_meta( $post_id, '_vc_additional_info', true ), array() );
|
|
$info_rows = array_filter( array_map( 'vc_feature_text', $info ) );
|
|
if ( ! empty( $info_rows ) ) :
|
|
?>
|
|
<section class="tour__block">
|
|
<h3 class="tour__ct"><?php echo esc_html__( 'Good to know', 'viator-collector' ); ?></h3>
|
|
<ul class="notes">
|
|
<?php foreach ( $info_rows as $row ) : ?>
|
|
<li class="note"><?php echo vc_icon( 'info', 'note__icon' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?><p><?php echo esc_html( $row ); ?></p></li>
|
|
<?php endforeach; ?>
|
|
</ul>
|
|
</section>
|
|
<?php endif; ?>
|
|
|
|
<?php
|
|
$policy = vc_json_decode_meta( get_post_meta( $post_id, '_vc_cancellation_policy', true ), array() );
|
|
$policy_text = is_scalar( $policy ) ? (string) $policy : (string) vc_array_get( $policy, 'description' );
|
|
$tiers = is_array( $policy ) ? (array) vc_array_get( $policy, 'refundEligibility', array() ) : array();
|
|
if ( $policy_text || $tiers ) :
|
|
?>
|
|
<section class="tour__block">
|
|
<h3 class="tour__ct"><?php echo esc_html__( 'Cancellation Policy', 'viator-collector' ); ?></h3>
|
|
<?php if ( $policy_text ) : ?><p class="tour__text"><?php echo esc_html( $policy_text ); ?></p><?php endif; ?>
|
|
<?php if ( $tiers ) : ?>
|
|
<div class="policy">
|
|
<?php
|
|
foreach ( $tiers as $tier ) :
|
|
$pct = (int) vc_array_get( $tier, 'percentageRefundable', 0 );
|
|
$max = vc_array_get( $tier, 'dayRangeMax', null );
|
|
$min = vc_array_get( $tier, 'dayRangeMin', null );
|
|
$yes = $pct > 0;
|
|
$when = ( null !== $max )
|
|
? sprintf( __( 'Less than %d hours before the start time', 'viator-collector' ), (int) $max * 24 )
|
|
: sprintf( __( '%d hours or more before the start time', 'viator-collector' ), (int) $min * 24 );
|
|
?>
|
|
<div class="statcard statcard--<?php echo $yes ? 'yes' : 'no'; ?>">
|
|
<?php echo vc_icon( $yes ? 'check' : 'x', 'icon--12 statcard__icon' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
|
|
<div class="statcard__text">
|
|
<span class="statcard__k"><?php echo esc_html( sprintf( __( '%d%% Refund', 'viator-collector' ), $pct ) ); ?></span>
|
|
<span class="statcard__v"><?php echo esc_html( $when ); ?></span>
|
|
</div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
<?php endif; ?>
|
|
</section>
|
|
<?php endif; ?>
|
|
|
|
<?php if ( '' !== (string) $rating && $reviews ) : ?>
|
|
<section class="tour__block" id="reviews">
|
|
<h3 class="tour__ct"><?php echo esc_html__( 'Reviews', 'viator-collector' ); ?></h3>
|
|
<div class="tmeta">
|
|
<span class="tmeta__item"><?php echo vc_icon( 'star', 'icon--12 tmeta__star' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?><b><?php echo esc_html( number_format_i18n( (float) $rating, 1 ) ); ?></b><i>/5</i></span>
|
|
<span class="tmeta__rule" aria-hidden="true"></span>
|
|
<span class="tmeta__item"><?php echo esc_html( sprintf( _n( '%s review', '%s reviews', $reviews, 'viator-collector' ), number_format_i18n( $reviews ) ) ); ?></span>
|
|
</div>
|
|
</section>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<aside class="tour__aside">
|
|
<div class="bookcard">
|
|
<h2 class="bookcard__title"><?php echo vc_icon( 'info' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?><?php echo esc_html__( 'Tour Details', 'viator-collector' ); ?></h2>
|
|
|
|
<div class="bookcard__price">
|
|
<?php if ( '' !== (string) $price ) : ?>
|
|
<p class="price">
|
|
<span class="price__k"><?php echo esc_html__( 'From', 'viator-collector' ); ?></span>
|
|
<span class="price__v"><?php echo esc_html( vc_money( $price, $currency ) ); ?></span>
|
|
<span class="price__unit"><?php echo esc_html( ( $currency ? strtoupper( $currency ) . ' ' : '' ) . __( '/ Per person', 'viator-collector' ) ); ?></span>
|
|
</p>
|
|
<?php endif; ?>
|
|
<a class="btn btn--dark bookcard__cta" href="<?php echo esc_url( $book_href ); ?>"<?php echo $book_attrs; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>><?php echo esc_html__( 'Check Availability', 'viator-collector' ); ?></a>
|
|
</div>
|
|
|
|
<div class="statcard statcard--teal">
|
|
<?php echo vc_icon( 'shield-check', 'statcard__icon' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
|
|
<div class="statcard__text">
|
|
<span class="statcard__k"><?php echo esc_html__( 'Free Cancellation', 'viator-collector' ); ?></span>
|
|
<span class="statcard__v"><?php echo esc_html__( 'Up to 24 hrs', 'viator-collector' ); ?></span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="statcard statcard--yes">
|
|
<?php echo vc_icon( 'shield-check', 'statcard__icon' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
|
|
<div class="statcard__text">
|
|
<span class="statcard__k"><?php echo esc_html__( 'Reserve now, pay later!', 'viator-collector' ); ?></span>
|
|
<span class="statcard__v"><?php echo esc_html__( 'Secure your spot today', 'viator-collector' ); ?></span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</aside>
|
|
</div>
|
|
</section>
|
|
|
|
<?php // Phones/tablets: price + booking button stay on screen (hidden on desktop, where the sidebar is sticky). ?>
|
|
<div class="tbar">
|
|
<?php if ( '' !== (string) $price ) : ?>
|
|
<p class="tbar__price"><span><?php echo esc_html__( 'From', 'viator-collector' ); ?></span><b><?php echo esc_html( vc_money( $price, $currency ) ); ?></b></p>
|
|
<?php endif; ?>
|
|
<a class="btn tbar__cta" href="<?php echo esc_url( $book_href ); ?>"<?php echo $book_attrs; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>><?php echo esc_html__( 'Check Availability', 'viator-collector' ); ?></a>
|
|
</div>
|
|
</div><!-- #vc-app -->
|
|
<?php
|
|
endwhile;
|
|
|
|
get_footer();
|