Пример установочного скрипта для Joomla
Last active
June 26, 2024 10:36
-
-
Save Septdir/8f16ca1dd5e71d5247fa4e3b564f53e0 to your computer and use it in GitHub Desktop.
Joomla Install Script
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; @package Joomla Install Script | |
; @version __DEPLOY_VERSION__ | |
; @author Septdir Workshop - septdir.com | |
; @copyright Copyright (c) 2018 - 2021 Septdir Workshop. All rights reserved. | |
; @license GNU/GPL license: https://www.gnu.org/copyleft/gpl.html | |
; @link https://www.septdir.com/ | |
; Note : All ini files need to be saved as UTF-8 | |
TYPE_NAME = "Joomla Install Script" | |
TYPE_NAME_DESCRIPTION = "Joomla Install Script Example" | |
TYPE_NAME_ERROR_COMPATIBLE_PHP = "This version is compatible only with PHP %s and later" | |
TYPE_NAME_ERROR_COMPATIBLE_JOOMLA = "This version is compatible only with Joomla %s and later" | |
TYPE_NAME_ERROR_COMPATIBLE_DATABASE = "This version is compatible only with MySQL %s or MariaDB %s and later" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; @package Joomla Install Script | |
; @version __DEPLOY_VERSION__ | |
; @author Septdir Workshop - septdir.com | |
; @copyright Copyright (c) 2018 - 2021 Septdir Workshop. All rights reserved. | |
; @license GNU/GPL license: https://www.gnu.org/copyleft/gpl.html | |
; @link https://www.septdir.com/ | |
; Note : All ini files need to be saved as UTF-8 | |
TYPE_NAME = "Установочный скрипт для Joomla" | |
TYPE_NAME_DESCRIPTION = "Пример установочного скрипта для Joomla" | |
TYPE_NAME_ERROR_COMPATIBLE_PHP = "Данная версия совместима только с PHP %s и более поздними версиями" | |
TYPE_NAME_ERROR_COMPATIBLE_JOOMLA = "Данная версия совместима только с Joomla %s и более поздними версиями" | |
TYPE_NAME_ERROR_COMPATIBLE_DATABASE = "Данная версия совместима только с MySQL %s или MariaDB %s и более поздними версиями" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* | |
* @package Joomla Install Script | |
* @version __DEPLOY_VERSION__ | |
* @author Septdir Workshop - septdir.com | |
* @copyright Copyright (c) 2018 - 2021 Septdir Workshop. All rights reserved. | |
* @license GNU/GPL license: https://www.gnu.org/copyleft/gpl.html | |
* @link https://www.septdir.com/ | |
*/ | |
\defined('_JEXEC') or die; | |
use Joomla\CMS\Application\AdministratorApplication; | |
use Joomla\CMS\Factory; | |
use Joomla\CMS\Installer\InstallerAdapter; | |
use Joomla\CMS\Installer\InstallerScriptInterface; | |
use Joomla\CMS\Language\Text; | |
use Joomla\CMS\Version; | |
use Joomla\Database\DatabaseDriver; | |
use Joomla\DI\Container; | |
use Joomla\DI\ServiceProviderInterface; | |
return new class () implements ServiceProviderInterface { | |
public function register(Container $container) | |
{ | |
$container->set(InstallerScriptInterface::class, | |
new class ($container->get(AdministratorApplication::class)) implements InstallerScriptInterface { | |
/** | |
* The application object | |
* | |
* @var AdministratorApplication | |
* | |
* @since __DEPLOY_VERSION__ | |
*/ | |
protected AdministratorApplication $app; | |
/** | |
* The Database object. | |
* | |
* @var DatabaseDriver | |
* | |
* @since __DEPLOY_VERSION__ | |
*/ | |
protected DatabaseDriver $db; | |
/** | |
* Minimum Joomla version required to install the extension. | |
* | |
* @var string | |
* | |
* @since __DEPLOY_VERSION__ | |
*/ | |
protected string $minimumJoomla = ''; | |
/** | |
* Minimum PHP version required to install the extension. | |
* | |
* @var string | |
* | |
* @since __DEPLOY_VERSION__ | |
*/ | |
protected string $minimumPhp = ''; | |
/** | |
* Minimum MySQL version required to install the extension. | |
* | |
* @var string | |
* | |
* @since __DEPLOY_VERSION__ | |
*/ | |
protected string $minimumMySQL = ''; | |
/** | |
* Minimum MariaDb version required to install the extension. | |
* | |
* @var string | |
* | |
* @since __DEPLOY_VERSION__ | |
*/ | |
protected string $minimumMariaDb = ''; | |
/** | |
* Language constant for errors. | |
* | |
* @var string | |
* | |
* @since __DEPLOY_VERSION__ | |
*/ | |
protected string $constant = ""; | |
/** | |
* Extension params for check. | |
* | |
* @var array | |
* | |
* @since __DEPLOY_VERSION__ | |
*/ | |
protected array $extensionParams = []; | |
/** | |
* Update methods. | |
* | |
* @var array | |
* | |
* @since __DEPLOY_VERSION__ | |
*/ | |
protected array $updateMethods = []; | |
/** | |
* Constructor. | |
* | |
* @param AdministratorApplication $app The application object. | |
* | |
* @since __DEPLOY_VERSION__ | |
*/ | |
public function __construct(AdministratorApplication $app) | |
{ | |
$this->app = $app; | |
$this->db = Factory::getContainer()->get('DatabaseDriver'); | |
} | |
/** | |
* Function called after the extension is installed. | |
* | |
* @param InstallerAdapter $adapter The adapter calling this method | |
* | |
* @return boolean True on success | |
* | |
* @since __DEPLOY_VERSION__ | |
*/ | |
public function install(InstallerAdapter $adapter): bool | |
{ | |
$this->enablePlugin($adapter); | |
return true; | |
} | |
/** | |
* Function called after the extension is updated. | |
* | |
* @param InstallerAdapter $adapter The adapter calling this method | |
* | |
* @return boolean True on success | |
* | |
* @since __DEPLOY_VERSION__ | |
*/ | |
public function update(InstallerAdapter $adapter): bool | |
{ | |
// Refresh media version | |
(new Version())->refreshMediaVersion(); | |
return true; | |
} | |
/** | |
* Function called after the extension is uninstalled. | |
* | |
* @param InstallerAdapter $adapter The adapter calling this method | |
* | |
* @return boolean True on success | |
* | |
* @since __DEPLOY_VERSION__ | |
*/ | |
public function uninstall(InstallerAdapter $adapter): bool | |
{ | |
return true; | |
} | |
/** | |
* Function called before extension installation/update/removal procedure commences. | |
* | |
* @param string $type The type of change (install or discover_install, update, uninstall) | |
* @param InstallerAdapter $adapter The adapter calling this method | |
* | |
* @return boolean True on success | |
* | |
* @since __DEPLOY_VERSION__ | |
*/ | |
public function preflight(string $type, InstallerAdapter $adapter): bool | |
{ | |
// Check compatible | |
if (!$this->checkCompatible()) | |
{ | |
return false; | |
} | |
return true; | |
} | |
/** | |
* Function called after extension installation/update/removal procedure commences. | |
* | |
* @param string $type The type of change (install or discover_install, update, uninstall) | |
* @param InstallerAdapter $adapter The adapter calling this method | |
* | |
* @return boolean True on success | |
* | |
* @since __DEPLOY_VERSION__ | |
*/ | |
public function postflight(string $type, InstallerAdapter $adapter): bool | |
{ | |
if ($type !== 'uninstall') | |
{ | |
// Parse layouts | |
$this->parseLayouts($installer->getManifest()->layouts, $installer); | |
// Check databases | |
$this->checkTables($adapter); | |
// Check root record | |
$this->checkRootRecord(''); | |
// Check extension params | |
$this->checkExtensionParams($adapter); | |
// Run updates script | |
if ($type === 'update') | |
{ | |
foreach ($this->updateMethods as $method) | |
{ | |
if (method_exists($this, $method)) | |
{ | |
$this->$method($adapter); | |
} | |
} | |
} | |
} | |
return true; | |
} | |
/** | |
* Enable plugin after installation. | |
* | |
* @param InstallerAdapter $adapter Parent object calling object. | |
* | |
* @since __DEPLOY_VERSION__ | |
*/ | |
protected function enablePlugin(InstallerAdapter $adapter) | |
{ | |
// Prepare plugin object | |
$plugin = new \stdClass(); | |
$plugin->type = 'plugin'; | |
$plugin->element = $adapter->getElement(); | |
$plugin->folder = (string) $adapter->getParent()->manifest->attributes()['group']; | |
$plugin->enabled = 1; | |
// Update record | |
$this->db->updateObject('#__extensions', $plugin, ['type', 'element', 'folder']); | |
} | |
/** | |
* Method to check compatible. | |
* | |
* @throws \Exception | |
* | |
* @return bool True on success, False on failure. | |
* | |
* @since __DEPLOY_VERSION__ | |
*/ | |
protected function checkCompatible(): bool | |
{ | |
$app = Factory::getApplication(); | |
// Check joomla version | |
if (!(new Version())->isCompatible($this->minimumJoomla)) | |
{ | |
$app->enqueueMessage(Text::sprintf($this->constant . '_ERROR_COMPATIBLE_JOOMLA', $this->minimumJoomla), | |
'error'); | |
return false; | |
} | |
// Check PHP | |
if (!(version_compare(PHP_VERSION, $this->minimumPhp) >= 0)) | |
{ | |
$app->enqueueMessage(Text::sprintf($this->constant . '_ERROR_COMPATIBLE_PHP', $this->minimumPhp), | |
'error'); | |
return false; | |
} | |
// Check database version | |
$db = $this->db; | |
$serverType = $db->getServerType(); | |
$serverVersion = $db->getVersion(); | |
if ($serverType == 'mysql' && stripos($serverVersion, 'mariadb') !== false) | |
{ | |
$serverVersion = preg_replace('/^5\.5\.5-/', '', $serverVersion); | |
if (!(version_compare($serverVersion, $this->minimumMariaDb) >= 0)) | |
{ | |
$app->enqueueMessage(Text::sprintf($this->constant . '_ERROR_COMPATIBLE_DATABASE', | |
$this->minimumMySQL, $this->minimumMariaDb), 'error'); | |
return false; | |
} | |
} | |
elseif ($serverType == 'mysql' && !(version_compare($serverVersion, $this->minimumMySQL) >= 0)) | |
{ | |
$app->enqueueMessage(Text::sprintf($this->constant . '_ERROR_COMPATIBLE_DATABASE', | |
$this->minimumMySQL, $this->minimumMariaDb), 'error'); | |
return false; | |
} | |
return true; | |
} | |
/** | |
* Method to parse through a layouts element of the installation manifest and take appropriate action. | |
* | |
* @param SimpleXMLElement|null $element The XML node to process. | |
* @param Installer|null $installer Installer calling object. | |
* | |
* @return bool True on success. | |
* | |
* @since 1.0.0 | |
*/ | |
public function parseLayouts(SimpleXMLElement $element = null, Installer $installer = null): bool | |
{ | |
if (!$element || !count($element->children())) | |
{ | |
return false; | |
} | |
// Get destination | |
$folder = ((string) $element->attributes()->destination) ? '/' . $element->attributes()->destination : null; | |
$destination = Path::clean(JPATH_ROOT . '/layouts' . $folder); | |
// Get source | |
$folder = (string) $element->attributes()->folder; | |
$source = ($folder && file_exists($installer->getPath('source') . '/' . $folder)) | |
? $installer->getPath('source') . '/' . $folder : $installer->getPath('source'); | |
// Prepare files | |
$copyFiles = []; | |
foreach ($element->children() as $file) | |
{ | |
$path['src'] = Path::clean($source . '/' . $file); | |
$path['dest'] = Path::clean($destination . '/' . $file); | |
// Is this path a file or folder? | |
$path['type'] = $file->getName() === 'folder' ? 'folder' : 'file'; | |
if (basename($path['dest']) !== $path['dest']) | |
{ | |
$newdir = dirname($path['dest']); | |
if (!Folder::create($newdir)) | |
{ | |
Log::add(Text::sprintf('JLIB_INSTALLER_ERROR_CREATE_DIRECTORY', $newdir), Log::WARNING, 'jerror'); | |
return false; | |
} | |
} | |
$copyFiles[] = $path; | |
} | |
return $installer->copyFiles($copyFiles, true); | |
} | |
/** | |
* Method to create database tables in not exist. | |
* | |
* @param InstallerAdapter $adapter Parent object calling object. | |
* | |
* @since __DEPLOY_VERSION__ | |
*/ | |
protected function checkTables(InstallerAdapter $adapter) | |
{ | |
if ($sql = file_get_contents($adapter->getParent()->getPath('extension_administrator') | |
. '/sql/install.mysql.utf8.sql')) | |
{ | |
$db = $this->db; | |
foreach ($db->splitSql($sql) as $query) | |
{ | |
$db->setQuery($db->convertUtf8mb4QueryToUtf8($query)); | |
try | |
{ | |
$db->execute(); | |
} | |
catch (JDataBaseExceptionExecuting $e) | |
{ | |
Log::add(Text::sprintf('JLIB_INSTALLER_ERROR_SQL_ERROR', $e->getMessage()), Log::WARNING, 'jerror'); | |
} | |
} | |
} | |
} | |
/** | |
* Method to create root record if don't exist. | |
* | |
* @param string|null $table Table name. | |
* | |
* @since __DEPLOY_VERSION__ | |
*/ | |
protected function checkRootRecord(string $table = null) | |
{ | |
$db = $this->db; | |
// Get base categories | |
$query = $db->getQuery(true) | |
->select('id') | |
->from($table) | |
->where('id = 1'); | |
$db->setQuery($query); | |
// Add root in not found | |
if (empty($db->loadResult())) | |
{ | |
$root = new \stdClass(); | |
$root->id = 1; | |
$root->parent_id = 0; | |
$root->lft = 0; | |
$root->rgt = 1; | |
$root->level = 0; | |
$root->path = ''; | |
$root->alias = 'root'; | |
$root->type = 'category'; | |
$root->state = 1; | |
$db->insertObject($table, $root); | |
} | |
} | |
/** | |
* Method to check extension params and set if needed. | |
* | |
* @param InstallerAdapter $adapter Parent object calling object. | |
* | |
* @since __DEPLOY_VERSION__ | |
*/ | |
protected function checkExtensionParams(InstallerAdapter $adapter) | |
{ | |
if (!empty($this->extensionParams)) | |
{ | |
$element = $adapter->getElement(); | |
$folder = (string) $adapter->getParent()->manifest->attributes()['group']; | |
// Get extension | |
$db = $this->db; | |
$query = $db->getQuery(true) | |
->select(['extension_id', 'params']) | |
->from($db->quoteName('#__extensions')) | |
->where($db->quoteName('element') . ' = :element') | |
->bind(':element', $element); | |
if (!empty($folder)) | |
{ | |
$query->where($db->quoteName('folder') . ' = :folder') | |
->bind(':folder', $folder); | |
} | |
if ($extension = $db->setQuery($query)->loadObject()) | |
{ | |
$extension->params = new Registry($extension->params); | |
// Check params | |
$needUpdate = false; | |
foreach ($this->extensionParams as $path => $value) | |
{ | |
if (!$extension->params->exists($path)) | |
{ | |
$needUpdate = true; | |
$extension->params->set($path, $value); | |
} | |
} | |
// Update | |
if ($needUpdate) | |
{ | |
$extension->params = (string) $extension->params; | |
$db->updateObject('#__extensions', $extension, 'extension_id'); | |
} | |
} | |
} | |
} | |
/** | |
* Method to parse through a layouts element of the installation manifest and remove the files that were installed. | |
* | |
* @param SimpleXMLElement|null $element The XML node to process. | |
* | |
* @return bool True on success. | |
* | |
* @since __DEPLOY_VERSION__ | |
*/ | |
protected function removeLayouts(SimpleXMLElement $element = null): bool | |
{ | |
if (!$element || !count($element->children())) | |
{ | |
return false; | |
} | |
// Get the array of file nodes to process | |
$files = $element->children(); | |
// Get source | |
$folder = ((string) $element->attributes()->destination) ? '/' . $element->attributes()->destination : null; | |
$source = Path::clean(JPATH_ROOT . '/layouts' . $folder); | |
// Process each file in the $files array (children of $tagName). | |
foreach ($files as $file) | |
{ | |
$path = Path::clean($source . '/' . $file); | |
// Actually delete the files/folders | |
if (is_dir($path)) | |
{ | |
$val = Folder::delete($path); | |
} | |
else | |
{ | |
$val = File::delete($path); | |
} | |
if ($val === false) | |
{ | |
Log::add('Failed to delete ' . $path, Log::WARNING, 'jerror'); | |
return false; | |
} | |
} | |
if (!empty($folder)) | |
{ | |
Folder::delete($source); | |
} | |
return true; | |
} | |
}); | |
} | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<extension version="3.9" type="type" method="upgrade"> | |
<name>TYPE_NAME</name> | |
<description>TYPE_NAME_DESCRIPTION</description> | |
<scriptfile>script.php</scriptfile> | |
<languages folder="language"> | |
<language tag="en-GB">en-GB/en-GB.type_name.sys.ini</language> | |
<language tag="ru-RU">ru-RU/ru-RU.type_name.sys.ini</language> | |
</languages> | |
<layouts destination="type/name" folder="layouts"> | |
<folder>folder_name</folder> | |
<filename>filename.php</filename> | |
</layouts> | |
<cli destination="" folder="cli"> | |
<folder>folder_name</folder> | |
<filename>filename.php</filename> | |
</cli> | |
</extension> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment