name : collection.php
<?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;
        }
    }
}

© 2025 Cubjrnet7