<?php /* * @package bfNetwork * @copyright Copyright (C) 2011,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021,2022,2023 Blue Flame Digital Solutions Ltd. All rights reserved. * @license GNU General Public License version 3 or later * * @see https://mySites.guru/ * @see https://www.phil-taylor.com/ * * @author Phil Taylor / Blue Flame Digital Solutions Limited. * * bfNetwork is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * bfNetwork is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this package. If not, see http://www.gnu.org/licenses/ * * If you have any questions regarding this code, please contact [email protected] */ /** * @copyright Copyright (c)2010-2014 Nicholas K. Dionysopoulos * @license GNU General Public License version 3, or later * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ class AcuUpdateProviderCollection { /** * 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 (null === $jVersion) { $jVersion = JVERSION; } // Initialise return value $updates = [ 'metadata' => [ 'name' => '', 'description' => '', ], 'categories' => [], 'extensions' => [], ]; // Download and parse the XML file $donwloader = new AcuDownload(); $xmlSource = $donwloader->getFromURL($url); try { $xml = new SimpleXMLElement($xmlSource, \LIBXML_NONET); } catch (Exception) { return $updates; } // Sanity check if (('extensionset' != $xml->getName())) { 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 = [ 'categories' => [], 'extensions' => [], ]; // Segregate the raw list to a hierarchy of extension and category entries foreach ($xml->children() as $extension) { switch ($extension->getName()) { case 'category': // These are the parameters we expect in a category $params = [ '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']] = []; } $rawUpdates['categories'][$params['category']][] = $params; break; case 'extension': // These are the parameters we expect in a category $params = [ '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']] = []; } if (! array_key_exists($params['element'], $rawUpdates['extensions'][$params['type']])) { $rawUpdates['extensions'][$params['type']][$params['element']] = []; } $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] = []; 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 (null === $jVersion) { $jVersion = JVERSION; } $versionParts = explode('.', (string) $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; } } /** * 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']; } if (array_key_exists($type, $allUpdates['extensions'])) { return $allUpdates['extensions'][$type]; } else { return; } } /** * 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; } if (array_key_exists($element, $allUpdates)) { return $allUpdates[$element]['detailsurl']; } else { return; } } }