Plugin.php 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016, Roeland Jago Douma <roeland@famdouma.nl>
  4. * @copyright Copyright (c) 2016, Joas Schilling <coding@schilljs.com>
  5. *
  6. * @author Joas Schilling <coding@schilljs.com>
  7. * @author Roeland Jago Douma <roeland@famdouma.nl>
  8. *
  9. * @license GNU AGPL version 3 or any later version
  10. *
  11. * This program is free software: you can redistribute it and/or modify
  12. * it under the terms of the GNU Affero General Public License as
  13. * published by the Free Software Foundation, either version 3 of the
  14. * License, or (at your option) any later version.
  15. *
  16. * This program is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. * GNU Affero General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU Affero General Public License
  22. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  23. *
  24. */
  25. namespace OCA\DAV\CalDAV\Schedule;
  26. use OCA\DAV\CalDAV\CalDavBackend;
  27. use OCA\DAV\CalDAV\CalendarHome;
  28. use Sabre\DAV\INode;
  29. use Sabre\DAV\PropFind;
  30. use Sabre\DAV\Server;
  31. use Sabre\DAV\Xml\Property\LocalHref;
  32. use Sabre\DAVACL\IPrincipal;
  33. use Sabre\HTTP\RequestInterface;
  34. use Sabre\HTTP\ResponseInterface;
  35. use Sabre\VObject\Component\VCalendar;
  36. use Sabre\VObject\Reader;
  37. class Plugin extends \Sabre\CalDAV\Schedule\Plugin {
  38. /**
  39. * Initializes the plugin
  40. *
  41. * @param Server $server
  42. * @return void
  43. */
  44. function initialize(Server $server) {
  45. parent::initialize($server);
  46. $server->on('propFind', [$this, 'propFindDefaultCalendarUrl'], 90);
  47. }
  48. /**
  49. * This method handler is invoked during fetching of properties.
  50. *
  51. * We use this event to add calendar-auto-schedule-specific properties.
  52. *
  53. * @param PropFind $propFind
  54. * @param INode $node
  55. * @return void
  56. */
  57. function propFind(PropFind $propFind, INode $node) {
  58. // overwrite Sabre/Dav's implementation
  59. $propFind->handle('{' . self::NS_CALDAV . '}calendar-user-type', function() use ($node) {
  60. $calendarUserType = '{' . self::NS_CALDAV . '}calendar-user-type';
  61. $props = $node->getProperties([$calendarUserType]);
  62. if (isset($props[$calendarUserType])) {
  63. return $props[$calendarUserType];
  64. }
  65. return 'INDIVIDUAL';
  66. });
  67. parent::propFind($propFind, $node);
  68. }
  69. /**
  70. * Returns a list of addresses that are associated with a principal.
  71. *
  72. * @param string $principal
  73. * @return array
  74. */
  75. protected function getAddressesForPrincipal($principal) {
  76. $result = parent::getAddressesForPrincipal($principal);
  77. if ($result === null) {
  78. $result = [];
  79. }
  80. return $result;
  81. }
  82. /**
  83. * Always use the personal calendar as target for scheduled events
  84. *
  85. * @param PropFind $propFind
  86. * @param INode $node
  87. * @return void
  88. */
  89. function propFindDefaultCalendarUrl(PropFind $propFind, INode $node) {
  90. if ($node instanceof IPrincipal) {
  91. $propFind->handle('{' . self::NS_CALDAV . '}schedule-default-calendar-URL', function() use ($node) {
  92. /** @var \OCA\DAV\CalDAV\Plugin $caldavPlugin */
  93. $caldavPlugin = $this->server->getPlugin('caldav');
  94. $principalUrl = $node->getPrincipalUrl();
  95. $calendarHomePath = $caldavPlugin->getCalendarHomeForPrincipal($principalUrl);
  96. if (!$calendarHomePath) {
  97. return null;
  98. }
  99. if (strpos($principalUrl, 'principals/users') === 0) {
  100. $uri = CalDavBackend::PERSONAL_CALENDAR_URI;
  101. $displayname = CalDavBackend::PERSONAL_CALENDAR_NAME;
  102. } elseif (strpos($principalUrl, 'principals/calendar-resources') === 0 ||
  103. strpos($principalUrl, 'principals/calendar-rooms') === 0) {
  104. $uri = CalDavBackend::RESOURCE_BOOKING_CALENDAR_URI;
  105. $displayname = CalDavBackend::RESOURCE_BOOKING_CALENDAR_NAME;
  106. } else {
  107. // How did we end up here?
  108. // TODO - throw exception or just ignore?
  109. return null;
  110. }
  111. /** @var CalendarHome $calendarHome */
  112. $calendarHome = $this->server->tree->getNodeForPath($calendarHomePath);
  113. if (!$calendarHome->childExists($uri)) {
  114. $calendarHome->getCalDAVBackend()->createCalendar($principalUrl, $uri, [
  115. '{DAV:}displayname' => $displayname,
  116. ]);
  117. }
  118. $result = $this->server->getPropertiesForPath($calendarHomePath . '/' . $uri, [], 1);
  119. if (empty($result)) {
  120. return null;
  121. }
  122. return new LocalHref($result[0]['href']);
  123. });
  124. }
  125. }
  126. /**
  127. * This method is triggered whenever there was a calendar object gets
  128. * created or updated.
  129. *
  130. * Basically just a copy of parent::calendarObjectChange, with the change
  131. * from:
  132. * $addresses = $this->getAddressesForPrincipal($calendarNode->getOwner());
  133. * to:
  134. * $addresses = $this->getAddressesForPrincipal($calendarNode->getPrincipalURI());
  135. *
  136. * @param RequestInterface $request HTTP request
  137. * @param ResponseInterface $response HTTP Response
  138. * @param VCalendar $vCal Parsed iCalendar object
  139. * @param mixed $calendarPath Path to calendar collection
  140. * @param mixed $modified The iCalendar object has been touched.
  141. * @param mixed $isNew Whether this was a new item or we're updating one
  142. * @return void
  143. */
  144. function calendarObjectChange(RequestInterface $request, ResponseInterface $response, VCalendar $vCal, $calendarPath, &$modified, $isNew) {
  145. if (!$this->scheduleReply($this->server->httpRequest)) {
  146. return;
  147. }
  148. $calendarNode = $this->server->tree->getNodeForPath($calendarPath);
  149. $addresses = $this->getAddressesForPrincipal(
  150. $calendarNode->getPrincipalURI()
  151. );
  152. if (!$isNew) {
  153. $node = $this->server->tree->getNodeForPath($request->getPath());
  154. $oldObj = Reader::read($node->get());
  155. } else {
  156. $oldObj = null;
  157. }
  158. $this->processICalendarChange($oldObj, $vCal, $addresses, [], $modified);
  159. if ($oldObj) {
  160. // Destroy circular references so PHP will GC the object.
  161. $oldObj->destroy();
  162. }
  163. }
  164. /**
  165. * This method checks the 'Schedule-Reply' header
  166. * and returns false if it's 'F', otherwise true.
  167. *
  168. * Copied from Sabre/DAV's Schedule plugin, because it's
  169. * private for whatever reason
  170. *
  171. * @param RequestInterface $request
  172. * @return bool
  173. */
  174. private function scheduleReply(RequestInterface $request) {
  175. $scheduleReply = $request->getHeader('Schedule-Reply');
  176. return $scheduleReply !== 'F';
  177. }
  178. }