mirror of
https://github.com/nextcloud/server.git
synced 2026-02-03 20:41:22 -05:00
chore: drop legacy WhatsNew
This feature was not used in 8 years and from frontend did not even properly work anymore and was implemented using deprecated API. So get rid of it. The last version that was using a changelog from the changelog server was Nextcloud 20. We use the firstrunwizard nowadays for informing about Nextcloud changes in new releases. Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
This commit is contained in:
parent
a9f3534bc4
commit
428e76214e
18 changed files with 25 additions and 1707 deletions
|
|
@ -9,7 +9,6 @@ declare(strict_types=1);
|
|||
*/
|
||||
namespace OCA\UpdateNotification;
|
||||
|
||||
use OC\Updater\ChangesCheck;
|
||||
use OC\Updater\VersionCheck;
|
||||
use OCP\AppFramework\Services\IInitialState;
|
||||
|
||||
|
|
@ -17,7 +16,6 @@ class UpdateChecker {
|
|||
|
||||
public function __construct(
|
||||
private VersionCheck $updater,
|
||||
private ChangesCheck $changesCheck,
|
||||
private IInitialState $initialState,
|
||||
) {
|
||||
}
|
||||
|
|
@ -41,13 +39,6 @@ class UpdateChecker {
|
|||
if (strpos($data['url'], 'https://') === 0) {
|
||||
$result['downloadLink'] = $data['url'];
|
||||
}
|
||||
if (strpos($data['changes'], 'https://') === 0) {
|
||||
try {
|
||||
$result['changes'] = $this->changesCheck->check($data['changes'], $data['version']);
|
||||
} catch (\Exception $e) {
|
||||
// no info, not a problem
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ declare(strict_types=1);
|
|||
*/
|
||||
namespace OCA\UpdateNotification\Tests;
|
||||
|
||||
use OC\Updater\ChangesCheck;
|
||||
use OC\Updater\VersionCheck;
|
||||
use OCA\UpdateNotification\UpdateChecker;
|
||||
use OCP\AppFramework\Services\IInitialState;
|
||||
|
|
@ -17,7 +16,6 @@ use Test\TestCase;
|
|||
|
||||
class UpdateCheckerTest extends TestCase {
|
||||
|
||||
private ChangesCheck&MockObject $changesChecker;
|
||||
private VersionCheck&MockObject $updater;
|
||||
private IInitialState&MockObject $initialState;
|
||||
private UpdateChecker $updateChecker;
|
||||
|
|
@ -26,11 +24,9 @@ class UpdateCheckerTest extends TestCase {
|
|||
parent::setUp();
|
||||
|
||||
$this->updater = $this->createMock(VersionCheck::class);
|
||||
$this->changesChecker = $this->createMock(ChangesCheck::class);
|
||||
$this->initialState = $this->createMock(IInitialState::class);
|
||||
$this->updateChecker = new UpdateChecker(
|
||||
$this->updater,
|
||||
$this->changesChecker,
|
||||
$this->initialState,
|
||||
);
|
||||
}
|
||||
|
|
@ -85,15 +81,10 @@ class UpdateCheckerTest extends TestCase {
|
|||
'versionstring' => 'Nextcloud 1.2.3',
|
||||
'web' => 'https://docs.nextcloud.com/myUrl',
|
||||
'url' => 'https://downloads.nextcloud.org/server',
|
||||
'changes' => 'https://updates.nextcloud.com/changelog_server/?version=123.0.0',
|
||||
'autoupdater' => '1',
|
||||
'eol' => '0',
|
||||
]);
|
||||
|
||||
$this->changesChecker->expects($this->once())
|
||||
->method('check')
|
||||
->willReturn($changes);
|
||||
|
||||
$expected = [
|
||||
'updateAvailable' => true,
|
||||
'updateVersion' => '1.2.3',
|
||||
|
|
@ -102,7 +93,6 @@ class UpdateCheckerTest extends TestCase {
|
|||
'versionIsEol' => false,
|
||||
'updateLink' => 'https://docs.nextcloud.com/myUrl',
|
||||
'downloadLink' => 'https://downloads.nextcloud.org/server',
|
||||
'changes' => $changes,
|
||||
];
|
||||
$this->assertSame($expected, $this->updateChecker->getUpdateState());
|
||||
}
|
||||
|
|
@ -126,7 +116,6 @@ class UpdateCheckerTest extends TestCase {
|
|||
'versionstring' => 'Nextcloud 1.2.3',
|
||||
'web' => 'https://docs.nextcloud.com/myUrl',
|
||||
'url' => 'https://downloads.nextcloud.org/server',
|
||||
'changes' => 'https://updates.nextcloud.com/changelog_server/?version=123.0.0',
|
||||
'autoupdater' => '1',
|
||||
'eol' => '0',
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -3215,12 +3215,6 @@
|
|||
)]]></code>
|
||||
</DeprecatedMethod>
|
||||
</file>
|
||||
<file src="core/Controller/WhatsNewController.php">
|
||||
<DeprecatedMethod>
|
||||
<code><![CDATA[getUserValue]]></code>
|
||||
<code><![CDATA[setUserValue]]></code>
|
||||
</DeprecatedMethod>
|
||||
</file>
|
||||
<file src="core/Middleware/TwoFactorMiddleware.php">
|
||||
<DeprecatedInterface>
|
||||
<code><![CDATA[private]]></code>
|
||||
|
|
|
|||
|
|
@ -1,104 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
namespace OC\Core\Controller;
|
||||
|
||||
use OC\Updater\ChangesCheck;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\AppFramework\Http;
|
||||
use OCP\AppFramework\Http\Attribute\ApiRoute;
|
||||
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
|
||||
use OCP\AppFramework\Http\DataResponse;
|
||||
use OCP\AppFramework\OCSController;
|
||||
use OCP\Defaults;
|
||||
use OCP\IConfig;
|
||||
use OCP\IRequest;
|
||||
use OCP\IUserSession;
|
||||
use OCP\L10N\IFactory;
|
||||
use OCP\PreConditionNotMetException;
|
||||
|
||||
class WhatsNewController extends OCSController {
|
||||
public function __construct(
|
||||
string $appName,
|
||||
IRequest $request,
|
||||
private IUserSession $userSession,
|
||||
private IConfig $config,
|
||||
private ChangesCheck $whatsNewService,
|
||||
private IFactory $langFactory,
|
||||
private Defaults $defaults,
|
||||
) {
|
||||
parent::__construct($appName, $request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the changes
|
||||
*
|
||||
* @return DataResponse<Http::STATUS_OK, array{changelogURL: string, product: string, version: string, whatsNew?: array{regular: list<string>, admin: list<string>}}, array{}>|DataResponse<Http::STATUS_NO_CONTENT, list<empty>, array{}>
|
||||
*
|
||||
* 200: Changes returned
|
||||
* 204: No changes
|
||||
*/
|
||||
#[NoAdminRequired]
|
||||
#[ApiRoute(verb: 'GET', url: '/whatsnew', root: '/core')]
|
||||
public function get():DataResponse {
|
||||
$user = $this->userSession->getUser();
|
||||
if ($user === null) {
|
||||
throw new \RuntimeException('Acting user cannot be resolved');
|
||||
}
|
||||
$lastRead = $this->config->getUserValue($user->getUID(), 'core', 'whatsNewLastRead', 0);
|
||||
$currentVersion = $this->whatsNewService->normalizeVersion($this->config->getSystemValue('version'));
|
||||
|
||||
if (version_compare($lastRead, $currentVersion, '>=')) {
|
||||
return new DataResponse([], Http::STATUS_NO_CONTENT);
|
||||
}
|
||||
|
||||
try {
|
||||
$iterator = $this->langFactory->getLanguageIterator();
|
||||
$whatsNew = $this->whatsNewService->getChangesForVersion($currentVersion);
|
||||
$resultData = [
|
||||
'changelogURL' => $whatsNew['changelogURL'],
|
||||
'product' => $this->defaults->getProductName(),
|
||||
'version' => $currentVersion,
|
||||
];
|
||||
do {
|
||||
$lang = $iterator->current();
|
||||
if (isset($whatsNew['whatsNew'][$lang])) {
|
||||
$resultData['whatsNew'] = $whatsNew['whatsNew'][$lang];
|
||||
break;
|
||||
}
|
||||
$iterator->next();
|
||||
} while ($lang !== 'en' && $iterator->valid());
|
||||
return new DataResponse($resultData);
|
||||
} catch (DoesNotExistException $e) {
|
||||
return new DataResponse([], Http::STATUS_NO_CONTENT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismiss the changes
|
||||
*
|
||||
* @param string $version Version to dismiss the changes for
|
||||
*
|
||||
* @return DataResponse<Http::STATUS_OK, list<empty>, array{}>
|
||||
* @throws PreConditionNotMetException
|
||||
* @throws DoesNotExistException
|
||||
*
|
||||
* 200: Changes dismissed
|
||||
*/
|
||||
#[NoAdminRequired]
|
||||
#[ApiRoute(verb: 'POST', url: '/whatsnew', root: '/core')]
|
||||
public function dismiss(string $version):DataResponse {
|
||||
$user = $this->userSession->getUser();
|
||||
if ($user === null) {
|
||||
throw new \RuntimeException('Acting user cannot be resolved');
|
||||
}
|
||||
$version = $this->whatsNewService->normalizeVersion($version);
|
||||
// checks whether it's a valid version, throws an Exception otherwise
|
||||
$this->whatsNewService->getChangesForVersion($version);
|
||||
$this->config->setUserValue($user->getUID(), 'core', 'whatsNewLastRead', $version);
|
||||
return new DataResponse();
|
||||
}
|
||||
}
|
||||
|
|
@ -99,14 +99,6 @@ class AddMissingIndicesListener implements IEventListener {
|
|||
true
|
||||
);
|
||||
|
||||
$event->addMissingIndex(
|
||||
'whats_new',
|
||||
'version',
|
||||
['version'],
|
||||
[],
|
||||
true
|
||||
);
|
||||
|
||||
$event->addMissingIndex(
|
||||
'cards',
|
||||
'cards_abiduri',
|
||||
|
|
|
|||
|
|
@ -1,52 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
namespace OC\Core\Migrations;
|
||||
|
||||
use OCP\DB\ISchemaWrapper;
|
||||
use OCP\Migration\IOutput;
|
||||
use OCP\Migration\SimpleMigrationStep;
|
||||
|
||||
class Version14000Date20180626223656 extends SimpleMigrationStep {
|
||||
public function changeSchema(IOutput $output, \Closure $schemaClosure, array $options) {
|
||||
/** @var ISchemaWrapper $schema */
|
||||
$schema = $schemaClosure();
|
||||
if (!$schema->hasTable('whats_new')) {
|
||||
$table = $schema->createTable('whats_new');
|
||||
$table->addColumn('id', 'integer', [
|
||||
'autoincrement' => true,
|
||||
'notnull' => true,
|
||||
'length' => 4,
|
||||
'unsigned' => true,
|
||||
]);
|
||||
$table->addColumn('version', 'string', [
|
||||
'notnull' => true,
|
||||
'length' => 64,
|
||||
'default' => '11',
|
||||
]);
|
||||
$table->addColumn('etag', 'string', [
|
||||
'notnull' => true,
|
||||
'length' => 64,
|
||||
'default' => '',
|
||||
]);
|
||||
$table->addColumn('last_check', 'integer', [
|
||||
'notnull' => true,
|
||||
'length' => 4,
|
||||
'unsigned' => true,
|
||||
'default' => 0,
|
||||
]);
|
||||
$table->addColumn('data', 'text', [
|
||||
'notnull' => true,
|
||||
'default' => '',
|
||||
]);
|
||||
$table->setPrimaryKey(['id']);
|
||||
$table->addUniqueIndex(['version'], 'version');
|
||||
$table->addIndex(['version', 'etag'], 'version_etag_idx');
|
||||
}
|
||||
|
||||
return $schema;
|
||||
}
|
||||
}
|
||||
23
core/Migrations/Version34000Date20260122120000.php
Normal file
23
core/Migrations/Version34000Date20260122120000.php
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OC\Core\Migrations;
|
||||
|
||||
use OCP\DB\ISchemaWrapper;
|
||||
use OCP\Migration\IOutput;
|
||||
use OCP\Migration\SimpleMigrationStep;
|
||||
|
||||
class Version34000Date20260122120000 extends SimpleMigrationStep {
|
||||
public function changeSchema(IOutput $output, \Closure $schemaClosure, array $options) {
|
||||
/** @var ISchemaWrapper $schema */
|
||||
$schema = $schemaClosure();
|
||||
if ($schema->hasTable('whats_new')) {
|
||||
$schema->dropTable('whats_new');
|
||||
}
|
||||
return $schema;
|
||||
}
|
||||
}
|
||||
|
|
@ -9002,249 +9002,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/ocs/v2.php/core/whatsnew": {
|
||||
"get": {
|
||||
"operationId": "whats_new-get",
|
||||
"summary": "Get the changes",
|
||||
"tags": [
|
||||
"whats_new"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"bearer_auth": []
|
||||
},
|
||||
{
|
||||
"basic_auth": []
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "OCS-APIRequest",
|
||||
"in": "header",
|
||||
"description": "Required to be true for the API request to pass",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Changes returned",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"changelogURL",
|
||||
"product",
|
||||
"version"
|
||||
],
|
||||
"properties": {
|
||||
"changelogURL": {
|
||||
"type": "string"
|
||||
},
|
||||
"product": {
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"type": "string"
|
||||
},
|
||||
"whatsNew": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"regular",
|
||||
"admin"
|
||||
],
|
||||
"properties": {
|
||||
"regular": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"admin": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"204": {
|
||||
"description": "No changes"
|
||||
},
|
||||
"401": {
|
||||
"description": "Current user is not logged in",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"operationId": "whats_new-dismiss",
|
||||
"summary": "Dismiss the changes",
|
||||
"tags": [
|
||||
"whats_new"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"bearer_auth": []
|
||||
},
|
||||
{
|
||||
"basic_auth": []
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"version"
|
||||
],
|
||||
"properties": {
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "Version to dismiss the changes for"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "OCS-APIRequest",
|
||||
"in": "header",
|
||||
"description": "Required to be true for the API request to pass",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Changes dismissed",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"text/plain": {
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Current user is not logged in",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/index.php/avatar/{userId}/{size}/dark": {
|
||||
"get": {
|
||||
"operationId": "avatar-get-avatar-dark",
|
||||
|
|
|
|||
|
|
@ -9002,249 +9002,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/ocs/v2.php/core/whatsnew": {
|
||||
"get": {
|
||||
"operationId": "whats_new-get",
|
||||
"summary": "Get the changes",
|
||||
"tags": [
|
||||
"whats_new"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"bearer_auth": []
|
||||
},
|
||||
{
|
||||
"basic_auth": []
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "OCS-APIRequest",
|
||||
"in": "header",
|
||||
"description": "Required to be true for the API request to pass",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Changes returned",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"changelogURL",
|
||||
"product",
|
||||
"version"
|
||||
],
|
||||
"properties": {
|
||||
"changelogURL": {
|
||||
"type": "string"
|
||||
},
|
||||
"product": {
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"type": "string"
|
||||
},
|
||||
"whatsNew": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"regular",
|
||||
"admin"
|
||||
],
|
||||
"properties": {
|
||||
"regular": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"admin": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"204": {
|
||||
"description": "No changes"
|
||||
},
|
||||
"401": {
|
||||
"description": "Current user is not logged in",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"operationId": "whats_new-dismiss",
|
||||
"summary": "Dismiss the changes",
|
||||
"tags": [
|
||||
"whats_new"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"bearer_auth": []
|
||||
},
|
||||
{
|
||||
"basic_auth": []
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"version"
|
||||
],
|
||||
"properties": {
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "Version to dismiss the changes for"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "OCS-APIRequest",
|
||||
"in": "header",
|
||||
"description": "Required to be true for the API request to pass",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Changes dismissed",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"text/plain": {
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Current user is not logged in",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/index.php/avatar/{userId}/{size}/dark": {
|
||||
"get": {
|
||||
"operationId": "avatar-get-avatar-dark",
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import Collaboration from './collaboration.js'
|
|||
import * as Comments from './comments.ts'
|
||||
import Loader from './loader.js'
|
||||
import Toast from './toast.js'
|
||||
import * as WhatsNew from './whatsnew.js'
|
||||
|
||||
/** @namespace OCP */
|
||||
export default {
|
||||
|
|
@ -32,5 +31,4 @@ export default {
|
|||
* @deprecated 19.0.0 use the `@nextcloud/dialogs` package instead
|
||||
*/
|
||||
Toast,
|
||||
WhatsNew,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,150 +0,0 @@
|
|||
/**
|
||||
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { generateOcsUrl } from '@nextcloud/router'
|
||||
import $ from 'jquery'
|
||||
import _ from 'underscore'
|
||||
import logger from '../logger.js'
|
||||
|
||||
/**
|
||||
* @param {any} options -
|
||||
*/
|
||||
export function query(options) {
|
||||
options = options || {}
|
||||
const dismissOptions = options.dismiss || {}
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: options.url || generateOcsUrl('core/whatsnew?format=json'),
|
||||
success: options.success || function(data, statusText, xhr) {
|
||||
onQuerySuccess(data, statusText, xhr, dismissOptions)
|
||||
},
|
||||
error: options.error || onQueryError,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} version -
|
||||
* @param {any} options -
|
||||
*/
|
||||
export function dismiss(version, options) {
|
||||
options = options || {}
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: options.url || generateOcsUrl('core/whatsnew'),
|
||||
data: { version: encodeURIComponent(version) },
|
||||
success: options.success || onDismissSuccess,
|
||||
error: options.error || onDismissError,
|
||||
})
|
||||
// remove element immediately
|
||||
$('.whatsNewPopover').remove()
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} data -
|
||||
* @param {any} statusText -
|
||||
* @param {any} xhr -
|
||||
* @param {any} dismissOptions -
|
||||
*/
|
||||
function onQuerySuccess(data, statusText, xhr, dismissOptions) {
|
||||
logger.debug('querying Whats New data was successful: ' + statusText, { data })
|
||||
|
||||
if (xhr.status !== 200) {
|
||||
return
|
||||
}
|
||||
|
||||
let item, menuItem, text, icon
|
||||
|
||||
const div = document.createElement('div')
|
||||
div.classList.add('popovermenu', 'open', 'whatsNewPopover', 'menu-left')
|
||||
|
||||
const list = document.createElement('ul')
|
||||
|
||||
// header
|
||||
item = document.createElement('li')
|
||||
menuItem = document.createElement('span')
|
||||
menuItem.className = 'menuitem'
|
||||
|
||||
text = document.createElement('span')
|
||||
text.innerText = t('core', 'New in') + ' ' + data.ocs.data.product
|
||||
text.className = 'caption'
|
||||
menuItem.appendChild(text)
|
||||
|
||||
icon = document.createElement('span')
|
||||
icon.className = 'icon-close'
|
||||
icon.onclick = function() {
|
||||
dismiss(data.ocs.data.version, dismissOptions)
|
||||
}
|
||||
menuItem.appendChild(icon)
|
||||
|
||||
item.appendChild(menuItem)
|
||||
list.appendChild(item)
|
||||
|
||||
// Highlights
|
||||
for (const i in data.ocs.data.whatsNew.regular) {
|
||||
const whatsNewTextItem = data.ocs.data.whatsNew.regular[i]
|
||||
item = document.createElement('li')
|
||||
|
||||
menuItem = document.createElement('span')
|
||||
menuItem.className = 'menuitem'
|
||||
|
||||
icon = document.createElement('span')
|
||||
icon.className = 'icon-checkmark'
|
||||
menuItem.appendChild(icon)
|
||||
|
||||
text = document.createElement('p')
|
||||
text.innerHTML = _.escape(whatsNewTextItem)
|
||||
menuItem.appendChild(text)
|
||||
|
||||
item.appendChild(menuItem)
|
||||
list.appendChild(item)
|
||||
}
|
||||
|
||||
// Changelog URL
|
||||
if (!_.isUndefined(data.ocs.data.changelogURL)) {
|
||||
item = document.createElement('li')
|
||||
|
||||
menuItem = document.createElement('a')
|
||||
menuItem.href = data.ocs.data.changelogURL
|
||||
menuItem.rel = 'noreferrer noopener'
|
||||
menuItem.target = '_blank'
|
||||
|
||||
icon = document.createElement('span')
|
||||
icon.className = 'icon-link'
|
||||
menuItem.appendChild(icon)
|
||||
|
||||
text = document.createElement('span')
|
||||
text.innerText = t('core', 'View changelog')
|
||||
menuItem.appendChild(text)
|
||||
|
||||
item.appendChild(menuItem)
|
||||
list.appendChild(item)
|
||||
}
|
||||
|
||||
div.appendChild(list)
|
||||
document.body.appendChild(div)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} x -
|
||||
* @param {any} t -
|
||||
* @param {any} e -
|
||||
*/
|
||||
function onQueryError(x, t, e) {
|
||||
logger.debug('querying Whats New Data resulted in an error: ' + t + e)
|
||||
logger.debug(x)
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
function onDismissSuccess() {
|
||||
// noop
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {any} data -
|
||||
*/
|
||||
function onDismissError(data) {
|
||||
logger.debug('dismissing Whats New data resulted in an error: ' + data)
|
||||
}
|
||||
|
|
@ -1476,7 +1476,6 @@ return array(
|
|||
'OC\\Core\\Controller\\WalledGardenController' => $baseDir . '/core/Controller/WalledGardenController.php',
|
||||
'OC\\Core\\Controller\\WebAuthnController' => $baseDir . '/core/Controller/WebAuthnController.php',
|
||||
'OC\\Core\\Controller\\WellKnownController' => $baseDir . '/core/Controller/WellKnownController.php',
|
||||
'OC\\Core\\Controller\\WhatsNewController' => $baseDir . '/core/Controller/WhatsNewController.php',
|
||||
'OC\\Core\\Controller\\WipeController' => $baseDir . '/core/Controller/WipeController.php',
|
||||
'OC\\Core\\Data\\LoginFlowV2Credentials' => $baseDir . '/core/Data/LoginFlowV2Credentials.php',
|
||||
'OC\\Core\\Data\\LoginFlowV2Tokens' => $baseDir . '/core/Data/LoginFlowV2Tokens.php',
|
||||
|
|
@ -1506,7 +1505,6 @@ return array(
|
|||
'OC\\Core\\Migrations\\Version14000Date20180516101403' => $baseDir . '/core/Migrations/Version14000Date20180516101403.php',
|
||||
'OC\\Core\\Migrations\\Version14000Date20180518120534' => $baseDir . '/core/Migrations/Version14000Date20180518120534.php',
|
||||
'OC\\Core\\Migrations\\Version14000Date20180522074438' => $baseDir . '/core/Migrations/Version14000Date20180522074438.php',
|
||||
'OC\\Core\\Migrations\\Version14000Date20180626223656' => $baseDir . '/core/Migrations/Version14000Date20180626223656.php',
|
||||
'OC\\Core\\Migrations\\Version14000Date20180710092004' => $baseDir . '/core/Migrations/Version14000Date20180710092004.php',
|
||||
'OC\\Core\\Migrations\\Version14000Date20180712153140' => $baseDir . '/core/Migrations/Version14000Date20180712153140.php',
|
||||
'OC\\Core\\Migrations\\Version15000Date20180926101451' => $baseDir . '/core/Migrations/Version15000Date20180926101451.php',
|
||||
|
|
@ -1585,6 +1583,7 @@ return array(
|
|||
'OC\\Core\\Migrations\\Version33000Date20251124110529' => $baseDir . '/core/Migrations/Version33000Date20251124110529.php',
|
||||
'OC\\Core\\Migrations\\Version33000Date20251126152410' => $baseDir . '/core/Migrations/Version33000Date20251126152410.php',
|
||||
'OC\\Core\\Migrations\\Version33000Date20251209123503' => $baseDir . '/core/Migrations/Version33000Date20251209123503.php',
|
||||
'OC\\Core\\Migrations\\Version34000Date20260122120000' => $baseDir . '/core/Migrations/Version34000Date20260122120000.php',
|
||||
'OC\\Core\\Notification\\CoreNotifier' => $baseDir . '/core/Notification/CoreNotifier.php',
|
||||
'OC\\Core\\ResponseDefinitions' => $baseDir . '/core/ResponseDefinitions.php',
|
||||
'OC\\Core\\Service\\CronService' => $baseDir . '/core/Service/CronService.php',
|
||||
|
|
@ -2230,9 +2229,6 @@ return array(
|
|||
'OC\\Translation\\TranslationManager' => $baseDir . '/lib/private/Translation/TranslationManager.php',
|
||||
'OC\\URLGenerator' => $baseDir . '/lib/private/URLGenerator.php',
|
||||
'OC\\Updater' => $baseDir . '/lib/private/Updater.php',
|
||||
'OC\\Updater\\Changes' => $baseDir . '/lib/private/Updater/Changes.php',
|
||||
'OC\\Updater\\ChangesCheck' => $baseDir . '/lib/private/Updater/ChangesCheck.php',
|
||||
'OC\\Updater\\ChangesMapper' => $baseDir . '/lib/private/Updater/ChangesMapper.php',
|
||||
'OC\\Updater\\Exceptions\\ReleaseMetadataException' => $baseDir . '/lib/private/Updater/Exceptions/ReleaseMetadataException.php',
|
||||
'OC\\Updater\\ReleaseMetadata' => $baseDir . '/lib/private/Updater/ReleaseMetadata.php',
|
||||
'OC\\Updater\\VersionCheck' => $baseDir . '/lib/private/Updater/VersionCheck.php',
|
||||
|
|
|
|||
|
|
@ -1517,7 +1517,6 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
|
|||
'OC\\Core\\Controller\\WalledGardenController' => __DIR__ . '/../../..' . '/core/Controller/WalledGardenController.php',
|
||||
'OC\\Core\\Controller\\WebAuthnController' => __DIR__ . '/../../..' . '/core/Controller/WebAuthnController.php',
|
||||
'OC\\Core\\Controller\\WellKnownController' => __DIR__ . '/../../..' . '/core/Controller/WellKnownController.php',
|
||||
'OC\\Core\\Controller\\WhatsNewController' => __DIR__ . '/../../..' . '/core/Controller/WhatsNewController.php',
|
||||
'OC\\Core\\Controller\\WipeController' => __DIR__ . '/../../..' . '/core/Controller/WipeController.php',
|
||||
'OC\\Core\\Data\\LoginFlowV2Credentials' => __DIR__ . '/../../..' . '/core/Data/LoginFlowV2Credentials.php',
|
||||
'OC\\Core\\Data\\LoginFlowV2Tokens' => __DIR__ . '/../../..' . '/core/Data/LoginFlowV2Tokens.php',
|
||||
|
|
@ -1547,7 +1546,6 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
|
|||
'OC\\Core\\Migrations\\Version14000Date20180516101403' => __DIR__ . '/../../..' . '/core/Migrations/Version14000Date20180516101403.php',
|
||||
'OC\\Core\\Migrations\\Version14000Date20180518120534' => __DIR__ . '/../../..' . '/core/Migrations/Version14000Date20180518120534.php',
|
||||
'OC\\Core\\Migrations\\Version14000Date20180522074438' => __DIR__ . '/../../..' . '/core/Migrations/Version14000Date20180522074438.php',
|
||||
'OC\\Core\\Migrations\\Version14000Date20180626223656' => __DIR__ . '/../../..' . '/core/Migrations/Version14000Date20180626223656.php',
|
||||
'OC\\Core\\Migrations\\Version14000Date20180710092004' => __DIR__ . '/../../..' . '/core/Migrations/Version14000Date20180710092004.php',
|
||||
'OC\\Core\\Migrations\\Version14000Date20180712153140' => __DIR__ . '/../../..' . '/core/Migrations/Version14000Date20180712153140.php',
|
||||
'OC\\Core\\Migrations\\Version15000Date20180926101451' => __DIR__ . '/../../..' . '/core/Migrations/Version15000Date20180926101451.php',
|
||||
|
|
@ -1626,6 +1624,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
|
|||
'OC\\Core\\Migrations\\Version33000Date20251124110529' => __DIR__ . '/../../..' . '/core/Migrations/Version33000Date20251124110529.php',
|
||||
'OC\\Core\\Migrations\\Version33000Date20251126152410' => __DIR__ . '/../../..' . '/core/Migrations/Version33000Date20251126152410.php',
|
||||
'OC\\Core\\Migrations\\Version33000Date20251209123503' => __DIR__ . '/../../..' . '/core/Migrations/Version33000Date20251209123503.php',
|
||||
'OC\\Core\\Migrations\\Version34000Date20260122120000' => __DIR__ . '/../../..' . '/core/Migrations/Version34000Date20260122120000.php',
|
||||
'OC\\Core\\Notification\\CoreNotifier' => __DIR__ . '/../../..' . '/core/Notification/CoreNotifier.php',
|
||||
'OC\\Core\\ResponseDefinitions' => __DIR__ . '/../../..' . '/core/ResponseDefinitions.php',
|
||||
'OC\\Core\\Service\\CronService' => __DIR__ . '/../../..' . '/core/Service/CronService.php',
|
||||
|
|
@ -2271,9 +2270,6 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
|
|||
'OC\\Translation\\TranslationManager' => __DIR__ . '/../../..' . '/lib/private/Translation/TranslationManager.php',
|
||||
'OC\\URLGenerator' => __DIR__ . '/../../..' . '/lib/private/URLGenerator.php',
|
||||
'OC\\Updater' => __DIR__ . '/../../..' . '/lib/private/Updater.php',
|
||||
'OC\\Updater\\Changes' => __DIR__ . '/../../..' . '/lib/private/Updater/Changes.php',
|
||||
'OC\\Updater\\ChangesCheck' => __DIR__ . '/../../..' . '/lib/private/Updater/ChangesCheck.php',
|
||||
'OC\\Updater\\ChangesMapper' => __DIR__ . '/../../..' . '/lib/private/Updater/ChangesMapper.php',
|
||||
'OC\\Updater\\Exceptions\\ReleaseMetadataException' => __DIR__ . '/../../..' . '/lib/private/Updater/Exceptions/ReleaseMetadataException.php',
|
||||
'OC\\Updater\\ReleaseMetadata' => __DIR__ . '/../../..' . '/lib/private/Updater/ReleaseMetadata.php',
|
||||
'OC\\Updater\\VersionCheck' => __DIR__ . '/../../..' . '/lib/private/Updater/VersionCheck.php',
|
||||
|
|
|
|||
|
|
@ -1,46 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
namespace OC\Updater;
|
||||
|
||||
use OCP\AppFramework\Db\Entity;
|
||||
use OCP\DB\Types;
|
||||
|
||||
/**
|
||||
* Class Changes
|
||||
*
|
||||
* @package OC\Updater
|
||||
* @method string getVersion()=1
|
||||
* @method void setVersion(string $version)
|
||||
* @method string getEtag()
|
||||
* @method void setEtag(string $etag)
|
||||
* @method int getLastCheck()
|
||||
* @method void setLastCheck(int $lastCheck)
|
||||
* @method string getData()
|
||||
* @method void setData(string $data)
|
||||
*/
|
||||
class Changes extends Entity {
|
||||
/** @var string */
|
||||
protected $version = '';
|
||||
|
||||
/** @var string */
|
||||
protected $etag = '';
|
||||
|
||||
/** @var int */
|
||||
protected $lastCheck = 0;
|
||||
|
||||
/** @var string */
|
||||
protected $data = '';
|
||||
|
||||
public function __construct() {
|
||||
$this->addType('version', 'string');
|
||||
$this->addType('etag', 'string');
|
||||
$this->addType('lastCheck', Types::INTEGER);
|
||||
$this->addType('data', 'string');
|
||||
}
|
||||
}
|
||||
|
|
@ -1,158 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
namespace OC\Updater;
|
||||
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\Http\Client\IClientService;
|
||||
use OCP\Http\Client\IResponse;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class ChangesCheck {
|
||||
/** @var IClientService */
|
||||
protected $clientService;
|
||||
/** @var ChangesMapper */
|
||||
private $mapper;
|
||||
private LoggerInterface $logger;
|
||||
|
||||
public const RESPONSE_NO_CONTENT = 0;
|
||||
public const RESPONSE_USE_CACHE = 1;
|
||||
public const RESPONSE_HAS_CONTENT = 2;
|
||||
|
||||
public function __construct(IClientService $clientService, ChangesMapper $mapper, LoggerInterface $logger) {
|
||||
$this->clientService = $clientService;
|
||||
$this->mapper = $mapper;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws DoesNotExistException
|
||||
* @return array{changelogURL: string, whatsNew: array<string, array{admin: list<string>, regular: list<string>}>}
|
||||
*/
|
||||
public function getChangesForVersion(string $version): array {
|
||||
$version = $this->normalizeVersion($version);
|
||||
$changesInfo = $this->mapper->getChanges($version);
|
||||
$changesData = json_decode($changesInfo->getData(), true);
|
||||
if (empty($changesData)) {
|
||||
throw new DoesNotExistException('Unable to decode changes info');
|
||||
}
|
||||
return $changesData;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function check(string $uri, string $version): array {
|
||||
try {
|
||||
$version = $this->normalizeVersion($version);
|
||||
$changesInfo = $this->mapper->getChanges($version);
|
||||
if ($changesInfo->getLastCheck() + 1800 > time()) {
|
||||
return json_decode($changesInfo->getData(), true);
|
||||
}
|
||||
} catch (DoesNotExistException $e) {
|
||||
$changesInfo = new Changes();
|
||||
}
|
||||
|
||||
$response = $this->queryChangesServer($uri, $changesInfo);
|
||||
|
||||
switch ($this->evaluateResponse($response)) {
|
||||
case self::RESPONSE_NO_CONTENT:
|
||||
return [];
|
||||
case self::RESPONSE_USE_CACHE:
|
||||
return json_decode($changesInfo->getData(), true);
|
||||
case self::RESPONSE_HAS_CONTENT:
|
||||
default:
|
||||
$data = $this->extractData($response->getBody());
|
||||
$changesInfo->setData(json_encode($data));
|
||||
$changesInfo->setEtag($response->getHeader('Etag'));
|
||||
$this->cacheResult($changesInfo, $version);
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
protected function evaluateResponse(IResponse $response): int {
|
||||
if ($response->getStatusCode() === 304) {
|
||||
return self::RESPONSE_USE_CACHE;
|
||||
} elseif ($response->getStatusCode() === 404) {
|
||||
return self::RESPONSE_NO_CONTENT;
|
||||
} elseif ($response->getStatusCode() === 200) {
|
||||
return self::RESPONSE_HAS_CONTENT;
|
||||
}
|
||||
$this->logger->debug('Unexpected return code {code} from changelog server', [
|
||||
'app' => 'core',
|
||||
'code' => $response->getStatusCode(),
|
||||
]);
|
||||
return self::RESPONSE_NO_CONTENT;
|
||||
}
|
||||
|
||||
protected function cacheResult(Changes $entry, string $version) {
|
||||
if ($entry->getVersion() === $version) {
|
||||
$this->mapper->update($entry);
|
||||
} else {
|
||||
$entry->setVersion($version);
|
||||
$this->mapper->insert($entry);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function queryChangesServer(string $uri, Changes $entry): IResponse {
|
||||
$headers = [];
|
||||
if ($entry->getEtag() !== '') {
|
||||
$headers['If-None-Match'] = [$entry->getEtag()];
|
||||
}
|
||||
|
||||
$entry->setLastCheck(time());
|
||||
$client = $this->clientService->newClient();
|
||||
return $client->get($uri, [
|
||||
'headers' => $headers,
|
||||
]);
|
||||
}
|
||||
|
||||
protected function extractData($body):array {
|
||||
$data = [];
|
||||
if ($body) {
|
||||
if (\LIBXML_VERSION < 20900) {
|
||||
$loadEntities = libxml_disable_entity_loader(true);
|
||||
$xml = @simplexml_load_string($body);
|
||||
libxml_disable_entity_loader($loadEntities);
|
||||
} else {
|
||||
$xml = @simplexml_load_string($body);
|
||||
}
|
||||
if ($xml !== false) {
|
||||
$data['changelogURL'] = (string)$xml->changelog['href'];
|
||||
$data['whatsNew'] = [];
|
||||
foreach ($xml->whatsNew as $infoSet) {
|
||||
$data['whatsNew'][(string)$infoSet['lang']] = [
|
||||
'regular' => (array)$infoSet->regular->item,
|
||||
'admin' => (array)$infoSet->admin->item,
|
||||
];
|
||||
}
|
||||
} else {
|
||||
libxml_clear_errors();
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns a x.y.z form of the provided version. Extra numbers will be
|
||||
* omitted, missing ones added as zeros.
|
||||
*/
|
||||
public function normalizeVersion(string $version): string {
|
||||
$versionNumbers = array_slice(explode('.', $version), 0, 3);
|
||||
$versionNumbers[0] = $versionNumbers[0] ?: '0'; // deal with empty input
|
||||
while (count($versionNumbers) < 3) {
|
||||
// changelog server expects x.y.z, pad 0 if it is too short
|
||||
$versionNumbers[] = 0;
|
||||
}
|
||||
return implode('.', $versionNumbers);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
namespace OC\Updater;
|
||||
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\AppFramework\Db\QBMapper;
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\IDBConnection;
|
||||
|
||||
/**
|
||||
* @template-extends QBMapper<Changes>
|
||||
*/
|
||||
class ChangesMapper extends QBMapper {
|
||||
public const TABLE_NAME = 'whats_new';
|
||||
|
||||
public function __construct(IDBConnection $db) {
|
||||
parent::__construct($db, self::TABLE_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws DoesNotExistException
|
||||
*/
|
||||
public function getChanges(string $version): Changes {
|
||||
/* @var $qb IQueryBuilder */
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$result = $qb->select('*')
|
||||
->from(self::TABLE_NAME)
|
||||
->where($qb->expr()->eq('version', $qb->createNamedParameter($version)))
|
||||
->executeQuery();
|
||||
|
||||
$data = $result->fetch();
|
||||
$result->closeCursor();
|
||||
if ($data === false) {
|
||||
throw new DoesNotExistException('Changes info is not present');
|
||||
}
|
||||
return Changes::fromRow($data);
|
||||
}
|
||||
}
|
||||
243
openapi.json
243
openapi.json
|
|
@ -12549,249 +12549,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/ocs/v2.php/core/whatsnew": {
|
||||
"get": {
|
||||
"operationId": "core-whats_new-get",
|
||||
"summary": "Get the changes",
|
||||
"tags": [
|
||||
"core/whats_new"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"bearer_auth": []
|
||||
},
|
||||
{
|
||||
"basic_auth": []
|
||||
}
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "OCS-APIRequest",
|
||||
"in": "header",
|
||||
"description": "Required to be true for the API request to pass",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Changes returned",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"changelogURL",
|
||||
"product",
|
||||
"version"
|
||||
],
|
||||
"properties": {
|
||||
"changelogURL": {
|
||||
"type": "string"
|
||||
},
|
||||
"product": {
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"type": "string"
|
||||
},
|
||||
"whatsNew": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"regular",
|
||||
"admin"
|
||||
],
|
||||
"properties": {
|
||||
"regular": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"admin": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"204": {
|
||||
"description": "No changes"
|
||||
},
|
||||
"401": {
|
||||
"description": "Current user is not logged in",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"operationId": "core-whats_new-dismiss",
|
||||
"summary": "Dismiss the changes",
|
||||
"tags": [
|
||||
"core/whats_new"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"bearer_auth": []
|
||||
},
|
||||
{
|
||||
"basic_auth": []
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"version"
|
||||
],
|
||||
"properties": {
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "Version to dismiss the changes for"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "OCS-APIRequest",
|
||||
"in": "header",
|
||||
"description": "Required to be true for the API request to pass",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Changes dismissed",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"text/plain": {
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "Current user is not logged in",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"ocs"
|
||||
],
|
||||
"properties": {
|
||||
"ocs": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"meta",
|
||||
"data"
|
||||
],
|
||||
"properties": {
|
||||
"meta": {
|
||||
"$ref": "#/components/schemas/OCSMeta"
|
||||
},
|
||||
"data": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/index.php/avatar/{userId}/{size}/dark": {
|
||||
"get": {
|
||||
"operationId": "core-avatar-get-avatar-dark",
|
||||
|
|
|
|||
|
|
@ -1,378 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace Test\Updater;
|
||||
|
||||
use OC\Updater\Changes;
|
||||
use OC\Updater\ChangesCheck;
|
||||
use OC\Updater\ChangesMapper;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\Http\Client\IClient;
|
||||
use OCP\Http\Client\IClientService;
|
||||
use OCP\Http\Client\IResponse;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Test\TestCase;
|
||||
|
||||
class ChangesCheckTest extends TestCase {
|
||||
/** @var IClientService|\PHPUnit\Framework\MockObject\MockObject */
|
||||
protected $clientService;
|
||||
|
||||
/** @var ChangesCheck */
|
||||
protected $checker;
|
||||
|
||||
/** @var ChangesMapper|\PHPUnit\Framework\MockObject\MockObject */
|
||||
protected $mapper;
|
||||
|
||||
/** @var LoggerInterface|\PHPUnit\Framework\MockObject\MockObject */
|
||||
protected $logger;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->clientService = $this->createMock(IClientService::class);
|
||||
$this->mapper = $this->createMock(ChangesMapper::class);
|
||||
$this->logger = $this->createMock(LoggerInterface::class);
|
||||
|
||||
$this->checker = new ChangesCheck($this->clientService, $this->mapper, $this->logger);
|
||||
}
|
||||
|
||||
public static function statusCodeProvider(): array {
|
||||
return [
|
||||
[200, ChangesCheck::RESPONSE_HAS_CONTENT],
|
||||
[304, ChangesCheck::RESPONSE_USE_CACHE],
|
||||
[404, ChangesCheck::RESPONSE_NO_CONTENT],
|
||||
[418, ChangesCheck::RESPONSE_NO_CONTENT],
|
||||
];
|
||||
}
|
||||
|
||||
#[\PHPUnit\Framework\Attributes\DataProvider('statusCodeProvider')]
|
||||
public function testEvaluateResponse(int $statusCode, int $expected): void {
|
||||
$response = $this->createMock(IResponse::class);
|
||||
$response->expects($this->atLeastOnce())
|
||||
->method('getStatusCode')
|
||||
->willReturn($statusCode);
|
||||
|
||||
if (!in_array($statusCode, [200, 304, 404])) {
|
||||
$this->logger->expects($this->once())
|
||||
->method('debug');
|
||||
}
|
||||
|
||||
$evaluation = $this->invokePrivate($this->checker, 'evaluateResponse', [$response]);
|
||||
$this->assertSame($expected, $evaluation);
|
||||
}
|
||||
|
||||
public function testCacheResultInsert(): void {
|
||||
$version = '13.0.4';
|
||||
$entry = $this->createMock(Changes::class);
|
||||
$entry->expects($this->exactly(2))
|
||||
->method('__call')
|
||||
->willReturnMap([
|
||||
['getVersion', [], ''],
|
||||
['setVersion', [$version], null],
|
||||
]);
|
||||
|
||||
$this->mapper->expects($this->once())
|
||||
->method('insert');
|
||||
$this->mapper->expects($this->never())
|
||||
->method('update');
|
||||
|
||||
$this->invokePrivate($this->checker, 'cacheResult', [$entry, $version]);
|
||||
}
|
||||
|
||||
public function testCacheResultUpdate(): void {
|
||||
$version = '13.0.4';
|
||||
$entry = $this->createMock(Changes::class);
|
||||
$entry->expects($this->once())
|
||||
->method('__call')
|
||||
->willReturn($version);
|
||||
|
||||
$this->mapper->expects($this->never())
|
||||
->method('insert');
|
||||
$this->mapper->expects($this->once())
|
||||
->method('update');
|
||||
|
||||
$this->invokePrivate($this->checker, 'cacheResult', [$entry, $version]);
|
||||
}
|
||||
|
||||
public static function changesXMLProvider(): array {
|
||||
return [
|
||||
[ # 0 - full example
|
||||
'<?xml version="1.0" encoding="utf-8" ?>
|
||||
<release xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="https://updates.nextcloud.com/changelog_server/schema.xsd"
|
||||
version="13.0.0">
|
||||
<changelog href="https://nextcloud.com/changelog/#13-0-0"/>
|
||||
<whatsNew lang="en">
|
||||
<regular>
|
||||
<item>Refined user interface</item>
|
||||
<item>End-to-end Encryption</item>
|
||||
<item>Video and Text Chat</item>
|
||||
</regular>
|
||||
<admin>
|
||||
<item>Changes to the Nginx configuration</item>
|
||||
<item>Theming: CSS files were consolidated</item>
|
||||
</admin>
|
||||
</whatsNew>
|
||||
<whatsNew lang="de">
|
||||
<regular>
|
||||
<item>Überarbeitete Benutzerschnittstelle</item>
|
||||
<item>Ende-zu-Ende Verschlüsselung</item>
|
||||
<item>Video- und Text-Chat</item>
|
||||
</regular>
|
||||
<admin>
|
||||
<item>Änderungen an der Nginx Konfiguration</item>
|
||||
<item>Theming: CSS Dateien wurden konsolidiert</item>
|
||||
</admin>
|
||||
</whatsNew>
|
||||
</release>',
|
||||
[
|
||||
'changelogURL' => 'https://nextcloud.com/changelog/#13-0-0',
|
||||
'whatsNew' => [
|
||||
'en' => [
|
||||
'regular' => [
|
||||
'Refined user interface',
|
||||
'End-to-end Encryption',
|
||||
'Video and Text Chat'
|
||||
],
|
||||
'admin' => [
|
||||
'Changes to the Nginx configuration',
|
||||
'Theming: CSS files were consolidated'
|
||||
],
|
||||
],
|
||||
'de' => [
|
||||
'regular' => [
|
||||
'Überarbeitete Benutzerschnittstelle',
|
||||
'Ende-zu-Ende Verschlüsselung',
|
||||
'Video- und Text-Chat'
|
||||
],
|
||||
'admin' => [
|
||||
'Änderungen an der Nginx Konfiguration',
|
||||
'Theming: CSS Dateien wurden konsolidiert'
|
||||
],
|
||||
],
|
||||
],
|
||||
]
|
||||
],
|
||||
[ # 1- admin part not translated
|
||||
'<?xml version="1.0" encoding="utf-8" ?>
|
||||
<release xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="https://updates.nextcloud.com/changelog_server/schema.xsd"
|
||||
version="13.0.0">
|
||||
<changelog href="https://nextcloud.com/changelog/#13-0-0"/>
|
||||
<whatsNew lang="en">
|
||||
<regular>
|
||||
<item>Refined user interface</item>
|
||||
<item>End-to-end Encryption</item>
|
||||
<item>Video and Text Chat</item>
|
||||
</regular>
|
||||
<admin>
|
||||
<item>Changes to the Nginx configuration</item>
|
||||
<item>Theming: CSS files were consolidated</item>
|
||||
</admin>
|
||||
</whatsNew>
|
||||
<whatsNew lang="de">
|
||||
<regular>
|
||||
<item>Überarbeitete Benutzerschnittstelle</item>
|
||||
<item>Ende-zu-Ende Verschlüsselung</item>
|
||||
<item>Video- und Text-Chat</item>
|
||||
</regular>
|
||||
</whatsNew>
|
||||
</release>',
|
||||
[
|
||||
'changelogURL' => 'https://nextcloud.com/changelog/#13-0-0',
|
||||
'whatsNew' => [
|
||||
'en' => [
|
||||
'regular' => [
|
||||
'Refined user interface',
|
||||
'End-to-end Encryption',
|
||||
'Video and Text Chat'
|
||||
],
|
||||
'admin' => [
|
||||
'Changes to the Nginx configuration',
|
||||
'Theming: CSS files were consolidated'
|
||||
],
|
||||
],
|
||||
'de' => [
|
||||
'regular' => [
|
||||
'Überarbeitete Benutzerschnittstelle',
|
||||
'Ende-zu-Ende Verschlüsselung',
|
||||
'Video- und Text-Chat'
|
||||
],
|
||||
'admin' => [
|
||||
],
|
||||
],
|
||||
],
|
||||
]
|
||||
],
|
||||
[ # 2 - minimal set
|
||||
'<?xml version="1.0" encoding="utf-8" ?>
|
||||
<release xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="https://updates.nextcloud.com/changelog_server/schema.xsd"
|
||||
version="13.0.0">
|
||||
<changelog href="https://nextcloud.com/changelog/#13-0-0"/>
|
||||
<whatsNew lang="en">
|
||||
<regular>
|
||||
<item>Refined user interface</item>
|
||||
<item>End-to-end Encryption</item>
|
||||
<item>Video and Text Chat</item>
|
||||
</regular>
|
||||
</whatsNew>
|
||||
</release>',
|
||||
[
|
||||
'changelogURL' => 'https://nextcloud.com/changelog/#13-0-0',
|
||||
'whatsNew' => [
|
||||
'en' => [
|
||||
'regular' => [
|
||||
'Refined user interface',
|
||||
'End-to-end Encryption',
|
||||
'Video and Text Chat'
|
||||
],
|
||||
'admin' => [],
|
||||
],
|
||||
],
|
||||
]
|
||||
],
|
||||
[ # 3 - minimal set (procrastinator edition)
|
||||
'<?xml version="1.0" encoding="utf-8" ?>
|
||||
<release xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="https://updates.nextcloud.com/changelog_server/schema.xsd"
|
||||
version="13.0.0">
|
||||
<changelog href="https://nextcloud.com/changelog/#13-0-0"/>
|
||||
<whatsNew lang="en">
|
||||
<regular>
|
||||
<item>Write this tomorrow</item>
|
||||
</regular>
|
||||
</whatsNew>
|
||||
</release>',
|
||||
[
|
||||
'changelogURL' => 'https://nextcloud.com/changelog/#13-0-0',
|
||||
'whatsNew' => [
|
||||
'en' => [
|
||||
'regular' => [
|
||||
'Write this tomorrow',
|
||||
],
|
||||
'admin' => [],
|
||||
],
|
||||
],
|
||||
]
|
||||
],
|
||||
[ # 4 - empty
|
||||
'',
|
||||
[]
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[\PHPUnit\Framework\Attributes\DataProvider('changesXMLProvider')]
|
||||
public function testExtractData(string $body, array $expected): void {
|
||||
$actual = $this->invokePrivate($this->checker, 'extractData', [$body]);
|
||||
$this->assertSame($expected, $actual);
|
||||
}
|
||||
|
||||
public static function etagProvider() {
|
||||
return [
|
||||
[''],
|
||||
['a27aab83d8205d73978435076e53d143']
|
||||
];
|
||||
}
|
||||
|
||||
#[\PHPUnit\Framework\Attributes\DataProvider('etagProvider')]
|
||||
public function testQueryChangesServer(string $etag): void {
|
||||
$uri = 'https://changes.nextcloud.server/?13.0.5';
|
||||
$entry = $this->createMock(Changes::class);
|
||||
$entry->expects($this->any())
|
||||
->method('__call')
|
||||
->willReturn($etag);
|
||||
|
||||
$expectedHeaders = $etag === '' ? [] : ['If-None-Match' => [$etag]];
|
||||
|
||||
$client = $this->createMock(IClient::class);
|
||||
$client->expects($this->once())
|
||||
->method('get')
|
||||
->with($uri, ['headers' => $expectedHeaders])
|
||||
->willReturn($this->createMock(IResponse::class));
|
||||
|
||||
$this->clientService->expects($this->once())
|
||||
->method('newClient')
|
||||
->willReturn($client);
|
||||
|
||||
$response = $this->invokePrivate($this->checker, 'queryChangesServer', [$uri, $entry]);
|
||||
$this->assertInstanceOf(IResponse::class, $response);
|
||||
}
|
||||
|
||||
public static function versionProvider(): array {
|
||||
return [
|
||||
['13.0.7', '13.0.7'],
|
||||
['13.0.7.3', '13.0.7'],
|
||||
['13.0.7.3.42', '13.0.7'],
|
||||
['13.0', '13.0.0'],
|
||||
['13', '13.0.0'],
|
||||
['', '0.0.0'],
|
||||
];
|
||||
}
|
||||
|
||||
#[\PHPUnit\Framework\Attributes\DataProvider('versionProvider')]
|
||||
public function testNormalizeVersion(string $input, string $expected): void {
|
||||
$normalized = $this->checker->normalizeVersion($input);
|
||||
$this->assertSame($expected, $normalized);
|
||||
}
|
||||
|
||||
public static function changeDataProvider():array {
|
||||
$testDataFound = $testDataNotFound = self::versionProvider();
|
||||
array_walk($testDataFound, static function (&$params): void {
|
||||
$params[] = true;
|
||||
});
|
||||
array_walk($testDataNotFound, static function (&$params): void {
|
||||
$params[] = false;
|
||||
});
|
||||
return array_merge($testDataFound, $testDataNotFound);
|
||||
}
|
||||
|
||||
#[\PHPUnit\Framework\Attributes\DataProvider('changeDataProvider')]
|
||||
public function testGetChangesForVersion(string $inputVersion, string $normalizedVersion, bool $isFound): void {
|
||||
$mocker = $this->mapper->expects($this->once())
|
||||
->method('getChanges')
|
||||
->with($normalizedVersion);
|
||||
|
||||
if (!$isFound) {
|
||||
$this->expectException(DoesNotExistException::class);
|
||||
$mocker->willThrowException(new DoesNotExistException('Changes info is not present'));
|
||||
} else {
|
||||
$entry = $this->createMock(Changes::class);
|
||||
$entry->expects($this->once())
|
||||
->method('__call')
|
||||
->with('getData')
|
||||
->willReturn('{"changelogURL":"https:\/\/nextcloud.com\/changelog\/#13-0-0","whatsNew":{"en":{"regular":["Refined user interface","End-to-end Encryption","Video and Text Chat"],"admin":["Changes to the Nginx configuration","Theming: CSS files were consolidated"]},"de":{"regular":["\u00dcberarbeitete Benutzerschnittstelle","Ende-zu-Ende Verschl\u00fcsselung","Video- und Text-Chat"],"admin":["\u00c4nderungen an der Nginx Konfiguration","Theming: CSS Dateien wurden konsolidiert"]}}}');
|
||||
|
||||
$mocker->willReturn($entry);
|
||||
}
|
||||
|
||||
/** @noinspection PhpUnhandledExceptionInspection */
|
||||
$data = $this->checker->getChangesForVersion($inputVersion);
|
||||
$this->assertTrue(isset($data['whatsNew']['en']['regular']));
|
||||
$this->assertTrue(isset($data['changelogURL']));
|
||||
}
|
||||
|
||||
public function testGetChangesForVersionEmptyData(): void {
|
||||
$entry = $this->createMock(Changes::class);
|
||||
$entry->expects($this->once())
|
||||
->method('__call')
|
||||
->with('getData')
|
||||
->willReturn('');
|
||||
|
||||
$this->mapper->expects($this->once())
|
||||
->method('getChanges')
|
||||
->with('13.0.7')
|
||||
->willReturn($entry);
|
||||
|
||||
$this->expectException(DoesNotExistException::class);
|
||||
/** @noinspection PhpUnhandledExceptionInspection */
|
||||
$this->checker->getChangesForVersion('13.0.7');
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue