diff --git a/administrator/com_joomgallery/forms/image.xml b/administrator/com_joomgallery/forms/image.xml index 6deda8a3f..a1198ea9f 100644 --- a/administrator/com_joomgallery/forms/image.xml +++ b/administrator/com_joomgallery/forms/image.xml @@ -24,6 +24,13 @@ + + here and make sure that the adapter is working correctly." COM_JOOMGALLERY_SERVICE_MIGRATION_CHECK_TITLE="Check results" COM_JOOMGALLERY_SERVICE_MIGRATION_GENERAL_PRECHECK_DESC="General checks like log file, site state, ..." COM_JOOMGALLERY_SERVICE_MIGRATION_SOURCE_PRECHECK_DESC="Check source extension, directories and tables if they are compatible and existent." diff --git a/administrator/com_joomgallery/sql/install.mysql.utf8.sql b/administrator/com_joomgallery/sql/install.mysql.utf8.sql index 9974fde74..b13d08a3d 100644 --- a/administrator/com_joomgallery/sql/install.mysql.utf8.sql +++ b/administrator/com_joomgallery/sql/install.mysql.utf8.sql @@ -16,6 +16,7 @@ CREATE TABLE IF NOT EXISTS `#__joomgallery` ( `imgmetadata` TEXT NOT NULL, `published` TINYINT(1) NOT NULL DEFAULT 0, `filename` VARCHAR(255) NOT NULL, +`filesystem` VARCHAR(100) NOT NULL DEFAULT "local-images", `hits` INT(11) UNSIGNED NOT NULL DEFAULT 0, `downloads` INT(11) UNSIGNED NOT NULL DEFAULT 0, `votes` INT(11) UNSIGNED NOT NULL DEFAULT 0, diff --git a/administrator/com_joomgallery/sql/updates/mysql/4.0.0.sql b/administrator/com_joomgallery/sql/updates/mysql/4.0.0.sql index 9974fde74..b13d08a3d 100644 --- a/administrator/com_joomgallery/sql/updates/mysql/4.0.0.sql +++ b/administrator/com_joomgallery/sql/updates/mysql/4.0.0.sql @@ -16,6 +16,7 @@ CREATE TABLE IF NOT EXISTS `#__joomgallery` ( `imgmetadata` TEXT NOT NULL, `published` TINYINT(1) NOT NULL DEFAULT 0, `filename` VARCHAR(255) NOT NULL, +`filesystem` VARCHAR(100) NOT NULL DEFAULT "local-images", `hits` INT(11) UNSIGNED NOT NULL DEFAULT 0, `downloads` INT(11) UNSIGNED NOT NULL DEFAULT 0, `votes` INT(11) UNSIGNED NOT NULL DEFAULT 0, diff --git a/administrator/com_joomgallery/src/Helper/FilesystemHelper.php b/administrator/com_joomgallery/src/Helper/FilesystemHelper.php new file mode 100644 index 000000000..4f414a3da --- /dev/null +++ b/administrator/com_joomgallery/src/Helper/FilesystemHelper.php @@ -0,0 +1,28 @@ + ** +** @copyright 2008 - 2025 JoomGallery::ProjectTeam ** +** @license GNU General Public License version 3 or later ** +*****************************************************************************************/ + +namespace Joomgallery\Component\Joomgallery\Administrator\Helper; + +// No direct access +defined('_JEXEC') or die; + +use \Joomla\Component\Media\Administrator\Provider\ProviderManagerHelperTrait; + +/** + * Helper for the filesystem adapters + * + * @static + * @package JoomGallery + * @since 4.0.0 + */ +class FilesystemHelper +{ + use ProviderManagerHelperTrait; +} diff --git a/administrator/com_joomgallery/src/Helper/JoomHelper.php b/administrator/com_joomgallery/src/Helper/JoomHelper.php index e225ae02f..6a6436386 100644 --- a/administrator/com_joomgallery/src/Helper/JoomHelper.php +++ b/administrator/com_joomgallery/src/Helper/JoomHelper.php @@ -23,6 +23,7 @@ use \Joomla\CMS\Http\HttpFactory; use \Joomla\CMS\Language\Multilanguage; use \Joomla\Database\DatabaseInterface; +use \Joomla\Component\Media\Administrator\Exception\FileNotFoundException; /** * JoomGallery Helper for the Backend @@ -498,18 +499,25 @@ public static function getImg($img, $type, $url=true, $root=true) { // Joomgallery internal URL // Example: https://www.example.org/index.php?option=com_joomgallery&controller=images&view=image&format=raw&type=orig&id=3&catid=1 - return Route::_(self::getViewRoute('image', $img->id, $img->catid, 'raw', $type)); + return Route::_(self::getViewRoute('image', $img->id, $img->catid, 'raw', $type)); } else { // Create file manager service $manager = self::getService('FileManager', array($img->catid)); // Create file manager service - $filesystem = self::getService('Filesystem'); + $filesystem = self::getService('Filesystem', array($img->filesystem)); // Real URL // Example: https://www.example.org/images/joomgallery/orig/test.jpg - return $filesystem->getUrl($manager->getImgPath($img, $type)); + try + { + return $filesystem->getUrl($manager->getImgPath($img, $type)); + } + catch (FileNotFoundException $e) + { + return self::getImgZero($type, $url, $root); + } } } else @@ -1051,6 +1059,94 @@ public static function fetchXML(string $uri): \SimpleXMLElement return new \SimpleXMLElement($xmlString); } + /** + * Method to check whether all needed filesystem plugins are available and enabled. + * + * @return bool + * + * @since 4.0.0 + */ + public static function checkFilesystems() + { + // Load filesystem helper + $helper = new FilesystemHelper; + + // Load all used filesystems from images table + $db = Factory::getContainer()->get(DatabaseInterface::class); + + $query = $db->getQuery(true) + ->select('DISTINCT ' .$db->quoteName('filesystem')) + ->from(_JOOM_TABLE_IMAGES) + ->where($db->quoteName('published') . ' = 1'); + + $db->setQuery($query); + $filesystems = $db->loadColumn(); + + // Loop through all found filesystems + foreach ($filesystems as $filesystem) + { + // Get corresponding names + $plugin_name = \explode('-', $filesystem, 2)[0]; + $plugin_fullname = 'plg_filesystem_'.$plugin_name; + $adapter_name = \explode('-', $filesystem, 2)[1]; + + // Try to get the corresponding filesystem adapter + try + { + $adapter = $helper->getAdapter($filesystem); + } catch (\Exception $e) + { + $adapter = false; + } + + if(!$adapter) + { + // Plugin is not installed, not enabled or not correctly configured. Show warning message. + $lang = Factory::getLanguage(); + + if(!$lang->getPaths($plugin_fullname)) + { + // Language file is not available + $langFile = JPATH_PLUGINS . '/filesystem/' . $plugin_name; + + // Try to load plugin language file + $lang->load($plugin_fullname); + $lang->load($plugin_fullname, $langFile); + } + + $plugins_url = Route::_('index.php?option=com_plugins&view=plugins&filter[folder]=filesystem'); + $plugin_title = Text::_($plugin_fullname); + + self::getComponent()->setWarning(Text::sprintf('COM_JOOMGALLERY_SERVICE_ERROR_FILESYSTEM_PLUGIN_NOT_ENABLED', $adapter_name, $plugin_title, $plugins_url)); + } + } + } + + /** + * Method to get the imagetype from an image path. + * + * @param string $path The image path. + * + * @return string + * + * @since 4.0.0 + */ + public static function getImagetypeFromPath(string $path) + { + $path = Path::clean($path, '/'); + $imagetypes = JoomHelper::getRecords('imagetypes'); + + foreach($imagetypes as $imagetype) + { + if(\strpos($path, $imagetype->path) !== false) + { + return $imagetype->typename; + } + } + + return 'thumbnail'; + } + /** * Returns a list of all available access action names available * diff --git a/administrator/com_joomgallery/src/Model/CategoryModel.php b/administrator/com_joomgallery/src/Model/CategoryModel.php index fad1ed4a2..235089826 100644 --- a/administrator/com_joomgallery/src/Model/CategoryModel.php +++ b/administrator/com_joomgallery/src/Model/CategoryModel.php @@ -366,15 +366,16 @@ public function delete(&$pks) */ public function save($data) { - $table = $this->getTable(); - $context = $this->option . '.' . $this->name; - $app = Factory::getApplication(); - $isNew = true; - $catMoved = false; - $isCopy = false; - $aliasChanged = false; - $hasChildren = false; - $hasImages = false; + $table = $this->getTable(); + $context = $this->option . '.' . $this->name; + $app = Factory::getApplication(); + $isNew = true; + $catMoved = false; + $isCopy = false; + $aliasChanged = false; + $hasChildren = false; + $hasImages = false; + $adapterChanged = false; $key = $table->getKeyName(); $pk = (isset($data[$key])) ? $data[$key] : (int) $this->getState($this->getName() . '.id'); @@ -449,7 +450,7 @@ public function save($data) } // Check if category has subcategories (children) - if(!$this->getChildren($pk)) + if($this->getChildren($pk)) { $hasChildren = true; } @@ -459,17 +460,24 @@ public function save($data) { $hasImages = true; } + + // Check if filesystem adapter has changed + $old_params = \json_decode($table->params); + if($old_params->{'jg_filesystem'} != $data['params']['jg_filesystem']) + { + $adapterChanged = true; + } } // Check that filesystem field content is allowed - if(\key_exists('jg_filesystem', $data['params']) && $data['params']['jg_filesystem'] != '' && $data['parent_id'] != 1) + if($adapterChanged && $data['parent_id'] != 1) { // Only allowed in toplevel categories $this->setError(Text::_('COM_JOOMGALLERY_ERROR_FILESYSTEM_ONLY_TOP_LEVEL_CAT')); return false; } - elseif(\key_exists('jg_filesystem', $data['params']) && $data['params']['jg_filesystem'] != '' && ($hasChildren || $hasImages)) + elseif($adapterChanged && ($hasChildren || $hasImages)) { // Only allowed if there are no images and no subcategories $this->setError(Text::_('COM_JOOMGALLERY_ERROR_FILESYSTEM_ONLY_EMPTY_CAT')); diff --git a/administrator/com_joomgallery/src/Model/ImageModel.php b/administrator/com_joomgallery/src/Model/ImageModel.php index b7e4be34b..9834cb3f4 100644 --- a/administrator/com_joomgallery/src/Model/ImageModel.php +++ b/administrator/com_joomgallery/src/Model/ImageModel.php @@ -1080,7 +1080,7 @@ public function recreate(int $pk, $type='original'): bool } // Perform the recreation - if(!$this->component->getFileManager()->createImages($source, $table->filename, $table->catid)) + if(!$this->component->getFileManager()->createImages($source, $table->filename, $table->catid, true, false, [$type])) { $this->setError($table->getError()); diff --git a/administrator/com_joomgallery/src/Service/FileManager/FileManager.php b/administrator/com_joomgallery/src/Service/FileManager/FileManager.php index 22161ff5c..223df4294 100644 --- a/administrator/com_joomgallery/src/Service/FileManager/FileManager.php +++ b/administrator/com_joomgallery/src/Service/FileManager/FileManager.php @@ -13,6 +13,7 @@ \defined('_JEXEC') or die; use \Joomla\CMS\Language\Text; +use \Joomla\CMS\Filesystem\Path; use \Joomgallery\Component\Joomgallery\Administrator\Helper\JoomHelper; use \Joomgallery\Component\Joomgallery\Administrator\Extension\ServiceTrait; use \Joomgallery\Component\Joomgallery\Administrator\Service\FileManager\FileManagerInterface; @@ -93,18 +94,19 @@ public function __construct($catid, $selection=False) /** * Creation of image types based on source file. * Source file has to be given with a full system path. - * * * @param string $source Source file with which the image types shall be created * @param string $filename Name for the files to be created * @param object|int|string $cat Object, ID or alias of the corresponding category (default: 2) - * @param bool $processing True to create imagetypes by processing source (default: True) + * @param bool $processing True to create imagetypes by processing source (defualt: True) + * @param bool $local_source True if the source is a file located in a local folder (default: True) + * @param array $skip List of imagetypes to skip creation (default: []) * * @return bool True on success, false otherwise * * @since 4.0.0 */ - public function createImages($source, $filename, $cat=2, $processing=True): bool + public function createImages($source, $filename, $cat=2, $processing=True, $local_source=True, $skip=[]): bool { if(!$filename) { @@ -131,6 +133,13 @@ public function createImages($source, $filename, $cat=2, $processing=True): bool // Debug info $this->component->addDebug(Text::sprintf('COM_JOOMGALLERY_SERVICE_PROCESSING_IMAGETYPE', $imagetype->typename), true, true); + if(\in_array($imagetype->typename, $skip) || \in_array($imagetype->type_alias, $skip)) + { + $this->component->addDebug(Text::_('COM_JOOMGALLERY_SERVICE_MANIPULATION_NOT_NEEDED')); + + continue; + } + // For original images: check if processing is really needed if( $imagetype->typename == 'original' && $processing && $imagetype->params->get('jg_imgtypeanim', 0) == 1 && @@ -170,12 +179,41 @@ public function createImages($source, $filename, $cat=2, $processing=True): bool // Grap resource if needed $isStream = false; - if(\strpos($this->component->getFilesystem()->getFilesystem(), 'local') === false) + if(\is_resource($source)) + { + $isStream = true; + } + elseif(\is_string($source) && !$local_source && \strpos($this->component->getFilesystem()->getFilesystem(), 'local') === false) { - // We are dealing with an external filesystem - $source = $this->component->getFilesystem()->getResource($source); + // The path is pointing to an external filesystem + list($file_info, $source) = $this->component->getFilesystem()->getResource($source); $isStream = true; } + elseif(\is_string($source) && ($local_source || \strpos($this->component->getFilesystem()->getFilesystem(), 'local') !== false)) + { + // The path is pointing to the local filesystem + $source = Path::clean($source); + + if(!\file_exists($source)) + { + // Add root to the path + $source = JPATH_ROOT.\DIRECTORY_SEPARATOR.$source; + + $source = Path::clean($source); + } + } + else + { + // Destroy the IMGtools service + $this->component->delIMGtools(); + + // Debug info + $this->component->addDebug(Text::sprintf('COM_JOOMGALLERY_SERVICE_ERROR_CREATE_IMAGETYPE', $filename, $imagetype->typename)); + $this->component->addLog(Text::sprintf('COM_JOOMGALLERY_SERVICE_ERROR_CREATE_IMAGETYPE', $filename, $imagetype->typename), 'error', 'jerror'); + $error = true; + + continue; + } // Read source image if(!$this->component->getIMGtools()->read($source, $isStream)) @@ -266,13 +304,14 @@ public function createImages($source, $filename, $cat=2, $processing=True): bool $folder = \dirname($file); try { - $res = $this->component->getFilesystem()->createFolder(\basename($folder), \dirname($folder)); + $res = $this->component->getFilesystem()->createFolder(\basename($folder), \dirname($folder), false); } - catch(\FileExistsException $e) + catch (FileExistsException $e) { - // Do nothing + // Folder already exists. + $res = true; } - catch(\Exception $e) + catch (\Exception $e) { // Destroy the IMGtools service $this->component->delIMGtools(); @@ -339,7 +378,6 @@ public function createImages($source, $filename, $cat=2, $processing=True): bool $error = true; continue; - } catch (InvalidPathException $e) { @@ -360,15 +398,23 @@ public function createImages($source, $filename, $cat=2, $processing=True): bool catch (\Exception $e) { // Any other error during file creation + if(\strpos(\strtolower($e->getMessage()), 'file exists') !== false) + { + // Debug info + $this->component->addDebug(Text::sprintf('COM_JOOMGALLERY_ERROR_FILE_ALREADY_EXISTING', $filename)); + $this->component->addLog(Text::sprintf('COM_JOOMGALLERY_ERROR_FILE_ALREADY_EXISTING', $filename), 'error', 'jerror'); + } + else + { + // Debug info + $this->component->addDebug(Text::sprintf('COM_JOOMGALLERY_SERVICE_ERROR_FILESYSTEM_ERROR', $e->getMessage())); + $this->component->addDebug(Text::sprintf('COM_JOOMGALLERY_SERVICE_ERROR_CREATE_IMAGETYPE', $filename, $imagetype->typename)); + $this->component->addLog(Text::sprintf('COM_JOOMGALLERY_SERVICE_ERROR_FILESYSTEM_ERROR', $e->getMessage()), 'error', 'jerror'); + $this->component->addLog(Text::sprintf('COM_JOOMGALLERY_SERVICE_ERROR_CREATE_IMAGETYPE', $filename, $imagetype->typename), 'error', 'jerror'); + } // Destroy the IMGtools service $this->component->delIMGtools(); - - // Debug info - $this->component->addDebug(Text::sprintf('COM_JOOMGALLERY_SERVICE_ERROR_FILESYSTEM_ERROR', $e->getMessage())); - $this->component->addDebug(Text::sprintf('COM_JOOMGALLERY_SERVICE_ERROR_CREATE_IMAGETYPE', $filename, $imagetype->typename)); - $this->component->addLog(Text::sprintf('COM_JOOMGALLERY_SERVICE_ERROR_FILESYSTEM_ERROR', $e->getMessage()), 'error', 'jerror'); - $this->component->addLog(Text::sprintf('COM_JOOMGALLERY_SERVICE_ERROR_CREATE_IMAGETYPE', $filename, $imagetype->typename), 'error', 'jerror'); $error = true; continue; @@ -420,16 +466,23 @@ public function deleteImages($img): bool } catch (FileNotFoundException $e) { - // Do nothing + // File already missing. Do nothing. } catch (\Exception $e) { - // Deletion failed - $this->component->addDebug(Text::sprintf('COM_JOOMGALLERY_SERVICE_ERROR_DELETE_IMAGETYPE', \basename($file), $imagetype->typename)); - $this->component->addLog(Text::sprintf('COM_JOOMGALLERY_SERVICE_ERROR_DELETE_IMAGETYPE', \basename($file), $imagetype->typename), 'error', 'jerror'); - $error = true; + if(\strpos(\strtolower($e->getMessage()), 'not found') !== false || \strpos(\strtolower($e->getMessage()), 'no such file') !== false) + { + // File already missing. Do nothing. + } + else + { + // Deletion failed + $this->component->addDebug(Text::sprintf('COM_JOOMGALLERY_SERVICE_ERROR_DELETE_IMAGETYPE', \basename($file), $imagetype->typename)); + $this->component->addLog(Text::sprintf('COM_JOOMGALLERY_SERVICE_ERROR_DELETE_IMAGETYPE', \basename($file), $imagetype->typename), 'error', 'jerror'); + $error = true; - continue; + continue; + } } // Deletion successful @@ -481,8 +534,17 @@ public function checkImages($img): array } catch (\Exception $e) { - $this->component->addDebug($e->getMessage()); - $this->component->addLog($e->getMessage(), 'error', 'jerror'); + if(\strpos(\strtolower($e->getMessage()), 'not found') !== false || \strpos(\strtolower($e->getMessage()), 'no such file') !== false) + { + // File not found + $this->component->addDebug(Text::sprintf('COM_JOOMGALLERY_ERROR_FILE_NOT_EXISTING').', '.\basename($file).' ('.$imagetype->typename.')'); + $this->component->addLog(Text::sprintf('COM_JOOMGALLERY_ERROR_FILE_NOT_EXISTING').', '.\basename($file).' ('.$imagetype->typename.')', 'error', 'jerror'); + } + else + { + $this->component->addDebug($e->getMessage()); + $this->component->addLog($e->getMessage(), 'error', 'jerror'); + } return false; } @@ -697,7 +759,8 @@ public function renameImages($img, $filename): bool public function createCategory($foldername, $parent=1): bool { // Loop through all imagetypes - $error = false; + $error = false; + $catsCreated = 0; foreach($this->imagetypes as $key => $imagetype) { // Category path @@ -710,14 +773,21 @@ public function createCategory($foldername, $parent=1): bool } catch(\FileExistsException $e) { - // Do nothing + // Category already exists. Do nothing. } catch(\Exception $e) { // Debug info - $this->component->addDebug(Text::sprintf('COM_JOOMGALLERY_SERVICE_ERROR_CREATE_CATEGORY', $foldername)); - $this->component->addLog(Text::sprintf('COM_JOOMGALLERY_SERVICE_ERROR_CREATE_CATEGORY', $foldername), 'error', 'jerror'); - $error = true; + if(\strpos(\strtolower($e->getMessage()), 'file exists') !== false) + { + // Category already exists. Do nothing. + } + else + { + $this->component->addDebug(Text::sprintf('COM_JOOMGALLERY_SERVICE_ERROR_CREATE_CATEGORY', $foldername)); + $this->component->addLog(Text::sprintf('COM_JOOMGALLERY_SERVICE_ERROR_CREATE_CATEGORY', $foldername), 'error', 'jerror'); + $error = true; + } continue; } @@ -731,6 +801,9 @@ public function createCategory($foldername, $parent=1): bool continue; } + + // Count up the # of created categories + $catsCreated = $catsCreated + 1; } if($error) @@ -741,8 +814,11 @@ public function createCategory($foldername, $parent=1): bool return false; } - // Debug info - $this->component->addDebug(Text::sprintf('COM_JOOMGALLERY_SERVICE_SUCCESS_CREATE_CATEGORY', $foldername)); + if($catsCreated > 0) + { + // Debug info + $this->component->addDebug(Text::sprintf('COM_JOOMGALLERY_SERVICE_SUCCESS_CREATE_CATEGORY', $foldername)); + } return true; } diff --git a/administrator/com_joomgallery/src/Service/FileManager/FileManagerInterface.php b/administrator/com_joomgallery/src/Service/FileManager/FileManagerInterface.php index e521563b0..357e6e302 100644 --- a/administrator/com_joomgallery/src/Service/FileManager/FileManagerInterface.php +++ b/administrator/com_joomgallery/src/Service/FileManager/FileManagerInterface.php @@ -29,17 +29,18 @@ interface FileManagerInterface * Creation of image types based on source file. * Source file has to be given with a full system path. * - * * @param string $source Source file with which the image types shall be created * @param string $filename Name for the files to be created * @param object|int|string $cat Object, ID or alias of the corresponding category (default: 2) - * @param bool $processing True to create imagetypes by processing source (default: True) - * + * @param bool $processing True to create imagetypes by processing source (defualt: True) + * @param bool $local_source True if the source is a file located in a local folder (default: True) + * @param array $skip List of imagetypes to skip creation (default: []) + * * @return bool True on success, false otherwise * * @since 4.0.0 */ - public function createImages($source, $filename, $cat=2, $processing=True): bool; + public function createImages($source, $filename, $cat=2, $processing=True, $local_source=True, $skip=[]): bool; /** * Deletion of image types diff --git a/administrator/com_joomgallery/src/Service/Filesystem/Exception/AdapterNotFoundException.php b/administrator/com_joomgallery/src/Service/Filesystem/Exception/AdapterNotFoundException.php new file mode 100644 index 000000000..7a501883e --- /dev/null +++ b/administrator/com_joomgallery/src/Service/Filesystem/Exception/AdapterNotFoundException.php @@ -0,0 +1,23 @@ + ** +** @copyright 2008 - 2025 JoomGallery::ProjectTeam ** +** @license GNU General Public License version 3 or later ** +*****************************************************************************************/ + +namespace Joomgallery\Component\Joomgallery\Administrator\Service\Filesystem\Exception; + +// No direct access +\defined('_JEXEC') or die; + +/** + * Media adapter not found exception. + * + * @since 4.0.0 + */ +class AdapterNotFoundException extends \Exception +{ +} diff --git a/administrator/com_joomgallery/src/Service/Filesystem/Filesystem.php b/administrator/com_joomgallery/src/Service/Filesystem/Filesystem.php index bab342525..b7a217023 100644 --- a/administrator/com_joomgallery/src/Service/Filesystem/Filesystem.php +++ b/administrator/com_joomgallery/src/Service/Filesystem/Filesystem.php @@ -14,7 +14,6 @@ \defined('_JEXEC') or die; use \Joomla\CMS\Factory; -use \Joomla\CMS\Log\Log; use \Joomla\CMS\Language\Text; use \Joomla\CMS\Object\CMSObject; use \Joomla\CMS\Plugin\PluginHelper; @@ -32,7 +31,9 @@ use \Joomla\Component\Media\Administrator\Provider\ProviderManagerHelperTrait; use \Joomgallery\Component\Joomgallery\Administrator\Service\Filesystem\FilesystemInterface; +use \Joomgallery\Component\Joomgallery\Administrator\Service\Filesystem\Exception\AdapterNotFoundException; use \Joomgallery\Component\Joomgallery\Administrator\Extension\ServiceTrait; +use Joomgallery\Component\Joomgallery\Administrator\Helper\JoomHelper; /** * Filesystem Base Class @@ -98,7 +99,7 @@ public function __construct(string $filesystem = '') else { // Define filesystem adapter based on configuration 'jg_filesystem' - $this->component->getConfig()->get('jg_filesystem','local-images'); + $this->filesystem = $this->component->getConfig()->get('jg_filesystem','local-images'); } // Load language of com_media @@ -219,7 +220,7 @@ public function createIndexHtml(string $path): string } else { - $this->component->addLog(Text::_('COM_JOOMGALLERY_SERVICE_ERROR_CREATE_FILE'), 'error', 'jerror'); + $this->component->addLog(Text::_('COM_JOOMGALLERY_SERVICE_ERROR_CREATE_FILE'), 'error', 'filesystem'); throw new InvalidPathException(Text::_('COM_JOOMGALLERY_SERVICE_ERROR_CREATE_FILE')); } } @@ -302,22 +303,28 @@ public function getFile(string $path = '/', array $options = []): \stdClass } catch (FileNotFoundException $e) { - $this->component->addLog('FileNotFoundException in function getFile in Filesystem.php: ' . Text::_('COM_JOOMGALLERY_SERVICE_ERROR_FILENOTFOUND'), 'warning', 'jerror'); - $this->component->addLog('$adapter: ' . $adapter, 'warning', 'jerror'); - $this->component->addLog('$path: ' . $path, 'warning', 'jerror'); + $this->component->addLog('FileNotFoundException in function getFile in Filesystem.php: ' . Text::_('COM_JOOMGALLERY_SERVICE_ERROR_FILENOTFOUND'), 'warning', 'filesystem'); + $this->component->addLog('$adapter: ' . $adapter, 'warning', 'filesystem'); + $this->component->addLog('$path: ' . $path, 'warning', 'filesystem'); throw new FileNotFoundException(Text::_('COM_JOOMGALLERY_SERVICE_ERROR_FILENOTFOUND')); } catch (\Exception $e) { - $msg = $e->getMessage(); - if(\strpos($e->getMessage(), 'account') !== false || \strpos($e->getMessage(), 'Account') !== false) + if(\strpos(\strtolower($e->getMessage()), 'account')) { - $this->component->addLog(Text::_('COM_JOOMGALLERY_SERVICE_ERROR_FILESYSTEM_NOT_FOUND'), 'error', 'jerror'); - throw new \Exception(Text::sprintf('COM_JOOMGALLERY_SERVICE_ERROR_FILESYSTEM_NOT_FOUND', $adapter)); + $this->component->addLog(Text::_('COM_JOOMGALLERY_SERVICE_ERROR_FILESYSTEM_NOT_FOUND', $adapter), 'error', 'filesystem'); + throw new AdapterNotFoundException(Text::sprintf('COM_JOOMGALLERY_SERVICE_ERROR_FILESYSTEM_NOT_FOUND', $adapter)); + } + elseif(\strpos(\strtolower($e->getMessage()), 'not found') !== false || \strpos(\strtolower($e->getMessage()), 'no such file') !== false) + { + $this->component->addLog('FileNotFoundException in function getFile in Filesystem.php: ' . Text::_('COM_JOOMGALLERY_SERVICE_ERROR_FILENOTFOUND'), 'warning', 'filesystem'); + $this->component->addLog('$adapter: ' . $adapter, 'warning', 'filesystem'); + $this->component->addLog('$path: ' . $path, 'warning', 'filesystem'); + throw new FileNotFoundException(Text::_('COM_JOOMGALLERY_SERVICE_ERROR_FILENOTFOUND')); } else { - $this->component->addLog($e->getMessage(), 'error', 'jerror'); + $this->component->addLog($e->getMessage(), 'error', 'filesystem'); throw new \Exception($e->getMessage()); } } @@ -325,7 +332,7 @@ public function getFile(string $path = '/', array $options = []): \stdClass // Check if it is an allowed file if($file->type == 'file' && !$this->isAllowedFile($file->path)) { - $this->component->addLog(Text::_('COM_JOOMGALLERY_ERROR_UNSUPPORTED_FILE_TYPE'), 'error', 'jerror'); + $this->component->addLog(Text::_('COM_JOOMGALLERY_ERROR_UNSUPPORTED_FILE_TYPE'), 'error', 'filesystem'); throw new InvalidPathException(Text::_('COM_JOOMGALLERY_ERROR_UNSUPPORTED_FILE_TYPE')); } @@ -336,7 +343,7 @@ public function getFile(string $path = '/', array $options = []): \stdClass if(isset($options['content']) && $options['content'] && $file->type == 'file') { - $resource = $this->getAdapter($adapter)->getResource($file->path); + list($file_info, $resource) = $this->getAdapter($adapter)->getResource($file->path); if($resource) { @@ -403,7 +410,7 @@ public function getFiles(string $path = '/', array $options = []): array if(isset($options['content']) && $options['content'] && $file->type == 'file') { - $resource = $this->getAdapter($adapter)->getResource($file->path); + list($file_info, $resource) = $this->getAdapter($adapter)->getResource($file->path); if($resource) { @@ -431,6 +438,7 @@ public function getFiles(string $path = '/', array $options = []): array * @param string $name The name * @param string $path The folder * @param boolean $override Should the folder being overridden when it exists (default: true) + * @param boolean $loop True, if we are in a recursive loop (default: false) * * @return string The folder name * @@ -438,7 +446,7 @@ public function getFiles(string $path = '/', array $options = []): array * @throws \Exception * @see AdapterInterface::createFolder() */ - public function createFolder(string $name, string $path, bool $override = true): string + public function createFolder(string $name, string $path, bool $override = true, bool $loop = false): string { $adapter = $this->getFilesystem(); $path = $this->cleanPath($this->adjustPath($path), '/'); @@ -449,14 +457,13 @@ public function createFolder(string $name, string $path, bool $override = true): } catch (FileNotFoundException $e) { - // Do nothing + // Folder not found; proceed to create it } - // Check if the file exists - if - (isset($file) && !$override) + if(isset($file) && !$override) { - throw new FileExistsException(); + // No need to create folders + throw new FileExistsException('Folder already exists: ' . $path . '/' . $name); } $object = new CMSObject(); @@ -468,12 +475,62 @@ public function createFolder(string $name, string $path, bool $override = true): $result = $this->app->triggerEvent('onContentBeforeSave', ['com_media.folder', $object, true, $object]); - if(in_array(false, $result, true)) + if(\in_array(false, $result, true)) { throw new \Exception($object->getError()); } - $object->name = $this->getAdapter($object->adapter)->createFolder($object->name, $object->path); + try + { + // Try to create folders recursively + $object->name = $this->getAdapter($object->adapter)->createFolder($object->name, $object->path); + } + catch (\Exception $e) + { + if(!$loop) + { + // If it doesnt work like that, try again by creating them one by one + $folders = \explode('/', trim($object->path, '/')); + $leading_slash = \strpos($object->path, '/') === 0; + $currentPath = $leading_slash ? '/' : ''; + + // Append the current folder to the folders array + $folders[] = $object->name; + + foreach($folders as $folder) + { + try + { + // Create each folder one by one + $object->name = $this->createFolder($folder, $currentPath, $override, true); + } + catch (FileExistsException $fee) + { + // Folder already exists; no action needed + } + catch (\Exception $e) + { + if(\strpos(\strtolower($e->getMessage()), 'file exists') !== false) + { + // Handle the case where the adapter does not throw a proper FileExistsException + // Folder already exists; no action needed + } + else + { + // Rethrow other exceptions for proper error handling + throw new \Exception($e->getMessage(), $e->getCode(), $e); + } + } + + // Adjust the currently existing path + $currentPath .= ($currentPath === '/' ? '' : '/') . $folder; + } + } + else + { + throw new \Exception($e->getMessage()); + } + } $this->app->triggerEvent('onContentAfterSave', ['com_media.folder', $object, true, $object]); @@ -569,7 +626,7 @@ public function updateFile(string $name, string $path, $data) // Check if it is an allowed file if(!$this->isAllowedFile($path . '/' . $name)) { - $this->component->addLog(Text::_('COM_JOOMGALLERY_ERROR_UNSUPPORTED_FILE_TYPE'), 'error', 'jerror'); + $this->component->addLog(Text::_('COM_JOOMGALLERY_ERROR_UNSUPPORTED_FILE_TYPE'), 'error', 'filesystem'); throw new InvalidPathException(Text::_('COM_JOOMGALLERY_ERROR_UNSUPPORTED_FILE_TYPE')); } @@ -589,7 +646,7 @@ public function updateFile(string $name, string $path, $data) if(in_array(false, $result, true)) { - $this->component->addLog($object->getError(), 'error', 'jerror'); + $this->component->addLog($object->getError(), 'error', 'filesystem'); throw new \Exception($object->getError()); } @@ -620,7 +677,7 @@ public function delete(string $path) // Check if it is an allowed file if($file->type == 'file' && !$this->isAllowedFile($file->path)) { - $this->component->addLog(Text::_('COM_JOOMGALLERY_ERROR_UNSUPPORTED_FILE_TYPE'), 'error', 'jerror'); + $this->component->addLog(Text::_('COM_JOOMGALLERY_ERROR_UNSUPPORTED_FILE_TYPE'), 'error', 'filesystem'); throw new InvalidPathException(Text::_('COM_JOOMGALLERY_ERROR_UNSUPPORTED_FILE_TYPE')); } @@ -638,7 +695,7 @@ public function delete(string $path) if(in_array(false, $result, true)) { - $this->component->addLog($object->getError(), 'error', 'jerror'); + $this->component->addLog($object->getError(), 'error', 'filesystem'); throw new \Exception($object->getError()); } @@ -710,11 +767,41 @@ public function getUrl(string $path): string // Check if it is an allowed file if(!$this->isAllowedFile($path)) { - $this->component->addLog(Text::_('COM_JOOMGALLERY_ERROR_UNSUPPORTED_FILE_TYPE'), 'error', 'jerror'); + $this->component->addLog(Text::_('COM_JOOMGALLERY_ERROR_UNSUPPORTED_FILE_TYPE'), 'error', 'filesystem'); throw new InvalidPathException(Text::_('COM_JOOMGALLERY_ERROR_UNSUPPORTED_FILE_TYPE')); } - $url = $this->getAdapter($adapter)->getUrl($path); + try + { + $url = $this->getAdapter($adapter)->getUrl($path); + } + catch (FileNotFoundException $e) + { + $this->component->addLog('FileNotFoundException in function getUrl in Filesystem.php: ' . Text::_('COM_JOOMGALLERY_SERVICE_ERROR_FILENOTFOUND'), 'warning', 'filesystem'); + $this->component->addLog('$adapter: ' . $adapter, 'warning', 'filesystem'); + $this->component->addLog('$path: ' . $path, 'warning', 'filesystem'); + throw new FileNotFoundException(Text::_('COM_JOOMGALLERY_SERVICE_ERROR_FILENOTFOUND')); + } + catch (\Exception $e) + { + if(\strpos(\strtolower($e->getMessage()), 'account')) + { + $this->component->addLog(Text::_('COM_JOOMGALLERY_SERVICE_ERROR_FILESYSTEM_NOT_FOUND', $adapter), 'error', 'filesystem'); + throw new AdapterNotFoundException(Text::sprintf('COM_JOOMGALLERY_SERVICE_ERROR_FILESYSTEM_NOT_FOUND', $adapter)); + } + elseif(\strpos(\strtolower($e->getMessage()), 'not found') !== false || \strpos(\strtolower($e->getMessage()), 'no such file') !== false) + { + $this->component->addLog('FileNotFoundException in function getUrl in Filesystem.php: ' . Text::_('COM_JOOMGALLERY_SERVICE_ERROR_FILENOTFOUND'), 'warning', 'filesystem'); + $this->component->addLog('$adapter: ' . $adapter, 'warning', 'filesystem'); + $this->component->addLog('$path: ' . $path, 'warning', 'filesystem'); + throw new FileNotFoundException(Text::_('COM_JOOMGALLERY_SERVICE_ERROR_FILENOTFOUND')); + } + else + { + $this->component->addLog($e->getMessage(), 'error', 'filesystem'); + throw new \Exception($e->getMessage()); + } + } $event = new FetchMediaItemUrlEvent('onFetchMediaFileUrl', ['adapter' => $adapter, 'path' => $path, 'url' => $url]); $this->app->getDispatcher()->dispatch($event->getName(), $event); @@ -757,11 +844,23 @@ public function getResource(string $path): array $adapter = $this->getFilesystem(); $path = $this->cleanPath($this->adjustPath($path), '/'); + try + { + $file = $this->getFile($path); + } + catch (FileNotFoundException $e) + { + # Get the 'no-image' instead + $adapter = 'local-images'; + $type = JoomHelper::getImagetypeFromPath($path); + $path = $this->cleanPath($this->adjustPath(JoomHelper::getImgZero($type, false, false)), '/'); + $file = $this->getFile($path); + } + // Check if it is an allowed file - $file = $this->getFile($path); if($file->type != 'file' || !$this->isAllowedFile($file->path)) { - $this->component->addLog(Text::_('COM_JOOMGALLERY_ERROR_UNSUPPORTED_FILE_TYPE'), 'error', 'jerror'); + $this->component->addLog(Text::_('COM_JOOMGALLERY_ERROR_UNSUPPORTED_FILE_TYPE'), 'error', 'filesystem'); throw new InvalidPathException(Text::_('COM_JOOMGALLERY_ERROR_UNSUPPORTED_FILE_TYPE')); } diff --git a/administrator/com_joomgallery/src/Service/Uploader/Uploader.php b/administrator/com_joomgallery/src/Service/Uploader/Uploader.php index 1bdc3589f..70402353e 100644 --- a/administrator/com_joomgallery/src/Service/Uploader/Uploader.php +++ b/administrator/com_joomgallery/src/Service/Uploader/Uploader.php @@ -207,6 +207,9 @@ public function retrieveImage(&$data, $filename=True): bool } } + // Set filesystem + $data['filesystem'] = $this->component->getFilesystem()->get('filesystem'); + // Trigger onJoomBeforeUpload $plugins = $this->app->triggerEvent('onJoomBeforeUpload', array($data['filename'])); if(in_array(false, $plugins, true)) diff --git a/administrator/com_joomgallery/src/View/Image/RawView.php b/administrator/com_joomgallery/src/View/Image/RawView.php index bfbdeed92..2b369edb0 100644 --- a/administrator/com_joomgallery/src/View/Image/RawView.php +++ b/administrator/com_joomgallery/src/View/Image/RawView.php @@ -44,18 +44,27 @@ public function display($tpl = null) if(!$this->access($id)) { $this->app->redirect(Route::_('index.php', false), 403); - } - - // Get image path - $img_path = JoomHelper::getImg($id, $type, false, false); + } - // Create filesystem service + // Choose the filesystem adapter $adapter = ''; if($id === 0) { // Force local-images adapter to load the no-image file $adapter = 'local-images'; } + else + { + // Take the adapter from the image object + $img_obj = $this->get('Item'); + $adapter = $img_obj->filesystem; + } + + // Get image path + $img_obj ? $img = $img_obj : $img = $id; + $img_path = JoomHelper::getImg($img, $type, false, false); + + // Create filesystem service $this->component->createFilesystem($adapter); // Get image resource diff --git a/administrator/com_joomgallery/src/View/Images/HtmlView.php b/administrator/com_joomgallery/src/View/Images/HtmlView.php index 139ebe5ce..84fd8ac77 100644 --- a/administrator/com_joomgallery/src/View/Images/HtmlView.php +++ b/administrator/com_joomgallery/src/View/Images/HtmlView.php @@ -18,6 +18,7 @@ use \Joomla\CMS\HTML\Helpers\Sidebar; use \Joomla\CMS\Toolbar\ToolbarHelper; use \Joomla\Component\Content\Administrator\Extension\ContentComponent; +use \Joomgallery\Component\Joomgallery\Administrator\Helper\JoomHelper; use \Joomgallery\Component\Joomgallery\Administrator\View\JoomGalleryView; /** @@ -48,6 +49,9 @@ public function display($tpl = null) $this->pagination = $this->get('Pagination'); $this->filterForm = $this->get('FilterForm'); $this->activeFilters = $this->get('ActiveFilters'); + + // Check if filesystem plugins are available + JoomHelper::checkFilesystems(); // Check for errors. if(\count($errors = $this->get('Errors'))) diff --git a/administrator/com_joomgallery/tmpl/image/edit.php b/administrator/com_joomgallery/tmpl/image/edit.php index 8409b7a32..f109f640d 100644 --- a/administrator/com_joomgallery/tmpl/image/edit.php +++ b/administrator/com_joomgallery/tmpl/image/edit.php @@ -96,6 +96,7 @@ +
form->renderField('filesystem'); ?>
diff --git a/site/com_joomgallery/tmpl/images/default.php b/site/com_joomgallery/tmpl/images/default.php index 72fd2b31d..dc484adc8 100644 --- a/site/com_joomgallery/tmpl/images/default.php +++ b/site/com_joomgallery/tmpl/images/default.php @@ -155,7 +155,7 @@ - + escape($item->title); ?>