aboutsummaryrefslogtreecommitdiffstats
path: root/apps/updatenotification/lib
diff options
context:
space:
mode:
Diffstat (limited to 'apps/updatenotification/lib')
-rw-r--r--apps/updatenotification/lib/AppInfo/Application.php69
-rw-r--r--apps/updatenotification/lib/BackgroundJob/AppUpdatedNotifications.php109
-rw-r--r--apps/updatenotification/lib/BackgroundJob/ResetToken.php49
-rw-r--r--apps/updatenotification/lib/BackgroundJob/UpdateAvailableNotifications.php (renamed from apps/updatenotification/lib/Notification/BackgroundJob.php)174
-rw-r--r--apps/updatenotification/lib/Command/Check.php52
-rw-r--r--apps/updatenotification/lib/Controller/APIController.php165
-rw-r--r--apps/updatenotification/lib/Controller/AdminController.php78
-rw-r--r--apps/updatenotification/lib/Controller/ChangelogController.php62
-rw-r--r--apps/updatenotification/lib/Listener/AppUpdateEventListener.php61
-rw-r--r--apps/updatenotification/lib/Listener/BeforeTemplateRenderedEventListener.php54
-rw-r--r--apps/updatenotification/lib/Manager.php114
-rw-r--r--apps/updatenotification/lib/Notification/AppUpdateNotifier.php104
-rw-r--r--apps/updatenotification/lib/Notification/Notifier.php153
-rw-r--r--apps/updatenotification/lib/ResetTokenBackgroundJob.php66
-rw-r--r--apps/updatenotification/lib/ResponseDefinitions.php19
-rw-r--r--apps/updatenotification/lib/Settings/Admin.php139
-rw-r--r--apps/updatenotification/lib/UpdateChecker.php57
17 files changed, 917 insertions, 608 deletions
diff --git a/apps/updatenotification/lib/AppInfo/Application.php b/apps/updatenotification/lib/AppInfo/Application.php
index 3290b88dcf9..2a1678da5db 100644
--- a/apps/updatenotification/lib/AppInfo/Application.php
+++ b/apps/updatenotification/lib/AppInfo/Application.php
@@ -3,64 +3,55 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2018, Joas Schilling <coding@schilljs.com>
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Lukas Reschke <lukas@statuscode.ch>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
+ * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\UpdateNotification\AppInfo;
+use OCA\UpdateNotification\Listener\AppUpdateEventListener;
+use OCA\UpdateNotification\Listener\BeforeTemplateRenderedEventListener;
+use OCA\UpdateNotification\Notification\AppUpdateNotifier;
use OCA\UpdateNotification\Notification\Notifier;
use OCA\UpdateNotification\UpdateChecker;
+use OCP\App\Events\AppUpdateEvent;
use OCP\App\IAppManager;
use OCP\AppFramework\App;
use OCP\AppFramework\Bootstrap\IBootContext;
use OCP\AppFramework\Bootstrap\IBootstrap;
use OCP\AppFramework\Bootstrap\IRegistrationContext;
-use OCP\AppFramework\IAppContainer;
-use OCP\AppFramework\QueryException;
+use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent;
use OCP\IConfig;
use OCP\IGroupManager;
-use OCP\ILogger;
use OCP\IUser;
use OCP\IUserSession;
use OCP\Util;
+use Psr\Container\ContainerExceptionInterface;
+use Psr\Container\ContainerInterface;
+use Psr\Log\LoggerInterface;
class Application extends App implements IBootstrap {
+ public const APP_NAME = 'updatenotification';
+
public function __construct() {
- parent::__construct('updatenotification', []);
+ parent::__construct(self::APP_NAME, []);
}
public function register(IRegistrationContext $context): void {
$context->registerNotifierService(Notifier::class);
+ $context->registerNotifierService(AppUpdateNotifier::class);
+
+ $context->registerEventListener(AppUpdateEvent::class, AppUpdateEventListener::class);
+ $context->registerEventListener(BeforeTemplateRenderedEvent::class, BeforeTemplateRenderedEventListener::class);
}
public function boot(IBootContext $context): void {
$context->injectFn(function (IConfig $config,
- IUserSession $userSession,
- IAppManager $appManager,
- IGroupManager $groupManager,
- IAppContainer $appContainer,
- ILogger $logger) {
+ IUserSession $userSession,
+ IAppManager $appManager,
+ IGroupManager $groupManager,
+ ContainerInterface $container,
+ LoggerInterface $logger,
+ ): void {
if ($config->getSystemValue('updatechecker', true) !== true) {
// Updater check is disabled
return;
@@ -72,18 +63,18 @@ class Application extends App implements IBootstrap {
return;
}
- if (!$appManager->isEnabledForUser('notifications') &&
- $groupManager->isAdmin($user->getUID())) {
+ if (!$appManager->isEnabledForUser('notifications')
+ && $groupManager->isAdmin($user->getUID())) {
try {
- $updateChecker = $appContainer->get(UpdateChecker::class);
- } catch (QueryException $e) {
- $logger->logException($e);
+ $updateChecker = $container->get(UpdateChecker::class);
+ } catch (ContainerExceptionInterface $e) {
+ $logger->error($e->getMessage(), ['exception' => $e]);
return;
}
if ($updateChecker->getUpdateState() !== []) {
- Util::addScript('updatenotification', 'legacy-notification');
- \OC_Hook::connect('\OCP\Config', 'js', $updateChecker, 'populateJavaScriptVariables');
+ Util::addScript(self::APP_NAME, 'update-notification-legacy');
+ $updateChecker->setInitialState();
}
}
});
diff --git a/apps/updatenotification/lib/BackgroundJob/AppUpdatedNotifications.php b/apps/updatenotification/lib/BackgroundJob/AppUpdatedNotifications.php
new file mode 100644
index 00000000000..049390546ed
--- /dev/null
+++ b/apps/updatenotification/lib/BackgroundJob/AppUpdatedNotifications.php
@@ -0,0 +1,109 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\UpdateNotification\BackgroundJob;
+
+use OCA\UpdateNotification\AppInfo\Application;
+use OCA\UpdateNotification\Manager;
+use OCP\App\IAppManager;
+use OCP\AppFramework\Services\IAppConfig;
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\BackgroundJob\QueuedJob;
+use OCP\IConfig;
+use OCP\IUser;
+use OCP\IUserManager;
+use OCP\Notification\IManager;
+use OCP\Notification\INotification;
+use Psr\Log\LoggerInterface;
+
+class AppUpdatedNotifications extends QueuedJob {
+ public function __construct(
+ ITimeFactory $time,
+ private IConfig $config,
+ private IAppConfig $appConfig,
+ private IManager $notificationManager,
+ private IUserManager $userManager,
+ private IAppManager $appManager,
+ private LoggerInterface $logger,
+ private Manager $manager,
+ ) {
+ parent::__construct($time);
+ }
+
+ /**
+ * @param array{appId: string, timestamp: int} $argument
+ */
+ protected function run(mixed $argument): void {
+ $appId = $argument['appId'];
+ $timestamp = $argument['timestamp'];
+ $dateTime = $this->time->getDateTime();
+ $dateTime->setTimestamp($timestamp);
+
+ $this->logger->debug(
+ 'Running background job to create app update notifications for "' . $appId . '"',
+ [
+ 'app' => Application::APP_NAME,
+ ],
+ );
+
+ if ($this->manager->getChangelogFile($appId, 'en') === null) {
+ $this->logger->debug('Skipping app updated notification - no changelog provided');
+ return;
+ }
+
+ $this->stopPreviousNotifications($appId);
+
+ // Create new notifications
+ $notification = $this->notificationManager->createNotification();
+ $notification->setApp(Application::APP_NAME)
+ ->setDateTime($dateTime)
+ ->setSubject('app_updated', [$appId])
+ ->setObject('app_updated', $appId);
+
+ $this->notifyUsers($appId, $notification);
+ }
+
+ /**
+ * Stop all previous notifications users might not have dismissed until now
+ * @param string $appId The app to stop update notifications for
+ */
+ private function stopPreviousNotifications(string $appId): void {
+ $notification = $this->notificationManager->createNotification();
+ $notification->setApp(Application::APP_NAME)
+ ->setObject('app_updated', $appId);
+ $this->notificationManager->markProcessed($notification);
+ }
+
+ /**
+ * Notify all users for which the updated app is enabled
+ */
+ private function notifyUsers(string $appId, INotification $notification): void {
+ $guestsEnabled = $this->appConfig->getAppValueBool('app_updated.notify_guests', false) && class_exists('\OCA\Guests\UserBackend');
+
+ $isDefer = $this->notificationManager->defer();
+
+ // Notify all seen users about the app update
+ $this->userManager->callForSeenUsers(function (IUser $user) use ($guestsEnabled, $appId, $notification): void {
+ if (!$guestsEnabled && ($user->getBackendClassName() === '\OCA\Guests\UserBackend')) {
+ return;
+ }
+
+ if (!$this->appManager->isEnabledForUser($appId, $user)) {
+ return;
+ }
+
+ $notification->setUser($user->getUID());
+ $this->notificationManager->notify($notification);
+ });
+
+ // If we enabled the defer we call the flush
+ if ($isDefer) {
+ $this->notificationManager->flush();
+ }
+ }
+}
diff --git a/apps/updatenotification/lib/BackgroundJob/ResetToken.php b/apps/updatenotification/lib/BackgroundJob/ResetToken.php
new file mode 100644
index 00000000000..35543ce5247
--- /dev/null
+++ b/apps/updatenotification/lib/BackgroundJob/ResetToken.php
@@ -0,0 +1,49 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+namespace OCA\UpdateNotification\BackgroundJob;
+
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\BackgroundJob\TimedJob;
+use OCP\IAppConfig;
+use OCP\IConfig;
+
+/**
+ * Deletes the updater secret after if it is older than 48h
+ */
+class ResetToken extends TimedJob {
+
+ /**
+ * @param IConfig $config
+ * @param ITimeFactory $timeFactory
+ */
+ public function __construct(
+ ITimeFactory $time,
+ private IConfig $config,
+ private IAppConfig $appConfig,
+ ) {
+ parent::__construct($time);
+ // Run all 10 minutes
+ parent::setInterval(60 * 10);
+ }
+
+ /**
+ * @param $argument
+ */
+ protected function run($argument) {
+ if ($this->config->getSystemValueBool('config_is_read_only')) {
+ return;
+ }
+
+ $secretCreated = $this->appConfig->getValueInt('core', 'updater.secret.created', $this->time->getTime());
+ // Delete old tokens after 2 days
+ if ($secretCreated >= 172800) {
+ $this->config->deleteSystemValue('updater.secret');
+ }
+ }
+}
diff --git a/apps/updatenotification/lib/Notification/BackgroundJob.php b/apps/updatenotification/lib/BackgroundJob/UpdateAvailableNotifications.php
index e7dc193df6c..8879bb0c223 100644
--- a/apps/updatenotification/lib/Notification/BackgroundJob.php
+++ b/apps/updatenotification/lib/BackgroundJob/UpdateAvailableNotifications.php
@@ -3,81 +3,58 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Morris Jobke <hey@morrisjobke.de>
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
-namespace OCA\UpdateNotification\Notification;
+namespace OCA\UpdateNotification\BackgroundJob;
-use OC\BackgroundJob\TimedJob;
use OC\Installer;
use OC\Updater\VersionCheck;
+use OCA\UpdateNotification\AppInfo\Application;
use OCP\App\IAppManager;
-use OCP\Http\Client\IClientService;
+use OCP\AppFramework\Services\IAppConfig;
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\BackgroundJob\TimedJob;
use OCP\IConfig;
use OCP\IGroup;
use OCP\IGroupManager;
use OCP\Notification\IManager;
+use OCP\ServerVersion;
-class BackgroundJob extends TimedJob {
- protected $connectionNotifications = [3, 7, 14, 30];
+class UpdateAvailableNotifications extends TimedJob {
- /** @var IConfig */
- protected $config;
-
- /** @var IManager */
- protected $notificationManager;
-
- /** @var IGroupManager */
- protected $groupManager;
-
- /** @var IAppManager */
- protected $appManager;
-
- /** @var IClientService */
- protected $client;
-
- /** @var Installer */
- protected $installer;
-
- /** @var string[] */
- protected $users;
-
- public function __construct(IConfig $config,
- IManager $notificationManager,
- IGroupManager $groupManager,
- IAppManager $appManager,
- IClientService $client,
- Installer $installer) {
+ /**
+ * Numbers of failed updater connection to report error as notification.
+ * @var list<int>
+ */
+ protected const CONNECTION_NOTIFICATIONS = [3, 7, 14, 30];
+
+ /** @var ?string[] */
+ protected $users = null;
+
+ public function __construct(
+ ITimeFactory $timeFactory,
+ protected ServerVersion $serverVersion,
+ protected IConfig $config,
+ protected IAppConfig $appConfig,
+ protected IManager $notificationManager,
+ protected IGroupManager $groupManager,
+ protected IAppManager $appManager,
+ protected Installer $installer,
+ protected VersionCheck $versionCheck,
+ ) {
+ parent::__construct($timeFactory);
// Run once a day
$this->setInterval(60 * 60 * 24);
-
- $this->config = $config;
- $this->notificationManager = $notificationManager;
- $this->groupManager = $groupManager;
- $this->appManager = $appManager;
- $this->client = $client;
- $this->installer = $installer;
+ $this->setTimeSensitivity(self::TIME_INSENSITIVE);
}
protected function run($argument) {
+ // Do not check for updates if not connected to the internet
+ if (!$this->config->getSystemValueBool('has_internet_connection', true)) {
+ return;
+ }
+
if (\OC::$CLI && !$this->config->getSystemValueBool('debug', false)) {
try {
// Jitter the pinging of the updater server and the appstore a bit.
@@ -93,26 +70,29 @@ class BackgroundJob extends TimedJob {
}
/**
- * Check for ownCloud update
+ * Check for Nextcloud server update
*/
- protected function checkCoreUpdate() {
- if (\in_array($this->getChannel(), ['daily', 'git'], true)) {
- // "These aren't the update channels you're looking for." - Ben Obi-Wan Kenobi
+ protected function checkCoreUpdate(): void {
+ if (!$this->config->getSystemValueBool('updatechecker', true)) {
+ // update checker is disabled so no core update check!
return;
}
- $updater = $this->createVersionCheck();
+ if (\in_array($this->serverVersion->getChannel(), ['daily', 'git'], true)) {
+ // "These aren't the update channels you're looking for." - Ben Obi-Wan Kenobi
+ return;
+ }
- $status = $updater->check();
+ $status = $this->versionCheck->check();
if ($status === false) {
- $errors = 1 + (int) $this->config->getAppValue('updatenotification', 'update_check_errors', 0);
- $this->config->setAppValue('updatenotification', 'update_check_errors', $errors);
+ $errors = 1 + $this->appConfig->getAppValueInt('update_check_errors', 0);
+ $this->appConfig->setAppValueInt('update_check_errors', $errors);
- if (\in_array($errors, $this->connectionNotifications, true)) {
+ if (\in_array($errors, self::CONNECTION_NOTIFICATIONS, true)) {
$this->sendErrorNotifications($errors);
}
} elseif (\is_array($status)) {
- $this->config->setAppValue('updatenotification', 'update_check_errors', 0);
+ $this->appConfig->setAppValueInt('update_check_errors', 0);
$this->clearErrorNotifications();
if (isset($status['version'])) {
@@ -125,14 +105,14 @@ class BackgroundJob extends TimedJob {
* Send a message to the admin when the update server could not be reached
* @param int $numDays
*/
- protected function sendErrorNotifications($numDays) {
+ protected function sendErrorNotifications($numDays): void {
$this->clearErrorNotifications();
$notification = $this->notificationManager->createNotification();
try {
- $notification->setApp('updatenotification')
+ $notification->setApp(Application::APP_NAME)
->setDateTime(new \DateTime())
- ->setObject('updatenotification', 'error')
+ ->setObject(Application::APP_NAME, 'error')
->setSubject('connection_error', ['days' => $numDays]);
foreach ($this->getUsersToNotify() as $uid) {
@@ -147,12 +127,12 @@ class BackgroundJob extends TimedJob {
/**
* Remove error notifications again
*/
- protected function clearErrorNotifications() {
+ protected function clearErrorNotifications(): void {
$notification = $this->notificationManager->createNotification();
try {
- $notification->setApp('updatenotification')
+ $notification->setApp(Application::APP_NAME)
->setSubject('connection_error')
- ->setObject('updatenotification', 'error');
+ ->setObject(Application::APP_NAME, 'error');
} catch (\InvalidArgumentException $e) {
return;
}
@@ -162,8 +142,8 @@ class BackgroundJob extends TimedJob {
/**
* Check all installed apps for updates
*/
- protected function checkAppUpdates() {
- $apps = $this->appManager->getInstalledApps();
+ protected function checkAppUpdates(): void {
+ $apps = $this->appManager->getEnabledApps();
foreach ($apps as $app) {
$update = $this->isUpdateAvailable($app);
if ($update !== false) {
@@ -179,21 +159,21 @@ class BackgroundJob extends TimedJob {
* @param string $version
* @param string $visibleVersion
*/
- protected function createNotifications($app, $version, $visibleVersion = '') {
- $lastNotification = $this->config->getAppValue('updatenotification', $app, false);
+ protected function createNotifications($app, $version, $visibleVersion = ''): void {
+ $lastNotification = $this->appConfig->getAppValueString($app, '');
if ($lastNotification === $version) {
// We already notified about this update
return;
}
- if ($lastNotification !== false) {
+ if ($lastNotification !== '') {
// Delete old updates
$this->deleteOutdatedNotifications($app, $lastNotification);
}
$notification = $this->notificationManager->createNotification();
try {
- $notification->setApp('updatenotification')
+ $notification->setApp(Application::APP_NAME)
->setDateTime(new \DateTime())
->setObject($app, $version);
@@ -211,7 +191,7 @@ class BackgroundJob extends TimedJob {
return;
}
- $this->config->setAppValue('updatenotification', $app, $version);
+ $this->appConfig->setAppValueString($app, $version);
}
/**
@@ -222,19 +202,18 @@ class BackgroundJob extends TimedJob {
return $this->users;
}
- $notifyGroups = (array) json_decode($this->config->getAppValue('updatenotification', 'notify_groups', '["admin"]'), true);
+ $notifyGroups = $this->appConfig->getAppValueArray('notify_groups', ['admin']);
$this->users = [];
foreach ($notifyGroups as $group) {
$groupToNotify = $this->groupManager->get($group);
if ($groupToNotify instanceof IGroup) {
foreach ($groupToNotify->getUsers() as $user) {
- $this->users[$user->getUID()] = true;
+ $this->users[] = $user->getUID();
}
}
}
- $this->users = array_keys($this->users);
-
+ $this->users = array_values(array_unique($this->users));
return $this->users;
}
@@ -244,35 +223,18 @@ class BackgroundJob extends TimedJob {
* @param string $app
* @param string $version
*/
- protected function deleteOutdatedNotifications($app, $version) {
+ protected function deleteOutdatedNotifications($app, $version): void {
$notification = $this->notificationManager->createNotification();
try {
- $notification->setApp('updatenotification')
+ $notification->setApp(Application::APP_NAME)
->setObject($app, $version);
- } catch (\InvalidArgumentException $e) {
+ } catch (\InvalidArgumentException) {
return;
}
$this->notificationManager->markProcessed($notification);
}
/**
- * @return VersionCheck
- */
- protected function createVersionCheck(): VersionCheck {
- return new VersionCheck(
- $this->client,
- $this->config
- );
- }
-
- /**
- * @return string
- */
- protected function getChannel(): string {
- return \OC_Util::getChannel();
- }
-
- /**
* @param string $app
* @return string|false
*/
diff --git a/apps/updatenotification/lib/Command/Check.php b/apps/updatenotification/lib/Command/Check.php
index 7126a6b50b0..d93e4935012 100644
--- a/apps/updatenotification/lib/Command/Check.php
+++ b/apps/updatenotification/lib/Command/Check.php
@@ -3,28 +3,8 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2018, Tobia De Koninck (tobia@ledfan.be)
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author J0WI <J0WI@users.noreply.github.com>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Tobia De Koninck <LEDfan@users.noreply.github.com>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
+ * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\UpdateNotification\Command;
@@ -37,26 +17,12 @@ use Symfony\Component\Console\Output\OutputInterface;
class Check extends Command {
- /**
- * @var Installer $installer
- */
- private $installer;
-
- /**
- * @var AppManager $appManager
- */
- private $appManager;
-
- /**
- * @var UpdateChecker $updateChecker
- */
- private $updateChecker;
-
- public function __construct(AppManager $appManager, UpdateChecker $updateChecker, Installer $installer) {
+ public function __construct(
+ private AppManager $appManager,
+ private UpdateChecker $updateChecker,
+ private Installer $installer,
+ ) {
parent::__construct();
- $this->installer = $installer;
- $this->appManager = $appManager;
- $this->updateChecker = $updateChecker;
}
protected function configure(): void {
@@ -72,13 +38,13 @@ class Check extends Command {
// Server
$r = $this->updateChecker->getUpdateState();
if (isset($r['updateAvailable']) && $r['updateAvailable']) {
- $output->writeln($r['updateVersionString'] . ' is available. Get more information on how to update at '. $r['updateLink'] . '.');
+ $output->writeln($r['updateVersionString'] . ' is available. Get more information on how to update at ' . $r['updateLink'] . '.');
$updatesAvailableCount += 1;
}
// Apps
- $apps = $this->appManager->getInstalledApps();
+ $apps = $this->appManager->getEnabledApps();
foreach ($apps as $app) {
$update = $this->installer->isUpdateAvailable($app);
if ($update !== false) {
diff --git a/apps/updatenotification/lib/Controller/APIController.php b/apps/updatenotification/lib/Controller/APIController.php
index 9d5d1c2d764..4360d814dd2 100644
--- a/apps/updatenotification/lib/Controller/APIController.php
+++ b/apps/updatenotification/lib/Controller/APIController.php
@@ -3,30 +3,14 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2017 Joas Schilling <coding@schilljs.com>
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Joas Schilling <coding@schilljs.com>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OCA\UpdateNotification\Controller;
use OC\App\AppStore\Fetcher\AppFetcher;
+use OCA\UpdateNotification\Manager;
+use OCA\UpdateNotification\ResponseDefinitions;
use OCP\App\AppPathNotFoundException;
use OCP\App\IAppManager;
use OCP\AppFramework\Http;
@@ -37,45 +21,50 @@ use OCP\IRequest;
use OCP\IUserSession;
use OCP\L10N\IFactory;
+/**
+ * @psalm-import-type UpdateNotificationApp from ResponseDefinitions
+ */
class APIController extends OCSController {
- /** @var IConfig */
- protected $config;
-
- /** @var IAppManager */
- protected $appManager;
-
- /** @var AppFetcher */
- protected $appFetcher;
-
- /** @var IFactory */
- protected $l10nFactory;
+ protected ?string $language = null;
- /** @var IUserSession */
- protected $userSession;
-
- /** @var string */
- protected $language;
-
- public function __construct(string $appName,
- IRequest $request,
- IConfig $config,
- IAppManager $appManager,
- AppFetcher $appFetcher,
- IFactory $l10nFactory,
- IUserSession $userSession) {
+ /**
+ * List of apps that were in the appstore but are now shipped and don't have
+ * a compatible update available.
+ *
+ * @var array<string, int>
+ */
+ protected array $appsShippedInFutureVersion = [
+ 'bruteforcesettings' => 25,
+ 'suspicious_login' => 25,
+ 'twofactor_totp' => 25,
+ 'files_downloadlimit' => 29,
+ 'twofactor_nextcloud_notification' => 30,
+ 'app_api' => 30,
+ ];
+
+ public function __construct(
+ string $appName,
+ IRequest $request,
+ protected IConfig $config,
+ protected IAppManager $appManager,
+ protected AppFetcher $appFetcher,
+ protected IFactory $l10nFactory,
+ protected IUserSession $userSession,
+ protected Manager $manager,
+ ) {
parent::__construct($appName, $request);
-
- $this->config = $config;
- $this->appManager = $appManager;
- $this->appFetcher = $appFetcher;
- $this->l10nFactory = $l10nFactory;
- $this->userSession = $userSession;
}
/**
- * @param string $newVersion
- * @return DataResponse
+ * List available updates for apps
+ *
+ * @param string $newVersion Server version to check updates for
+ *
+ * @return DataResponse<Http::STATUS_OK, array{missing: list<UpdateNotificationApp>, available: list<UpdateNotificationApp>}, array{}>|DataResponse<Http::STATUS_NOT_FOUND, array{appstore_disabled: bool, already_on_latest?: bool}, array{}>
+ *
+ * 200: Apps returned
+ * 404: New versions not found
*/
public function getAppList(string $newVersion): DataResponse {
if (!$this->config->getSystemValue('appstoreenabled', true)) {
@@ -85,14 +74,14 @@ class APIController extends OCSController {
}
// Get list of installed custom apps
- $installedApps = $this->appManager->getInstalledApps();
+ $installedApps = $this->appManager->getEnabledApps();
$installedApps = array_filter($installedApps, function ($app) {
try {
$this->appManager->getAppPath($app);
} catch (AppPathNotFoundException $e) {
return false;
}
- return !$this->appManager->isShipped($app);
+ return !$this->appManager->isShipped($app) && !isset($this->appsShippedInFutureVersion[$app]);
});
if (empty($installedApps)) {
@@ -105,7 +94,7 @@ class APIController extends OCSController {
$this->appFetcher->setVersion($newVersion, 'future-apps.json', false);
// Apps available on the app store for that version
- $availableApps = array_map(static function (array $app) {
+ $availableApps = array_map(static function (array $app): string {
return $app['id'];
}, $this->appFetcher->get());
@@ -116,7 +105,14 @@ class APIController extends OCSController {
], Http::STATUS_NOT_FOUND);
}
- $this->language = $this->l10nFactory->getUserLanguage($this->userSession->getUser());
+ // Ignore apps that are deployed from git
+ $installedApps = array_filter($installedApps, function (string $appId) {
+ try {
+ return !file_exists($this->appManager->getAppPath($appId) . '/.git');
+ } catch (AppPathNotFoundException $e) {
+ return true;
+ }
+ });
$missing = array_diff($installedApps, $availableApps);
$missing = array_map([$this, 'getAppDetails'], $missing);
@@ -136,13 +132,66 @@ class APIController extends OCSController {
* Get translated app name
*
* @param string $appId
- * @return string[]
+ * @return UpdateNotificationApp
*/
protected function getAppDetails(string $appId): array {
$app = $this->appManager->getAppInfo($appId, false, $this->language);
+ $name = $app['name'] ?? $appId;
return [
'appId' => $appId,
- 'appName' => $app['name'] ?? $appId,
+ 'appName' => $name,
];
}
+
+ protected function getLanguage(): string {
+ if ($this->language === null) {
+ $this->language = $this->l10nFactory->getUserLanguage($this->userSession->getUser());
+ }
+ return $this->language;
+ }
+
+ /**
+ * Get changelog entry for an app
+ *
+ * @param string $appId App to search changelog entry for
+ * @param string|null $version The version to search the changelog entry for (defaults to the latest installed)
+ *
+ * @return DataResponse<Http::STATUS_OK, array{appName: string, content: string, version: string}, array{}>|DataResponse<Http::STATUS_NOT_FOUND, array{}, array{}>|DataResponse<Http::STATUS_BAD_REQUEST, array{}, array{}>
+ *
+ * 200: Changelog entry returned
+ * 400: The `version` parameter is not a valid version format
+ * 404: No changelog found
+ */
+ public function getAppChangelogEntry(string $appId, ?string $version = null): DataResponse {
+ $version = $version ?? $this->appManager->getAppVersion($appId);
+ // handle pre-release versions
+ $matches = [];
+ $result = preg_match('/^(\d+\.\d+(\.\d+)?)/', $version, $matches);
+ if ($result === false || $result === 0) {
+ return new DataResponse([], Http::STATUS_BAD_REQUEST);
+ }
+ $shortVersion = $matches[0];
+
+ $changes = $this->manager->getChangelog($appId, $shortVersion);
+
+ if ($changes === null) {
+ return new DataResponse([], Http::STATUS_NOT_FOUND);
+ }
+
+ // Remove version headline
+ /** @var string[] */
+ $changes = explode("\n", $changes, 2);
+ $changes = trim(end($changes));
+
+ // Get app info for localized app name
+ $info = $this->appManager->getAppInfo($appId) ?? [];
+ /** @var string */
+ $appName = $info['name'] ?? $appId;
+
+ return new DataResponse([
+ 'appName' => $appName,
+ 'content' => $changes,
+ 'version' => $version,
+ ]);
+ }
}
diff --git a/apps/updatenotification/lib/Controller/AdminController.php b/apps/updatenotification/lib/Controller/AdminController.php
index b13ba66efd5..26745948890 100644
--- a/apps/updatenotification/lib/Controller/AdminController.php
+++ b/apps/updatenotification/lib/Controller/AdminController.php
@@ -3,36 +3,19 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Joas Schilling <coding@schilljs.com>
- * @author Lukas Reschke <lukas@statuscode.ch>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Vincent Petry <vincent@nextcloud.com>
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OCA\UpdateNotification\Controller;
-use OCA\UpdateNotification\ResetTokenBackgroundJob;
+use OCA\UpdateNotification\BackgroundJob\ResetToken;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\IJobList;
+use OCP\IAppConfig;
use OCP\IConfig;
use OCP\IL10N;
use OCP\IRequest;
@@ -40,43 +23,22 @@ use OCP\Security\ISecureRandom;
use OCP\Util;
class AdminController extends Controller {
- /** @var IJobList */
- private $jobList;
- /** @var ISecureRandom */
- private $secureRandom;
- /** @var IConfig */
- private $config;
- /** @var ITimeFactory */
- private $timeFactory;
- /** @var IL10N */
- private $l10n;
- /**
- * @param string $appName
- * @param IRequest $request
- * @param IJobList $jobList
- * @param ISecureRandom $secureRandom
- * @param IConfig $config
- * @param ITimeFactory $timeFactory
- * @param IL10N $l10n
- */
- public function __construct($appName,
- IRequest $request,
- IJobList $jobList,
- ISecureRandom $secureRandom,
- IConfig $config,
- ITimeFactory $timeFactory,
- IL10N $l10n) {
+ public function __construct(
+ string $appName,
+ IRequest $request,
+ private IJobList $jobList,
+ private ISecureRandom $secureRandom,
+ private IConfig $config,
+ private IAppConfig $appConfig,
+ private ITimeFactory $timeFactory,
+ private IL10N $l10n,
+ ) {
parent::__construct($appName, $request);
- $this->jobList = $jobList;
- $this->secureRandom = $secureRandom;
- $this->config = $config;
- $this->timeFactory = $timeFactory;
- $this->l10n = $l10n;
}
- private function isUpdaterEnabled() {
- return !$this->config->getSystemValue('upgrade.disable-web', false);
+ private function isUpdaterEnabled(): bool {
+ return !$this->config->getSystemValueBool('upgrade.disable-web');
}
/**
@@ -85,7 +47,7 @@ class AdminController extends Controller {
*/
public function setChannel(string $channel): DataResponse {
Util::setChannel($channel);
- $this->config->setAppValue('core', 'lastupdatedat', 0);
+ $this->appConfig->setValueInt('core', 'lastupdatedat', 0);
return new DataResponse(['status' => 'success', 'data' => ['message' => $this->l10n->t('Channel updated')]]);
}
@@ -98,8 +60,8 @@ class AdminController extends Controller {
}
// Create a new job and store the creation date
- $this->jobList->add(ResetTokenBackgroundJob::class);
- $this->config->setAppValue('core', 'updater.secret.created', $this->timeFactory->getTime());
+ $this->jobList->add(ResetToken::class);
+ $this->appConfig->setValueInt('core', 'updater.secret.created', $this->timeFactory->getTime());
// Create a new token
$newToken = $this->secureRandom->generate(64);
diff --git a/apps/updatenotification/lib/Controller/ChangelogController.php b/apps/updatenotification/lib/Controller/ChangelogController.php
new file mode 100644
index 00000000000..a274ed3d2b2
--- /dev/null
+++ b/apps/updatenotification/lib/Controller/ChangelogController.php
@@ -0,0 +1,62 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCA\UpdateNotification\Controller;
+
+use OCA\UpdateNotification\Manager;
+use OCP\App\IAppManager;
+use OCP\AppFramework\Controller;
+use OCP\AppFramework\Http\Attribute\NoAdminRequired;
+use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
+use OCP\AppFramework\Http\Attribute\OpenAPI;
+use OCP\AppFramework\Http\TemplateResponse;
+use OCP\AppFramework\Services\IInitialState;
+use OCP\IRequest;
+use OCP\Util;
+
+#[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)]
+class ChangelogController extends Controller {
+
+ public function __construct(
+ string $appName,
+ IRequest $request,
+ private Manager $manager,
+ private IAppManager $appManager,
+ private IInitialState $initialState,
+ ) {
+ parent::__construct($appName, $request);
+ }
+
+ /**
+ * This page is only used for clients not support showing the app changelog feature in-app and thus need to show it on a dedicated page.
+ * @param string $app App to show the changelog for
+ * @param string|null $version Version entry to show (defaults to latest installed)
+ */
+ #[NoAdminRequired]
+ #[NoCSRFRequired]
+ public function showChangelog(string $app, ?string $version = null): TemplateResponse {
+ $version = $version ?? $this->appManager->getAppVersion($app);
+ $appInfo = $this->appManager->getAppInfo($app) ?? [];
+ $appName = $appInfo['name'] ?? $app;
+
+ $changes = $this->manager->getChangelog($app, $version) ?? '';
+ // Remove version headline
+ /** @var string[] */
+ $changes = explode("\n", $changes, 2);
+ $changes = trim(end($changes));
+
+ $this->initialState->provideInitialState('changelog', [
+ 'appName' => $appName,
+ 'appVersion' => $version,
+ 'text' => $changes,
+ ]);
+
+ Util::addScript($this->appName, 'view-changelog-page');
+ return new TemplateResponse($this->appName, 'empty');
+ }
+}
diff --git a/apps/updatenotification/lib/Listener/AppUpdateEventListener.php b/apps/updatenotification/lib/Listener/AppUpdateEventListener.php
new file mode 100644
index 00000000000..49a2506d913
--- /dev/null
+++ b/apps/updatenotification/lib/Listener/AppUpdateEventListener.php
@@ -0,0 +1,61 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\UpdateNotification\Listener;
+
+use OCA\UpdateNotification\AppInfo\Application;
+use OCA\UpdateNotification\BackgroundJob\AppUpdatedNotifications;
+use OCP\App\Events\AppUpdateEvent;
+use OCP\BackgroundJob\IJobList;
+use OCP\EventDispatcher\Event;
+use OCP\EventDispatcher\IEventListener;
+use OCP\IAppConfig;
+use Psr\Log\LoggerInterface;
+
+/** @template-implements IEventListener<AppUpdateEvent> */
+class AppUpdateEventListener implements IEventListener {
+
+ public function __construct(
+ private IJobList $jobList,
+ private LoggerInterface $logger,
+ private IAppConfig $appConfig,
+ ) {
+ }
+
+ /**
+ * @param AppUpdateEvent $event
+ */
+ public function handle(Event $event): void {
+ if (!($event instanceof AppUpdateEvent)) {
+ return;
+ }
+
+ if (!$this->appConfig->getValueBool(Application::APP_NAME, 'app_updated.enabled', true)) {
+ return;
+ }
+
+ foreach ($this->jobList->getJobsIterator(AppUpdatedNotifications::class, null, 0) as $job) {
+ // Remove waiting notification jobs for this app
+ if ($job->getArgument()['appId'] === $event->getAppId()) {
+ $this->jobList->remove($job);
+ }
+ }
+
+ $this->jobList->add(AppUpdatedNotifications::class, [
+ 'appId' => $event->getAppId(),
+ 'timestamp' => time(),
+ ]);
+
+ $this->logger->debug(
+ 'Scheduled app update notification for "' . $event->getAppId() . '"',
+ [
+ 'app' => Application::APP_NAME,
+ ],
+ );
+ }
+}
diff --git a/apps/updatenotification/lib/Listener/BeforeTemplateRenderedEventListener.php b/apps/updatenotification/lib/Listener/BeforeTemplateRenderedEventListener.php
new file mode 100644
index 00000000000..974734a76f4
--- /dev/null
+++ b/apps/updatenotification/lib/Listener/BeforeTemplateRenderedEventListener.php
@@ -0,0 +1,54 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\UpdateNotification\Listener;
+
+use OCA\UpdateNotification\AppInfo\Application;
+use OCP\App\IAppManager;
+use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent;
+use OCP\EventDispatcher\Event;
+use OCP\EventDispatcher\IEventListener;
+use OCP\IAppConfig;
+use OCP\Util;
+use Psr\Log\LoggerInterface;
+
+/** @template-implements IEventListener<BeforeTemplateRenderedEvent> */
+class BeforeTemplateRenderedEventListener implements IEventListener {
+
+ public function __construct(
+ private IAppManager $appManager,
+ private LoggerInterface $logger,
+ private IAppConfig $appConfig,
+ ) {
+ }
+
+ /**
+ * @param BeforeTemplateRenderedEvent $event
+ */
+ public function handle(Event $event): void {
+ if (!($event instanceof BeforeTemplateRenderedEvent)) {
+ return;
+ }
+
+ if (!$this->appConfig->getValueBool(Application::APP_NAME, 'app_updated.enabled', true)) {
+ return;
+ }
+
+ // Only handle logged in users
+ if (!$event->isLoggedIn()) {
+ return;
+ }
+
+ // Ignore when notifications are disabled
+ if (!$this->appManager->isEnabledForUser('notifications')) {
+ return;
+ }
+
+ Util::addInitScript(Application::APP_NAME, 'init');
+ }
+}
diff --git a/apps/updatenotification/lib/Manager.php b/apps/updatenotification/lib/Manager.php
new file mode 100644
index 00000000000..ebc1c83a9b4
--- /dev/null
+++ b/apps/updatenotification/lib/Manager.php
@@ -0,0 +1,114 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\UpdateNotification;
+
+use OCP\App\IAppManager;
+use OCP\IUser;
+use OCP\IUserSession;
+use OCP\L10N\IFactory;
+use Psr\Log\LoggerInterface;
+
+class Manager {
+
+ private ?IUser $currentUser;
+
+ public function __construct(
+ IUserSession $currentSession,
+ private IAppManager $appManager,
+ private IFactory $l10NFactory,
+ private LoggerInterface $logger,
+ ) {
+ $this->currentUser = $currentSession->getUser();
+ }
+
+ /**
+ * Get the changelog entry for the given appId
+ * @param string $appId The app for which to query the entry
+ * @param string $version The version for which to query the changelog entry
+ * @param ?string $languageCode The language in which to query the changelog (defaults to current user language and fallsback to English)
+ * @return string|null Either the changelog entry or null if no changelog is found
+ */
+ public function getChangelog(string $appId, string $version, ?string $languageCode = null): ?string {
+ if ($languageCode === null) {
+ $languageCode = $this->l10NFactory->getUserLanguage($this->currentUser);
+ }
+
+ $path = $this->getChangelogFile($appId, $languageCode);
+ if ($path === null) {
+ $this->logger->debug('No changelog file found for app ' . $appId . ' and language code ' . $languageCode);
+ return null;
+ }
+
+ $changes = $this->retrieveChangelogEntry($path, $version);
+ return $changes;
+ }
+
+ /**
+ * Get the changelog file in the requested language or fallback to English
+ * @param string $appId The app to load the changelog for
+ * @param string $languageCode The language code to search
+ * @return string|null Either the file path or null if not found
+ */
+ public function getChangelogFile(string $appId, string $languageCode): ?string {
+ try {
+ $appPath = $this->appManager->getAppPath($appId);
+ $files = ["CHANGELOG.$languageCode.md", 'CHANGELOG.en.md'];
+ foreach ($files as $file) {
+ $path = $appPath . '/' . $file;
+ if (is_file($path)) {
+ return $path;
+ }
+ }
+ } catch (\Throwable $e) {
+ // ignore and return null below
+ }
+ return null;
+ }
+
+ /**
+ * Retrieve a log entry from the changelog
+ * @param string $path The path to the changelog file
+ * @param string $version The version to query (make sure to only pass in "{major}.{minor}(.{patch}" format)
+ */
+ protected function retrieveChangelogEntry(string $path, string $version): ?string {
+ $matches = [];
+ $content = file_get_contents($path);
+ if ($content === false) {
+ $this->logger->debug('Could not open changelog file', ['file-path' => $path]);
+ return null;
+ }
+
+ $result = preg_match_all('/^## (?:\[)?(?:v)?(\d+\.\d+(\.\d+)?)/m', $content, $matches, PREG_OFFSET_CAPTURE);
+ if ($result === false || $result === 0) {
+ $this->logger->debug('No entries in changelog found', ['file_path' => $path]);
+ return null;
+ }
+
+ // Get the key of the match that equals the requested version
+ $index = array_key_first(
+ // Get the array containing the match that equals the requested version, keys are preserved so: [1 => '1.2.4']
+ array_filter(
+ // This is the array of the versions found, like ['1.2.3', '1.2.4']
+ $matches[1],
+ // Callback to filter only version that matches the requested version
+ fn (array $match) => version_compare($match[0], $version, '=='),
+ )
+ );
+
+ if ($index === null) {
+ $this->logger->debug('No changelog entry for version ' . $version . ' found', ['file_path' => $path]);
+ return null;
+ }
+
+ $offsetChangelogEntry = $matches[0][$index][1];
+ // Length of the changelog entry (offset of next match - own offset) or null if the whole rest should be considered
+ $lengthChangelogEntry = $index < ($result - 1) ? ($matches[0][$index + 1][1] - $offsetChangelogEntry) : null;
+ return substr($content, $offsetChangelogEntry, $lengthChangelogEntry);
+ }
+}
diff --git a/apps/updatenotification/lib/Notification/AppUpdateNotifier.php b/apps/updatenotification/lib/Notification/AppUpdateNotifier.php
new file mode 100644
index 00000000000..353ca883aba
--- /dev/null
+++ b/apps/updatenotification/lib/Notification/AppUpdateNotifier.php
@@ -0,0 +1,104 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\UpdateNotification\Notification;
+
+use OCA\UpdateNotification\AppInfo\Application;
+use OCP\App\IAppManager;
+use OCP\IURLGenerator;
+use OCP\IUserManager;
+use OCP\L10N\IFactory;
+use OCP\Notification\AlreadyProcessedException;
+use OCP\Notification\IAction;
+use OCP\Notification\IManager as INotificationManager;
+use OCP\Notification\INotification;
+use OCP\Notification\INotifier;
+use OCP\Notification\UnknownNotificationException;
+use Psr\Log\LoggerInterface;
+
+class AppUpdateNotifier implements INotifier {
+
+ public function __construct(
+ private IFactory $l10nFactory,
+ private INotificationManager $notificationManager,
+ private IUserManager $userManager,
+ private IURLGenerator $urlGenerator,
+ private IAppManager $appManager,
+ private LoggerInterface $logger,
+ ) {
+ }
+
+ public function getID(): string {
+ return 'updatenotification_app_updated';
+ }
+
+ /**
+ * Human readable name describing the notifier
+ */
+ public function getName(): string {
+ return $this->l10nFactory->get(Application::APP_NAME)->t('App updated');
+ }
+
+ /**
+ * @param INotification $notification
+ * @param string $languageCode The code of the language that should be used to prepare the notification
+ * @return INotification
+ * @throws UnknownNotificationException When the notification was not prepared by a notifier
+ * @throws AlreadyProcessedException When the app is no longer known
+ */
+ public function prepare(INotification $notification, string $languageCode): INotification {
+ if ($notification->getApp() !== Application::APP_NAME) {
+ throw new UnknownNotificationException('Unknown app');
+ }
+
+ if ($notification->getSubject() !== 'app_updated') {
+ throw new UnknownNotificationException('Unknown subject');
+ }
+
+ $appId = $notification->getSubjectParameters()[0];
+ $appInfo = $this->appManager->getAppInfo($appId, lang:$languageCode);
+ if ($appInfo === null) {
+ throw new AlreadyProcessedException();
+ }
+
+ // Prepare translation factory for requested language
+ $l = $this->l10nFactory->get(Application::APP_NAME, $languageCode);
+
+ $icon = $this->appManager->getAppIcon($appId, true);
+ if ($icon === null) {
+ $icon = $this->urlGenerator->imagePath('core', 'actions/change.svg');
+ }
+
+ $action = $notification->createAction();
+ $action
+ ->setLabel($l->t('See what\'s new'))
+ ->setParsedLabel($l->t('See what\'s new'))
+ ->setLink($this->urlGenerator->linkToRouteAbsolute('updatenotification.Changelog.showChangelog', ['app' => $appId, 'version' => $this->appManager->getAppVersion($appId)]), IAction::TYPE_WEB);
+
+ $notification
+ ->setIcon($this->urlGenerator->getAbsoluteURL($icon))
+ ->addParsedAction($action)
+ ->setRichSubject(
+ $l->t('{app} updated to version {version}'),
+ [
+ 'app' => [
+ 'type' => 'app',
+ 'id' => $appId,
+ 'name' => $appInfo['name'],
+ ],
+ 'version' => [
+ 'type' => 'highlight',
+ 'id' => $appId,
+ 'name' => $appInfo['version'],
+ ],
+ ],
+ );
+
+ return $notification;
+ }
+}
diff --git a/apps/updatenotification/lib/Notification/Notifier.php b/apps/updatenotification/lib/Notification/Notifier.php
index bfbcc203480..787675bd98d 100644
--- a/apps/updatenotification/lib/Notification/Notifier.php
+++ b/apps/updatenotification/lib/Notification/Notifier.php
@@ -3,30 +3,14 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Joas Schilling <coding@schilljs.com>
- * @author Julius Härtl <jus@bitgrid.net>
- * @author Roeland Jago Douma <roeland@famdouma.nl>
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OCA\UpdateNotification\Notification;
-use OCP\IConfig;
+use OCA\UpdateNotification\AppInfo\Application;
+use OCP\App\IAppManager;
+use OCP\AppFramework\Services\IAppConfig;
use OCP\IGroupManager;
use OCP\IURLGenerator;
use OCP\IUser;
@@ -36,49 +20,27 @@ use OCP\Notification\AlreadyProcessedException;
use OCP\Notification\IManager;
use OCP\Notification\INotification;
use OCP\Notification\INotifier;
-use OCP\Util;
+use OCP\Notification\UnknownNotificationException;
+use OCP\ServerVersion;
class Notifier implements INotifier {
-
- /** @var IURLGenerator */
- protected $url;
-
- /** @var IConfig */
- protected $config;
-
- /** @var IManager */
- protected $notificationManager;
-
- /** @var IFactory */
- protected $l10NFactory;
-
- /** @var IUserSession */
- protected $userSession;
-
- /** @var IGroupManager */
- protected $groupManager;
-
/** @var string[] */
protected $appVersions;
/**
* Notifier constructor.
- *
- * @param IURLGenerator $url
- * @param IConfig $config
- * @param IManager $notificationManager
- * @param IFactory $l10NFactory
- * @param IUserSession $userSession
- * @param IGroupManager $groupManager
*/
- public function __construct(IURLGenerator $url, IConfig $config, IManager $notificationManager, IFactory $l10NFactory, IUserSession $userSession, IGroupManager $groupManager) {
- $this->url = $url;
- $this->notificationManager = $notificationManager;
- $this->config = $config;
- $this->l10NFactory = $l10NFactory;
- $this->userSession = $userSession;
- $this->groupManager = $groupManager;
- $this->appVersions = $this->getAppVersions();
+ public function __construct(
+ protected IURLGenerator $url,
+ protected IAppConfig $appConfig,
+ protected IManager $notificationManager,
+ protected IFactory $l10NFactory,
+ protected IUserSession $userSession,
+ protected IGroupManager $groupManager,
+ protected IAppManager $appManager,
+ protected ServerVersion $serverVersion,
+ ) {
+ $this->appVersions = $this->appManager->getAppInstalledVersions();
}
/**
@@ -88,7 +50,7 @@ class Notifier implements INotifier {
* @since 17.0.0
*/
public function getID(): string {
- return 'updatenotification';
+ return Application::APP_NAME;
}
/**
@@ -98,51 +60,60 @@ class Notifier implements INotifier {
* @since 17.0.0
*/
public function getName(): string {
- return $this->l10NFactory->get('updatenotification')->t('Update notifications');
+ return $this->l10NFactory->get(Application::APP_NAME)->t('Update notifications');
}
/**
* @param INotification $notification
* @param string $languageCode The code of the language that should be used to prepare the notification
* @return INotification
- * @throws \InvalidArgumentException When the notification was not prepared by a notifier
+ * @throws UnknownNotificationException When the notification was not prepared by a notifier
* @throws AlreadyProcessedException When the notification is not needed anymore and should be deleted
* @since 9.0.0
*/
public function prepare(INotification $notification, string $languageCode): INotification {
- if ($notification->getApp() !== 'updatenotification') {
- throw new \InvalidArgumentException('Unknown app id');
+ if ($notification->getApp() !== Application::APP_NAME) {
+ throw new UnknownNotificationException('Unknown app id');
}
- $l = $this->l10NFactory->get('updatenotification', $languageCode);
+ if ($notification->getSubject() !== 'update_available' && $notification->getSubject() !== 'connection_error') {
+ throw new UnknownNotificationException('Unknown subject');
+ }
+
+ $l = $this->l10NFactory->get(Application::APP_NAME, $languageCode);
if ($notification->getSubject() === 'connection_error') {
- $errors = (int) $this->config->getAppValue('updatenotification', 'update_check_errors', 0);
+ $errors = $this->appConfig->getAppValueInt('update_check_errors', 0);
if ($errors === 0) {
- $this->notificationManager->markProcessed($notification);
- throw new \InvalidArgumentException('Update checked worked again');
+ throw new AlreadyProcessedException();
}
$notification->setParsedSubject($l->t('The update server could not be reached since %d days to check for new updates.', [$errors]))
->setParsedMessage($l->t('Please check the Nextcloud and server log files for errors.'));
- } elseif ($notification->getObjectType() === 'core') {
- $this->updateAlreadyInstalledCheck($notification, $this->getCoreVersions());
+ } else {
+ if ($notification->getObjectType() === 'core') {
+ $this->updateAlreadyInstalledCheck($notification, $this->getCoreVersions());
- $parameters = $notification->getSubjectParameters();
- $notification->setParsedSubject($l->t('Update to %1$s is available.', [$parameters['version']]));
+ $parameters = $notification->getSubjectParameters();
+ $notification->setRichSubject($l->t('Update to {serverAndVersion} is available.'), [
+ 'serverAndVersion' => [
+ 'type' => 'highlight',
+ 'id' => $notification->getObjectType(),
+ 'name' => $parameters['version'],
+ ]
+ ]);
- if ($this->isAdmin()) {
- $notification->setLink($this->url->linkToRouteAbsolute('settings.AdminSettings.index', ['section' => 'overview']) . '#version');
- }
- } else {
- $appInfo = $this->getAppInfo($notification->getObjectType(), $languageCode);
- $appName = ($appInfo === null) ? $notification->getObjectType() : $appInfo['name'];
+ if ($this->isAdmin()) {
+ $notification->setLink($this->url->linkToRouteAbsolute('settings.AdminSettings.index', ['section' => 'overview']) . '#version');
+ }
+ } else {
+ $appInfo = $this->getAppInfo($notification->getObjectType(), $languageCode);
+ $appName = ($appInfo === null) ? $notification->getObjectType() : $appInfo['name'];
- if (isset($this->appVersions[$notification->getObjectType()])) {
- $this->updateAlreadyInstalledCheck($notification, $this->appVersions[$notification->getObjectType()]);
- }
+ if (isset($this->appVersions[$notification->getObjectType()])) {
+ $this->updateAlreadyInstalledCheck($notification, $this->appVersions[$notification->getObjectType()]);
+ }
- $notification->setParsedSubject($l->t('Update for %1$s to version %2$s is available.', [$appName, $notification->getObjectId()]))
- ->setRichSubject($l->t('Update for {app} to version %s is available.', [$notification->getObjectId()]), [
+ $notification->setRichSubject($l->t('Update for {app} to version %s is available.', [$notification->getObjectId()]), [
'app' => [
'type' => 'app',
'id' => $notification->getObjectType(),
@@ -150,12 +121,13 @@ class Notifier implements INotifier {
]
]);
- if ($this->isAdmin()) {
- $notification->setLink($this->url->linkToRouteAbsolute('settings.AppSettings.viewApps', ['category' => 'updates']) . '#app-' . $notification->getObjectType());
+ if ($this->isAdmin()) {
+ $notification->setLink($this->url->linkToRouteAbsolute('settings.AppSettings.viewApps', ['category' => 'updates']) . '#app-' . $notification->getObjectType());
+ }
}
}
- $notification->setIcon($this->url->getAbsoluteURL($this->url->imagePath('updatenotification', 'notification.svg')));
+ $notification->setIcon($this->url->getAbsoluteURL($this->url->imagePath(Application::APP_NAME, 'notification.svg')));
return $notification;
}
@@ -167,15 +139,12 @@ class Notifier implements INotifier {
* @param string $installedVersion
* @throws AlreadyProcessedException When the update is already installed
*/
- protected function updateAlreadyInstalledCheck(INotification $notification, $installedVersion) {
+ protected function updateAlreadyInstalledCheck(INotification $notification, $installedVersion): void {
if (version_compare($notification->getObjectId(), $installedVersion, '<=')) {
throw new AlreadyProcessedException();
}
}
- /**
- * @return bool
- */
protected function isAdmin(): bool {
$user = $this->userSession->getUser();
@@ -187,14 +156,10 @@ class Notifier implements INotifier {
}
protected function getCoreVersions(): string {
- return implode('.', Util::getVersion());
- }
-
- protected function getAppVersions(): array {
- return \OC_App::getAppVersions();
+ return implode('.', $this->serverVersion->getVersion());
}
- protected function getAppInfo($appId, $languageCode) {
- return \OC_App::getAppInfo($appId, false, $languageCode);
+ protected function getAppInfo(string $appId, ?string $languageCode): ?array {
+ return $this->appManager->getAppInfo($appId, false, $languageCode);
}
}
diff --git a/apps/updatenotification/lib/ResetTokenBackgroundJob.php b/apps/updatenotification/lib/ResetTokenBackgroundJob.php
deleted file mode 100644
index 96a50c5ff7f..00000000000
--- a/apps/updatenotification/lib/ResetTokenBackgroundJob.php
+++ /dev/null
@@ -1,66 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Lukas Reschke <lukas@statuscode.ch>
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
- */
-namespace OCA\UpdateNotification;
-
-use OC\BackgroundJob\TimedJob;
-use OCP\AppFramework\Utility\ITimeFactory;
-use OCP\IConfig;
-
-/**
- * Class ResetTokenBackgroundJob deletes any configured token all 24 hours for
- *
- *
- * @package OCA\UpdateNotification
- */
-class ResetTokenBackgroundJob extends TimedJob {
- /** @var IConfig */
- private $config;
- /** @var ITimeFactory */
- private $timeFactory;
-
- /**
- * @param IConfig $config
- * @param ITimeFactory $timeFactory
- */
- public function __construct(IConfig $config,
- ITimeFactory $timeFactory) {
- // Run all 10 minutes
- $this->setInterval(60 * 10);
- $this->config = $config;
- $this->timeFactory = $timeFactory;
- }
-
- /**
- * @param $argument
- */
- protected function run($argument) {
- // Delete old tokens after 2 days
- if ($this->timeFactory->getTime() - $this->config->getAppValue('core', 'updater.secret.created', $this->timeFactory->getTime()) >= 172800) {
- $this->config->deleteSystemValue('updater.secret');
- }
- }
-}
diff --git a/apps/updatenotification/lib/ResponseDefinitions.php b/apps/updatenotification/lib/ResponseDefinitions.php
new file mode 100644
index 00000000000..754787ea2a7
--- /dev/null
+++ b/apps/updatenotification/lib/ResponseDefinitions.php
@@ -0,0 +1,19 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\UpdateNotification;
+
+/**
+ * @psalm-type UpdateNotificationApp = array{
+ * appId: string,
+ * appName: string,
+ * }
+ */
+class ResponseDefinitions {
+}
diff --git a/apps/updatenotification/lib/Settings/Admin.php b/apps/updatenotification/lib/Settings/Admin.php
index b8062efd81f..22228f1bccc 100644
--- a/apps/updatenotification/lib/Settings/Admin.php
+++ b/apps/updatenotification/lib/Settings/Admin.php
@@ -3,89 +3,44 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Julius Härtl <jus@bitgrid.net>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Vincent Petry <vincent@nextcloud.com>
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OCA\UpdateNotification\Settings;
-use OC\User\Backend;
-use OCP\User\Backend\ICountUsersBackend;
+use OCA\UpdateNotification\AppInfo\Application;
use OCA\UpdateNotification\UpdateChecker;
use OCP\AppFramework\Http\TemplateResponse;
+use OCP\AppFramework\Services\IInitialState;
+use OCP\IAppConfig;
use OCP\IConfig;
use OCP\IDateTimeFormatter;
use OCP\IGroupManager;
+use OCP\IUserManager;
use OCP\L10N\IFactory;
+use OCP\ServerVersion;
use OCP\Settings\ISettings;
use OCP\Support\Subscription\IRegistry;
-use OCP\Util;
-use OCP\IUserManager;
use Psr\Log\LoggerInterface;
class Admin implements ISettings {
- /** @var IConfig */
- private $config;
- /** @var UpdateChecker */
- private $updateChecker;
- /** @var IGroupManager */
- private $groupManager;
- /** @var IDateTimeFormatter */
- private $dateTimeFormatter;
- /** @var IFactory */
- private $l10nFactory;
- /** @var IRegistry */
- private $subscriptionRegistry;
- /** @var IUserManager */
- private $userManager;
- /** @var LoggerInterface */
- private $logger;
-
public function __construct(
- IConfig $config,
- UpdateChecker $updateChecker,
- IGroupManager $groupManager,
- IDateTimeFormatter $dateTimeFormatter,
- IFactory $l10nFactory,
- IRegistry $subscriptionRegistry,
- IUserManager $userManager,
- LoggerInterface $logger
+ private IConfig $config,
+ private IAppConfig $appConfig,
+ private UpdateChecker $updateChecker,
+ private IGroupManager $groupManager,
+ private IDateTimeFormatter $dateTimeFormatter,
+ private IFactory $l10nFactory,
+ private IRegistry $subscriptionRegistry,
+ private IUserManager $userManager,
+ private LoggerInterface $logger,
+ private IInitialState $initialState,
+ private ServerVersion $serverVersion,
) {
- $this->config = $config;
- $this->updateChecker = $updateChecker;
- $this->groupManager = $groupManager;
- $this->dateTimeFormatter = $dateTimeFormatter;
- $this->l10nFactory = $l10nFactory;
- $this->subscriptionRegistry = $subscriptionRegistry;
- $this->userManager = $userManager;
- $this->logger = $logger;
}
- /**
- * @return TemplateResponse
- */
public function getForm(): TemplateResponse {
- $lastUpdateCheckTimestamp = $this->config->getAppValue('core', 'lastupdatedat');
+ $lastUpdateCheckTimestamp = $this->appConfig->getValueInt('core', 'lastupdatedat');
$lastUpdateCheck = $this->dateTimeFormatter->formatDateTime($lastUpdateCheckTimestamp);
$channels = [
@@ -94,14 +49,14 @@ class Admin implements ISettings {
'stable',
'production',
];
- $currentChannel = Util::getChannel();
+ $currentChannel = $this->serverVersion->getChannel();
if ($currentChannel === 'git') {
$channels[] = 'git';
}
$updateState = $this->updateChecker->getUpdateState();
- $notifyGroups = json_decode($this->config->getAppValue('updatenotification', 'notify_groups', '["admin"]'), true);
+ $notifyGroups = $this->appConfig->getValueArray(Application::APP_NAME, 'notify_groups', ['admin']);
$defaultUpdateServerURL = 'https://updates.nextcloud.com/updater_server/';
$updateServerURL = $this->config->getSystemValue('updater.server.url', $defaultUpdateServerURL);
@@ -131,12 +86,9 @@ class Admin implements ISettings {
'notifyGroups' => $this->getSelectedGroups($notifyGroups),
'hasValidSubscription' => $hasValidSubscription,
];
+ $this->initialState->provideInitialState('data', $params);
- $params = [
- 'json' => json_encode($params),
- ];
-
- return new TemplateResponse('updatenotification', 'admin', $params, '');
+ return new TemplateResponse('updatenotification', 'admin', [], '');
}
protected function filterChanges(array $changes): array {
@@ -162,8 +114,8 @@ class Admin implements ISettings {
}
/**
- * @param array $groupIds
- * @return array
+ * @param string[] $groupIds
+ * @return list<array{id: string, displayname: string}>
*/
protected function getSelectedGroups(array $groupIds): array {
$result = [];
@@ -174,51 +126,26 @@ class Admin implements ISettings {
continue;
}
- $result[] = ['value' => $group->getGID(), 'label' => $group->getDisplayName()];
+ $result[] = ['id' => $group->getGID(), 'displayname' => $group->getDisplayName()];
}
return $result;
}
- /**
- * @return string the section ID, e.g. 'sharing'
- */
- public function getSection(): string {
+ public function getSection(): ?string {
+ if (!$this->config->getSystemValueBool('updatechecker', true)) {
+ // update checker is disabled so we do not show the section at all
+ return null;
+ }
+
return 'overview';
}
- /**
- * @return int whether the form should be rather on the top or bottom of
- * the admin section. The forms are arranged in ascending order of the
- * priority values. It is required to return a value between 0 and 100.
- *
- * E.g.: 70
- */
public function getPriority(): int {
return 11;
}
private function isWebUpdaterRecommended(): bool {
- return $this->getUserCount() < 100;
- }
-
- /**
- * @see https://github.com/nextcloud/server/blob/39494fbf794d982f6f6551c984e6ca4c4e947d01/lib/private/Support/Subscription/Registry.php#L188-L216 implementation reference
- */
- private function getUserCount(): int {
- $userCount = 0;
- $backends = $this->userManager->getBackends();
- foreach ($backends as $backend) {
- // TODO: change below to 'if ($backend instanceof ICountUsersBackend) {'
- if ($backend->implementsActions(Backend::COUNT_USERS)) {
- /** @var ICountUsersBackend $backend */
- $backendUsers = $backend->countUsers();
- if ($backendUsers !== false) {
- $userCount += $backendUsers;
- }
- }
- }
-
- return $userCount;
+ return (int)$this->userManager->countUsersTotal(100) < 100;
}
}
diff --git a/apps/updatenotification/lib/UpdateChecker.php b/apps/updatenotification/lib/UpdateChecker.php
index 40bf784e605..b206ba4a3e4 100644
--- a/apps/updatenotification/lib/UpdateChecker.php
+++ b/apps/updatenotification/lib/UpdateChecker.php
@@ -3,46 +3,23 @@
declare(strict_types=1);
/**
- * @copyright Copyright (c) 2016, ownCloud, Inc.
- *
- * @author Arthur Schiwon <blizzz@arthur-schiwon.de>
- * @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @author Joas Schilling <coding@schilljs.com>
- * @author Lukas Reschke <lukas@statuscode.ch>
- * @author Thomas Müller <thomas.mueller@tmit.eu>
- *
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OCA\UpdateNotification;
use OC\Updater\ChangesCheck;
use OC\Updater\VersionCheck;
+use OCP\AppFramework\Services\IInitialState;
class UpdateChecker {
- /** @var VersionCheck */
- private $updater;
- /** @var ChangesCheck */
- private $changesCheck;
- /**
- * @param VersionCheck $updater
- */
- public function __construct(VersionCheck $updater, ChangesCheck $changesCheck) {
- $this->updater = $updater;
- $this->changesCheck = $changesCheck;
+ public function __construct(
+ private VersionCheck $updater,
+ private ChangesCheck $changesCheck,
+ private IInitialState $initialState,
+ ) {
}
/**
@@ -79,13 +56,17 @@ class UpdateChecker {
}
/**
- * @param array $data
+ * Provide update information as initial state
*/
- public function populateJavaScriptVariables(array $data) {
- $data['array']['oc_updateState'] = json_encode([
- 'updateAvailable' => true,
- 'updateVersion' => $this->getUpdateState()['updateVersionString'],
- 'updateLink' => $this->getUpdateState()['updateLink'] ?? '',
+ public function setInitialState(): void {
+ $updateState = $this->getUpdateState();
+ if (empty($updateState)) {
+ return;
+ }
+
+ $this->initialState->provideInitialState('updateState', [
+ 'updateVersion' => $updateState['updateVersionString'],
+ 'updateLink' => $updateState['updateLink'] ?? '',
]);
}
}