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:
Binary file not shown.
@@ -23,10 +23,10 @@ add_action( 'wp_enqueue_scripts', function () {
|
||||
if ( wp_style_is( 'astra-theme-css', 'registered' ) ) {
|
||||
$deps[] = 'astra-theme-css';
|
||||
}
|
||||
wp_enqueue_style( 'hoho-chrome', get_stylesheet_directory_uri() . '/assets/chrome.css', $deps, '1.0.12' );
|
||||
wp_enqueue_style( 'hoho-chrome', get_stylesheet_directory_uri() . '/assets/chrome.css', $deps, '1.0.13' );
|
||||
|
||||
// Header interactions: burger, submenu, sticky, back-to-top, search toggle.
|
||||
wp_enqueue_script( 'hoho-chrome', get_stylesheet_directory_uri() . '/assets/chrome.js', array(), '1.0.12', true );
|
||||
wp_enqueue_script( 'hoho-chrome', get_stylesheet_directory_uri() . '/assets/chrome.js', array(), '1.0.13', true );
|
||||
|
||||
wp_add_inline_style(
|
||||
'hoho-chrome',
|
||||
|
||||
@@ -12,8 +12,51 @@ $hoho_about = apply_filters(
|
||||
|
||||
$hoho_socials = hoho_socials();
|
||||
|
||||
$hoho_footer_col = function ( $location ) {
|
||||
/**
|
||||
* Default links for a footer column that has no menu assigned, so the footer
|
||||
* is never three empty headings. Pages are linked only if they exist.
|
||||
*/
|
||||
$hoho_footer_defaults = function ( $location ) {
|
||||
$page = function ( $slugs, $label ) {
|
||||
foreach ( (array) $slugs as $slug ) {
|
||||
$p = get_page_by_path( $slug );
|
||||
if ( $p && 'publish' === $p->post_status ) {
|
||||
return array( $label, get_permalink( $p ) );
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
$links = array();
|
||||
if ( 'hoho_quick' === $location ) {
|
||||
$links[] = array( __( 'Home', 'hoponhopoff' ), home_url( '/' ) );
|
||||
if ( get_post_type_archive_link( 'viator_tour' ) ) {
|
||||
$links[] = array( __( 'All Tours', 'hoponhopoff' ), get_post_type_archive_link( 'viator_tour' ) );
|
||||
}
|
||||
$links[] = get_option( 'page_for_posts' ) ? array( __( 'Blog', 'hoponhopoff' ), get_permalink( get_option( 'page_for_posts' ) ) ) : $page( array( 'blogs', 'blog' ), __( 'Blog', 'hoponhopoff' ) );
|
||||
} elseif ( 'hoho_tours' === $location && class_exists( 'VC_Structure' ) ) {
|
||||
// The first cities from Settings → Viator Structure.
|
||||
foreach ( array_slice( VC_Structure::cities( VC_Structure::get() ), 0, 6 ) as $city ) {
|
||||
$links[] = array( $city['name'], home_url( '/' . $city['slug'] . '/' ) );
|
||||
}
|
||||
} elseif ( 'hoho_account' === $location ) {
|
||||
$links[] = $page( array( 'contact-us', 'contact' ), __( 'Contact Us', 'hoponhopoff' ) );
|
||||
$links[] = $page( array( 'about-us', 'about' ), __( 'About Us', 'hoponhopoff' ) );
|
||||
$links[] = $page( array( 'faq', 'faqs' ), __( 'FAQ', 'hoponhopoff' ) );
|
||||
}
|
||||
$links = array_filter( $links );
|
||||
if ( ! $links ) {
|
||||
return;
|
||||
}
|
||||
echo '<ul>';
|
||||
foreach ( $links as $link ) {
|
||||
echo '<li><a href="' . esc_url( $link[1] ) . '">' . esc_html( $link[0] ) . '</a></li>';
|
||||
}
|
||||
echo '</ul>';
|
||||
};
|
||||
|
||||
$hoho_footer_col = function ( $location ) use ( $hoho_footer_defaults ) {
|
||||
if ( ! has_nav_menu( $location ) ) {
|
||||
$hoho_footer_defaults( $location );
|
||||
return;
|
||||
}
|
||||
wp_nav_menu(
|
||||
|
||||
@@ -4,7 +4,7 @@ Theme URI: https://git.kaamyab.dev/vargha/TourWebsite
|
||||
Description: Astra child theme that replaces the Astra header and footer with the "Hop On Hop Off" design. Pairs with the Viator Collector plugin.
|
||||
Author: Amir Bagheri
|
||||
Template: astra
|
||||
Version: 1.0.12
|
||||
Version: 1.0.13
|
||||
Text Domain: hoponhopoff
|
||||
*/
|
||||
|
||||
|
||||
Binary file not shown.
175
viator-collector/assets/css/vc-tour.css
Normal file
175
viator-collector/assets/css/vc-tour.css
Normal file
@@ -0,0 +1,175 @@
|
||||
/* Single tour — top section (photos + booking card first) and the mobile
|
||||
booking bar. Hand-written, loaded after vc-design.css on tour pages only. */
|
||||
|
||||
/* No hero any more: the header turns solid (chrome.js) and the page starts
|
||||
with the breadcrumb. */
|
||||
.vc-app.vc-app .tour--top {
|
||||
padding-block-start: var(--sp-5);
|
||||
}
|
||||
|
||||
.vc-app.vc-app .tour__crumbs {
|
||||
margin-block-end: var(--sp-3);
|
||||
color: var(--c-ink);
|
||||
}
|
||||
|
||||
.vc-app.vc-app .tour__crumbs li[aria-current] {
|
||||
overflow: hidden;
|
||||
max-width: 28ch;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* ---------- gallery: one large photo + two, the rest in the lightbox ---------- */
|
||||
.vc-app.vc-app .tgal {
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1fr;
|
||||
grid-template-rows: repeat(2, clamp(9rem, 6rem + 6vw, 12.5rem));
|
||||
gap: var(--sp-1);
|
||||
}
|
||||
|
||||
.vc-app.vc-app .tgal img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
aspect-ratio: auto;
|
||||
border-radius: var(--r-card);
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.vc-app.vc-app .tgal .tgal__main {
|
||||
grid-row: span 2;
|
||||
}
|
||||
|
||||
.vc-app.vc-app .tgal .tgal__extra {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.vc-app.vc-app .tgal--few {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.vc-app.vc-app .tgal--few .tgal__thumb {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.vc-app.vc-app .tgal__all {
|
||||
position: absolute;
|
||||
right: var(--sp-2);
|
||||
bottom: var(--sp-2);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.375rem;
|
||||
padding: 0.5rem 0.875rem;
|
||||
border: 0;
|
||||
border-radius: var(--r-pill);
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
color: var(--c-ink);
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 700;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.18);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* ---------- title row under the photos ---------- */
|
||||
.vc-app.vc-app .tour--top .tour-head__title {
|
||||
font-size: clamp(1.5rem, 1.1rem + 1.4vw, 2.25rem);
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.vc-app.vc-app .tmeta__reviews {
|
||||
margin-inline-start: 0.25rem;
|
||||
font-weight: 500;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
/* ---------- mobile booking bar ---------- */
|
||||
.vc-app.vc-app .tbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (max-width: 68.75rem) {
|
||||
/* The bar does the booking now: the full card moves below the content. */
|
||||
.vc-app.vc-app .tour--top .tour__aside {
|
||||
order: 1;
|
||||
}
|
||||
|
||||
.vc-app.vc-app .tbar {
|
||||
position: fixed;
|
||||
inset: auto 0 0 0;
|
||||
z-index: 50;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--sp-3);
|
||||
padding: 0.75rem var(--gutter) calc(0.75rem + env(safe-area-inset-bottom));
|
||||
background: var(--c-white);
|
||||
box-shadow: 0 -6px 24px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.vc-app.vc-app .tbar__price {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0;
|
||||
line-height: 1.15;
|
||||
}
|
||||
|
||||
.vc-app.vc-app .tbar__price span {
|
||||
font-size: 0.75rem;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.vc-app.vc-app .tbar__price b {
|
||||
font-size: 1.375rem;
|
||||
}
|
||||
|
||||
.vc-app.vc-app .tbar__cta {
|
||||
background: var(--c-accent);
|
||||
color: var(--c-black);
|
||||
}
|
||||
|
||||
/* room for the bar so it never covers the footer */
|
||||
.vc-app.vc-single {
|
||||
padding-block-end: 5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 48rem) {
|
||||
/* Swipe through every photo; tap one (or the button) for the lightbox. */
|
||||
.vc-app.vc-app .tgal {
|
||||
display: flex;
|
||||
overflow-x: auto;
|
||||
scroll-snap-type: x mandatory;
|
||||
scrollbar-width: none;
|
||||
margin-inline: calc(var(--gutter) * -1);
|
||||
padding-inline: var(--gutter);
|
||||
}
|
||||
|
||||
.vc-app.vc-app .tgal::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.vc-app.vc-app .tgal img,
|
||||
.vc-app.vc-app .tgal .tgal__extra,
|
||||
.vc-app.vc-app .tgal--few .tgal__thumb {
|
||||
display: block;
|
||||
flex: 0 0 88%;
|
||||
aspect-ratio: 4 / 3;
|
||||
scroll-snap-align: center;
|
||||
}
|
||||
|
||||
/* every photo is already in the swipe row */
|
||||
.vc-app.vc-app .tgal__all {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* the tour name is the page title right below — keep the trail short */
|
||||
.vc-app.vc-app .tour__crumbs li[aria-current] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
/* Compact 2 × 2 facts instead of four stacked full-width cards. */
|
||||
.vc-app.vc-app .tour--top .facts {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
@@ -138,6 +138,9 @@
|
||||
box.querySelector(".vc-lightbox__next").addEventListener("click", function () { show(idx + 1); });
|
||||
box.querySelector(".vc-lightbox__close").addEventListener("click", close);
|
||||
box.addEventListener("click", function (e) { if (e.target === box) close(); });
|
||||
/* tour page top gallery: "All N photos" opens at the first photo not on show */
|
||||
var allBtn = app.querySelector(".tgal__all");
|
||||
if (allBtn) allBtn.addEventListener("click", function () { open(Math.min(3, imgs.length - 1)); });
|
||||
document.addEventListener("keydown", function (e) {
|
||||
if (!box.classList.contains("is-open")) return;
|
||||
if (e.key === "Escape") close();
|
||||
|
||||
@@ -34,7 +34,7 @@ class VC_Display {
|
||||
// equal-specificity ties. Loading last makes our design win those ties.
|
||||
add_action( 'wp_enqueue_scripts', array( __CLASS__, 'enqueue_assets' ), 100 );
|
||||
add_action( 'template_redirect', array( __CLASS__, 'maybe_enrich_single' ) );
|
||||
add_action( 'pre_get_posts', array( __CLASS__, 'filter_archive_query' ) );
|
||||
add_action( 'pre_get_posts', array( __CLASS__, 'filter_archive_query' ), 999 );
|
||||
add_filter( 'single_template', array( __CLASS__, 'single_template' ) );
|
||||
add_filter( 'archive_template', array( __CLASS__, 'archive_template' ) );
|
||||
add_filter( 'taxonomy_template', array( __CLASS__, 'taxonomy_template' ) );
|
||||
@@ -164,6 +164,10 @@ class VC_Display {
|
||||
}
|
||||
wp_enqueue_style( 'vc-design', VC_PLUGIN_URL . 'assets/css/vc-design.css', $deps, VC_VERSION );
|
||||
wp_add_inline_style( 'vc-design', self::astra_overrides() );
|
||||
if ( $is_single ) {
|
||||
// Tour page top section + mobile booking bar (after the overrides above).
|
||||
wp_enqueue_style( 'vc-tour', VC_PLUGIN_URL . 'assets/css/vc-tour.css', array( 'vc-design' ), VC_VERSION );
|
||||
}
|
||||
|
||||
// Front-end interactions: card rails, FAQ accordion, testimonials
|
||||
// marquee, date field, and auto-submit on quick filter toggles.
|
||||
@@ -318,6 +322,8 @@ class VC_Display {
|
||||
* Apply the filter state to the main tours query (archive + taxonomy pages).
|
||||
*/
|
||||
public static function filter_archive_query( $query ) {
|
||||
// Runs late (priority 999, see init) so another plugin/theme setting the
|
||||
// page size can't leave the listing at 10 while it says "1–12".
|
||||
if ( is_admin() || ! $query->is_main_query() ) {
|
||||
return;
|
||||
}
|
||||
@@ -346,7 +352,9 @@ class VC_Display {
|
||||
*/
|
||||
public static function build_query_vars( array $base_tax = array() ) {
|
||||
$f = self::get_filter_state();
|
||||
$vars = array( 'posts_per_page' => 12, 'ignore_sticky_posts' => true );
|
||||
// posts_per_archive_page too: when set (Settings or another plugin), WP_Query
|
||||
// uses it instead of posts_per_page on archives.
|
||||
$vars = array( 'posts_per_page' => 12, 'posts_per_archive_page' => 12, 'ignore_sticky_posts' => true );
|
||||
|
||||
if ( '' !== $f['q'] ) {
|
||||
$vars['s'] = $f['q'];
|
||||
@@ -741,7 +749,7 @@ class VC_Display {
|
||||
'viator_browse_activities'
|
||||
);
|
||||
|
||||
$terms = self::get_section_terms( 'viator_tag', $atts['count'], $atts['ids'] );
|
||||
$terms = VC_Tags::public_terms( self::get_section_terms( 'viator_tag', $atts['count'], $atts['ids'] ) );
|
||||
if ( empty( $terms ) ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ final class VC_Plugin {
|
||||
'includes/class-vc-cpt.php',
|
||||
'includes/class-vc-taxonomies.php',
|
||||
'includes/class-vc-structure.php',
|
||||
'includes/class-vc-tags.php',
|
||||
'includes/class-vc-api.php',
|
||||
'includes/class-vc-importer.php',
|
||||
'includes/class-vc-cron.php',
|
||||
|
||||
41
viator-collector/includes/class-vc-tags.php
Normal file
41
viator-collector/includes/class-vc-tags.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
||||
/**
|
||||
* Viator attaches some internal, trade-only tags to products (e.g. "Agent Low
|
||||
* Quality Product"). They are imported like any tag but must never be shown
|
||||
* to visitors — not as a tour's category, not as a filter option.
|
||||
*/
|
||||
class VC_Tags {
|
||||
/**
|
||||
* Patterns (case-insensitive) that mark a tag as internal. Extend with the
|
||||
* `vc_internal_tag_patterns` filter when WordPress is loaded.
|
||||
*/
|
||||
public static function patterns() {
|
||||
$patterns = array( '/\bagent\b/i', '/\blow quality\b/i', '/\bquality product\b/i' );
|
||||
return function_exists( 'apply_filters' ) ? (array) apply_filters( 'vc_internal_tag_patterns', $patterns ) : $patterns;
|
||||
}
|
||||
|
||||
public static function is_internal( $name ) {
|
||||
foreach ( self::patterns() as $pattern ) {
|
||||
if ( preg_match( $pattern, (string) $name ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop internal tags from a list of term objects (keeps order).
|
||||
*/
|
||||
public static function public_terms( $terms ) {
|
||||
return array_values(
|
||||
array_filter(
|
||||
(array) $terms,
|
||||
function ( $term ) {
|
||||
return is_object( $term ) && ! self::is_internal( $term->name );
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,7 @@ if ( is_wp_error( $vc_action ) || ! $vc_action ) {
|
||||
$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_categories = is_wp_error( $vc_categories ) ? array() : VC_Tags::public_terms( $vc_categories );
|
||||
|
||||
$vc_total = (int) $vc_query->found_posts;
|
||||
$vc_per = 12;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
?>
|
||||
<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>
|
||||
</div>
|
||||
</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;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
define( 'ABSPATH', __DIR__ . '/' );
|
||||
|
||||
require_once dirname( __DIR__ ) . '/includes/class-vc-structure.php';
|
||||
require_once dirname( __DIR__ ) . '/includes/class-vc-tags.php';
|
||||
|
||||
function vc_assert_same( $expected, $actual, $msg = '' ) {
|
||||
if ( $expected !== $actual ) {
|
||||
|
||||
28
viator-collector/tests/test-tags.php
Normal file
28
viator-collector/tests/test-tags.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/**
|
||||
* VC_Tags unit tests: Viator's internal tags never reach visitors.
|
||||
*/
|
||||
|
||||
function test_internal_tag_agent_low_quality() {
|
||||
vc_assert_same( true, VC_Tags::is_internal( 'Agent Low Quality Product' ) );
|
||||
}
|
||||
|
||||
function test_internal_tag_variants() {
|
||||
vc_assert_same( true, VC_Tags::is_internal( 'Low Quality Supplier' ) );
|
||||
vc_assert_same( true, VC_Tags::is_internal( 'agent quality product' ) );
|
||||
}
|
||||
|
||||
function test_public_tags_kept() {
|
||||
vc_assert_same( false, VC_Tags::is_internal( 'Audio Guides' ) );
|
||||
vc_assert_same( false, VC_Tags::is_internal( 'Excellent Quality' ) );
|
||||
vc_assert_same( false, VC_Tags::is_internal( 'Hop-on Hop-off Tours' ) );
|
||||
}
|
||||
|
||||
function test_public_terms_filters_objects() {
|
||||
$terms = array(
|
||||
(object) array( 'name' => 'Agent Low Quality Product' ),
|
||||
(object) array( 'name' => 'Audio Guides' ),
|
||||
(object) array( 'name' => 'Bus Tours' ),
|
||||
);
|
||||
vc_assert_same( array( 'Audio Guides', 'Bus Tours' ), array_column( VC_Tags::public_terms( $terms ), 'name' ) );
|
||||
}
|
||||
@@ -4,13 +4,13 @@ defined('ABSPATH') || exit;
|
||||
/**
|
||||
* Plugin Name: Viator Collector
|
||||
* Description: Import Viator tours using the Viator Partner API and display them with native WordPress post types, templates, and shortcodes.
|
||||
* Version: 1.9.4
|
||||
* Version: 1.9.5
|
||||
* Author: Amir Bagheri
|
||||
* License: GPL-2.0-or-later
|
||||
* Text Domain: viator-collector
|
||||
*/
|
||||
|
||||
define('VC_VERSION', '1.9.4');
|
||||
define('VC_VERSION', '1.9.5');
|
||||
define('VC_PLUGIN_FILE', __FILE__);
|
||||
define('VC_PLUGIN_DIR', plugin_dir_path(__FILE__));
|
||||
define('VC_PLUGIN_URL', plugin_dir_url(__FILE__));
|
||||
|
||||
Reference in New Issue
Block a user