Spade
Mini Shell
array/array.php000064400000030761151167373210007526 0ustar00<?php
/**
* @package FrameworkOnFramework
* @subpackage utils
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('FOF_INCLUDED') or die;
/**
* A utility class to handle array manipulation.
*
* Based on the JArrayHelper class as found in Joomla! 3.2.0
*/
abstract class FOFUtilsArray
{
/**
* Option to perform case-sensitive sorts.
*
* @var mixed Boolean or array of booleans.
*/
protected static $sortCase;
/**
* Option to set the sort direction.
*
* @var mixed Integer or array of integers.
*/
protected static $sortDirection;
/**
* Option to set the object key to sort on.
*
* @var string
*/
protected static $sortKey;
/**
* Option to perform a language aware sort.
*
* @var mixed Boolean or array of booleans.
*/
protected static $sortLocale;
/**
* Function to convert array to integer values
*
* @param array &$array The source array to convert
* @param mixed $default A default value (int|array) to assign if
$array is not an array
*
* @return void
*/
public static function toInteger(&$array, $default = null)
{
if (is_array($array))
{
foreach ($array as $i => $v)
{
$array[$i] = (int) $v;
}
}
else
{
if ($default === null)
{
$array = array();
}
elseif (is_array($default))
{
self::toInteger($default, null);
$array = $default;
}
else
{
$array = array((int) $default);
}
}
}
/**
* Utility function to map an array to a stdClass object.
*
* @param array &$array The array to map.
* @param string $class Name of the class to create
*
* @return object The object mapped from the given array
*/
public static function toObject(&$array, $class =
'stdClass')
{
$obj = null;
if (is_array($array))
{
$obj = new $class;
foreach ($array as $k => $v)
{
if (is_array($v))
{
$obj->$k = self::toObject($v, $class);
}
else
{
$obj->$k = $v;
}
}
}
return $obj;
}
/**
* Utility function to map an array to a string.
*
* @param array $array The array to map.
* @param string $inner_glue The glue (optional, defaults to
'=') between the key and the value.
* @param string $outer_glue The glue (optional, defaults to '
') between array elements.
* @param boolean $keepOuterKey True if final key should be kept.
*
* @return string The string mapped from the given array
*/
public static function toString($array = null, $inner_glue =
'=', $outer_glue = ' ', $keepOuterKey = false)
{
$output = array();
if (is_array($array))
{
foreach ($array as $key => $item)
{
if (is_array($item))
{
if ($keepOuterKey)
{
$output[] = $key;
}
// This is value is an array, go and do it again!
$output[] = self::toString($item, $inner_glue, $outer_glue,
$keepOuterKey);
}
else
{
$output[] = $key . $inner_glue . '"' . $item .
'"';
}
}
}
return implode($outer_glue, $output);
}
/**
* Utility function to map an object to an array
*
* @param object $p_obj The source object
* @param boolean $recurse True to recurse through multi-level objects
* @param string $regex An optional regular expression to match on
field names
*
* @return array The array mapped from the given object
*/
public static function fromObject($p_obj, $recurse = true, $regex = null)
{
if (is_object($p_obj))
{
return self::_fromObject($p_obj, $recurse, $regex);
}
else
{
return null;
}
}
/**
* Utility function to map an object or array to an array
*
* @param mixed $item The source object or array
* @param boolean $recurse True to recurse through multi-level objects
* @param string $regex An optional regular expression to match on
field names
*
* @return array The array mapped from the given object
*/
protected static function _fromObject($item, $recurse, $regex)
{
if (is_object($item))
{
$result = array();
foreach (get_object_vars($item) as $k => $v)
{
if (!$regex || preg_match($regex, $k))
{
if ($recurse)
{
$result[$k] = self::_fromObject($v, $recurse, $regex);
}
else
{
$result[$k] = $v;
}
}
}
}
elseif (is_array($item))
{
$result = array();
foreach ($item as $k => $v)
{
$result[$k] = self::_fromObject($v, $recurse, $regex);
}
}
else
{
$result = $item;
}
return $result;
}
/**
* Extracts a column from an array of arrays or objects
*
* @param array &$array The source array
* @param string $index The index of the column or name of object
property
*
* @return array Column of values from the source array
*/
public static function getColumn(&$array, $index)
{
$result = array();
if (is_array($array))
{
foreach ($array as &$item)
{
if (is_array($item) && isset($item[$index]))
{
$result[] = $item[$index];
}
elseif (is_object($item) && isset($item->$index))
{
$result[] = $item->$index;
}
// Else ignore the entry
}
}
return $result;
}
/**
* Utility function to return a value from a named array or a specified
default
*
* @param array &$array A named array
* @param string $name The key to search for
* @param mixed $default The default value to give if no key found
* @param string $type Return type for the variable (INT, FLOAT,
STRING, WORD, BOOLEAN, ARRAY)
*
* @return mixed The value from the source array
*/
public static function getValue(&$array, $name, $default = null, $type
= '')
{
$result = null;
if (isset($array[$name]))
{
$result = $array[$name];
}
// Handle the default case
if (is_null($result))
{
$result = $default;
}
// Handle the type constraint
switch (strtoupper($type))
{
case 'INT':
case 'INTEGER':
// Only use the first integer value
@preg_match('/-?[0-9]+/', $result, $matches);
$result = @(int) $matches[0];
break;
case 'FLOAT':
case 'DOUBLE':
// Only use the first floating point value
@preg_match('/-?[0-9]+(\.[0-9]+)?/', $result, $matches);
$result = @(float) $matches[0];
break;
case 'BOOL':
case 'BOOLEAN':
$result = (bool) $result;
break;
case 'ARRAY':
if (!is_array($result))
{
$result = array($result);
}
break;
case 'STRING':
$result = (string) $result;
break;
case 'WORD':
$result = (string) preg_replace('#\W#', '',
$result);
break;
case 'NONE':
default:
// No casting necessary
break;
}
return $result;
}
/**
* Takes an associative array of arrays and inverts the array keys to
values using the array values as keys.
*
* Example:
* $input = array(
* 'New' => array('1000', '1500',
'1750'),
* 'Used' => array('3000', '4000',
'5000', '6000')
* );
* $output = FOFUtilsArray::invert($input);
*
* Output would be equal to:
* $output = array(
* '1000' => 'New',
* '1500' => 'New',
* '1750' => 'New',
* '3000' => 'Used',
* '4000' => 'Used',
* '5000' => 'Used',
* '6000' => 'Used'
* );
*
* @param array $array The source array.
*
* @return array The inverted array.
*/
public static function invert($array)
{
$return = array();
foreach ($array as $base => $values)
{
if (!is_array($values))
{
continue;
}
foreach ($values as $key)
{
// If the key isn't scalar then ignore it.
if (is_scalar($key))
{
$return[$key] = $base;
}
}
}
return $return;
}
/**
* Method to determine if an array is an associative array.
*
* @param array $array An array to test.
*
* @return boolean True if the array is an associative array.
*/
public static function isAssociative($array)
{
if (is_array($array))
{
foreach (array_keys($array) as $k => $v)
{
if ($k !== $v)
{
return true;
}
}
}
return false;
}
/**
* Pivots an array to create a reverse lookup of an array of scalars,
arrays or objects.
*
* @param array $source The source array.
* @param string $key Where the elements of the source array are
objects or arrays, the key to pivot on.
*
* @return array An array of arrays pivoted either on the value of the
keys, or an individual key of an object or array.
*/
public static function pivot($source, $key = null)
{
$result = array();
$counter = array();
foreach ($source as $index => $value)
{
// Determine the name of the pivot key, and its value.
if (is_array($value))
{
// If the key does not exist, ignore it.
if (!isset($value[$key]))
{
continue;
}
$resultKey = $value[$key];
$resultValue = &$source[$index];
}
elseif (is_object($value))
{
// If the key does not exist, ignore it.
if (!isset($value->$key))
{
continue;
}
$resultKey = $value->$key;
$resultValue = &$source[$index];
}
else
{
// Just a scalar value.
$resultKey = $value;
$resultValue = $index;
}
// The counter tracks how many times a key has been used.
if (empty($counter[$resultKey]))
{
// The first time around we just assign the value to the key.
$result[$resultKey] = $resultValue;
$counter[$resultKey] = 1;
}
elseif ($counter[$resultKey] == 1)
{
// If there is a second time, we convert the value into an array.
$result[$resultKey] = array(
$result[$resultKey],
$resultValue,
);
$counter[$resultKey]++;
}
else
{
// After the second time, no need to track any more. Just append to the
existing array.
$result[$resultKey][] = $resultValue;
}
}
unset($counter);
return $result;
}
/**
* Utility function to sort an array of objects on a given field
*
* @param array &$a An array of objects
* @param mixed $k The key (string) or a array of key to
sort on
* @param mixed $direction Direction (integer) or an array of
direction to sort in [1 = Ascending] [-1 = Descending]
* @param mixed $caseSensitive Boolean or array of booleans to let
sort occur case sensitive or insensitive
* @param mixed $locale Boolean or array of booleans to let
sort occur using the locale language or not
*
* @return array The sorted array of objects
*/
public static function sortObjects(&$a, $k, $direction = 1,
$caseSensitive = true, $locale = false)
{
if (!is_array($locale) || !is_array($locale[0]))
{
$locale = array($locale);
}
self::$sortCase = (array) $caseSensitive;
self::$sortDirection = (array) $direction;
self::$sortKey = (array) $k;
self::$sortLocale = $locale;
usort($a, array(__CLASS__, '_sortObjects'));
self::$sortCase = null;
self::$sortDirection = null;
self::$sortKey = null;
self::$sortLocale = null;
return $a;
}
/**
* Callback function for sorting an array of objects on a key
*
* @param array &$a An array of objects
* @param array &$b An array of objects
*
* @return integer Comparison status
*
* @see FOFUtilsArray::sortObjects()
*/
protected static function _sortObjects(&$a, &$b)
{
$key = self::$sortKey;
for ($i = 0, $count = count($key); $i < $count; $i++)
{
if (isset(self::$sortDirection[$i]))
{
$direction = self::$sortDirection[$i];
}
if (isset(self::$sortCase[$i]))
{
$caseSensitive = self::$sortCase[$i];
}
if (isset(self::$sortLocale[$i]))
{
$locale = self::$sortLocale[$i];
}
$va = $a->{$key[$i]};
$vb = $b->{$key[$i]};
if ((is_bool($va) || is_numeric($va)) && (is_bool($vb) ||
is_numeric($vb)))
{
$cmp = $va - $vb;
}
elseif ($caseSensitive)
{
$cmp = JString::strcmp($va, $vb, $locale);
}
else
{
$cmp = JString::strcasecmp($va, $vb, $locale);
}
if ($cmp > 0)
{
return $direction;
}
if ($cmp < 0)
{
return -$direction;
}
}
return 0;
}
/**
* Multidimensional array safe unique test
*
* @param array $myArray The array to make unique.
*
* @return array
*
* @see http://php.net/manual/en/function.array-unique.php
*/
public static function arrayUnique($myArray)
{
if (!is_array($myArray))
{
return $myArray;
}
foreach ($myArray as &$myvalue)
{
$myvalue = serialize($myvalue);
}
$myArray = array_unique($myArray);
foreach ($myArray as &$myvalue)
{
$myvalue = unserialize($myvalue);
}
return $myArray;
}
}
cache/cleaner.php000064400000004353151167373210007744 0ustar00<?php
/**
* @package FrameworkOnFramework
* @subpackage utils
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('FOF_INCLUDED') or die;
/**
* A utility class to help you quickly clean the Joomla! cache
*/
class FOFUtilsCacheCleaner
{
/**
* Clears the com_modules and com_plugins cache. You need to call this
whenever you alter the publish state or
* parameters of a module or plugin from your code.
*
* @return void
*/
public static function clearPluginsAndModulesCache()
{
self::clearPluginsCache();
self::clearModulesCache();
}
/**
* Clears the com_plugins cache. You need to call this whenever you alter
the publish state or parameters of a
* plugin from your code.
*
* @return void
*/
public static function clearPluginsCache()
{
self::clearCacheGroups(array('com_plugins'), array(0,1));
}
/**
* Clears the com_modules cache. You need to call this whenever you alter
the publish state or parameters of a
* module from your code.
*
* @return void
*/
public static function clearModulesCache()
{
self::clearCacheGroups(array('com_modules'), array(0,1));
}
/**
* Clears the specified cache groups.
*
* @param array $clearGroups Which cache groups to clear. Usually
this is com_yourcomponent to clear your
* component's cache.
* @param array $cacheClients Which cache clients to clear. 0 is the
back-end, 1 is the front-end. If you do not
* specify anything, both cache clients
will be cleared.
*
* @return void
*/
public static function clearCacheGroups(array $clearGroups, array
$cacheClients = array(0, 1))
{
$conf = JFactory::getConfig();
foreach ($clearGroups as $group)
{
foreach ($cacheClients as $client_id)
{
try
{
$options = array(
'defaultgroup' => $group,
'cachebase' => ($client_id) ? JPATH_ADMINISTRATOR .
'/cache' : $conf->get('cache_path', JPATH_SITE .
'/cache')
);
$cache = JCache::getInstance('callback', $options);
$cache->clean();
}
catch (Exception $e)
{
// suck it up
}
}
}
}
} config/helper.php000064400000005143151167373210010012 0ustar00<?php
/**
* @package FrameworkOnFramework
* @subpackage utils
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('FOF_INCLUDED') or die;
/**
* A utility class to help you fetch component parameters without going
through JComponentHelper
*/
class FOFUtilsConfigHelper
{
/**
* Caches the component parameters without going through JComponentHelper.
This is necessary since JComponentHelper
* cannot be reset or updated once you update parameters in the database.
*
* @var array
*/
private static $componentParams = array();
/**
* Loads the component's configuration parameters so they can be
accessed by getComponentConfigurationValue
*
* @param string $component The component for loading the parameters
* @param bool $force Should I force-reload the configuration
information?
*/
public final static function loadComponentConfig($component, $force =
false)
{
if (isset(self::$componentParams[$component]) &&
!is_null(self::$componentParams[$component]) && !$force)
{
return;
}
$db = FOFPlatform::getInstance()->getDbo();
$sql = $db->getQuery(true)
->select($db->qn('params'))
->from($db->qn('#__extensions'))
->where($db->qn('type') . ' = ' .
$db->q('component'))
->where($db->qn('element') . " = " .
$db->q($component));
$db->setQuery($sql);
$config_ini = $db->loadResult();
// OK, Joomla! 1.6 stores values JSON-encoded so, what do I do? Right!
$config_ini = trim($config_ini);
if ((substr($config_ini, 0, 1) == '{') &&
substr($config_ini, -1) == '}')
{
$config_ini = json_decode($config_ini, true);
}
else
{
$config_ini = FOFUtilsIniParser::parse_ini_file($config_ini, false,
true);
}
if (is_null($config_ini) || empty($config_ini))
{
$config_ini = array();
}
self::$componentParams[$component] = $config_ini;
}
/**
* Retrieves the value of a component configuration parameter without
going through JComponentHelper
*
* @param string $component The component for loading the parameter
value
* @param string $key The key to retrieve
* @param mixed $default The default value to use in case the key
is missing
*
* @return mixed
*/
public final static function getComponentConfigurationValue($component,
$key, $default = null)
{
self::loadComponentConfig($component, false);
if (array_key_exists($key, self::$componentParams[$component]))
{
return self::$componentParams[$component][$key];
}
else
{
return $default;
}
}
} filescheck/filescheck.php000064400000015641151167373210011472
0ustar00<?php
/**
* @package FrameworkOnFramework
* @subpackage utils
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('FOF_INCLUDED') or die;
/**
* A utility class to check that your extension's files are not
missing and have not been tampered with.
*
* You need a file called fileslist.php in your component's
administrator root directory with the following contents:
*
* $phpFileChecker = array(
* 'version' => 'revCEE2DAB',
* 'date' => '2014-10-16',
* 'directories' => array(
* 'administrator/components/com_foobar',
* ....
* ),
* 'files' => array(
* 'administrator/components/com_foobar/access.xml' =>
array('705', '09aa0351a316bf011ecc8c1145134761',
'b95f00c7b49a07a60570dc674f2497c45c4e7152'),
* ....
* )
* );
*
* All directory and file paths are relative to the site's root
*
* The directories array is a list of
*/
class FOFUtilsFilescheck
{
/** @var string The name of the component */
protected $option = '';
/** @var string Current component version */
protected $version = null;
/** @var string Current component release date */
protected $date = null;
/** @var array List of files to check as filepath => (filesize, md5,
sha1) */
protected $fileList = array();
/** @var array List of directories to check that exist */
protected $dirList = array();
/** @var bool Is the reported component version different than the version
of the #__extensions table? */
protected $wrongComponentVersion = false;
/** @var bool Is the fileslist.php reporting a version different than the
reported component version? */
protected $wrongFilesVersion = false;
/**
* Create and initialise the object
*
* @param string $option Component name, e.g. com_foobar
* @param string $version The current component version, as reported by
the component
* @param string $date The current component release date, as reported by
the component
*/
public function __construct($option, $version, $date)
{
// Initialise from parameters
$this->option = $option;
$this->version = $version;
$this->date = $date;
// Retrieve the date and version from the #__extensions table
$db = FOFPlatform::getInstance()->getDbo();
$query =
$db->getQuery(true)->select('*')->from($db->qn('#__extensions'))
->where($db->qn('element') . ' = ' .
$db->q($this->option))
->where($db->qn('type') . ' = ' .
$db->q('component'));
$extension = $db->setQuery($query)->loadObject();
// Check the version and date against those from #__extensions. I hate
heavily nested IFs as much as the next
// guy, but what can you do...
if (!is_null($extension))
{
$manifestCache = $extension->manifest_cache;
if (!empty($manifestCache))
{
$manifestCache = json_decode($manifestCache, true);
if (is_array($manifestCache) &&
isset($manifestCache['creationDate']) &&
isset($manifestCache['version']))
{
// Make sure the fileslist.php version and date match the
component's version
if ($this->version != $manifestCache['version'])
{
$this->wrongComponentVersion = true;
}
if ($this->date != $manifestCache['creationDate'])
{
$this->wrongComponentVersion = true;
}
}
}
}
// Try to load the fileslist.php file from the component's back-end
root
$filePath = JPATH_ADMINISTRATOR . '/components/' .
$this->option . '/fileslist.php';
if (!file_exists($filePath))
{
return;
}
include $filePath;
// Make sure the fileslist.php version and date match the
component's version
if ($this->version != $phpFileChecker['version'])
{
$this->wrongFilesVersion = true;
}
if ($this->date != $phpFileChecker['date'])
{
$this->wrongFilesVersion = true;
}
// Initialise the files and directories lists
$this->fileList = $phpFileChecker['files'];
$this->dirList = $phpFileChecker['directories'];
}
/**
* Is the reported component version different than the version of the
#__extensions table?
*
* @return boolean
*/
public function isWrongComponentVersion()
{
return $this->wrongComponentVersion;
}
/**
* Is the fileslist.php reporting a version different than the reported
component version?
*
* @return boolean
*/
public function isWrongFilesVersion()
{
return $this->wrongFilesVersion;
}
/**
* Performs a fast check of file and folders. If even one of the
files/folders doesn't exist, or even one file has
* the wrong file size it will return false.
*
* @return bool False when there are mismatched files and directories
*/
public function fastCheck()
{
// Check that all directories exist
foreach ($this->dirList as $directory)
{
$directory = JPATH_ROOT . '/' . $directory;
if (!@is_dir($directory))
{
return false;
}
}
// Check that all files exist and have the right size
foreach ($this->fileList as $filePath => $fileData)
{
$filePath = JPATH_ROOT . '/' . $filePath;
if (!@file_exists($filePath))
{
return false;
}
$fileSize = @filesize($filePath);
if ($fileSize != $fileData[0])
{
return false;
}
}
return true;
}
/**
* Performs a slow, thorough check of all files and folders (including
MD5/SHA1 sum checks)
*
* @param int $idx The index from where to start
*
* @return array Progress report
*/
public function slowCheck($idx = 0)
{
$ret = array(
'done' => false,
'files' => array(),
'folders' => array(),
'idx' => $idx
);
$totalFiles = count($this->fileList);
$totalFolders = count($this->dirList);
$fileKeys = array_keys($this->fileList);
$timer = new FOFUtilsTimer(3.0, 75.0);
while ($timer->getTimeLeft() && (($idx < $totalFiles) ||
($idx < $totalFolders)))
{
if ($idx < $totalFolders)
{
$directory = JPATH_ROOT . '/' . $this->dirList[$idx];
if (!@is_dir($directory))
{
$ret['folders'][] = $directory;
}
}
if ($idx < $totalFiles)
{
$fileKey = $fileKeys[$idx];
$filePath = JPATH_ROOT . '/' . $fileKey;
$fileData = $this->fileList[$fileKey];
if (!@file_exists($filePath))
{
$ret['files'][] = $fileKey . ' (missing)';
}
elseif (@filesize($filePath) != $fileData[0])
{
$ret['files'][] = $fileKey . ' (size ' .
@filesize($filePath) . ' ≠ ' . $fileData[0] . ')';
}
else
{
if (function_exists('sha1_file'))
{
$fileSha1 = @sha1_file($filePath);
if ($fileSha1 != $fileData[2])
{
$ret['files'][] = $fileKey . ' (SHA1 ' .
$fileSha1 . ' ≠ ' . $fileData[2] . ')';
}
}
elseif (function_exists('md5_file'))
{
$fileMd5 = @md5_file($filePath);
if ($fileMd5 != $fileData[1])
{
$ret['files'][] = $fileKey . ' (MD5 ' . $fileMd5
. ' ≠ ' . $fileData[1] . ')';
}
}
}
}
$idx++;
}
if (($idx >= $totalFiles) && ($idx >= $totalFolders))
{
$ret['done'] = true;
}
$ret['idx'] = $idx;
return $ret;
}
}ini/parser.php000064400000011061151167373210007335 0ustar00<?php
/**
* @package FrameworkOnFramework
* @subpackage utils
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*/
defined('FOF_INCLUDED') or die;
/**
* A utility class to parse INI files. This monstrosity is only required
because some impossibly misguided individuals
* who misrepresent themselves as hosts have disabled PHP's
parse_ini_file() function for "security reasons". Apparently
* their blatant ignorance doesn't allow them to discern between the
innocuous parse_ini_file and the _potentially_
* dangerous ini_set functions, leading them to disable the former and let
the latter enabled. In other words, THIS
* CLASS IS HERE TO FIX STUPID.
*/
class FOFUtilsIniParser
{
/**
* Parse an INI file and return an associative array.
*
* @param string $file The file to process
* @param bool $process_sections True to also process INI sections
*
* @return array An associative array of sections, keys and values
*/
public static function parse_ini_file($file, $process_sections, $rawdata =
false)
{
$isMoronHostFile = !function_exists('parse_ini_file');
$isMoronHostString = !function_exists('parse_ini_string');
if ($rawdata)
{
if ($isMoronHostString)
{
return self::parse_ini_file_php($file, $process_sections, $rawdata);
}
else
{
return parse_ini_string($file, $process_sections);
}
}
else
{
if ($isMoronHostFile)
{
return self::parse_ini_file_php($file, $process_sections);
}
else
{
return parse_ini_file($file, $process_sections);
}
}
}
/**
* A PHP based INI file parser.
*
* Thanks to asohn ~at~ aircanopy ~dot~ net for posting this handy
function on
* the parse_ini_file page on http://gr.php.net/parse_ini_file
*
* @param string $file Filename to process
* @param bool $process_sections True to also process INI sections
* @param bool $rawdata If true, the $file contains raw INI
data, not a filename
*
* @return array An associative array of sections, keys and values
*/
static function parse_ini_file_php($file, $process_sections = false,
$rawdata = false)
{
$process_sections = ($process_sections !== true) ? false : true;
if (!$rawdata)
{
$ini = file($file);
}
else
{
$file = str_replace("\r", "", $file);
$ini = explode("\n", $file);
}
if (count($ini) == 0)
{
return array();
}
$sections = array();
$values = array();
$result = array();
$globals = array();
$i = 0;
foreach ($ini as $line)
{
$line = trim($line);
$line = str_replace("\t", " ", $line);
// Comments
if (!preg_match('/^[a-zA-Z0-9[]/', $line))
{
continue;
}
// Sections
if ($line[0] == '[')
{
$tmp = explode(']', $line);
$sections[] = trim(substr($tmp[0], 1));
$i++;
continue;
}
// Key-value pair
$lineParts = explode('=', $line, 2);
if (count($lineParts) != 2)
{
continue;
}
$key = trim($lineParts[0]);
$value = trim($lineParts[1]);
unset($lineParts);
if (strstr($value, ";"))
{
$tmp = explode(';', $value);
if (count($tmp) == 2)
{
if ((($value[0] != '"') && ($value[0] !=
"'")) ||
preg_match('/^".*"\s*;/', $value) ||
preg_match('/^".*;[^"]*$/', $value) ||
preg_match("/^'.*'\s*;/", $value) ||
preg_match("/^'.*;[^']*$/", $value)
)
{
$value = $tmp[0];
}
}
else
{
if ($value[0] == '"')
{
$value = preg_replace('/^"(.*)".*/',
'$1', $value);
}
elseif ($value[0] == "'")
{
$value = preg_replace("/^'(.*)'.*/",
'$1', $value);
}
else
{
$value = $tmp[0];
}
}
}
$value = trim($value);
$value = trim($value, "'\"");
if ($i == 0)
{
if (substr($line, -1, 2) == '[]')
{
$globals[$key][] = $value;
}
else
{
$globals[$key] = $value;
}
}
else
{
if (substr($line, -1, 2) == '[]')
{
$values[$i - 1][$key][] = $value;
}
else
{
$values[$i - 1][$key] = $value;
}
}
}
for ($j = 0; $j < $i; $j++)
{
if ($process_sections === true)
{
if (isset($sections[$j]) && isset($values[$j]))
{
$result[$sections[$j]] = $values[$j];
}
}
else
{
if (isset($values[$j]))
{
$result[] = $values[$j];
}
}
}
return $result + $globals;
}
} installscript/installscript.php000064400000200207151167373210013052
0ustar00<?php
/**
* @package FrameworkOnFramework
* @subpackage utils
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no longer
reflects the original work of its author.
*/
defined('FOF_INCLUDED') or die;
JLoader::import('joomla.filesystem.folder');
JLoader::import('joomla.filesystem.file');
JLoader::import('joomla.installer.installer');
JLoader::import('joomla.utilities.date');
/**
* A helper class which you can use to create component installation
scripts
*/
abstract class FOFUtilsInstallscript
{
/**
* The component's name
*
* @var string
*/
protected $componentName = 'com_foobar';
/**
* The title of the component (printed on installation and uninstallation
messages)
*
* @var string
*/
protected $componentTitle = 'Foobar Component';
/**
* The list of extra modules and plugins to install on component
installation / update and remove on component
* uninstallation.
*
* @var array
*/
protected $installation_queue = array(
// modules => { (folder) => { (module) => { (position),
(published) } }* }*
'modules' => array(
'admin' => array(),
'site' => array()
),
// plugins => { (folder) => { (element) => (published) }* }*
'plugins' => array(
'system' => array(),
)
);
/**
* The list of obsolete extra modules and plugins to uninstall on
component upgrade / installation.
*
* @var array
*/
protected $uninstallation_queue = array(
// modules => { (folder) => { (module) }* }*
'modules' => array(
'admin' => array(),
'site' => array()
),
// plugins => { (folder) => { (element) }* }*
'plugins' => array(
'system' => array(),
)
);
/**
* Obsolete files and folders to remove from the free version only. This
is used when you move a feature from the
* free version of your extension to its paid version. If you don't
have such a distinction you can ignore this.
*
* @var array
*/
protected $removeFilesFree = array(
'files' => array(
// Use pathnames relative to your site's root, e.g.
// 'administrator/components/com_foobar/helpers/whatever.php'
),
'folders' => array(
// Use pathnames relative to your site's root, e.g.
// 'administrator/components/com_foobar/baz'
)
);
/**
* Obsolete files and folders to remove from both paid and free releases.
This is used when you refactor code and
* some files inevitably become obsolete and need to be removed.
*
* @var array
*/
protected $removeFilesAllVersions = array(
'files' => array(
// Use pathnames relative to your site's root, e.g.
// 'administrator/components/com_foobar/helpers/whatever.php'
),
'folders' => array(
// Use pathnames relative to your site's root, e.g.
// 'administrator/components/com_foobar/baz'
)
);
/**
* A list of scripts to be copied to the "cli" directory of the
site
*
* @var array
*/
protected $cliScriptFiles = array(
// Use just the filename, e.g.
// 'my-cron-script.php'
);
/**
* The path inside your package where cli scripts are stored
*
* @var string
*/
protected $cliSourcePath = 'cli';
/**
* The path inside your package where FOF is stored
*
* @var string
*/
protected $fofSourcePath = 'fof';
/**
* The path inside your package where Akeeba Strapper is stored
*
* @var string
*/
protected $strapperSourcePath = 'strapper';
/**
* The path inside your package where extra modules are stored
*
* @var string
*/
protected $modulesSourcePath = 'modules';
/**
* The path inside your package where extra plugins are stored
*
* @var string
*/
protected $pluginsSourcePath = 'plugins';
/**
* Is the schemaXmlPath class variable a relative path? If set to true the
schemaXmlPath variable contains a path
* relative to the component's back-end directory. If set to false
the schemaXmlPath variable contains an absolute
* filesystem path.
*
* @var boolean
*/
protected $schemaXmlPathRelative = true;
/**
* The path where the schema XML files are stored. Its contents depend on
the schemaXmlPathRelative variable above
* true => schemaXmlPath contains a path relative to the
component's back-end directory
* false => schemaXmlPath contains an absolute filesystem path
*
* @var string
*/
protected $schemaXmlPath = 'sql/xml';
/**
* The minimum PHP version required to install this extension
*
* @var string
*/
protected $minimumPHPVersion = '5.3.3';
/**
* The minimum Joomla! version required to install this extension
*
* @var string
*/
protected $minimumJoomlaVersion = '2.5.6';
/**
* The maximum Joomla! version this extension can be installed on
*
* @var string
*/
protected $maximumJoomlaVersion = '3.9.99';
/**
* Is this the paid version of the extension? This only determines which
files / extensions will be removed.
*
* @var boolean
*/
protected $isPaid = false;
/**
* 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 FOFUtilsInstallscript::addPostInstallationMessage
*
* @var array
*/
protected $postInstallationMessages = array();
/**
* Joomla! pre-flight event. This runs before Joomla! installs or updates
the component. This is our last chance to
* tell Joomla! if it should abort the installation.
*
* @param string $type Installation type (install, update,
discover_install)
* @param JInstaller $parent Parent object
*
* @return boolean True to let the installation proceed, false to halt
the installation
*/
public function preflight($type, $parent)
{
// Check the minimum PHP version
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 component</p>";
if (version_compare(JVERSION, '3.0', 'gt'))
{
JLog::add($msg, JLog::WARNING, 'jerror');
}
else
{
JError::raiseWarning(100, $msg);
}
return false;
}
}
// Check the minimum Joomla! version
if (!empty($this->minimumJoomlaVersion) &&
!version_compare(JVERSION, $this->minimumJoomlaVersion, 'ge'))
{
$msg = "<p>You need Joomla! $this->minimumJoomlaVersion or
later to install this component</p>";
if (version_compare(JVERSION, '3.0', 'gt'))
{
JLog::add($msg, JLog::WARNING, 'jerror');
}
else
{
JError::raiseWarning(100, $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 component</p>";
if (version_compare(JVERSION, '3.0', 'gt'))
{
JLog::add($msg, JLog::WARNING, 'jerror');
}
else
{
JError::raiseWarning(100, $msg);
}
return false;
}
// 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();
}
// Workarounds for JInstaller issues
if (in_array($type, array('install',
'discover_install')))
{
// Bugfix for "Database function returned no error"
$this->bugfixDBFunctionReturnedNoError();
}
else
{
// Bugfix for "Can not build admin menus"
$this->bugfixCantBuildAdminMenus();
}
return true;
}
/**
* Runs after install, update or discover_update. In other words, it
executes after Joomla! has finished installing
* or updating your component. This is the last chance you've got to
perform any additional installations, clean-up,
* database updates and similar housekeeping functions.
*
* @param string $type install, update or discover_update
* @param JInstaller $parent Parent object
*/
public function postflight($type, $parent)
{
// Install or update database
$dbInstaller = new FOFDatabaseInstaller(array(
'dbinstaller_directory' =>
($this->schemaXmlPathRelative ? JPATH_ADMINISTRATOR .
'/components/' . $this->componentName : '') .
'/' .
$this->schemaXmlPath
));
$dbInstaller->updateSchema();
// Install subextensions
$status = $this->installSubextensions($parent);
// Install FOF
$fofInstallationStatus = $this->installFOF($parent);
// Install Akeeba Straper
$strapperInstallationStatus = $this->installStrapper($parent);
// Make sure menu items are installed
$this->_createAdminMenus($parent);
// Make sure menu items are published (surprise goal in the 92' by
JInstaller wins the cup for "most screwed up
// bug in the history of Joomla!")
$this->_reallyPublishAdminMenuItems($parent);
// Which files should I remove?
if ($this->isPaid)
{
// This is the paid version, only remove the removeFilesAllVersions
files
$removeFiles = $this->removeFilesAllVersions;
}
else
{
// This is the free version, remove the removeFilesAllVersions and
removeFilesFree files
$removeFiles = array('files' => array(),
'folders' => array());
if (isset($this->removeFilesAllVersions['files']))
{
if (isset($this->removeFilesFree['files']))
{
$removeFiles['files'] =
array_merge($this->removeFilesAllVersions['files'],
$this->removeFilesFree['files']);
}
else
{
$removeFiles['files'] =
$this->removeFilesAllVersions['files'];
}
}
elseif (isset($this->removeFilesFree['files']))
{
$removeFiles['files'] =
$this->removeFilesFree['files'];
}
if (isset($this->removeFilesAllVersions['folders']))
{
if (isset($this->removeFilesFree['folders']))
{
$removeFiles['folders'] =
array_merge($this->removeFilesAllVersions['folders'],
$this->removeFilesFree['folders']);
}
else
{
$removeFiles['folders'] =
$this->removeFilesAllVersions['folders'];
}
}
elseif (isset($this->removeFilesFree['folders']))
{
$removeFiles['folders'] =
$this->removeFilesFree['folders'];
}
}
// Remove obsolete files and folders
$this->removeFilesAndFolders($removeFiles);
// Copy the CLI files (if any)
$this->copyCliFiles($parent);
// Show the post-installation page
$this->renderPostInstallation($status, $fofInstallationStatus,
$strapperInstallationStatus, $parent);
// Uninstall obsolete subextensions
$uninstall_status = $this->uninstallObsoleteSubextensions($parent);
// Clear the FOF cache
$platform = FOFPlatform::getInstance();
if (method_exists($platform, 'clearCache'))
{
FOFPlatform::getInstance()->clearCache();
}
// Make sure the Joomla! menu structure is correct
$this->_rebuildMenu();
// Add post-installation messages on Joomla! 3.2 and later
$this->_applyPostInstallationMessages();
}
/**
* Runs on uninstallation
*
* @param JInstaller $parent The parent object
*/
public function uninstall($parent)
{
// Uninstall database
$dbInstaller = new FOFDatabaseInstaller(array(
'dbinstaller_directory' =>
($this->schemaXmlPathRelative ? JPATH_ADMINISTRATOR .
'/components/' . $this->componentName : '') .
'/' .
$this->schemaXmlPath
));
$dbInstaller->removeSchema();
// Uninstall modules and plugins
$status = $this->uninstallSubextensions($parent);
// Uninstall post-installation messages on Joomla! 3.2 and later
$this->uninstallPostInstallationMessages();
// Show the post-uninstallation page
$this->renderPostUninstallation($status, $parent);
}
/**
* Copies the CLI scripts into Joomla!'s cli directory
*
* @param JInstaller $parent
*/
protected function copyCliFiles($parent)
{
$src = $parent->getParent()->getPath('source');
$cliPath = JPATH_ROOT . '/cli';
if (!JFolder::exists($cliPath))
{
JFolder::create($cliPath);
}
foreach ($this->cliScriptFiles as $script)
{
if (JFile::exists($cliPath . '/' . $script))
{
JFile::delete($cliPath . '/' . $script);
}
if (JFile::exists($src . '/' . $this->cliSourcePath .
'/' . $script))
{
JFile::copy($src . '/' . $this->cliSourcePath .
'/' . $script, $cliPath . '/' . $script);
}
}
}
/**
* Renders the message after installing or upgrading the component
*/
protected function renderPostInstallation($status, $fofInstallationStatus,
$strapperInstallationStatus, $parent)
{
$rows = 0;
?>
<table class="adminlist table table-striped"
width="100%">
<thead>
<tr>
<th class="title"
colspan="2">Extension</th>
<th width="30%">Status</th>
</tr>
</thead>
<tfoot>
<tr>
<td colspan="3"></td>
</tr>
</tfoot>
<tbody>
<tr class="row<?php echo($rows++ % 2); ?>">
<td class="key" colspan="2"><?php echo
$this->componentTitle ?></td>
<td><strong style="color:
green">Installed</strong></td>
</tr>
<?php if ($fofInstallationStatus['required']): ?>
<tr class="row<?php echo($rows++ % 2); ?>">
<td class="key" colspan="2">
<strong>Framework on Framework (FOF) <?php echo
$fofInstallationStatus['version'] ?></strong>
[<?php echo $fofInstallationStatus['date'] ?>]
</td>
<td><strong>
<span
style="color: <?php echo
$fofInstallationStatus['required'] ?
($fofInstallationStatus['installed'] ? 'green' :
'red') : '#660' ?>; font-weight: bold;">
<?php echo $fofInstallationStatus['required'] ?
($fofInstallationStatus['installed'] ? 'Installed' :
'Not Installed') : 'Already up-to-date'; ?>
</span>
</strong></td>
</tr>
<?php endif; ?>
<?php if ($strapperInstallationStatus['required']): ?>
<tr class="row<?php echo($rows++ % 2); ?>">
<td class="key" colspan="2">
<strong>Akeeba Strapper <?php echo
$strapperInstallationStatus['version'] ?></strong>
[<?php echo $strapperInstallationStatus['date'] ?>]
</td>
<td><strong>
<span
style="color: <?php echo
$strapperInstallationStatus['required'] ?
($strapperInstallationStatus['installed'] ? 'green' :
'red') : '#660' ?>; font-weight: bold;">
<?php echo $strapperInstallationStatus['required'] ?
($strapperInstallationStatus['installed'] ? 'Installed'
: 'Not Installed') : 'Already up-to-date'; ?>
</span>
</strong></td>
</tr>
<?php endif; ?>
<?php if (count($status->modules)) : ?>
<tr>
<th>Module</th>
<th>Client</th>
<th></th>
</tr>
<?php foreach ($status->modules as $module) : ?>
<tr class="row<?php echo($rows++ % 2); ?>">
<td class="key"><?php echo
$module['name']; ?></td>
<td class="key"><?php echo
ucfirst($module['client']); ?></td>
<td><strong
style="color: <?php echo ($module['result']) ?
"green" : "red" ?>"><?php echo
($module['result']) ? 'Installed' : 'Not
installed'; ?></strong>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
<?php if (count($status->plugins)) : ?>
<tr>
<th>Plugin</th>
<th>Group</th>
<th></th>
</tr>
<?php foreach ($status->plugins as $plugin) : ?>
<tr class="row<?php echo($rows++ % 2); ?>">
<td class="key"><?php echo
ucfirst($plugin['name']); ?></td>
<td class="key"><?php echo
ucfirst($plugin['group']); ?></td>
<td><strong
style="color: <?php echo ($plugin['result']) ?
"green" : "red" ?>"><?php echo
($plugin['result']) ? 'Installed' : 'Not
installed'; ?></strong>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
<?php
}
/**
* Renders the message after uninstalling the component
*/
protected function renderPostUninstallation($status, $parent)
{
$rows = 1;
?>
<table class="adminlist table table-striped"
width="100%">
<thead>
<tr>
<th class="title" colspan="2"><?php echo
JText::_('Extension'); ?></th>
<th width="30%"><?php echo
JText::_('Status'); ?></th>
</tr>
</thead>
<tfoot>
<tr>
<td colspan="3"></td>
</tr>
</tfoot>
<tbody>
<tr class="row<?php echo($rows++ % 2); ?>">
<td class="key" colspan="2"><?php echo
$this->componentTitle; ?></td>
<td><strong style="color:
green">Removed</strong></td>
</tr>
<?php if (count($status->modules)) : ?>
<tr>
<th>Module</th>
<th>Client</th>
<th></th>
</tr>
<?php foreach ($status->modules as $module) : ?>
<tr class="row<?php echo($rows++ % 2); ?>">
<td class="key"><?php echo
$module['name']; ?></td>
<td class="key"><?php echo
ucfirst($module['client']); ?></td>
<td><strong
style="color: <?php echo ($module['result']) ?
"green" : "red" ?>"><?php echo
($module['result']) ? 'Removed' : 'Not
removed'; ?></strong>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
<?php if (count($status->plugins)) : ?>
<tr>
<th>Plugin</th>
<th>Group</th>
<th></th>
</tr>
<?php foreach ($status->plugins as $plugin) : ?>
<tr class="row<?php echo($rows++ % 2); ?>">
<td class="key"><?php echo
ucfirst($plugin['name']); ?></td>
<td class="key"><?php echo
ucfirst($plugin['group']); ?></td>
<td><strong
style="color: <?php echo ($plugin['result']) ?
"green" : "red" ?>"><?php echo
($plugin['result']) ? 'Removed' : 'Not
removed'; ?></strong>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
<?php
}
/**
* Bugfix for "DB function returned no error"
*/
protected function bugfixDBFunctionReturnedNoError()
{
$db = FOFPlatform::getInstance()->getDbo();
// Fix broken #__assets records
$query = $db->getQuery(true);
$query->select('id')
->from('#__assets')
->where($db->qn('name') . ' = ' .
$db->q($this->componentName));
$db->setQuery($query);
try
{
$ids = $db->loadColumn();
}
catch (Exception $exc)
{
return;
}
if (!empty($ids))
{
foreach ($ids as $id)
{
$query = $db->getQuery(true);
$query->delete('#__assets')
->where($db->qn('id') . ' = ' .
$db->q($id));
$db->setQuery($query);
try
{
$db->execute();
}
catch (Exception $exc)
{
// Nothing
}
}
}
// Fix broken #__extensions records
$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);
$ids = $db->loadColumn();
if (!empty($ids))
{
foreach ($ids as $id)
{
$query = $db->getQuery(true);
$query->delete('#__extensions')
->where($db->qn('extension_id') . ' = ' .
$db->q($id));
$db->setQuery($query);
try
{
$db->execute();
}
catch (Exception $exc)
{
// Nothing
}
}
}
// Fix broken #__menu records
$query = $db->getQuery(true);
$query->select('id')
->from('#__menu')
->where($db->qn('type') . ' = ' .
$db->q('component'))
->where($db->qn('menutype') . ' = ' .
$db->q('main'))
->where($db->qn('link') . ' LIKE ' .
$db->q('index.php?option=' . $this->componentName));
$db->setQuery($query);
$ids = $db->loadColumn();
if (!empty($ids))
{
foreach ($ids as $id)
{
$query = $db->getQuery(true);
$query->delete('#__menu')
->where($db->qn('id') . ' = ' .
$db->q($id));
$db->setQuery($query);
try
{
$db->execute();
}
catch (Exception $exc)
{
// Nothing
}
}
}
}
/**
* Joomla! 1.6+ bugfix for "Can not build admin menus"
*/
protected function bugfixCantBuildAdminMenus()
{
$db = FOFPlatform::getInstance()->getDbo();
// If there are multiple #__extensions record, keep one of them
$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 (count($ids) > 1)
{
asort($ids);
$extension_id = array_shift($ids); // Keep the oldest id
foreach ($ids as $id)
{
$query = $db->getQuery(true);
$query->delete('#__extensions')
->where($db->qn('extension_id') . ' = ' .
$db->q($id));
$db->setQuery($query);
try
{
$db->execute();
}
catch (Exception $exc)
{
// Nothing
}
}
}
// If there are multiple assets records, delete all except the oldest one
$query = $db->getQuery(true);
$query->select('id')
->from('#__assets')
->where($db->qn('name') . ' = ' .
$db->q($this->componentName));
$db->setQuery($query);
$ids = $db->loadObjectList();
if (count($ids) > 1)
{
asort($ids);
$asset_id = array_shift($ids); // Keep the oldest id
foreach ($ids as $id)
{
$query = $db->getQuery(true);
$query->delete('#__assets')
->where($db->qn('id') . ' = ' .
$db->q($id));
$db->setQuery($query);
try
{
$db->execute();
}
catch (Exception $exc)
{
// Nothing
}
}
}
// Remove #__menu records for good measure! –– I think this is not
necessary and causes the menu item to
// disappear on extension update.
/**
$query = $db->getQuery(true);
$query->select('id')
->from('#__menu')
->where($db->qn('type') . ' = ' .
$db->q('component'))
->where($db->qn('menutype') . ' = ' .
$db->q('main'))
->where($db->qn('link') . ' LIKE ' .
$db->q('index.php?option=' . $this->componentName));
$db->setQuery($query);
try
{
$ids1 = $db->loadColumn();
}
catch (Exception $exc)
{
$ids1 = array();
}
if (empty($ids1))
{
$ids1 = array();
}
$query = $db->getQuery(true);
$query->select('id')
->from('#__menu')
->where($db->qn('type') . ' = ' .
$db->q('component'))
->where($db->qn('menutype') . ' = ' .
$db->q('main'))
->where($db->qn('link') . ' LIKE ' .
$db->q('index.php?option=' . $this->componentName .
'&%'));
$db->setQuery($query);
try
{
$ids2 = $db->loadColumn();
}
catch (Exception $exc)
{
$ids2 = array();
}
if (empty($ids2))
{
$ids2 = array();
}
$ids = array_merge($ids1, $ids2);
if (!empty($ids))
{
foreach ($ids as $id)
{
$query = $db->getQuery(true);
$query->delete('#__menu')
->where($db->qn('id') . ' = ' .
$db->q($id));
$db->setQuery($query);
try
{
$db->execute();
}
catch (Exception $exc)
{
// Nothing
}
}
}
/**/
}
/**
* Installs subextensions (modules, plugins) bundled with the main
extension
*
* @param JInstaller $parent
*
* @return JObject The subextension installation status
*/
protected function installSubextensions($parent)
{
$src = $parent->getParent()->getPath('source');
$db = FOFPlatform::getInstance()->getDbo();;
$status = new JObject();
$status->modules = array();
$status->plugins = array();
// Modules installation
if (isset($this->installation_queue['modules']) &&
count($this->installation_queue['modules']))
{
foreach ($this->installation_queue['modules'] as $folder
=> $modules)
{
if (count($modules))
{
foreach ($modules as $module => $modulePreferences)
{
// Install the module
if (empty($folder))
{
$folder = 'site';
}
$path = "$src/" . $this->modulesSourcePath .
"/$folder/$module";
if (!is_dir($path))
{
$path = "$src/" . $this->modulesSourcePath .
"/$folder/mod_$module";
}
if (!is_dir($path))
{
$path = "$src/" . $this->modulesSourcePath .
"/$module";
}
if (!is_dir($path))
{
$path = "$src/" . $this->modulesSourcePath .
"/mod_$module";
}
if (!is_dir($path))
{
continue;
}
// Was the module already installed?
$sql = $db->getQuery(true)
->select('COUNT(*)')
->from('#__modules')
->where($db->qn('module') . ' = ' .
$db->q('mod_' . $module));
$db->setQuery($sql);
try
{
$count = $db->loadResult();
}
catch (Exception $exc)
{
$count = 0;
}
$installer = new JInstaller;
$result = $installer->install($path);
$status->modules[] = array(
'name' => 'mod_' . $module,
'client' => $folder,
'result' => $result
);
// Modify where it's published and its published state
if (!$count)
{
// A. Position and state
list($modulePosition, $modulePublished) = $modulePreferences;
$sql = $db->getQuery(true)
->update($db->qn('#__modules'))
->set($db->qn('position') . ' = ' .
$db->q($modulePosition))
->where($db->qn('module') . ' = ' .
$db->q('mod_' . $module));
if ($modulePublished)
{
$sql->set($db->qn('published') . ' = ' .
$db->q('1'));
}
$db->setQuery($sql);
try
{
$db->execute();
}
catch (Exception $exc)
{
// Nothing
}
// B. Change the ordering of back-end modules to 1 + max ordering
if ($folder == 'admin')
{
try
{
$query = $db->getQuery(true);
$query->select('MAX(' .
$db->qn('ordering') . ')')
->from($db->qn('#__modules'))
->where($db->qn('position') . '=' .
$db->q($modulePosition));
$db->setQuery($query);
$position = $db->loadResult();
$position++;
$query = $db->getQuery(true);
$query->update($db->qn('#__modules'))
->set($db->qn('ordering') . ' = ' .
$db->q($position))
->where($db->qn('module') . ' = ' .
$db->q('mod_' . $module));
$db->setQuery($query);
$db->execute();
}
catch (Exception $exc)
{
// Nothing
}
}
// C. Link to all pages
try
{
$query = $db->getQuery(true);
$query->select('id')->from($db->qn('#__modules'))
->where($db->qn('module') . ' = ' .
$db->q('mod_' . $module));
$db->setQuery($query);
$moduleid = $db->loadResult();
$query = $db->getQuery(true);
$query->select('*')->from($db->qn('#__modules_menu'))
->where($db->qn('moduleid') . ' = ' .
$db->q($moduleid));
$db->setQuery($query);
$assignments = $db->loadObjectList();
$isAssigned = !empty($assignments);
if (!$isAssigned)
{
$o = (object)array(
'moduleid' => $moduleid,
'menuid' => 0
);
$db->insertObject('#__modules_menu', $o);
}
}
catch (Exception $exc)
{
// Nothing
}
}
}
}
}
}
// Plugins installation
if (isset($this->installation_queue['plugins']) &&
count($this->installation_queue['plugins']))
{
foreach ($this->installation_queue['plugins'] as $folder
=> $plugins)
{
if (count($plugins))
{
foreach ($plugins as $plugin => $published)
{
$path = "$src/" . $this->pluginsSourcePath .
"/$folder/$plugin";
if (!is_dir($path))
{
$path = "$src/" . $this->pluginsSourcePath .
"/$folder/plg_$plugin";
}
if (!is_dir($path))
{
$path = "$src/" . $this->pluginsSourcePath .
"/$plugin";
}
if (!is_dir($path))
{
$path = "$src/" . $this->pluginsSourcePath .
"/plg_$plugin";
}
if (!is_dir($path))
{
continue;
}
// Was the plugin already installed?
$query = $db->getQuery(true)
->select('COUNT(*)')
->from($db->qn('#__extensions'))
->where($db->qn('element') . ' = ' .
$db->q($plugin))
->where($db->qn('folder') . ' = ' .
$db->q($folder));
$db->setQuery($query);
try
{
$count = $db->loadResult();
}
catch (Exception $exc)
{
$count = 0;
}
$installer = new JInstaller;
$result = $installer->install($path);
$status->plugins[] = array('name' => 'plg_'
. $plugin, 'group' => $folder, 'result' =>
$result);
if ($published && !$count)
{
$query = $db->getQuery(true)
->update($db->qn('#__extensions'))
->set($db->qn('enabled') . ' = ' .
$db->q('1'))
->where($db->qn('element') . ' = ' .
$db->q($plugin))
->where($db->qn('folder') . ' = ' .
$db->q($folder));
$db->setQuery($query);
try
{
$db->execute();
}
catch (Exception $exc)
{
// Nothing
}
}
}
}
}
}
// Clear com_modules and com_plugins cache (needed when we alter
module/plugin state)
FOFUtilsCacheCleaner::clearPluginsAndModulesCache();
return $status;
}
/**
* Uninstalls subextensions (modules, plugins) bundled with the main
extension
*
* @param JInstaller $parent The parent object
*
* @return stdClass The subextension uninstallation status
*/
protected function uninstallSubextensions($parent)
{
$db = FOFPlatform::getInstance()->getDbo();
$status = new stdClass();
$status->modules = array();
$status->plugins = array();
$src = $parent->getParent()->getPath('source');
// Modules uninstallation
if (isset($this->installation_queue['modules']) &&
count($this->installation_queue['modules']))
{
foreach ($this->installation_queue['modules'] as $folder
=> $modules)
{
if (count($modules))
{
foreach ($modules as $module => $modulePreferences)
{
// Find the module ID
$sql = $db->getQuery(true)
->select($db->qn('extension_id'))
->from($db->qn('#__extensions'))
->where($db->qn('element') . ' = ' .
$db->q('mod_' . $module))
->where($db->qn('type') . ' = ' .
$db->q('module'));
$db->setQuery($sql);
try
{
$id = $db->loadResult();
}
catch (Exception $exc)
{
$id = 0;
}
// Uninstall the module
if ($id)
{
$installer = new JInstaller;
$result = $installer->uninstall('module', $id, 1);
$status->modules[] = array(
'name' => 'mod_' . $module,
'client' => $folder,
'result' => $result
);
}
}
}
}
}
// Plugins uninstallation
if (isset($this->installation_queue['plugins']) &&
count($this->installation_queue['plugins']))
{
foreach ($this->installation_queue['plugins'] as $folder
=> $plugins)
{
if (count($plugins))
{
foreach ($plugins as $plugin => $published)
{
$sql = $db->getQuery(true)
->select($db->qn('extension_id'))
->from($db->qn('#__extensions'))
->where($db->qn('type') . ' = ' .
$db->q('plugin'))
->where($db->qn('element') . ' = ' .
$db->q($plugin))
->where($db->qn('folder') . ' = ' .
$db->q($folder));
$db->setQuery($sql);
try
{
$id = $db->loadResult();
}
catch (Exception $exc)
{
$id = 0;
}
if ($id)
{
$installer = new JInstaller;
$result = $installer->uninstall('plugin', $id, 1);
$status->plugins[] = array(
'name' => 'plg_' . $plugin,
'group' => $folder,
'result' => $result
);
}
}
}
}
}
// Clear com_modules and com_plugins cache (needed when we alter
module/plugin state)
FOFUtilsCacheCleaner::clearPluginsAndModulesCache();
return $status;
}
/**
* Removes obsolete files and folders
*
* @param array $removeList The files and directories to remove
*/
protected function removeFilesAndFolders($removeList)
{
// Remove files
if (isset($removeList['files']) &&
!empty($removeList['files']))
{
foreach ($removeList['files'] as $file)
{
$f = JPATH_ROOT . '/' . $file;
if (!JFile::exists($f))
{
continue;
}
JFile::delete($f);
}
}
// Remove folders
if (isset($removeList['folders']) &&
!empty($removeList['folders']))
{
foreach ($removeList['folders'] as $folder)
{
$f = JPATH_ROOT . '/' . $folder;
if (!JFolder::exists($f))
{
continue;
}
JFolder::delete($f);
}
}
}
/**
* Installs FOF if necessary
*
* @param JInstaller $parent The parent object
*
* @return array The installation status
*/
protected function installFOF($parent)
{
// Get the source path
$src = $parent->getParent()->getPath('source');
$source = $src . '/' . $this->fofSourcePath;
if (!JFolder::exists($source))
{
return array(
'required' => false,
'installed' => false,
'version' => '0.0.0',
'date' => '2011-01-01',
);
}
// Get the target path
if (!defined('JPATH_LIBRARIES'))
{
$target = JPATH_ROOT . '/libraries/f0f';
}
else
{
$target = JPATH_LIBRARIES . '/f0f';
}
// Do I have to install FOF?
$haveToInstallFOF = false;
if (!JFolder::exists($target))
{
// FOF is not installed; install now
$haveToInstallFOF = true;
}
else
{
// FOF is already installed; check the version
$fofVersion = array();
if (JFile::exists($target . '/version.txt'))
{
$rawData = JFile::read($target . '/version.txt');
$rawData = ($rawData === false) ? "0.0.0\n2011-01-01\n" :
$rawData;
$info = explode("\n", $rawData);
$fofVersion['installed'] = array(
'version' => trim($info[0]),
'date' => new JDate(trim($info[1]))
);
}
else
{
$fofVersion['installed'] = array(
'version' => '0.0',
'date' => new JDate('2011-01-01')
);
}
$rawData = @file_get_contents($source . '/version.txt');
$rawData = ($rawData === false) ? "0.0.0\n2011-01-01\n" :
$rawData;
$info = explode("\n", $rawData);
$fofVersion['package'] = array(
'version' => trim($info[0]),
'date' => new JDate(trim($info[1]))
);
$haveToInstallFOF =
$fofVersion['package']['date']->toUNIX() >
$fofVersion['installed']['date']->toUNIX();
}
$installedFOF = false;
if ($haveToInstallFOF)
{
$versionSource = 'package';
$installer = new JInstaller;
$installedFOF = $installer->install($source);
}
else
{
$versionSource = 'installed';
}
if (!isset($fofVersion))
{
$fofVersion = array();
if (JFile::exists($target . '/version.txt'))
{
$rawData = @file_get_contents($source . '/version.txt');
$rawData = ($rawData === false) ? "0.0.0\n2011-01-01\n" :
$rawData;
$info = explode("\n", $rawData);
$fofVersion['installed'] = array(
'version' => trim($info[0]),
'date' => new JDate(trim($info[1]))
);
}
else
{
$fofVersion['installed'] = array(
'version' => '0.0',
'date' => new JDate('2011-01-01')
);
}
$rawData = @file_get_contents($source . '/version.txt');
$rawData = ($rawData === false) ? "0.0.0\n2011-01-01\n" :
$rawData;
$info = explode("\n", $rawData);
$fofVersion['package'] = array(
'version' => trim($info[0]),
'date' => new JDate(trim($info[1]))
);
$versionSource = 'installed';
}
if (!($fofVersion[$versionSource]['date'] instanceof JDate))
{
$fofVersion[$versionSource]['date'] = new JDate();
}
return array(
'required' => $haveToInstallFOF,
'installed' => $installedFOF,
'version' =>
$fofVersion[$versionSource]['version'],
'date' =>
$fofVersion[$versionSource]['date']->format('Y-m-d'),
);
}
/**
* Installs Akeeba Strapper if necessary
*
* @param JInstaller $parent The parent object
*
* @return array The installation status
*/
protected function installStrapper($parent)
{
$src = $parent->getParent()->getPath('source');
$source = $src . '/' . $this->strapperSourcePath;
$target = JPATH_ROOT . '/media/akeeba_strapper';
if (!JFolder::exists($source))
{
return array(
'required' => false,
'installed' => false,
'version' => '0.0.0',
'date' => '2011-01-01',
);
}
$haveToInstallStrapper = false;
if (!JFolder::exists($target))
{
$haveToInstallStrapper = true;
}
else
{
$strapperVersion = array();
if (JFile::exists($target . '/version.txt'))
{
$rawData = JFile::read($target . '/version.txt');
$rawData = ($rawData === false) ? "0.0.0\n2011-01-01\n" :
$rawData;
$info = explode("\n", $rawData);
$strapperVersion['installed'] = array(
'version' => trim($info[0]),
'date' => new JDate(trim($info[1]))
);
}
else
{
$strapperVersion['installed'] = array(
'version' => '0.0',
'date' => new JDate('2011-01-01')
);
}
$rawData = JFile::read($source . '/version.txt');
$rawData = ($rawData === false) ? "0.0.0\n2011-01-01\n" :
$rawData;
$info = explode("\n", $rawData);
$strapperVersion['package'] = array(
'version' => trim($info[0]),
'date' => new JDate(trim($info[1]))
);
$haveToInstallStrapper =
$strapperVersion['package']['date']->toUNIX() >
$strapperVersion['installed']['date']->toUNIX();
}
$installedStraper = false;
if ($haveToInstallStrapper)
{
$versionSource = 'package';
$installer = new JInstaller;
$installedStraper = $installer->install($source);
}
else
{
$versionSource = 'installed';
}
if (!isset($strapperVersion))
{
$strapperVersion = array();
if (JFile::exists($target . '/version.txt'))
{
$rawData = JFile::read($target . '/version.txt');
$rawData = ($rawData === false) ? "0.0.0\n2011-01-01\n" :
$rawData;
$info = explode("\n", $rawData);
$strapperVersion['installed'] = array(
'version' => trim($info[0]),
'date' => new JDate(trim($info[1]))
);
}
else
{
$strapperVersion['installed'] = array(
'version' => '0.0',
'date' => new JDate('2011-01-01')
);
}
$rawData = JFile::read($source . '/version.txt');
$rawData = ($rawData === false) ? "0.0.0\n2011-01-01\n" :
$rawData;
$info = explode("\n", $rawData);
$strapperVersion['package'] = array(
'version' => trim($info[0]),
'date' => new JDate(trim($info[1]))
);
$versionSource = 'installed';
}
if (!($strapperVersion[$versionSource]['date'] instanceof
JDate))
{
$strapperVersion[$versionSource]['date'] = new JDate();
}
return array(
'required' => $haveToInstallStrapper,
'installed' => $installedStraper,
'version' =>
$strapperVersion[$versionSource]['version'],
'date' =>
$strapperVersion[$versionSource]['date']->format('Y-m-d'),
);
}
/**
* Uninstalls obsolete subextensions (modules, plugins) bundled with the
main extension
*
* @param JInstaller $parent The parent object
*
* @return stdClass The subextension uninstallation status
*/
protected function uninstallObsoleteSubextensions($parent)
{
JLoader::import('joomla.installer.installer');
$db = FOFPlatform::getInstance()->getDbo();
$status = new stdClass();
$status->modules = array();
$status->plugins = array();
$src = $parent->getParent()->getPath('source');
// Modules uninstallation
if (isset($this->uninstallation_queue['modules']) &&
count($this->uninstallation_queue['modules']))
{
foreach ($this->uninstallation_queue['modules'] as $folder
=> $modules)
{
if (count($modules))
{
foreach ($modules as $module)
{
// Find the module ID
$sql = $db->getQuery(true)
->select($db->qn('extension_id'))
->from($db->qn('#__extensions'))
->where($db->qn('element') . ' = ' .
$db->q('mod_' . $module))
->where($db->qn('type') . ' = ' .
$db->q('module'));
$db->setQuery($sql);
$id = $db->loadResult();
// Uninstall the module
if ($id)
{
$installer = new JInstaller;
$result = $installer->uninstall('module', $id, 1);
$status->modules[] = array(
'name' => 'mod_' . $module,
'client' => $folder,
'result' => $result
);
}
}
}
}
}
// Plugins uninstallation
if (isset($this->uninstallation_queue['plugins']) &&
count($this->uninstallation_queue['plugins']))
{
foreach ($this->uninstallation_queue['plugins'] as $folder
=> $plugins)
{
if (count($plugins))
{
foreach ($plugins as $plugin)
{
$sql = $db->getQuery(true)
->select($db->qn('extension_id'))
->from($db->qn('#__extensions'))
->where($db->qn('type') . ' = ' .
$db->q('plugin'))
->where($db->qn('element') . ' = ' .
$db->q($plugin))
->where($db->qn('folder') . ' = ' .
$db->q($folder));
$db->setQuery($sql);
$id = $db->loadResult();
if ($id)
{
$installer = new JInstaller;
$result = $installer->uninstall('plugin', $id, 1);
$status->plugins[] = array(
'name' => 'plg_' . $plugin,
'group' => $folder,
'result' => $result
);
}
}
}
}
}
return $status;
}
/**
* @param JInstallerAdapterComponent $parent
*
* @return bool
*
* @throws Exception When the Joomla! menu is FUBAR
*/
private function _createAdminMenus($parent)
{
$db = $db = FOFPlatform::getInstance()->getDbo();
/** @var JTableMenu $table */
$table = JTable::getInstance('menu');
$option = $parent->get('element');
// If a component exists with this option in the table then we don't
need to add menus - check only 'main' menutype
$query = $db->getQuery(true)
->select('m.id, e.extension_id')
->from('#__menu AS m')
->join('LEFT', '#__extensions AS e ON m.component_id =
e.extension_id')
->where('m.parent_id = 1')
->where('m.client_id = 1')
->where('m.menutype = ' . $db->q('main'))
->where($db->qn('e') . '.' .
$db->qn('type') . ' = ' .
$db->q('component'))
->where('e.element = ' . $db->quote($option));
$db->setQuery($query);
$componentrow = $db->loadObject();
// Check if menu items exist
if ($componentrow)
{
// @todo Return if the menu item already exists to save some time
//return true;
}
// Let's find the extension id
$query->clear()
->select('e.extension_id')
->from('#__extensions AS e')
->where('e.type = ' . $db->quote('component'))
->where('e.element = ' . $db->quote($option));
$db->setQuery($query);
$component_id = $db->loadResult();
// Ok, now its time to handle the menus. Start with the component root
menu, then handle submenus.
$menuElement =
$parent->get('manifest')->administration->menu;
// We need to insert the menu item as the last child of Joomla!'s
menu root node. By default this is the
// menu item with ID=1. However, some crappy upgrade scripts enjoy
screwing it up. Hey, ho, the workaround
// way I go.
$query = $db->getQuery(true)
->select($db->qn('id'))
->from($db->qn('#__menu'))
->where($db->qn('id') . ' = ' . $db->q(1));
$rootItemId = $db->setQuery($query)->loadResult();
if (is_null($rootItemId))
{
// Guess what? The Problem has happened. Let's find the root node
by title.
$rootItemId = null;
$query = $db->getQuery(true)
->select($db->qn('id'))
->from($db->qn('#__menu'))
->where($db->qn('title') . ' = ' .
$db->q('Menu_Item_Root'));
$rootItemId = $db->setQuery($query, 0, 1)->loadResult();
}
if (is_null($rootItemId))
{
// For crying out loud, did that idiot changed the title too?!
Let's find it by alias.
$rootItemId = null;
$query = $db->getQuery(true)
->select($db->qn('id'))
->from($db->qn('#__menu'))
->where($db->qn('alias') . ' = ' .
$db->q('root'));
$rootItemId = $db->setQuery($query, 0, 1)->loadResult();
}
if (is_null($rootItemId))
{
// Dude. Dude! Duuuuuuude! The alias is screwed up, too?! Find it by
component ID.
$rootItemId = null;
$query = $db->getQuery(true)
->select($db->qn('id'))
->from($db->qn('#__menu'))
->where($db->qn('component_id') . ' = ' .
$db->q('0'));
$rootItemId = $db->setQuery($query, 0, 1)->loadResult();
}
if (is_null($rootItemId))
{
// Your site is more of a "shite" than a "site".
Let's try with minimum lft value.
$rootItemId = null;
$query = $db->getQuery(true)
->select($db->qn('id'))
->from($db->qn('#__menu'))
->order($db->qn('lft') . ' ASC');
$rootItemId = $db->setQuery($query, 0, 1)->loadResult();
}
if (is_null($rootItemId))
{
// I quit. Your site is broken. What the hell are you doing with it?
I'll just throw an error.
throw new Exception("Your site is broken. There is no root menu
item. As a result it is impossible to create menu items. The installation
of this component has failed. Please fix your database and retry!",
500);
}
if ($menuElement)
{
$data = array();
$data['menutype'] = 'main';
$data['client_id'] = 1;
$data['title'] = (string)trim($menuElement);
$data['alias'] = (string)$menuElement;
$data['link'] = 'index.php?option=' . $option;
$data['type'] = 'component';
$data['published'] = 0;
$data['parent_id'] = 1;
$data['component_id'] = $component_id;
$data['img'] = ((string)$menuElement->attributes()->img)
? (string)$menuElement->attributes()->img :
'class:component';
$data['home'] = 0;
$data['path'] = '';
$data['params'] = '';
}
// No menu element was specified, Let's make a generic menu item
else
{
$data = array();
$data['menutype'] = 'main';
$data['client_id'] = 1;
$data['title'] = $option;
$data['alias'] = $option;
$data['link'] = 'index.php?option=' . $option;
$data['type'] = 'component';
$data['published'] = 0;
$data['parent_id'] = 1;
$data['component_id'] = $component_id;
$data['img'] = 'class:component';
$data['home'] = 0;
$data['path'] = '';
$data['params'] = '';
}
try
{
$table->setLocation($rootItemId, 'last-child');
}
catch (InvalidArgumentException $e)
{
if (class_exists('JLog'))
{
JLog::add($e->getMessage(), JLog::WARNING, 'jerror');
}
return false;
}
if (!$table->bind($data) || !$table->check() ||
!$table->store())
{
// The menu item already exists. Delete it and retry instead of throwing
an error.
$query->clear()
->select('id')
->from('#__menu')
->where('menutype = ' . $db->quote('main'))
->where('client_id = 1')
->where('link = ' .
$db->quote('index.php?option=' . $option))
->where('type = ' . $db->quote('component'))
->where('parent_id = 1')
->where('home = 0');
$db->setQuery($query);
$menu_ids_level1 = $db->loadColumn();
if (empty($menu_ids_level1))
{
// Oops! Could not get the menu ID. Go back and rollback changes.
JError::raiseWarning(1, $table->getError());
return false;
}
else
{
$ids = implode(',', $menu_ids_level1);
$query->clear()
->select('id')
->from('#__menu')
->where('menutype = ' . $db->quote('main'))
->where('client_id = 1')
->where('type = ' . $db->quote('component'))
->where('parent_id in (' . $ids . ')')
->where('level = 2')
->where('home = 0');
$db->setQuery($query);
$menu_ids_level2 = $db->loadColumn();
$ids = implode(',', array_merge($menu_ids_level1,
$menu_ids_level2));
// Remove the old menu item
$query->clear()
->delete('#__menu')
->where('id in (' . $ids . ')');
$db->setQuery($query);
$db->query();
// Retry creating the menu item
$table->setLocation($rootItemId, 'last-child');
if (!$table->bind($data) || !$table->check() ||
!$table->store())
{
// Install failed, warn user and rollback changes
JError::raiseWarning(1, $table->getError());
return false;
}
}
}
/*
* Since we have created a menu item, we add it to the installation step
stack
* so that if we have to rollback the changes we can undo it.
*/
$parent->getParent()->pushStep(array('type' =>
'menu', 'id' => $component_id));
/*
* Process SubMenus
*/
if
(!$parent->get('manifest')->administration->submenu)
{
return true;
}
$parent_id = $table->id;
foreach
($parent->get('manifest')->administration->submenu->menu
as $child)
{
$data = array();
$data['menutype'] = 'main';
$data['client_id'] = 1;
$data['title'] = (string)trim($child);
$data['alias'] = (string)$child;
$data['type'] = 'component';
$data['published'] = 0;
$data['parent_id'] = $parent_id;
$data['component_id'] = $component_id;
$data['img'] = ((string)$child->attributes()->img) ?
(string)$child->attributes()->img : 'class:component';
$data['home'] = 0;
// Set the sub menu link
if ((string)$child->attributes()->link)
{
$data['link'] = 'index.php?' .
$child->attributes()->link;
}
else
{
$request = array();
if ((string)$child->attributes()->act)
{
$request[] = 'act=' . $child->attributes()->act;
}
if ((string)$child->attributes()->task)
{
$request[] = 'task=' . $child->attributes()->task;
}
if ((string)$child->attributes()->controller)
{
$request[] = 'controller=' .
$child->attributes()->controller;
}
if ((string)$child->attributes()->view)
{
$request[] = 'view=' . $child->attributes()->view;
}
if ((string)$child->attributes()->layout)
{
$request[] = 'layout=' . $child->attributes()->layout;
}
if ((string)$child->attributes()->sub)
{
$request[] = 'sub=' . $child->attributes()->sub;
}
$qstring = (count($request)) ? '&' .
implode('&', $request) : '';
$data['link'] = 'index.php?option=' . $option .
$qstring;
}
$table = JTable::getInstance('menu');
try
{
$table->setLocation($parent_id, 'last-child');
}
catch (InvalidArgumentException $e)
{
return false;
}
if (!$table->bind($data) || !$table->check() ||
!$table->store())
{
// Install failed, rollback changes
return false;
}
/*
* Since we have created a menu item, we add it to the installation step
stack
* so that if we have to rollback the changes we can undo it.
*/
$parent->getParent()->pushStep(array('type' =>
'menu', 'id' => $component_id));
}
return true;
}
/**
* Make sure the Component menu items are really published!
*
* @param JInstallerAdapterComponent $parent
*
* @return bool
*/
private function _reallyPublishAdminMenuItems($parent)
{
$db = FOFPlatform::getInstance()->getDbo();
$option = $parent->get('element');
$query = $db->getQuery(true)
->update('#__menu AS m')
->join('LEFT', '#__extensions AS e ON m.component_id =
e.extension_id')
->set($db->qn('published') . ' = ' .
$db->q(1))
->where('m.parent_id = 1')
->where('m.client_id = 1')
->where('m.menutype = ' . $db->quote('main'))
->where('e.type = ' . $db->quote('component'))
->where('e.element = ' . $db->quote($option));
$db->setQuery($query);
try
{
$db->execute();
}
catch (Exception $e)
{
// If it fails, it fails. Who cares.
}
}
/**
* Tells Joomla! to rebuild its menu structure to make triple-sure that
the Components menu items really do exist
* in the correct place and can really be rendered.
*/
private function _rebuildMenu()
{
/** @var JTableMenu $table */
$table = JTable::getInstance('menu');
$db = FOFPlatform::getInstance()->getDbo();
// We need to rebuild the menu based on its root item. By default this is
the menu item with ID=1. However, some
// crappy upgrade scripts enjoy screwing it up. Hey, ho, the workaround
way I go.
$query = $db->getQuery(true)
->select($db->qn('id'))
->from($db->qn('#__menu'))
->where($db->qn('id') . ' = ' . $db->q(1));
$rootItemId = $db->setQuery($query)->loadResult();
if (is_null($rootItemId))
{
// Guess what? The Problem has happened. Let's find the root node
by title.
$rootItemId = null;
$query = $db->getQuery(true)
->select($db->qn('id'))
->from($db->qn('#__menu'))
->where($db->qn('title') . ' = ' .
$db->q('Menu_Item_Root'));
$rootItemId = $db->setQuery($query, 0, 1)->loadResult();
}
if (is_null($rootItemId))
{
// For crying out loud, did that idiot changed the title too?!
Let's find it by alias.
$rootItemId = null;
$query = $db->getQuery(true)
->select($db->qn('id'))
->from($db->qn('#__menu'))
->where($db->qn('alias') . ' = ' .
$db->q('root'));
$rootItemId = $db->setQuery($query, 0, 1)->loadResult();
}
if (is_null($rootItemId))
{
// Dude. Dude! Duuuuuuude! The alias is screwed up, too?! Find it by
component ID.
$rootItemId = null;
$query = $db->getQuery(true)
->select($db->qn('id'))
->from($db->qn('#__menu'))
->where($db->qn('component_id') . ' = ' .
$db->q('0'));
$rootItemId = $db->setQuery($query, 0, 1)->loadResult();
}
if (is_null($rootItemId))
{
// Your site is more of a "shite" than a "site".
Let's try with minimum lft value.
$rootItemId = null;
$query = $db->getQuery(true)
->select($db->qn('id'))
->from($db->qn('#__menu'))
->order($db->qn('lft') . ' ASC');
$rootItemId = $db->setQuery($query, 0, 1)->loadResult();
}
if (is_null($rootItemId))
{
// I quit. Your site is broken.
return false;
}
$table->rebuild($rootItemId);
}
/**
* 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
FOFTemplateUtils::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 FOFTemplateUtils::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 = FOFPlatform::getInstance()->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 = FOFPlatform::getInstance()->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);
}
}
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 = FOFPlatform::getInstance()->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;
}
}
}
ip/ip.php000064400000026514151167373210006313 0ustar00<?php
/**
* @package FrameworkOnFramework
* @subpackage utils
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('FOF_INCLUDED') or die;
/**
* IP address utilities
*/
abstract class FOFUtilsIp
{
/** @var string The IP address of the current visitor */
protected static $ip = null;
/**
* Should I allow IP overrides through X-Forwarded-For or Client-Ip HTTP
headers?
*
* @var bool
*/
protected static $allowIpOverrides = true;
/**
* Get the current visitor's IP address
*
* @return string
*/
public static function getIp()
{
if (is_null(static::$ip))
{
$ip = self::detectAndCleanIP();
if (!empty($ip) && ($ip != '0.0.0.0') &&
function_exists('inet_pton') &&
function_exists('inet_ntop'))
{
$myIP = @inet_pton($ip);
if ($myIP !== false)
{
$ip = inet_ntop($myIP);
}
}
static::setIp($ip);
}
return static::$ip;
}
/**
* Set the IP address of the current visitor
*
* @param string $ip
*
* @return void
*/
public static function setIp($ip)
{
static::$ip = $ip;
}
/**
* Is it an IPv6 IP address?
*
* @param string $ip An IPv4 or IPv6 address
*
* @return boolean True if it's IPv6
*/
public static function isIPv6($ip)
{
if (strstr($ip, ':'))
{
return true;
}
return false;
}
/**
* Checks if an IP is contained in a list of IPs or IP expressions
*
* @param string $ip The IPv4/IPv6 address to check
* @param array|string $ipTable An IP expression (or a comma-separated
or array list of IP expressions) to check against
*
* @return null|boolean True if it's in the list
*/
public static function IPinList($ip, $ipTable = '')
{
// No point proceeding with an empty IP list
if (empty($ipTable))
{
return false;
}
// If the IP list is not an array, convert it to an array
if (!is_array($ipTable))
{
if (strpos($ipTable, ',') !== false)
{
$ipTable = explode(',', $ipTable);
$ipTable = array_map(function($x) { return trim($x); }, $ipTable);
}
else
{
$ipTable = trim($ipTable);
$ipTable = array($ipTable);
}
}
// If no IP address is found, return false
if ($ip == '0.0.0.0')
{
return false;
}
// If no IP is given, return false
if (empty($ip))
{
return false;
}
// Sanity check
if (!function_exists('inet_pton'))
{
return false;
}
// Get the IP's in_adds representation
$myIP = @inet_pton($ip);
// If the IP is in an unrecognisable format, quite
if ($myIP === false)
{
return false;
}
$ipv6 = self::isIPv6($ip);
foreach ($ipTable as $ipExpression)
{
$ipExpression = trim($ipExpression);
// Inclusive IP range, i.e. 123.123.123.123-124.125.126.127
if (strstr($ipExpression, '-'))
{
list($from, $to) = explode('-', $ipExpression, 2);
if ($ipv6 && (!self::isIPv6($from) || !self::isIPv6($to)))
{
// Do not apply IPv4 filtering on an IPv6 address
continue;
}
elseif (!$ipv6 && (self::isIPv6($from) || self::isIPv6($to)))
{
// Do not apply IPv6 filtering on an IPv4 address
continue;
}
$from = @inet_pton(trim($from));
$to = @inet_pton(trim($to));
// Sanity check
if (($from === false) || ($to === false))
{
continue;
}
// Swap from/to if they're in the wrong order
if ($from > $to)
{
list($from, $to) = array($to, $from);
}
if (($myIP >= $from) && ($myIP <= $to))
{
return true;
}
}
// Netmask or CIDR provided
elseif (strstr($ipExpression, '/'))
{
$binaryip = self::inet_to_bits($myIP);
list($net, $maskbits) = explode('/', $ipExpression, 2);
if ($ipv6 && !self::isIPv6($net))
{
// Do not apply IPv4 filtering on an IPv6 address
continue;
}
elseif (!$ipv6 && self::isIPv6($net))
{
// Do not apply IPv6 filtering on an IPv4 address
continue;
}
elseif ($ipv6 && strstr($maskbits, ':'))
{
// Perform an IPv6 CIDR check
if (self::checkIPv6CIDR($myIP, $ipExpression))
{
return true;
}
// If we didn't match it proceed to the next expression
continue;
}
elseif (!$ipv6 && strstr($maskbits, '.'))
{
// Convert IPv4 netmask to CIDR
$long = ip2long($maskbits);
$base = ip2long('255.255.255.255');
$maskbits = 32 - log(($long ^ $base) + 1, 2);
}
// Convert network IP to in_addr representation
$net = @inet_pton($net);
// Sanity check
if ($net === false)
{
continue;
}
// Get the network's binary representation
$binarynet = self::inet_to_bits($net);
$expectedNumberOfBits = $ipv6 ? 128 : 24;
$binarynet = str_pad($binarynet, $expectedNumberOfBits, '0',
STR_PAD_RIGHT);
// Check the corresponding bits of the IP and the network
$ip_net_bits = substr($binaryip, 0, $maskbits);
$net_bits = substr($binarynet, 0, $maskbits);
if ($ip_net_bits == $net_bits)
{
return true;
}
}
else
{
// IPv6: Only single IPs are supported
if ($ipv6)
{
$ipExpression = trim($ipExpression);
if (!self::isIPv6($ipExpression))
{
continue;
}
$ipCheck = @inet_pton($ipExpression);
if ($ipCheck === false)
{
continue;
}
if ($ipCheck == $myIP)
{
return true;
}
}
else
{
// Standard IPv4 address, i.e. 123.123.123.123 or partial IP address,
i.e. 123.[123.][123.][123]
$dots = 0;
if (substr($ipExpression, -1) == '.')
{
// Partial IP address. Convert to CIDR and re-match
foreach (count_chars($ipExpression, 1) as $i => $val)
{
if ($i == 46)
{
$dots = $val;
}
}
switch ($dots)
{
case 1:
$netmask = '255.0.0.0';
$ipExpression .= '0.0.0';
break;
case 2:
$netmask = '255.255.0.0';
$ipExpression .= '0.0';
break;
case 3:
$netmask = '255.255.255.0';
$ipExpression .= '0';
break;
default:
$dots = 0;
}
if ($dots)
{
$binaryip = self::inet_to_bits($myIP);
// Convert netmask to CIDR
$long = ip2long($netmask);
$base = ip2long('255.255.255.255');
$maskbits = 32 - log(($long ^ $base) + 1, 2);
$net = @inet_pton($ipExpression);
// Sanity check
if ($net === false)
{
continue;
}
// Get the network's binary representation
$binarynet = self::inet_to_bits($net);
$expectedNumberOfBits = $ipv6 ? 128 : 24;
$binarynet = str_pad($binarynet, $expectedNumberOfBits,
'0', STR_PAD_RIGHT);
// Check the corresponding bits of the IP and the network
$ip_net_bits = substr($binaryip, 0, $maskbits);
$net_bits = substr($binarynet, 0, $maskbits);
if ($ip_net_bits == $net_bits)
{
return true;
}
}
}
if (!$dots)
{
$ip = @inet_pton(trim($ipExpression));
if ($ip == $myIP)
{
return true;
}
}
}
}
}
return false;
}
/**
* Works around the REMOTE_ADDR not containing the user's IP
*/
public static function workaroundIPIssues()
{
$ip = self::getIp();
if ($_SERVER['REMOTE_ADDR'] == $ip)
{
return;
}
if (array_key_exists('REMOTE_ADDR', $_SERVER))
{
$_SERVER['FOF_REMOTE_ADDR'] =
$_SERVER['REMOTE_ADDR'];
}
elseif (function_exists('getenv'))
{
if (getenv('REMOTE_ADDR'))
{
$_SERVER['FOF_REMOTE_ADDR'] =
getenv('REMOTE_ADDR');
}
}
$_SERVER['REMOTE_ADDR'] = $ip;
}
/**
* Should I allow the remote client's IP to be overridden by an
X-Forwarded-For or Client-Ip HTTP header?
*
* @param bool $newState True to allow the override
*
* @return void
*/
public static function setAllowIpOverrides($newState)
{
self::$allowIpOverrides = $newState ? true : false;
}
/**
* Gets the visitor's IP address. Automatically handles reverse
proxies
* reporting the IPs of intermediate devices, like load balancers.
Examples:
*
https://www.akeebabackup.com/support/admin-tools/13743-double-ip-adresses-in-security-exception-log-warnings.html
*
http://stackoverflow.com/questions/2422395/why-is-request-envremote-addr-returning-two-ips
* The solution used is assuming that the last IP address is the external
one.
*
* @return string
*/
protected static function detectAndCleanIP()
{
$ip = self::detectIP();
if ((strstr($ip, ',') !== false) || (strstr($ip, ' ')
!== false))
{
$ip = str_replace(' ', ',', $ip);
$ip = str_replace(',,', ',', $ip);
$ips = explode(',', $ip);
$ip = '';
while (empty($ip) && !empty($ips))
{
$ip = array_pop($ips);
$ip = trim($ip);
}
}
else
{
$ip = trim($ip);
}
return $ip;
}
/**
* Gets the visitor's IP address
*
* @return string
*/
protected static function detectIP()
{
// Normally the $_SERVER superglobal is set
if (isset($_SERVER))
{
// Do we have an x-forwarded-for HTTP header (e.g. NginX)?
if (self::$allowIpOverrides &&
array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER))
{
return $_SERVER['HTTP_X_FORWARDED_FOR'];
}
// Do we have a client-ip header (e.g. non-transparent proxy)?
if (self::$allowIpOverrides &&
array_key_exists('HTTP_CLIENT_IP', $_SERVER))
{
return $_SERVER['HTTP_CLIENT_IP'];
}
// Normal, non-proxied server or server behind a transparent proxy
return $_SERVER['REMOTE_ADDR'];
}
// This part is executed on PHP running as CGI, or on SAPIs which do
// not set the $_SERVER superglobal
// If getenv() is disabled, you're screwed
if (!function_exists('getenv'))
{
return '';
}
// Do we have an x-forwarded-for HTTP header?
if (self::$allowIpOverrides &&
getenv('HTTP_X_FORWARDED_FOR'))
{
return getenv('HTTP_X_FORWARDED_FOR');
}
// Do we have a client-ip header?
if (self::$allowIpOverrides &&
getenv('HTTP_CLIENT_IP'))
{
return getenv('HTTP_CLIENT_IP');
}
// Normal, non-proxied server or server behind a transparent proxy
if (getenv('REMOTE_ADDR'))
{
return getenv('REMOTE_ADDR');
}
// Catch-all case for broken servers, apparently
return '';
}
/**
* Converts inet_pton output to bits string
*
* @param string $inet The in_addr representation of an IPv4 or IPv6
address
*
* @return string
*/
protected static function inet_to_bits($inet)
{
if (strlen($inet) == 4)
{
$unpacked = unpack('A4', $inet);
}
else
{
$unpacked = unpack('A16', $inet);
}
$unpacked = str_split($unpacked[1]);
$binaryip = '';
foreach ($unpacked as $char)
{
$binaryip .= str_pad(decbin(ord($char)), 8, '0',
STR_PAD_LEFT);
}
return $binaryip;
}
/**
* Checks if an IPv6 address $ip is part of the IPv6 CIDR block $cidrnet
*
* @param string $ip The IPv6 address to check, e.g.
21DA:00D3:0000:2F3B:02AC:00FF:FE28:9C5A
* @param string $cidrnet The IPv6 CIDR block, e.g.
21DA:00D3:0000:2F3B::/64
*
* @return bool
*/
protected static function checkIPv6CIDR($ip, $cidrnet)
{
$ip = inet_pton($ip);
$binaryip=self::inet_to_bits($ip);
list($net,$maskbits)=explode('/',$cidrnet);
$net=inet_pton($net);
$binarynet=self::inet_to_bits($net);
$ip_net_bits=substr($binaryip,0,$maskbits);
$net_bits =substr($binarynet,0,$maskbits);
return $ip_net_bits === $net_bits;
}
}object/object.php000064400000012143151167373210010000 0ustar00<?php
/**
* @package FrameworkOnFramework
* @subpackage utils
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('FOF_INCLUDED') or die;
/**
* Temporary class for backwards compatibility. You should not be using
this
* in your code. It is currently present to handle the validation error
stack
* for FOFTable::check() and will be removed in an upcoming version.
*
* This class is based on JObject as found in Joomla! 3.2.1
*
* @deprecated 2.1
* @codeCoverageIgnore
*/
class FOFUtilsObject
{
/**
* An array of error messages or Exception objects.
*
* @var array
*/
protected $_errors = array();
/**
* Class constructor, overridden in descendant classes.
*
* @param mixed $properties Either and associative array or another
* object to set the initial properties of
the object.
*/
public function __construct($properties = null)
{
if ($properties !== null)
{
$this->setProperties($properties);
}
}
/**
* Magic method to convert the object to a string gracefully.
*
* @return string The classname.
*/
public function __toString()
{
return get_class($this);
}
/**
* Sets a default value if not alreay assigned
*
* @param string $property The name of the property.
* @param mixed $default The default value.
*
* @return mixed
*/
public function def($property, $default = null)
{
$value = $this->get($property, $default);
return $this->set($property, $value);
}
/**
* Returns a property of the object or the default value if the
property is not set.
*
* @param string $property The name of the property.
* @param mixed $default The default value.
*
* @return mixed The value of the property.
*/
public function get($property, $default = null)
{
if (isset($this->$property))
{
return $this->$property;
}
return $default;
}
/**
* Returns an associative array of object properties.
*
* @param boolean $public If true, returns only the public
properties.
*
* @return array
*/
public function getProperties($public = true)
{
$vars = get_object_vars($this);
if ($public)
{
foreach ($vars as $key => $value)
{
if ('_' == substr($key, 0, 1))
{
unset($vars[$key]);
}
}
}
return $vars;
}
/**
* Get the most recent error message.
*
* @param integer $i Option error index.
* @param boolean $toString Indicates if JError objects should
return their error message.
*
* @return string Error message
*/
public function getError($i = null, $toString = true)
{
// Find the error
if ($i === null)
{
// Default, return the last message
$error = end($this->_errors);
}
elseif (!array_key_exists($i, $this->_errors))
{
// If $i has been specified but does not exist, return false
return false;
}
else
{
$error = $this->_errors[$i];
}
// Check if only the string is requested
if ($error instanceof Exception && $toString)
{
return (string) $error;
}
return $error;
}
/**
* Return all errors, if any.
*
* @return array Array of error messages or JErrors.
*/
public function getErrors()
{
return $this->_errors;
}
/**
* Modifies a property of the object, creating it if it does not
already exist.
*
* @param string $property The name of the property.
* @param mixed $value The value of the property to set.
*
* @return mixed Previous value of the property.
*/
public function set($property, $value = null)
{
$previous = isset($this->$property) ? $this->$property :
null;
$this->$property = $value;
return $previous;
}
/**
* Set the object properties based on a named array/hash.
*
* @param mixed $properties Either an associative array or another
object.
*
* @return boolean
*/
public function setProperties($properties)
{
if (is_array($properties) || is_object($properties))
{
foreach ((array) $properties as $k => $v)
{
// Use the set function which might be overridden.
$this->set($k, $v);
}
return true;
}
return false;
}
/**
* Add an error message.
*
* @param string $error Error message.
*
* @return void
*/
public function setError($error)
{
array_push($this->_errors, $error);
}
}
observable/dispatcher.php000064400000016452151167373210011545
0ustar00<?php
/**
* @package FrameworkOnFramework
* @subpackage utils
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('FOF_INCLUDED') or die;
/**
* Class to handle dispatching of events.
*
* This is the Observable part of the Observer design pattern
* for the event architecture.
*
* This class is based on JEventDispatcher as found in Joomla! 3.2.0
*/
class FOFUtilsObservableDispatcher extends FOFUtilsObject
{
/**
* An array of Observer objects to notify
*
* @var array
*/
protected $_observers = array();
/**
* The state of the observable object
*
* @var mixed
*/
protected $_state = null;
/**
* A multi dimensional array of [function][] = key for observers
*
* @var array
*/
protected $_methods = array();
/**
* Stores the singleton instance of the dispatcher.
*
* @var FOFUtilsObservableDispatcher
*/
protected static $instance = null;
/**
* Returns the global Event Dispatcher object, only creating it
* if it doesn't already exist.
*
* @return FOFUtilsObservableDispatcher The EventDispatcher object.
*/
public static function getInstance()
{
if (self::$instance === null)
{
self::$instance = new static;
}
return self::$instance;
}
/**
* Get the state of the FOFUtilsObservableDispatcher object
*
* @return mixed The state of the object.
*/
public function getState()
{
return $this->_state;
}
/**
* Registers an event handler to the event dispatcher
*
* @param string $event Name of the event to register handler for
* @param string $handler Name of the event handler
*
* @return void
*
* @throws InvalidArgumentException
*/
public function register($event, $handler)
{
// Are we dealing with a class or callback type handler?
if (is_callable($handler))
{
// Ok, function type event handler... let's attach it.
$method = array('event' => $event,
'handler' => $handler);
$this->attach($method);
}
elseif (class_exists($handler))
{
// Ok, class type event handler... let's instantiate and
attach it.
$this->attach(new $handler($this));
}
else
{
throw new InvalidArgumentException('Invalid event
handler.');
}
}
/**
* Triggers an event by dispatching arguments to all observers that
handle
* the event and returning their return values.
*
* @param string $event The event to trigger.
* @param array $args An array of arguments.
*
* @return array An array of results from each function call.
*/
public function trigger($event, $args = array())
{
$result = array();
/*
* If no arguments were passed, we still need to pass an empty
array to
* the call_user_func_array function.
*/
$args = (array) $args;
$event = strtolower($event);
// Check if any plugins are attached to the event.
if (!isset($this->_methods[$event]) ||
empty($this->_methods[$event]))
{
// No Plugins Associated To Event!
return $result;
}
// Loop through all plugins having a method matching our event
foreach ($this->_methods[$event] as $key)
{
// Check if the plugin is present.
if (!isset($this->_observers[$key]))
{
continue;
}
// Fire the event for an object based observer.
if (is_object($this->_observers[$key]))
{
$args['event'] = $event;
$value = $this->_observers[$key]->update($args);
}
// Fire the event for a function based observer.
elseif (is_array($this->_observers[$key]))
{
$value =
call_user_func_array($this->_observers[$key]['handler'],
$args);
}
if (isset($value))
{
$result[] = $value;
}
}
return $result;
}
/**
* Attach an observer object
*
* @param object $observer An observer object to attach
*
* @return void
*/
public function attach($observer)
{
if (is_array($observer))
{
if (!isset($observer['handler']) ||
!isset($observer['event']) ||
!is_callable($observer['handler']))
{
return;
}
// Make sure we haven't already attached this array as an
observer
foreach ($this->_observers as $check)
{
if (is_array($check) && $check['event']
== $observer['event'] && $check['handler'] ==
$observer['handler'])
{
return;
}
}
$this->_observers[] = $observer;
end($this->_observers);
$methods = array($observer['event']);
}
else
{
if (!($observer instanceof FOFUtilsObservableEvent))
{
return;
}
// Make sure we haven't already attached this object as an
observer
$class = get_class($observer);
foreach ($this->_observers as $check)
{
if ($check instanceof $class)
{
return;
}
}
$this->_observers[] = $observer;
// Required in PHP 7 since foreach() doesn't advance the
internal array counter, see
http://php.net/manual/en/migration70.incompatible.php
end($this->_observers);
$methods = array();
foreach(get_class_methods($observer) as $obs_method)
{
// Magic methods are not allowed
if(strpos('__', $obs_method) === 0)
{
continue;
}
$methods[] = $obs_method;
}
//$methods = get_class_methods($observer);
}
$key = key($this->_observers);
foreach ($methods as $method)
{
$method = strtolower($method);
if (!isset($this->_methods[$method]))
{
$this->_methods[$method] = array();
}
$this->_methods[$method][] = $key;
}
}
/**
* Detach an observer object
*
* @param object $observer An observer object to detach.
*
* @return boolean True if the observer object was detached.
*/
public function detach($observer)
{
$retval = false;
$key = array_search($observer, $this->_observers);
if ($key !== false)
{
unset($this->_observers[$key]);
$retval = true;
foreach ($this->_methods as &$method)
{
$k = array_search($key, $method);
if ($k !== false)
{
unset($method[$k]);
}
}
}
return $retval;
}
}
observable/event.php000064400000003631151167373210010533 0ustar00<?php
/**
* @package FrameworkOnFramework
* @subpackage utils
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('FOF_INCLUDED') or die;
/**
* Defines an observable event.
*
* This class is based on JEvent as found in Joomla! 3.2.0
*/
abstract class FOFUtilsObservableEvent extends FOFUtilsObject
{
/**
* Event object to observe.
*
* @var object
*/
protected $_subject = null;
/**
* Constructor
*
* @param object &$subject The object to observe.
*/
public function __construct(&$subject)
{
// Register the observer ($this) so we can be notified
$subject->attach($this);
// Set the subject to observe
$this->_subject = &$subject;
}
/**
* Method to trigger events.
* The method first generates the even from the argument array. Then it
unsets the argument
* since the argument has no bearing on the event handler.
* If the method exists it is called and returns its return value. If
it does not exist it
* returns null.
*
* @param array &$args Arguments
*
* @return mixed Routine return value
*/
public function update(&$args)
{
// First let's get the event from the argument array. Next we
will unset the
// event argument as it has no bearing on the method to handle the
event.
$event = $args['event'];
unset($args['event']);
/*
* If the method to handle an event exists, call it and return its
return
* value. If it does not exist, return null.
*/
if (method_exists($this, $event))
{
return call_user_func_array(array($this, $event), $args);
}
else
{
return null;
}
}
}
phpfunc/phpfunc.php000064400000001711151167373210010371 0ustar00<?php
/**
* @package FrameworkOnFramework
* @subpackage utils
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* Intercept calls to PHP functions.
*
* @method function_exists(string $function)
* @method mcrypt_list_algorithms()
* @method hash_algos()
* @method extension_loaded(string $ext)
* @method mcrypt_create_iv(int $bytes, int $source)
* @method openssl_get_cipher_methods()
*/
class FOFUtilsPhpfunc
{
/**
*
* Magic call to intercept any function pass to it.
*
* @param string $func The function to call.
*
* @param array $args Arguments passed to the function.
*
* @return mixed The result of the function call.
*
*/
public function __call($func, $args)
{
return call_user_func_array($func, $args);
}
}
timer/timer.php000064400000003775151167373210007537 0ustar00<?php
/**
* @package FrameworkOnFramework
* @subpackage utils
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
defined('FOF_INCLUDED') or die;
/**
* An execution timer monitor class
*/
class FOFUtilsTimer
{
/** @var float Maximum execution time allowance */
private $max_exec_time = null;
/** @var float Timestamp of execution start */
private $start_time = null;
/**
* Public constructor, creates the timer object and calculates the
execution
* time limits.
*
* @param float $max_exec_time Maximum execution time allowance
* @param float $runtime_bias Execution time bias (expressed as % of
$max_exec_time)
*/
public function __construct($max_exec_time = 5.0, $runtime_bias = 75.0)
{
// Initialize start time
$this->start_time = $this->microtime_float();
$this->max_exec_time = $max_exec_time * $runtime_bias / 100.0;
}
/**
* Wake-up function to reset internal timer when we get unserialized
*/
public function __wakeup()
{
// Re-initialize start time on wake-up
$this->start_time = $this->microtime_float();
}
/**
* Gets the number of seconds left, before we hit the "must
break" threshold. Negative
* values mean that we have already crossed that threshold.
*
* @return float
*/
public function getTimeLeft()
{
return $this->max_exec_time - $this->getRunningTime();
}
/**
* Gets the time elapsed since object creation/unserialization,
effectively
* how long we are running
*
* @return float
*/
public function getRunningTime()
{
return $this->microtime_float() - $this->start_time;
}
/**
* Returns the current timestamp in decimal seconds
*
* @return float
*/
private function microtime_float()
{
list($usec, $sec) = explode(" ", microtime());
return ((float)$usec + (float)$sec);
}
/**
* Reset the timer
*/
public function resetTime()
{
$this->start_time = $this->microtime_float();
}
}update/collection.php000064400000023630151167373210010704 0ustar00<?php
/**
* @package FrameworkOnFramework
* @subpackage utils
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* A helper class to read and parse "collection" update XML files
over the web
*/
class FOFUtilsUpdateCollection
{
/**
* Reads a "collection" XML update source and returns the
complete tree of categories
* and extensions applicable for platform version $jVersion
*
* @param string $url The collection XML update source URL to
read from
* @param string $jVersion Joomla! version to fetch updates for, or
null to use JVERSION
*
* @return array A list of update sources applicable to $jVersion
*/
public function getAllUpdates($url, $jVersion = null)
{
// Get the target platform
if (is_null($jVersion))
{
$jVersion = JVERSION;
}
// Initialise return value
$updates = array(
'metadata' => array(
'name' => '',
'description' => '',
),
'categories' => array(),
'extensions' => array(),
);
// Download and parse the XML file
$donwloader = new FOFDownload();
$xmlSource = $donwloader->getFromURL($url);
try
{
$xml = new SimpleXMLElement($xmlSource, LIBXML_NONET);
}
catch(Exception $e)
{
return $updates;
}
// Sanity check
if (($xml->getName() != 'extensionset'))
{
unset($xml);
return $updates;
}
// Initialise return value with the stream metadata (name, description)
$rootAttributes = $xml->attributes();
foreach ($rootAttributes as $k => $v)
{
$updates['metadata'][$k] = (string)$v;
}
// Initialise the raw list of updates
$rawUpdates = array(
'categories' => array(),
'extensions' => array(),
);
// Segregate the raw list to a hierarchy of extension and category
entries
/** @var SimpleXMLElement $extension */
foreach ($xml->children() as $extension)
{
switch ($extension->getName())
{
case 'category':
// These are the parameters we expect in a category
$params = array(
'name' => '',
'description' => '',
'category' => '',
'ref' => '',
'targetplatformversion' => $jVersion,
);
// These are the attributes of the element
$attributes = $extension->attributes();
// Merge them all
foreach ($attributes as $k => $v)
{
$params[$k] = (string)$v;
}
// We can't have a category with an empty category name
if (empty($params['category']))
{
break;
}
// We can't have a category with an empty ref
if (empty($params['ref']))
{
break;
}
if (empty($params['description']))
{
$params['description'] = $params['category'];
}
if (!array_key_exists($params['category'],
$rawUpdates['categories']))
{
$rawUpdates['categories'][$params['category']] =
array();
}
$rawUpdates['categories'][$params['category']][] =
$params;
break;
case 'extension':
// These are the parameters we expect in a category
$params = array(
'element' => '',
'type' => '',
'version' => '',
'name' => '',
'detailsurl' => '',
'targetplatformversion' => $jVersion,
);
// These are the attributes of the element
$attributes = $extension->attributes();
// Merge them all
foreach ($attributes as $k => $v)
{
$params[$k] = (string)$v;
}
// We can't have an extension with an empty element
if (empty($params['element']))
{
break;
}
// We can't have an extension with an empty type
if (empty($params['type']))
{
break;
}
// We can't have an extension with an empty version
if (empty($params['version']))
{
break;
}
if (empty($params['name']))
{
$params['name'] = $params['element'] . '
' . $params['version'];
}
if (!array_key_exists($params['type'],
$rawUpdates['extensions']))
{
$rawUpdates['extensions'][$params['type']] =
array();
}
if (!array_key_exists($params['element'],
$rawUpdates['extensions'][$params['type']]))
{
$rawUpdates['extensions'][$params['type']][$params['element']]
= array();
}
$rawUpdates['extensions'][$params['type']][$params['element']][]
= $params;
break;
default:
break;
}
}
unset($xml);
if (!empty($rawUpdates['categories']))
{
foreach ($rawUpdates['categories'] as $category =>
$entries)
{
$update = $this->filterListByPlatform($entries, $jVersion);
$updates['categories'][$category] = $update;
}
}
if (!empty($rawUpdates['extensions']))
{
foreach ($rawUpdates['extensions'] as $type => $extensions)
{
$updates['extensions'][$type] = array();
if (!empty($extensions))
{
foreach ($extensions as $element => $entries)
{
$update = $this->filterListByPlatform($entries, $jVersion);
$updates['extensions'][$type][$element] = $update;
}
}
}
}
return $updates;
}
/**
* Filters a list of updates, returning only those available for the
* specified platform version $jVersion
*
* @param array $updates An array containing update definitions
(categories or extensions)
* @param string $jVersion Joomla! version to fetch updates for, or
null to use JVERSION
*
* @return array|null The update definition that is compatible, or null
if none is compatible
*/
private function filterListByPlatform($updates, $jVersion = null)
{
// Get the target platform
if (is_null($jVersion))
{
$jVersion = JVERSION;
}
$versionParts = explode('.', $jVersion, 4);
$platformVersionMajor = $versionParts[0];
$platformVersionMinor = (count($versionParts) > 1) ?
$platformVersionMajor . '.' . $versionParts[1] :
$platformVersionMajor;
$platformVersionNormal = (count($versionParts) > 2) ?
$platformVersionMinor . '.' . $versionParts[2] :
$platformVersionMinor;
$platformVersionFull = (count($versionParts) > 3) ?
$platformVersionNormal . '.' . $versionParts[3] :
$platformVersionNormal;
$pickedExtension = null;
$pickedSpecificity = -1;
foreach ($updates as $update)
{
// Test the target platform
$targetPlatform = (string)$update['targetplatformversion'];
if ($targetPlatform === $platformVersionFull)
{
$pickedExtension = $update;
$pickedSpecificity = 4;
}
elseif (($targetPlatform === $platformVersionNormal) &&
($pickedSpecificity <= 3))
{
$pickedExtension = $update;
$pickedSpecificity = 3;
}
elseif (($targetPlatform === $platformVersionMinor) &&
($pickedSpecificity <= 2))
{
$pickedExtension = $update;
$pickedSpecificity = 2;
}
elseif (($targetPlatform === $platformVersionMajor) &&
($pickedSpecificity <= 1))
{
$pickedExtension = $update;
$pickedSpecificity = 1;
}
}
return $pickedExtension;
}
/**
* Returns only the category definitions of a collection
*
* @param string $url The URL of the collection update source
* @param string $jVersion Joomla! version to fetch updates for, or
null to use JVERSION
*
* @return array An array of category update definitions
*/
public function getCategories($url, $jVersion = null)
{
$allUpdates = $this->getAllUpdates($url, $jVersion);
return $allUpdates['categories'];
}
/**
* Returns the update source for a specific category
*
* @param string $url The URL of the collection update source
* @param string $category The category name you want to get the
update source URL of
* @param string $jVersion Joomla! version to fetch updates for, or
null to use JVERSION
*
* @return string|null The update stream URL, or null if it's not
found
*/
public function getCategoryUpdateSource($url, $category, $jVersion = null)
{
$allUpdates = $this->getAllUpdates($url, $jVersion);
if (array_key_exists($category, $allUpdates['categories']))
{
return $allUpdates['categories'][$category]['ref'];
}
else
{
return null;
}
}
/**
* Get a list of updates for extensions only, optionally of a specific
type
*
* @param string $url The URL of the collection update source
* @param string $type The extension type you want to get the
update source URL of, empty to get all
* extension types
* @param string $jVersion Joomla! version to fetch updates for, or
null to use JVERSION
*
* @return array|null An array of extension update definitions or null
if none is found
*/
public function getExtensions($url, $type = null, $jVersion = null)
{
$allUpdates = $this->getAllUpdates($url, $jVersion);
if (empty($type))
{
return $allUpdates['extensions'];
}
elseif (array_key_exists($type, $allUpdates['extensions']))
{
return $allUpdates['extensions'][$type];
}
else
{
return null;
}
}
/**
* Get the update source URL for a specific extension, based on the type
and element, e.g.
* type=file and element=joomla is Joomla! itself.
*
* @param string $url The URL of the collection update source
* @param string $type The extension type you want to get the
update source URL of
* @param string $element The extension element you want to get the
update source URL of
* @param string $jVersion Joomla! version to fetch updates for, or
null to use JVERSION
*
* @return string|null The update source URL or null if the extension is
not found
*/
public function getExtensionUpdateSource($url, $type, $element, $jVersion
= null)
{
$allUpdates = $this->getExtensions($url, $type, $jVersion);
if (empty($allUpdates))
{
return null;
}
elseif (array_key_exists($element, $allUpdates))
{
return $allUpdates[$element]['detailsurl'];
}
else
{
return null;
}
}
}update/extension.php000064400000006465151167373220010575 0ustar00<?php
/**
* @package FrameworkOnFramework
* @subpackage utils
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* A helper class to read and parse "extension" update XML files
over the web
*/
class FOFUtilsUpdateExtension
{
/**
* Reads an "extension" XML update source and returns all listed
update entries.
*
* If you have a "collection" XML update source you should do
something like this:
* $collection = new FOFUtilsUpdateCollection();
* $extensionUpdateURL = $collection->getExtensionUpdateSource($url,
'component', 'com_foobar', JVERSION);
* $extension = new FOFUtilsUpdateExtension();
* $updates = $extension->getUpdatesFromExtension($extensionUpdateURL);
*
* @param string $url The extension XML update source URL to read from
*
* @return array An array of update entries
*/
public function getUpdatesFromExtension($url)
{
// Initialise
$ret = array();
// Get and parse the XML source
$downloader = new FOFDownload();
$xmlSource = $downloader->getFromURL($url);
try
{
$xml = new SimpleXMLElement($xmlSource, LIBXML_NONET);
}
catch(Exception $e)
{
return $ret;
}
// Sanity check
if (($xml->getName() != 'updates'))
{
unset($xml);
return $ret;
}
// Let's populate the list of updates
/** @var SimpleXMLElement $update */
foreach ($xml->children() as $update)
{
// Sanity check
if ($update->getName() != 'update')
{
continue;
}
$entry = array(
'infourl' => array('title' => '',
'url' => ''),
'downloads' => array(),
'tags' => array(),
'targetplatform' => array(),
);
$properties = get_object_vars($update);
foreach ($properties as $nodeName => $nodeContent)
{
switch ($nodeName)
{
default:
$entry[$nodeName] = $nodeContent;
break;
case 'infourl':
case 'downloads':
case 'tags':
case 'targetplatform':
break;
}
}
$infourlNode = $update->xpath('infourl');
$entry['infourl']['title'] =
(string)$infourlNode[0]['title'];
$entry['infourl']['url'] = (string)$infourlNode[0];
$downloadNodes = $update->xpath('downloads/downloadurl');
foreach ($downloadNodes as $downloadNode)
{
$entry['downloads'][] = array(
'type' => (string)$downloadNode['type'],
'format' => (string)$downloadNode['format'],
'url' => (string)$downloadNode,
);
}
$tagNodes = $update->xpath('tags/tag');
foreach ($tagNodes as $tagNode)
{
$entry['tags'][] = (string)$tagNode;
}
/** @var SimpleXMLElement $targetPlatformNode */
$targetPlatformNode = $update->xpath('targetplatform');
$entry['targetplatform']['name'] =
(string)$targetPlatformNode[0]['name'];
$entry['targetplatform']['version'] =
(string)$targetPlatformNode[0]['version'];
$client = $targetPlatformNode[0]->xpath('client');
$entry['targetplatform']['client'] =
(is_array($client) && count($client)) ? (string)$client[0] :
'';
$folder = $targetPlatformNode[0]->xpath('folder');
$entry['targetplatform']['folder'] =
is_array($folder) && count($folder) ? (string)$folder[0] :
'';
$ret[] = $entry;
}
unset($xml);
return $ret;
}
}update/joomla.php000064400000040260151167373220010031 0ustar00<?php
/**
* @package FrameworkOnFramework
* @subpackage utils
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
* @note This file has been modified by the Joomla! Project and no
longer reflects the original work of its author.
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
/**
* A helper class which provides update information for the Joomla! CMS
itself. This is slightly different than the
* regular "extension" files as we need to know if a Joomla!
version is STS, LTS, testing, current and so on.
*/
class FOFUtilsUpdateJoomla extends FOFUtilsUpdateExtension
{
/**
* The source for LTS updates
*
* @var string
*/
protected static $lts_url =
'http://update.joomla.org/core/list.xml';
/**
* The source for STS updates
*
* @var string
*/
protected static $sts_url =
'http://update.joomla.org/core/sts/list_sts.xml';
/**
* The source for test release updates
*
* @var string
*/
protected static $test_url =
'http://update.joomla.org/core/test/list_test.xml';
/**
* Reads an "extension" XML update source and returns all listed
update entries.
*
* If you have a "collection" XML update source you should do
something like this:
* $collection = new CmsupdateHelperCollection();
* $extensionUpdateURL = $collection->getExtensionUpdateSource($url,
'component', 'com_foobar', JVERSION);
* $extension = new CmsupdateHelperExtension();
* $updates = $extension->getUpdatesFromExtension($extensionUpdateURL);
*
* @param string $url The extension XML update source URL to read from
*
* @return array An array of update entries
*/
public function getUpdatesFromExtension($url)
{
// Initialise
$ret = array();
// Get and parse the XML source
$downloader = new FOFDownload();
$xmlSource = $downloader->getFromURL($url);
try
{
$xml = new SimpleXMLElement($xmlSource, LIBXML_NONET);
}
catch (Exception $e)
{
return $ret;
}
// Sanity check
if (($xml->getName() != 'updates'))
{
unset($xml);
return $ret;
}
// Let's populate the list of updates
/** @var SimpleXMLElement $update */
foreach ($xml->children() as $update)
{
// Sanity check
if ($update->getName() != 'update')
{
continue;
}
$entry = array(
'infourl' => array('title' =>
'', 'url' => ''),
'downloads' => array(),
'tags' => array(),
'targetplatform' => array(),
);
$properties = get_object_vars($update);
foreach ($properties as $nodeName => $nodeContent)
{
switch ($nodeName)
{
default:
$entry[ $nodeName ] = $nodeContent;
break;
case 'infourl':
case 'downloads':
case 'tags':
case 'targetplatform':
break;
}
}
$infourlNode = $update->xpath('infourl');
$entry['infourl']['title'] = (string)
$infourlNode[0]['title'];
$entry['infourl']['url'] = (string)
$infourlNode[0];
$downloadNodes = $update->xpath('downloads/downloadurl');
foreach ($downloadNodes as $downloadNode)
{
$entry['downloads'][] = array(
'type' => (string) $downloadNode['type'],
'format' => (string) $downloadNode['format'],
'url' => (string) $downloadNode,
);
}
$tagNodes = $update->xpath('tags/tag');
foreach ($tagNodes as $tagNode)
{
$entry['tags'][] = (string) $tagNode;
}
/** @var SimpleXMLElement[] $targetPlatformNode */
$targetPlatformNode = $update->xpath('targetplatform');
$entry['targetplatform']['name'] = (string)
$targetPlatformNode[0]['name'];
$entry['targetplatform']['version'] = (string)
$targetPlatformNode[0]['version'];
$client =
$targetPlatformNode[0]->xpath('client');
$entry['targetplatform']['client'] =
(is_array($client) && count($client)) ? (string) $client[0] :
'';
$folder =
$targetPlatformNode[0]->xpath('folder');
$entry['targetplatform']['folder'] =
is_array($folder) && count($folder) ? (string) $folder[0] :
'';
$ret[] = $entry;
}
unset($xml);
return $ret;
}
/**
* Reads a "collection" XML update source and picks the correct
source URL
* for the extension update source.
*
* @param string $url The collection XML update source URL to read
from
* @param string $jVersion Joomla! version to fetch updates for, or null
to use JVERSION
*
* @return string The URL of the extension update source, or empty if no
updates are provided / fetching failed
*/
public function getUpdateSourceFromCollection($url, $jVersion = null)
{
$provider = new FOFUtilsUpdateCollection();
return $provider->getExtensionUpdateSource($url, 'file',
'joomla', $jVersion);
}
/**
* Determines the properties of a version: STS/LTS, normal or testing
*
* @param string $jVersion The version number to check
* @param string $currentVersion The current Joomla! version number
*
* @return array The properties analysis
*/
public function getVersionProperties($jVersion, $currentVersion = null)
{
// Initialise
$ret = array(
'lts' => true,
// Is this an LTS release? False means STS.
'current' => false,
// Is this a release in the $currentVersion branch?
'upgrade' => 'none',
// Upgrade relation of $jVersion to $currentVersion: 'none'
(can't upgrade), 'lts' (next or current LTS),
'sts' (next or current STS) or 'current' (same release,
no upgrade available)
'testing' => false,
// Is this a testing (alpha, beta, RC) release?
);
// Get the current version if none is defined
if (is_null($currentVersion))
{
$currentVersion = JVERSION;
}
// Sanitise version numbers
$sameVersion = $jVersion == $currentVersion;
$jVersion = $this->sanitiseVersion($jVersion);
$currentVersion = $this->sanitiseVersion($currentVersion);
$sameVersion = $sameVersion || ($jVersion == $currentVersion);
// Get the base version
$baseVersion = substr($jVersion, 0, 3);
// Get the minimum and maximum current version numbers
$current_minimum = substr($currentVersion, 0, 3);
$current_maximum = $current_minimum . '.9999';
// Initialise STS/LTS version numbers
$sts_minimum = false;
$sts_maximum = false;
$lts_minimum = false;
// Is it an LTS or STS release?
switch ($baseVersion)
{
case '1.5':
$ret['lts'] = true;
break;
case '1.6':
$ret['lts'] = false;
$sts_minimum = '1.7';
$sts_maximum = '1.7.999';
$lts_minimum = '2.5';
break;
case '1.7':
$ret['lts'] = false;
$sts_minimum = false;
$lts_minimum = '2.5';
break;
case '2.5':
$ret['lts'] = true;
$sts_minimum = false;
$lts_minimum = '2.5';
break;
default:
$majorVersion = (int) substr($jVersion, 0, 1);
//$minorVersion = (int) substr($jVersion, 2, 1);
$ret['lts'] = true;
$sts_minimum = false;
$lts_minimum = $majorVersion . '.0';
break;
}
// Is it a current release?
if (version_compare($jVersion, $current_minimum, 'ge')
&& version_compare($jVersion, $current_maximum, 'le'))
{
$ret['current'] = true;
}
// Is this a testing release?
$versionParts = explode('.', $jVersion);
$lastVersionPart = array_pop($versionParts);
if (in_array(substr($lastVersionPart, 0, 1), array('a',
'b')))
{
$ret['testing'] = true;
}
elseif (substr($lastVersionPart, 0, 2) == 'rc')
{
$ret['testing'] = true;
}
elseif (substr($lastVersionPart, 0, 3) == 'dev')
{
$ret['testing'] = true;
}
// Find the upgrade relation of $jVersion to $currentVersion
if (version_compare($jVersion, $currentVersion, 'eq'))
{
$ret['upgrade'] = 'current';
}
elseif (($sts_minimum !== false) && version_compare($jVersion,
$sts_minimum, 'ge') && version_compare($jVersion,
$sts_maximum, 'le'))
{
$ret['upgrade'] = 'sts';
}
elseif (($lts_minimum !== false) && version_compare($jVersion,
$lts_minimum, 'ge'))
{
$ret['upgrade'] = 'lts';
}
elseif ($baseVersion == $current_minimum)
{
$ret['upgrade'] = $ret['lts'] ? 'lts' :
'sts';
}
else
{
$ret['upgrade'] = 'none';
}
if ($sameVersion)
{
$ret['upgrade'] = 'none';
}
return $ret;
}
/**
* Filters a list of updates, making sure they apply to the specified CMS
* release.
*
* @param array $updates A list of update records returned by the
getUpdatesFromExtension method
* @param string $jVersion The current Joomla! version number
*
* @return array A filtered list of updates. Each update record also
includes version relevance information.
*/
public function filterApplicableUpdates($updates, $jVersion = null)
{
if (empty($jVersion))
{
$jVersion = JVERSION;
}
$versionParts = explode('.', $jVersion, 4);
$platformVersionMajor = $versionParts[0];
$platformVersionMinor = $platformVersionMajor . '.' .
$versionParts[1];
$platformVersionNormal = $platformVersionMinor . '.' .
$versionParts[2];
//$platformVersionFull = (count($versionParts) > 3) ?
$platformVersionNormal . '.' . $versionParts[3] :
$platformVersionNormal;
$ret = array();
foreach ($updates as $update)
{
// Check each update for platform match
if (strtolower($update['targetplatform']['name']) !=
'joomla')
{
continue;
}
$targetPlatformVersion =
$update['targetplatform']['version'];
if (!preg_match('/' . $targetPlatformVersion . '/',
$platformVersionMinor))
{
continue;
}
// Get some information from the version number
$updateVersion = $update['version'];
$versionProperties = $this->getVersionProperties($updateVersion,
$jVersion);
if ($versionProperties['upgrade'] == 'none')
{
continue;
}
// The XML files are ill-maintained. Maybe we already have this update?
if (!array_key_exists($updateVersion, $ret))
{
$ret[ $updateVersion ] = array_merge($update, $versionProperties);
}
}
return $ret;
}
/**
* Joomla! has a lousy track record in naming its alpha, beta and release
* candidate releases. The convention used seems to be "what the hell
the
* current package maintainer thinks looks better". This method tries
to
* figure out what was in the mind of the maintainer and translate the
* funky version number to an actual PHP-format version string.
*
* @param string $version The whatever-format version number
*
* @return string A standard formatted version number
*/
public function sanitiseVersion($version)
{
$test = strtolower($version);
$alphaQualifierPosition = strpos($test, 'alpha-');
$betaQualifierPosition = strpos($test, 'beta-');
$betaQualifierPosition2 = strpos($test, '-beta');
$rcQualifierPosition = strpos($test, 'rc-');
$rcQualifierPosition2 = strpos($test, '-rc');
$rcQualifierPosition3 = strpos($test, 'rc');
$devQualifiedPosition = strpos($test, 'dev');
if ($alphaQualifierPosition !== false)
{
$betaRevision = substr($test, $alphaQualifierPosition + 6);
if (!$betaRevision)
{
$betaRevision = 1;
}
$test = substr($test, 0, $alphaQualifierPosition) . '.a' .
$betaRevision;
}
elseif ($betaQualifierPosition !== false)
{
$betaRevision = substr($test, $betaQualifierPosition + 5);
if (!$betaRevision)
{
$betaRevision = 1;
}
$test = substr($test, 0, $betaQualifierPosition) . '.b' .
$betaRevision;
}
elseif ($betaQualifierPosition2 !== false)
{
$betaRevision = substr($test, $betaQualifierPosition2 + 5);
if (!$betaRevision)
{
$betaRevision = 1;
}
$test = substr($test, 0, $betaQualifierPosition2) . '.b' .
$betaRevision;
}
elseif ($rcQualifierPosition !== false)
{
$betaRevision = substr($test, $rcQualifierPosition + 5);
if (!$betaRevision)
{
$betaRevision = 1;
}
$test = substr($test, 0, $rcQualifierPosition) . '.rc' .
$betaRevision;
}
elseif ($rcQualifierPosition2 !== false)
{
$betaRevision = substr($test, $rcQualifierPosition2 + 3);
if (!$betaRevision)
{
$betaRevision = 1;
}
$test = substr($test, 0, $rcQualifierPosition2) . '.rc' .
$betaRevision;
}
elseif ($rcQualifierPosition3 !== false)
{
$betaRevision = substr($test, $rcQualifierPosition3 + 5);
if (!$betaRevision)
{
$betaRevision = 1;
}
$test = substr($test, 0, $rcQualifierPosition3) . '.rc' .
$betaRevision;
}
elseif ($devQualifiedPosition !== false)
{
$betaRevision = substr($test, $devQualifiedPosition + 6);
if (!$betaRevision)
{
$betaRevision = '';
}
$test = substr($test, 0, $devQualifiedPosition) . '.dev' .
$betaRevision;
}
return $test;
}
/**
* Reloads the list of all updates available for the specified Joomla!
version
* from the network.
*
* @param array $sources The enabled sources to look into
* @param string $jVersion The Joomla! version we are checking updates
for
*
* @return array A list of updates for the installed, current, lts and
sts versions
*/
public function getUpdates($sources = array(), $jVersion = null)
{
// Make sure we have a valid list of sources
if (empty($sources) || !is_array($sources))
{
$sources = array();
}
$defaultSources = array('lts' => true, 'sts' =>
true, 'test' => true, 'custom' => '');
$sources = array_merge($defaultSources, $sources);
// Use the current JVERSION if none is specified
if (empty($jVersion))
{
$jVersion = JVERSION;
}
// Get the current branch' min/max versions
$versionParts = explode('.', $jVersion, 4);
$currentMinVersion = $versionParts[0] . '.' . $versionParts[1];
$currentMaxVersion = $versionParts[0] . '.' . $versionParts[1]
. '.9999';
// Retrieve all updates
$allUpdates = array();
foreach ($sources as $source => $value)
{
if (($value === false) || empty($value))
{
continue;
}
switch ($source)
{
case 'lts':
$url = self::$lts_url;
break;
case 'sts':
$url = self::$sts_url;
break;
case 'test':
$url = self::$test_url;
break;
default:
case 'custom':
$url = $value;
break;
}
$url = $this->getUpdateSourceFromCollection($url, $jVersion);
if (!empty($url))
{
$updates = $this->getUpdatesFromExtension($url);
if (!empty($updates))
{
$applicableUpdates = $this->filterApplicableUpdates($updates,
$jVersion);
if (!empty($applicableUpdates))
{
$allUpdates = array_merge($allUpdates, $applicableUpdates);
}
}
}
}
$ret = array(
// Currently installed version (used to reinstall, if available)
'installed' => array(
'version' => '',
'package' => '',
'infourl' => '',
),
// Current branch
'current' => array(
'version' => '',
'package' => '',
'infourl' => '',
),
// Upgrade to STS release
'sts' => array(
'version' => '',
'package' => '',
'infourl' => '',
),
// Upgrade to LTS release
'lts' => array(
'version' => '',
'package' => '',
'infourl' => '',
),
// Upgrade to LTS release
'test' => array(
'version' => '',
'package' => '',
'infourl' => '',
),
);
foreach ($allUpdates as $update)
{
$sections = array();
if ($update['upgrade'] == 'current')
{
$sections[0] = 'installed';
}
elseif (version_compare($update['version'],
$currentMinVersion, 'ge') &&
version_compare($update['version'], $currentMaxVersion,
'le'))
{
$sections[0] = 'current';
}
else
{
$sections[0] = '';
}
$sections[1] = $update['lts'] ? 'lts' :
'sts';
if ($update['testing'])
{
$sections = array('test');
}
foreach ($sections as $section)
{
if (empty($section))
{
continue;
}
$existingVersionForSection = $ret[ $section ]['version'];
if (empty($existingVersionForSection))
{
$existingVersionForSection = '0.0.0';
}
if (version_compare($update['version'],
$existingVersionForSection, 'ge'))
{
$ret[ $section ]['version'] = $update['version'];
$ret[ $section ]['package'] =
$update['downloads'][0]['url'];
$ret[ $section ]['infourl'] =
$update['infourl']['url'];
}
}
}
// Catch the case when the latest current branch version is the installed
version (up to date site)
if (empty($ret['current']['version']) &&
!empty($ret['installed']['version']))
{
$ret['current'] = $ret['installed'];
}
return $ret;
}
}update/update.php000064400000101122151167373220010025 0ustar00<?php
/**
* @package FrameworkOnFramework
* @subpackage utils
* @copyright Copyright (C) 2010-2016 Nicholas K. Dionysopoulos / Akeeba
Ltd. All rights reserved.
* @license GNU General Public License version 2 or later; see
LICENSE.txt
*/
// Protect from unauthorized access
defined('FOF_INCLUDED') or die;
if (version_compare(JVERSION, '2.5.0', 'lt'))
{
jimport('joomla.updater.updater');
}
/**
* A helper Model to interact with Joomla!'s extensions update feature
*/
class FOFUtilsUpdate extends FOFModel
{
/** @var JUpdater The Joomla! updater object */
protected $updater = null;
/** @var int The extension_id of this component */
protected $extension_id = 0;
/** @var string The currently installed version, as reported by the
#__extensions table */
protected $version = 'dev';
/** @var string The machine readable name of the component e.g.
com_something */
protected $component = 'com_foobar';
/** @var string The human readable name of the component e.g. Your
Component's Name. Used for emails. */
protected $componentDescription = 'Foobar';
/** @var string The URL to the component's update XML stream */
protected $updateSite = null;
/** @var string The name to the component's update site (description
of the update XML stream) */
protected $updateSiteName = null;
/** @var string The extra query to append to (commercial) components'
download URLs */
protected $extraQuery = null;
/** @var string The common parameters' key, used for storing data in
the #__akeeba_common table */
protected $commonKey = 'foobar';
/**
* The common parameters table. It's a simple table with key(VARCHAR)
and value(LONGTEXT) fields.
* Here is an example MySQL CREATE TABLE command to make this kind of
table:
*
* CREATE TABLE `#__akeeba_common` (
* `key` varchar(255) NOT NULL,
* `value` longtext NOT NULL,
* PRIMARY KEY (`key`)
* ) DEFAULT COLLATE utf8_general_ci CHARSET=utf8;
*
* @var string
*/
protected $commonTable = '#__akeeba_common';
/**
* Subject of the component update emails
*
* @var string
*/
protected $updateEmailSubject = 'THIS EMAIL IS SENT FROM YOUR SITE
"[SITENAME]" - Update available for [COMPONENT], new version
[VERSION]';
/**
* Body of the component update email
*
* @var string
*/
protected $updateEmailBody= <<< ENDBLOCK
This email IS NOT sent by the authors of [COMPONENT].
It is sent automatically by your own site, [SITENAME].
================================================================================
UPDATE INFORMATION
================================================================================
Your site has determined that there is an updated version of [COMPONENT]
available for download.
New version number: [VERSION]
This email is sent to you by your site to remind you of this fact. The
authors
of the software will never contact you about available updates.
================================================================================
WHY AM I RECEIVING THIS EMAIL?
================================================================================
This email has been automatically sent by a CLI script or Joomla! plugin
you, or
the person who built or manages your site, has installed and explicitly
activated. This script or plugin looks for updated versions of the software
and
sends an email notification to all Super Users. You will receive several
similar
emails from your site, up to 6 times per day, until you either update the
software or disable these emails.
To disable these emails, please contact your site administrator.
If you do not understand what this means, please do not contact the authors
of
the software. They are NOT sending you this email and they cannot help you.
Instead, please contact the person who built or manages your site.
================================================================================
WHO SENT ME THIS EMAIL?
================================================================================
This email is sent to you by your own site, [SITENAME]
ENDBLOCK;
/**
* Public constructor. Initialises the protected members as well. Useful
$config keys:
* update_component The component name, e.g. com_foobar
* update_version The default version if the manifest cache is unreadable
* update_site The URL to the component's update XML stream
* update_extraquery The extra query to append to (commercial)
components' download URLs
* update_sitename The update site's name (description)
*
* @param array $config
*/
public function __construct($config = array())
{
parent::__construct($config);
// Get an instance of the updater class
$this->updater = JUpdater::getInstance();
// Get the component name
if (isset($config['update_component']))
{
$this->component = $config['update_component'];
}
else
{
$this->component = $this->input->getCmd('option',
'');
}
// Get the component description
if (isset($config['update_component_description']))
{
$this->component = $config['update_component_description'];
}
else
{
// Try to auto-translate (hopefully you've loaded the language
files)
$key = strtoupper($this->component);
$description = JText::_($key);
}
// Get the component version
if (isset($config['update_version']))
{
$this->version = $config['update_version'];
}
// Get the common key
if (isset($config['common_key']))
{
$this->commonKey = $config['common_key'];
}
else
{
// Convert com_foobar, pkg_foobar etc to "foobar"
$this->commonKey = substr($this->component, 4);
}
// Get the update site
if (isset($config['update_site']))
{
$this->updateSite = $config['update_site'];
}
// Get the extra query
if (isset($config['update_extraquery']))
{
$this->extraQuery = $config['update_extraquery'];
}
// Get the update site's name
if (isset($config['update_sitename']))
{
$this->updateSiteName = $config['update_sitename'];
}
// Find the extension ID
$db = FOFPlatform::getInstance()->getDbo();
$query = $db->getQuery(true)
->select('*')
->from($db->qn('#__extensions'))
->where($db->qn('type') . ' = ' .
$db->q('component'))
->where($db->qn('element') . ' = ' .
$db->q($this->component));
$db->setQuery($query);
$extension = $db->loadObject();
if (is_object($extension))
{
$this->extension_id = $extension->extension_id;
$data = json_decode($extension->manifest_cache, true);
if (isset($data['version']))
{
$this->version = $data['version'];
}
}
}
/**
* Retrieves the update information of the component, returning an array
with the following keys:
*
* hasUpdate True if an update is available
* version The version of the available update
* infoURL The URL to the download page of the update
*
* @param bool $force Set to true if you want to forcibly
reload the update information
* @param string $preferredMethod Preferred update method:
'joomla' or 'classic'
*
* @return array See the method description for more information
*/
public function getUpdates($force = false, $preferredMethod = null)
{
// Default response (no update)
$updateResponse = array(
'hasUpdate' => false,
'version' => '',
'infoURL' => '',
'downloadURL' => '',
);
if (empty($this->extension_id))
{
return $updateResponse;
}
$updateRecord = $this->findUpdates($force, $preferredMethod);
// If we have an update record in the database return the information
found there
if (is_object($updateRecord))
{
$updateResponse = array(
'hasUpdate' => true,
'version' => $updateRecord->version,
'infoURL' => $updateRecord->infourl,
'downloadURL' => $updateRecord->downloadurl,
);
}
return $updateResponse;
}
/**
* Find the available update record object. If we're at the latest
version it will return null.
*
* Please see getUpdateMethod for information on how the $preferredMethod
is handled and what it means.
*
* @param bool $force Should I forcibly reload the updates
from the server?
* @param string $preferredMethod Preferred update method:
'joomla' or 'classic'
*
* @return \stdClass|null
*/
public function findUpdates($force, $preferredMethod = null)
{
$preferredMethod = $this->getUpdateMethod($preferredMethod);
switch ($preferredMethod)
{
case 'joomla':
return $this->findUpdatesJoomla($force);
break;
default:
case 'classic':
return $this->findUpdatesClassic($force);
break;
}
}
/**
* Gets the update site Ids for our extension.
*
* @return mixed An array of Ids or null if the query failed.
*/
public function getUpdateSiteIds()
{
$db = FOFPlatform::getInstance()->getDbo();
$query = $db->getQuery(true)
->select($db->qn('update_site_id'))
->from($db->qn('#__update_sites_extensions'))
->where($db->qn('extension_id') . ' = ' .
$db->q($this->extension_id));
$db->setQuery($query);
$updateSiteIds = $db->loadColumn(0);
return $updateSiteIds;
}
/**
* Get the currently installed version as reported by the #__extensions
table
*
* @return string
*/
public function getVersion()
{
return $this->version;
}
/**
* Returns the name of the component, e.g. com_foobar
*
* @return string
*/
public function getComponentName()
{
return $this->component;
}
/**
* Returns the human readable component name, e.g. Foobar Component
*
* @return string
*/
public function getComponentDescription()
{
return $this->componentDescription;
}
/**
* Returns the numeric extension ID for the component
*
* @return int
*/
public function getExtensionId()
{
return $this->extension_id;
}
/**
* Returns the update site URL, i.e. the URL to the XML update stream
*
* @return string
*/
public function getUpdateSite()
{
return $this->updateSite;
}
/**
* Returns the human readable description of the update site
*
* @return string
*/
public function getUpdateSiteName()
{
return $this->updateSiteName;
}
/**
* Override the currently installed version as reported by the
#__extensions table
*
* @param string $version
*/
public function setVersion($version)
{
$this->version = $version;
}
/**
* Refreshes the Joomla! update sites for this extension as needed
*
* @return void
*/
public function refreshUpdateSite()
{
// Joomla! 1.5 does not have update sites.
if (version_compare(JVERSION, '1.6.0', 'lt'))
{
return;
}
if (empty($this->extension_id))
{
return;
}
// Remove obsolete update sites that don't match our extension ID
but match our name or update site location
$this->removeObsoleteUpdateSites();
// Create the update site definition we want to store to the database
$update_site = array(
'name' => $this->updateSiteName,
'type' => 'extension',
'location' => $this->updateSite,
'enabled' => 1,
'last_check_timestamp' => 0,
'extra_query' => $this->extraQuery
);
// Get a reference to the db driver
$db = FOFPlatform::getInstance()->getDbo();
// Get the #__update_sites columns
$columns = $db->getTableColumns('#__update_sites', true);
if (version_compare(JVERSION, '3.2.0', 'lt') ||
!array_key_exists('extra_query', $columns))
{
unset($update_site['extra_query']);
}
if (version_compare(JVERSION, '2.5.0', 'lt') ||
!array_key_exists('extra_query', $columns))
{
unset($update_site['last_check_timestamp']);
}
// Get the update sites for our extension
$updateSiteIds = $this->getUpdateSiteIds();
if (empty($updateSiteIds))
{
$updateSiteIds = array();
}
/** @var boolean $needNewUpdateSite Do I need to create a new update
site? */
$needNewUpdateSite = true;
/** @var int[] $deleteOldSites Old Site IDs to delete */
$deleteOldSites = array();
// Loop through all update sites
foreach ($updateSiteIds as $id)
{
$query = $db->getQuery(true)
->select('*')
->from($db->qn('#__update_sites'))
->where($db->qn('update_site_id') . ' = ' .
$db->q($id));
$db->setQuery($query);
$aSite = $db->loadObject();
if (empty($aSite))
{
// Update site is now up-to-date, don't need to refresh it
anymore.
continue;
}
// We have an update site that looks like ours
if ($needNewUpdateSite && ($aSite->name ==
$update_site['name']) && ($aSite->location ==
$update_site['location']))
{
$needNewUpdateSite = false;
$mustUpdate = false;
// Is it enabled? If not, enable it.
if (!$aSite->enabled)
{
$mustUpdate = true;
$aSite->enabled = 1;
}
// Do we have the extra_query property (J 3.2+) and does it match?
if (property_exists($aSite, 'extra_query') &&
isset($update_site['extra_query'])
&& ($aSite->extra_query !=
$update_site['extra_query']))
{
$mustUpdate = true;
$aSite->extra_query = $update_site['extra_query'];
}
// Update the update site if necessary
if ($mustUpdate)
{
$db->updateObject('#__update_sites', $aSite,
'update_site_id', true);
}
continue;
}
// In any other case we need to delete this update site, it's
obsolete
$deleteOldSites[] = $aSite->update_site_id;
}
if (!empty($deleteOldSites))
{
try
{
$obsoleteIDsQuoted = array_map(array($db, 'quote'),
$deleteOldSites);
// Delete update sites
$query = $db->getQuery(true)
->delete('#__update_sites')
->where($db->qn('update_site_id') . ' IN
(' . implode(',', $obsoleteIDsQuoted) . ')');
$db->setQuery($query)->execute();
// Delete update sites to extension ID records
$query = $db->getQuery(true)
->delete('#__update_sites_extensions')
->where($db->qn('update_site_id') . ' IN
(' . implode(',', $obsoleteIDsQuoted) . ')');
$db->setQuery($query)->execute();
}
catch (\Exception $e)
{
// Do nothing on failure
return;
}
}
// Do we still need to create a new update site?
if ($needNewUpdateSite)
{
// No update sites defined. Create a new one.
$newSite = (object)$update_site;
$db->insertObject('#__update_sites', $newSite);
$id = $db->insertid();
$updateSiteExtension = (object)array(
'update_site_id' => $id,
'extension_id' => $this->extension_id,
);
$db->insertObject('#__update_sites_extensions',
$updateSiteExtension);
}
}
/**
* Removes any update sites which go by the same name or the same location
as our update site but do not match the
* extension ID.
*/
public function removeObsoleteUpdateSites()
{
$db = $this->getDbo();
// Get update site IDs
$updateSiteIDs = $this->getUpdateSiteIds();
// Find update sites where the name OR the location matches BUT they are
not one of the update site IDs
$query = $db->getQuery(true)
->select($db->qn('update_site_id'))
->from($db->qn('#__update_sites'))
->where(
'((' . $db->qn('name') . ' = ' .
$db->q($this->updateSiteName) . ') OR ' .
'(' . $db->qn('location') . ' = ' .
$db->q($this->updateSite) . '))'
);
if (!empty($updateSiteIDs))
{
$updateSitesQuoted = array_map(array($db, 'quote'),
$updateSiteIDs);
$query->where($db->qn('update_site_id') . ' NOT IN
(' . implode(',', $updateSitesQuoted) . ')');
}
try
{
$ids = $db->setQuery($query)->loadColumn();
if (!empty($ids))
{
$obsoleteIDsQuoted = array_map(array($db, 'quote'), $ids);
// Delete update sites
$query = $db->getQuery(true)
->delete('#__update_sites')
->where($db->qn('update_site_id') . ' IN
(' . implode(',', $obsoleteIDsQuoted) . ')');
$db->setQuery($query)->execute();
// Delete update sites to extension ID records
$query = $db->getQuery(true)
->delete('#__update_sites_extensions')
->where($db->qn('update_site_id') . ' IN
(' . implode(',', $obsoleteIDsQuoted) . ')');
$db->setQuery($query)->execute();
}
}
catch (\Exception $e)
{
// Do nothing on failure
return;
}
}
/**
* Get the update method we should use, 'joomla' or
'classic'
*
* You can defined the preferred update method: 'joomla' uses
JUpdater whereas 'classic' handles update caching and
* parsing internally. If you are on Joomla! 3.1 or earlier this option is
forced to 'classic' since these old
* Joomla! versions couldn't handle updates of commercial components
correctly (that's why I contributed the fix to
* that problem, the extra_query field that's present in Joomla! 3.2
onwards).
*
* If 'classic' is defined then it will be used in *all* Joomla!
versions. It's the most stable method for fetching
* update information.
*
* @param string $preferred Preferred update method. One of
'joomla' or 'classic'.
*
* @return string
*/
public function getUpdateMethod($preferred = null)
{
$method = $preferred;
// Make sure the update fetch method is valid, otherwise load the
component's "update_method" parameter.
$validMethods = array('joomla', 'classic');
if (!in_array($method, $validMethods))
{
$method =
FOFUtilsConfigHelper::getComponentConfigurationValue($this->component,
'update_method', 'joomla');
}
// We can't handle updates using Joomla!'s extensions updater
in Joomla! 3.1 and earlier
if (($method == 'joomla') && version_compare(JVERSION,
'3.2.0', 'lt'))
{
$method = 'classic';
}
return $method;
}
/**
* Find the available update record object. If we're at the latest
version it will return null.
*
* @param bool $force Should I forcibly reload the updates from the
server?
*
* @return \stdClass|null
*/
protected function findUpdatesJoomla($force = false)
{
$db = FOFPlatform::getInstance()->getDbo();
// If we are forcing the reload, set the last_check_timestamp to 0
// and remove cached component update info in order to force a reload
if ($force)
{
// Find the update site IDs
$updateSiteIds = $this->getUpdateSiteIds();
if (empty($updateSiteIds))
{
return null;
}
// Set the last_check_timestamp to 0
if (version_compare(JVERSION, '2.5.0', 'ge'))
{
$query = $db->getQuery(true)
->update($db->qn('#__update_sites'))
->set($db->qn('last_check_timestamp') . ' =
' . $db->q('0'))
->where($db->qn('update_site_id') .' IN
('.implode(', ', $updateSiteIds).')');
$db->setQuery($query);
$db->execute();
}
// Remove cached component update info from #__updates
$query = $db->getQuery(true)
->delete($db->qn('#__updates'))
->where($db->qn('update_site_id') .' IN
('.implode(', ', $updateSiteIds).')');
$db->setQuery($query);
$db->execute();
}
// Use the update cache timeout specified in com_installer
$timeout = 3600 *
FOFUtilsConfigHelper::getComponentConfigurationValue('com_installer',
'cachetimeout', '6');
// Load any updates from the network into the #__updates table
$this->updater->findUpdates($this->extension_id, $timeout);
// Get the update record from the database
$query = $db->getQuery(true)
->select('*')
->from($db->qn('#__updates'))
->where($db->qn('extension_id') . ' = ' .
$db->q($this->extension_id));
$db->setQuery($query);
try
{
$updateObject = $db->loadObject();
}
catch (Exception $e)
{
return null;
}
if (!is_object($updateObject))
{
return null;
}
$updateObject->downloadurl = '';
JLoader::import('joomla.updater.update');
if (class_exists('JUpdate'))
{
$update = new JUpdate();
$update->loadFromXML($updateObject->detailsurl);
if (isset($update->get('downloadurl')->_data))
{
$url = trim($update->downloadurl->_data);
$extra_query = isset($updateObject->extra_query) ?
$updateObject->extra_query : $this->extraQuery;
if ($extra_query)
{
if (strpos($url, '?') === false)
{
$url .= '?';
}
else
{
$url .= '&';
}
$url .= $extra_query;
}
$updateObject->downloadurl = $url;
}
}
return $updateObject;
}
/**
* Find the available update record object. If we're at the latest
version return null.
*
* @param bool $force Should I forcibly reload the updates from the
server?
*
* @return \stdClass|null
*/
protected function findUpdatesClassic($force = false)
{
$allUpdates = $this->loadUpdatesClassic($force);
if (empty($allUpdates))
{
return null;
}
$bestVersion = '0.0.0';
$bestUpdate = null;
$bestUpdateObject = null;
foreach($allUpdates as $update)
{
if (!isset($update['version']))
{
continue;
}
if (version_compare($bestVersion, $update['version'],
'lt'))
{
$bestVersion = $update['version'];
$bestUpdate = $update;
}
}
// If the current version is newer or equal to the best one, unset
it. Otherwise the user will be always prompted to update
if(version_compare($this->version, $bestVersion,
'ge'))
{
$bestUpdate = null;
$bestVersion = '0.0.0';
}
if (!is_null($bestUpdate))
{
$url = '';
if (isset($bestUpdate['downloads']) &&
isset($bestUpdate['downloads'][0])
&&
isset($bestUpdate['downloads'][0]['url']))
{
$url = $bestUpdate['downloads'][0]['url'];
}
if ($this->extraQuery)
{
if (strpos($url, '?') === false)
{
$url .= '?';
}
else
{
$url .= '&';
}
$url .= $this->extraQuery;
}
$bestUpdateObject = (object)array(
'update_id' => 0,
'update_site_id' => 0,
'extension_id' => $this->extension_id,
'name' => $this->updateSiteName,
'description' => $bestUpdate['description'],
'element' => $bestUpdate['element'],
'type' => $bestUpdate['type'],
'folder' =>
count($bestUpdate['folder']) ? $bestUpdate['folder'][0]
: '',
'client_id' =>
isset($bestUpdate['client']) ? $bestUpdate['client'] :
0,
'version' => $bestUpdate['version'],
'data' => '',
'detailsurl' => $this->updateSite,
'infourl' =>
$bestUpdate['infourl']['url'],
'extra_query' => $this->extraQuery,
'downloadurl' => $url,
);
}
return $bestUpdateObject;
}
/**
* Load all available updates without going through JUpdate
*
* @param bool $force Should I forcibly reload the updates from the
server?
*
* @return array
*/
protected function loadUpdatesClassic($force = false)
{
// Is the cache busted? If it is I set $force = true to make sure I
download fresh updates
if (!$force)
{
// Get the cache timeout. On older Joomla! installations it will always
default to 6 hours.
$timeout = 3600 *
FOFUtilsConfigHelper::getComponentConfigurationValue('com_installer',
'cachetimeout', '6');
// Do I need to check for updates?
$lastCheck = $this->getCommonParameter('lastcheck', 0);
$now = time();
if (($now - $lastCheck) >= $timeout)
{
$force = true;
}
}
// Get the cached JSON-encoded updates list
$rawUpdates = $this->getCommonParameter('allUpdates',
'');
// Am I forced to reload the XML file (explicitly or because the cache is
busted)?
if ($force)
{
// Set the timestamp
$now = time();
$this->setCommonParameter('lastcheck', $now);
// Get all available updates
$updateHelper = new FOFUtilsUpdateExtension();
$updates =
$updateHelper->getUpdatesFromExtension($this->updateSite);
// Save the raw updates list in the database
$rawUpdates = json_encode($updates);
$this->setCommonParameter('allUpdates', $rawUpdates);
}
// Decode the updates list
$updates = json_decode($rawUpdates, true);
// Walk through the updates and find the ones compatible with our Joomla!
and PHP version
$compatibleUpdates = array();
// Get the Joomla! version family (e.g. 2.5)
$jVersion = JVERSION;
$jVersionParts = explode('.', $jVersion);
$jVersionShort = $jVersionParts[0] . '.' . $jVersionParts[1];
// Get the PHP version family (e.g. 5.6)
$phpVersion = PHP_VERSION;
$phpVersionParts = explode('.', $phpVersion);
$phpVersionShort = $phpVersionParts[0] . '.' .
$phpVersionParts[1];
foreach ($updates as $update)
{
// No platform?
if (!isset($update['targetplatform']))
{
continue;
}
// Wrong platform?
if ($update['targetplatform']['name'] !=
'joomla')
{
continue;
}
// Get the target Joomla! version
$targetJoomlaVersion =
$update['targetplatform']['version'];
$targetVersionParts = explode('.', $targetJoomlaVersion);
$targetVersionShort = $targetVersionParts[0] . '.' .
$targetVersionParts[1];
// The target version MUST be in the same Joomla! branch
if ($jVersionShort != $targetVersionShort)
{
continue;
}
// If the target version is major.minor.revision we must make sure our
current JVERSION is AT LEAST equal to that.
if (version_compare($targetJoomlaVersion, JVERSION, 'gt'))
{
continue;
}
// Do I have target PHP versions?
if (isset($update['ars-phpcompat']))
{
$phpCompatible = false;
foreach ($update['ars-phpcompat'] as $entry)
{
// Get the target PHP version family
$targetPHPVersion =
$entry['@attributes']['version'];
$targetPHPVersionParts = explode('.', $targetPHPVersion);
$targetPHPVersionShort = $targetPHPVersionParts[0] . '.' .
$targetPHPVersionParts[1];
// The target PHP version MUST be in the same PHP branch
if ($phpVersionShort != $targetPHPVersionShort)
{
continue;
}
// If the target version is major.minor.revision we must make sure our
current PHP_VERSION is AT LEAST equal to that.
if (version_compare($targetPHPVersion, PHP_VERSION, 'gt'))
{
continue;
}
$phpCompatible = true;
break;
}
if (!$phpCompatible)
{
continue;
}
}
// All checks pass. Add this update to the list of compatible updates.
$compatibleUpdates[] = $update;
}
return $compatibleUpdates;
}
/**
* Get a common parameter from the #__akeeba_common table
*
* @param string $key The key to retrieve
* @param mixed $default The default value in case none is set
*
* @return mixed The saved parameter value (or $default, if nothing is
currently set)
*/
protected function getCommonParameter($key, $default = null)
{
$dbKey = $this->commonKey . '_autoupdate_' . $key;
$db = FOFPlatform::getInstance()->getDbo();
$query = $db->getQuery(true)
->select($db->qn('value'))
->from($db->qn($this->commonTable))
->where($db->qn('key') . ' = ' .
$db->q($dbKey));
$result = $db->setQuery($query)->loadResult();
if (!$result)
{
return $default;
}
return $result;
}
/**
* Set a common parameter from the #__akeeba_common table
*
* @param string $key The key to set
* @param mixed $value The value to set
*
* @return void
*/
protected function setCommonParameter($key, $value)
{
$dbKey = $this->commonKey . '_autoupdate_' . $key;
$db = FOFPlatform::getInstance()->getDbo();
$query = $db->getQuery(true)
->select('COUNT(*)')
->from($db->qn($this->commonTable))
->where($db->qn('key') . ' = ' .
$db->q($dbKey));
$count = $db->setQuery($query)->loadResult();
if ($count)
{
$query = $db->getQuery(true)
->update($db->qn($this->commonTable))
->set($db->qn('value') . ' = ' .
$db->q($value))
->where($db->qn('key') . ' = ' .
$db->q($dbKey));
$db->setQuery($query)->execute();
}
else
{
$data = (object)array(
'key' => $dbKey,
'value' => $value,
);
$db->insertObject($this->commonTable, $data);
}
}
/**
* Proxy to updateComponent(). Required since old versions of our software
had an updateComponent method declared
* private. If we set the updateComponent() method public we cause a fatal
error.
*
* @return string
*/
public function doUpdateComponent()
{
return $this->updateComponent();
}
/**
* Automatically install the extension update under Joomla! 1.5.5 or later
(web) / 3.0 or later (CLI).
*
* @return string The update message
*/
private function updateComponent()
{
$isCli = FOFPlatform::getInstance()->isCli();
$minVersion = $isCli ? '3.0.0' : '1.5.5';
$errorQualifier = $isCli ? ' using an unattended CLI CRON script
' : ' ';
if (version_compare(JVERSION, $minVersion, 'lt'))
{
return "Extension updates{$errorQualifier}only work with Joomla!
$minVersion and later.";
}
try
{
$updatePackagePath = $this->downloadUpdate();
}
catch (Exception $e)
{
return $e->getMessage();
}
// Unpack the downloaded package file
jimport('joomla.installer.helper');
jimport('cms.installer.helper');
$package = JInstallerHelper::unpack($updatePackagePath);
if (!$package)
{
// Clean up
if (JFile::exists($updatePackagePath))
{
JFile::delete($updatePackagePath);
}
return "An error occurred while unpacking the file. Please double
check your Joomla temp-directory setting in Global Configuration.";
}
$installer = new JInstaller;
$installed = $installer->install($package['extractdir']);
// Let's cleanup the downloaded archive and the temp folder
if (JFolder::exists($package['extractdir']))
{
JFolder::delete($package['extractdir']);
}
if (JFile::exists($package['packagefile']))
{
JFile::delete($package['packagefile']);
}
if ($installed)
{
return "Component successfully updated";
}
else
{
return "An error occurred while trying to update the
component";
}
}
/**
* Downloads the latest update package to Joomla!'s temporary
directory
*
* @return string The absolute path to the downloaded update package.
*/
public function downloadUpdate()
{
// Get the update URL
$updateInformation = $this->getUpdates();
$url = $updateInformation['downloadURL'];
if (empty($url))
{
throw new RuntimeException("No download URL was provided in the
update information");
}
$config = JFactory::getConfig();
$tmp_dest = $config->get('tmp_path');
if (!$tmp_dest)
{
throw new RuntimeException("You must set a non-empty Joomla!
temp-directory in Global Configuration before continuing.");
}
if (!JFolder::exists($tmp_dest))
{
throw new RuntimeException("Joomla!'s temp-directory does not
exist. Please set the correct path in Global Configuration before
continuing.");
}
// Get the target filename
$filename = $this->component . '.zip';
$filename = rtrim($tmp_dest, '\\/') . '/' .
$filename;
try
{
$downloader = new FOFDownload();
$data = $downloader->getFromURL($url);
}
catch (Exception $e)
{
$code =$e->getCode();
$message =$e->getMessage();
throw new RuntimeException("An error occurred while trying to
download the update package. Double check your Download ID and your
server's network settings. The error message was: #$code:
$message");
}
if (!JFile::write($filename, $data))
{
if (!file_put_contents($filename, $data))
{
throw new RuntimeException("Joomla!'s temp-directory is not
writeable. Please check its permissions or set a different, writeable path
in Global Configuration before continuing.");
}
}
return $filename;
}
/**
* Gets a file name out of a url
*
* @param string $url URL to get name from
*
* @return mixed String filename or boolean false if failed
*/
private function getFilenameFromURL($url)
{
if (is_string($url))
{
$parts = explode('/', $url);
return $parts[count($parts) - 1];
}
return false;
}
/**
* Proxy to sendNotificationEmail(). Required since old versions of our
software had a sendNotificationEmail method
* declared private. If we set the sendNotificationEmail() method public
we cause a fatal error.
*
* @param string $version The new version of our software
* @param string $email The email address to send the notification
to
*
* @return mixed The result of JMail::send()
*/
public function doSendNotificationEmail($version, $email)
{
try
{
return $this->sendNotificationEmail($version, $email);
}
catch (\Exception $e)
{
// Joomla! 3.5 is buggy
}
}
/**
* Sends an update notification email
*
* @param string $version The new version of our software
* @param string $email The email address to send the notification
to
*
* @return mixed The result of JMail::send()
*/
private function sendNotificationEmail($version, $email)
{
$email_subject = $this->updateEmailSubject;
$email_body = $this->updateEmailBody;
$jconfig = JFactory::getConfig();
$sitename = $jconfig->get('sitename');
$substitutions = array(
'[VERSION]' => $version,
'[SITENAME]' => $sitename,
'[COMPONENT]' => $this->componentDescription,
);
$email_subject = str_replace(array_keys($substitutions),
array_values($substitutions), $email_subject);
$email_body = str_replace(array_keys($substitutions),
array_values($substitutions), $email_body);
$mailer = JFactory::getMailer();
$mailfrom = $jconfig->get('mailfrom');
$fromname = $jconfig->get('fromname');
$mailer->setSender(array( $mailfrom, $fromname ));
$mailer->addRecipient($email);
$mailer->setSubject($email_subject);
$mailer->setBody($email_body);
return $mailer->Send();
}
}