PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.3
Yatra – Travel Booking & Tour Operator Software v3.0.3
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 2.0.11 All 82 releases
yatra / app / Shortcodes / ActivityShortcode.php

ActivityShortcode.php in Yatra – Travel Booking & Tour Operator Software 3.0.3, at app/Shortcodes/ActivityShortcode.php

366 lines 13.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 declare(strict_types=1);
4
5 namespace Yatra\Shortcodes;
6
7 use Yatra\Services\SettingsService;
8
9 /**
10 * Activity Shortcode
11 *
12 * Displays activity listings with associated trips using trip-listing-card.php template
13 */
14 class ActivityShortcode extends BaseShortcode
15 {
16 public function __construct()
17 {
18 parent::__construct('yatra_activity', [
19 'order' => 'desc',
20 'per_page' => '10',
21 'columns' => '3',
22 'show_trip_count' => 'yes',
23 'show_description' => 'yes',
24 'show_image' => 'yes',
25 'show_pagination' => 'yes', // Default to show pagination like trip shortcode
26 'activity' => '', // Specific activity slug(s), comma separated
27 'hide_empty' => 'yes',
28 'title' => 'Activity Listings'
29 ]);
30 }
31
32 /**
33 * Render the activity shortcode content
34 */
35 protected function renderContent(array $atts): string
36 {
37 $atts = shortcode_atts($this->default_attributes, $atts, $this->tag);
38
39 // Extract per_page from attributes (only per_page parameter)
40 $per_page = 10; // default
41 if (!empty($atts['per_page']) && is_numeric($atts['per_page'])) {
42 $per_page = (int) $atts['per_page'];
43 }
44 $atts['per_page'] = $per_page;
45
46 // Get activities using Yatra's service
47 $activities_data = $this->getActivities($atts);
48
49 // Prepare data for template
50 $data = [
51 'activities' => $activities_data['activities'] ?? [],
52 'atts' => $atts,
53 'current_page' => $activities_data['current_page'] ?? 1,
54 'max_pages' => $activities_data['max_pages'] ?? 1,
55 'total_found' => $activities_data['total_found'] ?? 0,
56 'per_page' => $per_page
57 ];
58
59 $activityCssPath = YATRA_PLUGIN_PATH . 'assets/css/shortcodes/activity-shortcode.css';
60 $activityCssVer = is_readable($activityCssPath) ? YATRA_VERSION . '.' . filemtime($activityCssPath) : YATRA_VERSION;
61 wp_enqueue_style(
62 'yatra-activity-shortcode',
63 YATRA_PLUGIN_URL . 'assets/css/shortcodes/activity-shortcode.css',
64 \Yatra\Providers\FrontendAssetsProvider::shortcodeStyleDependencies(),
65 $activityCssVer
66 );
67
68 // Enqueue shortcode-specific JavaScript
69 wp_enqueue_script(
70 'yatra-activity-shortcode',
71 YATRA_PLUGIN_URL . 'assets/js/activity-shortcode.js',
72 ['jquery'],
73 YATRA_VERSION,
74 true
75 );
76
77 // Pass data to JavaScript
78 wp_localize_script('yatra-activity-shortcode', 'yatraActivityShortcode', [
79 'ajaxurl' => admin_url('admin-ajax.php'),
80 'nonce' => wp_create_nonce('yatra_activity_shortcode_nonce')
81 ]);
82
83 return $this->loadTemplate('shortcodes/activity.php', $data);
84 }
85
86 /**
87 * Get activities using Yatra's service
88 */
89 public function getActivities(array $atts): array
90 {
91 try {
92 $activityService = new \Yatra\Services\ActivityService();
93
94 // Get current page from query string or attributes (for AJAX)
95 $current_page = isset($atts['current_page']) ? (int) $atts['current_page'] : (isset($_GET['activity_page']) ? (int) $_GET['activity_page'] : 1);
96 // Enhanced per_page handling with validation and debugging
97
98 $per_page = (int) $atts['per_page'];
99
100
101 // Validate per_page to prevent issues
102 $per_page = max(1, min($per_page, 100)); // Between 1 and 100 items
103 $offset = ($current_page - 1) * $per_page;
104
105 // Start with very basic arguments to ensure we get activities
106 $args = [
107 'limit' => $per_page,
108 'offset' => $offset,
109 'order_by' => 'name',
110 'order' => $atts['order'] === 'asc' ? 'ASC' : 'DESC'
111 ];
112
113 // Filter by specific activities if provided
114 if (!empty($atts['activity'])) {
115 $args['where']['slug'] = explode(',', $atts['activity']);
116 }
117
118 // Get total count for pagination
119 $count_args = $args;
120 unset($count_args['limit']);
121 unset($count_args['offset']);
122 $total_activities = $activityService->count($count_args);
123
124 // Try using the base repository method to bypass status filtering
125 $result = $activityService->getAll($args);
126
127
128
129 $activities = [];
130
131 foreach ($result as $activityData) {
132
133
134 // Get real trip data for this activity using classification tables
135 global $wpdb;
136
137 $tripClassificationsTable = \Yatra\Database\Tables\TripClassificationsTable::getTableName();
138 $tripsTable = \Yatra\Database\Tables\TripsTable::getTableName();
139
140 // Get trip IDs for this activity
141 $trip_ids = $wpdb->get_col($wpdb->prepare(
142 "SELECT tc.trip_id
143 FROM {$tripClassificationsTable} tc
144 INNER JOIN {$tripsTable} t ON tc.trip_id = t.id
145 WHERE tc.classification_id = %d
146 AND tc.classification_type = 'activity'
147 AND t.status = 'publish'",
148 $activityData->id
149 ));
150
151 $trip_count = count($trip_ids);
152
153 // Get actual trips
154 $trips = [];
155 if (!empty($trip_ids)) {
156 $placeholders = implode(',', array_fill(0, count($trip_ids), '%d'));
157 $trips = $wpdb->get_results($wpdb->prepare(
158 "SELECT * FROM {$tripsTable}
159 WHERE id IN ({$placeholders})
160 AND status = 'publish'
161 ORDER BY created_at DESC
162 LIMIT 6",
163 ...$trip_ids
164 ));
165 }
166
167 // Calculate pricing from real trips
168 $min_price = null;
169 $max_price = null;
170 $durations = [];
171 $group_sizes = [];
172 $difficulties = [];
173
174 // Calculate rating from reviews table directly
175 $total_rating_sum = 0;
176 $total_review_count = 0;
177
178 if (!empty($trip_ids)) {
179 $reviewsTable = \Yatra\Database\Tables\ReviewsTable::getTableName();
180 $placeholders = implode(',', array_fill(0, count($trip_ids), '%d'));
181
182 $reviews = $wpdb->get_results($wpdb->prepare(
183 "SELECT rating, COUNT(*) as review_count
184 FROM {$reviewsTable}
185 WHERE trip_id IN ({$placeholders})
186 AND status = 'approved'",
187 ...$trip_ids
188 ));
189
190
191 foreach ($reviews as $review) {
192 $total_rating_sum += $review->rating * $review->review_count;
193 $total_review_count += $review->review_count;
194
195 }
196 }
197
198 // Calculate average rating only for trips that actually have reviews
199 // Trips with no reviews are excluded from the average (not treated as 0 rating)
200 $avg_rating = $total_review_count > 0 ? $total_rating_sum / $total_review_count : 0;
201
202 foreach ($trips as $trip) {
203 // Debug: Log all trip data to see what fields exist
204
205
206 // Get pricing via centralized TripPricingService
207 $effective = \Yatra\Services\TripPricingService::getEffectivePrice($trip);
208 if ($effective > 0) {
209 if ($min_price === null || $effective < $min_price) {
210 $min_price = $effective;
211 }
212 if ($max_price === null || $effective > $max_price) {
213 $max_price = $effective;
214 }
215 }
216
217 // Get duration
218 if (!empty($trip->duration)) {
219 $durations[] = $trip->duration;
220 }
221
222 // Get group size
223 if (!empty($trip->max_group_size)) {
224 $group_sizes[] = $trip->max_group_size;
225 }
226
227 // Get difficulty
228 if (!empty($trip->difficulty)) {
229 $difficulties[] = $trip->difficulty;
230 }
231 }
232
233 // Calculate averages
234 $final_avg_rating = $avg_rating; // Already calculated correctly above
235 $avg_duration = !empty($durations) ? array_sum($durations) / count($durations) : 0;
236 $avg_group_size = !empty($group_sizes) ? round(array_sum($group_sizes) / count($group_sizes)) : 0;
237
238
239
240 $activities[] = [
241 'term' => $activityData,
242 'trips' => $trips,
243 'trip_count' => $trip_count,
244 'description' => $activityData->description ?? '',
245 'image' => $this->getActivityImage($activityData),
246 'link' => $this->getActivityLink($activityData),
247 'min_price' => $min_price,
248 'max_price' => $max_price,
249 'avg_rating' => $final_avg_rating,
250 'rating_count' => $total_review_count,
251 'avg_duration' => $avg_duration,
252 'avg_group_size' => $avg_group_size,
253 'difficulty' => !empty($difficulties) ? $this->getMostCommonDifficulty($difficulties) : null
254 ];
255 }
256
257 // Filter out empty activities if requested
258 if ($atts['hide_empty'] === 'yes') {
259 $activities = array_filter($activities, function($activity) {
260 return !empty($activity['term']->name) && !empty($activity['term']->slug);
261 });
262 }
263
264 // Calculate pagination data
265 $max_pages = $per_page > 0 ? ceil($total_activities / $per_page) : 1;
266
267
268
269 return [
270 'activities' => $activities,
271 'current_page' => $current_page,
272 'max_pages' => $max_pages,
273 'total_found' => $total_activities,
274 'per_page' => $per_page
275 ];
276
277 } catch (\Exception $e) {
278
279 return [];
280 }
281 }
282
283 /**
284 * Get the most common difficulty from an array of difficulties
285 */
286 private function getMostCommonDifficulty(array $difficulties): string
287 {
288 if (empty($difficulties)) {
289 return 'Moderate';
290 }
291
292 $counts = array_count_values($difficulties);
293 arsort($counts);
294 return array_key_first($counts);
295 }
296
297
298 /**
299 * Get activity image
300 */
301 private function getActivityImage($activity): string
302 {
303 // Check for activity image in metadata
304 if (isset($activity->image) && !empty($activity->image)) {
305 return $activity->image;
306 }
307
308 // Check for activity icon in metadata
309 if (isset($activity->icon) && !empty($activity->icon)) {
310 $icon_data = maybe_unserialize($activity->icon);
311 if (is_array($icon_data) && isset($icon_data['type']) && $icon_data['type'] === 'image') {
312 return is_numeric($icon_data['value']) ? wp_get_attachment_url($icon_data['value']) : $icon_data['value'];
313 }
314 }
315
316 // Check for activity thumbnail/featured image
317 if (isset($activity->thumbnail) && !empty($activity->thumbnail)) {
318 return is_numeric($activity->thumbnail) ? wp_get_attachment_url($activity->thumbnail) : $activity->thumbnail;
319 }
320
321 // Check for activity banner
322 if (isset($activity->banner) && !empty($activity->banner)) {
323 return $activity->banner;
324 }
325
326 // Check for metadata with image
327 if (isset($activity->metadata) && !empty($activity->metadata)) {
328 $metadata = maybe_unserialize($activity->metadata);
329 if (is_array($metadata)) {
330 // Check for various image fields in metadata
331 $image_fields = ['image', 'thumbnail', 'banner', 'featured_image', 'cover_image'];
332 foreach ($image_fields as $field) {
333 if (isset($metadata[$field]) && !empty($metadata[$field])) {
334 return is_numeric($metadata[$field]) ? wp_get_attachment_url($metadata[$field]) : $metadata[$field];
335 }
336 }
337 }
338 }
339
340 // Fallback to placeholder
341 $fallback_url = YATRA_PLUGIN_URL . 'assets/images/placeholder.png';
342
343
344
345 return $fallback_url;
346 }
347
348 /**
349 * Get activity link
350 */
351 private function getActivityLink($activity): string
352 {
353 if (isset($activity->slug)) {
354 // Use permalink helper so global base + plain permalinks are respected
355 if (function_exists('yatra_get_activity_permalink')) {
356 return yatra_get_activity_permalink($activity);
357 }
358
359 $base = SettingsService::getString('activity_base', 'activity');
360 return home_url('/' . $base . '/' . $activity->slug . '/');
361 }
362
363 return '#'; // Fallback
364 }
365 }
366