%PDF- %PDF-
Direktori : /home/lightco1/upgrade.lightco.com.au/libraries/fof30/Utils/InstallScript/ |
Current File : /home/lightco1/upgrade.lightco.com.au/libraries/fof30/Utils/InstallScript/BaseInstaller.php |
<?php /** * @package FOF * @copyright 2010-2017 Nicholas K. Dionysopoulos / Akeeba Ltd * @license GNU GPL version 2 or later */ namespace FOF30\Utils\InstallScript; use DirectoryIterator; use Exception; use JFactory; use JFile; use JFolder; use JLoader; use JLog; defined('_JEXEC') or die; JLoader::import('joomla.filesystem.folder'); JLoader::import('joomla.filesystem.file'); JLoader::import('joomla.installer.installer'); JLoader::import('joomla.utilities.date'); class BaseInstaller { /** * The minimum PHP version required to install this extension * * @var string */ protected $minimumPHPVersion = '5.4.0'; /** * The minimum Joomla! version required to install this extension * * @var string */ protected $minimumJoomlaVersion = '3.3.0'; /** * The maximum Joomla! version this extension can be installed on * * @var string */ protected $maximumJoomlaVersion = '4.0.99'; /** * Post-installation message definitions for Joomla! 3.2 or later. * * This array contains the message definitions for the Post-installation Messages component added in Joomla! 3.2 and * later versions. Each element is also a hashed array. For the keys used in these message definitions please see * addPostInstallationMessage * * @var array */ protected $postInstallationMessages = array(); /** * Recursively copy a bunch of files, but only if the source and target file have a different size. * * @param string $source Path to copy FROM * @param string $dest Path to copy TO * @param array $ignored List of entries to ignore (first level entries are taken into account) * * @return void */ protected function recursiveConditionalCopy($source, $dest, $ignored = array()) { // Make sure source and destination exist if (!@is_dir($source)) { return; } if (!@is_dir($dest)) { if (!@mkdir($dest, 0755)) { JFolder::create($dest, 0755); } } if (!@is_dir($dest)) { $this->log(__CLASS__ . ": Cannot create folder $dest"); return; } // List the contents of the source folder try { $di = new DirectoryIterator($source); } catch (Exception $e) { return; } // Process each entry foreach ($di as $entry) { // Ignore dot dirs (. and ..) if ($entry->isDot()) { continue; } $sourcePath = $entry->getPathname(); $fileName = $entry->getFilename(); // Do not copy ignored files if (!empty($ignored) && in_array($fileName, $ignored)) { continue; } // If it's a directory do a recursive copy if ($entry->isDir()) { $this->recursiveConditionalCopy($sourcePath, $dest . DIRECTORY_SEPARATOR . $fileName); continue; } // If it's a file check if it's missing or identical $mustCopy = false; $targetPath = $dest . DIRECTORY_SEPARATOR . $fileName; if (!@is_file($targetPath)) { $mustCopy = true; } else { $sourceSize = @filesize($sourcePath); $targetSize = @filesize($targetPath); $mustCopy = $sourceSize != $targetSize; } if (!$mustCopy) { continue; } if (!@copy($sourcePath, $targetPath)) { if (!JFile::copy($sourcePath, $targetPath)) { $this->log(__CLASS__ . ": Cannot copy $sourcePath to $targetPath"); } } } } /** * Try to log a warning / error with Joomla * * @param string $message The message to write to the log * @param bool $error Is this an error? If not, it's a warning. (default: false) * @param string $category Log category, default jerror * * @return void */ protected function log($message, $error = false, $category = 'jerror') { // Just in case... if (!class_exists('JLog', true)) { return; } $priority = $error ? JLog::ERROR : JLog::WARNING; try { JLog::add($message, $priority, $category); } catch (Exception $e) { // Swallow the exception. } } /** * Check that the server meets the minimum PHP version requirements. * * @return bool */ protected function checkPHPVersion() { if (!empty($this->minimumPHPVersion)) { if (defined('PHP_VERSION')) { $version = PHP_VERSION; } elseif (function_exists('phpversion')) { $version = phpversion(); } else { $version = '5.0.0'; // all bets are off! } if (!version_compare($version, $this->minimumPHPVersion, 'ge')) { $msg = "<p>You need PHP $this->minimumPHPVersion or later to install this extension</p>"; $this->log($msg); return false; } } return true; } /** * Check the minimum and maximum Joomla! versions for this extension * * @return bool */ protected function checkJoomlaVersion() { if (!empty($this->minimumJoomlaVersion) && !version_compare(JVERSION, $this->minimumJoomlaVersion, 'ge')) { $msg = "<p>You need Joomla! $this->minimumJoomlaVersion or later to install this extension</p>"; $this->log($msg); return false; } // Check the maximum Joomla! version if (!empty($this->maximumJoomlaVersion) && !version_compare(JVERSION, $this->maximumJoomlaVersion, 'le')) { $msg = "<p>You need Joomla! $this->maximumJoomlaVersion or earlier to install this extension</p>"; $this->log($msg); return false; } return true; } /** * Clear PHP opcode caches * * @return void */ protected function clearOpcodeCaches() { // Always reset the OPcache if it's enabled. Otherwise there's a good chance the server will not know we are // replacing .php scripts. This is a major concern since PHP 5.5 included and enabled OPcache by default. if (function_exists('opcache_reset')) { opcache_reset(); } // Also do that for APC cache elseif (function_exists('apc_clear_cache')) { @apc_clear_cache(); } } /** * Get the dependencies for a package from the #__akeeba_common table * * @param string $package The package * * @return array The dependencies */ protected function getDependencies($package) { $db = JFactory::getDbo(); $query = $db->getQuery(true) ->select($db->qn('value')) ->from($db->qn('#__akeeba_common')) ->where($db->qn('key') . ' = ' . $db->q($package)); try { $dependencies = $db->setQuery($query)->loadResult(); $dependencies = json_decode($dependencies, true); if (empty($dependencies)) { $dependencies = array(); } } catch (Exception $e) { $dependencies = array(); } return $dependencies; } /** * Sets the dependencies for a package into the #__akeeba_common table * * @param string $package The package * @param array $dependencies The dependencies list */ protected function setDependencies($package, array $dependencies) { $db = JFactory::getDbo(); $query = $db->getQuery(true) ->delete('#__akeeba_common') ->where($db->qn('key') . ' = ' . $db->q($package)); try { $db->setQuery($query)->execute(); } catch (Exception $e) { // Do nothing if the old key wasn't found } $object = (object) array( 'key' => $package, 'value' => json_encode($dependencies), ); try { $db->insertObject('#__akeeba_common', $object, 'key'); } catch (Exception $e) { // Do nothing if the old key wasn't found } } /** * Adds a package dependency to #__akeeba_common * * @param string $package The package * @param string $dependency The dependency to add */ protected function addDependency($package, $dependency) { $dependencies = $this->getDependencies($package); if (!in_array($dependency, $dependencies)) { $dependencies[] = $dependency; $this->setDependencies($package, $dependencies); } } /** * Removes a package dependency from #__akeeba_common * * @param string $package The package * @param string $dependency The dependency to remove */ protected function removeDependency($package, $dependency) { $dependencies = $this->getDependencies($package); if (in_array($dependency, $dependencies)) { $index = array_search($dependency, $dependencies); unset($dependencies[$index]); $this->setDependencies($package, $dependencies); } } /** * Do I have a dependency for a package in #__akeeba_common * * @param string $package The package * @param string $dependency The dependency to check for * * @return bool */ protected function hasDependency($package, $dependency) { $dependencies = $this->getDependencies($package); return in_array($dependency, $dependencies); } /** * Adds or updates a post-installation message (PIM) definition for Joomla! 3.2 or later. You can use this in your * post-installation script using this code: * * The $options array contains the following mandatory keys: * * extension_id The numeric ID of the extension this message is for (see the #__extensions table) * * type One of message, link or action. Their meaning is: * message Informative message. The user can dismiss it. * link The action button links to a URL. The URL is defined in the action parameter. * action A PHP action takes place when the action button is clicked. You need to specify the * action_file (RAD path to the PHP file) and action (PHP function name) keys. See * below for more information. * * title_key The JText language key for the title of this PIM * Example: COM_FOOBAR_POSTINSTALL_MESSAGEONE_TITLE * * description_key The JText language key for the main body (description) of this PIM * Example: COM_FOOBAR_POSTINSTALL_MESSAGEONE_DESCRIPTION * * action_key The JText language key for the action button. Ignored and not required when type=message * Example: COM_FOOBAR_POSTINSTALL_MESSAGEONE_ACTION * * language_extension The extension name which holds the language keys used above. For example, com_foobar, * mod_something, plg_system_whatever, tpl_mytemplate * * language_client_id Should we load the front-end (0) or back-end (1) language keys? * * version_introduced Which was the version of your extension where this message appeared for the first time? * Example: 3.2.1 * * enabled Must be 1 for this message to be enabled. If you omit it, it defaults to 1. * * condition_file The RAD path to a PHP file containing a PHP function which determines whether this message * should be shown to the user. @see Template::parsePath() for RAD path format. Joomla! * will include this file before calling the condition_method. * Example: admin://components/com_foobar/helpers/postinstall.php * * condition_method The name of a PHP function which will be used to determine whether to show this message to * the user. This must be a simple PHP user function (not a class method, static method etc) * which returns true to show the message and false to hide it. This function is defined in the * condition_file. * Example: com_foobar_postinstall_messageone_condition * * When type=message no additional keys are required. * * When type=link the following additional keys are required: * * action The URL which will open when the user clicks on the PIM's action button * Example: index.php?option=com_foobar&view=tools&task=installSampleData * * Then type=action the following additional keys are required: * * action_file The RAD path to a PHP file containing a PHP function which performs the action of this PIM. * * @see Template::parsePath() for RAD path format. Joomla! will include this file * before calling the function defined in the action key below. * Example: admin://components/com_foobar/helpers/postinstall.php * * action The name of a PHP function which will be used to run the action of this PIM. This must be a * simple PHP user function (not a class method, static method etc) which returns no result. * Example: com_foobar_postinstall_messageone_action * * @param array $options See description * * @return void * * @throws Exception */ protected function addPostInstallationMessage(array $options) { // Make sure there are options set if (!is_array($options)) { throw new Exception('Post-installation message definitions must be of type array', 500); } // Initialise array keys $defaultOptions = array( 'extension_id' => '', 'type' => '', 'title_key' => '', 'description_key' => '', 'action_key' => '', 'language_extension' => '', 'language_client_id' => '', 'action_file' => '', 'action' => '', 'condition_file' => '', 'condition_method' => '', 'version_introduced' => '', 'enabled' => '1', ); $options = array_merge($defaultOptions, $options); // Array normalisation. Removes array keys not belonging to a definition. $defaultKeys = array_keys($defaultOptions); $allKeys = array_keys($options); $extraKeys = array_diff($allKeys, $defaultKeys); if (!empty($extraKeys)) { foreach ($extraKeys as $key) { unset($options[$key]); } } // Normalisation of integer values $options['extension_id'] = (int) $options['extension_id']; $options['language_client_id'] = (int) $options['language_client_id']; $options['enabled'] = (int) $options['enabled']; // Normalisation of 0/1 values foreach (array('language_client_id', 'enabled') as $key) { $options[$key] = $options[$key] ? 1 : 0; } // Make sure there's an extension_id if (!(int) $options['extension_id']) { throw new Exception('Post-installation message definitions need an extension_id', 500); } // Make sure there's a valid type if (!in_array($options['type'], array('message', 'link', 'action'))) { throw new Exception('Post-installation message definitions need to declare a type of message, link or action', 500); } // Make sure there's a title key if (empty($options['title_key'])) { throw new Exception('Post-installation message definitions need a title key', 500); } // Make sure there's a description key if (empty($options['description_key'])) { throw new Exception('Post-installation message definitions need a description key', 500); } // If the type is anything other than message you need an action key if (($options['type'] != 'message') && empty($options['action_key'])) { throw new Exception('Post-installation message definitions need an action key when they are of type "' . $options['type'] . '"', 500); } // You must specify the language extension if (empty($options['language_extension'])) { throw new Exception('Post-installation message definitions need to specify which extension contains their language keys', 500); } // The action file and method are only required for the "action" type if ($options['type'] == 'action') { if (empty($options['action_file'])) { throw new Exception('Post-installation message definitions need an action file when they are of type "action"', 500); } $file_path = \FOFTemplateUtils::parsePath($options['action_file'], true); if (!@is_file($file_path)) { throw new Exception('The action file ' . $options['action_file'] . ' of your post-installation message definition does not exist', 500); } if (empty($options['action'])) { throw new Exception('Post-installation message definitions need an action (function name) when they are of type "action"', 500); } } if ($options['type'] == 'link') { if (empty($options['link'])) { throw new Exception('Post-installation message definitions need an action (URL) when they are of type "link"', 500); } } // The condition file and method are only required when the type is not "message" if ($options['type'] != 'message') { if (empty($options['condition_file'])) { throw new Exception('Post-installation message definitions need a condition file when they are of type "' . $options['type'] . '"', 500); } $file_path = \FOFTemplateUtils::parsePath($options['condition_file'], true); if (!@is_file($file_path)) { throw new Exception('The condition file ' . $options['condition_file'] . ' of your post-installation message definition does not exist', 500); } if (empty($options['condition_method'])) { throw new Exception('Post-installation message definitions need a condition method (function name) when they are of type "' . $options['type'] . '"', 500); } } // Check if the definition exists $tableName = '#__postinstall_messages'; $db = JFactory::getDbo(); $query = $db->getQuery(true) ->select('*') ->from($db->qn($tableName)) ->where($db->qn('extension_id') . ' = ' . $db->q($options['extension_id'])) ->where($db->qn('type') . ' = ' . $db->q($options['type'])) ->where($db->qn('title_key') . ' = ' . $db->q($options['title_key'])); $existingRow = $db->setQuery($query)->loadAssoc(); // Is the existing definition the same as the one we're trying to save (ignore the enabled flag)? if (!empty($existingRow)) { $same = true; foreach ($options as $k => $v) { if ($k == 'enabled') { continue; } if ($existingRow[$k] != $v) { $same = false; break; } } // Trying to add the same row as the existing one; quit if ($same) { return; } // Otherwise it's not the same row. Remove the old row before insert a new one. $query = $db->getQuery(true) ->delete($db->qn($tableName)) ->where($db->q('extension_id') . ' = ' . $db->q($options['extension_id'])) ->where($db->q('type') . ' = ' . $db->q($options['type'])) ->where($db->q('title_key') . ' = ' . $db->q($options['title_key'])); $db->setQuery($query)->execute(); } // Insert the new row $options = (object) $options; $db->insertObject($tableName, $options); } /** * Applies the post-installation messages for Joomla! 3.2 or later * * @return void */ protected function _applyPostInstallationMessages() { // Make sure it's Joomla! 3.2.0 or later if (!version_compare(JVERSION, '3.2.0', 'ge')) { return; } // Make sure there are post-installation messages if (empty($this->postInstallationMessages)) { return; } // Get the extension ID for our component $db = JFactory::getDbo(); $query = $db->getQuery(true); $query->select('extension_id') ->from('#__extensions') ->where($db->qn('type') . ' = ' . $db->q('component')) ->where($db->qn('element') . ' = ' . $db->q($this->componentName)); $db->setQuery($query); try { $ids = $db->loadColumn(); } catch (Exception $exc) { return; } if (empty($ids)) { return; } $extension_id = array_shift($ids); foreach ($this->postInstallationMessages as $message) { $message['extension_id'] = $extension_id; $this->addPostInstallationMessage($message); } } /** * Uninstalls the post-installation messages for Joomla! 3.2 or later * * @return void */ protected function uninstallPostInstallationMessages() { // Make sure it's Joomla! 3.2.0 or later if (!version_compare(JVERSION, '3.2.0', 'ge')) { return; } // Make sure there are post-installation messages if (empty($this->postInstallationMessages)) { return; } // Get the extension ID for our component $db = JFactory::getDbo(); $query = $db->getQuery(true); $query->select('extension_id') ->from('#__extensions') ->where($db->qn('type') . ' = ' . $db->q('component')) ->where($db->qn('element') . ' = ' . $db->q($this->componentName)); $db->setQuery($query); try { $ids = $db->loadColumn(); } catch (Exception $exc) { return; } if (empty($ids)) { return; } $extension_id = array_shift($ids); $query = $db->getQuery(true) ->delete($db->qn('#__postinstall_messages')) ->where($db->qn('extension_id') . ' = ' . $db->q($extension_id)); try { $db->setQuery($query)->execute(); } catch (Exception $e) { return; } } }