From 6a229b1eccc7e9472d0b33d3e1b3069b29db3409 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Thu, 15 Sep 2016 15:07:30 +0200 Subject: [PATCH 1/2] Add support for rename operations --- lib/AppInfo/Application.php | 2 + lib/FilesHooks.php | 132 ++++++++++++++++++++++++++++++++++++ lib/FilesHooksStatic.php | 16 +++++ 3 files changed, 150 insertions(+) diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 328b6bc45..585b9b1f2 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -109,6 +109,8 @@ public function registerFilesActivity() { Util::connectHook('OC_Filesystem', 'post_create', FilesHooksStatic::class, 'fileCreate'); Util::connectHook('OC_Filesystem', 'post_update', FilesHooksStatic::class, 'fileUpdate'); Util::connectHook('OC_Filesystem', 'delete', FilesHooksStatic::class, 'fileDelete'); + Util::connectHook('OC_Filesystem', 'rename', FilesHooksStatic::class, 'fileMove'); + Util::connectHook('OC_Filesystem', 'post_rename', FilesHooksStatic::class, 'fileMovePost'); Util::connectHook('\OCA\Files_Trashbin\Trashbin', 'post_restore', FilesHooksStatic::class, 'fileRestore'); Util::connectHook('OCP\Share', 'post_shared', FilesHooksStatic::class, 'share'); Util::connectHook('OCP\Share', 'pre_unshare', FilesHooksStatic::class, 'unShare'); diff --git a/lib/FilesHooks.php b/lib/FilesHooks.php index 94a25501d..034f98a52 100755 --- a/lib/FilesHooks.php +++ b/lib/FilesHooks.php @@ -68,6 +68,9 @@ class FilesHooks { /** @var CurrentUser */ protected $currentUser; + /** @var string|bool */ + protected $moveCase = false; + /** * Constructor * @@ -174,6 +177,135 @@ protected function addNotificationsForFileAction($filePath, $activityType, $subj } } + /** + * Collect some information for move/renames + * + * @param string $oldPath Path of the file that has been moved + * @param string $newPath Path of the file that has been moved + */ + public function fileMove($oldPath, $newPath) { + if (substr($oldPath, -5) === '.part' || substr($newPath, -5) === '.part') { + // Do not add activities for .part-files + $this->moveCase = false; + } + + $oldDir = dirname($oldPath); + $newDir = dirname($newPath); + + if ($oldDir === $newDir) { + /** + * a/b moved to a/c + * + * Cases: + * - a/b shared: no visible change + * - a/ shared: rename + */ + $this->moveCase = 'rename'; + } else if (strpos($oldDir, $newDir) === 0) { + /** + * a/b/c moved to a/d + * + * Cases: + * - a/b/c shared: no visible change + * - a/b/ shared: delete + * - a/ shared: move + */ + $this->moveCase = 'moveUp'; + } else if (strpos($newDir, $oldDir) === 0) { + /** + * a/b moved to a/c/d + * + * Cases: + * - a/b shared: no visible change + * - a/c/ shared: add + * - a/ shared: move + */ + $this->moveCase = 'moveDown'; + } else if (strpos($newDir, $oldDir) === 0) { + /** + * a/b/c moved to a/d/e + * + * Cases: + * - a/b/c shared: no visible change + * - a/b/ shared: delete + * - a/d/ shared: add + * - a/ shared: move + */ + $this->moveCase = 'moveCross'; + } + } + + /** + * Store the move hook events + * + * @param string $oldPath Path of the file that has been moved + * @param string $newPath Path of the file that has been moved + */ + public function fileMovePost($oldPath, $newPath) { + // Do not add activities for .part-files + if ($this->moveCase === false) { + return; + } + + switch ($this->moveCase) { + case 'rename': + $this->fileRename($oldPath, $newPath); + break; + } + + $this->moveCase = false; + } + + + /** + * Renaming a file inside the same folder + * + * @param string $oldPath + * @param string $newPath + */ + protected function fileRename($oldPath, $newPath) { + $dirName = dirname($newPath); + $fileName = basename($newPath); + $oldFileName = basename($oldPath); + + if (dirname($oldPath) !== $dirName) { + return; + } + + list(, , $fileId) = $this->getSourcePathAndOwner($newPath); + list($parentPath, $parentOwner, $parentId) = $this->getSourcePathAndOwner($dirName); + if ($fileId === 0 || $parentId === 0) { + // Could not find the file for the owner ... + return; + } + + $affectedUsers = $this->getUserPathsFromPath($parentPath, $parentOwner); + $filteredStreamUsers = $this->userSettings->filterUsersBySetting(array_keys($affectedUsers), 'stream', Files::TYPE_SHARE_CHANGED); + $filteredEmailUsers = $this->userSettings->filterUsersBySetting(array_keys($affectedUsers), 'email', Files::TYPE_SHARE_CHANGED); + + foreach ($affectedUsers as $user => $path) { + if (empty($filteredStreamUsers[$user]) && empty($filteredEmailUsers[$user])) { + continue; + } + + if ($user === $this->currentUser->getUID()) { + $userSubject = 'renamed_self'; + $userParams = [[$fileId => $path . '/' . $fileName], $oldFileName]; + } else { + $userSubject = 'renamed_by'; + $userParams = [[$fileId => $path . '/' . $fileName], $this->currentUser->getUserIdentifier(), $oldFileName]; + } + + $this->addNotificationsForUser( + $user, $userSubject, $userParams, + $fileId, $path . '/' . $fileName, true, + !empty($filteredStreamUsers[$user]), + !empty($filteredEmailUsers[$user]) ? $filteredEmailUsers[$user] : 0, + Files::TYPE_SHARE_CHANGED + ); + } + } + /** * Returns a "username => path" map for all affected users * diff --git a/lib/FilesHooksStatic.php b/lib/FilesHooksStatic.php index f1720935d..ea480769d 100755 --- a/lib/FilesHooksStatic.php +++ b/lib/FilesHooksStatic.php @@ -62,6 +62,22 @@ public static function fileDelete($params) { self::getHooks()->fileDelete($params['path']); } + /** + * Store the rename hook events + * @param array $params The hook params + */ + public static function fileMove($params) { + self::getHooks()->fileMove($params['oldpath'], $params['newpath']); + } + + /** + * Store the rename hook events + * @param array $params The hook params + */ + public static function fileMovePost($params) { + self::getHooks()->fileMovePost($params['oldpath'], $params['newpath']); + } + /** * Store the restore hook events * @param array $params The hook params From c2130a72a8d6977e5522596e9d360add600b6349 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Thu, 15 Sep 2016 16:36:31 +0200 Subject: [PATCH 2/2] Add activity support for moving of files up/down/mixed --- lib/FilesHooks.php | 221 +++++++++++++++++++++++++++++++++++---- lib/FilesHooksStatic.php | 3 +- 2 files changed, 204 insertions(+), 20 deletions(-) diff --git a/lib/FilesHooks.php b/lib/FilesHooks.php index 034f98a52..ba3fe5cda 100755 --- a/lib/FilesHooks.php +++ b/lib/FilesHooks.php @@ -70,6 +70,14 @@ class FilesHooks { /** @var string|bool */ protected $moveCase = false; + /** @var string[] */ + protected $oldParentUsers; + /** @var string */ + protected $oldParentPath; + /** @var string */ + protected $oldParentOwner; + /** @var string */ + protected $oldParentId; /** * Constructor @@ -187,6 +195,7 @@ public function fileMove($oldPath, $newPath) { if (substr($oldPath, -5) === '.part' || substr($newPath, -5) === '.part') { // Do not add activities for .part-files $this->moveCase = false; + return; } $oldDir = dirname($oldPath); @@ -201,40 +210,52 @@ public function fileMove($oldPath, $newPath) { * - a/ shared: rename */ $this->moveCase = 'rename'; - } else if (strpos($oldDir, $newDir) === 0) { + return; + } + + if (strpos($oldDir, $newDir) === 0) { /** - * a/b/c moved to a/d + * a/b/c moved to a/c * * Cases: * - a/b/c shared: no visible change * - a/b/ shared: delete - * - a/ shared: move + * - a/ shared: move/rename */ $this->moveCase = 'moveUp'; } else if (strpos($newDir, $oldDir) === 0) { /** - * a/b moved to a/c/d + * a/b moved to a/c/b * * Cases: * - a/b shared: no visible change * - a/c/ shared: add - * - a/ shared: move + * - a/ shared: move/rename */ $this->moveCase = 'moveDown'; - } else if (strpos($newDir, $oldDir) === 0) { + } else { /** - * a/b/c moved to a/d/e + * a/b/c moved to a/d/c * * Cases: * - a/b/c shared: no visible change * - a/b/ shared: delete * - a/d/ shared: add - * - a/ shared: move + * - a/ shared: move/rename */ $this->moveCase = 'moveCross'; } + + list($this->oldParentPath, $this->oldParentOwner, $this->oldParentId) = $this->getSourcePathAndOwner($oldDir); + if ($this->oldParentId === 0) { + // Could not find the file for the owner ... + $this->moveCase = false; + return; + } + $this->oldParentUsers = $this->getUserPathsFromPath($this->oldParentPath, $this->oldParentOwner); } + /** * Store the move hook events * @@ -249,7 +270,12 @@ public function fileMovePost($oldPath, $newPath) { switch ($this->moveCase) { case 'rename': - $this->fileRename($oldPath, $newPath); + $this->fileRenaming($oldPath, $newPath); + break; + case 'moveUp': + case 'moveDown': + case 'moveCross': + $this->fileMoving($oldPath, $newPath); break; } @@ -258,28 +284,24 @@ public function fileMovePost($oldPath, $newPath) { /** - * Renaming a file inside the same folder + * Renaming a file inside the same folder (a/b to a/c) * * @param string $oldPath * @param string $newPath */ - protected function fileRename($oldPath, $newPath) { + protected function fileRenaming($oldPath, $newPath) { $dirName = dirname($newPath); $fileName = basename($newPath); $oldFileName = basename($oldPath); - if (dirname($oldPath) !== $dirName) { - return; - } - list(, , $fileId) = $this->getSourcePathAndOwner($newPath); list($parentPath, $parentOwner, $parentId) = $this->getSourcePathAndOwner($dirName); if ($fileId === 0 || $parentId === 0) { // Could not find the file for the owner ... return; } - $affectedUsers = $this->getUserPathsFromPath($parentPath, $parentOwner); + $filteredStreamUsers = $this->userSettings->filterUsersBySetting(array_keys($affectedUsers), 'stream', Files::TYPE_SHARE_CHANGED); $filteredEmailUsers = $this->userSettings->filterUsersBySetting(array_keys($affectedUsers), 'email', Files::TYPE_SHARE_CHANGED); @@ -290,10 +312,17 @@ protected function fileRename($oldPath, $newPath) { if ($user === $this->currentUser->getUID()) { $userSubject = 'renamed_self'; - $userParams = [[$fileId => $path . '/' . $fileName], $oldFileName]; + $userParams = [ + [$fileId => $path . '/' . $fileName], + [$fileId => $path . '/' . $oldFileName], + ]; } else { $userSubject = 'renamed_by'; - $userParams = [[$fileId => $path . '/' . $fileName], $this->currentUser->getUserIdentifier(), $oldFileName]; + $userParams = [ + [$fileId => $path . '/' . $fileName], + $this->currentUser->getUserIdentifier(), + [$fileId => $path . '/' . $oldFileName], + ]; } $this->addNotificationsForUser( @@ -306,6 +335,162 @@ protected function fileRename($oldPath, $newPath) { } } + /** + * Moving a file from one folder to another + * + * @param string $oldPath + * @param string $newPath + */ + protected function fileMoving($oldPath, $newPath) { + $dirName = dirname($newPath); + $fileName = basename($newPath); + $oldFileName = basename($oldPath); + + list(, , $fileId) = $this->getSourcePathAndOwner($newPath); + list($parentPath, $parentOwner, $parentId) = $this->getSourcePathAndOwner($dirName); + if ($fileId === 0 || $parentId === 0) { + // Could not find the file for the owner ... + return; + } + $affectedUsers = $this->getUserPathsFromPath($parentPath, $parentOwner); + + $beforeUsers = array_keys($this->oldParentUsers); + $afterUsers = array_keys($affectedUsers); + + $deleteUsers = array_diff($beforeUsers, $afterUsers); + $this->generateDeleteActivities($deleteUsers, $this->oldParentUsers, $fileId, $oldFileName); + + $addUsers = array_diff($afterUsers, $beforeUsers); + $this->generateAddActivities($addUsers, $affectedUsers, $fileId, $fileName); + + $moveUsers = array_intersect($beforeUsers, $afterUsers); + $this->generateMoveActivities($moveUsers, $this->oldParentUsers, $affectedUsers, $fileId, $oldFileName, $fileName); + } + + /** + * @param string[] $users + * @param string[] $pathMap + * @param int $fileId + * @param string $oldFileName + */ + protected function generateDeleteActivities($users, $pathMap, $fileId, $oldFileName) { + if (empty($users)) { + return; + } + + $filteredStreamUsers = $this->userSettings->filterUsersBySetting($users, 'stream', Files::TYPE_SHARE_DELETED); + $filteredEmailUsers = $this->userSettings->filterUsersBySetting($users, 'email', Files::TYPE_SHARE_DELETED); + + foreach ($users as $user) { + if (empty($filteredStreamUsers[$user]) && empty($filteredEmailUsers[$user])) { + continue; + } + + $path = $pathMap[$user]; + + if ($user === $this->currentUser->getUID()) { + $userSubject = 'deleted_self'; + $userParams = [[$fileId => $path . '/' . $oldFileName]]; + } else { + $userSubject = 'deleted_by'; + $userParams = [[$fileId => $path . '/' . $oldFileName], $this->currentUser->getUserIdentifier()]; + } + + $this->addNotificationsForUser( + $user, $userSubject, $userParams, + $fileId, $path . '/' . $oldFileName, true, + !empty($filteredStreamUsers[$user]), + !empty($filteredEmailUsers[$user]) ? $filteredEmailUsers[$user] : 0, + Files::TYPE_SHARE_DELETED + ); + } + } + + /** + * @param string[] $users + * @param string[] $pathMap + * @param int $fileId + * @param string $fileName + */ + protected function generateAddActivities($users, $pathMap, $fileId, $fileName) { + if (empty($users)) { + return; + } + + $filteredStreamUsers = $this->userSettings->filterUsersBySetting($users, 'stream', Files::TYPE_SHARE_CREATED); + $filteredEmailUsers = $this->userSettings->filterUsersBySetting($users, 'email', Files::TYPE_SHARE_CREATED); + + foreach ($users as $user) { + if (empty($filteredStreamUsers[$user]) && empty($filteredEmailUsers[$user])) { + continue; + } + + $path = $pathMap[$user]; + + if ($user === $this->currentUser->getUID()) { + $userSubject = 'created_self'; + $userParams = [[$fileId => $path . '/' . $fileName]]; + } else { + $userSubject = 'created_by'; + $userParams = [[$fileId => $path . '/' . $fileName], $this->currentUser->getUserIdentifier()]; + } + + $this->addNotificationsForUser( + $user, $userSubject, $userParams, + $fileId, $path . '/' . $fileName, true, + !empty($filteredStreamUsers[$user]), + !empty($filteredEmailUsers[$user]) ? $filteredEmailUsers[$user] : 0, + Files::TYPE_SHARE_CREATED + ); + } + } + + /** + * @param string[] $users + * @param string[] $beforePathMap + * @param string[] $afterPathMap + * @param int $fileId + * @param string $oldFileName + * @param string $fileName + */ + protected function generateMoveActivities($users, $beforePathMap, $afterPathMap, $fileId, $oldFileName, $fileName) { + if (empty($users)) { + return; + } + + $filteredStreamUsers = $this->userSettings->filterUsersBySetting($users, 'stream', Files::TYPE_SHARE_CHANGED); + $filteredEmailUsers = $this->userSettings->filterUsersBySetting($users, 'email', Files::TYPE_SHARE_CHANGED); + + foreach ($users as $user) { + if (empty($filteredStreamUsers[$user]) && empty($filteredEmailUsers[$user])) { + continue; + } + + if ($user === $this->currentUser->getUID()) { + $userSubject = 'moved_self'; + $userParams = [ + [$fileId => $afterPathMap[$user] . '/' . $fileName], + [$fileId => $beforePathMap[$user] . '/' . $oldFileName], + ]; + } else { + $userSubject = 'moved_by'; + $userParams = [ + [$fileId => $afterPathMap[$user] . '/' . $fileName], + $this->currentUser->getUserIdentifier(), + [$fileId => $beforePathMap[$user] . '/' . $oldFileName], + ]; + } + + $this->addNotificationsForUser( + $user, $userSubject, $userParams, + $fileId, $afterPathMap[$user] . '/' . $fileName, true, + !empty($filteredStreamUsers[$user]), + !empty($filteredEmailUsers[$user]) ? $filteredEmailUsers[$user] : 0, + Files::TYPE_SHARE_CHANGED + ); + } + } + /** * Returns a "username => path" map for all affected users * diff --git a/lib/FilesHooksStatic.php b/lib/FilesHooksStatic.php index ea480769d..346012288 100755 --- a/lib/FilesHooksStatic.php +++ b/lib/FilesHooksStatic.php @@ -34,8 +34,7 @@ class FilesHooksStatic { * @return FilesHooks */ static protected function getHooks() { - $app = new AppInfo\Application(); - return $app->getContainer()->query(FilesHooks::class); + return \OC::$server->query(FilesHooks::class); } /**