]> BookStack Code Mirror - bookstack/commitdiff
Improved export base64 encoding of images
authorDan Brown <redacted>
Sun, 22 Apr 2018 11:23:43 +0000 (12:23 +0100)
committerDan Brown <redacted>
Sun, 22 Apr 2018 11:23:43 +0000 (12:23 +0100)
Now will use set storage mechanism to find image files.
Fixes #786

Added test to cover

app/Services/ExportService.php
app/Services/ImageService.php
tests/ImageTest.php

index ada2261e454455a6a4f81d2e8896bf619caf9210..01e87f1678ebf7a2616b54b21e9b6cfc9d564a15 100644 (file)
@@ -9,14 +9,16 @@ class ExportService
 {
 
     protected $entityRepo;
+    protected $imageService;
 
     /**
      * ExportService constructor.
      * @param $entityRepo
      */
-    public function __construct(EntityRepo $entityRepo)
+    public function __construct(EntityRepo $entityRepo, ImageService $imageService)
     {
         $this->entityRepo = $entityRepo;
+        $this->imageService = $imageService;
     }
 
     /**
@@ -24,6 +26,7 @@ class ExportService
      * Includes required CSS & image content. Images are base64 encoded into the HTML.
      * @param Page $page
      * @return mixed|string
+     * @throws \Throwable
      */
     public function pageToContainedHtml(Page $page)
     {
@@ -38,6 +41,7 @@ class ExportService
      * Convert a chapter to a self-contained HTML file.
      * @param Chapter $chapter
      * @return mixed|string
+     * @throws \Throwable
      */
     public function chapterToContainedHtml(Chapter $chapter)
     {
@@ -56,6 +60,7 @@ class ExportService
      * Convert a book to a self-contained HTML file.
      * @param Book $book
      * @return mixed|string
+     * @throws \Throwable
      */
     public function bookToContainedHtml(Book $book)
     {
@@ -71,6 +76,7 @@ class ExportService
      * Convert a page to a PDF file.
      * @param Page $page
      * @return mixed|string
+     * @throws \Throwable
      */
     public function pageToPdf(Page $page)
     {
@@ -85,6 +91,7 @@ class ExportService
      * Convert a chapter to a PDF file.
      * @param Chapter $chapter
      * @return mixed|string
+     * @throws \Throwable
      */
     public function chapterToPdf(Chapter $chapter)
     {
@@ -103,6 +110,7 @@ class ExportService
      * Convert a book to a PDF file
      * @param Book $book
      * @return string
+     * @throws \Throwable
      */
     public function bookToPdf(Book $book)
     {
@@ -118,6 +126,7 @@ class ExportService
      * Convert normal webpage HTML to a PDF.
      * @param $html
      * @return string
+     * @throws \Exception
      */
     protected function htmlToPdf($html)
     {
@@ -146,45 +155,14 @@ class ExportService
         // Replace image src with base64 encoded image strings
         if (isset($imageTagsOutput[0]) && count($imageTagsOutput[0]) > 0) {
             foreach ($imageTagsOutput[0] as $index => $imgMatch) {
-                $oldImgString = $imgMatch;
+                $oldImgTagString = $imgMatch;
                 $srcString = $imageTagsOutput[2][$index];
-                $isLocal = strpos(trim($srcString), 'http') !== 0;
-                if ($isLocal) {
-                    $pathString = public_path(trim($srcString, '/'));
-                } else {
-                    $pathString = $srcString;
-                }
-
-                // Attempt to find local files even if url not absolute
-                $base = baseUrl('/');
-                if (strpos($srcString, $base) === 0) {
-                    $isLocal = true;
-                    $relString = str_replace($base, '', $srcString);
-                    $pathString = public_path(trim($relString, '/'));
-                }
-
-                if ($isLocal && !file_exists($pathString)) {
-                    continue;
-                }
-                try {
-                    if ($isLocal) {
-                        $imageContent = file_get_contents($pathString);
-                    } else {
-                        $ch = curl_init();
-                        curl_setopt_array($ch, [CURLOPT_URL => $pathString, CURLOPT_RETURNTRANSFER => 1, CURLOPT_CONNECTTIMEOUT => 5]);
-                        $imageContent = curl_exec($ch);
-                        $err = curl_error($ch);
-                        curl_close($ch);
-                        if ($err) {
-                            throw new \Exception("Image fetch failed, Received error: " . $err);
-                        }
-                    }
-                    $imageEncoded = 'data:image/' . pathinfo($pathString, PATHINFO_EXTENSION) . ';base64,' . base64_encode($imageContent);
-                    $newImageString = str_replace($srcString, $imageEncoded, $oldImgString);
-                } catch (\ErrorException $e) {
-                    $newImageString = '';
+                $imageEncoded = $this->imageService->imageUriToBase64($srcString);
+                if ($imageEncoded === null) {
+                    $imageEncoded = $srcString;
                 }
-                $htmlContent = str_replace($oldImgString, $newImageString, $htmlContent);
+                $newImgTagString = str_replace($srcString, $imageEncoded, $oldImgTagString);
+                $htmlContent = str_replace($oldImgTagString, $newImgTagString, $htmlContent);
             }
         }
 
index c2e915e2d91e64353bf303eb40dea4f0c83c3553..589bf870acc3cb4c46b48e2c0699d9c0bd1c8403 100644 (file)
@@ -321,6 +321,52 @@ class ImageService extends UploadService
         return $image;
     }
 
+    /**
+     * Convert a image URI to a Base64 encoded string.
+     * Attempts to find locally via set storage method first.
+     * @param string $uri
+     * @return null|string
+     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
+     */
+    public function imageUriToBase64(string $uri)
+    {
+        $isLocal = strpos(trim($uri), 'http') !== 0;
+
+        // Attempt to find local files even if url not absolute
+        $base = baseUrl('/');
+        if (!$isLocal && strpos($uri, $base) === 0) {
+            $isLocal = true;
+            $uri = str_replace($base, '', $uri);
+        }
+
+        $imageData = null;
+
+        if ($isLocal) {
+            $uri = trim($uri, '/');
+            $storage = $this->getStorage();
+            if ($storage->exists($uri)) {
+                $imageData = $storage->get($uri);
+            }
+        } else {
+            try {
+                $ch = curl_init();
+                curl_setopt_array($ch, [CURLOPT_URL => $uri, CURLOPT_RETURNTRANSFER => 1, CURLOPT_CONNECTTIMEOUT => 5]);
+                $imageData = curl_exec($ch);
+                $err = curl_error($ch);
+                curl_close($ch);
+                if ($err) {
+                    throw new \Exception("Image fetch failed, Received error: " . $err);
+                }
+            } catch (\Exception $e) {}
+        }
+
+        if ($imageData === null) {
+            return null;
+        }
+
+        return 'data:image/' . pathinfo($uri, PATHINFO_EXTENSION) . ';base64,' . base64_encode($imageData);
+    }
+
     /**
      * Gets a public facing url for an image by checking relevant environment variables.
      * @param string $filePath
index 8c96ae9251b3ee4d598f5e4c6a9f09d1246279c2..49912ec4c19d899a879fbee2d1640e4a9ea76ce6 100644 (file)
@@ -106,6 +106,29 @@ class ImageTest extends TestCase
         }
     }
 
+    public function test_secure_images_included_in_exports()
+    {
+        config()->set('filesystems.default', 'local_secure');
+        $this->asEditor();
+        $galleryFile = $this->getTestImage('my-secure-test-upload');
+        $page = Page::first();
+        $expectedPath = storage_path('uploads/images/gallery/' . Date('Y-m-M') . '/my-secure-test-upload');
+
+        $upload = $this->call('POST', '/images/gallery/upload', ['uploaded_to' => $page->id], [], ['file' => $galleryFile], []);
+        $imageUrl = json_decode($upload->getContent(), true)['url'];
+        $page->html .= "<img src=\"{$imageUrl}\">";
+        $page->save();
+        $upload->assertStatus(200);
+
+        $encodedImageContent = base64_encode(file_get_contents($expectedPath));
+        $export = $this->get($page->getUrl('/export/html'));
+        $this->assertTrue(str_contains($export->getContent(), $encodedImageContent), 'Uploaded image in export content');
+
+        if (file_exists($expectedPath)) {
+            unlink($expectedPath);
+        }
+    }
+
     public function test_system_images_remain_public()
     {
         config()->set('filesystems.default', 'local_secure');
Morty Proxy This is a proxified and sanitized view of the page, visit original site.