Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add calendar sharing #21964

Merged
merged 9 commits into from
Feb 4, 2016
2 changes: 1 addition & 1 deletion apps/dav/appinfo/register_command.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@

/** @var Symfony\Component\Console\Application $application */
$application->add(new CreateAddressBook($userManager, $groupManager, $dbConnection, $logger));
$application->add(new CreateCalendar($userManager, $dbConnection));
$application->add(new CreateCalendar($userManager, $groupManager, $dbConnection));
$application->add(new SyncSystemAddressBook($app->getSyncService()));

// the occ tool is *for now* only available in debug mode for developers to test
Expand Down
15 changes: 13 additions & 2 deletions apps/dav/command/createcalendar.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
namespace OCA\DAV\Command;

use OCA\DAV\CalDAV\CalDavBackend;
use OCA\DAV\Connector\Sabre\Principal;
use OCP\IDBConnection;
use OCP\IGroupManager;
use OCP\IUserManager;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
Expand All @@ -33,16 +35,20 @@ class CreateCalendar extends Command {
/** @var IUserManager */
protected $userManager;

/** @var IGroupManager $groupManager */
private $groupManager;

/** @var \OCP\IDBConnection */
protected $dbConnection;

/**
* @param IUserManager $userManager
* @param IDBConnection $dbConnection
*/
function __construct(IUserManager $userManager, IDBConnection $dbConnection) {
function __construct(IUserManager $userManager, IGroupManager $groupManager, IDBConnection $dbConnection) {
parent::__construct();
$this->userManager = $userManager;
$this->groupManager = $groupManager;
$this->dbConnection = $dbConnection;
}

Expand All @@ -63,8 +69,13 @@ protected function execute(InputInterface $input, OutputInterface $output) {
if (!$this->userManager->userExists($user)) {
throw new \InvalidArgumentException("User <$user> in unknown.");
}
$principalBackend = new Principal(
$this->userManager,
$this->groupManager
);

$name = $input->getArgument('name');
$caldav = new CalDavBackend($this->dbConnection);
$caldav = new CalDavBackend($this->dbConnection, $principalBackend);
$caldav->createCalendar("principals/users/$user", $name, []);
}
}
90 changes: 87 additions & 3 deletions apps/dav/lib/caldav/caldavbackend.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
namespace OCA\DAV\CalDAV;

use OCP\DB\QueryBuilder\IQueryBuilder;
use OCA\DAV\Connector\Sabre\Principal;
use OCA\DAV\DAV\Sharing\Backend;
use Sabre\CalDAV\Backend\AbstractBackend;
use Sabre\CalDAV\Backend\SchedulingSupport;
use Sabre\CalDAV\Backend\SubscriptionSupport;
Expand All @@ -32,6 +34,7 @@
use Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet;
use Sabre\DAV;
use Sabre\DAV\Exception\Forbidden;
use Sabre\HTTP\URLUtil;
use Sabre\VObject\DateTimeParser;
use Sabre\VObject\Reader;
use Sabre\VObject\RecurrenceIterator;
Expand Down Expand Up @@ -86,8 +89,24 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
'{http://calendarserver.org/ns/}subscribed-strip-attachments' => 'stripattachments',
];

