// SPDX-License-Identifier: AGPL-3.0-or-later namespace OCA\Forum\Controller; use OCA\Forum\Attribute\RequirePermission; use OCA\Forum\Db\BBCodeMapper; 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\DataResponse; use OCP\AppFramework\OCSController; use OCP\IRequest; use Psr\Log\LoggerInterface; class BBCodeController extends OCSController { public function __construct( string $appName, IRequest $request, private BBCodeMapper $bbCodeMapper, private LoggerInterface $logger, ) { parent::__construct($appName, $request); } /** * Get all BBCodes (excludes builtin codes) * * @param int<1, 100> $limit Maximum number of BBCodes to return * @param int<0, max> $offset Offset for pagination * @return DataResponse>, array{}> * * 200: BBCodes returned */ #[NoAdminRequired] #[RequirePermission('canEditBbcodes')] #[ApiRoute(verb: 'GET', url: '/api/bbcodes')] public function index(int $limit = 100, int $offset = 0): DataResponse { try { $bbcodes = array_slice($this->bbCodeMapper->findAllNonBuiltin(), $offset, $limit); return new DataResponse(array_map(fn ($b) => $b->jsonSerialize(), $bbcodes)); } catch (\Exception $e) { $this->logger->error('Error fetching BBCodes: ' . $e->getMessage()); return new DataResponse(['error' => 'Failed to fetch BBCodes'], Http::STATUS_INTERNAL_SERVER_ERROR); } } /** * Get enabled BBCodes * * @param int<1, 100> $limit Maximum number of BBCodes to return * @param int<0, max> $offset Offset for pagination * @return DataResponse>, array{}> * * 200: Enabled BBCodes returned */ #[NoAdminRequired] #[ApiRoute(verb: 'GET', url: '/api/bbcodes/enabled')] public function enabled(int $limit = 100, int $offset = 0): DataResponse { try { $bbcodes = array_slice($this->bbCodeMapper->findAllEnabled(), $offset, $limit); return new DataResponse(array_map(fn ($b) => $b->jsonSerialize(), $bbcodes)); } catch (\Exception $e) { $this->logger->error('Error fetching enabled BBCodes: ' . $e->getMessage()); return new DataResponse(['error' => 'Failed to fetch BBCodes'], Http::STATUS_INTERNAL_SERVER_ERROR); } } /** * Get builtin BBCodes (for help dialog) * * @param int<1, 100> $limit Maximum number of BBCodes to return * @param int<0, max> $offset Offset for pagination * @return DataResponse>, array{}> * * 200: Builtin BBCodes returned */ #[NoAdminRequired] #[ApiRoute(verb: 'GET', url: '/api/bbcodes/builtin')] public function builtin(int $limit = 100, int $offset = 0): DataResponse { try { $bbcodes = array_slice($this->bbCodeMapper->findAllBuiltin(), $offset, $limit); return new DataResponse(array_map(fn ($b) => $b->jsonSerialize(), $bbcodes)); } catch (\Exception $e) { $this->logger->error('Error fetching builtin BBCodes: ' . $e->getMessage()); return new DataResponse(['error' => 'Failed to fetch BBCodes'], Http::STATUS_INTERNAL_SERVER_ERROR); } } /** * Get a single BBCode * * @param int $id BBCode ID * @return DataResponse, array{}> * * 200: BBCode returned */ #[NoAdminRequired] #[RequirePermission('canEditBbcodes')] #[ApiRoute(verb: 'GET', url: '/api/bbcodes/{id}')] public function show(int $id): DataResponse { try { $bbcode = $this->bbCodeMapper->find($id); // Prevent access to builtin BBCodes if ($bbcode->getIsBuiltin()) { return new DataResponse(['error' => 'Cannot access builtin BBCode'], Http::STATUS_FORBIDDEN); } return new DataResponse($bbcode->jsonSerialize()); } catch (DoesNotExistException $e) { return new DataResponse(['error' => 'BBCode not found'], Http::STATUS_NOT_FOUND); } catch (\Exception $e) { $this->logger->error('Error fetching BBCode: ' . $e->getMessage()); return new DataResponse(['error' => 'Failed to fetch BBCode'], Http::STATUS_INTERNAL_SERVER_ERROR); } } /** * Create a new BBCode * * @param string $tag BBCode tag name * @param string $replacement Replacement pattern * @param string $example Example usage * @param string|null $description Optional description * @param bool $enabled Whether BBCode is enabled * @param bool $parseInner Whether to parse inner BBCode tags * @return DataResponse, array{}> * * 201: BBCode created */ #[NoAdminRequired] #[RequirePermission('canEditBbcodes')] #[ApiRoute(verb: 'POST', url: '/api/bbcodes')] public function create(string $tag, string $replacement, string $example, ?string $description = null, bool $enabled = true, bool $parseInner = true): DataResponse { try { $bbcode = new \OCA\Forum\Db\BBCode(); $bbcode->setTag($tag); $bbcode->setReplacement($replacement); $bbcode->setExample($example); $bbcode->setDescription($description); $bbcode->setEnabled($enabled); $bbcode->setParseInner($parseInner); $bbcode->setIsBuiltin(false); // User-created BBCodes are never builtin $bbcode->setCreatedAt(time()); /** @var \OCA\Forum\Db\BBCode */ $createdBBCode = $this->bbCodeMapper->insert($bbcode); return new DataResponse($createdBBCode->jsonSerialize(), Http::STATUS_CREATED); } catch (\Exception $e) { $this->logger->error('Error creating BBCode: ' . $e->getMessage()); return new DataResponse(['error' => 'Failed to create BBCode'], Http::STATUS_INTERNAL_SERVER_ERROR); } } /** * Update a BBCode * * @param int $id BBCode ID * @param string|null $tag BBCode tag name * @param string|null $replacement Replacement pattern * @param string|null $example Example usage * @param string|null $description Description * @param bool|null $enabled Whether BBCode is enabled * @param bool|null $parseInner Whether to parse inner BBCode tags * @return DataResponse, array{}> * * 200: BBCode updated */ #[NoAdminRequired] #[RequirePermission('canEditBbcodes')] #[ApiRoute(verb: 'PUT', url: '/api/bbcodes/{id}')] public function update(int $id, ?string $tag = null, ?string $replacement = null, ?string $example = null, ?string $description = null, ?bool $enabled = null, ?bool $parseInner = null): DataResponse { try { $bbcode = $this->bbCodeMapper->find($id); // Prevent updating builtin BBCodes if ($bbcode->getIsBuiltin()) { return new DataResponse(['error' => 'Cannot update builtin BBCode'], Http::STATUS_FORBIDDEN); } if ($tag !== null) { $bbcode->setTag($tag); } if ($replacement !== null) { $bbcode->setReplacement($replacement); } if ($example !== null) { $bbcode->setExample($example); } if ($description !== null) { $bbcode->setDescription($description); } if ($enabled !== null) { $bbcode->setEnabled($enabled); } if ($parseInner !== null) { $bbcode->setParseInner($parseInner); } /** @var \OCA\Forum\Db\BBCode */ $updatedBBCode = $this->bbCodeMapper->update($bbcode); return new DataResponse($updatedBBCode->jsonSerialize()); } catch (DoesNotExistException $e) { return new DataResponse(['error' => 'BBCode not found'], Http::STATUS_NOT_FOUND); } catch (\Exception $e) { $this->logger->error('Error updating BBCode: ' . $e->getMessage()); return new DataResponse(['error' => 'Failed to update BBCode'], Http::STATUS_INTERNAL_SERVER_ERROR); } } /** * Delete a BBCode * * @param int $id BBCode ID * @return DataResponse * * 200: BBCode deleted */ #[NoAdminRequired] #[RequirePermission('canEditBbcodes')] #[ApiRoute(verb: 'DELETE', url: '/api/bbcodes/{id}')] public function destroy(int $id): DataResponse { try { $bbcode = $this->bbCodeMapper->find($id); // Prevent deleting builtin BBCodes if ($bbcode->getIsBuiltin()) { return new DataResponse(['error' => 'Cannot delete builtin BBCode'], Http::STATUS_FORBIDDEN); } $this->bbCodeMapper->delete($bbcode); return new DataResponse(['success' => true]); } catch (DoesNotExistException $e) { return new DataResponse(['error' => 'BBCode not found'], Http::STATUS_NOT_FOUND); } catch (\Exception $e) { $this->logger->error('Error deleting BBCode: ' . $e->getMessage()); return new DataResponse(['error' => 'Failed to delete BBCode'], Http::STATUS_INTERNAL_SERVER_ERROR); } } }