CalendarSearchReport.php 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. <?php
  2. /**
  3. * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
  4. * SPDX-License-Identifier: AGPL-3.0-or-later
  5. */
  6. namespace OCA\DAV\CalDAV\Search\Xml\Request;
  7. use OCA\DAV\CalDAV\Search\SearchPlugin;
  8. use Sabre\DAV\Exception\BadRequest;
  9. use Sabre\Xml\Reader;
  10. use Sabre\Xml\XmlDeserializable;
  11. /**
  12. * CalendarSearchReport request parser.
  13. *
  14. * This class parses the {urn:ietf:params:xml:ns:caldav}calendar-query
  15. * REPORT, as defined in:
  16. *
  17. * https:// link to standard
  18. */
  19. class CalendarSearchReport implements XmlDeserializable {
  20. /**
  21. * An array with requested properties.
  22. *
  23. * @var array
  24. */
  25. public $properties;
  26. /**
  27. * List of property/component filters.
  28. *
  29. * @var array
  30. */
  31. public $filters;
  32. /**
  33. * @var int
  34. */
  35. public $limit;
  36. /**
  37. * @var int
  38. */
  39. public $offset;
  40. /**
  41. * The deserialize method is called during xml parsing.
  42. *
  43. * This method is called statically, this is because in theory this method
  44. * may be used as a type of constructor, or factory method.
  45. *
  46. * Often you want to return an instance of the current class, but you are
  47. * free to return other data as well.
  48. *
  49. * You are responsible for advancing the reader to the next element. Not
  50. * doing anything will result in a never-ending loop.
  51. *
  52. * If you just want to skip parsing for this element altogether, you can
  53. * just call $reader->next();
  54. *
  55. * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
  56. * the next element.
  57. *
  58. * @param Reader $reader
  59. * @return mixed
  60. */
  61. public static function xmlDeserialize(Reader $reader) {
  62. $elems = $reader->parseInnerTree([
  63. '{http://nextcloud.com/ns}comp-filter' => 'OCA\\DAV\\CalDAV\\Search\\Xml\\Filter\\CompFilter',
  64. '{http://nextcloud.com/ns}prop-filter' => 'OCA\\DAV\\CalDAV\\Search\\Xml\\Filter\\PropFilter',
  65. '{http://nextcloud.com/ns}param-filter' => 'OCA\\DAV\\CalDAV\\Search\\Xml\\Filter\\ParamFilter',
  66. '{http://nextcloud.com/ns}search-term' => 'OCA\\DAV\\CalDAV\\Search\\Xml\\Filter\\SearchTermFilter',
  67. '{http://nextcloud.com/ns}limit' => 'OCA\\DAV\\CalDAV\\Search\\Xml\\Filter\\LimitFilter',
  68. '{http://nextcloud.com/ns}offset' => 'OCA\\DAV\\CalDAV\\Search\\Xml\\Filter\\OffsetFilter',
  69. '{DAV:}prop' => 'Sabre\\Xml\\Element\\KeyValue',
  70. ]);
  71. $newProps = [
  72. 'filters' => [],
  73. 'properties' => [],
  74. 'limit' => null,
  75. 'offset' => null
  76. ];
  77. if (!is_array($elems)) {
  78. $elems = [];
  79. }
  80. foreach ($elems as $elem) {
  81. switch ($elem['name']) {
  82. case '{DAV:}prop':
  83. $newProps['properties'] = array_keys($elem['value']);
  84. break;
  85. case '{' . SearchPlugin::NS_Nextcloud . '}filter':
  86. foreach ($elem['value'] as $subElem) {
  87. if ($subElem['name'] === '{' . SearchPlugin::NS_Nextcloud . '}comp-filter') {
  88. if (!isset($newProps['filters']['comps']) || !is_array($newProps['filters']['comps'])) {
  89. $newProps['filters']['comps'] = [];
  90. }
  91. $newProps['filters']['comps'][] = $subElem['value'];
  92. } elseif ($subElem['name'] === '{' . SearchPlugin::NS_Nextcloud . '}prop-filter') {
  93. if (!isset($newProps['filters']['props']) || !is_array($newProps['filters']['props'])) {
  94. $newProps['filters']['props'] = [];
  95. }
  96. $newProps['filters']['props'][] = $subElem['value'];
  97. } elseif ($subElem['name'] === '{' . SearchPlugin::NS_Nextcloud . '}param-filter') {
  98. if (!isset($newProps['filters']['params']) || !is_array($newProps['filters']['params'])) {
  99. $newProps['filters']['params'] = [];
  100. }
  101. $newProps['filters']['params'][] = $subElem['value'];
  102. } elseif ($subElem['name'] === '{' . SearchPlugin::NS_Nextcloud . '}search-term') {
  103. $newProps['filters']['search-term'] = $subElem['value'];
  104. }
  105. }
  106. break;
  107. case '{' . SearchPlugin::NS_Nextcloud . '}limit':
  108. $newProps['limit'] = $elem['value'];
  109. break;
  110. case '{' . SearchPlugin::NS_Nextcloud . '}offset':
  111. $newProps['offset'] = $elem['value'];
  112. break;
  113. }
  114. }
  115. if (empty($newProps['filters'])) {
  116. throw new BadRequest('The {' . SearchPlugin::NS_Nextcloud . '}filter element is required for this request');
  117. }
  118. $propsOrParamsDefined = (!empty($newProps['filters']['props']) || !empty($newProps['filters']['params']));
  119. $noCompsDefined = empty($newProps['filters']['comps']);
  120. if ($propsOrParamsDefined && $noCompsDefined) {
  121. throw new BadRequest('{' . SearchPlugin::NS_Nextcloud . '}prop-filter or {' . SearchPlugin::NS_Nextcloud . '}param-filter given without any {' . SearchPlugin::NS_Nextcloud . '}comp-filter');
  122. }
  123. if (!isset($newProps['filters']['search-term'])) {
  124. throw new BadRequest('{' . SearchPlugin::NS_Nextcloud . '}search-term is required for this request');
  125. }
  126. if (empty($newProps['filters']['props']) && empty($newProps['filters']['params'])) {
  127. throw new BadRequest('At least one{' . SearchPlugin::NS_Nextcloud . '}prop-filter or {' . SearchPlugin::NS_Nextcloud . '}param-filter is required for this request');
  128. }
  129. $obj = new self();
  130. foreach ($newProps as $key => $value) {
  131. $obj->$key = $value;
  132. }
  133. return $obj;
  134. }
  135. }