PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.16
Yatra – Travel Booking & Tour Operator Software v3.0.16
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
← All changes | app/Services/EnquiryService.php +81 -9 3.0.3 → 3.0.16 View file →
@@ -64,8 +64,55 @@
64 64 * @return array {success: bool, enquiry_id?: int, message: string}
65 65 */
66 66 public function createEnquiry(array $data): array
67 67 {
68 + // Normalize common client-side key variants (REST/JS often uses camelCase).
69 + if ((!isset($data['trip_id']) || $data['trip_id'] === '' || $data['trip_id'] === null)
70 + && isset($data['tripId'])
71 + && $data['tripId'] !== ''
72 + && $data['tripId'] !== null
73 + ) {
74 + $data['trip_id'] = $data['tripId'];
75 + }
76 +
77 + // If trip_id is missing, attempt to derive it from trip slug or the referring URL.
78 + // This makes enquiry emails resilient even if a client drops hidden fields.
79 + if (empty($data['trip_id']) || (string) $data['trip_id'] === '0') {
80 + $candidateSlug = '';
81 + if (!empty($data['trip_slug'])) {
82 + $candidateSlug = sanitize_title((string) $data['trip_slug']);
83 + } elseif (!empty($data['tripSlug'])) {
84 + $candidateSlug = sanitize_title((string) $data['tripSlug']);
85 + } else {
86 + $ref = isset($_SERVER['HTTP_REFERER']) ? (string) $_SERVER['HTTP_REFERER'] : '';
87 + if ($ref !== '') {
88 + $parts = wp_parse_url($ref);
89 + $path = isset($parts['path']) ? trim((string) $parts['path'], '/') : '';
90 + if ($path !== '') {
91 + $segments = array_values(array_filter(explode('/', $path), static fn ($s) => $s !== ''));
92 + $tripBase = trim((string) SettingsService::getTripBase(), '/');
93 + if ($tripBase !== '' && !empty($segments)) {
94 + $baseIndex = array_search($tripBase, $segments, true);
95 + if ($baseIndex !== false && isset($segments[$baseIndex + 1])) {
96 + $candidateSlug = sanitize_title((string) $segments[$baseIndex + 1]);
97 + }
98 + }
99 + }
100 + }
101 + }
102 +
103 + if ($candidateSlug !== '') {
104 + try {
105 + $trip = $this->tripRepository->findBySlug($candidateSlug);
106 + if ($trip && !empty($trip->id)) {
107 + $data['trip_id'] = (int) $trip->id;
108 + }
109 + } catch (\Throwable $e) {
110 + // Ignore; will proceed as general enquiry.
111 + }
112 + }
113 + }
114 +
68 115 // Validate required fields
69 116 if (empty($data['name']) || empty($data['email']) || empty($data['message'])) {
70 117 return ['success' => false, 'message' => __('Name, email, and message are required.', 'yatra')];
71 118 }
@@ -144,23 +191,35 @@
144 191 if (empty($data['status'])) {
145 192 $data['status'] = 'pending';
146 193 }
147 194
148 - // Create enquiry
149 - $enquiryId = $this->enquiryRepository->create($data);
195 + // Create enquiry. The repository throws when the INSERT is rejected;
196 + // this endpoint is public, so turn that into the same friendly failure
197 + // every other rejection returns instead of a 500 that echoes the raw
198 + // database error back to the visitor.
199 + try {
200 + $enquiryId = $this->enquiryRepository->create($data);
201 + } catch (\Throwable $e) {
202 + error_log('Yatra: failed to create enquiry - ' . $e->getMessage());
150 203
204 + return ['success' => false, 'message' => __('Failed to submit enquiry.', 'yatra')];
205 + }
206 +
151 207 if (!$enquiryId) {
152 208 return ['success' => false, 'message' => __('Failed to submit enquiry.', 'yatra')];
153 209 }
154 210
155 - // Get the full enquiry object for the action
156 - $enquiry = $this->enquiryRepository->find($enquiryId);
157 -
211 + // Load with trip JOIN so listeners (e.g. Pro Email Automation's onEnquiryCreated
212 + // → buildEnquiryVariables) receive trip_title / trip_slug. Without these, Pro
213 + // resolves {{trip_name}} to the literal string "General Enquiry".
214 + $enquiry = $this->enquiryRepository->findWithTrip($enquiryId)
215 + ?: $this->enquiryRepository->find($enquiryId);
216 +
158 217 /**
159 218 * Action: Enquiry created
160 219 * Fires after a new enquiry is successfully created
161 220 *
162 - * @param object $enquiry The enquiry object
221 + * @param object $enquiry The enquiry object (joined with trip when available)
163 222 * @since 3.0.0
164 223 */
165 224 do_action('yatra_enquiry_created', $enquiry);
166 225
@@ -288,9 +347,14 @@
288 347 */
289 348 public function bulkUpdateStatus(array $ids, string $status): array
290 349 {
291 350 // Allowed statuses for bulk updates. This list is mirrored in the admin UI.
292 - $validStatuses = ['pending', 'read', 'responded', 'archived', 'spam', 'trash'];
351 + // 'completed' marks enquiries that have been fully handled, distinct from
352 + // open/in-progress ones. 'closed' is the "no further action" end state the
353 + // enquiry edit screen has always offered; it is accepted here too so the
354 + // list's quick status actions and bulk actions can set it without the
355 + // operator having to open each enquiry.
356 + $validStatuses = ['pending', 'read', 'responded', 'completed', 'closed', 'archived', 'spam', 'trash'];
293 357
294 358 if (!in_array($status, $validStatuses, true)) {
295 359 return ['success' => false, 'affected' => 0, 'message' => __('Invalid status.', 'yatra')];
296 360 }
@@ -299,9 +363,13 @@
299 363
300 364 return [
301 365 'success' => true,
302 366 'affected' => $affected,
303 - 'message' => sprintf(__('%d enquiries updated.', 'yatra'), $affected),
367 + 'message' => sprintf(
368 + /* translators: %d: number of enquiries updated. */
369 + __('%d enquiries updated.', 'yatra'),
370 + $affected
371 + ),
304 372 ];
305 373 }
306 374
307 375 /**
@@ -316,9 +384,13 @@
316 384
317 385 return [
318 386 'success' => true,
319 387 'affected' => $affected,
320 - 'message' => sprintf(__('%d enquiries deleted.', 'yatra'), $affected),
388 + 'message' => sprintf(
389 + /* translators: %d: number of enquiries deleted. */
390 + __('%d enquiries deleted.', 'yatra'),
391 + $affected
392 + ),
321 393 ];
322 394 }
323 395
324 396 /**