]> BookStack Code Mirror - bookstack/blobdiff - app/Uploads/FaviconHandler.php
Themes: Documented public file serving
[bookstack] / app / Uploads / FaviconHandler.php
index 78c9a899b1bf009d21cbe47475a2092ca94927c7..d5044943cb7305eb43b03023ab6fcc8acd8aacad 100644 (file)
@@ -3,13 +3,15 @@
 namespace BookStack\Uploads;
 
 use Illuminate\Http\UploadedFile;
-use Intervention\Image\ImageManager;
 
 class FaviconHandler
 {
+    protected string $path;
+
     public function __construct(
-        protected ImageManager $imageTool
+        protected ImageResizer $imageResizer,
     ) {
+        $this->path = public_path('favicon.ico');
     }
 
     /**
@@ -17,31 +19,67 @@ class FaviconHandler
      */
     public function saveForUploadedImage(UploadedFile $file): void
     {
+        if (!is_writeable($this->path)) {
+            return;
+        }
+
         $imageData = file_get_contents($file->getRealPath());
-        $image = $this->imageTool->make($imageData);
-        $image->resize(32, 32);
-        $bmpData = $image->encode('bmp');
-        $icoData = $this->bmpToIco($bmpData, 32, 32);
-
-        // TODO - Below are test paths
-        file_put_contents(public_path('uploads/test.ico'), $icoData);
-        file_put_contents(public_path('uploads/test.bmp'), $bmpData);
-
-        // TODO - Permission check for icon overwrite
-        // TODO - Write to correct location
-        // TODO - Handle deletion and restore of original icon on user icon clear
+        $pngData = $this->imageResizer->resizeImageData($imageData, 32, 32, false, 'png');
+        $icoData = $this->pngToIco($pngData, 32, 32);
+
+        file_put_contents($this->path, $icoData);
+    }
+
+    /**
+     * Restore the original favicon image.
+     * Returned boolean indicates if the copy occurred.
+     */
+    public function restoreOriginal(): bool
+    {
+        $permissionItem = file_exists($this->path) ? $this->path : dirname($this->path);
+        if (!is_writeable($permissionItem)) {
+            return false;
+        }
+
+        return copy($this->getOriginalPath(), $this->path);
+    }
+
+    /**
+     * Restore the original favicon image if no favicon image is already in use.
+     * Returns a boolean to indicate if the file exists.
+     */
+    public function restoreOriginalIfNotExists(): bool
+    {
+        if (file_exists($this->path)) {
+            return true;
+        }
+
+        return $this->restoreOriginal();
+    }
+
+    /**
+     * Get the path to the favicon file.
+     */
+    public function getPath(): string
+    {
+        return $this->path;
     }
 
     /**
-     * Convert BMP image data to ICO file format.
+     * Get the path of the original favicon copy.
+     */
+    public function getOriginalPath(): string
+    {
+        return public_path('icon.ico');
+    }
+
+    /**
+     * Convert PNG image data to ICO file format.
      * Built following the file format info from Wikipedia:
      * https://en.wikipedia.org/wiki/ICO_(file_format)
      */
-    protected function bmpToIco(string $bmpData, int $width, int $height): string
+    protected function pngToIco(string $pngData, int $width, int $height): string
     {
-        // Trim off the header of the bitmap file
-        $rawBmpData = substr($bmpData, 14);
-
         // ICO header
         $header = pack('v', 0x00); // Reserved. Must always be 0
         $header .= pack('v', 0x01); // Specifies ico image
@@ -56,14 +94,14 @@ class FaviconHandler
         // Color planes, Appears to remain 1 for bmp image data
         $entry .= pack('v', 0x01);
         // Bits per pixel, can range from 1 to 32. From testing conversion
-        // via intervention from png typically provides this as 32.
-        $entry .= pack('v', 0x20);
+        // via intervention from png typically provides this as 24.
+        $entry .= pack('v', 0x00);
         // Size of the image data in bytes
-        $entry .= pack('V', strlen($rawBmpData));
+        $entry .= pack('V', strlen($pngData));
         // Offset of the bmp data from file start
         $entry .= pack('V', strlen($header) + strlen($entry) + 4);
 
         // Join & return the combined parts of the ICO image data
-        return $header . $entry . $rawBmpData;
+        return $header . $entry . $pngData;
     }
 }