HookConnector.php 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
  5. * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
  6. * SPDX-License-Identifier: AGPL-3.0-only
  7. */
  8. namespace OC\Files\Node;
  9. use OC\Files\Filesystem;
  10. use OC\Files\View;
  11. use OCP\EventDispatcher\GenericEvent;
  12. use OCP\EventDispatcher\IEventDispatcher;
  13. use OCP\Exceptions\AbortedEventException;
  14. use OCP\Files\Events\Node\BeforeNodeCopiedEvent;
  15. use OCP\Files\Events\Node\BeforeNodeCreatedEvent;
  16. use OCP\Files\Events\Node\BeforeNodeDeletedEvent;
  17. use OCP\Files\Events\Node\BeforeNodeReadEvent;
  18. use OCP\Files\Events\Node\BeforeNodeRenamedEvent;
  19. use OCP\Files\Events\Node\BeforeNodeTouchedEvent;
  20. use OCP\Files\Events\Node\BeforeNodeWrittenEvent;
  21. use OCP\Files\Events\Node\NodeCopiedEvent;
  22. use OCP\Files\Events\Node\NodeCreatedEvent;
  23. use OCP\Files\Events\Node\NodeDeletedEvent;
  24. use OCP\Files\Events\Node\NodeRenamedEvent;
  25. use OCP\Files\Events\Node\NodeTouchedEvent;
  26. use OCP\Files\Events\Node\NodeWrittenEvent;
  27. use OCP\Files\FileInfo;
  28. use OCP\Files\IRootFolder;
  29. use OCP\Util;
  30. use Psr\Log\LoggerInterface;
  31. class HookConnector {
  32. /** @var FileInfo[] */
  33. private array $deleteMetaCache = [];
  34. public function __construct(
  35. private IRootFolder $root,
  36. private View $view,
  37. private IEventDispatcher $dispatcher,
  38. private LoggerInterface $logger,
  39. ) {
  40. }
  41. public function viewToNode() {
  42. Util::connectHook('OC_Filesystem', 'write', $this, 'write');
  43. Util::connectHook('OC_Filesystem', 'post_write', $this, 'postWrite');
  44. Util::connectHook('OC_Filesystem', 'create', $this, 'create');
  45. Util::connectHook('OC_Filesystem', 'post_create', $this, 'postCreate');
  46. Util::connectHook('OC_Filesystem', 'delete', $this, 'delete');
  47. Util::connectHook('OC_Filesystem', 'post_delete', $this, 'postDelete');
  48. Util::connectHook('OC_Filesystem', 'rename', $this, 'rename');
  49. Util::connectHook('OC_Filesystem', 'post_rename', $this, 'postRename');
  50. Util::connectHook('OC_Filesystem', 'copy', $this, 'copy');
  51. Util::connectHook('OC_Filesystem', 'post_copy', $this, 'postCopy');
  52. Util::connectHook('OC_Filesystem', 'touch', $this, 'touch');
  53. Util::connectHook('OC_Filesystem', 'post_touch', $this, 'postTouch');
  54. Util::connectHook('OC_Filesystem', 'read', $this, 'read');
  55. }
  56. public function write($arguments) {
  57. $node = $this->getNodeForPath($arguments['path']);
  58. $this->root->emit('\OC\Files', 'preWrite', [$node]);
  59. $this->dispatcher->dispatch('\OCP\Files::preWrite', new GenericEvent($node));
  60. $event = new BeforeNodeWrittenEvent($node);
  61. $this->dispatcher->dispatchTyped($event);
  62. }
  63. public function postWrite($arguments) {
  64. $node = $this->getNodeForPath($arguments['path']);
  65. $this->root->emit('\OC\Files', 'postWrite', [$node]);
  66. $this->dispatcher->dispatch('\OCP\Files::postWrite', new GenericEvent($node));
  67. $event = new NodeWrittenEvent($node);
  68. $this->dispatcher->dispatchTyped($event);
  69. }
  70. public function create($arguments) {
  71. $node = $this->getNodeForPath($arguments['path']);
  72. $this->root->emit('\OC\Files', 'preCreate', [$node]);
  73. $this->dispatcher->dispatch('\OCP\Files::preCreate', new GenericEvent($node));
  74. $event = new BeforeNodeCreatedEvent($node);
  75. $this->dispatcher->dispatchTyped($event);
  76. }
  77. public function postCreate($arguments) {
  78. $node = $this->getNodeForPath($arguments['path']);
  79. $this->root->emit('\OC\Files', 'postCreate', [$node]);
  80. $this->dispatcher->dispatch('\OCP\Files::postCreate', new GenericEvent($node));
  81. $event = new NodeCreatedEvent($node);
  82. $this->dispatcher->dispatchTyped($event);
  83. }
  84. public function delete($arguments) {
  85. $node = $this->getNodeForPath($arguments['path']);
  86. $this->deleteMetaCache[$node->getPath()] = $node->getFileInfo();
  87. $this->root->emit('\OC\Files', 'preDelete', [$node]);
  88. $this->dispatcher->dispatch('\OCP\Files::preDelete', new GenericEvent($node));
  89. $event = new BeforeNodeDeletedEvent($node);
  90. try {
  91. $this->dispatcher->dispatchTyped($event);
  92. } catch (AbortedEventException $e) {
  93. $arguments['run'] = false;
  94. $this->logger->warning('delete process aborted', ['exception' => $e]);
  95. }
  96. }
  97. public function postDelete($arguments) {
  98. $node = $this->getNodeForPath($arguments['path']);
  99. unset($this->deleteMetaCache[$node->getPath()]);
  100. $this->root->emit('\OC\Files', 'postDelete', [$node]);
  101. $this->dispatcher->dispatch('\OCP\Files::postDelete', new GenericEvent($node));
  102. $event = new NodeDeletedEvent($node);
  103. $this->dispatcher->dispatchTyped($event);
  104. }
  105. public function touch($arguments) {
  106. $node = $this->getNodeForPath($arguments['path']);
  107. $this->root->emit('\OC\Files', 'preTouch', [$node]);
  108. $this->dispatcher->dispatch('\OCP\Files::preTouch', new GenericEvent($node));
  109. $event = new BeforeNodeTouchedEvent($node);
  110. $this->dispatcher->dispatchTyped($event);
  111. }
  112. public function postTouch($arguments) {
  113. $node = $this->getNodeForPath($arguments['path']);
  114. $this->root->emit('\OC\Files', 'postTouch', [$node]);
  115. $this->dispatcher->dispatch('\OCP\Files::postTouch', new GenericEvent($node));
  116. $event = new NodeTouchedEvent($node);
  117. $this->dispatcher->dispatchTyped($event);
  118. }
  119. public function rename($arguments) {
  120. $source = $this->getNodeForPath($arguments['oldpath']);
  121. $target = $this->getNodeForPath($arguments['newpath']);
  122. $this->root->emit('\OC\Files', 'preRename', [$source, $target]);
  123. $this->dispatcher->dispatch('\OCP\Files::preRename', new GenericEvent([$source, $target]));
  124. $event = new BeforeNodeRenamedEvent($source, $target);
  125. try {
  126. $this->dispatcher->dispatchTyped($event);
  127. } catch (AbortedEventException $e) {
  128. $arguments['run'] = false;
  129. $this->logger->warning('rename process aborted', ['exception' => $e]);
  130. }
  131. }
  132. public function postRename($arguments) {
  133. $source = $this->getNodeForPath($arguments['oldpath']);
  134. $target = $this->getNodeForPath($arguments['newpath']);
  135. $this->root->emit('\OC\Files', 'postRename', [$source, $target]);
  136. $this->dispatcher->dispatch('\OCP\Files::postRename', new GenericEvent([$source, $target]));
  137. $event = new NodeRenamedEvent($source, $target);
  138. $this->dispatcher->dispatchTyped($event);
  139. }
  140. public function copy($arguments) {
  141. $source = $this->getNodeForPath($arguments['oldpath']);
  142. $target = $this->getNodeForPath($arguments['newpath']);
  143. $this->root->emit('\OC\Files', 'preCopy', [$source, $target]);
  144. $this->dispatcher->dispatch('\OCP\Files::preCopy', new GenericEvent([$source, $target]));
  145. $event = new BeforeNodeCopiedEvent($source, $target);
  146. try {
  147. $this->dispatcher->dispatchTyped($event);
  148. } catch (AbortedEventException $e) {
  149. $arguments['run'] = false;
  150. $this->logger->warning('copy process aborted', ['exception' => $e]);
  151. }
  152. }
  153. public function postCopy($arguments) {
  154. $source = $this->getNodeForPath($arguments['oldpath']);
  155. $target = $this->getNodeForPath($arguments['newpath']);
  156. $this->root->emit('\OC\Files', 'postCopy', [$source, $target]);
  157. $this->dispatcher->dispatch('\OCP\Files::postCopy', new GenericEvent([$source, $target]));
  158. $event = new NodeCopiedEvent($source, $target);
  159. $this->dispatcher->dispatchTyped($event);
  160. }
  161. public function read($arguments) {
  162. $node = $this->getNodeForPath($arguments['path']);
  163. $this->root->emit('\OC\Files', 'read', [$node]);
  164. $this->dispatcher->dispatch('\OCP\Files::read', new GenericEvent([$node]));
  165. $event = new BeforeNodeReadEvent($node);
  166. $this->dispatcher->dispatchTyped($event);
  167. }
  168. private function getNodeForPath(string $path): Node {
  169. $info = Filesystem::getView()->getFileInfo($path);
  170. if (!$info) {
  171. $fullPath = Filesystem::getView()->getAbsolutePath($path);
  172. if (isset($this->deleteMetaCache[$fullPath])) {
  173. $info = $this->deleteMetaCache[$fullPath];
  174. } else {
  175. $info = null;
  176. }
  177. if (Filesystem::is_dir($path)) {
  178. return new NonExistingFolder($this->root, $this->view, $fullPath, $info);
  179. } else {
  180. return new NonExistingFile($this->root, $this->view, $fullPath, $info);
  181. }
  182. }
  183. if ($info->getType() === FileInfo::TYPE_FILE) {
  184. return new File($this->root, $this->view, $info->getPath(), $info);
  185. } else {
  186. return new Folder($this->root, $this->view, $info->getPath(), $info);
  187. }
  188. }
  189. }