| @@ -51,19 +51,24 @@ | ||
| 51 | 51 | ], |
| 52 | 52 | ]); |
| 53 | 53 | } |
| 54 | 54 | |
| 55 | + /** | |
| 56 | + * Module toggle — critical-sensitivity cap. Enabling or disabling | |
| 57 | + * a Pro module flips significant feature surfaces (Webhooks, | |
| 58 | + * Channel Manager, Team & Access, etc.) on or off, so only the | |
| 59 | + * Owner role holds `yatra_manage_modules` by default. The | |
| 60 | + * previous implementation gated this on `yatra_edit_trips` which | |
| 61 | + * meant any trip editor could toggle modules — that was the wrong | |
| 62 | + * cap entirely. WP admins pass via the Team module's admin- | |
| 63 | + * fallback filter. | |
| 64 | + */ | |
| 55 | 65 | public function check_permission(?WP_REST_Request $request = null): bool |
| 56 | 66 | { |
| 57 | 67 | if (!is_user_logged_in()) { |
| 58 | 68 | return false; |
| 59 | 69 | } |
| 60 | - | |
| 61 | - if (current_user_can('manage_options')) { | |
| 62 | - return true; | |
| 63 | - } | |
| 64 | - | |
| 65 | - return current_user_can('yatra_edit_trips'); | |
| 70 | + return current_user_can('yatra_manage_modules'); | |
| 66 | 71 | } |
| 67 | 72 | |
| 68 | 73 | public function get_modules(): WP_REST_Response |
| 69 | 74 | { |
| @@ -100,8 +105,9 @@ | ||
| 100 | 105 | // Check if trying to enable a premium module without Pro |
| 101 | 106 | if ($enabled && !empty($target_module['is_premium']) && !$target_module['is_available']) { |
| 102 | 107 | return $this->error_response( |
| 103 | 108 | sprintf( |
| 109 | + /* translators: %s: module name. */ | |
| 104 | 110 | __('%s is a premium module. Yatra Pro is required to enable this module.', 'yatra'), |
| 105 | 111 | $target_module['name'] |
| 106 | 112 | ), |
| 107 | 113 | 403 |
| @@ -113,8 +119,9 @@ | ||
| 113 | 119 | $pro_active = apply_filters('yatra_is_pro_active', false); |
| 114 | 120 | if (!$pro_active) { |
| 115 | 121 | return $this->error_response( |
| 116 | 122 | sprintf( |
| 123 | + /* translators: %s: module name. */ | |
| 117 | 124 | __('%s requires Yatra Pro. Please install and activate Yatra Pro to enable this module.', 'yatra'), |
| 118 | 125 | $target_module['name'] |
| 119 | 126 | ), |
| 120 | 127 | 403 |
| @@ -121,8 +128,37 @@ | ||
| 121 | 128 | ); |
| 122 | 129 | } |
| 123 | 130 | } |
| 124 | 131 | |
| 132 | + // Agency-tier gate: even with Pro active, the white-label/agency-only | |
| 133 | + // modules need an Agency Yearly or Lifetime license. | |
| 134 | + if ($enabled && !empty($target_module['requires_agency'])) { | |
| 135 | + if (!apply_filters('yatra_is_agency_active', false)) { | |
| 136 | + return $this->error_response( | |
| 137 | + sprintf( | |
| 138 | + /* translators: %s: module name. */ | |
| 139 | + __('%s is available on the Yatra Pro Scale plan only. Upgrade your license to enable it.', 'yatra'), | |
| 140 | + $target_module['name'] | |
| 141 | + ), | |
| 142 | + 403 | |
| 143 | + ); | |
| 144 | + } | |
| 145 | + } | |
| 146 | + | |
| 147 | + // Growth-or-Agency gate (e.g. AI Assistant). | |
| 148 | + if ($enabled && !empty($target_module['requires_growth_or_agency'])) { | |
| 149 | + if (!apply_filters('yatra_is_ai_eligible', false)) { | |
| 150 | + return $this->error_response( | |
| 151 | + sprintf( | |
| 152 | + /* translators: %s: module name. */ | |
| 153 | + __('%s requires a Growth or Scale license. Upgrade your plan to enable it.', 'yatra'), | |
| 154 | + $target_module['name'] | |
| 155 | + ), | |
| 156 | + 403 | |
| 157 | + ); | |
| 158 | + } | |
| 159 | + } | |
| 160 | + | |
| 125 | 161 | $updated = ModuleManager::setModuleStatus($slug, (bool) $enabled); |
| 126 | 162 | |
| 127 | 163 | return $this->success_response([ |
| 128 | 164 | 'data' => $updated, |
| @@ -177,8 +213,22 @@ | ||
| 177 | 213 | continue; |
| 178 | 214 | } |
| 179 | 215 | } |
| 180 | 216 | |
| 217 | + if ($enabled && !empty($target_module['requires_agency'])) { | |
| 218 | + if (!apply_filters('yatra_is_agency_active', false)) { | |
| 219 | + $blocked_modules[] = $target_module['name']; | |
| 220 | + continue; | |
| 221 | + } | |
| 222 | + } | |
| 223 | + | |
| 224 | + if ($enabled && !empty($target_module['requires_growth_or_agency'])) { | |
| 225 | + if (!apply_filters('yatra_is_ai_eligible', false)) { | |
| 226 | + $blocked_modules[] = $target_module['name']; | |
| 227 | + continue; | |
| 228 | + } | |
| 229 | + } | |
| 230 | + | |
| 181 | 231 | $sanitized[] = [ |
| 182 | 232 | 'slug' => $slug, |
| 183 | 233 | 'enabled' => (bool) $enabled, |
| 184 | 234 | ]; |
| @@ -186,8 +236,9 @@ | ||
| 186 | 236 | |
| 187 | 237 | if (empty($sanitized) && !empty($blocked_modules)) { |
| 188 | 238 | return $this->error_response( |
| 189 | 239 | sprintf( |
| 240 | + /* translators: %s: comma-separated list of module names. */ | |
| 190 | 241 | __('The following modules require Yatra Pro: %s', 'yatra'), |
| 191 | 242 | implode(', ', $blocked_modules) |
| 192 | 243 | ), |
| 193 | 244 | 403 |
| @@ -196,12 +247,13 @@ | ||
| 196 | 247 | |
| 197 | 248 | if (!empty($blocked_modules)) { |
| 198 | 249 | // Partial success - some modules processed, some blocked |
| 199 | 250 | $updated = ModuleManager::setMultipleStatuses($sanitized); |
| 200 | - | |
| 251 | + | |
| 201 | 252 | return $this->success_response([ |
| 202 | 253 | 'data' => $updated, |
| 203 | 254 | 'message' => sprintf( |
| 255 | + /* translators: %s: comma-separated list of module names. */ | |
| 204 | 256 | __('Some modules were skipped because they require Yatra Pro: %s', 'yatra'), |
| 205 | 257 | implode(', ', $blocked_modules) |
| 206 | 258 | ), |
| 207 | 259 | ]); |