first commit

This commit is contained in:
2025-06-17 11:53:18 +02:00
commit 9f0f7ba12b
8804 changed files with 1369176 additions and 0 deletions

View File

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="filesystem" method="upgrade">
<name>plg_filesystem_local</name>
<author>Joomla! Project</author>
<creationDate>2017-04</creationDate>
<copyright>(C) 2017 Open Source Matters, Inc.</copyright>
<license>GNU General Public License version 2 or later; see LICENSE.txt</license>
<authorEmail>admin@joomla.org</authorEmail>
<authorUrl>www.joomla.org</authorUrl>
<version>4.0.0</version>
<description>PLG_FILESYSTEM_LOCAL_XML_DESCRIPTION</description>
<namespace path="src">Joomla\Plugin\Filesystem\Local</namespace>
<files>
<folder plugin="local">services</folder>
<folder>src</folder>
</files>
<languages>
<language tag="en-GB">language/en-GB/plg_filesystem_local.ini</language>
<language tag="en-GB">language/en-GB/plg_filesystem_local.sys.ini</language>
</languages>
<config>
<fields name="params">
<fieldset name="basic">
<field
name="directories"
type="subform"
label="PLG_FILESYSTEM_LOCAL_DIRECTORIES_LABEL"
multiple="true"
layout="joomla.form.field.subform.repeatable-table"
buttons="add,remove,move"
default='[{"directory":"images"}]'
>
<form>
<field
name="directory"
type="folderlist"
default="images"
label="PLG_FILESYSTEM_LOCAL_DIRECTORIES_DIRECTORY_LABEL"
folderFilter=""
exclude=""
stripext=""
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>
</fields>
</config>
</extension>

View File

@ -0,0 +1,47 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Filesystem.local
*
* @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
\defined('_JEXEC') or die;
use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\Filesystem\Local\Extension\Local;
return new class () implements ServiceProviderInterface {
/**
* Registers the service provider with a DI container.
*
* @param Container $container The DI container.
*
* @return void
*
* @since 4.3.0
*/
public function register(Container $container)
{
$container->set(
PluginInterface::class,
function (Container $container) {
$plugin = new Local(
$container->get(DispatcherInterface::class),
(array) PluginHelper::getPlugin('filesystem', 'local'),
JPATH_ROOT
);
$plugin->setApplication(Factory::getApplication());
return $plugin;
}
);
}
};

View File

