-
-
Notifications
You must be signed in to change notification settings - Fork 529
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix Dashboard Updates widget's display of the most recent modx version (
#16608) ### What does it do? Added new `SoftwareUpdate` Processors for retrieving available MODX and Extras updates information, utilizing the new upgrades API. This PR is a Phase 1 fix that solves the issue at hand, but also paves the way for a more flexible and comprehensive Updates widget display to be done in a Phase 2 PR. In that next phase, users will be able to control what level of update MODX candidates should be shown (via a few new system settings). The new MODX download functionality provides a direct download instead of a link to the modx.com downloads area. ### Why is it needed? The widget currently incorrectly shows the current -dev version. ### How to test 1. Manually delete session data from the database. 2. Clear browser and MODX caches and verify that the widget displays the correct update version. 3. Test with an up-to-date `-pl` release (currently 3.0.5), a `-dev` release (github 3.0.x or 3.x), and an older `-pl` or `-dev` release (3.0.0 - 3.0.4). This is a little tricky, because you're going to pull down the 3.1.0-dev from github to review. You may want to manually override the installed version in `core/src/Revolution/Processors/SoftwareUpdate/GetList.php` (line 33), setting it to various versions to see what happens in the Dashboard widget (e.g., 3.0.1-pl or 3.0.6-dev, etc.). ### Example (with 3.0.4-dev installed) https://github.com/user-attachments/assets/a74f8872-4047-4307-b1c0-0d522581066d ### Related issue(s)/PR(s) Resolves #16466
- Loading branch information
Showing
6 changed files
with
364 additions
and
61 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of MODX Revolution. | ||
* | ||
* Copyright (c) MODX, LLC. All Rights Reserved. | ||
* | ||
* For complete copyright and license information, see the COPYRIGHT and LICENSE | ||
* files found in the top-level directory of this distribution. | ||
*/ | ||
|
||
namespace MODX\Revolution\Processors\SoftwareUpdate; | ||
|
||
use MODX\Revolution\Processors\Processor; | ||
use Psr\Http\Client\ClientInterface; | ||
use Psr\Http\Message\RequestFactoryInterface; | ||
use MODX\Revolution\modX; | ||
|
||
/** | ||
* Provides base methods and shared properties for building status data used | ||
* in the front end display of software updates (MODX and Extras) | ||
* | ||
* @package MODX\Revolution\Processors\SoftwareUpdate | ||
*/ | ||
class Base extends Processor | ||
{ | ||
public $apiClient = null; | ||
public $apiFactory = null; | ||
public $apiHost = 'https://sentinel.modx.com'; | ||
public $apiGetReleasesPath = '/releases/products/997329d1-6f68-48d5-8e5e-5251adbb1f38/upgrades/'; | ||
public $apiGetFilePath = '/releases/variants/[downloadId]/download/'; | ||
|
||
public function process() | ||
{ | ||
return parent::process(); | ||
} | ||
|
||
/** | ||
* Initialize the client responsible for fetching upgrades-related data. | ||
* | ||
* @return | ||
*/ | ||
public function initApiClient() | ||
{ | ||
if (!$this->apiClient) { | ||
$this->apiClient = $this->modx->services->get(ClientInterface::class); | ||
$this->apiFactory = $this->modx->services->get(RequestFactoryInterface::class); | ||
} | ||
} | ||
|
||
/** | ||
* Builds the API link used to fetch file data | ||
* | ||
* @param array $requestParams Query parameters | ||
* @param string $targetId An intermediate id used to fetch the actual download link | ||
* @return string The full URI to pass into the upgrades API | ||
*/ | ||
public function buildRequestUri(array $requestParams = [], string $targetId = ''): string | ||
{ | ||
$uri = $this->apiHost; | ||
/* | ||
When a $targetId is passed in, we are making the final request whose response | ||
reveals the real update file path. Otherwise the request gets a full list of | ||
potential upgrades based on criteria passed in the $requestParams | ||
*/ | ||
$uri .= !empty($targetId) | ||
? str_replace('[downloadId]', $targetId, $this->apiGetFilePath) | ||
: $this->apiGetReleasesPath | ||
; | ||
if (count($requestParams) > 0) { | ||
$uri .= '?' . http_build_query($requestParams); | ||
} | ||
return $uri; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of MODX Revolution. | ||
* | ||
* Copyright (c) MODX, LLC. All Rights Reserved. | ||
* | ||
* For complete copyright and license information, see the COPYRIGHT and LICENSE | ||
* files found in the top-level directory of this distribution. | ||
*/ | ||
|
||
namespace MODX\Revolution\Processors\SoftwareUpdate; | ||
|
||
use MODX\Revolution\Processors\SoftwareUpdate\Base; | ||
use Psr\Http\Client\ClientExceptionInterface; | ||
use MODX\Revolution\modX; | ||
|
||
/** | ||
* Retrieves the downloadable file URL and other metadata for the specified MODX upgrade package | ||
* | ||
* @property string $downloadId An identifier used to retrieve the package's download URL | ||
* @package MODX\Revolution\Processors\SoftwareUpdate | ||
*/ | ||
class GetFile extends Base | ||
{ | ||
public function process() | ||
{ | ||
$downloadId = $this->getProperty('downloadId', null); | ||
$responseData = []; | ||
|
||
if ($downloadId) { | ||
$this->initApiClient(); | ||
|
||
$uri = $this->buildRequestUri(['uuid' => $this->modx->uuid], $downloadId); | ||
$request = $this->apiFactory->createRequest('GET', $uri) | ||
->withHeader('Accept', 'application/json') | ||
->withHeader('Content-Type', 'application/json'); | ||
try { | ||
$response = $this->apiClient->sendRequest($request); | ||
} catch (ClientExceptionInterface $e) { | ||
$this->modx->log(modX::LOG_LEVEL_ERROR, $e->getMessage()); | ||
return $this->failure($e->getMessage()); | ||
} | ||
|
||
$fileData = $response->getBody()->getContents(); | ||
|
||
if ($fileData) { | ||
$fileData = json_decode($fileData, true); | ||
if (!empty($fileData['zip_url']) && strpos($fileData['zip_url'], 'http') === 0) { | ||
$name = basename($fileData['zip_url']); | ||
$responseData['filename'] = $name; | ||
$responseData['zip'] = $fileData['zip_url']; | ||
$responseData['status'] = $response->getStatusCode(); | ||
} | ||
} | ||
return $this->success('', $responseData); | ||
} | ||
} | ||
} |
145 changes: 145 additions & 0 deletions
145
core/src/Revolution/Processors/SoftwareUpdate/GetList.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of MODX Revolution. | ||
* | ||
* Copyright (c) MODX, LLC. All Rights Reserved. | ||
* | ||
* For complete copyright and license information, see the COPYRIGHT and LICENSE | ||
* files found in the top-level directory of this distribution. | ||
*/ | ||
|
||
namespace MODX\Revolution\Processors\SoftwareUpdate; | ||
|
||
use MODX\Revolution\Processors\SoftwareUpdate\Base; | ||
use MODX\Revolution\Processors\Workspace\Packages\GetList as PackagesGetList; | ||
use MODX\Revolution\Transport\modTransportPackage; | ||
use Psr\Http\Client\ClientExceptionInterface; | ||
use MODX\Revolution\modX; | ||
|
||
/** | ||
* Retrieves status data for use in the front end display of software updates (MODX and Extras) | ||
* | ||
* @property string $softwareType Identifies which type of software status data should be | ||
* retrieved (currently only two options: 'modx' or 'extras') | ||
* @package MODX\Revolution\Processors\SoftwareUpdate | ||
*/ | ||
class GetList extends Base | ||
{ | ||
public $installedVersionData; | ||
|
||
public function initialize() | ||
{ | ||
$this->installedVersionData = $this->modx->getVersionData(); | ||
return parent::initialize(); | ||
} | ||
|
||
public function process() | ||
{ | ||
$softwareType = $this->getProperty('softwareType', 'modx'); | ||
$categoryData = [ | ||
'updateable' => 0 | ||
]; | ||
if ($softwareType === 'modx') { | ||
$modxData = $this->getModxUpdates(); | ||
if (is_array($modxData)) { | ||
$categoryData = array_merge($categoryData, $modxData); | ||
} | ||
} else { | ||
$extrasData = $this->getExtrasUpdates(); | ||
if (is_array($extrasData)) { | ||
$categoryData = array_merge($categoryData, $extrasData); | ||
} | ||
} | ||
return $this->success('', $categoryData); | ||
} | ||
|
||
/** | ||
* Fetches a list of MODX update candidates | ||
* | ||
* @return array Data indicating whether the current installation is | ||
* updatable and the available releases if so | ||
*/ | ||
public function getModxUpdates(): array | ||
{ | ||
$this->initApiClient(); | ||
|
||
$uri = $this->buildRequestUri([ | ||
'current' => $this->installedVersionData['full_version'], | ||
'level' => 'major', | ||
'variant' => 'Traditional', | ||
'prereleases' => 0 | ||
]); | ||
|
||
$request = $this->apiFactory->createRequest('GET', $uri) | ||
->withHeader('Accept', 'application/json') | ||
->withHeader('Content-Type', 'application/json'); | ||
try { | ||
$response = $this->apiClient->sendRequest($request); | ||
} catch (ClientExceptionInterface $e) { | ||
$this->modx->log(modX::LOG_LEVEL_ERROR, 'ClientExceptionInterface Err: ' . $e->getMessage()); | ||
return $this->failure($e->getMessage()); | ||
} | ||
|
||
$listData = $response->getBody()->getContents(); | ||
$categoryData = []; | ||
if ($listData) { | ||
$listData = json_decode($listData, true); | ||
$upgrades = $listData['upgrades']; | ||
$selectedUpgrade = null; | ||
if (!empty($upgrades)) { | ||
$i = 0; | ||
$upgradesCount = count($upgrades); | ||
if ($upgradesCount === 1) { | ||
$categoryData['updateable'] = 1; | ||
$selectedUpgrade = $upgrades; | ||
} else { | ||
foreach ($upgrades as $upgrade) { | ||
$selectedUpgrade = $upgrade; | ||
break; | ||
} | ||
$categoryData['updateable'] = (int)version_compare($this->installedVersionData['full_version'], $upgrade['version'], '<'); | ||
} | ||
if ($categoryData['updateable']) { | ||
/* | ||
NOTE: This is superfluous now, but is done in preparation | ||
for iterating through multiple displayable versions | ||
*/ | ||
$categoryData['versions'][$i]['version'] = $selectedUpgrade['version']; | ||
$urlSegments = explode('/', trim($selectedUpgrade['url'], '/')); | ||
$categoryData['versions'][$i]['downloadId'] = $urlSegments[count($urlSegments) - 2]; | ||
|
||
$categoryData['latest']['version'] = $categoryData['versions'][0]['version']; | ||
$categoryData['latest']['downloadId'] = $categoryData['versions'][0]['downloadId']; | ||
} | ||
} | ||
} | ||
return $categoryData; | ||
} | ||
|
||
/** | ||
* Fetches a list of Extras update candidates | ||
* | ||
* @return array Data indicating whether any installed Extras are updatable | ||
* and, if so, providing the names of those that are update candidates | ||
*/ | ||
public function getExtrasUpdates(): array | ||
{ | ||
$categoryData = []; | ||
$packages = $this->modx->call(modTransportPackage::class, 'listPackages', [$this->modx, 1]); | ||
if ($packages && array_key_exists('collection', $packages)) { | ||
$packagesProcessor = new PackagesGetList($this->modx); | ||
|
||
/** @var modTransportPackage $package */ | ||
foreach ($packages['collection'] as $package) { | ||
$tmp = []; | ||
$tmp = $packagesProcessor->checkForUpdates($package, $tmp); | ||
if (!empty($tmp['updateable'])) { | ||
$categoryData['names'][] = $package->get('package_name'); | ||
$categoryData['updateable']++; | ||
} | ||
} | ||
} | ||
return $categoryData; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.