Files
hoponhopofftickets/viator-collector/templates/listing.php
Amir Bagheri 2ded000a27 feat: city guide on city pages (tagline, facts, About, seasons)
Editable per city under Viator tours > Destinations. Shown above the
tours on page 1 of an unfiltered listing only; filtering and paging jump
to the results. Text recovered from the six old /tour-destination/ pages
is imported once into empty guides.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 09:58:54 +03:30

271 lines
15 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* Tours listing UI — "Hop On Hop Off" design (post-type archive, taxonomy
* archives, and the [viator_listing] shortcode).
*
* Filtering stays server-side: every control lives inside #vc-filter-form and
* submits via GET. VC_Display::filter_archive_query() reads the same state.
*
* Args: $vc_title, $vc_subtitle, $vc_query, $vc_form_action, $vc_after_hero
* (HTML printed between the hero and the results, e.g. the region city grid),
* $vc_tagline (line under the title), $vc_list_heading (heading above the
* results) and $vc_jump (true: filtering and paging land on the results).
*/
defined( 'ABSPATH' ) || exit;
$vc_after_hero = isset( $vc_after_hero ) ? (string) $vc_after_hero : '';
$vc_tagline = isset( $vc_tagline ) ? (string) $vc_tagline : '';
$vc_list_heading = isset( $vc_list_heading ) ? (string) $vc_list_heading : '';
$vc_anchor = empty( $vc_jump ) ? '' : '#tour-list';
$f = VC_Display::get_filter_state();
$vc_title = isset( $vc_title ) ? $vc_title : __( 'Explore Tours', 'viator-collector' );
$vc_subtitle = isset( $vc_subtitle ) ? $vc_subtitle : '';
$vc_title = apply_filters( 'vc_listing_title', $vc_title );
$vc_subtitle = apply_filters( 'vc_listing_subtitle', $vc_subtitle );
$vc_query = ( isset( $vc_query ) && $vc_query instanceof WP_Query ) ? $vc_query : $GLOBALS['wp_query'];
$vc_is_shortcode = ( $vc_query !== $GLOBALS['wp_query'] );
$vc_term = is_tax() ? get_queried_object() : null;
if ( isset( $vc_form_action ) && $vc_form_action ) {
$vc_action = $vc_form_action;
} else {
$vc_action = $vc_term ? get_term_link( $vc_term ) : get_post_type_archive_link( 'viator_tour' );
}
if ( is_wp_error( $vc_action ) || ! $vc_action ) {
$vc_action = home_url( '/' );
}
$vc_destinations = get_terms( array( 'taxonomy' => 'viator_destination', 'hide_empty' => true, 'number' => 20, 'orderby' => 'count', 'order' => 'DESC' ) );
$vc_categories = get_terms( array( 'taxonomy' => 'viator_tag', 'hide_empty' => true, 'number' => 20, 'orderby' => 'count', 'order' => 'DESC' ) );
$vc_destinations = is_wp_error( $vc_destinations ) ? array() : $vc_destinations;
$vc_categories = is_wp_error( $vc_categories ) ? array() : VC_Tags::public_terms( $vc_categories );
$vc_total = (int) $vc_query->found_posts;
$vc_per = 12;
$vc_paged = max( 1, (int) $vc_query->get( 'paged' ), (int) get_query_var( 'paged' ), isset( $_GET['paged'] ) ? absint( $_GET['paged'] ) : 1 ); // phpcs:ignore WordPress.Security.NonceVerification
$vc_first = $vc_total ? ( ( $vc_paged - 1 ) * $vc_per ) + 1 : 0;
$vc_last = min( $vc_total, $vc_paged * $vc_per );
$vc_max = (int) $vc_query->max_num_pages;
$vc_price_val = ( $f['pmin'] > 0 || $f['pmax'] < 1000 ) ? $f['pmin'] . '-' . $f['pmax'] : '';
$vc_dur_labels = array(
'half' => __( 'Half Day (≤5h)', 'viator-collector' ),
'full' => __( 'Full Day (5–24h)', 'viator-collector' ),
'multi' => __( 'Multi-Day', 'viator-collector' ),
);
// Whether any filter is active (drives the "Clear filters" control).
$vc_active = VC_Display::filters_active( $f );
// Current filters as a base URL, so pagination links keep the query string.
$vc_base_args = array();
if ( '' !== $f['q'] ) { $vc_base_args['vc_q'] = $f['q']; }
if ( ! empty( $f['dest'] ) ) { $vc_base_args['dest'] = $f['dest']; }
if ( ! empty( $f['cat'] ) ) { $vc_base_args['cat'] = $f['cat']; }
if ( ! empty( $f['dur'] ) ) { $vc_base_args['dur'] = $f['dur']; }
if ( '' !== $vc_price_val ) { $vc_base_args['price'] = $vc_price_val; }
if ( $f['rating'] > 0 ) { $vc_base_args['rating'] = $f['rating']; }
if ( $f['freecancel'] ) { $vc_base_args['freecancel'] = 1; }
if ( $f['bestdeal'] ) { $vc_base_args['bestdeal'] = 1; }
if ( $f['sellout'] ) { $vc_base_args['sellout'] = 1; }
if ( 'newest' !== $f['sort'] ) { $vc_base_args['sort'] = $f['sort']; }
$vc_page_url = function ( $n ) use ( $vc_action, $vc_base_args, $vc_anchor ) {
$args = $vc_base_args;
if ( $n > 1 ) {
$args['paged'] = $n;
}
return ( empty( $args ) ? $vc_action : add_query_arg( $args, $vc_action ) ) . $vc_anchor;
};
$vc_hero_img = vc_hero_image_source();
?>
<section class="hero hero--archive">
<div class="hero__media">
<?php echo vc_hero_img_html( $vc_hero_img, 'hero__img', true ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<div class="hero__scrim" aria-hidden="true"></div>
</div>
<div class="container hero__content">
<?php
// Short current-page label: "Tours" on the archive, the page's own
// title on a page hosting [viator_listing], else the heading.
$vc_crumb_current = ! empty( $vc_crumb_label ) ? $vc_crumb_label : ( is_page() ? get_the_title() : $vc_title );
echo vc_breadcrumb_html( vc_breadcrumb_trail(), $vc_crumb_current ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
?>
<h1 class="hero__title"><?php echo esc_html( $vc_title ); ?></h1>
<?php if ( '' !== $vc_tagline ) : ?>
<p class="hero__tagline"><?php echo esc_html( $vc_tagline ); ?></p>
<?php endif; ?>
<form class="filters" id="vc-filter-form" method="get" action="<?php echo esc_url( $vc_action . $vc_anchor ); ?>" novalidate>
<div class="filters__row">
<div class="fsearch">
<?php echo vc_icon( 'search', 'fsearch__icon' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<input class="fsearch__input" name="vc_q" type="search" value="<?php echo esc_attr( $f['q'] ); ?>" placeholder="<?php echo esc_attr__( 'Search tours, cities …', 'viator-collector' ); ?>" aria-label="<?php echo esc_attr__( 'Search tours and cities', 'viator-collector' ); ?>">
</div>
<div class="fsel">
<?php echo vc_icon( 'location', 'fsel__icon' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<select class="fsel__input" name="dest[]" aria-label="<?php echo esc_attr__( 'Destination', 'viator-collector' ); ?>">
<option value=""><?php echo esc_html__( 'Destination', 'viator-collector' ); ?></option>
<?php foreach ( $vc_destinations as $t ) : ?>
<option value="<?php echo esc_attr( $t->slug ); ?>" <?php selected( in_array( $t->slug, $f['dest'], true ) ); ?>><?php echo esc_html( $t->name ); ?></option>
<?php endforeach; ?>
</select>
<?php echo vc_icon( 'chevron', 'fsel__chevron' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</div>
<div class="fsel">
<?php echo vc_icon( 'grid', 'fsel__icon' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<select class="fsel__input" name="cat[]" aria-label="<?php echo esc_attr__( 'Category', 'viator-collector' ); ?>">
<option value=""><?php echo esc_html__( 'Categorize', 'viator-collector' ); ?></option>
<?php foreach ( $vc_categories as $t ) : ?>
<option value="<?php echo esc_attr( $t->slug ); ?>" <?php selected( in_array( $t->slug, $f['cat'], true ) ); ?>><?php echo esc_html( $t->name ); ?></option>
<?php endforeach; ?>
</select>
<?php echo vc_icon( 'chevron', 'fsel__chevron' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</div>
<div class="fsel">
<?php echo vc_icon( 'price', 'fsel__icon' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<select class="fsel__input" name="price" aria-label="<?php echo esc_attr__( 'Price range', 'viator-collector' ); ?>">
<option value=""><?php echo esc_html__( 'Price Range', 'viator-collector' ); ?></option>
<option value="0-50" <?php selected( $vc_price_val, '0-50' ); ?>><?php echo esc_html__( 'Up to $50', 'viator-collector' ); ?></option>
<option value="50-150" <?php selected( $vc_price_val, '50-150' ); ?>>$50 &ndash; $150</option>
<option value="150-1000" <?php selected( $vc_price_val, '150-1000' ); ?>><?php echo esc_html__( '$150 and up', 'viator-collector' ); ?></option>
</select>
<?php echo vc_icon( 'chevron', 'fsel__chevron' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</div>
<div class="fsel">
<?php echo vc_icon( 'duration', 'fsel__icon' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
<select class="fsel__input" name="dur[]" aria-label="<?php echo esc_attr__( 'Duration', 'viator-collector' ); ?>">
<option value=""><?php echo esc_html__( 'Duration', 'viator-collector' ); ?></option>
<?php foreach ( $vc_dur_labels as $key => $label ) : ?>
<option value="<?php echo esc_attr( $key ); ?>" <?php selected( in_array( $key, $f['dur'], true ) ); ?>><?php echo esc_html( $label ); ?></option>
<?php endforeach; ?>
</select>
<?php echo vc_icon( 'chevron', 'fsel__chevron' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</div>
<button class="btn btn--dark filters__apply" type="submit"><?php echo esc_html__( 'Apply Filters', 'viator-collector' ); ?></button>
</div>
<div class="filters__bar">
<div class="filters__checks">
<label class="check">
<input class="check__input vc-autosubmit" type="checkbox" name="freecancel" value="1" <?php checked( $f['freecancel'], 1 ); ?>>
<span class="check__box" aria-hidden="true"><?php echo vc_icon( 'check', 'icon--12' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span>
<span class="check__text"><?php echo esc_html__( 'Free Cancellation', 'viator-collector' ); ?></span>
</label>
<label class="check">
<input class="check__input vc-autosubmit" type="checkbox" name="bestdeal" value="1" <?php checked( $f['bestdeal'], 1 ); ?>>
<span class="check__box" aria-hidden="true"><?php echo vc_icon( 'check', 'icon--12' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span>
<span class="check__text"><?php echo esc_html__( 'Best Deal', 'viator-collector' ); ?></span>
</label>
<label class="check">
<input class="check__input vc-autosubmit" type="checkbox" name="sellout" value="1" <?php checked( $f['sellout'], 1 ); ?>>
<span class="check__box" aria-hidden="true"><?php echo vc_icon( 'check', 'icon--12' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></span>
<span class="check__text"><?php echo esc_html__( 'Likely to Sell Out', 'viator-collector' ); ?></span>
</label>
</div>
<div class="filters__sort">
<span class="filters__sort-label"><?php echo vc_icon( 'sort' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?><label for="f-sort"><?php echo esc_html__( 'Sort by', 'viator-collector' ); ?></label></span>
<div class="fsel fsel--bare">
<select class="fsel__input vc-autosubmit" id="f-sort" name="sort">
<option value="newest" <?php selected( $f['sort'], 'newest' ); ?>><?php echo esc_html__( 'Newest', 'viator-collector' ); ?></option>
<option value="price_asc" <?php selected( $f['sort'], 'price_asc' ); ?>><?php echo esc_html__( 'Price: low to high', 'viator-collector' ); ?></option>
<option value="price_desc" <?php selected( $f['sort'], 'price_desc' ); ?>><?php echo esc_html__( 'Price: high to low', 'viator-collector' ); ?></option>
<option value="rating" <?php selected( $f['sort'], 'rating' ); ?>><?php echo esc_html__( 'Top rated', 'viator-collector' ); ?></option>
</select>
<?php echo vc_icon( 'chevron', 'fsel__chevron' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</div>
</div>
</div>
</form>
</div>
<?php echo vc_ring_badge( 'SCROLL DOWN · SCROLL DOWN · ', '#tour-list', 'scroll' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
</section>
<?php echo $vc_after_hero; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- built by the calling template. ?>
<section class="section" id="tour-list">
<div class="container">
<?php if ( '' !== $vc_list_heading ) : ?>
<p class="pill cty-eyebrow"><?php echo esc_html__( 'Tours', 'viator-collector' ); ?></p>
<h2 class="cty-h2 cty-tours-h"><?php echo esc_html( $vc_list_heading ); ?></h2>
<?php endif; ?>
<div class="results">
<p class="results__count" role="status">
<?php if ( $vc_total ) : ?>
<?php
printf(
/* translators: 1: first result, 2: last result, 3: total. */
wp_kses( __( 'Showing <b>%1$d&ndash;%2$d</b> of <b>%3$d</b> tours', 'viator-collector' ), array( 'b' => array() ) ),
(int) $vc_first,
(int) $vc_last,
(int) $vc_total
);
?>
<?php else : ?>
<?php echo esc_html__( 'No tours found', 'viator-collector' ); ?>
<?php endif; ?>
</p>
<?php if ( $vc_active ) : ?>
<a class="btn btn--ghost results__reset" href="<?php echo esc_url( $vc_action ); ?>"><?php echo esc_html__( 'Clear filters', 'viator-collector' ); ?></a>
<?php endif; ?>
</div>
<?php if ( $vc_query->have_posts() ) : ?>
<ul class="tgrid" id="tourGrid">
<?php
while ( $vc_query->have_posts() ) :
$vc_query->the_post();
echo vc_get_template( 'card-tour.php', array( 'post_id' => get_the_ID(), 'variant' => 'wide' ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
endwhile;
wp_reset_postdata();
?>
</ul>
<?php if ( $vc_max > 1 ) : ?>
<nav class="pager" aria-label="<?php echo esc_attr__( 'Tour list pages', 'viator-collector' ); ?>">
<?php if ( $vc_paged > 1 ) : ?>
<a class="pager__btn pager__btn--icon" href="<?php echo esc_url( $vc_page_url( $vc_paged - 1 ) ); ?>" aria-label="<?php echo esc_attr__( 'Previous page', 'viator-collector' ); ?>"><?php echo vc_icon( 'arrow-left' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></a>
<?php endif; ?>
<span class="pager__pages">
<?php
$vc_range = vc_pager_range( $vc_paged, $vc_max );
foreach ( $vc_range as $n ) :
if ( null === $n ) :
?>
<span class="pager__gap" aria-hidden="true">&hellip;</span>
<?php
elseif ( $n === $vc_paged ) :
?>
<span class="pager__num is-current" aria-current="page"><?php echo esc_html( $n ); ?></span>
<?php
else :
?>
<a class="pager__num" href="<?php echo esc_url( $vc_page_url( $n ) ); ?>"><?php echo esc_html( $n ); ?></a>
<?php
endif;
endforeach;
?>
</span>
<?php if ( $vc_paged < $vc_max ) : ?>
<a class="pager__btn" href="<?php echo esc_url( $vc_page_url( $vc_paged + 1 ) ); ?>"><span><?php echo esc_html__( 'Next', 'viator-collector' ); ?></span><?php echo vc_icon( 'arrow-right' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?></a>
<?php endif; ?>
</nav>
<?php endif; ?>
<?php else : ?>
<p class="tgrid__empty"><?php echo esc_html__( 'No tours match these filters. Try widening your search or', 'viator-collector' ); ?> <a class="linkbtn" href="<?php echo esc_url( $vc_action ); ?>"><?php echo esc_html__( 'clear the filters', 'viator-collector' ); ?></a>.</p>
<?php endif; ?>
</div>
</section>