__( 'Set out on the journey.', 'viator-collector' ), 'eyebrow' => __( 'Tour & Travel', 'viator-collector' ), 'image' => '', 'image_id' => '', // WordPress media attachment ID (responsive). ), $atts, self::HERO ); // Prefer a media-library image (responsive srcset); fall back to a URL, // the plugin's Hero image setting, then the bundled image. $img = ''; if ( '' !== $atts['image_id'] ) { $img = absint( $atts['image_id'] ); } elseif ( '' !== $atts['image'] ) { $img = $atts['image']; } elseif ( vc_hero_image_url() ) { $img = vc_hero_image_url(); } else { $img = vc_asset_url( 'img/hero.png' ); } $archive = get_post_type_archive_link( 'viator_tour' ); $archive = $archive ? $archive : home_url( '/' ); $dests = get_terms( array( 'taxonomy' => 'viator_destination', 'hide_empty' => true, 'number' => 12, 'orderby' => 'count', 'order' => 'DESC' ) ); $dests = is_wp_error( $dests ) ? array() : $dests; ob_start(); ?>

with cards and then calls close_rail(). */ private static function open_rail( $title, $lead, $view_all_url, $view_all_text ) { $id = 'vcRail' . ( ++self::$rail_seq ); ob_start(); ?>

array() ) ); ?>

    $id, 'head' => ob_get_clean() ); } private static function close_rail() { return '
'; } /** * [viator_top_tours limit="8" title="Most Liked Tours"] */ public static function top_tours( $atts ) { $atts = shortcode_atts( array( 'ids' => '', 'codes' => '', 'destination' => '', 'tag' => '', 'limit' => 8, 'orderby' => 'rating', 'title' => __( 'Most Liked
Tours', 'viator-collector' ), 'lead' => __( 'Hand-picked tours travellers loved most.', 'viator-collector' ), ), $atts, self::TOP_TOURS ); return self::tour_rail( $atts ); } /** * [viator_top_attractions limit="8"] */ public static function top_attractions( $atts ) { $atts = shortcode_atts( array( 'ids' => '', 'codes' => '', 'destination' => '', 'tag' => '', 'limit' => 8, 'orderby' => 'rating', 'title' => __( 'Top
Attractions', 'viator-collector' ), 'lead' => __( 'The must-see experiences everyone books.', 'viator-collector' ), ), $atts, self::TOP_ATTRS ); return self::tour_rail( $atts ); } /** * [viator_browse_activities limit="8"] */ public static function browse_activities( $atts ) { $atts = shortcode_atts( array( 'ids' => '', 'codes' => '', 'destination' => '', 'tag' => '', 'limit' => 8, 'orderby' => 'rating', 'title' => __( 'Browse By
Activity', 'viator-collector' ), 'lead' => __( 'Find your style — from city sights to river cruises.', 'viator-collector' ), ), $atts, self::ACTIVITIES ); return self::tour_rail( $atts ); } /** * Shared: a rail of tour cards from a curated/filtered query. */ private static function tour_rail( array $atts ) { $query = self::tour_query( $atts ); if ( ! $query->have_posts() ) { return ''; } $archive = get_post_type_archive_link( 'viator_tour' ); $rail = self::open_rail( $atts['title'], $atts['lead'], $archive, __( 'See all', 'viator-collector' ) ); ob_start(); echo $rail['head']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped while ( $query->have_posts() ) { $query->the_post(); echo vc_get_template( 'card-tour.php', array( 'post_id' => get_the_ID(), 'variant' => 'rail' ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } echo self::close_rail(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped wp_reset_postdata(); return ob_get_clean(); } /** * [viator_top_destinations limit="8" title="Where you wanna go?"] * * A rail of destination-term cards; each links to the term archive. */ public static function top_destinations( $atts ) { $atts = shortcode_atts( array( 'ids' => '', 'slugs' => '', 'limit' => 8, 'orderby' => 'count', 'title' => __( 'Where you
wanna go?', 'viator-collector' ), 'lead' => __( 'Popular destinations our travellers explore.', 'viator-collector' ), 'link_text' => __( 'View tours', 'viator-collector' ), ), $atts, self::TOP_DESTS ); $args = array( 'taxonomy' => 'viator_destination', 'hide_empty' => true ); if ( '' !== $atts['ids'] ) { $args['include'] = array_filter( array_map( 'absint', explode( ',', $atts['ids'] ) ) ); $args['orderby'] = 'include'; } elseif ( '' !== $atts['slugs'] ) { $args['slug'] = array_filter( array_map( 'sanitize_title', explode( ',', $atts['slugs'] ) ) ); $args['orderby'] = 'name'; } else { $args['number'] = max( 1, absint( $atts['limit'] ) ); $args['orderby'] = ( 'name' === $atts['orderby'] ) ? 'name' : 'count'; $args['order'] = ( 'name' === $atts['orderby'] ) ? 'ASC' : 'DESC'; } $terms = get_terms( $args ); if ( is_wp_error( $terms ) || empty( $terms ) ) { return ''; } $archive = get_post_type_archive_link( 'viator_tour' ); $rail = self::open_rail( $atts['title'], $atts['lead'], $archive, __( 'See all', 'viator-collector' ) ); ob_start(); echo $rail['head']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped foreach ( $terms as $term ) { $link = get_term_link( $term ); if ( is_wp_error( $link ) ) { continue; } $image = vc_term_image_url( $term->term_id, 'viator_destination' ); ?>
  • <?php echo esc_attr( $term->name ); ?>

    name ); ?>

    count ) ); ?>
  • __( "FAQ's", 'viator-collector' ), 'lead' => __( 'Everything you need to know before you book.', 'viator-collector' ), 'items' => '', ), $atts, self::FAQ ); $items = array(); if ( '' !== $atts['items'] ) { foreach ( explode( ';;', $atts['items'] ) as $pair ) { $parts = array_map( 'trim', explode( '|', $pair, 2 ) ); if ( '' !== $parts[0] && isset( $parts[1] ) ) { $items[] = array( 'q' => $parts[0], 'a' => $parts[1] ); } } } if ( empty( $items ) ) { $items = apply_filters( 'vc_faq_items', array( array( 'q' => __( 'Is the booking process fully online?', 'viator-collector' ), 'a' => __( 'Yes — you can browse, choose a date and complete your booking entirely online in a few minutes.', 'viator-collector' ) ), array( 'q' => __( 'How can I book a tour?', 'viator-collector' ), 'a' => __( 'Pick a tour, choose your date and number of seats, then confirm. You will receive your tickets by email.', 'viator-collector' ) ), array( 'q' => __( 'Can I cancel or change my booking?', 'viator-collector' ), 'a' => __( 'Most tours offer free cancellation up to 24 hours before the start time. Check each tour for details.', 'viator-collector' ) ), array( 'q' => __( 'Do I need to print my ticket?', 'viator-collector' ), 'a' => __( 'No — a mobile ticket is accepted for the vast majority of tours.', 'viator-collector' ) ), ) ); } if ( empty( $items ) ) { return ''; } ob_start(); ?>

    $item ) : ?>

    __( 'Your Comments', 'viator-collector' ), 'lead' => __( 'What travellers say about their tours.', 'viator-collector' ), ), $atts, self::REVIEWS ); $a1 = vc_asset_url( 'img/ellipse-17.png' ); $a2 = vc_asset_url( 'img/ellipse-17-2.png' ); $items = apply_filters( 'vc_testimonials', array( array( 'name' => 'Malika Favre', 'role' => __( 'Paris Tour', 'viator-collector' ), 'text' => __( 'An unforgettable day — the open-top ride and the guide made the whole city feel new. Booking was effortless.', 'viator-collector' ), 'avatar' => $a1, 'size' => 'lg' ), array( 'name' => 'James Carter', 'role' => __( 'London Tour', 'viator-collector' ), 'text' => __( 'Great value and flexible timing. We hopped on and off all day and saw everything on our list.', 'viator-collector' ), 'avatar' => $a2, 'size' => '' ), array( 'name' => 'Sofia Rossi', 'role' => __( 'Sydney Tour', 'viator-collector' ), 'text' => __( 'Smooth from start to finish. Highly recommended.', 'viator-collector' ), 'avatar' => $a1, 'size' => 'sm' ), array( 'name' => 'Liam Nguyen', 'role' => __( 'San Diego Tour', 'viator-collector' ), 'text' => __( 'Friendly crew, comfortable seats and the best views of the coast.', 'viator-collector' ), 'avatar' => $a2, 'size' => '' ), ) ); if ( empty( $items ) ) { return ''; } ob_start(); ?>

    'viator_tour', 'post_status' => 'publish', 'posts_per_page' => max( 1, absint( $atts['limit'] ) ), 'no_found_rows' => true, 'ignore_sticky_posts' => true, ); if ( '' !== $atts['ids'] ) { $args['post__in'] = array_filter( array_map( 'absint', explode( ',', $atts['ids'] ) ) ); $args['orderby'] = 'post__in'; } elseif ( '' !== $atts['codes'] ) { $args['meta_query'] = array( 'vc_product_codes' => array( 'key' => '_vc_viator_product_code', 'value' => array_filter( array_map( 'sanitize_text_field', explode( ',', $atts['codes'] ) ) ), 'compare' => 'IN', ), ); } $tax_query = array(); if ( '' !== $atts['destination'] ) { $tax_query[] = array( 'taxonomy' => 'viator_destination', 'field' => 'slug', 'terms' => array_map( 'sanitize_title', explode( ',', $atts['destination'] ) ) ); } if ( '' !== $atts['tag'] ) { $tax_query[] = array( 'taxonomy' => 'viator_tag', 'field' => 'slug', 'terms' => array_map( 'sanitize_title', explode( ',', $atts['tag'] ) ) ); } if ( $tax_query ) { $args['tax_query'] = $tax_query; } if ( ! isset( $args['orderby'] ) ) { switch ( $atts['orderby'] ) { case 'price': $args['meta_key'] = '_vc_price_from'; $args['orderby'] = 'meta_value_num'; $args['order'] = 'ASC'; break; case 'rand': $args['orderby'] = 'rand'; break; case 'date': $args['orderby'] = 'date'; $args['order'] = 'DESC'; break; default: $args['meta_key'] = '_vc_rating'; $args['orderby'] = 'meta_value_num'; $args['order'] = 'DESC'; } } $query = new WP_Query( $args ); // Ordering by a meta key silently drops posts that lack it; fall back to // newest so the section never empties. if ( ! $query->have_posts() && isset( $args['meta_key'] ) ) { unset( $args['meta_key'] ); $args['orderby'] = 'date'; $args['order'] = 'DESC'; $query = new WP_Query( $args ); } return $query; } }