aboutsummaryrefslogtreecommitdiffstats
path: root/apps/dav
diff options
context:
space:
mode:
Diffstat (limited to 'apps/dav')
-rw-r--r--apps/dav/appinfo/app.php13
-rw-r--r--apps/dav/appinfo/info.xml5
-rw-r--r--apps/dav/appinfo/routes.php4
-rw-r--r--apps/dav/appinfo/v1/caldav.php1
-rw-r--r--apps/dav/appinfo/v1/carddav.php1
-rw-r--r--apps/dav/composer/composer/autoload_classmap.php12
-rw-r--r--apps/dav/composer/composer/autoload_static.php12
-rw-r--r--apps/dav/css/schedule-response.css78
-rw-r--r--apps/dav/js/schedule-response.js3
-rw-r--r--apps/dav/js/settings-admin-caldav.js4
-rw-r--r--apps/dav/l10n/cs.js13
-rw-r--r--apps/dav/l10n/cs.json13
-rw-r--r--apps/dav/l10n/da.js9
-rw-r--r--apps/dav/l10n/da.json9
-rw-r--r--apps/dav/l10n/de.js11
-rw-r--r--apps/dav/l10n/de.json11
-rw-r--r--apps/dav/l10n/de_DE.js11
-rw-r--r--apps/dav/l10n/de_DE.json11
-rw-r--r--apps/dav/l10n/en_GB.js1
-rw-r--r--apps/dav/l10n/en_GB.json1
-rw-r--r--apps/dav/l10n/es.js11
-rw-r--r--apps/dav/l10n/es.json11
-rw-r--r--apps/dav/l10n/es_MX.js1
-rw-r--r--apps/dav/l10n/es_MX.json1
-rw-r--r--apps/dav/l10n/fr.js8
-rw-r--r--apps/dav/l10n/fr.json8
-rw-r--r--apps/dav/l10n/he.js40
-rw-r--r--apps/dav/l10n/he.json40
-rw-r--r--apps/dav/l10n/it.js11
-rw-r--r--apps/dav/l10n/it.json11
-rw-r--r--apps/dav/l10n/pl.js2
-rw-r--r--apps/dav/l10n/pl.json2
-rw-r--r--apps/dav/l10n/pt_BR.js13
-rw-r--r--apps/dav/l10n/pt_BR.json13
-rw-r--r--apps/dav/l10n/ru.js1
-rw-r--r--apps/dav/l10n/ru.json1
-rw-r--r--apps/dav/l10n/sr.js11
-rw-r--r--apps/dav/l10n/sr.json11
-rw-r--r--apps/dav/l10n/tr.js1
-rw-r--r--apps/dav/l10n/tr.json1
-rw-r--r--apps/dav/lib/BackgroundJob/CleanupInvitationTokenJob.php53
-rw-r--r--apps/dav/lib/BackgroundJob/UpdateCalendarResourcesRoomsBackgroundJob.php337
-rw-r--r--apps/dav/lib/CalDAV/Activity/Filter/Calendar.php2
-rw-r--r--apps/dav/lib/CalDAV/Activity/Provider/Calendar.php2
-rw-r--r--apps/dav/lib/CalDAV/CalDavBackend.php3
-rw-r--r--apps/dav/lib/CalDAV/Calendar.php2
-rw-r--r--apps/dav/lib/CalDAV/CalendarRoot.php11
-rw-r--r--apps/dav/lib/CalDAV/InvitationResponse/InvitationResponseServer.php118
-rw-r--r--apps/dav/lib/CalDAV/Plugin.php16
-rw-r--r--apps/dav/lib/CalDAV/ResourceBooking/AbstractPrincipalBackend.php361
-rw-r--r--apps/dav/lib/CalDAV/ResourceBooking/ResourcePrincipalBackend.php45
-rw-r--r--apps/dav/lib/CalDAV/ResourceBooking/RoomPrincipalBackend.php45
-rw-r--r--apps/dav/lib/CalDAV/Schedule/IMipPlugin.php94
-rw-r--r--apps/dav/lib/CalDAV/Schedule/Plugin.php89
-rw-r--r--apps/dav/lib/CardDAV/AddressBook.php2
-rw-r--r--apps/dav/lib/Command/CreateCalendar.php3
-rw-r--r--apps/dav/lib/Command/RemoveInvalidShares.php82
-rw-r--r--apps/dav/lib/Connector/Sabre/AnonymousOptionsPlugin.php66
-rw-r--r--apps/dav/lib/Connector/Sabre/File.php39
-rw-r--r--apps/dav/lib/Connector/Sabre/FilesPlugin.php38
-rw-r--r--apps/dav/lib/Connector/Sabre/Node.php13
-rw-r--r--apps/dav/lib/Connector/Sabre/ObjectTree.php2
-rw-r--r--apps/dav/lib/Connector/Sabre/Principal.php26
-rw-r--r--apps/dav/lib/Connector/Sabre/ServerFactory.php1
-rw-r--r--apps/dav/lib/Controller/InvitationResponseController.php236
-rw-r--r--apps/dav/lib/DAV/Sharing/Backend.php12
-rw-r--r--apps/dav/lib/Files/FilesHome.php9
-rw-r--r--apps/dav/lib/Files/LazySearchBackend.php72
-rw-r--r--apps/dav/lib/Files/RootCollection.php7
-rw-r--r--apps/dav/lib/Migration/Version1005Date20180530124431.php84
-rw-r--r--apps/dav/lib/Migration/Version1006Date20180619154313.php71
-rw-r--r--apps/dav/lib/RootCollection.php36
-rw-r--r--apps/dav/lib/Server.php64
-rw-r--r--apps/dav/lib/Settings/CalDAVSettings.php4
-rw-r--r--apps/dav/templates/schedule-response-error.php7
-rw-r--r--apps/dav/templates/schedule-response-options.php35
-rw-r--r--apps/dav/templates/schedule-response-success.php4
-rw-r--r--apps/dav/tests/unit/BackgroundJob/CleanupInvitationTokenJobTest.php100
-rw-r--r--apps/dav/tests/unit/BackgroundJob/UpdateCalendarResourcesRoomsBackgroundJobTest.php285
-rw-r--r--apps/dav/tests/unit/CalDAV/AbstractCalDavBackend.php11
-rw-r--r--apps/dav/tests/unit/CalDAV/Activity/Filter/CalendarTest.php2
-rw-r--r--apps/dav/tests/unit/CalDAV/PluginTest.php15
-rw-r--r--apps/dav/tests/unit/CalDAV/ResourceBooking/AbstractPrincipalBackendTest.php930
-rw-r--r--apps/dav/tests/unit/CalDAV/ResourceBooking/ResourcePrincipalBackendTest.php35
-rw-r--r--apps/dav/tests/unit/CalDAV/ResourceBooking/RoomPrincipalBackendTest.php35
-rw-r--r--apps/dav/tests/unit/CalDAV/Schedule/IMipPluginTest.php86
-rw-r--r--apps/dav/tests/unit/CardDAV/CardDavBackendTest.php11
-rw-r--r--apps/dav/tests/unit/Command/RemoveInvalidSharesTest.php71
-rw-r--r--apps/dav/tests/unit/Connector/Sabre/NodeTest.php3
-rw-r--r--apps/dav/tests/unit/Connector/Sabre/PrincipalTest.php228
-rw-r--r--apps/dav/tests/unit/Controller/InvitationResponseControllerTest.php455
-rw-r--r--apps/dav/tests/unit/DAV/AnonymousOptionsTest.php71
-rw-r--r--apps/dav/tests/unit/ServerTest.php22
-rw-r--r--apps/dav/tests/unit/Settings/CalDAVSettingsTest.php4
94 files changed, 4684 insertions, 192 deletions
diff --git a/apps/dav/appinfo/app.php b/apps/dav/appinfo/app.php
index 4f4fbe6e126..089aaeb6c78 100644
--- a/apps/dav/appinfo/app.php
+++ b/apps/dav/appinfo/app.php
@@ -48,6 +48,19 @@ $eventDispatcher->addListener('OCP\Federation\TrustedServerEvent::remove',
}
);
+$eventHandler = function() use ($app) {
+ try {
+ $job = $app->getContainer()->query(\OCA\DAV\BackgroundJob\UpdateCalendarResourcesRoomsBackgroundJob::class);
+ $job->run([]);
+ $app->getContainer()->getServer()->getJobList()->setLastRun($job);
+ } catch(\Exception $ex) {
+ $app->getContainer()->getServer()->getLogger()->logException($ex);
+ }
+};
+
+$eventDispatcher->addListener('\OCP\Calendar\Resource\ForceRefreshEvent', $eventHandler);
+$eventDispatcher->addListener('\OCP\Calendar\Room\ForceRefreshEvent', $eventHandler);
+
$cm = \OC::$server->getContactsManager();
$cm->register(function() use ($cm, $app) {
$user = \OC::$server->getUserSession()->getUser();
diff --git a/apps/dav/appinfo/info.xml b/apps/dav/appinfo/info.xml
index d31851fe17e..9e0779a934d 100644
--- a/apps/dav/appinfo/info.xml
+++ b/apps/dav/appinfo/info.xml
@@ -5,7 +5,7 @@
<name>WebDAV</name>
<summary>WebDAV endpoint</summary>
<description>WebDAV endpoint</description>
- <version>1.5.2</version>
+ <version>1.6.0</version>
<licence>agpl</licence>
<author>owncloud.org</author>
<namespace>DAV</namespace>
@@ -21,6 +21,8 @@
<background-jobs>
<job>OCA\DAV\BackgroundJob\CleanupDirectLinksJob</job>
+ <job>OCA\DAV\BackgroundJob\UpdateCalendarResourcesRoomsBackgroundJob</job>
+ <job>OCA\DAV\BackgroundJob\CleanupInvitationTokenJob</job>
</background-jobs>
<repair-steps>
@@ -36,6 +38,7 @@
<command>OCA\DAV\Command\CreateCalendar</command>
<command>OCA\DAV\Command\SyncBirthdayCalendar</command>
<command>OCA\DAV\Command\SyncSystemAddressBook</command>
+ <command>OCA\DAV\Command\RemoveInvalidShares</command>
</commands>
<settings>
diff --git a/apps/dav/appinfo/routes.php b/apps/dav/appinfo/routes.php
index 2aaeda98964..a7d9e2ec33c 100644
--- a/apps/dav/appinfo/routes.php
+++ b/apps/dav/appinfo/routes.php
@@ -25,6 +25,10 @@ return [
'routes' => [
['name' => 'birthday_calendar#enable', 'url' => '/enableBirthdayCalendar', 'verb' => 'POST'],
['name' => 'birthday_calendar#disable', 'url' => '/disableBirthdayCalendar', 'verb' => 'POST'],
+ ['name' => 'invitation_response#accept', 'url' => '/invitation/accept/{token}', 'verb' => 'GET'],
+ ['name' => 'invitation_response#decline', 'url' => '/invitation/decline/{token}', 'verb' => 'GET'],
+ ['name' => 'invitation_response#options', 'url' => '/invitation/moreOptions/{token}', 'verb' => 'GET'],
+ ['name' => 'invitation_response#processMoreOptionsResult', 'url' => '/invitation/moreOptions/{token}', 'verb' => 'POST']
],
'ocs' => [
['name' => 'direct#getUrl', 'url' => '/api/v1/direct', 'verb' => 'POST'],
diff --git a/apps/dav/appinfo/v1/caldav.php b/apps/dav/appinfo/v1/caldav.php
index b4907ce94cb..ecc4fbb8b60 100644
--- a/apps/dav/appinfo/v1/caldav.php
+++ b/apps/dav/appinfo/v1/caldav.php
@@ -46,6 +46,7 @@ $principalBackend = new Principal(
\OC::$server->getGroupManager(),
\OC::$server->getShareManager(),
\OC::$server->getUserSession(),
+ \OC::$server->getConfig(),
'principals/'
);
$db = \OC::$server->getDatabaseConnection();
diff --git a/apps/dav/appinfo/v1/carddav.php b/apps/dav/appinfo/v1/carddav.php
index eddc7a794ac..e55eee610ef 100644
--- a/apps/dav/appinfo/v1/carddav.php
+++ b/apps/dav/appinfo/v1/carddav.php
@@ -47,6 +47,7 @@ $principalBackend = new Principal(
\OC::$server->getGroupManager(),
\OC::$server->getShareManager(),
\OC::$server->getUserSession(),
+ \OC::$server->getConfig(),
'principals/'
);
$db = \OC::$server->getDatabaseConnection();
diff --git a/apps/dav/composer/composer/autoload_classmap.php b/apps/dav/composer/composer/autoload_classmap.php
index 50689568ebb..8b266c156f3 100644
--- a/apps/dav/composer/composer/autoload_classmap.php
+++ b/apps/dav/composer/composer/autoload_classmap.php
@@ -12,7 +12,9 @@ return array(
'OCA\\DAV\\Avatars\\AvatarNode' => $baseDir . '/../lib/Avatars/AvatarNode.php',
'OCA\\DAV\\Avatars\\RootCollection' => $baseDir . '/../lib/Avatars/RootCollection.php',
'OCA\\DAV\\BackgroundJob\\CleanupDirectLinksJob' => $baseDir . '/../lib/BackgroundJob/CleanupDirectLinksJob.php',
+ 'OCA\\DAV\\BackgroundJob\\CleanupInvitationTokenJob' => $baseDir . '/../lib/BackgroundJob/CleanupInvitationTokenJob.php',
'OCA\\DAV\\BackgroundJob\\GenerateBirthdayCalendarBackgroundJob' => $baseDir . '/../lib/BackgroundJob/GenerateBirthdayCalendarBackgroundJob.php',
+ 'OCA\\DAV\\BackgroundJob\\UpdateCalendarResourcesRoomsBackgroundJob' => $baseDir . '/../lib/BackgroundJob/UpdateCalendarResourcesRoomsBackgroundJob.php',
'OCA\\DAV\\CalDAV\\Activity\\Backend' => $baseDir . '/../lib/CalDAV/Activity/Backend.php',
'OCA\\DAV\\CalDAV\\Activity\\Filter\\Calendar' => $baseDir . '/../lib/CalDAV/Activity/Filter/Calendar.php',
'OCA\\DAV\\CalDAV\\Activity\\Filter\\Todo' => $baseDir . '/../lib/CalDAV/Activity/Filter/Todo.php',
@@ -32,6 +34,7 @@ return array(
'OCA\\DAV\\CalDAV\\CalendarManager' => $baseDir . '/../lib/CalDAV/CalendarManager.php',
'OCA\\DAV\\CalDAV\\CalendarObject' => $baseDir . '/../lib/CalDAV/CalendarObject.php',
'OCA\\DAV\\CalDAV\\CalendarRoot' => $baseDir . '/../lib/CalDAV/CalendarRoot.php',
+ 'OCA\\DAV\\CalDAV\\InvitationResponse\\InvitationResponseServer' => $baseDir . '/../lib/CalDAV/InvitationResponse/InvitationResponseServer.php',
'OCA\\DAV\\CalDAV\\Plugin' => $baseDir . '/../lib/CalDAV/Plugin.php',
'OCA\\DAV\\CalDAV\\Principal\\Collection' => $baseDir . '/../lib/CalDAV/Principal/Collection.php',
'OCA\\DAV\\CalDAV\\Principal\\User' => $baseDir . '/../lib/CalDAV/Principal/User.php',
@@ -40,6 +43,9 @@ return array(
'OCA\\DAV\\CalDAV\\PublicCalendarRoot' => $baseDir . '/../lib/CalDAV/PublicCalendarRoot.php',
'OCA\\DAV\\CalDAV\\Publishing\\PublishPlugin' => $baseDir . '/../lib/CalDAV/Publishing/PublishPlugin.php',
'OCA\\DAV\\CalDAV\\Publishing\\Xml\\Publisher' => $baseDir . '/../lib/CalDAV/Publishing/Xml/Publisher.php',
+ 'OCA\\DAV\\CalDAV\\ResourceBooking\\AbstractPrincipalBackend' => $baseDir . '/../lib/CalDAV/ResourceBooking/AbstractPrincipalBackend.php',
+ 'OCA\\DAV\\CalDAV\\ResourceBooking\\ResourcePrincipalBackend' => $baseDir . '/../lib/CalDAV/ResourceBooking/ResourcePrincipalBackend.php',
+ 'OCA\\DAV\\CalDAV\\ResourceBooking\\RoomPrincipalBackend' => $baseDir . '/../lib/CalDAV/ResourceBooking/RoomPrincipalBackend.php',
'OCA\\DAV\\CalDAV\\Schedule\\IMipPlugin' => $baseDir . '/../lib/CalDAV/Schedule/IMipPlugin.php',
'OCA\\DAV\\CalDAV\\Schedule\\Plugin' => $baseDir . '/../lib/CalDAV/Schedule/Plugin.php',
'OCA\\DAV\\CalDAV\\Search\\SearchPlugin' => $baseDir . '/../lib/CalDAV/Search/SearchPlugin.php',
@@ -65,6 +71,7 @@ return array(
'OCA\\DAV\\CardDAV\\Xml\\Groups' => $baseDir . '/../lib/CardDAV/Xml/Groups.php',
'OCA\\DAV\\Command\\CreateAddressBook' => $baseDir . '/../lib/Command/CreateAddressBook.php',
'OCA\\DAV\\Command\\CreateCalendar' => $baseDir . '/../lib/Command/CreateCalendar.php',
+ 'OCA\\DAV\\Command\\RemoveInvalidShares' => $baseDir . '/../lib/Command/RemoveInvalidShares.php',
'OCA\\DAV\\Command\\SyncBirthdayCalendar' => $baseDir . '/../lib/Command/SyncBirthdayCalendar.php',
'OCA\\DAV\\Command\\SyncSystemAddressBook' => $baseDir . '/../lib/Command/SyncSystemAddressBook.php',
'OCA\\DAV\\Comments\\CommentNode' => $baseDir . '/../lib/Comments/CommentNode.php',
@@ -74,6 +81,7 @@ return array(
'OCA\\DAV\\Comments\\RootCollection' => $baseDir . '/../lib/Comments/RootCollection.php',
'OCA\\DAV\\Connector\\LegacyDAVACL' => $baseDir . '/../lib/Connector/LegacyDAVACL.php',
'OCA\\DAV\\Connector\\PublicAuth' => $baseDir . '/../lib/Connector/PublicAuth.php',
+ 'OCA\\DAV\\Connector\\Sabre\\AnonymousOptionsPlugin' => $baseDir . '/../lib/Connector/Sabre/AnonymousOptionsPlugin.php',
'OCA\\DAV\\Connector\\Sabre\\AppEnabledPlugin' => $baseDir . '/../lib/Connector/Sabre/AppEnabledPlugin.php',
'OCA\\DAV\\Connector\\Sabre\\Auth' => $baseDir . '/../lib/Connector/Sabre/Auth.php',
'OCA\\DAV\\Connector\\Sabre\\BearerAuth' => $baseDir . '/../lib/Connector/Sabre/BearerAuth.php',
@@ -111,6 +119,7 @@ return array(
'OCA\\DAV\\Connector\\Sabre\\TagsPlugin' => $baseDir . '/../lib/Connector/Sabre/TagsPlugin.php',
'OCA\\DAV\\Controller\\BirthdayCalendarController' => $baseDir . '/../lib/Controller/BirthdayCalendarController.php',
'OCA\\DAV\\Controller\\DirectController' => $baseDir . '/../lib/Controller/DirectController.php',
+ 'OCA\\DAV\\Controller\\InvitationResponseController' => $baseDir . '/../lib/Controller/InvitationResponseController.php',
'OCA\\DAV\\DAV\\CustomPropertiesBackend' => $baseDir . '/../lib/DAV/CustomPropertiesBackend.php',
'OCA\\DAV\\DAV\\GroupPrincipalBackend' => $baseDir . '/../lib/DAV/GroupPrincipalBackend.php',
'OCA\\DAV\\DAV\\PublicAuth' => $baseDir . '/../lib/DAV/PublicAuth.php',
@@ -129,6 +138,7 @@ return array(
'OCA\\DAV\\Files\\BrowserErrorPagePlugin' => $baseDir . '/../lib/Files/BrowserErrorPagePlugin.php',
'OCA\\DAV\\Files\\FileSearchBackend' => $baseDir . '/../lib/Files/FileSearchBackend.php',
'OCA\\DAV\\Files\\FilesHome' => $baseDir . '/../lib/Files/FilesHome.php',
+ 'OCA\\DAV\\Files\\LazySearchBackend' => $baseDir . '/../lib/Files/LazySearchBackend.php',
'OCA\\DAV\\Files\\RootCollection' => $baseDir . '/../lib/Files/RootCollection.php',
'OCA\\DAV\\Files\\Sharing\\FilesDropPlugin' => $baseDir . '/../lib/Files/Sharing/FilesDropPlugin.php',
'OCA\\DAV\\Files\\Sharing\\PublicLinkCheckPlugin' => $baseDir . '/../lib/Files/Sharing/PublicLinkCheckPlugin.php',
@@ -142,6 +152,8 @@ return array(
'OCA\\DAV\\Migration\\Version1004Date20170924124212' => $baseDir . '/../lib/Migration/Version1004Date20170924124212.php',
'OCA\\DAV\\Migration\\Version1004Date20170926103422' => $baseDir . '/../lib/Migration/Version1004Date20170926103422.php',
'OCA\\DAV\\Migration\\Version1005Date20180413093149' => $baseDir . '/../lib/Migration/Version1005Date20180413093149.php',
+ 'OCA\\DAV\\Migration\\Version1005Date20180530124431' => $baseDir . '/../lib/Migration/Version1005Date20180530124431.php',
+ 'OCA\\DAV\\Migration\\Version1006Date20180619154313' => $baseDir . '/../lib/Migration/Version1006Date20180619154313.php',
'OCA\\DAV\\RootCollection' => $baseDir . '/../lib/RootCollection.php',
'OCA\\DAV\\Server' => $baseDir . '/../lib/Server.php',
'OCA\\DAV\\Settings\\CalDAVSettings' => $baseDir . '/../lib/Settings/CalDAVSettings.php',
diff --git a/apps/dav/composer/composer/autoload_static.php b/apps/dav/composer/composer/autoload_static.php
index 760ca3426f7..09eb4d257cc 100644
--- a/apps/dav/composer/composer/autoload_static.php
+++ b/apps/dav/composer/composer/autoload_static.php
@@ -27,7 +27,9 @@ class ComposerStaticInitDAV
'OCA\\DAV\\Avatars\\AvatarNode' => __DIR__ . '/..' . '/../lib/Avatars/AvatarNode.php',
'OCA\\DAV\\Avatars\\RootCollection' => __DIR__ . '/..' . '/../lib/Avatars/RootCollection.php',
'OCA\\DAV\\BackgroundJob\\CleanupDirectLinksJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/CleanupDirectLinksJob.php',
+ 'OCA\\DAV\\BackgroundJob\\CleanupInvitationTokenJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/CleanupInvitationTokenJob.php',
'OCA\\DAV\\BackgroundJob\\GenerateBirthdayCalendarBackgroundJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/GenerateBirthdayCalendarBackgroundJob.php',
+ 'OCA\\DAV\\BackgroundJob\\UpdateCalendarResourcesRoomsBackgroundJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/UpdateCalendarResourcesRoomsBackgroundJob.php',
'OCA\\DAV\\CalDAV\\Activity\\Backend' => __DIR__ . '/..' . '/../lib/CalDAV/Activity/Backend.php',
'OCA\\DAV\\CalDAV\\Activity\\Filter\\Calendar' => __DIR__ . '/..' . '/../lib/CalDAV/Activity/Filter/Calendar.php',
'OCA\\DAV\\CalDAV\\Activity\\Filter\\Todo' => __DIR__ . '/..' . '/../lib/CalDAV/Activity/Filter/Todo.php',
@@ -47,6 +49,7 @@ class ComposerStaticInitDAV
'OCA\\DAV\\CalDAV\\CalendarManager' => __DIR__ . '/..' . '/../lib/CalDAV/CalendarManager.php',
'OCA\\DAV\\CalDAV\\CalendarObject' => __DIR__ . '/..' . '/../lib/CalDAV/CalendarObject.php',
'OCA\\DAV\\CalDAV\\CalendarRoot' => __DIR__ . '/..' . '/../lib/CalDAV/CalendarRoot.php',
+ 'OCA\\DAV\\CalDAV\\InvitationResponse\\InvitationResponseServer' => __DIR__ . '/..' . '/../lib/CalDAV/InvitationResponse/InvitationResponseServer.php',
'OCA\\DAV\\CalDAV\\Plugin' => __DIR__ . '/..' . '/../lib/CalDAV/Plugin.php',
'OCA\\DAV\\CalDAV\\Principal\\Collection' => __DIR__ . '/..' . '/../lib/CalDAV/Principal/Collection.php',
'OCA\\DAV\\CalDAV\\Principal\\User' => __DIR__ . '/..' . '/../lib/CalDAV/Principal/User.php',
@@ -55,6 +58,9 @@ class ComposerStaticInitDAV
'OCA\\DAV\\CalDAV\\PublicCalendarRoot' => __DIR__ . '/..' . '/../lib/CalDAV/PublicCalendarRoot.php',
'OCA\\DAV\\CalDAV\\Publishing\\PublishPlugin' => __DIR__ . '/..' . '/../lib/CalDAV/Publishing/PublishPlugin.php',
'OCA\\DAV\\CalDAV\\Publishing\\Xml\\Publisher' => __DIR__ . '/..' . '/../lib/CalDAV/Publishing/Xml/Publisher.php',
+ 'OCA\\DAV\\CalDAV\\ResourceBooking\\AbstractPrincipalBackend' => __DIR__ . '/..' . '/../lib/CalDAV/ResourceBooking/AbstractPrincipalBackend.php',
+ 'OCA\\DAV\\CalDAV\\ResourceBooking\\ResourcePrincipalBackend' => __DIR__ . '/..' . '/../lib/CalDAV/ResourceBooking/ResourcePrincipalBackend.php',
+ 'OCA\\DAV\\CalDAV\\ResourceBooking\\RoomPrincipalBackend' => __DIR__ . '/..' . '/../lib/CalDAV/ResourceBooking/RoomPrincipalBackend.php',
'OCA\\DAV\\CalDAV\\Schedule\\IMipPlugin' => __DIR__ . '/..' . '/../lib/CalDAV/Schedule/IMipPlugin.php',
'OCA\\DAV\\CalDAV\\Schedule\\Plugin' => __DIR__ . '/..' . '/../lib/CalDAV/Schedule/Plugin.php',
'OCA\\DAV\\CalDAV\\Search\\SearchPlugin' => __DIR__ . '/..' . '/../lib/CalDAV/Search/SearchPlugin.php',
@@ -80,6 +86,7 @@ class ComposerStaticInitDAV
'OCA\\DAV\\CardDAV\\Xml\\Groups' => __DIR__ . '/..' . '/../lib/CardDAV/Xml/Groups.php',
'OCA\\DAV\\Command\\CreateAddressBook' => __DIR__ . '/..' . '/../lib/Command/CreateAddressBook.php',
'OCA\\DAV\\Command\\CreateCalendar' => __DIR__ . '/..' . '/../lib/Command/CreateCalendar.php',
+ 'OCA\\DAV\\Command\\RemoveInvalidShares' => __DIR__ . '/..' . '/../lib/Command/RemoveInvalidShares.php',
'OCA\\DAV\\Command\\SyncBirthdayCalendar' => __DIR__ . '/..' . '/../lib/Command/SyncBirthdayCalendar.php',
'OCA\\DAV\\Command\\SyncSystemAddressBook' => __DIR__ . '/..' . '/../lib/Command/SyncSystemAddressBook.php',
'OCA\\DAV\\Comments\\CommentNode' => __DIR__ . '/..' . '/../lib/Comments/CommentNode.php',
@@ -89,6 +96,7 @@ class ComposerStaticInitDAV
'OCA\\DAV\\Comments\\RootCollection' => __DIR__ . '/..' . '/../lib/Comments/RootCollection.php',
'OCA\\DAV\\Connector\\LegacyDAVACL' => __DIR__ . '/..' . '/../lib/Connector/LegacyDAVACL.php',
'OCA\\DAV\\Connector\\PublicAuth' => __DIR__ . '/..' . '/../lib/Connector/PublicAuth.php',
+ 'OCA\\DAV\\Connector\\Sabre\\AnonymousOptionsPlugin' => __DIR__ . '/..' . '/../lib/Connector/Sabre/AnonymousOptionsPlugin.php',
'OCA\\DAV\\Connector\\Sabre\\AppEnabledPlugin' => __DIR__ . '/..' . '/../lib/Connector/Sabre/AppEnabledPlugin.php',
'OCA\\DAV\\Connector\\Sabre\\Auth' => __DIR__ . '/..' . '/../lib/Connector/Sabre/Auth.php',
'OCA\\DAV\\Connector\\Sabre\\BearerAuth' => __DIR__ . '/..' . '/../lib/Connector/Sabre/BearerAuth.php',
@@ -126,6 +134,7 @@ class ComposerStaticInitDAV
'OCA\\DAV\\Connector\\Sabre\\TagsPlugin' => __DIR__ . '/..' . '/../lib/Connector/Sabre/TagsPlugin.php',
'OCA\\DAV\\Controller\\BirthdayCalendarController' => __DIR__ . '/..' . '/../lib/Controller/BirthdayCalendarController.php',
'OCA\\DAV\\Controller\\DirectController' => __DIR__ . '/..' . '/../lib/Controller/DirectController.php',
+ 'OCA\\DAV\\Controller\\InvitationResponseController' => __DIR__ . '/..' . '/../lib/Controller/InvitationResponseController.php',
'OCA\\DAV\\DAV\\CustomPropertiesBackend' => __DIR__ . '/..' . '/../lib/DAV/CustomPropertiesBackend.php',
'OCA\\DAV\\DAV\\GroupPrincipalBackend' => __DIR__ . '/..' . '/../lib/DAV/GroupPrincipalBackend.php',
'OCA\\DAV\\DAV\\PublicAuth' => __DIR__ . '/..' . '/../lib/DAV/PublicAuth.php',
@@ -144,6 +153,7 @@ class ComposerStaticInitDAV
'OCA\\DAV\\Files\\BrowserErrorPagePlugin' => __DIR__ . '/..' . '/../lib/Files/BrowserErrorPagePlugin.php',
'OCA\\DAV\\Files\\FileSearchBackend' => __DIR__ . '/..' . '/../lib/Files/FileSearchBackend.php',
'OCA\\DAV\\Files\\FilesHome' => __DIR__ . '/..' . '/../lib/Files/FilesHome.php',
+ 'OCA\\DAV\\Files\\LazySearchBackend' => __DIR__ . '/..' . '/../lib/Files/LazySearchBackend.php',
'OCA\\DAV\\Files\\RootCollection' => __DIR__ . '/..' . '/../lib/Files/RootCollection.php',
'OCA\\DAV\\Files\\Sharing\\FilesDropPlugin' => __DIR__ . '/..' . '/../lib/Files/Sharing/FilesDropPlugin.php',
'OCA\\DAV\\Files\\Sharing\\PublicLinkCheckPlugin' => __DIR__ . '/..' . '/../lib/Files/Sharing/PublicLinkCheckPlugin.php',
@@ -157,6 +167,8 @@ class ComposerStaticInitDAV
'OCA\\DAV\\Migration\\Version1004Date20170924124212' => __DIR__ . '/..' . '/../lib/Migration/Version1004Date20170924124212.php',
'OCA\\DAV\\Migration\\Version1004Date20170926103422' => __DIR__ . '/..' . '/../lib/Migration/Version1004Date20170926103422.php',
'OCA\\DAV\\Migration\\Version1005Date20180413093149' => __DIR__ . '/..' . '/../lib/Migration/Version1005Date20180413093149.php',
+ 'OCA\\DAV\\Migration\\Version1005Date20180530124431' => __DIR__ . '/..' . '/../lib/Migration/Version1005Date20180530124431.php',
+ 'OCA\\DAV\\Migration\\Version1006Date20180619154313' => __DIR__ . '/..' . '/../lib/Migration/Version1006Date20180619154313.php',
'OCA\\DAV\\RootCollection' => __DIR__ . '/..' . '/../lib/RootCollection.php',
'OCA\\DAV\\Server' => __DIR__ . '/..' . '/../lib/Server.php',
'OCA\\DAV\\Settings\\CalDAVSettings' => __DIR__ . '/..' . '/../lib/Settings/CalDAVSettings.php',
diff --git a/apps/dav/css/schedule-response.css b/apps/dav/css/schedule-response.css
new file mode 100644
index 00000000000..789ea16df7a
--- /dev/null
+++ b/apps/dav/css/schedule-response.css
@@ -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
+} \ No newline at end of file
diff --git a/apps/dav/js/schedule-response.js b/apps/dav/js/schedule-response.js
new file mode 100644
index 00000000000..b2514b1ba82
--- /dev/null
+++ b/apps/dav/js/schedule-response.js
@@ -0,0 +1,3 @@
+// $(document).ready(function() {
+// $('#selectPartStatForm').buttonset();
+// }); \ No newline at end of file
diff --git a/apps/dav/js/settings-admin-caldav.js b/apps/dav/js/settings-admin-caldav.js
index cf1a2006f69..1a40c208dfe 100644
--- a/apps/dav/js/settings-admin-caldav.js
+++ b/apps/dav/js/settings-admin-caldav.js
@@ -31,8 +31,8 @@ $('#caldavGenerateBirthdayCalendar').change(function() {
var val = $(this)[0].checked;
if (val) {
- $.post(OC.generateUrl(OC.linkTo("dav", "enableBirthdayCalendar")));
+ $.post(OC.generateUrl('/apps/dav/enableBirthdayCalendar'));
} else {
- $.post(OC.generateUrl(OC.linkTo("dav", "disableBirthdayCalendar")));
+ $.post(OC.generateUrl('/apps/dav/disableBirthdayCalendar'));
}
});
diff --git a/apps/dav/l10n/cs.js b/apps/dav/l10n/cs.js
index 6171105a7b1..8cb5bbd2a39 100644
--- a/apps/dav/l10n/cs.js
+++ b/apps/dav/l10n/cs.js
@@ -5,15 +5,15 @@ OC.L10N.register(
"Todos" : "Úkoly",
"Personal" : "Osobní",
"{actor} created calendar {calendar}" : "{actor} vytvořil(a) kalendář {calendar}",
- "You created calendar {calendar}" : "Vytvořil(a",
+ "You created calendar {calendar}" : "Vytvořili jste kalendář {calendar}",
"{actor} deleted calendar {calendar}" : "{actor} smazal(a) kalendář {calendar}",
- "You deleted calendar {calendar}" : "Smazal(a) jste kalendář {calendar}",
+ "You deleted calendar {calendar}" : "Smazali jste kalendář {calendar}",
"{actor} updated calendar {calendar}" : "{actor} aktualizoval(a) kalendář {calendar}",
- "You updated calendar {calendar}" : "Aktualizoval(a) jste kalendář {calendar}",
+ "You updated calendar {calendar}" : "Aktualizovali jste kalendář {calendar}",
"You shared calendar {calendar} as public link" : "Sdílel(a) jste kalendář {calendar} jako veřejný odkaz",
"You removed public link for calendar {calendar}" : "Odstranil(a) jste veřejný odkaz pro kalendář {calendar} ",
"{actor} shared calendar {calendar} with you" : "{actor} s vámi nasdílel(a) kalendář {calendar}",
- "You shared calendar {calendar} with {user}" : "S uživatelem {user} jste začal(a) sdílet kalendář {calendar}",
+ "You shared calendar {calendar} with {user}" : "S uživatelem {user} jste začali sdílet kalendář {calendar}",
"{actor} shared calendar {calendar} with {user}" : "{actor} začal sdílet kalendář {calendar} s uživatelem {user}",
"{actor} unshared calendar {calendar} from you" : "{actor} s vámi přestal(a) sdílet kalendář {calendar}",
"You unshared calendar {calendar} from {user}" : "S uživatelem {user} jste přestal(a) sdílet kalendář {calendar}",
@@ -54,11 +54,16 @@ OC.L10N.register(
"Where:" : "Kde:",
"Description:" : "Popis:",
"Link:" : "Odkaz:",
+ "Accept" : "Přijmout",
+ "More options ..." : "Další volby…",
"Contacts" : "Kontakty",
"WebDAV" : "WebDAV",
"Technical details" : "Technické detaily",
"Remote Address: %s" : "Vzdálená adresa: %s",
"Request ID: %s" : "ID požadavku: %s",
+ "Please contact the organizer directly." : "Kontaktujte organizátora přímo.",
+ "Are you accepting the invitation?" : "Přijímáte pozvání?",
+ "Save" : "Uložit",
"CalDAV server" : "CalDAV server",
"Send invitations to attendees" : "Poslat pozvánky na adresy účastníků",
"Please make sure to properly set up the email settings above." : "Ujistěte se, že jste správně nastavili výše uvedená nastavení e-mailu.",
diff --git a/apps/dav/l10n/cs.json b/apps/dav/l10n/cs.json
index ce0bc06e43e..3b66407ba2a 100644
--- a/apps/dav/l10n/cs.json
+++ b/apps/dav/l10n/cs.json
@@ -3,15 +3,15 @@
"Todos" : "Úkoly",
"Personal" : "Osobní",
"{actor} created calendar {calendar}" : "{actor} vytvořil(a) kalendář {calendar}",
- "You created calendar {calendar}" : "Vytvořil(a",
+ "You created calendar {calendar}" : "Vytvořili jste kalendář {calendar}",
"{actor} deleted calendar {calendar}" : "{actor} smazal(a) kalendář {calendar}",
- "You deleted calendar {calendar}" : "Smazal(a) jste kalendář {calendar}",
+ "You deleted calendar {calendar}" : "Smazali jste kalendář {calendar}",
"{actor} updated calendar {calendar}" : "{actor} aktualizoval(a) kalendář {calendar}",
- "You updated calendar {calendar}" : "Aktualizoval(a) jste kalendář {calendar}",
+ "You updated calendar {calendar}" : "Aktualizovali jste kalendář {calendar}",
"You shared calendar {calendar} as public link" : "Sdílel(a) jste kalendář {calendar} jako veřejný odkaz",
"You removed public link for calendar {calendar}" : "Odstranil(a) jste veřejný odkaz pro kalendář {calendar} ",
"{actor} shared calendar {calendar} with you" : "{actor} s vámi nasdílel(a) kalendář {calendar}",
- "You shared calendar {calendar} with {user}" : "S uživatelem {user} jste začal(a) sdílet kalendář {calendar}",
+ "You shared calendar {calendar} with {user}" : "S uživatelem {user} jste začali sdílet kalendář {calendar}",
"{actor} shared calendar {calendar} with {user}" : "{actor} začal sdílet kalendář {calendar} s uživatelem {user}",
"{actor} unshared calendar {calendar} from you" : "{actor} s vámi přestal(a) sdílet kalendář {calendar}",
"You unshared calendar {calendar} from {user}" : "S uživatelem {user} jste přestal(a) sdílet kalendář {calendar}",
@@ -52,11 +52,16 @@
"Where:" : "Kde:",
"Description:" : "Popis:",
"Link:" : "Odkaz:",
+ "Accept" : "Přijmout",
+ "More options ..." : "Další volby…",
"Contacts" : "Kontakty",
"WebDAV" : "WebDAV",
"Technical details" : "Technické detaily",
"Remote Address: %s" : "Vzdálená adresa: %s",
"Request ID: %s" : "ID požadavku: %s",
+ "Please contact the organizer directly." : "Kontaktujte organizátora přímo.",
+ "Are you accepting the invitation?" : "Přijímáte pozvání?",
+ "Save" : "Uložit",
"CalDAV server" : "CalDAV server",
"Send invitations to attendees" : "Poslat pozvánky na adresy účastníků",
"Please make sure to properly set up the email settings above." : "Ujistěte se, že jste správně nastavili výše uvedená nastavení e-mailu.",
diff --git a/apps/dav/l10n/da.js b/apps/dav/l10n/da.js
index 8ddad543dc7..63f9716ff52 100644
--- a/apps/dav/l10n/da.js
+++ b/apps/dav/l10n/da.js
@@ -10,6 +10,8 @@ OC.L10N.register(
"You deleted calendar {calendar}" : "Du slettede kalenderen {calendar}",
"{actor} updated calendar {calendar}" : "{actor} opdaterede kalenderen {calendar}",
"You updated calendar {calendar}" : "Du opdaterede kalenderen {calendar}",
+ "You shared calendar {calendar} as public link" : "Du har delt kalenderen {calendar} som offentligt link",
+ "You removed public link for calendar {calendar}" : "Du har fjernet det offentlige link til kalenderen {calendar}",
"{actor} shared calendar {calendar} with you" : "{actor} delte kalenderen {calendar} med dig",
"You shared calendar {calendar} with {user}" : "Du delte kalenderen {calendar} med {user}",
"{actor} shared calendar {calendar} with {user}" : "{actor} delte kalenderen {calendar} med {user}",
@@ -53,11 +55,16 @@ OC.L10N.register(
"Description:" : "Beskrivelse:",
"Link:" : "Link:",
"Contacts" : "Kontakter",
+ "WebDAV" : "WebDAV",
+ "WebDAV endpoint" : "WebDAV endpoint",
"Technical details" : "Tekniske detaljer",
"Remote Address: %s" : "Fjernadresse: %s",
"Request ID: %s" : "Forespørgsels-ID: %s",
"CalDAV server" : "CalDAV server",
"Send invitations to attendees" : "Send invitation til deltagere",
- "Please make sure to properly set up the email settings above." : "Vær venligst sikker på at indstille email indstillingerne ovenover ordenligt."
+ "Please make sure to properly set up the email settings above." : "Vær venligst sikker på at indstille email indstillingerne ovenover ordenligt.",
+ "Automatically generate a birthday calendar" : "Generer en fødselsdagskalender automatisk",
+ "Birthday calendars will be generated by a background job." : "Fødselsdagskalendere vil blive oprettet af et job, der kører i baggrunden.",
+ "Hence they will not be available immediately after enabling but will show up after some time." : "Derfor vil de ikke blive synlige med det samme efter aktivering, men vil vise sig efter noget tid."
},
"nplurals=2; plural=(n != 1);");
diff --git a/apps/dav/l10n/da.json b/apps/dav/l10n/da.json
index fd40e7d0350..c40ec28963a 100644
--- a/apps/dav/l10n/da.json
+++ b/apps/dav/l10n/da.json
@@ -8,6 +8,8 @@
"You deleted calendar {calendar}" : "Du slettede kalenderen {calendar}",
"{actor} updated calendar {calendar}" : "{actor} opdaterede kalenderen {calendar}",
"You updated calendar {calendar}" : "Du opdaterede kalenderen {calendar}",
+ "You shared calendar {calendar} as public link" : "Du har delt kalenderen {calendar} som offentligt link",
+ "You removed public link for calendar {calendar}" : "Du har fjernet det offentlige link til kalenderen {calendar}",
"{actor} shared calendar {calendar} with you" : "{actor} delte kalenderen {calendar} med dig",
"You shared calendar {calendar} with {user}" : "Du delte kalenderen {calendar} med {user}",
"{actor} shared calendar {calendar} with {user}" : "{actor} delte kalenderen {calendar} med {user}",
@@ -51,11 +53,16 @@
"Description:" : "Beskrivelse:",
"Link:" : "Link:",
"Contacts" : "Kontakter",
+ "WebDAV" : "WebDAV",
+ "WebDAV endpoint" : "WebDAV endpoint",
"Technical details" : "Tekniske detaljer",
"Remote Address: %s" : "Fjernadresse: %s",
"Request ID: %s" : "Forespørgsels-ID: %s",
"CalDAV server" : "CalDAV server",
"Send invitations to attendees" : "Send invitation til deltagere",
- "Please make sure to properly set up the email settings above." : "Vær venligst sikker på at indstille email indstillingerne ovenover ordenligt."
+ "Please make sure to properly set up the email settings above." : "Vær venligst sikker på at indstille email indstillingerne ovenover ordenligt.",
+ "Automatically generate a birthday calendar" : "Generer en fødselsdagskalender automatisk",
+ "Birthday calendars will be generated by a background job." : "Fødselsdagskalendere vil blive oprettet af et job, der kører i baggrunden.",
+ "Hence they will not be available immediately after enabling but will show up after some time." : "Derfor vil de ikke blive synlige med det samme efter aktivering, men vil vise sig efter noget tid."
},"pluralForm" :"nplurals=2; plural=(n != 1);"
} \ No newline at end of file
diff --git a/apps/dav/l10n/de.js b/apps/dav/l10n/de.js
index 4402c07a397..d7af0e5f459 100644
--- a/apps/dav/l10n/de.js
+++ b/apps/dav/l10n/de.js
@@ -54,11 +54,22 @@ OC.L10N.register(
"Where:" : "Wo:",
"Description:" : "Beschreibung:",
"Link:" : "Link:",
+ "Accept" : "Akzeptieren",
+ "Decline" : "Ablehnen",
+ "More options ..." : "Weitere Optionen…",
+ "More options at %s" : "Weitere Optionen unter %s",
"Contacts" : "Kontakte",
"WebDAV" : "WebDAV",
+ "WebDAV endpoint" : "WebDAV-Endpunkt",
"Technical details" : "Technische Details",
"Remote Address: %s" : "Entfernte Adresse: %s",
"Request ID: %s" : "Anfragekennung: %s",
+ "There was an error updating your attendance status." : "Es ist ein Fehler beim Aktualisieren Deines Teilnehmerstatus aufgetreten.",
+ "Please contact the organizer directly." : "Bitte den Organisator direkt kontaktieren.",
+ "Are you accepting the invitation?" : "Die Einladung annehmen?",
+ "Tentative" : "Vorläufig",
+ "Save" : "Speichern",
+ "Your attendance was updated successfully." : "Dein Anwesenheits-Status wurde aktualisiert.",
"CalDAV server" : "CalDAV-Server",
"Send invitations to attendees" : "Einladungen an die Teilnehmer versenden",
"Please make sure to properly set up the email settings above." : "Bitte sicherstellen, dass die E-Mail Einstellungen oben korrekt angegeben sind.",
diff --git a/apps/dav/l10n/de.json b/apps/dav/l10n/de.json
index 83973d8a7b6..c4161d1d1da 100644
--- a/apps/dav/l10n/de.json
+++ b/apps/dav/l10n/de.json
@@ -52,11 +52,22 @@
"Where:" : "Wo:",
"Description:" : "Beschreibung:",
"Link:" : "Link:",
+ "Accept" : "Akzeptieren",
+ "Decline" : "Ablehnen",
+ "More options ..." : "Weitere Optionen…",
+ "More options at %s" : "Weitere Optionen unter %s",
"Contacts" : "Kontakte",
"WebDAV" : "WebDAV",
+ "WebDAV endpoint" : "WebDAV-Endpunkt",
"Technical details" : "Technische Details",
"Remote Address: %s" : "Entfernte Adresse: %s",
"Request ID: %s" : "Anfragekennung: %s",
+ "There was an error updating your attendance status." : "Es ist ein Fehler beim Aktualisieren Deines Teilnehmerstatus aufgetreten.",
+ "Please contact the organizer directly." : "Bitte den Organisator direkt kontaktieren.",
+ "Are you accepting the invitation?" : "Die Einladung annehmen?",
+ "Tentative" : "Vorläufig",
+ "Save" : "Speichern",
+ "Your attendance was updated successfully." : "Dein Anwesenheits-Status wurde aktualisiert.",
"CalDAV server" : "CalDAV-Server",
"Send invitations to attendees" : "Einladungen an die Teilnehmer versenden",
"Please make sure to properly set up the email settings above." : "Bitte sicherstellen, dass die E-Mail Einstellungen oben korrekt angegeben sind.",
diff --git a/apps/dav/l10n/de_DE.js b/apps/dav/l10n/de_DE.js
index 058a2af65a0..4edd79558a8 100644
--- a/apps/dav/l10n/de_DE.js
+++ b/apps/dav/l10n/de_DE.js
@@ -54,11 +54,22 @@ OC.L10N.register(
"Where:" : "Wo:",
"Description:" : "Beschreibung:",
"Link:" : "Link:",
+ "Accept" : "Akzeptieren",
+ "Decline" : "Ablehnen",
+ "More options ..." : "Weitere Optionen…",
+ "More options at %s" : "Weitere Optionen unter %s",
"Contacts" : "Kontakte",
"WebDAV" : "WebDAV",
+ "WebDAV endpoint" : "WebDAV-Endpunkt",
"Technical details" : "Technische Details",
"Remote Address: %s" : "Entfernte Adresse: %s",
"Request ID: %s" : "Anfragekennung: %s",
+ "There was an error updating your attendance status." : "Es ist ein Fehler beim Aktualisieren Ihres Teilnehmerstatus aufgetreten.",
+ "Please contact the organizer directly." : "Bitte den Organisator direkt kontaktieren.",
+ "Are you accepting the invitation?" : "Die Einladung annehmen?",
+ "Tentative" : "Vorläufig",
+ "Save" : "Speichern",
+ "Your attendance was updated successfully." : "Ihr Teilnehmerstatus wurde aktualisiert.",
"CalDAV server" : "CalDAV-Server",
"Send invitations to attendees" : "Einladungen an die Teilnehmer versenden",
"Please make sure to properly set up the email settings above." : "Stellen Sie sicher, dass die obigen E-Mail-Einstellungen korrekt sind.",
diff --git a/apps/dav/l10n/de_DE.json b/apps/dav/l10n/de_DE.json
index 824e670dfa2..72c60c0c6e6 100644
--- a/apps/dav/l10n/de_DE.json
+++ b/apps/dav/l10n/de_DE.json
@@ -52,11 +52,22 @@
"Where:" : "Wo:",
"Description:" : "Beschreibung:",
"Link:" : "Link:",
+ "Accept" : "Akzeptieren",
+ "Decline" : "Ablehnen",
+ "More options ..." : "Weitere Optionen…",
+ "More options at %s" : "Weitere Optionen unter %s",
"Contacts" : "Kontakte",
"WebDAV" : "WebDAV",
+ "WebDAV endpoint" : "WebDAV-Endpunkt",
"Technical details" : "Technische Details",
"Remote Address: %s" : "Entfernte Adresse: %s",
"Request ID: %s" : "Anfragekennung: %s",
+ "There was an error updating your attendance status." : "Es ist ein Fehler beim Aktualisieren Ihres Teilnehmerstatus aufgetreten.",
+ "Please contact the organizer directly." : "Bitte den Organisator direkt kontaktieren.",
+ "Are you accepting the invitation?" : "Die Einladung annehmen?",
+ "Tentative" : "Vorläufig",
+ "Save" : "Speichern",
+ "Your attendance was updated successfully." : "Ihr Teilnehmerstatus wurde aktualisiert.",
"CalDAV server" : "CalDAV-Server",
"Send invitations to attendees" : "Einladungen an die Teilnehmer versenden",
"Please make sure to properly set up the email settings above." : "Stellen Sie sicher, dass die obigen E-Mail-Einstellungen korrekt sind.",
diff --git a/apps/dav/l10n/en_GB.js b/apps/dav/l10n/en_GB.js
index eeda81b6612..9dbace82f50 100644
--- a/apps/dav/l10n/en_GB.js
+++ b/apps/dav/l10n/en_GB.js
@@ -56,6 +56,7 @@ OC.L10N.register(
"Link:" : "Link:",
"Contacts" : "Contacts",
"WebDAV" : "WebDAV",
+ "WebDAV endpoint" : "WebDAV endpoint",
"Technical details" : "Technical details",
"Remote Address: %s" : "Remote Address: %s",
"Request ID: %s" : "Request ID: %s",
diff --git a/apps/dav/l10n/en_GB.json b/apps/dav/l10n/en_GB.json
index e3a7a0b46b6..4a36e95c3d6 100644
--- a/apps/dav/l10n/en_GB.json
+++ b/apps/dav/l10n/en_GB.json
@@ -54,6 +54,7 @@
"Link:" : "Link:",
"Contacts" : "Contacts",
"WebDAV" : "WebDAV",
+ "WebDAV endpoint" : "WebDAV endpoint",
"Technical details" : "Technical details",
"Remote Address: %s" : "Remote Address: %s",
"Request ID: %s" : "Request ID: %s",
diff --git a/apps/dav/l10n/es.js b/apps/dav/l10n/es.js
index 07b1600e8b0..398741d9626 100644
--- a/apps/dav/l10n/es.js
+++ b/apps/dav/l10n/es.js
@@ -54,11 +54,22 @@ OC.L10N.register(
"Where:" : "Dónde:",
"Description:" : "Descripción:",
"Link:" : "Enlace:",
+ "Accept" : "Aceptar",
+ "Decline" : "Rechazar",
+ "More options ..." : "Más opciones...",
+ "More options at %s" : "Más opciones en %s",
"Contacts" : "Contactos",
"WebDAV" : "WebDAV",
+ "WebDAV endpoint" : "Extremo del WebDAV",
"Technical details" : "Detalles técnicos",
"Remote Address: %s" : "Dirección remota: %s",
"Request ID: %s" : "ID de solicitud: %s",
+ "There was an error updating your attendance status." : "Ha habido un error al actualizar tu estado de asistencia.",
+ "Please contact the organizer directly." : "Por favor, contacta directamente con el organizador.",
+ "Are you accepting the invitation?" : "¿Aceptas la invitación?",
+ "Tentative" : "Provisional",
+ "Save" : "Guardar",
+ "Your attendance was updated successfully." : "Tu asistencia se ha actualizado con éxito.",
"CalDAV server" : "Servidor CalDAV",
"Send invitations to attendees" : "Enviar invitaciones a los asistentes",
"Please make sure to properly set up the email settings above." : "Por favor, asegúrate de que las configuraciones de correo de arriba son correctas",
diff --git a/apps/dav/l10n/es.json b/apps/dav/l10n/es.json
index 9bb9118d043..d31347608a3 100644
--- a/apps/dav/l10n/es.json
+++ b/apps/dav/l10n/es.json
@@ -52,11 +52,22 @@
"Where:" : "Dónde:",
"Description:" : "Descripción:",
"Link:" : "Enlace:",
+ "Accept" : "Aceptar",
+ "Decline" : "Rechazar",
+ "More options ..." : "Más opciones...",
+ "More options at %s" : "Más opciones en %s",
"Contacts" : "Contactos",
"WebDAV" : "WebDAV",
+ "WebDAV endpoint" : "Extremo del WebDAV",
"Technical details" : "Detalles técnicos",
"Remote Address: %s" : "Dirección remota: %s",
"Request ID: %s" : "ID de solicitud: %s",
+ "There was an error updating your attendance status." : "Ha habido un error al actualizar tu estado de asistencia.",
+ "Please contact the organizer directly." : "Por favor, contacta directamente con el organizador.",
+ "Are you accepting the invitation?" : "¿Aceptas la invitación?",
+ "Tentative" : "Provisional",
+ "Save" : "Guardar",
+ "Your attendance was updated successfully." : "Tu asistencia se ha actualizado con éxito.",
"CalDAV server" : "Servidor CalDAV",
"Send invitations to attendees" : "Enviar invitaciones a los asistentes",
"Please make sure to properly set up the email settings above." : "Por favor, asegúrate de que las configuraciones de correo de arriba son correctas",
diff --git a/apps/dav/l10n/es_MX.js b/apps/dav/l10n/es_MX.js
index d01873bd444..00f6fd81bbb 100644
--- a/apps/dav/l10n/es_MX.js
+++ b/apps/dav/l10n/es_MX.js
@@ -56,6 +56,7 @@ OC.L10N.register(
"Link:" : "Enlace:",
"Contacts" : "Contactos",
"WebDAV" : "WebDAV",
+ "WebDAV endpoint" : "Endpoint WebDAV",
"Technical details" : "Detalles técnicos",
"Remote Address: %s" : "Dirección remota: %s",
"Request ID: %s" : "ID de solicitud: %s",
diff --git a/apps/dav/l10n/es_MX.json b/apps/dav/l10n/es_MX.json
index 5cbd4755efd..bd83d8e3ad3 100644
--- a/apps/dav/l10n/es_MX.json
+++ b/apps/dav/l10n/es_MX.json
@@ -54,6 +54,7 @@
"Link:" : "Enlace:",
"Contacts" : "Contactos",
"WebDAV" : "WebDAV",
+ "WebDAV endpoint" : "Endpoint WebDAV",
"Technical details" : "Detalles técnicos",
"Remote Address: %s" : "Dirección remota: %s",
"Request ID: %s" : "ID de solicitud: %s",
diff --git a/apps/dav/l10n/fr.js b/apps/dav/l10n/fr.js
index 291e5615265..eb814d7a6cb 100644
--- a/apps/dav/l10n/fr.js
+++ b/apps/dav/l10n/fr.js
@@ -54,11 +54,19 @@ OC.L10N.register(
"Where:" : "Où :",
"Description:" : "Description :",
"Link:" : "Lien :",
+ "Accept" : "Accepter",
+ "Decline" : "Refuser",
+ "More options ..." : "Plus d'options ...",
+ "More options at %s" : "Plus d'options à %s",
"Contacts" : "Contacts",
"WebDAV" : "WebDAV",
"Technical details" : "Détails techniques",
"Remote Address: %s" : "Adresse distante : %s",
"Request ID: %s" : "ID de la requête : %s",
+ "Please contact the organizer directly." : "Merci de contacter l'organisateur directement.",
+ "Are you accepting the invitation?" : "Acceptez-vous l'invitation ?",
+ "Tentative" : "Provisoire",
+ "Save" : "Sauvegarder",
"CalDAV server" : "Serveur CalDAV",
"Send invitations to attendees" : "Envoyer des invitations aux participants",
"Please make sure to properly set up the email settings above." : "Merci de vérifier d'avoir correctement configuré les paramètres de courriel ci-dessus",
diff --git a/apps/dav/l10n/fr.json b/apps/dav/l10n/fr.json
index b09c10b96f3..672e6aec7e3 100644
--- a/apps/dav/l10n/fr.json
+++ b/apps/dav/l10n/fr.json
@@ -52,11 +52,19 @@
"Where:" : "Où :",
"Description:" : "Description :",
"Link:" : "Lien :",
+ "Accept" : "Accepter",
+ "Decline" : "Refuser",
+ "More options ..." : "Plus d'options ...",
+ "More options at %s" : "Plus d'options à %s",
"Contacts" : "Contacts",
"WebDAV" : "WebDAV",
"Technical details" : "Détails techniques",
"Remote Address: %s" : "Adresse distante : %s",
"Request ID: %s" : "ID de la requête : %s",
+ "Please contact the organizer directly." : "Merci de contacter l'organisateur directement.",
+ "Are you accepting the invitation?" : "Acceptez-vous l'invitation ?",
+ "Tentative" : "Provisoire",
+ "Save" : "Sauvegarder",
"CalDAV server" : "Serveur CalDAV",
"Send invitations to attendees" : "Envoyer des invitations aux participants",
"Please make sure to properly set up the email settings above." : "Merci de vérifier d'avoir correctement configuré les paramètres de courriel ci-dessus",
diff --git a/apps/dav/l10n/he.js b/apps/dav/l10n/he.js
index 137476ddc05..2f91c82d3e5 100644
--- a/apps/dav/l10n/he.js
+++ b/apps/dav/l10n/he.js
@@ -12,9 +12,38 @@ OC.L10N.register(
"You updated calendar {calendar}" : "עדכנת את היומן {calendar}",
"You shared calendar {calendar} as public link" : "שיתפת את היומן {calendar} כקישור ציבורי",
"You removed public link for calendar {calendar}" : "הסרת את הקישור הציבורי ליומן {calendar}",
+ "{actor} shared calendar {calendar} with you" : "שותף אתך לוח השנה {calendar} על ידי {actor}",
+ "You shared calendar {calendar} with {user}" : "שיתפת לוח שנה {calendar} עם {user}",
+ "{actor} shared calendar {calendar} with {user}" : "לוח השנה {calendar} שותף על ידי {actor} עם {user}",
+ "{actor} unshared calendar {calendar} from you" : "השיתוף של לוח השנה {calendar} אתך הופסק על ידי {actor}",
+ "You unshared calendar {calendar} from {user}" : "ביטלת את שיתוף לוח השנה {calendar} עם {user}",
+ "{actor} unshared calendar {calendar} from {user}" : "השיתוף של לוח השנה {calendar} עם {user} הופסק על ידי {actor}",
+ "{actor} unshared calendar {calendar} from themselves" : "השיתוף של לוח השנה {calendar} עם עצמם הופסק על ידי {actor}",
+ "You shared calendar {calendar} with group {group}" : "שיתפת את לוח השנה {calendar} עם הקבוצה {group}",
+ "{actor} shared calendar {calendar} with group {group}" : "לוח השנה {calendar} שותף עם הקבוצה {group} על ידי {actor}",
+ "You unshared calendar {calendar} from group {group}" : "הפסקת את שיתוף לוח השנה {calendar} עם הקבוצה {group}",
+ "{actor} unshared calendar {calendar} from group {group}" : "השיתוף של לוח השנה {calendar} עם {group} הופסק על ידי {actor}",
+ "{actor} created event {event} in calendar {calendar}" : "האירוע {event} נוצר בלוח השנה {calendar} על ידי {actor}",
+ "You created event {event} in calendar {calendar}" : "יצרת אירוע {event} בלוח השנה {calendar}",
+ "{actor} deleted event {event} from calendar {calendar}" : "האירוע {event} נמחק מלוח השנה {calendar} על ידי {actor}",
+ "You deleted event {event} from calendar {calendar}" : "מחקת אירוע {event} מלוח השנה {calendar}",
+ "{actor} updated event {event} in calendar {calendar}" : "האירוע {event} עודכן בלוח השנה {calendar} על ידי {actor}",
+ "You updated event {event} in calendar {calendar}" : "עדכנת את האירוע {event} בלוח השנה {calendar}",
+ "{actor} created todo {todo} in list {calendar}" : "המשימה לביצוע {todo} ברשימה {calendar} נוצרה על ידי {actor}",
+ "You created todo {todo} in list {calendar}" : "יצרת את המשימה לביצוע {todo} ברשימה {calendar}",
+ "{actor} deleted todo {todo} from list {calendar}" : "המשימה לביצוע {todo} מהרשימה {calendar} נמחקה על ידי {actor}",
+ "You deleted todo {todo} from list {calendar}" : "מחקת את המשימה לביצוע {todo} מהרשימה {calendar}",
+ "{actor} updated todo {todo} in list {calendar}" : "המשימה לביצוע {todo} ברשימה {calendar} עודכנה על ידי {actor}",
+ "You updated todo {todo} in list {calendar}" : "עדכנת את המשימה לביצוע {todo} ברשימה {calendar}",
+ "{actor} solved todo {todo} in list {calendar}" : "המשימה לביצוע {todo} ברשימה {calendar} נפתרה על ידי {actor}",
+ "You solved todo {todo} in list {calendar}" : "פתרת משימה לביצוע {todo} ברשימה {calendar}",
+ "{actor} reopened todo {todo} in list {calendar}" : "המשימה לביצוע {todo} ברשימה {calendar} נפתחה מחדש על ידי {actor}",
+ "You reopened todo {todo} in list {calendar}" : "פתחת מחדש את המשימה לביצוע {todo} ברשימה {calendar}",
"A <strong>calendar</strong> was modified" : " <strong>יומן</strong> נערך",
"A calendar <strong>event</strong> was modified" : "<strong>אירוע</strong> ביומן נערך",
+ "A calendar <strong>todo</strong> was modified" : "נערכה <strong>מטלה</strong> בלוח שנה",
"Contact birthdays" : "ימי הולדת של אנשי קשר",
+ "%s via %s" : "%s דרך %s",
"Invitation canceled" : "ההזמנה בוטלה",
"Hello %s," : "שלום %s,",
"The meeting »%s« with %s was canceled." : "הפגישה „%s” עם %s בוטלה.",
@@ -24,11 +53,22 @@ OC.L10N.register(
"Where:" : "איפה:",
"Description:" : "תיאור:",
"Link:" : "קישור:",
+ "Accept" : "קבלה",
+ "Decline" : "דחייה",
+ "More options ..." : "אפשרויות נוספות…",
+ "More options at %s" : "אפשרויות נוספים ב־%s",
"Contacts" : "אנשי קשר",
"WebDAV" : "WebDAV",
+ "WebDAV endpoint" : "נקודת קצה WebDAV",
"Technical details" : "פרטים טכניים",
"Remote Address: %s" : "כתובת מרוחקת: %s",
"Request ID: %s" : "מזהה בקשה: %s",
+ "There was an error updating your attendance status." : "אירעה שגיאה בעת עדכון מצב ההשתתפות שלך.",
+ "Please contact the organizer directly." : "נא ליצור קשר עם הגוף מארגן ישירות.",
+ "Are you accepting the invitation?" : "האם להיענות להזמנה?",
+ "Tentative" : "טנטטיבית",
+ "Save" : "שמירה",
+ "Your attendance was updated successfully." : "ההשתתפות שלך עודכנה בהצלחה.",
"CalDAV server" : "שרת CalDAV",
"Send invitations to attendees" : "שליחת הזמנות למשתתפים",
"Please make sure to properly set up the email settings above." : "נא לוודא שהגדרת את הדוא״ל שלהלן כראוי.",
diff --git a/apps/dav/l10n/he.json b/apps/dav/l10n/he.json
index b0d2b58a58d..5b46ea6e0a0 100644
--- a/apps/dav/l10n/he.json
+++ b/apps/dav/l10n/he.json
@@ -10,9 +10,38 @@
"You updated calendar {calendar}" : "עדכנת את היומן {calendar}",
"You shared calendar {calendar} as public link" : "שיתפת את היומן {calendar} כקישור ציבורי",
"You removed public link for calendar {calendar}" : "הסרת את הקישור הציבורי ליומן {calendar}",
+ "{actor} shared calendar {calendar} with you" : "שותף אתך לוח השנה {calendar} על ידי {actor}",
+ "You shared calendar {calendar} with {user}" : "שיתפת לוח שנה {calendar} עם {user}",
+ "{actor} shared calendar {calendar} with {user}" : "לוח השנה {calendar} שותף על ידי {actor} עם {user}",
+ "{actor} unshared calendar {calendar} from you" : "השיתוף של לוח השנה {calendar} אתך הופסק על ידי {actor}",
+ "You unshared calendar {calendar} from {user}" : "ביטלת את שיתוף לוח השנה {calendar} עם {user}",
+ "{actor} unshared calendar {calendar} from {user}" : "השיתוף של לוח השנה {calendar} עם {user} הופסק על ידי {actor}",
+ "{actor} unshared calendar {calendar} from themselves" : "השיתוף של לוח השנה {calendar} עם עצמם הופסק על ידי {actor}",
+ "You shared calendar {calendar} with group {group}" : "שיתפת את לוח השנה {calendar} עם הקבוצה {group}",
+ "{actor} shared calendar {calendar} with group {group}" : "לוח השנה {calendar} שותף עם הקבוצה {group} על ידי {actor}",
+ "You unshared calendar {calendar} from group {group}" : "הפסקת את שיתוף לוח השנה {calendar} עם הקבוצה {group}",
+ "{actor} unshared calendar {calendar} from group {group}" : "השיתוף של לוח השנה {calendar} עם {group} הופסק על ידי {actor}",
+ "{actor} created event {event} in calendar {calendar}" : "האירוע {event} נוצר בלוח השנה {calendar} על ידי {actor}",
+ "You created event {event} in calendar {calendar}" : "יצרת אירוע {event} בלוח השנה {calendar}",
+ "{actor} deleted event {event} from calendar {calendar}" : "האירוע {event} נמחק מלוח השנה {calendar} על ידי {actor}",
+ "You deleted event {event} from calendar {calendar}" : "מחקת אירוע {event} מלוח השנה {calendar}",
+ "{actor} updated event {event} in calendar {calendar}" : "האירוע {event} עודכן בלוח השנה {calendar} על ידי {actor}",
+ "You updated event {event} in calendar {calendar}" : "עדכנת את האירוע {event} בלוח השנה {calendar}",
+ "{actor} created todo {todo} in list {calendar}" : "המשימה לביצוע {todo} ברשימה {calendar} נוצרה על ידי {actor}",
+ "You created todo {todo} in list {calendar}" : "יצרת את המשימה לביצוע {todo} ברשימה {calendar}",
+ "{actor} deleted todo {todo} from list {calendar}" : "המשימה לביצוע {todo} מהרשימה {calendar} נמחקה על ידי {actor}",
+ "You deleted todo {todo} from list {calendar}" : "מחקת את המשימה לביצוע {todo} מהרשימה {calendar}",
+ "{actor} updated todo {todo} in list {calendar}" : "המשימה לביצוע {todo} ברשימה {calendar} עודכנה על ידי {actor}",
+ "You updated todo {todo} in list {calendar}" : "עדכנת את המשימה לביצוע {todo} ברשימה {calendar}",
+ "{actor} solved todo {todo} in list {calendar}" : "המשימה לביצוע {todo} ברשימה {calendar} נפתרה על ידי {actor}",
+ "You solved todo {todo} in list {calendar}" : "פתרת משימה לביצוע {todo} ברשימה {calendar}",
+ "{actor} reopened todo {todo} in list {calendar}" : "המשימה לביצוע {todo} ברשימה {calendar} נפתחה מחדש על ידי {actor}",
+ "You reopened todo {todo} in list {calendar}" : "פתחת מחדש את המשימה לביצוע {todo} ברשימה {calendar}",
"A <strong>calendar</strong> was modified" : " <strong>יומן</strong> נערך",
"A calendar <strong>event</strong> was modified" : "<strong>אירוע</strong> ביומן נערך",
+ "A calendar <strong>todo</strong> was modified" : "נערכה <strong>מטלה</strong> בלוח שנה",
"Contact birthdays" : "ימי הולדת של אנשי קשר",
+ "%s via %s" : "%s דרך %s",
"Invitation canceled" : "ההזמנה בוטלה",
"Hello %s," : "שלום %s,",
"The meeting »%s« with %s was canceled." : "הפגישה „%s” עם %s בוטלה.",
@@ -22,11 +51,22 @@
"Where:" : "איפה:",
"Description:" : "תיאור:",
"Link:" : "קישור:",
+ "Accept" : "קבלה",
+ "Decline" : "דחייה",
+ "More options ..." : "אפשרויות נוספות…",
+ "More options at %s" : "אפשרויות נוספים ב־%s",
"Contacts" : "אנשי קשר",
"WebDAV" : "WebDAV",
+ "WebDAV endpoint" : "נקודת קצה WebDAV",
"Technical details" : "פרטים טכניים",
"Remote Address: %s" : "כתובת מרוחקת: %s",
"Request ID: %s" : "מזהה בקשה: %s",
+ "There was an error updating your attendance status." : "אירעה שגיאה בעת עדכון מצב ההשתתפות שלך.",
+ "Please contact the organizer directly." : "נא ליצור קשר עם הגוף מארגן ישירות.",
+ "Are you accepting the invitation?" : "האם להיענות להזמנה?",
+ "Tentative" : "טנטטיבית",
+ "Save" : "שמירה",
+ "Your attendance was updated successfully." : "ההשתתפות שלך עודכנה בהצלחה.",
"CalDAV server" : "שרת CalDAV",
"Send invitations to attendees" : "שליחת הזמנות למשתתפים",
"Please make sure to properly set up the email settings above." : "נא לוודא שהגדרת את הדוא״ל שלהלן כראוי.",
diff --git a/apps/dav/l10n/it.js b/apps/dav/l10n/it.js
index 34893da53b0..10fa534a9d6 100644
--- a/apps/dav/l10n/it.js
+++ b/apps/dav/l10n/it.js
@@ -54,11 +54,22 @@ OC.L10N.register(
"Where:" : "Dove:",
"Description:" : "Descrizione:",
"Link:" : "Collegamento:",
+ "Accept" : "Accetta",
+ "Decline" : "Rifiuta",
+ "More options ..." : "Altre opzioni...",
+ "More options at %s" : "Altre opzioni alle %s",
"Contacts" : "Contatti",
"WebDAV" : "WebDAV",
+ "WebDAV endpoint" : "Terminatore WebDAV",
"Technical details" : "Dettagli tecnici",
"Remote Address: %s" : "Indirizzo remoto: %s",
"Request ID: %s" : "ID richiesta: %s",
+ "There was an error updating your attendance status." : "Si è verificato un errore durante l'aggiornamento dello stato della tua partecipazione.",
+ "Please contact the organizer directly." : "Contatta direttamente l'amministratore.",
+ "Are you accepting the invitation?" : "Accetti l'invito?",
+ "Tentative" : "Provvisorio",
+ "Save" : "Salva",
+ "Your attendance was updated successfully." : "La tua partecipazione è stata aggiornata correttamente.",
"CalDAV server" : "Server CalDAV",
"Send invitations to attendees" : "Invia gli inviti ai partecipanti",
"Please make sure to properly set up the email settings above." : "Assicurati di configurare correttamente le impostazioni di posta sopra.",
diff --git a/apps/dav/l10n/it.json b/apps/dav/l10n/it.json
index 538b041e10a..eee0dddcf95 100644
--- a/apps/dav/l10n/it.json
+++ b/apps/dav/l10n/it.json
@@ -52,11 +52,22 @@
"Where:" : "Dove:",
"Description:" : "Descrizione:",
"Link:" : "Collegamento:",
+ "Accept" : "Accetta",
+ "Decline" : "Rifiuta",
+ "More options ..." : "Altre opzioni...",
+ "More options at %s" : "Altre opzioni alle %s",
"Contacts" : "Contatti",
"WebDAV" : "WebDAV",
+ "WebDAV endpoint" : "Terminatore WebDAV",
"Technical details" : "Dettagli tecnici",
"Remote Address: %s" : "Indirizzo remoto: %s",
"Request ID: %s" : "ID richiesta: %s",
+ "There was an error updating your attendance status." : "Si è verificato un errore durante l'aggiornamento dello stato della tua partecipazione.",
+ "Please contact the organizer directly." : "Contatta direttamente l'amministratore.",
+ "Are you accepting the invitation?" : "Accetti l'invito?",
+ "Tentative" : "Provvisorio",
+ "Save" : "Salva",
+ "Your attendance was updated successfully." : "La tua partecipazione è stata aggiornata correttamente.",
"CalDAV server" : "Server CalDAV",
"Send invitations to attendees" : "Invia gli inviti ai partecipanti",
"Please make sure to properly set up the email settings above." : "Assicurati di configurare correttamente le impostazioni di posta sopra.",
diff --git a/apps/dav/l10n/pl.js b/apps/dav/l10n/pl.js
index 11d53da887d..a36bdc8c44b 100644
--- a/apps/dav/l10n/pl.js
+++ b/apps/dav/l10n/pl.js
@@ -59,6 +59,8 @@ OC.L10N.register(
"Technical details" : "Szczegóły techniczne",
"Remote Address: %s" : "Adres zdalny: %s",
"Request ID: %s" : "ID żądania: %s",
+ "Are you accepting the invitation?" : "Czy akceptujesz zaproszenie?",
+ "Save" : "Zapisz",
"CalDAV server" : "Serwer CalDAV",
"Send invitations to attendees" : "Wyślij uczestnikom zaproszenia",
"Please make sure to properly set up the email settings above." : "Upewnij się, że dobrze skonfigurowano powyżej ustawienia poczty e-mail.",
diff --git a/apps/dav/l10n/pl.json b/apps/dav/l10n/pl.json
index e04a6e25b5c..899a32ce5da 100644
--- a/apps/dav/l10n/pl.json
+++ b/apps/dav/l10n/pl.json
@@ -57,6 +57,8 @@
"Technical details" : "Szczegóły techniczne",
"Remote Address: %s" : "Adres zdalny: %s",
"Request ID: %s" : "ID żądania: %s",
+ "Are you accepting the invitation?" : "Czy akceptujesz zaproszenie?",
+ "Save" : "Zapisz",
"CalDAV server" : "Serwer CalDAV",
"Send invitations to attendees" : "Wyślij uczestnikom zaproszenia",
"Please make sure to properly set up the email settings above." : "Upewnij się, że dobrze skonfigurowano powyżej ustawienia poczty e-mail.",
diff --git a/apps/dav/l10n/pt_BR.js b/apps/dav/l10n/pt_BR.js
index 75346f3aacb..9a14b215132 100644
--- a/apps/dav/l10n/pt_BR.js
+++ b/apps/dav/l10n/pt_BR.js
@@ -54,14 +54,25 @@ OC.L10N.register(
"Where:" : "Onde:",
"Description:" : "Descrição:",
"Link:" : "Link:",
+ "Accept" : "Aceitar",
+ "Decline" : "Rejeitar",
+ "More options ..." : "Mais opções...",
+ "More options at %s" : "Mais opções em %s",
"Contacts" : "Contatos",
"WebDAV" : "WebDAV",
+ "WebDAV endpoint" : "Ponto final WebDAV",
"Technical details" : "Detalhes técnicos",
"Remote Address: %s" : "Endereço remoto: %s",
"Request ID: %s" : "ID do solicitante: %s",
+ "There was an error updating your attendance status." : "Houve um erro ao atualizar seu status de participação.",
+ "Please contact the organizer directly." : "Por favor, contate o organizador diretamente.",
+ "Are you accepting the invitation?" : "Você está aceitando o convite?",
+ "Tentative" : "Tentativa",
+ "Save" : "Salvar",
+ "Your attendance was updated successfully." : "Sua participação foi atualizada com sucesso.",
"CalDAV server" : "Servidor CalDAV",
"Send invitations to attendees" : "Envie convites aos participantes",
- "Please make sure to properly set up the email settings above." : "Certifique-se de configurar corretamente o email acima.",
+ "Please make sure to properly set up the email settings above." : "Certifique-se de configurar corretamente o e-mail acima.",
"Automatically generate a birthday calendar" : "Gerar um calendário de aniversários automaticamente",
"Birthday calendars will be generated by a background job." : "Os calendários de aniversários serão gerados na retaguarda.",
"Hence they will not be available immediately after enabling but will show up after some time." : "Portanto, eles não estarão disponíveis imediatamente ao habilitar mas após algum tempo."
diff --git a/apps/dav/l10n/pt_BR.json b/apps/dav/l10n/pt_BR.json
index f773ee0d93f..867977e00d1 100644
--- a/apps/dav/l10n/pt_BR.json
+++ b/apps/dav/l10n/pt_BR.json
@@ -52,14 +52,25 @@
"Where:" : "Onde:",
"Description:" : "Descrição:",
"Link:" : "Link:",
+ "Accept" : "Aceitar",
+ "Decline" : "Rejeitar",
+ "More options ..." : "Mais opções...",
+ "More options at %s" : "Mais opções em %s",
"Contacts" : "Contatos",
"WebDAV" : "WebDAV",
+ "WebDAV endpoint" : "Ponto final WebDAV",
"Technical details" : "Detalhes técnicos",
"Remote Address: %s" : "Endereço remoto: %s",
"Request ID: %s" : "ID do solicitante: %s",
+ "There was an error updating your attendance status." : "Houve um erro ao atualizar seu status de participação.",
+ "Please contact the organizer directly." : "Por favor, contate o organizador diretamente.",
+ "Are you accepting the invitation?" : "Você está aceitando o convite?",
+ "Tentative" : "Tentativa",
+ "Save" : "Salvar",
+ "Your attendance was updated successfully." : "Sua participação foi atualizada com sucesso.",
"CalDAV server" : "Servidor CalDAV",
"Send invitations to attendees" : "Envie convites aos participantes",
- "Please make sure to properly set up the email settings above." : "Certifique-se de configurar corretamente o email acima.",
+ "Please make sure to properly set up the email settings above." : "Certifique-se de configurar corretamente o e-mail acima.",
"Automatically generate a birthday calendar" : "Gerar um calendário de aniversários automaticamente",
"Birthday calendars will be generated by a background job." : "Os calendários de aniversários serão gerados na retaguarda.",
"Hence they will not be available immediately after enabling but will show up after some time." : "Portanto, eles não estarão disponíveis imediatamente ao habilitar mas após algum tempo."
diff --git a/apps/dav/l10n/ru.js b/apps/dav/l10n/ru.js
index 70cfd8050a6..cbe6509e952 100644
--- a/apps/dav/l10n/ru.js
+++ b/apps/dav/l10n/ru.js
@@ -56,6 +56,7 @@ OC.L10N.register(
"Link:" : "Ссылка:",
"Contacts" : "Контакты",
"WebDAV" : "WebDAV",
+ "WebDAV endpoint" : "Конечная точка WebDAV",
"Technical details" : "Технические подробности",
"Remote Address: %s" : "Удаленный адрес: %s",
"Request ID: %s" : "ID запроса: %s",
diff --git a/apps/dav/l10n/ru.json b/apps/dav/l10n/ru.json
index 934bcaa719e..dc87356477e 100644
--- a/apps/dav/l10n/ru.json
+++ b/apps/dav/l10n/ru.json
@@ -54,6 +54,7 @@
"Link:" : "Ссылка:",
"Contacts" : "Контакты",
"WebDAV" : "WebDAV",
+ "WebDAV endpoint" : "Конечная точка WebDAV",
"Technical details" : "Технические подробности",
"Remote Address: %s" : "Удаленный адрес: %s",
"Request ID: %s" : "ID запроса: %s",
diff --git a/apps/dav/l10n/sr.js b/apps/dav/l10n/sr.js
index e31b17da482..ce3292345ec 100644
--- a/apps/dav/l10n/sr.js
+++ b/apps/dav/l10n/sr.js
@@ -54,11 +54,22 @@ OC.L10N.register(
"Where:" : "Место:",
"Description:" : "Опис:",
"Link:" : "Веза:",
+ "Accept" : "Прихвати",
+ "Decline" : "Одбиј",
+ "More options ..." : "Још опција...",
+ "More options at %s" : "Још опција на %s",
"Contacts" : "Контакти",
"WebDAV" : "ВебДАВ",
+ "WebDAV endpoint" : "WebDAV крајња тачка",
"Technical details" : "Технички детаљи",
"Remote Address: %s" : "Удаљена адреса: %s",
"Request ID: %s" : "ИД захтева: %s",
+ "There was an error updating your attendance status." : "Десила се грешка приликом ажурирања статуса Вашег присуства.",
+ "Please contact the organizer directly." : "Контактирајте директно организатора.",
+ "Are you accepting the invitation?" : "Да ли прихватате позивницу?",
+ "Tentative" : "Условна потврда",
+ "Save" : "Сачувај",
+ "Your attendance was updated successfully." : "Ваше присуство је успешно ажурирано.",
"CalDAV server" : "CalDAV сервер",
"Send invitations to attendees" : "Пошаљи позивницу учесницима",
"Please make sure to properly set up the email settings above." : "Пазите да правилно подесите поставке е-поште изнад.",
diff --git a/apps/dav/l10n/sr.json b/apps/dav/l10n/sr.json
index fea5b91a7d0..2f9951b0e4d 100644
--- a/apps/dav/l10n/sr.json
+++ b/apps/dav/l10n/sr.json
@@ -52,11 +52,22 @@
"Where:" : "Место:",
"Description:" : "Опис:",
"Link:" : "Веза:",
+ "Accept" : "Прихвати",
+ "Decline" : "Одбиј",
+ "More options ..." : "Још опција...",
+ "More options at %s" : "Још опција на %s",
"Contacts" : "Контакти",
"WebDAV" : "ВебДАВ",
+ "WebDAV endpoint" : "WebDAV крајња тачка",
"Technical details" : "Технички детаљи",
"Remote Address: %s" : "Удаљена адреса: %s",
"Request ID: %s" : "ИД захтева: %s",
+ "There was an error updating your attendance status." : "Десила се грешка приликом ажурирања статуса Вашег присуства.",
+ "Please contact the organizer directly." : "Контактирајте директно организатора.",
+ "Are you accepting the invitation?" : "Да ли прихватате позивницу?",
+ "Tentative" : "Условна потврда",
+ "Save" : "Сачувај",
+ "Your attendance was updated successfully." : "Ваше присуство је успешно ажурирано.",
"CalDAV server" : "CalDAV сервер",
"Send invitations to attendees" : "Пошаљи позивницу учесницима",
"Please make sure to properly set up the email settings above." : "Пазите да правилно подесите поставке е-поште изнад.",
diff --git a/apps/dav/l10n/tr.js b/apps/dav/l10n/tr.js
index 4053bf54330..8fa0ceb5baf 100644
--- a/apps/dav/l10n/tr.js
+++ b/apps/dav/l10n/tr.js
@@ -56,6 +56,7 @@ OC.L10N.register(
"Link:" : "Bağlantı:",
"Contacts" : "Kişiler",
"WebDAV" : "WebDAV",
+ "WebDAV endpoint" : "WebDAV Bağlantı Noktası",
"Technical details" : "Teknik ayrıntılar",
"Remote Address: %s" : "Uzak Adres: %s",
"Request ID: %s" : "İstek Kodu: %s",
diff --git a/apps/dav/l10n/tr.json b/apps/dav/l10n/tr.json
index 254ca8d63fb..c9856ad5b03 100644
--- a/apps/dav/l10n/tr.json
+++ b/apps/dav/l10n/tr.json
@@ -54,6 +54,7 @@
"Link:" : "Bağlantı:",
"Contacts" : "Kişiler",
"WebDAV" : "WebDAV",
+ "WebDAV endpoint" : "WebDAV Bağlantı Noktası",
"Technical details" : "Teknik ayrıntılar",
"Remote Address: %s" : "Uzak Adres: %s",
"Request ID: %s" : "İstek Kodu: %s",
diff --git a/apps/dav/lib/BackgroundJob/CleanupInvitationTokenJob.php b/apps/dav/lib/BackgroundJob/CleanupInvitationTokenJob.php
new file mode 100644
index 00000000000..942f8e23ecb
--- /dev/null
+++ b/apps/dav/lib/BackgroundJob/CleanupInvitationTokenJob.php
@@ -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_invitations')
+ ->where($query->expr()->lt('expiration',
+ $query->createNamedParameter($this->timeFactory->getTime())))
+ ->execute();
+ }
+}
diff --git a/apps/dav/lib/BackgroundJob/UpdateCalendarResourcesRoomsBackgroundJob.php b/apps/dav/lib/BackgroundJob/UpdateCalendarResourcesRoomsBackgroundJob.php
new file mode 100644
index 00000000000..c5f8f6586e9
--- /dev/null
+++ b/apps/dav/lib/BackgroundJob/UpdateCalendarResourcesRoomsBackgroundJob.php
@@ -0,0 +1,337 @@
+<?php
+/**
+ * @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 OCA\DAV\CalDAV\CalDavBackend;
+use OCP\Calendar\BackendTemporarilyUnavailableException;
+use OCP\Calendar\Resource\IManager as IResourceManager;
+use OCP\Calendar\Resource\IResource;
+use OCP\Calendar\Room\IManager as IRoomManager;
+use OCP\Calendar\Room\IRoom;
+use OCP\IDBConnection;
+
+class UpdateCalendarResourcesRoomsBackgroundJob extends TimedJob {
+
+ /** @var IResourceManager */
+ private $resourceManager;
+
+ /** @var IRoomManager */
+ private $roomManager;
+
+ /** @var IDBConnection */
+ private $db;
+
+ /** @var CalDavBackend */
+ private $calDavBackend;
+
+ /** @var string */
+ private $resourceDbTable;
+
+ /** @var string */
+ private $resourcePrincipalUri;
+
+ /** @var string */
+ private $roomDbTable;
+
+ /** @var string */
+ private $roomPrincipalUri;
+
+ /**
+ * UpdateCalendarResourcesRoomsBackgroundJob constructor.
+ *
+ * @param IResourceManager $resourceManager
+ * @param IRoomManager $roomManager
+ * @param IDBConnection $dbConnection
+ * @param CalDavBackend $calDavBackend
+ */
+ public function __construct(IResourceManager $resourceManager, IRoomManager $roomManager,
+ IDBConnection $dbConnection, CalDavBackend $calDavBackend) {
+ $this->resourceManager = $resourceManager;
+ $this->roomManager = $roomManager;
+ $this->db = $dbConnection;
+ $this->calDavBackend = $calDavBackend;
+ $this->resourceDbTable = 'calendar_resources';
+ $this->resourcePrincipalUri = 'principals/calendar-resources';
+ $this->roomDbTable = 'calendar_rooms';
+ $this->roomPrincipalUri = 'principals/calendar-rooms';
+
+ // run once an hour
+ $this->setInterval(60 * 60);
+ }
+
+ /**
+ * @param $argument
+ */
+ public function run($argument) {
+ $this->runResources();
+ $this->runRooms();
+ }
+
+ /**
+ * run timed job for resources
+ */
+ private function runResources() {
+ $resourceBackends = $this->resourceManager->getBackends();
+ $cachedResources = $this->getCached($this->resourceDbTable);
+ $cachedResourceIds = $this->getCachedResourceIds($cachedResources);
+
+ $remoteResourceIds = [];
+ foreach($resourceBackends as $resourceBackend) {
+ try {
+ $remoteResourceIds[$resourceBackend->getBackendIdentifier()] =
+ $resourceBackend->listAllResources();
+ } catch(BackendTemporarilyUnavailableException $ex) {
+ // If the backend is temporarily unavailable
+ // ignore this backend in this execution
+ unset($cachedResourceIds[$resourceBackend->getBackendIdentifier()]);
+ }
+ }
+
+ $sortedResources = $this->sortByNewDeletedExisting($cachedResourceIds, $remoteResourceIds);
+
+ foreach($sortedResources['new'] as $backendId => $newResources) {
+ foreach ($newResources as $newResource) {
+ $resource = $this->resourceManager->getBackend($backendId)
+ ->getResource($newResource);
+ $this->addToCache($this->resourceDbTable, $resource);
+ }
+ }
+ foreach($sortedResources['deleted'] as $backendId => $deletedResources) {
+ foreach ($deletedResources as $deletedResource) {
+ $this->deleteFromCache($this->resourceDbTable,
+ $this->resourcePrincipalUri, $backendId, $deletedResource);
+ }
+ }
+ foreach($sortedResources['edited'] as $backendId => $editedResources) {
+ foreach ($editedResources as $editedResource) {
+ $resource = $this->resourceManager->getBackend($backendId)
+ ->getResource($editedResource);
+ $this->updateCache($this->resourceDbTable, $resource);
+ }
+ }
+ }
+
+ /**
+ * run timed job for rooms
+ */
+ private function runRooms() {
+ $roomBackends = $this->roomManager->getBackends();
+ $cachedRooms = $this->getCached($this->roomDbTable);
+ $cachedRoomIds = $this->getCachedRoomIds($cachedRooms);
+
+ $remoteRoomIds = [];
+ foreach($roomBackends as $roomBackend) {
+ try {
+ $remoteRoomIds[$roomBackend->getBackendIdentifier()] =
+ $roomBackend->listAllRooms();
+ } catch(BackendTemporarilyUnavailableException $ex) {
+ // If the backend is temporarily unavailable
+ // ignore this backend in this execution
+ unset($cachedRoomIds[$roomBackend->getBackendIdentifier()]);
+ }
+ }
+
+ $sortedRooms = $this->sortByNewDeletedExisting($cachedRoomIds, $remoteRoomIds);
+
+ foreach($sortedRooms['new'] as $backendId => $newRooms) {
+ foreach ($newRooms as $newRoom) {
+ $resource = $this->roomManager->getBackend($backendId)
+ ->getRoom($newRoom);
+ $this->addToCache($this->roomDbTable, $resource);
+ }
+ }
+ foreach($sortedRooms['deleted'] as $backendId => $deletedRooms) {
+ foreach ($deletedRooms as $deletedRoom) {
+ $this->deleteFromCache($this->roomDbTable,
+ $this->roomPrincipalUri, $backendId, $deletedRoom);
+ }
+ }
+ foreach($sortedRooms['edited'] as $backendId => $editedRooms) {
+ foreach ($editedRooms as $editedRoom) {
+ $resource = $this->roomManager->getBackend($backendId)
+ ->getRoom($editedRoom);
+ $this->updateCache($this->roomDbTable, $resource);
+ }
+ }
+ }
+
+ /**
+ * get cached db rows for resources / rooms
+ * @param string $tableName
+ * @return array
+ */
+ private function getCached($tableName):array {
+ $query = $this->db->getQueryBuilder();
+ $query->select('*')->from($tableName);
+
+ $rows = [];
+ $stmt = $query->execute();
+ while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
+ $rows[] = $row;
+ }
+
+ return $rows;
+ }
+
+ /**
+ * @param array $cachedResources
+ * @return array
+ */
+ private function getCachedResourceIds(array $cachedResources):array {
+ $cachedResourceIds = [];
+ foreach ($cachedResources as $cachedResource) {
+ if (!isset($cachedResourceIds[$cachedResource['backend_id']])) {
+ $cachedResourceIds[$cachedResource['backend_id']] = [];
+ }
+
+ $cachedResourceIds[$cachedResource['backend_id']][] =
+ $cachedResource['resource_id'];
+ }
+
+ return $cachedResourceIds;
+ }
+
+ /**
+ * @param array $cachedRooms
+ * @return array
+ */
+ private function getCachedRoomIds(array $cachedRooms):array {
+ $cachedRoomIds = [];
+ foreach ($cachedRooms as $cachedRoom) {
+ if (!isset($cachedRoomIds[$cachedRoom['backend_id']])) {
+ $cachedRoomIds[$cachedRoom['backend_id']] = [];
+ }
+
+ $cachedRoomIds[$cachedRoom['backend_id']][] =
+ $cachedRoom['resource_id'];
+ }
+
+ return $cachedRoomIds;
+ }
+
+ /**
+ * sort list of ids by whether they appear only in the backend /
+ * only in the cache / in both
+ *
+ * @param array $cached
+ * @param array $remote
+ * @return array
+ */
+ private function sortByNewDeletedExisting(array $cached, array $remote):array {
+ $sorted = [
+ 'new' => [],
+ 'deleted' => [],
+ 'edited' => [],
+ ];
+
+ $backendIds = array_merge(array_keys($cached), array_keys($remote));
+ foreach($backendIds as $backendId) {
+ if (!isset($cached[$backendId])) {
+ $sorted['new'][$backendId] = $remote[$backendId];
+ } elseif (!isset($remote[$backendId])) {
+ $sorted['deleted'][$backendId] = $cached[$backendId];
+ } else {
+ $sorted['new'][$backendId] = array_diff($remote[$backendId], $cached[$backendId]);
+ $sorted['deleted'][$backendId] = array_diff($cached[$backendId], $remote[$backendId]);
+ $sorted['edited'][$backendId] = array_intersect($remote[$backendId], $cached[$backendId]);
+ }
+ }
+
+ return $sorted;
+ }
+
+ /**
+ * add entry to cache that exists remotely but not yet in cache
+ *
+ * @param string $table
+ * @param IResource|IRoom $remote
+ */
+ private function addToCache($table, $remote) {
+ $query = $this->db->getQueryBuilder();
+ $query->insert($table)
+ ->values([
+ 'backend_id' => $query->createNamedParameter($remote->getBackend()->getBackendIdentifier()),
+ 'resource_id' => $query->createNamedParameter($remote->getId()),
+ 'email' => $query->createNamedParameter($remote->getEMail()),
+ 'displayname' => $query->createNamedParameter($remote->getDisplayName()),
+ 'group_restrictions' => $query->createNamedParameter(
+ $this->serializeGroupRestrictions(
+ $remote->getGroupRestrictions()
+ ))
+ ])
+ ->execute();
+ }
+
+ /**
+ * delete entry from cache that does not exist anymore remotely
+ *
+ * @param string $table
+ * @param string $principalUri
+ * @param string $backendId
+ * @param string $resourceId
+ */
+ private function deleteFromCache($table, $principalUri, $backendId, $resourceId) {
+ $query = $this->db->getQueryBuilder();
+ $query->delete($table)
+ ->where($query->expr()->eq('backend_id', $query->createNamedParameter($backendId)))
+ ->andWhere($query->expr()->eq('resource_id', $query->createNamedParameter($resourceId)))
+ ->execute();
+
+ $calendar = $this->calDavBackend->getCalendarByUri($principalUri, implode('-', [$backendId, $resourceId]));
+ if ($calendar !== null) {
+ $this->calDavBackend->deleteCalendar($calendar['id']);
+ }
+ }
+
+ /**
+ * update an existing entry in cache
+ *
+ * @param string $table
+ * @param IResource|IRoom $remote
+ */
+ private function updateCache($table, $remote) {
+ $query = $this->db->getQueryBuilder();
+ $query->update($table)
+ ->set('email', $query->createNamedParameter($remote->getEMail()))
+ ->set('displayname', $query->createNamedParameter($remote->getDisplayName()))
+ ->set('group_restrictions', $query->createNamedParameter(
+ $this->serializeGroupRestrictions(
+ $remote->getGroupRestrictions()
+ )))
+ ->where($query->expr()->eq('backend_id', $query->createNamedParameter($remote->getBackend()->getBackendIdentifier())))
+ ->andWhere($query->expr()->eq('resource_id', $query->createNamedParameter($remote->getId())))
+ ->execute();
+ }
+
+ /**
+ * serialize array of group restrictions to store them in database
+ *
+ * @param array $groups
+ * @return string
+ */
+ private function serializeGroupRestrictions(array $groups):string {
+ return \json_encode($groups);
+ }
+}
diff --git a/apps/dav/lib/CalDAV/Activity/Filter/Calendar.php b/apps/dav/lib/CalDAV/Activity/Filter/Calendar.php
index 7456074915b..f85ca1a7769 100644
--- a/apps/dav/lib/CalDAV/Activity/Filter/Calendar.php
+++ b/apps/dav/lib/CalDAV/Activity/Filter/Calendar.php
@@ -72,7 +72,7 @@ class Calendar implements IFilter {
* @since 11.0.0
*/
public function getIcon() {
- return $this->url->getAbsoluteURL($this->url->imagePath('core', 'places/calendar-dark.svg'));
+ return $this->url->getAbsoluteURL($this->url->imagePath('core', 'places/calendar.svg'));
}
/**
diff --git a/apps/dav/lib/CalDAV/Activity/Provider/Calendar.php b/apps/dav/lib/CalDAV/Activity/Provider/Calendar.php
index db79b0f6656..45bc3d71c4a 100644
--- a/apps/dav/lib/CalDAV/Activity/Provider/Calendar.php
+++ b/apps/dav/lib/CalDAV/Activity/Provider/Calendar.php
@@ -176,6 +176,8 @@ class Calendar extends Base {
case self::SUBJECT_DELETE . '_self':
case self::SUBJECT_UPDATE:
case self::SUBJECT_UPDATE . '_self':
+ case self::SUBJECT_PUBLISH . '_self':
+ case self::SUBJECT_UNPUBLISH . '_self':
case self::SUBJECT_SHARE_USER:
case self::SUBJECT_UNSHARE_USER:
case self::SUBJECT_UNSHARE_USER . '_self':
diff --git a/apps/dav/lib/CalDAV/CalDavBackend.php b/apps/dav/lib/CalDAV/CalDavBackend.php
index b28c8534aaa..de46dfeb244 100644
--- a/apps/dav/lib/CalDAV/CalDavBackend.php
+++ b/apps/dav/lib/CalDAV/CalDavBackend.php
@@ -76,6 +76,9 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
const PERSONAL_CALENDAR_URI = 'personal';
const PERSONAL_CALENDAR_NAME = 'Personal';
+ const RESOURCE_BOOKING_CALENDAR_URI = 'calendar';
+ const RESOURCE_BOOKING_CALENDAR_NAME = 'Calendar';
+
/**
* We need to specify a max date, because we need to stop *somewhere*
*
diff --git a/apps/dav/lib/CalDAV/Calendar.php b/apps/dav/lib/CalDAV/Calendar.php
index 02808ab5662..a07bbe93218 100644
--- a/apps/dav/lib/CalDAV/Calendar.php
+++ b/apps/dav/lib/CalDAV/Calendar.php
@@ -203,7 +203,7 @@ class Calendar extends \Sabre\CalDAV\Calendar implements IShareable {
}
$this->caldavBackend->updateShares($this, [], [
- 'href' => $principal
+ $principal
]);
return;
}
diff --git a/apps/dav/lib/CalDAV/CalendarRoot.php b/apps/dav/lib/CalDAV/CalendarRoot.php
index 2c1c8bb4ef2..f84e8a96780 100644
--- a/apps/dav/lib/CalDAV/CalendarRoot.php
+++ b/apps/dav/lib/CalDAV/CalendarRoot.php
@@ -27,4 +27,15 @@ class CalendarRoot extends \Sabre\CalDAV\CalendarRoot {
function getChildForPrincipal(array $principal) {
return new CalendarHome($this->caldavBackend, $principal);
}
+
+ function getName() {
+ if ($this->principalPrefix === 'principals/calendar-resources' ||
+ $this->principalPrefix === 'principals/calendar-rooms') {
+ $parts = explode('/', $this->principalPrefix);
+
+ return $parts[1];
+ }
+
+ return parent::getName();
+ }
} \ No newline at end of file
diff --git a/apps/dav/lib/CalDAV/InvitationResponse/InvitationResponseServer.php b/apps/dav/lib/CalDAV/InvitationResponse/InvitationResponseServer.php
new file mode 100644
index 00000000000..61ead99ce12
--- /dev/null
+++ b/apps/dav/lib/CalDAV/InvitationResponse/InvitationResponseServer.php
@@ -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);
+ }
+} \ No newline at end of file
diff --git a/apps/dav/lib/CalDAV/Plugin.php b/apps/dav/lib/CalDAV/Plugin.php
index 4aa9762899f..f37d9c571a0 100644
--- a/apps/dav/lib/CalDAV/Plugin.php
+++ b/apps/dav/lib/CalDAV/Plugin.php
@@ -25,15 +25,27 @@ namespace OCA\DAV\CalDAV;
class Plugin extends \Sabre\CalDAV\Plugin {
+ const SYSTEM_CALENDAR_ROOT = 'system-calendars';
+
/**
* @inheritdoc
*/
- function getCalendarHomeForPrincipal($principalUrl) {
+ function getCalendarHomeForPrincipal($principalUrl):string {
if (strrpos($principalUrl, 'principals/users', -strlen($principalUrl)) !== false) {
list(, $principalId) = \Sabre\Uri\split($principalUrl);
- return self::CALENDAR_ROOT .'/' . $principalId;
+ return self::CALENDAR_ROOT . '/' . $principalId;
+ }
+ if (strrpos($principalUrl, 'principals/calendar-resources', -strlen($principalUrl)) !== false) {
+ list(, $principalId) = \Sabre\Uri\split($principalUrl);
+ return self::SYSTEM_CALENDAR_ROOT . '/calendar-resources/' . $principalId;
}
+ if (strrpos($principalUrl, 'principals/calendar-rooms', -strlen($principalUrl)) !== false) {
+ list(, $principalId) = \Sabre\Uri\split($principalUrl);
+ return self::SYSTEM_CALENDAR_ROOT . '/calendar-rooms/' . $principalId;
+ }
+
+ throw new \LogicException('This is not supposed to happen');
}
}
diff --git a/apps/dav/lib/CalDAV/ResourceBooking/AbstractPrincipalBackend.php b/apps/dav/lib/CalDAV/ResourceBooking/AbstractPrincipalBackend.php
new file mode 100644
index 00000000000..dcd393b512e
--- /dev/null
+++ b/apps/dav/lib/CalDAV/ResourceBooking/AbstractPrincipalBackend.php
@@ -0,0 +1,361 @@
+<?php
+/**
+ * @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\CalDAV\ResourceBooking;
+
+use OCP\IDBConnection;
+use OCP\IGroupManager;
+use OCP\ILogger;
+use OCP\IUserSession;
+use Sabre\DAVACL\PrincipalBackend\BackendInterface;
+use Sabre\DAV\Exception;
+use \Sabre\DAV\PropPatch;
+
+abstract class AbstractPrincipalBackend implements BackendInterface {
+
+ /** @var IDBConnection */
+ private $db;
+
+ /** @var IUserSession */
+ private $userSession;
+
+ /** @var IGroupManager */
+ private $groupManager;
+
+ /** @var ILogger */
+ private $logger;
+
+ /** @var string */
+ private $principalPrefix;
+
+ /** @var string */
+ private $dbTableName;
+
+ /**
+ * @param IDBConnection $dbConnection
+ * @param IUserSession $userSession
+ * @param IGroupManager $groupManager
+ * @param ILogger $logger
+ * @param string $principalPrefix
+ * @param string $dbPrefix
+ */
+ public function __construct(IDBConnection $dbConnection,
+ IUserSession $userSession,
+ IGroupManager $groupManager,
+ ILogger $logger,
+ $principalPrefix, $dbPrefix) {
+ $this->db = $dbConnection;
+ $this->userSession = $userSession;
+ $this->groupManager = $groupManager;
+ $this->logger = $logger;
+ $this->principalPrefix = $principalPrefix;
+ $this->dbTableName = 'calendar_' . $dbPrefix;
+ }
+
+ /**
+ * Returns a list of principals based on a prefix.
+ *
+ * This prefix will often contain something like 'principals'. You are only
+ * expected to return principals that are in this base path.
+ *
+ * You are expected to return at least a 'uri' for every user, you can
+ * return any additional properties if you wish so. Common properties are:
+ * {DAV:}displayname
+ *
+ * @param string $prefixPath
+ * @return string[]
+ */
+ public function getPrincipalsByPrefix($prefixPath) {
+ $principals = [];
+
+ if ($prefixPath === $this->principalPrefix) {
+ $query = $this->db->getQueryBuilder();
+ $query->select(['id', 'backend_id', 'resource_id', 'email', 'displayname'])
+ ->from($this->dbTableName);
+ $stmt = $query->execute();
+
+ while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
+ $principals[] = $this->rowToPrincipal($row);
+ }
+
+ $stmt->closeCursor();
+ }
+
+ return $principals;
+ }
+
+ /**
+ * Returns a specific principal, specified by it's path.
+ * The returned structure should be the exact same as from
+ * getPrincipalsByPrefix.
+ *
+ * @param string $path
+ * @return array
+ */
+ public function getPrincipalByPath($path) {
+ if (strpos($path, $this->principalPrefix) !== 0) {
+ return null;
+ }
+ list(, $name) = \Sabre\Uri\split($path);
+
+ list($backendId, $resourceId) = explode('-', $name, 2);
+
+ $query = $this->db->getQueryBuilder();
+ $query->select(['id', 'backend_id', 'resource_id', 'email', 'displayname'])
+ ->from($this->dbTableName)
+ ->where($query->expr()->eq('backend_id', $query->createNamedParameter($backendId)))
+ ->andWhere($query->expr()->eq('resource_id', $query->createNamedParameter($resourceId)));
+ $stmt = $query->execute();
+ $row = $stmt->fetch(\PDO::FETCH_ASSOC);
+
+ if(!$row) {
+ return null;
+ }
+
+ return $this->rowToPrincipal($row);
+ }
+
+ /**
+ * Returns the list of members for a group-principal
+ *
+ * @param string $principal
+ * @return string[]
+ */
+ public function getGroupMemberSet($principal) {
+ return [];
+ }
+
+ /**
+ * Returns the list of groups a principal is a member of
+ *
+ * @param string $principal
+ * @return array
+ */
+ public function getGroupMembership($principal) {
+ return [];
+ }
+
+ /**
+ * Updates the list of group members for a group principal.
+ *
+ * The principals should be passed as a list of uri's.
+ *
+ * @param string $principal
+ * @param string[] $members
+ * @throws Exception
+ */
+ public function setGroupMemberSet($principal, array $members) {
+ throw new Exception('Setting members of the group is not supported yet');
+ }
+
+ /**
+ * @param string $path
+ * @param PropPatch $propPatch
+ * @return int
+ */
+ function updatePrincipal($path, PropPatch $propPatch) {
+ return 0;
+ }
+
+ /**
+ * @param string $prefixPath
+ * @param array $searchProperties
+ * @param string $test
+ * @return array
+ */
+ function searchPrincipals($prefixPath, array $searchProperties, $test = 'allof') {
+ $results = [];
+ if (\count($searchProperties) === 0) {
+ return [];
+ }
+ if ($prefixPath !== $this->principalPrefix) {
+ return [];
+ }
+
+ $user = $this->userSession->getUser();
+ if (!$user) {
+ return [];
+ }
+ $usersGroups = $this->groupManager->getUserGroupIds($user);
+
+ foreach ($searchProperties as $prop => $value) {
+ switch ($prop) {
+ case '{http://sabredav.org/ns}email-address':
+ $query = $this->db->getQueryBuilder();
+ $query->select(['id', 'backend_id', 'resource_id', 'email', 'displayname', 'group_restrictions'])
+ ->from($this->dbTableName)
+ ->where($query->expr()->iLike('email', $query->createNamedParameter('%' . $this->db->escapeLikeParameter($value) . '%')));
+
+ $stmt = $query->execute();
+ $principals = [];
+ while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
+ if (!$this->isAllowedToAccessResource($row, $usersGroups)) {
+ continue;
+ }
+ $principals[] = $this->rowToPrincipal($row)['uri'];
+ }
+ $results[] = $principals;
+
+ $stmt->closeCursor();
+ break;
+
+ case '{DAV:}displayname':
+ $query = $this->db->getQueryBuilder();
+ $query->select(['id', 'backend_id', 'resource_id', 'email', 'displayname', 'group_restrictions'])
+ ->from($this->dbTableName)
+ ->where($query->expr()->iLike('displayname', $query->createNamedParameter('%' . $this->db->escapeLikeParameter($value) . '%')));
+
+ $stmt = $query->execute();
+ $principals = [];
+ while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
+ if (!$this->isAllowedToAccessResource($row, $usersGroups)) {
+ continue;
+ }
+ $principals[] = $this->rowToPrincipal($row)['uri'];
+ }
+ $results[] = $principals;
+
+ $stmt->closeCursor();
+ break;
+
+ default:
+ $results[] = [];
+ break;
+ }
+ }
+
+ // results is an array of arrays, so this is not the first search result
+ // but the results of the first searchProperty
+ if (count($results) === 1) {
+ return $results[0];
+ }
+
+ switch ($test) {
+ case 'anyof':
+ return array_values(array_unique(array_merge(...$results)));
+
+ case 'allof':
+ default:
+ return array_values(array_intersect(...$results));
+ }
+ }
+
+ /**
+ * @param string $uri
+ * @param string $principalPrefix
+ * @return null|string
+ */
+ function findByUri($uri, $principalPrefix) {
+ $user = $this->userSession->getUser();
+ if (!$user) {
+ return null;
+ }
+ $usersGroups = $this->groupManager->getUserGroupIds($user);
+
+ if (strpos($uri, 'mailto:') === 0) {
+ $email = substr($uri, 7);
+ $query = $this->db->getQueryBuilder();
+ $query->select(['id', 'backend_id', 'resource_id', 'email', 'displayname', 'group_restrictions'])
+ ->from($this->dbTableName)
+ ->where($query->expr()->eq('email', $query->createNamedParameter($email)));
+
+ $stmt = $query->execute();
+ $row = $stmt->fetch(\PDO::FETCH_ASSOC);
+
+ if(!$row) {
+ return null;
+ }
+ if (!$this->isAllowedToAccessResource($row, $usersGroups)) {
+ return null;
+ }
+
+ return $this->rowToPrincipal($row)['uri'];
+ }
+
+ if (strpos($uri, 'principal:') === 0) {
+ $path = substr($uri, 10);
+ if (strpos($path, $this->principalPrefix) !== 0) {
+ return null;
+ }
+
+ list(, $name) = \Sabre\Uri\split($path);
+ list($backendId, $resourceId) = explode('-', $name, 2);
+
+ $query = $this->db->getQueryBuilder();
+ $query->select(['id', 'backend_id', 'resource_id', 'email', 'displayname', 'group_restrictions'])
+ ->from($this->dbTableName)
+ ->where($query->expr()->eq('backend_id', $query->createNamedParameter($backendId)))
+ ->andWhere($query->expr()->eq('resource_id', $query->createNamedParameter($resourceId)));
+ $stmt = $query->execute();
+ $row = $stmt->fetch(\PDO::FETCH_ASSOC);
+
+ if(!$row) {
+ return null;
+ }
+ if (!$this->isAllowedToAccessResource($row, $usersGroups)) {
+ return null;
+ }
+
+ return $this->rowToPrincipal($row)['uri'];
+ }
+
+ return null;
+ }
+
+ /**
+ * convert database row to principal
+ */
+ private function rowToPrincipal($row) {
+ return [
+ 'uri' => $this->principalPrefix . '/' . $row['backend_id'] . '-' . $row['resource_id'],
+ '{DAV:}displayname' => $row['displayname'],
+ '{http://sabredav.org/ns}email-address' => $row['email']
+ ];
+ }
+
+ /**
+ * @param $row
+ * @param $userGroups
+ * @return bool
+ */
+ private function isAllowedToAccessResource($row, $userGroups) {
+ if (!isset($row['group_restrictions']) ||
+ $row['group_restrictions'] === null ||
+ $row['group_restrictions'] === '') {
+ return true;
+ }
+
+ // group restrictions contains something, but not parsable, deny access and log warning
+ $json = json_decode($row['group_restrictions']);
+ if (!\is_array($json)) {
+ $this->logger->info('group_restrictions field could not be parsed for ' . $this->dbTableName . '::' . $row['id'] . ', denying access to resource');
+ return false;
+ }
+
+ // empty array => no group restrictions
+ if (empty($json)) {
+ return true;
+ }
+
+ return !empty(array_intersect($json, $userGroups));
+ }
+}
diff --git a/apps/dav/lib/CalDAV/ResourceBooking/ResourcePrincipalBackend.php b/apps/dav/lib/CalDAV/ResourceBooking/ResourcePrincipalBackend.php
new file mode 100644
index 00000000000..a1030376c11
--- /dev/null
+++ b/apps/dav/lib/CalDAV/ResourceBooking/ResourcePrincipalBackend.php
@@ -0,0 +1,45 @@
+<?php
+/**
+ * @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\CalDAV\ResourceBooking;
+
+use OCP\IDBConnection;
+use OCP\IGroupManager;
+use OCP\ILogger;
+use OCP\IUserSession;
+
+class ResourcePrincipalBackend extends AbstractPrincipalBackend {
+
+ /**
+ * @param IDBConnection $dbConnection
+ * @param IUserSession $userSession
+ * @param IGroupManager $groupManager
+ * @param ILogger $logger
+ */
+ public function __construct(IDBConnection $dbConnection,
+ IUserSession $userSession,
+ IGroupManager $groupManager,
+ ILogger $logger) {
+ parent::__construct($dbConnection, $userSession, $groupManager, $logger,
+ 'principals/calendar-resources', 'resources');
+ }
+}
diff --git a/apps/dav/lib/CalDAV/ResourceBooking/RoomPrincipalBackend.php b/apps/dav/lib/CalDAV/ResourceBooking/RoomPrincipalBackend.php
new file mode 100644
index 00000000000..1d22299515f
--- /dev/null
+++ b/apps/dav/lib/CalDAV/ResourceBooking/RoomPrincipalBackend.php
@@ -0,0 +1,45 @@
+<?php
+/**
+ * @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\CalDAV\ResourceBooking;
+
+use OCP\IDBConnection;
+use OCP\IGroupManager;
+use OCP\ILogger;
+use OCP\IUserSession;
+
+class RoomPrincipalBackend extends AbstractPrincipalBackend {
+
+ /**
+ * @param IDBConnection $dbConnection
+ * @param IUserSession $userSession
+ * @param IGroupManager $groupManager
+ * @param ILogger $logger
+ */
+ public function __construct(IDBConnection $dbConnection,
+ IUserSession $userSession,
+ IGroupManager $groupManager,
+ ILogger $logger) {
+ parent::__construct($dbConnection, $userSession, $groupManager, $logger,
+ 'principals/calendar-rooms', 'rooms');
+ }
+}
diff --git a/apps/dav/lib/CalDAV/Schedule/IMipPlugin.php b/apps/dav/lib/CalDAV/Schedule/IMipPlugin.php
index 85973a8be12..4065c21b8a1 100644
--- a/apps/dav/lib/CalDAV/Schedule/IMipPlugin.php
+++ b/apps/dav/lib/CalDAV/Schedule/IMipPlugin.php
@@ -28,12 +28,14 @@ namespace OCA\DAV\CalDAV\Schedule;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\Defaults;
use OCP\IConfig;
+use OCP\IDBConnection;
use OCP\IL10N;
use OCP\ILogger;
use OCP\IURLGenerator;
use OCP\L10N\IFactory as L10NFactory;
use OCP\Mail\IEMailTemplate;
use OCP\Mail\IMailer;
+use OCP\Security\ISecureRandom;
use Sabre\CalDAV\Schedule\IMipPlugin as SabreIMipPlugin;
use Sabre\VObject\Component\VCalendar;
use Sabre\VObject\Component\VEvent;
@@ -79,6 +81,12 @@ class IMipPlugin extends SabreIMipPlugin {
/** @var IURLGenerator */
private $urlGenerator;
+ /** @var ISecureRandom */
+ private $random;
+
+ /** @var IDBConnection */
+ private $db;
+
/** @var Defaults */
private $defaults;
@@ -96,9 +104,14 @@ class IMipPlugin extends SabreIMipPlugin {
* @param L10NFactory $l10nFactory
* @param IUrlGenerator $urlGenerator
* @param Defaults $defaults
+ * @param ISecureRandom $random
+ * @param IDBConnection $db
* @param string $userId
*/
- public function __construct(IConfig $config, IMailer $mailer, ILogger $logger, ITimeFactory $timeFactory, L10NFactory $l10nFactory, IURLGenerator $urlGenerator, Defaults $defaults, $userId) {
+ public function __construct(IConfig $config, IMailer $mailer, ILogger $logger,
+ ITimeFactory $timeFactory, L10NFactory $l10nFactory,
+ IURLGenerator $urlGenerator, Defaults $defaults,
+ ISecureRandom $random, IDBConnection $db, $userId) {
parent::__construct('');
$this->userId = $userId;
$this->config = $config;
@@ -107,6 +120,8 @@ class IMipPlugin extends SabreIMipPlugin {
$this->timeFactory = $timeFactory;
$this->l10nFactory = $l10nFactory;
$this->urlGenerator = $urlGenerator;
+ $this->random = $random;
+ $this->db = $db;
$this->defaults = $defaults;
}
@@ -138,7 +153,9 @@ class IMipPlugin extends SabreIMipPlugin {
}
// don't send out mails for events that already took place
- if ($this->isEventInThePast($iTipMessage->message)) {
+ $lastOccurrence = $this->getLastOccurrence($iTipMessage->message);
+ $currentTime = $this->timeFactory->getTime();
+ if ($lastOccurrence < $currentTime) {
return;
}
@@ -222,6 +239,7 @@ class IMipPlugin extends SabreIMipPlugin {
$meetingAttendeeName, $meetingInviteeName);
$this->addBulletList($template, $l10n, $meetingWhen, $meetingLocation,
$meetingDescription, $meetingUrl);
+ $this->addResponseButtons($template, $l10n, $iTipMessage, $lastOccurrence);
$template->addFooter();
$message->useTemplate($template);
@@ -249,9 +267,9 @@ class IMipPlugin extends SabreIMipPlugin {
/**
* check if event took place in the past already
* @param VCalendar $vObject
- * @return bool
+ * @return int
*/
- private function isEventInThePast(VCalendar $vObject) {
+ private function getLastOccurrence(VCalendar $vObject) {
/** @var VEvent $component */
$component = $vObject->VEVENT;
@@ -291,8 +309,7 @@ class IMipPlugin extends SabreIMipPlugin {
}
}
- $currentTime = $this->timeFactory->getTime();
- return $lastOccurrence < $currentTime;
+ return $lastOccurrence;
}
@@ -460,6 +477,38 @@ class IMipPlugin extends SabreIMipPlugin {
}
/**
+ * @param IEMailTemplate $template
+ * @param IL10N $l10n
+ * @param Message $iTipMessage
+ * @param int $lastOccurrence
+ */
+ private function addResponseButtons(IEMailTemplate $template, IL10N $l10n,
+ Message $iTipMessage, $lastOccurrence) {
+ $token = $this->createInvitationToken($iTipMessage, $lastOccurrence);
+
+ $template->addBodyButtonGroup(
+ $l10n->t('Accept'),
+ $this->urlGenerator->linkToRouteAbsolute('dav.invitation_response.accept', [
+ 'token' => $token,
+ ]),
+ $l10n->t('Decline'),
+ $this->urlGenerator->linkToRouteAbsolute('dav.invitation_response.decline', [
+ 'token' => $token,
+ ])
+ );
+
+ $moreOptionsURL = $this->urlGenerator->linkToRouteAbsolute('dav.invitation_response.options', [
+ 'token' => $token,
+ ]);
+ $html = vsprintf('<small><a href="%s">%s</a></small>', [
+ $moreOptionsURL, $l10n->t('More options ...')
+ ]);
+ $text = $l10n->t('More options at %s', [$moreOptionsURL]);
+
+ $template->addBodyText($html, $text);
+ }
+
+ /**
* @param string $path
* @return string
*/
@@ -468,4 +517,37 @@ class IMipPlugin extends SabreIMipPlugin {
$this->urlGenerator->imagePath('core', $path)
);
}
+
+ /**
+ * @param Message $iTipMessage
+ * @param int $lastOccurrence
+ * @return string
+ */
+ private function createInvitationToken(Message $iTipMessage, $lastOccurrence):string {
+ $token = $this->random->generate(60, ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS);
+
+ /** @var VEvent $vevent */
+ $vevent = $iTipMessage->message->VEVENT;
+ $attendee = $iTipMessage->recipient;
+ $organizer = $iTipMessage->sender;
+ $sequence = $iTipMessage->sequence;
+ $recurrenceId = isset($vevent->{'RECURRENCE-ID'}) ?
+ $vevent->{'RECURRENCE-ID'}->serialize() : null;
+ $uid = $vevent->{'UID'};
+
+ $query = $this->db->getQueryBuilder();
+ $query->insert('calendar_invitations')
+ ->values([
+ 'token' => $query->createNamedParameter($token),
+ 'attendee' => $query->createNamedParameter($attendee),
+ 'organizer' => $query->createNamedParameter($organizer),
+ 'sequence' => $query->createNamedParameter($sequence),
+ 'recurrenceid' => $query->createNamedParameter($recurrenceId),
+ 'expiration' => $query->createNamedParameter($lastOccurrence),
+ 'uid' => $query->createNamedParameter($uid)
+ ])
+ ->execute();
+
+ return $token;
+ }
}
diff --git a/apps/dav/lib/CalDAV/Schedule/Plugin.php b/apps/dav/lib/CalDAV/Schedule/Plugin.php
index 34df666637c..b3f7232c2fe 100644
--- a/apps/dav/lib/CalDAV/Schedule/Plugin.php
+++ b/apps/dav/lib/CalDAV/Schedule/Plugin.php
@@ -31,6 +31,10 @@ use Sabre\DAV\PropFind;
use Sabre\DAV\Server;
use Sabre\DAV\Xml\Property\LocalHref;
use Sabre\DAVACL\IPrincipal;
+use Sabre\HTTP\RequestInterface;
+use Sabre\HTTP\ResponseInterface;
+use Sabre\VObject\Component\VCalendar;
+use Sabre\VObject\Reader;
class Plugin extends \Sabre\CalDAV\Schedule\Plugin {
@@ -76,20 +80,32 @@ class Plugin extends \Sabre\CalDAV\Schedule\Plugin {
$principalUrl = $node->getPrincipalUrl();
$calendarHomePath = $caldavPlugin->getCalendarHomeForPrincipal($principalUrl);
-
if (!$calendarHomePath) {
return null;
}
+ if (strpos($principalUrl, 'principals/users') === 0) {
+ $uri = CalDavBackend::PERSONAL_CALENDAR_URI;
+ $displayname = CalDavBackend::PERSONAL_CALENDAR_NAME;
+ } elseif (strpos($principalUrl, 'principals/calendar-resources') === 0 ||
+ strpos($principalUrl, 'principals/calendar-rooms') === 0) {
+ $uri = CalDavBackend::RESOURCE_BOOKING_CALENDAR_URI;
+ $displayname = CalDavBackend::RESOURCE_BOOKING_CALENDAR_NAME;
+ } else {
+ // How did we end up here?
+ // TODO - throw exception or just ignore?
+ return null;
+ }
+
/** @var CalendarHome $calendarHome */
$calendarHome = $this->server->tree->getNodeForPath($calendarHomePath);
- if (!$calendarHome->childExists(CalDavBackend::PERSONAL_CALENDAR_URI)) {
- $calendarHome->getCalDAVBackend()->createCalendar($principalUrl, CalDavBackend::PERSONAL_CALENDAR_URI, [
- '{DAV:}displayname' => CalDavBackend::PERSONAL_CALENDAR_NAME,
+ if (!$calendarHome->childExists($uri)) {
+ $calendarHome->getCalDAVBackend()->createCalendar($principalUrl, $uri, [
+ '{DAV:}displayname' => $displayname,
]);
}
- $result = $this->server->getPropertiesForPath($calendarHomePath . '/' . CalDavBackend::PERSONAL_CALENDAR_URI, [], 1);
+ $result = $this->server->getPropertiesForPath($calendarHomePath . '/' . $uri, [], 1);
if (empty($result)) {
return null;
}
@@ -98,4 +114,67 @@ class Plugin extends \Sabre\CalDAV\Schedule\Plugin {
});
}
}
+
+ /**
+ * This method is triggered whenever there was a calendar object gets
+ * created or updated.
+ *
+ * Basically just a copy of parent::calendarObjectChange, with the change
+ * from:
+ * $addresses = $this->getAddressesForPrincipal($calendarNode->getOwner());
+ * to:
+ * $addresses = $this->getAddressesForPrincipal($calendarNode->getPrincipalURI());
+ *
+ * @param RequestInterface $request HTTP request
+ * @param ResponseInterface $response HTTP Response
+ * @param VCalendar $vCal Parsed iCalendar object
+ * @param mixed $calendarPath Path to calendar collection
+ * @param mixed $modified The iCalendar object has been touched.
+ * @param mixed $isNew Whether this was a new item or we're updating one
+ * @return void
+ */
+ function calendarObjectChange(RequestInterface $request, ResponseInterface $response, VCalendar $vCal, $calendarPath, &$modified, $isNew) {
+
+ if (!$this->scheduleReply($this->server->httpRequest)) {
+ return;
+ }
+
+ $calendarNode = $this->server->tree->getNodeForPath($calendarPath);
+
+ $addresses = $this->getAddressesForPrincipal(
+ $calendarNode->getPrincipalURI()
+ );
+
+ if (!$isNew) {
+ $node = $this->server->tree->getNodeForPath($request->getPath());
+ $oldObj = Reader::read($node->get());
+ } else {
+ $oldObj = null;
+ }
+
+ $this->processICalendarChange($oldObj, $vCal, $addresses, [], $modified);
+
+ if ($oldObj) {
+ // Destroy circular references so PHP will GC the object.
+ $oldObj->destroy();
+ }
+
+ }
+
+ /**
+ * This method checks the 'Schedule-Reply' header
+ * and returns false if it's 'F', otherwise true.
+ *
+ * Copied from Sabre/DAV's Schedule plugin, because it's
+ * private for whatever reason
+ *
+ * @param RequestInterface $request
+ * @return bool
+ */
+ private function scheduleReply(RequestInterface $request) {
+
+ $scheduleReply = $request->getHeader('Schedule-Reply');
+ return $scheduleReply !== 'F';
+
+ }
}
diff --git a/apps/dav/lib/CardDAV/AddressBook.php b/apps/dav/lib/CardDAV/AddressBook.php
index a034f8b9426..71202319874 100644
--- a/apps/dav/lib/CardDAV/AddressBook.php
+++ b/apps/dav/lib/CardDAV/AddressBook.php
@@ -181,7 +181,7 @@ class AddressBook extends \Sabre\CardDAV\AddressBook implements IShareable {
}
$this->carddavBackend->updateShares($this, [], [
- 'href' => $principal
+ $principal
]);
return;
}
diff --git a/apps/dav/lib/Command/CreateCalendar.php b/apps/dav/lib/Command/CreateCalendar.php
index 1cbd7b60944..45dd9ba941a 100644
--- a/apps/dav/lib/Command/CreateCalendar.php
+++ b/apps/dav/lib/Command/CreateCalendar.php
@@ -77,7 +77,8 @@ class CreateCalendar extends Command {
$this->userManager,
$this->groupManager,
\OC::$server->getShareManager(),
- \OC::$server->getUserSession()
+ \OC::$server->getUserSession(),
+ \OC::$server->getConfig()
);
$random = \OC::$server->getSecureRandom();
$logger = \OC::$server->getLogger();
diff --git a/apps/dav/lib/Command/RemoveInvalidShares.php b/apps/dav/lib/Command/RemoveInvalidShares.php
new file mode 100644
index 00000000000..12a5ee43d47
--- /dev/null
+++ b/apps/dav/lib/Command/RemoveInvalidShares.php
@@ -0,0 +1,82 @@
+<?php
+declare(strict_types=1);
+/**
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2018, ownCloud GmbH
+ * @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\Command;
+
+use OCA\DAV\Connector\Sabre\Principal;
+use OCP\IDBConnection;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+/**
+ * Class RemoveInvalidShares - removes shared calendars and addressbook which
+ * have no matching principal. Happened because of a bug in the calendar app.
+ */
+class RemoveInvalidShares extends Command {
+
+ /** @var IDBConnection */
+ private $connection;
+ /** @var Principal */
+ private $principalBackend;
+
+ public function __construct(IDBConnection $connection,
+ Principal $principalBackend) {
+ parent::__construct();
+
+ $this->connection = $connection;
+ $this->principalBackend = $principalBackend;
+ }
+
+ protected function configure() {
+ $this
+ ->setName('dav:remove-invalid-shares')
+ ->setDescription('Remove invalid dav shares');
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $query = $this->connection->getQueryBuilder();
+ $result = $query->selectDistinct('principaluri')
+ ->from('dav_shares')
+ ->execute();
+
+ while($row = $result->fetch()) {
+ $principaluri = $row['principaluri'];
+ $p = $this->principalBackend->getPrincipalByPath($principaluri);
+ if ($p === null) {
+ $this->deleteSharesForPrincipal($principaluri);
+ }
+ }
+
+ $result->closeCursor();
+ }
+
+ /**
+ * @param string $principaluri
+ */
+ private function deleteSharesForPrincipal($principaluri) {
+ $delete = $this->connection->getQueryBuilder();
+ $delete->delete('dav_shares')
+ ->where($delete->expr()->eq('principaluri', $delete->createNamedParameter($principaluri)));
+ $delete->execute();
+ }
+}
diff --git a/apps/dav/lib/Connector/Sabre/AnonymousOptionsPlugin.php b/apps/dav/lib/Connector/Sabre/AnonymousOptionsPlugin.php
new file mode 100644
index 00000000000..7a62f706378
--- /dev/null
+++ b/apps/dav/lib/Connector/Sabre/AnonymousOptionsPlugin.php
@@ -0,0 +1,66 @@
+<?php
+/**
+ * @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.nl>
+ *
+ * @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\Connector\Sabre;
+
+use Sabre\DAV\CorePlugin;
+use Sabre\DAV\FS\Directory;
+use Sabre\DAV\ServerPlugin;
+use Sabre\DAV\Tree;
+use Sabre\HTTP\RequestInterface;
+use Sabre\HTTP\ResponseInterface;
+
+class AnonymousOptionsPlugin extends ServerPlugin {
+
+ /**
+ * @var \Sabre\DAV\Server
+ */
+ private $server;
+
+ /**
+ * @param \Sabre\DAV\Server $server
+ * @return void
+ */
+ public function initialize(\Sabre\DAV\Server $server) {
+ $this->server = $server;
+ // before auth
+ $this->server->on('beforeMethod', [$this, 'handleAnonymousOptions'], 9);
+ }
+
+ /**
+ * @throws \Sabre\DAV\Exception\Forbidden
+ * @return bool
+ */
+ public function handleAnonymousOptions(RequestInterface $request, ResponseInterface $response) {
+ if ($request->getMethod() === 'OPTIONS' && $request->getPath() === '') {
+ /** @var CorePlugin $corePlugin */
+ $corePlugin = $this->server->getPlugin('core');
+ // setup a fake tree for anonymous access
+ $this->server->tree = new Tree(new Directory(''));
+ $corePlugin->httpOptions($request, $response);
+ $this->server->emit('afterMethod', [$request, $response]);
+ $this->server->emit('afterMethod:OPTIONS', [$request, $response]);
+
+ $this->server->sapi->sendResponse($response);
+ return false;
+ }
+ }
+}
diff --git a/apps/dav/lib/Connector/Sabre/File.php b/apps/dav/lib/Connector/Sabre/File.php
index bbd6717d94f..e46bdcb2984 100644
--- a/apps/dav/lib/Connector/Sabre/File.php
+++ b/apps/dav/lib/Connector/Sabre/File.php
@@ -150,12 +150,21 @@ class File extends Node implements IFile {
$this->emitPreHooks($exists);
}
+ $view = \OC\Files\Filesystem::getView();
+
// the part file and target file might be on a different storage in case of a single file storage (e.g. single file share)
/** @var \OC\Files\Storage\Storage $partStorage */
list($partStorage, $internalPartPath) = $this->fileView->resolvePath($partFilePath);
/** @var \OC\Files\Storage\Storage $storage */
list($storage, $internalPath) = $this->fileView->resolvePath($this->path);
try {
+ if (!$needsPartFile) {
+ if ($view && !$this->emitPreHooks($exists)) {
+ throw new Exception('Could not write to final file, canceled by hook');
+ }
+ $this->changeLock(ILockingProvider::LOCK_EXCLUSIVE);
+ }
+
$target = $partStorage->fopen($internalPartPath, 'wb');
if ($target === false) {
\OC::$server->getLogger()->error('\OC\Files\Filesystem::fopen() failed', ['app' => 'webdav']);
@@ -184,6 +193,7 @@ class File extends Node implements IFile {
}
} catch (\Exception $e) {
+ \OC::$server->getLogger()->logException($e);
if ($needsPartFile) {
$partStorage->unlink($internalPartPath);
}
@@ -191,26 +201,25 @@ class File extends Node implements IFile {
}
try {
- $view = \OC\Files\Filesystem::getView();
- $run = ($view && $needsPartFile) ? $this->emitPreHooks($exists) : true;
-
- try {
- $this->changeLock(ILockingProvider::LOCK_EXCLUSIVE);
- } catch (LockedException $e) {
- if ($needsPartFile) {
+ if ($needsPartFile) {
+ if ($view && !$this->emitPreHooks($exists)) {
$partStorage->unlink($internalPartPath);
+ throw new Exception('Could not rename part file to final file, canceled by hook');
+ }
+ try {
+ $this->changeLock(ILockingProvider::LOCK_EXCLUSIVE);
+ } catch (LockedException $e) {
+ if ($needsPartFile) {
+ $partStorage->unlink($internalPartPath);
+ }
+ throw new FileLocked($e->getMessage(), $e->getCode(), $e);
}
- throw new FileLocked($e->getMessage(), $e->getCode(), $e);
- }
- if ($needsPartFile) {
// rename to correct path
try {
- if ($run) {
- $renameOkay = $storage->moveFromStorage($partStorage, $internalPartPath, $internalPath);
- $fileExists = $storage->file_exists($internalPath);
- }
- if (!$run || $renameOkay === false || $fileExists === false) {
+ $renameOkay = $storage->moveFromStorage($partStorage, $internalPartPath, $internalPath);
+ $fileExists = $storage->file_exists($internalPath);
+ if ($renameOkay === false || $fileExists === false) {
\OC::$server->getLogger()->error('renaming part file to final file failed ($run: ' . ( $run ? 'true' : 'false' ) . ', $renameOkay: ' . ( $renameOkay ? 'true' : 'false' ) . ', $fileExists: ' . ( $fileExists ? 'true' : 'false' ) . ')', ['app' => 'webdav']);
throw new Exception('Could not rename part file to final file');
}
diff --git a/apps/dav/lib/Connector/Sabre/FilesPlugin.php b/apps/dav/lib/Connector/Sabre/FilesPlugin.php
index f36ebe5636c..f53f13c5687 100644
--- a/apps/dav/lib/Connector/Sabre/FilesPlugin.php
+++ b/apps/dav/lib/Connector/Sabre/FilesPlugin.php
@@ -33,6 +33,7 @@
namespace OCA\DAV\Connector\Sabre;
use OC\AppFramework\Http\Request;
+use OCP\Constants;
use OCP\Files\ForbiddenException;
use OCP\IPreview;
use Sabre\DAV\Exception\Forbidden;
@@ -57,6 +58,7 @@ class FilesPlugin extends ServerPlugin {
const INTERNAL_FILEID_PROPERTYNAME = '{http://owncloud.org/ns}fileid';
const PERMISSIONS_PROPERTYNAME = '{http://owncloud.org/ns}permissions';
const SHARE_PERMISSIONS_PROPERTYNAME = '{http://open-collaboration-services.org/ns}share-permissions';
+ const OCM_SHARE_PERMISSIONS_PROPERTYNAME = '{http://open-cloud-mesh.org/ns}share-permissions';
const DOWNLOADURL_PROPERTYNAME = '{http://owncloud.org/ns}downloadURL';
const SIZE_PROPERTYNAME = '{http://owncloud.org/ns}size';
const GETETAG_PROPERTYNAME = '{DAV:}getetag';
@@ -149,6 +151,7 @@ class FilesPlugin extends ServerPlugin {
$server->protectedProperties[] = self::INTERNAL_FILEID_PROPERTYNAME;
$server->protectedProperties[] = self::PERMISSIONS_PROPERTYNAME;
$server->protectedProperties[] = self::SHARE_PERMISSIONS_PROPERTYNAME;
+ $server->protectedProperties[] = self::OCM_SHARE_PERMISSIONS_PROPERTYNAME;
$server->protectedProperties[] = self::SIZE_PROPERTYNAME;
$server->protectedProperties[] = self::DOWNLOADURL_PROPERTYNAME;
$server->protectedProperties[] = self::OWNER_ID_PROPERTYNAME;
@@ -318,6 +321,14 @@ class FilesPlugin extends ServerPlugin {
);
});
+ $propFind->handle(self::OCM_SHARE_PERMISSIONS_PROPERTYNAME, function() use ($node, $httpRequest) {
+ $ncPermissions = $node->getSharePermissions(
+ $httpRequest->getRawServerValue('PHP_AUTH_USER')
+ );
+ $ocmPermissions = $this->ncPermissions2ocmPermissions($ncPermissions);
+ return json_encode($ocmPermissions);
+ });
+
$propFind->handle(self::GETETAG_PROPERTYNAME, function() use ($node) {
return $node->getETag();
});
@@ -395,6 +406,33 @@ class FilesPlugin extends ServerPlugin {
}
/**
+ * translate Nextcloud permissions to OCM Permissions
+ *
+ * @param $ncPermissions
+ * @return array
+ */
+ protected function ncPermissions2ocmPermissions($ncPermissions) {
+
+ $ocmPermissions = [];
+
+ if ($ncPermissions & Constants::PERMISSION_SHARE) {
+ $ocmPermissions[] = 'share';
+ }
+
+ if ($ncPermissions & Constants::PERMISSION_READ) {
+ $ocmPermissions[] = 'read';
+ }
+
+ if (($ncPermissions & Constants::PERMISSION_CREATE) ||
+ ($ncPermissions & Constants::PERMISSION_UPDATE)) {
+ $ocmPermissions[] = 'write';
+ }
+
+ return $ocmPermissions;
+
+ }
+
+ /**
* Update ownCloud-specific properties
*
* @param string $path
diff --git a/apps/dav/lib/Connector/Sabre/Node.php b/apps/dav/lib/Connector/Sabre/Node.php
index 9e78d21a39d..38d0ff57fb2 100644
--- a/apps/dav/lib/Connector/Sabre/Node.php
+++ b/apps/dav/lib/Connector/Sabre/Node.php
@@ -38,6 +38,7 @@ use OC\Files\Mount\MoveableMount;
use OC\Files\View;
use OCA\DAV\Connector\Sabre\Exception\InvalidPath;
use OCP\Files\FileInfo;
+use OCP\Files\StorageNotAvailableException;
use OCP\Share\Exceptions\ShareNotFound;
use OCP\Share\IManager;
@@ -250,15 +251,17 @@ abstract class Node implements \Sabre\DAV\INode {
}
}
- $storage = $this->info->getStorage();
-
- $path = $this->info->getInternalPath();
+ try {
+ $storage = $this->info->getStorage();
+ } catch (StorageNotAvailableException $e) {
+ $storage = null;
+ }
- if ($storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage')) {
+ if ($storage && $storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage')) {
/** @var \OCA\Files_Sharing\SharedStorage $storage */
$permissions = (int)$storage->getShare()->getPermissions();
} else {
- $permissions = $storage->getPermissions($path);
+ $permissions = $this->info->getPermissions();
}
/*
diff --git a/apps/dav/lib/Connector/Sabre/ObjectTree.php b/apps/dav/lib/Connector/Sabre/ObjectTree.php
index 15988cdadb4..ae185b1a611 100644
--- a/apps/dav/lib/Connector/Sabre/ObjectTree.php
+++ b/apps/dav/lib/Connector/Sabre/ObjectTree.php
@@ -159,7 +159,7 @@ class ObjectTree extends CachingTree {
throw new StorageNotAvailableException();
}
} catch (StorageNotAvailableException $e) {
- throw new \Sabre\DAV\Exception\ServiceUnavailable('Storage is temporarily not available');
+ throw new \Sabre\DAV\Exception\ServiceUnavailable('Storage is temporarily not available', 0, $e);
} catch (StorageInvalidException $e) {
throw new \Sabre\DAV\Exception\NotFound('Storage ' . $path . ' is invalid');
} catch (LockedException $e) {
diff --git a/apps/dav/lib/Connector/Sabre/Principal.php b/apps/dav/lib/Connector/Sabre/Principal.php
index b2f57cf715c..feba4d04624 100644
--- a/apps/dav/lib/Connector/Sabre/Principal.php
+++ b/apps/dav/lib/Connector/Sabre/Principal.php
@@ -30,6 +30,7 @@
namespace OCA\DAV\Connector\Sabre;
+use OCP\IConfig;
use OCP\IGroup;
use OCP\IGroupManager;
use OCP\IUser;
@@ -54,6 +55,9 @@ class Principal implements BackendInterface {
/** @var IUserSession */
private $userSession;
+ /** @var IConfig */
+ private $config;
+
/** @var string */
private $principalPrefix;
@@ -65,17 +69,20 @@ class Principal implements BackendInterface {
* @param IGroupManager $groupManager
* @param IShareManager $shareManager
* @param IUserSession $userSession
+ * @param IConfig $config
* @param string $principalPrefix
*/
public function __construct(IUserManager $userManager,
IGroupManager $groupManager,
IShareManager $shareManager,
IUserSession $userSession,
+ IConfig $config,
$principalPrefix = 'principals/users/') {
$this->userManager = $userManager;
$this->groupManager = $groupManager;
$this->shareManager = $shareManager;
$this->userSession = $userSession;
+ $this->config = $config;
$this->principalPrefix = trim($principalPrefix, '/');
$this->hasGroups = ($principalPrefix === 'principals/users/');
}
@@ -205,8 +212,10 @@ class Principal implements BackendInterface {
protected function searchUserPrincipals(array $searchProperties, $test = 'allof') {
$results = [];
- // If sharing is disabled, return the empty array
- if (!$this->shareManager->shareApiEnabled()) {
+ // If sharing is disabled (or FreeBusy was disabled on purpose), return the empty array
+ $shareAPIEnabled = $this->shareManager->shareApiEnabled();
+ $disableFreeBusy = $this->config->getAppValue('dav', 'disableFreeBusy', $shareAPIEnabled ? 'no' : 'yes');
+ if ($disableFreeBusy === 'yes') {
return [];
}
@@ -289,8 +298,10 @@ class Principal implements BackendInterface {
* @return string
*/
function findByUri($uri, $principalPrefix) {
- // If sharing is disabled, return null as in user not found
- if (!$this->shareManager->shareApiEnabled()) {
+ // If sharing is disabled (or FreeBusy was disabled on purpose), return the empty array
+ $shareAPIEnabled = $this->shareManager->shareApiEnabled();
+ $disableFreeBusy = $this->config->getAppValue('dav', 'disableFreeBusy', $shareAPIEnabled ? 'no' : 'yes');
+ if ($disableFreeBusy === 'yes') {
return null;
}
@@ -324,6 +335,13 @@ class Principal implements BackendInterface {
return $this->principalPrefix . '/' . $user->getUID();
}
}
+ if (substr($uri, 0, 10) === 'principal:') {
+ $principal = substr($uri, 10);
+ $principal = $this->getPrincipalByPath($principal);
+ if ($principal !== null) {
+ return $principal['uri'];
+ }
+ }
return null;
}
diff --git a/apps/dav/lib/Connector/Sabre/ServerFactory.php b/apps/dav/lib/Connector/Sabre/ServerFactory.php
index 302e1f52249..12b00be43f5 100644
--- a/apps/dav/lib/Connector/Sabre/ServerFactory.php
+++ b/apps/dav/lib/Connector/Sabre/ServerFactory.php
@@ -110,6 +110,7 @@ class ServerFactory {
// Load plugins
$server->addPlugin(new \OCA\DAV\Connector\Sabre\MaintenancePlugin($this->config));
$server->addPlugin(new \OCA\DAV\Connector\Sabre\BlockLegacyClientPlugin($this->config));
+ $server->addPlugin(new \OCA\DAV\Connector\Sabre\AnonymousOptionsPlugin());
$server->addPlugin($authPlugin);
// FIXME: The following line is a workaround for legacy components relying on being able to send a GET to /
$server->addPlugin(new \OCA\DAV\Connector\Sabre\DummyGetResponsePlugin());
diff --git a/apps/dav/lib/Controller/InvitationResponseController.php b/apps/dav/lib/Controller/InvitationResponseController.php
new file mode 100644
index 00000000000..e3bdab90aaf
--- /dev/null
+++ b/apps/dav/lib/Controller/InvitationResponseController.php
@@ -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_invitations')
+ ->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;
+ }
+}
diff --git a/apps/dav/lib/DAV/Sharing/Backend.php b/apps/dav/lib/DAV/Sharing/Backend.php
index 87c094c6d62..433d9db9c08 100644
--- a/apps/dav/lib/DAV/Sharing/Backend.php
+++ b/apps/dav/lib/DAV/Sharing/Backend.php
@@ -67,12 +67,18 @@ class Backend {
* @param string[] $add
* @param string[] $remove
*/
- public function updateShares($shareable, $add, $remove) {
+ public function updateShares(IShareable $shareable, array $add, array $remove) {
foreach($add as $element) {
- $this->shareWith($shareable, $element);
+ $principal = $this->principalBackend->findByUri($element['href'], '');
+ if ($principal !== '') {
+ $this->shareWith($shareable, $element);
+ }
}
foreach($remove as $element) {
- $this->unshare($shareable, $element);
+ $principal = $this->principalBackend->findByUri($element, '');
+ if ($principal !== '') {
+ $this->unshare($shareable, $element);
+ }
}
}
diff --git a/apps/dav/lib/Files/FilesHome.php b/apps/dav/lib/Files/FilesHome.php
index 63e7916edcf..1ff918aabbb 100644
--- a/apps/dav/lib/Files/FilesHome.php
+++ b/apps/dav/lib/Files/FilesHome.php
@@ -39,15 +39,12 @@ class FilesHome extends Directory {
* FilesHome constructor.
*
* @param array $principalInfo
+ * @param FileInfo $userFolder
*/
- public function __construct($principalInfo) {
+ public function __construct($principalInfo, FileInfo $userFolder) {
$this->principalInfo = $principalInfo;
$view = \OC\Files\Filesystem::getView();
- $rootInfo = $view->getFileInfo('');
- if (!($rootInfo instanceof FileInfo)) {
- throw new \Exception('Home does not exist');
- }
- parent::__construct($view, $rootInfo);
+ parent::__construct($view, $userFolder);
}
function delete() {
diff --git a/apps/dav/lib/Files/LazySearchBackend.php b/apps/dav/lib/Files/LazySearchBackend.php
new file mode 100644
index 00000000000..dc7fec607a4
--- /dev/null
+++ b/apps/dav/lib/Files/LazySearchBackend.php
@@ -0,0 +1,72 @@
+<?php
+/**
+ * @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.nl>
+ *
+ * @author Robin Appelman <robin@icewind.nl>
+ *
+ * @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\Files;
+
+use SearchDAV\Backend\ISearchBackend;
+use SearchDAV\Query\Query;
+
+class LazySearchBackend implements ISearchBackend {
+ /**
+ * @var ISearchBackend $backend
+ */
+ private $backend = null;
+
+ public function setBackend(ISearchBackend $backend) {
+ $this->backend = $backend;
+ }
+
+ public function getArbiterPath() {
+ if ($this->backend) {
+ return $this->backend->getArbiterPath();
+ } else {
+ return '';
+ }
+ }
+
+ public function isValidScope($href, $depth, $path) {
+ if ($this->backend) {
+ return $this->backend->getArbiterPath();
+ } else {
+ return false;
+ }
+ }
+
+ public function getPropertyDefinitionsForScope($href, $path) {
+ if ($this->backend) {
+ return $this->backend->getPropertyDefinitionsForScope($href, $path);
+ } else {
+ return [];
+ }
+ }
+
+ public function search(Query $query) {
+ if ($this->backend) {
+ return $this->backend->search($query);
+ } else {
+ return [];
+ }
+ }
+
+
+}
diff --git a/apps/dav/lib/Files/RootCollection.php b/apps/dav/lib/Files/RootCollection.php
index 59b6690a026..f5544693f2c 100644
--- a/apps/dav/lib/Files/RootCollection.php
+++ b/apps/dav/lib/Files/RootCollection.php
@@ -23,6 +23,7 @@
*/
namespace OCA\DAV\Files;
+use OCP\Files\FileInfo;
use Sabre\DAV\INode;
use Sabre\DAVACL\AbstractPrincipalCollection;
use Sabre\DAV\SimpleCollection;
@@ -48,7 +49,11 @@ class RootCollection extends AbstractPrincipalCollection {
// in the future this could be considered to be used for accessing shared files
return new SimpleCollection($name);
}
- return new FilesHome($principalInfo);
+ $userFolder = \OC::$server->getUserFolder();
+ if (!($userFolder instanceof FileInfo)) {
+ throw new \Exception('Home does not exist');
+ }
+ return new FilesHome($principalInfo, $userFolder);
}
function getName() {
diff --git a/apps/dav/lib/Migration/Version1005Date20180530124431.php b/apps/dav/lib/Migration/Version1005Date20180530124431.php
new file mode 100644
index 00000000000..6f3d6b9cc1f
--- /dev/null
+++ b/apps/dav/lib/Migration/Version1005Date20180530124431.php
@@ -0,0 +1,84 @@
+<?php
+/**
+ * @copyright 2017 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\Migration;
+
+use Doctrine\DBAL\Types\Type;
+use OCP\DB\ISchemaWrapper;
+use OCP\Migration\SimpleMigrationStep;
+use OCP\Migration\IOutput;
+
+class Version1005Date20180530124431 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();
+
+ $types = ['resources', 'rooms'];
+ foreach($types as $type) {
+ if (!$schema->hasTable('calendar_' . $type)) {
+ $table = $schema->createTable('calendar_' . $type);
+
+ $table->addColumn('id', Type::BIGINT, [
+ 'autoincrement' => true,
+ 'notnull' => true,
+ 'length' => 11,
+ 'unsigned' => true,
+ ]);
+ $table->addColumn('backend_id', Type::STRING, [
+ 'notnull' => false,
+ 'length' => 64,
+ ]);
+ $table->addColumn('resource_id', Type::STRING, [
+ 'notnull' => false,
+ 'length' => 64,
+ ]);
+ $table->addColumn('email', Type::STRING, [
+ 'notnull' => false,
+ 'length' => 255,
+ ]);
+ $table->addColumn('displayname', Type::STRING, [
+ 'notnull' => false,
+ 'length' => 255,
+ ]);
+ $table->addColumn('group_restrictions', Type::STRING, [
+ 'notnull' => false,
+ 'length' => 4000,
+ ]);
+
+ $table->setPrimaryKey(['id']);
+ $table->addIndex(['backend_id', 'resource_id'], 'calendar_' . $type . '_bkdrsc');
+ $table->addIndex(['email'], 'calendar_' . $type . '_email');
+ $table->addIndex(['displayname'], 'calendar_' . $type . '_name');
+ }
+ }
+
+ return $schema;
+ }
+}
diff --git a/apps/dav/lib/Migration/Version1006Date20180619154313.php b/apps/dav/lib/Migration/Version1006Date20180619154313.php
new file mode 100644
index 00000000000..91d4826c277
--- /dev/null
+++ b/apps/dav/lib/Migration/Version1006Date20180619154313.php
@@ -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_invitations')) {
+ $table = $schema->createTable('calendar_invitations');
+
+ $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']);
+ $table->addIndex(['token'], 'calendar_invitation_tokens');
+
+ return $schema;
+ }
+ }
+}
diff --git a/apps/dav/lib/RootCollection.php b/apps/dav/lib/RootCollection.php
index a39b8716110..9a3261c388c 100644
--- a/apps/dav/lib/RootCollection.php
+++ b/apps/dav/lib/RootCollection.php
@@ -27,6 +27,8 @@ namespace OCA\DAV;
use OCA\DAV\CalDAV\CalDavBackend;
use OCA\DAV\CalDAV\CalendarRoot;
use OCA\DAV\CalDAV\PublicCalendarRoot;
+use OCA\DAV\CalDAV\ResourceBooking\ResourcePrincipalBackend;
+use OCA\DAV\CalDAV\ResourceBooking\RoomPrincipalBackend;
use OCA\DAV\CardDAV\AddressBookRoot;
use OCA\DAV\CardDAV\CardDavBackend;
use OCA\DAV\Connector\Sabre\Principal;
@@ -43,6 +45,7 @@ class RootCollection extends SimpleCollection {
$random = \OC::$server->getSecureRandom();
$logger = \OC::$server->getLogger();
$userManager = \OC::$server->getUserManager();
+ $userSession = \OC::$server->getUserSession();
$groupManager = \OC::$server->getGroupManager();
$shareManager = \OC::$server->getShareManager();
$db = \OC::$server->getDatabaseConnection();
@@ -51,9 +54,12 @@ class RootCollection extends SimpleCollection {
$userManager,
$groupManager,
$shareManager,
- \OC::$server->getUserSession()
+ \OC::$server->getUserSession(),
+ $config
);
$groupPrincipalBackend = new GroupPrincipalBackend($groupManager);
+ $calendarResourcePrincipalBackend = new ResourcePrincipalBackend($db, $userSession, $groupManager, $logger);
+ $calendarRoomPrincipalBackend = new RoomPrincipalBackend($db, $userSession, $groupManager, $logger);
// as soon as debug mode is enabled we allow listing of principals
$disableListing = !$config->getSystemValue('debug', false);
@@ -64,11 +70,25 @@ class RootCollection extends SimpleCollection {
$groupPrincipals->disableListing = $disableListing;
$systemPrincipals = new Collection(new SystemPrincipalBackend(), 'principals/system');
$systemPrincipals->disableListing = $disableListing;
+ $calendarResourcePrincipals = new Collection($calendarResourcePrincipalBackend, 'principals/calendar-resources');
+ $calendarResourcePrincipals->disableListing = $disableListing;
+ $calendarRoomPrincipals = new Collection($calendarRoomPrincipalBackend, 'principals/calendar-rooms');
+ $calendarRoomPrincipals->disableListing = $disableListing;
+
+
$filesCollection = new Files\RootCollection($userPrincipalBackend, 'principals/users');
$filesCollection->disableListing = $disableListing;
$caldavBackend = new CalDavBackend($db, $userPrincipalBackend, $userManager, $groupManager, $random, $logger, $dispatcher);
- $calendarRoot = new CalendarRoot($userPrincipalBackend, $caldavBackend, 'principals/users');
- $calendarRoot->disableListing = $disableListing;
+ $userCalendarRoot = new CalendarRoot($userPrincipalBackend, $caldavBackend, 'principals/users');
+ $userCalendarRoot->disableListing = $disableListing;
+
+ $resourceCalendarCaldavBackend = new CalDavBackend($db, $userPrincipalBackend, $userManager, $groupManager, $random, $logger, $dispatcher);
+ $resourceCalendarRoot = new CalendarRoot($calendarResourcePrincipalBackend, $caldavBackend, 'principals/calendar-resources');
+ $resourceCalendarRoot->disableListing = $disableListing;
+ $roomCalendarCaldavBackend = new CalDavBackend($db, $userPrincipalBackend, $userManager, $groupManager, $random, $logger, $dispatcher);
+ $roomCalendarRoot = new CalendarRoot($calendarRoomPrincipalBackend, $roomCalendarCaldavBackend, 'principals/calendar-rooms');
+ $roomCalendarRoot->disableListing = $disableListing;
+
$publicCalendarRoot = new PublicCalendarRoot($caldavBackend, $l10n, $config);
$publicCalendarRoot->disableListing = $disableListing;
@@ -110,9 +130,15 @@ class RootCollection extends SimpleCollection {
new SimpleCollection('principals', [
$userPrincipals,
$groupPrincipals,
- $systemPrincipals]),
+ $systemPrincipals,
+ $calendarResourcePrincipals,
+ $calendarRoomPrincipals]),
$filesCollection,
- $calendarRoot,
+ $userCalendarRoot,
+ new SimpleCollection('system-calendars', [
+ $resourceCalendarRoot,
+ $roomCalendarRoot,
+ ]),
$publicCalendarRoot,
new SimpleCollection('addressbooks', [
$usersAddressBookRoot,
diff --git a/apps/dav/lib/Server.php b/apps/dav/lib/Server.php
index 7fbd7671e8d..fc8bc91c80a 100644
--- a/apps/dav/lib/Server.php
+++ b/apps/dav/lib/Server.php
@@ -52,6 +52,8 @@ use OCA\DAV\DAV\PublicAuth;
use OCA\DAV\DAV\CustomPropertiesBackend;
use OCA\DAV\Connector\Sabre\QuotaPlugin;
use OCA\DAV\Files\BrowserErrorPagePlugin;
+use OCA\DAV\Connector\Sabre\AnonymousOptionsPlugin;
+use OCA\DAV\Files\LazySearchBackend;
use OCA\DAV\SystemTag\SystemTagPlugin;
use OCA\DAV\Upload\ChunkingPlugin;
use OCP\IRequest;
@@ -71,14 +73,13 @@ class Server {
private $baseUri;
/** @var Connector\Sabre\Server */
- private $server;
+ public $server;
public function __construct(IRequest $request, $baseUri) {
$this->request = $request;
$this->baseUri = $baseUri;
$logger = \OC::$server->getLogger();
$dispatcher = \OC::$server->getEventDispatcher();
- $sendInvitations = \OC::$server->getConfig()->getAppValue('dav', 'sendInvitations', 'yes') === 'yes';
$root = new RootCollection();
$this->server = new \OCA\DAV\Connector\Sabre\Server(new CachingTree($root));
@@ -100,6 +101,7 @@ class Server {
$this->server->setBaseUri($this->baseUri);
$this->server->addPlugin(new BlockLegacyClientPlugin(\OC::$server->getConfig()));
+ $this->server->addPlugin(new AnonymousOptionsPlugin());
$authPlugin = new Plugin();
$authPlugin->addBackend(new PublicAuth());
$this->server->addPlugin($authPlugin);
@@ -131,30 +133,37 @@ class Server {
// acl
$acl = new DavAclPlugin();
$acl->principalCollectionSet = [
- 'principals/users', 'principals/groups'
+ 'principals/users', 'principals/groups',
+ 'principals/calendar-resources',
+ 'principals/calendar-rooms',
];
$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());
- if ($sendInvitations) {
- $this->server->addPlugin(\OC::$server->query(\OCA\DAV\CalDAV\Schedule\IMipPlugin::class));
+ if ($this->requestIsForSubtree(['calendars', 'public-calendars', 'system-calendars', 'principals'])) {
+ $this->server->addPlugin(new \OCA\DAV\CalDAV\Plugin());
+ $this->server->addPlugin(new \Sabre\CalDAV\ICSExportPlugin());
+ $this->server->addPlugin(new \OCA\DAV\CalDAV\Schedule\Plugin());
+ if (\OC::$server->getConfig()->getAppValue('dav', 'sendInvitations', 'yes') === 'yes') {
+ $this->server->addPlugin(\OC::$server->query(\OCA\DAV\CalDAV\Schedule\IMipPlugin::class));
+ }
+ $this->server->addPlugin(new \Sabre\CalDAV\Subscriptions\Plugin());
+ $this->server->addPlugin(new \Sabre\CalDAV\Notifications\Plugin());
+ $this->server->addPlugin(new DAV\Sharing\Plugin($authBackend, \OC::$server->getRequest()));
+ $this->server->addPlugin(new \OCA\DAV\CalDAV\Publishing\PublishPlugin(
+ \OC::$server->getConfig(),
+ \OC::$server->getURLGenerator()
+ ));
}
- $this->server->addPlugin(new \Sabre\CalDAV\Subscriptions\Plugin());
- $this->server->addPlugin(new \Sabre\CalDAV\Notifications\Plugin());
- $this->server->addPlugin(new DAV\Sharing\Plugin($authBackend, \OC::$server->getRequest()));
- $this->server->addPlugin(new \OCA\DAV\CalDAV\Publishing\PublishPlugin(
- \OC::$server->getConfig(),
- \OC::$server->getURLGenerator()
- ));
// addressbook plugins
- $this->server->addPlugin(new \OCA\DAV\CardDAV\Plugin());
- $this->server->addPlugin(new VCFExportPlugin());
- $this->server->addPlugin(new ImageExportPlugin(new PhotoCache(\OC::$server->getAppDataDir('dav-photocache'))));
+ if ($this->requestIsForSubtree(['addressbooks', 'principals'])) {
+ $this->server->addPlugin(new DAV\Sharing\Plugin($authBackend, \OC::$server->getRequest()));
+ $this->server->addPlugin(new \OCA\DAV\CardDAV\Plugin());
+ $this->server->addPlugin(new VCFExportPlugin());
+ $this->server->addPlugin(new ImageExportPlugin(new PhotoCache(\OC::$server->getAppDataDir('dav-photocache'))));
+ }
// system tags plugins
$this->server->addPlugin(new SystemTagPlugin(
@@ -189,8 +198,11 @@ class Server {
$this->server->addPlugin(new BrowserErrorPagePlugin());
}
+ $lazySearchBackend = new LazySearchBackend();
+ $this->server->addPlugin(new SearchPlugin($lazySearchBackend));
+
// wait with registering these until auth is handled and the filesystem is setup
- $this->server->on('beforeMethod', function () use ($root) {
+ $this->server->on('beforeMethod', function () use ($root, $lazySearchBackend) {
// custom properties plugin must be the last one
$userSession = \OC::$server->getUserSession();
$user = $userSession->getUser();
@@ -249,13 +261,13 @@ class Server {
\OC::$server->getGroupManager(),
$userFolder
));
- $this->server->addPlugin(new SearchPlugin(new \OCA\DAV\Files\FileSearchBackend(
+ $lazySearchBackend->setBackend(new \OCA\DAV\Files\FileSearchBackend(
$this->server->tree,
$user,
\OC::$server->getRootFolder(),
\OC::$server->getShareManager(),
$view
- )));
+ ));
}
$this->server->addPlugin(new \OCA\DAV\CalDAV\BirthdayCalendar\EnablePlugin(
\OC::$server->getConfig(),
@@ -280,4 +292,14 @@ class Server {
public function exec() {
$this->server->exec();
}
+
+ private function requestIsForSubtree(array $subTrees): bool {
+ foreach ($subTrees as $subTree) {
+ $subTree = trim($subTree, ' /');
+ if (strpos($this->server->getRequestUri(), $subTree.'/') === 0) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/apps/dav/lib/Settings/CalDAVSettings.php b/apps/dav/lib/Settings/CalDAVSettings.php
index e4b4ca6452b..f38143b5b4e 100644
--- a/apps/dav/lib/Settings/CalDAVSettings.php
+++ b/apps/dav/lib/Settings/CalDAVSettings.php
@@ -57,13 +57,13 @@ class CalDAVSettings implements ISettings {
* @return string
*/
public function getSection() {
- return 'server';
+ return 'groupware';
}
/**
* @return int
*/
public function getPriority() {
- return 20;
+ return 10;
}
}
diff --git a/apps/dav/templates/schedule-response-error.php b/apps/dav/templates/schedule-response-error.php
new file mode 100644
index 00000000000..c65875f3b0b
--- /dev/null
+++ b/apps/dav/templates/schedule-response-error.php
@@ -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>
diff --git a/apps/dav/templates/schedule-response-options.php b/apps/dav/templates/schedule-response-options.php
new file mode 100644
index 00000000000..da95454e4f5
--- /dev/null
+++ b/apps/dav/templates/schedule-response-options.php
@@ -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>
diff --git a/apps/dav/templates/schedule-response-success.php b/apps/dav/templates/schedule-response-success.php
new file mode 100644
index 00000000000..f60cb1e0fa9
--- /dev/null
+++ b/apps/dav/templates/schedule-response-success.php
@@ -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>
diff --git a/apps/dav/tests/unit/BackgroundJob/CleanupInvitationTokenJobTest.php b/apps/dav/tests/unit/BackgroundJob/CleanupInvitationTokenJobTest.php
new file mode 100644
index 00000000000..2d80b5bf0ea
--- /dev/null
+++ b/apps/dav/tests/unit/BackgroundJob/CleanupInvitationTokenJobTest.php
@@ -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_invitations')
+ ->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([]);
+ }
+}
diff --git a/apps/dav/tests/unit/BackgroundJob/UpdateCalendarResourcesRoomsBackgroundJobTest.php b/apps/dav/tests/unit/BackgroundJob/UpdateCalendarResourcesRoomsBackgroundJobTest.php
new file mode 100644
index 00000000000..e012d5e3f18
--- /dev/null
+++ b/apps/dav/tests/unit/BackgroundJob/UpdateCalendarResourcesRoomsBackgroundJobTest.php
@@ -0,0 +1,285 @@
+<?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\Tests\unit\BackgroundJob;
+
+use OCA\DAV\BackgroundJob\UpdateCalendarResourcesRoomsBackgroundJob;
+
+use OCA\DAV\CalDAV\CalDavBackend;
+use OCP\Calendar\BackendTemporarilyUnavailableException;
+use OCP\Calendar\Resource\IBackend;
+use OCP\Calendar\Resource\IManager as IResourceManager;
+use OCP\Calendar\Resource\IResource;
+use OCP\Calendar\Room\IManager as IRoomManager;
+use Test\TestCase;
+
+class UpdateCalendarResourcesRoomsBackgroundJobTest extends TestCase {
+
+ /** @var UpdateCalendarResourcesRoomsBackgroundJob */
+ private $backgroundJob;
+
+ /** @var IResourceManager | \PHPUnit_Framework_MockObject_MockObject */
+ private $resourceManager;
+
+ /** @var IRoomManager | \PHPUnit_Framework_MockObject_MockObject */
+ private $roomManager;
+
+ /** @var CalDavBackend | \PHPUnit_Framework_MockObject_MockObject */
+ private $calDavBackend;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->resourceManager = $this->createMock(IResourceManager::class);
+ $this->roomManager = $this->createMock(IRoomManager::class);
+ $this->calDavBackend = $this->createMock(CalDavBackend::class);
+
+ $this->backgroundJob = new UpdateCalendarResourcesRoomsBackgroundJob(
+ $this->resourceManager, $this->roomManager, self::$realDatabase,
+ $this->calDavBackend);
+ }
+
+ protected function tearDown() {
+ $query = self::$realDatabase->getQueryBuilder();
+ $query->delete('calendar_resources')->execute();
+ $query->delete('calendar_rooms')->execute();
+ }
+
+ /**
+ * Data in Cache:
+ * resources:
+ * [backend1, res1, Beamer1, {}]
+ * [backend1, res2, TV1, {}]
+ * [backend2, res3, Beamer2, {}]
+ * [backend2, res4, TV2, {}]
+ * [backend3, res5, Beamer3, {}]
+ * [backend3, res6, Pointer, {foo, bar}]
+ *
+ * Data in Backend:
+ * backend1 gone
+ * backend2 throws BackendTemporarilyUnavailableException
+ * [backend3, res6, Pointer123, {foo, biz}]
+ * [backend3, res7, Resource4, {biz}]
+ * [backend4, res8, Beamer, {}]
+ * [backend4, res9, Beamer2, {}]
+ *
+ * Expected after run:
+ * [backend2, res3, Beamer2, {}]
+ * [backend2, res4, TV2, {}]
+ * [backend3, res6, Pointer123, {foo, biz}]
+ * [backend3, res7, Resource4, {biz}]
+ * [backend4, res8, Beamer, {}]
+ * [backend4, res9, Beamer2, {}]
+ */
+
+ public function testRun() {
+ $this->createTestResourcesInCache();
+
+ $backend2 = $this->createMock(IBackend::class);
+ $backend3 = $this->createMock(IBackend::class);
+ $backend4 = $this->createMock(IBackend::class);
+
+ $res6 = $this->createMock(IResource::class);
+ $res7 = $this->createMock(IResource::class);
+ $res8 = $this->createMock(IResource::class);
+ $res9 = $this->createMock(IResource::class);
+
+ $backend2->method('getBackendIdentifier')
+ ->will($this->returnValue('backend2'));
+ $backend2->method('listAllResources')
+ ->will($this->throwException(new BackendTemporarilyUnavailableException()));
+ $backend2->method('getResource')
+ ->will($this->throwException(new BackendTemporarilyUnavailableException()));
+ $backend2->method('getAllResources')
+ ->will($this->throwException(new BackendTemporarilyUnavailableException()));
+ $backend3->method('getBackendIdentifier')
+ ->will($this->returnValue('backend3'));
+ $backend3->method('listAllResources')
+ ->will($this->returnValue(['res6', 'res7']));
+ $backend3->method('getResource')
+ ->will($this->returnValueMap([
+ ['res6', $res6],
+ ['res7', $res7],
+ ]));
+ $backend4->method('getBackendIdentifier')
+ ->will($this->returnValue('backend4'));
+ $backend4->method('listAllResources')
+ ->will($this->returnValue(['res8', 'res9']));
+ $backend4->method('getResource')
+ ->will($this->returnValueMap([
+ ['res8', $res8],
+ ['res9', $res9],
+ ]));
+
+ $res6->method('getId')->will($this->returnValue('res6'));
+ $res6->method('getDisplayName')->will($this->returnValue('Pointer123'));
+ $res6->method('getGroupRestrictions')->will($this->returnValue(['foo', 'biz']));
+ $res6->method('getEMail')->will($this->returnValue('res6@foo.bar'));
+ $res6->method('getBackend')->will($this->returnValue($backend3));
+
+ $res7->method('getId')->will($this->returnValue('res7'));
+ $res7->method('getDisplayName')->will($this->returnValue('Resource4'));
+ $res7->method('getGroupRestrictions')->will($this->returnValue(['biz']));
+ $res7->method('getEMail')->will($this->returnValue('res7@foo.bar'));
+ $res7->method('getBackend')->will($this->returnValue($backend3));
+
+ $res8->method('getId')->will($this->returnValue('res8'));
+ $res8->method('getDisplayName')->will($this->returnValue('Beamer'));
+ $res8->method('getGroupRestrictions')->will($this->returnValue([]));
+ $res8->method('getEMail')->will($this->returnValue('res8@foo.bar'));
+ $res8->method('getBackend')->will($this->returnValue($backend4));
+
+ $res9->method('getId')->will($this->returnValue('res9'));
+ $res9->method('getDisplayName')->will($this->returnValue('Beamer2'));
+ $res9->method('getGroupRestrictions')->will($this->returnValue([]));
+ $res9->method('getEMail')->will($this->returnValue('res9@foo.bar'));
+ $res9->method('getBackend')->will($this->returnValue($backend4));
+
+ $this->resourceManager
+ ->method('getBackends')
+ ->will($this->returnValue([
+ $backend2, $backend3, $backend4
+ ]));
+ $this->resourceManager
+ ->method('getBackend')
+ ->will($this->returnValueMap([
+ ['backend2', $backend2],
+ ['backend3', $backend3],
+ ['backend4', $backend4],
+ ]));
+
+ $this->backgroundJob->run([]);
+
+ $query = self::$realDatabase->getQueryBuilder();
+ $query->select('*')->from('calendar_resources');
+
+ $rows = [];
+ $stmt = $query->execute();
+ while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
+ unset($row['id']);
+ $rows[] = $row;
+ }
+
+ $this->assertEquals([
+ [
+ 'backend_id' => 'backend2',
+ 'resource_id' => 'res3',
+ 'displayname' => 'Beamer2',
+ 'email' => 'res3@foo.bar',
+ 'group_restrictions' => '[]',
+ ],
+ [
+ 'backend_id' => 'backend2',
+ 'resource_id' => 'res4',
+ 'displayname' => 'TV2',
+ 'email' => 'res4@foo.bar',
+ 'group_restrictions' => '[]',
+ ],
+ [
+ 'backend_id' => 'backend3',
+ 'resource_id' => 'res6',
+ 'displayname' => 'Pointer123',
+ 'email' => 'res6@foo.bar',
+ 'group_restrictions' => '["foo","biz"]',
+ ],
+ [
+ 'backend_id' => 'backend3',
+ 'resource_id' => 'res7',
+ 'displayname' => 'Resource4',
+ 'email' => 'res7@foo.bar',
+ 'group_restrictions' => '["biz"]',
+ ],
+ [
+ 'backend_id' => 'backend4',
+ 'resource_id' => 'res8',
+ 'displayname' => 'Beamer',
+ 'email' => 'res8@foo.bar',
+ 'group_restrictions' => '[]',
+ ],
+ [
+ 'backend_id' => 'backend4',
+ 'resource_id' => 'res9',
+ 'displayname' => 'Beamer2',
+ 'email' => 'res9@foo.bar',
+ 'group_restrictions' => '[]',
+ ],
+ ], $rows);
+ }
+
+ protected function createTestResourcesInCache() {
+ $query = self::$realDatabase->getQueryBuilder();
+ $query->insert('calendar_resources')
+ ->values([
+ 'backend_id' => $query->createNamedParameter('backend1'),
+ 'resource_id' => $query->createNamedParameter('res1'),
+ 'email' => $query->createNamedParameter('res1@foo.bar'),
+ 'displayname' => $query->createNamedParameter('Beamer1'),
+ 'group_restrictions' => $query->createNamedParameter('[]'),
+ ])
+ ->execute();
+ $query->insert('calendar_resources')
+ ->values([
+ 'backend_id' => $query->createNamedParameter('backend1'),
+ 'resource_id' => $query->createNamedParameter('res2'),
+ 'email' => $query->createNamedParameter('res2@foo.bar'),
+ 'displayname' => $query->createNamedParameter('TV1'),
+ 'group_restrictions' => $query->createNamedParameter('[]'),
+ ])
+ ->execute();
+ $query->insert('calendar_resources')
+ ->values([
+ 'backend_id' => $query->createNamedParameter('backend2'),
+ 'resource_id' => $query->createNamedParameter('res3'),
+ 'email' => $query->createNamedParameter('res3@foo.bar'),
+ 'displayname' => $query->createNamedParameter('Beamer2'),
+ 'group_restrictions' => $query->createNamedParameter('[]'),
+ ])
+ ->execute();
+ $query->insert('calendar_resources')
+ ->values([
+ 'backend_id' => $query->createNamedParameter('backend2'),
+ 'resource_id' => $query->createNamedParameter('res4'),
+ 'email' => $query->createNamedParameter('res4@foo.bar'),
+ 'displayname' => $query->createNamedParameter('TV2'),
+ 'group_restrictions' => $query->createNamedParameter('[]'),
+ ])
+ ->execute();
+ $query->insert('calendar_resources')
+ ->values([
+ 'backend_id' => $query->createNamedParameter('backend3'),
+ 'resource_id' => $query->createNamedParameter('res5'),
+ 'email' => $query->createNamedParameter('res5@foo.bar'),
+ 'displayname' => $query->createNamedParameter('Beamer3'),
+ 'group_restrictions' => $query->createNamedParameter('[]'),
+ ])
+ ->execute();
+ $query->insert('calendar_resources')
+ ->values([
+ 'backend_id' => $query->createNamedParameter('backend3'),
+ 'resource_id' => $query->createNamedParameter('res6'),
+ 'email' => $query->createNamedParameter('res6@foo.bar'),
+ 'displayname' => $query->createNamedParameter('Pointer'),
+ 'group_restrictions' => $query->createNamedParameter('["foo", "bar"]'),
+ ])
+ ->execute();
+ }
+}
diff --git a/apps/dav/tests/unit/CalDAV/AbstractCalDavBackend.php b/apps/dav/tests/unit/CalDAV/AbstractCalDavBackend.php
index 310433f0913..d49e3bdc778 100644
--- a/apps/dav/tests/unit/CalDAV/AbstractCalDavBackend.php
+++ b/apps/dav/tests/unit/CalDAV/AbstractCalDavBackend.php
@@ -27,10 +27,13 @@ namespace OCA\DAV\Tests\unit\CalDAV;
use OCA\DAV\CalDAV\CalDavBackend;
use OCA\DAV\Connector\Sabre\Principal;
+use OCP\IConfig;
use OCP\IGroupManager;
use OCP\ILogger;
use OCP\IUserManager;
+use OCP\IUserSession;
use OCP\Security\ISecureRandom;
+use OCP\Share\IManager as ShareManager;
use Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Test\TestCase;
@@ -73,7 +76,13 @@ abstract class AbstractCalDavBackend extends TestCase {
$this->groupManager = $this->createMock(IGroupManager::class);
$this->dispatcher = $this->createMock(EventDispatcherInterface::class);
$this->principal = $this->getMockBuilder(Principal::class)
- ->disableOriginalConstructor()
+ ->setConstructorArgs([
+ $this->userManager,
+ $this->groupManager,
+ $this->createMock(ShareManager::class),
+ $this->createMock(IUserSession::class),
+ $this->createMock(IConfig::class),
+ ])
->setMethods(['getPrincipalByPath', 'getGroupMembership'])
->getMock();
$this->principal->expects($this->any())->method('getPrincipalByPath')
diff --git a/apps/dav/tests/unit/CalDAV/Activity/Filter/CalendarTest.php b/apps/dav/tests/unit/CalDAV/Activity/Filter/CalendarTest.php
index 0fdd50b54c4..1c31508255a 100644
--- a/apps/dav/tests/unit/CalDAV/Activity/Filter/CalendarTest.php
+++ b/apps/dav/tests/unit/CalDAV/Activity/Filter/CalendarTest.php
@@ -55,7 +55,7 @@ class CalendarTest extends TestCase {
public function testGetIcon() {
$this->url->expects($this->once())
->method('imagePath')
- ->with('core', 'places/calendar-dark.svg')
+ ->with('core', 'places/calendar.svg')
->willReturn('path-to-icon');
$this->url->expects($this->once())
diff --git a/apps/dav/tests/unit/CalDAV/PluginTest.php b/apps/dav/tests/unit/CalDAV/PluginTest.php
index 7d283b6d1ed..47190d583f0 100644
--- a/apps/dav/tests/unit/CalDAV/PluginTest.php
+++ b/apps/dav/tests/unit/CalDAV/PluginTest.php
@@ -43,8 +43,12 @@ class PluginTest extends TestCase {
'calendars/MyUserName',
],
[
- 'FooFoo',
- null,
+ 'principals/calendar-resources/Resource-ABC',
+ 'system-calendars/calendar-resources/Resource-ABC',
+ ],
+ [
+ 'principals/calendar-rooms/Room-ABC',
+ 'system-calendars/calendar-rooms/Room-ABC',
],
];
}
@@ -59,4 +63,11 @@ class PluginTest extends TestCase {
$this->assertSame($expected, $this->plugin->getCalendarHomeForPrincipal($input));
}
+ /**
+ * @expectedException \LogicException
+ * @expectedExceptionMessage This is not supposed to happen
+ */
+ public function testGetCalendarHomeForUnknownPrincipal() {
+ $this->plugin->getCalendarHomeForPrincipal('FOO/BAR/BLUB');
+ }
}
diff --git a/apps/dav/tests/unit/CalDAV/ResourceBooking/AbstractPrincipalBackendTest.php b/apps/dav/tests/unit/CalDAV/ResourceBooking/AbstractPrincipalBackendTest.php
new file mode 100644
index 00000000000..4dee0220fc8
--- /dev/null
+++ b/apps/dav/tests/unit/CalDAV/ResourceBooking/AbstractPrincipalBackendTest.php
@@ -0,0 +1,930 @@
+<?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\Tests\unit\CalDAV\ResourceBooking;
+
+use OCP\DB\QueryBuilder\IQueryBuilder;
+use OCP\IDBConnection;
+use OCP\IGroupManager;
+use OCP\ILogger;
+use OCP\IUser;
+use OCP\IUserSession;
+use Sabre\DAV\PropPatch;
+use Test\TestCase;
+
+abstract class AbstractPrincipalBackendTest extends TestCase {
+
+ /** @var \OCA\DAV\CalDAV\ResourceBooking\ResourcePrincipalBackend|\OCA\DAV\CalDAV\ResourceBooking\RoomPrincipalBackend */
+ protected $principalBackend;
+
+ /** @var IDBConnection|\PHPUnit_Framework_MockObject_MockObject */
+ protected $dbConnection;
+
+ /** @var IUserSession|\PHPUnit_Framework_MockObject_MockObject */
+ protected $userSession;
+
+ /** @var IGroupManager|\PHPUnit_Framework_MockObject_MockObject */
+ protected $groupManager;
+
+ /** @var ILogger|\PHPUnit_Framework_MockObject_MockObject */
+ protected $logger;
+
+ /** @var string */
+ protected $expectedDbTable;
+
+ /** @var string */
+ protected $principalPrefix;
+
+ public function setUp() {
+ parent::setUp();
+
+ $this->dbConnection = $this->createMock(IDBConnection::class);
+ $this->userSession = $this->createMock(IUserSession::class);
+ $this->groupManager = $this->createMock(IGroupManager::class);
+ $this->logger = $this->createMock(ILogger::class);
+ }
+
+ public function testGetPrincipalsByPrefix() {
+ $queryBuilder = $this->createMock(IQueryBuilder::class);
+ $stmt = $this->createMock(\Doctrine\DBAL\Driver\Statement::class);
+ $this->dbConnection->expects($this->once())
+ ->method('getQueryBuilder')
+ ->with()
+ ->will($this->returnValue($queryBuilder));
+
+ $queryBuilder->expects($this->at(0))
+ ->method('select')
+ ->with(['id', 'backend_id', 'resource_id', 'email', 'displayname'])
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->expects($this->at(1))
+ ->method('from')
+ ->with($this->expectedDbTable)
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->expects($this->at(2))
+ ->method('execute')
+ ->with()
+ ->will($this->returnValue($stmt));
+
+ $stmt->expects($this->at(0))
+ ->method('fetch')
+ ->with(\PDO::FETCH_ASSOC)
+ ->will($this->returnValue([
+ 'id' => 0,
+ 'backend_id' => 'db',
+ 'resource_id' => '123',
+ 'email' => 'foo@bar.com',
+ 'displayname' => 'Resource 123'
+ ]));
+ $stmt->expects($this->at(1))
+ ->method('fetch')
+ ->with(\PDO::FETCH_ASSOC)
+ ->will($this->returnValue([
+ 'id' => 1,
+ 'backend_id' => 'ldap',
+ 'resource_id' => '123',
+ 'email' => 'ldap@bar.com',
+ 'displayname' => 'Resource 123 ldap'
+ ]));
+ $stmt->expects($this->at(2))
+ ->method('fetch')
+ ->with(\PDO::FETCH_ASSOC)
+ ->will($this->returnValue([
+ 'id' => 2,
+ 'backend_id' => 'db',
+ 'resource_id' => '456',
+ 'email' => 'bli@bar.com',
+ 'displayname' => 'Resource 456'
+ ]));
+ $stmt->expects($this->at(3))
+ ->method('fetch')
+ ->with(\PDO::FETCH_ASSOC)
+ ->will($this->returnValue(null));
+ $stmt->expects($this->at(4))
+ ->method('closeCursor')
+ ->with();
+
+ $actual = $this->principalBackend->getPrincipalsByPrefix($this->principalPrefix);
+ $this->assertEquals([
+ [
+ 'uri' => $this->principalPrefix . '/db-123',
+ '{DAV:}displayname' => 'Resource 123',
+ '{http://sabredav.org/ns}email-address' => 'foo@bar.com',
+ ],
+ [
+ 'uri' => $this->principalPrefix . '/ldap-123',
+ '{DAV:}displayname' => 'Resource 123 ldap',
+ '{http://sabredav.org/ns}email-address' => 'ldap@bar.com',
+ ],
+ [
+ 'uri' => $this->principalPrefix . '/db-456',
+ '{DAV:}displayname' => 'Resource 456',
+ '{http://sabredav.org/ns}email-address' => 'bli@bar.com',
+ ],
+ ], $actual);
+
+ }
+
+ public function testGetNoPrincipalsByPrefixForWrongPrincipalPrefix() {
+ $this->dbConnection->expects($this->never())
+ ->method('getQueryBuilder');
+
+ $actual = $this->principalBackend->getPrincipalsByPrefix('principals/users');
+ $this->assertEquals([], $actual);
+ }
+
+ public function testGetPrincipalByPath() {
+ $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->at(0))
+ ->method('getQueryBuilder')
+ ->with()
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->method('expr')
+ ->will($this->returnValue($expr));
+ $expr->method('eq')
+ ->will($this->returnValueMap([
+ ['backend_id', 'createNamedParameter-1', null, 'WHERE_CLAUSE_1'],
+ ['resource_id', 'createNamedParameter-2', null, 'WHERE_CLAUSE_2'],
+ ]));
+ $queryBuilder->method('createNamedParameter')
+ ->will($this->returnValueMap([
+ ['db', \PDO::PARAM_STR, null, 'createNamedParameter-1'],
+ ['123', \PDO::PARAM_STR, null, 'createNamedParameter-2'],
+ ]));
+
+ $queryBuilder->expects($this->at(0))
+ ->method('select')
+ ->with(['id', 'backend_id', 'resource_id', 'email', 'displayname'])
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->expects($this->at(1))
+ ->method('from')
+ ->with($this->expectedDbTable)
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->expects($this->at(4))
+ ->method('where')
+ ->with('WHERE_CLAUSE_1')
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->expects($this->at(7))
+ ->method('andWhere')
+ ->with('WHERE_CLAUSE_2')
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->expects($this->at(8))
+ ->method('execute')
+ ->with()
+ ->will($this->returnValue($stmt));
+
+ $stmt->expects($this->at(0))
+ ->method('fetch')
+ ->with(\PDO::FETCH_ASSOC)
+ ->will($this->returnValue([
+ 'id' => 0,
+ 'backend_id' => 'db',
+ 'resource_id' => '123',
+ 'email' => 'foo@bar.com',
+ 'displayname' => 'Resource 123'
+ ]));
+
+ $actual = $this->principalBackend->getPrincipalByPath($this->principalPrefix . '/db-123');
+ $this->assertEquals([
+ 'uri' => $this->principalPrefix . '/db-123',
+ '{DAV:}displayname' => 'Resource 123',
+ '{http://sabredav.org/ns}email-address' => 'foo@bar.com',
+ ], $actual);
+ }
+
+ public function testGetPrincipalByPathNotFound() {
+ $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->at(0))
+ ->method('getQueryBuilder')
+ ->with()
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->method('expr')
+ ->will($this->returnValue($expr));
+ $expr->method('eq')
+ ->will($this->returnValueMap([
+ ['backend_id', 'createNamedParameter-1', null, 'WHERE_CLAUSE_1'],
+ ['resource_id', 'createNamedParameter-2', null, 'WHERE_CLAUSE_2'],
+ ]));
+ $queryBuilder->method('createNamedParameter')
+ ->will($this->returnValueMap([
+ ['db', \PDO::PARAM_STR, null, 'createNamedParameter-1'],
+ ['123', \PDO::PARAM_STR, null, 'createNamedParameter-2'],
+ ]));
+
+ $queryBuilder->expects($this->at(0))
+ ->method('select')
+ ->with(['id', 'backend_id', 'resource_id', 'email', 'displayname'])
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->expects($this->at(1))
+ ->method('from')
+ ->with($this->expectedDbTable)
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->expects($this->at(4))
+ ->method('where')
+ ->with('WHERE_CLAUSE_1')
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->expects($this->at(7))
+ ->method('andWhere')
+ ->with('WHERE_CLAUSE_2')
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->expects($this->at(8))
+ ->method('execute')
+ ->with()
+ ->will($this->returnValue($stmt));
+
+ $stmt->expects($this->at(0))
+ ->method('fetch')
+ ->with(\PDO::FETCH_ASSOC)
+ ->will($this->returnValue(false));
+
+ $actual = $this->principalBackend->getPrincipalByPath($this->principalPrefix . '/db-123');
+ $this->assertEquals(null, $actual);
+ }
+
+ public function testGetPrincipalByPathWrongPrefix() {
+ $this->dbConnection->expects($this->never())
+ ->method('getQueryBuilder');
+
+ $actual = $this->principalBackend->getPrincipalByPath('principals/users/foo-bar');
+ $this->assertEquals(null, $actual);
+ }
+
+ public function testGetGroupMemberSet() {
+ $actual = $this->principalBackend->getGroupMemberSet($this->principalPrefix . '/foo-bar');
+ $this->assertEquals([], $actual);
+ }
+
+ public function testGetGroupMembership() {
+ $actual = $this->principalBackend->getGroupMembership($this->principalPrefix . '/foo-bar');
+ $this->assertEquals([], $actual);
+ }
+
+ /**
+ * @expectedException \Sabre\DAV\Exception
+ * @expectedExceptionMessage Setting members of the group is not supported yet
+ */
+ public function testSetGroupMemberSet() {
+ $this->principalBackend->setGroupMemberSet($this->principalPrefix . '/foo-bar', ['foo', 'bar']);
+ }
+
+ public function testUpdatePrincipal() {
+ $propPatch = $this->createMock(PropPatch::class);
+ $actual = $this->principalBackend->updatePrincipal($this->principalPrefix . '/foo-bar', $propPatch);
+
+ $this->assertEquals(0, $actual);
+ }
+
+ /**
+ * @dataProvider dataSearchPrincipals
+ */
+ public function testSearchPrincipals($expected, $test) {
+ $user = $this->createMock(IUser::class);
+ $this->userSession->expects($this->once())
+ ->method('getUser')
+ ->with()
+ ->will($this->returnValue($user));
+ $this->groupManager->expects($this->once())
+ ->method('getUserGroupIds')
+ ->with($user)
+ ->will($this->returnValue(['group1', 'group2']));
+
+ $queryBuilder1 = $this->createMock(IQueryBuilder::class);
+ $queryBuilder2 = $this->createMock(IQueryBuilder::class);
+ $stmt1 = $this->createMock(\Doctrine\DBAL\Driver\Statement::class);
+ $stmt2 = $this->createMock(\Doctrine\DBAL\Driver\Statement::class);
+ $expr1 = $this->createMock(\OCP\DB\QueryBuilder\IExpressionBuilder::class);
+ $expr2 = $this->createMock(\OCP\DB\QueryBuilder\IExpressionBuilder::class);
+
+ $this->dbConnection->expects($this->at(0))
+ ->method('getQueryBuilder')
+ ->with()
+ ->will($this->returnValue($queryBuilder1));
+ $this->dbConnection->expects($this->at(1))
+ ->method('escapeLikeParameter')
+ ->with('foo')
+ ->will($this->returnValue('escapedFoo'));
+ $this->dbConnection->expects($this->at(2))
+ ->method('getQueryBuilder')
+ ->with()
+ ->will($this->returnValue($queryBuilder2));
+ $this->dbConnection->expects($this->at(3))
+ ->method('escapeLikeParameter')
+ ->with('bar')
+ ->will($this->returnValue('escapedBar'));
+
+ $queryBuilder1->method('expr')
+ ->will($this->returnValue($expr1));
+ $queryBuilder2->method('expr')
+ ->will($this->returnValue($expr2));
+
+ $expr1->method('iLike')
+ ->will($this->returnValueMap([
+ ['email', 'createNamedParameter-1', null, 'ILIKE_CLAUSE_1'],
+ ]));
+ $expr2->method('iLike')
+ ->will($this->returnValueMap([
+ ['displayname', 'createNamedParameter-2', null, 'ILIKE_CLAUSE_2'],
+ ]));
+
+ $queryBuilder1->method('expr')
+ ->will($this->returnValue($expr1));
+ $queryBuilder2->method('expr')
+ ->will($this->returnValue($expr2));
+
+ $queryBuilder1->method('createNamedParameter')
+ ->will($this->returnValueMap([
+ ['%escapedFoo%', \PDO::PARAM_STR, null, 'createNamedParameter-1'],
+ ]));
+ $queryBuilder2->method('createNamedParameter')
+ ->will($this->returnValueMap([
+ ['%escapedBar%', \PDO::PARAM_STR, null, 'createNamedParameter-2'],
+ ]));
+
+ $queryBuilder1->expects($this->at(0))
+ ->method('select')
+ ->with(['id', 'backend_id', 'resource_id', 'email', 'displayname', 'group_restrictions'])
+ ->will($this->returnValue($queryBuilder1));
+ $queryBuilder1->expects($this->at(1))
+ ->method('from')
+ ->with($this->expectedDbTable)
+ ->will($this->returnValue($queryBuilder1));
+ $queryBuilder1->expects($this->at(4))
+ ->method('where')
+ ->with('ILIKE_CLAUSE_1')
+ ->will($this->returnValue($queryBuilder1));
+ $queryBuilder1->expects($this->at(5))
+ ->method('execute')
+ ->with()
+ ->will($this->returnValue($stmt1));
+
+ $queryBuilder2->expects($this->at(0))
+ ->method('select')
+ ->with(['id', 'backend_id', 'resource_id', 'email', 'displayname', 'group_restrictions'])
+ ->will($this->returnValue($queryBuilder2));
+ $queryBuilder2->expects($this->at(1))
+ ->method('from')
+ ->with($this->expectedDbTable)
+ ->will($this->returnValue($queryBuilder2));
+ $queryBuilder2->expects($this->at(4))
+ ->method('where')
+ ->with('ILIKE_CLAUSE_2')
+ ->will($this->returnValue($queryBuilder2));
+ $queryBuilder2->expects($this->at(5))
+ ->method('execute')
+ ->with()
+ ->will($this->returnValue($stmt2));
+
+ $stmt1->expects($this->at(0))
+ ->method('fetch')
+ ->with(\PDO::FETCH_ASSOC)
+ ->will($this->returnValue([
+ 'id' => 0,
+ 'backend_id' => 'db',
+ 'resource_id' => '1',
+ 'email' => '1',
+ 'displayname' => 'Resource 1',
+ 'group_restrictions' => null,
+ ]));
+ $stmt1->expects($this->at(1))
+ ->method('fetch')
+ ->with(\PDO::FETCH_ASSOC)
+ ->will($this->returnValue([
+ 'id' => 1,
+ 'backend_id' => 'db',
+ 'resource_id' => '2',
+ 'email' => '2',
+ 'displayname' => 'Resource 2',
+ 'group_restrictions' => '',
+ ]));
+ $stmt1->expects($this->at(2))
+ ->method('fetch')
+ ->with(\PDO::FETCH_ASSOC)
+ ->will($this->returnValue([
+ 'id' => 2,
+ 'backend_id' => 'db',
+ 'resource_id' => '3',
+ 'email' => '3',
+ 'displayname' => 'Resource 3',
+ 'group_restrictions' => '["group3"]',
+ ]));
+ $stmt1->expects($this->at(3))
+ ->method('fetch')
+ ->with(\PDO::FETCH_ASSOC)
+ ->will($this->returnValue([
+ 'id' => 99,
+ 'backend_id' => 'db',
+ 'resource_id' => '99',
+ 'email' => '99',
+ 'displayname' => 'Resource 99',
+ 'group_restrictions' => '["group1", "group2"]',
+ ]));
+ $stmt1->expects($this->at(4))
+ ->method('fetch')
+ ->with(\PDO::FETCH_ASSOC)
+ ->will($this->returnValue(null));
+
+ $stmt2->expects($this->at(0))
+ ->method('fetch')
+ ->with(\PDO::FETCH_ASSOC)
+ ->will($this->returnValue([
+ 'id' => 0,
+ 'backend_id' => 'db',
+ 'resource_id' => '4',
+ 'email' => '4',
+ 'displayname' => 'Resource 4',
+ 'group_restrictions' => '[]'
+ ]));
+ $stmt2->expects($this->at(1))
+ ->method('fetch')
+ ->with(\PDO::FETCH_ASSOC)
+ ->will($this->returnValue([
+ 'id' => 1,
+ 'backend_id' => 'db',
+ 'resource_id' => '5',
+ 'email' => '5',
+ 'displayname' => 'Resource 5',
+ 'group_restrictions' => '["group1", "group5"]'
+ ]));
+ $stmt2->expects($this->at(2))
+ ->method('fetch')
+ ->with(\PDO::FETCH_ASSOC)
+ ->will($this->returnValue([
+ 'id' => 99,
+ 'backend_id' => 'db',
+ 'resource_id' => '99',
+ 'email' => '99',
+ 'displayname' => 'Resource 99',
+ 'group_restrictions' => '["group1", "group2"]',
+ ]));
+ $stmt2->expects($this->at(3))
+ ->method('fetch')
+ ->with(\PDO::FETCH_ASSOC)
+ ->will($this->returnValue(null));
+
+ $actual = $this->principalBackend->searchPrincipals($this->principalPrefix, [
+ '{http://sabredav.org/ns}email-address' => 'foo',
+ '{DAV:}displayname' => 'bar',
+ ], $test);
+
+ $this->assertEquals(
+ str_replace('%prefix%', $this->principalPrefix, $expected),
+ $actual);
+ }
+
+ public function dataSearchPrincipals() {
+ // data providers are called before we subclass
+ // this class, $this->principalPrefix is null
+ // at that point, so we need this hack
+ return [
+ [[
+ '%prefix%/db-99'
+ ], 'allof'],
+ [[
+ '%prefix%/db-1',
+ '%prefix%/db-2',
+ '%prefix%/db-99',
+ '%prefix%/db-4',
+ '%prefix%/db-5',
+ ], 'anyof'],
+ ];
+ }
+
+ public function testSearchPrincipalsEmptySearchProperties() {
+ $this->userSession->expects($this->never())
+ ->method('getUser');
+ $this->groupManager->expects($this->never())
+ ->method('getUserGroupIds');
+ $this->dbConnection->expects($this->never())
+ ->method('getQueryBuilder');
+
+ $this->principalBackend->searchPrincipals($this->principalPrefix, []);
+ }
+
+ public function testSearchPrincipalsWrongPrincipalPrefix() {
+ $this->userSession->expects($this->never())
+ ->method('getUser');
+ $this->groupManager->expects($this->never())
+ ->method('getUserGroupIds');
+ $this->dbConnection->expects($this->never())
+ ->method('getQueryBuilder');
+
+ $this->principalBackend->searchPrincipals('principals/users', [
+ '{http://sabredav.org/ns}email-address' => 'foo'
+ ]);
+ }
+
+ public function testFindByUriByEmail() {
+ $user = $this->createMock(IUser::class);
+ $this->userSession->expects($this->once())
+ ->method('getUser')
+ ->with()
+ ->will($this->returnValue($user));
+ $this->groupManager->expects($this->once())
+ ->method('getUserGroupIds')
+ ->with($user)
+ ->will($this->returnValue(['group1', 'group2']));
+
+ $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->at(0))
+ ->method('getQueryBuilder')
+ ->with()
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->method('expr')
+ ->will($this->returnValue($expr));
+ $expr->method('eq')
+ ->will($this->returnValueMap([
+ ['email', 'createNamedParameter-1', null, 'WHERE_CLAUSE_1'],
+ ]));
+ $queryBuilder->method('createNamedParameter')
+ ->will($this->returnValueMap([
+ ['foo@bar.com', \PDO::PARAM_STR, null, 'createNamedParameter-1'],
+ ]));
+
+ $queryBuilder->expects($this->at(0))
+ ->method('select')
+ ->with(['id', 'backend_id', 'resource_id', 'email', 'displayname', 'group_restrictions'])
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->expects($this->at(1))
+ ->method('from')
+ ->with($this->expectedDbTable)
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->expects($this->at(4))
+ ->method('where')
+ ->with('WHERE_CLAUSE_1')
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->expects($this->at(5))
+ ->method('execute')
+ ->with()
+ ->will($this->returnValue($stmt));
+
+ $stmt->expects($this->at(0))
+ ->method('fetch')
+ ->with(\PDO::FETCH_ASSOC)
+ ->will($this->returnValue([
+ 'id' => 0,
+ 'backend_id' => 'db',
+ 'resource_id' => '123',
+ 'email' => 'foo@bar.com',
+ 'displayname' => 'Resource 123',
+ 'group_restrictions' => '["group1"]',
+ ]));
+
+ $actual = $this->principalBackend->findByUri('mailto:foo@bar.com', $this->principalPrefix);
+ $this->assertEquals($this->principalPrefix . '/db-123', $actual);
+ }
+
+ public function testFindByUriByEmailForbiddenResource() {
+ $user = $this->createMock(IUser::class);
+ $this->userSession->expects($this->once())
+ ->method('getUser')
+ ->with()
+ ->will($this->returnValue($user));
+ $this->groupManager->expects($this->once())
+ ->method('getUserGroupIds')
+ ->with($user)
+ ->will($this->returnValue(['group1', 'group2']));
+
+ $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->at(0))
+ ->method('getQueryBuilder')
+ ->with()
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->method('expr')
+ ->will($this->returnValue($expr));
+ $expr->method('eq')
+ ->will($this->returnValueMap([
+ ['email', 'createNamedParameter-1', null, 'WHERE_CLAUSE_1'],
+ ]));
+ $queryBuilder->method('createNamedParameter')
+ ->will($this->returnValueMap([
+ ['foo@bar.com', \PDO::PARAM_STR, null, 'createNamedParameter-1'],
+ ]));
+
+ $queryBuilder->expects($this->at(0))
+ ->method('select')
+ ->with(['id', 'backend_id', 'resource_id', 'email', 'displayname', 'group_restrictions'])
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->expects($this->at(1))
+ ->method('from')
+ ->with($this->expectedDbTable)
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->expects($this->at(4))
+ ->method('where')
+ ->with('WHERE_CLAUSE_1')
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->expects($this->at(5))
+ ->method('execute')
+ ->with()
+ ->will($this->returnValue($stmt));
+
+ $stmt->expects($this->at(0))
+ ->method('fetch')
+ ->with(\PDO::FETCH_ASSOC)
+ ->will($this->returnValue([
+ 'id' => 0,
+ 'backend_id' => 'db',
+ 'resource_id' => '123',
+ 'email' => 'foo@bar.com',
+ 'displayname' => 'Resource 123',
+ 'group_restrictions' => '["group3"]',
+ ]));
+
+ $actual = $this->principalBackend->findByUri('mailto:foo@bar.com', $this->principalPrefix);
+ $this->assertEquals(null, $actual);
+ }
+
+ public function testFindByUriByEmailNotFound() {
+ $user = $this->createMock(IUser::class);
+ $this->userSession->expects($this->once())
+ ->method('getUser')
+ ->with()
+ ->will($this->returnValue($user));
+ $this->groupManager->expects($this->once())
+ ->method('getUserGroupIds')
+ ->with($user)
+ ->will($this->returnValue(['group1', 'group2']));
+
+ $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->at(0))
+ ->method('getQueryBuilder')
+ ->with()
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->method('expr')
+ ->will($this->returnValue($expr));
+ $expr->method('eq')
+ ->will($this->returnValueMap([
+ ['email', 'createNamedParameter-1', null, 'WHERE_CLAUSE_1'],
+ ]));
+ $queryBuilder->method('createNamedParameter')
+ ->will($this->returnValueMap([
+ ['foo@bar.com', \PDO::PARAM_STR, null, 'createNamedParameter-1'],
+ ]));
+
+ $queryBuilder->expects($this->at(0))
+ ->method('select')
+ ->with(['id', 'backend_id', 'resource_id', 'email', 'displayname', 'group_restrictions'])
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->expects($this->at(1))
+ ->method('from')
+ ->with($this->expectedDbTable)
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->expects($this->at(4))
+ ->method('where')
+ ->with('WHERE_CLAUSE_1')
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->expects($this->at(5))
+ ->method('execute')
+ ->with()
+ ->will($this->returnValue($stmt));
+
+ $stmt->expects($this->at(0))
+ ->method('fetch')
+ ->with(\PDO::FETCH_ASSOC)
+ ->will($this->returnValue(null));
+
+ $actual = $this->principalBackend->findByUri('mailto:foo@bar.com', $this->principalPrefix);
+ $this->assertEquals(null, $actual);
+ }
+
+ public function testFindByUriByPrincipal() {
+ $user = $this->createMock(IUser::class);
+ $this->userSession->expects($this->once())
+ ->method('getUser')
+ ->with()
+ ->will($this->returnValue($user));
+ $this->groupManager->expects($this->once())
+ ->method('getUserGroupIds')
+ ->with($user)
+ ->will($this->returnValue(['group1', 'group2']));
+
+ $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->at(0))
+ ->method('getQueryBuilder')
+ ->with()
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->method('expr')
+ ->will($this->returnValue($expr));
+ $expr->method('eq')
+ ->will($this->returnValueMap([
+ ['email', 'createNamedParameter-1', null, 'WHERE_CLAUSE_1'],
+ ]));
+ $queryBuilder->method('createNamedParameter')
+ ->will($this->returnValueMap([
+ ['foo@bar.com', \PDO::PARAM_STR, null, 'createNamedParameter-1'],
+ ]));
+
+ $queryBuilder->expects($this->at(0))
+ ->method('select')
+ ->with(['id', 'backend_id', 'resource_id', 'email', 'displayname', 'group_restrictions'])
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->expects($this->at(1))
+ ->method('from')
+ ->with($this->expectedDbTable)
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->expects($this->at(4))
+ ->method('where')
+ ->with('WHERE_CLAUSE_1')
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->expects($this->at(5))
+ ->method('execute')
+ ->with()
+ ->will($this->returnValue($stmt));
+
+ $stmt->expects($this->at(0))
+ ->method('fetch')
+ ->with(\PDO::FETCH_ASSOC)
+ ->will($this->returnValue([
+ 'id' => 0,
+ 'backend_id' => 'db',
+ 'resource_id' => '123',
+ 'email' => 'foo@bar.com',
+ 'displayname' => 'Resource 123',
+ 'group_restrictions' => '["group1"]',
+ ]));
+
+ $actual = $this->principalBackend->findByUri('mailto:foo@bar.com', $this->principalPrefix);
+ $this->assertEquals($this->principalPrefix . '/db-123', $actual);
+ }
+
+ public function testFindByUriByPrincipalForbiddenResource() {
+ $user = $this->createMock(IUser::class);
+ $this->userSession->expects($this->once())
+ ->method('getUser')
+ ->with()
+ ->will($this->returnValue($user));
+ $this->groupManager->expects($this->once())
+ ->method('getUserGroupIds')
+ ->with($user)
+ ->will($this->returnValue(['group1', 'group2']));
+
+ $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->at(0))
+ ->method('getQueryBuilder')
+ ->with()
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->method('expr')
+ ->will($this->returnValue($expr));
+ $expr->method('eq')
+ ->will($this->returnValueMap([
+ ['backend_id', 'createNamedParameter-1', null, 'WHERE_CLAUSE_1'],
+ ['resource_id', 'createNamedParameter-2', null, 'WHERE_CLAUSE_2'],
+ ]));
+ $queryBuilder->method('createNamedParameter')
+ ->will($this->returnValueMap([
+ ['db', \PDO::PARAM_STR, null, 'createNamedParameter-1'],
+ ['123', \PDO::PARAM_STR, null, 'createNamedParameter-2'],
+ ]));
+
+ $queryBuilder->expects($this->at(0))
+ ->method('select')
+ ->with(['id', 'backend_id', 'resource_id', 'email', 'displayname', 'group_restrictions'])
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->expects($this->at(1))
+ ->method('from')
+ ->with($this->expectedDbTable)
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->expects($this->at(4))
+ ->method('where')
+ ->with('WHERE_CLAUSE_1')
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->expects($this->at(7))
+ ->method('andWhere')
+ ->with('WHERE_CLAUSE_2')
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->expects($this->at(8))
+ ->method('execute')
+ ->with()
+ ->will($this->returnValue($stmt));
+
+ $stmt->expects($this->at(0))
+ ->method('fetch')
+ ->with(\PDO::FETCH_ASSOC)
+ ->will($this->returnValue([
+ 'id' => 0,
+ 'backend_id' => 'db',
+ 'resource_id' => '123',
+ 'email' => 'foo@bar.com',
+ 'displayname' => 'Resource 123',
+ 'group_restrictions' => '["group3"]',
+ ]));
+
+ $actual = $this->principalBackend->findByUri('principal:' . $this->principalPrefix . '/db-123', $this->principalPrefix);
+ $this->assertEquals(null, $actual);
+ }
+
+ public function testFindByUriByPrincipalNotFound() {
+ $user = $this->createMock(IUser::class);
+ $this->userSession->expects($this->once())
+ ->method('getUser')
+ ->with()
+ ->will($this->returnValue($user));
+ $this->groupManager->expects($this->once())
+ ->method('getUserGroupIds')
+ ->with($user)
+ ->will($this->returnValue(['group1', 'group2']));
+
+ $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->at(0))
+ ->method('getQueryBuilder')
+ ->with()
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->method('expr')
+ ->will($this->returnValue($expr));
+ $expr->method('eq')
+ ->will($this->returnValueMap([
+ ['backend_id', 'createNamedParameter-1', null, 'WHERE_CLAUSE_1'],
+ ['resource_id', 'createNamedParameter-2', null, 'WHERE_CLAUSE_2'],
+ ]));
+ $queryBuilder->method('createNamedParameter')
+ ->will($this->returnValueMap([
+ ['db', \PDO::PARAM_STR, null, 'createNamedParameter-1'],
+ ['123', \PDO::PARAM_STR, null, 'createNamedParameter-2'],
+ ]));
+
+ $queryBuilder->expects($this->at(0))
+ ->method('select')
+ ->with(['id', 'backend_id', 'resource_id', 'email', 'displayname', 'group_restrictions'])
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->expects($this->at(1))
+ ->method('from')
+ ->with($this->expectedDbTable)
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->expects($this->at(4))
+ ->method('where')
+ ->with('WHERE_CLAUSE_1')
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->expects($this->at(7))
+ ->method('andWhere')
+ ->with('WHERE_CLAUSE_2')
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->expects($this->at(8))
+ ->method('execute')
+ ->with()
+ ->will($this->returnValue($stmt));
+
+ $stmt->expects($this->at(0))
+ ->method('fetch')
+ ->with(\PDO::FETCH_ASSOC)
+ ->will($this->returnValue(null));
+
+ $actual = $this->principalBackend->findByUri('principal:' . $this->principalPrefix . '/db-123', $this->principalPrefix);
+ $this->assertEquals(null, $actual);
+ }
+
+ public function testFindByUriByUnknownUri() {
+ $user = $this->createMock(IUser::class);
+ $this->userSession->expects($this->once())
+ ->method('getUser')
+ ->with()
+ ->will($this->returnValue($user));
+ $this->groupManager->expects($this->once())
+ ->method('getUserGroupIds')
+ ->with($user)
+ ->will($this->returnValue(['group1', 'group2']));
+
+ $actual = $this->principalBackend->findByUri('foobar:blub', $this->principalPrefix);
+ $this->assertEquals(null, $actual);
+ }
+
+}
diff --git a/apps/dav/tests/unit/CalDAV/ResourceBooking/ResourcePrincipalBackendTest.php b/apps/dav/tests/unit/CalDAV/ResourceBooking/ResourcePrincipalBackendTest.php
new file mode 100644
index 00000000000..90db4bb4b7b
--- /dev/null
+++ b/apps/dav/tests/unit/CalDAV/ResourceBooking/ResourcePrincipalBackendTest.php
@@ -0,0 +1,35 @@
+<?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\Tests\unit\CalDAV\ResourceBooking;
+
+use OCA\DAV\CalDAV\ResourceBooking\ResourcePrincipalBackend;
+
+Class ResourcePrincipalBackendTest extends AbstractPrincipalBackendTest {
+ public function setUp() {
+ parent::setUp();
+
+ $this->principalBackend = new ResourcePrincipalBackend($this->dbConnection,
+ $this->userSession, $this->groupManager, $this->logger);
+ $this->expectedDbTable = 'calendar_resources';
+ $this->principalPrefix = 'principals/calendar-resources';
+ }
+}
diff --git a/apps/dav/tests/unit/CalDAV/ResourceBooking/RoomPrincipalBackendTest.php b/apps/dav/tests/unit/CalDAV/ResourceBooking/RoomPrincipalBackendTest.php
new file mode 100644
index 00000000000..b55c6ddceb6
--- /dev/null
+++ b/apps/dav/tests/unit/CalDAV/ResourceBooking/RoomPrincipalBackendTest.php
@@ -0,0 +1,35 @@
+<?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\Tests\unit\CalDAV\ResourceBooking;
+
+use OCA\DAV\CalDAV\ResourceBooking\RoomPrincipalBackend;
+
+Class RoomPrincipalBackendTest extends AbstractPrincipalBackendTest {
+ public function setUp() {
+ parent::setUp();
+
+ $this->principalBackend = new RoomPrincipalBackend($this->dbConnection,
+ $this->userSession, $this->groupManager, $this->logger);
+ $this->expectedDbTable = 'calendar_rooms';
+ $this->principalPrefix = 'principals/calendar-rooms';
+ }
+}
diff --git a/apps/dav/tests/unit/CalDAV/Schedule/IMipPluginTest.php b/apps/dav/tests/unit/CalDAV/Schedule/IMipPluginTest.php
index 3f89002ab98..c95b32041e0 100644
--- a/apps/dav/tests/unit/CalDAV/Schedule/IMipPluginTest.php
+++ b/apps/dav/tests/unit/CalDAV/Schedule/IMipPluginTest.php
@@ -29,8 +29,10 @@ namespace OCA\DAV\Tests\unit\CalDAV\Schedule;
use OC\Mail\Mailer;
use OCA\DAV\CalDAV\Schedule\IMipPlugin;
use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Defaults;
use OCP\IConfig;
+use OCP\IDBConnection;
use OCP\IL10N;
use OCP\ILogger;
use OCP\IURLGenerator;
@@ -39,6 +41,7 @@ use OCP\Mail\IAttachment;
use OCP\Mail\IEMailTemplate;
use OCP\Mail\IMailer;
use OCP\Mail\IMessage;
+use OCP\Security\ISecureRandom;
use Sabre\VObject\Component\VCalendar;
use Sabre\VObject\ITip\Message;
use Test\TestCase;
@@ -70,13 +73,38 @@ class IMipPluginTest extends TestCase {
$l10nFactory->method('get')->willReturn($l10n);
/** @var IURLGenerator | \PHPUnit_Framework_MockObject_MockObject $urlGenerator */
$urlGenerator = $this->createMock(IURLGenerator::class);
+ /** @var IDBConnection | \PHPUnit_Framework_MockObject_MockObject $db */
+ $db = $this->createMock(IDBConnection::class);
+ /** @var ISecureRandom | \PHPUnit_Framework_MockObject_MockObject $random */
+ $random = $this->createMock(ISecureRandom::class);
/** @var Defaults | \PHPUnit_Framework_MockObject_MockObject $defaults */
$defaults = $this->createMock(Defaults::class);
$defaults->expects($this->once())
->method('getName')
->will($this->returnValue('Instance Name 123'));
- $plugin = new IMipPlugin($config, $mailer, $logger, $timeFactory, $l10nFactory, $urlGenerator, $defaults, 'user123');
+ $random->expects($this->once())
+ ->method('generate')
+ ->with(60, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789')
+ ->will($this->returnValue('random_token'));
+
+ $queryBuilder = $this->createMock(IQueryBuilder::class);
+
+ $db->expects($this->once())
+ ->method('getQueryBuilder')
+ ->with()
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->expects($this->at(0))
+ ->method('insert')
+ ->with('calendar_invitations')
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->expects($this->at(8))
+ ->method('values')
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->expects($this->at(9))
+ ->method('execute');
+
+ $plugin = new IMipPlugin($config, $mailer, $logger, $timeFactory, $l10nFactory, $urlGenerator, $defaults, $random, $db, 'user123');
$message = new Message();
$message->method = 'REQUEST';
$message->message = new VCalendar();
@@ -128,10 +156,35 @@ class IMipPluginTest extends TestCase {
$l10nFactory->method('get')->willReturn($l10n);
/** @var IURLGenerator | \PHPUnit_Framework_MockObject_MockObject $urlGenerator */
$urlGenerator = $this->createMock(IURLGenerator::class);
+ /** @var IDBConnection | \PHPUnit_Framework_MockObject_MockObject $db */
+ $db = $this->createMock(IDBConnection::class);
+ /** @var ISecureRandom | \PHPUnit_Framework_MockObject_MockObject $random */
+ $random = $this->createMock(ISecureRandom::class);
/** @var Defaults | \PHPUnit_Framework_MockObject_MockObject $defaults */
$defaults = $this->createMock(Defaults::class);
- $plugin = new IMipPlugin($config, $mailer, $logger, $timeFactory, $l10nFactory, $urlGenerator, $defaults, 'user123');
+ $random->expects($this->once())
+ ->method('generate')
+ ->with(60, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789')
+ ->will($this->returnValue('random_token'));
+
+ $queryBuilder = $this->createMock(IQueryBuilder::class);
+
+ $db->expects($this->once())
+ ->method('getQueryBuilder')
+ ->with()
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->expects($this->at(0))
+ ->method('insert')
+ ->with('calendar_invitations')
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->expects($this->at(8))
+ ->method('values')
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->expects($this->at(9))
+ ->method('execute');
+
+ $plugin = new IMipPlugin($config, $mailer, $logger, $timeFactory, $l10nFactory, $urlGenerator, $defaults, $random, $db, 'user123');
$message = new Message();
$message->method = 'REQUEST';
$message->message = new VCalendar();
@@ -190,10 +243,37 @@ class IMipPluginTest extends TestCase {
$l10nFactory->method('get')->willReturn($l10n);
/** @var IURLGenerator | \PHPUnit_Framework_MockObject_MockObject $urlGenerator */
$urlGenerator = $this->createMock(IURLGenerator::class);
+ /** @var IDBConnection | \PHPUnit_Framework_MockObject_MockObject $db */
+ $db = $this->createMock(IDBConnection::class);
+ /** @var ISecureRandom | \PHPUnit_Framework_MockObject_MockObject $random */
+ $random = $this->createMock(ISecureRandom::class);
/** @var Defaults | \PHPUnit_Framework_MockObject_MockObject $defaults */
$defaults = $this->createMock(Defaults::class);
- $plugin = new IMipPlugin($config, $mailer, $logger, $timeFactory, $l10nFactory, $urlGenerator, $defaults, 'user123');
+ if ($expectsMail) {
+ $random->expects($this->once())
+ ->method('generate')
+ ->with(60, 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789')
+ ->will($this->returnValue('random_token'));
+
+ $queryBuilder = $this->createMock(IQueryBuilder::class);
+
+ $db->expects($this->once())
+ ->method('getQueryBuilder')
+ ->with()
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->expects($this->at(0))
+ ->method('insert')
+ ->with('calendar_invitations')
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->expects($this->at(8))
+ ->method('values')
+ ->will($this->returnValue($queryBuilder));
+ $queryBuilder->expects($this->at(9))
+ ->method('execute');
+ }
+
+ $plugin = new IMipPlugin($config, $mailer, $logger, $timeFactory, $l10nFactory, $urlGenerator, $defaults, $random, $db, 'user123');
$message = new Message();
$message->method = 'REQUEST';
$message->message = new VCalendar();
diff --git a/apps/dav/tests/unit/CardDAV/CardDavBackendTest.php b/apps/dav/tests/unit/CardDAV/CardDavBackendTest.php
index 63e090873bb..816ba670990 100644
--- a/apps/dav/tests/unit/CardDAV/CardDavBackendTest.php
+++ b/apps/dav/tests/unit/CardDAV/CardDavBackendTest.php
@@ -36,10 +36,13 @@ use OCA\DAV\CardDAV\AddressBook;
use OCA\DAV\CardDAV\CardDavBackend;
use OCA\DAV\Connector\Sabre\Principal;
use OCP\DB\QueryBuilder\IQueryBuilder;
+use OCP\IConfig;
use OCP\IDBConnection;
use OCP\IGroupManager;
use OCP\IL10N;
use OCP\IUserManager;
+use OCP\IUserSession;
+use OCP\Share\IManager as ShareManager;
use Sabre\DAV\PropPatch;
use Sabre\VObject\Component\VCard;
use Sabre\VObject\Property\Text;
@@ -90,7 +93,13 @@ class CardDavBackendTest extends TestCase {
$this->userManager = $this->createMock(IUserManager::class);
$this->groupManager = $this->createMock(IGroupManager::class);
$this->principal = $this->getMockBuilder(Principal::class)
- ->disableOriginalConstructor()
+ ->setConstructorArgs([
+ $this->userManager,
+ $this->groupManager,
+ $this->createMock(ShareManager::class),
+ $this->createMock(IUserSession::class),
+ $this->createMock(IConfig::class),
+ ])
->setMethods(['getPrincipalByPath', 'getGroupMembership'])
->getMock();
$this->principal->method('getPrincipalByPath')
diff --git a/apps/dav/tests/unit/Command/RemoveInvalidSharesTest.php b/apps/dav/tests/unit/Command/RemoveInvalidSharesTest.php
new file mode 100644
index 00000000000..2574e4d0aec
--- /dev/null
+++ b/apps/dav/tests/unit/Command/RemoveInvalidSharesTest.php
@@ -0,0 +1,71 @@
+<?php
+/**
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2018, ownCloud GmbH
+ * @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\Command;
+
+
+use OCA\DAV\Connector\Sabre\Principal;
+use OCA\DAV\Command\RemoveInvalidShares;
+use OCP\Migration\IOutput;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+use Test\TestCase;
+
+/**
+ * Class RemoveInvalidSharesTest
+ *
+ * @package OCA\DAV\Tests\Unit\Repair
+ * @group DB
+ */
+class RemoveInvalidSharesTest extends TestCase {
+
+ public function setUp() {
+ parent::setUp();
+ $db = \OC::$server->getDatabaseConnection();
+
+ $db->insertIfNotExist('*PREFIX*dav_shares', [
+ 'principaluri' => 'principal:unknown',
+ 'type' => 'calendar',
+ 'access' => 2,
+ 'resourceid' => 666,
+ ]);
+ }
+
+ public function test() {
+ $db = \OC::$server->getDatabaseConnection();
+ /** @var Principal | \PHPUnit_Framework_MockObject_MockObject $principal */
+ $principal = $this->createMock(Principal::class);
+
+ /** @var IOutput | \PHPUnit_Framework_MockObject_MockObject $output */
+ $output = $this->createMock(IOutput::class);
+
+ $repair = new RemoveInvalidShares($db, $principal);
+ $this->invokePrivate($repair, 'run', [$this->createMock(InputInterface::class), $this->createMock(OutputInterface::class)]);
+
+ $query = $db->getQueryBuilder();
+ $result = $query->select('*')->from('dav_shares')
+ ->where($query->expr()->eq('principaluri', $query->createNamedParameter('principal:unknown')))->execute();
+ $data = $result->fetchAll();
+ $result->closeCursor();
+ $this->assertEquals(0, count($data));
+ }
+}
diff --git a/apps/dav/tests/unit/Connector/Sabre/NodeTest.php b/apps/dav/tests/unit/Connector/Sabre/NodeTest.php
index b46c731d3dc..2b84d8475fc 100644
--- a/apps/dav/tests/unit/Connector/Sabre/NodeTest.php
+++ b/apps/dav/tests/unit/Connector/Sabre/NodeTest.php
@@ -151,12 +151,13 @@ class NodeTest extends \Test\TestCase {
$info = $this->getMockBuilder(FileInfo::class)
->disableOriginalConstructor()
- ->setMethods(['getStorage', 'getType', 'getMountPoint'])
+ ->setMethods(['getStorage', 'getType', 'getMountPoint', 'getPermissions'])
->getMock();
$info->method('getStorage')->willReturn($storage);
$info->method('getType')->willReturn($type);
$info->method('getMountPoint')->willReturn($mountpoint);
+ $info->method('getPermissions')->willReturn($permissions);
$view = $this->getMockBuilder(View::class)
->disableOriginalConstructor()
diff --git a/apps/dav/tests/unit/Connector/Sabre/PrincipalTest.php b/apps/dav/tests/unit/Connector/Sabre/PrincipalTest.php
index 7b9929bc4f3..7e82c446760 100644
--- a/apps/dav/tests/unit/Connector/Sabre/PrincipalTest.php
+++ b/apps/dav/tests/unit/Connector/Sabre/PrincipalTest.php
@@ -27,6 +27,7 @@
namespace OCA\DAV\Tests\unit\Connector\Sabre;
use OC\User\User;
+use OCP\IConfig;
use OCP\IGroup;
use OCP\IGroupManager;
use OCP\IUser;
@@ -47,18 +48,22 @@ class PrincipalTest extends TestCase {
private $shareManager;
/** @var IUserSession | \PHPUnit_Framework_MockObject_MockObject */
private $userSession;
+ /** @var IConfig | \PHPUnit_Framework_MockObject_MockObject */
+ private $config;
public function setUp() {
$this->userManager = $this->createMock(IUserManager::class);
$this->groupManager = $this->createMock(IGroupManager::class);
$this->shareManager = $this->createMock(IManager::class);
$this->userSession = $this->createMock(IUserSession::class);
+ $this->config = $this->createMock(IConfig::class);
$this->connector = new \OCA\DAV\Connector\Sabre\Principal(
$this->userManager,
$this->groupManager,
$this->shareManager,
- $this->userSession);
+ $this->userSession,
+ $this->config);
parent::setUp();
}
@@ -278,26 +283,37 @@ class PrincipalTest extends TestCase {
/**
* @dataProvider searchPrincipalsDataProvider
*/
- public function testSearchPrincipals($sharingEnabled, $groupsOnly, $result) {
+ public function testSearchPrincipals($disableFreeBusy, $sharingEnabled, $disableFBSharingCombination, $groupsOnly, $result) {
$this->shareManager->expects($this->once())
->method('shareAPIEnabled')
->will($this->returnValue($sharingEnabled));
-
- if ($sharingEnabled) {
- $this->shareManager->expects($this->once())
- ->method('shareWithGroupMembersOnly')
- ->will($this->returnValue($groupsOnly));
-
- if ($groupsOnly) {
- $user = $this->createMock(IUser::class);
- $this->userSession->expects($this->once())
- ->method('getUser')
- ->will($this->returnValue($user));
-
- $this->groupManager->expects($this->at(0))
- ->method('getUserGroupIds')
- ->with($user)
- ->will($this->returnValue(['group1', 'group2']));
+ $this->config->expects($this->once())
+ ->method('getAppValue')
+ ->with('dav', 'disableFreeBusy', $sharingEnabled ? 'no' : 'yes')
+ ->will($this->returnValue($disableFBSharingCombination));
+
+ if ($disableFreeBusy === 'no') {
+ if ($sharingEnabled) {
+ $this->shareManager->expects($this->once())
+ ->method('shareWithGroupMembersOnly')
+ ->will($this->returnValue($groupsOnly));
+
+ if ($groupsOnly) {
+ $user = $this->createMock(IUser::class);
+ $this->userSession->expects($this->once())
+ ->method('getUser')
+ ->will($this->returnValue($user));
+
+ $this->groupManager->expects($this->at(0))
+ ->method('getUserGroupIds')
+ ->with($user)
+ ->will($this->returnValue(['group1', 'group2']));
+ }
+ } else {
+ $this->shareManager->expects($this->never())
+ ->method('shareWithGroupMembersOnly');
+ $this->groupManager->expects($this->never())
+ ->method($this->anything());
}
} else {
$this->shareManager->expects($this->never())
@@ -306,27 +322,43 @@ class PrincipalTest extends TestCase {
->method($this->anything());
}
+
$user2 = $this->createMock(IUser::class);
$user2->method('getUID')->will($this->returnValue('user2'));
$user3 = $this->createMock(IUser::class);
$user3->method('getUID')->will($this->returnValue('user3'));
- if ($sharingEnabled) {
- $this->userManager->expects($this->at(0))
- ->method('getByEmail')
- ->with('user')
- ->will($this->returnValue([$user2, $user3]));
+ if ($disableFreeBusy === 'no') {
+ if ($sharingEnabled) {
+ $this->userManager->expects($this->at(0))
+ ->method('getByEmail')
+ ->with('user')
+ ->will($this->returnValue([$user2, $user3]));
+ } else {
+ $this->userManager->expects($this->never())
+ ->method('getByEmail');
+ }
+ } else {
+ $this->userManager->expects($this->never())
+ ->method('getByEmail');
}
- if ($sharingEnabled && $groupsOnly) {
- $this->groupManager->expects($this->at(1))
- ->method('getUserGroupIds')
- ->with($user2)
- ->will($this->returnValue(['group1', 'group3']));
- $this->groupManager->expects($this->at(2))
- ->method('getUserGroupIds')
- ->with($user3)
- ->will($this->returnValue(['group3', 'group4']));
+ if ($disableFreeBusy === 'no') {
+ if ($sharingEnabled && $groupsOnly) {
+ $this->groupManager->expects($this->at(1))
+ ->method('getUserGroupIds')
+ ->with($user2)
+ ->will($this->returnValue(['group1', 'group3']));
+ $this->groupManager->expects($this->at(2))
+ ->method('getUserGroupIds')
+ ->with($user3)
+ ->will($this->returnValue(['group3', 'group4']));
+ }
+ } else {
+ $this->groupManager->expects($this->never())
+ ->method('getUserGroupIds');
+ $this->groupManager->expects($this->never())
+ ->method('getUserGroupIds');
}
$this->assertEquals($result, $this->connector->searchPrincipals('principals/users',
@@ -335,9 +367,12 @@ class PrincipalTest extends TestCase {
public function searchPrincipalsDataProvider() {
return [
- [true, false, ['principals/users/user2', 'principals/users/user3']],
- [true, true, ['principals/users/user2']],
- [false, false, []],
+ ['yes', true, 'yes', false, []],
+ ['no', true, 'no', false, ['principals/users/user2', 'principals/users/user3']],
+ ['yes', true, 'yes', true, []],
+ ['no', true, 'no', true, ['principals/users/user2']],
+ ['yes', false, 'yes', false, []],
+ ['no', false, 'yes', false, []],
];
}
@@ -345,6 +380,10 @@ class PrincipalTest extends TestCase {
$this->shareManager->expects($this->once())
->method('shareApiEnabled')
->will($this->returnValue(false));
+ $this->config->expects($this->once())
+ ->method('getAppValue')
+ ->with('dav', 'disableFreeBusy', 'yes')
+ ->will($this->returnValue('yes'));
$this->assertEquals(null, $this->connector->findByUri('mailto:user@foo.com', 'principals/users'));
}
@@ -352,45 +391,56 @@ class PrincipalTest extends TestCase {
/**
* @dataProvider findByUriWithGroupRestrictionDataProvider
*/
- public function testFindByUriWithGroupRestriction($uri, $email, $expects) {
+ public function testFindByUriWithGroupRestriction($disableFreeBusy, $uri, $email, $expects) {
$this->shareManager->expects($this->once())
->method('shareApiEnabled')
->will($this->returnValue(true));
+ $this->config->expects($this->once())
+ ->method('getAppValue')
+ ->with('dav', 'disableFreeBusy', 'no')
+ ->will($this->returnValue($disableFreeBusy));
- $this->shareManager->expects($this->once())
- ->method('shareWithGroupMembersOnly')
- ->will($this->returnValue(true));
+ if ($disableFreeBusy === 'yes') {
+ $this->shareManager->expects($this->never())
+ ->method('shareWithGroupMembersOnly');
+ $this->userSession->expects($this->never())
+ ->method('getUser');
+ } else {
+ $this->shareManager->expects($this->once())
+ ->method('shareWithGroupMembersOnly')
+ ->will($this->returnValue(true));
- $user = $this->createMock(IUser::class);
- $this->userSession->expects($this->once())
- ->method('getUser')
- ->will($this->returnValue($user));
+ $user = $this->createMock(IUser::class);
+ $this->userSession->expects($this->once())
+ ->method('getUser')
+ ->will($this->returnValue($user));
- $this->groupManager->expects($this->at(0))
- ->method('getUserGroupIds')
- ->with($user)
- ->will($this->returnValue(['group1', 'group2']));
+ $this->groupManager->expects($this->at(0))
+ ->method('getUserGroupIds')
+ ->with($user)
+ ->will($this->returnValue(['group1', 'group2']));
- $user2 = $this->createMock(IUser::class);
- $user2->method('getUID')->will($this->returnValue('user2'));
- $user3 = $this->createMock(IUser::class);
- $user3->method('getUID')->will($this->returnValue('user3'));
+ $user2 = $this->createMock(IUser::class);
+ $user2->method('getUID')->will($this->returnValue('user2'));
+ $user3 = $this->createMock(IUser::class);
+ $user3->method('getUID')->will($this->returnValue('user3'));
- $this->userManager->expects($this->once())
- ->method('getByEmail')
- ->with($email)
- ->will($this->returnValue([$email === 'user2@foo.bar' ? $user2 : $user3]));
+ $this->userManager->expects($this->once())
+ ->method('getByEmail')
+ ->with($email)
+ ->will($this->returnValue([$email === 'user2@foo.bar' ? $user2 : $user3]));
- if ($email === 'user2@foo.bar') {
- $this->groupManager->expects($this->at(1))
- ->method('getUserGroupIds')
- ->with($user2)
- ->will($this->returnValue(['group1', 'group3']));
- } else {
- $this->groupManager->expects($this->at(1))
- ->method('getUserGroupIds')
- ->with($user3)
- ->will($this->returnValue(['group3', 'group3']));
+ if ($email === 'user2@foo.bar') {
+ $this->groupManager->expects($this->at(1))
+ ->method('getUserGroupIds')
+ ->with($user2)
+ ->will($this->returnValue(['group1', 'group3']));
+ } else {
+ $this->groupManager->expects($this->at(1))
+ ->method('getUserGroupIds')
+ ->with($user3)
+ ->will($this->returnValue(['group3', 'group3']));
+ }
}
$this->assertEquals($expects, $this->connector->findByUri($uri, 'principals/users'));
@@ -398,40 +448,56 @@ class PrincipalTest extends TestCase {
public function findByUriWithGroupRestrictionDataProvider() {
return [
- ['mailto:user2@foo.bar', 'user2@foo.bar', 'principals/users/user2'],
- ['mailto:user3@foo.bar', 'user3@foo.bar', null],
+ ['yes', 'mailto:user2@foo.bar', 'user2@foo.bar', null],
+ ['no', 'mailto:user2@foo.bar', 'user2@foo.bar', 'principals/users/user2'],
+ ['yes', 'mailto:user3@foo.bar', 'user3@foo.bar', null],
+ ['no', 'mailto:user3@foo.bar', 'user3@foo.bar', null],
];
}
/**
* @dataProvider findByUriWithoutGroupRestrictionDataProvider
*/
- public function testFindByUriWithoutGroupRestriction($uri, $email, $expects) {
+ public function testFindByUriWithoutGroupRestriction($disableFreeBusy, $uri, $email, $expects) {
$this->shareManager->expects($this->once())
->method('shareApiEnabled')
->will($this->returnValue(true));
+ $this->config->expects($this->once())
+ ->method('getAppValue')
+ ->with('dav', 'disableFreeBusy', 'no')
+ ->will($this->returnValue($disableFreeBusy));
- $this->shareManager->expects($this->once())
- ->method('shareWithGroupMembersOnly')
- ->will($this->returnValue(false));
+ if ($disableFreeBusy === 'yes') {
+ $this->shareManager->expects($this->never())
+ ->method('shareWithGroupMembersOnly');
- $user2 = $this->createMock(IUser::class);
- $user2->method('getUID')->will($this->returnValue('user2'));
- $user3 = $this->createMock(IUser::class);
- $user3->method('getUID')->will($this->returnValue('user3'));
+ $this->userManager->expects($this->never())
+ ->method('getByEmail');
+ } else {
+ $this->shareManager->expects($this->once())
+ ->method('shareWithGroupMembersOnly')
+ ->will($this->returnValue(false));
- $this->userManager->expects($this->once())
- ->method('getByEmail')
- ->with($email)
- ->will($this->returnValue([$email === 'user2@foo.bar' ? $user2 : $user3]));
+ $user2 = $this->createMock(IUser::class);
+ $user2->method('getUID')->will($this->returnValue('user2'));
+ $user3 = $this->createMock(IUser::class);
+ $user3->method('getUID')->will($this->returnValue('user3'));
+
+ $this->userManager->expects($this->once())
+ ->method('getByEmail')
+ ->with($email)
+ ->will($this->returnValue([$email === 'user2@foo.bar' ? $user2 : $user3]));
+ }
$this->assertEquals($expects, $this->connector->findByUri($uri, 'principals/users'));
}
public function findByUriWithoutGroupRestrictionDataProvider() {
return [
- ['mailto:user2@foo.bar', 'user2@foo.bar', 'principals/users/user2'],
- ['mailto:user3@foo.bar', 'user3@foo.bar', 'principals/users/user3'],
+ ['yes', 'mailto:user2@foo.bar', 'user2@foo.bar', null],
+ ['yes', 'mailto:user3@foo.bar', 'user3@foo.bar', null],
+ ['no', 'mailto:user2@foo.bar', 'user2@foo.bar', 'principals/users/user2'],
+ ['no', 'mailto:user3@foo.bar', 'user3@foo.bar', 'principals/users/user3'],
];
}
}
diff --git a/apps/dav/tests/unit/Controller/InvitationResponseControllerTest.php b/apps/dav/tests/unit/Controller/InvitationResponseControllerTest.php
new file mode 100644
index 00000000000..7efb64e3dd4
--- /dev/null
+++ b/apps/dav/tests/unit/Controller/InvitationResponseControllerTest.php
@@ -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_invitations')
+ ->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));
+ }
+}
diff --git a/apps/dav/tests/unit/DAV/AnonymousOptionsTest.php b/apps/dav/tests/unit/DAV/AnonymousOptionsTest.php
new file mode 100644
index 00000000000..4e440e6644b
--- /dev/null
+++ b/apps/dav/tests/unit/DAV/AnonymousOptionsTest.php
@@ -0,0 +1,71 @@
+<?php
+/**
+ * @copyright Copyright (c) 2018 Robin Appelman <robin@icewind.nl>
+ *
+ * @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;
+
+use OCA\DAV\Connector\Sabre\AnonymousOptionsPlugin;
+use Sabre\DAV\Auth\Backend\BasicCallBack;
+use Sabre\DAV\Auth\Plugin;
+use Sabre\DAV\Server;
+use Sabre\HTTP\ResponseInterface;
+use Sabre\HTTP\Sapi;
+use Test\TestCase;
+
+class AnonymousOptionsTest extends TestCase {
+ private function sendRequest($method, $path) {
+ $server = new Server();
+ $server->addPlugin(new AnonymousOptionsPlugin());
+ $server->addPlugin(new Plugin(new BasicCallBack(function() {
+ return false;
+ })));
+
+ $server->httpRequest->setMethod($method);
+ $server->httpRequest->setUrl($path);
+
+ $server->sapi = new SapiMock();
+ $server->exec();
+ return $server->httpResponse;
+ }
+
+ public function testAnonymousOptionsRoot() {
+ $response = $this->sendRequest('OPTIONS', '');
+
+ $this->assertEquals(200, $response->getStatus());
+ }
+
+ public function testAnonymousOptionsNonRoot() {
+ $response = $this->sendRequest('OPTIONS', 'foo');
+
+ $this->assertEquals(401, $response->getStatus());
+ }
+}
+
+class SapiMock extends Sapi {
+ /**
+ * Overriding this so nothing is ever echo'd.
+ *
+ * @return void
+ */
+ static function sendResponse(ResponseInterface $response) {
+
+ }
+
+}
diff --git a/apps/dav/tests/unit/ServerTest.php b/apps/dav/tests/unit/ServerTest.php
index 58c77c1b0ec..986899a2107 100644
--- a/apps/dav/tests/unit/ServerTest.php
+++ b/apps/dav/tests/unit/ServerTest.php
@@ -38,12 +38,24 @@ use OCA\DAV\AppInfo\PluginManager;
*/
class ServerTest extends \Test\TestCase {
- public function test() {
- /** @var IRequest $r */
+ /**
+ * @dataProvider providesUris
+ */
+ public function test($uri, array $plugins) {
+ /** @var IRequest | \PHPUnit_Framework_MockObject_MockObject $r */
$r = $this->createMock(IRequest::class);
- $r->method('getRequestUri')
- ->willReturn('/');
+ $r->expects($this->any())->method('getRequestUri')->willReturn($uri);
$s = new Server($r, '/');
- $this->assertInstanceOf('OCA\DAV\Server', $s);
+ $this->assertNotNull($s->server);
+ foreach ($plugins as $plugin) {
+ $this->assertNotNull($s->server->getPlugin($plugin));
+ }
+ }
+ public function providesUris() {
+ return [
+ 'principals' => ['principals/users/admin', ['caldav', 'oc-resource-sharing', 'carddav']],
+ 'calendars' => ['calendars/admin', ['caldav', 'oc-resource-sharing']],
+ 'addressbooks' => ['addressbooks/admin', ['carddav', 'oc-resource-sharing']],
+ ];
}
}
diff --git a/apps/dav/tests/unit/Settings/CalDAVSettingsTest.php b/apps/dav/tests/unit/Settings/CalDAVSettingsTest.php
index 36e2aaa9ebb..a9df63a03ab 100644
--- a/apps/dav/tests/unit/Settings/CalDAVSettingsTest.php
+++ b/apps/dav/tests/unit/Settings/CalDAVSettingsTest.php
@@ -49,10 +49,10 @@ class CalDAVSettingsTest extends TestCase {
}
public function testGetSection() {
- $this->assertEquals('server', $this->settings->getSection());
+ $this->assertEquals('groupware', $this->settings->getSection());
}
public function testGetPriority() {
- $this->assertEquals(20, $this->settings->getPriority());
+ $this->assertEquals(10, $this->settings->getPriority());
}
}