feat: tour page photos-first layout, mobile booking bar; board 1 fixes

Tour page (boards 2 and 3):
- Replace the blurred 'Welcome to {city}' hero with a breadcrumb and a photo
  gallery (1 large + 2, 'All N photos' opens the lightbox); the H1 title,
  rating with review count and facts follow; booking card stays sticky
- Phones/tablets: swipeable photos, name before price, 2x2 facts, and a
  fixed bottom bar with price + Check availability (vc-tour.css)

Board 1:
- Hide Viator internal tags ('Agent Low Quality Product'...) from the tour
  category, listing filter and activities section (VC_Tags, tested)
- Listings: run the query filter late and set posts_per_archive_page so
  pages show 12 tours as the count says
- Footer columns without a menu show default links (quick links, structure
  cities, contact/about/faq) instead of empty headings

Plugin 1.9.5, theme 1.0.13.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-29 21:01:13 +03:30
parent 2f1ecd99b4
commit de9814cf3d
15 changed files with 367 additions and 57 deletions

View File

@@ -25,7 +25,7 @@ while ( have_posts() ) :
$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( $cat_terms, 'name' ), 0, 2 ) : array();
$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 ) );
@@ -33,48 +33,62 @@ while ( have_posts() ) :
if ( ! $primary && ! empty( $gallery ) ) {
$primary = $gallery[0];
}
$more = array_slice( array_values( array_diff( $gallery, array( $primary ) ) ), 0, 5 );
$big_word = ! empty( $dest_names ) ? $dest_names[0] : get_the_title();
// 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 ) ) ) );
// Breadcrumb: Home › Region › City › Tour (the tour's canonical city).
$crumbs = array( array( __( 'Home', 'viator-collector' ), home_url( '/' ) ) );
$city_slug = class_exists( 'VC_Routing' ) ? VC_Routing::product_city( $post_id ) : '';
$city = $city_slug ? get_term_by( 'slug', $city_slug, 'viator_destination' ) : null;
if ( ! $city && $dest_terms && ! is_wp_error( $dest_terms ) ) {
$city = $dest_terms[0];
}
if ( $city && $city->parent ) {
$region = get_term( $city->parent, 'viator_destination' );
if ( $region instanceof WP_Term && ! is_wp_error( get_term_link( $region ) ) ) {
$crumbs[] = array( $region->name, get_term_link( $region ) );
}
}
if ( $city && ! is_wp_error( get_term_link( $city ) ) ) {
$crumbs[] = array( $city->name, get_term_link( $city ) );
}
$book_href = $booking_url ? $booking_url : '#';
$book_attrs = $booking_url ? ' target="_blank" rel="nofollow noopener"' : '';
?>
<div class="vc-app vc-single">
<section class="hero-tour">
<?php
// Prefer a WordPress featured image (full size, responsive srcset) for a
// sharp hero — Viator's own image URLs cap out around 720px and look soft
// blown up to a full-width banner. Fall back to the Viator primary image.
if ( has_post_thumbnail( $post_id ) ) :
echo get_the_post_thumbnail( $post_id, 'full', array( 'class' => 'hero-tour__photo', 'fetchpriority' => 'high', 'alt' => '', 'sizes' => '100vw' ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
elseif ( $primary ) :
?>
<img class="hero-tour__photo" src="<?php echo esc_url( $primary ); ?>" alt="" fetchpriority="high">
<?php
endif;
?>
<div class="hero-tour__sky" aria-hidden="true"></div>
<section class="section tour tour--top">
<div class="container">
<div class="hero-tour__block">
<p class="hero-tour__kicker"><?php echo esc_html__( 'Welcome to', 'viator-collector' ); ?></p>
<div class="hero-tour__stage">
<h1 class="hero-tour__word"><?php echo esc_html( $big_word ); ?></h1>
<?php
$vc_figure_id = (int) vc_get_option( 'single_hero_figure_id', 0 );
if ( $vc_figure_id ) {
echo wp_get_attachment_image( $vc_figure_id, 'medium_large', false, array( 'class' => 'hero-tour__figure', 'loading' => 'eager', 'alt' => '' ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
?>
</div>
</div>
<nav class="crumbs tour__crumbs" aria-label="<?php echo esc_attr__( 'Breadcrumb', 'viator-collector' ); ?>">
<ol class="crumbs__list">
<?php foreach ( $crumbs as $crumb ) : ?>
<li><a href="<?php echo esc_url( $crumb[1] ); ?>"><?php echo esc_html( $crumb[0] ); ?></a></li>
<?php endforeach; ?>
<li aria-current="page"><?php the_title(); ?></li>
</ol>
</nav>
</div>
</section>
<section class="section tour">
<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">
<h2 class="tour-head__title"><?php the_title(); ?></h2>
<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></span>
<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>
@@ -109,18 +123,6 @@ while ( have_posts() ) :
</section>
<?php endif; ?>
<?php if ( $primary ) : ?>
<section class="tour__block">
<h3 class="tour__ct"><?php echo esc_html__( 'Gallery', 'viator-collector' ); ?></h3>
<div class="gallery">
<img class="gallery__hero" src="<?php echo esc_url( $primary ); ?>" alt="<?php echo esc_attr( get_the_title() ); ?>" loading="lazy">
<?php foreach ( $more as $url ) : ?>
<img src="<?php echo esc_url( $url ); ?>" alt="<?php echo esc_attr( get_the_title() ); ?>" loading="lazy">
<?php endforeach; ?>
</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() ) ) );
@@ -240,7 +242,7 @@ while ( have_posts() ) :
<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( $booking_url ? $booking_url : '#' ); ?>"<?php echo $booking_url ? ' target="_blank" rel="nofollow noopener"' : ''; ?>><?php echo esc_html__( 'Check Availability', 'viator-collector' ); ?></a>
<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">
@@ -262,6 +264,14 @@ while ( have_posts() ) :
</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;