// SPDX-License-Identifier: AGPL-3.0-or-later namespace OCA\Forum\Controller; use OCA\Forum\Attribute\RequirePermission; use OCA\Forum\Db\CategoryMapper; use OCA\Forum\Db\CatHeaderMapper; 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 Psr\Log\LoggerInterface; class CatHeaderController extends OCSController { public function __construct( string $appName, IRequest $request, private CatHeaderMapper $catHeaderMapper, private CategoryMapper $categoryMapper, private LoggerInterface $logger, ) { parent::__construct($appName, $request); } /** * Get all category headers * * @param int<1, 100> $limit Maximum number of headers to return * @param int<0, max> $offset Offset for pagination * @return DataResponse>, array{}> * * 200: Category headers returned */ #[NoAdminRequired] #[PublicPage] #[ApiRoute(verb: 'GET', url: '/api/headers')] public function index(int $limit = 100, int $offset = 0): DataResponse { try { $headers = array_slice($this->catHeaderMapper->findAll(), $offset, $limit); return new DataResponse(array_map(fn ($h) => $h->jsonSerialize(), $headers)); } catch (\Exception $e) { $this->logger->error('Error fetching category headers: ' . $e->getMessage()); return new DataResponse(['error' => 'Failed to fetch category headers'], Http::STATUS_INTERNAL_SERVER_ERROR); } } /** * Get a single category header * * @param int $id Category header ID * @return DataResponse, array{}> * * 200: Category header returned */ #[NoAdminRequired] #[PublicPage] #[ApiRoute(verb: 'GET', url: '/api/headers/{id}')] public function show(int $id): DataResponse { try { $header = $this->catHeaderMapper->find($id); return new DataResponse($header->jsonSerialize()); } catch (DoesNotExistException $e) { return new DataResponse(['error' => 'Category header not found'], Http::STATUS_NOT_FOUND); } catch (\Exception $e) { $this->logger->error('Error fetching category header: ' . $e->getMessage()); return new DataResponse(['error' => 'Failed to fetch category header'], Http::STATUS_INTERNAL_SERVER_ERROR); } } /** * Create a new category header * * @param string $name Category header name * @param string|null $description Category header description * @param int $sortOrder Sort order * @return DataResponse, array{}> * * 201: Category header created */ #[NoAdminRequired] #[RequirePermission('canEditCategories')] #[ApiRoute(verb: 'POST', url: '/api/headers')] public function create(string $name, ?string $description = null, int $sortOrder = 0): DataResponse { try { $header = new \OCA\Forum\Db\CatHeader(); $header->setName($name); $header->setDescription($description); $header->setSortOrder($sortOrder); $header->setCreatedAt(time()); /** @var \OCA\Forum\Db\CatHeader */ $createdHeader = $this->catHeaderMapper->insert($header); return new DataResponse($createdHeader->jsonSerialize(), Http::STATUS_CREATED); } catch (\Exception $e) { $this->logger->error('Error creating category header: ' . $e->getMessage()); return new DataResponse(['error' => 'Failed to create category header'], Http::STATUS_INTERNAL_SERVER_ERROR); } } /** * Update a category header * * @param int $id Category header ID * @param string|null $name Category header name * @param string|null $description Category header description * @param int|null $sortOrder Sort order * @return DataResponse, array{}> * * 200: Category header updated */ #[NoAdminRequired] #[RequirePermission('canEditCategories')] #[ApiRoute(verb: 'PUT', url: '/api/headers/{id}')] public function update(int $id, ?string $name = null, ?string $description = null, ?int $sortOrder = null): DataResponse { try { $header = $this->catHeaderMapper->find($id); if ($name !== null) { $header->setName($name); } if ($description !== null) { $header->setDescription($description); } if ($sortOrder !== null) { $header->setSortOrder($sortOrder); } /** @var \OCA\Forum\Db\CatHeader */ $updatedHeader = $this->catHeaderMapper->update($header); return new DataResponse($updatedHeader->jsonSerialize()); } catch (DoesNotExistException $e) { return new DataResponse(['error' => 'Category header not found'], Http::STATUS_NOT_FOUND); } catch (\Exception $e) { $this->logger->error('Error updating category header: ' . $e->getMessage()); return new DataResponse(['error' => 'Failed to update category header'], Http::STATUS_INTERNAL_SERVER_ERROR); } } /** * Delete a category header * * @param int $id Category header ID * @param int|null $migrateToHeaderId Header ID to migrate categories to (null to delete categories) * @return DataResponse * * 200: Category header deleted */ #[NoAdminRequired] #[RequirePermission('canEditCategories')] #[ApiRoute(verb: 'DELETE', url: '/api/headers/{id}')] public function destroy(int $id, ?int $migrateToHeaderId = null): DataResponse { try { $header = $this->catHeaderMapper->find($id); $categoriesAffected = 0; // Handle categories migration or deletion if ($migrateToHeaderId !== null) { // Verify target header exists try { $this->catHeaderMapper->find($migrateToHeaderId); } catch (DoesNotExistException $e) { return new DataResponse(['error' => 'Target header not found'], Http::STATUS_NOT_FOUND); } // Move categories to the target header $categoriesAffected = $this->categoryMapper->moveToHeaderId($id, $migrateToHeaderId); } else { // Delete all categories in this header (cascade will handle threads) $categories = $this->categoryMapper->findByHeaderId($id); foreach ($categories as $category) { $this->categoryMapper->delete($category); } $categoriesAffected = count($categories); } // Delete the header $this->catHeaderMapper->delete($header); return new DataResponse([ 'success' => true, 'categoriesAffected' => $categoriesAffected, ]); } catch (DoesNotExistException $e) { return new DataResponse(['error' => 'Category header not found'], Http::STATUS_NOT_FOUND); } catch (\Exception $e) { $this->logger->error('Error deleting category header: ' . $e->getMessage()); return new DataResponse(['error' => 'Failed to delete category header'], Http::STATUS_INTERNAL_SERVER_ERROR); } } /** * Reorder category headers * * @param list $headers Array of headers with new sort orders * @return DataResponse * * 200: Headers reordered successfully */ #[NoAdminRequired] #[RequirePermission('canEditCategories')] #[ApiRoute(verb: 'POST', url: '/api/headers/reorder')] public function reorder(array $headers): DataResponse { try { foreach ($headers as $headerData) { $header = $this->catHeaderMapper->find($headerData['id']); $header->setSortOrder($headerData['sortOrder']); $this->catHeaderMapper->update($header); } return new DataResponse(['success' => true]); } catch (DoesNotExistException $e) { return new DataResponse(['error' => 'Header not found'], Http::STATUS_NOT_FOUND); } catch (\Exception $e) { $this->logger->error('Error reordering headers: ' . $e->getMessage()); return new DataResponse(['error' => 'Failed to reorder headers'], Http::STATUS_INTERNAL_SERVER_ERROR); } } }