PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.2.7
Yatra – Travel Booking & Tour Operator Software v3.0.2.7
3.0.16 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 All 84 releases
yatra / templates / single-taxonomy.php

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

358 lines 17.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 'trips_title' => __('Trips in %s', 'yatra'),
73 ],
74 'activity' => [
75 'singular' => __('Activity', 'yatra'),
76 'plural' => __('Activities', 'yatra'),
77 'trips_title' => __('%s Trips', 'yatra'),
78 ],
79 'category' => [
80 'singular' => __('Category', 'yatra'),
81 'plural' => __('Categories', 'yatra'),
82 'trips_title' => __('%s Trips', 'yatra'),
83 ],
84 ];
85
86 $labels = $type_labels[$type] ?? $type_labels['category'];
87
88 // Pagination: repository + handler totals; page size from WordPress Reading (posts per page)
89 $trips = is_array($taxonomy_data->trips ?? null) ? $taxonomy_data->trips : [];
90 $per_page = isset($taxonomy_data->trips_per_page) ? (int) $taxonomy_data->trips_per_page : yatra_get_posts_per_page();
91 $total_trips = isset($taxonomy_data->trips_total) ? (int) $taxonomy_data->trips_total : count($trips);
92 $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);
93 $current_page = isset($taxonomy_data->trips_current_page) ? (int) $taxonomy_data->trips_current_page : yatra_get_archive_listing_paged();
94 $current_page = min(max(1, $current_page), $total_pages);
95 $paged_trips = $trips;
96
97 // Get entity image (no external hardcoded external URL)
98 $entity_image = '';
99
100 // Prefer explicit featured_image or image when present
101 if (!empty($taxonomy_data->featured_image)) {
102 $entity_image = wp_get_attachment_url($taxonomy_data->featured_image);
103 } elseif (!empty($taxonomy_data->image)) {
104 $entity_image = $taxonomy_data->image;
105 }
106
107 // If still empty, try resolving from icon configuration (same logic as listing-destination.php)
108 if (empty($entity_image) && !empty($taxonomy_data->icon)) {
109 $icon = maybe_unserialize($taxonomy_data->icon);
110
111 if (is_array($icon)) {
112 $type = $icon['type'] ?? '';
113 $value = $icon['value'] ?? '';
114
115 if ($type === 'image' && !empty($value)) {
116 if (is_numeric($value)) {
117 $maybe_url = wp_get_attachment_image_url((int) $value, 'large');
118 if (!empty($maybe_url)) {
119 $entity_image = $maybe_url;
120 }
121 } elseif (is_string($value) && filter_var($value, FILTER_VALIDATE_URL)) {
122 $entity_image = $value;
123 }
124 } elseif (!empty($icon['url']) && filter_var($icon['url'], FILTER_VALIDATE_URL)) {
125 // Legacy format: ['url' => 'https://...']
126 $entity_image = $icon['url'];
127 } elseif (!empty($icon['id'])) {
128 // Legacy format: ['id' => attachment_id]
129 $maybe_url = wp_get_attachment_image_url((int) $icon['id'], 'large');
130 if (!empty($maybe_url)) {
131 $entity_image = $maybe_url;
132 }
133 }
134 } elseif (is_numeric($icon)) {
135 $maybe_url = wp_get_attachment_image_url((int) $icon, 'large');
136 if (!empty($maybe_url)) {
137 $entity_image = $maybe_url;
138 }
139 } elseif (is_string($icon) && filter_var($icon, FILTER_VALIDATE_URL)) {
140 $entity_image = $icon;
141 }
142 }
143
144 // Final internal placeholder fallback (no external Unsplash)
145 if (empty($entity_image)) {
146 $placeholder_src = plugins_url('assets/images/placeholder.png', dirname(__FILE__));
147 $entity_image = $placeholder_src;
148 }
149
150 yatra_get_header();
151 ?>
152
153 <div class="yatra-listing-page yatra-taxonomy-page yatra-<?php echo esc_attr($type); ?>-page">
154
155 <!-- Hero Section -->
156 <div class="yatra-taxonomy-hero"<?php echo $entity_image ? ' style="background-image: url(' . esc_url($entity_image) . ');"' : ''; ?>>
157 <div class="yatra-taxonomy-hero-overlay"></div>
158 <div class="yatra-taxonomy-hero-content">
159 <h1 class="yatra-taxonomy-title"><?php echo esc_html($taxonomy_data->name); ?></h1>
160 <?php if (!empty($taxonomy_data->description)): ?>
161 <p class="yatra-taxonomy-description"><?php echo wp_kses_post($taxonomy_data->description); ?></p>
162 <?php endif; ?>
163 <div class="yatra-taxonomy-stats">
164 <span class="yatra-taxonomy-trip-count">
165 <svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24">
166 <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"/>
167 </svg>
168 <?php echo sprintf(_n('%d Trip', '%d Trips', $total_trips, 'yatra'), (int) $total_trips); ?>
169 </span>
170 </div>
171 </div>
172 </div>
173
174 <div class="yatra-listing-wrapper">
175 <div class="yatra-listing-container">
176
177 <!-- Full Width Content (no sidebar) -->
178 <div class="yatra-listing-content-full">
179 <!-- Results Header -->
180 <div class="yatra-results-header">
181 <div class="yatra-results-info">
182 <h2><?php echo sprintf(esc_html($labels['trips_title']), esc_html($taxonomy_data->name)); ?></h2>
183 <p class="yatra-results-count">
184 <?php
185 $start_item = ($current_page - 1) * $per_page + 1;
186 $end_item = min($current_page * $per_page, $total_trips);
187
188 if ($total_trips > 0) {
189 echo sprintf(
190 __('Showing <strong>%d-%d</strong> of %d trips (Page %d of %d)', 'yatra'),
191 $start_item,
192 $end_item,
193 $total_trips,
194 $current_page,
195 $total_pages
196 );
197 } else {
198 echo esc_html__('No trips found', 'yatra');
199 }
200 ?>
201 </p>
202 </div>
203 <div class="yatra-results-controls">
204 <div class="yatra-sort-control">
205 <label><?php echo esc_html__('Sort by:', 'yatra'); ?></label>
206 <?php
207 $current_trip_sort = isset($_GET['sort']) ? sanitize_text_field(wp_unslash((string) $_GET['sort'])) : '';
208 $sort_definitions = [
209 '' => __('Recommended', 'yatra'),
210 'most_popular' => __('Most Popular', 'yatra'),
211 'price_low' => __('Price: Low to High', 'yatra'),
212 'price_high' => __('Price: High to Low', 'yatra'),
213 'rating_high' => __('Rating: Highest', 'yatra'),
214 'duration_short' => __('Duration: Shortest', 'yatra'),
215 'duration_long' => __('Duration: Longest', 'yatra'),
216 ];
217 ?>
218 <select id="yatra-sort-filter" class="yatra-sort-filter-select">
219 <?php foreach ($sort_definitions as $sort_key => $sort_label) : ?>
220 <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>
221 <?php endforeach; ?>
222 </select>
223 </div>
224 <div class="yatra-view-toggle">
225 <button class="yatra-view-btn active" data-view="grid" title="<?php echo esc_attr__('Grid View', 'yatra'); ?>">
226 <svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24">
227 <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"/>
228 </svg>
229 </button>
230 <button class="yatra-view-btn" data-view="list" title="<?php echo esc_attr__('List View', 'yatra'); ?>">
231 <svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24">
232 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/>
233 </svg>
234 </button>
235 </div>
236 </div>
237 </div>
238
239 <!-- Trip Grid -->
240 <?php if (!empty($paged_trips)): ?>
241 <div class="yatra-trip-grid" id="trip-grid">
242 <?php foreach ($paged_trips as $trip): ?>
243 <?php
244 // Convert stdClass to Trip model instance if needed for consistent data access
245 if (!($trip instanceof \Yatra\Models\Trip)) {
246 $trip = \Yatra\Models\Trip::fromStdClass($trip);
247 }
248 ?>
249 <?php include(dirname(__FILE__) . '/trip-listing-card.php'); ?>
250 <?php endforeach; ?>
251 </div>
252 <?php else: ?>
253 <div class="yatra-no-trips-found">
254 <div class="yatra-no-trips-icon">
255 <svg width="32" height="32" fill="none" stroke="currentColor" viewBox="0 0 24 24">
256 <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"/>
257 </svg>
258 </div>
259 <h3><?php echo esc_html__('No trips found', 'yatra'); ?></h3>
260 <p><?php 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)); ?></p>
261 <a href="<?php echo esc_url(home_url('/' . \Yatra\Services\SettingsService::getTripBase() . '/')); ?>" class="yatra-btn yatra-btn-primary yatra-archive-card-cta">
262 <?php echo yatra_svg_icon('globe', 'yatra-btn-icon'); ?>
263 <span><?php echo esc_html__('Browse All Trips', 'yatra'); ?></span>
264 </a>
265 </div>
266 <?php endif; ?>
267
268 <?php if ($total_pages > 1): ?>
269 <div class="yatra-listing-pagination">
270 <?php
271 $prev_page = max(1, $current_page - 1);
272 $next_page = min($total_pages, $current_page + 1);
273 ?>
274
275 <?php if ($current_page <= 1): ?>
276 <span class="yatra-pagination-btn disabled" aria-disabled="true">
277 <?php echo yatra_svg_icon('chevron-left', 'yatra-btn-icon'); ?>
278 <span><?php echo esc_html__('Previous', 'yatra'); ?></span>
279 </span>
280 <?php else: ?>
281 <a class="yatra-pagination-btn" href="<?php echo esc_url(yatra_build_current_request_paged_url($prev_page)); ?>">
282 <?php echo yatra_svg_icon('chevron-left', 'yatra-btn-icon'); ?>
283 <span><?php echo esc_html__('Previous', 'yatra'); ?></span>
284 </a>
285 <?php endif; ?>
286
287 <?php
288 $range = 2;
289 for ($i = 1; $i <= $total_pages; $i++) :
290 if ($i == 1 || $i == $total_pages || ($i >= $current_page - $range && $i <= $current_page + $range)) :
291 if ($i === $current_page) :
292 ?>
293 <span class="yatra-pagination-btn active"><?php echo esc_html((string) $i); ?></span>
294 <?php
295 else :
296 ?>
297 <a class="yatra-pagination-btn" href="<?php echo esc_url(yatra_build_current_request_paged_url($i)); ?>"><?php echo esc_html((string) $i); ?></a>
298 <?php
299 endif;
300 elseif ($i == $current_page - $range - 1 || $i == $current_page + $range + 1) :
301 ?>
302 <span class="yatra-pagination-ellipsis">...</span>
303 <?php
304 endif;
305 endfor;
306 ?>
307
308 <?php if ($current_page >= $total_pages): ?>
309 <span class="yatra-pagination-btn disabled" aria-disabled="true">
310 <span><?php echo esc_html__('Next', 'yatra'); ?></span>
311 <?php echo yatra_svg_icon('chevron-right', 'yatra-btn-icon'); ?>
312 </span>
313 <?php else: ?>
314 <a class="yatra-pagination-btn" href="<?php echo esc_url(yatra_build_current_request_paged_url($next_page)); ?>">
315 <span><?php echo esc_html__('Next', 'yatra'); ?></span>
316 <?php echo yatra_svg_icon('chevron-right', 'yatra-btn-icon'); ?>
317 </a>
318 <?php endif; ?>
319 </div>
320 <?php endif; ?>
321
322 </div>
323 </div>
324 </div>
325 </div>
326
327 <script>
328 document.addEventListener('DOMContentLoaded', function() {
329 // Grid/list view: handled by assets/js/listing.js (per-page localStorage via grid id)
330
331 // Sort: server-side (#yatra-sort-filter + listing-filters.js).
332
333 // Make trip image area clickable via JS (preserve markup/design)
334 const imageWrappers = document.querySelectorAll('.yatra-trip-card .yatra-trip-image[data-permalink]');
335 imageWrappers.forEach(function(wrapper) {
336 wrapper.addEventListener('click', function (event) {
337 // Do not navigate when clicking the favorite button inside the image area
338 var target = event.target;
339 if (target instanceof Element && target.closest('.yatra-favorite-btn')) {
340 return;
341 }
342
343 var url = wrapper.getAttribute('data-permalink');
344 if (url) {
345 window.location.href = url;
346 }
347 });
348 });
349 });
350 </script>
351
352 <?php
353 yatra_get_footer();
354 ?>
355
356
357
358