diff --git a/app/Http/Controllers/ImageController.php b/app/Http/Controllers/ImageController.php index d40f88255..1b064de01 100644 --- a/app/Http/Controllers/ImageController.php +++ b/app/Http/Controllers/ImageController.php @@ -96,6 +96,7 @@ class ImageController extends Controller * @param string $type * @param Request $request * @return \Illuminate\Http\JsonResponse + * @throws \Exception */ public function uploadByType($type, Request $request) { @@ -103,11 +104,12 @@ class ImageController extends Controller $this->validate($request, [ 'file' => 'is_image' ]); + // TODO - Restrict & validate types $imageUpload = $request->file('file'); try { - $uploadedTo = $request->filled('uploaded_to') ? $request->get('uploaded_to') : 0; + $uploadedTo = $request->get('uploaded_to', 0); $image = $this->imageRepo->saveNew($imageUpload, $type, $uploadedTo); } catch (ImageUploadException $e) { return response($e->getMessage(), 500); @@ -116,6 +118,47 @@ class ImageController extends Controller return response()->json($image); } + /** + * Upload a drawing to the system. + * @param Request $request + * @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\JsonResponse|\Symfony\Component\HttpFoundation\Response + */ + public function uploadDrawing(Request $request) + { + $this->validate($request, [ + 'image' => 'required|string', + 'uploaded_to' => 'required|integer' + ]); + $this->checkPermission('image-create-all'); + $imageBase64Data = $request->get('image'); + + try { + $uploadedTo = $request->get('uploaded_to', 0); + $image = $this->imageRepo->saveDrawing($imageBase64Data, $uploadedTo); + } catch (ImageUploadException $e) { + return response($e->getMessage(), 500); + } + + return response()->json($image); + } + + /** + * Get the content of an image based64 encoded. + * @param $id + * @return \Illuminate\Http\JsonResponse|mixed + */ + public function getBase64Image($id) + { + $image = $this->imageRepo->getById($id); + $imageData = $this->imageRepo->getImageData($image); + if ($imageData === null) { + return $this->jsonError("Image data could not be found"); + } + return response()->json([ + 'content' => base64_encode($imageData) + ]); + } + /** * Generate a sized thumbnail for an image. * @param $id @@ -123,6 +166,8 @@ class ImageController extends Controller * @param $height * @param $crop * @return \Illuminate\Http\JsonResponse + * @throws ImageUploadException + * @throws \Exception */ public function getThumbnail($id, $width, $height, $crop) { @@ -137,6 +182,8 @@ class ImageController extends Controller * @param integer $imageId * @param Request $request * @return \Illuminate\Http\JsonResponse + * @throws ImageUploadException + * @throws \Exception */ public function update($imageId, Request $request) { diff --git a/app/Repos/ImageRepo.php b/app/Repos/ImageRepo.php index 8ddde7b0f..734254b3d 100644 --- a/app/Repos/ImageRepo.php +++ b/app/Repos/ImageRepo.php @@ -132,6 +132,8 @@ class ImageRepo * @param string $type * @param int $uploadedTo * @return Image + * @throws \BookStack\Exceptions\ImageUploadException + * @throws \Exception */ public function saveNew(UploadedFile $uploadFile, $type, $uploadedTo = 0) { @@ -140,11 +142,27 @@ class ImageRepo return $image; } + /** + * Save a drawing the the database; + * @param string $base64Uri + * @param int $uploadedTo + * @return Image + * @throws \BookStack\Exceptions\ImageUploadException + */ + public function saveDrawing(string $base64Uri, int $uploadedTo) + { + $name = 'Drawing-' . user()->getShortName(40) . '-' . strval(time()) . '.png'; + $image = $this->imageService->saveNewFromBase64Uri($base64Uri, $name, 'drawing', $uploadedTo); + return $image; + } + /** * Update the details of an image via an array of properties. * @param Image $image * @param array $updateDetails * @return Image + * @throws \BookStack\Exceptions\ImageUploadException + * @throws \Exception */ public function updateImageDetails(Image $image, $updateDetails) { @@ -170,6 +188,8 @@ class ImageRepo /** * Load thumbnails onto an image object. * @param Image $image + * @throws \BookStack\Exceptions\ImageUploadException + * @throws \Exception */ private function loadThumbs(Image $image) { @@ -189,6 +209,8 @@ class ImageRepo * @param int $height * @param bool $keepRatio * @return string + * @throws \BookStack\Exceptions\ImageUploadException + * @throws \Exception */ public function getThumbnail(Image $image, $width = 220, $height = 220, $keepRatio = false) { @@ -200,5 +222,19 @@ class ImageRepo } } + /** + * Get the raw image data from an Image. + * @param Image $image + * @return null|string + */ + public function getImageData(Image $image) + { + try { + return $this->imageService->getImageData($image); + } catch (\Exception $exception) { + return null; + } + } + } \ No newline at end of file diff --git a/app/Services/ImageService.php b/app/Services/ImageService.php index e34b3fb2b..82d1acef7 100644 --- a/app/Services/ImageService.php +++ b/app/Services/ImageService.php @@ -46,6 +46,24 @@ class ImageService extends UploadService return $this->saveNew($imageName, $imageData, $type, $uploadedTo); } + /** + * Save a new image from a uri-encoded base64 string of data. + * @param string $base64Uri + * @param string $name + * @param string $type + * @param int $uploadedTo + * @return Image + * @throws ImageUploadException + */ + public function saveNewFromBase64Uri(string $base64Uri, string $name, string $type, $uploadedTo = 0) + { + $splitData = explode(';base64,', $base64Uri); + if (count($splitData) < 2) { + throw new ImageUploadException("Invalid base64 image data provided"); + } + $data = base64_decode($splitData[1]); + return $this->saveNew($name, $data, $type, $uploadedTo); + } /** * Gets an image from url and saves it to the database. @@ -183,6 +201,19 @@ class ImageService extends UploadService return $this->getPublicUrl($thumbFilePath); } + /** + * Get the raw data content from an image. + * @param Image $image + * @return string + * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException + */ + public function getImageData(Image $image) + { + $imagePath = $this->getPath($image); + $storage = $this->getStorage(); + return $storage->get($imagePath); + } + /** * Destroys an Image object along with its files and thumbnails. * @param Image $image diff --git a/resources/assets/js/pages/page-form.js b/resources/assets/js/pages/page-form.js index e47c575f3..b1b9680c7 100644 --- a/resources/assets/js/pages/page-form.js +++ b/resources/assets/js/pages/page-form.js @@ -47,7 +47,7 @@ function uploadImageFile(file) { let formData = new FormData(); formData.append('file', file, remoteFilename); - return window.$http.post('/images/gallery/upload', formData).then(resp => (resp.data)); + return window.$http.post(window.baseUrl('/images/gallery/upload'), formData).then(resp => (resp.data)); } function registerEditorShortcuts(editor) { @@ -225,25 +225,27 @@ function drawIoPlugin() { const drawIoUrl = 'https://www.draw.io/?embed=1&ui=atlas&spin=1&proto=json'; let iframe = null; let pageEditor = null; + let currentNode = null; function isDrawing(node) { return node.hasAttribute('drawio-diagram'); } - function showDrawingEditor(mceEditor) { + function showDrawingEditor(mceEditor, selectedNode = null) { pageEditor = mceEditor; + currentNode = selectedNode; iframe = document.createElement('iframe'); iframe.setAttribute('frameborder', '0'); window.addEventListener('message', drawReceive); iframe.setAttribute('src', drawIoUrl); iframe.setAttribute('class', 'fullscreen'); + iframe.style.backgroundColor = '#FFFFFF'; document.body.appendChild(iframe); } function drawReceive(event) { if (!event.data || event.data.length < 1) return; let message = JSON.parse(event.data); - console.log(message); if (message.event === 'init') { drawEventInit(); } else if (message.event === 'exit') { @@ -255,19 +257,28 @@ function drawIoPlugin() { } } - function updateContent(svg) { - let svgWrap = document.createElement('div'); - svgWrap.setAttribute('drawio-diagram', svg); - svgWrap.setAttribute('contenteditable', 'false'); - pageEditor.insertContent(svgWrap.outerHTML); - } + function updateContent(pngData) { + let id = "image-" + Math.random().toString(16).slice(2); + let loadingImage = window.baseUrl('/loading.gif'); + let data = { + image: pngData, + uploaded_to: Number(document.getElementById('page-editor').getAttribute('page-id')) + }; - function b64DecodeUnicode(str) { - str = str.split(';base64,')[1]; - // Going backwards: from bytestream, to percent-encoding, to original string. - return decodeURIComponent(atob(str).split('').map(function(c) { - return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2); - }).join('')); + // TODO - Handle updating an existing image + + setTimeout(() => { + pageEditor.insertContent(`