From 173b695cf2a81fce5fff75b11caa82c286b7ef5f Mon Sep 17 00:00:00 2001 From: nathangray Date: Fri, 6 Mar 2020 14:48:26 -0700 Subject: [PATCH] Api: Sharing listens to Vfs hooks to stay up-to-date with deleted or renamed files --- api/setup/setup.inc.php | 31 +++++---- api/src/Vfs/Sharing.php | 48 ++++++++++++- api/tests/Vfs/SharingBase.php | 21 ++++-- api/tests/Vfs/SharingHooksTest.php | 108 +++++++++++++++++++++++++++++ 4 files changed, 189 insertions(+), 19 deletions(-) create mode 100644 api/tests/Vfs/SharingHooksTest.php diff --git a/api/setup/setup.inc.php b/api/setup/setup.inc.php index 499af76ca5..e44bad84c6 100644 --- a/api/setup/setup.inc.php +++ b/api/setup/setup.inc.php @@ -54,24 +54,29 @@ $setup_info['api']['tables'][] = 'egw_ea_valid'; $setup_info['api']['tables'][] = 'egw_ea_notifications'; // hooks used by vfs_home_hooks to manage user- and group-directories for the new stream based VFS -$setup_info['api']['hooks']['addaccount'] = array('EGroupware\\Api\\Vfs\\Hooks::addAccount', 'EGroupware\\Api\\Mail\\Hooks::addaccount'); -$setup_info['api']['hooks']['deleteaccount'] = array('EGroupware\\Api\\Vfs\\Hooks::deleteAccount', 'EGroupware\\Api\\Mail\\Hooks::deleteaccount'); -$setup_info['api']['hooks']['editaccount'] = array('EGroupware\\Api\\Vfs\\Hooks::editAccount', 'EGroupware\\Api\\Mail\\Hooks::addaccount'); -$setup_info['api']['hooks']['addgroup'] = 'EGroupware\\Api\\Vfs\\Hooks::addGroup'; -$setup_info['api']['hooks']['deletegroup'] = array('EGroupware\\Api\\Vfs\\Hooks::deleteGroup', 'EGroupware\\Api\\Mail\\Hooks::deletegroup'); -$setup_info['api']['hooks']['editgroup'] = 'EGroupware\\Api\\Vfs\\Hooks::editGroup'; +$setup_info['api']['hooks']['addaccount'] = array('EGroupware\\Api\\Vfs\\Hooks::addAccount', 'EGroupware\\Api\\Mail\\Hooks::addaccount'); +$setup_info['api']['hooks']['deleteaccount'] = array('EGroupware\\Api\\Vfs\\Hooks::deleteAccount', 'EGroupware\\Api\\Mail\\Hooks::deleteaccount'); +$setup_info['api']['hooks']['editaccount'] = array('EGroupware\\Api\\Vfs\\Hooks::editAccount', 'EGroupware\\Api\\Mail\\Hooks::addaccount'); +$setup_info['api']['hooks']['addgroup'] = 'EGroupware\\Api\\Vfs\\Hooks::addGroup'; +$setup_info['api']['hooks']['deletegroup'] = array('EGroupware\\Api\\Vfs\\Hooks::deleteGroup', 'EGroupware\\Api\\Mail\\Hooks::deletegroup'); +$setup_info['api']['hooks']['editgroup'] = 'EGroupware\\Api\\Vfs\\Hooks::editGroup'; $setup_info['api']['hooks']['changepassword'] = 'EGroupware\\Api\\Mail\\Hooks::changepassword'; $setup_info['api']['hooks']['sidebox_all'] = 'EGroupware\\Api\\Framework\\Tutorial::tutorial_menu'; +// Hooks to delete shares when file is deleted +$setup_info['api']['hooks']['vfs_unlink'] = 'EGroupware\\Api\\Vfs\\Sharing::vfsUpdate'; +$setup_info['api']['hooks']['vfs_rename'] = 'EGroupware\\Api\\Vfs\\Sharing::vfsUpdate'; +$setup_info['api']['hooks']['vfs_rmdir'] = 'EGroupware\\Api\\Vfs\\Sharing::vfsUpdate'; + // installation checks $setup_info['api']['check_install'] = array( - '' => array( - 'func' => 'pear_check', - 'from' => 'Api/Mail', - ), - 'pear.horde.org/Horde_Imap_Client' => array( - 'func' => 'pear_check', - 'from' => 'Api/Mail', + '' => array( + 'func' => 'pear_check', + 'from' => 'Api/Mail', + ), + 'pear.horde.org/Horde_Imap_Client' => array( + 'func' => 'pear_check', + 'from' => 'Api/Mail', 'version' => '2.24.2', ), 'pear.horde.org/Horde_Nls' => array( diff --git a/api/src/Vfs/Sharing.php b/api/src/Vfs/Sharing.php index 3a89043dc6..1214a08b28 100644 --- a/api/src/Vfs/Sharing.php +++ b/api/src/Vfs/Sharing.php @@ -15,8 +15,6 @@ namespace EGroupware\Api\Vfs; use EGroupware\Api; use EGroupware\Api\Vfs; -use EGroupware\Collabora\Wopi; - use filemanager_ui; /** @@ -398,6 +396,52 @@ class Sharing extends \EGroupware\Api\Sharing return $actions; } + /** + * Hook callback to watch VFS and remove any shares for files that get moved or removed + */ + public static function vfsUpdate($data) + { + $path = $data['location'] == 'vfs_rename' ? $data['from'] : $data['path']; + if (parse_url($path, PHP_URL_SCHEME) !== 'vfs') + { + $path = Api\Vfs::PREFIX . ($path[0] == '/' ? '' : '/') . $path; + } + if ($data['location'] == 'vfs_rmdir') + { + // Normally removing a directory removes the files first, so any shares inside the directory would + // be handled already, but just in case, get it all. + $path .= '%'; + } + + $shares = array(); + foreach ($GLOBALS['egw']->db->select(self::TABLE, array( + 'share_id', 'share_path', 'share_owner' + ), + array( + "share_path LIKE '$path'" + ), + __LINE__, __FILE__, false) as $share) + { + $shares[] = $share; + } + foreach ($shares as $share) + { + if ($data['location'] == 'vfs_rename') + { + if (parse_url($data['to'], PHP_URL_SCHEME) !== 'vfs') + { + $data['to'] = $path = Api\Vfs::PREFIX . ($data['to'][0] == '/' ? '' : '/') . $data['to']; + } + $GLOBALS['egw']->db->update(self::TABLE, array( + 'share_path' => $data['to'] + ), $share, __LINE__, __FILE__); + } + else + { + static::delete($share['share_id']); + } + } + } } if (file_exists(__DIR__.'/../../../filemanager/inc/class.filemanager_ui.inc.php')) diff --git a/api/tests/Vfs/SharingBase.php b/api/tests/Vfs/SharingBase.php index 2109250690..b5013d7182 100644 --- a/api/tests/Vfs/SharingBase.php +++ b/api/tests/Vfs/SharingBase.php @@ -17,8 +17,8 @@ namespace EGroupware\Api\Vfs; require_once __DIR__ . '/../LoggedInTest.php'; use EGroupware\Api; -use EGroupware\Api\Vfs; use EGroupware\Api\LoggedInTest as LoggedInTest; +use EGroupware\Api\Vfs; use EGroupware\Stylite\Vfs\Versioning; @@ -421,6 +421,19 @@ class SharingBase extends LoggedInTest return $share; } + public function readShare($share_id) + { + foreach ($GLOBALS['egw']->db->select(Sharing::TABLE, '*', + array( + 'share_id' => (int)$share_id + ), + __LINE__, __FILE__, false) as $share) + { + return $share; + } + return array(); + } + /** * Make an infolog entry */ @@ -428,9 +441,9 @@ class SharingBase extends LoggedInTest { $bo = new \infolog_bo(); $element = array( - 'info_subject' => "Test infolog for #{$this->getName()}", - 'info_des' => 'Test element for ' . $this->getName() ."\n". Api\DateTime::to(), - 'info_status' => 'open' + 'info_subject' => "Test infolog for #{$this->getName()}", + 'info_des' => 'Test element for ' . $this->getName() . "\n" . Api\DateTime::to(), + 'info_status' => 'open' ); $element_id = $bo->write($element, true, true, true, true); diff --git a/api/tests/Vfs/SharingHooksTest.php b/api/tests/Vfs/SharingHooksTest.php new file mode 100644 index 0000000000..525e4549f8 --- /dev/null +++ b/api/tests/Vfs/SharingHooksTest.php @@ -0,0 +1,108 @@ +files = $this->addFiles($target); + $test_file = $this->files[0]; + + // Make sure there are no leftover shares + Sharing::delete(array('share_path' => $test_file)); + + // Create share + $this->shares[] = $created_share = Sharing::create($test_file, Sharing::READONLY, '', ''); + + $this->assertEquals(Vfs::PREFIX . $test_file, $created_share['share_path']); + + // Now delete the file + Vfs::remove($test_file); + + // Check for share for that file + $read_share = $this->readShare($created_share['share_id']); + + $this->assertEquals(array(), $read_share, "Expected not to find the share, but something was found"); + } + + + /** + * Test that deleting a directory deletes shares on any file in that directory + */ + public function testDeleteDirectoryDeletesShare() + { + $target = Vfs::get_home_dir(); + + $this->files = $this->addFiles($target); + $test_file = $target . '/sub_dir/subdir_test_file.txt'; + + // Make sure there are no leftover shares + Sharing::delete(array('share_path' => $test_file)); + + // Create share + $this->shares[] = $created_share = Sharing::create($test_file, Sharing::READONLY, '', ''); + + $this->assertEquals(Vfs::PREFIX . $test_file, $created_share['share_path']); + + // Now delete the parent directory + Vfs::remove($target . '/sub_dir'); + + // Check for share for that file + $read_share = $this->readShare($created_share['share_id']); + + $this->assertEquals(array(), $read_share, "Expected not to find the share, but something was found"); + } + + /** + * Test renaming a file updates the share + */ + public function testRenameFileUpdatesShare() + { + $target = Vfs::get_home_dir(); + + $this->files = $this->addFiles($target); + $test_file = $this->files[0]; + + // Make sure there are no leftover shares + Sharing::delete(array('share_path' => $test_file)); + + // Create share + $this->shares[] = $created_share = Sharing::create($test_file, Sharing::READONLY, '', ''); + + $this->assertEquals(Vfs::PREFIX . $test_file, $created_share['share_path']); + + // Now rename the file + $this->files[] = $moved = $target . '/moved.txt'; + Vfs::rename($test_file, $moved); + + // Check for share for that file + $read_share = $this->readShare($created_share['share_id']); + + $this->assertEquals(Vfs::PREFIX . $moved, $read_share['share_path'], "Expected find the share with a different path"); + $this->assertNotEquals(Vfs::PREFIX . $moved, $created_share['share_path'], "Expected find the share with a different path"); + } +}