Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 27 commits into from
Jan 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
23aef7e
Update plg_filesystem_local.ini
obuisard Sep 21, 2022
d2fd2ad
Update Image.php
obuisard Sep 22, 2022
a2dc643
Update local.php
obuisard Sep 22, 2022
44b5d3e
Update local.xml
obuisard Sep 22, 2022
b65ed2d
Update MediaCest.php
obuisard Sep 22, 2022
04b4cbb
Update LocalAdapter.php
obuisard Sep 22, 2022
8c4b439
Update LocalAdapter.php
obuisard Sep 22, 2022
1951162
Update Image.php
obuisard Sep 22, 2022
623da04
Update MediaCest.php
obuisard Sep 22, 2022
83319a6
Update LocalAdapter.php
obuisard Sep 23, 2022
03db9e8
Merge branch '4.3-dev' into pr-36552-clone
obuisard Oct 5, 2022
17eaa64
Update local.php
obuisard Oct 17, 2022
3066a1f
Update local.xml
obuisard Oct 17, 2022
84a6d92
Merge branch '4.3-dev' into pr-36552-clone
obuisard Oct 17, 2022
53bc63a
Update LocalAdapter.php
obuisard Oct 26, 2022
b832d6a
Merge branch '4.3-dev' into pr-36552-clone
obuisard Nov 8, 2022
a3fe8f9
Merge branch '4.3-dev' into pr-36552-clone
obuisard Nov 8, 2022
27019c7
Merge branch '4.3-dev' into pr-36552-clone
laoneo Nov 10, 2022
112db00
Update plugins/filesystem/local/src/Adapter/LocalAdapter.php
obuisard Nov 10, 2022
5d84be4
Update local.xml
obuisard Nov 10, 2022
2b14ad6
Update plg_filesystem_local.ini
obuisard Nov 10, 2022
fed24b2
Update LocalAdapter.php
obuisard Nov 10, 2022
958fc7f
Update LocalAdapter.php
obuisard Nov 10, 2022
1e19a9c
Update Image.php
obuisard Nov 10, 2022
e9e6d68
Update Image.php
obuisard Nov 10, 2022
d82abd9
Merge branch '4.3-dev' into pr-36552-clone
obuisard Nov 11, 2022
bb1a022
Merge branch '4.3-dev' into pr-36552-clone
obuisard Jan 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion administrator/language/en-GB/plg_filesystem_local.ini
Original file line number Diff line number Diff line change
Expand Up @@ -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."
40 changes: 33 additions & 7 deletions libraries/src/Image/Image.php
Original file line number Diff line number Diff line change
Expand Up @@ -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()) {
Expand Down Expand Up @@ -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;
Expand All @@ -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)
obuisard marked this conversation as resolved.
Show resolved Hide resolved
{
return $this->createThumbnails($thumbSizes, $creationMethod, $thumbsFolder, false);
}

/**
* Method to crop the current image.
*
Expand Down
10 changes: 8 additions & 2 deletions plugins/filesystem/local/local.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
Expand Down
11 changes: 11 additions & 0 deletions plugins/filesystem/local/local.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
170 changes: 157 additions & 13 deletions plugins/filesystem/local/src/Adapter/LocalAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}

/**
Expand Down Expand Up @@ -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;
}

Expand All @@ -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']);
}
}

/**
Expand All @@ -269,20 +320,29 @@ 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)) {
throw new FileNotFoundException();
}

$success = Folder::delete($localPath);

if ($this->thumbnails && !empty($thumbnailPaths['fs']) && is_dir($thumbnailPaths['fs'])) {
Folder::delete($thumbnailPaths['fs']);
}
}

if (!$success) {
Expand All @@ -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
*
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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));
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
}