Пример установочного скрипта для Joomla
-
-
Save cave2006/85dc95e7a28d32417c1787aa01620ab4 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\Factory; | |
use Joomla\CMS\Filesystem\File; | |
use Joomla\CMS\Filesystem\Folder; | |
use Joomla\CMS\Filesystem\Path; | |
use Joomla\CMS\Installer\Adapter\PackageAdapter; | |
use Joomla\CMS\Installer\Installer; | |
use Joomla\CMS\Installer\InstallerAdapter; | |
use Joomla\CMS\Language\Text; | |
use Joomla\CMS\Log\Log; | |
use Joomla\CMS\Version; | |
use Joomla\Registry\Registry; | |
class type_nameInstallerScript | |
{ | |
/** | |
* Minimum PHP version required to install the extension. | |
* | |
* @var string | |
* | |
* @since __DEPLOY_VERSION__ | |
*/ | |
protected $minimumPhp = '7.0'; | |
/** | |
* Minimum Joomla version required to install the extension. | |
* | |
* @var string | |
* | |
* @since __DEPLOY_VERSION__ | |
*/ | |
protected $minimumJoomla = '3.9.0'; | |
/** | |
* Minimum MySQL version required to install the extension. | |
* | |
* @var string | |
* | |
* @since __DEPLOY_VERSION__ | |
*/ | |
protected $minimumMySQL = '8.0'; | |
/** | |
* Minimum MariaDb version required to install the extension. | |
* | |
* @var string | |
* | |
* @since __DEPLOY_VERSION__ | |
*/ | |
protected $minimumMariaDb = '10.4.1'; | |
/** | |
* Extension params for check. | |
* | |
* @var array | |
* | |
* @since __DEPLOY_VERSION__ | |
*/ | |
protected $extensionParams = array( | |
'param' => 'value', | |
); | |
/** | |
* Runs right before any installation action. | |
* | |
* @param string $type Type of PostFlight action. | |
* @param InstallerAdapter|PackageAdapter $parent Parent object calling object. | |
* | |
* @throws Exception | |
* | |
* @return boolean True on success, false on failure. | |
* | |
* @since __DEPLOY_VERSION__ | |
*/ | |
function preflight($type, $parent) | |
{ | |
// Check compatible | |
if (!$this->checkCompatible('TYPE_NAME_')) return false; | |
return true; | |
} | |
/** | |
* Method to check compatible. | |
* | |
* @param string $prefix Language constants prefix. | |
* | |
* @throws Exception | |
* | |
* @return boolean True on success, false on failure. | |
* | |
* @since __DEPLOY_VERSION__ | |
*/ | |
protected function checkCompatible($prefix = null) | |
{ | |
// Check old Joomla | |
if (!class_exists('Joomla\CMS\Version')) | |
{ | |
JFactory::getApplication()->enqueueMessage(JText::sprintf($prefix . 'ERROR_COMPATIBLE_JOOMLA', | |
$this->minimumJoomla), 'error'); | |
return false; | |
} | |
$app = Factory::getApplication(); | |
// Check PHP | |
if (!(version_compare(PHP_VERSION, $this->minimumPhp) >= 0)) | |
{ | |
$app->enqueueMessage(Text::sprintf($prefix . 'ERROR_COMPATIBLE_PHP', $this->minimumPhp), | |
'error'); | |
return false; | |
} | |
// Check joomla version | |
if (!(new Version())->isCompatible($this->minimumJoomla)) | |
{ | |
$app->enqueueMessage(Text::sprintf($prefix . 'ERROR_COMPATIBLE_JOOMLA', $this->minimumJoomla), | |
'error'); | |
return false; | |
} | |
// Check database version | |
$db = Factory::getDbo(); | |
$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($prefix . 'ERROR_COMPATIBLE_DATABASE', | |
$this->minimumMySQL, $this->minimumMariaDb), 'error'); | |
return false; | |
} | |
} | |
elseif ($serverType == 'mysql' && !(version_compare($serverVersion, $this->minimumMySQL) >= 0)) | |
{ | |
$app->enqueueMessage(Text::sprintf($prefix . 'ERROR_COMPATIBLE_DATABASE', | |
$this->minimumMySQL, $this->minimumMariaDb), 'error'); | |
return false; | |
} | |
return true; | |
} | |
/** | |
* Runs right after any installation action. | |
* | |
* @param string $type Type of PostFlight action. Possible values are: | |
* @param InstallerAdapter $parent Parent object calling object. | |
* | |
* @throws Exception | |
* | |
* @return boolean True on success, false on failure. | |
* | |
* @since __DEPLOY_VERSION__ | |
*/ | |
function postflight($type, $parent) | |
{ | |
// Enable plugin | |
if ($type == 'install') $this->enablePlugin($parent); | |
// Parse layouts | |
$this->parseLayouts($parent->getParent()->getManifest()->layouts, $parent->getParent()); | |
// Parse cli | |
$this->parseCLI($parent->getParent()->getManifest()->cli, $parent->getParent()); | |
// Check databases | |
$this->checkTables($parent); | |
// Check root category | |
$this->checkRootRecord('#__categories_table'); | |
// Check extension params | |
$this->checkExtensionParams($parent); | |
// Refresh media | |
if ($type === 'update') (new Version())->refreshMediaVersion(); | |
return true; | |
} | |
/** | |
* Enable plugin after installation. | |
* | |
* @param InstallerAdapter $parent Parent object calling object. | |
* | |
* @since __DEPLOY_VERSION__ | |
*/ | |
protected function enablePlugin($parent) | |
{ | |
// Prepare plugin object | |
$plugin = new stdClass(); | |
$plugin->type = 'plugin'; | |
$plugin->element = $parent->getElement(); | |
$plugin->folder = (string) $parent->getParent()->manifest->attributes()['group']; | |
$plugin->enabled = 1; | |
// Update record | |
Factory::getDbo()->updateObject('#__extensions', $plugin, array('type', 'element', 'folder')); | |
} | |
/** | |
* Method to parse through a layout element of the installation manifest and take appropriate action. | |
* | |
* @param SimpleXMLElement $element The XML node to process. | |
* @param Installer $installer Installer calling object. | |
* | |
* @return boolean True on success. | |
* | |
* @since __DEPLOY_VERSION__ | |
*/ | |
public function parseLayouts(SimpleXMLElement $element, $installer) | |
{ | |
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 = array(); | |
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 parse through a cli element of the installation manifest and take appropriate action. | |
* | |
* @param SimpleXMLElement $element The XML node to process. | |
* @param Installer $installer Installer calling object. | |
* | |
* @return boolean True on success. | |
* | |
* @since __DEPLOY_VERSION__ | |
*/ | |
public function parseCLI(SimpleXMLElement $element, $installer) | |
{ | |
if (!$element || !count($element->children())) return false; | |
// Get destination | |
$folder = ((string) $element->attributes()->destination) ? '/' . $element->attributes()->destination : null; | |
$destination = Path::clean(JPATH_ROOT . '/cli' . $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 = array(); | |
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 $parent Parent object calling object. | |
* | |
* @since __DEPLOY_VERSION__ | |
*/ | |
protected function checkTables($parent) | |
{ | |
if ($sql = file_get_contents($parent->getParent()->getPath('extension_administrator') | |
. '/sql/install.mysql.utf8.sql')) | |
{ | |
$db = Factory::getDbo(); | |
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 $table Table name. | |
* | |
* @since __DEPLOY_VERSION__ | |
*/ | |
protected function checkRootRecord($table = null) | |
{ | |
$db = Factory::getDbo(); | |
// 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->state = 1; | |
$db->insertObject($table, $root); | |
} | |
} | |
/** | |
* Method to check extension params and set if need. | |
* | |
* @param InstallerAdapter $parent Parent object calling object. | |
* | |
* @since __DEPLOY_VERSION__ | |
*/ | |
protected function checkExtensionParams($parent) | |
{ | |
if (!empty($this->extensionParams)) | |
{ | |
$element = $parent->getElement(); | |
$folder = (string) $parent->getParent()->manifest->attributes()['group']; | |
// Get extension | |
$db = Factory::getDbo(); | |
$query = $db->getQuery(true) | |
->select(array('extension_id', 'params')) | |
->from($db->quoteName('#__extensions')) | |
->where($db->quoteName('element') . ' = ' . $db->quote($element)); | |
if (!empty($folder)) $query->where($db->quoteName('folder') . ' = ' . $db->quote($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'); | |
} | |
} | |
} | |
} | |
/** | |
* This method is called after extension is uninstalled. | |
* | |
* @param InstallerAdapter $parent Parent object calling object. | |
* | |
* @since __DEPLOY_VERSION__ | |
*/ | |
public function uninstall($parent) | |
{ | |
// Remove layouts | |
$this->removeLayouts($parent->getParent()->getManifest()->layouts); | |
// Remove cli | |
$this->removeCLI($parent->getParent()->getManifest()->cli); | |
} | |
/** | |
* Method to parse through a layouts element of the installation manifest and remove the files that were installed. | |
* | |
* @param SimpleXMLElement $element The XML node to process. | |
* | |
* @return boolean True on success. | |
* | |
* @since __DEPLOY_VERSION__ | |
*/ | |
protected function removeLayouts(SimpleXMLElement $element) | |
{ | |
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; | |
} | |
/** | |
* Method to parse through a cli element of the installation manifest and remove the files that were installed. | |
* | |
* @param SimpleXMLElement $element The XML node to process. | |
* | |
* @return boolean True on success. | |
* | |
* @since __DEPLOY_VERSION__ | |
*/ | |
protected function removeCLI(SimpleXMLElement $element) | |
{ | |
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 . '/cli' . $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