Skip to content

[4.3] Local adapter thumbnails (PR 36552 clone) #2722

@jgerman-bot

Description

@jgerman-bot

New language relevant PR in upstream repo: joomla/joomla-cms#38805 Here are the upstream changes:

Click to expand the diff!
diff --git a/administrator/language/en-GB/plg_filesystem_local.ini b/administrator/language/en-GB/plg_filesystem_local.ini
index c7b40cb9e941..86fe8999570e 100644
--- a/administrator/language/en-GB/plg_filesystem_local.ini
+++ b/administrator/language/en-GB/plg_filesystem_local.ini
@@ -5,6 +5,7 @@
 
 PLG_FILESYSTEM_LOCAL="FileSystem - Local"
 PLG_FILESYSTEM_LOCAL_DEFAULT_NAME="Local"
-PLG_FILESYSTEM_LOCAL_DIRECTORIES_DIRECTORY_LABEL="Select directories"
+PLG_FILESYSTEM_LOCAL_DIRECTORIES_DIRECTORY_LABEL="Select Directories"
+PLG_FILESYSTEM_LOCAL_DIRECTORIES_DIRECTORY_THUMBNAILS_LABEL="Create Thumbnails"
 PLG_FILESYSTEM_LOCAL_DIRECTORIES_LABEL="Directories"
 PLG_FILESYSTEM_LOCAL_XML_DESCRIPTION="Filesystem plugin to define one or multiple local directories to store your media files."
