VCFExportPlugin.php 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. <?php
  2. declare(strict_types=1);
  3. namespace Sabre\CardDAV;
  4. use Sabre\DAV;
  5. use Sabre\HTTP\RequestInterface;
  6. use Sabre\HTTP\ResponseInterface;
  7. use Sabre\VObject;
  8. /**
  9. * VCF Exporter.
  10. *
  11. * This plugin adds the ability to export entire address books as .vcf files.
  12. * This is useful for clients that don't support CardDAV yet. They often do
  13. * support vcf files.
  14. *
  15. * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
  16. * @author Evert Pot (http://evertpot.com/)
  17. * @author Thomas Tanghus (http://tanghus.net/)
  18. * @license http://sabre.io/license/ Modified BSD License
  19. */
  20. class VCFExportPlugin extends DAV\ServerPlugin
  21. {
  22. /**
  23. * Reference to Server class.
  24. *
  25. * @var DAV\Server
  26. */
  27. protected $server;
  28. /**
  29. * Initializes the plugin and registers event handlers.
  30. */
  31. public function initialize(DAV\Server $server)
  32. {
  33. $this->server = $server;
  34. $this->server->on('method:GET', [$this, 'httpGet'], 90);
  35. $server->on('browserButtonActions', function ($path, $node, &$actions) {
  36. if ($node instanceof IAddressBook) {
  37. $actions .= '<a href="'.htmlspecialchars($path, ENT_QUOTES, 'UTF-8').'?export"><span class="oi" data-glyph="book"></span></a>';
  38. }
  39. });
  40. }
  41. /**
  42. * Intercepts GET requests on addressbook urls ending with ?export.
  43. *
  44. * @return bool
  45. */
  46. public function httpGet(RequestInterface $request, ResponseInterface $response)
  47. {
  48. $queryParams = $request->getQueryParameters();
  49. if (!array_key_exists('export', $queryParams)) {
  50. return;
  51. }
  52. $path = $request->getPath();
  53. $node = $this->server->tree->getNodeForPath($path);
  54. if (!($node instanceof IAddressBook)) {
  55. return;
  56. }
  57. $this->server->transactionType = 'get-addressbook-export';
  58. // Checking ACL, if available.
  59. if ($aclPlugin = $this->server->getPlugin('acl')) {
  60. $aclPlugin->checkPrivileges($path, '{DAV:}read');
  61. }
  62. $nodes = $this->server->getPropertiesForPath($path, [
  63. '{'.Plugin::NS_CARDDAV.'}address-data',
  64. ], 1);
  65. $format = 'text/directory';
  66. $output = null;
  67. $filenameExtension = null;
  68. switch ($format) {
  69. case 'text/directory':
  70. $output = $this->generateVCF($nodes);
  71. $filenameExtension = '.vcf';
  72. break;
  73. }
  74. $filename = preg_replace(
  75. '/[^a-zA-Z0-9-_ ]/um',
  76. '',
  77. $node->getName()
  78. );
  79. $filename .= '-'.date('Y-m-d').$filenameExtension;
  80. $response->setHeader('Content-Disposition', 'attachment; filename="'.$filename.'"');
  81. $response->setHeader('Content-Type', $format);
  82. $response->setStatus(200);
  83. $response->setBody($output);
  84. // Returning false to break the event chain
  85. return false;
  86. }
  87. /**
  88. * Merges all vcard objects, and builds one big vcf export.
  89. *
  90. * @return string
  91. */
  92. public function generateVCF(array $nodes)
  93. {
  94. $output = '';
  95. foreach ($nodes as $node) {
  96. if (!isset($node[200]['{'.Plugin::NS_CARDDAV.'}address-data'])) {
  97. continue;
  98. }
  99. $nodeData = $node[200]['{'.Plugin::NS_CARDDAV.'}address-data'];
  100. // Parsing this node so VObject can clean up the output.
  101. $vcard = VObject\Reader::read($nodeData);
  102. $output .= $vcard->serialize();
  103. // Destroy circular references to PHP will GC the object.
  104. $vcard->destroy();
  105. }
  106. return $output;
  107. }
  108. /**
  109. * Returns a plugin name.
  110. *
  111. * Using this name other plugins will be able to access other plugins
  112. * using \Sabre\DAV\Server::getPlugin
  113. *
  114. * @return string
  115. */
  116. public function getPluginName()
  117. {
  118. return 'vcf-export';
  119. }
  120. /**
  121. * Returns a bunch of meta-data about the plugin.
  122. *
  123. * Providing this information is optional, and is mainly displayed by the
  124. * Browser plugin.
  125. *
  126. * The description key in the returned array may contain html and will not
  127. * be sanitized.
  128. *
  129. * @return array
  130. */
  131. public function getPluginInfo()
  132. {
  133. return [
  134. 'name' => $this->getPluginName(),
  135. 'description' => 'Adds the ability to export CardDAV addressbooks as a single vCard file.',
  136. 'link' => 'http://sabre.io/dav/vcf-export-plugin/',
  137. ];
  138. }
  139. }