summaryrefslogtreecommitdiffstats
path: root/apps
diff options
context:
space:
mode:
authorVincent Petry <pvince81@owncloud.com>2016-05-25 09:13:10 +0200
committerVincent Petry <pvince81@owncloud.com>2016-05-25 09:13:10 +0200
commitc36cf30ade2107342710c31ce963a7b9a224c902 (patch)
tree5fc58fc8e4f7e29949f99a09b53544f99d2805a5 /apps
parent768a057aab5f4ae9bd876cb1a9c951c48048d8f5 (diff)
parenta1e872aad6c2483e7349609690f9172192f4559a (diff)
downloadnextcloud-server-c36cf30ade2107342710c31ce963a7b9a224c902.tar.gz
nextcloud-server-c36cf30ade2107342710c31ce963a7b9a224c902.zip
Merge pull request #24444 from owncloud/update-notifications-for-core-and-apps
Update notifications for core and apps
Diffstat (limited to 'apps')
-rw-r--r--apps/updatenotification/appinfo/app.php14
-rw-r--r--apps/updatenotification/appinfo/info.xml6
-rw-r--r--apps/updatenotification/js/admin.js16
-rw-r--r--apps/updatenotification/lib/Controller/AdminController.php5
-rw-r--r--apps/updatenotification/lib/Notification/BackgroundJob.php213
-rw-r--r--apps/updatenotification/lib/Notification/Notifier.php108
-rw-r--r--apps/updatenotification/templates/admin.php11
-rw-r--r--apps/updatenotification/tests/Notification/BackgroundJobTest.php430
-rw-r--r--apps/updatenotification/tests/Notification/NotifierTest.php105
9 files changed, 907 insertions, 1 deletions
diff --git a/apps/updatenotification/appinfo/app.php b/apps/updatenotification/appinfo/app.php
index a88861c0942..e9780757cc4 100644
--- a/apps/updatenotification/appinfo/app.php
+++ b/apps/updatenotification/appinfo/app.php
@@ -38,4 +38,18 @@ if(\OC::$server->getConfig()->getSystemValue('updatechecker', true) === true) {
\OC_App::registerAdmin('updatenotification', 'admin');
}
}
+
+ $manager = \OC::$server->getNotificationManager();
+ $manager->registerNotifier(function() use ($manager) {
+ return new \OCA\UpdateNotification\Notification\Notifier(
+ $manager,
+ \OC::$server->getL10NFactory()
+ );
+ }, function() {
+ $l = \OC::$server->getL10N('updatenotification');
+ return [
+ 'id' => 'updatenotification',
+ 'name' => $l->t('Update notifications'),
+ ];
+ });
}
diff --git a/apps/updatenotification/appinfo/info.xml b/apps/updatenotification/appinfo/info.xml
index 5d7a647fd03..3ea2d1a13de 100644
--- a/apps/updatenotification/appinfo/info.xml
+++ b/apps/updatenotification/appinfo/info.xml
@@ -5,10 +5,14 @@
<description>Displays update notifications for ownCloud and provides the SSO for the updater.</description>
<licence>AGPL</licence>
<author>Lukas Reschke</author>
- <version>0.2.0</version>
+ <version>0.2.1</version>
<namespace>UpdateNotification</namespace>
<default_enable/>
<dependencies>
<owncloud min-version="9.1" max-version="9.1" />
</dependencies>
+
+ <background-jobs>
+ <job>OCA\UpdateNotification\Notification\BackgroundJob</job>
+ </background-jobs>
</info>
diff --git a/apps/updatenotification/js/admin.js b/apps/updatenotification/js/admin.js
index 3bc5dd21527..3ca45a191d4 100644
--- a/apps/updatenotification/js/admin.js
+++ b/apps/updatenotification/js/admin.js
@@ -39,8 +39,16 @@ $(document).ready(function(){
});
});
});
+
$('#release-channel').change(function() {
var newChannel = $('#release-channel').find(":selected").val();
+
+ if (newChannel === 'git' || newChannel === 'daily') {
+ $('#oca_updatenotification_groups em').removeClass('hidden');
+ } else {
+ $('#oca_updatenotification_groups em').addClass('hidden');
+ }
+
$.post(
OC.generateUrl('/apps/updatenotification/channel'),
{
@@ -51,4 +59,12 @@ $(document).ready(function(){
}
);
});
+
+ var $notificationTargetGroups = $('#oca_updatenotification_groups_list');
+ OC.Settings.setupGroupsSelect($notificationTargetGroups);
+ $notificationTargetGroups.change(function(ev) {
+ var groups = ev.val || [];
+ groups = JSON.stringify(groups);
+ OC.AppConfig.setValue('updatenotification', 'notify_groups', groups);
+ });
});
diff --git a/apps/updatenotification/lib/Controller/AdminController.php b/apps/updatenotification/lib/Controller/AdminController.php
index 5dbcc685809..2fc12140b20 100644
--- a/apps/updatenotification/lib/Controller/AdminController.php
+++ b/apps/updatenotification/lib/Controller/AdminController.php
@@ -100,12 +100,17 @@ class AdminController extends Controller {
unset($channels[$key]);
}
$updateState = $this->updateChecker->getUpdateState();
+
+ $notifyGroups = json_decode($this->config->getAppValue('updatenotification', 'notify_groups', '["admin"]'), true);
+
$params = [
'isNewVersionAvailable' => ($updateState === []) ? false : true,
'lastChecked' => $lastUpdateCheck,
'currentChannel' => $currentChannel,
'channels' => $channels,
'newVersionString' => ($updateState === []) ? '' : $updateState['updateVersion'],
+
+ 'notify_groups' => implode('|', $notifyGroups),
];
return new TemplateResponse($this->appName, 'admin', $params, '');
diff --git a/apps/updatenotification/lib/Notification/BackgroundJob.php b/apps/updatenotification/lib/Notification/BackgroundJob.php
new file mode 100644
index 00000000000..e455a1642f4
--- /dev/null
+++ b/apps/updatenotification/lib/Notification/BackgroundJob.php
@@ -0,0 +1,213 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @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\Notification;
+
+
+use OC\BackgroundJob\TimedJob;
+use OC\Installer;
+use OC\Updater\VersionCheck;
+use OCP\App\IAppManager;
+use OCP\Http\Client\IClientService;
+use OCP\IConfig;
+use OCP\IGroup;
+use OCP\IGroupManager;
+use OCP\IURLGenerator;
+use OCP\IUser;
+use OCP\Notification\IManager;
+
+class BackgroundJob extends TimedJob {
+
+ /** @var IConfig */
+ protected $config;
+
+ /** @var IManager */
+ protected $notificationManager;
+
+ /** @var IGroupManager */
+ protected $groupManager;
+
+ /** @var IAppManager */
+ protected $appManager;
+
+ /** @var IClientService */
+ protected $client;
+
+ /** @var IURLGenerator */
+ protected $urlGenerator;
+
+ /** @var IUser[] */
+ protected $users;
+
+ /**
+ * NotificationBackgroundJob constructor.
+ *
+ * @param IConfig $config
+ * @param IManager $notificationManager
+ * @param IGroupManager $groupManager
+ * @param IAppManager $appManager
+ * @param IClientService $client
+ * @param IURLGenerator $urlGenerator
+ */
+ public function __construct(IConfig $config, IManager $notificationManager, IGroupManager $groupManager, IAppManager $appManager, IClientService $client, IURLGenerator $urlGenerator) {
+ // 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->urlGenerator = $urlGenerator;
+ }
+
+ protected function run($argument) {
+ $this->checkCoreUpdate();
+ $this->checkAppUpdates();
+ }
+
+ /**
+ * Check for ownCloud update
+ */
+ protected function checkCoreUpdate() {
+ if (in_array($this->getChannel(), ['daily', 'git'])) {
+ // "These aren't the update channels you're looking for." - Ben Obi-Wan Kenobi
+ return;
+ }
+
+ $updater = $this->createVersionCheck();
+
+ $status = $updater->check();
+ if (isset($status['version'])) {
+ $url = $this->urlGenerator->linkToRouteAbsolute('settings_admin') . '#updater';
+ $this->createNotifications('core', $status['version'], $url);
+ }
+ }
+
+ /**
+ * Check all installed apps for updates
+ */
+ protected function checkAppUpdates() {
+ $apps = $this->appManager->getInstalledApps();
+ foreach ($apps as $app) {
+ $update = $this->isUpdateAvailable($app);
+ if ($update !== false) {
+ $url = $this->urlGenerator->linkToRouteAbsolute('settings.AppSettings.viewApps') . '#app-' . $app;
+ $this->createNotifications($app, $update, $url);
+ }
+ }
+ }
+
+ /**
+ * Create notifications for this app version
+ *
+ * @param string $app
+ * @param string $version
+ * @param string $url
+ */
+ protected function createNotifications($app, $version, $url) {
+ $lastNotification = $this->config->getAppValue('updatenotification', $app, false);
+ if ($lastNotification === $version) {
+ // We already notified about this update
+ return;
+ } else if ($lastNotification !== false) {
+ // Delete old updates
+ $this->deleteOutdatedNotifications($app, $lastNotification);
+ }
+
+
+ $notification = $this->notificationManager->createNotification();
+ $notification->setApp('updatenotification')
+ ->setDateTime(new \DateTime())
+ ->setObject($app, $version)
+ ->setSubject('update_available')
+ ->setLink($url);
+
+ foreach ($this->getUsersToNotify() as $uid) {
+ $notification->setUser($uid);
+ $this->notificationManager->notify($notification);
+ }
+
+ $this->config->setAppValue('updatenotification', $app, $version);
+ }
+
+ /**
+ * @return string[]
+ */
+ protected function getUsersToNotify() {
+ if ($this->users !== null) {
+ return $this->users;
+ }
+
+ $notifyGroups = json_decode($this->config->getAppValue('updatenotification', 'notify_groups', '["admin"]'), true);
+ $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 = array_keys($this->users);
+
+ return $this->users;
+ }
+
+ /**
+ * Delete notifications for old updates
+ *
+ * @param string $app
+ * @param string $version
+ */
+ protected function deleteOutdatedNotifications($app, $version) {
+ $notification = $this->notificationManager->createNotification();
+ $notification->setApp('updatenotification')
+ ->setObject($app, $version);
+ $this->notificationManager->markProcessed($notification);
+ }
+
+ /**
+ * @return VersionCheck
+ */
+ protected function createVersionCheck() {
+ return new VersionCheck(
+ $this->client,
+ $this->config
+ );
+ }
+
+ /**
+ * @return string
+ */
+ protected function getChannel() {
+ return \OC_Util::getChannel();
+ }
+
+ /**
+ * @param string $app
+ * @return string|false
+ */
+ protected function isUpdateAvailable($app) {
+ return Installer::isUpdateAvailable($app);
+ }
+}
diff --git a/apps/updatenotification/lib/Notification/Notifier.php b/apps/updatenotification/lib/Notification/Notifier.php
new file mode 100644
index 00000000000..50505ef13f6
--- /dev/null
+++ b/apps/updatenotification/lib/Notification/Notifier.php
@@ -0,0 +1,108 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @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\Notification;
+
+
+use OCP\L10N\IFactory;
+use OCP\Notification\IManager;
+use OCP\Notification\INotification;
+use OCP\Notification\INotifier;
+
+class Notifier implements INotifier {
+
+ /** @var IManager */
+ protected $notificationManager;
+
+ /** @var IFactory */
+ protected $l10NFactory;
+
+ /** @var string[] */
+ protected $appVersions;
+
+ /**
+ * Notifier constructor.
+ *
+ * @param IManager $notificationManager
+ * @param IFactory $l10NFactory
+ */
+ public function __construct(IManager $notificationManager, IFactory $l10NFactory) {
+ $this->notificationManager = $notificationManager;
+ $this->l10NFactory = $l10NFactory;
+ $this->appVersions = $this->getAppVersions();
+ }
+
+ /**
+ * @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
+ * @since 9.0.0
+ */
+ public function prepare(INotification $notification, $languageCode) {
+ if ($notification->getApp() !== 'updatenotification') {
+ throw new \InvalidArgumentException();
+ }
+
+ $l = $this->l10NFactory->get('updatenotification', $languageCode);
+ if ($notification->getObjectType() === 'core') {
+ $appName = $l->t('ownCloud core');
+
+ $this->updateAlreadyInstalledCheck($notification, $this->getCoreVersions());
+ } else {
+ $appInfo = $this->getAppInfo($notification->getObjectType());
+ $appName = ($appInfo === null) ? $notification->getObjectType() : $appInfo['name'];
+
+ 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()]));
+ return $notification;
+ }
+
+ /**
+ * Remove the notification and prevent rendering, when the update is installed
+ *
+ * @param INotification $notification
+ * @param string $installedVersion
+ * @throws \InvalidArgumentException When the update is already installed
+ */
+ protected function updateAlreadyInstalledCheck(INotification $notification, $installedVersion) {
+ if (version_compare($notification->getObjectId(), $installedVersion, '<=')) {
+ $this->notificationManager->markProcessed($notification);
+ throw new \InvalidArgumentException();
+ }
+ }
+
+ protected function getCoreVersions() {
+ return implode('.', \OCP\Util::getVersion());
+ }
+
+ protected function getAppVersions() {
+ return \OC_App::getAppVersions();
+ }
+
+ protected function getAppInfo($appId) {
+ return \OC_App::getAppInfo($appId);
+ }
+}
diff --git a/apps/updatenotification/templates/admin.php b/apps/updatenotification/templates/admin.php
index c1adc8d0d3e..5974b106f72 100644
--- a/apps/updatenotification/templates/admin.php
+++ b/apps/updatenotification/templates/admin.php
@@ -39,4 +39,15 @@
<p>
<em><?php p($l->t('You can always update to a newer version / experimental channel. But you can never downgrade to a more stable channel.')); ?></em>
</p>
+
+
+ <p id="oca_updatenotification_groups">
+ <br />
+ <?php p($l->t('Notify members of the following groups about available updates:')); ?>
+ <input name="oca_updatenotification_groups_list" type="hidden" id="oca_updatenotification_groups_list" value="<?php p($_['notify_groups']) ?>" style="width: 400px">
+ <em class="<?php if (!in_array($currentChannel, ['daily', 'git'])) p('hidden'); ?>">
+ <br />
+ <?php p($l->t('Only notification for app updates are available, because the selected update channel for ownCloud itself does not allow notifications.')); ?>
+ </em>
+ </p>
</form>
diff --git a/apps/updatenotification/tests/Notification/BackgroundJobTest.php b/apps/updatenotification/tests/Notification/BackgroundJobTest.php
new file mode 100644
index 00000000000..d1076e10be5
--- /dev/null
+++ b/apps/updatenotification/tests/Notification/BackgroundJobTest.php
@@ -0,0 +1,430 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @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\Tests\Notification;
+
+
+use OCA\UpdateNotification\Notification\BackgroundJob;
+use OCP\App\IAppManager;
+use OCP\Http\Client\IClientService;
+use OCP\IConfig;
+use OCP\IGroupManager;
+use OCP\IURLGenerator;
+use OCP\IUser;
+use OCP\Notification\IManager;
+use Test\TestCase;
+
+class BackgroundJobTest extends TestCase {
+
+ /** @var IConfig|\PHPUnit_Framework_MockObject_MockObject */
+ protected $config;
+ /** @var IManager|\PHPUnit_Framework_MockObject_MockObject */
+ protected $notificationManager;
+ /** @var IGroupManager|\PHPUnit_Framework_MockObject_MockObject */
+ protected $groupManager;
+ /** @var IAppManager|\PHPUnit_Framework_MockObject_MockObject */
+ protected $appManager;
+ /** @var IClientService|\PHPUnit_Framework_MockObject_MockObject */
+ protected $client;
+ /** @var IURLGenerator|\PHPUnit_Framework_MockObject_MockObject */
+ protected $urlGenerator;
+
+ public function setUp() {
+ parent::setUp();
+
+ $this->config = $this->getMock('OCP\IConfig');
+ $this->notificationManager = $this->getMock('OCP\Notification\IManager');
+ $this->groupManager = $this->getMock('OCP\IGroupManager');
+ $this->appManager = $this->getMock('OCP\App\IAppManager');
+ $this->client = $this->getMock('OCP\Http\Client\IClientService');
+ $this->urlGenerator = $this->getMock('OCP\IURLGenerator');
+ }
+
+ /**
+ * @param array $methods
+ * @return BackgroundJob|\PHPUnit_Framework_MockObject_MockObject
+ */
+ protected function getJob(array $methods = []) {
+ if (empty($methods)) {
+ return new BackgroundJob(
+ $this->config,
+ $this->notificationManager,
+ $this->groupManager,
+ $this->appManager,
+ $this->client,
+ $this->urlGenerator
+ );
+ } {
+ return $this->getMockBuilder('OCA\UpdateNotification\Notification\BackgroundJob')
+ ->setConstructorArgs([
+ $this->config,
+ $this->notificationManager,
+ $this->groupManager,
+ $this->appManager,
+ $this->client,
+ $this->urlGenerator,
+ ])
+ ->setMethods($methods)
+ ->getMock();
+ }
+ }
+
+ public function testRun() {
+ $job = $this->getJob([
+ 'checkCoreUpdate',
+ 'checkAppUpdates',
+ ]);
+
+ $job->expects($this->once())
+ ->method('checkCoreUpdate');
+ $job->expects($this->once())
+ ->method('checkAppUpdates');
+
+ $this->invokePrivate($job, 'run', [null]);
+ }
+
+ public function dataCheckCoreUpdate() {
+ return [
+ ['daily', null, null],
+ ['git', null, null],
+ ['beta', false, null],
+ ['beta', [
+ 'version' => '9.2.0',
+ ], '9.2.0'],
+ ['stable', false, null],
+ ['stable', [
+ 'version' => '9.2.0',
+ ], '9.2.0'],
+ ['production', false, null],
+ ['production', [
+ 'version' => '9.2.0',
+ ], '9.2.0'],
+ ];
+ }
+
+ /**
+ * @dataProvider dataCheckCoreUpdate
+ *
+ * @param string $channel
+ * @param mixed $versionCheck
+ * @param null|string $notification
+ */
+ public function testCheckCoreUpdate($channel, $versionCheck, $notification) {
+ $job = $this->getJob([
+ 'getChannel',
+ 'createVersionCheck',
+ 'createNotifications',
+ ]);
+
+ $job->expects($this->once())
+ ->method('getChannel')
+ ->willReturn($channel);
+
+ if ($versionCheck === null) {
+ $job->expects($this->never())
+ ->method('createVersionCheck');
+ } else {
+ $check = $this->getMockBuilder('OC\Updater\VersionCheck')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $check->expects($this->once())
+ ->method('check')
+ ->willReturn($versionCheck);
+
+ $job->expects($this->once())
+ ->method('createVersionCheck')
+ ->willReturn($check);
+ }
+
+ if ($notification === null) {
+ $this->urlGenerator->expects($this->never())
+ ->method('linkToRouteAbsolute');
+
+ $job->expects($this->never())
+ ->method('createNotifications');
+ } else {
+ $this->urlGenerator->expects($this->once())
+ ->method('linkToRouteAbsolute')
+ ->with('settings_admin')
+ ->willReturn('admin-url');
+
+ $job->expects($this->once())
+ ->method('createNotifications')
+ ->willReturn('core', $notification, 'admin-url#updater');
+ }
+
+ $this->invokePrivate($job, 'checkCoreUpdate');
+ }
+
+ public function dataCheckAppUpdates() {
+ return [
+ [
+ ['app1', 'app2'],
+ [
+ ['app1', false],
+ ['app2', '1.9.2'],
+ ],
+ [
+ ['app2', '1.9.2', 'apps-url#app-app2'],
+ ],
+ ],
+ ];
+ }
+
+ /**
+ * @dataProvider dataCheckAppUpdates
+ *
+ * @param string[] $apps
+ * @param array $isUpdateAvailable
+ * @param array $notifications
+ */
+ public function testCheckAppUpdates(array $apps, array $isUpdateAvailable, array $notifications) {
+ $job = $this->getJob([
+ 'isUpdateAvailable',
+ 'createNotifications',
+ ]);
+
+ $this->appManager->expects($this->once())
+ ->method('getInstalledApps')
+ ->willReturn($apps);
+
+ $job->expects($this->exactly(sizeof($apps)))
+ ->method('isUpdateAvailable')
+ ->willReturnMap($isUpdateAvailable);
+
+ $this->urlGenerator->expects($this->exactly(sizeof($notifications)))
+ ->method('linkToRouteAbsolute')
+ ->with('settings.AppSettings.viewApps')
+ ->willReturn('apps-url');
+
+ $mockedMethod = $job->expects($this->exactly(sizeof($notifications)))
+ ->method('createNotifications');
+ call_user_func_array([$mockedMethod, 'withConsecutive'], $notifications);
+
+ $this->invokePrivate($job, 'checkAppUpdates');
+ }
+
+ public function dataCreateNotifications() {
+ return [
+ ['app1', '1.0.0', 'link1', '1.0.0', false, false, null, null],
+ ['app2', '1.0.1', 'link2', '1.0.0', '1.0.0', true, ['user1'], [['user1']]],
+ ['app3', '1.0.1', 'link3', false, false, true, ['user2', 'user3'], [['user2'], ['user3']]],
+ ];
+ }
+
+ /**
+ * @dataProvider dataCreateNotifications
+ *
+ * @param string $app
+ * @param string $version
+ * @param string $url
+ * @param string|false $lastNotification
+ * @param string|false $callDelete
+ * @param bool $createNotification
+ * @param string[]|null $users
+ * @param array|null $userNotifications
+ */
+ public function testCreateNotifications($app, $version, $url, $lastNotification, $callDelete, $createNotification, $users, $userNotifications) {
+ $job = $this->getJob([
+ 'deleteOutdatedNotifications',
+ 'getUsersToNotify',
+ ]);
+
+ $this->config->expects($this->once())
+ ->method('getAppValue')
+ ->with('updatenotification', $app, false)
+ ->willReturn($lastNotification);
+
+ if ($lastNotification !== $version) {
+ $this->config->expects($this->once())
+ ->method('setAppValue')
+ ->with('updatenotification', $app, $version);
+ }
+
+ if ($callDelete === false) {
+ $job->expects($this->never())
+ ->method('deleteOutdatedNotifications');
+ } else {
+ $job->expects($this->once())
+ ->method('deleteOutdatedNotifications')
+ ->with($app, $callDelete);
+ }
+
+ if ($users === null) {
+ $job->expects($this->never())
+ ->method('getUsersToNotify');
+ } else {
+ $job->expects($this->once())
+ ->method('getUsersToNotify')
+ ->willReturn($users);
+ }
+
+ if ($createNotification) {
+ $notification = $this->getMock('OCP\Notification\INotification');
+ $notification->expects($this->once())
+ ->method('setApp')
+ ->with('updatenotification')
+ ->willReturnSelf();
+ $notification->expects($this->once())
+ ->method('setDateTime')
+ ->willReturnSelf();
+ $notification->expects($this->once())
+ ->method('setObject')
+ ->with($app, $version)
+ ->willReturnSelf();
+ $notification->expects($this->once())
+ ->method('setSubject')
+ ->with('update_available')
+ ->willReturnSelf();
+ $notification->expects($this->once())
+ ->method('setLink')
+ ->with($url)
+ ->willReturnSelf();
+
+ if ($userNotifications !== null) {
+ $mockedMethod = $notification->expects($this->exactly(sizeof($userNotifications)))
+ ->method('setUser')
+ ->willReturnSelf();
+ call_user_func_array([$mockedMethod, 'withConsecutive'], $userNotifications);
+
+ $this->notificationManager->expects($this->exactly(sizeof($userNotifications)))
+ ->method('notify')
+ ->willReturn($notification);
+ }
+
+ $this->notificationManager->expects($this->once())
+ ->method('createNotification')
+ ->willReturn($notification);
+ } else {
+ $this->notificationManager->expects($this->never())
+ ->method('createNotification');
+ }
+
+ $this->invokePrivate($job, 'createNotifications', [$app, $version, $url]);
+ }
+
+ public function dataGetUsersToNotify() {
+ return [
+ [['g1', 'g2'], ['g1' => null, 'g2' => ['u1', 'u2']], ['u1', 'u2']],
+ [['g3', 'g4'], ['g3' => ['u1', 'u2'], 'g4' => ['u2', 'u3']], ['u1', 'u2', 'u3']],
+ ];
+ }
+
+ /**
+ * @dataProvider dataGetUsersToNotify
+ * @param string[] $groups
+ * @param array $groupUsers
+ * @param string[] $expected
+ */
+ public function testGetUsersToNotify($groups, array $groupUsers, array $expected) {
+ $job = $this->getJob();
+
+ $this->config->expects($this->once())
+ ->method('getAppValue')
+ ->with('updatenotification', 'notify_groups', '["admin"]')
+ ->willReturn(json_encode($groups));
+
+ $groupMap = [];
+ foreach ($groupUsers as $gid => $uids) {
+ if ($uids === null) {
+ $group = null;
+ } else {
+ $group = $this->getGroup($gid);
+ $group->expects($this->any())
+ ->method('getUsers')
+ ->willReturn($this->getUsers($uids));
+ }
+ $groupMap[] = [$gid, $group];
+ }
+ $this->groupManager->expects($this->exactly(sizeof($groups)))
+ ->method('get')
+ ->willReturnMap($groupMap);
+
+ $result = $this->invokePrivate($job, 'getUsersToNotify');
+ $this->assertEquals($expected, $result);
+
+ // Test caching
+ $result = $this->invokePrivate($job, 'getUsersToNotify');
+ $this->assertEquals($expected, $result);
+ }
+
+ public function dataDeleteOutdatedNotifications() {
+ return [
+ ['app1', '1.1.0'],
+ ['app2', '1.2.0'],
+ ];
+ }
+
+ /**
+ * @dataProvider dataDeleteOutdatedNotifications
+ * @param string $app
+ * @param string $version
+ */
+ public function testDeleteOutdatedNotifications($app, $version) {
+ $notification = $this->getMock('OCP\Notification\INotification');
+ $notification->expects($this->once())
+ ->method('setApp')
+ ->with('updatenotification')
+ ->willReturnSelf();
+ $notification->expects($this->once())
+ ->method('setObject')
+ ->with($app, $version)
+ ->willReturnSelf();
+
+ $this->notificationManager->expects($this->once())
+ ->method('createNotification')
+ ->willReturn($notification);
+ $this->notificationManager->expects($this->once())
+ ->method('markProcessed')
+ ->with($notification);
+
+ $job = $this->getJob();
+ $this->invokePrivate($job, 'deleteOutdatedNotifications', [$app, $version]);
+ }
+
+ /**
+ * @param string[] $userIds
+ * @return IUser[]|\PHPUnit_Framework_MockObject_MockObject[]
+ */
+ protected function getUsers(array $userIds) {
+ $users = [];
+ foreach ($userIds as $uid) {
+ $user = $this->getMock('OCP\IUser');
+ $user->expects($this->any())
+ ->method('getUID')
+ ->willReturn($uid);
+ $users[] = $user;
+ }
+ return $users;
+ }
+
+ /**
+ * @param $gid
+ * @return \OCP\IGroup|\PHPUnit_Framework_MockObject_MockObject
+ */
+ protected function getGroup($gid) {
+ $group = $this->getMock('OCP\IGroup');
+ $group->expects($this->any())
+ ->method('getGID')
+ ->willReturn($gid);
+ return $group;
+ }
+}
diff --git a/apps/updatenotification/tests/Notification/NotifierTest.php b/apps/updatenotification/tests/Notification/NotifierTest.php
new file mode 100644
index 00000000000..d85cf3e7072
--- /dev/null
+++ b/apps/updatenotification/tests/Notification/NotifierTest.php
@@ -0,0 +1,105 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @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\Tests\Notification;
+
+
+use OCA\UpdateNotification\Notification\Notifier;
+use OCP\L10N\IFactory;
+use OCP\Notification\IManager;
+use Test\TestCase;
+
+class NotifierTest extends TestCase {
+
+ /** @var IManager|\PHPUnit_Framework_MockObject_MockObject */
+ protected $notificationManager;
+ /** @var IFactory|\PHPUnit_Framework_MockObject_MockObject */
+ protected $l10nFactory;
+
+ public function setUp() {
+ parent::setUp();
+
+ $this->notificationManager = $this->getMock('OCP\Notification\IManager');
+ $this->l10nFactory = $this->getMock('OCP\L10n\IFactory');
+ }
+
+ /**
+ * @param array $methods
+ * @return Notifier|\PHPUnit_Framework_MockObject_MockObject
+ */
+ protected function getNotifier(array $methods = []) {
+ if (empty($methods)) {
+ return new Notifier(
+ $this->notificationManager,
+ $this->l10nFactory
+ );
+ } {
+ return $this->getMockBuilder('OCA\UpdateNotification\Notification\Notifier')
+ ->setConstructorArgs([
+ $this->notificationManager,
+ $this->l10nFactory,
+ ])
+ ->setMethods($methods)
+ ->getMock();
+ }
+ }
+
+ public function dataUpdateAlreadyInstalledCheck() {
+ return [
+ ['1.1.0', '1.0.0', false],
+ ['1.1.0', '1.1.0', true],
+ ['1.1.0', '1.2.0', true],
+ ];
+ }
+
+ /**
+ * @dataProvider dataUpdateAlreadyInstalledCheck
+ *
+ * @param string $versionNotification
+ * @param string $versionInstalled
+ * @param bool $exception
+ */
+ public function testUpdateAlreadyInstalledCheck($versionNotification, $versionInstalled, $exception) {
+ $notifier = $this->getNotifier();
+
+ $notification = $this->getMock('OCP\Notification\INotification');
+ $notification->expects($this->once())
+ ->method('getObjectId')
+ ->willReturn($versionNotification);
+
+ if ($exception) {
+ $this->notificationManager->expects($this->once())
+ ->method('markProcessed')
+ ->with($notification);
+ } else {
+ $this->notificationManager->expects($this->never())
+ ->method('markProcessed');
+ }
+
+ try {
+ $this->invokePrivate($notifier, 'updateAlreadyInstalledCheck', [$notification, $versionInstalled]);
+ $this->assertFalse($exception);
+ } catch (\Exception $e) {
+ $this->assertTrue($exception);
+ $this->assertInstanceOf('InvalidArgumentException', $e);
+ }
+ }
+}