diff --git a/appinfo/info.xml b/appinfo/info.xml index a695b9fabd50f7d579fbeb089f13f6422598d363..44ce9959d2672d24f9f69d91206f512c58a04a06 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -27,4 +27,9 @@ OCA\EcloudAccounts\Command\Migrate2FASecrets + + + OCA\EcloudAccounts\Migration\CreateTasksCalendar + + diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 3574f538feb8edcd24943bce57dfec8f9d867f8c..47f82a4cad9ebf090114962503e390a6aaf700f5 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -26,20 +26,29 @@ declare(strict_types=1); namespace OCA\EcloudAccounts\AppInfo; +use OCA\DAV\CalDAV\CalDavBackend; +use OCA\EcloudAccounts\Listeners\BeforeTemplateRenderedListener; +use OCA\EcloudAccounts\Listeners\BeforeUserDeletedListener; +use OCA\EcloudAccounts\Listeners\UserChangedListener; +use OCA\EcloudAccounts\Service\LDAPConnectionService; use OCP\AppFramework\App; +use OCP\AppFramework\Bootstrap\IBootContext; use OCP\AppFramework\Bootstrap\IBootstrap; use OCP\AppFramework\Bootstrap\IRegistrationContext; -use OCP\AppFramework\Bootstrap\IBootContext; -use OCA\EcloudAccounts\Listeners\BeforeUserDeletedListener; -use OCA\EcloudAccounts\Service\LDAPConnectionService; +use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent; +use OCP\Defaults; +use OCP\IDBConnection; +use OCP\IUser; use OCP\User\Events\BeforeUserDeletedEvent; use OCP\User\Events\UserChangedEvent; -use OCA\EcloudAccounts\Listeners\UserChangedListener; -use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent; -use OCA\EcloudAccounts\Listeners\BeforeTemplateRenderedListener; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\EventDispatcher\GenericEvent; class Application extends App implements IBootstrap { public const APP_ID = 'ecloud-accounts'; + public const TASKS_CALENDAR_URI = 'tasks'; + public const TASKS_CALENDAR_NAME = 'Tasks'; + public const TASKS_CALENDAR_COMPONENT = 'VTODO'; public function __construct(array $urlParams = []) { parent::__construct(self::APP_ID, $urlParams); @@ -52,9 +61,38 @@ class Application extends App implements IBootstrap { } public function boot(IBootContext $context): void { + $context->injectFn([$this, 'createTasksCalendar']); $serverContainer = $context->getServerContainer(); $serverContainer->registerService('LDAPConnectionService', function ($c) { return new LDAPConnectionService(); }); } + + public function createTasksCalendar(CalDavBackend $calDav, IDBConnection $db, Defaults $themingDefaults, EventDispatcherInterface $dispatcher): void { + $dispatcher->addListener(IUser::class . '::firstLogin', function (GenericEvent $event) use ($calDav, $themingDefaults, $db) { + $user = $event->getSubject(); + if (!$user instanceof IUser) { + return; + } + $userId = $user->getUID(); + $principal = 'principals/users/' . $userId; + $calendar = $calDav->getCalendarByUri($principal, self::TASKS_CALENDAR_URI); + $query = $db->getQueryBuilder(); + $query->select('uri')->from('calendars') + ->where($query->expr()->eq('uri', $query->createNamedParameter(self::TASKS_CALENDAR_URI))) + ->andWhere($query->expr()->eq('principaluri', $query->createNamedParameter($principal))) + ->andWhere($query->expr()->eq('components', $query->createNamedParameter(self::TASKS_CALENDAR_COMPONENT))) + ->setMaxResults(1); + $stmt = $query->executeQuery(); + $row = $stmt->fetch(); + $stmt->closeCursor(); + if ($row === false) { + $calDav->createCalendar($principal, self::TASKS_CALENDAR_URI, [ + '{DAV:}displayname' => self::TASKS_CALENDAR_NAME, + '{http://apple.com/ns/ical/}calendar-color' => $themingDefaults->getColorPrimary(), + 'components' => self::TASKS_CALENDAR_COMPONENT + ]); + } + }); + } } diff --git a/lib/Migration/CreateTasksCalendar.php b/lib/Migration/CreateTasksCalendar.php new file mode 100644 index 0000000000000000000000000000000000000000..b7b8f24d722af45b0f27e50e10e6b800128a2904 --- /dev/null +++ b/lib/Migration/CreateTasksCalendar.php @@ -0,0 +1,153 @@ + + * + * @author Joas Schilling + * + * @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\EcloudAccounts\Migration; + +use OCA\DAV\CalDAV\CalDavBackend; +use OCP\Defaults; +use OCP\IConfig; +use OCP\IDBConnection; +use OCP\IUserManager; +use OCP\Migration\IOutput; +use OCP\Migration\IRepairStep; + +/** + * Class CreateTasksCalendar + * + * @package OCA\EcloudAccounts\Migration + */ +class CreateTasksCalendar implements IRepairStep { + public const APP_ID = 'ecloud-accounts'; + public const TASKS_CALENDAR_URI = 'tasks'; + public const TASKS_CALENDAR_NAME = 'Tasks'; + public const TASKS_CALENDAR_COMPONENT = 'VTODO'; + + /** @var IDBConnection */ + protected $connection; + + /** @var IConfig */ + protected $config; + + /** @var IUserManager */ + private $userManager; + + /** @var CalDavBackend */ + protected $calDav; + + /** @var Defaults */ + protected $themingDefaults; + + + public function __construct(IDBConnection $connection, IConfig $config, IUserManager $userManager, CalDavBackend $calDav, Defaults $themingDefaults) { + $this->connection = $connection; + $this->config = $config; + $this->userManager = $userManager; + $this->calDav = $calDav; + $this->themingDefaults = $themingDefaults; + } + + /** + * Returns the step's name + * + * @return string + * @since 9.1.0 + */ + public function getName() { + return 'Fix by creating Tasks calendar for user if not exist.'; + } + + /** + * Returns the array of calendars + * + * @return array + */ + private function getPrincipalUriByCalendar():array { + $query = $this->connection->getQueryBuilder(); + $expr = $query->expr(); + $query->select($query->createFunction('DISTINCT ' . $query->getColumnName('c1.principaluri'))) + ->from('calendars', 'c1') + ->leftJoin('c1', 'calendars', 'c2', $expr->andX( + $expr->eq('c1.principaluri', 'c2.principaluri'), + $expr->eq('c2.uri', $query->createNamedParameter('tasks')), + $expr->eq('c2.components', $query->createNamedParameter('VTODO')) + ) + ) + ->where($query->expr()->isNull('c2.principaluri')); + $stmt = $query->executeQuery(); + return $stmt->fetchAll(); + } + + /** + * Returns the unique Task Uri + * + * @return string + */ + private function getUniqueTaskUri($principal, $taskUri):string { + $qb = $this->connection->getQueryBuilder(); + $qb->select('uri') + ->from('calendars') + ->where($qb->expr()->eq('uri', $qb->createNamedParameter($taskUri))) + ->andWhere($qb->expr()->eq('principaluri', $qb->createNamedParameter($principal))); + $count = $qb->execute()->fetchColumn(); + + // If the name already exists, add a suffix until you find an available task uri + if ($count > 0) { + $i = 1; + while ($count > 0) { + $newUriName = $taskUri . '-' . $i; + $qb->select('uri'); + $qb->where($qb->expr()->eq('uri', $qb->createNamedParameter($newUriName))); + $qb->andWhere($qb->expr()->eq('principaluri', $qb->createNamedParameter($principal))); + $count = $qb->execute()->fetchColumn(); + $i++; + } + $taskUri = $newUriName; + } + return $taskUri; + } + + + + /** + * @param IOutput $output + */ + public function run(IOutput $output) { + if ($this->config->getAppValue(self::APP_ID, 'CreateTasksHasRun') === 'yes') { + $output->info('Repair step already executed'); + return; + } + //get all principal uri having no task calendar with component as TODO but have personal calendar + $result = $this->getPrincipalUriByCalendar(); + foreach ($result as $row) { + $principal = $row['principaluri']; + $taskUri = $this->getUniqueTaskUri($principal, self::TASKS_CALENDAR_URI); + $this->calDav->createCalendar($principal, $taskUri, [ + '{DAV:}displayname' => self::TASKS_CALENDAR_NAME, + '{http://apple.com/ns/ical/}calendar-color' => $this->themingDefaults->getColorPrimary(), + 'components' => 'VTODO' + ]); + }; + // if everything is done, no need to redo the repair during next upgrade + $this->config->setAppValue(self::APP_ID, 'CreateTasksHasRun', 'yes'); + } +}