PluginProbe
Yatra – Travel Booking & Tour Operator Software / trunk
Yatra – Travel Booking & Tour Operator Software vtrunk
3.0.15 3.0.14 3.0.14.1 3.0.14.2 3.0.12 3.0.13 3.0.11 3.0.10 3.0.9 3.0.8 3.0.7 3.0.6 3.0.5 3.0.5.1 3.0.4 3.0.3 3.0.2.9 3.0.2.7 3.0.2.8 3.0.2.6 trunk 1.0.0 2.0.0 2.0.1 2.0.10 All 83 releases
yatra / templates / single-taxonomy.php

single-taxonomy.php in Yatra – Travel Booking & Tour Operator Software trunk, at templates/single-taxonomy.php

373 lines 18.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Single Taxonomy Page Template (Destination, Activity, Category)
4 * Shows trips filtered by the selected taxonomy with full filtering capabilities
5 *
6 * @package Yatra
7 */
8
9 // Prevent direct access
10 if (!defined('ABSPATH')) {
11 exit;
12 }
13
14 // Get taxonomy data from global
15 $taxonomy_data = $GLOBALS['yatra_taxonomy_data'] ?? null;
16
17 if (!$taxonomy_data) {
18 wp_die(__('No taxonomy data found', 'yatra'));
19 }
20
21 $type = $taxonomy_data->type;
22 $entity = $taxonomy_data; // Use the taxonomy object itself as entity
23
24 // Helper function to safely get array from $_GET
25 function yatra_get_filter_array($key, $sanitize_callback = 'sanitize_text_field') {
26 if (!isset($_GET[$key])) {
27 return [];
28 }
29
30 $value = $_GET[$key];
31
32 // If it's already an array, sanitize each element
33 if (is_array($value)) {
34 return array_map($sanitize_callback, $value);
35 }
36
37 // If it's a string, convert to array and sanitize
38 if (is_string($value) && !empty($value)) {
39 // Handle comma-separated values
40 $array = explode(',', $value);
41 return array_map($sanitize_callback, array_filter($array));
42 }
43
44 return [];
45 }
46
47 // Get active filters from URL parameters (same as main trip listing)
48 $active_filters = [
49 'destination' => sanitize_text_field($_GET['destination'] ?? ''),
50 'activity' => sanitize_text_field($_GET['activity'] ?? ''),
51 'price_min' => !empty($_GET['price_min']) ? intval($_GET['price_min']) : '',
52 'price_max' => !empty($_GET['price_max']) ? intval($_GET['price_max']) : '',
53 'trip_type' => sanitize_text_field($_GET['trip_type'] ?? ''),
54 'sort' => sanitize_text_field($_GET['sort'] ?? ''),
55 'difficulty' => yatra_get_filter_array('difficulty', 'intval'),
56 'rating' => yatra_get_filter_array('rating', 'intval'),
57 'categories' => yatra_get_filter_array('categories', 'intval'),
58 'destinations' => yatra_get_filter_array('destinations', 'intval'),
59 'activities' => yatra_get_filter_array('activities', 'intval'),
60 'accommodation' => yatra_get_filter_array('accommodation', 'sanitize_text_field'),
61 'included_services' => yatra_get_filter_array('included_services', 'sanitize_text_field'),
62 'special_offers' => yatra_get_filter_array('special_offers', 'sanitize_text_field'),
63 'booking_options' => yatra_get_filter_array('booking_options', 'sanitize_text_field'),
64 'age_suitability' => yatra_get_filter_array('age_suitability', 'sanitize_text_field'),
65 ];
66
67 // Get type labels
68 $type_labels = [
69 'destination' => [
70 'singular' => __('Destination', 'yatra'),
71 'plural' => __('Destinations', 'yatra'),
72 /* translators: %s: destination name (e.g. "Nepal"). */
73 'trips_title' => __('Trips in %s', 'yatra'),
74 ],
75 'activity' => [
76 'singular' => __('Activity', 'yatra'),
77 'plural' => __('Activities', 'yatra'),
78 /* translators: %s: term name (activity or category, e.g. "Hiking" or "Adventure"). */
79 'trips_title' => __('%s Trips', 'yatra'),
80 ],
81 'category' => [
82 'singular' => __('Category', 'yatra'),
83 'plural' => __('Categories', 'yatra'),
84 /* translators: %s: term name (activity or category, e.g. "Hiking" or "Adventure"). */
85 'trips_title' => __('%s Trips', 'yatra'),
86 ],
87 ];
88
89 $labels = $type_labels[$type] ?? $type_labels['category'];
90
91 // Pagination: repository + handler totals; page size from WordPress Reading (posts per page)
92 $trips = is_array($taxonomy_data->trips ?? null) ? $taxonomy_data->trips : [];
93 $per_page = isset($taxonomy_data->trips_per_page) ? (int) $taxonomy_data->trips_per_page : yatra_get_posts_per_page();
94 $total_trips = isset($taxonomy_data->trips_total) ? (int) $taxonomy_data->trips_total : count($trips);
95 $total_pages = isset($taxonomy_data->trips_pages) ? max(1, (int) $taxonomy_data->trips_pages) : ($total_trips > 0 ? (int) ceil($total_trips / max(1, $per_page)) : 1);
96 $current_page = isset($taxonomy_data->trips_current_page) ? (int) $taxonomy_data->trips_current_page : yatra_get_archive_listing_paged();
97 $current_page = min(max(1, $current_page), $total_pages);
98 $paged_trips = $trips;
99
100 // Get entity image (no external hardcoded external URL)
101 $entity_image = '';
102
103 // Prefer explicit featured_image or image when present
104 if (!empty($taxonomy_data->featured_image)) {
105 $entity_image = wp_get_attachment_url($taxonomy_data->featured_image);
106 } elseif (!empty($taxonomy_data->image)) {
107 $entity_image = $taxonomy_data->image;
108 }
109
110 // If still empty, try resolving from icon configuration (same logic as listing-destination.php)
111 if (empty($entity_image) && !empty($taxonomy_data->icon)) {
112 $icon = maybe_unserialize($taxonomy_data->icon);
113
114 if (is_array($icon)) {
115 $type = $icon['type'] ?? '';
116 $value = $icon['value'] ?? '';
117
118 if ($type === 'image' && !empty($value)) {
119 if (is_numeric($value)) {
120 $maybe_url = wp_get_attachment_image_url((int) $value, 'large');
121 if (!empty($maybe_url)) {
122 $entity_image = $maybe_url;
123 }
124 } elseif (is_string($value) && filter_var($value, FILTER_VALIDATE_URL)) {
125 $entity_image = $value;
126 }
127 } elseif (!empty($icon['url']) && filter_var($icon['url'], FILTER_VALIDATE_URL)) {
128 // Legacy format: ['url' => 'https://...']
129 $entity_image = $icon['url'];
130 } elseif (!empty($icon['id'])) {
131 // Legacy format: ['id' => attachment_id]
132 $maybe_url = wp_get_attachment_image_url((int) $icon['id'], 'large');
133 if (!empty($maybe_url)) {
134 $entity_image = $maybe_url;
135 }
136 }
137 } elseif (is_numeric($icon)) {
138 $maybe_url = wp_get_attachment_image_url((int) $icon, 'large');
139 if (!empty($maybe_url)) {
140 $entity_image = $maybe_url;
141 }
142 } elseif (is_string($icon) && filter_var($icon, FILTER_VALIDATE_URL)) {
143 $entity_image = $icon;
144 }
145 }
146
147 // Final internal placeholder fallback (no external Unsplash)
148 if (empty($entity_image)) {
149 $placeholder_src = plugins_url('assets/images/placeholder.png', dirname(__FILE__));
150 $entity_image = $placeholder_src;
151 }
152
153 yatra_get_header();
154
155 // Trip-listing card layout (Settings → Design); follows the site-wide setting.
156 $yatra_listing_layout_class = \Yatra\Providers\FrontendAssetsProvider::listingLayoutClasses(
157 \Yatra\Providers\FrontendAssetsProvider::resolveListingLayout()
158 );
159 ?>
160
161 <div class="yatra-listing-page yatra-taxonomy-page yatra-<?php echo esc_attr($type); ?>-page<?php echo $yatra_listing_layout_class ? ' ' . esc_attr($yatra_listing_layout_class) : ''; ?>">
162
163 <!-- Hero Section -->
164 <div class="yatra-taxonomy-hero"<?php echo $entity_image ? ' style="background-image: url(' . esc_url($entity_image) . ');"' : ''; ?>>
165 <div class="yatra-taxonomy-hero-overlay"></div>
166 <div class="yatra-taxonomy-hero-content">
167 <h1 class="yatra-taxonomy-title"><?php echo esc_html($taxonomy_data->name); ?></h1>
168 <?php if (!empty($taxonomy_data->description)): ?>
169 <p class="yatra-taxonomy-description"><?php echo wp_kses_post($taxonomy_data->description); ?></p>
170 <?php endif; ?>
171 <div class="yatra-taxonomy-stats">
172 <span class="yatra-taxonomy-trip-count">
173 <svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24">
174 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l4.553 2.276A1 1 0 0021 18.382V7.618a1 1 0 00-.553-.894L15 4m0 13V4m0 0L9 7"/>
175 </svg>
176 <?php
177 /* translators: %d: total number of trips in this taxonomy term. */
178 echo sprintf(_n('%d Trip', '%d Trips', $total_trips, 'yatra'), (int) $total_trips);
179 ?>
180 </span>
181 </div>
182 </div>
183 </div>
184
185 <div class="yatra-listing-wrapper">
186 <div class="yatra-listing-container">
187
188 <!-- Full Width Content (no sidebar) -->
189 <div class="yatra-listing-content-full">
190 <!-- Results Header -->
191 <div class="yatra-results-header">
192 <div class="yatra-results-info">
193 <h2><?php echo sprintf(esc_html($labels['trips_title']), esc_html($taxonomy_data->name)); ?></h2>
194 <p class="yatra-results-count">
195 <?php
196 $start_item = ($current_page - 1) * $per_page + 1;
197 $end_item = min($current_page * $per_page, $total_trips);
198
199 if ($total_trips > 0) {
200 echo sprintf(
201 /* translators: 1: first item index on the page, 2: last item index on the page, 3: total trips, 4: current page number, 5: total page count. */
202 __('Showing <strong>%1$d-%2$d</strong> of %3$d trips (Page %4$d of %5$d)', 'yatra'),
203 $start_item,
204 $end_item,
205 $total_trips,
206 $current_page,
207 $total_pages
208 );
209 } else {
210 echo esc_html__('No trips found', 'yatra');
211 }
212 ?>
213 </p>
214 </div>
215 <div class="yatra-results-controls">
216 <div class="yatra-sort-control">
217 <label><?php echo esc_html__('Sort by:', 'yatra'); ?></label>
218 <?php
219 $current_trip_sort = isset($_GET['sort']) ? sanitize_text_field(wp_unslash((string) $_GET['sort'])) : '';
220 $sort_definitions = [
221 '' => __('Recommended', 'yatra'),
222 'most_popular' => __('Most Popular', 'yatra'),
223 'price_low' => __('Price: Low to High', 'yatra'),
224 'price_high' => __('Price: High to Low', 'yatra'),
225 'rating_high' => __('Rating: Highest', 'yatra'),
226 'duration_short' => __('Duration: Shortest', 'yatra'),
227 'duration_long' => __('Duration: Longest', 'yatra'),
228 ];
229 ?>
230 <select id="yatra-sort-filter" class="yatra-sort-filter-select">
231 <?php foreach ($sort_definitions as $sort_key => $sort_label) : ?>
232 <option value="<?php echo esc_attr(esc_url(yatra_build_current_request_sort_url($sort_key))); ?>"<?php selected($sort_key, $current_trip_sort, true); ?>><?php echo esc_html($sort_label); ?></option>
233 <?php endforeach; ?>
234 </select>
235 </div>
236 <div class="yatra-view-toggle">
237 <button class="yatra-view-btn active" data-view="grid" title="<?php echo esc_attr__('Grid View', 'yatra'); ?>">
238 <svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24">
239 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z"/>
240 </svg>
241 </button>
242 <button class="yatra-view-btn" data-view="list" title="<?php echo esc_attr__('List View', 'yatra'); ?>">
243 <svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24">
244 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/>
245 </svg>
246 </button>
247 </div>
248 </div>
249 </div>
250
251 <!-- Trip Grid -->
252 <?php if (!empty($paged_trips)): ?>
253 <div class="yatra-trip-grid" id="trip-grid">
254 <?php foreach ($paged_trips as $trip): ?>
255 <?php
256 // Convert stdClass to Trip model instance if needed for consistent data access
257 if (!($trip instanceof \Yatra\Models\Trip)) {
258 $trip = \Yatra\Models\Trip::fromStdClass($trip);
259 }
260 ?>
261 <?php include(dirname(__FILE__) . '/trip-listing-card.php'); ?>
262 <?php endforeach; ?>
263 </div>
264 <?php else: ?>
265 <div class="yatra-no-trips-found">
266 <div class="yatra-no-trips-icon">
267 <svg width="32" height="32" fill="none" stroke="currentColor" viewBox="0 0 24 24">
268 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l4.553 2.276A1 1 0 0021 18.382V7.618a1 1 0 00-.553-.894L15 4m0 13V4m0 0L9 7"/>
269 </svg>
270 </div>
271 <h3><?php echo esc_html__('No trips found', 'yatra'); ?></h3>
272 <p><?php
273 /* translators: %s: taxonomy term name (destination, activity, or category). */
274 echo sprintf(esc_html__('There are no trips available for %s at the moment. Please check back later or explore other options.', 'yatra'), esc_html($taxonomy_data->name));
275 ?></p>
276 <a href="<?php echo esc_url(home_url('/' . \Yatra\Services\SettingsService::getTripBase() . '/')); ?>" class="yatra-btn yatra-btn-primary yatra-archive-card-cta">
277 <?php echo yatra_svg_icon('globe', 'yatra-btn-icon'); ?>
278 <span><?php echo esc_html__('Browse All Trips', 'yatra'); ?></span>
279 </a>
280 </div>
281 <?php endif; ?>
282
283 <?php if ($total_pages > 1): ?>
284 <div class="yatra-listing-pagination">
285 <?php
286 $prev_page = max(1, $current_page - 1);
287 $next_page = min($total_pages, $current_page + 1);
288 ?>
289
290 <?php if ($current_page <= 1): ?>
291 <span class="yatra-pagination-btn disabled" aria-disabled="true">
292 <?php echo yatra_svg_icon('chevron-left', 'yatra-btn-icon'); ?>
293 <span><?php echo esc_html__('Previous', 'yatra'); ?></span>
294 </span>
295 <?php else: ?>
296 <a class="yatra-pagination-btn" href="<?php echo esc_url(yatra_build_current_request_paged_url($prev_page)); ?>">
297 <?php echo yatra_svg_icon('chevron-left', 'yatra-btn-icon'); ?>
298 <span><?php echo esc_html__('Previous', 'yatra'); ?></span>
299 </a>
300 <?php endif; ?>
301
302 <?php
303 $range = 2;
304 for ($i = 1; $i <= $total_pages; $i++) :
305 if ($i == 1 || $i == $total_pages || ($i >= $current_page - $range && $i <= $current_page + $range)) :
306 if ($i === $current_page) :
307 ?>
308 <span class="yatra-pagination-btn active"><?php echo esc_html((string) $i); ?></span>
309 <?php
310 else :
311 ?>
312 <a class="yatra-pagination-btn" href="<?php echo esc_url(yatra_build_current_request_paged_url($i)); ?>"><?php echo esc_html((string) $i); ?></a>
313 <?php
314 endif;
315 elseif ($i == $current_page - $range - 1 || $i == $current_page + $range + 1) :
316 ?>
317 <span class="yatra-pagination-ellipsis">...</span>
318 <?php
319 endif;
320 endfor;
321 ?>
322
323 <?php if ($current_page >= $total_pages): ?>
324 <span class="yatra-pagination-btn disabled" aria-disabled="true">
325 <span><?php echo esc_html__('Next', 'yatra'); ?></span>
326 <?php echo yatra_svg_icon('chevron-right', 'yatra-btn-icon'); ?>
327 </span>
328 <?php else: ?>
329 <a class="yatra-pagination-btn" href="<?php echo esc_url(yatra_build_current_request_paged_url($next_page)); ?>">
330 <span><?php echo esc_html__('Next', 'yatra'); ?></span>
331 <?php echo yatra_svg_icon('chevron-right', 'yatra-btn-icon'); ?>
332 </a>
333 <?php endif; ?>
334 </div>
335 <?php endif; ?>
336
337 </div>
338 </div>
339 </div>
340 </div>
341
342 <script>
343 document.addEventListener('DOMContentLoaded', function() {
344 // Grid/list view: handled by assets/js/listing.js (per-page localStorage via grid id)
345
346 // Sort: server-side (#yatra-sort-filter + listing-filters.js).
347
348 // Make trip image area clickable via JS (preserve markup/design)
349 const imageWrappers = document.querySelectorAll('.yatra-trip-card .yatra-trip-image[data-permalink]');
350 imageWrappers.forEach(function(wrapper) {
351 wrapper.addEventListener('click', function (event) {
352 // Do not navigate when clicking the favorite button inside the image area
353 var target = event.target;
354 if (target instanceof Element && target.closest('.yatra-favorite-btn')) {
355 return;
356 }
357
358 var url = wrapper.getAttribute('data-permalink');
359 if (url) {
360 window.location.href = url;
361 }
362 });
363 });
364 });
365 </script>
366
367 <?php
368 yatra_get_footer();
369 ?>
370
371
372
373