validateFile(); if ($fileError !== null) { return Response::error($fileError)->httpStatus(self::HTTP_BAD_REQUEST); } // The server-verified file, never the raw $_FILES entry. Assigned to a // variable first because wp_handle_upload() takes it by reference and // will not accept a method call. $file = $request->validatedFile(); // wp_handle_upload() is an admin-side function, not loaded on REST requests. require_once ABSPATH . 'wp-admin/includes/file.php'; $uploaded = wp_handle_upload($file, ['test_form' => false]); if (isset($uploaded['error'])) { return Response::error('Upload failed: ' . $uploaded['error'])->httpStatus(self::HTTP_SERVER_ERROR); } $userId = (int) $request->id; $attachmentId = wp_insert_attachment( [ 'post_mime_type' => $uploaded['type'], 'post_title' => sanitize_file_name(pathinfo($uploaded['file'], \PATHINFO_FILENAME)), // Named rather than left to derive from the title: a picture // uploaded as `Aiden-Carter.webp` would otherwise take the slug // `aiden-carter`, which is a URL the portal already publishes — // the member's own profile, and any topic that happens to match. // WordPress resolves the attachment there and redirects the // visitor to the raw image. 'post_name' => 'bit-connect-avatar-' . $userId, 'post_content' => '', 'post_status' => 'inherit', 'post_author' => $userId, ], $uploaded['file'] ); if (is_wp_error($attachmentId)) { return Response::error('Failed to store image: ' . $attachmentId->get_error_message()) ->httpStatus(self::HTTP_SERVER_ERROR); } // Generates the sized images AvatarService serves; without this every // avatar would fall back to the full-size original. // wp_generate_attachment_metadata() is admin-side too. require_once ABSPATH . 'wp-admin/includes/image.php'; wp_update_attachment_metadata( $attachmentId, wp_generate_attachment_metadata($attachmentId, $uploaded['file']) ); UploadClaims::markPortal((int) $attachmentId); AvatarService::setAvatar($userId, $attachmentId); return Response::success( [ 'avatar' => AvatarService::avatarUrl($userId, 200), 'id' => $attachmentId, ] ); } public function remove(DeleteAvatarRequest $request) { $userId = (int) $request->id; $removed = AvatarService::removeAvatar($userId); return Response::success( [ // Whatever the member falls back to — normally their Gravatar. 'avatar' => get_avatar_url($userId), 'removed' => $removed, ] ); } }