Skip to content

Instantly share code, notes, and snippets.

@Septdir
Last active Dec 22, 2021
Embed
What would you like to do?
Joomla Install Script

Установочный скрипт для Joomla

Пример установочного скрипта для Joomla

Joomla Install Script

Joomla Install Script Example

; @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"
; @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 и более поздними версиями"
<?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;
}
}
<?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