mirror of
https://github.com/chenasraf/nextcloud-forum.git
synced 2026-05-18 01:28:58 +00:00
130 lines
3.7 KiB
PHP
130 lines
3.7 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
// SPDX-FileCopyrightText: Chen Asraf <contact@casraf.dev>
|
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
namespace OCA\Forum\Controller;
|
|
|
|
use OCA\Forum\Db\Post;
|
|
use OCA\Forum\Db\Thread;
|
|
use OCA\Forum\Db\ThreadMapper;
|
|
use OCA\Forum\Service\SearchService;
|
|
use OCP\AppFramework\Http;
|
|
use OCP\AppFramework\Http\Attribute\ApiRoute;
|
|
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
|
|
use OCP\AppFramework\Http\DataResponse;
|
|
use OCP\AppFramework\OCSController;
|
|
use OCP\IRequest;
|
|
use OCP\IUserSession;
|
|
use Psr\Log\LoggerInterface;
|
|
|
|
class SearchController extends OCSController {
|
|
public function __construct(
|
|
string $appName,
|
|
IRequest $request,
|
|
private SearchService $searchService,
|
|
private ThreadMapper $threadMapper,
|
|
private IUserSession $userSession,
|
|
private LoggerInterface $logger,
|
|
) {
|
|
parent::__construct($appName, $request);
|
|
}
|
|
|
|
/**
|
|
* Search forum threads and posts
|
|
*
|
|
* @param string $q Search query (supports quoted phrases, AND/OR operators, parentheses, -exclusions)
|
|
* @param bool $searchThreads Include threads in search (title + first post content)
|
|
* @param bool $searchPosts Include reply posts in search
|
|
* @param int|null $categoryId Optional category ID filter
|
|
* @param int $limit Maximum results per type
|
|
* @param int $offset Results offset per type
|
|
* @return DataResponse<Http::STATUS_OK, array{threads: array<string, mixed>, posts: array<string, mixed>, threadCount: int, postCount: int, query: string}, array{}>
|
|
*
|
|
* 200: Search results returned
|
|
*/
|
|
#[NoAdminRequired]
|
|
#[ApiRoute(verb: 'GET', url: '/api/search')]
|
|
public function index(
|
|
string $q = '',
|
|
bool $searchThreads = true,
|
|
bool $searchPosts = true,
|
|
?int $categoryId = null,
|
|
int $limit = 50,
|
|
int $offset = 0,
|
|
): DataResponse {
|
|
try {
|
|
$user = $this->userSession->getUser();
|
|
if (!$user) {
|
|
return new DataResponse(['error' => 'User not authenticated'], Http::STATUS_UNAUTHORIZED);
|
|
}
|
|
|
|
// Validate query
|
|
$q = trim($q);
|
|
if (empty($q)) {
|
|
return new DataResponse([
|
|
'error' => 'Search query is required'
|
|
], Http::STATUS_BAD_REQUEST);
|
|
}
|
|
|
|
// Validate search scope
|
|
if (!$searchThreads && !$searchPosts) {
|
|
return new DataResponse([
|
|
'error' => 'At least one search scope must be selected (threads or posts)'
|
|
], Http::STATUS_BAD_REQUEST);
|
|
}
|
|
|
|
// Perform search
|
|
$results = $this->searchService->search(
|
|
$q,
|
|
$user->getUID(),
|
|
$searchThreads,
|
|
$searchPosts,
|
|
$categoryId,
|
|
$limit,
|
|
$offset
|
|
);
|
|
|
|
// Enrich threads
|
|
$enrichedThreads = array_map(function ($thread) {
|
|
return Thread::enrichThread($thread);
|
|
}, $results['threads']);
|
|
|
|
// Enrich posts (with thread context)
|
|
$enrichedPosts = array_map(function ($post) {
|
|
$enriched = Post::enrichPostContent($post);
|
|
// Add thread info for context
|
|
try {
|
|
$thread = $this->threadMapper->find($post->getThreadId());
|
|
$enriched['threadTitle'] = $thread->getTitle();
|
|
$enriched['threadSlug'] = $thread->getSlug();
|
|
} catch (\OCP\AppFramework\Db\DoesNotExistException $e) {
|
|
// Thread not found (deleted or inaccessible)
|
|
$enriched['threadTitle'] = null;
|
|
$enriched['threadSlug'] = null;
|
|
}
|
|
|
|
return $enriched;
|
|
}, $results['posts']);
|
|
|
|
return new DataResponse([
|
|
'threads' => $enrichedThreads,
|
|
'posts' => $enrichedPosts,
|
|
'threadCount' => $results['threadCount'],
|
|
'postCount' => $results['postCount'],
|
|
'query' => $q,
|
|
]);
|
|
} catch (\Exception $e) {
|
|
$this->logger->error('Error performing search: ' . $e->getMessage(), [
|
|
'exception' => $e,
|
|
'trace' => $e->getTraceAsString(),
|
|
]);
|
|
return new DataResponse([
|
|
'error' => 'Failed to perform search'
|
|
], Http::STATUS_INTERNAL_SERVER_ERROR);
|
|
}
|
|
}
|
|
}
|