SimplePDO.php 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. <?php
  2. declare(strict_types=1);
  3. namespace Sabre\CalDAV\Backend;
  4. use Sabre\CalDAV;
  5. use Sabre\DAV;
  6. /**
  7. * Simple PDO CalDAV backend.
  8. *
  9. * This class is basically the most minimum example to get a caldav backend up
  10. * and running. This class uses the following schema (MySQL example):
  11. *
  12. * CREATE TABLE simple_calendars (
  13. * id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
  14. * uri VARBINARY(200) NOT NULL,
  15. * principaluri VARBINARY(200) NOT NULL
  16. * );
  17. *
  18. * CREATE TABLE simple_calendarobjects (
  19. * id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
  20. * calendarid INT UNSIGNED NOT NULL,
  21. * uri VARBINARY(200) NOT NULL,
  22. * calendardata MEDIUMBLOB
  23. * )
  24. *
  25. * To make this class work, you absolutely need to have the PropertyStorage
  26. * plugin enabled.
  27. *
  28. * @copyright Copyright (C) 2007-2015 fruux GmbH (https://fruux.com/).
  29. * @author Evert Pot (http://evertpot.com/)
  30. * @license http://sabre.io/license/ Modified BSD License
  31. */
  32. class SimplePDO extends AbstractBackend
  33. {
  34. /**
  35. * pdo.
  36. *
  37. * @var \PDO
  38. */
  39. protected $pdo;
  40. /**
  41. * Creates the backend.
  42. */
  43. public function __construct(\PDO $pdo)
  44. {
  45. $this->pdo = $pdo;
  46. }
  47. /**
  48. * Returns a list of calendars for a principal.
  49. *
  50. * Every project is an array with the following keys:
  51. * * id, a unique id that will be used by other functions to modify the
  52. * calendar. This can be the same as the uri or a database key.
  53. * * uri. This is just the 'base uri' or 'filename' of the calendar.
  54. * * principaluri. The owner of the calendar. Almost always the same as
  55. * principalUri passed to this method.
  56. *
  57. * Furthermore it can contain webdav properties in clark notation. A very
  58. * common one is '{DAV:}displayname'.
  59. *
  60. * Many clients also require:
  61. * {urn:ietf:params:xml:ns:caldav}supported-calendar-component-set
  62. * For this property, you can just return an instance of
  63. * Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet.
  64. *
  65. * If you return {http://sabredav.org/ns}read-only and set the value to 1,
  66. * ACL will automatically be put in read-only mode.
  67. *
  68. * @param string $principalUri
  69. *
  70. * @return array
  71. */
  72. public function getCalendarsForUser($principalUri)
  73. {
  74. // Making fields a comma-delimited list
  75. $stmt = $this->pdo->prepare('SELECT id, uri FROM simple_calendars WHERE principaluri = ? ORDER BY id ASC');
  76. $stmt->execute([$principalUri]);
  77. $calendars = [];
  78. while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
  79. $calendars[] = [
  80. 'id' => $row['id'],
  81. 'uri' => $row['uri'],
  82. 'principaluri' => $principalUri,
  83. ];
  84. }
  85. return $calendars;
  86. }
  87. /**
  88. * Creates a new calendar for a principal.
  89. *
  90. * If the creation was a success, an id must be returned that can be used
  91. * to reference this calendar in other methods, such as updateCalendar.
  92. *
  93. * @param string $principalUri
  94. * @param string $calendarUri
  95. *
  96. * @return string
  97. */
  98. public function createCalendar($principalUri, $calendarUri, array $properties)
  99. {
  100. $stmt = $this->pdo->prepare('INSERT INTO simple_calendars (principaluri, uri) VALUES (?, ?)');
  101. $stmt->execute([$principalUri, $calendarUri]);
  102. return $this->pdo->lastInsertId();
  103. }
  104. /**
  105. * Delete a calendar and all it's objects.
  106. *
  107. * @param string $calendarId
  108. */
  109. public function deleteCalendar($calendarId)
  110. {
  111. $stmt = $this->pdo->prepare('DELETE FROM simple_calendarobjects WHERE calendarid = ?');
  112. $stmt->execute([$calendarId]);
  113. $stmt = $this->pdo->prepare('DELETE FROM simple_calendars WHERE id = ?');
  114. $stmt->execute([$calendarId]);
  115. }
  116. /**
  117. * Returns all calendar objects within a calendar.
  118. *
  119. * Every item contains an array with the following keys:
  120. * * calendardata - The iCalendar-compatible calendar data
  121. * * uri - a unique key which will be used to construct the uri. This can
  122. * be any arbitrary string, but making sure it ends with '.ics' is a
  123. * good idea. This is only the basename, or filename, not the full
  124. * path.
  125. * * lastmodified - a timestamp of the last modification time
  126. * * etag - An arbitrary string, surrounded by double-quotes. (e.g.:
  127. * ' "abcdef"')
  128. * * size - The size of the calendar objects, in bytes.
  129. * * component - optional, a string containing the type of object, such
  130. * as 'vevent' or 'vtodo'. If specified, this will be used to populate
  131. * the Content-Type header.
  132. *
  133. * Note that the etag is optional, but it's highly encouraged to return for
  134. * speed reasons.
  135. *
  136. * The calendardata is also optional. If it's not returned
  137. * 'getCalendarObject' will be called later, which *is* expected to return
  138. * calendardata.
  139. *
  140. * If neither etag or size are specified, the calendardata will be
  141. * used/fetched to determine these numbers. If both are specified the
  142. * amount of times this is needed is reduced by a great degree.
  143. *
  144. * @param string $calendarId
  145. *
  146. * @return array
  147. */
  148. public function getCalendarObjects($calendarId)
  149. {
  150. $stmt = $this->pdo->prepare('SELECT id, uri, calendardata FROM simple_calendarobjects WHERE calendarid = ?');
  151. $stmt->execute([$calendarId]);
  152. $result = [];
  153. foreach ($stmt->fetchAll(\PDO::FETCH_ASSOC) as $row) {
  154. $result[] = [
  155. 'id' => $row['id'],
  156. 'uri' => $row['uri'],
  157. 'etag' => '"'.md5($row['calendardata']).'"',
  158. 'calendarid' => $calendarId,
  159. 'size' => strlen($row['calendardata']),
  160. 'calendardata' => $row['calendardata'],
  161. ];
  162. }
  163. return $result;
  164. }
  165. /**
  166. * Returns information from a single calendar object, based on it's object
  167. * uri.
  168. *
  169. * The object uri is only the basename, or filename and not a full path.
  170. *
  171. * The returned array must have the same keys as getCalendarObjects. The
  172. * 'calendardata' object is required here though, while it's not required
  173. * for getCalendarObjects.
  174. *
  175. * This method must return null if the object did not exist.
  176. *
  177. * @param string $calendarId
  178. * @param string $objectUri
  179. *
  180. * @return array|null
  181. */
  182. public function getCalendarObject($calendarId, $objectUri)
  183. {
  184. $stmt = $this->pdo->prepare('SELECT id, uri, calendardata FROM simple_calendarobjects WHERE calendarid = ? AND uri = ?');
  185. $stmt->execute([$calendarId, $objectUri]);
  186. $row = $stmt->fetch(\PDO::FETCH_ASSOC);
  187. if (!$row) {
  188. return null;
  189. }
  190. return [
  191. 'id' => $row['id'],
  192. 'uri' => $row['uri'],
  193. 'etag' => '"'.md5($row['calendardata']).'"',
  194. 'calendarid' => $calendarId,
  195. 'size' => strlen($row['calendardata']),
  196. 'calendardata' => $row['calendardata'],
  197. ];
  198. }
  199. /**
  200. * Creates a new calendar object.
  201. *
  202. * The object uri is only the basename, or filename and not a full path.
  203. *
  204. * It is possible return an etag from this function, which will be used in
  205. * the response to this PUT request. Note that the ETag must be surrounded
  206. * by double-quotes.
  207. *
  208. * However, you should only really return this ETag if you don't mangle the
  209. * calendar-data. If the result of a subsequent GET to this object is not
  210. * the exact same as this request body, you should omit the ETag.
  211. *
  212. * @param mixed $calendarId
  213. * @param string $objectUri
  214. * @param string $calendarData
  215. *
  216. * @return string|null
  217. */
  218. public function createCalendarObject($calendarId, $objectUri, $calendarData)
  219. {
  220. $stmt = $this->pdo->prepare('INSERT INTO simple_calendarobjects (calendarid, uri, calendardata) VALUES (?,?,?)');
  221. $stmt->execute([
  222. $calendarId,
  223. $objectUri,
  224. $calendarData,
  225. ]);
  226. return '"'.md5($calendarData).'"';
  227. }
  228. /**
  229. * Updates an existing calendarobject, based on it's uri.
  230. *
  231. * The object uri is only the basename, or filename and not a full path.
  232. *
  233. * It is possible return an etag from this function, which will be used in
  234. * the response to this PUT request. Note that the ETag must be surrounded
  235. * by double-quotes.
  236. *
  237. * However, you should only really return this ETag if you don't mangle the
  238. * calendar-data. If the result of a subsequent GET to this object is not
  239. * the exact same as this request body, you should omit the ETag.
  240. *
  241. * @param mixed $calendarId
  242. * @param string $objectUri
  243. * @param string $calendarData
  244. *
  245. * @return string|null
  246. */
  247. public function updateCalendarObject($calendarId, $objectUri, $calendarData)
  248. {
  249. $stmt = $this->pdo->prepare('UPDATE simple_calendarobjects SET calendardata = ? WHERE calendarid = ? AND uri = ?');
  250. $stmt->execute([$calendarData, $calendarId, $objectUri]);
  251. return '"'.md5($calendarData).'"';
  252. }
  253. /**
  254. * Deletes an existing calendar object.
  255. *
  256. * The object uri is only the basename, or filename and not a full path.
  257. *
  258. * @param string $calendarId
  259. * @param string $objectUri
  260. */
  261. public function deleteCalendarObject($calendarId, $objectUri)
  262. {
  263. $stmt = $this->pdo->prepare('DELETE FROM simple_calendarobjects WHERE calendarid = ? AND uri = ?');
  264. $stmt->execute([$calendarId, $objectUri]);
  265. }
  266. }