From 7c908a0016db30c70271304c4d080383911645e3 Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Tue, 10 Jul 2012 18:56:22 -0400 Subject: Sharing files working using share API --- apps/files_sharing/lib/share/file.php | 94 +++++++++++++++++++++++++++++++++ apps/files_sharing/lib/share/folder.php | 64 ++++++++++++++++++++++ 2 files changed, 158 insertions(+) create mode 100644 apps/files_sharing/lib/share/file.php create mode 100644 apps/files_sharing/lib/share/folder.php (limited to 'apps/files_sharing/lib') diff --git a/apps/files_sharing/lib/share/file.php b/apps/files_sharing/lib/share/file.php new file mode 100644 index 00000000000..86b82b19acd --- /dev/null +++ b/apps/files_sharing/lib/share/file.php @@ -0,0 +1,94 @@ +. +*/ + +class OC_Share_Backend_File extends OCP\Share_Backend { + + const FORMAT_SOURCE_PATH = 0; + const FORMAT_FILE_APP = 1; + const FORMAT_FILE_APP_ROOT = 2; + const FORMAT_OPENDIR = 3; + + public function getSource($item, $uid) { + if (OC_Filesystem::file_exists($item)) { + return array('item' => null, 'file' => $item); + } + return false; + } + + public function generateTarget($item, $uid, $exclude = null) { + // TODO Make sure target path doesn't exist already + return '/Shared'.$item; + } + + public function formatItems($items, $format, $parameters = null) { + if ($format == self::FORMAT_OPENDIR) { + $files = array(); + foreach ($items as $file) { + $files[] = basename($file['file_target']); + } + return $files; + } else { + $shares = array(); + $ids = array(); + foreach ($items as $item) { + $shares[$item['file_source']] = $item; + $ids[] = $item['file_source']; + } + $ids = "'".implode("','", $ids)."'"; + if ($format == self::FORMAT_SOURCE_PATH) { + $query = OCP\DB::prepare('SELECT path FROM *PREFIX*fscache WHERE id IN ('.$ids.')'); + $result = $query->execute()->fetchAll(); + if (isset($result[0]['path'])) { + return $result[0]['path']; + } + return false; + } else if ($format == self::FORMAT_FILE_APP) { + $query = OCP\DB::prepare('SELECT id, path, name, ctime, mtime, mimetype, size, encrypted, versioned, writable FROM *PREFIX*fscache WHERE id IN ('.$ids.')'); + $result = $query->execute(); + $files = array(); + while ($file = $result->fetchRow()) { + // Set target path + $file['path'] = $shares[$file['id']]['file_target']; + $file['name'] = basename($file['path']); + // TODO Set permissions: $file['writable'] + $files[] = $file; + } + return $files; + } else if ($format == self::FORMAT_FILE_APP_ROOT) { + $query = OCP\DB::prepare('SELECT id, path, name, ctime, mtime, mimetype, size, encrypted, versioned, writable FROM *PREFIX*fscache WHERE id IN ('.$ids.')'); + $result = $query->execute(); + $mtime = 0; + $size = 0; + while ($file = $result->fetchRow()) { + if ($file['mtime'] > $mtime) { + $mtime = $file['mtime']; + } + $size += $file['size']; + } + return array(0 => array('name' => 'Shared', 'mtime' => $mtime, 'mimetype' => 'httpd/unix-directory', 'size' => $size, 'writable' => false)); + } + } + return array(); + } + +} + +?> \ No newline at end of file diff --git a/apps/files_sharing/lib/share/folder.php b/apps/files_sharing/lib/share/folder.php new file mode 100644 index 00000000000..033e2ba9667 --- /dev/null +++ b/apps/files_sharing/lib/share/folder.php @@ -0,0 +1,64 @@ +. +*/ + +class OC_Share_Backend_Folder extends OC_Share_Backend_File { + + public function inCollection($collections, $item) { + // TODO + } + + public function getChildrenSources($item) { + return OC_FileCache::getFolderContent($item); + } + + public function formatItems($items, $format, $parameters = null) { + if ($format == self::FORMAT_FILE_APP && isset($parameters['folder'])) { + $folder = $items[key($items)]; + $query = OCP\DB::prepare('SELECT path FROM *PREFIX*fscache WHERE id = ?'); + $result = $query->execute(array($folder['file_source']))->fetchRow(); + if (isset($result['path'])) { + if (isset($parameters['mimetype_filter'])) { + $mimetype_filter = $parameters['mimetype_filter']; + } else { + $mimetype_filter = ''; + } + $pos = strpos($result['path'], $folder['item']); + $path = substr($result['path'], $pos).substr($parameters['folder'], strlen($folder['file_target'])); + $root = substr($result['path'], 0, $pos); + return OC_FileCache::getFolderContent($path, $root, $mimetype_filter); + } + }/* else if ($format == self::FORMAT_OPENDIR_ROOT) { + $query = OCP\DB::prepare('SELECT name FROM *PREFIX*fscache WHERE id IN ('.$ids.')'); + $result = $query->execute(); + $files = array(); + while ($file = $result->fetchRow()) { + // Set target path + $files[] = basename($shares[$file['id']]['item_target']); + } + return $files; + }*/ + return array(); + } + +} + + +?> \ No newline at end of file -- cgit v1.2.3 From 7b01e3285e59d8184aae6c1a8de54c742b35fc54 Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Mon, 23 Jul 2012 20:08:05 -0400 Subject: Include a few CRUDS permissions checks in shared storage, plus a little clean-up in shared storage --- apps/files_sharing/lib/share/file.php | 24 +-- apps/files_sharing/sharedstorage.php | 297 +++++++++++++--------------------- 2 files changed, 128 insertions(+), 193 deletions(-) (limited to 'apps/files_sharing/lib') diff --git a/apps/files_sharing/lib/share/file.php b/apps/files_sharing/lib/share/file.php index 86b82b19acd..658c42ee327 100644 --- a/apps/files_sharing/lib/share/file.php +++ b/apps/files_sharing/lib/share/file.php @@ -1,3 +1,4 @@ + execute(array($id))->fetchAll(); + if (isset($result[0]['path'])) { + return array('path' => $result[0]['path'], 'permissions' => $items[key($items)]['permissions']); + } + return false; } else { $shares = array(); $ids = array(); @@ -53,14 +62,7 @@ class OC_Share_Backend_File extends OCP\Share_Backend { $ids[] = $item['file_source']; } $ids = "'".implode("','", $ids)."'"; - if ($format == self::FORMAT_SOURCE_PATH) { - $query = OCP\DB::prepare('SELECT path FROM *PREFIX*fscache WHERE id IN ('.$ids.')'); - $result = $query->execute()->fetchAll(); - if (isset($result[0]['path'])) { - return $result[0]['path']; - } - return false; - } else if ($format == self::FORMAT_FILE_APP) { + if ($format == self::FORMAT_FILE_APP) { $query = OCP\DB::prepare('SELECT id, path, name, ctime, mtime, mimetype, size, encrypted, versioned, writable FROM *PREFIX*fscache WHERE id IN ('.$ids.')'); $result = $query->execute(); $files = array(); @@ -89,6 +91,4 @@ class OC_Share_Backend_File extends OCP\Share_Backend { return array(); } -} - -?> \ No newline at end of file +} \ No newline at end of file diff --git a/apps/files_sharing/sharedstorage.php b/apps/files_sharing/sharedstorage.php index 1dfdf133791..f2379b7be25 100644 --- a/apps/files_sharing/sharedstorage.php +++ b/apps/files_sharing/sharedstorage.php @@ -26,17 +26,22 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { private $sharedFolder; - private $sourcePaths = array(); + private $files = array(); public function __construct($arguments) { $this->sharedFolder = $arguments['sharedFolder']; } - private function getSourcePath($target) { + /** + * @brief Get the source file path and the permissions granted for a shared file + * @param string Shared target file path + * @return Returns array with the keys path and permissions or false if not found + */ + private function getFile($target) { $target = $this->sharedFolder.'/'.$target; $target = rtrim($target, '/'); - if (isset($this->sourcePaths[$target])) { - return $this->sourcePaths[$target]; + if (isset($this->files[$target])) { + return $this->files[$target]; } else { $pos = strpos($target, '/', 8); // Get shared folder name @@ -45,16 +50,48 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { } else { $itemTarget = $target; } - $sourcePath = OCP\Share::getItemSharedWith('file', $itemTarget, OC_Share_Backend_File::FORMAT_SOURCE_PATH); - if ($sourcePath) { - $this->sourcePaths[$target] = $sourcePath.substr($target, strlen($itemTarget)); - return $this->sourcePaths[$target]; + $file = OCP\Share::getItemSharedWith('file', $itemTarget, OC_Share_Backend_File::FORMAT_SHARED_STORAGE); + if ($file) { + $this->files[$target]['path'] = $file['path'].substr($target, strlen($itemTarget)); + $this->files[$target]['permissions'] = $file['permissions']; + return $this->files[$target]; } - OCP\Util::writeLog('files_sharing', 'File source path not found for: '.$target, OCP\Util::ERROR); + OCP\Util::writeLog('files_sharing', 'File source not found for: '.$target, OCP\Util::ERROR); return false; } } + /** + * @brief Get the source file path for a shared file + * @param string Shared target file path + * @return Returns source file path or false if not found + */ + private function getSourcePath($target) { + $file = $this->getFile($target); + if (isset($file['path'])) { + return $file['path']; + } + return false; + } + + /** + * @brief Get the permissions granted for a shared file + * @param string Shared target file path + * @return Returns CRUDS permissions granted or false if not found + */ + private function getPermissions($target) { + $file = $this->getFile($target); + if (isset($file['permissions'])) { + return $file['permissions']; + } + return false; + } + + /** + * @brief Get the internal path to pass to the storage filesystem call + * @param string Source file path + * @return Source file path with mount point stripped out + */ private function getInternalPath($path) { $mountPoint = OC_Filesystem::getMountPoint($path); $internalPath = substr($path, strlen($mountPoint)); @@ -62,19 +99,21 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { } public function mkdir($path) { - if ($path == "" || $path == "/" || !$this->is_writable($path)) { + if ($path == '' || $path == '/' || !$this->is_writable($path)) { return false; - } else { - if ($source = $this->getSourcePath($path)) { - $storage = OC_Filesystem::getStorage($source); - return $storage->mkdir($this->getInternalPath($source)); - } + } else if ($source = $this->getSourcePath($path)) { + $storage = OC_Filesystem::getStorage($source); + return $storage->mkdir($this->getInternalPath($source)); } + return false; } public function rmdir($path) { - // The folder will be removed from the database, but won't be deleted from the owner's filesystem - // TODO + if ($source = $this->getSourcePath($path) && ($this->getPermissions($path) & OCP\Share::PERMISSION_DELETE)) { + $storage = OC_Filesystem::getStorage($source); + return $storage->rmdir($this->getInternalPath($source)); + } + return false; } public function opendir($path) { @@ -82,23 +121,21 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { $files = OCP\Share::getItemsSharedWith('file', OC_Share_Backend_Folder::FORMAT_OPENDIR); OC_FakeDirStream::$dirs['shared'] = $files; return opendir('fakedir://shared'); - } else { - if ($source = $this->getSourcePath($path)) { - $storage = OC_Filesystem::getStorage($source); - return $storage->opendir($this->getInternalPath($source)); - } + } else if ($source = $this->getSourcePath($path)) { + $storage = OC_Filesystem::getStorage($source); + return $storage->opendir($this->getInternalPath($source)); } + return false; } public function is_dir($path) { if ($path == '' || $path == '/') { return true; - } else { - if ($source = $this->getSourcePath($path)) { - $storage = OC_Filesystem::getStorage($source); - return $storage->is_dir($this->getInternalPath($source)); - } + } else if ($source = $this->getSourcePath($path)) { + $storage = OC_Filesystem::getStorage($source); + return $storage->is_dir($this->getInternalPath($source)); } + return false; } public function is_file($path) { @@ -106,6 +143,7 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { $storage = OC_Filesystem::getStorage($source); return $storage->is_file($this->getInternalPath($source)); } + return false; } public function stat($path) { @@ -114,67 +152,64 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { $stat['mtime'] = $this->filemtime($path); $stat['ctime'] = $this->filectime($path); return $stat; - } else { - if ($source = $this->getSourcePath($path)) { - $storage = OC_Filesystem::getStorage($source); - return $storage->stat($this->getInternalPath($source)); - } + } else if ($source = $this->getSourcePath($path)) { + $storage = OC_Filesystem::getStorage($source); + return $storage->stat($this->getInternalPath($source)); } + return false; } public function filetype($path) { - if ($path == "" || $path == "/") { - return "dir"; - } else { - $source = $this->getSourcePath($path); - if ($source) { - $storage = OC_Filesystem::getStorage($source); - return $storage->filetype($this->getInternalPath($source)); - } + if ($path == '' || $path == '/') { + return 'dir'; + } else if ($source = $this->getSourcePath($path)) { + $storage = OC_Filesystem::getStorage($source); + return $storage->filetype($this->getInternalPath($source)); } - + return false; } public function filesize($path) { - if ($path == "" || $path == "/" || $this->is_dir($path)) { - return $this->getFolderSize($path); - } else { - $source = $this->getSourcePath($path); - if ($source) { - $storage = OC_Filesystem::getStorage($source); - return $storage->filesize($this->getInternalPath($source)); - } + if ($path == '' || $path == '/' || $this->is_dir($path)) { + return 0; + } else if ($source = $this->getSourcePath($path)) { + $storage = OC_Filesystem::getStorage($source); + return $storage->filesize($this->getInternalPath($source)); } + return false; } public function is_readable($path) { - return true; + return $this->file_exists($path); } public function is_writable($path) { - if($path == "" || $path == "/"){ + if ($path == '' || $path == '/') { return false; - }elseif (OC_Share::getPermissions($this->sharedFolder.$path) & OC_Share::WRITE) { + // Folders need CREATE permission to be writable + } else if ($this->is_dir($path)) { + if ($this->getPermissions($path) & OCP\Share::PERMISSION_CREATE) { + return true; + } + // Files need UPDATE permission to be writable + } else if ($this->getPermissions($path) & OCP\Share::PERMISSION_UPDATE) { return true; - } else { - return false; } + return false; } public function file_exists($path) { - if ($path == "" || $path == "/") { + if ($path == '' || $path == '/') { return true; - } else { - $source = $this->getSourcePath($path); - if ($source) { - $storage = OC_Filesystem::getStorage($source); - return $storage->file_exists($this->getInternalPath($source)); - } + } else if ($source = $this->getSourcePath($path)) { + $storage = OC_Filesystem::getStorage($source); + return $storage->file_exists($this->getInternalPath($source)); } + return false; } public function filectime($path) { - if ($path == "" || $path == "/") { + if ($path == '' || $path == '/') { $ctime = 0; if ($dh = $this->opendir($path)) { while (($filename = readdir($dh)) !== false) { @@ -195,7 +230,7 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { } public function filemtime($path) { - if ($path == "" || $path == "/") { + if ($path == '' || $path == '/') { $mtime = 0; if ($dh = $this->opendir($path)) { while (($filename = readdir($dh)) !== false) { @@ -248,82 +283,19 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { } public function unlink($path) { - // The item will be removed from the database, but won't be touched on the owner's filesystem - $target = $this->sharedFolder.$path; - // Check if the item is inside a shared folder - if (OC_Share::getParentFolders($target)) { - // If entry for item already exists - if (OC_Share::getItem($target)) { - OC_Share::unshareFromMySelf($target, false); - } else { - OC_Share::pullOutOfFolder($target, $target); - OC_Share::unshareFromMySelf($target, false); - } - // Delete the database entry - } else { - OC_Share::unshareFromMySelf($target); - } - $this->clearFolderSizeCache($this->getInternalPath($target)); - return true; + // TODO } public function rename($path1, $path2) { - $oldTarget = $this->sharedFolder.$path1; - $newTarget = $this->sharedFolder.$path2; - // Check if the item is inside a shared folder - if ($folders = OC_Share::getParentFolders($oldTarget)) { - $root1 = substr($path1, 0, strpos($path1, "/")); - $root2 = substr($path1, 0, strpos($path2, "/")); - // Prevent items from being moved into different shared folders until versioning (cut and paste) and prevent items going into 'Shared' - if ($root1 !== $root2) { - return false; - // Check if both paths have write permission - } else if ($this->is_writable($path1) && $this->is_writable($path2)) { - $oldSource = $this->getSourcePath($path1); - $newSource = $folders['source'].substr($newTarget, strlen($folders['target'])); - if ($oldSource) { - $storage = OC_Filesystem::getStorage($oldSource); - return $storage->rename($this->getInternalPath($oldSource), $this->getInternalPath($newSource)); - } - // If the user doesn't have write permission, items can only be renamed and not moved - } else if (dirname($path1) !== dirname($path2)) { - return false; - // The item will be renamed in the database, but won't be touched on the owner's filesystem - } else { - OC_Share::pullOutOfFolder($oldTarget, $newTarget); - // If this is a folder being renamed, call setTarget in case there are any database entries inside the folder - if (self::is_dir($path1)) { - OC_Share::setTarget($oldTarget, $newTarget); - } - } - } else { - OC_Share::setTarget($oldTarget, $newTarget); - } - $this->clearFolderSizeCache($this->getInternalPath($oldTarget)); - $this->clearFolderSizeCache($this->getInternalPath($newTarget)); - return true; + // TODO } public function copy($path1, $path2) { - if ($path2 == "" || $path2 == "/") { - // TODO Construct new shared item or should this not be allowed? - } else { - if ($this->is_writable($path2)) { - $tmpFile = $this->toTmpFile($path1); - $result = $this->fromTmpFile($tmpFile, $path2); - if ($result) { - $this->clearFolderSizeCache($path2); - } - return $result; - } else { - return false; - } - } + // TODO } public function fopen($path, $mode) { - $source = $this->getSourcePath($path); - if ($source) { + if ($source = $this->getSourcePath($path)) { $info = array( 'target' => $this->sharedFolder.$path, 'source' => $source, @@ -333,46 +305,30 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { $storage = OC_Filesystem::getStorage($source); return $storage->fopen($this->getInternalPath($source), $mode); } + return false; } public function toTmpFile($path) { - $source = $this->getSourcePath($path); - if ($source) { + if ($source = $this->getSourcePath($path)) { $storage = OC_Filesystem::getStorage($source); return $storage->toTmpFile($this->getInternalPath($source)); } - } - - public function fromTmpFile($tmpFile, $path) { - if ($this->is_writable($path)) { - $source = $this->getSourcePath($path); - if ($source) { - $storage = OC_Filesystem::getStorage($source); - $result = $storage->fromTmpFile($tmpFile, $this->getInternalPath($source)); - if ($result) { - $this->clearFolderSizeCache($path); - } - return $result; - } - } else { - return false; - } + return false; } public function getMimeType($path) { - if ($path == "" || $path == "/") { + if ($path == '' || $path == '/') { return 'httpd/unix-directory'; } - $source = $this->getSourcePath($path); - if ($source) { + if ($source = $this->getSourcePath($path)) { $storage = OC_Filesystem::getStorage($source); return $storage->getMimeType($this->getInternalPath($source)); } + return false; } public function hash($type, $path, $raw) { - $source = $this->getSourcePath($path); - if ($source) { + if ($source = $this->getSourcePath($path)) { $storage = OC_Filesystem::getStorage($source); return $storage->hash($type, $this->getInternalPath($source), $raw); } @@ -385,41 +341,20 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { return $storage->free_space($this->getInternalPath($source)); } } - - public function search($query) { - return $this->searchInDir($query); - } - - protected function searchInDir($query, $path = "") { - $files = array(); - if ($dh = $this->opendir($path)) { - while (($filename = readdir($dh)) !== false) { - if ($filename != "." && $filename != "..") { - if (strstr(strtolower($filename), strtolower($query))) { - $files[] = $path."/".$filename; - } - if ($this->is_dir($path."/".$filename)) { - $files = array_merge($files, $this->searchInDir($query, $path."/".$filename)); - } - } - } - } - return $files; - } public function getLocalFile($path) { - $source = $this->getSourcePath($path); - if ($source) { + if ($source = $this->getSourcePath($path)) { $storage = OC_Filesystem::getStorage($source); return $storage->getLocalFile($this->getInternalPath($source)); } + return false; } - public function touch($path, $mtime=null){ - $source = $this->getSourcePath($path); - if ($source) { + public function touch($path, $mtime = null) { + if ($source = $this->getSourcePath($path)) { $storage = OC_Filesystem::getStorage($source); - return $storage->touch($this->getInternalPath($source),$time); + return $storage->touch($this->getInternalPath($source), $mtime); } + return false; } public static function setup($options) { @@ -436,4 +371,4 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { //TODO return false; } -} +} \ No newline at end of file -- cgit v1.2.3 From 4d17ed2f71c8cbb0d34c039aa7953b2427ce5c78 Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Wed, 25 Jul 2012 16:33:08 -0400 Subject: Make file actions permissions aware --- apps/calendar/js/loader.js | 2 +- apps/contacts/js/loader.js | 4 +-- apps/files/js/fileactions.js | 54 ++++++++++++++++++++------------- apps/files/js/files.js | 5 +-- apps/files/templates/part.list.php | 3 +- apps/files_archive/js/archive.js | 4 +-- apps/files_imageviewer/js/lightbox.js | 2 +- apps/files_pdfviewer/js/viewer.js | 2 +- apps/files_sharing/lib/share/file.php | 11 +++++-- apps/files_sharing/lib/share/folder.php | 13 +++++++- apps/files_texteditor/js/editor.js | 16 +++++++--- apps/files_versions/js/versions.js | 2 +- apps/media/js/loader.js | 4 +-- core/js/share.js | 2 +- lib/files.php | 18 ++++++++--- 15 files changed, 94 insertions(+), 48 deletions(-) (limited to 'apps/files_sharing/lib') diff --git a/apps/calendar/js/loader.js b/apps/calendar/js/loader.js index cef95afc3aa..57cf5adff0e 100644 --- a/apps/calendar/js/loader.js +++ b/apps/calendar/js/loader.js @@ -75,7 +75,7 @@ Calendar_Import={ } $(document).ready(function(){ if(typeof FileActions !== 'undefined'){ - FileActions.register('text/calendar','importcal', '', Calendar_Import.importdialog); + FileActions.register('text/calendar','importcal', FileActions.PERMISSION_READ, '', Calendar_Import.importdialog); FileActions.setDefault('text/calendar','importcal'); }; }); diff --git a/apps/contacts/js/loader.js b/apps/contacts/js/loader.js index 577ad103064..3b1f4070485 100644 --- a/apps/contacts/js/loader.js +++ b/apps/contacts/js/loader.js @@ -78,9 +78,9 @@ Contacts_Import={ } $(document).ready(function(){ if(typeof FileActions !== 'undefined'){ - FileActions.register('text/vcard','importaddressbook', '', Contacts_Import.importdialog); + FileActions.register('text/vcard','importaddressbook', FileActions.PERMISSION_READ, '', Contacts_Import.importdialog); FileActions.setDefault('text/vcard','importaddressbook'); - FileActions.register('text/x-vcard','importaddressbook', '', Contacts_Import.importdialog); + FileActions.register('text/x-vcard','importaddressbook', FileActions.PERMISSION_READ, '', Contacts_Import.importdialog); FileActions.setDefault('text/x-vcard','importaddressbook'); }; }); \ No newline at end of file diff --git a/apps/files/js/fileactions.js b/apps/files/js/fileactions.js index 4dc05088eed..d54dd466469 100644 --- a/apps/files/js/fileactions.js +++ b/apps/files/js/fileactions.js @@ -1,19 +1,28 @@ FileActions={ + PERMISSION_CREATE:4, + PERMISSION_READ:1, + PERMISSION_UPDATE:2, + PERMISSION_DELETE:8, + PERMISSION_SHARE:16, actions:{}, defaults:{}, icons:{}, currentFile:null, - register:function(mime,name,icon,action){ + register:function(mime,name,permissions,icon,action){ if(!FileActions.actions[mime]){ FileActions.actions[mime]={}; } - FileActions.actions[mime][name]=action; + if (!FileActions.actions[mime][name]) { + FileActions.actions[mime][name] = {}; + } + FileActions.actions[mime][name]['action'] = action; + FileActions.actions[mime][name]['permissions'] = permissions; FileActions.icons[name]=icon; }, setDefault:function(mime,name){ FileActions.defaults[mime]=name; }, - get:function(mime,type){ + get:function(mime,type,permissions){ var actions={}; if(FileActions.actions.all){ actions=$.extend( actions, FileActions.actions.all ) @@ -32,9 +41,15 @@ FileActions={ actions=$.extend( actions, FileActions.actions[type] ) } } - return actions; + var filteredActions = {}; + $.each(actions, function(name, action) { + if (action.permissions & permissions) { + filteredActions[name] = action.action; + } + }); + return filteredActions; }, - getDefault:function(mime,type){ + getDefault:function(mime,type,permissions){ if(mime){ var mimePart=mime.substr(0,mime.indexOf('/')); } @@ -48,22 +63,20 @@ FileActions={ }else{ name=FileActions.defaults.all; } - var actions=this.get(mime,type); + var actions=this.get(mime,type,permissions); return actions[name]; }, - display:function(parent, filename, type){ + display:function(parent){ FileActions.currentFile=parent; $('#fileList span.fileactions, #fileList td.date a.action').remove(); - var actions=FileActions.get(FileActions.getCurrentMimeType(),FileActions.getCurrentType()); + var actions=FileActions.get(FileActions.getCurrentMimeType(),FileActions.getCurrentType(), FileActions.getCurrentPermissions()); var file=FileActions.getCurrentFile(); if($('tr').filterAttr('data-file',file).data('renaming')){ return; } parent.children('a.name').append(''); - var defaultAction=FileActions.getDefault(FileActions.getCurrentMimeType(),FileActions.getCurrentType()); + var defaultAction=FileActions.getDefault(FileActions.getCurrentMimeType(),FileActions.getCurrentType(), FileActions.getCurrentPermissions()); for(name in actions){ - // no rename and share action for the 'Shared' dir - if((name=='Rename' || name =='Share') && type=='dir' && filename=='Shared') { continue; } if((name=='Download' || actions[name]!=defaultAction) && name!='Delete'){ var img=FileActions.icons[name]; if(img.call){ @@ -86,16 +99,12 @@ FileActions={ parent.find('a.name>span.fileactions').append(element); } } - if(actions['Delete'] && (type!='dir' || filename != 'Shared')){ // no delete action for the 'Shared' dir + if(actions['Delete']){ var img=FileActions.icons['Delete']; if(img.call){ img=img(file); } - if ($('#dir').val().indexOf('Shared') != -1) { - var html=' diff --git a/apps/files_archive/js/archive.js b/apps/files_archive/js/archive.js index 9fb9853e299..6bcbe092662 100644 --- a/apps/files_archive/js/archive.js +++ b/apps/files_archive/js/archive.js @@ -7,11 +7,11 @@ $(document).ready(function() { if(typeof FileActions!=='undefined'){ - FileActions.register('application/zip','Open','',function(filename){ + FileActions.register('application/zip','Open', FileActions.PERMISSION_READ, '',function(filename){ window.location=OC.linkTo('files', 'index.php')+'&dir='+encodeURIComponent($('#dir').val()).replace(/%2F/g, '/')+'/'+encodeURIComponent(filename); }); FileActions.setDefault('application/zip','Open'); - FileActions.register('application/x-gzip','Open','',function(filename){ + FileActions.register('application/x-gzip','Open', FileActions.PERMISSION_READ, '',function(filename){ window.location=OC.linkTo('files', 'index.php')+'&dir='+encodeURIComponent($('#dir').val()).replace(/%2F/g, '/')+'/'+encodeURIComponent(filename); }); FileActions.setDefault('application/x-gzip','Open'); diff --git a/apps/files_imageviewer/js/lightbox.js b/apps/files_imageviewer/js/lightbox.js index 31f08456d22..ff12d808bc8 100644 --- a/apps/files_imageviewer/js/lightbox.js +++ b/apps/files_imageviewer/js/lightbox.js @@ -1,6 +1,6 @@ $(document).ready(function() { if(typeof FileActions!=='undefined'){ - FileActions.register('image','View','',function(filename){ + FileActions.register('image','View', FileActions.PERMISSION_READ, '',function(filename){ viewImage($('#dir').val(),filename); }); FileActions.setDefault('image','View'); diff --git a/apps/files_pdfviewer/js/viewer.js b/apps/files_pdfviewer/js/viewer.js index 2c9cbb9b431..29db2ea7f24 100644 --- a/apps/files_pdfviewer/js/viewer.js +++ b/apps/files_pdfviewer/js/viewer.js @@ -40,7 +40,7 @@ $(document).ready(function(){ if(location.href.indexOf("files")!=-1) { PDFJS.workerSrc = OC.filePath('files_pdfviewer','js','pdfjs/build/pdf.js'); if(typeof FileActions!=='undefined'){ - FileActions.register('application/pdf','Edit','',function(filename){ + FileActions.register('application/pdf','Edit', FileActions.PERMISSION_READ, '',function(filename){ showPDFviewer($('#dir').val(),filename); }); FileActions.setDefault('application/pdf','Edit'); diff --git a/apps/files_sharing/lib/share/file.php b/apps/files_sharing/lib/share/file.php index 658c42ee327..dd63539cdfe 100644 --- a/apps/files_sharing/lib/share/file.php +++ b/apps/files_sharing/lib/share/file.php @@ -70,7 +70,14 @@ class OC_Share_Backend_File extends OCP\Share_Backend { // Set target path $file['path'] = $shares[$file['id']]['file_target']; $file['name'] = basename($file['path']); - // TODO Set permissions: $file['writable'] + $file['directory'] = $parameters['folder']; + $file['type'] = ($file['mimetype'] == 'httpd/unix-directory') ? 'dir' : 'file'; + $permissions = $shares[$file['id']]['permissions']; + if ($file['type'] == 'file') { + // Remove Create permission if type is file + $permissions &= ~OCP\Share::PERMISSION_CREATE; + } + $file['permissions'] = $permissions; $files[] = $file; } return $files; @@ -85,7 +92,7 @@ class OC_Share_Backend_File extends OCP\Share_Backend { } $size += $file['size']; } - return array(0 => array('name' => 'Shared', 'mtime' => $mtime, 'mimetype' => 'httpd/unix-directory', 'size' => $size, 'writable' => false)); + return array(0 => array('name' => 'Shared', 'mtime' => $mtime, 'mimetype' => 'httpd/unix-directory', 'size' => $size, 'writable' => false, 'type' => 'dir', 'directory' => '', 'permissions' => OCP\Share::PERMISSION_READ)); } } return array(); diff --git a/apps/files_sharing/lib/share/folder.php b/apps/files_sharing/lib/share/folder.php index 033e2ba9667..a43ce2b2caf 100644 --- a/apps/files_sharing/lib/share/folder.php +++ b/apps/files_sharing/lib/share/folder.php @@ -43,7 +43,18 @@ class OC_Share_Backend_Folder extends OC_Share_Backend_File { $pos = strpos($result['path'], $folder['item']); $path = substr($result['path'], $pos).substr($parameters['folder'], strlen($folder['file_target'])); $root = substr($result['path'], 0, $pos); - return OC_FileCache::getFolderContent($path, $root, $mimetype_filter); + $files = OC_FileCache::getFolderContent($path, $root, $mimetype_filter); + foreach ($files as &$file) { + $file['directory'] = $parameters['folder']; + $file['type'] = ($file['mimetype'] == 'httpd/unix-directory') ? 'dir' : 'file'; + $permissions = $folder['permissions']; + if ($file['type'] == 'file') { + // Remove Create permission if type is file + $permissions &= ~OCP\Share::PERMISSION_CREATE; + } + $file['permissions'] = $permissions; + } + return $files; } }/* else if ($format == self::FORMAT_OPENDIR_ROOT) { $query = OCP\DB::prepare('SELECT name FROM *PREFIX*fscache WHERE id IN ('.$ids.')'); diff --git a/apps/files_texteditor/js/editor.js b/apps/files_texteditor/js/editor.js index 70bb74a9101..3784ea1032f 100644 --- a/apps/files_texteditor/js/editor.js +++ b/apps/files_texteditor/js/editor.js @@ -222,9 +222,17 @@ function showFileEditor(dir,filename){ } }); // Add the ctrl+s event - window.aceEditor.commands.addCommand({ name: "save", bindKey: { win: "Ctrl-S", mac: "Command-S", sender: "editor" }, exec: function(){ + window.aceEditor.commands.addCommand({ + name: "save", + bindKey: { + win: "Ctrl-S", + mac: "Command-S", + sender: "editor" + }, + exec: function(){ doFileSave(); - } }); + } + }); }); } else { // Failed to get the file. @@ -297,11 +305,11 @@ $(window).resize(function() { var is_editor_shown = false; $(document).ready(function(){ if(typeof FileActions!=='undefined'){ - FileActions.register('text','Edit','',function(filename){ + FileActions.register('text','Edit', FileActions.PERMISSION_READ, '',function(filename){ showFileEditor($('#dir').val(),filename); }); FileActions.setDefault('text','Edit'); - FileActions.register('application/xml','Edit','',function(filename){ + FileActions.register('application/xml','Edit', FileActions.PERMISSION_READ, '',function(filename){ showFileEditor($('#dir').val(),filename); }); FileActions.setDefault('application/xml','Edit'); diff --git a/apps/files_versions/js/versions.js b/apps/files_versions/js/versions.js index a090fde446e..c5c1553f1a8 100644 --- a/apps/files_versions/js/versions.js +++ b/apps/files_versions/js/versions.js @@ -11,7 +11,7 @@ $(document).ready(function() { $(document).ready(function(){ if (typeof FileActions !== 'undefined') { // Add history button to files/index.php - FileActions.register('file','History',function(){return OC.imagePath('core','actions/history')},function(filename){ + FileActions.register('file','History', FileActions.PERMISSION_UPDATE, function(){return OC.imagePath('core','actions/history')},function(filename){ if (scanFiles.scanning){return;}//workaround to prevent additional http request block scanning feedback diff --git a/apps/media/js/loader.js b/apps/media/js/loader.js index 393f8ba914e..ffe9c1cdd61 100644 --- a/apps/media/js/loader.js +++ b/apps/media/js/loader.js @@ -45,8 +45,8 @@ $(document).ready(function() { // FileActions.register('application/ogg','Add to playlist','',addAudio); if(typeof FileActions!=='undefined'){ - FileActions.register('audio','Play','',playAudio); - FileActions.register('application/ogg','','Play',playAudio); + FileActions.register('audio','Play', FileActions.PERMISSION_READ, '',playAudio); + FileActions.register('application/ogg', FileActions.PERMISSION_READ, '','Play',playAudio); FileActions.setDefault('audio','Play'); FileActions.setDefault('application/ogg','Play'); } diff --git a/core/js/share.js b/core/js/share.js index 42002ea7985..4cff9ec1a67 100644 --- a/core/js/share.js +++ b/core/js/share.js @@ -164,7 +164,7 @@ $(document).ready(function() { if (typeof FileActions !== 'undefined') { OC.Share.loadIcons('file'); - FileActions.register('all', 'Share', function(filename) { + FileActions.register('all', 'Share', FileActions.PERMISSION_SHARE, function(filename) { // Return the correct sharing icon if (scanFiles.scanning) { return; } // workaround to prevent additional http request block scanning feedback var item = $('#dir').val() + '/' + filename; diff --git a/lib/files.php b/lib/files.php index f7a7aecc167..fee71b777b3 100644 --- a/lib/files.php +++ b/lib/files.php @@ -36,7 +36,7 @@ class OC_Files { $files = array(); if (substr($directory, 0, 7) == '/Shared') { if ($directory == '/Shared') { - $files = OCP\Share::getItemsSharedWith('file', OC_Share_Backend_File::FORMAT_FILE_APP, array('mimetype_filter' => $mimetype_filter)); + $files = OCP\Share::getItemsSharedWith('file', OC_Share_Backend_File::FORMAT_FILE_APP, array('folder' => $directory, 'mimetype_filter' => $mimetype_filter)); } else { $pos = strpos($directory, '/', 8); // Get shared folder name @@ -49,15 +49,23 @@ class OC_Files { } } else { $files = OC_FileCache::getFolderContent($directory, false, $mimetype_filter); + foreach ($files as &$file) { + $file['directory'] = $directory; + $file['type'] = ($file['mimetype'] == 'httpd/unix-directory') ? 'dir' : 'file'; + $permissions = OCP\Share::PERMISSION_READ | OCP\Share::PERMISSION_SHARE; + if ($file['type'] == 'dir' && $file['writable']) { + $permissions |= OCP\Share::PERMISSION_CREATE; + } + if ($file['writable']) { + $permissions |= OCP\Share::PERMISSION_UPDATE | OCP\Share::PERMISSION_DELETE; + } + $file['permissions'] = $permissions; + } if ($directory == '') { // Add 'Shared' folder $files = array_merge($files, OCP\Share::getItemsSharedWith('file', OC_Share_Backend_File::FORMAT_FILE_APP_ROOT)); } } - foreach ($files as &$file) { - $file['directory'] = $directory; - $file['type'] = ($file['mimetype'] == 'httpd/unix-directory') ? 'dir' : 'file'; - } usort($files, "fileCmp");//TODO: remove this once ajax is merged return $files; } -- cgit v1.2.3 From 3b2c853916fb75968ae759e54bb61a8095debe7e Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Mon, 6 Aug 2012 11:27:13 -0400 Subject: Lots of refactoring to share API --- apps/contacts/appinfo/app.php | 8 +- apps/contacts/lib/addressbook.php | 2 +- apps/files_sharing/lib/share/file.php | 18 +- apps/files_sharing/lib/share/folder.php | 9 +- apps/files_sharing/sharedstorage.php | 2 +- apps/gallery/appinfo/app.php | 2 +- apps/gallery/lib/share.php | 2 +- core/js/share.js | 25 +-- lib/public/share.php | 326 +++++++++++++++++++------------- 9 files changed, 238 insertions(+), 156 deletions(-) (limited to 'apps/files_sharing/lib') diff --git a/apps/contacts/appinfo/app.php b/apps/contacts/appinfo/app.php index aa0f229dd9f..3c32f7f5c93 100644 --- a/apps/contacts/appinfo/app.php +++ b/apps/contacts/appinfo/app.php @@ -3,8 +3,8 @@ OC::$CLASSPATH['OC_Contacts_App'] = 'apps/contacts/lib/app.php'; OC::$CLASSPATH['OC_Contacts_Addressbook'] = 'apps/contacts/lib/addressbook.php'; OC::$CLASSPATH['OC_Contacts_VCard'] = 'apps/contacts/lib/vcard.php'; OC::$CLASSPATH['OC_Contacts_Hooks'] = 'apps/contacts/lib/hooks.php'; -OC::$CLASSPATH['OC_Share_Contact_Backend'] = 'apps/contacts/lib/share/contact.php'; -OC::$CLASSPATH['OC_Share_Addressbook_Backend'] = 'apps/contacts/lib/share/addressbook.php'; +OC::$CLASSPATH['OC_Share_Backend_Contact'] = 'apps/contacts/lib/share/contact.php'; +OC::$CLASSPATH['OC_Share_Backend_Addressbook'] = 'apps/contacts/lib/share/addressbook.php'; OC::$CLASSPATH['OC_Connector_Sabre_CardDAV'] = 'apps/contacts/lib/connector_sabre.php'; OC::$CLASSPATH['Sabre_CardDAV_VCFExportPlugin'] = 'apps/contacts/lib/VCFExportPlugin.php'; OC::$CLASSPATH['OC_Search_Provider_Contacts'] = 'apps/contacts/lib/search.php'; @@ -24,6 +24,6 @@ OCP\App::addNavigationEntry( array( OCP\App::registerPersonal('contacts', 'settings'); OCP\Util::addscript('contacts', 'loader'); OC_Search::registerProvider('OC_Search_Provider_Contacts'); -OCP\Share::registerBackend('contact', 'OC_Share_Contact_Backend'); -OCP\Share::registerBackend('addressbook', 'OC_Share_Addressbook_Backend', 'contact'); +OCP\Share::registerBackend('contact', 'OC_Share_Backend_Contact'); +OCP\Share::registerBackend('addressbook', 'OC_Share_Backend_Addressbook', 'contact'); diff --git a/apps/contacts/lib/addressbook.php b/apps/contacts/lib/addressbook.php index d3cceb58807..a81b1f77985 100644 --- a/apps/contacts/lib/addressbook.php +++ b/apps/contacts/lib/addressbook.php @@ -64,7 +64,7 @@ class OC_Contacts_Addressbook { while( $row = $result->fetchRow()) { $addressbooks[] = $row; } - $addressbooks = array_merge($addressbooks, OCP\Share::getItemsSharedWith('addressbook', OC_Share_Addressbook_Backend::FORMAT_ADDRESSBOOKS)); + $addressbooks = array_merge($addressbooks, OCP\Share::getItemsSharedWith('addressbook', OC_Share_Backend_Addressbook::FORMAT_ADDRESSBOOKS)); if(!$active && !count($addressbooks)) { self::addDefault($uid); } diff --git a/apps/files_sharing/lib/share/file.php b/apps/files_sharing/lib/share/file.php index dd63539cdfe..927e9462f23 100644 --- a/apps/files_sharing/lib/share/file.php +++ b/apps/files_sharing/lib/share/file.php @@ -20,23 +20,27 @@ * License along with this library. If not, see . */ -class OC_Share_Backend_File extends OCP\Share_Backend { +class OC_Share_Backend_File implements OCP\Share_Backend_File_Dependent { const FORMAT_SHARED_STORAGE = 0; const FORMAT_FILE_APP = 1; const FORMAT_FILE_APP_ROOT = 2; const FORMAT_OPENDIR = 3; - public function getSource($item, $uid) { - if (OC_Filesystem::file_exists($item)) { - return array('item' => null, 'file' => $item); - } - return false; + public function isValidSource($item, $uid) { +// if (OC_Filesystem::file_exists($item)) { + return true; +// } +// return false; + } + + public function getFilePath($item, $uid) { + return $item; } public function generateTarget($item, $uid, $exclude = null) { // TODO Make sure target path doesn't exist already - return '/Shared'.$item; + return $item; } public function formatItems($items, $format, $parameters = null) { diff --git a/apps/files_sharing/lib/share/folder.php b/apps/files_sharing/lib/share/folder.php index a43ce2b2caf..2f101d33c8a 100644 --- a/apps/files_sharing/lib/share/folder.php +++ b/apps/files_sharing/lib/share/folder.php @@ -25,8 +25,13 @@ class OC_Share_Backend_Folder extends OC_Share_Backend_File { // TODO } - public function getChildrenSources($item) { - return OC_FileCache::getFolderContent($item); + public function getChildren($itemSource) { + $files = OC_FileCache::getFolderContent($itemSource); + $sources = array(); + foreach ($files as $file) { + $sources[] = $file['path']; + } + return $sources; } public function formatItems($items, $format, $parameters = null) { diff --git a/apps/files_sharing/sharedstorage.php b/apps/files_sharing/sharedstorage.php index 2071942b062..6e37837aac9 100644 --- a/apps/files_sharing/sharedstorage.php +++ b/apps/files_sharing/sharedstorage.php @@ -38,7 +38,7 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { * @return Returns array with the keys path and permissions or false if not found */ private function getFile($target) { - $target = $this->sharedFolder.'/'.$target; + $target = '/'.$target; $target = rtrim($target, '/'); if (isset($this->files[$target])) { return $this->files[$target]; diff --git a/apps/gallery/appinfo/app.php b/apps/gallery/appinfo/app.php index 9efb4346c3e..9103f66441d 100644 --- a/apps/gallery/appinfo/app.php +++ b/apps/gallery/appinfo/app.php @@ -30,7 +30,7 @@ OC::$CLASSPATH['Pictures_Managers'] = 'gallery/lib/managers.php'; OC::$CLASSPATH['Pictures_Tiles'] = 'gallery/lib/tiles.php'; OC::$CLASSPATH['OC_Share_Backend_Photo'] = 'gallery/lib/share.php'; -OCP\Share::registerBackend('photo', new OC_Share_Backend_Photo()); +// OCP\Share::registerBackend('photo', new OC_Share_Backend_Photo()); $l = OC_L10N::get('gallery'); diff --git a/apps/gallery/lib/share.php b/apps/gallery/lib/share.php index 6f3db45375f..d6c5f40d492 100644 --- a/apps/gallery/lib/share.php +++ b/apps/gallery/lib/share.php @@ -19,7 +19,7 @@ * License along with this library. If not, see . */ -class OC_Share_Backend_Photo extends OCP\Share_Backend { +abstract class OC_Share_Photo_Backend implements OCP\Share_Backend { public $dependsOn = 'file'; public $supportedFileExtensions = array('jpg', 'png', 'gif'); diff --git a/core/js/share.js b/core/js/share.js index e3dfd27e760..dd790d004ee 100644 --- a/core/js/share.js +++ b/core/js/share.js @@ -16,28 +16,29 @@ OC.Share={ if (result && result.status === 'success') { $.each(result.data, function(item, hasPrivateLink) { // Private links override shared in terms of icon display - if (itemType == 'file') { - OC.Share.statuses[item] = hasPrivateLink; - } else { + if (itemType != 'file' && itemType != 'folder') { if (hasPrivateLink) { $('.share').find('[data-item="'+item+'"]').attr('src', OC.imagePath('core', 'actions/public')); } else { $('.share').find('[data-item="'+item+'"]').attr('src', OC.imagePath('core', 'actions/shared')); } } + OC.Share.statuses[item] = hasPrivateLink; }); } }); }, loadItem:function(itemType, item) { var data = ''; - $.ajax({type: 'GET', url: OC.filePath('core', 'ajax', 'share.php'), data: { fetch: 'getItem', itemType: itemType, item: item }, async: false, success: function(result) { - if (result && result.status === 'success') { - data = result.data; - } else { - data = false; - } - }}); +// if (typeof OC.Share.statuses[item] !== 'undefined') { + $.ajax({type: 'GET', url: OC.filePath('core', 'ajax', 'share.php'), data: { fetch: 'getItem', itemType: itemType, item: item }, async: false, success: function(result) { + if (result && result.status === 'success') { + data = result.data; + } else { + data = false; + } + }}); +// } return data; }, share:function(itemType, item, shareType, shareWith, permissions, callback) { @@ -241,12 +242,12 @@ $(document).ready(function() { }); if (typeof FileActions !== 'undefined') { - OC.Share.loadIcons('file'); + OC.Share.loadIcons('folder'); FileActions.register('all', 'Share', FileActions.PERMISSION_SHARE, function(filename) { // Return the correct sharing icon if (scanFiles.scanning) { return; } // workaround to prevent additional http request block scanning feedback - var item = $('#dir').val() + '/' + filename; + var item = $('#dir').val() + '/' + filename; // Check if status is in cache if (OC.Share.statuses[item] === true) { return OC.imagePath('core', 'actions/public'); diff --git a/lib/public/share.php b/lib/public/share.php index 813b5558e8d..1ebbbccc901 100644 --- a/lib/public/share.php +++ b/lib/public/share.php @@ -27,7 +27,7 @@ namespace OCP; /** * This class provides the ability for apps to share their content between users. -* Apps must create a backend class that extends OCP\Share_Backend and register it with this class. +* Apps must create a backend class that implements OCP\Share_Backend and register it with this class. */ class Share { @@ -60,7 +60,7 @@ class Share { private static $backendTypes = array(); /** - * @brief Register a sharing backend class that extends OCP\Share_Backend for an item type + * @brief Register a sharing backend class that implements OCP\Share_Backend for an item type * @param string Item type * @param string Backend class * @param string (optional) Depends on item type @@ -112,41 +112,41 @@ class Share { /** * @brief Get the shared item of item type owned by the current user * @param string Item type - * @param string Item + * @param string Item source * @param int Format (optional) Format type must be defined by the backend * @return Return depends on format */ - public static function getItemShared($itemType, $item, $format = self::FORMAT_NONE, $parameters = null, $includeCollections = false) { - return self::getItems($itemType, $item, null, null, \OC_User::getUser(), $format, $parameters, -1, $includeCollections); + public static function getItemShared($itemType, $itemSource, $format = self::FORMAT_NONE, $parameters = null, $includeCollections = false) { + return self::getItems($itemType, $itemSource, null, null, \OC_User::getUser(), $format, $parameters, -1, $includeCollections); } /** * @brief Share an item with a user, group, or via private link * @param string Item type - * @param string Item + * @param string Item source * @param int SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_PRIVATE_LINK * @param string User or group the item is being shared with * @param int CRUDS permissions * @return bool Returns true on success or false on failure */ - public static function share($itemType, $item, $shareType, $shareWith, $permissions) { + public static function share($itemType, $itemSource, $shareType, $shareWith, $permissions) { $uidOwner = \OC_User::getUser(); // Verify share type and sharing conditions are met switch ($shareType) { case self::SHARE_TYPE_USER: if ($shareWith == $uidOwner) { - $message = 'Sharing '.$item.' failed, because the user '.$shareWith.' is the item owner'; + $message = 'Sharing '.$itemSource.' failed, because the user '.$shareWith.' is the itemSource owner'; \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); throw new \Exception($message); } if (!\OC_User::userExists($shareWith)) { - $message = 'Sharing '.$item.' failed, because the user '.$shareWith.' does not exist'; + $message = 'Sharing '.$itemSource.' failed, because the user '.$shareWith.' does not exist'; \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); throw new \Exception($message); } else { $inGroup = array_intersect(\OC_Group::getUserGroups($uidOwner), \OC_Group::getUserGroups($shareWith)); if (empty($inGroup)) { - $message = 'Sharing '.$item.' failed, because the user '.$shareWith.' is not a member of any groups that '.$uidOwner.' is a member of'; + $message = 'Sharing '.$itemSource.' failed, because the user '.$shareWith.' is not a member of any groups that '.$uidOwner.' is a member of'; \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); throw new \Exception($message); } @@ -154,11 +154,11 @@ class Share { break; case self::SHARE_TYPE_GROUP: if (!\OC_Group::groupExists($shareWith)) { - $message = 'Sharing '.$item.' failed, because the group '.$shareWith.' does not exist'; + $message = 'Sharing '.$itemSource.' failed, because the group '.$shareWith.' does not exist'; \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); throw new \Exception($message); } else if (!\OC_Group::inGroup($uidOwner, $shareWith)) { - $message = 'Sharing '.$item.' failed, because '.$uidOwner.' is not a member of the group '.$shareWith; + $message = 'Sharing '.$itemSource.' failed, because '.$uidOwner.' is not a member of the group '.$shareWith; \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); throw new \Exception($message); } @@ -169,45 +169,46 @@ class Share { $shareWith['users'] = array_diff(\OC_Group::usersInGroup($group), array($uidOwner)); break; case self::SHARE_TYPE_PRIVATE_LINK: - $shareWith = md5(uniqid($item, true)); - return self::put($itemType, $item, $shareType, $shareWith, $uidOwner, $permissions); + $shareWith = md5(uniqid($itemSource, true)); + return self::put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions); case self::SHARE_TYPE_CONTACT: if (!\OC_App::isEnabled('contacts')) { - $message = 'Sharing '.$item.' failed, because the contacts app is not enabled'; + $message = 'Sharing '.$itemSource.' failed, because the contacts app is not enabled'; \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); return false; } $vcard = \OC_Contacts_App::getContactVCard($shareWith); if (!isset($vcard)) { - $message = 'Sharing '.$item.' failed, because the contact does not exist'; + $message = 'Sharing '.$itemSource.' failed, because the contact does not exist'; \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); throw new \Exception($message); } $details = \OC_Contacts_VCard::structureContact($vcard); // TODO Add ownCloud user to contacts vcard if (!isset($details['EMAIL'])) { - $message = 'Sharing '.$item.' failed, because no email address is associated with the contact'; + $message = 'Sharing '.$itemSource.' failed, because no email address is associated with the contact'; \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); throw new \Exception($message); } - return self::share($itemType, $item, self::SHARE_TYPE_EMAIL, $details['EMAIL'], $permissions); + return self::share($itemType, $itemSource, self::SHARE_TYPE_EMAIL, $details['EMAIL'], $permissions); break; // Future share types need to include their own conditions default: - \OC_Log::write('OCP\Share', 'Share type '.$shareType.' is not valid for '.$item, \OC_Log::ERROR); + \OC_Log::write('OCP\Share', 'Share type '.$shareType.' is not valid for '.$itemSource, \OC_Log::ERROR); return false; } - if (self::getItems($itemType, $item, $shareType, $shareWith, $uidOwner, self::FORMAT_NONE, null, 1, true)) { - $message = 'Sharing '.$item.' failed, because this item is already shared with '.$shareWith; + // TODO This query has pretty bad performance if there are large collections, figure out a way to make the collection searching more efficient + if (self::getItems($itemType, $itemSource, $shareType, $shareWith, $uidOwner, self::FORMAT_NONE, null, 1, true)) { + $message = 'Sharing '.$itemSource.' failed, because this itemSource is already shared with '.$shareWith; \OC_Log::write('OCP\Share', $message, \OC_Log::ERROR); throw new \Exception($message); } // If the item is a folder, scan through the folder looking for equivalent item types if ($itemType == 'folder') { - $parentFolder = self::put('folder', $item, $shareType, $shareWith, $uidOwner, $permissions, true); - if ($parentFolder && $files = \OC_Files::getDirectoryContent($item)) { + $parentFolder = self::put('folder', $itemSource, $shareType, $shareWith, $uidOwner, $permissions, true); + if ($parentFolder && $files = \OC_Files::getDirectoryContent($itemSource)) { for ($i = 0; $i < count($files); $i++) { - $name = substr($files[$i]['name'], strpos($files[$i]['name'], $item) - strlen($item)); + $name = substr($files[$i]['name'], strpos($files[$i]['name'], $itemSource) - strlen($itemSource)); if ($files[$i]['mimetype'] == 'httpd/unix-directory' && $children = \OC_Files::getDirectoryContent($name, '/')) { // Continue scanning into child folders array_push($files, $children); @@ -221,20 +222,20 @@ class Share { return false; } else { // Put the item into the database - return self::put($itemType, $item, $shareType, $shareWith, $uidOwner, $permissions); + return self::put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions); } } /** * @brief Unshare an item from a user, group, or delete a private link * @param string Item type - * @param string Item + * @param string Item source * @param int SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_PRIVATE_LINK * @param string User or group the item is being shared with * @return Returns true on success or false on failure */ - public static function unshare($itemType, $item, $shareType, $shareWith) { - if ($item = self::getItems($itemType, $item, $shareType, $shareWith, \OC_User::getUser(), self::FORMAT_NONE, null, 1, false)) { + public static function unshare($itemType, $itemSource, $shareType, $shareWith) { + if ($item = self::getItems($itemType, $itemSource, $shareType, $shareWith, \OC_User::getUser(), self::FORMAT_NONE, null, 1, false)) { self::delete($item['id']); return true; } @@ -315,20 +316,20 @@ class Share { /** * @brief Set the permissions of an item for a specific user or group * @param string Item type - * @param string Item + * @param string Item source * @param int SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_PRIVATE_LINK * @param string User or group the item is being shared with * @param int CRUDS permissions * @return Returns true on success or false on failure */ - public static function setPermissions($itemType, $item, $shareType, $shareWith, $permissions) { - if ($item = self::getItems($itemType, $item, $shareType, $shareWith, \OC_User::getUser(), self::FORMAT_NONE, null, 1, false)) { + public static function setPermissions($itemType, $itemSource, $shareType, $shareWith, $permissions) { + if ($item = self::getItems($itemType, $itemSource, $shareType, $shareWith, \OC_User::getUser(), self::FORMAT_NONE, null, 1, false)) { // Check if this item is a reshare and verify that the permissions granted don't exceed the parent shared item if (isset($item['parent'])) { $query = \OC_DB::prepare('SELECT permissions FROM *PREFIX*share WHERE id = ? LIMIT 1'); $result = $query->execute(array($item['parent']))->fetchRow(); if (~(int)$result['permissions'] & $permissions) { - \OC_Log::write('OCP\Share', 'Setting permissions for '.$item.' failed, because the permissions exceed permissions granted to the parent item', \OC_Log::ERROR); + \OC_Log::write('OCP\Share', 'Setting permissions for '.$itemSource.' failed, because the permissions exceed permissions granted to the parent item', \OC_Log::ERROR); return false; } } @@ -367,7 +368,7 @@ class Share { } return true; } - \OC_Log::write('OCP\Share', 'Setting permissions for '.$item.' failed, because the item was not found', \OC_Log::ERROR); + \OC_Log::write('OCP\Share', 'Setting permissions for '.$itemSource.' failed, because the item was not found', \OC_Log::ERROR); return false; } @@ -383,8 +384,8 @@ class Share { $class = self::$backendTypes[$itemType]['class']; if (class_exists($class)) { self::$backends[$itemType] = new $class; - if (!is_subclass_of(self::$backends[$itemType], 'OCP\Share_Backend')) { - \OC_Log::write('OCP\Share', 'Sharing backend '.$class.' must extend abstract class OC_Share_Backend', \OC_Log::ERROR); + if (!(self::$backends[$itemType] instanceof Share_Backend)) { + \OC_Log::write('OCP\Share', 'Sharing backend '.$class.' must implement the interface OCP\Share_Backend', \OC_Log::ERROR); return false; } return self::$backends[$itemType]; @@ -419,14 +420,14 @@ class Share { /** * @brief Get shared items from the database * @param string Item type - * @param string Item or item target (optional) + * @param string Item source or target (optional) * @param int SHARE_TYPE_USER, SHARE_TYPE_GROUP, SHARE_TYPE_PRIVATE_LINK, $shareTypeUserAndGroups, or $shareTypeGroupUserUnique * @param string User or group the item is being shared with * @param string User that is the owner of shared items (optional) * @param int Format to convert items to with formatItems() * @param mixed Parameters to pass to formatItems() * @param int Number of items to return, -1 to return all matches (optional) - * @param bool Is item the source (optional) + * @param bool Include collection item types (optional) * @return mixed * * See public functions getItem(s)... for parameter usage @@ -434,8 +435,10 @@ class Share { */ private static function getItems($itemType, $item = null, $shareType = null, $shareWith = null, $uidOwner = null, $format = self::FORMAT_NONE, $parameters = null, $limit = -1, $includeCollections = false) { if ($backend = self::getBackend($itemType)) { + // Get filesystem root to add it to the file target and remove from the file source + $root = \OC_Filesystem::getRoot(); // If includeCollections is true, find collections of this item type, e.g. a music album contains songs - if ($includeCollections && $collectionTypes = self::getCollectionItemTypes($itemType)) { + if ($includeCollections && !isset($item) && $collectionTypes = self::getCollectionItemTypes($itemType)) { $where = "WHERE item_type IN ('".implode("','", array_merge(array($itemType), $collectionTypes))."')"; } else { $where = "WHERE item_type = '".$itemType."'"; @@ -457,22 +460,27 @@ class Share { // Prevent unique user targets for group shares from being selected $where .= " AND share_type != '".self::$shareTypeGroupUserUnique."'"; } + if ($itemType == 'file' || $itemType == 'folder') { + $where = "INNER JOIN *PREFIX*fscache ON file_source = *PREFIX*fscache.id ".$where; + $column = 'file_source'; + } else { + $column = 'item_source'; + } + } else { + if ($itemType == 'file' || $itemType == 'folder') { + $column = 'file_target'; + } else { + $column = 'item_target'; + } } - if (isset($item) && !$includeCollections) { + if (isset($item)) { // If looking for own shared items, check item_source else check item_target if (isset($uidOwner)) { - $source = $backend->getSource($item, $uidOwner); // If item type is a file, file source needs to be checked in case the item was converted if ($itemType == 'file' || $itemType == 'folder') { - $where .= " AND file_source = ".\OC_FileCache::getId($source['file']); + $where .= " AND path = '".$root.$item."'"; } else { - // Check if this item depends on a file and getSource() returned an array - if (is_array($source)) { - $itemSource = $source['item']; - } else { - $itemSource = $source; - } - $where .= " AND item_source = '".$itemSource."'"; + $where .= " AND item_source = '".$item."'"; } } else { if ($itemType == 'file' || $itemType == 'folder') { @@ -481,53 +489,70 @@ class Share { $where .= " AND item_target = '".$item."'"; } } - } else if ($itemType == 'file') { - // TODO Exclude converted items inside shared folders + if ($includeCollections && $collectionTypes = self::getCollectionItemTypes($itemType)) { + $where .= " OR item_type IN ('".implode("','", $collectionTypes)."')"; + } } if ($limit != -1 && !$includeCollections) { if ($shareType == self::$shareTypeUserAndGroups) { // Make sure the unique user target is returned if it exists, unique targets should follow the group share in the database // If the limit is not 1, the filtering can be done later - $where .= ' ORDER BY id DESC'; + $where .= ' ORDER BY *PREFIX*share.id DESC'; } $where .= ' LIMIT '.$limit; } if ($format == self::FORMAT_STATUSES) { - $select = 'id, item_type, item, item_source, parent, share_type'; - } else if ($format == self::FORMAT_SOURCES) { - $select = 'id, item_source, parent, share_type'; + if ($itemType == 'file' || $itemType == 'folder') { + $select = '*PREFIX*share.id, item_type, *PREFIX*share.parent, share_type, *PREFIX*fscache.path as file_source'; + } else { + $select = 'id, item_source, parent, share_type'; + } } else { if (isset($uidOwner)) { - $select = 'id, item_type, item, item_source, parent, share_type, share_with, permissions, stime, file_source'; + if ($itemType == 'file' || $itemType == 'folder') { + $select = '*PREFIX*share.id, item_type, *PREFIX*fscache.path as file_source, *PREFIX*share.parent, share_type, share_with, permissions, stime'; + } else { + $select = 'id, item_type, item_source, parent, share_type, share_with, permissions, stime, file_source'; + } } else { $select = '*'; } } - error_log($where); + $root = strlen($root); $query = \OC_DB::prepare('SELECT '.$select.' FROM *PREFIX*share '.$where); $result = $query->execute(); $items = array(); while ($row = $result->fetchRow()) { // Return only the item instead of a 2-dimensional array - if ($limit == 1 && $format == self::FORMAT_NONE && !$includeCollections) { - return $row; + if ($limit == 1 && $row['item_type'] == $itemType && $row[$column] == $item) { + if ($format == self::FORMAT_NONE) { + return $row; + } else { + $items[$row['id']] = $row; + break; + } } // Filter out duplicate group shares for users with unique targets if ($row['share_type'] == self::$shareTypeGroupUserUnique) { // Remove the parent group share unset($items[$row['parent']]); } + // Remove root from file source paths + if (isset($uidOwner) && isset($row['file_source'])) { + $row['file_source'] = substr($row['file_source'], $root); + } + // TODO Check this outside of the loop // Check if this is a collection of the requested item type if ($row['item_type'] != $itemType) { if ($collectionBackend = self::getBackend($row['item_type'])) { - $row['collection'] = array('item_type' => $itemType, 'item_source' => $row['item_source']); + $row['collection'] = array('item_type' => $itemType, $column => $row[$column]); // Fetch all of the children sources - $children = $collectionBackend->getChildren($row['item']); + $children = $collectionBackend->getChildren($row[$column]); foreach ($children as $child) { $row['item_source'] = $child; - $row['item_target'] = ''; // TODO +// $row['item_target'] = $child['target']; TODO if (isset($item)) { - if ((isset($uidOwner) && $row['item'] == $item) || $row['item_target'] == $item) { + if ($row[$column] == $item) { // Return only the item instead of a 2-dimensional array if ($limit == 1 && $format == self::FORMAT_NONE) { return $row; @@ -544,29 +569,22 @@ class Share { } } } else { - $items[] = $row; + $items[$row['id']] = $row; } } if (!empty($items)) { - error_log(var_export($items, true)); if ($format == self::FORMAT_NONE) { return $items; } else if ($format == self::FORMAT_STATUSES) { $statuses = array(); foreach ($items as $item) { if ($item['share_type'] == self::SHARE_TYPE_PRIVATE_LINK) { - $statuses[$item['item']] = true; - } else if (!isset($statuses[$item['item']])) { - $statuses[$item['item']] = false; + $statuses[$item[$column]] = true; + } else if (!isset($statuses[$item[$column]])) { + $statuses[$item[$column]] = false; } } return $statuses; - } else if ($format == self::FORMAT_SOURCES) { - $sources = array(); - foreach ($items as $item) { - $sources[] = $item['item_source']; - } - return $sources; } else { return $backend->formatItems($items, $format, $parameters); } @@ -580,17 +598,17 @@ class Share { /** * @brief Put shared item into the database * @param string Item type - * @param string Item + * @param string Item source * @param int SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_PRIVATE_LINK * @param string User or group the item is being shared with * @param int CRUDS permissions * @param bool|array Parent folder target (optional) * @return bool Returns true on success or false on failure */ - private static function put($itemType, $item, $shareType, $shareWith, $uidOwner, $permissions, $parentFolder = null) { + private static function put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions, $parentFolder = null) { // Check file extension for an equivalent item type to convert to if ($itemType == 'file') { - $extension = strtolower(substr($item, strrpos($item, '.') + 1)); + $extension = strtolower(substr($itemSource, strrpos($itemSource, '.') + 1)); foreach (self::$backends as $type => $backend) { if (isset($backend->dependsOn) && $backend->dependsOn == 'file' && isset($backend->supportedFileExtensions) && in_array($extension, $backend->supportedFileExtensions)) { $itemType = $type; @@ -604,7 +622,8 @@ class Share { } if ($backend = self::getBackend($itemType)) { // Check if this is a reshare - if ($checkReshare = self::getItemSharedWith($itemType, $item)) { + // TODO This query has pretty bad performance if there are large collections, figure out a way to make the collection searching more efficient + if ($checkReshare = self::getItemSharedWith($itemType, $itemSource, self::FORMAT_NONE, null, true)) { if ($checkReshare['permissions'] & self::PERMISSION_SHARE) { // TODO Don't check if inside folder $parent = $checkReshare['id']; @@ -612,77 +631,78 @@ class Share { $fileSource = $checkReshare['file_source']; $fileTarget = $checkReshare['file_target']; } else { - \OC_Log::write('OCP\Share', 'Sharing '.$item.' failed, because resharing is not allowed', \OC_Log::ERROR); + \OC_Log::write('OCP\Share', 'Sharing '.$itemSource.' failed, because resharing is not allowed', \OC_Log::ERROR); return false; } } else { $parent = null; - $source = $backend->getSource($item, $uidOwner); - if (!$source) { - \OC_Log::write('OCP\Share', 'Sharing '.$item.' failed, because the sharing backend for '.$itemType.' could not find its source', \OC_Log::ERROR); + if (!$backend->isValidSource($itemSource, $uidOwner)) { + \OC_Log::write('OCP\Share', 'Sharing '.$itemSource.' failed, because the sharing backend for '.$itemType.' could not find its source', \OC_Log::ERROR); return false; - } else if (is_array($source)) { - $itemSource = $source['item']; - $fileSource = \OC_FileCache::getId($source['file']); + } + $parent = null; + if ($backend instanceof Share_Backend_File_Dependent) { + // NOTE Apps should start using the file cache ids in their tables + $filePath = $backend->getFilePath($itemSource, $uidOwner); + $fileSource = \OC_FileCache::getId($filePath); if ($fileSource == -1) { - \OC_Log::write('OCP\Share', 'Sharing '.$item.' failed, because the file could not be found in the file cache', \OC_Log::ERROR); + \OC_Log::write('OCP\Share', 'Sharing '.$itemSource.' failed, because the file could not be found in the file cache', \OC_Log::ERROR); return false; } } else { - $itemSource = $source; $fileSource = null; } } - $query = \OC_DB::prepare('INSERT INTO *PREFIX*share (item_type, item, item_source, item_target, parent, share_type, share_with, uid_owner, permissions, stime, file_source, file_target) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)'); + $query = \OC_DB::prepare('INSERT INTO *PREFIX*share (item_type, item_source, item_target, parent, share_type, share_with, uid_owner, permissions, stime, file_source, file_target) VALUES (?,?,?,?,?,?,?,?,?,?,?)'); // Share with a group if ($shareType == self::SHARE_TYPE_GROUP) { if (isset($fileSource)) { if ($parentFolder) { if ($parentFolder === true) { - $groupFileTarget = self::getBackend('file')->generateTarget($source['file'], false); + $groupFileTarget = self::generateTarget('file', $filePath, $shareType, $shareWith); // Set group default file target for future use $parentFolders[0]['folder'] = $groupFileTarget; } else { // Get group default file target - $groupFileTarget = $parentFolder[0]['folder'].$item; + $groupFileTarget = $parentFolder[0]['folder'].$itemSource; $parent = $parentFolder[0]['id']; unset($parentFolder[0]); // Only loop through users we know have different file target paths $uidSharedWith = array_keys($parentFolder); } } else { - $groupFileTarget = self::getBackend('file')->generateTarget($source['file'], false); + $groupFileTarget = self::generateTarget('file', $filePath, $shareType, $shareWith); } } else { $groupFileTarget = null; } - $groupItemTarget = $backend->generateTarget($item, false); - $query->execute(array($itemType, $item, $itemSource, $groupItemTarget, $parent, $shareType, $shareWith['group'], $uidOwner, $permissions, time(), $fileSource, $groupFileTarget)); + $groupItemTarget = self::generateTarget($itemType, $itemSource, $shareType, $shareWith); + $query->execute(array($itemType, $itemSource, $groupItemTarget, $parent, $shareType, $shareWith['group'], $uidOwner, $permissions, time(), $fileSource, $groupFileTarget)); // Save this id, any extra rows for this group share will need to reference it $parent = \OC_DB::insertid('*PREFIX*share'); // Loop through all users of this group in case we need to add an extra row foreach ($shareWith['users'] as $uid) { - $itemTarget = $backend->generateTarget($item, $uid); + $itemTarget = self::generateTarget($itemType, $itemSource, self::SHARE_TYPE_USER, $uid); if (isset($fileSource)) { if ($parentFolder) { if ($parentFolder === true) { - $fileTarget = self::getBackend('file')->generateTarget($source['file'], $uid); + $fileTarget = self::generateTarget('file', $filePath, self::SHARE_TYPE_USER, $uid); if ($fileTarget != $groupFileTarget) { $parentFolders[$uid]['folder'] = $fileTarget; } } else if (isset($parentFolder[$uid])) { - $fileTarget = $parentFolder[$uid]['folder'].$item; + $fileTarget = $parentFolder[$uid]['folder'].$itemSource; $parent = $parentFolder[$uid]['id']; } } else { - $fileTarget = self::getBackend('file')->generateTarget($source['file'], $uid); + $fileTarget = self::generateTarget('file', $filePath, self::SHARE_TYPE_USER, $uid); } } else { $fileTarget = null; } // Insert an extra row for the group share if the item or file target is unique for this user if ($itemTarget != $groupItemTarget || (isset($fileSource) && $fileTarget != $groupFileTarget)) { - $query->execute(array($itemType, $item, $itemSource, $itemTarget, $parent, self::$shareTypeGroupUserUnique, $uid, $uidOwner, $permissions, time(), $fileSource, $fileTarget)); + $query->execute(array($itemType, $itemSource, $itemTarget, $parent, self::$shareTypeGroupUserUnique, $uid, $uidOwner, $permissions, time(), $fileSource, $fileTarget)); $id = \OC_DB::insertid('*PREFIX*share'); } if ($parentFolder === true) { @@ -694,31 +714,23 @@ class Share { return $parentFolders; } } else { - if ($shareType == self::SHARE_TYPE_PRIVATE_LINK) { - $itemTarget = null; - } else { - $itemTarget = $backend->generateTarget($item, $shareWith); - } + $itemTarget = self::generateTarget($itemType, $itemSource, $shareType, $shareWith); if (isset($fileSource)) { if ($parentFolder) { if ($parentFolder === true) { - $fileTarget = self::getBackend('file')->generateTarget($source['file'], $shareWith); + $fileTarget = self::generateTarget('file', $filePath, $shareType, $shareWith); $parentFolders['folder'] = $fileTarget; } else { - $fileTarget = $parentFolder['folder'].$item; + $fileTarget = $parentFolder['folder'].$itemSource; $parent = $parentFolder['id']; } } else { - if ($shareType == self::SHARE_TYPE_PRIVATE_LINK) { - $fileTarget = basename($source['file']); - } else { - $fileTarget = self::getBackend('file')->generateTarget($source['file'], $shareWith); - } + $fileTarget = self::generateTarget('file', $filePath, $shareType, $shareWith); } } else { $fileTarget = null; } - $query->execute(array($itemType, $item, $itemSource, $itemTarget, $parent, $shareType, $shareWith, $uidOwner, $permissions, time(), $fileSource, $fileTarget)); + $query->execute(array($itemType, $itemSource, $itemTarget, $parent, $shareType, $shareWith, $uidOwner, $permissions, time(), $fileSource, $fileTarget)); $id = \OC_DB::insertid('*PREFIX*share'); if ($parentFolder === true) { $parentFolders['id'] = $id; @@ -731,6 +743,54 @@ class Share { return false; } + /** + * @brief Generate a unique target for the item + * @param string Item type + * @param string Item source + * @param int SHARE_TYPE_USER, SHARE_TYPE_GROUP, or SHARE_TYPE_PRIVATE_LINK + * @param string User or group the item is being shared with + * @return string Item target + */ + private static function generateTarget($itemType, $itemSource, $shareType, $shareWith) { + if ($backend = self::getBackend($itemType)) { + if ($shareType == self::SHARE_TYPE_PRIVATE_LINK) { + return $backend->generateTarget($itemSource, false); + } else { + if ($itemType == 'file' || $itemType == 'folder') { + $column = 'file_target'; + } else { + $column = 'item_target'; + } + $exclude = null; + // Backend has 3 opportunities to generate a unique target + for ($i = 0; $i < 2; $i++) { + if ($shareType == self::SHARE_TYPE_GROUP) { + $target = $backend->generateTarget($itemSource, false, $exclude); + } else { + $target = $backend->generateTarget($itemSource, $shareWith, $exclude); + } + if (is_array($exclude) && in_array($target, $exclude)) { + break; + } + // Check if target already exists + if ($checkTarget = self::getItems($itemType, $target, $shareType, $shareWith, null, self::FORMAT_NONE, null, 1)) { + // Find similar targets to improve backend's chances to generate a unqiue target + // TODO query needs to be setup like getItems + $checkTargets = \OC_DB::prepare('SELECT item_target FROM *PREFIX*share WHERE item_type = ? AND share_with = ? AND '.$column.' LIKE ?'); + $result = $checkTargets->execute(array($itemType, $shareWith, '%'.$target.'%')); + while ($row = $result->fetchRow()) { + $exclude[] = $row[$column]; + } + } else { + return $target; + } + } + } + } + \OC_Log::write('OCP\Share', 'Backend did not generate a unique target', \OC_Log::ERROR); + return false; + } + /** * @brief Delete all reshares of an item * @param int Id of item to delete @@ -803,16 +863,13 @@ class Share { } /** -* Abstract backend class that apps must extend to share content. +* Interface that apps must implement to share content. */ -abstract class Share_Backend { - - public $dependsOn; - public $supportedFileExtensions = array(); +interface Share_Backend { /** * @brief Get the source of the item to be stored in the database - * @param string Item + * @param string Item source * @param string Owner of the item * @return mixed|array|false Source * @@ -821,13 +878,11 @@ abstract class Share_Backend { * * The formatItems() function will translate the source returned back into the item */ - public abstract function getSource($item, $uid); - - + public function isValidSource($itemSource, $uidOwner); /** * @brief Get a unique name of the item for the specified user - * @param string Item + * @param string Item source * @param string|false User the item is being shared with * @param array|null List of similar item names already existing as shared items * @return string Target name @@ -835,9 +890,7 @@ abstract class Share_Backend { * This function needs to verify that the user does not already have an item with this name. * If it does generate a new name e.g. name_# */ - public abstract function generateTarget($item, $uid, $exclude = null); - - + public function generateTarget($itemSource, $shareWith, $exclude = null); /** * @brief Converts the shared item sources back into the item in the specified format @@ -852,19 +905,38 @@ abstract class Share_Backend { * This function allows the backend to control the output of shared items with custom formats. * It is only called through calls to the public getItem(s)Shared(With) functions. */ - public abstract function formatItems($items, $format, $parameters = null); + public function formatItems($items, $format, $parameters = null); +} + +/** +* Interface for share backends that share content that is dependent on files. +* Extends the Share_Backend interface. +*/ +interface Share_Backend_File_Dependent extends Share_Backend { + + /** + * @brief Get the file path of the item + * @param + * @param + * @return + */ + public function getFilePath($itemSource, $uidOwner); } -abstract class Share_Backend_Collection extends Share_Backend { +/** +* Interface for collections of of items implemented by another share backend. +* Extends the Share_Backend interface. +*/ +interface Share_Backend_Collection extends Share_Backend { /** * @brief Get the sources of the children of the item - * @param string Item + * @param string Item source * @return array Returns an array of sources */ - public abstract function getChildren($item); + public function getChildren($itemSource); } -- cgit v1.2.3 From ba0cf9817526e1206a25a5fe6d70255ab3c9e3bc Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Mon, 6 Aug 2012 13:33:53 -0400 Subject: Remove extra line, caused 'headers already sent' issues --- apps/files_sharing/lib/share/file.php | 1 - 1 file changed, 1 deletion(-) (limited to 'apps/files_sharing/lib') diff --git a/apps/files_sharing/lib/share/file.php b/apps/files_sharing/lib/share/file.php index 927e9462f23..66be5f2b155 100644 --- a/apps/files_sharing/lib/share/file.php +++ b/apps/files_sharing/lib/share/file.php @@ -1,4 +1,3 @@ - Date: Thu, 16 Aug 2012 12:20:14 -0400 Subject: File sharing cleanup, works perfectly I think :) --- apps/files_sharing/lib/share/file.php | 91 ++++++++++++++------------------- apps/files_sharing/lib/share/folder.php | 73 ++++++++++---------------- apps/files_sharing/sharedstorage.php | 26 +++++----- core/js/share.js | 1 - lib/files.php | 4 +- lib/public/share.php | 29 ++++++++--- tests/lib/share/share.php | 8 +++ 7 files changed, 111 insertions(+), 121 deletions(-) (limited to 'apps/files_sharing/lib') diff --git a/apps/files_sharing/lib/share/file.php b/apps/files_sharing/lib/share/file.php index 66be5f2b155..ae6315600f8 100644 --- a/apps/files_sharing/lib/share/file.php +++ b/apps/files_sharing/lib/share/file.php @@ -27,10 +27,10 @@ class OC_Share_Backend_File implements OCP\Share_Backend_File_Dependent { const FORMAT_OPENDIR = 3; public function isValidSource($item, $uid) { -// if (OC_Filesystem::file_exists($item)) { + if (OC_Filesystem::file_exists($item)) { return true; -// } -// return false; + } + return false; } public function getFilePath($item, $uid) { @@ -43,61 +43,48 @@ class OC_Share_Backend_File implements OCP\Share_Backend_File_Dependent { } public function formatItems($items, $format, $parameters = null) { - if ($format == self::FORMAT_OPENDIR) { + if ($format == self::FORMAT_SHARED_STORAGE) { + // Only 1 item should come through for this format call + return array('path' => $items[key($items)]['file_source'], 'permissions' => $items[key($items)]['permissions']); + } else if ($format == self::FORMAT_FILE_APP) { $files = array(); - foreach ($items as $file) { - $files[] = basename($file['file_target']); + foreach ($items as $item) { + $file = array(); + $file['path'] = $item['file_target']; + $file['name'] = basename($item['file_target']); + $file['ctime'] = $item['ctime']; + $file['mtime'] = $item['mtime']; + $file['mimetype'] = $item['mimetype']; + $file['size'] = $item['size']; + $file['encrypted'] = $item['encrypted']; + $file['versioned'] = $item['versioned']; + $file['directory'] = $parameters['folder']; + $file['type'] = ($item['mimetype'] == 'httpd/unix-directory') ? 'dir' : 'file'; + $file['permissions'] = $item['permissions']; + if ($file['type'] == 'file') { + // Remove Create permission if type is file + $file['permissions'] &= ~OCP\Share::PERMISSION_CREATE; + } + $files[] = $file; } return $files; - } else if ($format == self::FORMAT_SHARED_STORAGE) { - $id = $items[key($items)]['file_source']; - $query = OCP\DB::prepare('SELECT path FROM *PREFIX*fscache WHERE id = ?'); - $result = $query->execute(array($id))->fetchAll(); - if (isset($result[0]['path'])) { - return array('path' => $result[0]['path'], 'permissions' => $items[key($items)]['permissions']); - } - return false; - } else { - $shares = array(); - $ids = array(); + } else if ($format == self::FORMAT_FILE_APP_ROOT) { + $mtime = 0; + $size = 0; foreach ($items as $item) { - $shares[$item['file_source']] = $item; - $ids[] = $item['file_source']; - } - $ids = "'".implode("','", $ids)."'"; - if ($format == self::FORMAT_FILE_APP) { - $query = OCP\DB::prepare('SELECT id, path, name, ctime, mtime, mimetype, size, encrypted, versioned, writable FROM *PREFIX*fscache WHERE id IN ('.$ids.')'); - $result = $query->execute(); - $files = array(); - while ($file = $result->fetchRow()) { - // Set target path - $file['path'] = $shares[$file['id']]['file_target']; - $file['name'] = basename($file['path']); - $file['directory'] = $parameters['folder']; - $file['type'] = ($file['mimetype'] == 'httpd/unix-directory') ? 'dir' : 'file'; - $permissions = $shares[$file['id']]['permissions']; - if ($file['type'] == 'file') { - // Remove Create permission if type is file - $permissions &= ~OCP\Share::PERMISSION_CREATE; - } - $file['permissions'] = $permissions; - $files[] = $file; - } - return $files; - } else if ($format == self::FORMAT_FILE_APP_ROOT) { - $query = OCP\DB::prepare('SELECT id, path, name, ctime, mtime, mimetype, size, encrypted, versioned, writable FROM *PREFIX*fscache WHERE id IN ('.$ids.')'); - $result = $query->execute(); - $mtime = 0; - $size = 0; - while ($file = $result->fetchRow()) { - if ($file['mtime'] > $mtime) { - $mtime = $file['mtime']; - } - $size += $file['size']; + if ($item['mtime'] > $mtime) { + $mtime = $item['mtime']; } - return array(0 => array('name' => 'Shared', 'mtime' => $mtime, 'mimetype' => 'httpd/unix-directory', 'size' => $size, 'writable' => false, 'type' => 'dir', 'directory' => '', 'permissions' => OCP\Share::PERMISSION_READ)); + $size += $item['size']; } - } + return array(0 => array('name' => 'Shared', 'mtime' => $mtime, 'mimetype' => 'httpd/unix-directory', 'size' => $size, 'writable' => false, 'type' => 'dir', 'directory' => '', 'permissions' => OCP\Share::PERMISSION_READ)); + } else if ($format == self::FORMAT_OPENDIR) { + $files = array(); + foreach ($items as $item) { + $files[] = basename($item['file_target']); + } + return $files; + } return array(); } diff --git a/apps/files_sharing/lib/share/folder.php b/apps/files_sharing/lib/share/folder.php index 2f101d33c8a..b6db96614fd 100644 --- a/apps/files_sharing/lib/share/folder.php +++ b/apps/files_sharing/lib/share/folder.php @@ -21,8 +21,32 @@ class OC_Share_Backend_Folder extends OC_Share_Backend_File { - public function inCollection($collections, $item) { - // TODO + public function formatItems($items, $format, $parameters = null) { + if ($format == self::FORMAT_SHARED_STORAGE) { + // Only 1 item should come through for this format call + return array('path' => $items[key($items)]['file_source'], 'permissions' => $items[key($items)]['permissions']); + } else if ($format == self::FORMAT_FILE_APP && isset($parameters['folder'])) { + // Only 1 item should come through for this format call + $folder = $items[key($items)]; + if (isset($parameters['mimetype_filter'])) { + $mimetype_filter = $parameters['mimetype_filter']; + } else { + $mimetype_filter = ''; + } + $path = $folder['file_source'].substr($parameters['folder'], 7 + strlen($folder['file_target'])); + $files = OC_FileCache::getFolderContent($path, '', $mimetype_filter); + foreach ($files as &$file) { + $file['directory'] = $parameters['folder']; + $file['type'] = ($file['mimetype'] == 'httpd/unix-directory') ? 'dir' : 'file'; + $file['permissions'] = $folder['permissions']; + if ($file['type'] == 'file') { + // Remove Create permission if type is file + $file['permissions'] &= ~OCP\Share::PERMISSION_CREATE; + } + } + return $files; + } + return array(); } public function getChildren($itemSource) { @@ -34,47 +58,4 @@ class OC_Share_Backend_Folder extends OC_Share_Backend_File { return $sources; } - public function formatItems($items, $format, $parameters = null) { - if ($format == self::FORMAT_FILE_APP && isset($parameters['folder'])) { - $folder = $items[key($items)]; - $query = OCP\DB::prepare('SELECT path FROM *PREFIX*fscache WHERE id = ?'); - $result = $query->execute(array($folder['file_source']))->fetchRow(); - if (isset($result['path'])) { - if (isset($parameters['mimetype_filter'])) { - $mimetype_filter = $parameters['mimetype_filter']; - } else { - $mimetype_filter = ''; - } - $pos = strpos($result['path'], $folder['item']); - $path = substr($result['path'], $pos).substr($parameters['folder'], strlen($folder['file_target'])); - $root = substr($result['path'], 0, $pos); - $files = OC_FileCache::getFolderContent($path, $root, $mimetype_filter); - foreach ($files as &$file) { - $file['directory'] = $parameters['folder']; - $file['type'] = ($file['mimetype'] == 'httpd/unix-directory') ? 'dir' : 'file'; - $permissions = $folder['permissions']; - if ($file['type'] == 'file') { - // Remove Create permission if type is file - $permissions &= ~OCP\Share::PERMISSION_CREATE; - } - $file['permissions'] = $permissions; - } - return $files; - } - }/* else if ($format == self::FORMAT_OPENDIR_ROOT) { - $query = OCP\DB::prepare('SELECT name FROM *PREFIX*fscache WHERE id IN ('.$ids.')'); - $result = $query->execute(); - $files = array(); - while ($file = $result->fetchRow()) { - // Set target path - $files[] = basename($shares[$file['id']]['item_target']); - } - return $files; - }*/ - return array(); - } - -} - - -?> \ No newline at end of file +} \ No newline at end of file diff --git a/apps/files_sharing/sharedstorage.php b/apps/files_sharing/sharedstorage.php index fc2e6e32c7c..582c9c66172 100644 --- a/apps/files_sharing/sharedstorage.php +++ b/apps/files_sharing/sharedstorage.php @@ -50,7 +50,7 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { if (isset($this->files[$folder])) { $file = $this->files[$folder]; } else { - $file = OCP\Share::getItemSharedWith('file', $folder, OC_Share_Backend_File::FORMAT_SHARED_STORAGE); + $file = OCP\Share::getItemSharedWith('folder', $folder, OC_Share_Backend_File::FORMAT_SHARED_STORAGE); } if ($file) { $this->files[$target]['path'] = $file['path'].substr($target, strlen($folder)); @@ -285,19 +285,21 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { } public function file_put_contents($path, $data) { - if ($this->is_writable($path)) { - $source = $this->getSourcePath($path); - if ($source) { - $info = array( - 'target' => $this->sharedFolder.$path, - 'source' => $source, - ); - OCP\Util::emitHook('OC_Filestorage_Shared', 'file_put_contents', $info); - $storage = OC_Filesystem::getStorage($source); - $result = $storage->file_put_contents($this->getInternalPath($source), $data); - return $result; + if ($source = $this->getSourcePath($path)) { + // Check if permission is granted + if (($this->file_exists($path) && !$this->isUpdatable($path)) || ($this->is_dir($path) && !$this->isCreatable($path))) { + return false; } + $info = array( + 'target' => $this->sharedFolder.$path, + 'source' => $source, + ); + OCP\Util::emitHook('OC_Filestorage_Shared', 'file_put_contents', $info); + $storage = OC_Filesystem::getStorage($source); + $result = $storage->file_put_contents($this->getInternalPath($source), $data); + return $result; } + return false; } public function unlink($path) { diff --git a/core/js/share.js b/core/js/share.js index 70d822f8814..38c20a75709 100644 --- a/core/js/share.js +++ b/core/js/share.js @@ -267,7 +267,6 @@ $(document).ready(function() { if (typeof FileActions !== 'undefined') { OC.Share.loadIcons('file'); - OC.Share.loadIcons('folder'); FileActions.register('all', 'Share', FileActions.PERMISSION_SHARE, function(filename) { // Return the correct sharing icon if (scanFiles.scanning) { return; } // workaround to prevent additional http request block scanning feedback diff --git a/lib/files.php b/lib/files.php index fee71b777b3..07d432f8066 100644 --- a/lib/files.php +++ b/lib/files.php @@ -41,9 +41,9 @@ class OC_Files { $pos = strpos($directory, '/', 8); // Get shared folder name if ($pos !== false) { - $itemTarget = substr($directory, 0, $pos); + $itemTarget = substr($directory, 7, $pos - 7); } else { - $itemTarget = $directory; + $itemTarget = substr($directory, 7); } $files = OCP\Share::getItemSharedWith('folder', $itemTarget, OC_Share_Backend_File::FORMAT_FILE_APP, array('folder' => $directory, 'mimetype_filter' => $mimetype_filter)); } diff --git a/lib/public/share.php b/lib/public/share.php index c420943da26..940bf2d48a0 100644 --- a/lib/public/share.php +++ b/lib/public/share.php @@ -399,23 +399,28 @@ class Share { */ private static function getItems($itemType, $item = null, $shareType = null, $shareWith = null, $uidOwner = null, $format = self::FORMAT_NONE, $parameters = null, $limit = -1, $includeCollections = false, $itemShareWithBySource = false) { $backend = self::getBackend($itemType); - // Get filesystem root to add it to the file target and remove from the file source + // Get filesystem root to add it to the file target and remove from the file source, match file_source with the file cache if ($backend instanceof Share_Backend_File_Dependent) { + $fileDependent = true; $root = \OC_Filesystem::getRoot(); + // TODO No need to do this for FORMAT_STATUSES and loading the item in the dropdown, it's a performance waste + $where = 'INNER JOIN *PREFIX*fscache ON file_source = *PREFIX*fscache.id '; } else { + $fileDependent = false; + $where = ''; $root = ''; } if ($itemType == 'file' && !isset($item)) { - $where = 'WHERE file_target IS NOT NULL'; + $where .= 'WHERE file_target IS NOT NULL'; $query_args = array(); } else if ($includeCollections && !isset($item) && $collectionTypes = self::getCollectionItemTypes($itemType)) { // If includeCollections is true, find collections of this item type, e.g. a music album contains songs $item_types = array_merge(array($itemType), $collectionTypes); $placeholders = join(',', array_fill(0, count($item_types), '?')); - $where = "WHERE item_type IN ('".$placeholders."')"; + $where .= "WHERE item_type IN ('".$placeholders."')"; $query_args = $item_types; - } else { - $where = "WHERE item_type = ?"; + } else if ($itemType != 'file' && $itemType != 'folder') { + $where .= "WHERE item_type = ?"; $query_args = array($itemType); } if (isset($shareType) && isset($shareWith)) { @@ -445,7 +450,6 @@ class Share { $query_args[] = self::$shareTypeGroupUserUnique; } if ($itemType == 'file' || $itemType == 'folder') { - $where = "INNER JOIN *PREFIX*fscache ON file_source = *PREFIX*fscache.id ".$where; $column = 'file_source'; } else { $column = 'item_source'; @@ -491,6 +495,7 @@ class Share { } $where .= ' LIMIT '.$limit; } + // TODO Optimize selects if ($format == self::FORMAT_STATUSES) { if ($itemType == 'file' || $itemType == 'folder') { $select = '*PREFIX*share.id, item_type, *PREFIX*share.parent, share_type, *PREFIX*fscache.path as file_source'; @@ -505,7 +510,15 @@ class Share { $select = 'id, item_type, item_source, parent, share_type, share_with, permissions, stime, file_source'; } } else { - $select = '*'; + if ($fileDependent) { + if (($itemType == 'file' || $itemType == 'folder') && $format == \OC_Share_Backend_File::FORMAT_FILE_APP || $format == \OC_Share_Backend_File::FORMAT_FILE_APP_ROOT) { + $select = '*PREFIX*share.id, item_type, *PREFIX*share.parent, share_type, share_with, permissions, file_target, *PREFIX*fscache.id, path as file_source, name, ctime, mtime, mimetype, size, encrypted, versioned, writable'; + } else { + $select = '*PREFIX*share.id, item_type, item_source, item_target, *PREFIX*share.parent, share_type, share_with, permissions, stime, path as file_source, file_target'; + } + } else { + $select = '*'; + } } } $root = strlen($root); @@ -534,7 +547,7 @@ class Share { // TODO Check this outside of the loop // Check if this is a collection of the requested item type if ($row['item_type'] != $itemType && $itemType != 'file' && !isset($item)) { - if ($collectionBackend = self::getBackend($row['item_type'])) { + if ($collectionBackend = self::getBackend($row['item_type'] && $collectionBackend instanceof Share_Backend_Collection)) { $row['collection'] = array('item_type' => $itemType, $column => $row[$column]); // Fetch all of the children sources $children = $collectionBackend->getChildren($row[$column]); diff --git a/tests/lib/share/share.php b/tests/lib/share/share.php index 4b73cc183a3..efff2c5522c 100644 --- a/tests/lib/share/share.php +++ b/tests/lib/share/share.php @@ -233,6 +233,8 @@ class Test_Share extends UnitTestCase { $this->assertTrue(OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user2, OCP\Share::PERMISSION_READ)); OC_User::setUserId($this->user2); $this->assertEqual(OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET), array('test.txt', 'test1.txt')); + + // Remove user } public function testShareWithGroup() { @@ -280,6 +282,12 @@ class Test_Share extends UnitTestCase { $this->assertEqual(OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET), array('test.txt', 'test1.txt')); OC_User::setUserId($this->user3); $this->assertEqual(OCP\Share::getItemsSharedWith('test', Test_Share_Backend::FORMAT_TARGET), array('test.txt')); + + // Remove user from group + + // Add user to group + + // Remove group } } -- cgit v1.2.3