| Current Path : /proc/1908984/root/proc/self/root/proc/self/root/proc/2411249/cwd/ |
| Current File : //proc/1908984/root/proc/self/root/proc/self/root/proc/2411249/cwd/Update.tar |
Collection.php 0000644 00000023363 15234623540 0007363 0 ustar 00 <?php
/**
* @package FOF
* @copyright Copyright (c)2010-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 2 or later
*/
namespace FOF30\Update;
use FOF30\Container\Container;
use FOF30\Download\Download;
use Exception;
use SimpleXMLElement;
defined('_JEXEC') or die;
class Collection
{
/**
* 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
$container = Container::getInstance('com_foobar');
$downloader = new Download($container);
$xmlSource = $downloader->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']))
{
continue;
}
// We can't have a category with an empty ref
if (empty($params['ref']))
{
continue;
}
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']))
{
continue;
}
// We can't have an extension with an empty type
if (empty($params['type']))
{
continue;
}
// We can't have an extension with an empty version
if (empty($params['version']))
{
continue;
}
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;
}
}
}
Extension.php 0000644 00000006525 15234623540 0007245 0 ustar 00 <?php
/**
* @package FOF
* @copyright Copyright (c)2010-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 2 or later
*/
namespace FOF30\Update;
use FOF30\Container\Container;
use FOF30\Download\Download;
use Exception;
use SimpleXMLElement;
defined('_JEXEC') or die;
/**
* A helper class to read and parse "extension" update XML files over the web
*/
class Extension
{
/**
* 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 F0FUtilsUpdateCollection();
* $extensionUpdateURL = $collection->getExtensionUpdateSource($url, 'component', 'com_foobar', JVERSION);
* $extension = new F0FUtilsUpdateExtension();
* $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
$container = Container::getInstance('com_FOOBAR');
$downloader = new Download($container);
$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;
}
}
Joomla.php 0000644 00000031305 15234623540 0006504 0 ustar 00 <?php
/**
* @package FOF
* @copyright Copyright (c)2010-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 2 or later
*/
namespace FOF30\Update;
defined('_JEXEC') or die;
class Joomla extends Extension
{
/**
* 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 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 Collection();
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 specifed 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)
{
default:
case 'lts':
$url = self::$lts_url;
break;
case 'sts':
$url = self::$sts_url;
break;
case 'test':
$url = self::$test_url;
break;
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.php 0000644 00000032372 15234623540 0006512 0 ustar 00 <?php
/**
* @package FOF
* @copyright Copyright (c)2010-2019 Nicholas K. Dionysopoulos / Akeeba Ltd
* @license GNU GPL version 2 or later
*/
namespace FOF30\Update;
use FOF30\Container\Container;
use FOF30\Model\Model;
use JUpdater;
defined('_JEXEC') or die;
/**
* A helper Model to interact with Joomla!'s extensions update feature
*/
class Update extends Model
{
/** @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 name of the component e.g. com_something */
protected $component = 'com_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;
/**
* 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())
{
$container = Container::getInstance('com_FOOBAR');
parent::__construct($container);
// 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 version
if (isset($config['update_version']))
{
$this->version = $config['update_version'];
}
// 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 extra query
if (isset($config['update_sitename']))
{
$this->updateSiteName = $config['update_sitename'];
}
// Get the extension type
$extension = $this->getExtensionObject();
if (is_object($extension))
{
$this->extension_id = $extension->extension_id;
if (empty($this->version) || ($this->version == 'dev'))
{
$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
*
* @return array See the method description for more information
*/
public function getUpdates($force = false)
{
$db = $this->container->db;
// Default response (no update)
$updateResponse = array(
'hasUpdate' => false,
'version' => '',
'infoURL' => ''
);
if (empty($this->extension_id))
{
return $updateResponse;
}
// If we had to update the version number stored in the database then we should force reload the updates
if ($this->updatedCachedVersionNumber())
{
$force = true;
}
// 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 $updateResponse;
}
// Set the last_check_timestamp to 0
$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
$comInstallerParams = \JComponentHelper::getParams('com_installer', false);
$timeout = 3600 * $comInstallerParams->get('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);
$updateRecord = $db->loadObject();
// 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,
);
}
return $updateResponse;
}
/**
* Gets the update site Ids for our extension.
*
* @return mixed An array of Ids or null if the query failed.
*/
public function getUpdateSiteIds()
{
$db = $this->container->db;
$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;
}
/**
* 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()
{
if (empty($this->extension_id))
{
return;
}
// 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 = $this->container->db;
// Get the #__update_sites columns
$columns = $db->getTableColumns('#__update_sites', true);
if (version_compare(JVERSION, '3.0.0', 'lt') || !array_key_exists('extra_query', $columns))
{
unset($update_site['extra_query']);
}
// 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->container->db;
// 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;
}
}
/**
* Makes sure that the version number cached in the #__extensions table is consistent with the version number set in
* this model.
*
* @return bool True if we updated the version number cached in the #__extensions table.
*
* @since 3.1.2
*/
public function updatedCachedVersionNumber()
{
$extension = $this->getExtensionObject();
if (!is_object($extension))
{
return false;
}
$data = json_decode($extension->manifest_cache, true);
$mustUpdate = true;
if (isset($data['version']))
{
$mustUpdate = $this->version != $data['version'];
}
if (!$mustUpdate)
{
return false;
}
// The cached version is wrong; let's update it
$data['version'] = $this->version;
$extension->manifest_cache = json_encode($data);
$db = $this->container->db;
return $db->updateObject('#__extensions', $extension, ['extension_id']);
}
/**
* Returns an object with the #__extensions table record for the current extension.
*
* @return mixed
*/
private function getExtensionObject()
{
list($extensionPrefix, $extensionName) = explode('_', $this->component);
switch ($extensionPrefix)
{
default:
case 'com':
$type = 'component';
$name = $this->component;
break;
case 'pkg':
$type = 'package';
$name = $this->component;
break;
}
// Find the extension ID
$db = $this->container->db;
$query = $db->getQuery(true)
->select('*')
->from($db->qn('#__extensions'))
->where($db->qn('type') . ' = ' . $db->q($type))
->where($db->qn('element') . ' = ' . $db->q($name));
$db->setQuery($query);
$extension = $db->loadObject();
return $extension;
}
}