public function __construct(\OCP\IDBConnection $db) {
/** @var \OCP\IDBConnection */
private $db;

/** @var Backend */
private $sharingBackend;

/** @var Principal */
private $principalBackend;

/**
* CalDavBackend constructor.
*
* @param \OCP\IDBConnection $db
*/
public function __construct(\OCP\IDBConnection $db, Principal $principalBackend) {
$this->db = $db;
$this->principalBackend = $principalBackend;
$this->sharingBackend = new Backend($this->db, 'calendar');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no DI necessary?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at rest of the code it's ok

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not really from my pov since this is there to share common code between the two backends

but I'm happy to discuss this

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I did not miss anything, they were onliners in methods, without further logic, so per se it's sufficient to test the Backend itself. OTOH it leaves a heritage, if the code would change in future.

}

/**
Expand Down Expand Up @@ -153,10 +172,60 @@ function getCalendarsForUser($principalUri) {
$calendar[$xmlName] = $row[$dbName];
}

$calendars[] = $calendar;
$calendars[$calendar['id']] = $calendar;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

$stmt->closeCursor();

// query for shared calendars
$principals = $this->principalBackend->getGroupMembership($principalUri);
$principals[]= $principalUri;

$fields = array_values($this->propertyMap);
$fields[] = 'a.id';
$fields[] = 'a.uri';
$fields[] = 'a.synctoken';
$fields[] = 'a.components';
$fields[] = 'a.principaluri';
$fields[] = 'a.transparent';
$query = $this->db->getQueryBuilder();
$result = $query->select($fields)
->from('dav_shares', 's')
->join('s', 'calendars', 'a', $query->expr()->eq('s.resourceid', 'a.id'))
->where($query->expr()->in('s.principaluri', $query->createParameter('principaluri')))
->andWhere($query->expr()->eq('s.type', $query->createParameter('type')))
->setParameter('type', 'calendar')
->setParameter('principaluri', $principals, \Doctrine\DBAL\Connection::PARAM_STR_ARRAY)
->execute();

while($row = $result->fetch()) {
list(, $name) = URLUtil::splitPath($row['principaluri']);
$uri = $row['uri'] . '_shared_by_' . $name;
$row['displayname'] = $row['displayname'] . "($name)";
$components = [];
if ($row['components']) {
$components = explode(',',$row['components']);
}
$calendar = [
'id' => $row['id'],
'uri' => $uri,
'principaluri' => $principalUri,
'{' . Plugin::NS_CALENDARSERVER . '}getctag' => 'http://sabre.io/ns/sync/' . ($row['synctoken']?$row['synctoken']:'0'),
'{http://sabredav.org/ns}sync-token' => $row['synctoken']?$row['synctoken']:'0',
'{' . Plugin::NS_CALDAV . '}supported-calendar-component-set' => new SupportedCalendarComponentSet($components),
'{' . Plugin::NS_CALDAV . '}schedule-calendar-transp' => new ScheduleCalendarTransp($row['transparent']?'transparent':'opaque'),
'{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}owner-principal' => $row['principaluri'],
];

foreach($this->propertyMap as $xmlName=>$dbName) {
$calendar[$xmlName] = $row[$dbName];
}

$calendars[$calendar['id']] = $calendar;
}
$result->closeCursor();

return $calendars;
return array_values($calendars);
}

/**
Expand Down Expand Up @@ -271,6 +340,8 @@ function deleteCalendar($calendarId) {

$stmt = $this->db->prepare('DELETE FROM `*PREFIX*calendarchanges` WHERE `calendarid` = ?');
$stmt->execute([$calendarId]);

$this->sharingBackend->deleteAllShares($calendarId);
}

/**
Expand Down Expand Up @@ -1173,4 +1244,17 @@ private function readBlob($cardData) {

return $cardData;
}

public function updateShares($shareable, $add, $remove) {
$this->sharingBackend->updateShares($shareable, $add, $remove);
}

public function getShares($resourceId) {
return $this->sharingBackend->getShares($resourceId);
}

public function applyShareAcl($addressBookId, $acl) {
return $this->sharingBackend->applyShareAcl($addressBookId, $acl);
}

}
102 changes: 102 additions & 0 deletions apps/dav/lib/caldav/calendar.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?php

namespace OCA\DAV\CalDAV;

use OCA\DAV\DAV\Sharing\IShareable;
use Sabre\DAV\Exception\Forbidden;

class Calendar extends \Sabre\CalDAV\Calendar implements IShareable {

/**
* Updates the list of shares.
*
* The first array is a list of people that are to be added to the
* resource.
*
* Every element in the add array has the following properties:
* * href - A url. Usually a mailto: address
* * commonName - Usually a first and last name, or false
* * summary - A description of the share, can also be false
* * readOnly - A boolean value
*
* Every element in the remove array is just the address string.
*
* @param array $add
* @param array $remove
* @return void
*/
function updateShares(array $add, array $remove) {
/** @var CalDavBackend $calDavBackend */
$calDavBackend = $this->caldavBackend;
$calDavBackend->updateShares($this, $add, $remove);
}

/**
* Returns the list of people whom this resource is shared with.
*
* Every element in this array should have the following properties:
* * href - Often a mailto: address
* * commonName - Optional, for example a first + last name
* * status - See the Sabre\CalDAV\SharingPlugin::STATUS_ constants.
* * readOnly - boolean
* * summary - Optional, a description for the share
*
* @return array
*/
function getShares() {
/** @var CalDavBackend $calDavBackend */
$calDavBackend = $this->caldavBackend;
return $calDavBackend->getShares($this->getResourceId());
}

/**
* @return int
*/
public function getResourceId() {
return $this->calendarInfo['id'];
}

function getACL() {
$acl = parent::getACL();

/** @var CalDavBackend $calDavBackend */
$calDavBackend = $this->caldavBackend;
return $calDavBackend->applyShareAcl($this->getResourceId(), $acl);
}

function getChildACL() {
$acl = parent::getChildACL();

/** @var CalDavBackend $calDavBackend */
$calDavBackend = $this->caldavBackend;
return $calDavBackend->applyShareAcl($this->getResourceId(), $acl);
}

function getOwner() {
if (isset($this->calendarInfo['{http://owncloud.org/ns}owner-principal'])) {
return $this->calendarInfo['{http://owncloud.org/ns}owner-principal'];
}
return parent::getOwner();
}

function delete() {
if (isset($this->calendarInfo['{http://owncloud.org/ns}owner-principal'])) {
$principal = 'principal:' . parent::getOwner();
$shares = $this->getShares();
$shares = array_filter($shares, function($share) use ($principal){
return $share['href'] === $principal;
});
if (empty($shares)) {
throw new Forbidden();
}

/** @var CalDavBackend $calDavBackend */
$calDavBackend = $this->caldavBackend;
$calDavBackend->updateShares($this, [], [
'href' => $principal
]);
return;
}
parent::delete();
}
}
78 changes: 78 additions & 0 deletions apps/dav/lib/caldav/calendarhome.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?php

