diff options
Diffstat (limited to 'apps/dav')
24 files changed, 2057 insertions, 30 deletions
diff --git a/apps/dav/appinfo/application.php b/apps/dav/appinfo/application.php index 07905db7368..11ab384d75d 100644 --- a/apps/dav/appinfo/application.php +++ b/apps/dav/appinfo/application.php @@ -20,12 +20,16 @@ */ namespace OCA\Dav\AppInfo; +use OCA\DAV\CalDAV\CalDavBackend; +use OCA\DAV\CardDAV\CardDavBackend; use OCA\DAV\CardDAV\ContactsManager; use OCA\DAV\CardDAV\SyncJob; use OCA\DAV\CardDAV\SyncService; use OCA\DAV\HookManager; use OCA\Dav\Migration\AddressBookAdapter; +use OCA\Dav\Migration\CalendarAdapter; use OCA\Dav\Migration\MigrateAddressbooks; +use OCA\Dav\Migration\MigrateCalendars; use \OCP\AppFramework\App; use OCP\AppFramework\IAppContainer; use OCP\Contacts\IManager; @@ -73,7 +77,17 @@ class Application extends App { $c->getServer()->getUserManager(), $c->getServer()->getGroupManager() ); - return new \OCA\DAV\CardDAV\CardDavBackend($db, $principal, $logger); + return new CardDavBackend($db, $principal, $logger); + }); + + $container->registerService('CalDavBackend', function($c) { + /** @var IAppContainer $c */ + $db = $c->getServer()->getDatabaseConnection(); + $principal = new \OCA\DAV\Connector\Sabre\Principal( + $c->getServer()->getUserManager(), + $c->getServer()->getGroupManager() + ); + return new CalDavBackend($db, $principal); }); $container->registerService('MigrateAddressbooks', function($c) { @@ -84,6 +98,15 @@ class Application extends App { $c->query('CardDavBackend') ); }); + + $container->registerService('MigrateCalendars', function($c) { + /** @var IAppContainer $c */ + $db = $c->getServer()->getDatabaseConnection(); + return new MigrateCalendars( + new CalendarAdapter($db), + $c->query('CalDavBackend') + ); + }); } /** @@ -112,8 +135,8 @@ class Application extends App { } public function migrateAddressbooks() { - try { + /** @var MigrateAddressbooks $migration */ $migration = $this->getContainer()->query('MigrateAddressbooks'); $migration->setup(); $userManager = $this->getContainer()->getServer()->getUserManager(); @@ -127,4 +150,19 @@ class Application extends App { } } + public function migrateCalendars() { + try { + /** @var MigrateCalendars $migration */ + $migration = $this->getContainer()->query('MigrateCalendars'); + $migration->setup(); + $userManager = $this->getContainer()->getServer()->getUserManager(); + + $userManager->callForAllUsers(function($user) use($migration) { + /** @var IUser $user */ + $migration->migrateForUser($user->getUID()); + }); + } catch (\Exception $ex) { + $this->getContainer()->getServer()->getLogger()->logException($ex); + } + } } diff --git a/apps/dav/appinfo/install.php b/apps/dav/appinfo/install.php index f6ef533958e..a7a3220b90f 100644 --- a/apps/dav/appinfo/install.php +++ b/apps/dav/appinfo/install.php @@ -24,3 +24,4 @@ use OCA\Dav\AppInfo\Application; $app = new Application(); $app->setupCron(); $app->migrateAddressbooks(); +$app->migrateCalendars(); diff --git a/apps/dav/appinfo/register_command.php b/apps/dav/appinfo/register_command.php index e8ca370f84f..4981cab9264 100644 --- a/apps/dav/appinfo/register_command.php +++ b/apps/dav/appinfo/register_command.php @@ -23,6 +23,7 @@ use OCA\Dav\AppInfo\Application; use OCA\DAV\Command\CreateAddressBook; use OCA\DAV\Command\CreateCalendar; use OCA\Dav\Command\MigrateAddressbooks; +use OCA\Dav\Command\MigrateCalendars; use OCA\DAV\Command\SyncSystemAddressBook; $config = \OC::$server->getConfig(); @@ -44,4 +45,6 @@ if ($config->getSystemValue('debug', false)){ $app = new \OCA\Dav\AppInfo\Application(); $migration = $app->getContainer()->query('MigrateAddressbooks'); $application->add(new MigrateAddressbooks($userManager, $migration)); + $migration = $app->getContainer()->query('MigrateCalendars'); + $application->add(new MigrateCalendars($userManager, $migration)); } diff --git a/apps/dav/appinfo/v1/publicwebdav.php b/apps/dav/appinfo/v1/publicwebdav.php index 6ddb570aca8..b0ee264aac3 100644 --- a/apps/dav/appinfo/v1/publicwebdav.php +++ b/apps/dav/appinfo/v1/publicwebdav.php @@ -46,7 +46,9 @@ $serverFactory = new OCA\DAV\Connector\Sabre\ServerFactory( $requestUri = \OC::$server->getRequest()->getRequestUri(); -$server = $serverFactory->createServer($baseuri, $requestUri, $authBackend, function () use ($authBackend) { +$linkCheckPlugin = new \OCA\DAV\Files\Sharing\PublicLinkCheckPlugin(); + +$server = $serverFactory->createServer($baseuri, $requestUri, $authBackend, function (\Sabre\DAV\Server $server) use ($authBackend, $linkCheckPlugin) { $isAjax = (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest'); if (OCA\Files_Sharing\Helper::isOutgoingServer2serverShareEnabled() === false && !$isAjax) { // this is what is thrown when trying to access a non-existing share @@ -68,9 +70,13 @@ $server = $serverFactory->createServer($baseuri, $requestUri, $authBackend, func OC_Util::setupFS($owner); $ownerView = \OC\Files\Filesystem::getView(); $path = $ownerView->getPath($fileId); + $fileInfo = $ownerView->getFileInfo($path); + $linkCheckPlugin->setFileInfo($fileInfo); return new \OC\Files\View($ownerView->getAbsolutePath($path)); }); +$server->addPlugin($linkCheckPlugin); + // And off we go! $server->exec(); diff --git a/apps/dav/command/migrateaddressbooks.php b/apps/dav/command/migrateaddressbooks.php index 2ab7113ab1f..f37c29e7ab3 100644 --- a/apps/dav/command/migrateaddressbooks.php +++ b/apps/dav/command/migrateaddressbooks.php @@ -68,6 +68,7 @@ class MigrateAddressbooks extends Command { } $output->writeln("Start migration for $user"); $this->service->migrateForUser($user); + return; } $output->writeln("Start migration of all known users ..."); $p = new ProgressBar($output); diff --git a/apps/dav/command/migratecalendars.php b/apps/dav/command/migratecalendars.php new file mode 100644 index 00000000000..eda4f5fb417 --- /dev/null +++ b/apps/dav/command/migratecalendars.php @@ -0,0 +1,66 @@ +<?php + +namespace OCA\Dav\Command; + +use OCP\IUser; +use OCP\IUserManager; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\ProgressBar; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class MigrateCalendars extends Command { + + /** @var IUserManager */ + protected $userManager; + + /** @var \OCA\Dav\Migration\MigrateCalendars */ + private $service; + + /** + * @param IUserManager $userManager + * @param \OCA\Dav\Migration\MigrateCalendars $service + */ + function __construct(IUserManager $userManager, + \OCA\Dav\Migration\MigrateCalendars $service + ) { + parent::__construct(); + $this->userManager = $userManager; + $this->service = $service; + } + + protected function configure() { + $this + ->setName('dav:migrate-calendars') + ->setDescription('Migrate calendars from the calendar app to core') + ->addArgument('user', + InputArgument::OPTIONAL, + 'User for whom all calendars will be migrated'); + } + + protected function execute(InputInterface $input, OutputInterface $output) { + $this->service->setup(); + + if ($input->hasArgument('user')) { + $user = $input->getArgument('user'); + if (!$this->userManager->userExists($user)) { + throw new \InvalidArgumentException("User <$user> in unknown."); + } + $output->writeln("Start migration for $user"); + $this->service->migrateForUser($user); + return; + } + $output->writeln("Start migration of all known users ..."); + $p = new ProgressBar($output); + $p->start(); + $this->userManager->callForAllUsers(function($user) use ($p) { + $p->advance(); + /** @var IUser $user */ + $this->service->migrateForUser($user->getUID()); + }); + + $p->finish(); + $output->writeln(''); + } +} diff --git a/apps/dav/lib/caldav/caldavbackend.php b/apps/dav/lib/caldav/caldavbackend.php index 52b4812b05b..775612487f9 100644 --- a/apps/dav/lib/caldav/caldavbackend.php +++ b/apps/dav/lib/caldav/caldavbackend.php @@ -22,9 +22,11 @@ namespace OCA\DAV\CalDAV; +use OCA\DAV\DAV\Sharing\IShareable; use OCP\DB\QueryBuilder\IQueryBuilder; use OCA\DAV\Connector\Sabre\Principal; use OCA\DAV\DAV\Sharing\Backend; +use OCP\IDBConnection; use Sabre\CalDAV\Backend\AbstractBackend; use Sabre\CalDAV\Backend\SchedulingSupport; use Sabre\CalDAV\Backend\SubscriptionSupport; @@ -59,7 +61,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription const MAX_DATE = '2038-01-01'; /** - * List of CalDAV properties, and how they map to database fieldnames + * List of CalDAV properties, and how they map to database field names * Add your own properties by simply adding on to this array. * * Note that only string-based properties are supported here. @@ -75,7 +77,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription ]; /** - * List of subscription properties, and how they map to database fieldnames. + * List of subscription properties, and how they map to database field names. * * @var array */ @@ -89,7 +91,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription '{http://calendarserver.org/ns/}subscribed-strip-attachments' => 'stripattachments', ]; - /** @var \OCP\IDBConnection */ + /** @var IDBConnection */ private $db; /** @var Backend */ @@ -101,9 +103,10 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription /** * CalDavBackend constructor. * - * @param \OCP\IDBConnection $db + * @param IDBConnection $db + * @param Principal $principalBackend */ - public function __construct(\OCP\IDBConnection $db, Principal $principalBackend) { + public function __construct(IDBConnection $db, Principal $principalBackend) { $this->db = $db; $this->principalBackend = $principalBackend; $this->sharingBackend = new Backend($this->db, $principalBackend, 'calendar'); @@ -232,6 +235,95 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription return array_values($calendars); } + public function getCalendarByUri($principal, $uri) { + $fields = array_values($this->propertyMap); + $fields[] = 'id'; + $fields[] = 'uri'; + $fields[] = 'synctoken'; + $fields[] = 'components'; + $fields[] = 'principaluri'; + $fields[] = 'transparent'; + + // Making fields a comma-delimited list + $query = $this->db->getQueryBuilder(); + $query->select($fields)->from('calendars') + ->where($query->expr()->eq('uri', $query->createNamedParameter($uri))) + ->andWhere($query->expr()->eq('principaluri', $query->createNamedParameter($principal))) + ->setMaxResults(1); + $stmt = $query->execute(); + + $row = $stmt->fetch(\PDO::FETCH_ASSOC); + $stmt->closeCursor(); + if ($row === false) { + return null; + } + + $components = []; + if ($row['components']) { + $components = explode(',',$row['components']); + } + + $calendar = [ + 'id' => $row['id'], + 'uri' => $row['uri'], + 'principaluri' => $row['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'), + ]; + + foreach($this->propertyMap as $xmlName=>$dbName) { + $calendar[$xmlName] = $row[$dbName]; + } + + return $calendar; + } + + public function getCalendarById($calendarId) { + $fields = array_values($this->propertyMap); + $fields[] = 'id'; + $fields[] = 'uri'; + $fields[] = 'synctoken'; + $fields[] = 'components'; + $fields[] = 'principaluri'; + $fields[] = 'transparent'; + + // Making fields a comma-delimited list + $query = $this->db->getQueryBuilder(); + $query->select($fields)->from('calendars') + ->where($query->expr()->eq('id', $query->createNamedParameter($calendarId))) + ->setMaxResults(1); + $stmt = $query->execute(); + + $row = $stmt->fetch(\PDO::FETCH_ASSOC); + $stmt->closeCursor(); + if ($row === false) { + return null; + } + + $components = []; + if ($row['components']) { + $components = explode(',',$row['components']); + } + + $calendar = [ + 'id' => $row['id'], + 'uri' => $row['uri'], + 'principaluri' => $row['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'), + ]; + + foreach($this->propertyMap as $xmlName=>$dbName) { + $calendar[$xmlName] = $row[$dbName]; + } + + return $calendar; + } + /** * Creates a new calendar for a principal. * @@ -241,7 +333,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription * @param string $principalUri * @param string $calendarUri * @param array $properties - * @return void + * @return int */ function createCalendar($principalUri, $calendarUri, array $properties) { $values = [ @@ -278,6 +370,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription $query->setValue($column, $query->createNamedParameter($value)); } $query->execute(); + return $query->getLastInsertId(); } /** @@ -1249,16 +1342,29 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription return $cardData; } + /** + * @param IShareable $shareable + * @param array $add + * @param array $remove + */ public function updateShares($shareable, $add, $remove) { $this->sharingBackend->updateShares($shareable, $add, $remove); } + /** + * @param int $resourceId + * @return array + */ public function getShares($resourceId) { return $this->sharingBackend->getShares($resourceId); } - public function applyShareAcl($addressBookId, $acl) { - return $this->sharingBackend->applyShareAcl($addressBookId, $acl); + /** + * @param int $resourceId + * @param array $acl + * @return array + */ + public function applyShareAcl($resourceId, $acl) { + return $this->sharingBackend->applyShareAcl($resourceId, $acl); } - } diff --git a/apps/dav/lib/comments/commentnode.php b/apps/dav/lib/comments/commentnode.php index d3cd53bceb1..339abc6382d 100644 --- a/apps/dav/lib/comments/commentnode.php +++ b/apps/dav/lib/comments/commentnode.php @@ -24,9 +24,11 @@ namespace OCA\DAV\Comments; use OCP\Comments\IComment; use OCP\Comments\ICommentsManager; +use OCP\Comments\MessageTooLongException; use OCP\ILogger; use OCP\IUserManager; use OCP\IUserSession; +use Sabre\DAV\Exception\BadRequest; use Sabre\DAV\Exception\Forbidden; use Sabre\DAV\Exception\MethodNotAllowed; use Sabre\DAV\PropPatch; @@ -168,6 +170,7 @@ class CommentNode implements \Sabre\DAV\INode, \Sabre\DAV\IProperties { * * @param $propertyValue * @return bool + * @throws BadRequest * @throws Forbidden */ public function updateComment($propertyValue) { @@ -178,6 +181,10 @@ class CommentNode implements \Sabre\DAV\INode, \Sabre\DAV\IProperties { return true; } catch (\Exception $e) { $this->logger->logException($e, ['app' => 'dav/comments']); + if($e instanceof MessageTooLongException) { + $msg = 'Message exceeds allowed character limit of '; + throw new BadRequest($msg . IComment::MAX_MESSAGE_LENGTH, 0, $e); + } return false; } } diff --git a/apps/dav/lib/comments/commentsplugin.php b/apps/dav/lib/comments/commentsplugin.php index 56d94cc33e9..7abf6e71ee5 100644 --- a/apps/dav/lib/comments/commentsplugin.php +++ b/apps/dav/lib/comments/commentsplugin.php @@ -242,6 +242,9 @@ class CommentsPlugin extends ServerPlugin { return $comment; } catch (\InvalidArgumentException $e) { throw new BadRequest('Invalid input values', 0, $e); + } catch (\OCP\Comments\MessageTooLongException $e) { + $msg = 'Message exceeds allowed character limit of '; + throw new BadRequest($msg . \OCP\Comments\IComment::MAX_MESSAGE_LENGTH, 0, $e); } } diff --git a/apps/dav/lib/connector/sabre/checksumlist.php b/apps/dav/lib/connector/sabre/checksumlist.php new file mode 100644 index 00000000000..f137222acca --- /dev/null +++ b/apps/dav/lib/connector/sabre/checksumlist.php @@ -0,0 +1,71 @@ +<?php +/** + * @author Roeland Jago Douma <rullzer@owncloud.com> + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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\Connector\Sabre; + +use Sabre\Xml\XmlSerializable; +use Sabre\Xml\Element; +use Sabre\Xml\Writer; + +/** + * Checksumlist property + * + * This property contains multiple "checksum" elements, each containing a + * checksum name. + */ +class ChecksumList implements XmlSerializable { + const NS_OWNCLOUD = 'http://owncloud.org/ns'; + + /** @var string[] of TYPE:CHECKSUM */ + private $checksums; + + /** + * @param string $checksum + */ + public function __construct($checksum) { + $this->checksums = explode(',', $checksum); + } + + /** + * The xmlSerialize metod is called during xml writing. + * + * Use the $writer argument to write its own xml serialization. + * + * An important note: do _not_ create a parent element. Any element + * implementing XmlSerializble should only ever write what's considered + * its 'inner xml'. + * + * The parent of the current element is responsible for writing a + * containing element. + * + * This allows serializers to be re-used for different element names. + * + * If you are opening new elements, you must also close them again. + * + * @param Writer $writer + * @return void + */ + function xmlSerialize(Writer $writer) { + + foreach ($this->checksums as $checksum) { + $writer->writeElement('{' . self::NS_OWNCLOUD . '}checksum', $checksum); + } + } +} diff --git a/apps/dav/lib/connector/sabre/filesplugin.php b/apps/dav/lib/connector/sabre/filesplugin.php index 82d00014905..2e913ee1077 100644 --- a/apps/dav/lib/connector/sabre/filesplugin.php +++ b/apps/dav/lib/connector/sabre/filesplugin.php @@ -47,7 +47,7 @@ class FilesPlugin extends \Sabre\DAV\ServerPlugin { const LASTMODIFIED_PROPERTYNAME = '{DAV:}lastmodified'; const OWNER_ID_PROPERTYNAME = '{http://owncloud.org/ns}owner-id'; const OWNER_DISPLAY_NAME_PROPERTYNAME = '{http://owncloud.org/ns}owner-display-name'; - const CHECKSUM_PROPERTYNAME = '{http://owncloud.org/ns}checksum'; + const CHECKSUMS_PROPERTYNAME = '{http://owncloud.org/ns}checksums'; /** * Reference to main server object @@ -108,7 +108,7 @@ class FilesPlugin extends \Sabre\DAV\ServerPlugin { $server->protectedProperties[] = self::DOWNLOADURL_PROPERTYNAME; $server->protectedProperties[] = self::OWNER_ID_PROPERTYNAME; $server->protectedProperties[] = self::OWNER_DISPLAY_NAME_PROPERTYNAME; - $server->protectedProperties[] = self::CHECKSUM_PROPERTYNAME; + $server->protectedProperties[] = self::CHECKSUMS_PROPERTYNAME; // normally these cannot be changed (RFC4918), but we want them modifiable through PROPPATCH $allowedProperties = ['{DAV:}getetag']; @@ -248,13 +248,9 @@ class FilesPlugin extends \Sabre\DAV\ServerPlugin { return false; }); - $propFind->handle(self::CHECKSUM_PROPERTYNAME, function() use ($node) { + $propFind->handle(self::CHECKSUMS_PROPERTYNAME, function() use ($node) { $checksum = $node->getChecksum(); - - if ($checksum === null) { - return ''; - } - return $checksum; + return new ChecksumList($checksum); }); } diff --git a/apps/dav/lib/connector/sabre/filesreportplugin.php b/apps/dav/lib/connector/sabre/filesreportplugin.php new file mode 100644 index 00000000000..141b684360e --- /dev/null +++ b/apps/dav/lib/connector/sabre/filesreportplugin.php @@ -0,0 +1,332 @@ +<?php +/** + * @author Vincent Petry <pvince81@owncloud.com> + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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\Connector\Sabre; + +use OC\Files\View; +use Sabre\DAV\Exception\NotFound; +use Sabre\DAV\Exception\PreconditionFailed; +use Sabre\DAV\Exception\ReportNotSupported; +use Sabre\DAV\Exception\BadRequest; +use Sabre\DAV\ServerPlugin; +use Sabre\DAV\Tree; +use Sabre\DAV\Xml\Element\Response; +use Sabre\DAV\Xml\Response\MultiStatus; +use Sabre\DAV\PropFind; +use OCP\SystemTag\ISystemTagObjectMapper; +use OCP\IUserSession; +use OCP\Files\Folder; +use OCP\IGroupManager; +use OCP\SystemTag\ISystemTagManager; +use OCP\SystemTag\TagNotFoundException; + +class FilesReportPlugin extends ServerPlugin { + + // namespace + const NS_OWNCLOUD = 'http://owncloud.org/ns'; + const REPORT_NAME = '{http://owncloud.org/ns}filter-files'; + const SYSTEMTAG_PROPERTYNAME = '{http://owncloud.org/ns}systemtag'; + + /** + * Reference to main server object + * + * @var \Sabre\DAV\Server + */ + private $server; + + /** + * @var Tree + */ + private $tree; + + /** + * @var View + */ + private $fileView; + + /** + * @var ISystemTagManager + */ + private $tagManager; + + /** + * @var ISystemTagObjectMapper + */ + private $tagMapper; + + /** + * @var IUserSession + */ + private $userSession; + + /** + * @var IGroupManager + */ + private $groupManager; + + /** + * @var Folder + */ + private $userFolder; + + /** + * @param Tree $tree + * @param View $view + */ + public function __construct(Tree $tree, + View $view, + ISystemTagManager $tagManager, + ISystemTagObjectMapper $tagMapper, + IUserSession $userSession, + IGroupManager $groupManager, + Folder $userFolder + ) { + $this->tree = $tree; + $this->fileView = $view; + $this->tagManager = $tagManager; + $this->tagMapper = $tagMapper; + $this->userSession = $userSession; + $this->groupManager = $groupManager; + $this->userFolder = $userFolder; + } + + /** + * This initializes the plugin. + * + * This function is called by \Sabre\DAV\Server, after + * addPlugin is called. + * + * This method should set up the required event subscriptions. + * + * @param \Sabre\DAV\Server $server + * @return void + */ + public function initialize(\Sabre\DAV\Server $server) { + + $server->xml->namespaceMap[self::NS_OWNCLOUD] = 'oc'; + + $this->server = $server; + $this->server->on('report', array($this, 'onReport')); + } + + /** + * Returns a list of reports this plugin supports. + * + * This will be used in the {DAV:}supported-report-set property. + * + * @param string $uri + * @return array + */ + public function getSupportedReportSet($uri) { + return [self::REPORT_NAME]; + } + + /** + * REPORT operations to look for files + * + * @param string $reportName + * @param [] $report + * @param string $uri + * @return bool + * @throws NotFound + * @throws ReportNotSupported + */ + public function onReport($reportName, $report, $uri) { + $reportTargetNode = $this->server->tree->getNodeForPath($uri); + if (!$reportTargetNode instanceof Directory || $reportName !== self::REPORT_NAME) { + throw new ReportNotSupported(); + } + + $ns = '{' . $this::NS_OWNCLOUD . '}'; + $requestedProps = []; + $filterRules = []; + + // parse report properties and gather filter info + foreach ($report as $reportProps) { + $name = $reportProps['name']; + if ($name === $ns . 'filter-rules') { + $filterRules = $reportProps['value']; + } else if ($name === '{DAV:}prop') { + // propfind properties + foreach ($reportProps['value'] as $propVal) { + $requestedProps[] = $propVal['name']; + } + } + } + + if (empty($filterRules)) { + // an empty filter would return all existing files which would be slow + throw new BadRequest('Missing filter-rule block in request'); + } + + // gather all file ids matching filter + try { + $resultFileIds = $this->processFilterRules($filterRules); + } catch (TagNotFoundException $e) { + throw new PreconditionFailed('Cannot filter by non-existing tag', 0, $e); + } + + // find sabre nodes by file id, restricted to the root node path + $results = $this->findNodesByFileIds($reportTargetNode, $resultFileIds); + + $responses = $this->prepareResponses($requestedProps, $results); + + $xml = $this->server->xml->write( + '{DAV:}multistatus', + new MultiStatus($responses) + ); + + $this->server->httpResponse->setStatus(207); + $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8'); + $this->server->httpResponse->setBody($xml); + + return false; + } + + /** + * Find file ids matching the given filter rules + * + * @param array $filterRules + * @return array array of unique file id results + * + * @throws TagNotFoundException whenever a tag was not found + */ + public function processFilterRules($filterRules) { + $ns = '{' . $this::NS_OWNCLOUD . '}'; + $resultFileIds = null; + $systemTagIds = []; + foreach ($filterRules as $filterRule) { + if ($filterRule['name'] === $ns . 'systemtag') { + $systemTagIds[] = $filterRule['value']; + } + } + + // check user permissions, if applicable + if (!$this->isAdmin()) { + // check visibility/permission + $tags = $this->tagManager->getTagsByIds($systemTagIds); + $unknownTagIds = []; + foreach ($tags as $tag) { + if (!$tag->isUserVisible()) { + $unknownTagIds[] = $tag->getId(); + } + } + + if (!empty($unknownTagIds)) { + throw new TagNotFoundException('Tag with ids ' . implode(', ', $unknownTagIds) . ' not found'); + } + } + + // fetch all file ids and intersect them + foreach ($systemTagIds as $systemTagId) { + $fileIds = $this->tagMapper->getObjectIdsForTags($systemTagId, 'files'); + + if (empty($fileIds)) { + // This tag has no files, nothing can ever show up + return []; + } + + // first run ? + if ($resultFileIds === null) { + $resultFileIds = $fileIds; + } else { + $resultFileIds = array_intersect($resultFileIds, $fileIds); + } + + if (empty($resultFileIds)) { + // Empty intersection, nothing can show up anymore + return []; + } + } + return $resultFileIds; + } + + /** + * Prepare propfind response for the given nodes + * + * @param string[] $requestedProps requested properties + * @param Node[] nodes nodes for which to fetch and prepare responses + * @return Response[] + */ + public function prepareResponses($requestedProps, $nodes) { + $responses = []; + foreach ($nodes as $node) { + $propFind = new PropFind($node->getPath(), $requestedProps); + + $this->server->getPropertiesByNode($propFind, $node); + // copied from Sabre Server's getPropertiesForPath + $result = $propFind->getResultForMultiStatus(); + $result['href'] = $propFind->getPath(); + + $resourceType = $this->server->getResourceTypeForNode($node); + if (in_array('{DAV:}collection', $resourceType) || in_array('{DAV:}principal', $resourceType)) { + $result['href'] .= '/'; + } + + $responses[] = new Response( + rtrim($this->server->getBaseUri(), '/') . $node->getPath(), + $result, + 200 + ); + } + return $responses; + } + + /** + * Find Sabre nodes by file ids + * + * @param Node $rootNode root node for search + * @param array $fileIds file ids + * @return Node[] array of Sabre nodes + */ + public function findNodesByFileIds($rootNode, $fileIds) { + $folder = $this->userFolder; + if (trim($rootNode->getPath(), '/') !== '') { + $folder = $folder->get($rootNode->getPath()); + } + + $results = []; + foreach ($fileIds as $fileId) { + $entry = $folder->getById($fileId); + if ($entry) { + $entry = current($entry); + if ($entry instanceof \OCP\Files\File) { + $results[] = new File($this->fileView, $entry); + } else if ($entry instanceof \OCP\Files\Folder) { + $results[] = new Directory($this->fileView, $entry); + } + } + } + + return $results; + } + + /** + * Returns whether the currently logged in user is an administrator + */ + private function isAdmin() { + $user = $this->userSession->getUser(); + if ($user !== null) { + return $this->groupManager->isAdmin($user->getUID()); + } + return false; + } +} diff --git a/apps/dav/lib/connector/sabre/principal.php b/apps/dav/lib/connector/sabre/principal.php index 4f26390e3cc..a573124007d 100644 --- a/apps/dav/lib/connector/sabre/principal.php +++ b/apps/dav/lib/connector/sabre/principal.php @@ -49,6 +49,9 @@ class Principal implements BackendInterface { /** @var string */ private $principalPrefix; + /** @var bool */ + private $hasGroups; + /** * @param IUserManager $userManager * @param IGroupManager $groupManager @@ -60,6 +63,7 @@ class Principal implements BackendInterface { $this->userManager = $userManager; $this->groupManager = $groupManager; $this->principalPrefix = trim($principalPrefix, '/'); + $this->hasGroups = ($principalPrefix === 'principals/users/'); } /** @@ -141,13 +145,15 @@ class Principal implements BackendInterface { throw new Exception('Principal not found'); } - $groups = $this->groupManager->getUserGroups($user); - $groups = array_map(function($group) { - /** @var IGroup $group */ - return $this->principalPrefix . '/' . $group->getGID(); - }, $groups); + if ($this->hasGroups) { + $groups = $this->groupManager->getUserGroups($user); + $groups = array_map(function($group) { + /** @var IGroup $group */ + return 'principals/groups/' . $group->getGID(); + }, $groups); - return $groups; + return $groups; + } } return []; } diff --git a/apps/dav/lib/connector/sabre/serverfactory.php b/apps/dav/lib/connector/sabre/serverfactory.php index fa4fda46870..8253948d96f 100644 --- a/apps/dav/lib/connector/sabre/serverfactory.php +++ b/apps/dav/lib/connector/sabre/serverfactory.php @@ -115,10 +115,10 @@ class ServerFactory { // wait with registering these until auth is handled and the filesystem is setup $server->on('beforeMethod', function () use ($server, $objectTree, $viewCallBack) { // ensure the skeleton is copied - \OC::$server->getUserFolder(); + $userFolder = \OC::$server->getUserFolder(); /** @var \OC\Files\View $view */ - $view = $viewCallBack(); + $view = $viewCallBack($server); $rootInfo = $view->getFileInfo(''); // Create ownCloud Dir @@ -135,6 +135,15 @@ class ServerFactory { if($this->userSession->isLoggedIn()) { $server->addPlugin(new \OCA\DAV\Connector\Sabre\TagsPlugin($objectTree, $this->tagManager)); $server->addPlugin(new \OCA\DAV\Connector\Sabre\CommentPropertiesPlugin(\OC::$server->getCommentsManager(), $this->userSession)); + $server->addPlugin(new \OCA\DAV\Connector\Sabre\FilesReportPlugin( + $objectTree, + $view, + \OC::$server->getSystemTagManager(), + \OC::$server->getSystemTagObjectMapper(), + $this->userSession, + \OC::$server->getGroupManager(), + $userFolder + )); // custom properties plugin must be the last one $server->addPlugin( new \Sabre\DAV\PropertyStorage\Plugin( diff --git a/apps/dav/lib/files/sharing/publiclinkcheckplugin.php b/apps/dav/lib/files/sharing/publiclinkcheckplugin.php new file mode 100644 index 00000000000..bbb5c611204 --- /dev/null +++ b/apps/dav/lib/files/sharing/publiclinkcheckplugin.php @@ -0,0 +1,63 @@ +<?php +/** + * @author Robin Appelman <icewind@owncloud.com> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @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\Files\Sharing; + +use OCP\Files\FileInfo; +use Sabre\DAV\Exception\NotFound; +use Sabre\DAV\ServerPlugin; +use Sabre\HTTP\RequestInterface; +use Sabre\HTTP\ResponseInterface; + +/** + * Verify that the public link share is valid + */ +class PublicLinkCheckPlugin extends ServerPlugin { + /** + * @var FileInfo + */ + private $fileInfo; + + /** + * @param FileInfo $fileInfo + */ + public function setFileInfo($fileInfo) { + $this->fileInfo = $fileInfo; + } + + /** + * This initializes the plugin. + * + * @param \Sabre\DAV\Server $server Sabre server + * + * @return void + */ + public function initialize(\Sabre\DAV\Server $server) { + $server->on('beforeMethod', [$this, 'beforeMethod']); + } + + public function beforeMethod(RequestInterface $request, ResponseInterface $response){ + // verify that the owner didn't have his share permissions revoked + if ($this->fileInfo && !$this->fileInfo->isShareable()) { + throw new NotFound(); + } + } +} diff --git a/apps/dav/lib/migration/calendaradapter.php b/apps/dav/lib/migration/calendaradapter.php new file mode 100644 index 00000000000..39faf2cec37 --- /dev/null +++ b/apps/dav/lib/migration/calendaradapter.php @@ -0,0 +1,83 @@ +<?php + +namespace OCA\Dav\Migration; + +use OCP\IDBConnection; + +class CalendarAdapter { + + /** @var \OCP\IDBConnection */ + protected $dbConnection; + + /** @var string */ + private $sourceCalendarTable; + + /** @var string */ + private $sourceCalObjTable; + + /** + * @param IDBConnection $dbConnection + * @param string $sourceCalendarTable + * @param string $sourceCalObjTable + */ + function __construct(IDBConnection $dbConnection, + $sourceCalendarTable = 'clndr_calendars', + $sourceCalObjTable = 'clndr_objects') { + $this->dbConnection = $dbConnection; + $this->sourceCalendarTable = $sourceCalendarTable; + $this->sourceCalObjTable = $sourceCalObjTable; + } + + /** + * @param string $user + * @param \Closure $callBack + */ + public function foreachCalendar($user, \Closure $callBack) { + // get all calendars of that user + $query = $this->dbConnection->getQueryBuilder(); + $stmt = $query->select('*')->from($this->sourceCalendarTable) + ->where($query->expr()->eq('userid', $query->createNamedParameter($user))) + ->execute(); + + while($row = $stmt->fetch()) { + $callBack($row); + } + } + + public function setup() { + if (!$this->dbConnection->tableExists($this->sourceCalendarTable)) { + throw new \DomainException('Calendar tables are missing. Nothing to do.'); + } + } + + /** + * @param int $calendarId + * @param \Closure $callBack + */ + public function foreachCalendarObject($calendarId, \Closure $callBack) { + $query = $this->dbConnection->getQueryBuilder(); + $stmt = $query->select('*')->from($this->sourceCalObjTable) + ->where($query->expr()->eq('calendarid', $query->createNamedParameter($calendarId))) + ->execute(); + + while($row = $stmt->fetch()) { + $callBack($row); + } + } + + /** + * @param int $addressBookId + * @return array + */ + public function getShares($addressBookId) { + $query = $this->dbConnection->getQueryBuilder(); + $shares = $query->select('*')->from('share') + ->where($query->expr()->eq('item_source', $query->createNamedParameter($addressBookId))) + ->andWhere($query->expr()->eq('item_type', $query->expr()->literal('calendar'))) + ->andWhere($query->expr()->in('share_type', [ $query->expr()->literal(0), $query->expr()->literal(1)])) + ->execute() + ->fetchAll(); + + return $shares; + } +} diff --git a/apps/dav/lib/migration/migratecalendars.php b/apps/dav/lib/migration/migratecalendars.php new file mode 100644 index 00000000000..33f8a105180 --- /dev/null +++ b/apps/dav/lib/migration/migratecalendars.php @@ -0,0 +1,94 @@ +<?php + +namespace OCA\Dav\Migration; + +use OCA\DAV\CalDAV\CalDavBackend; +use OCA\DAV\CalDAV\Calendar; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; + +class MigrateCalendars { + + /** @var CalendarAdapter */ + protected $adapter; + + /** @var CalDavBackend */ + private $backend; + + /** + * @param CalendarAdapter $adapter + * @param CalDavBackend $backend + */ + function __construct(CalendarAdapter $adapter, + CalDavBackend $backend + ) { + $this->adapter = $adapter; + $this->backend = $backend; + } + + /** + * @param string $user + */ + public function migrateForUser($user) { + + $this->adapter->foreachCalendar($user, function($calendar) use ($user) { + $principal = "principals/users/$user"; + $calendarByUri = $this->backend->getCalendarByUri($principal, $calendar['uri']); + if (!is_null($calendarByUri)) { + return; + } + + $newId = $this->backend->createCalendar($principal, $calendar['uri'], [ + '{DAV:}displayname' => $calendar['displayname'], + '{urn:ietf:params:xml:ns:caldav}calendar-description' => $calendar['displayname'], + '{urn:ietf:params:xml:ns:caldav}calendar-timezone' => $calendar['timezone'], + '{http://apple.com/ns/ical/}calendar-order' => $calendar['calendarorder'], + '{http://apple.com/ns/ical/}calendar-color' => $calendar['calendarcolor'], + ]); + + $this->migrateCalendar($calendar['id'], $newId); + $this->migrateShares($calendar['id'], $newId); + }); + } + + public function setup() { + $this->adapter->setup(); + } + + /** + * @param int $calendarId + * @param int $newCalendarId + */ + private function migrateCalendar($calendarId, $newCalendarId) { + $this->adapter->foreachCalendarObject($calendarId, function($calObject) use ($newCalendarId) { + $this->backend->createCalendarObject($newCalendarId, $calObject['uri'], $calObject['calendardata']); + }); + } + + /** + * @param int $calendarId + * @param int $newCalendarId + */ + private function migrateShares($calendarId, $newCalendarId) { + $shares =$this->adapter->getShares($calendarId); + if (empty($shares)) { + return; + } + + $add = array_map(function($s) { + $prefix = 'principal:principals/users/'; + if ($s['share_type'] === 1) { + $prefix = 'principal:principals/groups/'; + } + return [ + 'href' => $prefix . $s['share_with'] + ]; + }, $shares); + + $newCalendar = $this->backend->getCalendarById($newCalendarId); + $calendar = new Calendar($this->backend, $newCalendar); + $this->backend->updateShares($calendar, $add, []); + } +} diff --git a/apps/dav/tests/unit/comments/commentnode.php b/apps/dav/tests/unit/comments/commentnode.php index 8d1bf06ab60..8ebc5c2ff2c 100644 --- a/apps/dav/tests/unit/comments/commentnode.php +++ b/apps/dav/tests/unit/comments/commentnode.php @@ -22,6 +22,8 @@ namespace OCA\DAV\Tests\Unit\Comments; use OCA\DAV\Comments\CommentNode; +use OCP\Comments\IComment; +use OCP\Comments\MessageTooLongException; class CommentsNode extends \Test\TestCase { @@ -199,6 +201,43 @@ class CommentsNode extends \Test\TestCase { } /** + * @expectedException \Sabre\DAV\Exception\BadRequest + * @expectedExceptionMessage Message exceeds allowed character limit of + */ + public function testUpdateCommentMessageTooLongException() { + $user = $this->getMock('\OCP\IUser'); + + $user->expects($this->once()) + ->method('getUID') + ->will($this->returnValue('alice')); + + $this->userSession->expects($this->once()) + ->method('getUser') + ->will($this->returnValue($user)); + + $this->comment->expects($this->once()) + ->method('setMessage') + ->will($this->throwException(new MessageTooLongException())); + + $this->comment->expects($this->any()) + ->method('getActorType') + ->will($this->returnValue('users')); + + $this->comment->expects($this->any()) + ->method('getActorId') + ->will($this->returnValue('alice')); + + $this->commentsManager->expects($this->never()) + ->method('save'); + + $this->logger->expects($this->once()) + ->method('logException'); + + // imagine 'foo' has >1k characters. comment is mocked anyway. + $this->node->updateComment('foo'); + } + + /** * @expectedException \Sabre\DAV\Exception\Forbidden */ public function testUpdateForbiddenByUser() { diff --git a/apps/dav/tests/unit/comments/commentsplugin.php b/apps/dav/tests/unit/comments/commentsplugin.php index 9822137bbea..d6f489f5e80 100644 --- a/apps/dav/tests/unit/comments/commentsplugin.php +++ b/apps/dav/tests/unit/comments/commentsplugin.php @@ -23,6 +23,7 @@ namespace OCA\DAV\Tests\Unit\Comments; use OC\Comments\Comment; use OCA\DAV\Comments\CommentsPlugin as CommentsPluginImplementation; +use OCP\Comments\IComment; use Sabre\DAV\Exception\NotFound; class CommentsPlugin extends \Test\TestCase { @@ -506,6 +507,98 @@ class CommentsPlugin extends \Test\TestCase { } /** + * @expectedException \Sabre\DAV\Exception\BadRequest + * @expectedExceptionMessage Message exceeds allowed character limit of + */ + public function testCreateCommentMessageTooLong() { + $commentData = [ + 'actorType' => 'users', + 'verb' => 'comment', + 'message' => str_pad('', IComment::MAX_MESSAGE_LENGTH + 1, 'x'), + ]; + + $comment = new Comment([ + 'objectType' => 'files', + 'objectId' => '42', + 'actorType' => 'users', + 'actorId' => 'alice', + 'verb' => 'comment', + ]); + $comment->setId('23'); + + $path = 'comments/files/42'; + + $requestData = json_encode($commentData); + + $user = $this->getMock('OCP\IUser'); + $user->expects($this->once()) + ->method('getUID') + ->will($this->returnValue('alice')); + + $node = $this->getMockBuilder('\OCA\DAV\Comments\EntityCollection') + ->disableOriginalConstructor() + ->getMock(); + $node->expects($this->once()) + ->method('getName') + ->will($this->returnValue('files')); + $node->expects($this->once()) + ->method('getId') + ->will($this->returnValue('42')); + + $node->expects($this->never()) + ->method('setReadMarker'); + + $this->commentsManager->expects($this->once()) + ->method('create') + ->with('users', 'alice', 'files', '42') + ->will($this->returnValue($comment)); + + $this->userSession->expects($this->once()) + ->method('getUser') + ->will($this->returnValue($user)); + + // technically, this is a shortcut. Inbetween EntityTypeCollection would + // be returned, but doing it exactly right would not be really + // unit-testing like, as it would require to haul in a lot of other + // things. + $this->tree->expects($this->any()) + ->method('getNodeForPath') + ->with('/' . $path) + ->will($this->returnValue($node)); + + $request = $this->getMockBuilder('Sabre\HTTP\RequestInterface') + ->disableOriginalConstructor() + ->getMock(); + + $response = $this->getMockBuilder('Sabre\HTTP\ResponseInterface') + ->disableOriginalConstructor() + ->getMock(); + + $request->expects($this->once()) + ->method('getPath') + ->will($this->returnValue('/' . $path)); + + $request->expects($this->once()) + ->method('getBodyAsString') + ->will($this->returnValue($requestData)); + + $request->expects($this->once()) + ->method('getHeader') + ->with('Content-Type') + ->will($this->returnValue('application/json')); + + $response->expects($this->never()) + ->method('setHeader'); + + $this->server->expects($this->any()) + ->method('getRequestUri') + ->will($this->returnValue($path)); + $this->plugin->initialize($this->server); + + $this->plugin->httpPost($request, $response); + } + + /** * @expectedException \Sabre\DAV\Exception\ReportNotSupported */ public function testOnReportInvalidNode() { diff --git a/apps/dav/tests/unit/connector/sabre/filesreportplugin.php b/apps/dav/tests/unit/connector/sabre/filesreportplugin.php new file mode 100644 index 00000000000..b528e2d2427 --- /dev/null +++ b/apps/dav/tests/unit/connector/sabre/filesreportplugin.php @@ -0,0 +1,602 @@ +<?php +/** + * @author Vincent Petry <pvince81@owncloud.com> + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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\Tests\Unit\Sabre\Connector; + +use OCA\DAV\Connector\Sabre\FilesReportPlugin as FilesReportPluginImplementation; +use Sabre\DAV\Exception\NotFound; +use OCP\SystemTag\ISystemTagObjectMapper; +use OC\Files\View; +use OCP\Files\Folder; +use OCP\IGroupManager; +use OCP\SystemTag\ISystemTagManager; + +class FilesReportPlugin extends \Test\TestCase { + /** @var \Sabre\DAV\Server|\PHPUnit_Framework_MockObject_MockObject */ + private $server; + + /** @var \Sabre\DAV\Tree|\PHPUnit_Framework_MockObject_MockObject */ + private $tree; + + /** @var ISystemTagObjectMapper|\PHPUnit_Framework_MockObject_MockObject */ + private $tagMapper; + + /** @var ISystemTagManager|\PHPUnit_Framework_MockObject_MockObject */ + private $tagManager; + + /** @var \OCP\IUserSession */ + private $userSession; + + /** @var FilesReportPluginImplementation */ + private $plugin; + + /** @var View|\PHPUnit_Framework_MockObject_MockObject **/ + private $view; + + /** @var IGroupManager|\PHPUnit_Framework_MockObject_MockObject **/ + private $groupManager; + + /** @var Folder|\PHPUnit_Framework_MockObject_MockObject **/ + private $userFolder; + + public function setUp() { + parent::setUp(); + $this->tree = $this->getMockBuilder('\Sabre\DAV\Tree') + ->disableOriginalConstructor() + ->getMock(); + + $this->view = $this->getMockBuilder('\OC\Files\View') + ->disableOriginalConstructor() + ->getMock(); + + $this->server = $this->getMockBuilder('\Sabre\DAV\Server') + ->setConstructorArgs([$this->tree]) + ->setMethods(['getRequestUri']) + ->getMock(); + + $this->groupManager = $this->getMockBuilder('\OCP\IGroupManager') + ->disableOriginalConstructor() + ->getMock(); + + $this->userFolder = $this->getMockBuilder('\OCP\Files\Folder') + ->disableOriginalConstructor() + ->getMock(); + + $this->tagManager = $this->getMock('\OCP\SystemTag\ISystemTagManager'); + $this->tagMapper = $this->getMock('\OCP\SystemTag\ISystemTagObjectMapper'); + $this->userSession = $this->getMock('\OCP\IUserSession'); + + $user = $this->getMock('\OCP\IUser'); + $user->expects($this->any()) + ->method('getUID') + ->will($this->returnValue('testuser')); + $this->userSession->expects($this->any()) + ->method('getUser') + ->will($this->returnValue($user)); + + $this->plugin = new FilesReportPluginImplementation( + $this->tree, + $this->view, + $this->tagManager, + $this->tagMapper, + $this->userSession, + $this->groupManager, + $this->userFolder + ); + } + + /** + * @expectedException \Sabre\DAV\Exception\ReportNotSupported + */ + public function testOnReportInvalidNode() { + $path = 'totally/unrelated/13'; + + $this->tree->expects($this->any()) + ->method('getNodeForPath') + ->with('/' . $path) + ->will($this->returnValue($this->getMock('\Sabre\DAV\INode'))); + + $this->server->expects($this->any()) + ->method('getRequestUri') + ->will($this->returnValue($path)); + $this->plugin->initialize($this->server); + + $this->plugin->onReport(FilesReportPluginImplementation::REPORT_NAME, [], '/' . $path); + } + + /** + * @expectedException \Sabre\DAV\Exception\ReportNotSupported + */ + public function testOnReportInvalidReportName() { + $path = 'test'; + + $this->tree->expects($this->any()) + ->method('getNodeForPath') + ->with('/' . $path) + ->will($this->returnValue($this->getMock('\Sabre\DAV\INode'))); + + $this->server->expects($this->any()) + ->method('getRequestUri') + ->will($this->returnValue($path)); + $this->plugin->initialize($this->server); + + $this->plugin->onReport('{whoever}whatever', [], '/' . $path); + } + + public function testOnReport() { + $path = 'test'; + + $parameters = [ + [ + 'name' => '{DAV:}prop', + 'value' => [ + ['name' => '{DAV:}getcontentlength', 'value' => ''], + ['name' => '{http://owncloud.org/ns}size', 'value' => ''], + ], + ], + [ + 'name' => '{http://owncloud.org/ns}filter-rules', + 'value' => [ + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'], + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'], + ], + ], + ]; + + $this->groupManager->expects($this->any()) + ->method('isAdmin') + ->will($this->returnValue(true)); + + $this->tagMapper->expects($this->at(0)) + ->method('getObjectIdsForTags') + ->with('123', 'files') + ->will($this->returnValue(['111', '222'])); + $this->tagMapper->expects($this->at(1)) + ->method('getObjectIdsForTags') + ->with('456', 'files') + ->will($this->returnValue(['111', '222', '333'])); + + $reportTargetNode = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Directory') + ->disableOriginalConstructor() + ->getMock(); + + $response = $this->getMockBuilder('Sabre\HTTP\ResponseInterface') + ->disableOriginalConstructor() + ->getMock(); + + $response->expects($this->once()) + ->method('setHeader') + ->with('Content-Type', 'application/xml; charset=utf-8'); + + $response->expects($this->once()) + ->method('setStatus') + ->with(207); + + $response->expects($this->once()) + ->method('setBody'); + + $this->tree->expects($this->any()) + ->method('getNodeForPath') + ->with('/' . $path) + ->will($this->returnValue($reportTargetNode)); + + $filesNode1 = $this->getMockBuilder('\OCP\Files\Folder') + ->disableOriginalConstructor() + ->getMock(); + $filesNode2 = $this->getMockBuilder('\OCP\Files\File') + ->disableOriginalConstructor() + ->getMock(); + + $this->userFolder->expects($this->at(0)) + ->method('getById') + ->with('111') + ->will($this->returnValue([$filesNode1])); + $this->userFolder->expects($this->at(1)) + ->method('getById') + ->with('222') + ->will($this->returnValue([$filesNode2])); + + $this->server->expects($this->any()) + ->method('getRequestUri') + ->will($this->returnValue($path)); + $this->server->httpResponse = $response; + $this->plugin->initialize($this->server); + + $this->plugin->onReport(FilesReportPluginImplementation::REPORT_NAME, $parameters, '/' . $path); + } + + public function testFindNodesByFileIdsRoot() { + $filesNode1 = $this->getMockBuilder('\OCP\Files\Folder') + ->disableOriginalConstructor() + ->getMock(); + $filesNode1->expects($this->once()) + ->method('getName') + ->will($this->returnValue('first node')); + + $filesNode2 = $this->getMockBuilder('\OCP\Files\File') + ->disableOriginalConstructor() + ->getMock(); + $filesNode2->expects($this->once()) + ->method('getName') + ->will($this->returnValue('second node')); + + $reportTargetNode = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Directory') + ->disableOriginalConstructor() + ->getMock(); + $reportTargetNode->expects($this->any()) + ->method('getPath') + ->will($this->returnValue('/')); + + $this->userFolder->expects($this->at(0)) + ->method('getById') + ->with('111') + ->will($this->returnValue([$filesNode1])); + $this->userFolder->expects($this->at(1)) + ->method('getById') + ->with('222') + ->will($this->returnValue([$filesNode2])); + + /** @var \OCA\DAV\Connector\Sabre\Directory|\PHPUnit_Framework_MockObject_MockObject $reportTargetNode */ + $result = $this->plugin->findNodesByFileIds($reportTargetNode, ['111', '222']); + + $this->assertCount(2, $result); + $this->assertInstanceOf('\OCA\DAV\Connector\Sabre\Directory', $result[0]); + $this->assertEquals('first node', $result[0]->getName()); + $this->assertInstanceOf('\OCA\DAV\Connector\Sabre\File', $result[1]); + $this->assertEquals('second node', $result[1]->getName()); + } + + public function testFindNodesByFileIdsSubDir() { + $filesNode1 = $this->getMockBuilder('\OCP\Files\Folder') + ->disableOriginalConstructor() + ->getMock(); + $filesNode1->expects($this->once()) + ->method('getName') + ->will($this->returnValue('first node')); + + $filesNode2 = $this->getMockBuilder('\OCP\Files\File') + ->disableOriginalConstructor() + ->getMock(); + $filesNode2->expects($this->once()) + ->method('getName') + ->will($this->returnValue('second node')); + + $reportTargetNode = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Directory') + ->disableOriginalConstructor() + ->getMock(); + $reportTargetNode->expects($this->any()) + ->method('getPath') + ->will($this->returnValue('/sub1/sub2')); + + + $subNode = $this->getMockBuilder('\OCP\Files\Folder') + ->disableOriginalConstructor() + ->getMock(); + + $this->userFolder->expects($this->at(0)) + ->method('get') + ->with('/sub1/sub2') + ->will($this->returnValue($subNode)); + + $subNode->expects($this->at(0)) + ->method('getById') + ->with('111') + ->will($this->returnValue([$filesNode1])); + $subNode->expects($this->at(1)) + ->method('getById') + ->with('222') + ->will($this->returnValue([$filesNode2])); + + /** @var \OCA\DAV\Connector\Sabre\Directory|\PHPUnit_Framework_MockObject_MockObject $reportTargetNode */ + $result = $this->plugin->findNodesByFileIds($reportTargetNode, ['111', '222']); + + $this->assertCount(2, $result); + $this->assertInstanceOf('\OCA\DAV\Connector\Sabre\Directory', $result[0]); + $this->assertEquals('first node', $result[0]->getName()); + $this->assertInstanceOf('\OCA\DAV\Connector\Sabre\File', $result[1]); + $this->assertEquals('second node', $result[1]->getName()); + } + + public function testPrepareResponses() { + $requestedProps = ['{DAV:}getcontentlength', '{http://owncloud.org/ns}fileid', '{DAV:}resourcetype']; + + $node1 = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Directory') + ->disableOriginalConstructor() + ->getMock(); + $node2 = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\File') + ->disableOriginalConstructor() + ->getMock(); + + $node1->expects($this->once()) + ->method('getInternalFileId') + ->will($this->returnValue('111')); + $node2->expects($this->once()) + ->method('getInternalFileId') + ->will($this->returnValue('222')); + $node2->expects($this->once()) + ->method('getSize') + ->will($this->returnValue(1024)); + + $this->server->addPlugin(new \OCA\DAV\Connector\Sabre\FilesPlugin($this->tree, $this->view)); + $this->plugin->initialize($this->server); + $responses = $this->plugin->prepareResponses($requestedProps, [$node1, $node2]); + + $this->assertCount(2, $responses); + + $this->assertEquals(200, $responses[0]->getHttpStatus()); + $this->assertEquals(200, $responses[1]->getHttpStatus()); + + $props1 = $responses[0]->getResponseProperties(); + $this->assertEquals('111', $props1[200]['{http://owncloud.org/ns}fileid']); + $this->assertNull($props1[404]['{DAV:}getcontentlength']); + $this->assertInstanceOf('\Sabre\DAV\Xml\Property\ResourceType', $props1[200]['{DAV:}resourcetype']); + $resourceType1 = $props1[200]['{DAV:}resourcetype']->getValue(); + $this->assertEquals('{DAV:}collection', $resourceType1[0]); + + $props2 = $responses[1]->getResponseProperties(); + $this->assertEquals('1024', $props2[200]['{DAV:}getcontentlength']); + $this->assertEquals('222', $props2[200]['{http://owncloud.org/ns}fileid']); + $this->assertInstanceOf('\Sabre\DAV\Xml\Property\ResourceType', $props2[200]['{DAV:}resourcetype']); + $this->assertCount(0, $props2[200]['{DAV:}resourcetype']->getValue()); + } + + public function testProcessFilterRulesSingle() { + $this->groupManager->expects($this->any()) + ->method('isAdmin') + ->will($this->returnValue(true)); + + $this->tagMapper->expects($this->exactly(1)) + ->method('getObjectIdsForTags') + ->withConsecutive( + ['123', 'files'] + ) + ->willReturnMap([ + ['123', 'files', ['111', '222']], + ]); + + $rules = [ + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'], + ]; + + $this->assertEquals(['111', '222'], $this->plugin->processFilterRules($rules)); + } + + public function testProcessFilterRulesAndCondition() { + $this->groupManager->expects($this->any()) + ->method('isAdmin') + ->will($this->returnValue(true)); + + $this->tagMapper->expects($this->exactly(2)) + ->method('getObjectIdsForTags') + ->withConsecutive( + ['123', 'files'], + ['456', 'files'] + ) + ->willReturnMap([ + ['123', 'files', ['111', '222']], + ['456', 'files', ['222', '333']], + ]); + + $rules = [ + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'], + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'], + ]; + + $this->assertEquals(['222'], array_values($this->plugin->processFilterRules($rules))); + } + + public function testProcessFilterRulesAndConditionWithOneEmptyResult() { + $this->groupManager->expects($this->any()) + ->method('isAdmin') + ->will($this->returnValue(true)); + + $this->tagMapper->expects($this->exactly(2)) + ->method('getObjectIdsForTags') + ->withConsecutive( + ['123', 'files'], + ['456', 'files'] + ) + ->willReturnMap([ + ['123', 'files', ['111', '222']], + ['456', 'files', []], + ]); + + $rules = [ + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'], + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'], + ]; + + $this->assertEquals([], array_values($this->plugin->processFilterRules($rules))); + } + + public function testProcessFilterRulesAndConditionWithFirstEmptyResult() { + $this->groupManager->expects($this->any()) + ->method('isAdmin') + ->will($this->returnValue(true)); + + $this->tagMapper->expects($this->exactly(1)) + ->method('getObjectIdsForTags') + ->withConsecutive( + ['123', 'files'], + ['456', 'files'] + ) + ->willReturnMap([ + ['123', 'files', []], + ['456', 'files', ['111', '222']], + ]); + + $rules = [ + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'], + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'], + ]; + + $this->assertEquals([], array_values($this->plugin->processFilterRules($rules))); + } + + public function testProcessFilterRulesAndConditionWithEmptyMidResult() { + $this->groupManager->expects($this->any()) + ->method('isAdmin') + ->will($this->returnValue(true)); + + $this->tagMapper->expects($this->exactly(2)) + ->method('getObjectIdsForTags') + ->withConsecutive( + ['123', 'files'], + ['456', 'files'], + ['789', 'files'] + ) + ->willReturnMap([ + ['123', 'files', ['111', '222']], + ['456', 'files', ['333']], + ['789', 'files', ['111', '222']], + ]); + + $rules = [ + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'], + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'], + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '789'], + ]; + + $this->assertEquals([], array_values($this->plugin->processFilterRules($rules))); + } + + public function testProcessFilterRulesInvisibleTagAsAdmin() { + $this->groupManager->expects($this->any()) + ->method('isAdmin') + ->will($this->returnValue(true)); + + $tag1 = $this->getMock('\OCP\SystemTag\ISystemTag'); + $tag1->expects($this->any()) + ->method('getId') + ->will($this->returnValue('123')); + $tag1->expects($this->any()) + ->method('isUserVisible') + ->will($this->returnValue(true)); + + $tag2 = $this->getMock('\OCP\SystemTag\ISystemTag'); + $tag2->expects($this->any()) + ->method('getId') + ->will($this->returnValue('123')); + $tag2->expects($this->any()) + ->method('isUserVisible') + ->will($this->returnValue(false)); + + // no need to fetch tags to check permissions + $this->tagManager->expects($this->never()) + ->method('getTagsByIds'); + + $this->tagMapper->expects($this->at(0)) + ->method('getObjectIdsForTags') + ->with('123') + ->will($this->returnValue(['111', '222'])); + $this->tagMapper->expects($this->at(1)) + ->method('getObjectIdsForTags') + ->with('456') + ->will($this->returnValue(['222', '333'])); + + $rules = [ + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'], + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'], + ]; + + $this->assertEquals(['222'], array_values($this->plugin->processFilterRules($rules))); + } + + /** + * @expectedException \OCP\SystemTag\TagNotFoundException + */ + public function testProcessFilterRulesInvisibleTagAsUser() { + $this->groupManager->expects($this->any()) + ->method('isAdmin') + ->will($this->returnValue(false)); + + $tag1 = $this->getMock('\OCP\SystemTag\ISystemTag'); + $tag1->expects($this->any()) + ->method('getId') + ->will($this->returnValue('123')); + $tag1->expects($this->any()) + ->method('isUserVisible') + ->will($this->returnValue(true)); + + $tag2 = $this->getMock('\OCP\SystemTag\ISystemTag'); + $tag2->expects($this->any()) + ->method('getId') + ->will($this->returnValue('123')); + $tag2->expects($this->any()) + ->method('isUserVisible') + ->will($this->returnValue(false)); // invisible + + $this->tagManager->expects($this->once()) + ->method('getTagsByIds') + ->with(['123', '456']) + ->will($this->returnValue([$tag1, $tag2])); + + $rules = [ + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'], + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'], + ]; + + $this->plugin->processFilterRules($rules); + } + + public function testProcessFilterRulesVisibleTagAsUser() { + $this->groupManager->expects($this->any()) + ->method('isAdmin') + ->will($this->returnValue(false)); + + $tag1 = $this->getMock('\OCP\SystemTag\ISystemTag'); + $tag1->expects($this->any()) + ->method('getId') + ->will($this->returnValue('123')); + $tag1->expects($this->any()) + ->method('isUserVisible') + ->will($this->returnValue(true)); + + $tag2 = $this->getMock('\OCP\SystemTag\ISystemTag'); + $tag2->expects($this->any()) + ->method('getId') + ->will($this->returnValue('123')); + $tag2->expects($this->any()) + ->method('isUserVisible') + ->will($this->returnValue(true)); + + $this->tagManager->expects($this->once()) + ->method('getTagsByIds') + ->with(['123', '456']) + ->will($this->returnValue([$tag1, $tag2])); + + $this->tagMapper->expects($this->at(0)) + ->method('getObjectIdsForTags') + ->with('123') + ->will($this->returnValue(['111', '222'])); + $this->tagMapper->expects($this->at(1)) + ->method('getObjectIdsForTags') + ->with('456') + ->will($this->returnValue(['222', '333'])); + + $rules = [ + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '123'], + ['name' => '{http://owncloud.org/ns}systemtag', 'value' => '456'], + ]; + + $this->assertEquals(['222'], array_values($this->plugin->processFilterRules($rules))); + } +} diff --git a/apps/dav/tests/unit/connector/sabre/principal.php b/apps/dav/tests/unit/connector/sabre/principal.php index 07bfd5d263b..1747885240a 100644 --- a/apps/dav/tests/unit/connector/sabre/principal.php +++ b/apps/dav/tests/unit/connector/sabre/principal.php @@ -202,16 +202,26 @@ class Principal extends TestCase { public function testGetGroupMembership() { $fooUser = $this->getMockBuilder('\OC\User\User') ->disableOriginalConstructor()->getMock(); + $group = $this->getMockBuilder('\OCP\IGroup') + ->disableOriginalConstructor()->getMock(); + $group->expects($this->once()) + ->method('getGID') + ->willReturn('group1'); $this->userManager ->expects($this->once()) ->method('get') ->with('foo') ->willReturn($fooUser); $this->groupManager + ->expects($this->once()) ->method('getUserGroups') - ->willReturn([]); + ->willReturn([ + $group + ]); - $expectedResponse = []; + $expectedResponse = [ + 'principals/groups/group1' + ]; $response = $this->connector->getGroupMembership('principals/users/foo'); $this->assertSame($expectedResponse, $response); } diff --git a/apps/dav/tests/unit/migration/calendar_schema.xml b/apps/dav/tests/unit/migration/calendar_schema.xml new file mode 100644 index 00000000000..6c88b596a3f --- /dev/null +++ b/apps/dav/tests/unit/migration/calendar_schema.xml @@ -0,0 +1,191 @@ +<?xml version="1.0" encoding="ISO-8859-1" ?> +<database> + + <name>*dbname*</name> + <create>true</create> + <overwrite>false</overwrite> + + <charset>utf8</charset> + + <table> + + <name>*dbprefix*clndr_objects</name> + + <declaration> + + <field> + <name>id</name> + <type>integer</type> + <default>0</default> + <notnull>true</notnull> + <autoincrement>1</autoincrement> + <unsigned>true</unsigned> + <length>4</length> + </field> + + <field> + <name>calendarid</name> + <type>integer</type> + <default></default> + <notnull>true</notnull> + <unsigned>true</unsigned> + <length>4</length> + </field> + + <field> + <name>objecttype</name> + <type>text</type> + <default></default> + <notnull>true</notnull> + <length>40</length> + </field> + + <field> + <name>startdate</name> + <type>timestamp</type> + <default>1970-01-01 00:00:00</default> + <notnull>false</notnull> + </field> + + <field> + <name>enddate</name> + <type>timestamp</type> + <default>1970-01-01 00:00:00</default> + <notnull>false</notnull> + </field> + + <field> + <name>repeating</name> + <type>integer</type> + <default></default> + <notnull>false</notnull> + <length>4</length> + </field> + + <field> + <name>summary</name> + <type>text</type> + <default></default> + <notnull>false</notnull> + <length>255</length> + </field> + + <field> + <name>calendardata</name> + <type>clob</type> + <notnull>false</notnull> + </field> + + <field> + <name>uri</name> + <type>text</type> + <default></default> + <notnull>false</notnull> + <length>255</length> + </field> + + <field> + <name>lastmodified</name> + <type>integer</type> + <default></default> + <notnull>false</notnull> + <length>4</length> + </field> + + </declaration> + + </table> + + <table> + + <name>*dbprefix*clndr_calendars</name> + + <declaration> + + <field> + <name>id</name> + <type>integer</type> + <default>0</default> + <notnull>true</notnull> + <autoincrement>1</autoincrement> + <unsigned>true</unsigned> + <length>4</length> + </field> + + <field> + <name>userid</name> + <type>text</type> + <default></default> + <notnull>false</notnull> + <length>255</length> + </field> + + <field> + <name>displayname</name> + <type>text</type> + <default></default> + <notnull>false</notnull> + <length>100</length> + </field> + + <field> + <name>uri</name> + <type>text</type> + <default></default> + <notnull>false</notnull> + <length>255</length> + </field> + + <field> + <name>active</name> + <type>integer</type> + <default>1</default> + <notnull>true</notnull> + <length>4</length> + </field> + + <field> + <name>ctag</name> + <type>integer</type> + <default>0</default> + <notnull>true</notnull> + <unsigned>true</unsigned> + <length>4</length> + </field> + + <field> + <name>calendarorder</name> + <type>integer</type> + <default>0</default> + <notnull>true</notnull> + <unsigned>true</unsigned> + <length>4</length> + </field> + + <field> + <name>calendarcolor</name> + <type>text</type> + <default></default> + <notnull>false</notnull> + <length>10</length> + </field> + + <field> + <name>timezone</name> + <type>clob</type> + <notnull>false</notnull> + </field> + + <field> + <name>components</name> + <type>text</type> + <default></default> + <notnull>false</notnull> + <length>100</length> + </field> + + </declaration> + + </table> + +</database> diff --git a/apps/dav/tests/unit/migration/calendaradaptertest.php b/apps/dav/tests/unit/migration/calendaradaptertest.php new file mode 100644 index 00000000000..f92774ef6ad --- /dev/null +++ b/apps/dav/tests/unit/migration/calendaradaptertest.php @@ -0,0 +1,131 @@ +<?php +/** + * @author Thomas Müller <thomas.mueller@tmit.eu> + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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\Tests\Unit\Migration; + +use DomainException; +use OCA\Dav\Migration\AddressBookAdapter; +use OCA\Dav\Migration\CalendarAdapter; +use OCP\IDBConnection; +use Test\TestCase; + +/** + * Class CalendarAdapterTest + * + * @group DB + * + * @package OCA\DAV\Tests\Unit\Migration + */ +class CalendarAdapterTest extends TestCase { + + /** @var IDBConnection */ + private $db; + /** @var CalendarAdapter */ + private $adapter; + /** @var array */ + private $cals = []; + /** @var array */ + private $calObjs = []; + + public function setUp() { + parent::setUp(); + $this->db = \OC::$server->getDatabaseConnection(); + + $manager = new \OC\DB\MDB2SchemaManager($this->db); + $manager->createDbFromStructure(__DIR__ . '/calendar_schema.xml'); + + $this->adapter = new CalendarAdapter($this->db); + } + + public function tearDown() { + $this->db->dropTable('clndr_calendars'); + $this->db->dropTable('clndr_objects'); + parent::tearDown(); + } + + /** + * @expectedException DomainException + */ + public function testOldTablesDoNotExist() { + $adapter = new AddressBookAdapter(\OC::$server->getDatabaseConnection(), 'crazy_table_that_does_no_exist'); + $adapter->setup(); + } + + public function test() { + + // insert test data + $builder = $this->db->getQueryBuilder(); + $builder->insert('clndr_calendars') + ->values([ + 'userid' => $builder->createNamedParameter('test-user-666'), + 'displayname' => $builder->createNamedParameter('Display Name'), + 'uri' => $builder->createNamedParameter('events'), + 'ctag' => $builder->createNamedParameter('112233'), + 'active' => $builder->createNamedParameter('1') + ]) + ->execute(); + $builder = $this->db->getQueryBuilder(); + $builder->insert('clndr_objects') + ->values([ + 'calendarid' => $builder->createNamedParameter(6666), + 'objecttype' => $builder->createNamedParameter('VEVENT'), + 'startdate' => $builder->createNamedParameter(new \DateTime(), 'datetime'), + 'enddate' => $builder->createNamedParameter(new \DateTime(), 'datetime'), + 'repeating' => $builder->createNamedParameter(0), + 'summary' => $builder->createNamedParameter('Something crazy will happen'), + 'uri' => $builder->createNamedParameter('event.ics'), + 'lastmodified' => $builder->createNamedParameter('112233'), + ]) + ->execute(); + $builder = $this->db->getQueryBuilder(); + $builder->insert('share') + ->values([ + 'share_type' => $builder->createNamedParameter(1), + 'share_with' => $builder->createNamedParameter('user01'), + 'uid_owner' => $builder->createNamedParameter('user02'), + 'item_type' => $builder->createNamedParameter('calendar'), + 'item_source' => $builder->createNamedParameter(6666), + 'item_target' => $builder->createNamedParameter('Contacts (user02)'), + ]) + ->execute(); + + // test the adapter + $this->adapter->foreachCalendar('test-user-666', function($row) { + $this->cals[] = $row; + }); + $this->assertArrayHasKey('id', $this->cals[0]); + $this->assertEquals('test-user-666', $this->cals[0]['userid']); + $this->assertEquals('Display Name', $this->cals[0]['displayname']); + $this->assertEquals('events', $this->cals[0]['uri']); + $this->assertEquals('112233', $this->cals[0]['ctag']); + + $this->adapter->foreachCalendarObject(6666, function($row) { + $this->calObjs[]= $row; + }); + $this->assertArrayHasKey('id', $this->calObjs[0]); + $this->assertEquals(6666, $this->calObjs[0]['calendarid']); + + // test getShares + $shares = $this->adapter->getShares(6666); + $this->assertEquals(1, count($shares)); + + } + +} diff --git a/apps/dav/tests/unit/migration/migratecalendartest.php b/apps/dav/tests/unit/migration/migratecalendartest.php new file mode 100644 index 00000000000..1058773ffff --- /dev/null +++ b/apps/dav/tests/unit/migration/migratecalendartest.php @@ -0,0 +1,76 @@ +<?php +/** + * @author Thomas Müller <thomas.mueller@tmit.eu> + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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\Tests\Unit\Migration; + +use OCA\DAV\CalDAV\CalDavBackend; +use OCA\Dav\Migration\CalendarAdapter; +use Test\TestCase; + +class MigrateCalendarTest extends TestCase { + + public function testMigration() { + /** @var CalendarAdapter | \PHPUnit_Framework_MockObject_MockObject $adapter */ + $adapter = $this->mockAdapter(); + + /** @var CalDavBackend | \PHPUnit_Framework_MockObject_MockObject $cardDav */ + $cardDav = $this->getMockBuilder('\OCA\Dav\CalDAV\CalDAVBackend')->disableOriginalConstructor()->getMock(); + $cardDav->method('createCalendar')->willReturn(666); + $cardDav->expects($this->once())->method('createCalendar')->with('principals/users/test01', 'test_contacts'); + $cardDav->expects($this->once())->method('createCalendarObject')->with(666, '63f0dd6c-39d5-44be-9d34-34e7a7441fc2.ics', 'BEGIN:VCARD'); + + $m = new \OCA\Dav\Migration\MigrateCalendars($adapter, $cardDav); + $m->migrateForUser('test01'); + } + + /** + * @return \PHPUnit_Framework_MockObject_MockObject + */ + private function mockAdapter($shares = []) { + $adapter = $this->getMockBuilder('\OCA\Dav\Migration\CalendarAdapter') + ->disableOriginalConstructor() + ->getMock(); + $adapter->method('foreachCalendar')->willReturnCallback(function ($user, \Closure $callBack) { + $callBack([ + // calendarorder | calendarcolor | timezone | components + 'id' => 0, + 'userid' => $user, + 'displayname' => 'Test Contacts', + 'uri' => 'test_contacts', + 'ctag' => 1234567890, + 'active' => 1, + 'calendarorder' => '0', + 'calendarcolor' => '#b3dc6c', + 'timezone' => null, + 'components' => 'VEVENT,VTODO,VJOURNAL' + ]); + }); + $adapter->method('foreachCalendarObject')->willReturnCallback(function ($addressBookId, \Closure $callBack) { + $callBack([ + 'userid' => $addressBookId, + 'uri' => '63f0dd6c-39d5-44be-9d34-34e7a7441fc2.ics', + 'calendardata' => 'BEGIN:VCARD' + ]); + }); + $adapter->method('getShares')->willReturn($shares); + return $adapter; + } + +} |