@ -0,0 +1,965 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage Filesystem.local
*
* @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Plugin\Filesystem\Local\Adapter;
use Joomla\CMS\Date\Date;
use Joomla\CMS\Factory;
use Joomla\CMS\Filesystem\File;
use Joomla\CMS\Filesystem\Folder;
use Joomla\CMS\Helper\MediaHelper;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Image\Exception\UnparsableImageException;
use Joomla\CMS\Image\Image;
use Joomla\CMS\Language\Text;
use Joomla\CMS\String\PunycodeHelper;
use Joomla\CMS\Uri\Uri;
use Joomla\CMS\User\CurrentUserTrait;
use Joomla\Component\Media\Administrator\Adapter\AdapterInterface;
use Joomla\Component\Media\Administrator\Exception\FileNotFoundException;
use Joomla\Component\Media\Administrator\Exception\InvalidPathException;
use Joomla\Filesystem\Path;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* Local file adapter.
*
* @since 4.0.0
*/
class LocalAdapter implements AdapterInterface
{
use CurrentUserTrait;
/**
* The root path to gather file information from.
*
* @var string
*
* @since 4.0.0
*/
private $rootPath = null;
/**
* The file_path of media directory related to site
*
* @var string
*
* @since 4.0.0
*/
private $filePath = null;
/**
* Should the adapter create a thumbnail for the image?
*
* @var boolean
*
* @since 4.3.0
*/
private $thumbnails = false;
/**
* Thumbnail dimensions in pixels, [0] = width, [1] = height
*
* @var array
*
* @since 4.3.0
*/
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 boolean $thumbnails The thumbnails option
* @param array $thumbnailSize The thumbnail dimensions in pixels
*
* @since 4.0.0
*/
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->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);
}
}
}
/**
* Returns the requested file or folder. The returned object
* has the following properties available:
* - type: The type can be file or dir
* - name: The name of the file
* - path: The relative path to the root
* - extension: The file extension
* - size: The size of the file
* - create_date: The date created
* - modified_date: The date modified
* - mime_type: The mime type
* - width: The width, when available
* - height: The height, when available
*
* If the path doesn't exist a FileNotFoundException is thrown.
*
* @param string $path The path to the file or folder
*
* @return \stdClass
*
* @since 4.0.0
* @throws \Exception
*/
public function getFile(string $path = '/'): \stdClass
{
// Get the local path
$basePath = $this->getLocalPath($path);
// Check if file exists
if (!file_exists($basePath)) {
throw new FileNotFoundException();
}
return $this->getPathInformation($basePath);
}
/**
* Returns the folders and files for the given path. The returned objects
* have the following properties available:
* - type: The type can be file or dir
* - name: The name of the file
* - path: The relative path to the root
* - extension: The file extension
* - size: The size of the file
* - create_date: The date created
* - modified_date: The date modified
* - mime_type: The mime type
* - width: The width, when available
* - height: The height, when available
*
* If the path doesn't exist a FileNotFoundException is thrown.
*
* @param string $path The folder
*
* @return \stdClass[]
*
* @since 4.0.0
* @throws \Exception
*/
public function getFiles(string $path = '/'): array
{
// Get the local path
$basePath = $this->getLocalPath($path);
// Check if file exists
if (!file_exists($basePath)) {
throw new FileNotFoundException();
}
// Check if the path points to a file
if (is_file($basePath)) {
return [$this->getPathInformation($basePath)];
}
// The data to return
$data = [];
// Read the folders
foreach (Folder::folders($basePath) as $folder) {
$data[] = $this->getPathInformation(Path::clean($basePath . '/' . $folder));
}
// Read the files
foreach (Folder::files($basePath) as $file) {
$data[] = $this->getPathInformation(Path::clean($basePath . '/' . $file));
}
// Return the data
return $data;
}
/**
* Returns a resource to download the path.
*
* @param string $path The path to download
*
* @return resource
*
* @since 4.0.0
* @throws \Exception
*/
public function getResource(string $path)
{
return fopen($this->rootPath . '/' . $path, 'r');
}
/**
* Creates a folder with the given name in the given path.
*
* It returns the new folder name. This allows the implementation
* classes to normalise the file name.
*
* @param string $name The name
* @param string $path The folder
*
* @return string
*
* @since 4.0.0
* @throws \Exception
*/
public function createFolder(string $name, string $path): string
{
$name = $this->getSafeName($name);
$localPath = $this->getLocalPath($path . '/' . $name);
Folder::create($localPath);
return $name;
}
/**
* Creates a file with the given name in the given path with the data.
*
* It returns the new file name. This allows the implementation
* classes to normalise the file name.
*
* @param string $name The name
* @param string $path The folder
* @param string $data The data
*
* @return string
*
* @since 4.0.0
* @throws \Exception
*/
public function createFile(string $name, string $path, $data): string
{
$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;
}
/**
* Updates the file with the given name in the given path with the data.
*
* @param string $name The name
* @param string $path The folder
* @param string $data The data
*
* @return void
*
* @since 4.0.0
* @throws \Exception
*/
public function updateFile(string $name, string $path, $data)
{
$localPath = $this->getLocalPath($path . '/' . $name);
if (!is_file($localPath)) {
throw new FileNotFoundException();
}
$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']);
}
}
/**
* Deletes the folder or file of the given path.
*
* @param string $path The path to the file or folder
*
* @return void
*
* @since 4.0.0
* @throws \Exception
*/
public function delete(string $path)
{
$localPath = $this->getLocalPath($path);
$thumbnailPaths = $this->getLocalThumbnailPaths($localPath);
if (is_file($localPath)) {
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) {
throw new \Exception('Delete not possible!');
}
}
/**
* Returns the folder or file information for the given path. The returned object
* has the following properties:
* - type: The type can be file or dir
* - name: The name of the file
* - path: The relative path to the root
* - extension: The file extension
* - size: The size of the file
* - create_date: The date created
* - modified_date: The date modified
* - mime_type: The mime type
* - width: The width, when available
* - height: The height, when available
* - thumb_path: The thumbnail path of file, when available
*
* @param string $path The folder
*
* @return \stdClass
*
* @since 4.0.0
*/
private function getPathInformation(string $path): \stdClass
{
// Prepare the path
$path = Path::clean($path, '/');
// The boolean if it is a dir
$isDir = is_dir($path);
$createDate = $this->getDate(filectime($path));
$modifiedDate = $this->getDate(filemtime($path));
// Set the values
$obj = new \stdClass();
$obj->type = $isDir ? 'dir' : 'file';
$obj->name = $this->getFileName($path);
$obj->path = str_replace($this->rootPath, '', $path);
$obj->extension = !$isDir ? File::getExt($obj->name) : '';
$obj->size = !$isDir ? filesize($path) : '';
$obj->mime_type = MediaHelper::getMimeType($path, MediaHelper::isImage($obj->name));
$obj->width = 0;
$obj->height = 0;
// Dates
$obj->create_date = $createDate->format('c', true);
$obj->create_date_formatted = HTMLHelper::_('date', $createDate, Text::_('DATE_FORMAT_LC5'));
$obj->modified_date = $modifiedDate->format('c', true);
$obj->modified_date_formatted = HTMLHelper::_('date', $modifiedDate, Text::_('DATE_FORMAT_LC5'));
if ($obj->mime_type === 'image/svg+xml' && $obj->extension === 'svg') {
$obj->thumb_path = $this->getUrl($obj->path);
return $obj;
}
if (MediaHelper::isImage($obj->name)) {
// Get the image properties
try {
$props = Image::getImageFileProperties($path);
$obj->width = $props->width;
$obj->height = $props->height;
$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
}
}
return $obj;
}
/**
* Returns a Date with the correct Joomla timezone for the given date.
*
* @param string $date The date to create a Date from
*
* @return Date
*
* @since 4.0.0
*/
private function getDate($date = null): Date
{
$dateObj = Factory::getDate($date);
$timezone = Factory::getApplication()->get('offset');
$user = $this->getCurrentUser();
if ($user->id) {
$userTimezone = $user->getParam('timezone');
if (!empty($userTimezone)) {
$timezone = $userTimezone;
}
}
if ($timezone) {
$dateObj->setTimezone(new \DateTimeZone($timezone));
}
return $dateObj;
}
/**
* Copies a file or folder from source to destination.
*
* It returns the new destination path. This allows the implementation
* classes to normalise the file name.
*
* @param string $sourcePath The source path
* @param string $destinationPath The destination path
* @param bool $force Force to overwrite
*
* @return string
*
* @since 4.0.0
* @throws \Exception
*/
public function copy(string $sourcePath, string $destinationPath, bool $force = false): string
{
// Get absolute paths from relative paths
$sourcePath = Path::clean($this->getLocalPath($sourcePath), '/');
$destinationPath = Path::clean($this->getLocalPath($destinationPath), '/');
if (!file_exists($sourcePath)) {
throw new FileNotFoundException();
}
$name = $this->getFileName($destinationPath);
$safeName = $this->getSafeName($name);
// If the safe name is different normalise the file name
if ($safeName != $name) {
$destinationPath = substr($destinationPath, 0, -\strlen($name)) . '/' . $safeName;
}
// Check for existence of the file in destination
// if it does not exists simply copy source to destination
if (is_dir($sourcePath)) {
$this->copyFolder($sourcePath, $destinationPath, $force);
} else {
$this->copyFile($sourcePath, $destinationPath, $force);
}
// Get the relative path
$destinationPath = str_replace($this->rootPath, '', $destinationPath);
return $destinationPath;
}
/**
* Copies a file
*
* @param string $sourcePath Source path of the file or directory
* @param string $destinationPath Destination path of the file or directory
* @param bool $force Set true to overwrite files or directories
*
* @return void
*
* @since 4.0.0
* @throws \Exception
*/
private function copyFile(string $sourcePath, string $destinationPath, bool $force = false)
{
if (is_dir($destinationPath)) {
// If the destination is a folder we create a file with the same name as the source
$destinationPath .= '/' . $this->getFileName($sourcePath);
}
if (file_exists($destinationPath) && !$force) {
throw new \Exception('Copy file is not possible as destination file already exists');
}
if (!File::copy($sourcePath, $destinationPath)) {
throw new \Exception('Copy file is not possible');
}
}
/**
* Copies a folder
*
* @param string $sourcePath Source path of the file or directory
* @param string $destinationPath Destination path of the file or directory
* @param bool $force Set true to overwrite files or directories
*
* @return void
*
* @since 4.0.0
* @throws \Exception
*/
private function copyFolder(string $sourcePath, string $destinationPath, bool $force = false)
{
if (file_exists($destinationPath) && !$force) {
throw new \Exception('Copy folder is not possible as destination folder already exists');
}
if (is_file($destinationPath) && !File::delete($destinationPath)) {
throw new \Exception('Copy folder is not possible as destination folder is a file and can not be deleted');
}
if (!Folder::copy($sourcePath, $destinationPath, '', $force)) {
throw new \Exception('Copy folder is not possible');
}
}
/**
* Moves a file or folder from source to destination.
*
* It returns the new destination path. This allows the implementation
* classes to normalise the file name.
*
* @param string $sourcePath The source path
* @param string $destinationPath The destination path
* @param bool $force Force to overwrite
*
* @return string
*
* @since 4.0.0
* @throws \Exception
*/
public function move(string $sourcePath, string $destinationPath, bool $force = false): string
{
// Get absolute paths from relative paths
$sourcePath = Path::clean($this->getLocalPath($sourcePath), '/');
$destinationPath = Path::clean($this->getLocalPath($destinationPath), '/');
if (!file_exists($sourcePath)) {
throw new FileNotFoundException();
}
$name = $this->getFileName($destinationPath);
$safeName = $this->getSafeName($name);
// If transliterating could not happen, and all characters except of the file extension are filtered out, then throw an error.
if ($safeName === pathinfo($sourcePath, PATHINFO_EXTENSION)) {
throw new \Exception(Text::_('COM_MEDIA_ERROR_MAKESAFE'));
}
// If the safe name is different normalise the file name
if ($safeName != $name) {
$destinationPath = substr($destinationPath, 0, -\strlen($name)) . $safeName;
}
if (is_dir($sourcePath)) {
$this->moveFolder($sourcePath, $destinationPath, $force);
} else {
$this->moveFile($sourcePath, $destinationPath, $force);
}
// Get the relative path
$destinationPath = str_replace($this->rootPath, '', $destinationPath);
return $destinationPath;
}
/**
* Moves a file
*
* @param string $sourcePath Absolute path of source
* @param string $destinationPath Absolute path of destination
* @param bool $force Set true to overwrite file if exists
*
* @return void
*
* @since 4.0.0
* @throws \Exception
*/
private function moveFile(string $sourcePath, string $destinationPath, bool $force = false)
{
if (is_dir($destinationPath)) {
// If the destination is a folder we create a file with the same name as the source
$destinationPath .= '/' . $this->getFileName($sourcePath);
}
if (!MediaHelper::checkFileExtension(pathinfo($destinationPath, PATHINFO_EXTENSION))) {
throw new \Exception('Move file is not possible as the extension is invalid');
}
if (file_exists($destinationPath) && !$force) {
throw new \Exception('Move file is not possible as destination file already exists');
}
if (!File::move($sourcePath, $destinationPath)) {
throw new \Exception('Move file is not possible');
}
}
/**
* Moves a folder from source to destination
*
* @param string $sourcePath Source path of the file or directory
* @param string $destinationPath Destination path of the file or directory
* @param bool $force Set true to overwrite files or directories
*
* @return void
*
* @since 4.0.0
* @throws \Exception
*/
private function moveFolder(string $sourcePath, string $destinationPath, bool $force = false)
{
if (file_exists($destinationPath) && !$force) {
throw new \Exception('Move folder is not possible as destination folder already exists');
}
if (is_file($destinationPath) && !File::delete($destinationPath)) {
throw new \Exception('Move folder is not possible as destination folder is a file and can not be deleted');
}
if (is_dir($destinationPath)) {
// We need to bypass exception thrown in JFolder when destination exists
// So we only copy it in forced condition, then delete the source to simulate a move
if (!Folder::copy($sourcePath, $destinationPath, '', true)) {
throw new \Exception('Move folder to an existing destination failed');
}
// Delete the source
Folder::delete($sourcePath);
return;
}
// Perform usual moves
$value = Folder::move($sourcePath, $destinationPath);
if ($value !== true) {
throw new \Exception($value);
}
}
/**
* Returns a url which can be used to display an image from within the "images" directory.
*
* @param string $path Path of the file relative to adapter
*
* @return string
*
* @since 4.0.0
*/
public function getUrl(string $path): string
{
return Uri::root() . $this->getEncodedPath($this->filePath . $path);
}
/**
* Returns the name of this adapter.
*
* @return string
*
* @since 4.0.0
*/
public function getAdapterName(): string
{
return $this->filePath;
}
/**
* Search for a pattern in a given path
*
* @param string $path The base path for the search
* @param string $needle The path to file
* @param bool $recursive Do a recursive search
*
* @return \stdClass[]
*
* @since 4.0.0
*/
public function search(string $path, string $needle, bool $recursive = false): array
{
$pattern = Path::clean($this->getLocalPath($path) . '/*' . $needle . '*');
if ($recursive) {
$results = $this->rglob($pattern);
} else {
$results = glob($pattern);
}
$searchResults = [];
foreach ($results as $result) {
$searchResults[] = $this->getPathInformation($result);
}
return $searchResults;
}
/**
* Do a recursive search on a given path
*
* @param string $pattern The pattern for search
* @param int $flags Flags for search
*
* @return array
*
* @since 4.0.0
*/
private function rglob(string $pattern, int $flags = 0): array
{
$files = glob($pattern, $flags);
foreach (glob(\dirname($pattern) . '/*', GLOB_ONLYDIR | GLOB_NOSORT) as $dir) {
$files = array_merge($files, $this->rglob($dir . '/' . $this->getFileName($pattern), $flags));
}
return $files;
}
/**
* Replace spaces on a path with %20
*
* @param string $path The Path to be encoded
*
* @return string
*
* @since 4.0.0
* @throws FileNotFoundException
*/
private function getEncodedPath(string $path): string
{
return str_replace(" ", "%20", $path);
}
/**
* Creates a safe file name for the given name.
*
* @param string $name The filename
*
* @return string
*
* @since 4.0.0
* @throws \Exception
*/
private function getSafeName(string $name): string
{
// Make the filename safe
if (!$name = File::makeSafe($name)) {
throw new \Exception(Text::_('COM_MEDIA_ERROR_MAKESAFE'));
}
// Transform filename to punycode
$name = PunycodeHelper::toPunycode($name);
// Get the extension
$extension = File::getExt($name);
// Normalise extension, always lower case
if ($extension) {
$extension = '.' . strtolower($extension);
}
$nameWithoutExtension = substr($name, 0, \strlen($name) - \strlen($extension));
return $nameWithoutExtension . $extension;
}
/**
* Performs various check if it is allowed to save the content with the given name.
*
* @param string $localPath The local path
* @param string $mediaContent The media content
*
* @return void
*
* @since 4.0.0
* @throws \Exception
*/
private function checkContent(string $localPath, string $mediaContent)
{
$name = $this->getFileName($localPath);
// The helper
$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));
if (!File::write($tmpFile, $mediaContent)) {
throw new \Exception(Text::_('JLIB_MEDIA_ERROR_UPLOAD_INPUT'), 500);
}
$can = $helper->canUpload(['name' => $name, 'size' => \strlen($mediaContent), 'tmp_name' => $tmpFile], 'com_media');
File::delete($tmpFile);
if (!$can) {
throw new \Exception(Text::_('JLIB_MEDIA_ERROR_UPLOAD_INPUT'), 403);
}
}
/**
* Returns the file name of the given path.
*
* @param string $path The path
*
* @return string
*
* @since 4.0.0
* @throws \Exception
*/
private function getFileName(string $path): string
{
$path = Path::clean($path);
// Basename does not work here as it strips out certain characters like upper case umlaut u
$path = explode(DIRECTORY_SEPARATOR, $path);
// Return the last element
return array_pop($path);
}
/**
* Returns the local filesystem path for the given path.
*
* Throws an InvalidPathException if the path is invalid.
*
* @param string $path The path
*
* @return string
*
* @since 4.0.0
* @throws InvalidPathException
*/
private function getLocalPath(string $path): string
{
try {
return Path::check($this->rootPath . '/' . $path);
} catch (\Exception $e) {
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 4.3.0
* @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 4.3.0
*/
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 4.3.0
*/
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;
}
}