namespace OCA\DAV\CalDAV;

use Sabre\CalDAV\Backend\NotificationSupport;
use Sabre\CalDAV\Backend\SchedulingSupport;
use Sabre\CalDAV\Backend\SubscriptionSupport;
use Sabre\CalDAV\Schedule\Inbox;
use Sabre\CalDAV\Schedule\Outbox;
use Sabre\CalDAV\Subscriptions\Subscription;
use Sabre\DAV\Exception\NotFound;

class CalendarHome extends \Sabre\CalDAV\CalendarHome {

/**
* @inheritdoc
*/
function getChildren() {
$calendars = $this->caldavBackend->getCalendarsForUser($this->principalInfo['uri']);
$objs = [];
foreach ($calendars as $calendar) {
$objs[] = new Calendar($this->caldavBackend, $calendar);
}

if ($this->caldavBackend instanceof SchedulingSupport) {
$objs[] = new Inbox($this->caldavBackend, $this->principalInfo['uri']);
$objs[] = new Outbox($this->principalInfo['uri']);
}

// We're adding a notifications node, if it's supported by the backend.
if ($this->caldavBackend instanceof NotificationSupport) {
$objs[] = new \Sabre\CalDAV\Notifications\Collection($this->caldavBackend, $this->principalInfo['uri']);
}

// If the backend supports subscriptions, we'll add those as well,
if ($this->caldavBackend instanceof SubscriptionSupport) {
foreach ($this->caldavBackend->getSubscriptionsForUser($this->principalInfo['uri']) as $subscription) {
$objs[] = new Subscription($this->caldavBackend, $subscription);
}
}

return $objs;
}

/**
* @inheritdoc
*/
function getChild($name) {
// Special nodes
if ($name === 'inbox' && $this->caldavBackend instanceof SchedulingSupport) {
return new Inbox($this->caldavBackend, $this->principalInfo['uri']);
}
if ($name === 'outbox' && $this->caldavBackend instanceof SchedulingSupport) {
return new Outbox($this->principalInfo['uri']);
}
if ($name === 'notifications' && $this->caldavBackend instanceof NotificationSupport) {
return new \Sabre\CalDAv\Notifications\Collection($this->caldavBackend, $this->principalInfo['uri']);
}

// Calendars
foreach ($this->caldavBackend->getCalendarsForUser($this->principalInfo['uri']) as $calendar) {
if ($calendar['uri'] === $name) {
return new Calendar($this->caldavBackend, $calendar);
}
}

if ($this->caldavBackend instanceof SubscriptionSupport) {
foreach ($this->caldavBackend->getSubscriptionsForUser($this->principalInfo['uri']) as $subscription) {
if ($subscription['uri'] === $name) {
return new Subscription($this->caldavBackend, $subscription);
}
}

}

throw new NotFound('Node with name \'' . $name . '\' could not be found');
}
}
10 changes: 10 additions & 0 deletions apps/dav/lib/caldav/calendarroot.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace OCA\DAV\CalDAV;

class CalendarRoot extends \Sabre\CalDAV\CalendarRoot {

function getChildForPrincipal(array $principal) {
return new CalendarHome($this->caldavBackend, $principal);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

linebreak missin

Loading