| @@ -4,8 +4,9 @@ | ||
| 4 | 4 | |
| 5 | 5 | use FluentBoards\App\Models\Board; |
| 6 | 6 | use FluentBoards\App\Models\User; |
| 7 | 7 | use FluentBoards\App\Services\Helper; |
| 8 | +use FluentBoards\App\Services\MCPAccessService; | |
| 8 | 9 | use FluentBoards\App\Services\PermissionManager; |
| 9 | 10 | use FluentBoards\App\Services\UserService; |
| 10 | 11 | use FluentBoards\Framework\Http\Request\Request; |
| 11 | 12 | use FluentCrm\App\Models\Subscriber; |
| @@ -188,149 +189,74 @@ | ||
| 188 | 189 | 200 |
| 189 | 190 | ); |
| 190 | 191 | } |
| 191 | 192 | |
| 193 | + /** Update the authenticated member's display name. */ | |
| 192 | 194 | public function updateDisplayName(Request $request, $user_id) |
| 193 | 195 | { |
| 194 | - $user_id = absint($user_id); | |
| 195 | - $currentUserId = get_current_user_id(); | |
| 196 | - | |
| 197 | - if ($currentUserId !== $user_id && !PermissionManager::isAdmin($currentUserId)) { | |
| 198 | - return $this->sendError( | |
| 199 | - __('You do not have permission to update this display name', 'fluent-boards'), | |
| 200 | - 403 | |
| 201 | - ); | |
| 196 | + try { | |
| 197 | + return $this->sendSuccess([ | |
| 198 | + 'message' => __('Display name has been updated', 'fluent-boards'), | |
| 199 | + 'user' => $this->userService->updateDisplayName($user_id, $request->get('display_name')), | |
| 200 | + ]); | |
| 201 | + } catch (\Exception $e) { | |
| 202 | + return $this->sendError(['message' => $e->getMessage()], $e->getCode() === 403 ? 403 : 400); | |
| 202 | 203 | } |
| 203 | - | |
| 204 | - $displayName = $request->getSafe('display_name', 'sanitize_text_field'); | |
| 205 | - | |
| 206 | - if(!$displayName) { | |
| 207 | - return $this->sendError('Display name is required', 400); | |
| 208 | - } | |
| 209 | - | |
| 210 | - $updateResult = wp_update_user([ | |
| 211 | - 'ID' => $user_id, | |
| 212 | - 'display_name' => $displayName, | |
| 213 | - ]); | |
| 214 | - | |
| 215 | - if (is_wp_error($updateResult)) { | |
| 216 | - return $this->sendError( | |
| 217 | - $updateResult->get_error_message(), | |
| 218 | - 400 | |
| 219 | - ); | |
| 220 | - } | |
| 221 | - | |
| 222 | - $user = User::findOrFail($user_id); | |
| 223 | - $user = Helper::sanitizeUserCollections($user); | |
| 224 | - $user->fbs_role = PermissionManager::isFluentBoardsAdmin($user_id) ? 'fbs_admin' : 'member'; | |
| 225 | - $user->is_wp_admin = user_can($user_id, 'manage_options') ? 'yes' : 'no'; | |
| 226 | - | |
| 227 | - if (defined('FLUENTCRM')) { | |
| 228 | - $subscriber = Subscriber::where('user_id', $user_id)->first(); | |
| 229 | - $user->fluentcrm_subscriber = $subscriber ?? null; | |
| 230 | - } | |
| 231 | - | |
| 232 | - return $this->sendSuccess([ | |
| 233 | - 'message' => __('Display name has been updated', 'fluent-boards'), | |
| 234 | - 'user' => $user, | |
| 235 | - ], 200); | |
| 236 | - | |
| 237 | 204 | } |
| 238 | 205 | |
| 206 | + /** Upload a profile photo for the authenticated member. */ | |
| 239 | 207 | public function updateProfilePhoto(Request $request, $user_id) |
| 240 | -{ | |
| 241 | - $user_id = absint($user_id); | |
| 242 | - $currentUserId = get_current_user_id(); | |
| 243 | - | |
| 244 | - // Only the user themself OR an admin can change the profile picture | |
| 245 | - if ($currentUserId !== $user_id && !PermissionManager::isAdmin($currentUserId)) { | |
| 246 | - return $this->sendError( | |
| 247 | - __('You do not have permission to update this profile photo', 'fluent-boards'), | |
| 248 | - 403 | |
| 249 | - ); | |
| 208 | + { | |
| 209 | + try { | |
| 210 | + return $this->sendSuccess([ | |
| 211 | + 'message' => __('Profile photo has been updated', 'fluent-boards'), | |
| 212 | + 'user' => $this->userService->updateProfilePhoto($user_id, $_FILES['photo'] ?? []), | |
| 213 | + ]); | |
| 214 | + } catch (\Exception $e) { | |
| 215 | + return $this->sendError(['message' => $e->getMessage()], $e->getCode() === 403 ? 403 : 400); | |
| 216 | + } | |
| 250 | 217 | } |
| 251 | 218 | |
| 252 | - // We’ll use native WordPress upload handling | |
| 253 | - if (empty($_FILES['photo']) || !empty($_FILES['photo']['error'])) { | |
| 254 | - return $this->sendError( | |
| 255 | - __('No photo uploaded or upload error', 'fluent-boards'), | |
| 256 | - 400 | |
| 257 | - ); | |
| 258 | - } | |
| 219 | + public function getMcpConnections(Request $request, $id) | |
| 220 | + { | |
| 221 | + if (!current_user_can('list_app_passwords', (int) $id)) { | |
| 222 | + return $this->sendError(['message' => __('You are not allowed to list application passwords.', 'fluent-boards')], 403); | |
| 223 | + } | |
| 259 | 224 | |
| 260 | - // Limit file size to 2MB for profile photos | |
| 261 | - $maxSize = 2 * 1024 * 1024; | |
| 262 | - if ($_FILES['photo']['size'] > $maxSize) { | |
| 263 | - return $this->sendError( | |
| 264 | - __('Photo must be under 2MB', 'fluent-boards'), | |
| 265 | - 400 | |
| 266 | - ); | |
| 225 | + return $this->sendSuccess(MCPAccessService::getProfileState((int) $id)); | |
| 267 | 226 | } |
| 268 | 227 | |
| 269 | - $file = $_FILES['photo']; | |
| 228 | + /** | |
| 229 | + * Create an MCP connection key. The password is only ever in this response. | |
| 230 | + */ | |
| 231 | + public function createMcpConnection(Request $request, $id) | |
| 232 | + { | |
| 233 | + $result = MCPAccessService::createConnection((int) $id, (string) $request->get('name')); | |
| 270 | 234 | |
| 271 | - // Validate MIME type server-side (client-sent type is spoofable) | |
| 272 | - $fileType = wp_check_filetype($file['name'], [ | |
| 273 | - 'jpg|jpeg|jpe' => 'image/jpeg', | |
| 274 | - 'gif' => 'image/gif', | |
| 275 | - 'png' => 'image/png', | |
| 276 | - 'webp' => 'image/webp', | |
| 277 | - ]); | |
| 235 | + if (is_wp_error($result)) { | |
| 236 | + $data = $result->get_error_data(); | |
| 237 | + return $this->sendError(['message' => $result->get_error_message()], $data['status'] ?? 400); | |
| 238 | + } | |
| 278 | 239 | |
| 279 | - if (!$fileType['type']) { | |
| 280 | - return $this->sendError( | |
| 281 | - __('Invalid image type', 'fluent-boards'), | |
| 282 | - 400 | |
| 283 | - ); | |
| 284 | - } | |
| 240 | + nocache_headers(); | |
| 285 | 241 | |
| 286 | - // Load WordPress upload helpers | |
| 287 | - if (!function_exists('wp_handle_upload')) { | |
| 288 | - require_once ABSPATH . 'wp-admin/includes/file.php'; | |
| 242 | + return $this->sendSuccess($result); | |
| 289 | 243 | } |
| 290 | 244 | |
| 291 | - $overrides = [ | |
| 292 | - 'test_form' => false, | |
| 293 | - 'mimes' => [ | |
| 294 | - 'jpg|jpeg|jpe' => 'image/jpeg', | |
| 295 | - 'gif' => 'image/gif', | |
| 296 | - 'png' => 'image/png', | |
| 297 | - 'webp' => 'image/webp', | |
| 298 | - ], | |
| 299 | - ]; | |
| 245 | + public function revokeMcpConnection(Request $request, $id, $uuid) | |
| 246 | + { | |
| 247 | + if (!current_user_can('delete_app_password', (int) $id, $uuid)) { | |
| 248 | + return $this->sendError(['message' => __('You are not allowed to delete this application password.', 'fluent-boards')], 403); | |
| 249 | + } | |
| 300 | 250 | |
| 301 | - $uploaded = wp_handle_upload($file, $overrides); | |
| 251 | + $result = MCPAccessService::revokeConnection((int) $id, sanitize_text_field($uuid)); | |
| 302 | 252 | |
| 303 | - if (isset($uploaded['error'])) { | |
| 304 | - return $this->sendError( | |
| 305 | - $uploaded['error'], | |
| 306 | - 400 | |
| 307 | - ); | |
| 308 | - } | |
| 253 | + if (is_wp_error($result)) { | |
| 254 | + $data = $result->get_error_data(); | |
| 255 | + return $this->sendError(['message' => $result->get_error_message()], $data['status'] ?? 400); | |
| 256 | + } | |
| 309 | 257 | |
| 310 | - $photoUrl = esc_url_raw($uploaded['url']); | |
| 311 | - | |
| 312 | - // Store custom profile photo in user meta | |
| 313 | - update_user_meta($user_id, 'fbs_profile_photo', $photoUrl); | |
| 314 | - | |
| 315 | - // Reload user and sanitize same as in getMemberInfo() | |
| 316 | - $user = User::findOrFail($user_id); | |
| 317 | - $user = Helper::sanitizeUserCollections($user); | |
| 318 | - | |
| 319 | - // Override photo field if your sanitizer does not already use the meta | |
| 320 | - $user->photo = $photoUrl; | |
| 321 | - | |
| 322 | - $user->fbs_role = PermissionManager::isFluentBoardsAdmin($user_id) ? 'fbs_admin' : 'member'; | |
| 323 | - $user->is_wp_admin = user_can($user_id, 'manage_options') ? 'yes' : 'no'; | |
| 324 | - | |
| 325 | - if (defined('FLUENTCRM')) { | |
| 326 | - $subscriber = Subscriber::where('user_id', $user_id)->first(); | |
| 327 | - $user->fluentcrm_subscriber = $subscriber ?? null; | |
| 258 | + return $this->sendSuccess([ | |
| 259 | + 'message' => __('Connection revoked.', 'fluent-boards'), | |
| 260 | + ]); | |
| 328 | 261 | } |
| 329 | - | |
| 330 | - return $this->sendSuccess([ | |
| 331 | - 'message' => __('Profile photo has been updated', 'fluent-boards'), | |
| 332 | - 'photo_url' => $photoUrl, | |
| 333 | - 'user' => $user, | |
| 334 | - ], 200); | |
| 335 | -} | |
| 336 | 262 | } |