View File

@ -0,0 +1,143 @@
<?php
/**
* @package Joomla.Plugin
* @subpackage FileSystem.local
*
* @copyright (C) 2017 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Plugin\Filesystem\Local\Extension;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Component\Media\Administrator\Event\MediaProviderEvent;
use Joomla\Component\Media\Administrator\Provider\ProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\Filesystem\Local\Adapter\LocalAdapter;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
/**
* FileSystem Local plugin.
*
* The plugin to deal with the local filesystem in Media Manager.
*
* @since 4.0.0
*/
final class Local extends CMSPlugin implements ProviderInterface
{
/**
* Affects constructor behavior. If true, language files will be loaded automatically.
*
* @var boolean
* @since 4.0.0
*/
protected $autoloadLanguage = true;
/**
* The root directory path
*
* @var string
* @since 4.3.0
*/
private $rootDirectory;
/**
* Constructor.
*
* @param DispatcherInterface $dispatcher The dispatcher
* @param array $config An optional associative array of configuration settings
* @param string $rootDirectory The root directory to look for images
*
* @since 4.3.0
*/
public function __construct(DispatcherInterface $dispatcher, array $config, string $rootDirectory)
{
parent::__construct($dispatcher, $config);
$this->rootDirectory = $rootDirectory;
}
/**
* Setup Providers for Local Adapter
*
* @param MediaProviderEvent $event Event for ProviderManager
*
* @return void
*
* @since 4.0.0
*/
public function onSetupProviders(MediaProviderEvent $event)
{
$event->getProviderManager()->registerProvider($this);
}
/**
* Returns the ID of the provider
*
* @return string
*
* @since 4.0.0
*/
public function getID()
{
return $this->_name;
}
/**
* Returns the display name of the provider
*
* @return string
*
* @since 4.0.0
*/
public function getDisplayName()
{
return $this->getLanguage()->_('PLG_FILESYSTEM_LOCAL_DEFAULT_NAME');
}
/**
* Returns and array of adapters
*
* @return \Joomla\Component\Media\Administrator\Adapter\AdapterInterface[]
*
* @since 4.0.0
*/
public function getAdapters()
{
$adapters = [];
$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
if (\is_string($directories)) {
$directories = json_decode($directories);
}
foreach ($directories as $directoryEntity) {
if (!$directoryEntity->directory) {
continue;
}
$directoryPath = $this->rootDirectory . '/' . $directoryEntity->directory;
$directoryPath = rtrim($directoryPath) . '/';
if (!isset($directoryEntity->thumbs)) {
$directoryEntity->thumbs = 0;
}
$adapter = new LocalAdapter(
$directoryPath,
$directoryEntity->directory,
$directoryEntity->thumbs,
[200, 200]
);
$adapter->setCurrentUser($this->getApplication()->getIdentity());
$adapters[$adapter->getAdapterName()] = $adapter;
}
return $adapters;
}
}