PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.7
Yatra – Travel Booking & Tour Operator Software v3.0.7
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 / Controllers / ActivityController.php

ActivityController.php in Yatra – Travel Booking & Tour Operator Software 3.0.7, at app/Controllers/ActivityController.php

368 lines 11.5 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\Controllers;
6
7 use WP_REST_Request;
8 use WP_REST_Response;
9 use WP_Error;
10 use Yatra\Services\ActivityService;
11 use Yatra\Database\Tables\TripsTable;
12 use Yatra\Database\Tables\TripClassificationsTable;
13 use Yatra\Validators\ActivityValidator;
14 use Yatra\Exceptions\ValidationException;
15 use Yatra\Utils\Logger;
16 use Yatra\Helpers\FormatHelper;
17
18 /**
19 * Activity REST API Controller
20 *
21 * Endpoints:
22 * - GET /activities - List activities
23 * - POST /activities - Create activity
24 * - GET /activities/{id} - Get single activity
25 * - PUT /activities/{id} - Update activity
26 * - DELETE /activities/{id} - Delete activity
27 */
28 class ActivityController extends BaseController
29 {
30 /**
31 * REST API base
32 */
33 protected string $rest_base = 'activities';
34
35 /**
36 * @var ActivityService
37 */
38 private ActivityService $service;
39
40 /**
41 * Constructor
42 */
43 public function __construct()
44 {
45 $this->service = new ActivityService();
46 }
47
48 /**
49 * Register routes
50 */
51 public function register_routes(): void
52 {
53 // Register standard CRUD routes with status filter
54 $this->registerCrudRoutes($this->getStatusArg());
55
56 register_rest_route($this->namespace, '/' . $this->rest_base . '/bulk', [
57 'methods' => \WP_REST_Server::CREATABLE,
58 'callback' => [$this, 'bulkAction'],
59 'permission_callback' => [$this, 'check_permission'],
60 ]);
61
62 register_rest_route($this->namespace, '/' . $this->rest_base . '/stats', [
63 'methods' => \WP_REST_Server::READABLE,
64 'callback' => [$this, 'getStats'],
65 'permission_callback' => [$this, 'check_permission'],
66 ]);
67 }
68
69 /**
70 * Check permissions for activity endpoints
71 */
72 public function check_permission(?WP_REST_Request $request = null): bool
73 {
74 if ($request === null) {
75 return true;
76 }
77
78 if (!is_user_logged_in()) {
79 return false;
80 }
81
82 if (current_user_can('manage_options')) {
83 return true;
84 }
85
86 $method = $request->get_method();
87
88 switch ($method) {
89 case 'GET':
90 return current_user_can('yatra_view_trips');
91 case 'POST':
92 case 'PUT':
93 case 'PATCH':
94 case 'DELETE':
95 return current_user_can('yatra_edit_trips');
96 default:
97 // Unknown HTTP method — deny explicitly. The earlier
98 // fallback to `manage_options` was a regression that
99 // silently broadened admin-only access for any verb not
100 // in the explicit switch.
101 return false;
102 }
103 }
104
105 /**
106 * Get items
107 */
108 public function get_items(WP_REST_Request $request)
109 {
110 try {
111 $params = $this->getPaginationParams($request);
112
113 Logger::apiRequest('/activities', 'GET', $params);
114
115 $args = [
116 'limit' => $params['per_page'],
117 'offset' => ($params['page'] - 1) * $params['per_page'],
118 'order_by' => $params['orderby'],
119 'order' => $params['order'],
120 ];
121
122 if (!empty($params['search'])) {
123 $args['search'] = $params['search'];
124 }
125
126 $status = $request->get_param('status');
127 if ($status && $status !== 'all') {
128 $args['status'] = sanitize_text_field($status);
129 }
130
131 $items = $this->service->getAll($args);
132 $total = $this->service->count($args);
133
134 // Attach trip counts for each activity
135 if (!empty($items)) {
136 foreach ($items as $item) {
137 $activityId = isset($item->id) ? (int) $item->id : 0;
138 if ($activityId <= 0) {
139 continue;
140 }
141
142 $tripCount = $this->service->getTripCount($activityId);
143 $item->trip_count = $tripCount;
144 }
145 }
146
147 $prepared = array_map([$this, 'prepareActivity'], $items);
148
149 Logger::info("Activities retrieved successfully", ['count' => count($prepared), 'total' => $total]);
150 return $this->paginated_response($prepared, $total, $params['page'], $params['per_page']);
151
152 } catch (\Exception $e) {
153 Logger::error("Failed to get activities", ['error' => $e->getMessage(), 'params' => $params ?? []]);
154 return $this->handle_exception($e);
155 }
156 }
157
158 /**
159 * Get single item
160 */
161 public function get_item(WP_REST_Request $request)
162 {
163 try {
164 $id = $this->getId($request);
165
166 if ($id <= 0) {
167 throw new ValidationException('Invalid activity ID', ['id' => ['Activity ID must be a positive integer']]);
168 }
169
170 Logger::apiRequest("/activities/{$id}", 'GET');
171
172 $item = $this->service->getById($id);
173
174 if (!$item) {
175 Logger::warning("Activity not found", ['activity_id' => $id]);
176 return $this->not_found(__('Activity not found', 'yatra'));
177 }
178
179 // Attach trip count for single activity
180 $activityId = isset($item->id) ? (int) $item->id : 0;
181 if ($activityId > 0) {
182 $tripCount = $this->service->getTripCount($activityId);
183 $item->trip_count = $tripCount;
184 }
185
186 Logger::info("Activity retrieved successfully", ['activity_id' => $id]);
187 return $this->success_response($this->prepareActivity($item));
188
189 } catch (\Exception $e) {
190 Logger::error("Failed to get activity", ['activity_id' => isset($id) ? $id : 0, 'error' => $e->getMessage()]);
191 return $this->handle_exception($e);
192 }
193 }
194
195 /**
196 * Create item
197 */
198 public function create_item(WP_REST_Request $request)
199 {
200 try {
201 $data = $this->getBody($request);
202
203 // Validate and sanitize input data
204 ActivityValidator::validateCreate($data);
205 $data = ActivityValidator::sanitize($data);
206
207 // Description will be sanitized in the Service layer with FormatHelper::sanitizeQuillHtml()
208
209 Logger::apiRequest('/activities', 'POST', $data);
210
211 $id = $this->service->create($data);
212
213 Logger::info("Activity created successfully", ['activity_id' => $id]);
214 return $this->success_response([
215 'id' => $id,
216 'message' => __('Activity created successfully', 'yatra'),
217 ], 201);
218
219 } catch (\Exception $e) {
220 Logger::error("Failed to create activity", ['data' => $data ?? [], 'error' => $e->getMessage()]);
221 return $this->handle_exception($e);
222 }
223 }
224
225 /**
226 * Update item
227 */
228 public function update_item(WP_REST_Request $request)
229 {
230 try {
231 $id = $this->getId($request);
232 $data = $this->getBody($request);
233
234
235 // Description will be sanitized in the Service layer with FormatHelper::sanitizeQuillHtml()
236
237 $result = $this->service->update($id, $data);
238
239 if (!$result) {
240 return $this->error_response(__('Failed to update activity', 'yatra'), 500);
241 }
242
243 return $this->success_response([
244 'message' => __('Activity updated successfully', 'yatra'),
245 ]);
246 } catch (\InvalidArgumentException $e) {
247 return $this->validation_error($e->getMessage());
248 } catch (\Exception $e) {
249 return $this->error_response($e->getMessage(), 500);
250 }
251 }
252
253 /**
254 * Delete item
255 */
256 public function delete_item(WP_REST_Request $request)
257 {
258 try {
259 $result = $this->service->delete($this->getId($request));
260
261 if (!$result) {
262 return $this->error_response(__('Failed to delete activity', 'yatra'), 500);
263 }
264
265 return $this->success_response([
266 'message' => __('Activity deleted successfully', 'yatra'),
267 ]);
268 } catch (\Exception $e) {
269 return $this->error_response($e->getMessage(), 500);
270 }
271 }
272
273 /**
274 * Bulk actions endpoint
275 */
276 public function bulkAction(WP_REST_Request $request)
277 {
278 try {
279 $data = $request->get_json_params();
280 $action = sanitize_text_field($data['action'] ?? '');
281 $ids = array_filter(array_map('absint', $data['ids'] ?? []));
282
283 if (empty($action)) {
284 return $this->validation_error(__('Action is required', 'yatra'));
285 }
286
287 if (empty($ids)) {
288 return $this->validation_error(__('No activities selected', 'yatra'));
289 }
290
291 switch ($action) {
292 case 'trash':
293 $result = $this->service->bulkUpdateStatus($ids, 'trash');
294 break;
295 case 'publish':
296 $result = $this->service->bulkUpdateStatus($ids, 'publish');
297 break;
298 case 'draft':
299 $result = $this->service->bulkUpdateStatus($ids, 'draft');
300 break;
301 case 'restore':
302 $result = $this->service->bulkUpdateStatus($ids, 'publish');
303 break;
304 case 'delete':
305 $result = $this->service->bulkDelete($ids);
306 break;
307 default:
308 throw new \InvalidArgumentException(__('Invalid action', 'yatra'));
309 }
310
311 return $this->success_response($result);
312 } catch (\InvalidArgumentException $e) {
313 return $this->validation_error($e->getMessage());
314 } catch (\Exception $e) {
315 return $this->error_response($e->getMessage(), 500);
316 }
317 }
318
319 /**
320 * Get statistics for admin views
321 */
322 public function getStats(WP_REST_Request $request)
323 {
324 try {
325 $stats = $this->service->getStatusCounts();
326 return $this->success_response($stats);
327 } catch (\Exception $e) {
328 return $this->error_response($e->getMessage(), 500);
329 }
330 }
331
332 /**
333 * Prepare activity for response
334 */
335 private function prepareActivity($item): array
336 {
337 $prepared = (array) $item;
338
339
340 // Handle icon
341 if (isset($prepared['icon']) && is_string($prepared['icon'])) {
342 $prepared['icon'] = maybe_unserialize($prepared['icon']);
343 }
344 if (isset($prepared['icon'])) {
345 $prepared['icon'] = $this->convert_icon_attachment_id_to_url($prepared['icon']);
346 }
347
348 // Handle metadata
349 if (isset($prepared['metadata']) && is_string($prepared['metadata'])) {
350 $prepared['metadata'] = maybe_unserialize($prepared['metadata']);
351 }
352
353
354 // Add user names
355 if (!empty($prepared['created_by'])) {
356 $user = get_userdata((int) $prepared['created_by']);
357 $prepared['created_by_name'] = $user ? esc_html($user->display_name) : null;
358 }
359
360 if (!empty($prepared['updated_by'])) {
361 $user = get_userdata((int) $prepared['updated_by']);
362 $prepared['updated_by_name'] = $user ? esc_html($user->display_name) : null;
363 }
364
365 return $prepared;
366 }
367 }
368