mirror of https://github.com/nextcloud/server.git
Include accept / decline links in CalDAV invitation emails
Signed-off-by: Georg Ehrke <developer@georgehrke.com>pull/9942/head
parent
3ff3141a1e
commit
4aa4e4080c
@ -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
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
// $(document).ready(function() {
|
||||
// $('#selectPartStatForm').buttonset();
|
||||
// });
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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>
|
@ -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>
|
@ -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([]);
|
||||
}
|
||||
}
|
@ -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 New Issue