diff --git a/libraries/src/Image/Image.php b/libraries/src/Image/Image.php
index 32b01a766b1e..688ba83b02a5 100644
--- a/libraries/src/Image/Image.php
+++ b/libraries/src/Image/Image.php
@@ -305,17 +305,18 @@ public function generateThumbs($thumbSizes, $creationMethod = self::SCALE_INSIDE
     /**
      * Method to create thumbnails from the current image and save them to disk. It allows creation by resizing or cropping the original image.
      *
-     * @param   mixed    $thumbSizes      string or array of strings. Example: $thumbSizes = array('150x75','250x150');
-     * @param   integer  $creationMethod  1-3 resize $scaleMethod | 4 create cropping
-     * @param   string   $thumbsFolder    destination thumbs folder. null generates a thumbs folder in the image folder
+     * @param   mixed    $thumbSizes       string or array of strings. Example: $thumbSizes = ['150x75','250x150'];
+     * @param   integer  $creationMethod   1-3 resize $scaleMethod | 4 create cropping
+     * @param   string   $thumbsFolder     destination thumbs folder. null generates a thumbs folder in the image folder
+     * @param   boolean  $useOriginalName  Shall we use the original image name? Defaults is false, {filename}_{width}x{height}.{ext}
      *
      * @return  array
      *
-     * @since   2.5.0
+     * @since   __DEPLOY_VERSION__
      * @throws  \LogicException
      * @throws  \InvalidArgumentException
      */
-    public function createThumbs($thumbSizes, $creationMethod = self::SCALE_INSIDE, $thumbsFolder = null)
+    public function createThumbnails($thumbSizes, $creationMethod = self::SCALE_INSIDE, $thumbsFolder = null, $useOriginalName = false)
     {
         // Make sure the resource handle is valid.
         if (!$this->isLoaded()) {
@@ -349,8 +350,13 @@ public function createThumbs($thumbSizes, $creationMethod = self::SCALE_INSIDE,
                 $thumbWidth  = $thumb->getWidth();
                 $thumbHeight = $thumb->getHeight();
 
-                // Generate thumb name
-                $thumbFileName = $filename . '_' . $thumbWidth . 'x' . $thumbHeight . '.' . $fileExtension;
+                if ($useOriginalName) {
+                    // Generate thumb name
+                    $thumbFileName = $filename . '.' . $fileExtension;
+                } else {
+                    // Generate thumb name
+                    $thumbFileName = $filename . '_' . $thumbWidth . 'x' . $thumbHeight . '.' . $fileExtension;
+                }
 
                 // Save thumb file to disk
                 $thumbFileName = $thumbsFolder . '/' . $thumbFileName;
@@ -366,6 +372,26 @@ public function createThumbs($thumbSizes, $creationMethod = self::SCALE_INSIDE,
         return $thumbsCreated;
     }
 
+    /**
+     * Method to create thumbnails from the current image and save them to disk. It allows creation by resizing or cropping the original image.
+     *
+     * @param   mixed    $thumbSizes       string or array of strings. Example: $thumbSizes = ['150x75','250x150'];
+     * @param   integer  $creationMethod   1-3 resize $scaleMethod | 4 create cropping
+     * @param   string   $thumbsFolder     destination thumbs folder. null generates a thumbs folder in the image folder
+     *
+     * @return  array
+     *
+     * @since   2.5.0
+     * @throws  \LogicException
+     * @throws  \InvalidArgumentException
+     *
+     * @deprecated 6.0 Use \Joomla\CMS\Image\createThumbnails instead
+     */
+    public function createThumbs($thumbSizes, $creationMethod = self::SCALE_INSIDE, $thumbsFolder = null)
+    {
+        return $this->createThumbnails($thumbSizes, $creationMethod, $thumbsFolder, false);
+    }
+
     /**
      * Method to crop the current image.
      *
diff --git a/plugins/filesystem/local/local.php b/plugins/filesystem/local/local.php
index d23ab606b4e1..5f2df57e2131 100644
--- a/plugins/filesystem/local/local.php
+++ b/plugins/filesystem/local/local.php
@@ -84,7 +84,7 @@ public function getDisplayName()
     public function getAdapters()
     {
         $adapters = [];
-        $directories = $this->params->get('directories', '[{"directory": "images"}]');
+        $directories = $this->params->get('directories', '[{"directory": "images", "thumbs": 0}]');
 
         // Do a check if default settings are not saved by user
         // If not initialize them manually
@@ -97,9 +97,15 @@ public function getAdapters()
                 $directoryPath = JPATH_ROOT . '/' . $directoryEntity->directory;
                 $directoryPath = rtrim($directoryPath) . '/';
 
+                if (!isset($directoryEntity->thumbs)) {
+                    $directoryEntity->thumbs = 0;
+                }
+
                 $adapter = new \Joomla\Plugin\Filesystem\Local\Adapter\LocalAdapter(
                     $directoryPath,
-                    $directoryEntity->directory
+                    $directoryEntity->directory,
+                    $directoryEntity->thumbs,
+                    [200, 200]
                 );
 
                 $adapters[$adapter->getAdapterName()] = $adapter;
diff --git a/plugins/filesystem/local/local.xml b/plugins/filesystem/local/local.xml
index 8f93dc5bc9e2..bcb9a676767f 100644
--- a/plugins/filesystem/local/local.xml
+++ b/plugins/filesystem/local/local.xml
@@ -44,6 +44,17 @@
 							hide_none="true"
 							validate="options"
 						/>
+						<field
+							name="thumbs"
+							type="radio"
+							label="PLG_FILESYSTEM_LOCAL_DIRECTORIES_DIRECTORY_THUMBNAILS_LABEL"
+							layout="joomla.form.field.radio.switcher"
+							default="0"
+							filter="integer"
+							>
+							<option value="0">JNO</option>
+							<option value="1">JYES</option>
+						</field>
 					</form>
 				</field>
 			</fieldset>
diff --git a/plugins/filesystem/local/src/Adapter/LocalAdapter.php b/plugins/filesystem/local/src/Adapter/LocalAdapter.php
index e9f43f0a1711..ec000af50a53 100644
--- a/plugins/filesystem/local/src/Adapter/LocalAdapter.php
+++ b/plugins/filesystem/local/src/Adapter/LocalAdapter.php
@@ -55,22 +55,52 @@ class LocalAdapter implements AdapterInterface
      */
     private $filePath = null;
 
+    /**
+     * Should the adapter create a thumbnail for the image?
+     *
+     * @var boolean
+     *
+     * @since  __DEPLOY_VERSION__
+     */
+    private $thumbnails = false;
+
+    /**
+     * Thumbnail dimensions in pixels, [0] = width, [1] = height
+     *
+     * @var array
+     *
+     * @since  __DEPLOY_VERSION__
+     */
+    private $thumbnailSize = [200, 200];
+
     /**
      * The absolute root path in the local file system.
      *
-     * @param   string  $rootPath  The root path
-     * @param   string  $filePath  The file path of media folder
+     * @param   string    $rootPath    The root path
+     * @param   string    $filePath    The file path of media folder
+     * @param   boolean   $thumbnails      The thumbnails option
+     * @param   array     $thumbnailSize   The thumbnail dimensions in pixels
      *
      * @since   4.0.0
      */
-    public function __construct(string $rootPath, string $filePath)
+    public function __construct(string $rootPath, string $filePath, bool $thumbnails = false, array $thumbnailSize = [200, 200])
     {
         if (!file_exists($rootPath)) {
             throw new \InvalidArgumentException(Text::_('COM_MEDIA_ERROR_MISSING_DIR'));
         }
 
-        $this->rootPath = Path::clean(realpath($rootPath), '/');
-        $this->filePath = $filePath;
+        $this->rootPath  = Path::clean(realpath($rootPath), '/');
+        $this->filePath  = $filePath;
+        $this->thumbnails    = $thumbnails;
+        $this->thumbnailSize = $thumbnailSize;
+
+        if ($this->thumbnails) {
+            $dir = JPATH_ROOT . '/media/cache/com_media/thumbs/' . $this->filePath;
+
+            if (!is_dir($dir)) {
+                Folder::create($dir);
+            }
+        }
     }
 
     /**
@@ -221,14 +251,24 @@ public function createFolder(string $name, string $path): string
      */
     public function createFile(string $name, string $path, $data): string
     {
-        $name = $this->getSafeName($name);
-
+        $name =      $this->getSafeName($name);
         $localPath = $this->getLocalPath($path . '/' . $name);
 
         $this->checkContent($localPath, $data);
 
         File::write($localPath, $data);
 
+        if ($this->thumbnails && MediaHelper::isImage(pathinfo($localPath)['basename'])) {
+            $thumbnailPaths = $this->getLocalThumbnailPaths($localPath);
+
+            if (empty($thumbnailPaths)) {
+                return $name;
+            }
+
+            // Create the thumbnail
+            $this->createThumbnail($localPath, $thumbnailPaths['fs']);
+        }
+
         return $name;
     }
 
@@ -255,6 +295,17 @@ public function updateFile(string $name, string $path, $data)
         $this->checkContent($localPath, $data);
 
         File::write($localPath, $data);
+
+        if ($this->thumbnails && MediaHelper::isImage(pathinfo($localPath)['basename'])) {
+            $thumbnailPaths = $this->getLocalThumbnailPaths($localPath);
+
+            if (empty($thumbnailPaths['fs'])) {
+                return;
+            }
+
+            // Create the thumbnail
+            $this->createThumbnail($localPath, $thumbnailPaths['fs']);
+        }
     }
 
     /**
@@ -269,13 +320,18 @@ public function updateFile(string $name, string $path, $data)
      */
     public function delete(string $path)
     {
-        $localPath = $this->getLocalPath($path);
+        $localPath =  $this->getLocalPath($path);
+        $thumbnailPaths = $this->getLocalThumbnailPaths($localPath);
 
         if (is_file($localPath)) {
             if (!File::exists($localPath)) {
                 throw new FileNotFoundException();
             }
 
+            if ($this->thumbnails && !empty($thumbnailPaths['fs']) && is_file($thumbnailPaths['fs'])) {
+                File::delete($thumbnailPaths['fs']);
+            }
+
             $success = File::delete($localPath);
         } else {
             if (!Folder::exists($localPath)) {
@@ -283,6 +339,10 @@ public function delete(string $path)
             }
 
             $success = Folder::delete($localPath);
+
+            if ($this->thumbnails && !empty($thumbnailPaths['fs']) && is_dir($thumbnailPaths['fs'])) {
+                Folder::delete($thumbnailPaths['fs']);
+            }
         }
 
         if (!$success) {
@@ -303,7 +363,7 @@ public function delete(string $path)
      * - mime_type:     The mime type
      * - width:         The width, when available
      * - height:        The height, when available
-     * - thumb_path     The thumbnail path of file, when available
+     * - thumb_path:    The thumbnail path of file, when available
      *
      * @param   string  $path  The folder
      *
@@ -351,8 +411,7 @@ private function getPathInformation(string $path): \stdClass
                 $obj->width  = $props->width;
                 $obj->height = $props->height;
 
-                // @todo : Change this path to an actual thumbnail path
-                $obj->thumb_path = $this->getUrl($obj->path);
+                $obj->thumb_path = $this->thumbnails ? $this->getThumbnail($path) : $this->getUrl($obj->path);
             } catch (UnparsableImageException $e) {
                 // Ignore the exception - it's an image that we don't know how to parse right now
             }
@@ -689,7 +748,7 @@ private function rglob(string $pattern, int $flags = 0): array
     {
         $files = glob($pattern, $flags);
 
-        foreach (glob(\dirname($pattern) . '/*', GLOB_ONLYDIR | GLOB_NOSORT) as $dir) {
+        foreach (glob(dirname($pattern) . '/*', GLOB_ONLYDIR | GLOB_NOSORT) as $dir) {
             $files = array_merge($files, $this->rglob($dir . '/' . $this->getFileName($pattern), $flags));
         }
 
@@ -763,7 +822,7 @@ private function checkContent(string $localPath, string $mediaContent)
         $helper = new MediaHelper();
 
         // @todo find a better way to check the input, by not writing the file to the disk
-        $tmpFile = Path::clean(\dirname($localPath) . '/' . uniqid() . '.' . File::getExt($name));
+        $tmpFile = Path::clean(dirname($localPath) . '/' . uniqid() . '.' . File::getExt($name));
 
         if (!File::write($tmpFile, $mediaContent)) {
             throw new \Exception(Text::_('JLIB_MEDIA_ERROR_UPLOAD_INPUT'), 500);
@@ -819,4 +878,89 @@ private function getLocalPath(string $path): string
             throw new InvalidPathException($e->getMessage());
         }
     }
+
+    /**
+     * Returns the local filesystem thumbnail path for the given path.
+     *
+     * Throws an InvalidPathException if the path is invalid.
+     *
+     * @param   string  $path  The path
+     *
+     * @return  array
+     *
+     * @since   __DEPLOY_VERSION__
+     * @throws  InvalidPathException
+     */
+    private function getLocalThumbnailPaths(string $path): array
+    {
+        $rootPath = str_replace(['\\', '/'], '/', $this->rootPath);
+        $path     = str_replace(['\\', '/'], '/', $path);
+
+        try {
+            $fs  = Path::check(str_replace($rootPath, JPATH_ROOT . '/media/cache/com_media/thumbs/' . $this->filePath, $path));
+            $url = str_replace($rootPath, 'media/cache/com_media/thumbs/' . $this->filePath, $path);
+
+            return [
+                'fs'  => $fs,
+                'url' => $url,
+            ];
+        } catch (\Exception $e) {
+            throw new InvalidPathException($e->getMessage());
+        }
+    }
+
+    /**
+     * Returns the path for the thumbnail of the given image.
+     * If the thumbnail does not exist, it will be created.
+     *
+     * @param   string  $path  The path of the image
+     *
+     * @return  string
+     *
+     * @since   __DEPLOY_VERSION__
+     */
+    private function getThumbnail(string $path): string
+    {
+        $thumbnailPaths = $this->getLocalThumbnailPaths($path);
+
+        if (empty($thumbnailPaths['fs'])) {
+            return $this->getUrl($path);
+        }
+
+        $dir = dirname($thumbnailPaths['fs']);
+
+        if (!is_dir($dir)) {
+            Folder::create($dir);
+        }
+
+        // Create the thumbnail
+        if (!is_file($thumbnailPaths['fs']) && !$this->createThumbnail($path, $thumbnailPaths['fs'])) {
+            return $this->getUrl($path);
+        }
+
+        return Uri::root() . $this->getEncodedPath($thumbnailPaths['url']);
+    }
+
+    /**
+     * Create a thumbnail of the given image.
+     *
+     * @param   string  $path       The path of the image
+     * @param   string  $thumbnailPath  The path of the thumbnail
+     *
+     * @return  boolean
+     *
+     * @since   __DEPLOY_VERSION__
+     */
+    private function createThumbnail(string $path, string $thumbnailPath): bool
+    {
+        $image = new Image($path);
+
+        try {
+            $image->createThumbnails([$this->thumbnailSize[0] . 'x' . $this->thumbnailSize[1]], $image::SCALE_INSIDE, dirname($thumbnailPath), true);
+        } catch (\Exception $e) {
+            return false;
+        }
+
+        return true;
+    }
 }

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions