Skip to content
Open
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
10e8f8c
Added after package download events.
BrainforgeUK Sep 8, 2025
4a01d1e
Update UpdateAdapter.php
BrainforgeUK Sep 8, 2025
532596d
Update InstallerHelper.php
BrainforgeUK Sep 8, 2025
1292449
Update InstallerHelper.php
BrainforgeUK Sep 8, 2025
4b99f74
Update InstallerHelper.php
BrainforgeUK Sep 8, 2025
e077f9a
Update UpdateModel.php
BrainforgeUK Sep 8, 2025
7568417
Update InstallerHelper.php
BrainforgeUK Sep 8, 2025
c66e160
Update ExtensionAdapter.php
BrainforgeUK Sep 9, 2025
d7e5e1e
Update InstallerHelper.php
BrainforgeUK Sep 9, 2025
8bea8b1
Revert "Update InstallerHelper.php"
BrainforgeUK Sep 9, 2025
639c4fc
Update InstallerHelper.php
BrainforgeUK Sep 9, 2025
90321af
Update UpdateModel.php
BrainforgeUK Sep 9, 2025
dea94b8
Update InstallerHelper.php
BrainforgeUK Sep 9, 2025
e95aaa0
Update InstallerHelper.php
BrainforgeUK Sep 9, 2025
c0d8988
Update administrator/components/com_installer/src/Model/UpdateModel.php
BrainforgeUK Sep 9, 2025
2f03821
Update libraries/src/Installer/InstallerHelper.php
BrainforgeUK Sep 9, 2025
e5927da
Update libraries/src/Installer/InstallerHelper.php
BrainforgeUK Sep 9, 2025
12eafcc
Update libraries/src/Installer/InstallerHelper.php
BrainforgeUK Sep 9, 2025
d39184c
Update libraries/src/Installer/InstallerHelper.php
BrainforgeUK Sep 9, 2025
a5f0dca
Update administrator/components/com_installer/src/Model/UpdateModel.php
BrainforgeUK Sep 9, 2025
1f668ca
Update libraries/src/Updater/UpdateAdapter.php
BrainforgeUK Sep 9, 2025
916ef5b
Update libraries/src/Installer/InstallerHelper.php
BrainforgeUK Sep 9, 2025
27200a4
Update libraries/src/Event/Installer/AfterPackageDownloadEvent.php
BrainforgeUK Sep 9, 2025
fb81db3
Update libraries/src/Event/Installer/AfterPackageDownloadFailedEvent.php
BrainforgeUK Sep 9, 2025
fcfd170
Update libraries/src/Installer/InstallerHelper.php
BrainforgeUK Sep 9, 2025
377b48c
Update libraries/src/Installer/InstallerHelper.php
BrainforgeUK Sep 9, 2025
71210a7
Update libraries/src/Installer/InstallerHelper.php
BrainforgeUK Sep 9, 2025
733f46c
Update libraries/src/Installer/InstallerHelper.php
BrainforgeUK Sep 9, 2025
4fee864
Update libraries/src/Installer/InstallerHelper.php
BrainforgeUK Sep 9, 2025
d8a7107
Update libraries/src/Installer/InstallerHelper.php
BrainforgeUK Sep 9, 2025
2ba9f37
Update libraries/src/Installer/InstallerHelper.php
BrainforgeUK Sep 9, 2025
265a5aa
Update libraries/src/Installer/InstallerHelper.php
BrainforgeUK Sep 9, 2025
37def21
Update libraries/src/Installer/InstallerHelper.php
BrainforgeUK Sep 9, 2025
f7fec30
Update libraries/src/Installer/InstallerHelper.php
BrainforgeUK Sep 9, 2025
cd64500
Update libraries/src/Installer/InstallerHelper.php
BrainforgeUK Sep 9, 2025
6e6c7b8
Update libraries/src/Installer/InstallerHelper.php
BrainforgeUK Sep 9, 2025
674cc56
Update libraries/src/Installer/InstallerHelper.php
BrainforgeUK Sep 9, 2025
03b3a66
Update InstallerHelper.php
BrainforgeUK Sep 9, 2025
ca448e4
Update libraries/src/Installer/InstallerHelper.php
BrainforgeUK Sep 9, 2025
f6c38c6
Update libraries/src/Installer/InstallerHelper.php
BrainforgeUK Sep 9, 2025
e5f009d
Update libraries/src/Event/Installer/AfterPackageDownloadEvent.php
BrainforgeUK Sep 10, 2025
059df37
Update libraries/src/Event/Installer/AfterPackageDownloadFailedEvent.php
BrainforgeUK Sep 10, 2025
e4683ca
Refactor response code checks to use getStatusCode
BrainforgeUK Nov 4, 2025
c3da77c
Merge branch '6.1-dev' into 6.1-dev
BrainforgeUK Nov 4, 2025
f6ab3be
Update header retrieval method in InstallerHelper
BrainforgeUK Nov 4, 2025
6a61602
Update libraries/src/Installer/InstallerHelper.php
BrainforgeUK Nov 4, 2025
9866580
Fix body assignment to use getBody method
BrainforgeUK Nov 4, 2025
2f268f2
Fix json_decode to use getBody method
BrainforgeUK Nov 4, 2025
8f575bb
Enhance URL handling in InstallerHelper, added comments.
BrainforgeUK Nov 5, 2025
d3c8ac1
Refactor URL replacement and formatting in InstallerHelper
BrainforgeUK Nov 5, 2025
07ba211
Refactor foreach loop syntax in InstallerHelper.php
BrainforgeUK Nov 5, 2025
5a63607
Update phpstan-baseline.neon
richard67 Nov 9, 2025
4af9025
Merge branch '6.1-dev' into 6.1-dev
richard67 Nov 9, 2025
a46cdb4
Add error message for downloading package
BrainforgeUK Nov 11, 2025
b039143
Enhance error handling for 403 response status
BrainforgeUK Nov 11, 2025
50a0fcd
Fix typo in download error message
BrainforgeUK Nov 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion administrator/components/com_installer/src/Model/UpdateModel.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

