diff --git a/lib/Controller/AttachmentsController.php b/lib/Controller/AttachmentsController.php index 6ed8af92..4a882a00 100644 --- a/lib/Controller/AttachmentsController.php +++ b/lib/Controller/AttachmentsController.php @@ -2,6 +2,7 @@ namespace OCA\OpenCatalogi\Controller; +use Exception; use GuzzleHttp\Exception\GuzzleException; use OCA\OpenCatalogi\Db\AttachmentMapper; use OCA\OpenCatalogi\Service\ElasticSearchService; @@ -145,7 +146,7 @@ public function show(string|int $id, ObjectService $objectService): JSONResponse /** * @NoAdminRequired * @NoCSRFRequired - * @throws GuzzleException In case the file upload to NextCloud fails. + * @throws Exception|GuzzleException In case the file upload to NextCloud fails. Or if creating the share link fails. */ public function create(ObjectService $objectService, ElasticSearchService $elasticSearchService): JSONResponse { @@ -215,26 +216,6 @@ public function update(string|int $id, ObjectService $objectService, ElasticSear { $data = $this->request->getParams(); - // Todo: $uploadedFile is empty when doing a PUT... - $uploadedFile = $this->request->getUploadedFile(key: '_file'); - - // Save the uploaded file - $this->fileService->createFolder(folderPath: 'Attachments'); - $this->fileService->uploadFile( - content: file_get_contents(filename: $uploadedFile['tmp_name']), - filePath: 'Attachments/'.$uploadedFile['name'], - update: true - ); - - // Update Attachment data - // Todo: when should we create a new share link? -// $data['downloadUrl'] = $this->fileService->createShareLink(path: 'Attachments/'.$uploadedFile['name']); - $data['type'] = $uploadedFile['type']; - $data['size'] = $uploadedFile['size']; - $explodedName = explode(separator: '.', string: $uploadedFile['name']); - $data['title'] = $explodedName[0]; - $data['extension'] = end(array: $explodedName); - // Remove fields we should never post unset($data['id']); foreach($data as $key => $value) { diff --git a/lib/Service/FileService.php b/lib/Service/FileService.php index 2d70bc85..363d3b2e 100644 --- a/lib/Service/FileService.php +++ b/lib/Service/FileService.php @@ -5,8 +5,10 @@ use Exception; use GuzzleHttp\Client; use GuzzleHttp\Exception\GuzzleException; +use OCP\Files\IRootFolder; use OCP\IAppConfig; use OCP\IUserSession; +use OCP\Share\IShare; use Psr\Log\LoggerInterface; class FileService @@ -17,7 +19,9 @@ class FileService public function __construct( private readonly IUserSession $userSession, private readonly LoggerInterface $logger, - private readonly IAppConfig $config + private readonly IAppConfig $config, + private readonly IRootFolder $rootFolder, + private readonly IShare $share ) { $this->client = new Client(); } @@ -117,38 +121,40 @@ public function createShareLink(string $path, ?int $shareType = 3, ?int $permiss * Uploads a file to NextCloud. Will overwrite a file if it already exists and create a new one if it doesn't exist. * * @param mixed $content The content of the file. - * @param string|null $filePath Path (from root) where to save the file. NOTE: this should include the name and extension/format of the file as well! (example.pdf) - * @param bool|null $update If set to true, the response status code 204 will also be seen as a success result. (NextCloud will return 204 when successfully updating a file) + * @param string $filePath Path (from root) where to save the file. NOTE: this should include the name and extension/format of the file as well! (example.pdf) * * @return bool True if successful. - * @throws GuzzleException|Exception In case the Guzzle call returns an exception. + * @throws Exception In case we can't write to file because it is not permitted. */ - public function uploadFile(mixed $content, ?string $filePath = '', ?bool $update = false): bool + public function uploadFile(mixed $content, string $filePath): bool { - // Get the admin username & password for auth & get the current username - $userInfo = $this->getUserInfo(); + $filePath = trim(string: $filePath, characters: '/'); - // API endpoint to upload the file - $url = $this->getCurrentDomain() . '/remote.php/dav/files/' - . $userInfo['currentUsername'] . '/' . trim(string: $filePath, characters: '/'); + // Get the current user. + $currentUser = $this->userSession->getUser(); + $userFolder = $this->rootFolder->getUserFolder(userId: $currentUser ? $currentUser->getUID() : 'Guest'); + // Check if file exists and create it if not. try { - $response = $this->client->request(method: 'PUT', uri: $url, options: [ - 'auth' => [$userInfo['username'], $userInfo['password']], - 'body' => $content - ]); + try { + $userFolder->get(path: $filePath); + } catch(\OCP\Files\NotFoundException $e) { + $userFolder->newFile(path: $filePath); + $file = $userFolder->get(path: $filePath); + + $file->putContent(data: $content); - // 201 Created indicates that the file was created, 204 No Content indicates that the file was updated. - if ($response->getStatusCode() === 201 || ($update === true && $response->getStatusCode() === 204)) { return true; } - } catch (Exception $e) { - $str = $update === true ? 'update' : 'upload'; - $this->logger->error("File $str failed: " . $e->getMessage()); - throw $e; - } - return false; + // File already exists. + $this->logger->warning("File $filePath already exists."); + return false; + + } catch(\OCP\Files\NotPermittedException $e) { + $this->logger->error("Can't create file $filePath: " . $e->getMessage()); + throw new Exception('Can\'t write to file'); + } } /** @@ -157,101 +163,68 @@ public function uploadFile(mixed $content, ?string $filePath = '', ?bool $update * @param string $filePath Path (from root) to the file you want to delete. * * @return bool True if successful. - * @throws GuzzleException|Exception In case the Guzzle call returns an exception. + * @throws Exception In case deleting the file is not permitted. */ public function deleteFile(string $filePath): bool { - // Get the admin username & password for auth & get the current username - $userInfo = $this->getUserInfo(); + $filePath = trim(string: $filePath, characters: '/'); - // API endpoint to delete the file - $url = $this->getCurrentDomain() . '/remote.php/dav/files/' - . $userInfo['currentUsername'] . '/' . trim(string: $filePath, characters: '/'); + // Get the current user. + $currentUser = $this->userSession->getUser(); + $userFolder = $this->rootFolder->getUserFolder(userId: $currentUser ? $currentUser->getUID() : 'Guest'); + // Check if file exists and delete it if it does. try { - $response = $this->client->request(method: 'DELETE', uri: $url, options: [ - 'auth' => [$userInfo['username'], $userInfo['password']], - ]); + try { + $file = $userFolder->get(path: $filePath); + $file->delete(); - if ($response->getStatusCode() === 204) { // 204 No Content indicates the file was deleted. return true; - } - } catch (Exception $e) { - $this->logger->error('File deletion failed: ' . $e->getMessage()); - throw $e; - } - - return false; - } - - /** - * Checks if a folder exists in NextCloud. - * - * @param string $folderPath Path (from root) to a folder you want to check if exists. - * - * @return bool True if the folder exists. - * @throws GuzzleException|Exception In case the Guzzle call returns an exception. - */ - public function folderExists(string $folderPath): bool - { - // Get the admin username & password for auth & get the current username - $userInfo = $this->getUserInfo(); + } catch(\OCP\Files\NotFoundException $e) { + // File does not exist. + $this->logger->warning("File $filePath does not exist."); - // API endpoint to check if a folder exists - $url = $this->getCurrentDomain() . '/remote.php/dav/files/' - . $userInfo['currentUsername'] . '/' . trim(string: $folderPath, characters: '/'); - - try { - $response = $this->client->request(method: 'PROPFIND', uri: $url, options: [ - 'auth' => [$userInfo['username'], $userInfo['password']], - 'headers' => [ - 'Depth' => '1', - ], - 'body' => '', - ]); - - return $response->getStatusCode() === 207; // Multi-Status indicates the folder exists. - } catch (Exception $e) { - if ($e->getCode() === 404) { return false; } - - $this->logger->error('Folder existence check failed: ' . $e->getMessage()); - throw $e; + } catch(\OCP\Files\NotPermittedException $e) { + $this->logger->error("Can't delete file $filePath: " . $e->getMessage()); + throw new Exception('Can\'t delete file'); } } /** * Creates a new folder in NextCloud, unless it already exists. * - * @param string $folderPath Path (from root) to where you want to create a folder. NOTE: this should include the name of the folder as well! (/Media/exampleFolder) + * @param string $folderPath Path (from root) to where you want to create a folder, include the name of the folder. (/Media/exampleFolder) * * @return bool True if successfully created a new folder. - * @throws GuzzleException|Exception In case the Guzzle call returns an exception. + * @throws Exception In case we can't create the folder because it is not permitted. */ public function createFolder(string $folderPath): bool { - if ($this->folderExists(folderPath: $folderPath) === true) { - $this->logger->info('Folder creation failed: Folder already exists'); - return false; - } - - // Get the admin username & password for auth & get the current username - $userInfo = $this->getUserInfo(); + $folderPath = trim(string: $folderPath, characters: '/'); - // API endpoint to create a folder - $url = $this->getCurrentDomain() . '/remote.php/dav/files/' - . $userInfo['currentUsername'] . '/' . trim(string: $folderPath, characters: '/'); + // Get the current user. + $currentUser = $this->userSession->getUser(); + $userFolder = $this->rootFolder->getUserFolder(userId: $currentUser ? $currentUser->getUID() : 'Guest'); + // Check if folder exists and if not create it. try { - $response = $this->client->request(method: 'MKCOL', uri: $url, options: [ - 'auth' => [$userInfo['username'], $userInfo['password']], - ]); + try { + $userFolder->get(path: $folderPath); + } catch(\OCP\Files\NotFoundException $e) { + $userFolder->newFolder(path: $folderPath); - return $response->getStatusCode() === 201; // 201 Created indicates the folder was created. - } catch (\Exception $e) { - $this->logger->error('Folder creation failed: ' . $e->getMessage()); - throw $e; + return true; + } + + // Folder already exists. + $this->logger->info("This folder already exits $folderPath"); + return false; + + } catch(\OCP\Files\NotPermittedException $e) { + $this->logger->error("Can't create folder $folderPath: " . $e->getMessage()); + throw new Exception('Can\'t create folder'); } }