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/Controllers/AttributeController.php +111 -59 3.0.2.6 → 3.0.16 View file →
@@ -104,8 +104,15 @@
104 104 ],
105 105 ],
106 106 ]);
107 107
108 + // Bulk operations
109 + register_rest_route($this->namespace, "/{$this->rest_base}/bulk", [
110 + 'methods' => \WP_REST_Server::CREATABLE,
111 + 'callback' => [$this, 'bulkAction'],
112 + 'permission_callback' => [$this, 'check_permission'],
113 + ]);
114 +
108 115 register_rest_route($this->namespace, "/{$this->rest_base}/orders", [
109 116 'methods' => 'POST',
110 117 'callback' => [$this, 'update_display_orders'],
111 118 'permission_callback' => [$this, 'check_permission'],
@@ -353,8 +360,10 @@
353 360 $attribute->field_options = $metadata['field_options'] ?? null;
354 361 $attribute->validation_rules = $metadata['validation_rules'] ?? null;
355 362 }
356 363 }
364 +
365 + $this->attachDecodedIconToAttribute($attribute);
357 366
358 367 return $this->success_response($attribute, 201);
359 368 }
360 369
@@ -397,39 +406,9 @@
397 406 $attribute->validation_rules = $metadata['validation_rules'] ?? null;
398 407 }
399 408 }
400 409
401 - // Process icon field - unserialize if it's serialized
402 - if (!empty($attribute->icon)) {
403 - $icon_data = maybe_unserialize($attribute->icon);
404 - if (is_array($icon_data)) {
405 - // Resolve image URLs for image type icons
406 - if ($icon_data['type'] === 'image' && !empty($icon_data['value'])) {
407 - $value = $icon_data['value'];
408 - $image_url = '';
409 -
410 - if (is_numeric($value)) {
411 - $maybe_url = wp_get_attachment_image_url((int) $value, 'large');
412 - if (!empty($maybe_url)) {
413 - $image_url = $maybe_url;
414 - }
415 - } elseif (is_string($value) && filter_var($value, FILTER_VALIDATE_URL)) {
416 - $image_url = $value;
417 - }
418 -
419 - $icon_data['value'] = $image_url;
420 - }
421 - $attribute->icon = $icon_data;
422 - } else {
423 - // Handle legacy string format
424 - $attribute->icon = [
425 - 'type' => 'icon',
426 - 'value' => $attribute->icon
427 - ];
428 - }
429 - } else {
430 - $attribute->icon = null;
431 - }
410 + $this->attachDecodedIconToAttribute($attribute);
432 411
433 412 return $this->success_response($attribute);
434 413
435 414 } catch (\Exception $e) {
@@ -501,8 +480,52 @@
501 480 }
502 481 }
503 482
504 483 /**
484 + * Handle bulk operations
485 + */
486 + public function bulkAction(WP_REST_Request $request): WP_REST_Response
487 + {
488 + try {
489 + $action = sanitize_text_field((string) $request->get_param('action'));
490 + $ids = $request->get_param('ids');
491 +
492 + if (empty($action)) {
493 + return $this->error_response(__('Action is required', 'yatra'), 400);
494 + }
495 +
496 + if (empty($ids) || !is_array($ids)) {
497 + return $this->error_response(__('No attributes selected', 'yatra'), 400);
498 + }
499 +
500 + switch ($action) {
501 + case 'trash':
502 + $result = $this->attributeService->bulkUpdateStatus($ids, 'trash');
503 + break;
504 + case 'publish':
505 + case 'restore':
506 + $result = $this->attributeService->bulkUpdateStatus($ids, 'publish');
507 + break;
508 + case 'draft':
509 + $result = $this->attributeService->bulkUpdateStatus($ids, 'draft');
510 + break;
511 + case 'delete':
512 + $result = $this->attributeService->bulkDelete($ids);
513 + break;
514 + default:
515 + return $this->error_response(__('Invalid action', 'yatra'), 400);
516 + }
517 +
518 + return $this->success_response($result);
519 +
520 + } catch (\InvalidArgumentException $e) {
521 + return $this->error_response($e->getMessage(), 400);
522 + } catch (\Exception $e) {
523 + return $this->error_response($e->getMessage(), 500);
524 + }
525 + }
526 +
527 + /**
505 528 * Get attribute values
506 529 */
507 530 public function get_attribute_values(WP_REST_Request $request)
508 531 {
@@ -692,17 +715,18 @@
692 715 // Handle icon field
693 716 if ($request->has_param('icon')) {
694 717 $icon = $request->get_param('icon');
695 718 if (is_array($icon)) {
696 - // Sanitize icon array
697 - $data['icon'] = [
698 - 'type' => isset($icon['type']) && in_array($icon['type'], ['icon', 'image'], true)
699 - ? $icon['type']
700 - : 'icon',
701 - 'value' => isset($icon['value'])
702 - ? sanitize_text_field($icon['value'])
703 - : '',
704 - ];
719 + $data['icon'] = function_exists('yatra_normalize_icon_picker_for_storage')
720 + ? yatra_normalize_icon_picker_for_storage($icon)
721 + : [
722 + 'type' => isset($icon['type']) && in_array($icon['type'], ['icon', 'image'], true)
723 + ? $icon['type']
724 + : 'icon',
725 + 'value' => isset($icon['value'])
726 + ? sanitize_text_field((string) $icon['value'])
727 + : '',
728 + ];
705 729 } elseif (is_string($icon)) {
706 730 // Handle legacy string format
707 731 $data['icon'] = sanitize_text_field($icon);
708 732 }
@@ -786,42 +810,33 @@
786 810 return $data;
787 811 }
788 812
789 813 /**
790 - * Check permissions for read operations
814 + * Granular permission checks. Trip attributes are a trip-taxonomy
815 + * concept — they classify trips for filtering / display — so the
816 + * Team module's `yatra_manage_trip_taxonomies` cap is the right
817 + * gate for write operations, and `yatra_view_trips` for reads.
818 + * WP administrators pass every cap via the Team module's admin-
819 + * fallback filter so no explicit `manage_options` check is needed.
791 820 */
792 821 public function get_permissions_check(): bool
793 822 {
794 - return current_user_can('manage_options');
823 + return current_user_can('yatra_view_trips');
795 824 }
796 825
797 - /**
798 - * Check permissions for create/update/delete operations
799 - */
800 826 public function check_permission(?WP_REST_Request $request = null): bool
801 827 {
802 - $hasPermission = current_user_can('manage_options');
803 -
804 - if (defined('WP_DEBUG') && WP_DEBUG) {
805 - }
806 -
807 - return $hasPermission;
828 + return current_user_can('yatra_manage_trip_taxonomies');
808 829 }
809 830
810 - /**
811 - * Check permissions for search operations
812 - */
813 831 public function search_permissions_check(): bool
814 832 {
815 - return current_user_can('manage_options');
833 + return current_user_can('yatra_view_trips');
816 834 }
817 835
818 - /**
819 - * Check permissions for update operations
820 - */
821 836 public function update_permissions_check(): bool
822 837 {
823 - return current_user_can('manage_options');
838 + return current_user_can('yatra_manage_trip_taxonomies');
824 839 }
825 840
826 841 /**
827 842 * Get item schema
@@ -936,7 +951,44 @@
936 951 $stats = $this->attributeService->getStatusCounts();
937 952 return $this->success_response($stats);
938 953 } catch (\Exception $e) {
939 954 return $this->error_response($e->getMessage(), 500);
955 + }
956 + }
957 +
958 + /**
959 + * Replace raw DB icon (serialized array or legacy string) with REST JSON (preserves Font Awesome provider).
960 + *
961 + * @param object $attribute Row from AttributeService::getById()
962 + */
963 + private function attachDecodedIconToAttribute(object $attribute): void
964 + {
965 + if (!empty($attribute->icon)) {
966 + $icon_data = maybe_unserialize($attribute->icon);
967 + if (is_array($icon_data)) {
968 + if ($icon_data['type'] === 'image' && !empty($icon_data['value'])) {
969 + $value = $icon_data['value'];
970 + $image_url = '';
971 +
972 + if (is_numeric($value)) {
973 + $maybe_url = wp_get_attachment_image_url((int) $value, 'large');
974 + if (!empty($maybe_url)) {
975 + $image_url = $maybe_url;
976 + }
977 + } elseif (is_string($value) && filter_var($value, FILTER_VALIDATE_URL)) {
978 + $image_url = $value;
979 + }
980 +
981 + $icon_data['value'] = $image_url;
982 + }
983 + $attribute->icon = $icon_data;
984 + } else {
985 + $attribute->icon = [
986 + 'type' => 'icon',
987 + 'value' => (string) $attribute->icon,
988 + ];
989 + }
990 + } else {
991 + $attribute->icon = null;
940 992 }
941 993 }
942 994 }