Plugin (viator-collector 1.8.1): - is_vc_page() now matches any Viator shortcode, so shortcode Pages (home) get the full-width no-sidebar canvas - load vc-design.css after Astra + inline overrides: strip Astra page-builder article padding (removes extra gap above hero), full-bleed containers, enforce design font inside .vc-app - vc_hero_img_html(): render hero from the WP media library with responsive srcset when the image is an attachment; used in listing + [viator_hero] ([viator_hero image_id=".."]) Theme (astra child): - remove the user/profile header item - working header search (expands, submits to the tours archive ?vc_q=) - load chrome.css after Astra + enforce design font inside .hoho - rebuild installable ZIP Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01J4hNL1PSHxXbu94SCenpKj
254 lines
14 KiB
PHP
254 lines
14 KiB
PHP
<?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.
|
||
*/
|
||
defined( 'ABSPATH' ) || exit;
|
||
|
||
$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_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 = ( '' !== $f['q'] || ! empty( $f['dest'] ) || ! empty( $f['cat'] ) || ! empty( $f['dur'] )
|
||
|| $f['pmin'] > 0 || $f['pmax'] < 1000 || $f['rating'] > 0
|
||
|| $f['freecancel'] || $f['bestdeal'] || $f['sellout'] );
|
||
|
||
// 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 ) {
|
||
$args = $vc_base_args;
|
||
if ( $n > 1 ) {
|
||
$args['paged'] = $n;
|
||
}
|
||
return empty( $args ) ? $vc_action : add_query_arg( $args, $vc_action );
|
||
};
|
||
|
||
$vc_hero_img = vc_hero_image_url();
|
||
if ( ! $vc_hero_img ) {
|
||
$vc_hero_img = vc_asset_url( 'img/hero2.png' );
|
||
}
|
||
?>
|
||
<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">
|
||
<p class="pill hero__eyebrow"><?php echo esc_html__( 'Tour & Travel', 'viator-collector' ); ?></p>
|
||
<h1 class="hero__title"><?php echo esc_html( $vc_title ); ?></h1>
|
||
|
||
<form class="filters" id="vc-filter-form" method="get" action="<?php echo esc_url( $vc_action ); ?>" 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 – $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>
|
||
|
||
<section class="section" id="tour-list">
|
||
<div class="container">
|
||
<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–%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">…</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>
|