primo commit
This commit is contained in:
		
							
								
								
									
										963
									
								
								plugins/filesystem/local/src/Adapter/LocalAdapter.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										963
									
								
								plugins/filesystem/local/src/Adapter/LocalAdapter.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,963 @@ | ||||
| <?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 (!is_dir(Path::clean($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(Text::_('COM_MEDIA_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 = !$isDir ? (string) 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 (!$isDir && 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(Text::_('COM_MEDIA_COPY_FILE_NOT_POSSIBLE_FILE_ALREADY_EXISTS')); | ||||
|         } | ||||
|  | ||||
|         if (!File::copy($sourcePath, $destinationPath)) { | ||||
|             throw new \Exception(Text::_('COM_MEDIA_COPY_FILE_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(Text::_('COM_MEDIA_COPY_FOLDER_ALREADY_EXISTS')); | ||||
|         } | ||||
|  | ||||
|         if (is_file($destinationPath) && !File::delete($destinationPath)) { | ||||
|             throw new \Exception(Text::_('COM_MEDIA_COPY_FOLDER_DESTINATION_CAN_NOT_DELETE')); | ||||
|         } | ||||
|  | ||||
|         if (!Folder::copy($sourcePath, $destinationPath, '', $force)) { | ||||
|             throw new \Exception(Text::_('COM_MEDIA_COPY_FOLDER_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(Text::_('COM_MEDIA_MOVE_FILE_EXTENSION_INVALID')); | ||||
|         } | ||||
|  | ||||
|         if (file_exists($destinationPath) && !$force) { | ||||
|             throw new \Exception(Text::_('COM_MEDIA_MOVE_FILE_ALREADY_EXISTS')); | ||||
|         } | ||||
|  | ||||
|         if (!File::move($sourcePath, $destinationPath)) { | ||||
|             throw new \Exception(Text::_('COM_MEDIA_MOVE_FILE_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(Text::_('COM_MEDIA_MOVE_FOLDER_ALREADY_EXISTS')); | ||||
|         } | ||||
|  | ||||
|         if (is_file($destinationPath) && !File::delete($destinationPath)) { | ||||
|             throw new \Exception(Text::_('COM_MEDIA_MOVE_FOLDER_NOT_POSSIBLE')); | ||||
|         } | ||||
|  | ||||
|         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(Text::_('COM_MEDIA_MOVE_FOLDER_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() . '.' . strtolower(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 | ||||
|     { | ||||
|         try { | ||||
|             (new Image($path))->createThumbnails([$this->thumbnailSize[0] . 'x' . $this->thumbnailSize[1]], Image::SCALE_INSIDE, \dirname($thumbnailPath), true); | ||||
|         } catch (\Exception $e) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										146
									
								
								plugins/filesystem/local/src/Extension/Local.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								plugins/filesystem/local/src/Extension/Local.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,146 @@ | ||||
| <?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] | ||||
|             ); | ||||
|  | ||||
|             if ($this->getApplication()->getIdentity()) { | ||||
|                 $adapter->setCurrentUser($this->getApplication()->getIdentity()); | ||||
|             } | ||||
|  | ||||
|             $adapters[$adapter->getAdapterName()] = $adapter; | ||||
|         } | ||||
|  | ||||
|         return $adapters; | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user