diff options
author | Julien Veyssier <eneiluj@posteo.net> | 2021-10-22 17:07:07 +0300 |
---|---|---|
committer | Julien Veyssier <eneiluj@posteo.net> | 2022-01-03 12:27:34 +0300 |
commit | 7267f0b71d931ee9c464c1607cf9f5ed7a1a7e87 (patch) | |
tree | aca70e1ca1ede9880f690a7a99162c65e69c0971 | |
parent | a103b9834596abaecf21584ff08543a9ee4315db (diff) |
display/upload/add-link works with attachment folder in user context
Signed-off-by: Julien Veyssier <eneiluj@posteo.net>
-rw-r--r-- | appinfo/routes.php | 2 | ||||
-rw-r--r-- | lib/Controller/ImageController.php | 23 | ||||
-rw-r--r-- | lib/Service/ImageService.php | 91 | ||||
-rw-r--r-- | src/components/EditorWrapper.vue | 1 | ||||
-rw-r--r-- | src/components/MenuBar.vue | 66 | ||||
-rw-r--r-- | src/nodes/ImageView.vue | 17 |
6 files changed, 147 insertions, 53 deletions
diff --git a/appinfo/routes.php b/appinfo/routes.php index 33cfebf24..2e2a75789 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -29,6 +29,8 @@ return [ 'routes' => [ ['name' => 'Image#insertImageLink', 'url' => '/image/link', 'verb' => 'POST'], ['name' => 'Image#uploadImage', 'url' => '/image/upload', 'verb' => 'POST'], + ['name' => 'Image#getImage', 'url' => '/image', 'verb' => 'GET'], + ['name' => 'Image#getImagePublic', 'url' => '/image/public', 'verb' => 'GET'], ['name' => 'Session#create', 'url' => '/session/create', 'verb' => 'PUT'], ['name' => 'Session#fetch', 'url' => '/session/fetch', 'verb' => 'POST'], diff --git a/lib/Controller/ImageController.php b/lib/Controller/ImageController.php index 942967415..91bafa925 100644 --- a/lib/Controller/ImageController.php +++ b/lib/Controller/ImageController.php @@ -29,6 +29,7 @@ use Exception; use OCP\AppFramework\Http; use OCA\Text\Service\ImageService; use OCP\AppFramework\Controller; +use OCP\AppFramework\Http\DataDisplayResponse; use OCP\AppFramework\Http\DataResponse; use OCP\IRequest; @@ -56,8 +57,8 @@ class ImageController extends Controller { /** * @NoAdminRequired */ - public function insertImageLink(string $link): DataResponse { - $downloadResult = $this->imageService->insertImageLink($link, $this->userId); + public function insertImageLink(int $textFileId, string $link): DataResponse { + $downloadResult = $this->imageService->insertImageLink($textFileId, $link, $this->userId); if (isset($downloadResult['error'])) { return new DataResponse($downloadResult, Http::STATUS_BAD_REQUEST); } else { @@ -68,13 +69,13 @@ class ImageController extends Controller { /** * @NoAdminRequired */ - public function uploadImage(string $textFilePath): DataResponse { + public function uploadImage(int $textFileId): DataResponse { try { $file = $this->request->getUploadedFile('image'); if ($file !== null && isset($file['tmp_name'], $file['name'])) { $newFileContent = file_get_contents($file['tmp_name']); $newFileName = $file['name']; - $uploadResult = $this->imageService->uploadImage($textFilePath, $newFileName, $newFileContent, $this->userId); + $uploadResult = $this->imageService->uploadImage($textFileId, $newFileName, $newFileContent, $this->userId); return new DataResponse($uploadResult); } else { return new DataResponse(['error' => 'No uploaded file'], Http::STATUS_BAD_REQUEST); @@ -83,4 +84,18 @@ class ImageController extends Controller { return new DataResponse(['error' => 'Upload error'], Http::STATUS_BAD_REQUEST); } } + + /** + * @NoAdminRequired + * @NoCSRFRequired + */ + public function getImage(int $textFileId, int $imageFileId): DataDisplayResponse { + $imageContent = $this->imageService->getImage($textFileId, $imageFileId, $this->userId); + if ($imageContent !== null) { + return new DataDisplayResponse($imageContent); + } else { + error_log('image not found response'); + return new DataDisplayResponse('', Http::STATUS_NOT_FOUND); + } + } } diff --git a/lib/Service/ImageService.php b/lib/Service/ImageService.php index f0a4a6fe2..3075ef5b8 100644 --- a/lib/Service/ImageService.php +++ b/lib/Service/ImageService.php @@ -28,6 +28,7 @@ namespace OCA\Text\Service; use Exception; use OCP\Files\Folder; +use OCP\Files\File; use Throwable; use GuzzleHttp\Exception\ClientException; use GuzzleHttp\Exception\ConnectException; @@ -70,36 +71,78 @@ class ImageService { } /** - * @param string $textFilePath + * @param int $textFileId + * @param int $imageFileId + * @param string $userId + * @return string|null + * @throws \OCP\Files\NotFoundException + * @throws \OCP\Files\NotPermittedException + * @throws \OCP\Lock\LockedException + * @throws \OC\User\NoUserException + */ + public function getImage(int $textFileId, int $imageFileId, string $userId): ?string { + error_log('ImageService::getImage'); + $attachmentFolder = $this->getOrCreateAttachmentDirectory($textFileId, $userId); + if ($attachmentFolder !== null) { + error_log('ATT OK'); + $imageFile = $attachmentFolder->getById($imageFileId); + if (count($imageFile) > 0) { + error_log('IMG file found'); + $imageFile = $imageFile[0]; + if ($imageFile instanceof File) { + error_log('IMG IS FILE'); + return $imageFile->getContent(); + } + } + } + return null; + } + + /** + * @param int $textFileId * @param string $newFileName * @param string $newFileContent * @param string $userId * @return array + * @throws \OCP\Files\InvalidPathException + * @throws \OCP\Files\NotFoundException + * @throws \OCP\Files\NotPermittedException + * @throws \OC\User\NoUserException */ - public function uploadImage(string $textFilePath, string $newFileName, string $newFileContent, string $userId): array { + public function uploadImage(int $textFileId, string $newFileName, string $newFileContent, string $userId): array { $fileName = (string) time() . '-' . $newFileName; - $saveDir = $this->getOrCreateTextDirectory($userId); + // $saveDir = $this->getOrCreateTextDirectory($userId); + $saveDir = $this->getOrCreateAttachmentDirectory($textFileId, $userId); if ($saveDir !== null) { $savedFile = $saveDir->newFile($fileName, $newFileContent); $path = preg_replace('/^files/', '', $savedFile->getInternalPath()); return [ 'name' => $fileName, 'path' => $path, + 'id' => $savedFile->getId(), ]; } else { return [ - 'error' => 'Impossible to create /Text directory', + 'error' => 'Impossible to get attachment directory', ]; } } /** + * @param string $textFilePath * @param string $link + * @param string $userId * @return array + * @throws \OCP\Files\InvalidPathException + * @throws \OCP\Files\NotFoundException + * @throws \OCP\Files\NotPermittedException + * @throws \OCP\Lock\LockedException + * @throws \OC\User\NoUserException */ - public function insertImageLink(string $link, string $userId): array { + public function insertImageLink(int $textFileId, string $link, string $userId): array { $fileName = (string) time(); - $saveDir = $this->getOrCreateTextDirectory($userId); + // $saveDir = $this->getOrCreateTextDirectory($userId); + $saveDir = $this->getOrCreateAttachmentDirectory($textFileId, $userId); if ($saveDir !== null) { $savedFile = $saveDir->newFile($fileName); $resource = $savedFile->fopen('w'); @@ -125,6 +168,7 @@ class ImageService { return [ 'name' => $fileName, 'path' => $path, + 'id' => $savedFile->getId(), ]; } else { return $res; @@ -151,6 +195,41 @@ class ImageService { } } + /** + * @param int $textFileId + * @param string $userId + * @return Folder|null + * @throws \OCP\Files\InvalidPathException + * @throws \OCP\Files\NotFoundException + * @throws \OCP\Files\NotPermittedException + * @throws \OC\User\NoUserException + */ + private function getOrCreateAttachmentDirectory(int $textFileId, string $userId): ?Folder { + $userFolder = $this->rootFolder->getUserFolder($userId); + $textFile = $userFolder->getById($textFileId); + if (count($textFile) > 0 && $textFile[0] instanceof File) { + $textFile = $textFile[0]; + $owner = $textFile->getOwner(); + $ownerId = $owner->getUID(); + $ownerUserFolder = $this->rootFolder->getUserFolder($ownerId); + $ownerTextFile = $ownerUserFolder->getById($textFile->getId()); + if (count($ownerTextFile) > 0) { + $ownerTextFile = $ownerTextFile[0]; + $ownerParentFolder = $ownerTextFile->getParent(); + $attachmentFolderName = '.' . $textFile->getId(); + if ($ownerParentFolder->nodeExists($attachmentFolderName)) { + $attachmentFolder = $ownerParentFolder->get($attachmentFolderName); + if ($attachmentFolder instanceof Folder) { + return $attachmentFolder; + } + } else { + return $ownerParentFolder->newFolder($attachmentFolderName); + } + } + } + return null; + } + private function simpleDownload(string $url, $resource, array $params = [], string $method = 'GET'): array { $client = $this->clientService->newClient(); try { diff --git a/src/components/EditorWrapper.vue b/src/components/EditorWrapper.vue index 16525892f..9438a439a 100644 --- a/src/components/EditorWrapper.vue +++ b/src/components/EditorWrapper.vue @@ -39,6 +39,7 @@ ref="menubar" :editor="tiptap" :file-path="relativePath" + :file-id="fileId" :is-rich-editor="isRichEditor" :is-public="isPublic" :autohide="autohide" diff --git a/src/components/MenuBar.vue b/src/components/MenuBar.vue index b98f36497..a1b521bb3 100644 --- a/src/components/MenuBar.vue +++ b/src/components/MenuBar.vue @@ -29,7 +29,7 @@ accept="image/*" aria-hidden="true" class="hidden-visually" - @change="onImageFilePicked"> + @change="onImageUploadFilePicked"> <div v-if="isRichEditor" ref="menubar" class="menubar-icons"> <template v-for="(icon, $index) in allIcons"> <EmojiPicker v-if="icon.class === 'icon-emoji'" @@ -184,6 +184,11 @@ export default { required: false, default: '', }, + fileId: { + type: Number, + required: false, + default: 0, + }, }, data: () => { return { @@ -295,6 +300,7 @@ export default { }, }, mounted() { + console.debug('MY FILE ID', this.fileId) window.addEventListener('resize', this.getWindowWidth) this.checkInterval = setInterval(() => { const isWidthAvailable = (this.$refs.menubar && this.$refs.menubar.clientWidth > 0) @@ -337,51 +343,27 @@ export default { this.imageCommand = command this.$refs.imageFileInput.click() }, - onImageFilePicked(event) { + onImageUploadFilePicked(event) { this.uploadingImage = true - - const client = OC.Files.getClient() - const uploadId = new Date().getTime() const files = event.target.files - const filename = uploadId + '-' + files[0].name - const targetDirectoryPath = '/Text' - const targetFilePath = targetDirectoryPath + '/' + filename const image = files[0] // Clear input to ensure that the change event will be emitted if // the same file is picked again. event.target.value = '' - // create /Text - client.getFileInfo(targetDirectoryPath).then((status, fileInfo) => { - if (fileInfo.type === 'dir') { - this.uploadImage(targetFilePath, image) - } else { - this.uploadingImage = false - } - }).catch((error) => { - console.debug('/Text directory does not exist', error) - client.createDirectory('/Text').then((response) => { - this.uploadImage(targetFilePath, image) - }).catch((error) => { - console.error(error) - this.uploadingImage = false - }) - }) - }, - uploadImage(targetFilePath, image) { const formData = new FormData() formData.append('image', image) - formData.append('textFilePath', this.filePath) + formData.append('textFileId', this.fileId) // TODO change the url if we are in a public context const url = generateUrl('/apps/text/image/upload') axios.post(url, formData, { headers: { - 'Content-Type': 'multipart/form-data' - } + 'Content-Type': 'multipart/form-data', + }, }).then((response) => { - console.debug('upload RESPONSE', response.data) - this.insertImage(response.data?.path, this.imageCommand) + // this.insertImage(response.data?.path, this.imageCommand) + this.insertImageById(response.data?.id, response.data?.name, this.imageCommand) }).catch((error) => { console.error(error) showError(error?.response?.data?.error) @@ -389,17 +371,6 @@ export default { this.imageCommand = null this.uploadingImage = false }) - // this can be done in a simple fashion with the file client - /* - const client = OC.Files.getClient() - client.putFileContents(targetFilePath, image, { - contentType: image.type, - contentLength: image.size, - overwrite: false, - }).then((response) => { - this.insertImage(targetFilePath, this.imageCommand) - }) - */ }, onImageLinkUpdateValue(newImageLink) { // this avoids the input being reset on each file polling @@ -411,11 +382,13 @@ export default { this.$refs.imageActions[0].closeMenu() const params = { + textFileId: this.fileId, link: this.imageLink, } const url = generateUrl('/apps/text/image/link') axios.post(url, params).then((response) => { - this.insertImage(response.data?.path, command) + // this.insertImage(response.data?.path, command) + this.insertImageById(response.data?.id, response.data?.name, command) }).catch((error) => { console.error(error) showError(error?.response?.data?.error) @@ -433,6 +406,13 @@ export default { this.insertImage(file, command) }, false, [], true, undefined, this.imagePath) }, + insertImageById(id, name, command) { + const src = 'text://image?imageFileId=' + id + '&textFileId=' + this.fileId + command({ + src, + alt: name, + }) + }, insertImage(path, command) { const client = OC.Files.getClient() client.getFileInfo(path).then((_status, fileInfo) => { diff --git a/src/nodes/ImageView.vue b/src/nodes/ImageView.vue index 8a36cbc7a..07a27ea7e 100644 --- a/src/nodes/ImageView.vue +++ b/src/nodes/ImageView.vue @@ -115,6 +115,23 @@ export default { } }, imageUrl() { + if (this.src.startsWith('text://')) { + const imageFileId = getQueryVariable(this.src, 'imageFileId') + const textFileId = getQueryVariable(this.src, 'textFileId') + if (getCurrentUser()) { + return generateUrl('/apps/text/image?textFileId={textFileId}&imageFileId={imageFileId}', + { + textFileId, + imageFileId, + }) + } else { + return generateUrl('/apps/text/image/public?textFileId={textFileId}&imageFileId={imageFileId}', + { + textFileId, + imageFileId, + }) + } + } if (this.isRemoteUrl || this.isPreviewUrl) { return this.src } |