Files
conservatorio-tomadini/administrator/components/com_highlights/script.php
2024-12-31 11:07:09 +01:00

1082 lines
25 KiB
PHP

<?php
/**
* @package Com_Highlights
* @author Eddy Prosperi <eddy.prosperi@protocollicreativi.it>
* @copyright 2024 Eddy Prosperi
* @license GNU General Public License versione 2 o successiva; vedi LICENSE.txt
*/
define('MODIFIED', 1);
define('NOT_MODIFIED', 2);
defined('_JEXEC') or die();
use \Joomla\CMS\Factory;
use \Joomla\CMS\Language\Text;
use \Joomla\CMS\Installer\Installer;
use \Joomla\CMS\Installer\InstallerScript;
/**
* Updates the database structure of the component
*
* @version Release: 0.2b
* @author Component Creator <support@component-creator.com>
* @since 0.1b
*/
class com_highlightsInstallerScript extends InstallerScript
{
/**
* The title of the component (printed on installation and uninstallation messages)
*
* @var string
*/
protected $extension = 'Highlights';
/**
* The minimum Joomla! version required to install this extension
*
* @var string
*/
protected $minimumJoomla = '4.0';
/**
* Method called before install/update the component. Note: This method won't be called during uninstall process.
*
* @param string $type Type of process [install | update]
* @param mixed $parent Object who called this method
*
* @return boolean True if the process should continue, false otherwise
* @throws Exception
*/
public function preflight($type, $parent)
{
$result = parent::preflight($type, $parent);
if (!$result)
{
return $result;
}
// logic for preflight before install
return $result;
}
/**
* Method to install the component
*
* @param mixed $parent Object who called this method.
*
* @return void
*
* @since 0.2b
*/
public function install($parent)
{
$this->installDb($parent);
$this->installPlugins($parent);
$this->installModules($parent);
}
/**
* Method to update the DB of the component
*
* @param mixed $parent Object who started the upgrading process
*
* @return void
*
* @since 0.2b
* @throws Exception
*/
private function installDb($parent)
{
$installation_folder = $parent->getParent()->getPath('source');
$app = Factory::getApplication();
if (function_exists('simplexml_load_file') && file_exists($installation_folder . '/installer/structure.xml'))
{
$component_data = simplexml_load_file($installation_folder . '/installer/structure.xml');
// Check if there are tables to import.
foreach ($component_data->children() as $table)
{
$this->processTable($app, $table);
}
}
else
{
if (!function_exists('simplexml_load_file'))
{
$app->enqueueMessage(Text::_('This script needs \'simplexml_load_file\' to update the component'));
}
else
{
$app->enqueueMessage(Text::_('Structure file was not found.'));
}
}
}
/**
* Process a table
*
* @param CMSApplication $app Application object
* @param SimpleXMLElement $table Table to process
*
* @return void
*
* @since 0.2b
*/
private function processTable($app, $table)
{
$db = Factory::getContainer()->get('DatabaseDriver');
$table_added = false;
if (isset($table['action']))
{
switch ($table['action'])
{
case 'add':
// Check if the table exists before create the statement
if (!$this->existsTable($table['table_name']))
{
$create_statement = $this->generateCreateTableStatement($table);
$db->setQuery($create_statement);
try
{
$db->execute();
$app->enqueueMessage(
Text::sprintf(
'Table `%s` has been successfully created',
(string) $table['table_name']
)
);
$table_added = true;
} catch (Exception $ex)
{
$app->enqueueMessage(
Text::sprintf(
'There was an error creating the table `%s`. Error: %s',
(string) $table['table_name'],
$ex->getMessage()
), 'error'
);
}
}
break;
case 'change':
// Check if the table exists first to avoid errors.
if ($this->existsTable($table['old_name']) && !$this->existsTable($table['new_name']))
{
try
{
$db->renameTable($table['old_name'], $table['new_name']);
$app->enqueueMessage(
Text::sprintf(
'Table `%s` was successfully renamed to `%s`',
$table['old_name'],
$table['new_name']
)
);
} catch (Exception $ex)
{
$app->enqueueMessage(
Text::sprintf(
'There was an error renaming the table `%s`. Error: %s',
$table['old_name'],
$ex->getMessage()
), 'error'
);
}
}
else
{
if (!$this->existsTable($table['table_name']))
{
// If the table does not exists, let's create it.
$create_statement = $this->generateCreateTableStatement($table);
$db->setQuery($create_statement);
try
{
$db->execute();
$app->enqueueMessage(
Text::sprintf('Table `%s` has been successfully created', $table['table_name'])
);
$table_added = true;
} catch (Exception $ex)
{
$app->enqueueMessage(
Text::sprintf(
'There was an error creating the table `%s`. Error: %s',
$table['table_name'],
$ex->getMessage()
), 'error'
);
}
}
}
break;
case 'remove':
try
{
// We make sure that the table will be removed only if it exists specifying ifExists argument as true.
$db->dropTable((string) $table['table_name'], true);
$app->enqueueMessage(
Text::sprintf('Table `%s` was successfully deleted', $table['table_name'])
);
} catch (Exception $ex)
{
$app->enqueueMessage(
Text::sprintf(
'There was an error deleting Table `%s`. Error: %s',
$table['table_name'], $ex->getMessage()
), 'error'
);
}
break;
}
}
// If the table wasn't added before, let's process the fields of the table
if (!$table_added)
{
if ($this->existsTable($table['table_name']))
{
$this->executeFieldsUpdating($app, $table);
}
}
}
/**
* Checks if a certain exists on the current database
*
* @param string $table_name Name of the table
*
* @return boolean True if it exists, false if it does not.
*/
private function existsTable($table_name)
{
$db = Factory::getContainer()->get('DatabaseDriver');
$table_name = str_replace('#__', $db->getPrefix(), (string) $table_name);
return in_array($table_name, $db->getTableList());
}
/**
* Generates a 'CREATE TABLE' statement for the tables passed by argument.
*
* @param SimpleXMLElement $table Table of the database
*
* @return string 'CREATE TABLE' statement
*/
private function generateCreateTableStatement($table)
{
$create_table_statement = '';
if (isset($table->field))
{
$fields = $table->children();
$fields_definitions = array();
$indexes = array();
$db = Factory::getContainer()->get('DatabaseDriver');
foreach ($fields as $field)
{
$field_definition = $this->generateColumnDeclaration($field);
if ($field_definition !== false)
{
$fields_definitions[] = $field_definition;
}
if ($field['index'] == 'index')
{
$indexes[] = $field['field_name'];
}
}
foreach ($indexes as $index)
{
$fields_definitions[] = Text::sprintf(
'INDEX %s (%s ASC)',
$db->quoteName((string) $index), $index
);
}
// Avoid duplicate PK definition
if (strpos(implode(',', $fields_definitions), 'PRIMARY KEY') === false)
{
$fields_definitions[] = 'PRIMARY KEY (`id`)';
}
$create_table_statement = Text::sprintf(
'CREATE TABLE IF NOT EXISTS %s (%s)',
$table['table_name'],
implode(',', $fields_definitions)
);
if(isset($table['storage_engine']) && !empty($table['storage_engine']))
{
$create_table_statement .= " ENGINE=" . $table['storage_engine'];
}
if(isset($table['collation']))
{
$create_table_statement .= " DEFAULT COLLATE=" . $table['collation'];
}
}
return $create_table_statement;
}
/**
* Generate a column declaration
*
* @param SimpleXMLElement $field Field data
*
* @return string Column declaration
*/
private function generateColumnDeclaration($field)
{
$db = Factory::getContainer()->get('DatabaseDriver');
$col_name = $db->quoteName((string) $field['field_name']);
$data_type = $this->getFieldType($field);
if ($data_type !== false)
{
$default_value = (isset($field['default'])) ? 'DEFAULT ' . $field['default'] : '';
$other_data = '';
if (isset($field['is_autoincrement']) && $field['is_autoincrement'] == 1)
{
$other_data .= ' AUTO_INCREMENT PRIMARY KEY';
}
$comment_value = (isset($field['description'])) ? 'COMMENT ' . $db->quote((string) $field['description']) : '';
if(strtolower($field['field_type']) == 'datetime' || strtolower($field['field_type']) == 'text')
{
return Text::sprintf(
'%s %s %s %s %s', $col_name, $data_type,
$default_value, $other_data, $comment_value
);
}
if((isset($field['required']) && $field['required'] == 1) || $field['field_name'] == 'id')
{
return Text::sprintf(
'%s %s NOT NULL %s %s %s', $col_name, $data_type,
$default_value, $other_data, $comment_value
);
}
return Text::sprintf(
'%s %s NULL %s %s %s', $col_name, $data_type,
$default_value, $other_data, $comment_value
);
}
return false;
}
/**
* Generates SQL field type of a field.
*
* @param SimpleXMLElement $field Field information
*
* @return mixed SQL string data type, false on failure.
*/
private function getFieldType($field)
{
$data_type = (string) $field['field_type'];
if (isset($field['field_length']) && ($this->allowsLengthField($data_type) || $data_type == 'ENUM'))
{
$data_type .= '(' . (string) $field['field_length'] . ')';
}
return (!empty($data_type)) ? $data_type : false;
}
/**
* Check if a SQL type allows length values.
*
* @param string $field_type SQL type
*
* @return boolean True if it allows length values, false if it does not.
*/
private function allowsLengthField($field_type)
{
$allow_length = array(
'INT',
'VARCHAR',
'CHAR',
'TINYINT',
'SMALLINT',
'MEDIUMINT',
'INTEGER',
'BIGINT',
'FLOAT',
'DOUBLE',
'DECIMAL',
'NUMERIC'
);
return (in_array((string) $field_type, $allow_length));
}
/**
* Updates all the fields related to a table.
*
* @param CMSApplication $app Application Object
* @param SimpleXMLElement $table Table information.
*
* @return void
*/
private function executeFieldsUpdating($app, $table)
{
if (isset($table->field))
{
foreach ($table->children() as $field)
{
$table_name = (string) $table['table_name'];
$this->processField($app, $table_name, $field);
}
}
}
/**
* Process a certain field.
*
* @param CMSApplication $app Application object
* @param string $table_name The name of the table that contains the field.
* @param SimpleXMLElement $field Field Information.
*
* @return void
*/
private function processField($app, $table_name, $field)
{
$db = Factory::getContainer()->get('DatabaseDriver');
if (isset($field['action']))
{
switch ($field['action'])
{
case 'add':
$result = $this->addField($table_name, $field);
if ($result === MODIFIED)
{
$app->enqueueMessage(
Text::sprintf('Field `%s` has been successfully added', $field['field_name'])
);
}
else
{
if ($result !== NOT_MODIFIED)
{
$app->enqueueMessage(
Text::sprintf(
'There was an error adding the field `%s`. Error: %s',
$field['field_name'], $result
), 'error'
);
}
}
break;
case 'change':
if (isset($field['old_name']) && isset($field['new_name']))
{
if ($this->existsField($table_name, $field['old_name']) && !$this->existsField($table_name, $field['new_name']))
{
$renaming_statement = Text::sprintf(
'ALTER TABLE %s CHANGE %s %s %s',
$table_name, $db->quoteName($field['old_name']->__toString()),
$db->quoteName($field['new_name']->__toString()),
$this->getFieldType($field)
);
$db->setQuery($renaming_statement);
try
{
$db->execute();
$app->enqueueMessage(
Text::sprintf('Field `%s` has been successfully modified', $field['old_name'])
);
} catch (Exception $ex)
{
$app->enqueueMessage(
Text::sprintf(
'There was an error modifying the field `%s`. Error: %s',
$field['field_name'],
$ex->getMessage()
), 'error'
);
}
}
else
{
$result = $this->addField($table_name, $field);
if ($result === MODIFIED)
{
$app->enqueueMessage(
Text::sprintf('Field `%s` has been successfully modified', $field['field_name'])
);
}
else
{
if ($result !== NOT_MODIFIED)
{
$app->enqueueMessage(
Text::sprintf(
'There was an error modifying the field `%s`. Error: %s',
$field['field_name'], $result
), 'error'
);
}
}
}
}
else
{
$result = $this->addField($table_name, $field);
if ($result === MODIFIED)
{
$app->enqueueMessage(
Text::sprintf('Field `%s` has been successfully modified', $field['field_name'])
);
}
else
{
if ($result !== NOT_MODIFIED)
{
$app->enqueueMessage(
Text::sprintf(
'There was an error modifying the field `%s`. Error: %s',
$field['field_name'], $result
), 'error'
);
}
}
}
break;
case 'remove':
// Check if the field exists first to prevent issue removing the field
if ($this->existsField($table_name, $field['field_name']))
{
$drop_statement = Text::sprintf(
'ALTER TABLE `%s` DROP COLUMN `%s`',
$table_name, $field['field_name']
);
$db->setQuery($drop_statement);
try
{
$db->execute();
$app->enqueueMessage(
Text::sprintf('Field `%s` has been successfully deleted', $field['field_name'])
);
} catch (Exception $ex)
{
$app->enqueueMessage(
Text::sprintf(
'There was an error deleting the field `%s`. Error: %s',
$field['field_name'],
$ex->getMessage()
), 'error'
);
}
}
break;
}
}
else
{
$result = $this->addField($table_name, $field);
if ($result === MODIFIED)
{
$app->enqueueMessage(
Text::sprintf('Field `%s` has been successfully added', $field['field_name'])
);
}
else
{
if ($result !== NOT_MODIFIED)
{
$app->enqueueMessage(
Text::sprintf(
'There was an error adding the field `%s`. Error: %s',
$field['field_name'], $result
), 'error'
);
}
}
}
}
/**
* Add a field if it does not exists or modify it if it does.
*
* @param string $table_name Table name
* @param SimpleXMLElement $field Field Information
*
* @return mixed Constant on success(self::$MODIFIED | self::$NOT_MODIFIED), error message if an error occurred
*/
private function addField($table_name, $field)
{
$db = Factory::getContainer()->get('DatabaseDriver');
$query_generated = false;
// Check if the field exists first to prevent issues adding the field
if ($this->existsField($table_name, $field['field_name']))
{
if ($this->needsToUpdate($table_name, $field))
{
$change_statement = $this->generateChangeFieldStatement($table_name, $field);
$db->setQuery($change_statement);
$query_generated = true;
}
}
else
{
$add_statement = $this->generateAddFieldStatement($table_name, $field);
$db->setQuery($add_statement);
$query_generated = true;
}
if ($query_generated)
{
try
{
$db->execute();
return MODIFIED;
} catch (Exception $ex)
{
return $ex->getMessage();
}
}
return NOT_MODIFIED;
}
/**
* Checks if a field exists on a table
*
* @param string $table_name Table name
* @param string $field_name Field name
*
* @return boolean True if exists, false if it do
*/
private function existsField($table_name, $field_name)
{
$db = Factory::getContainer()->get('DatabaseDriver');
return in_array((string) $field_name, array_keys($db->getTableColumns($table_name)));
}
/**
* Check if a field needs to be updated.
*
* @param string $table_name Table name
* @param SimpleXMLElement $field Field information
*
* @return boolean True if the field has to be updated, false otherwise
*/
private function needsToUpdate($table_name, $field)
{
if(!isset($field['action']) || $field['field_name'] == 'id')
{
return false;
}
$db = Factory::getContainer()->get('DatabaseDriver');
$query = Text::sprintf(
'SHOW FULL COLUMNS FROM `%s` WHERE Field LIKE %s', $table_name, $db->quote((string) $field['field_name'])
);
$db->setQuery($query);
$field_info = $db->loadObject();
if (strcasecmp($field_info->Type, $this->getFieldType($field)) !=0)
{
return true;
}
else
{
return false;
}
}
/**
* Generates an change column statement
*
* @param string $table_name Table name
* @param SimpleXMLElement $field Field Information
*
* @return string Change column statement
*/
private function generateChangeFieldStatement($table_name, $field)
{
$column_declaration = $this->generateColumnDeclaration($field);
return Text::sprintf('ALTER TABLE %s MODIFY %s', $table_name, $column_declaration);
}
/**
* Generates an add column statement
*
* @param string $table_name Table name
* @param SimpleXMLElement $field Field Information
*
* @return string Add column statement
*/
private function generateAddFieldStatement($table_name, $field)
{
$column_declaration = $this->generateColumnDeclaration($field);
return Text::sprintf('ALTER TABLE %s ADD %s', $table_name, $column_declaration);
}
/**
* Installs plugins for this component
*
* @param mixed $parent Object who called the install/update method
*
* @return void
*/
private function installPlugins($parent)
{
$installation_folder = $parent->getParent()->getPath('source');
$app = Factory::getApplication();
/* @var $plugins SimpleXMLElement */
if (method_exists($parent, 'getManifest'))
{
$plugins = $parent->getManifest()->plugins;
}
else
{
$plugins = $parent->get('manifest')->plugins;
}
if (count($plugins->children()))
{
$db = Factory::getContainer()->get('DatabaseDriver');
$query = $db->getQuery(true);
foreach ($plugins->children() as $plugin)
{
$pluginName = (string) $plugin['plugin'];
$pluginGroup = (string) $plugin['group'];
$path = $installation_folder . '/plugins/' . $pluginGroup . '/' . $pluginName;
$installer = new Installer;
if (!$this->isAlreadyInstalled('plugin', $pluginName, $pluginGroup))
{
$result = $installer->install($path);
}
else
{
$result = $installer->update($path);
}
if ($result)
{
$app->enqueueMessage('Plugin ' . $pluginName . ' was installed successfully');
}
else
{
$app->enqueueMessage('There was an issue installing the plugin ' . $pluginName,
'error');
}
$query
->clear()
->update('#__extensions')
->set('enabled = 1')
->where(
array(
'type LIKE ' . $db->quote('plugin'),
'element LIKE ' . $db->quote($pluginName),
'folder LIKE ' . $db->quote($pluginGroup)
)
);
$db->setQuery($query);
$db->execute();
}
}
}
/**
* Check if an extension is already installed in the system
*
* @param string $type Extension type
* @param string $name Extension name
* @param mixed $folder Extension folder(for plugins)
*
* @return boolean
*/
private function isAlreadyInstalled($type, $name, $folder = null)
{
$result = false;
switch ($type)
{
case 'plugin':
$result = file_exists(JPATH_PLUGINS . '/' . $folder . '/' . $name);
break;
case 'module':
$result = file_exists(JPATH_SITE . '/modules/' . $name);
break;
}
return $result;
}
/**
* Installs plugins for this component
*
* @param mixed $parent Object who called the install/update method
*
* @return void
*/
private function installModules($parent)
{
$installation_folder = $parent->getParent()->getPath('source');
$app = Factory::getApplication();
if (method_exists($parent, 'getManifest'))
{
$modules = $parent->getManifest()->modules;
}
else
{
$modules = $parent->get('manifest')->modules;
}
if (!empty($modules))
{
if (count($modules->children()))
{
foreach ($modules->children() as $module)
{
$moduleName = (string) $module['module'];
$path = $installation_folder . '/modules/' . $moduleName;
$installer = new Installer;
if (!$this->isAlreadyInstalled('module', $moduleName))
{
$result = $installer->install($path);
}
else
{
$result = $installer->update($path);
}
if ($result)
{
$app->enqueueMessage('Module ' . $moduleName . ' was installed successfully');
}
else
{
$app->enqueueMessage('There was an issue installing the module ' . $moduleName,
'error');
}
}
}
}
}
/**
* Method to update the component
*
* @param mixed $parent Object who called this method.
*
* @return void
*/
public function update($parent)
{
$this->installDb($parent);
$this->installPlugins($parent);
$this->installModules($parent);
}
/**
* Method to uninstall the component
*
* @param mixed $parent Object who called this method.
*
* @return void
*/
public function uninstall($parent)
{
$this->uninstallPlugins($parent);
$this->uninstallModules($parent);
}
/**
* Uninstalls plugins
*
* @param mixed $parent Object who called the uninstall method
*
* @return void
*/
private function uninstallPlugins($parent)
{
$app = Factory::getApplication();
if (method_exists($parent, 'getManifest'))
{
$plugins = $parent->getManifest()->plugins;
}
else
{
$plugins = $parent->get('manifest')->plugins;
}
if (count($plugins->children()))
{
$db = Factory::getContainer()->get('DatabaseDriver');
$query = $db->getQuery(true);
foreach ($plugins->children() as $plugin)
{
$pluginName = (string) $plugin['plugin'];
$pluginGroup = (string) $plugin['group'];
$query
->clear()
->select('extension_id')
->from('#__extensions')
->where(
array(
'type LIKE ' . $db->quote('plugin'),
'element LIKE ' . $db->quote($pluginName),
'folder LIKE ' . $db->quote($pluginGroup)
)
);
$db->setQuery($query);
$extension = $db->loadResult();
if (!empty($extension))
{
$installer = new Installer;
$result = $installer->uninstall('plugin', $extension);
if ($result)
{
$app->enqueueMessage('Plugin ' . $pluginName . ' was uninstalled successfully');
}
else
{
$app->enqueueMessage('There was an issue uninstalling the plugin ' . $pluginName,
'error');
}
}
}
}
}
/**
* Uninstalls plugins
*
* @param mixed $parent Object who called the uninstall method
*
* @return void
*/
private function uninstallModules($parent)
{
$app = Factory::getApplication();
if (method_exists($parent, 'getManifest'))
{
$modules = $parent->getManifest()->modules;
}
else
{
$modules = $parent->get('manifest')->modules;
}
if (!empty($modules))
{
if (count($modules->children()))
{
$db = Factory::getContainer()->get('DatabaseDriver');
$query = $db->getQuery(true);
foreach ($modules->children() as $plugin)
{
$moduleName = (string) $plugin['module'];
$query
->clear()
->select('extension_id')
->from('#__extensions')
->where(
array(
'type LIKE ' . $db->quote('module'),
'element LIKE ' . $db->quote($moduleName)
)
);
$db->setQuery($query);
$extension = $db->loadResult();
if (!empty($extension))
{
$installer = new Installer;
$result = $installer->uninstall('module', $extension);
if ($result)
{
$app->enqueueMessage('Module ' . $moduleName . ' was uninstalled successfully');
}
else
{
$app->enqueueMessage('There was an issue uninstalling the module ' . $moduleName,
'error');
}
}
}
}
}
}
/**
* @param string $type type
* @param string $parent parent
*
* @return boolean
* @since Kunena
*/
public function postflight($type, $parent)
{
return true;
}
}