Файловый менеджер - Редактировать - /home/opticamezl/www/newok/osmylicensesmanager.zip
Назад
PK ��\(<�� script.installer.phpnu &1i� <?php /** * @package ShackExtensionSupport * @contact www.joomlashack.com, help@joomlashack.com * @copyright 2016-2025 Joomlashack.com. All rights reserved * @license https://www.gnu.org/licenses/gpl.html GNU/GPL * * This file is part of ShackExtensionSupport. * * ShackExtensionSupport is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * ShackExtensionSupport is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ShackExtensionSupport. If not, see <https://www.gnu.org/licenses/>. */ use Alledia\Installer\AbstractScript; // phpcs:disable PSR1.Files.SideEffects defined('_JEXEC') or die(); require_once 'library/Installer/include.php'; // phpcs:enable PSR1.Files.SideEffects // phpcs:disable PSR1.Classes.ClassDeclaration.MissingNamespace class PlgSystemOsmylicensesmanagerInstallerScript extends AbstractScript { } PK ��\�a � 1 language/en-GB/en-GB.lib_allediainstaller.sys.ininu &1i� ; @package AllediaInstaller ; @contact www.joomlashack.com, help@joomlashack.com ; @copyright Copyright (C) 2016 Open Sources Training, LLC, All rights reserved ; @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL ; ; Note : All ini files need to be saved as UTF-8 - No BOM ; ; @TODO: this file should be removed once all extensions have been updated LIB_ALLEDIAINSTALLER_CHANGE_LICENSE_KEY = "Change my license key" LIB_ALLEDIAINSTALLER_I_DONT_REMEMBER_MY_KEY = "I don't remember my key!" LIB_ALLEDIAINSTALLER_INSTALL_CANCELLED = "Install process canceled." LIB_ALLEDIAINSTALLER_LICENSE_KEY_ERROR = "We found an error trying to save your license key. Please try again inside the Joomlashack License Key Manager plugin. You can find it by going to Extensions -> Plugins -> search for 'license'." LIB_ALLEDIAINSTALLER_LICENSE_KEY_SUCCESS = "Your license key was saved successfully!" LIB_ALLEDIAINSTALLER_LICENSE_KEYS_MANAGER_REQUIRED = "You are using a Joomlashack Pro extension, but you don't have the Joomlashack License Key Manager plugin installed. Please install it and configure your license key in order to be able to update this extension." LIB_ALLEDIAINSTALLER_LICENSE_KEYS_PLACEHOLDER = "license key" LIB_ALLEDIAINSTALLER_LICENSED_AS = "%s and all related extensions are licensed as %s" LIB_ALLEDIAINSTALLER_MANIFEST_NOT_FOUND = "Manifest file not found: %s" LIB_ALLEDIAINSTALLER_MSG_LICENSE_KEYS_EMPTY = "* Update requires a valid license key." LIB_ALLEDIAINSTALLER_OBSOLETE_UNINSTALLED_FAIL = "Uninstalling %s %s, failed" LIB_ALLEDIAINSTALLER_OBSOLETE_UNINSTALLED_SUCCESS = "Uninstalling %s %s was successful" LIB_ALLEDIAINSTALLER_PUBLISHED = "Published" LIB_ALLEDIAINSTALLER_RELATED_EXTENSIONS = "Related Extensions" LIB_ALLEDIAINSTALLER_RELATED_INSTALL = "%s %s was successfully installed" LIB_ALLEDIAINSTALLER_RELATED_INSTALL_FAIL = "Installation of %s %s failed" LIB_ALLEDIAINSTALLER_RELATED_NOT_UNINSTALLED = "The %s %s won't be uninstalled because of possible dependencies" LIB_ALLEDIAINSTALLER_RELATED_UNINSTALL = "%s %s was successfully removed" LIB_ALLEDIAINSTALLER_RELATED_UNINSTALL_FAIL = "Unable to remove %s %s" LIB_ALLEDIAINSTALLER_RELATED_UPDATE = "%s %s was successfully updated" LIB_ALLEDIAINSTALLER_RELATED_UPDATE_FAIL = "Update of %s %s failed" LIB_ALLEDIAINSTALLER_RELATED_UPDATE_STATE_FAILED = "Failed, version %s" LIB_ALLEDIAINSTALLER_RELATED_UPDATE_STATE_INSTALLED = "Installed v%s" LIB_ALLEDIAINSTALLER_RELATED_UPDATE_STATE_SKIPED = "Skipped %s, the version %s is already installed" LIB_ALLEDIAINSTALLER_RELEASE_V = "Release: v%s" LIB_ALLEDIAINSTALLER_SAVE_LICENSE_KEY = "Save my license key" LIB_ALLEDIAINSTALLER_SHOW_DETAILS = "Show more details..." LIB_ALLEDIAINSTALLER_SORTED = "Sorted [%s]" LIB_ALLEDIAINSTALLER_THANKS_INSTALL = "Thanks for installing %s!" LIB_ALLEDIAINSTALLER_THANKS_UPDATE = "Thanks for updating %s!" LIB_ALLEDIAINSTALLER_WRONG_MYSQL = "Sorry, %s requires at least MySQL %s." LIB_ALLEDIAINSTALLER_WRONG_PHP = "Sorry, %s requires at least php %s." LIB_ALLEDIAINSTALLER_WRONG_PLATFORM = "Sorry, %s requires Joomla! %s." LIB_ALLEDIAINSTALLER_WRONG_PREVIOUS = "Sorry, updating %s requires at least version %s to be installed first." ; Copied from lib_shackinstaller.sys while we transition from old extension LIB_SHACKINSTALLER_CHANGE_LICENSE_KEY = "Change my license key" LIB_SHACKINSTALLER_I_DONT_REMEMBER_MY_KEY = "I don't remember my key!" LIB_SHACKINSTALLER_INSTALL_CANCELLED = "Install process canceled." LIB_SHACKINSTALLER_LICENSE_KEY_ERROR = "We found an error trying to save your license key. Please try again inside the Joomlashack License Key Manager plugin. You can find it by going to Extensions -> Plugins -> search for 'license'." LIB_SHACKINSTALLER_LICENSE_KEY_SUCCESS = "Your license key was saved successfully!" LIB_SHACKINSTALLER_LICENSE_KEYS_MANAGER_REQUIRED = "You are using a Joomlashack Pro extension, but you don't have the Joomlashack License Key Manager plugin installed. Please install it and configure your license key in order to be able to update this extension." LIB_SHACKINSTALLER_LICENSE_KEYS_PLACEHOLDER = "license key" LIB_SHACKINSTALLER_LICENSED_AS = "%s and all related extensions are licensed as %s" LIB_SHACKINSTALLER_MANIFEST_NOT_FOUND = "Manifest file not found: %s" LIB_SHACKINSTALLER_MSG_LICENSE_KEYS_EMPTY = "* Update requires a valid license key." LIB_SHACKINSTALLER_OBSOLETE_UNINSTALLED_FAIL = "Uninstalling %s %s, failed" LIB_SHACKINSTALLER_OBSOLETE_UNINSTALLED_SUCCESS = "Uninstalling %s %s was successful" LIB_SHACKINSTALLER_PUBLISHED = "Published" LIB_SHACKINSTALLER_RELATED_EXTENSIONS = "Related Extensions" LIB_SHACKINSTALLER_RELATED_INSTALL = "%s %s was successfully installed" LIB_SHACKINSTALLER_RELATED_INSTALL_FAIL = "Installation of %s %s failed" LIB_SHACKINSTALLER_RELATED_NOT_UNINSTALLED = "This extension does not uninstall %s %s" LIB_SHACKINSTALLER_RELATED_NOT_UNINSTALLED_SYSTEM = "The %s %s won't be uninstalled because of possible dependencies" LIB_SHACKINSTALLER_RELATED_UNINSTALL = "%s %s was successfully removed" LIB_SHACKINSTALLER_RELATED_UNINSTALL_FAIL = "Unable to remove %s %s" LIB_SHACKINSTALLER_RELATED_UPDATE = "%s %s was successfully updated" LIB_SHACKINSTALLER_RELATED_UPDATE_FAIL = "Update of %s %s failed" LIB_SHACKINSTALLER_RELATED_UPDATE_STATE_FAILED = "Failed, version %s" LIB_SHACKINSTALLER_RELATED_UPDATE_STATE_INSTALLED = "Installed v%s" LIB_SHACKINSTALLER_RELATED_UPDATE_STATE_SKIPED = "Skipped %s, the version %s is already installed" LIB_SHACKINSTALLER_RELEASE_V = "Release: v%s" LIB_SHACKINSTALLER_SAVE_LICENSE_KEY = "Save my license key" LIB_SHACKINSTALLER_SHOW_DETAILS = "Show more details..." LIB_SHACKINSTALLER_SORTED = "Sorted [%s]" LIB_SHACKINSTALLER_THANKS_INSTALL = "Thanks for installing %s!" LIB_SHACKINSTALLER_THANKS_UPDATE = "Thanks for updating %s!" LIB_SHACKINSTALLER_WRONG_MYSQL = "Sorry, %s requires at least MySQL %s." LIB_SHACKINSTALLER_WRONG_PHP = "Sorry, %s requires at least php %s." LIB_SHACKINSTALLER_WRONG_PLATFORM = "Sorry, %s requires Joomla! %s." LIB_SHACKINSTALLER_WRONG_PREVIOUS = "Sorry, updating %s requires at least version %s to be installed first." PK ��\�3�� � 7 language/en-GB/en-GB.plg_system_osmylicensesmanager.ininu &1i� ; @package ShackExtensionSupport ; @contact www.joomlashack.com, help@joomlashack.com ; @copyright 2016-2025 Joomlashack.com. All rights reserved ; @license https://www.gnu.org/licenses/gpl.html GNU/GPL ; ; This file is part of ShackExtensionSupport. ; ; ShackExtensionSupport is free software: you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by ; the Free Software Foundation, either version 2 of the License, or ; (at your option) any later version. ; ; ShackExtensionSupport is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with ShackExtensionSupport. If not, see <https://www.gnu.org/licenses/>. ; ; Note : All ini files need to be saved as UTF-8 - No BOM PLG_SYSTEM_OSMYLICENSESMANAGER = "System - Joomlashack Extension Support" PLG_SYSTEM_OSMYLICENSESMANAGER_DESCRIPTION = "For Pro licensed Joomlashack extensions, you must have a valid License Key to receive extension updates. Get your license key here: <a href=\"https://www.joomlashack.com/account/key/\" target=\"_blank\">Your Pro License Key</a>." PLG_SYSTEM_OSMYLICENSESMANAGER_FIELD_LICENSE_KEYS_LABEL = "License Key" PLG_SYSTEM_OSMYLICENSESMANAGER_FIELD_LICENSE_KEYS_DESC = "Joomlashack license key to update Pro extensions." PK ��\Aܐ� � * language/en-GB/en-GB.shackdefaultfiles.ininu &1i� ; @package ShackDefaultFiles ; @contact www.joomlashack.com, help@joomlashack.com ; @copyright 2021-2024 Joomlashack.com. All rights reserved ; @license https://www.gnu.org/licenses/gpl.html GNU/GPL ; ; This file is part of ShackDefaultFiles. ; ; ShackDefaultFiles is free software: you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by ; the Free Software Foundation, either version 2 of the License, or ; (at your option) any later version. ; ; ShackDefaultFiles is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with ShackDefaultFiles. If not, see <https://www.gnu.org/licenses/>. ; ; Note : All ini files need to be saved as UTF-8 - No BOM SHACKDEFAULTFILES_GO_PRO = "Go Pro to access more features" SHACKDEFAULTFILES_LEAVE_A_REVIEW_ON_JED = "Leave a review on the JED" SHACKDEFAULTFILES_LIKE_THIS_EXTENSION = "Like this extension?" PK ��\�/��4 4 ; language/en-GB/en-GB.plg_system_osmylicensesmanager.sys.ininu &1i� ; @package ShackExtensionSupport ; @contact www.joomlashack.com, help@joomlashack.com ; @copyright 2016-2025 Joomlashack.com. All rights reserved ; @license https://www.gnu.org/licenses/gpl.html GNU/GPL ; ; This file is part of ShackExtensionSupport. ; ; ShackExtensionSupport is free software: you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by ; the Free Software Foundation, either version 2 of the License, or ; (at your option) any later version. ; ; ShackExtensionSupport is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with ShackExtensionSupport. If not, see <https://www.gnu.org/licenses/>. ; ; Note : All ini files need to be saved as UTF-8 - No BOM PLG_SYSTEM_OSMYLICENSESMANAGER = "System - Joomlashack Extension Support" PLG_SYSTEM_OSMYLICENSESMANAGER_DESCRIPTION = "For Pro licensed Joomlashack extensions, you must have a valid License Key to receive extension updates. Get your license key here: <a href=\"https://www.joomlashack.com/account/key/\" target=\"_blank\">Your Pro License Key</a>." PK ��\�U�H� � / language/en-GB/en-GB.lib_shackinstaller.sys.ininu &1i� ; @package ShackInstaller ; @contact www.joomlashack.com, help@joomlashack.com ; @copyright 2016-2023 Joomlashack.com. All rights reserved ; @license https://www.gnu.org/licenses/gpl.html GNU/GPL ; ; This file is part of ShackInstaller. ; ; ShackInstaller is free software: you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by ; the Free Software Foundation, either version 2 of the License, or ; (at your option) any later version. ; ; ShackInstaller is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with ShackInstaller. If not, see <https://www.gnu.org/licenses/>. ; ; Note : All ini files need to be saved as UTF-8 - No BOM LIB_SHACKINSTALLER_ABORT_INSTALL = "Another extension is preventing installation. Please disable it and try again: %s" LIB_SHACKINSTALLER_ABORT_UNINSTALL = "Another extension has prevented completing the uninstll. Additional, related extensions may need to uninstalled." LIB_SHACKINSTALLER_CHANGE_LICENSE_KEY = "Change my license key" LIB_SHACKINSTALLER_I_DONT_REMEMBER_MY_KEY = "I don't remember my key!" LIB_SHACKINSTALLER_INSTALL_CANCELLED = "Install process canceled." LIB_SHACKINSTALLER_LICENSE_KEY_ERROR = "We found an error trying to save your license key. Please try again inside the Joomlashack License Key Manager plugin. You can find it by going to Extensions -> Plugins -> search for 'license'." LIB_SHACKINSTALLER_LICENSE_KEY_SUCCESS = "Your license key was saved successfully!" LIB_SHACKINSTALLER_LICENSE_KEYS_MANAGER_REQUIRED = "You are using a Joomlashack Pro extension, but you don't have the Joomlashack License Key Manager plugin installed. Please install it and configure your license key in order to be able to update this extension." LIB_SHACKINSTALLER_LICENSE_KEYS_PLACEHOLDER = "license key" LIB_SHACKINSTALLER_LICENSED_AS = "%s and all related extensions are licensed as %s" LIB_SHACKINSTALLER_MANIFEST_NOT_FOUND = "Manifest file not found: %s" LIB_SHACKINSTALLER_MSG_LICENSE_KEYS_EMPTY = "* Update requires a valid license key." LIB_SHACKINSTALLER_OBSOLETE_UNINSTALLED_FAIL = "Uninstalling %s %s, failed" LIB_SHACKINSTALLER_OBSOLETE_UNINSTALLED_SUCCESS = "Uninstalling %s %s was successful" LIB_SHACKINSTALLER_PUBLISHED = "Published" LIB_SHACKINSTALLER_RELATED_EXTENSIONS = "Related Extensions" LIB_SHACKINSTALLER_RELATED_INSTALL = "%s %s was successfully installed" LIB_SHACKINSTALLER_RELATED_INSTALL_FAIL = "Installation of %s %s failed" LIB_SHACKINSTALLER_RELATED_NOT_UNINSTALLED = "This extension does not uninstall %s %s" LIB_SHACKINSTALLER_RELATED_NOT_UNINSTALLED_SYSTEM = "The %s %s won't be uninstalled because of possible dependencies" LIB_SHACKINSTALLER_RELATED_UNINSTALL = "%s %s was successfully removed" LIB_SHACKINSTALLER_RELATED_UNINSTALL_FAIL = "Unable to remove %s %s" LIB_SHACKINSTALLER_RELATED_UPDATE = "%s %s was successfully updated" LIB_SHACKINSTALLER_RELATED_UPDATE_FAIL = "Update of %s %s failed" LIB_SHACKINSTALLER_RELATED_UPDATE_STATE_FAILED = "Failed, version %s" LIB_SHACKINSTALLER_RELATED_UPDATE_STATE_INSTALLED = "Installed v%s" LIB_SHACKINSTALLER_RELATED_UPDATE_STATE_SKIPED = "Skipped %s, the version %s is already installed" LIB_SHACKINSTALLER_RELEASE_V = "Release: v%s" LIB_SHACKINSTALLER_SAVE_LICENSE_KEY = "Save my license key" LIB_SHACKINSTALLER_SHOW_DETAILS = "Show more details..." LIB_SHACKINSTALLER_SORTED = "Sorted [%s]" LIB_SHACKINSTALLER_THANKS_INSTALL = "Thanks for installing %s!" LIB_SHACKINSTALLER_THANKS_UPDATE = "Thanks for updating %s!" LIB_SHACKINSTALLER_WRONG_MYSQL = "Sorry, %s requires at least MySQL %s." LIB_SHACKINSTALLER_WRONG_PHP = "Sorry, %s requires at least php %s." LIB_SHACKINSTALLER_WRONG_PLATFORM = "Sorry, %s requires Joomla! %s." LIB_SHACKINSTALLER_WRONG_PREVIOUS = "Sorry, updating %s requires at least version %s to be installed first." PK ��\�1��� � library/Free/PluginHelper.phpnu &1i� <?php /** * @package ShackExtensionSupport * @contact www.joomlashack.com, help@joomlashack.com * @copyright 2016-2025 Joomlashack.com. All rights reserved * @license https://www.gnu.org/licenses/gpl.html GNU/GPL * * This file is part of ShackExtensionSupport. * * ShackExtensionSupport is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * ShackExtensionSupport is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ShackExtensionSupport. If not, see <https://www.gnu.org/licenses/>. */ namespace Alledia\OSMyLicensesManager\Free; use Alledia\Framework\Joomla\Extension\Licensed; // phpcs:disable PSR1.Files.SideEffects defined('_JEXEC') or die(); // phpcs:enable PSR1.Files.SideEffects /** * PluginHelper class */ abstract class PluginHelper { /** * @var string */ protected static $downloadBaseURL = 'https://deploy.ostraining.com/client/download/'; /** * Update the license key on the plugin params * * @param ?string $licenseKeys * * @return bool */ public static function updateLicenseKeys(?string $licenseKeys = ''): bool { $licenseKeys = PluginHelper::sanitizeKey($licenseKeys); // Update the extension params $extension = new Licensed('osmylicensesmanager', 'plugin', 'system'); $extension->params->set('license-keys', $licenseKeys); $extension->storeParams(); return true; } /** * Detects if the passed URL is our download URL, returning a boolean value. * * @param string $url * * @return bool */ public static function isOurDownloadURL(string $url): bool { return preg_match('#^' . static::$downloadBaseURL . '#', $url) === 1; } /** * Removes the license key from the URL and returns it. * * @param string $url * * @return string */ public static function getURLWithoutLicenseKey(string $url): string { if (static::isOurDownloadURL($url)) { $url = preg_replace('#^(' . static::$downloadBaseURL . '(free|pro|paid)/[^/]+/[^/]+).*$#i', '$1', $url); $url .= '/'; } return $url; } /** * Sanitizes the license key, making sure we have only valid chars. * * @param string $key * * @return string */ public static function sanitizeKey(string $key): string { return preg_replace('/[^a-z0-9,]/i', '', $key); } /** * Appends the license key to the URL and returns it. We recognize the url * for designated generic extensions using a default license key to allow * legacy customers to download updates. * * @param string $url * @param string $keys * * @return string */ public static function appendLicenseKeyToURL(string $url, string $keys): string { if (static::isOurDownloadURL($url)) { $url = PluginHelper::getURLWithoutLicenseKey($url); $sanitizedKeys = static::sanitizeKey($keys); if ($keys) { $encodedKeys = base64_encode($sanitizedKeys); $url .= $encodedKeys; } } return $url; } /** * Detects the license type based on the URL. If no license is detected, * returns false * * @param string $url * * @return ?string */ public static function getLicenseTypeFromURL(string $url): ?string { if (preg_match('#^' . static::$downloadBaseURL . '(free|pro)/#', $url, $matches)) { return $matches[1]; } return null; } } PK ��\Y# �# $ library/Installer/AbstractScript.phpnu &1i� <?php /** * @package ShackInstaller * @contact www.joomlashack.com, help@joomlashack.com * @copyright 2016-2023 Joomlashack.com. All rights reserved * @license https://www.gnu.org/licenses/gpl.html GNU/GPL * * This file is part of ShackInstaller. * * ShackInstaller is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * ShackInstaller is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ShackInstaller. If not, see <https://www.gnu.org/licenses/>. */ namespace Alledia\Installer; use Alledia\Installer\Extension\Licensed; use JEventDispatcher; use JFormFieldCustomFooter; use Joomla\CMS\Application\CMSApplication; use Joomla\CMS\Application\CMSApplicationInterface; use Joomla\CMS\Factory; use Joomla\CMS\Installer\Installer; use Joomla\CMS\Installer\InstallerAdapter; use Joomla\CMS\Language\Text; use Joomla\CMS\MVC\Model\BaseDatabaseModel; use Joomla\CMS\Table\Extension; use Joomla\CMS\Table\Table; use Joomla\CMS\Uri\Uri; use Joomla\CMS\Version; use Joomla\Component\Plugins\Administrator\Model\PluginModel; use Joomla\Database\DatabaseDriver; use Joomla\Database\DatabaseInterface; use Joomla\Event\DispatcherInterface; use Joomla\Filesystem\File; use Joomla\Filesystem\Folder; use Joomla\Registry\Registry; use SimpleXMLElement; use Throwable; // phpcs:disable PSR1.Files.SideEffects defined('_JEXEC') or die(); require_once 'include.php'; // phpcs:enable PSR1.Files.SideEffects abstract class AbstractScript { public const VERSION = '2.6.1'; protected const TYPE_INSTALL = 'install'; protected const TYPE_DISCOVER_INSTALL = 'discover_install'; protected const TYPE_UPDATE = 'update'; protected const TYPE_UNINSTALL = 'uninstall'; /** * @var bool */ protected $outputAllowed = true; /** * @var CMSApplication */ protected $app = null; /** * @var DatabaseDriver */ protected $dbo = null; /** * @var string */ protected $schemaVersion = null; /** * @var JEventDispatcher|DispatcherInterface */ protected $dispatcher = null; /** * @var Installer */ protected $installer = null; /** * @var SimpleXMLElement */ protected $manifest = null; /** * @var SimpleXMLElement */ protected $previousManifest = null; /** * @var string */ protected $mediaFolder = null; /** * @var string */ protected $element = null; /** * @var string[] */ protected $systemExtensions = [ 'library..allediaframework', 'plugin.system.osmylicensesmanager', ]; /** * @var bool */ protected $isLicensesManagerInstalled = false; /** * @var Licensed */ protected $license = null; /** * @var string */ protected $licenseKey = null; /** * @var string */ protected $footer = null; /** * @var string */ protected $mediaURL = null; /** * @var string */ protected $type = null; /** * @var string */ protected $group = null; /** * List of tables and respective columns * * @var array */ protected $columns = null; /** * @var object[] */ protected $tableColumns = []; /** * @var object[] */ protected $tableIndexes = []; /** * @var object[] */ protected $tableConstraints = []; /** * @var array */ protected $tables = null; /** * Flag to cancel the installation * * @var bool */ protected $cancelInstallation = false; /** * Feedback of the install by related extension * * @var array */ protected $relatedExtensionFeedback = []; /** * @var string */ protected $welcomeMessage = null; /** * @var bool */ protected $debug = false; /** * @param InstallerAdapter $parent * * @return void * @throws \Exception */ public function __construct(InstallerAdapter $parent) { $this->sendDebugMessage('ShackInstaller v' . static::VERSION); $this->sendDebugMessage('Base v' . SHACK_INSTALLER_VERSION); $this->sendDebugMessage(__METHOD__); $this->initProperties($parent); } /** * cross-version method for getting a new installer instance * * @return Installer */ private function getNewInstaller(): Installer { $installer = new Installer(); if (Version::MAJOR_VERSION > 5) { $installer->setDatabase(Factory::getContainer()->get(DatabaseInterface::class)); } return $installer; } /** * @param InstallerAdapter $parent * * @return bool * @throws \Exception */ protected function checkInheritance(InstallerAdapter $parent): bool { $parentClasses = class_parents($this); $scriptClassName = array_pop($parentClasses); $scriptClass = new \ReflectionClass($scriptClassName); $sourcePath = dirname($scriptClass->getFileName()); $sourceBase = strpos($sourcePath, JPATH_PLUGINS) === 0 ? 3 : 2; $sourceVersion = AbstractScript::VERSION ?? '0.0.0'; $sourcePath = $this->cleanPath($sourcePath); $targetPath = $this->cleanPath(SHACK_INSTALLER_BASE); if ($sourcePath != $targetPath && version_compare($sourceVersion, SHACK_INSTALLER_COMPATIBLE, 'lt')) { $source = join('/', array_slice(explode('/', $sourcePath), 0, $sourceBase)); $errorMessage = 'LIB_SHACKINSTALLER_ABORT_' . ($parent->getRoute() == 'uninstall' ? 'UNINSTALL' : 'INSTALL'); Factory::getApplication()->enqueueMessage(Text::sprintf($errorMessage, $source), 'error'); $this->cancelInstallation = true; return false; } return true; } /** * @param string $path * * @return string */ protected function cleanPath(string $path): string { return str_replace(DIRECTORY_SEPARATOR, '/', str_replace(JPATH_ROOT . '/', '', $path)); } /** * @param InstallerAdapter $parent * * @return void * @throws \Exception */ protected function initProperties(InstallerAdapter $parent): void { $this->sendDebugMessage(__METHOD__); $this->app = Factory::getApplication(); $this->outputAllowed = JPATH_BASE == JPATH_ADMINISTRATOR; $language = Factory::getLanguage(); $language->load('lib_shackinstaller.sys', realpath(__DIR__ . '/../..')); if ($this->checkInheritance($parent) == false) { return; } try { $this->dbo = Version::MAJOR_VERSION > 4 ? Factory::getContainer()->get(DatabaseInterface::class) : Factory::getDbo(); $this->installer = $parent->getParent(); $this->manifest = $this->installer->getManifest(); $this->schemaVersion = $this->getSchemaVersion(); if ($media = $this->manifest->media) { $this->mediaFolder = JPATH_SITE . '/' . $media['folder'] . '/' . $media['destination']; } $attributes = $this->manifest->attributes(); $this->type = (string)$attributes['type']; $this->group = (string)$attributes['group']; // Get the previous manifest for use in upgrades $targetPath = $this->installer->getPath('extension_administrator') ?: $this->installer->getPath('extension_root'); $manifestPath = $targetPath . '/' . basename($this->installer->getPath('manifest')); if (is_file($manifestPath)) { $this->previousManifest = simplexml_load_file($manifestPath); } // Determine basepath for localized files $basePath = $this->installer->getPath('source'); if (is_dir($basePath)) { if ($this->type == 'component' && $basePath != $targetPath) { // For components sourced by manifest, need to find the admin folder if ($files = $this->manifest->administration->files) { if ($files = (string)$files['folder']) { $basePath .= '/' . $files; } } } } else { $basePath = $this->getExtensionPath( $this->type, (string)$this->manifest->alledia->element, $this->group ); } // All the files we want to load $languageFiles = [ $this->getFullElement(), ]; // Load from localized or core language folder foreach ($languageFiles as $languageFile) { $language->load($languageFile, $basePath) || $language->load($languageFile, JPATH_ADMINISTRATOR); } } catch (Throwable $error) { $this->cancelInstallation = true; $this->sendErrorMessage($error); } } /** * @return JEventDispatcher|DispatcherInterface */ protected function getDispatcher() { if ($this->dispatcher === null) { if (Version::MAJOR_VERSION < 4) { $this->dispatcher = JEventDispatcher::getInstance(); } else { $this->dispatcher = $this->app->getDispatcher(); } } return $this->dispatcher; } /** * @param InstallerAdapter $parent * * @return bool */ final public function install(InstallerAdapter $parent): bool { $this->sendDebugMessage(__METHOD__); try { return $this->customInstall($parent); } catch (Throwable $error) { $this->sendErrorMessage($error); } return false; } // phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps /** * @param InstallerAdapter $parent * * @return bool */ final public function discover_install(InstallerAdapter $parent): bool { $this->sendDebugMessage(__METHOD__); try { if ($this->install($parent)) { return $this->customDiscoverInstall($parent); } } catch (Throwable $error) { $this->sendErrorMessage($error); } return false; } // phpcs:enable PSR1.Methods.CamelCapsMethodName.NotCamelCaps /** * @param InstallerAdapter $parent * * @return bool */ final public function update(InstallerAdapter $parent): bool { $this->sendDebugMessage(__METHOD__); try { return $this->customUpdate($parent); } catch (Throwable $error) { $this->sendErrorMessage($error); } return false; } /** * @param string $type * @param InstallerAdapter $parent * * @return bool * @throws \Exception */ final public function preFlight(string $type, InstallerAdapter $parent): bool { if ($this->cancelInstallation) { $this->sendDebugMessage('CANCEL: ' . __METHOD__); return false; } try { $this->sendDebugMessage(__METHOD__); $success = true; if ($type === 'update') { $this->clearUpdateServers(); } if (in_array($type, [static::TYPE_INSTALL, static::TYPE_UPDATE])) { // Check minimum target Joomla Platform if (isset($this->manifest->alledia->targetplatform)) { $targetPlatform = (string)$this->manifest->alledia->targetplatform; if ($this->validateTargetVersion(JVERSION, $targetPlatform) == false) { // Platform version is invalid. Displays a warning and cancel the install $targetPlatform = str_replace('*', 'x', $targetPlatform); $msg = Text::sprintf('LIB_SHACKINSTALLER_WRONG_PLATFORM', $this->getName(), $targetPlatform); $this->sendMessage($msg, 'warning'); $success = false; } } // Check for minimum mysql version if ($targetMySqlVersion = $this->manifest->alledia->mysqlminimum) { $targetMySqlVersion = (string)$targetMySqlVersion; if ($this->dbo->getServerType() == 'mysql') { $dbVersion = $this->dbo->getVersion(); if (stripos($dbVersion, 'maria') !== false) { // For MariaDB this is a bit of a punt. We'll assume any version of Maria will do $dbVersion = $targetMySqlVersion; } if ($this->validateTargetVersion($dbVersion, $targetMySqlVersion) == false) { // mySQL version too low $minimumMySql = str_replace('*', 'x', $targetMySqlVersion); $msg = Text::sprintf('LIB_SHACKINSTALLER_WRONG_MYSQL', $this->getName(), $minimumMySql); $this->sendMessage($msg, 'warning'); $success = false; } } } // Check for minimum php version if (isset($this->manifest->alledia->phpminimum)) { $targetPhpVersion = (string)$this->manifest->alledia->phpminimum; if ($this->validateTargetVersion(phpversion(), $targetPhpVersion) == false) { // php version is too low $minimumPhp = str_replace('*', 'x', $targetPhpVersion); $msg = Text::sprintf('LIB_SHACKINSTALLER_WRONG_PHP', $this->getName(), $minimumPhp); $this->sendMessage($msg, 'warning'); $success = false; } } // Check for minimum previous version $targetVersion = (string)$this->manifest->alledia->previousminimum; if ($type == static::TYPE_UPDATE && $targetVersion) { if (!$this->validatePreviousVersion($targetVersion)) { // Previous minimum is not installed $minimumVersion = str_replace('*', 'x', $targetVersion); $msg = Text::sprintf('LIB_SHACKINSTALLER_WRONG_PREVIOUS', $this->getName(), $minimumVersion); $this->sendMessage($msg, 'warning'); $success = false; } } } if ($success) { $success = $this->customPreFlight($type, $parent); } if ($success) { if ( $type !== static::TYPE_UNINSTALL && empty($this->manifest->alledia->obsolete->preflight) == false ) { $this->clearObsolete($this->manifest->alledia->obsolete->preflight); } } $this->cancelInstallation = $success == false; return $success; } catch (Throwable $error) { $this->sendErrorMessage($error); } return false; } /** * @param string $type * @param InstallerAdapter $parent * * @return void * @throws \Exception */ final public function postFlight(string $type, InstallerAdapter $parent): void { $this->sendDebugMessage(__METHOD__); if ($this->cancelInstallation) { $this->sendMessage('LIB_SHACKINSTALLER_INSTALL_CANCELLED', 'warning'); return; } try { /* * Joomla 4 now calls postFlight on uninstalls. Which is kinda cool actually. * But this code is problematic in that scenario */ if ($type != static::TYPE_UNINSTALL) { $this->clearObsolete(); $this->installRelated(); $this->addAllediaAuthorshipToExtension(); $this->element = (string)$this->manifest->alledia->element; // Check and publish/reorder the plugin, if required if ( $this->type === 'plugin' && in_array($type, [static::TYPE_INSTALL, static::TYPE_DISCOVER_INSTALL]) ) { $this->publishThisPlugin(); $this->reorderThisPlugin(); } // If Free, remove any Pro library $license = $this->getLicense(); if (!$license->isPro()) { $proLibraryPath = $license->getProLibraryPath(); if (is_dir($proLibraryPath)) { Folder::delete($proLibraryPath); } } } $this->customPostFlight($type, $parent); if ($type != static::TYPE_UNINSTALL) { $this->displayWelcome($type); } } catch (Throwable $error) { $this->sendErrorMessage($error); } } /** * @param InstallerAdapter $parent * * @return void * @throws \Exception */ final public function uninstall(InstallerAdapter $parent): void { $this->sendDebugMessage(__METHOD__); try { $this->uninstallRelated(); $this->customUninstall($parent); } catch (Throwable $error) { $this->sendErrorMessage($error); } } /** * @param int $number * @param string $error * @param ?string $file * @param ?int $line * * @return void */ final public static function errorHandler(int $number, string $error, ?string $file = null, ?int $line = null): void { try { $codes = get_defined_constants(true); $codes = $codes['Core']; $codes = array_filter( $codes, function ($key) { return strpos($key, 'E_') === 0; }, ARRAY_FILTER_USE_KEY ); $name = array_search($number, $codes); Factory::getApplication()->enqueueMessage( sprintf('%s: %s<br>(%s) %s', $name, $error, $line ?: 'NA', $file ?: 'NA'), 'warning' ); } catch (Throwable $error) { // ignore } } /** * For use in subclasses * * @param string $type * @param InstallerAdapter $parent * * @return bool * @throws Throwable */ protected function customPreFlight(string $type, InstallerAdapter $parent): bool { return true; } /** * For use in subclasses * * @param InstallerAdapter $parent * * @return bool * @throws Throwable */ protected function customInstall(InstallerAdapter $parent): bool { return true; } /** * For use in subclasses * * @param InstallerAdapter $parent * * @return bool * @throws Throwable */ protected function customDiscoverInstall(InstallerAdapter $parent): bool { return true; } /** * For use in subclasses * * @param InstallerAdapter $parent * * @return bool * @throws Throwable */ protected function customUpdate(InstallerAdapter $parent): bool { return true; } /** * For use in subclassses * * @param string $type * @param InstallerAdapter $parent * * @return void * @throws Throwable */ protected function customPostFlight(string $type, InstallerAdapter $parent): void { } /** * For use in subclasses * * @param InstallerAdapter $parent * * @return void * @throws Throwable */ protected function customUninstall(InstallerAdapter $parent): void { } /** * @return void * @throws \Exception */ private function installRelated(): void { $this->sendDebugMessage(__METHOD__); if ($this->manifest->alledia->relatedExtensions) { $source = $this->installer->getPath('source'); $extensionsPath = $source . '/extensions'; $defaultAttributes = $this->manifest->alledia->relatedExtensions->attributes(); $defaultDowngrade = $this->getXmlValue($defaultAttributes['downgrade'], 'bool'); $defaultPublish = $this->getXmlValue($defaultAttributes['publish'], 'bool'); foreach ($this->manifest->alledia->relatedExtensions->extension as $extension) { $path = $extensionsPath . '/' . $this->getXmlValue($extension); if (is_dir($path)) { $type = $this->getXmlValue($extension['type']); $element = $this->getXmlValue($extension['element']); $group = $this->getXmlValue($extension['group']); $key = md5(join(':', [$type, $element, $group])); $this->sendDebugMessage( sprintf('Related: %s%s/%s', $type, $group ? ($group . '/') : '', $element) ); try { if ($type == 'plugin' && in_array($group, ['search', 'finder'])) { if (is_dir(JPATH_ADMINISTRATOR . '/components/com_' . $group) == false) { // skip search/finder plugins based on installed components $this->sendDebugMessage( sprintf( 'Skipped/Uninstalled plugin %s', ucwords($group . ' ' . $element) ) ); $this->uninstallExtension($type, $element, $group); continue; } } $current = $this->findExtension($type, $element, $group); $isNew = empty($current); $typeName = ucwords(trim($group . ' ' . $type)); // Get data from the manifest $tmpInstaller = $this->getNewInstaller(); $tmpInstaller->setPath('source', $path); $tmpInstaller->setPath('parent', $this->installer->getPath('source')); $newManifest = $tmpInstaller->getManifest(); $newVersion = (string)$newManifest->version; $this->storeFeedbackForRelatedExtension($key, 'name', (string)$newManifest->name); $downgrade = $this->getXmlValue($extension['downgrade'], 'bool', $defaultDowngrade); if (($isNew || $downgrade) == false) { $currentManifestPath = $this->getManifestPath($type, $element, $group); $currentManifest = $this->getInfoFromManifest($currentManifestPath); // Avoid to update for an outdated version $currentVersion = $currentManifest->get('version'); if (version_compare($currentVersion, $newVersion, '>')) { // Store the state of the install/update $this->storeFeedbackForRelatedExtension( $key, 'message', Text::sprintf( 'LIB_SHACKINSTALLER_RELATED_UPDATE_STATE_SKIPED', $newVersion, $currentVersion ) ); // Skip the installation for this extension continue; } } $text = 'LIB_SHACKINSTALLER_RELATED_' . ($isNew ? 'INSTALL' : 'UPDATE'); if ($tmpInstaller->install($path)) { $this->sendMessage(Text::sprintf($text, $typeName, $element)); if ($isNew) { $current = $this->findExtension($type, $element, $group); if (is_object($current)) { if ($type === 'plugin') { if ($this->getXmlValue($extension['publish'], 'bool', $defaultPublish)) { $current->publish(); $this->storeFeedbackForRelatedExtension($key, 'publish', true); } if ($ordering = $this->getXmlValue($extension['ordering'])) { $this->setPluginOrder($current, $ordering); $this->storeFeedbackForRelatedExtension($key, 'ordering', $ordering); } } } } $this->storeFeedbackForRelatedExtension( $key, 'message', Text::sprintf('LIB_SHACKINSTALLER_RELATED_UPDATE_STATE_INSTALLED', $newVersion) ); } else { $this->sendMessage(Text::sprintf($text . '_FAIL', $typeName, $element), 'error'); $this->storeFeedbackForRelatedExtension( $key, 'message', Text::sprintf( 'LIB_SHACKINSTALLER_RELATED_UPDATE_STATE_FAILED', $newVersion ) ); } unset($tmpInstaller); } catch (Throwable $error) { $this->sendErrorMessage($error, false); } } } } } /** * Uninstall the related extensions that are useless without the component * * @return void * @throws \Exception */ private function uninstallRelated(): void { if ($this->manifest->alledia->relatedExtensions) { $defaultAttributes = $this->manifest->alledia->relatedExtensions->attributes(); $defaultUninstall = $this->getXmlValue($defaultAttributes['uninstall'], 'bool'); foreach ($this->manifest->alledia->relatedExtensions->extension as $extension) { $type = $this->getXmlValue($extension['type']); $element = $this->getXmlValue($extension['element']); $group = $this->getXmlValue($extension['group']); $uninstall = $this->getXmlValue($extension['uninstall'], 'bool', $defaultUninstall); $systemExtension = in_array(join('.', [$type, $group, $element]), $this->systemExtensions); if ($uninstall && $systemExtension == false) { $this->uninstallExtension($type, $element, $group); } else { $message = 'LIB_SHACKINSTALLER_RELATED_NOT_UNINSTALLED' . ($systemExtension ? '_SYSTEM' : ''); if ($type == 'plugin') { $type = $group . ' ' . $type; } $this->sendDebugMessage(Text::sprintf($message, ucwords($type), $element)); } } } } /** * @param string $type * @param string $element * @param ?string $group * * @return void * @throws \Exception */ private function uninstallExtension(string $type, string $element, ?string $group = null): void { if ($extension = $this->findExtension($type, $element, $group)) { $installer = $this->getNewInstaller(); $success = $installer->uninstall($extension->get('type'), $extension->get('extension_id')); $msg = 'LIB_SHACKINSTALLER_RELATED_UNINSTALL' . ($success ? '' : '_FAIL'); if ($type == 'plugin') { $type = $group . ' ' . $type; } $this->sendMessage( Text::sprintf($msg, ucwords($type), $element), $success ? 'message' : 'error' ); } } /** * @param ?string $type * @param ?string $element * @param ?string $group * * @return ?Extension * @throws \Exception */ final protected function findExtension(?string $type, ?string $element, ?string $group = null): ?Extension { // @TODO: Why do we need to use JTable? /** @var Extension $row */ $row = Table::getInstance('extension'); $prefixes = [ 'component' => 'com_', 'module' => 'mod_', ]; // Fix the element, if the prefix is not found if (array_key_exists($type, $prefixes)) { if (substr_count($element, $prefixes[$type]) === 0) { $element = $prefixes[$type] . $element; } } $terms = [ 'type' => $type, 'element' => $element, ]; if ($type === 'plugin') { $terms['folder'] = $group; } $eid = $row->find($terms); if ($eid) { if ($row->load($eid) == false) { throw new \Exception($row->getError()); } return $row; } return null; } /** * Set requested ordering for selected plugin extension * Accepted ordering arguments: * (n<=1 | first) First within folder * (* | last) Last within folder * (before:element) Before the named plugin * (after:element) After the named plugin * * @param Extension $extension * @param string $order * * @return void */ final protected function setPluginOrder(Extension $extension, string $order): void { if ($extension->get('type') == 'plugin' && empty($order) == false) { $db = $this->dbo; $query = $db->getQuery(true); $query->select('extension_id, element'); $query->from('#__extensions'); $query->where([ $db->quoteName('folder') . ' = ' . $db->quote($extension->get('folder')), $db->quoteName('type') . ' = ' . $db->quote($extension->get('type')), ]); $query->order($db->quoteName('ordering')); $plugins = $db->setQuery($query)->loadObjectList('element'); // Set the order only if plugin already successfully installed if (array_key_exists($extension->get('element'), $plugins)) { $target = [ $extension->get('element') => $plugins[$extension->get('element')], ]; $others = array_diff_key($plugins, $target); if ((is_numeric($order) && $order <= 1) || $order == 'first') { // First in order $neworder = array_merge($target, $others); } elseif (($order == '*') || ($order == 'last')) { // Last in order $neworder = array_merge($others, $target); } elseif (preg_match('/^(before|after):(\S+)$/', $order, $match)) { // place before or after named plugin $place = $match[1]; $element = $match[2]; $neworder = []; $previous = ''; foreach ($others as $plugin) { if ( (($place == 'before') && ($plugin->element == $element)) || (($place == 'after') && ($previous == $element)) ) { $neworder = array_merge($neworder, $target); } $neworder[$plugin->element] = $plugin; $previous = $plugin->element; } if (count($neworder) < count($plugins)) { // Make it last if the requested plugin isn't installed $neworder = array_merge($neworder, $target); } } else { $neworder = []; } if (count($neworder) == count($plugins)) { // Only reorder if have a validated new order BaseDatabaseModel::addIncludePath( JPATH_ADMINISTRATOR . '/components/com_plugins/models', 'PluginsModels' ); // @TODO: Model class is (\PluginsModelPlugin) in J3 but this works either way /** @var PluginModel $model */ $model = BaseDatabaseModel::getInstance('Plugin', 'PluginsModel'); $ids = []; foreach ($neworder as $plugin) { $ids[] = $plugin->extension_id; } $order = range(1, count($ids)); $model->saveorder($ids, $order); } } } } /** * Delete obsolete files, folders and extensions. * Files and folders are identified from the site * root path. * * @param ?SimpleXMLElement $obsolete * * @return void * @throws \Exception */ final protected function clearObsolete(?SimpleXMLElement $obsolete = null): void { $obsolete = $obsolete ?: $this->manifest->alledia->obsolete; $this->sendDebugMessage(__METHOD__ . '<pre>' . print_r($obsolete, 1) . '</pre>'); $this->clearOldSystemPlugin(); if ($obsolete) { if ($obsolete->extension) { foreach ($obsolete->extension as $extension) { $type = $this->getXmlValue($extension['type']); $element = $this->getXmlValue($extension['element']); $group = $this->getXmlValue($extension['group']); $current = $this->findExtension($type, $element, $group); if (empty($current) == false) { // Try to uninstall $tmpInstaller = $this->getNewInstaller(); $uninstalled = $tmpInstaller->uninstall($type, $current->get('extension_id')); $typeName = ucfirst(trim(($group ?: '') . ' ' . $type)); if ($uninstalled) { $this->sendMessage( Text::sprintf( 'LIB_SHACKINSTALLER_OBSOLETE_UNINSTALLED_SUCCESS', strtolower($typeName), $element ) ); } else { $this->sendMessage( Text::sprintf( 'LIB_SHACKINSTALLER_OBSOLETE_UNINSTALLED_FAIL', strtolower($typeName), $element ), 'error' ); } } } } if ($obsolete->file) { foreach ($obsolete->file as $file) { $path = JPATH_ROOT . '/' . trim((string)$file, '/'); if (is_file($path)) { File::delete($path); } } } if ($obsolete->folder) { foreach ($obsolete->folder as $folder) { $path = JPATH_ROOT . '/' . trim((string)$folder, '/'); if (is_dir($path)) { Folder::delete($path); } } } } $oldLanguageFiles = Folder::files(JPATH_ADMINISTRATOR . '/language', '\.lib_allediainstaller\.', true, true); foreach ($oldLanguageFiles as $oldLanguageFile) { File::delete($oldLanguageFile); } } /** * Finds the extension row for the main extension * * @return ?Extension * @throws \Exception */ final protected function findThisExtension(): ?Extension { return $this->findExtension( $this->getXmlValue($this->manifest['type']), $this->getXmlValue($this->manifest->alledia->element), $this->getXmlValue($this->manifest['group']) ); } /** * Use this in preflight to clear out obsolete update servers when the url has changed. * * @return void * @throws \Exception */ final protected function clearUpdateServers(): void { if ($extension = $this->findThisExtension()) { $db = $this->dbo; $query = $db->getQuery(true) ->select($db->quoteName('update_site_id')) ->from($db->quoteName('#__update_sites_extensions')) ->where($db->quoteName('extension_id') . '=' . (int)$extension->get('extension_id')); if ($list = $db->setQuery($query)->loadColumn()) { $query = $db->getQuery(true) ->delete($db->quoteName('#__update_sites_extensions')) ->where($db->quoteName('extension_id') . '=' . (int)$extension->get('extension_id')); $db->setQuery($query)->execute(); array_walk($list, 'intval'); $query = $db->getQuery(true) ->delete($db->quoteName('#__update_sites')) ->where($db->quoteName('update_site_id') . ' IN (' . join(',', $list) . ')'); $db->setQuery($query)->execute(); } } } /** * Get the full element, like com_myextension, lib_extension * * @param ?string $type * @param ?string $element * @param ?string $group * * @return string */ final protected function getFullElement( ?string $type = null, ?string $element = null, ?string $group = null ): string { $prefixes = [ 'component' => 'com', 'plugin' => 'plg', 'template' => 'tpl', 'library' => 'lib', 'cli' => 'cli', 'module' => 'mod', 'file' => 'file', ]; $type = $type ?: $this->type; $element = $element ?: (string)$this->manifest->alledia->element; $group = $group ?: $this->group; $fullElement = $prefixes[$type] . '_'; if ($type === 'plugin') { $fullElement .= $group . '_'; } return $fullElement . $element; } /** * @return Licensed */ final protected function getLicense(): Licensed { if ($this->license === null) { $this->license = new Licensed( (string)$this->manifest->alledia->namespace, $this->type, $this->group ); } return $this->license; } /** * @param string $manifestPath * * @return Registry */ final protected function getInfoFromManifest(string $manifestPath): Registry { $info = new Registry(); if (is_file($manifestPath)) { $xml = simplexml_load_file($manifestPath); $attributes = (array)$xml->attributes(); $attributes = $attributes['@attributes']; foreach ($attributes as $attribute => $value) { $info->set($attribute, $value); } foreach ($xml->children() as $e) { if (!$e->children()) { $info->set($e->getName(), (string)$e); } } } else { $relativePath = str_replace(JPATH_SITE . '/', '', $manifestPath); $this->sendMessage( Text::sprintf('LIB_SHACKINSTALLER_MANIFEST_NOT_FOUND', $relativePath), 'error' ); } return $info; } /** * @param string $type * @param string $element * @param ?string $group * * @return string */ final protected function getExtensionPath(string $type, string $element, ?string $group = ''): string { $folders = [ 'component' => 'administrator/components/', 'plugin' => 'plugins/', 'template' => 'templates/', 'library' => 'libraries/', 'cli' => 'cli/', 'module' => 'modules/', 'file' => 'administrator/manifests/files/', ]; $basePath = JPATH_SITE . '/' . $folders[$type]; switch ($type) { case 'plugin': $basePath .= $group . '/'; break; case 'module': if (!preg_match('/^mod_/', $element)) { $basePath .= 'mod_'; } break; case 'component': if (!preg_match('/^com_/', $element)) { $basePath .= 'com_'; } break; case 'template': if (preg_match('/^tpl_/', $element)) { $element = str_replace('tpl_', '', $element); } break; } if ($type !== 'file') { $basePath .= $element; } return $basePath; } /** * @param string $type * @param string $element * @param ?string $group * * @return int */ final protected function getExtensionId(string $type, string $element, ?string $group = ''): int { $db = $this->dbo; $query = $db->getQuery(true) ->select('extension_id') ->from('#__extensions') ->where([ $db->quoteName('element') . ' = ' . $db->quote($element), $db->quoteName('folder') . ' = ' . $db->quote($group), $db->quoteName('type') . ' = ' . $db->quote($type), ]); $db->setQuery($query); return (int)$db->loadResult(); } /** * Get the path for the manifest file * * @return string The path */ final protected function getManifestPath($type, $element, $group = ''): string { switch ($type) { case 'library': case 'file': $folders = [ 'library' => 'libraries', 'file' => 'files', ]; $manifestPath = JPATH_SITE . '/administrator/manifests/' . $folders[$type] . '/' . $element . '.xml'; $installer = $this->getNewInstaller(); if (!file_exists($manifestPath) || !$installer->isManifest($manifestPath)) { $manifestPath = false; } break; default: $basePath = $this->getExtensionPath($type, $element, $group); $installer = $this->getNewInstaller(); $installer->setPath('source', $basePath); $installer->getManifest(); $manifestPath = $installer->getPath('manifest'); break; } return $manifestPath; } /** * Check if it needs to publish the extension * * @return void * @throws \Exception */ final protected function publishThisPlugin(): void { $attributes = $this->manifest->alledia->element->attributes(); $publish = (string)$attributes['publish']; if ($publish === 'true' || $publish === '1') { $extension = $this->findThisExtension(); $extension->publish(); } } /** * Check if it needs to reorder the extension * * @return void * @throws \Exception */ final protected function reorderThisPlugin(): void { $attributes = $this->manifest->alledia->element->attributes(); $ordering = (string)$attributes['ordering']; if ($ordering !== '') { $extension = $this->findThisExtension(); $this->setPluginOrder($extension, $ordering); } } /** * Stores feedback data for related extensions to display after install * * @param string $key * @param string $property * @param string $value * * @return void */ final protected function storeFeedbackForRelatedExtension(string $key, string $property, string $value): void { $this->sendDebugMessage(sprintf( '%s<br>**** %s-%s-%s<br><br>', __METHOD__, $key, $property, $value )); if (empty($this->relatedExtensionFeedback[$key])) { $this->relatedExtensionFeedback[$key] = []; } $this->relatedExtensionFeedback[$key][$property] = $value; } /** * This method add a mark to the extensions, allowing to detect our extensions * on the extensions table. * * @return void * @throws \Exception */ final protected function addAllediaAuthorshipToExtension(): void { if ($extension = $this->findThisExtension()) { $db = $this->dbo; // Update the extension $customData = json_decode($extension->get('custom_data')) ?: (object)[]; $customData->author = 'Joomlashack'; $query = $db->getQuery(true) ->update($db->quoteName('#__extensions')) ->set($db->quoteName('custom_data') . '=' . $db->quote(json_encode($customData))) ->where($db->quoteName('extension_id') . '=' . (int)$extension->get('extension_id')); $db->setQuery($query)->execute(); // Update the Alledia framework // @TODO: remove this after libraries be able to have a custom install script $query = $db->getQuery(true) ->update($db->quoteName('#__extensions')) ->set($db->quoteName('custom_data') . '=' . $db->quote('{"author":"Joomlashack"}')) ->where([ $db->quoteName('type') . '=' . $db->quote('library'), $db->quoteName('element') . '=' . $db->quote('allediaframework'), ]); $db->setQuery($query)->execute(); } } /** * Add styles to the output. Used because when the postFlight * method is called, we can't add stylesheets to the head. * * @param mixed $stylesheets * * @return void */ final protected function addStyle($stylesheets): void { if (is_string($stylesheets)) { $stylesheets = [$stylesheets]; } foreach ($stylesheets as $path) { if (file_exists($path)) { $style = file_get_contents($path); echo '<style>' . $style . '</style>'; } } } /** * On new component install, this will check and fix any menus * that may have been created in a previous installation. * * @return void * @throws \Exception */ final protected function fixMenus(): void { if ($this->type == 'component') { $db = $this->dbo; if ($extension = $this->findThisExtension()) { $id = $extension->get('extension_id'); $option = $extension->get('name'); $query = $db->getQuery(true) ->update('#__menu') ->set('component_id = ' . $db->quote($id)) ->where([ 'type = ' . $db->quote('component'), 'link LIKE ' . $db->quote("%option={$option}%"), ]); $db->setQuery($query)->execute(); // Check hidden admin menu option // @TODO: Remove after Joomla! incorporates this natively $menuElement = $this->manifest->administration->menu; if (in_array((string)$menuElement['hidden'], ['true', 'hidden'])) { $menu = Table::getInstance('Menu'); $menu->load(['component_id' => $id, 'client_id' => 1]); if ($menu->id) { $menu->delete(); } } } } } /** * Parses a conditional string, returning a Boolean value (default: false). * For now it only supports an extension name and * as version. * * @param string $expression * * @return bool * @throws \Exception */ final protected function parseConditionalExpression(string $expression): bool { $expression = strtolower($expression); $terms = explode('=', $expression); $firstTerm = array_shift($terms); if (count($terms) == 0) { return $firstTerm == 'true' || $firstTerm == '1'; } elseif (preg_match('/^(com_|plg_|mod_|lib_|tpl_|cli_)/', $firstTerm)) { // The first term is the name of an extension $info = $this->getExtensionInfoFromElement($firstTerm); $extension = $this->findExtension($info['type'], $firstTerm, $info['group']); // @TODO: compare the version, if specified, or different than * // @TODO: Check if the extension is enabled, not just installed if (empty($extension) == false) { return true; } } return false; } /** * Get extension's info from element string, or extension name * * @param string $element The extension name, as element * * @return string[] An associative array with information about the extension */ final protected function getExtensionInfoFromElement(string $element): array { $result = array_fill_keys( ['type', 'name', 'group', 'prefix', 'namespace'], null ); $types = [ 'com' => 'component', 'plg' => 'plugin', 'mod' => 'module', 'lib' => 'library', 'tpl' => 'template', 'cli' => 'cli', ]; $element = explode('_', $element, 3); $prefix = $result['prefix'] = array_shift($element); $name = array_pop($element); $group = array_pop($element); if (array_key_exists($prefix, $types)) { $result = array_merge( $result, [ 'type' => $types[$prefix], 'group' => $group, 'name' => $name, ] ); } $result['namespace'] = preg_replace_callback( '/^(os[a-z])(.*)/i', function ($matches) { return strtoupper($matches[1]) . $matches[2]; }, $name ?? '' ); return $result; } /** * Check if the actual version is at least the minimum target version. * * @param string $actualVersion * @param string $targetVersion * @param ?string $compare * * @return bool True, if the target version is greater than or equal to actual version */ final protected function validateTargetVersion( string $actualVersion, string $targetVersion, ?string $compare = null ): bool { if ($targetVersion === '.*') { // Any version is valid return true; } $targetVersion = str_replace('*', '0', $targetVersion); return version_compare($actualVersion, $targetVersion, $compare ?: 'ge'); } /** * @param string $targetVersion * @param ?string $compare * * @return bool */ final protected function validatePreviousVersion(string $targetVersion, ?string $compare = null): bool { if ($this->previousManifest) { $lastVersion = (string)$this->previousManifest->version; return $this->validateTargetVersion($lastVersion, $targetVersion, $compare); } return true; } /** * Get the extension name. If no custom name is set, uses the namespace * * @return string */ final protected function getName(): string { return (string)($this->manifest->alledia->name ?? $this->manifest->alledia->namespace); } /** * @param ?bool $force Force to get a fresh list of tables * * @return string[] List of tables */ final protected function getTables(?bool $force = false): array { if ($force || $this->tables === null) { $this->tables = $this->dbo->setQuery('SHOW TABLES')->loadColumn(); } return $this->tables; } /** * @param string $table * * @return bool */ final protected function findTable(string $table): bool { return in_array($this->dbo->replacePrefix($table), $this->getTables()); } /** * @param string[] $columnSpecs * * @return void * @TODO: allow use of specification array */ final protected function addColumns(array $columnSpecs): void { $db = $this->dbo; foreach ($columnSpecs as $columnId => $specification) { if (strpos($columnId, '.') !== false && empty($this->findColumn($columnId))) { [$table, $columnName] = explode('.', $columnId); $db->setQuery( sprintf( 'ALTER TABLE %s ADD COLUMN %s %s', $db->quoteName($table), $db->quoteName($columnName), $specification ) ) ->execute(); } } } /** * @param string[] $columnIds * * @return void */ final protected function dropColumns(array $columnIds): void { $db = $this->dbo; foreach ($columnIds as $columnId) { if (strpos($columnId, '.') !== false) { [$table, $column] = explode('.', $columnId); $db->setQuery( sprintf( 'ALTER TABLE %s DROP COLUMN %s', $db->quoteName($table), $column ) ) ->execute(); } } } /** * @param string $columnId * * @return ?object */ final protected function findColumn(string $columnId): ?object { if (strpos($columnId, '.') !== false) { $db = $this->dbo; [$table, $field] = explode('.', $columnId, 2); if (isset($this->tableColumns[$table]) == false) { $this->tableColumns[$table] = $db->setQuery('SHOW COLUMNS FROM ' . $db->quoteName($table)) ->loadObjectList(); } foreach ($this->tableColumns[$table] as $column) { if ($column->Field == $field) { return $column; } } } return null; } /** * @param array $indexes * * @return void */ final protected function addIndexes(array $indexes): void { $db = $this->dbo; foreach ($indexes as $indexId => $ordering) { if (strpos($indexId, '.') !== false) { $index = explode('.', $indexId); $indexTable = array_shift($index) ?: ''; $indexName = array_shift($index) ?: ''; $indexType = array_shift($index) ?: ''; if ($this->findIndex($indexTable . '.' . $indexName) == false) { $db->setQuery( sprintf( 'ALTER TABLE %s ADD %s INDEX %s(%s)', $db->quoteName($indexTable), $indexType, $db->quoteName($indexName), join(',', $ordering) ) ) ->execute(); } } } } /** * @param string[] $indexIds * * @return void */ final protected function dropIndexes(array $indexIds): void { $db = $this->dbo; foreach ($indexIds as $indexId) { if (strpos($indexId, '.') !== false) { if ($this->findIndex($indexId)) { [$table, $indexName] = explode('.', $indexId); $db->setQuery( sprintf( 'ALTER TABLE %s DROP INDEX %s', $db->quoteName($table), $db->quoteName($indexName) ) ) ->execute(); } } } } /** * @param string $indexId * * @return object[] */ final protected function findIndex(string $indexId): array { if (strpos($indexId, '.') !== false) { $db = $this->dbo; [$table, $indexName] = explode('.', $indexId); if (isset($this->tableIndexes[$table]) == false) { $this->tableIndexes[$table] = $db->setQuery('SHOW INDEX FROM ' . $db->quoteName($table)) ->loadObjectList(); } $indexes = []; foreach ($this->tableIndexes[$table] as $index) { if ($index->Key_name == $indexName) { $indexes[] = $index; } } return $indexes; } return []; } /** * @param string[] $constraintIds * * @return void */ final protected function dropConstraints(array $constraintIds): void { $db = $this->dbo; foreach ($constraintIds as $constraintId) { if (strpos($constraintId, '.') !== false && $this->findConstraint($constraintId)) { [$table, $constraintName] = explode('.', $constraintId); $db->setQuery( sprintf( 'ALTER TABLE %s DROP FOREIGN KEY %s', $db->quoteName($table), $db->quoteName($constraintName) ) ) ->execute(); } } } /** * @param string $constraintId * * @return object[] */ final protected function findConstraint(string $constraintId): array { if (strpos($constraintId, '.') !== false) { $db = $this->dbo; [$table, $constraint] = explode('.', $constraintId); if (isset($this->tableConstraints[$table]) == false) { $query = $db->getQuery(true) ->select('*') ->from('information_schema.KEY_COLUMN_USAGE') ->where('TABLE_NAME = ' . $db->quote($db->replacePrefix($table))); $this->tableConstraints[$table] = $db->setQuery($query)->loadObjectList(); } $items = []; foreach ($this->tableConstraints[$table] as $item) { if ($item->CONSTRAINT_NAME == $constraint) { $items[] = $item; } } return $items ?: []; } return []; } /** * @return ?string * @throws \Exception */ final protected function getSchemaVersion(): ?string { if ($extension = $this->findThisExtension()) { $query = $this->dbo->getQuery(true) ->select('version_id') ->from('#__schemas') ->where('extension_id = ' . $extension->get('extension_id')); return $this->dbo->setQuery($query)->loadResult(); } return null; } /** * @param string|string[] $queries * * @return bool|Throwable */ final protected function executeQuery($schemaVersion, $queries) { $this->sendDebugMessage(__METHOD__); $this->sendDebugMessage($this->schemaVersion . ' / ' . $schemaVersion); if ($this->schemaVersion && version_compare($this->schemaVersion, $schemaVersion, 'lt')) { $this->sendDebugMessage(sprintf('Running v%s Schema Updates', $schemaVersion)); $db = $this->dbo; try { foreach ((array)$queries as $query) { $this->sendDebugMessage($query); if ($db->setQuery($query)->execute() == false) { return new \Exception('Query Error: ' . $query); } } } catch (Throwable $error) { return $error; } } return true; } /** * Joomla 4 does a database check that has lots of problems with standard sql syntax * causing it to declare the database tables as not up to date and in some cases * generates various sql errors. This can optionally be called during Post Install to * clear out all update files and still maintain the latest schema version correctly. * * @param string $basePath * * @return void */ final protected function clearDBUpdateFiles(string $basePath): void { $this->sendDebugMessage(__METHOD__); $updatePath = $basePath . '/sql/updates'; if (is_dir($updatePath) && $files = Folder::files($updatePath, '\.sql$', true, true)) { $this->sendDebugMessage('Removing:<pre>' . print_r($files, 1) . '</pre>'); $final = reset($files); foreach ($files as $file) { $version = basename($file, '.sql'); $lastVersion = basename($final, '.sql'); if (version_compare($version, $lastVersion, 'gt')) { $final = $file; } File::delete($file); } if ($final) { File::write($final, ''); $this->sendDebugMessage('Wrote blank: ' . $final); } } } /** * @param SimpleXMLElement|string $element * @param ?string $type * @param mixed $default * * @return ?bool|string */ final protected function getXmlValue($element, ?string $type = 'string', $default = null) { $value = $element ? (string)$element : $default; switch ($type) { case 'bool': case 'boolean': $value = $element ? $value == 'true' || $value == '1' : (bool)$default; break; case 'string': default: if ($value) { $value = trim($value); } break; } return $value; } /** * @param string $text * @param string $type * * @return void */ final protected function sendMessage(string $text, string $type = 'message'): void { if ($this->outputAllowed) { try { $this->app = $this->app ?: Factory::getApplication(); $this->app->enqueueMessage($text, $type); } catch (Throwable $error) { // Give up trying to send a message normally } } } /** * @param Throwable $error * @param bool $cancel * * @return void */ final protected function sendErrorMessage(Throwable $error, bool $cancel = true): void { if ($cancel) { $this->cancelInstallation = true; } if ($this->outputAllowed) { $trace = $error->getTrace(); $trace = array_shift($trace); if (empty($trace['class'])) { $caller = basename($trace['file']); } else { $className = explode('\\', $trace['class']); $caller = array_pop($className); } $line = $trace['line']; $function = $trace['function'] ?? null; $file = $trace['file']; if ($function) { $message = sprintf('%s: %s<br>%s::%s() - %s', $line, $file, $caller, $function, $error->getMessage()); } else { $message = sprintf('%s:%s (%s) - %s', $line, $caller, $file, $error->getMessage()); } $this->sendMessage($message, 'error'); } } /** * @param string $text * * @return void */ final protected function sendDebugMessage(string $text): void { if ($this->debug) { $type = Version::MAJOR_VERSION == 3 ? 'Debug-' . get_class($this) : CMSApplicationInterface::MSG_DEBUG; $this->sendMessage($text, $type); } } /** * @param string $type * * @return void */ final protected function displayWelcome(string $type): void { if ($this->outputAllowed == false) { return; } $this->sendDebugMessage( sprintf( '%s<br>Parent: %s<br>Current: %s', __METHOD__, $this->installer->getPath('parent'), $this->installer->getPath('source') ) ); $license = $this->getLicense(); $name = $this->getName() . ($license->isPro() ? ' Pro' : ''); // Get the footer content $this->footer = ''; // Check if we have a dedicated config.xml file $configPath = $license->getExtensionPath() . '/config.xml'; if (is_file($configPath)) { $config = $license->getConfig(); if (empty($config) == false) { $footerElement = $config->xpath('//field[@type="customfooter"]'); } } else { $footerElement = $this->manifest->xpath('//field[@type="customfooter"]'); } if (empty($footerElement) == false) { if (class_exists('\\JFormFieldCustomFooter') == false) { // Custom footer field is not (and should not be) automatically loaded $customFooterPath = $license->getExtensionPath() . '/form/fields/customfooter.php'; if (is_file($customFooterPath)) { include_once $customFooterPath; } } if (class_exists('\\JFormFieldCustomFooter')) { $field = new JFormFieldCustomFooter(); $field->fromInstaller = true; $this->footer = $field->getInputUsingCustomElement($footerElement[0]); unset($field, $footerElement); } } else { $this->sendDebugMessage('No Footer element was found'); } // Show additional installation messages $extensionPath = $this->getExtensionPath( $this->type, (string)$this->manifest->alledia->element, $this->group ); // If Pro extension, includes the license form view if ($license->isPro()) { // Get the OSMyLicensesManager extension to handle the license key if ($licensesManagerExtension = new Licensed('osmylicensesmanager', 'plugin', 'system')) { if (isset($licensesManagerExtension->params)) { $this->licenseKey = $licensesManagerExtension->params->get('license-keys', ''); } else { $this->licenseKey = ''; } $this->isLicensesManagerInstalled = true; } $this->sendDebugMessage('License Manager plugin: ' . (int)$this->isLicensesManagerInstalled); } // Welcome message if (in_array($type, [static::TYPE_INSTALL, static::TYPE_DISCOVER_INSTALL])) { $string = 'LIB_SHACKINSTALLER_THANKS_INSTALL'; } else { $string = 'LIB_SHACKINSTALLER_THANKS_UPDATE'; } // Variables for the included template $this->welcomeMessage = Text::sprintf($string, $name); $this->mediaURL = Uri::root() . 'media/' . $license->getFullElement(); $this->addStyle($this->mediaFolder . '/css/installer.css'); /* * Include the template * Try to find the template in an alternative folder, since some extensions * which uses FOF will display the "Installers" view on admin, errouniously. * FOF look for views automatically reading the views folder. So on that * case we move the installer view to another folder. */ $path = $extensionPath . '/views/installer/tmpl/default.php'; if (is_file($path) == false) { $path = $extensionPath . '/alledia_views/installer/tmpl/default.php'; } $this->sendDebugMessage(sprintf('Welcome View (%s): %s', (int)is_file($path), $path)); if (is_file($path)) { include $path; } } /** * WARNIMG! This is duplicated from the Joomlashack Framework * * @param string $name * @param string $prefix * @param string $component * @param ?string $appName * @param ?array $options * * @return mixed * @throws \Exception */ protected function getJoomlaModel( string $name, string $prefix, string $component, ?string $appName = null, ?array $options = [] ) { $defaultApp = 'Site'; $appNames = [$defaultApp, 'Administrator']; $appName = ucfirst($appName ?: $defaultApp); $appName = in_array($appName, $appNames) ? $appName : $defaultApp; if (Version::MAJOR_VERSION < 4) { $basePath = $appName == 'Administrator' ? JPATH_ADMINISTRATOR : JPATH_SITE; $path = $basePath . '/components/' . $component; BaseDatabaseModel::addIncludePath($path . '/models'); Table::addIncludePath($path . '/tables'); $model = BaseDatabaseModel::getInstance($name, $prefix, $options); } else { $model = Factory::getApplication()->bootComponent($component) ->getMVCFactory()->createModel($name, $appName, $options); } return $model; } /** * Utility function to setting extension states * @param array $extensions * @param int $state * * @return array * @throws \Exception */ final protected function setExtensionState(array $extensions, int $state = 0): array { $states = []; if (in_array($state, [0, 1])) { foreach ($extensions as $extension) { $parts = explode('.', $extension); $element = array_pop($parts); $folder = null; switch (count($parts)) { case 1: $type = array_pop($parts); break; case 2: $folder = array_pop($parts); $type = array_pop($parts); break; default: // Badly structured extension identifier break 2; } if ($object = $this->findExtension($type, $element, $folder)) { $states[$extension] = (int)$object->get('enabled'); if ($states[$extension] != $state) { $this->sendDebugMessage( sprintf( '%s: %s', $extension, $state ? 'Enabled' : 'Disabled' ) ); $object->set('enabled', $state); $object->store(); } } } } return $states; } /** * If the old system plugin is installed, it requires special handling to avoid * fatal conflicts with its install script * * @return void * @throws \Exception */ final protected function clearOldSystemPlugin() { if ($this->findExtension('plugin', 'ossystem', 'system')) { if (class_exists('PlgSystemOSSystemInstallerScript') == false) { class_alias(static::class, 'PlgSystemOSSystemInstallerScript'); } } } } PK ��\��Փ � ( library/Installer/Extension/Licensed.phpnu &1i� <?php /** * @package ShackInstaller * @contact www.joomlashack.com, help@joomlashack.com * @copyright 2016-2023 Joomlashack.com. All rights reserved * @license https://www.gnu.org/licenses/gpl.html GNU/GPL * * This file is part of ShackInstaller. * * ShackInstaller is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * ShackInstaller is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ShackInstaller. If not, see <https://www.gnu.org/licenses/>. */ namespace Alledia\Installer\Extension; defined('_JEXEC') or die(); use Alledia\Installer\AutoLoader; /** * Licensed class, for extensions with Free and Pro versions */ class Licensed extends Generic { /** * License type: free or pro * * @var string */ protected $license = null; /** * The path for the pro library * * @var string */ protected $proLibraryPath = null; /** * The path for the free library * * @var string */ protected $libraryPath = null; /** * @inheritDoc */ public function __construct(string $namespace, string $type, ?string $folder = '', string $basePath = JPATH_SITE) { parent::__construct($namespace, $type, $folder, $basePath); $this->license = $this->manifest ? strtolower($this->manifest->alledia->license) : 'free'; $this->getLibraryPath(); $this->getProLibraryPath(); } /** * Check if the license is pro * * @return bool True for pro license */ public function isPro(): bool { return $this->license === 'pro'; } /** * Check if the license is free * * @return bool */ public function isFree(): bool { return !$this->isPro(); } /** * Get the path for the free library, based on the extension type * * @return string The path for pro */ public function getLibraryPath(): string { if (empty($this->libraryPath)) { $basePath = $this->getExtensionPath(); $this->libraryPath = $basePath . '/library'; } return $this->libraryPath; } /** * Get path for the pro library, based on the extension type * * @return string The path for pro */ public function getProLibraryPath(): string { if ($this->proLibraryPath === null) { $basePath = $this->getLibraryPath(); $this->proLibraryPath = $basePath . '/Pro'; } return $this->proLibraryPath; } /** * Loads the library, if existent (including the Pro Library) * * @return bool * @throws \Exception */ public function loadLibrary(): bool { $libraryPath = $this->getLibraryPath(); // If we have a library path, lets load it if (is_dir($libraryPath)) { if ($this->isPro()) { // Check if the pro library exists if (!is_dir($this->getProLibraryPath())) { throw new \Exception("Pro library not found: {$this->type}, {$this->element}"); } } AutoLoader::register('Alledia\\' . $this->namespace, $libraryPath); return true; } return false; } } PK ��\D�ѣ�+ �+ '