Include accept / decline links in CalDAV invitation emails
Signed-off-by: Georg Ehrke <developer@georgehrke.com>
This commit is contained in:
parent
3ff3141a1e
commit
4aa4e4080c
17 changed files with 1344 additions and 9 deletions
|
@ -22,6 +22,7 @@
|
|||
<background-jobs>
|
||||
<job>OCA\DAV\BackgroundJob\CleanupDirectLinksJob</job>
|
||||
<job>OCA\DAV\BackgroundJob\UpdateCalendarResourcesRoomsBackgroundJob</job>
|
||||
<job>OCA\DAV\BackgroundJob\CleanupInvitationTokenJob</job>
|
||||
</background-jobs>
|
||||
|
||||
<repair-steps>
|
||||
|
|
|
@ -25,6 +25,10 @@ return [
|
|||
'routes' => [
|
||||
['name' => 'birthday_calendar#enable', 'url' => '/enableBirthdayCalendar', 'verb' => 'POST'],
|
||||
['name' => 'birthday_calendar#disable', 'url' => '/disableBirthdayCalendar', 'verb' => 'POST'],
|
||||
['name' => 'invitation_response#accept', 'url' => '/invitation/accept/{token}', 'verb' => 'GET'],
|
||||
['name' => 'invitation_response#decline', 'url' => '/invitation/decline/{token}', 'verb' => 'GET'],
|
||||
['name' => 'invitation_response#options', 'url' => '/invitation/moreOptions/{token}', 'verb' => 'GET'],
|
||||
['name' => 'invitation_response#processMoreOptionsResult', 'url' => '/invitation/moreOptions/{token}', 'verb' => 'POST']
|
||||
],
|
||||
'ocs' => [
|
||||
['name' => 'direct#getUrl', 'url' => '/api/v1/direct', 'verb' => 'POST'],
|
||||
|
|
|
@ -12,6 +12,7 @@ return array(
|
|||
'OCA\\DAV\\Avatars\\AvatarNode' => $baseDir . '/../lib/Avatars/AvatarNode.php',
|
||||
'OCA\\DAV\\Avatars\\RootCollection' => $baseDir . '/../lib/Avatars/RootCollection.php',
|
||||
'OCA\\DAV\\BackgroundJob\\CleanupDirectLinksJob' => $baseDir . '/../lib/BackgroundJob/CleanupDirectLinksJob.php',
|
||||
'OCA\\DAV\\BackgroundJob\\CleanupInvitationTokenJob' => $baseDir . '/../lib/BackgroundJob/CleanupInvitationTokenJob.php',
|
||||
'OCA\\DAV\\BackgroundJob\\GenerateBirthdayCalendarBackgroundJob' => $baseDir . '/../lib/BackgroundJob/GenerateBirthdayCalendarBackgroundJob.php',
|
||||
'OCA\\DAV\\BackgroundJob\\UpdateCalendarResourcesRoomsBackgroundJob' => $baseDir . '/../lib/BackgroundJob/UpdateCalendarResourcesRoomsBackgroundJob.php',
|
||||
'OCA\\DAV\\CalDAV\\Activity\\Backend' => $baseDir . '/../lib/CalDAV/Activity/Backend.php',
|
||||
|
@ -33,6 +34,7 @@ return array(
|
|||
'OCA\\DAV\\CalDAV\\CalendarManager' => $baseDir . '/../lib/CalDAV/CalendarManager.php',
|
||||
'OCA\\DAV\\CalDAV\\CalendarObject' => $baseDir . '/../lib/CalDAV/CalendarObject.php',
|
||||
'OCA\\DAV\\CalDAV\\CalendarRoot' => $baseDir . '/../lib/CalDAV/CalendarRoot.php',
|
||||
'OCA\\DAV\\CalDAV\\InvitationResponse\\InvitationResponseServer' => $baseDir . '/../lib/CalDAV/InvitationResponse/InvitationResponseServer.php',
|
||||
'OCA\\DAV\\CalDAV\\Plugin' => $baseDir . '/../lib/CalDAV/Plugin.php',
|
||||
'OCA\\DAV\\CalDAV\\Principal\\Collection' => $baseDir . '/../lib/CalDAV/Principal/Collection.php',
|
||||
'OCA\\DAV\\CalDAV\\Principal\\User' => $baseDir . '/../lib/CalDAV/Principal/User.php',
|
||||
|
@ -117,6 +119,7 @@ return array(
|
|||
'OCA\\DAV\\Connector\\Sabre\\TagsPlugin' => $baseDir . '/../lib/Connector/Sabre/TagsPlugin.php',
|
||||
'OCA\\DAV\\Controller\\BirthdayCalendarController' => $baseDir . '/../lib/Controller/BirthdayCalendarController.php',
|
||||
'OCA\\DAV\\Controller\\DirectController' => $baseDir . '/../lib/Controller/DirectController.php',
|
||||
'OCA\\DAV\\Controller\\InvitationResponseController' => $baseDir . '/../lib/Controller/InvitationResponseController.php',
|
||||
'OCA\\DAV\\DAV\\CustomPropertiesBackend' => $baseDir . '/../lib/DAV/CustomPropertiesBackend.php',
|
||||
'OCA\\DAV\\DAV\\GroupPrincipalBackend' => $baseDir . '/../lib/DAV/GroupPrincipalBackend.php',
|
||||
'OCA\\DAV\\DAV\\PublicAuth' => $baseDir . '/../lib/DAV/PublicAuth.php',
|
||||
|
@ -150,6 +153,7 @@ return array(
|
|||
'OCA\\DAV\\Migration\\Version1004Date20170926103422' => $baseDir . '/../lib/Migration/Version1004Date20170926103422.php',
|
||||
'OCA\\DAV\\Migration\\Version1005Date20180413093149' => $baseDir . '/../lib/Migration/Version1005Date20180413093149.php',
|
||||
'OCA\\DAV\\Migration\\Version1005Date20180530124431' => $baseDir . '/../lib/Migration/Version1005Date20180530124431.php',
|
||||
'OCA\\DAV\\Migration\\Version1006Date20180619154313' => $baseDir . '/../lib/Migration/Version1006Date20180619154313.php',
|
||||
'OCA\\DAV\\RootCollection' => $baseDir . '/../lib/RootCollection.php',
|
||||
'OCA\\DAV\\Server' => $baseDir . '/../lib/Server.php',
|
||||
'OCA\\DAV\\Settings\\CalDAVSettings' => $baseDir . '/../lib/Settings/CalDAVSettings.php',
|
||||
|
|
|
@ -27,6 +27,7 @@ class ComposerStaticInitDAV
|
|||
'OCA\\DAV\\Avatars\\AvatarNode' => __DIR__ . '/..' . '/../lib/Avatars/AvatarNode.php',
|
||||
'OCA\\DAV\\Avatars\\RootCollection' => __DIR__ . '/..' . '/../lib/Avatars/RootCollection.php',
|
||||
'OCA\\DAV\\BackgroundJob\\CleanupDirectLinksJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/CleanupDirectLinksJob.php',
|
||||
'OCA\\DAV\\BackgroundJob\\CleanupInvitationTokenJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/CleanupInvitationTokenJob.php',
|
||||
'OCA\\DAV\\BackgroundJob\\GenerateBirthdayCalendarBackgroundJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/GenerateBirthdayCalendarBackgroundJob.php',
|
||||
'OCA\\DAV\\BackgroundJob\\UpdateCalendarResourcesRoomsBackgroundJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/UpdateCalendarResourcesRoomsBackgroundJob.php',
|
||||
'OCA\\DAV\\CalDAV\\Activity\\Backend' => __DIR__ . '/..' . '/../lib/CalDAV/Activity/Backend.php',
|
||||
|
@ -48,6 +49,7 @@ class ComposerStaticInitDAV
|
|||
'OCA\\DAV\\CalDAV\\CalendarManager' => __DIR__ . '/..' . '/../lib/CalDAV/CalendarManager.php',
|
||||
'OCA\\DAV\\CalDAV\\CalendarObject' => __DIR__ . '/..' . '/../lib/CalDAV/CalendarObject.php',
|
||||
'OCA\\DAV\\CalDAV\\CalendarRoot' => __DIR__ . '/..' . '/../lib/CalDAV/CalendarRoot.php',
|
||||
'OCA\\DAV\\CalDAV\\InvitationResponse\\InvitationResponseServer' => __DIR__ . '/..' . '/../lib/CalDAV/InvitationResponse/InvitationResponseServer.php',
|
||||
'OCA\\DAV\\CalDAV\\Plugin' => __DIR__ . '/..' . '/../lib/CalDAV/Plugin.php',
|
||||
'OCA\\DAV\\CalDAV\\Principal\\Collection' => __DIR__ . '/..' . '/../lib/CalDAV/Principal/Collection.php',
|
||||
'OCA\\DAV\\CalDAV\\Principal\\User' => __DIR__ . '/..' . '/../lib/CalDAV/Principal/User.php',
|
||||
|
@ -132,6 +134,7 @@ class ComposerStaticInitDAV
|
|||
'OCA\\DAV\\Connector\\Sabre\\TagsPlugin' => __DIR__ . '/..' . '/../lib/Connector/Sabre/TagsPlugin.php',
|
||||
'OCA\\DAV\\Controller\\BirthdayCalendarController' => __DIR__ . '/..' . '/../lib/Controller/BirthdayCalendarController.php',
|
||||
'OCA\\DAV\\Controller\\DirectController' => __DIR__ . '/..' . '/../lib/Controller/DirectController.php',
|
||||
'OCA\\DAV\\Controller\\InvitationResponseController' => __DIR__ . '/..' . '/../lib/Controller/InvitationResponseController.php',
|
||||
'OCA\\DAV\\DAV\\CustomPropertiesBackend' => __DIR__ . '/..' . '/../lib/DAV/CustomPropertiesBackend.php',
|
||||
'OCA\\DAV\\DAV\\GroupPrincipalBackend' => __DIR__ . '/..' . '/../lib/DAV/GroupPrincipalBackend.php',
|
||||
'OCA\\DAV\\DAV\\PublicAuth' => __DIR__ . '/..' . '/../lib/DAV/PublicAuth.php',
|
||||
|
@ -165,6 +168,7 @@ class ComposerStaticInitDAV
|
|||
'OCA\\DAV\\Migration\\Version1004Date20170926103422' => __DIR__ . '/..' . '/../lib/Migration/Version1004Date20170926103422.php',
|
||||
'OCA\\DAV\\Migration\\Version1005Date20180413093149' => __DIR__ . '/..' . '/../lib/Migration/Version1005Date20180413093149.php',
|
||||
'OCA\\DAV\\Migration\\Version1005Date20180530124431' => __DIR__ . '/..' . '/../lib/Migration/Version1005Date20180530124431.php',
|
||||
'OCA\\DAV\\Migration\\Version1006Date20180619154313' => __DIR__ . '/..' . '/../lib/Migration/Version1006Date20180619154313.php',
|
||||
'OCA\\DAV\\RootCollection' => __DIR__ . '/..' . '/../lib/RootCollection.php',
|
||||
'OCA\\DAV\\Server' => __DIR__ . '/..' . '/../lib/Server.php',
|
||||
'OCA\\DAV\\Settings\\CalDAVSettings' => __DIR__ . '/..' . '/../lib/Settings/CalDAVSettings.php',
|
||||
|
|
78
apps/dav/css/schedule-response.css
Normal file
78
apps/dav/css/schedule-response.css
Normal file
|
@ -0,0 +1,78 @@
|
|||
/* Database selector on install page */
|
||||
form #selectPartStatForm {
|
||||
text-align:center;
|
||||
white-space: nowrap;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
form #selectPartStatForm .info {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
form #selectPartStatForm input[type="radio"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
form #selectPartStatForm input[type="radio"]:checked+label {
|
||||
background-color: #e8e8e8;
|
||||
}
|
||||
|
||||
form #selectPartStatForm input[type="radio"]:checked ~ form fieldset#more_options {
|
||||
display: none;
|
||||
}
|
||||
|
||||
form #selectPartStatForm label {
|
||||
color: #000;
|
||||
background-color: #f8f8f8;
|
||||
position: static;
|
||||
margin: 0 -3px 5px;
|
||||
cursor:pointer;
|
||||
border: 1px solid #ddd;
|
||||
display: inline-block;
|
||||
padding: 0;
|
||||
line-height: normal;
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
form #selectPartStatForm label:first-of-type {
|
||||
border-top-left-radius: 4px;
|
||||
border-bottom-left-radius: 4px;
|
||||
}
|
||||
|
||||
form #selectPartStatForm label:last-of-type {
|
||||
border-top-right-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
}
|
||||
|
||||
form #selectPartStatForm label span {
|
||||
cursor: pointer;
|
||||
padding: 10px 20px;
|
||||
display: block;
|
||||
line-height: normal;
|
||||
}
|
||||
form #selectPartStatForm label.ui-state-hover,
|
||||
form #selectPartStatForm label.ui-state-active {
|
||||
color:#000;
|
||||
background-color:#e8e8e8;
|
||||
}
|
||||
|
||||
form input[type="number"] {
|
||||
width: 249px;
|
||||
background: #fff;
|
||||
color: #555;
|
||||
cursor: text;
|
||||
font-family: inherit;
|
||||
-webkit-appearance: textfield;
|
||||
-moz-appearance: textfield;
|
||||
box-sizing: content-box;
|
||||
border: none;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
form input[type="submit"] {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
padding: 11px 20px 9px
|
||||
}
|
3
apps/dav/js/schedule-response.js
Normal file
3
apps/dav/js/schedule-response.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
// $(document).ready(function() {
|
||||
// $('#selectPartStatForm').buttonset();
|
||||
// });
|
53
apps/dav/lib/BackgroundJob/CleanupInvitationTokenJob.php
Normal file
53
apps/dav/lib/BackgroundJob/CleanupInvitationTokenJob.php
Normal file
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright 2018, Georg Ehrke <oc.list@georgehrke.com>
|
||||
*
|
||||
* @author Georg Ehrke <oc.list@georgehrke.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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\DAV\BackgroundJob;
|
||||
|
||||
use OC\BackgroundJob\TimedJob;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\IDBConnection;
|
||||
|
||||
class CleanupInvitationTokenJob extends TimedJob {
|
||||
|
||||
/** @var IDBConnection */
|
||||
private $db;
|
||||
|
||||
/** @var ITimeFactory */
|
||||
private $timeFactory;
|
||||
|
||||
public function __construct(IDBConnection $db, ITimeFactory $timeFactory) {
|
||||
$this->db = $db;
|
||||
$this->timeFactory = $timeFactory;
|
||||
|
||||
$this->setInterval(60 * 60 * 24);
|
||||
}
|
||||
|
||||
public function run($argument) {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->delete('calendar_invitation_tokens')
|
||||
->where($query->expr()->lt('expiration',
|
||||
$query->createNamedParameter($this->timeFactory->getTime())))
|
||||
->execute();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2018, Georg Ehrke.
|
||||
*
|
||||
* @author Georg Ehrke <oc.list@georgehrke.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/>
|
||||
*
|
||||
*/
|
||||
namespace OCA\DAV\CalDAV\InvitationResponse;
|
||||
|
||||
use OCA\DAV\Connector\Sabre\BlockLegacyClientPlugin;
|
||||
use OCA\DAV\Connector\Sabre\CachingTree;
|
||||
use OCA\DAV\Connector\Sabre\DavAclPlugin;
|
||||
use OCA\DAV\Connector\Sabre\AnonymousOptionsPlugin;
|
||||
use OCA\DAV\RootCollection;
|
||||
use OCP\SabrePluginEvent;
|
||||
use Sabre\DAV\Auth\Plugin;
|
||||
use OCA\DAV\AppInfo\PluginManager;
|
||||
use Sabre\VObject\ITip\Message;
|
||||
|
||||
class InvitationResponseServer {
|
||||
|
||||
/** @var \OCA\DAV\Connector\Sabre\Server */
|
||||
public $server;
|
||||
|
||||
/**
|
||||
* InvitationResponseServer constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
$baseUri = \OC::$WEBROOT . '/remote.php/dav/';
|
||||
$logger = \OC::$server->getLogger();
|
||||
$dispatcher = \OC::$server->getEventDispatcher();
|
||||
|
||||
$root = new RootCollection();
|
||||
$this->server = new \OCA\DAV\Connector\Sabre\Server(new CachingTree($root));
|
||||
|
||||
// Add maintenance plugin
|
||||
$this->server->addPlugin(new \OCA\DAV\Connector\Sabre\MaintenancePlugin(\OC::$server->getConfig()));
|
||||
|
||||
// Set URL explicitly due to reverse-proxy situations
|
||||
$this->server->httpRequest->setUrl($baseUri);
|
||||
$this->server->setBaseUri($baseUri);
|
||||
|
||||
$this->server->addPlugin(new BlockLegacyClientPlugin(\OC::$server->getConfig()));
|
||||
$this->server->addPlugin(new AnonymousOptionsPlugin());
|
||||
$this->server->addPlugin(new class() extends Plugin {
|
||||
public function getCurrentPrincipal() {
|
||||
return 'principals/system/public';
|
||||
}
|
||||
});
|
||||
|
||||
// allow setup of additional auth backends
|
||||
$event = new SabrePluginEvent($this->server);
|
||||
$dispatcher->dispatch('OCA\DAV\Connector\Sabre::authInit', $event);
|
||||
|
||||
$this->server->addPlugin(new \OCA\DAV\Connector\Sabre\ExceptionLoggerPlugin('webdav', $logger));
|
||||
$this->server->addPlugin(new \OCA\DAV\Connector\Sabre\LockPlugin());
|
||||
$this->server->addPlugin(new \Sabre\DAV\Sync\Plugin());
|
||||
|
||||
// acl
|
||||
$acl = new DavAclPlugin();
|
||||
$acl->principalCollectionSet = [
|
||||
'principals/users', 'principals/groups'
|
||||
];
|
||||
$acl->defaultUsernamePath = 'principals/users';
|
||||
$this->server->addPlugin($acl);
|
||||
|
||||
// calendar plugins
|
||||
$this->server->addPlugin(new \OCA\DAV\CalDAV\Plugin());
|
||||
$this->server->addPlugin(new \Sabre\CalDAV\ICSExportPlugin());
|
||||
$this->server->addPlugin(new \OCA\DAV\CalDAV\Schedule\Plugin());
|
||||
$this->server->addPlugin(new \Sabre\CalDAV\Subscriptions\Plugin());
|
||||
$this->server->addPlugin(new \Sabre\CalDAV\Notifications\Plugin());
|
||||
//$this->server->addPlugin(new \OCA\DAV\DAV\Sharing\Plugin($authBackend, \OC::$server->getRequest()));
|
||||
$this->server->addPlugin(new \OCA\DAV\CalDAV\Publishing\PublishPlugin(
|
||||
\OC::$server->getConfig(),
|
||||
\OC::$server->getURLGenerator()
|
||||
));
|
||||
|
||||
// wait with registering these until auth is handled and the filesystem is setup
|
||||
$this->server->on('beforeMethod', function () use ($root) {
|
||||
// register plugins from apps
|
||||
$pluginManager = new PluginManager(
|
||||
\OC::$server,
|
||||
\OC::$server->getAppManager()
|
||||
);
|
||||
foreach ($pluginManager->getAppPlugins() as $appPlugin) {
|
||||
$this->server->addPlugin($appPlugin);
|
||||
}
|
||||
foreach ($pluginManager->getAppCollections() as $appCollection) {
|
||||
$root->addChild($appCollection);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Message $iTipMessage
|
||||
* @return void
|
||||
*/
|
||||
public function handleITipMessage(Message $iTipMessage) {
|
||||
/** @var \OCA\DAV\CalDAV\Schedule\Plugin $schedulingPlugin */
|
||||
$schedulingPlugin = $this->server->getPlugin('caldav-schedule');
|
||||
$schedulingPlugin->scheduleLocalDelivery($iTipMessage);
|
||||
}
|
||||
}
|
|
@ -28,12 +28,14 @@ namespace OCA\DAV\CalDAV\Schedule;
|
|||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\Defaults;
|
||||
use OCP\IConfig;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\IL10N;
|
||||
use OCP\ILogger;
|
||||
use OCP\IURLGenerator;
|
||||
use OCP\L10N\IFactory as L10NFactory;
|
||||
use OCP\Mail\IEMailTemplate;
|
||||
use OCP\Mail\IMailer;
|
||||
use OCP\Security\ISecureRandom;
|
||||
use Sabre\CalDAV\Schedule\IMipPlugin as SabreIMipPlugin;
|
||||
use Sabre\VObject\Component\VCalendar;
|
||||
use Sabre\VObject\Component\VEvent;
|
||||
|
@ -79,6 +81,12 @@ class IMipPlugin extends SabreIMipPlugin {
|
|||
/** @var IURLGenerator */
|
||||
private $urlGenerator;
|
||||
|
||||
/** @var ISecureRandom */
|
||||
private $random;
|
||||
|
||||
/** @var IDBConnection */
|
||||
private $db;
|
||||
|
||||
/** @var Defaults */
|
||||
private $defaults;
|
||||
|
||||
|
@ -96,9 +104,14 @@ class IMipPlugin extends SabreIMipPlugin {
|
|||
* @param L10NFactory $l10nFactory
|
||||
* @param IUrlGenerator $urlGenerator
|
||||
* @param Defaults $defaults
|
||||
* @param ISecureRandom $random
|
||||
* @param IDBConnection $db
|
||||
* @param string $userId
|
||||
*/
|
||||
public function __construct(IConfig $config, IMailer $mailer, ILogger $logger, ITimeFactory $timeFactory, L10NFactory $l10nFactory, IURLGenerator $urlGenerator, Defaults $defaults, $userId) {
|
||||
public function __construct(IConfig $config, IMailer $mailer, ILogger $logger,
|
||||
ITimeFactory $timeFactory, L10NFactory $l10nFactory,
|
||||
IURLGenerator $urlGenerator, Defaults $defaults,
|
||||
ISecureRandom $random, IDBConnection $db, $userId) {
|
||||
parent::__construct('');
|
||||
$this->userId = $userId;
|
||||
$this->config = $config;
|
||||
|
@ -107,6 +120,8 @@ class IMipPlugin extends SabreIMipPlugin {
|
|||
$this->timeFactory = $timeFactory;
|
||||
$this->l10nFactory = $l10nFactory;
|
||||
$this->urlGenerator = $urlGenerator;
|
||||
$this->random = $random;
|
||||
$this->db = $db;
|
||||
$this->defaults = $defaults;
|
||||
}
|
||||
|
||||
|
@ -138,7 +153,9 @@ class IMipPlugin extends SabreIMipPlugin {
|
|||
}
|
||||
|
||||
// don't send out mails for events that already took place
|
||||
if ($this->isEventInThePast($iTipMessage->message)) {
|
||||
$lastOccurrence = $this->getLastOccurrence($iTipMessage->message);
|
||||
$currentTime = $this->timeFactory->getTime();
|
||||
if ($lastOccurrence < $currentTime) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -222,6 +239,7 @@ class IMipPlugin extends SabreIMipPlugin {
|
|||
$meetingAttendeeName, $meetingInviteeName);
|
||||
$this->addBulletList($template, $l10n, $meetingWhen, $meetingLocation,
|
||||
$meetingDescription, $meetingUrl);
|
||||
$this->addResponseButtons($template, $l10n, $iTipMessage, $lastOccurrence);
|
||||
|
||||
$template->addFooter();
|
||||
$message->useTemplate($template);
|
||||
|
@ -249,9 +267,9 @@ class IMipPlugin extends SabreIMipPlugin {
|
|||
/**
|
||||
* check if event took place in the past already
|
||||
* @param VCalendar $vObject
|
||||
* @return bool
|
||||
* @return int
|
||||
*/
|
||||
private function isEventInThePast(VCalendar $vObject) {
|
||||
private function getLastOccurrence(VCalendar $vObject) {
|
||||
/** @var VEvent $component */
|
||||
$component = $vObject->VEVENT;
|
||||
|
||||
|
@ -291,8 +309,7 @@ class IMipPlugin extends SabreIMipPlugin {
|
|||
}
|
||||
}
|
||||
|
||||
$currentTime = $this->timeFactory->getTime();
|
||||
return $lastOccurrence < $currentTime;
|
||||
return $lastOccurrence;
|
||||
}
|
||||
|
||||
|
||||
|
@ -459,6 +476,38 @@ class IMipPlugin extends SabreIMipPlugin {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param IEMailTemplate $template
|
||||
* @param IL10N $l10n
|
||||
* @param Message $iTipMessage
|
||||
* @param int $lastOccurrence
|
||||
*/
|
||||
private function addResponseButtons(IEMailTemplate $template, IL10N $l10n,
|
||||
Message $iTipMessage, $lastOccurrence) {
|
||||
$token = $this->createInvitationToken($iTipMessage, $lastOccurrence);
|
||||
|
||||
$template->addBodyButtonGroup(
|
||||
$l10n->t('Accept'),
|
||||
$this->urlGenerator->linkToRouteAbsolute('dav.invitation_response.accept', [
|
||||
'token' => $token,
|
||||
]),
|
||||
$l10n->t('Decline'),
|
||||
$this->urlGenerator->linkToRouteAbsolute('dav.invitation_response.decline', [
|
||||
'token' => $token,
|
||||
])
|
||||
);
|
||||
|
||||
$moreOptionsURL = $this->urlGenerator->linkToRouteAbsolute('dav.invitation_response.options', [
|
||||
'token' => $token,
|
||||
]);
|
||||
$html = vsprintf('<small><a href="%s">%s</a></small>', [
|
||||
$moreOptionsURL, $l10n->t('More options ...')
|
||||
]);
|
||||
$text = $l10n->t('More options at %s', [$moreOptionsURL]);
|
||||
|
||||
$template->addBodyText($html, $text);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @return string
|
||||
|
@ -468,4 +517,37 @@ class IMipPlugin extends SabreIMipPlugin {
|
|||
$this->urlGenerator->imagePath('core', $path)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Message $iTipMessage
|
||||
* @param int $lastOccurrence
|
||||
* @return string
|
||||
*/
|
||||
private function createInvitationToken(Message $iTipMessage, $lastOccurrence):string {
|
||||
$token = $this->random->generate(60, ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS);
|
||||
|
||||
/** @var VEvent $vevent */
|
||||
$vevent = $iTipMessage->message->VEVENT;
|
||||
$attendee = $iTipMessage->recipient;
|
||||
$organizer = $iTipMessage->sender;
|
||||
$sequence = $iTipMessage->sequence;
|
||||
$recurrenceId = isset($vevent->{'RECURRENCE-ID'}) ?
|
||||
$vevent->{'RECURRENCE-ID'}->serialize() : null;
|
||||
$uid = $vevent->{'UID'};
|
||||
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->insert('calendar_invitation_tokens')
|
||||
->values([
|
||||
'token' => $query->createNamedParameter($token),
|
||||
'attendee' => $query->createNamedParameter($attendee),
|
||||
'organizer' => $query->createNamedParameter($organizer),
|
||||
'sequence' => $query->createNamedParameter($sequence),
|
||||
'recurrenceid' => $query->createNamedParameter($recurrenceId),
|
||||
'expiration' => $query->createNamedParameter($lastOccurrence),
|
||||
'uid' => $query->createNamedParameter($uid)
|
||||
])
|
||||
->execute();
|
||||
|
||||
return $token;
|
||||
}
|
||||
}
|
||||
|
|
236
apps/dav/lib/Controller/InvitationResponseController.php
Normal file
236
apps/dav/lib/Controller/InvitationResponseController.php
Normal file
|
@ -0,0 +1,236 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright 2018, Georg Ehrke <oc.list@georgehrke.com>
|
||||
*
|
||||
* @author Georg Ehrke <oc.list@georgehrke.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/>.
|
||||
*
|
||||
*/
|
||||
namespace OCA\DAV\Controller;
|
||||
|
||||
use OCA\DAV\CalDAV\InvitationResponse\InvitationResponseServer;
|
||||
use OCP\AppFramework\Controller;
|
||||
use OCP\AppFramework\Http\TemplateResponse;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\IRequest;
|
||||
use Sabre\VObject\ITip\Message;
|
||||
use Sabre\VObject\Reader;
|
||||
|
||||
class InvitationResponseController extends Controller {
|
||||
|
||||
/** @var IDBConnection */
|
||||
private $db;
|
||||
|
||||
/** @var ITimeFactory */
|
||||
private $timeFactory;
|
||||
|
||||
/** @var InvitationResponseServer */
|
||||
private $responseServer;
|
||||
|
||||
/**
|
||||
* InvitationResponseController constructor.
|
||||
*
|
||||
* @param string $appName
|
||||
* @param IRequest $request
|
||||
* @param IDBConnection $db
|
||||
* @param ITimeFactory $timeFactory
|
||||
* @param InvitationResponseServer $responseServer
|
||||
*/
|
||||
public function __construct(string $appName, IRequest $request,
|
||||
IDBConnection $db, ITimeFactory $timeFactory,
|
||||
InvitationResponseServer $responseServer) {
|
||||
parent::__construct($appName, $request);
|
||||
$this->db = $db;
|
||||
$this->timeFactory = $timeFactory;
|
||||
$this->responseServer = $responseServer;
|
||||
// Don't run `$server->exec()`, because we just need access to the
|
||||
// fully initialized schedule plugin, but we don't want Sabre/DAV
|
||||
// to actually handle and reply to the request
|
||||
}
|
||||
|
||||
/**
|
||||
* @PublicPage
|
||||
* @NoCSRFRequired
|
||||
*
|
||||
* @param string $token
|
||||
* @return TemplateResponse
|
||||
*/
|
||||
public function accept(string $token):TemplateResponse {
|
||||
$row = $this->getTokenInformation($token);
|
||||
if (!$row) {
|
||||
return new TemplateResponse($this->appName, 'schedule-response-error', [], 'guest');
|
||||
}
|
||||
|
||||
$iTipMessage = $this->buildITipResponse($row, 'ACCEPTED');
|
||||
$this->responseServer->handleITipMessage($iTipMessage);
|
||||
if ($iTipMessage->getScheduleStatus() === '1.2') {
|
||||
return new TemplateResponse($this->appName, 'schedule-response-success', [], 'guest');
|
||||
}
|
||||
|
||||
return new TemplateResponse($this->appName, 'schedule-response-error', [
|
||||
'organizer' => $row['organizer'],
|
||||
], 'guest');
|
||||
}
|
||||
|
||||
/**
|
||||
* @PublicPage
|
||||
* @NoCSRFRequired
|
||||
*
|
||||
* @param string $token
|
||||
* @return TemplateResponse
|
||||
*/
|
||||
public function decline(string $token):TemplateResponse {
|
||||
$row = $this->getTokenInformation($token);
|
||||
if (!$row) {
|
||||
return new TemplateResponse($this->appName, 'schedule-response-error', [], 'guest');
|
||||
}
|
||||
|
||||
$iTipMessage = $this->buildITipResponse($row, 'DECLINED');
|
||||
$this->responseServer->handleITipMessage($iTipMessage);
|
||||
|
||||
if ($iTipMessage->getScheduleStatus() === '1.2') {
|
||||
return new TemplateResponse($this->appName, 'schedule-response-success', [], 'guest');
|
||||
}
|
||||
|
||||
return new TemplateResponse($this->appName, 'schedule-response-error', [
|
||||
'organizer' => $row['organizer'],
|
||||
], 'guest');
|
||||
}
|
||||
|
||||
/**
|
||||
* @PublicPage
|
||||
* @NoCSRFRequired
|
||||
*
|
||||
* @param string $token
|
||||
* @return TemplateResponse
|
||||
*/
|
||||
public function options(string $token):TemplateResponse {
|
||||
return new TemplateResponse($this->appName, 'schedule-response-options', [
|
||||
'token' => $token
|
||||
], 'guest');
|
||||
}
|
||||
|
||||
/**
|
||||
* @PublicPage
|
||||
* @NoCSRFRequired
|
||||
*
|
||||
* @param string $token
|
||||
*
|
||||
* @return TemplateResponse
|
||||
*/
|
||||
public function processMoreOptionsResult(string $token):TemplateResponse {
|
||||
$partstat = $this->request->getParam('partStat');
|
||||
$guests = (int) $this->request->getParam('guests');
|
||||
$comment = $this->request->getParam('comment');
|
||||
|
||||
$row = $this->getTokenInformation($token);
|
||||
if (!$row || !\in_array($partstat, ['ACCEPTED', 'DECLINED', 'TENTATIVE'])) {
|
||||
return new TemplateResponse($this->appName, 'schedule-response-error', [], 'guest');
|
||||
}
|
||||
|
||||
$iTipMessage = $this->buildITipResponse($row, $partstat, $guests, $comment);
|
||||
$this->responseServer->handleITipMessage($iTipMessage);
|
||||
if ($iTipMessage->getScheduleStatus() === '1.2') {
|
||||
return new TemplateResponse($this->appName, 'schedule-response-success', [], 'guest');
|
||||
}
|
||||
|
||||
return new TemplateResponse($this->appName, 'schedule-response-error', [
|
||||
'organizer' => $row['organizer'],
|
||||
], 'guest');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $token
|
||||
* @return array|null
|
||||
*/
|
||||
private function getTokenInformation(string $token) {
|
||||
$query = $this->db->getQueryBuilder();
|
||||
$query->select('*')
|
||||
->from('calendar_invitation_tokens')
|
||||
->where($query->expr()->eq('token', $query->createNamedParameter($token)));
|
||||
$stmt = $query->execute();
|
||||
$row = $stmt->fetch(\PDO::FETCH_ASSOC);
|
||||
|
||||
if(!$row) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$currentTime = $this->timeFactory->getTime();
|
||||
if (((int) $row['expiration']) < $currentTime) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $row;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $row
|
||||
* @param string $partStat participation status of attendee - SEE RFC 5545
|
||||
* @param int|null $guests
|
||||
* @param string|null $comment
|
||||
* @return Message
|
||||
*/
|
||||
private function buildITipResponse(array $row, string $partStat, int $guests=null,
|
||||
string $comment=null):Message {
|
||||
$iTipMessage = new Message();
|
||||
$iTipMessage->uid = $row['uid'];
|
||||
$iTipMessage->component = 'VEVENT';
|
||||
$iTipMessage->method = 'REPLY';
|
||||
$iTipMessage->sequence = $row['sequence'];
|
||||
$iTipMessage->sender = $row['attendee'];
|
||||
$iTipMessage->recipient = $row['organizer'];
|
||||
|
||||
$message = <<<EOF
|
||||
BEGIN:VCALENDAR
|
||||
PRODID:-//Nextcloud/Nextcloud CalDAV Server//EN
|
||||
METHOD:REPLY
|
||||
VERSION:2.0
|
||||
BEGIN:VEVENT
|
||||
ATTENDEE;PARTSTAT=%s:%s
|
||||
ORGANIZER:%s
|
||||
UID:%s
|
||||
SEQUENCE:%s
|
||||
REQUEST-STATUS:2.0;Success
|
||||
%sEND:VEVENT
|
||||
END:VCALENDAR
|
||||
EOF;
|
||||
|
||||
$vObject = Reader::read(vsprintf($message, [
|
||||
$partStat, $row['attendee'], $row['organizer'],
|
||||
$row['uid'], $row['sequence'] ?? 0, $row['recurrenceid'] ?? ''
|
||||
]));
|
||||
$vEvent = $vObject->{'VEVENT'};
|
||||
/** @var \Sabre\VObject\Property\ICalendar\CalAddress $attendee */
|
||||
$attendee = $vEvent->{'ATTENDEE'};
|
||||
|
||||
$vEvent->DTSTAMP = date('Ymd\\THis\\Z', $this->timeFactory->getTime());
|
||||
|
||||
if ($comment) {
|
||||
$attendee->add('X-RESPONSE-COMMENT', $comment);
|
||||
$vEvent->add('COMMENT', $comment);
|
||||
}
|
||||
if ($guests) {
|
||||
$attendee->add('X-NUM-GUESTS', $guests);
|
||||
}
|
||||
|
||||
$iTipMessage->message = $vObject;
|
||||
|
||||
return $iTipMessage;
|
||||
}
|
||||
}
|
71
apps/dav/lib/Migration/Version1006Date20180619154313.php
Normal file
71
apps/dav/lib/Migration/Version1006Date20180619154313.php
Normal file
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
namespace OCA\DAV\Migration;
|
||||
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use OCP\DB\ISchemaWrapper;
|
||||
use OCP\Migration\SimpleMigrationStep;
|
||||
use OCP\Migration\IOutput;
|
||||
|
||||
/**
|
||||
* Auto-generated migration step: Please modify to your needs!
|
||||
*/
|
||||
class Version1006Date20180619154313 extends SimpleMigrationStep {
|
||||
|
||||
/**
|
||||
* @param IOutput $output
|
||||
* @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
|
||||
* @param array $options
|
||||
* @return null|ISchemaWrapper
|
||||
* @since 13.0.0
|
||||
*/
|
||||
public function changeSchema(IOutput $output, \Closure $schemaClosure, array $options) {
|
||||
/** @var ISchemaWrapper $schema */
|
||||
$schema = $schemaClosure();
|
||||
|
||||
if (!$schema->hasTable('calendar_invitation_tokens')) {
|
||||
$table = $schema->createTable('calendar_invitation_tokens');
|
||||
|
||||
$table->addColumn('id', Type::BIGINT, [
|
||||
'autoincrement' => true,
|
||||
'notnull' => true,
|
||||
'length' => 11,
|
||||
'unsigned' => true,
|
||||
]);
|
||||
$table->addColumn('uid', Type::STRING, [
|
||||
'notnull' => true,
|
||||
'length' => 255,
|
||||
]);
|
||||
$table->addColumn('recurrenceid', Type::STRING, [
|
||||
'notnull' => false,
|
||||
'length' => 255,
|
||||
]);
|
||||
$table->addColumn('attendee', Type::STRING, [
|
||||
'notnull' => true,
|
||||
'length' => 255,
|
||||
]);
|
||||
$table->addColumn('organizer', Type::STRING, [
|
||||
'notnull' => true,
|
||||
'length' => 255,
|
||||
]);
|
||||
$table->addColumn('sequence', Type::BIGINT, [
|
||||
'notnull' => false,
|
||||
'length' => 11,
|
||||
'unsigned' => true,
|
||||
]);
|
||||
$table->addColumn('token', Type::STRING, [
|
||||
'notnull' => true,
|
||||
'length' => 60,
|
||||
]);
|
||||
$table->addColumn('expiration', Type::BIGINT, [
|
||||
'notnull' => true,
|
||||
'length' => 11,
|
||||
'unsigned' => true,
|
||||
]);
|
||||
|
||||
$table->setPrimaryKey(['id'], 'calendar_invitation_tokens_id_idx');
|
||||
$table->addIndex(['token'], 'calendar_invitation_tokens_token_idx');
|
||||
|
||||
return $schema;
|
||||
}
|
||||
}
|
||||
}
|
7
apps/dav/templates/schedule-response-error.php
Normal file
7
apps/dav/templates/schedule-response-error.php
Normal file
|
@ -0,0 +1,7 @@
|
|||
<div class="update">
|
||||
<p class="message"><?php p($l->t('There was an error updating your attendance status.'));?></p>
|
||||
<p class="message"><?php p($l->t('Please contact the organizer directly.'));?></p>
|
||||
<?php if(isset($_['organizer'])): ?>
|
||||
<p class="message"><a href="<?php p($_['organizer']) ?>"><?php p(substr($_['organizer'], 7)) ?></a></p>
|
||||
<?php endif; ?>
|
||||
</div>
|
35
apps/dav/templates/schedule-response-options.php
Normal file
35
apps/dav/templates/schedule-response-options.php
Normal file
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
style('dav', 'schedule-response');
|
||||
//script('dav', 'schedule-response');
|
||||
?>
|
||||
|
||||
<div class="update">
|
||||
<form action="" method="post">
|
||||
<fieldset id="partStat">
|
||||
<h2><?php p($l->t('Are you accepting the invitation?')); ?></h2>
|
||||
<div id="selectPartStatForm">
|
||||
<input type="radio" id="partStatAccept" name="partStat" value="ACCEPTED" checked />
|
||||
<label for="partStatAccept">
|
||||
<span><?php p($l->t('Accept')); ?></span>
|
||||
</label>
|
||||
|
||||
<input type="radio" id="partStatTentative" name="partStat" value="TENTATIVE" />
|
||||
<label for="partStatTentative">
|
||||
<span><?php p($l->t('Tentative')); ?></span>
|
||||
</label>
|
||||
|
||||
<input type="radio" class="declined" id="partStatDeclined" name="partStat" value="DECLINED" />
|
||||
<label for="partStatDeclined">
|
||||
<span><?php p($l->t('Decline')); ?></span>
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset id="more_options">
|
||||
<input type="number" min="0" name="guests" placeholder="Guests" />
|
||||
<input type="text" name="comment" placeholder="Comment" />
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<input type="submit" value="<?php p($l->t('Save'));?>">
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
4
apps/dav/templates/schedule-response-success.php
Normal file
4
apps/dav/templates/schedule-response-success.php
Normal file
|
@ -0,0 +1,4 @@
|
|||
<div class="update" style="justify-content: space-around; display: flex;">
|
||||
<span class="icon icon-checkmark-white"></span>
|
||||
<p class="message"><?php p($l->t('Your attendance was updated successfully.'));?></p>
|
||||
</div>
|
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright 2018, Georg Ehrke <oc.list@georgehrke.com>
|
||||
*
|
||||
* @author Georg Ehrke <oc.list@georgehrke.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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\DAV\Tests\unit\BackgroundJob;
|
||||
|
||||
use OCA\DAV\BackgroundJob\CleanupInvitationTokenJob;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\IDBConnection;
|
||||
use Test\TestCase;
|
||||
|
||||
class CleanupInvitationTokenJobTest extends TestCase {
|
||||
|
||||
/** @var IDBConnection | \PHPUnit_Framework_MockObject_MockObject */
|
||||
private $dbConnection;
|
||||
|
||||
/** @var ITimeFactory | \PHPUnit_Framework_MockObject_MockObject */
|
||||
private $timeFactory;
|
||||
|
||||
/** @var \OCA\DAV\BackgroundJob\GenerateBirthdayCalendarBackgroundJob */
|
||||
private $backgroundJob;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->dbConnection = $this->createMock(IDBConnection::class);
|
||||
$this->timeFactory = $this->createMock(ITimeFactory::class);
|
||||
|
||||
$this->backgroundJob = new CleanupInvitationTokenJob(
|
||||
$this->dbConnection, $this->timeFactory);
|
||||
}
|
||||
|
||||
public function testRun() {
|
||||
$this->timeFactory->expects($this->once())
|
||||
->method('getTime')
|
||||
->with()
|
||||
->will($this->returnValue(1337));
|
||||
|
||||
$queryBuilder = $this->createMock(IQueryBuilder::class);
|
||||
$expr = $this->createMock(\OCP\DB\QueryBuilder\IExpressionBuilder::class);
|
||||
$stmt = $this->createMock(\Doctrine\DBAL\Driver\Statement::class);
|
||||
|
||||
$this->dbConnection->expects($this->once())
|
||||
->method('getQueryBuilder')
|
||||
->with()
|
||||
->will($this->returnValue($queryBuilder));
|
||||
$queryBuilder->method('expr')
|
||||
->will($this->returnValue($expr));
|
||||
$queryBuilder->method('createNamedParameter')
|
||||
->will($this->returnValueMap([
|
||||
[1337, \PDO::PARAM_STR, null, 'namedParameter1337']
|
||||
]));
|
||||
|
||||
$expr->expects($this->once())
|
||||
->method('lt')
|
||||
->with('expiration', 'namedParameter1337')
|
||||
->will($this->returnValue('LT STATEMENT'));
|
||||
|
||||
$this->dbConnection->expects($this->once())
|
||||
->method('getQueryBuilder')
|
||||
->with()
|
||||
->will($this->returnValue($queryBuilder));
|
||||
|
||||
$queryBuilder->expects($this->at(0))
|
||||
->method('delete')
|
||||
->with('calendar_invitation_tokens')
|
||||
->will($this->returnValue($queryBuilder));
|
||||
$queryBuilder->expects($this->at(3))
|
||||
->method('where')
|
||||
->with('LT STATEMENT')
|
||||
->will($this->returnValue($queryBuilder));
|
||||
$queryBuilder->expects($this->at(4))
|
||||
->method('execute')
|
||||
->with()
|
||||
->will($this->returnValue($stmt));
|
||||
|
||||
$this->backgroundJob->run([]);
|
||||
}
|
||||
}
|
|
@ -29,8 +29,10 @@ namespace OCA\DAV\Tests\unit\CalDAV\Schedule;
|
|||
use OC\Mail\Mailer;
|
||||
use OCA\DAV\CalDAV\Schedule\IMipPlugin;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\Defaults;
|
||||
use OCP\IConfig;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\IL10N;
|
||||
use OCP\ILogger;
|
||||
use OCP\IURLGenerator;
|
||||
|
@ -39,6 +41,7 @@ use OCP\Mail\IAttachment;
|
|||
use OCP\Mail\IEMailTemplate;
|
||||
use OCP\Mail\IMailer;
|
||||
use OCP\Mail\IMessage;
|
||||
use OCP\Security\ISecureRandom;
|
||||
use Sabre\VObject\Component\VCalendar;
|
||||
use Sabre\VObject\ITip\Message;
|
||||
use Test\TestCase;
|
||||
|
@ -70,13 +73,38 @@ class IMipPluginTest extends TestCase {
|
|||
$l10nFactory->method('get')->willReturn($l10n);
|
||||
/** @var IURLGenerator | \PHPUnit_Framework_MockObject_MockObject $urlGenerator */
|
||||
$urlGenerator = $this->createMock(IURLGenerator::class);
|
||||
/** @var IDBConnection | \PHPUnit_Framework_MockObject_MockObject $db */
|
||||
$db = $this->createMock(IDBConnection::class);
|
||||
/** @var ISecureRandom | \PHPUnit_Framework_MockObject_MockObject $random */
|
||||
$random = $this->createMock(ISecureRandom::class);
|
||||
/** @var Defaults | \PHPUnit_Framework_MockObject_MockObject $defaults */
|
||||
$defaults = $this->createMock(Defaults::class);
|
||||
$defaults->expects($this->once())
|
||||
->method('getName')
|
||||
->will($this->returnValue('Instance Name 123'));
|
||||
|
||||
$plugin = new IMipPlugin($config, $mailer, $logger, $timeFactory, $l10nFactory, $urlGenerator, $defaults, 'user123');
|
||||
$random->expects($this->once())
|
||||
->method('generate')
|
||||
->with(60, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789')
|
||||
->will($this->returnValue('random_token'));
|
||||
|
||||
$queryBuilder = $this->createMock(IQueryBuilder::class);
|
||||
|
||||
$db->expects($this->once())
|
||||
->method('getQueryBuilder')
|
||||
->with()
|
||||
->will($this->returnValue($queryBuilder));
|
||||
$queryBuilder->expects($this->at(0))
|
||||
->method('insert')
|
||||
->with('calendar_invitation_tokens')
|
||||
->will($this->returnValue($queryBuilder));
|
||||
$queryBuilder->expects($this->at(8))
|
||||
->method('values')
|
||||
->will($this->returnValue($queryBuilder));
|
||||
$queryBuilder->expects($this->at(9))
|
||||
->method('execute');
|
||||
|
||||
$plugin = new IMipPlugin($config, $mailer, $logger, $timeFactory, $l10nFactory, $urlGenerator, $defaults, $random, $db, 'user123');
|
||||
$message = new Message();
|
||||
$message->method = 'REQUEST';
|
||||
$message->message = new VCalendar();
|
||||
|
@ -128,10 +156,35 @@ class IMipPluginTest extends TestCase {
|
|||
$l10nFactory->method('get')->willReturn($l10n);
|
||||
/** @var IURLGenerator | \PHPUnit_Framework_MockObject_MockObject $urlGenerator */
|
||||
$urlGenerator = $this->createMock(IURLGenerator::class);
|
||||
/** @var IDBConnection | \PHPUnit_Framework_MockObject_MockObject $db */
|
||||
$db = $this->createMock(IDBConnection::class);
|
||||
/** @var ISecureRandom | \PHPUnit_Framework_MockObject_MockObject $random */
|
||||
$random = $this->createMock(ISecureRandom::class);
|
||||
/** @var Defaults | \PHPUnit_Framework_MockObject_MockObject $defaults */
|
||||
$defaults = $this->createMock(Defaults::class);
|
||||
|
||||
$plugin = new IMipPlugin($config, $mailer, $logger, $timeFactory, $l10nFactory, $urlGenerator, $defaults, 'user123');
|
||||
$random->expects($this->once())
|
||||
->method('generate')
|
||||
->with(60, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789')
|
||||
->will($this->returnValue('random_token'));
|
||||
|
||||
$queryBuilder = $this->createMock(IQueryBuilder::class);
|
||||
|
||||
$db->expects($this->once())
|
||||
->method('getQueryBuilder')
|
||||
->with()
|
||||
->will($this->returnValue($queryBuilder));
|
||||
$queryBuilder->expects($this->at(0))
|
||||
->method('insert')
|
||||
->with('calendar_invitation_tokens')
|
||||
->will($this->returnValue($queryBuilder));
|
||||
$queryBuilder->expects($this->at(8))
|
||||
->method('values')
|
||||
->will($this->returnValue($queryBuilder));
|
||||
$queryBuilder->expects($this->at(9))
|
||||
->method('execute');
|
||||
|
||||
$plugin = new IMipPlugin($config, $mailer, $logger, $timeFactory, $l10nFactory, $urlGenerator, $defaults, $random, $db, 'user123');
|
||||
$message = new Message();
|
||||
$message->method = 'REQUEST';
|
||||
$message->message = new VCalendar();
|
||||
|
@ -190,10 +243,37 @@ class IMipPluginTest extends TestCase {
|
|||
$l10nFactory->method('get')->willReturn($l10n);
|
||||
/** @var IURLGenerator | \PHPUnit_Framework_MockObject_MockObject $urlGenerator */
|
||||
$urlGenerator = $this->createMock(IURLGenerator::class);
|
||||
/** @var IDBConnection | \PHPUnit_Framework_MockObject_MockObject $db */
|
||||
$db = $this->createMock(IDBConnection::class);
|
||||
/** @var ISecureRandom | \PHPUnit_Framework_MockObject_MockObject $random */
|
||||
$random = $this->createMock(ISecureRandom::class);
|
||||
/** @var Defaults | \PHPUnit_Framework_MockObject_MockObject $defaults */
|
||||
$defaults = $this->createMock(Defaults::class);
|
||||
|
||||
$plugin = new IMipPlugin($config, $mailer, $logger, $timeFactory, $l10nFactory, $urlGenerator, $defaults, 'user123');
|
||||
if ($expectsMail) {
|
||||
$random->expects($this->once())
|
||||
->method('generate')
|
||||
->with(60, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789')
|
||||
->will($this->returnValue('random_token'));
|
||||
|
||||
$queryBuilder = $this->createMock(IQueryBuilder::class);
|
||||
|
||||
$db->expects($this->once())
|
||||
->method('getQueryBuilder')
|
||||
->with()
|
||||
->will($this->returnValue($queryBuilder));
|
||||
$queryBuilder->expects($this->at(0))
|
||||
->method('insert')
|
||||
->with('calendar_invitation_tokens')
|
||||
->will($this->returnValue($queryBuilder));
|
||||
$queryBuilder->expects($this->at(8))
|
||||
->method('values')
|
||||
->will($this->returnValue($queryBuilder));
|
||||
$queryBuilder->expects($this->at(9))
|
||||
->method('execute');
|
||||
}
|
||||
|
||||
$plugin = new IMipPlugin($config, $mailer, $logger, $timeFactory, $l10nFactory, $urlGenerator, $defaults, $random, $db, 'user123');
|
||||
$message = new Message();
|
||||
$message->method = 'REQUEST';
|
||||
$message->message = new VCalendar();
|
||||
|
|
|
@ -0,0 +1,455 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
* @copyright 2018, Georg Ehrke <oc.list@georgehrke.com>
|
||||
*
|
||||
* @author Georg Ehrke <oc.list@georgehrke.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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\DAV\Tests\Unit\DAV\Controller;
|
||||
|
||||
use OCA\DAV\CalDAV\InvitationResponse\InvitationResponseServer;
|
||||
use OCA\DAV\CalDAV\Schedule\Plugin;
|
||||
use OCA\DAV\Controller\InvitationResponseController;
|
||||
use OCP\AppFramework\Http\TemplateResponse;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\IRequest;
|
||||
use Sabre\VObject\ITip\Message;
|
||||
use Test\TestCase;
|
||||
|
||||
class InvitationResponseControllerTest extends TestCase {
|
||||
|
||||
/** @var InvitationResponseController */
|
||||
private $controller;
|
||||
|
||||
/** @var IDBConnection|\PHPUnit_Framework_MockObject_MockObject */
|
||||
private $dbConnection;
|
||||
|
||||
/** @var IRequest|\PHPUnit_Framework_MockObject_MockObject */
|
||||
private $request;
|
||||
|
||||
/** @var ITimeFactory|\PHPUnit_Framework_MockObject_MockObject */
|
||||
private $timeFactory;
|
||||
|
||||
/** @var InvitationResponseServer|\PHPUnit_Framework_MockObject_MockObject */
|
||||
private $responseServer;
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->dbConnection = $this->createMock(IDBConnection::class);
|
||||
$this->request = $this->createMock(IRequest::class);
|
||||
$this->timeFactory = $this->createMock(ITimeFactory::class);
|
||||
$this->responseServer = $this->getMockBuilder(InvitationResponseServer::class)
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
$this->controller = new InvitationResponseController(
|
||||
'appName',
|
||||
$this->request,
|
||||
$this->dbConnection,
|
||||
$this->timeFactory,
|
||||
$this->responseServer
|
||||
);
|
||||
}
|
||||
|
||||
public function testAccept() {
|
||||
$this->buildQueryExpects('TOKEN123', [
|
||||
'id' => 0,
|
||||
'uid' => 'this-is-the-events-uid',
|
||||
'recurrenceid' => null,
|
||||
'attendee' => 'mailto:attendee@foo.bar',
|
||||
'organizer' => 'mailto:organizer@foo.bar',
|
||||
'sequence' => null,
|
||||
'token' => 'TOKEN123',
|
||||
'expiration' => 420000,
|
||||
], 1337);
|
||||
|
||||
$expected = <<<EOF
|
||||
BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
PRODID:-//Nextcloud/Nextcloud CalDAV Server//EN
|
||||
METHOD:REPLY
|
||||
BEGIN:VEVENT
|
||||
ATTENDEE;PARTSTAT=ACCEPTED:mailto:attendee@foo.bar
|
||||
ORGANIZER:mailto:organizer@foo.bar
|
||||
UID:this-is-the-events-uid
|
||||
SEQUENCE:0
|
||||
REQUEST-STATUS:2.0;Success
|
||||
DTSTAMP:19700101T002217Z
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
|
||||
EOF;
|
||||
$expected = preg_replace('~\R~u', "\r\n", $expected);
|
||||
|
||||
$called = false;
|
||||
$this->responseServer->expects($this->once())
|
||||
->method('handleITipMessage')
|
||||
->will($this->returnCallback(function(Message $iTipMessage) use (&$called, $expected) {
|
||||
$called = true;
|
||||
$this->assertEquals('this-is-the-events-uid', $iTipMessage->uid);
|
||||
$this->assertEquals('VEVENT', $iTipMessage->component);
|
||||
$this->assertEquals('REPLY', $iTipMessage->method);
|
||||
$this->assertEquals(null, $iTipMessage->sequence);
|
||||
$this->assertEquals('mailto:attendee@foo.bar', $iTipMessage->sender);
|
||||
$this->assertEquals('mailto:organizer@foo.bar', $iTipMessage->recipient);
|
||||
|
||||
$iTipMessage->scheduleStatus = '1.2;Message delivered locally';
|
||||
|
||||
$this->assertEquals($expected, $iTipMessage->message->serialize());
|
||||
}));
|
||||
|
||||
|
||||
|
||||
$response = $this->controller->accept('TOKEN123');
|
||||
$this->assertInstanceOf(TemplateResponse::class, $response);
|
||||
$this->assertEquals('schedule-response-success', $response->getTemplateName());
|
||||
$this->assertEquals([], $response->getParams());
|
||||
$this->assertTrue($called);
|
||||
}
|
||||
|
||||
public function testAcceptSequence() {
|
||||
$this->buildQueryExpects('TOKEN123', [
|
||||
'id' => 0,
|
||||
'uid' => 'this-is-the-events-uid',
|
||||
'recurrenceid' => null,
|
||||
'attendee' => 'mailto:attendee@foo.bar',
|
||||
'organizer' => 'mailto:organizer@foo.bar',
|
||||
'sequence' => 1337,
|
||||
'token' => 'TOKEN123',
|
||||
'expiration' => 420000,
|
||||
], 1337);
|
||||
|
||||
$expected = <<<EOF
|
||||
BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
PRODID:-//Nextcloud/Nextcloud CalDAV Server//EN
|
||||
METHOD:REPLY
|
||||
BEGIN:VEVENT
|
||||
ATTENDEE;PARTSTAT=ACCEPTED:mailto:attendee@foo.bar
|
||||
ORGANIZER:mailto:organizer@foo.bar
|
||||
UID:this-is-the-events-uid
|
||||
SEQUENCE:1337
|
||||
REQUEST-STATUS:2.0;Success
|
||||
DTSTAMP:19700101T002217Z
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
|
||||
EOF;
|
||||
$expected = preg_replace('~\R~u', "\r\n", $expected);
|
||||
|
||||
$called = false;
|
||||
$this->responseServer->expects($this->once())
|
||||
->method('handleITipMessage')
|
||||
->will($this->returnCallback(function(Message $iTipMessage) use (&$called, $expected) {
|
||||
$called = true;
|
||||
$this->assertEquals('this-is-the-events-uid', $iTipMessage->uid);
|
||||
$this->assertEquals('VEVENT', $iTipMessage->component);
|
||||
$this->assertEquals('REPLY', $iTipMessage->method);
|
||||
$this->assertEquals(1337, $iTipMessage->sequence);
|
||||
$this->assertEquals('mailto:attendee@foo.bar', $iTipMessage->sender);
|
||||
$this->assertEquals('mailto:organizer@foo.bar', $iTipMessage->recipient);
|
||||
|
||||
$iTipMessage->scheduleStatus = '1.2;Message delivered locally';
|
||||
|
||||
$this->assertEquals($expected, $iTipMessage->message->serialize());
|
||||
}));
|
||||
|
||||
|
||||
|
||||
$response = $this->controller->accept('TOKEN123');
|
||||
$this->assertInstanceOf(TemplateResponse::class, $response);
|
||||
$this->assertEquals('schedule-response-success', $response->getTemplateName());
|
||||
$this->assertEquals([], $response->getParams());
|
||||
$this->assertTrue($called);
|
||||
}
|
||||
|
||||
public function testAcceptRecurrenceId() {
|
||||
$this->buildQueryExpects('TOKEN123', [
|
||||
'id' => 0,
|
||||
'uid' => 'this-is-the-events-uid',
|
||||
'recurrenceid' => "RECURRENCE-ID;TZID=Europe/Berlin:20180726T150000\n",
|
||||
'attendee' => 'mailto:attendee@foo.bar',
|
||||
'organizer' => 'mailto:organizer@foo.bar',
|
||||
'sequence' => null,
|
||||
'token' => 'TOKEN123',
|
||||
'expiration' => 420000,
|
||||
], 1337);
|
||||
|
||||
$expected = <<<EOF
|
||||
BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
PRODID:-//Nextcloud/Nextcloud CalDAV Server//EN
|
||||
METHOD:REPLY
|
||||
BEGIN:VEVENT
|
||||
ATTENDEE;PARTSTAT=ACCEPTED:mailto:attendee@foo.bar
|
||||
ORGANIZER:mailto:organizer@foo.bar
|
||||
UID:this-is-the-events-uid
|
||||
SEQUENCE:0
|
||||
REQUEST-STATUS:2.0;Success
|
||||
RECURRENCE-ID;TZID=Europe/Berlin:20180726T150000
|
||||
DTSTAMP:19700101T002217Z
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
|
||||
EOF;
|
||||
$expected = preg_replace('~\R~u', "\r\n", $expected);
|
||||
|
||||
$called = false;
|
||||
$this->responseServer->expects($this->once())
|
||||
->method('handleITipMessage')
|
||||
->will($this->returnCallback(function(Message $iTipMessage) use (&$called, $expected) {
|
||||
$called = true;
|
||||
$this->assertEquals('this-is-the-events-uid', $iTipMessage->uid);
|
||||
$this->assertEquals('VEVENT', $iTipMessage->component);
|
||||
$this->assertEquals('REPLY', $iTipMessage->method);
|
||||
$this->assertEquals(0, $iTipMessage->sequence);
|
||||
$this->assertEquals('mailto:attendee@foo.bar', $iTipMessage->sender);
|
||||
$this->assertEquals('mailto:organizer@foo.bar', $iTipMessage->recipient);
|
||||
|
||||
$iTipMessage->scheduleStatus = '1.2;Message delivered locally';
|
||||
|
||||
$this->assertEquals($expected, $iTipMessage->message->serialize());
|
||||
}));
|
||||
|
||||
|
||||
|
||||
$response = $this->controller->accept('TOKEN123');
|
||||
$this->assertInstanceOf(TemplateResponse::class, $response);
|
||||
$this->assertEquals('schedule-response-success', $response->getTemplateName());
|
||||
$this->assertEquals([], $response->getParams());
|
||||
$this->assertTrue($called);
|
||||
}
|
||||
|
||||
public function testAcceptTokenNotFound() {
|
||||
$this->buildQueryExpects('TOKEN123', null, 1337);
|
||||
|
||||
$response = $this->controller->accept('TOKEN123');
|
||||
$this->assertInstanceOf(TemplateResponse::class, $response);
|
||||
$this->assertEquals('schedule-response-error', $response->getTemplateName());
|
||||
$this->assertEquals([], $response->getParams());
|
||||
}
|
||||
|
||||
public function testAcceptExpiredToken() {
|
||||
$this->buildQueryExpects('TOKEN123', [
|
||||
'id' => 0,
|
||||
'uid' => 'this-is-the-events-uid',
|
||||
'recurrenceid' => null,
|
||||
'attendee' => 'mailto:attendee@foo.bar',
|
||||
'organizer' => 'mailto:organizer@foo.bar',
|
||||
'sequence' => null,
|
||||
'token' => 'TOKEN123',
|
||||
'expiration' => 42,
|
||||
], 1337);
|
||||
|
||||
$response = $this->controller->accept('TOKEN123');
|
||||
$this->assertInstanceOf(TemplateResponse::class, $response);
|
||||
$this->assertEquals('schedule-response-error', $response->getTemplateName());
|
||||
$this->assertEquals([], $response->getParams());
|
||||
}
|
||||
|
||||
public function testDecline() {
|
||||
$this->buildQueryExpects('TOKEN123', [
|
||||
'id' => 0,
|
||||
'uid' => 'this-is-the-events-uid',
|
||||
'recurrenceid' => null,
|
||||
'attendee' => 'mailto:attendee@foo.bar',
|
||||
'organizer' => 'mailto:organizer@foo.bar',
|
||||
'sequence' => null,
|
||||
'token' => 'TOKEN123',
|
||||
'expiration' => 420000,
|
||||
], 1337);
|
||||
|
||||
$expected = <<<EOF
|
||||
BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
PRODID:-//Nextcloud/Nextcloud CalDAV Server//EN
|
||||
METHOD:REPLY
|
||||
BEGIN:VEVENT
|
||||
ATTENDEE;PARTSTAT=DECLINED:mailto:attendee@foo.bar
|
||||
ORGANIZER:mailto:organizer@foo.bar
|
||||
UID:this-is-the-events-uid
|
||||
SEQUENCE:0
|
||||
REQUEST-STATUS:2.0;Success
|
||||
DTSTAMP:19700101T002217Z
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
|
||||
EOF;
|
||||
$expected = preg_replace('~\R~u', "\r\n", $expected);
|
||||
|
||||
$called = false;
|
||||
$this->responseServer->expects($this->once())
|
||||
->method('handleITipMessage')
|
||||
->will($this->returnCallback(function(Message $iTipMessage) use (&$called, $expected) {
|
||||
$called = true;
|
||||
$this->assertEquals('this-is-the-events-uid', $iTipMessage->uid);
|
||||
$this->assertEquals('VEVENT', $iTipMessage->component);
|
||||
$this->assertEquals('REPLY', $iTipMessage->method);
|
||||
$this->assertEquals(null, $iTipMessage->sequence);
|
||||
$this->assertEquals('mailto:attendee@foo.bar', $iTipMessage->sender);
|
||||
$this->assertEquals('mailto:organizer@foo.bar', $iTipMessage->recipient);
|
||||
|
||||
$iTipMessage->scheduleStatus = '1.2;Message delivered locally';
|
||||
|
||||
$this->assertEquals($expected, $iTipMessage->message->serialize());
|
||||
}));
|
||||
|
||||
|
||||
|
||||
$response = $this->controller->decline('TOKEN123');
|
||||
$this->assertInstanceOf(TemplateResponse::class, $response);
|
||||
$this->assertEquals('schedule-response-success', $response->getTemplateName());
|
||||
$this->assertEquals([], $response->getParams());
|
||||
$this->assertTrue($called);
|
||||
}
|
||||
|
||||
public function testOptions() {
|
||||
$response = $this->controller->options('TOKEN123');
|
||||
$this->assertInstanceOf(TemplateResponse::class, $response);
|
||||
$this->assertEquals('schedule-response-options', $response->getTemplateName());
|
||||
$this->assertEquals(['token' => 'TOKEN123'], $response->getParams());
|
||||
}
|
||||
|
||||
public function testProcessMoreOptionsResult() {
|
||||
$this->request->expects($this->at(0))
|
||||
->method('getParam')
|
||||
->with('partStat')
|
||||
->will($this->returnValue('TENTATIVE'));
|
||||
$this->request->expects($this->at(1))
|
||||
->method('getParam')
|
||||
->with('guests')
|
||||
->will($this->returnValue('7'));
|
||||
$this->request->expects($this->at(2))
|
||||
->method('getParam')
|
||||
->with('comment')
|
||||
->will($this->returnValue('Foo bar Bli blub'));
|
||||
|
||||
$this->buildQueryExpects('TOKEN123', [
|
||||
'id' => 0,
|
||||
'uid' => 'this-is-the-events-uid',
|
||||
'recurrenceid' => null,
|
||||
'attendee' => 'mailto:attendee@foo.bar',
|
||||
'organizer' => 'mailto:organizer@foo.bar',
|
||||
'sequence' => null,
|
||||
'token' => 'TOKEN123',
|
||||
'expiration' => 420000,
|
||||
], 1337);
|
||||
|
||||
$expected = <<<EOF
|
||||
BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
PRODID:-//Nextcloud/Nextcloud CalDAV Server//EN
|
||||
METHOD:REPLY
|
||||
BEGIN:VEVENT
|
||||
ATTENDEE;PARTSTAT=TENTATIVE;X-RESPONSE-COMMENT=Foo bar Bli blub;X-NUM-GUEST
|
||||
S=7:mailto:attendee@foo.bar
|
||||
ORGANIZER:mailto:organizer@foo.bar
|
||||
UID:this-is-the-events-uid
|
||||
SEQUENCE:0
|
||||
REQUEST-STATUS:2.0;Success
|
||||
DTSTAMP:19700101T002217Z
|
||||
COMMENT:Foo bar Bli blub
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
|
||||
EOF;
|
||||
$expected = preg_replace('~\R~u', "\r\n", $expected);
|
||||
|
||||
$called = false;
|
||||
$this->responseServer->expects($this->once())
|
||||
->method('handleITipMessage')
|
||||
->will($this->returnCallback(function(Message $iTipMessage) use (&$called, $expected) {
|
||||
$called = true;
|
||||
$this->assertEquals('this-is-the-events-uid', $iTipMessage->uid);
|
||||
$this->assertEquals('VEVENT', $iTipMessage->component);
|
||||
$this->assertEquals('REPLY', $iTipMessage->method);
|
||||
$this->assertEquals(null, $iTipMessage->sequence);
|
||||
$this->assertEquals('mailto:attendee@foo.bar', $iTipMessage->sender);
|
||||
$this->assertEquals('mailto:organizer@foo.bar', $iTipMessage->recipient);
|
||||
|
||||
$iTipMessage->scheduleStatus = '1.2;Message delivered locally';
|
||||
|
||||
$this->assertEquals($expected, $iTipMessage->message->serialize());
|
||||
}));
|
||||
|
||||
|
||||
|
||||
$response = $this->controller->processMoreOptionsResult('TOKEN123');
|
||||
$this->assertInstanceOf(TemplateResponse::class, $response);
|
||||
$this->assertEquals('schedule-response-success', $response->getTemplateName());
|
||||
$this->assertEquals([], $response->getParams());
|
||||
$this->assertTrue($called);
|
||||
}
|
||||
|
||||
private function buildQueryExpects($token, $return, $time) {
|
||||
$queryBuilder = $this->createMock(IQueryBuilder::class);
|
||||
$stmt = $this->createMock(\Doctrine\DBAL\Driver\Statement::class);
|
||||
$expr = $this->createMock(\OCP\DB\QueryBuilder\IExpressionBuilder::class);
|
||||
|
||||
$this->dbConnection->expects($this->once())
|
||||
->method('getQueryBuilder')
|
||||
->with()
|
||||
->will($this->returnValue($queryBuilder));
|
||||
$queryBuilder->method('expr')
|
||||
->will($this->returnValue($expr));
|
||||
$queryBuilder->method('createNamedParameter')
|
||||
->will($this->returnValueMap([
|
||||
[$token, \PDO::PARAM_STR, null, 'namedParameterToken']
|
||||
]));
|
||||
|
||||
$stmt->expects($this->once())
|
||||
->method('fetch')
|
||||
->with(\PDO::FETCH_ASSOC)
|
||||
->will($this->returnValue($return));
|
||||
|
||||
$expr->expects($this->once())
|
||||
->method('eq')
|
||||
->with('token', 'namedParameterToken')
|
||||
->will($this->returnValue('EQ STATEMENT'));
|
||||
|
||||
$this->dbConnection->expects($this->once())
|
||||
->method('getQueryBuilder')
|
||||
->with()
|
||||
->will($this->returnValue($queryBuilder));
|
||||
|
||||
$queryBuilder->expects($this->at(0))
|
||||
->method('select')
|
||||
->with('*')
|
||||
->will($this->returnValue($queryBuilder));
|
||||
$queryBuilder->expects($this->at(1))
|
||||
->method('from')
|
||||
->with('calendar_invitation_tokens')
|
||||
->will($this->returnValue($queryBuilder));
|
||||
$queryBuilder->expects($this->at(4))
|
||||
->method('where')
|
||||
->with('EQ STATEMENT')
|
||||
->will($this->returnValue($queryBuilder));
|
||||
$queryBuilder->expects($this->at(5))
|
||||
->method('execute')
|
||||
->with()
|
||||
->will($this->returnValue($stmt));
|
||||
|
||||
$this->timeFactory->method('getTime')
|
||||
->will($this->returnValue($time));
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue