// SPDX-License-Identifier: AGPL-3.0-or-later namespace OCA\Forum\Controller; use OCA\Forum\Db\ForumUserMapper; use OCA\Forum\Db\RoleMapper; use OCA\Forum\Service\UserService; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Http; use OCP\AppFramework\Http\Attribute\ApiRoute; use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\AppFramework\Http\Attribute\PublicPage; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\OCSController; use OCP\IRequest; use OCP\IUserSession; use Psr\Log\LoggerInterface; /** * Controller for forum users * Note: Forum users are automatically created on first post/thread */ class ForumUserController extends OCSController { public function __construct( string $appName, IRequest $request, private ForumUserMapper $forumUserMapper, private RoleMapper $roleMapper, private UserService $userService, private IUserSession $userSession, private LoggerInterface $logger, ) { parent::__construct($appName, $request); } /** * Get all forum users * * @param int<1, 200> $limit Maximum number of users to return * @param int<0, max> $offset Offset for pagination * @return DataResponse>, array{}> * * 200: Forum users returned */ #[NoAdminRequired] #[PublicPage] #[ApiRoute(verb: 'GET', url: '/api/users')] public function index(int $limit = 200, int $offset = 0): DataResponse { try { $users = array_slice($this->forumUserMapper->findAll(), $offset, $limit); return new DataResponse(array_map(fn ($u) => $u->jsonSerialize(), $users)); } catch (\Exception $e) { $this->logger->error('Error fetching forum users: ' . $e->getMessage()); return new DataResponse(['error' => 'Failed to fetch forum users'], Http::STATUS_INTERNAL_SERVER_ERROR); } } /** * Search Nextcloud users for autocomplete * Returns users matching the search query in the format expected by NcRichContenteditable * Excludes the current user from results (users cannot mention themselves) * * @param string $search Search query (matches against user ID and display name) * @param int<1, 100> $limit Maximum number of results to return * @return DataResponse, array{}> * * 200: Users returned */ #[NoAdminRequired] #[ApiRoute(verb: 'GET', url: '/api/users/autocomplete')] public function autocomplete(string $search = '', int $limit = 10): DataResponse { try { // Exclude current user from autocomplete results $currentUserId = $this->userSession->getUser()?->getUID(); $users = $this->userService->searchUsersForAutocomplete($search, $limit, $currentUserId); return new DataResponse($users); } catch (\Exception $e) { $this->logger->error('Error searching users for autocomplete: ' . $e->getMessage()); return new DataResponse(['error' => 'Failed to search users'], Http::STATUS_INTERNAL_SERVER_ERROR); } } /** * Get forum user by Nextcloud user ID * Special case: use "me" to get current forum user * * @param string $userId Nextcloud user ID or "me" for current user * @return DataResponse, array{}>|DataResponse * * 200: Forum user returned * 404: Forum user not found (user has not posted yet) or guest user accessing "me" */ #[NoAdminRequired] #[PublicPage] #[ApiRoute(verb: 'GET', url: '/api/users/{userId}')] public function show(string $userId): DataResponse { try { $isMe = $userId === 'me'; // Handle "me" as special case for current user if ($isMe) { $currentUser = $this->userSession->getUser(); if (!$currentUser) { // Guest users have no forum profile - return 404 like a user who hasn't posted yet return new DataResponse(['error' => 'Forum user not found'], Http::STATUS_NOT_FOUND); } $userId = $currentUser->getUID(); } try { $forumUser = $this->forumUserMapper->find($userId); $data = $forumUser->jsonSerialize(); } catch (DoesNotExistException $e) { if (!$isMe) { return new DataResponse(['error' => 'Forum user not found'], Http::STATUS_NOT_FOUND); } // For "me", return a minimal response so roles are still included $data = ['userId' => $userId]; } $roles = $this->roleMapper->findByUserId($userId); $data['roles'] = array_map(fn ($role) => $role->jsonSerialize(), $roles); return new DataResponse($data); } catch (\Exception $e) { $this->logger->error('Error fetching forum user: ' . $e->getMessage()); return new DataResponse(['error' => 'Failed to fetch forum user'], Http::STATUS_INTERNAL_SERVER_ERROR); } } /** * Create forum user * Note: This is typically not needed as forum users are auto-created on first post * * @param string $userId Nextcloud user ID * @return DataResponse, array{}> * * 201: Forum user created */ #[NoAdminRequired] #[ApiRoute(verb: 'POST', url: '/api/users')] public function create(string $userId): DataResponse { try { $forumUser = $this->forumUserMapper->createOrUpdate($userId); return new DataResponse($forumUser->jsonSerialize(), Http::STATUS_CREATED); } catch (\Exception $e) { $this->logger->error('Error creating forum user: ' . $e->getMessage()); return new DataResponse(['error' => 'Failed to create forum user'], Http::STATUS_INTERNAL_SERVER_ERROR); } } }