namespace Joomla\Component\Installer\Administrator\Model;

use Joomla\CMS\Event\Installer\AfterPackageDownloadFailedEvent;
use Joomla\CMS\Extension\ExtensionHelper;
use Joomla\CMS\Factory;
use Joomla\CMS\Installer\Installer;
Expand Down Expand Up @@ -440,7 +441,22 @@ private function install($update)

// Was the package downloaded?
if (!$p_file) {
Factory::getApplication()->enqueueMessage(Text::sprintf('COM_INSTALLER_PACKAGE_DOWNLOAD_FAILED', $url), 'error');
$dispatcher = $this->getDispatcher();
PluginHelper::importPlugin('installer', null, true, $dispatcher);
$event = new AfterPackageDownloadFailedEvent('onInstallerPackageDownloadFailed', [
'url' => $url,
'message' => true,
'type' => 'error',
]);
$dispatcher->dispatch('onInstallerPackageDownloadFailed', $event);
$message = $event->getArgument('message', true);
$type = $event->getArgument('type', 'error');

if ($message === true) {
InstallerHelper::enqueueDownloadFailMessage($url);
} elseif ($message !== false) {
Factory::getApplication()->enqueueMessage($message, $type);
}

return false;
}
Expand Down
25 changes: 25 additions & 0 deletions libraries/src/Event/Installer/AfterPackageDownloadEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

/**
* Joomla! Content Management System
*
* @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/

namespace Joomla\CMS\Event\Installer;

use Joomla\Event\Event;

// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects

/**
* Class for Installer events
*
* @since __DEPLOY_VERSION__
*/
class AfterPackageDownloadEvent extends Event
{
}
25 changes: 25 additions & 0 deletions libraries/src/Event/Installer/AfterPackageDownloadFailedEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

/**
* Joomla! Content Management System
*
* @copyright (C) 2023 Open Source Matters, Inc. <https://www.joomla.org>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/

namespace Joomla\CMS\Event\Installer;

use Joomla\Event\Event;

// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects

/**
* Class for Installer events
*
* @since __DEPLOY_VERSION__
*/
class AfterPackageDownloadFailedEvent extends Event
{
}
141 changes: 118 additions & 23 deletions libraries/src/Installer/InstallerHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
namespace Joomla\CMS\Installer;

use Joomla\Archive\Archive;
use Joomla\CMS\Event\Installer\AfterPackageDownloadEvent;
use Joomla\CMS\Event\Installer\BeforePackageDownloadEvent;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
Expand Down Expand Up @@ -57,6 +58,15 @@
*/
public const HASH_NOT_PROVIDED = 2;

