diff --git a/astra-child-hoponhopoff.zip b/astra-child-hoponhopoff.zip index 64a1e9c..fb0bdd2 100644 Binary files a/astra-child-hoponhopoff.zip and b/astra-child-hoponhopoff.zip differ diff --git a/astra-child-hoponhopoff/functions.php b/astra-child-hoponhopoff/functions.php index cc07b68..cf6ee6c 100644 --- a/astra-child-hoponhopoff/functions.php +++ b/astra-child-hoponhopoff/functions.php @@ -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', diff --git a/astra-child-hoponhopoff/inc/footer.php b/astra-child-hoponhopoff/inc/footer.php index a5179dd..c621063 100644 --- a/astra-child-hoponhopoff/inc/footer.php +++ b/astra-child-hoponhopoff/inc/footer.php @@ -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 ''; +}; + +$hoho_footer_col = function ( $location ) use ( $hoho_footer_defaults ) { if ( ! has_nav_menu( $location ) ) { + $hoho_footer_defaults( $location ); return; } wp_nav_menu( diff --git a/astra-child-hoponhopoff/style.css b/astra-child-hoponhopoff/style.css index 339bf8b..758727c 100644 --- a/astra-child-hoponhopoff/style.css +++ b/astra-child-hoponhopoff/style.css @@ -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 */ diff --git a/viator-collector.zip b/viator-collector.zip index ec4d876..2568b6e 100644 Binary files a/viator-collector.zip and b/viator-collector.zip differ diff --git a/viator-collector/assets/css/vc-tour.css b/viator-collector/assets/css/vc-tour.css new file mode 100644 index 0000000..d6bf7ea --- /dev/null +++ b/viator-collector/assets/css/vc-tour.css @@ -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)); + } +} diff --git a/viator-collector/assets/js/vc-design.js b/viator-collector/assets/js/vc-design.js index de5591d..ccb3ed2 100644 --- a/viator-collector/assets/js/vc-design.js +++ b/viator-collector/assets/js/vc-design.js @@ -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(); diff --git a/viator-collector/includes/class-vc-display.php b/viator-collector/includes/class-vc-display.php index cd07386..0823e68 100644 --- a/viator-collector/includes/class-vc-display.php +++ b/viator-collector/includes/class-vc-display.php @@ -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 ''; } diff --git a/viator-collector/includes/class-vc-plugin.php b/viator-collector/includes/class-vc-plugin.php index 388d6cd..c630ee1 100644 --- a/viator-collector/includes/class-vc-plugin.php +++ b/viator-collector/includes/class-vc-plugin.php @@ -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', diff --git a/viator-collector/includes/class-vc-tags.php b/viator-collector/includes/class-vc-tags.php new file mode 100644 index 0000000..c3fdb97 --- /dev/null +++ b/viator-collector/includes/class-vc-tags.php @@ -0,0 +1,41 @@ +name ); + } + ) + ); + } +} diff --git a/viator-collector/templates/listing.php b/viator-collector/templates/listing.php index 90e4c94..d34bdbc 100644 --- a/viator-collector/templates/listing.php +++ b/viator-collector/templates/listing.php @@ -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; diff --git a/viator-collector/templates/single-viator_tour.php b/viator-collector/templates/single-viator_tour.php index 7aa9f6e..93f8c25 100644 --- a/viator-collector/templates/single-viator_tour.php +++ b/viator-collector/templates/single-viator_tour.php @@ -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"' : ''; ?>
-
- 'hero-tour__photo', 'fetchpriority' => 'high', 'alt' => '', 'sizes' => '100vw' ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped - elseif ( $primary ) : - ?> - - - +
-
-

-
-

- 'hero-tour__figure', 'loading' => 'eager', 'alt' => '' ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped - } - ?> -
-
+
-
- -
+ + + + +
-

+

- /5 + /5 () @@ -109,18 +123,6 @@ while ( have_posts() ) :
- -
-

- -
- -

- > + >
@@ -262,6 +264,14 @@ while ( have_posts() ) :
+ + +
+ +

+ + > +
'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' ) ); +} diff --git a/viator-collector/viator-collector.php b/viator-collector/viator-collector.php index 7702d66..b3154ec 100644 --- a/viator-collector/viator-collector.php +++ b/viator-collector/viator-collector.php @@ -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__));