From 856518cdaee39386eb511ee46c8c97f1bc94a4ba Mon Sep 17 00:00:00 2001 From: Maxence Lange Date: Thu, 25 Jun 2020 01:48:36 -0100 Subject: [PATCH] Notification and Migration Signed-off-by: Maxence Lange --- appinfo/app.php | 6 + appinfo/info.xml | 2 +- img/social_dark.svg | 55 +++++++++ lib/AppInfo/Application.php | 56 +++++++++ lib/Db/ActionsRequestBuilder.php | 2 + lib/Db/ActorsRequestBuilder.php | 7 +- lib/Db/CacheActorsRequestBuilder.php | 3 +- lib/Db/CacheDocumentsRequestBuilder.php | 7 +- lib/Db/FollowsRequest.php | 4 +- lib/Db/FollowsRequestBuilder.php | 8 +- lib/Db/HashtagsRequestBuilder.php | 7 +- lib/Db/RequestQueueRequestBuilder.php | 3 +- lib/Db/StreamActionsRequestBuilder.php | 7 +- lib/Db/StreamDestRequestBuilder.php | 10 +- lib/Db/StreamQueueRequest.php | 2 +- lib/Db/StreamQueueRequestBuilder.php | 7 +- lib/Db/StreamTagsRequestBuilder.php | 1 + lib/Notification/Notifier.php | 150 ++++++++++++++++++++++++ lib/Service/UpdateService.php | 141 ++++++++++++++++++++++ 19 files changed, 450 insertions(+), 28 deletions(-) create mode 100644 img/social_dark.svg create mode 100644 lib/Notification/Notifier.php create mode 100644 lib/Service/UpdateService.php diff --git a/appinfo/app.php b/appinfo/app.php index e61bbf60..5a8c2071 100644 --- a/appinfo/app.php +++ b/appinfo/app.php @@ -33,3 +33,9 @@ namespace OCA\Social\AppInfo; require_once __DIR__ . '/autoload.php'; +/** @var Application $app */ +$app = \OC::$server->query(Application::class); + +$app->checkUpgradeStatus(); + + diff --git a/appinfo/info.xml b/appinfo/info.xml index 2568bb2b..44156d58 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -32,7 +32,7 @@ https://github.com/nextcloud/social.git https://raw.githubusercontent.com/nextcloud/social/master/img/screenshot.png - + pgsql sqlite mysql diff --git a/img/social_dark.svg b/img/social_dark.svg new file mode 100644 index 00000000..8bfbf968 --- /dev/null +++ b/img/social_dark.svg @@ -0,0 +1,55 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index d20ff7f1..18996bac 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -31,15 +31,36 @@ declare(strict_types=1); namespace OCA\Social\AppInfo; +use OC\DB\SchemaWrapper; +use OCA\Social\Notification\Notifier; +use OCA\Social\Service\ConfigService; +use OCA\Social\Service\UpdateService; use OCP\AppFramework\App; +use OCP\AppFramework\IAppContainer; +use OCP\AppFramework\QueryException; +/** + * Class Application + * + * @package OCA\Social\AppInfo + */ class Application extends App { const APP_NAME = 'social'; + /** @var ConfigService */ + private $configService; + + /** @var UpdateService */ + private $updateService; + + /** @var IAppContainer */ + private $container; + + /** * Application constructor. * @@ -47,6 +68,41 @@ class Application extends App { */ public function __construct(array $params = []) { parent::__construct(self::APP_NAME, $params); + + $this->container = $this->getContainer(); + + $manager = $this->container->getServer() + ->getNotificationManager(); + $manager->registerNotifierService(Notifier::class); + } + + + /** + * + */ + public function checkUpgradeStatus() { + $upgradeChecked = $this->container->getServer() + ->getConfig() + ->getAppValue(Application::APP_NAME, 'update_checked', ''); + + if ($upgradeChecked === '0.3') { + return; + } + + try { + $this->configService = $this->container->query(ConfigService::class); + $this->updateService = $this->container->query(UpdateService::class); + } catch (QueryException $e) { + return; + } + + $server = $this->container->getServer(); + $schema = new SchemaWrapper($server->getDatabaseConnection()); + if ($schema->hasTable('social_a2_stream')) { + $this->updateService->checkUpdateStatus(); + } + + $this->configService->setAppValue('update_checked', '0.3'); } } diff --git a/lib/Db/ActionsRequestBuilder.php b/lib/Db/ActionsRequestBuilder.php index fa760304..3178b8d8 100644 --- a/lib/Db/ActionsRequestBuilder.php +++ b/lib/Db/ActionsRequestBuilder.php @@ -88,6 +88,7 @@ class ActionsRequestBuilder extends CoreRequestBuilder { ->from(self::TABLE_ACTIONS, 'a'); $this->defaultSelectAlias = 'a'; + $qb->setDefaultSelectAlias('a'); return $qb; } @@ -104,6 +105,7 @@ class ActionsRequestBuilder extends CoreRequestBuilder { ->from(self::TABLE_ACTIONS, 'a'); $this->defaultSelectAlias = 'a'; + $qb->setDefaultSelectAlias('a'); return $qb; } diff --git a/lib/Db/ActorsRequestBuilder.php b/lib/Db/ActorsRequestBuilder.php index a1fb23ec..c8bcd6cc 100644 --- a/lib/Db/ActorsRequestBuilder.php +++ b/lib/Db/ActorsRequestBuilder.php @@ -70,10 +70,10 @@ class ActorsRequestBuilder extends CoreRequestBuilder { /** * Base of the Sql Select request for Shares * - * @return IQueryBuilder + * @return SocialQueryBuilder */ - protected function getActorsSelectSql(): IQueryBuilder { - $qb = $this->dbConnection->getQueryBuilder(); + protected function getActorsSelectSql(): SocialQueryBuilder { + $qb = $this->getQueryBuilder(); /** @noinspection PhpMethodParametersCountMismatchInspection */ $qb->select( @@ -83,6 +83,7 @@ class ActorsRequestBuilder extends CoreRequestBuilder { ->from(self::TABLE_ACTORS, 'a'); $this->defaultSelectAlias = 'a'; + $qb->setDefaultSelectAlias('a'); return $qb; } diff --git a/lib/Db/CacheActorsRequestBuilder.php b/lib/Db/CacheActorsRequestBuilder.php index 3d595838..0289e204 100644 --- a/lib/Db/CacheActorsRequestBuilder.php +++ b/lib/Db/CacheActorsRequestBuilder.php @@ -90,7 +90,8 @@ class CacheActorsRequestBuilder extends CoreRequestBuilder { /** @deprecated */ $this->defaultSelectAlias = 'ca'; - + $qb->setDefaultSelectAlias('ca'); + return $qb; } diff --git a/lib/Db/CacheDocumentsRequestBuilder.php b/lib/Db/CacheDocumentsRequestBuilder.php index c21869b9..59171dec 100644 --- a/lib/Db/CacheDocumentsRequestBuilder.php +++ b/lib/Db/CacheDocumentsRequestBuilder.php @@ -69,10 +69,10 @@ class CacheDocumentsRequestBuilder extends CoreRequestBuilder { /** * Base of the Sql Select request for Shares * - * @return IQueryBuilder + * @return SocialQueryBuilder */ - protected function getCacheDocumentsSelectSql(): IQueryBuilder { - $qb = $this->dbConnection->getQueryBuilder(); + protected function getCacheDocumentsSelectSql(): SocialQueryBuilder { + $qb = $this->getQueryBuilder(); /** @noinspection PhpMethodParametersCountMismatchInspection */ $qb->select( @@ -82,6 +82,7 @@ class CacheDocumentsRequestBuilder extends CoreRequestBuilder { ->from(self::TABLE_CACHE_DOCUMENTS, 'cd'); $this->defaultSelectAlias = 'cd'; + $qb->setDefaultSelectAlias('cd'); return $qb; } diff --git a/lib/Db/FollowsRequest.php b/lib/Db/FollowsRequest.php index c9138ba4..d466aa4f 100644 --- a/lib/Db/FollowsRequest.php +++ b/lib/Db/FollowsRequest.php @@ -167,7 +167,7 @@ class FollowsRequest extends FollowsRequestBuilder { */ public function countFollowers(string $actorId): int { $qb = $this->countFollowsSelectSql(); - $qb->limitToObjectId($actorId); + $qb->limitToObjectIdPrim($qb->prim($actorId)); $qb->limitToType(Follow::TYPE); $qb->limitToAccepted(true); @@ -186,7 +186,7 @@ class FollowsRequest extends FollowsRequestBuilder { */ public function countFollowing(string $actorId): int { $qb = $this->countFollowsSelectSql(); - $qb->limitToActorId($actorId); + $qb->limitToActorIdPrim($qb->prim($actorId)); $qb->limitToType(Follow::TYPE); $qb->limitToAccepted(true); diff --git a/lib/Db/FollowsRequestBuilder.php b/lib/Db/FollowsRequestBuilder.php index eb3bc2d4..ff11642f 100644 --- a/lib/Db/FollowsRequestBuilder.php +++ b/lib/Db/FollowsRequestBuilder.php @@ -77,10 +77,10 @@ class FollowsRequestBuilder extends CoreRequestBuilder { /** * Base of the Sql Select request for Shares * - * @return IQueryBuilder + * @return SocialQueryBuilder */ - protected function getFollowsSelectSql(): IQueryBuilder { - $qb = $this->dbConnection->getQueryBuilder(); + protected function getFollowsSelectSql(): SocialQueryBuilder { + $qb = $this->getQueryBuilder(); /** @noinspection PhpMethodParametersCountMismatchInspection */ $qb->select( @@ -89,6 +89,7 @@ class FollowsRequestBuilder extends CoreRequestBuilder { ->from(self::TABLE_FOLLOWS, 'f'); $this->defaultSelectAlias = 'f'; + $qb->setDefaultSelectAlias('f'); return $qb; } @@ -104,6 +105,7 @@ class FollowsRequestBuilder extends CoreRequestBuilder { $qb->selectAlias($qb->createFunction('COUNT(*)'), 'count') ->from(self::TABLE_FOLLOWS, 'f'); + $qb->setDefaultSelectAlias('f'); $this->defaultSelectAlias = 'f'; return $qb; diff --git a/lib/Db/HashtagsRequestBuilder.php b/lib/Db/HashtagsRequestBuilder.php index 6ed4e92b..f3ab9d0c 100644 --- a/lib/Db/HashtagsRequestBuilder.php +++ b/lib/Db/HashtagsRequestBuilder.php @@ -75,16 +75,17 @@ class HashtagsRequestBuilder extends CoreRequestBuilder { /** * Base of the Sql Select request for Shares * - * @return IQueryBuilder + * @return SocialQueryBuilder */ - protected function getHashtagsSelectSql(): IQueryBuilder { - $qb = $this->dbConnection->getQueryBuilder(); + protected function getHashtagsSelectSql(): SocialQueryBuilder { + $qb = $this->getQueryBuilder(); /** @noinspection PhpMethodParametersCountMismatchInspection */ $qb->select('h.hashtag', 'h.trend') ->from(self::TABLE_HASHTAGS, 'h'); $this->defaultSelectAlias = 'h'; + $qb->setDefaultSelectAlias('h'); return $qb; } diff --git a/lib/Db/RequestQueueRequestBuilder.php b/lib/Db/RequestQueueRequestBuilder.php index 4d0f5f63..eb2de66d 100644 --- a/lib/Db/RequestQueueRequestBuilder.php +++ b/lib/Db/RequestQueueRequestBuilder.php @@ -72,7 +72,7 @@ class RequestQueueRequestBuilder extends CoreRequestBuilder { * @return IQueryBuilder */ protected function getRequestQueueSelectSql(): IQueryBuilder { - $qb = $this->dbConnection->getQueryBuilder(); + $qb = $this->getQueryBuilder(); /** @noinspection PhpMethodParametersCountMismatchInspection */ $qb->select( @@ -82,6 +82,7 @@ class RequestQueueRequestBuilder extends CoreRequestBuilder { ->from(self::TABLE_REQUEST_QUEUE, 'rq'); $this->defaultSelectAlias = 'rq'; + $qb->setDefaultSelectAlias('rq'); return $qb; } diff --git a/lib/Db/StreamActionsRequestBuilder.php b/lib/Db/StreamActionsRequestBuilder.php index fc68aff5..5297cc12 100644 --- a/lib/Db/StreamActionsRequestBuilder.php +++ b/lib/Db/StreamActionsRequestBuilder.php @@ -75,16 +75,17 @@ class StreamActionsRequestBuilder extends CoreRequestBuilder { /** * Base of the Sql Select request for Shares * - * @return IQueryBuilder + * @return SocialQueryBuilder */ - protected function getStreamActionSelectSql(): IQueryBuilder { - $qb = $this->dbConnection->getQueryBuilder(); + protected function getStreamActionSelectSql(): SocialQueryBuilder { + $qb = $this->getQueryBuilder(); /** @noinspection PhpMethodParametersCountMismatchInspection */ $qb->select('sa.id', 'sa.actor_id', 'sa.stream_id', 'sa.values') ->from(self::TABLE_STREAM_ACTIONS, 'sa'); $this->defaultSelectAlias = 'sa'; + $qb->setDefaultSelectAlias('sa'); return $qb; } diff --git a/lib/Db/StreamDestRequestBuilder.php b/lib/Db/StreamDestRequestBuilder.php index 986e0215..38c89863 100644 --- a/lib/Db/StreamDestRequestBuilder.php +++ b/lib/Db/StreamDestRequestBuilder.php @@ -74,9 +74,9 @@ class StreamDestRequestBuilder extends CoreRequestBuilder { /** * Base of the Sql Select request for Shares * - * @return IQueryBuilder + * @return SocialQueryBuilder */ - protected function getStreamDestSelectSql(): IQueryBuilder { + protected function getStreamDestSelectSql(): SocialQueryBuilder { $qb = $this->getQueryBuilder(); /** @noinspection PhpMethodParametersCountMismatchInspection */ @@ -84,6 +84,7 @@ class StreamDestRequestBuilder extends CoreRequestBuilder { ->from(self::TABLE_STREAM_DEST, 'sd'); $this->defaultSelectAlias = 'sd'; + $qb->setDefaultSelectAlias('sd'); return $qb; } @@ -105,14 +106,15 @@ class StreamDestRequestBuilder extends CoreRequestBuilder { /** * Base of the Sql Select request for Shares * - * @return IQueryBuilder + * @return SocialQueryBuilder */ - protected function countStreamDestSelectSql(): IQueryBuilder { + protected function countStreamDestSelectSql(): SocialQueryBuilder { $qb = $this->getQueryBuilder(); $qb->selectAlias($qb->createFunction('COUNT(*)'), 'count') ->from(self::TABLE_STREAM_DEST, 'sd'); $this->defaultSelectAlias = 'sd'; + $qb->setDefaultSelectAlias('sd'); return $qb; } diff --git a/lib/Db/StreamQueueRequest.php b/lib/Db/StreamQueueRequest.php index 70b1eb9d..871b8f99 100644 --- a/lib/Db/StreamQueueRequest.php +++ b/lib/Db/StreamQueueRequest.php @@ -91,7 +91,7 @@ class StreamQueueRequest extends StreamQueueRequestBuilder { */ public function getFromToken(string $token): array { $qb = $this->getStreamQueueSelectSql(); - $this->limitToToken($qb, $token); + $qb->limitToToken($token); $queue = []; $cursor = $qb->execute(); diff --git a/lib/Db/StreamQueueRequestBuilder.php b/lib/Db/StreamQueueRequestBuilder.php index 39509ce0..1e108920 100644 --- a/lib/Db/StreamQueueRequestBuilder.php +++ b/lib/Db/StreamQueueRequestBuilder.php @@ -69,10 +69,10 @@ class StreamQueueRequestBuilder extends CoreRequestBuilder { /** * Base of the Sql Select request for Shares * - * @return IQueryBuilder + * @return SocialQueryBuilder */ - protected function getStreamQueueSelectSql(): IQueryBuilder { - $qb = $this->dbConnection->getQueryBuilder(); + protected function getStreamQueueSelectSql(): SocialQueryBuilder { + $qb = $this->getQueryBuilder(); /** @noinspection PhpMethodParametersCountMismatchInspection */ $qb->select( @@ -81,6 +81,7 @@ class StreamQueueRequestBuilder extends CoreRequestBuilder { ->from(self::TABLE_STREAM_QUEUE, 'qs'); $this->defaultSelectAlias = 'qs'; + $qb->setDefaultSelectAlias('qs'); return $qb; } diff --git a/lib/Db/StreamTagsRequestBuilder.php b/lib/Db/StreamTagsRequestBuilder.php index 25fe9f67..0290253a 100644 --- a/lib/Db/StreamTagsRequestBuilder.php +++ b/lib/Db/StreamTagsRequestBuilder.php @@ -84,6 +84,7 @@ class StreamTagsRequestBuilder extends CoreRequestBuilder { ->from(self::TABLE_STREAM_TAGS, 'st'); $this->defaultSelectAlias = 'st'; + $qb->setDefaultSelectAlias('st'); return $qb; } diff --git a/lib/Notification/Notifier.php b/lib/Notification/Notifier.php new file mode 100644 index 00000000..1b7db739 --- /dev/null +++ b/lib/Notification/Notifier.php @@ -0,0 +1,150 @@ + + * @copyright 2018, Maxence Lange + * @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 . + * + */ + + +namespace OCA\Social\Notification; + + +use InvalidArgumentException; +use OC; +use OCA\Social\AppInfo\Application; +use OCP\Contacts\IManager; +use OCP\Federation\ICloudIdManager; +use OCP\IL10N; +use OCP\IURLGenerator; +use OCP\L10N\IFactory; +use OCP\Notification\INotification; +use OCP\Notification\INotifier; + + +/** + * Class Notifier + * + * @package OCA\Social\Notification + */ +class Notifier implements INotifier { + + + /** @var IL10N */ + private $l10n; + + /** @var IFactory */ + protected $factory; + + /** @var IManager */ + protected $contactsManager; + + /** @var IURLGenerator */ + protected $url; + + /** @var ICloudIdManager */ + protected $cloudIdManager; + + + public function __construct( + IL10N $l10n, IFactory $factory, IManager $contactsManager, IURLGenerator $url, + ICloudIdManager $cloudIdManager + ) { + $this->l10n = $l10n; + $this->factory = $factory; + $this->contactsManager = $contactsManager; + $this->url = $url; + $this->cloudIdManager = $cloudIdManager; + } + + /** + * Identifier of the notifier, only use [a-z0-9_] + * + * @return string + * @since 17.0.0 + */ + public function getID(): string { + return Application::APP_NAME; + } + + /** + * Human readable name describing the notifier + * + * @return string + * @since 17.0.0 + */ + public function getName(): string { + return $this->l10n->t('Social'); + } + + /** + * @param INotification $notification + * @param string $languageCode The code of the language that should be used to prepare the notification + * + * @return INotification + * @throws InvalidArgumentException + */ + public function prepare(INotification $notification, string $languageCode): INotification { + if ($notification->getApp() !== Application::APP_NAME) { + throw new InvalidArgumentException(); + } + + $l10n = OC::$server->getL10N(Application::APP_NAME, $languageCode); + + $notification->setIcon( + $this->url->getAbsoluteURL($this->url->imagePath('social', 'social_dark.svg')) + ); + $params = $notification->getSubjectParameters(); + + switch ($notification->getSubject()) { + case 'update_alpha3': + $notification->setParsedSubject('The Social App have been updated to alpha3.'); + $notification->setParsedMessage( + $l10n->t( + 'Please note that the data from alpha2 can only be migrated manually. + A detailed documentation to guide you during this process is available using the button below.', + $params + ) + ); + break; + + default: + throw new InvalidArgumentException(); + } + + + foreach ($notification->getActions() as $action) { + switch ($action->getLabel()) { + case 'help': + $action->setParsedLabel((string)$l10n->t('Help')) + ->setPrimary(true); + break; + } + + $notification->addParsedAction($action); + } + + return $notification; + } + +} diff --git a/lib/Service/UpdateService.php b/lib/Service/UpdateService.php new file mode 100644 index 00000000..6a36bd19 --- /dev/null +++ b/lib/Service/UpdateService.php @@ -0,0 +1,141 @@ + + * + * @author Julius Härtl + * + * @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 . + * + */ + +namespace OCA\Social\Service; + + +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\IGroupManager; +use OCP\IUserManager; +use OCP\Notification\IManager as INotificationManager; +use OCP\Notification\INotification; + + +/** + * Class UpdateService + * + * @package OCA\Social\Service + */ +class UpdateService { + + + /** @var IUserManager */ + private $userManager; + + /** @var IGroupManager */ + private $groupManager; + + /** @var ITimeFactory */ + private $time; + + /** @var INotificationManager */ + private $notificationManager; + + + /** @var string */ + private $updateId = 'alpha3'; + + + /** + * UpdateService constructor. + * + * @param IUserManager $userManager + * @param IGroupManager $groupManager + * @param ITimeFactory $time + * @param INotificationManager $notificationManager + */ + public function __construct( + IUserManager $userManager, IGroupManager $groupManager, ITimeFactory $time, + INotificationManager $notificationManager + ) { + $this->userManager = $userManager; + $this->groupManager = $groupManager; + $this->time = $time; + $this->notificationManager = $notificationManager; + } + + + public function checkUpdateStatus() { + $notifications = $this->generateNotifications(true, 'update_alpha3', []); + + foreach ($notifications as $notif) { + $help = $notif->createAction(); + $help->setLabel('help') + ->setLink('https://help.nextcloud.com/t/social-alpha3-how-to-upgrade/85535', 'WEB'); + + $notif->addAction($help); + $this->notificationManager->notify($notif); + } + } + + + /** + * @param bool $adminOnly + * @param string $subject + * @param array $data + * + * @return INotification[] + */ + public function generateNotifications(bool $adminOnly, string $subject, array $data): array { + $notifications = []; + $users = $this->userManager->search(''); + + if ($adminOnly) { + $admin = []; + foreach ($users as $user) { + if ($this->groupManager->isAdmin($user->getUID())) { + $admin[] = $user; + } + } + $users = $admin; + } + + foreach ($users as $user) { + $notifications[] = $this->createNotification($user->getUID(), $subject, $data); + } + + return $notifications; + } + + + /** + * @param string $userId + * @param string $subject + * @param array $data + * + * @return INotification + */ + public function createNotification(string $userId, string $subject, array $data): INotification { + $now = $this->time->getDateTime(); + $notification = $this->notificationManager->createNotification(); + $notification->setApp('social') + ->setDateTime($now) + ->setUser($userId) + ->setObject('update', 'update_' . $this->updateId) + ->setSubject($subject, $data); + + return $notification; + } + +} +