/**
* Message displayed when download fails.
*
* @var string
* @since __DEPLOY_VERSION__
*/
protected static $downloadFailMessage = 'COM_INSTALLER_PACKAGE_DOWNLOAD_FAILED';

/**
* Downloads a package
*
Expand All @@ -74,58 +84,125 @@
ini_set('user_agent', $version->getUserAgent('Installer'));

// Load installer plugins, and allow URL and headers modification
$headers = [];
$headers = [];
$headers['accept'] = 'application/json, application/zip';

// Load installer plugins, and allow URL and headers modification
$dispatcher = Factory::getApplication()->getDispatcher();

Check failure on line 91 in libraries/src/Installer/InstallerHelper.php

View workflow job for this annotation

GitHub Actions / Run PHPstan

Ignored error pattern #^Call to method getDispatcher\(\) of deprecated interface Joomla\\CMS\\Application\\EventAwareInterface\: 4\.3 will be removed in 7\.0 This interface will be removed without replacement as the Joomla 3\.x compatibility layer will be removed$# (method.deprecatedInterface) in path /__w/joomla-cms/joomla-cms/libraries/src/Installer/InstallerHelper.php is expected to occur 1 time, but occurred 2 times.
PluginHelper::importPlugin('installer', null, true, $dispatcher);
$event = new BeforePackageDownloadEvent('onInstallerBeforePackageDownload', [
'url' => &$url, // @todo: Remove reference in Joomla 7, see BeforePackageDownloadEvent::__constructor()
'headers' => &$headers, // @todo: Remove reference in Joomla 7, see BeforePackageDownloadEvent::__constructor()
'url' => &$url, // @todo: Remove reference in Joomla 6, see BeforePackageDownloadEvent::__constructor()
'headers' => &$headers, // @todo: Remove reference in Joomla 6, see BeforePackageDownloadEvent::__constructor()
]);
$dispatcher->dispatch('onInstallerBeforePackageDownload', $event);
$url = $event->getArgument('url', $url);
$headers = $event->getArgument('headers', $headers);

if (empty($url)) {
// Any logging and messaging of this are the responsibility of the event handlers.
return false;
}

try {
$response = (new HttpFactory())->getHttp()->get($url, $headers);
$response = HttpFactory::getHttp()->get($url, $headers);

Check failure on line 107 in libraries/src/Installer/InstallerHelper.php

View workflow job for this annotation

GitHub Actions / Run PHPstan

Static call to instance method Joomla\Http\HttpFactory::getHttp().

// Convert keys of headers to lowercase, to accommodate for case variations
$headers = array_change_key_case($response->headers, CASE_LOWER);
} catch (\RuntimeException $exception) {
Log::add(Text::sprintf('JLIB_INSTALLER_ERROR_DOWNLOAD_SERVER_CONNECT', $exception->getMessage()), Log::WARNING, 'jerror');
$response = $exception;
}

// Load installer plugins, and check response
$dispatcher = Factory::getApplication()->getDispatcher();

Check failure on line 116 in libraries/src/Installer/InstallerHelper.php

View workflow job for this annotation

GitHub Actions / Run PHPstan

Call to method getDispatcher() of deprecated interface Joomla\CMS\Application\EventAwareInterface: 4.3 will be removed in 7.0 This interface will be removed without replacement as the Joomla 3.x compatibility layer will be removed
PluginHelper::importPlugin('installer', null, true, $dispatcher);
$event = new AfterPackageDownloadEvent('onInstallerAfterPackageDownload', [
'url' => &$url,
'response' => &$response,
'headers' => &$headers,
'return' => true,
]);
$dispatcher->dispatch('onInstallerAfterPackageDownload', $event);
$return = $event->getArgument('return');

if ($return === false) {
return false;
}

// Convert keys of headers to lowercase, to accommodate for case variations
$headers = array_change_key_case($response->getHeaders(), CASE_LOWER);
if ($return !== true) {
return $return;
}

if ($response instanceof \Exception) {
Log::add(Text::sprintf('JLIB_INSTALLER_ERROR_DOWNLOAD_SERVER_CONNECT', $exception->getMessage()), Log::WARNING, 'jerror');

return false;
}

if (302 == $response->getStatusCode() && !empty($headers['location'])) {
if (302 == $response->code && !empty($headers['location'])) {
return self::downloadPackage($headers['location']);
}

if (200 != $response->getStatusCode()) {
if (200 != $response->code) {
Log::add(Text::sprintf('JLIB_INSTALLER_ERROR_DOWNLOAD_SERVER_CONNECT', $response->getStatusCode()), Log::WARNING, 'jerror');

return false;
}

// Parse the Content-Disposition header to get the file name
if (
!empty($headers['content-disposition'])
&& preg_match("/\s*filename\s?=\s?(.*)/", $headers['content-disposition'][0], $parts)
!empty($headers['content-type'])
&& !empty($headers['content-type'][0])
&& strpos($headers['content-type'][0], 'application/json') !== false
) {
$flds = explode(';', $parts[1]);
$target = trim($flds[0], '"');
}
$response = json_decode($response->body, true);

$tmpPath = Factory::getApplication()->get('tmp_path');
if (isset($response['downloadfailmessage'])) {
// Empty string will disable the download fail message.
// The response message may be used on errors. Allows the message type to be customised.
self::$downloadFailMessage = $response['downloadfailmessage'];
}

if (!empty($response['message'])) {
Factory::getApplication()->enqueueMessage($response['message'], $response['type'] ?? 'info');

if (!empty($response['error'])) {
return false;
}
}

if (
!empty($response['error'])
|| empty($response['package'])
|| empty($response['target'])
) {
Log::add(Text::sprintf('JLIB_INSTALLER_ERROR_DOWNLOAD_SERVER_CONNECT', ''), Log::WARNING, 'jerror');

// Set the target path if not given
if (!$target) {
$target = $tmpPath . '/' . self::getFilenameFromUrl($url);
return false;
}

$body = base64_decode($response['package']);
$target = $response['target'];
} else {
$target = $tmpPath . '/' . basename($target);
}
// Parse the Content-Disposition header to get the file name
if (
!empty($headers['content-disposition'])
&& preg_match("/\s*filename\s?=\s?(.*)/", $headers['content-disposition'][0], $parts)
) {
$flds = explode(';', $parts[1]);
$target = trim($flds[0], '"');
}

$tmpPath = Factory::getApplication()->get('tmp_path');

// Set the target path if not given
if (!$target) {
$target = $tmpPath . '/' . self::getFilenameFromUrl($url);
} else {
$target = $tmpPath . '/' . basename($target);
}

// Fix Indirect Modification of Overloaded Property
$body = (string) $response->getBody();
// Fix Indirect Modification of Overloaded Property
$body = $response->body;
}

// Write buffer to file
File::write($target, $body);
Expand All @@ -139,6 +216,24 @@
return basename($target);
}

/*
* Enqueues the download fail message (if any).
*
* @param string $url URL of file to download
*
* @return void
*
* @since 6.1
*/
public static function enqueueDownloadFailMessage($url)
{
if (empty(self::$downloadFailMessage)) {
return;
}

Factory::getApplication()->enqueueMessage(Text::sprintf('COM_INSTALLER_PACKAGE_DOWNLOAD_FAILED', $url), 'error');
}

/**
* Unpacks a file and verifies it as a Joomla element package
* Supports .gz .tar .tar.gz and .zip
Expand Down
3 changes: 2 additions & 1 deletion libraries/src/Updater/Adapter/ExtensionAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ protected function _endElement($parser, $name)

// Check that the product matches and that the version matches (optionally a regexp)
if (
$product == $this->currentUpdate->targetplatform['NAME']
!empty($this->currentUpdate->targetplatform)
&& $product == $this->currentUpdate->targetplatform['NAME']
&& preg_match('/^' . $this->currentUpdate->targetplatform['VERSION'] . '/', JVERSION)
) {
// Check if PHP version supported via <php_minimum> tag, assume true if tag isn't present
Expand Down
5 changes: 5 additions & 0 deletions libraries/src/Updater/UpdateAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,11 @@ protected function getUpdateSiteResponse($options = [])
$newUrl = $event->getArgument('url', $url);
$headers = $event->getArgument('headers', $headers);

if (empty($newUrl)) {
// Any logging and messaging of this are the responsibility of the event handlers.
return false;
}

// Http transport throws an exception when there's no response.
try {
$http = (new HttpFactory())->getHttp($httpOption);
Expand Down
Loading