From d332b1d4a2e9382aaa8e8a11b6200efaadb18768 Mon Sep 17 00:00:00 2001 From: Georg Ehrke Date: Tue, 2 Jul 2013 11:13:22 +0200 Subject: implement preview loading after upload --- apps/files/js/files.js | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) (limited to 'apps/files/js/files.js') diff --git a/apps/files/js/files.js b/apps/files/js/files.js index a79d34c9b23..224167b99c1 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -513,8 +513,9 @@ $(document).ready(function() { var tr=$('tr').filterAttr('data-file',name); tr.attr('data-mime',result.data.mime); tr.attr('data-id', result.data.id); - getMimeIcon(result.data.mime,function(path){ - tr.find('td.filename').attr('style','background-image:url('+path+')'); + var path = $('#dir').val()+'/'+name; + getPreviewIcon(path, function(previewpath){ + tr.find('td.filename').attr('style','background-image:url('+previewpath+')'); }); } else { OC.dialogs.alert(result.data.message, t('core', 'Error')); @@ -577,8 +578,9 @@ $(document).ready(function() { var tr=$('tr').filterAttr('data-file',localName); tr.data('mime',mime).data('id',id); tr.attr('data-id', id); - getMimeIcon(mime,function(path){ - tr.find('td.filename').attr('style','background-image:url('+path+')'); + var path = $('#dir').val()+'/'+localName; + getPreviewIcon(path, function(previewpath){ + tr.find('td.filename').attr('style','background-image:url('+previewpath+')'); }); }); eventSource.listen('error',function(error){ @@ -769,8 +771,9 @@ var createDragShadow = function(event){ if (elem.type === 'dir') { newtr.find('td.filename').attr('style','background-image:url('+OC.imagePath('core', 'filetypes/folder.png')+')'); } else { - getMimeIcon(elem.mime,function(path){ - newtr.find('td.filename').attr('style','background-image:url('+path+')'); + var path = $('#dir').val()+'/'+elem.name; + getPreviewIcon(path, function(previewpath){ + newtr.find('td.filename').attr('style','background-image:url('+previewpath+')'); }); } }); @@ -956,6 +959,10 @@ function getMimeIcon(mime, ready){ } getMimeIcon.cache={}; +function getPreviewIcon(path, ready){ + ready(OC.Router.generate('core_ajax_preview', {file:path, x:44, y:44})); +} + function getUniqueName(name){ if($('tr').filterAttr('data-file',name).length>0){ var parts=name.split('.'); -- cgit v1.2.3 From cf449d42e87b21dff0de35c394031c5fa28b56aa Mon Sep 17 00:00:00 2001 From: Georg Ehrke Date: Wed, 10 Jul 2013 11:25:28 +0200 Subject: properly encode path --- apps/files/js/files.js | 2 +- apps/files_trashbin/lib/trash.php | 2 +- lib/helper.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'apps/files/js/files.js') diff --git a/apps/files/js/files.js b/apps/files/js/files.js index 224167b99c1..06d193bf8f3 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -960,7 +960,7 @@ function getMimeIcon(mime, ready){ getMimeIcon.cache={}; function getPreviewIcon(path, ready){ - ready(OC.Router.generate('core_ajax_preview', {file:path, x:44, y:44})); + ready(OC.Router.generate('core_ajax_preview', {file: encodeURIComponent(path), x:44, y:44})); } function getUniqueName(name){ diff --git a/apps/files_trashbin/lib/trash.php b/apps/files_trashbin/lib/trash.php index e82a597c61e..fb99fca5b3f 100644 --- a/apps/files_trashbin/lib/trash.php +++ b/apps/files_trashbin/lib/trash.php @@ -852,6 +852,6 @@ class Trashbin { } public static function preview_icon($path) { - return \OC_Helper::linkToRoute( 'core_ajax_trashbin_preview', array('x' => 44, 'y' => 44, 'file' => $path)); + return \OC_Helper::linkToRoute( 'core_ajax_trashbin_preview', array('x' => 44, 'y' => 44, 'file' => urlencode($path) )); } } diff --git a/lib/helper.php b/lib/helper.php index 0a8962a5312..326f2567f99 100644 --- a/lib/helper.php +++ b/lib/helper.php @@ -231,7 +231,7 @@ class OC_Helper { * Returns the path to the preview of the file. */ public static function previewIcon($path) { - return self::linkToRoute( 'core_ajax_preview', array('x' => 44, 'y' => 44, 'file' => $path)); + return self::linkToRoute( 'core_ajax_preview', array('x' => 44, 'y' => 44, 'file' => urlencode($path) )); } /** -- cgit v1.2.3 From ac6a3133eca86b853da838ae310534b76e9fb662 Mon Sep 17 00:00:00 2001 From: Georg Ehrke Date: Tue, 30 Jul 2013 12:29:12 +0200 Subject: style fixes --- apps/files/js/files.js | 2 +- apps/files/templates/part.list.php | 2 +- apps/files_sharing/public.php | 2 +- core/ajax/preview.php | 42 +++++ core/ajax/publicpreview.php | 92 +++++++++++ core/ajax/trashbinpreview.php | 46 ++++++ core/routes.php | 6 +- lib/helper.php | 2 +- lib/preview.php | 308 ++++++++----------------------------- lib/preview/images.php | 7 +- lib/preview/libreoffice-cl.php | 24 +-- lib/preview/movies.php | 22 +-- lib/preview/mp3.php | 22 +-- lib/preview/msoffice.php | 24 +-- lib/preview/office.php | 7 +- lib/preview/pdf.php | 8 +- lib/preview/txt.php | 15 +- lib/preview/unknown.php | 8 +- lib/template.php | 3 + lib/template/functions.php | 16 ++ 20 files changed, 349 insertions(+), 309 deletions(-) create mode 100644 core/ajax/preview.php create mode 100644 core/ajax/publicpreview.php create mode 100644 core/ajax/trashbinpreview.php (limited to 'apps/files/js/files.js') diff --git a/apps/files/js/files.js b/apps/files/js/files.js index 53c6de09dd0..8b66ed6747b 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -825,7 +825,7 @@ function getMimeIcon(mime, ready){ getMimeIcon.cache={}; function getPreviewIcon(path, ready){ - ready(OC.Router.generate('core_ajax_preview', {file: encodeURIComponent(path), x:44, y:44})); + ready(OC.Router.generate('core_ajax_preview', {file: encodeURIComponent(path), x:36, y:36})); } function getUniqueName(name){ diff --git a/apps/files/templates/part.list.php b/apps/files/templates/part.list.php index a957a94f332..ab1b91167db 100644 --- a/apps/files/templates/part.list.php +++ b/apps/files/templates/part.list.php @@ -3,7 +3,7 @@ $totaldirs = 0; $totalsize = 0; ?> 6 $totalsize += $file['size']; if ($file['type'] === 'dir') { $totaldirs++; diff --git a/apps/files_sharing/public.php b/apps/files_sharing/public.php index 284b7a30208..650fa6a7c27 100644 --- a/apps/files_sharing/public.php +++ b/apps/files_sharing/public.php @@ -196,7 +196,7 @@ if (isset($path)) { OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&download&path='); $list->assign('isPublic', true); $list->assign('sharingtoken', $token); - $list->assign('sharingroot', ($path)); + $list->assign('sharingroot', $basePath); $breadcrumbNav = new OCP\Template('files', 'part.breadcrumb', ''); $breadcrumbNav->assign('breadcrumb', $breadcrumb); $breadcrumbNav->assign('baseURL', OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&path='); diff --git a/core/ajax/preview.php b/core/ajax/preview.php new file mode 100644 index 00000000000..a9d127ffcc4 --- /dev/null +++ b/core/ajax/preview.php @@ -0,0 +1,42 @@ +setFile($file); + $preview->setMaxX($maxX); + $preview->setMaxY($maxY); + $preview->setScalingUp($scalingUp); + + $preview->show(); +}catch(\Exception $e) { + \OC_Response::setStatus(500); + \OC_Log::write('core', $e->getmessage(), \OC_Log::ERROR); + \OC\Preview::showErrorPreview(); + exit; +} \ No newline at end of file diff --git a/core/ajax/publicpreview.php b/core/ajax/publicpreview.php new file mode 100644 index 00000000000..aace24caa21 --- /dev/null +++ b/core/ajax/publicpreview.php @@ -0,0 +1,92 @@ +setFile($sharedFile); + $preview->setMaxX($maxX); + $preview->setMaxY($maxY); + $preview->setScalingUp($scalingUp); + + $preview->show(); +}catch(\Exception $e) { + \OC_Response::setStatus(500); + \OC_Log::write('core', $e->getmessage(), \OC_Log::ERROR); + \OC\Preview::showErrorPreview(); + exit; +} \ No newline at end of file diff --git a/core/ajax/trashbinpreview.php b/core/ajax/trashbinpreview.php new file mode 100644 index 00000000000..d018a57d37b --- /dev/null +++ b/core/ajax/trashbinpreview.php @@ -0,0 +1,46 @@ +setFile($file); + $preview->setMaxX($maxX); + $preview->setMaxY($maxY); + $preview->setScalingUp($scalingUp); + + $preview->showPreview(); +}catch(\Exception $e) { + \OC_Response::setStatus(500); + \OC_Log::write('core', $e->getmessage(), \OC_Log::ERROR); + \OC\Preview::showErrorPreview(); + exit; +} \ No newline at end of file diff --git a/core/routes.php b/core/routes.php index 41e82f8a73d..75cc4d511c0 100644 --- a/core/routes.php +++ b/core/routes.php @@ -43,11 +43,11 @@ $this->create('js_config', '/core/js/config.js') $this->create('core_ajax_routes', '/core/routes.json') ->action('OC_Router', 'JSRoutes'); $this->create('core_ajax_preview', '/core/preview.png') - ->action('OC\Preview', 'previewRouter'); + ->actionInclude('core/ajax/preview.php'); $this->create('core_ajax_trashbin_preview', '/core/trashbinpreview.png') - ->action('OC\Preview', 'trashbinPreviewRouter'); + ->actionInclude('core/ajax/trashbinpreview.php'); $this->create('core_ajax_public_preview', '/core/publicpreview.png') - ->action('OC\Preview', 'publicPreviewRouter'); + ->actionInclude('core/ajax/publicpreview.php'); OC::$CLASSPATH['OC_Core_LostPassword_Controller'] = 'core/lostpassword/controller.php'; $this->create('core_lostpassword_index', '/lostpassword/') ->get() diff --git a/lib/helper.php b/lib/helper.php index 460e5679b02..b74e4c4512e 100644 --- a/lib/helper.php +++ b/lib/helper.php @@ -233,7 +233,7 @@ class OC_Helper { return self::linkToRoute( 'core_ajax_preview', array('x' => 36, 'y' => 36, 'file' => urlencode($path) )); } - public static function publicPreview_icon( $path, $token ) { + public static function publicPreviewIcon( $path, $token ) { return self::linkToRoute( 'core_ajax_public_preview', array('x' => 36, 'y' => 36, 'file' => urlencode($path), 't' => $token)); } diff --git a/lib/preview.php b/lib/preview.php index 245ad64014e..9f4d20b4650 100755 --- a/lib/preview.php +++ b/lib/preview.php @@ -55,12 +55,12 @@ class Preview { * @param string $file The path to the file where you want a thumbnail from * @param int $maxX The maximum X size of the thumbnail. It can be smaller depending on the shape of the image * @param int $maxY The maximum Y size of the thumbnail. It can be smaller depending on the shape of the image - * @param bool $scalingup Disable/Enable upscaling of previews + * @param bool $scalingUp Disable/Enable upscaling of previews * @return mixed (bool / string) * false if thumbnail does not exist * path to thumbnail if thumbnail exists */ - public function __construct($user='', $root='/', $file='', $maxX=1, $maxY=1, $scalingup=true) { + public function __construct($user='', $root='/', $file='', $maxX=1, $maxY=1, $scalingUp=true) { //set config $this->configMaxX = \OC_Config::getValue('preview_max_x', null); $this->configMaxY = \OC_Config::getValue('preview_max_y', null); @@ -70,11 +70,11 @@ class Preview { $this->setFile($file); $this->setMaxX($maxX); $this->setMaxY($maxY); - $this->setScalingUp($scalingup); + $this->setScalingUp($scalingUp); //init fileviews if($user === ''){ - $user = OC_User::getUser(); + $user = \OC_User::getUser(); } $this->fileview = new \OC\Files\View('/' . $user . '/' . $root); $this->userview = new \OC\Files\View('/' . $user); @@ -120,7 +120,7 @@ class Preview { * @brief returns whether or not scalingup is enabled * @return bool */ - public function getScalingup() { + public function getScalingUp() { return $this->scalingup; } @@ -172,8 +172,8 @@ class Preview { * @return $this */ public function setMaxX($maxX=1) { - if($maxX === 0) { - throw new \Exception('Cannot set width of 0!'); + if($maxX <= 0) { + throw new \Exception('Cannot set width of 0 or smaller!'); } $configMaxX = $this->getConfigMaxX(); if(!is_null($configMaxX)) { @@ -192,8 +192,8 @@ class Preview { * @return $this */ public function setMaxY($maxY=1) { - if($maxY === 0) { - throw new \Exception('Cannot set height of 0!'); + if($maxY <= 0) { + throw new \Exception('Cannot set height of 0 or smaller!'); } $configMaxY = $this->getConfigMaxY(); if(!is_null($configMaxY)) { @@ -208,14 +208,14 @@ class Preview { /** * @brief set whether or not scalingup is enabled - * @param bool $scalingup + * @param bool $scalingUp * @return $this */ - public function setScalingup($scalingup) { + public function setScalingup($scalingUp) { if($this->getMaxScaleFactor() === 1) { - $scalingup = false; + $scalingUp = false; } - $this->scalingup = $scalingup; + $this->scalingup = $scalingUp; return $this; } @@ -245,12 +245,12 @@ class Preview { public function deletePreview() { $file = $this->getFile(); - $fileinfo = $this->fileview->getFileInfo($file); - $fileid = $fileinfo['fileid']; + $fileInfo = $this->fileview->getFileInfo($file); + $fileId = $fileInfo['fileid']; - $previewpath = $this->getThumbnailsFolder() . '/' . $fileid . '/' . $this->getMaxX() . '-' . $this->getMaxY() . '.png'; - $this->userview->unlink($previewpath); - return !$this->userview->file_exists($previewpath); + $previewPath = $this->getThumbnailsFolder() . '/' . $fileId . '/' . $this->getMaxX() . '-' . $this->getMaxY() . '.png'; + $this->userview->unlink($previewPath); + return !$this->userview->file_exists($previewPath); } /** @@ -260,13 +260,13 @@ class Preview { public function deleteAllPreviews() { $file = $this->getFile(); - $fileinfo = $this->fileview->getFileInfo($file); - $fileid = $fileinfo['fileid']; + $fileInfo = $this->fileview->getFileInfo($file); + $fileId = $fileInfo['fileid']; - $previewpath = $this->getThumbnailsFolder() . '/' . $fileid . '/'; - $this->userview->deleteAll($previewpath); - $this->userview->rmdir($previewpath); - return !$this->userview->is_dir($previewpath); + $previewPath = $this->getThumbnailsFolder() . '/' . $fileId . '/'; + $this->userview->deleteAll($previewPath); + $this->userview->rmdir($previewPath); + return !$this->userview->is_dir($previewPath); } /** @@ -279,45 +279,45 @@ class Preview { $file = $this->getFile(); $maxX = $this->getMaxX(); $maxY = $this->getMaxY(); - $scalingup = $this->getScalingup(); + $scalingUp = $this->getScalingUp(); $maxscalefactor = $this->getMaxScaleFactor(); - $fileinfo = $this->fileview->getFileInfo($file); - $fileid = $fileinfo['fileid']; + $fileInfo = $this->fileview->getFileInfo($file); + $fileId = $fileInfo['fileid']; - if(is_null($fileid)) { + if(is_null($fileId)) { return false; } - $previewpath = $this->getThumbnailsFolder() . '/' . $fileid . '/'; - if(!$this->userview->is_dir($previewpath)) { + $previewPath = $this->getThumbnailsFolder() . '/' . $fileId . '/'; + if(!$this->userview->is_dir($previewPath)) { return false; } //does a preview with the wanted height and width already exist? - if($this->userview->file_exists($previewpath . $maxX . '-' . $maxY . '.png')) { - return $previewpath . $maxX . '-' . $maxY . '.png'; + if($this->userview->file_exists($previewPath . $maxX . '-' . $maxY . '.png')) { + return $previewPath . $maxX . '-' . $maxY . '.png'; } - $wantedaspectratio = (float) ($maxX / $maxY); + $wantedAspectRatio = (float) ($maxX / $maxY); //array for usable cached thumbnails - $possiblethumbnails = array(); + $possibleThumbnails = array(); - $allthumbnails = $this->userview->getDirectoryContent($previewpath); - foreach($allthumbnails as $thumbnail) { + $allThumbnails = $this->userview->getDirectoryContent($previewPath); + foreach($allThumbnails as $thumbnail) { $name = rtrim($thumbnail['name'], '.png'); $size = explode('-', $name); $x = (int) $size[0]; $y = (int) $size[1]; - $aspectratio = (float) ($x / $y); - if($aspectratio !== $wantedaspectratio) { + $aspectRatio = (float) ($x / $y); + if($aspectRatio !== $wantedAspectRatio) { continue; } if($x < $maxX || $y < $maxY) { - if($scalingup) { + if($scalingUp) { $scalefactor = $maxX / $x; if($scalefactor > $maxscalefactor) { continue; @@ -326,28 +326,28 @@ class Preview { continue; } } - $possiblethumbnails[$x] = $thumbnail['path']; + $possibleThumbnails[$x] = $thumbnail['path']; } - if(count($possiblethumbnails) === 0) { + if(count($possibleThumbnails) === 0) { return false; } - if(count($possiblethumbnails) === 1) { - return current($possiblethumbnails); + if(count($possibleThumbnails) === 1) { + return current($possibleThumbnails); } - ksort($possiblethumbnails); + ksort($possibleThumbnails); - if(key(reset($possiblethumbnails)) > $maxX) { - return current(reset($possiblethumbnails)); + if(key(reset($possibleThumbnails)) > $maxX) { + return current(reset($possibleThumbnails)); } - if(key(end($possiblethumbnails)) < $maxX) { - return current(end($possiblethumbnails)); + if(key(end($possibleThumbnails)) < $maxX) { + return current(end($possibleThumbnails)); } - foreach($possiblethumbnails as $width => $path) { + foreach($possibleThumbnails as $width => $path) { if($width < $maxX) { continue; }else{ @@ -358,7 +358,7 @@ class Preview { /** * @brief return a preview of a file - * @return image + * @return \OC_Image */ public function getPreview() { if(!is_null($this->preview) && $this->preview->valid()){ @@ -369,10 +369,10 @@ class Preview { $file = $this->getFile(); $maxX = $this->getMaxX(); $maxY = $this->getMaxY(); - $scalingup = $this->getScalingup(); + $scalingUp = $this->getScalingUp(); - $fileinfo = $this->fileview->getFileInfo($file); - $fileid = $fileinfo['fileid']; + $fileInfo = $this->fileview->getFileInfo($file); + $fileId = $fileInfo['fileid']; $cached = $this->isCached(); @@ -386,12 +386,12 @@ class Preview { $mimetype = $this->fileview->getMimeType($file); $preview = null; - foreach(self::$providers as $supportedmimetype => $provider) { - if(!preg_match($supportedmimetype, $mimetype)) { + foreach(self::$providers as $supportedMimetype => $provider) { + if(!preg_match($supportedMimetype, $mimetype)) { continue; } - $preview = $provider->getThumbnail($file, $maxX, $maxY, $scalingup, $this->fileview); + $preview = $provider->getThumbnail($file, $maxX, $maxY, $scalingUp, $this->fileview); if(!($preview instanceof \OC_Image)) { continue; @@ -400,18 +400,18 @@ class Preview { $this->preview = $preview; $this->resizeAndCrop(); - $previewpath = $this->getThumbnailsFolder() . '/' . $fileid . '/'; - $cachepath = $previewpath . $maxX . '-' . $maxY . '.png'; + $previewPath = $this->getThumbnailsFolder() . '/' . $fileId . '/'; + $cachePath = $previewPath . $maxX . '-' . $maxY . '.png'; if($this->userview->is_dir($this->getThumbnailsFolder() . '/') === false) { $this->userview->mkdir($this->getThumbnailsFolder() . '/'); } - if($this->userview->is_dir($previewpath) === false) { - $this->userview->mkdir($previewpath); + if($this->userview->is_dir($previewPath) === false) { + $this->userview->mkdir($previewPath); } - $this->userview->file_put_contents($cachepath, $preview->data()); + $this->userview->file_put_contents($cachePath, $preview->data()); break; } @@ -447,13 +447,13 @@ class Preview { /** * @brief resize, crop and fix orientation - * @return image + * @return void */ private function resizeAndCrop() { $image = $this->preview; $x = $this->getMaxX(); $y = $this->getMaxY(); - $scalingup = $this->getScalingup(); + $scalingUp = $this->getScalingUp(); $maxscalefactor = $this->getMaxScaleFactor(); if(!($image instanceof \OC_Image)) { @@ -480,7 +480,7 @@ class Preview { $factor = $factorY; } - if($scalingup === false) { + if($scalingUp === false) { if($factor > 1) { $factor = 1; } @@ -583,182 +583,6 @@ class Preview { array_multisort($keys, SORT_DESC, self::$providers); } - /** - * @brief method that handles preview requests from users that are logged in - * @return void - */ - public static function previewRouter() { - \OC_Util::checkLoggedIn(); - - $file = array_key_exists('file', $_GET) ? (string) urldecode($_GET['file']) : ''; - $maxX = array_key_exists('x', $_GET) ? (int) $_GET['x'] : '36'; - $maxY = array_key_exists('y', $_GET) ? (int) $_GET['y'] : '36'; - $scalingup = array_key_exists('scalingup', $_GET) ? (bool) $_GET['scalingup'] : true; - - if($file === '') { - \OC_Response::setStatus(400); //400 Bad Request - \OC_Log::write('core-preview', 'No file parameter was passed', \OC_Log::DEBUG); - self::showErrorPreview(); - exit; - } - - if($maxX === 0 || $maxY === 0) { - \OC_Response::setStatus(400); //400 Bad Request - \OC_Log::write('core-preview', 'x and/or y set to 0', \OC_Log::DEBUG); - self::showErrorPreview(); - exit; - } - - try{ - $preview = new Preview(\OC_User::getUser(), 'files'); - $preview->setFile($file); - $preview->setMaxX($maxX); - $preview->setMaxY($maxY); - $preview->setScalingUp($scalingup); - - $preview->show(); - }catch(\Exception $e) { - \OC_Response::setStatus(500); - \OC_Log::write('core', $e->getmessage(), \OC_Log::ERROR); - self::showErrorPreview(); - exit; - } - } - - /** - * @brief method that handles preview requests from users that are not logged in / view shared folders that are public - * @return void - */ - public static function publicPreviewRouter() { - if(!\OC_App::isEnabled('files_sharing')){ - exit; - } - - $file = array_key_exists('file', $_GET) ? (string) urldecode($_GET['file']) : ''; - $maxX = array_key_exists('x', $_GET) ? (int) $_GET['x'] : '36'; - $maxY = array_key_exists('y', $_GET) ? (int) $_GET['y'] : '36'; - $scalingup = array_key_exists('scalingup', $_GET) ? (bool) $_GET['scalingup'] : true; - $token = array_key_exists('t', $_GET) ? (string) $_GET['t'] : ''; - - if($token === ''){ - \OC_Response::setStatus(400); //400 Bad Request - \OC_Log::write('core-preview', 'No token parameter was passed', \OC_Log::DEBUG); - self::showErrorPreview(); - exit; - } - - $linkedItem = \OCP\Share::getShareByToken($token); - if($linkedItem === false || ($linkedItem['item_type'] !== 'file' && $linkedItem['item_type'] !== 'folder')) { - \OC_Response::setStatus(404); - \OC_Log::write('core-preview', 'Passed token parameter is not valid', \OC_Log::DEBUG); - self::showErrorPreview(); - exit; - } - - if(!isset($linkedItem['uid_owner']) || !isset($linkedItem['file_source'])) { - \OC_Response::setStatus(500); - \OC_Log::write('core-preview', 'Passed token seems to be valid, but it does not contain all necessary information . ("' . $token . '")'); - self::showErrorPreview(); - exit; - } - - $userid = $linkedItem['uid_owner']; - \OC_Util::setupFS($userid); - - $pathid = $linkedItem['file_source']; - $path = \OC\Files\Filesystem::getPath($pathid); - $pathinfo = \OC\Files\Filesystem::getFileInfo($path); - $sharedfile = null; - - if($linkedItem['item_type'] === 'folder') { - $isvalid = \OC\Files\Filesystem::isValidPath($file); - if(!$isvalid) { - \OC_Response::setStatus(400); //400 Bad Request - \OC_Log::write('core-preview', 'Passed filename is not valid, might be malicious (file:"' . $file . '";ip:"' . $_SERVER['REMOTE_ADDR'] . '")', \OC_Log::WARN); - self::showErrorPreview(); - exit; - } - $sharedfile = \OC\Files\Filesystem::normalizePath($file); - } - - if($linkedItem['item_type'] === 'file') { - $parent = $pathinfo['parent']; - $path = \OC\Files\Filesystem::getPath($parent); - $sharedfile = $pathinfo['name']; - } - - $path = \OC\Files\Filesystem::normalizePath($path, false); - if(substr($path, 0, 1) === '/') { - $path = substr($path, 1); - } - - if($maxX === 0 || $maxY === 0) { - \OC_Response::setStatus(400); //400 Bad Request - \OC_Log::write('core-preview', 'x and/or y set to 0', \OC_Log::DEBUG); - self::showErrorPreview(); - exit; - } - - $root = 'files/' . $path; - - try{ - $preview = new Preview($userid, $root); - $preview->setFile($file); - $preview->setMaxX($maxX); - $preview->setMaxY($maxY); - $preview->setScalingUp($scalingup); - - $preview->show(); - }catch(\Exception $e) { - \OC_Response::setStatus(500); - \OC_Log::write('core', $e->getmessage(), \OC_Log::ERROR); - self::showErrorPreview(); - exit; - } - } - - public static function trashbinPreviewRouter() { - \OC_Util::checkLoggedIn(); - - if(!\OC_App::isEnabled('files_trashbin')){ - exit; - } - - $file = array_key_exists('file', $_GET) ? (string) urldecode($_GET['file']) : ''; - $maxX = array_key_exists('x', $_GET) ? (int) $_GET['x'] : '44'; - $maxY = array_key_exists('y', $_GET) ? (int) $_GET['y'] : '44'; - $scalingup = array_key_exists('scalingup', $_GET) ? (bool) $_GET['scalingup'] : true; - - if($file === '') { - \OC_Response::setStatus(400); //400 Bad Request - \OC_Log::write('core-preview', 'No file parameter was passed', \OC_Log::DEBUG); - self::showErrorPreview(); - exit; - } - - if($maxX === 0 || $maxY === 0) { - \OC_Response::setStatus(400); //400 Bad Request - \OC_Log::write('core-preview', 'x and/or y set to 0', \OC_Log::DEBUG); - self::showErrorPreview(); - exit; - } - - try{ - $preview = new Preview(\OC_User::getUser(), 'files_trashbin/files'); - $preview->setFile($file); - $preview->setMaxX($maxX); - $preview->setMaxY($maxY); - $preview->setScalingUp($scalingup); - - $preview->showPreview(); - }catch(\Exception $e) { - \OC_Response::setStatus(500); - \OC_Log::write('core', $e->getmessage(), \OC_Log::ERROR); - self::showErrorPreview(); - exit; - } - } - public static function post_write($args) { self::post_delete($args); } @@ -780,8 +604,8 @@ class Preview { //remove last element because it has the mimetype * $providers = array_slice(self::$providers, 0, -1); - foreach($providers as $supportedmimetype => $provider) { - if(preg_match($supportedmimetype, $mimetype)) { + foreach($providers as $supportedMimetype => $provider) { + if(preg_match($supportedMimetype, $mimetype)) { return true; } } diff --git a/lib/preview/images.php b/lib/preview/images.php index 987aa9aef0a..9aec967282d 100644 --- a/lib/preview/images.php +++ b/lib/preview/images.php @@ -16,10 +16,13 @@ class Image extends Provider { public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) { //get fileinfo - $fileinfo = $fileview->getFileInfo($path); + $fileInfo = $fileview->getFileInfo($path); + if(!$fileInfo) { + return false; + } //check if file is encrypted - if($fileinfo['encrypted'] === true) { + if($fileInfo['encrypted'] === true) { $image = new \OC_Image(stream_get_contents($fileview->fopen($path, 'r'))); }else{ $image = new \OC_Image(); diff --git a/lib/preview/libreoffice-cl.php b/lib/preview/libreoffice-cl.php index 2749c4867e9..0f4ec3d0348 100644 --- a/lib/preview/libreoffice-cl.php +++ b/lib/preview/libreoffice-cl.php @@ -22,28 +22,30 @@ class Office extends Provider { return false; } - $abspath = $fileview->toTmpFile($path); + $absPath = $fileview->toTmpFile($path); - $tmpdir = get_temp_dir(); + $tmpDir = get_temp_dir(); - $exec = $this->cmd . ' --headless --nologo --nofirststartwizard --invisible --norestore -convert-to pdf -outdir ' . escapeshellarg($tmpdir) . ' ' . escapeshellarg($abspath); - $export = 'export HOME=/' . $tmpdir; + $exec = $this->cmd . ' --headless --nologo --nofirststartwizard --invisible --norestore -convert-to pdf -outdir ' . escapeshellarg($tmpDir) . ' ' . escapeshellarg($absPath); + $export = 'export HOME=/' . $tmpDir; shell_exec($export . "\n" . $exec); //create imagick object from pdf try{ - $pdf = new \imagick($abspath . '.pdf' . '[0]'); + $pdf = new \imagick($absPath . '.pdf' . '[0]'); $pdf->setImageFormat('jpg'); - }catch(\Exception $e){ + }catch (\Exception $e) { + unlink($absPath); + unlink($absPath . '.pdf'); \OC_Log::write('core', $e->getmessage(), \OC_Log::ERROR); return false; } $image = new \OC_Image($pdf); - unlink($abspath); - unlink($abspath . '.pdf'); + unlink($absPath); + unlink($absPath . '.pdf'); return $image->valid() ? $image : false; } @@ -55,11 +57,13 @@ class Office extends Provider { $cmd = \OC_Config::getValue('preview_libreoffice_path', null); } - if($cmd === '' && shell_exec('libreoffice --headless --version')) { + $whichLibreOffice = shell_exec('which libreoffice'); + if($cmd === '' && !empty($whichLibreOffice)) { $cmd = 'libreoffice'; } - if($cmd === '' && shell_exec('openoffice --headless --version')) { + $whichOpenOffice = shell_exec('which openoffice'); + if($cmd === '' && !empty($whichOpenOffice)) { $cmd = 'openoffice'; } diff --git a/lib/preview/movies.php b/lib/preview/movies.php index 8531050d112..e2a1b8edddc 100644 --- a/lib/preview/movies.php +++ b/lib/preview/movies.php @@ -8,7 +8,11 @@ */ namespace OC\Preview; -if(!is_null(shell_exec('ffmpeg -version'))) { +$isShellExecEnabled = !in_array('shell_exec', explode(', ', ini_get('disable_functions'))); +$whichFFMPEG = shell_exec('which ffmpeg'); +$isFFMPEGAvailable = !empty($whichFFMPEG); + +if($isShellExecEnabled && $isFFMPEGAvailable) { class Movie extends Provider { @@ -17,23 +21,23 @@ if(!is_null(shell_exec('ffmpeg -version'))) { } public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) { - $abspath = \OC_Helper::tmpFile(); - $tmppath = \OC_Helper::tmpFile(); + $absPath = \OC_Helper::tmpFile(); + $tmpPath = \OC_Helper::tmpFile(); $handle = $fileview->fopen($path, 'rb'); $firstmb = stream_get_contents($handle, 1048576); //1024 * 1024 = 1048576 - file_put_contents($abspath, $firstmb); + file_put_contents($absPath, $firstmb); - //$cmd = 'ffmpeg -y -i ' . escapeshellarg($abspath) . ' -f mjpeg -vframes 1 -ss 1 -s ' . escapeshellarg($maxX) . 'x' . escapeshellarg($maxY) . ' ' . $tmppath; - $cmd = 'ffmpeg -an -y -i ' . escapeshellarg($abspath) . ' -f mjpeg -vframes 1 -ss 1 ' . escapeshellarg($tmppath); + //$cmd = 'ffmpeg -y -i ' . escapeshellarg($absPath) . ' -f mjpeg -vframes 1 -ss 1 -s ' . escapeshellarg($maxX) . 'x' . escapeshellarg($maxY) . ' ' . $tmpPath; + $cmd = 'ffmpeg -an -y -i ' . escapeshellarg($absPath) . ' -f mjpeg -vframes 1 -ss 1 ' . escapeshellarg($tmpPath); shell_exec($cmd); - $image = new \OC_Image($tmppath); + $image = new \OC_Image($tmpPath); - unlink($abspath); - unlink($tmppath); + unlink($absPath); + unlink($tmpPath); return $image->valid() ? $image : false; } diff --git a/lib/preview/mp3.php b/lib/preview/mp3.php index 835ff529000..1eed566315c 100644 --- a/lib/preview/mp3.php +++ b/lib/preview/mp3.php @@ -18,19 +18,21 @@ class MP3 extends Provider { $getID3 = new \getID3(); - $tmppath = $fileview->toTmpFile($path); - - $tags = $getID3->analyze($tmppath); - \getid3_lib::CopyTagsToComments($tags); - $picture = @$tags['id3v2']['APIC'][0]['data']; - - unlink($tmppath); + $tmpPath = $fileview->toTmpFile($path); + + $tags = $getID3->analyze($tmpPath); + \getid3_lib::CopyTagsToComments($tags); + if(isset($tags['id3v2']['APIC'][0]['data'])) { + $picture = @$tags['id3v2']['APIC'][0]['data']; + unlink($tmpPath); + $image = new \OC_Image($picture); + return $image->valid() ? $image : $this->getNoCoverThumbnail(); + } - $image = new \OC_Image($picture); - return $image->valid() ? $image : $this->getNoCoverThumbnail($maxX, $maxY); + return $this->getNoCoverThumbnail(); } - public function getNoCoverThumbnail($maxX, $maxY) { + private function getNoCoverThumbnail() { $icon = \OC::$SERVERROOT . '/core/img/filetypes/audio.png'; if(!file_exists($icon)) { diff --git a/lib/preview/msoffice.php b/lib/preview/msoffice.php index ccf1d674c7a..e69ab0ab8cb 100644 --- a/lib/preview/msoffice.php +++ b/lib/preview/msoffice.php @@ -32,16 +32,16 @@ class DOCX extends Provider { public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) { require_once('phpdocx/classes/TransformDoc.inc'); - $tmpdoc = $fileview->toTmpFile($path); + $tmpDoc = $fileview->toTmpFile($path); $transformdoc = new \TransformDoc(); - $transformdoc->setStrFile($tmpdoc); - $transformdoc->generatePDF($tmpdoc); + $transformdoc->setStrFile($tmpDoc); + $transformdoc->generatePDF($tmpDoc); - $pdf = new \imagick($tmpdoc . '[0]'); + $pdf = new \imagick($tmpDoc . '[0]'); $pdf->setImageFormat('jpg'); - unlink($tmpdoc); + unlink($tmpDoc); $image = new \OC_Image($pdf); @@ -62,23 +62,23 @@ class MSOfficeExcel extends Provider { require_once('PHPExcel/Classes/PHPExcel.php'); require_once('PHPExcel/Classes/PHPExcel/IOFactory.php'); - $abspath = $fileview->toTmpFile($path); - $tmppath = \OC_Helper::tmpFile(); + $absPath = $fileview->toTmpFile($path); + $tmpPath = \OC_Helper::tmpFile(); $rendererName = \PHPExcel_Settings::PDF_RENDERER_DOMPDF; $rendererLibraryPath = \OC::$THIRDPARTYROOT . '/3rdparty/dompdf'; \PHPExcel_Settings::setPdfRenderer($rendererName, $rendererLibraryPath); - $phpexcel = new \PHPExcel($abspath); + $phpexcel = new \PHPExcel($absPath); $excel = \PHPExcel_IOFactory::createWriter($phpexcel, 'PDF'); - $excel->save($tmppath); + $excel->save($tmpPath); - $pdf = new \imagick($tmppath . '[0]'); + $pdf = new \imagick($tmpPath . '[0]'); $pdf->setImageFormat('jpg'); - unlink($abspath); - unlink($tmppath); + unlink($absPath); + unlink($tmpPath); $image = new \OC_Image($pdf); diff --git a/lib/preview/office.php b/lib/preview/office.php index b6783bc5798..b93e1e57c8b 100644 --- a/lib/preview/office.php +++ b/lib/preview/office.php @@ -7,8 +7,13 @@ */ //both, libreoffice backend and php fallback, need imagick if (extension_loaded('imagick')) { + $isShellExecEnabled = !in_array('shell_exec', explode(', ', ini_get('disable_functions'))); + $whichLibreOffice = shell_exec('which libreoffice'); + $isLibreOfficeAvailable = !empty($whichLibreOffice); + $whichOpenOffice = shell_exec('which libreoffice'); + $isOpenOfficeAvailable = !empty($whichOpenOffice); //let's see if there is libreoffice or openoffice on this machine - if(shell_exec('libreoffice --headless --version') || shell_exec('openoffice --headless --version') || is_string(\OC_Config::getValue('preview_libreoffice_path', null))) { + if($isShellExecEnabled && ($isLibreOfficeAvailable || $isOpenOfficeAvailable || is_string(\OC_Config::getValue('preview_libreoffice_path', null)))) { require_once('libreoffice-cl.php'); }else{ //in case there isn't, use our fallback diff --git a/lib/preview/pdf.php b/lib/preview/pdf.php index 3eabd201156..723dc1d80d2 100644 --- a/lib/preview/pdf.php +++ b/lib/preview/pdf.php @@ -16,18 +16,18 @@ if (extension_loaded('imagick')) { } public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) { - $tmppath = $fileview->toTmpFile($path); + $tmpPath = $fileview->toTmpFile($path); //create imagick object from pdf try{ - $pdf = new \imagick($tmppath . '[0]'); + $pdf = new \imagick($tmpPath . '[0]'); $pdf->setImageFormat('jpg'); - }catch(\Exception $e){ + }catch (\Exception $e) { \OC_Log::write('core', $e->getmessage(), \OC_Log::ERROR); return false; } - unlink($tmppath); + unlink($tmpPath); //new image object $image = new \OC_Image($pdf); diff --git a/lib/preview/txt.php b/lib/preview/txt.php index c7b8fabc6b0..89927fd580a 100644 --- a/lib/preview/txt.php +++ b/lib/preview/txt.php @@ -18,24 +18,23 @@ class TXT extends Provider { $content = stream_get_contents($content); $lines = preg_split("/\r\n|\n|\r/", $content); - $numoflines = count($lines); - $fontsize = 5; //5px - $linesize = ceil($fontsize * 1.25); + $fontSize = 5; //5px + $lineSize = ceil($fontSize * 1.25); $image = imagecreate($maxX, $maxY); - $imagecolor = imagecolorallocate($image, 255, 255, 255); - $textcolor = imagecolorallocate($image, 0, 0, 0); + imagecolorallocate($image, 255, 255, 255); + $textColor = imagecolorallocate($image, 0, 0, 0); foreach($lines as $index => $line) { $index = $index + 1; $x = (int) 1; - $y = (int) ($index * $linesize) - $fontsize; + $y = (int) ($index * $lineSize) - $fontSize; - imagestring($image, 1, $x, $y, $line, $textcolor); + imagestring($image, 1, $x, $y, $line, $textColor); - if(($index * $linesize) >= $maxY) { + if(($index * $lineSize) >= $maxY) { break; } } diff --git a/lib/preview/unknown.php b/lib/preview/unknown.php index a31b365722e..ba13ca35d66 100644 --- a/lib/preview/unknown.php +++ b/lib/preview/unknown.php @@ -20,7 +20,7 @@ class Unknown extends Provider { list($type, $subtype) = explode('/', $mimetype); } - $iconsroot = \OC::$SERVERROOT . '/core/img/filetypes/'; + $iconsRoot = \OC::$SERVERROOT . '/core/img/filetypes/'; if(isset($type)){ $icons = array($mimetype, $type, 'text'); @@ -30,10 +30,10 @@ class Unknown extends Provider { foreach($icons as $icon) { $icon = str_replace('/', '-', $icon); - $iconpath = $iconsroot . $icon . '.png'; + $iconPath = $iconsRoot . $icon . '.png'; - if(file_exists($iconpath)) { - return new \OC_Image($iconpath); + if(file_exists($iconPath)) { + return new \OC_Image($iconPath); } } return false; diff --git a/lib/template.php b/lib/template.php index caa1e667c61..9b2c1211e61 100644 --- a/lib/template.php +++ b/lib/template.php @@ -23,6 +23,9 @@ require_once __DIR__.'/template/functions.php'; +/** + * This class provides the templates for ownCloud. + */ class OC_Template extends \OC\Template\Base { private $renderas; // Create a full page? private $path; // The path to the template diff --git a/lib/template/functions.php b/lib/template/functions.php index a864614c9a1..842f28c90e0 100644 --- a/lib/template/functions.php +++ b/lib/template/functions.php @@ -47,6 +47,22 @@ function image_path( $app, $image ) { return OC_Helper::imagePath( $app, $image ); } +/** + * @brief make preview_icon available as a simple function + * Returns the path to the preview of the image. + * @param $path path of file + * @returns link to the preview + * + * For further information have a look at OC_Helper::previewIcon + */ +function preview_icon( $path ) { + return OC_Helper::previewIcon( $path ); +} + +function publicPreview_icon ( $path, $token ) { + return OC_Helper::publicPreviewIcon( $path, $token ); +} + /** * @brief make OC_Helper::mimetypeIcon available as a simple function * @param string $mimetype mimetype -- cgit v1.2.3 From 3cbbe395eba76be37c16dcb00ac93760e965975e Mon Sep 17 00:00:00 2001 From: Georg Ehrke Date: Wed, 14 Aug 2013 11:38:52 +0200 Subject: don't use hardcoded size for preview --- apps/files/js/files.js | 4 +++- apps/files/templates/index.php | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'apps/files/js/files.js') diff --git a/apps/files/js/files.js b/apps/files/js/files.js index 8b66ed6747b..180c23cbfa4 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -825,7 +825,9 @@ function getMimeIcon(mime, ready){ getMimeIcon.cache={}; function getPreviewIcon(path, ready){ - ready(OC.Router.generate('core_ajax_preview', {file: encodeURIComponent(path), x:36, y:36})); + var x = $('#filestable').data('preview-x'); + var y = $('#filestable').data('preview-y'); + ready(OC.Router.generate('core_ajax_preview', {file: encodeURIComponent(path), x:x, y:y})); } function getUniqueName(name){ diff --git a/apps/files/templates/index.php b/apps/files/templates/index.php index e4348904671..311ada70dfd 100644 --- a/apps/files/templates/index.php +++ b/apps/files/templates/index.php @@ -59,7 +59,7 @@
t('Nothing in here. Upload something!'))?>
- +
-- cgit v1.2.3 From 4f525c864df7eb6caf1bc945c3c9e48b20bcce5e Mon Sep 17 00:00:00 2001 From: Georg Ehrke Date: Wed, 14 Aug 2013 13:25:07 +0200 Subject: lazy load preview icons --- apps/files/js/filelist.js | 2 +- apps/files/js/files.js | 20 ++++++++++++++------ 2 files changed, 15 insertions(+), 7 deletions(-) (limited to 'apps/files/js/files.js') diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 288648693be..138329940b4 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -186,7 +186,7 @@ var FileList={ tr.attr('data-id', id); } var path = $('#dir').val()+'/'+name; - getPreviewIcon(path, function(previewpath){ + lazyLoadPreview(path, mime, function(previewpath){ tr.find('td.filename').attr('style','background-image:url('+previewpath+')'); }); tr.find('td.filename').draggable(dragOptions); diff --git a/apps/files/js/files.js b/apps/files/js/files.js index 180c23cbfa4..7b01a5202da 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -366,8 +366,8 @@ $(document).ready(function() { var tr=$('tr').filterAttr('data-file',name); tr.attr('data-mime',result.data.mime); tr.attr('data-id', result.data.id); - var path = $('#dir').val()+'/'+name; - getPreviewIcon(path, function(previewpath){ + var path = $('#dir').val() + '/' + name; + lazyLoadPreview(path, result.data.mime, function(previewpath){ tr.find('td.filename').attr('style','background-image:url('+previewpath+')'); }); } else { @@ -432,7 +432,7 @@ $(document).ready(function() { tr.data('mime',mime).data('id',id); tr.attr('data-id', id); var path = $('#dir').val()+'/'+localName; - getPreviewIcon(path, function(previewpath){ + lazyLoadPreview(path, mime, function(previewpath){ tr.find('td.filename').attr('style','background-image:url('+previewpath+')'); }); }); @@ -639,7 +639,7 @@ var createDragShadow = function(event){ newtr.find('td.filename').attr('style','background-image:url('+OC.imagePath('core', 'filetypes/folder.png')+')'); } else { var path = $('#dir').val()+'/'+elem.name; - getPreviewIcon(path, function(previewpath){ + lazyLoadPreview(path, elem.mime, function(previewpath){ newtr.find('td.filename').attr('style','background-image:url('+previewpath+')'); }); } @@ -824,10 +824,18 @@ function getMimeIcon(mime, ready){ } getMimeIcon.cache={}; -function getPreviewIcon(path, ready){ +function lazyLoadPreview(path, mime, ready) { + getMimeIcon(mime,ready); var x = $('#filestable').data('preview-x'); var y = $('#filestable').data('preview-y'); - ready(OC.Router.generate('core_ajax_preview', {file: encodeURIComponent(path), x:x, y:y})); + var previewURL = OC.Router.generate('core_ajax_preview', {file: encodeURIComponent(path), x:x, y:y}); + $.ajax({ + url: previewURL, + type: 'GET', + success: function() { + ready(previewURL); + } + }); } function getUniqueName(name){ -- cgit v1.2.3 From f94e6036980644bdd6312e75a8973f2633cf5ff2 Mon Sep 17 00:00:00 2001 From: Jörn Friedrich Dreyer Date: Fri, 16 Aug 2013 11:40:55 +0200 Subject: progress fixes --- apps/files/ajax/upload.php | 9 +- apps/files/css/files.css | 23 +++- apps/files/js/file-upload.js | 263 ++++++++++++++++++++++++++------------- apps/files/js/fileactions.js | 2 +- apps/files/js/filelist.js | 265 ++++++++++++++++++++++++---------------- apps/files/js/files.js | 29 +---- apps/files_sharing/js/public.js | 7 +- core/js/oc-dialogs.js | 215 ++++++++++++++++++-------------- 8 files changed, 503 insertions(+), 310 deletions(-) (limited to 'apps/files/js/files.js') diff --git a/apps/files/ajax/upload.php b/apps/files/ajax/upload.php index 8d183bd1f9c..619b5f6a04b 100644 --- a/apps/files/ajax/upload.php +++ b/apps/files/ajax/upload.php @@ -99,8 +99,8 @@ if (strpos($dir, '..') === false) { $fileCount = count($files['name']); for ($i = 0; $i < $fileCount; $i++) { // $path needs to be normalized - this failed within drag'n'drop upload to a sub-folder - if (isset($_POST['new_name'])) { - $newName = $_POST['new_name']; + if (isset($_POST['newname'])) { + $newName = $_POST['newname']; } else { $newName = $files['name'][$i]; } @@ -109,11 +109,12 @@ if (strpos($dir, '..') === false) { } else { $replace = false; } - $target = \OC\Files\Filesystem::normalizePath(stripslashes($dir).$newName); + $target = \OC\Files\Filesystem::normalizePath(stripslashes($dir).'/'.$newName); if ( ! $replace && \OC\Files\Filesystem::file_exists($target)) { $meta = \OC\Files\Filesystem::getFileInfo($target); $result[] = array('status' => 'existserror', - 'mime' => $meta['mimetype'], + 'type' => $meta['mimetype'], + 'mtime' => $meta['mtime'], 'size' => $meta['size'], 'id' => $meta['fileid'], 'name' => basename($target), diff --git a/apps/files/css/files.css b/apps/files/css/files.css index acee8471aff..0ff25a24d76 100644 --- a/apps/files/css/files.css +++ b/apps/files/css/files.css @@ -190,6 +190,9 @@ table.dragshadow td.size { margin-left: -200px; } +.oc-dialog .fileexists table { + width: 100%; +} .oc-dialog .fileexists .original .icon { width: 64px; height: 64px; @@ -201,6 +204,7 @@ table.dragshadow td.size { .oc-dialog .fileexists .replacement { margin-top: 20px; + margin-bottom: 20px; } .oc-dialog .fileexists .replacement .icon { @@ -213,10 +217,23 @@ table.dragshadow td.size { clear: both; } -.oc-dialog .fileexists label[for="new-name"] { - margin-top: 20px; - display: block; +.oc-dialog .fileexists .toggle { + background-image: url('%webroot%/core/img/actions/triangle-e.png'); + width: 16px; + height: 16px; +} +.oc-dialog .fileexists #allfileslabel { + float:right; } +.oc-dialog .fileexists #allfiles { + vertical-align: bottom; + position: relative; + top: -3px; +} +.oc-dialog .fileexists #allfiles + span{ + vertical-align: bottom; +} + .oc-dialog .fileexists h3 { font-weight: bold; } diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index bd9757b5ffc..f8899cb07e6 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -1,4 +1,30 @@ /** + * 1. tracking which file to upload next -> upload queue with data elements added whenever add is called + * 2. tracking progress for each folder individually -> track progress in a progress[dirname] object + * - every new selection increases the total size and number of files for a directory + * - add increases, successful done decreases, skip decreases, cancel decreases + * 3. track selections -> the general skip / overwrite decision is selection based and can change + * - server might send already exists error -> show dialog & remember decision for selection again + * - server sends error, how do we find collection? + * 4. track jqXHR object to prevent browser from navigationg away -> track in a uploads[dirname][filename] object [x] + * + * selections can progress in parrallel but each selection progresses sequentially + * + * -> store everything in context? + * context.folder + * context.element? + * context.progressui? + * context.jqXHR + * context.selection + * context.selection.onExistsAction? + * + * context available in what events? + * build in drop() add dir + * latest in add() add file? add selection! + * progress? -> update progress? + * onsubmit -> context.jqXHR? + * fail() -> + * done() * * when versioning app is active -> always overwrite * @@ -22,24 +48,74 @@ * dialoge: * -> skip, replace, choose (or abort) () * -> choose left or right (with skip) (when only one file in list also show rename option and remember for all option) + * + * progress always based on filesize + * number of files as text, bytes as bar + * */ -OC.upload = { + +//TODO clean uploads when all progress has completed +OC.Upload = { + /** + * map to lookup the selections for a given directory. + * @type Array + */ + _selections: {}, + /* + * queue which progress tracker to use for the next upload + * @type Array + */ + _queue: [], + queueUpload:function(data) { + // add to queue + this._queue.push(data); //remember what to upload next + if ( ! this.isProcessing() ) { + this.startUpload(); + } + }, + getSelection:function(originalFiles) { + if (!originalFiles.selectionKey) { + originalFiles.selectionKey = 'selection-' + $.assocArraySize(this._selections); + this._selections[originalFiles.selectionKey] = { + selectionKey:originalFiles.selectionKey, + files:{}, + totalBytes:0, + loadedBytes:0, + currentFile:0, + uploads:{}, + checked:false + }; + } + return this._selections[originalFiles.selectionKey]; + }, + cancelUpload:function(dir, filename) { + var deleted = false; + jQuery.each(this._selections, function(i, selection) { + if (selection.dir === dir && selection.uploads[filename]) { + delete selection.uploads[filename]; + deleted = true; + return false; // end searching through selections + } + }); + return deleted; + }, + cancelUploads:function() { + jQuery.each(this._selections,function(i,selection){ + jQuery.each(selection.uploads, function (j, jqXHR) { + delete jqXHR; + }); + }); + this._queue = []; + this._isProcessing = false; + }, _isProcessing:false, isProcessing:function(){ return this._isProcessing; }, - _uploadQueue:[], - addUpload:function(data){ - this._uploadQueue.push(data); - - if ( ! OC.upload.isProcessing() ) { - OC.upload.startUpload(); - } - }, startUpload:function(){ - if (this._uploadQueue.length > 0) { + if (this._queue.length > 0) { this._isProcessing = true; this.nextUpload(); return true; @@ -48,32 +124,50 @@ OC.upload = { } }, nextUpload:function(){ - if (this._uploadQueue.length > 0) { - var data = this._uploadQueue.pop(); - var jqXHR = data.submit(); - - // remember jqXHR to show warning to user when he navigates away but an upload is still in progress - if (typeof data.context !== 'undefined' && data.context.data('type') === 'dir') { - var dirName = data.context.data('file'); - if(typeof uploadingFiles[dirName] === 'undefined') { - uploadingFiles[dirName] = {}; - } - uploadingFiles[dirName][data.files[0].name] = jqXHR; - } else { - uploadingFiles[data.files[0].name] = jqXHR; - } + if (this._queue.length > 0) { + var data = this._queue.pop(); + var selection = this.getSelection(data.originalFiles); + selection.uploads[data.files[0]] = data.submit(); + } else { //queue is empty, we are done this._isProcessing = false; + //TODO free data } + }, + progressBytes: function() { + var total = 0; + var loaded = 0; + jQuery.each(this._selections, function (i, selection) { + total += selection.totalBytes; + loaded += selection.loadedBytes; + }); + return (loaded/total)*100; + }, + loadedBytes: function() { + var loaded = 0; + jQuery.each(this._selections, function (i, selection) { + loaded += selection.loadedBytes; + }); + return loaded; + }, + totalBytes: function() { + var total = 0; + jQuery.each(this._selections, function (i, selection) { + total += selection.totalBytes; + }); + return total; + }, + handleExists:function(data) { + }, onCancel:function(data){ //TODO cancel all uploads - Files.cancelUploads(); - this._uploadQueue = []; - this._isProcessing = false; + OC.Upload.cancelUploads(); }, onSkip:function(data){ + var selection = this.getSelection(data.originalFiles); + selection.loadedBytes += data.loaded; this.nextUpload(); }, onReplace:function(data){ @@ -83,8 +177,14 @@ OC.upload = { }, onRename:function(data, newName){ //TODO rename file in filelist, stop spinner - data.data.append('new_name', newName); + data.data.append('newname', newName); data.submit(); + }, + setAction:function(data, action) { + + }, + setDefaultAction:function(action) { + } }; @@ -92,15 +192,23 @@ $(document).ready(function() { var file_upload_param = { dropZone: $('#content'), // restrict dropZone to content div + //singleFileUploads is on by default, so the data.files array will always have length 1 add: function(e, data) { var that = $(this); - - if (typeof data.originalFiles.checked === 'undefined') { + + // lookup selection for dir + var selection = OC.Upload.getSelection(data.originalFiles); + + if (!selection.dir) { + selection.dir = $('#dir').val(); + } + + if ( ! selection.checked ) { - var totalSize = 0; + selection.totalBytes = 0; $.each(data.originalFiles, function(i, file) { - totalSize += file.size; + selection.totalBytes += file.size; if (file.type === '' && file.size === 4096) { data.textStatus = 'dirorzero'; @@ -111,11 +219,10 @@ $(document).ready(function() { } }); - if (totalSize > $('#max_upload').val()) { + if (selection.totalBytes > $('#max_upload').val()) { data.textStatus = 'notenoughspace'; data.errorThrown = t('files', 'Not enough space available'); } - if (data.errorThrown) { //don't upload anything var fu = that.data('blueimp-fileupload') || that.data('fileupload'); @@ -123,9 +230,22 @@ $(document).ready(function() { return false; } - data.originalFiles.checked = true; // this will skip the checks on subsequent adds + //TODO refactor away: + //show cancel button + if($('html.lte9').length === 0 && data.dataType !== 'iframe') { + $('#uploadprogresswrapper input.stop').show(); + } } + //all subsequent add calls for this selection can be ignored + //allow navigating to the selection from a context + //context.selection = data.originalFiles.selection; + + //allow navigating to contexts / files of a selection + selection.files[data.files[0].name] = data; + + OC.Upload.queueUpload(data); + //TODO check filename already exists /* if ($('tr[data-file="'+data.files[0].name+'"][data-id]').length > 0) { @@ -140,14 +260,6 @@ $(document).ready(function() { } */ - //add files to queue - OC.upload.addUpload(data); - - //TODO refactor away: - //show cancel button - if($('html.lte9').length === 0 && data.dataType !== 'iframe') { - $('#uploadprogresswrapper input.stop').show(); - } return true; }, /** @@ -176,7 +288,8 @@ $(document).ready(function() { $('#notification').fadeOut(); }, 5000); } - delete uploadingFiles[data.files[0].name]; + var selection = OC.Upload.getSelection(data.originalFiles); + delete selection.uploads[data.files[0]]; }, progress: function(e, data) { // TODO: show nice progress bar in file row @@ -186,7 +299,8 @@ $(document).ready(function() { if($('html.lte9').length > 0) { return; } - var progress = (data.loaded/data.total)*100; + //var progress = (data.loaded/data.total)*100; + var progress = OC.Upload.progressBytes(); $('#uploadprogressbar').progressbar('value', progress); }, /** @@ -204,27 +318,22 @@ $(document).ready(function() { response = data.result[0].body.innerText; } var result=$.parseJSON(response); + var selection = OC.Upload.getSelection(data.originalFiles); - if(typeof result[0] !== 'undefined' && result[0].status === 'success') { - OC.upload.nextUpload(); + if(typeof result[0] !== 'undefined' + && result[0].status === 'success' + ) { + selection.loadedBytes+=data.loaded; + OC.Upload.nextUpload(); } else { if (result[0].status === 'existserror') { - //TODO open dialog and retry with other name? - // get jqXHR reference - if (typeof data.context !== 'undefined' && data.context.data('type') === 'dir') { - var dirName = data.context.data('file'); - var jqXHR = uploadingFiles[dirName][filename]; - } else { - var jqXHR = uploadingFiles[filename]; - } - //filenames can only be changed on the server side - //TODO show "file already exists" dialog - //options: abort | skip | replace / rename - //TODO reset all-files flag? when done with selection? + //show "file already exists" dialog var original = result[0]; var replacement = data.files[0]; - OC.dialogs.fileexists(data, original, replacement, OC.upload); + var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); + OC.dialogs.fileexists(data, original, replacement, OC.Upload, fu); } else { + delete selection.uploads[data.files[0]]; data.textStatus = 'servererror'; data.errorThrown = t('files', result.data.message); var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); @@ -232,19 +341,6 @@ $(document).ready(function() { } } - var filename = result[0].originalname; - - // delete jqXHR reference - if (typeof data.context !== 'undefined' && data.context.data('type') === 'dir') { - var dirName = data.context.data('file'); - delete uploadingFiles[dirName][filename]; - if ($.assocArraySize(uploadingFiles[dirName]) === 0) { - delete uploadingFiles[dirName]; - } - } else { - delete uploadingFiles[filename]; - } - }, /** * called after last upload @@ -252,17 +348,20 @@ $(document).ready(function() { * @param data */ stop: function(e, data) { - if(data.dataType !== 'iframe') { - $('#uploadprogresswrapper input.stop').hide(); - } + if(OC.Upload.progressBytes()>=100) { - //IE < 10 does not fire the necessary events for the progress bar. - if($('html.lte9').length > 0) { - return; - } + if(data.dataType !== 'iframe') { + $('#uploadprogresswrapper input.stop').hide(); + } - $('#uploadprogressbar').progressbar('value', 100); - $('#uploadprogressbar').fadeOut(); + //IE < 10 does not fire the necessary events for the progress bar. + if($('html.lte9').length > 0) { + return; + } + + $('#uploadprogressbar').progressbar('value', 100); + $('#uploadprogressbar').fadeOut(); + } } }; diff --git a/apps/files/js/fileactions.js b/apps/files/js/fileactions.js index aa66a57a7b5..277abcfdb15 100644 --- a/apps/files/js/fileactions.js +++ b/apps/files/js/fileactions.js @@ -174,7 +174,7 @@ $(document).ready(function () { FileActions.register('all', 'Delete', OC.PERMISSION_DELETE, function () { return OC.imagePath('core', 'actions/delete'); }, function (filename) { - if (Files.cancelUpload(filename)) { + if (OC.Upload.cancelUpload($('#dir').val(), filename)) { if (filename.substr) { filename = [filename]; } diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index f4863837ce7..335f81e04b9 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -407,151 +407,212 @@ $(document).ready(function(){ // handle upload events var file_upload_start = $('#file_upload_start'); + file_upload_start.on('fileuploaddrop', function(e, data) { - // only handle drop to dir if fileList exists - if ($('#fileList').length > 0) { - var dropTarget = $(e.originalEvent.target).closest('tr'); - if(dropTarget && dropTarget.data('type') === 'dir') { // drag&drop upload to folder - data.context = dropTarget; - var dirName = dropTarget.data('file'); - // update folder in form - data.formData = function(form) { - var formArray = form.serializeArray(); - // array index 0 contains the max files size - // array index 1 contains the request token - // array index 2 contains the directory - var parentDir = formArray[2]['value']; - if (parentDir === '/') { - formArray[2]['value'] += dirName; - } else { - formArray[2]['value'] += '/'+dirName; - } - return formArray; - }; + console.log('fileuploaddrop ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + + var dropTarget = $(e.originalEvent.target).closest('tr'); + if(dropTarget && dropTarget.data('type') === 'dir') { // drag&drop upload to folder + + // lookup selection for dir + var selection = OC.Upload.getSelection(data.files); + + // remember drop target + selection.dropTarget = dropTarget; + + selection.dir = dropTarget.data('file'); + if (selection.dir !== '/') { + if ($('#dir').val() === '/') { + selection.dir = '/' + selection.dir; + } else { + selection.dir = $('#dir').val() + '/' + selection.dir; + } } - } + + // update folder in form + data.formData = function(form) { + var formArray = form.serializeArray(); + // array index 0 contains the max files size + // array index 1 contains the request token + // array index 2 contains the directory + var parentDir = formArray[2]['value']; + if (parentDir === '/') { + formArray[2]['value'] += selection.dir; + } else { + formArray[2]['value'] += '/' + selection.dir; + } + + return formArray; + }; + } + }); file_upload_start.on('fileuploadadd', function(e, data) { - // only add to fileList if it exists - if ($('#fileList').length > 0) { + console.log('fileuploadadd ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + + // lookup selection for dir + var selection = OC.Upload.getSelection(data.originalFiles); + + if(FileList.deleteFiles && FileList.deleteFiles.indexOf(data.files[0].name)!==-1){//finish delete if we are uploading a deleted file + FileList.finishDelete(null, true); //delete file before continuing + } + + // add ui visualization to existing folder + if(selection.dropTarget && selection.dropTarget.data('type') === 'dir') { + // add to existing folder + var dirName = selection.dropTarget.data('file'); + + // set dir context + data.context = $('tr').filterAttr('data-type', 'dir').filterAttr('data-file', dirName); - if(FileList.deleteFiles && FileList.deleteFiles.indexOf(data.files[0].name)!==-1){//finish delete if we are uploading a deleted file - FileList.finishDelete(null, true); //delete file before continuing + // update upload counter ui + var uploadtext = data.context.find('.uploadtext'); + var currentUploads = parseInt(uploadtext.attr('currentUploads')); + currentUploads += 1; + uploadtext.attr('currentUploads', currentUploads); + + if(currentUploads === 1) { + var img = OC.imagePath('core', 'loading.gif'); + data.context.find('td.filename').attr('style','background-image:url('+img+')'); + uploadtext.text(t('files', '1 file uploading')); + uploadtext.show(); + } else { + uploadtext.text(currentUploads + ' ' + t('files', 'files uploading')); } + } + + }); + file_upload_start.on('fileuploaddone', function(e, data) { + console.log('fileuploaddone ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + + var response; + if (typeof data.result === 'string') { + response = data.result; + } else { + // fetch response from iframe + response = data.result[0].body.innerText; + } + var result=$.parseJSON(response); - // add ui visualization to existing folder - var dropTarget = $(e.originalEvent.target).closest('tr'); - if(dropTarget && dropTarget.data('type') === 'dir') { - // add to existing folder - var dirName = dropTarget.data('file'); + if(typeof result[0] !== 'undefined' && result[0].status === 'success') { + var file = result[0]; - // set dir context - data.context = $('tr').filterAttr('data-type', 'dir').filterAttr('data-file', dirName); + if (data.context && data.context.data('type') === 'dir') { // update upload counter ui var uploadtext = data.context.find('.uploadtext'); var currentUploads = parseInt(uploadtext.attr('currentUploads')); - currentUploads += 1; + currentUploads -= 1; uploadtext.attr('currentUploads', currentUploads); - if(currentUploads === 1) { - var img = OC.imagePath('core', 'loading.gif'); + if(currentUploads === 0) { + var img = OC.imagePath('core', 'filetypes/folder.png'); data.context.find('td.filename').attr('style','background-image:url('+img+')'); - uploadtext.text(t('files', '1 file uploading')); - uploadtext.show(); + uploadtext.text(''); + uploadtext.hide(); } else { uploadtext.text(currentUploads + ' ' + t('files', 'files uploading')); } - } - } - }); - file_upload_start.on('fileuploaddone', function(e, data) { - // only update the fileList if it exists - if ($('#fileList').length > 0) { - var response; - if (typeof data.result === 'string') { - response = data.result; + + // update folder size + var size = parseInt(data.context.data('size')); + size += parseInt(file.size); + data.context.attr('data-size', size); + data.context.find('td.filesize').text(humanFileSize(size)); + } else { - // fetch response from iframe - response = data.result[0].body.innerText; - } - var result=$.parseJSON(response); - - if(typeof result[0] !== 'undefined' && result[0].status === 'success') { - var file = result[0]; - - if (data.context && data.context.data('type') === 'dir') { - - // update upload counter ui - var uploadtext = data.context.find('.uploadtext'); - var currentUploads = parseInt(uploadtext.attr('currentUploads')); - currentUploads -= 1; - uploadtext.attr('currentUploads', currentUploads); - if(currentUploads === 0) { - var img = OC.imagePath('core', 'filetypes/folder.png'); - data.context.find('td.filename').attr('style','background-image:url('+img+')'); - uploadtext.text(''); - uploadtext.hide(); - } else { - uploadtext.text(currentUploads + ' ' + t('files', 'files uploading')); - } - // update folder size - var size = parseInt(data.context.data('size')); - size += parseInt(file.size) ; - data.context.attr('data-size', size); - data.context.find('td.filesize').text(humanFileSize(size)); + // add as stand-alone row to filelist + var size=t('files','Pending'); + if (data.files[0].size>=0){ + size=data.files[0].size; + } + var date=new Date(); + var param = {}; + if ($('#publicUploadRequestToken').length) { + param.download_url = document.location.href + '&download&path=/' + $('#dir').val() + '/' + file.name; + } + //should the file exist in the list remove it + FileList.remove(file.name); + + // create new file context + data.context = FileList.addFile(file.name, file.size, date, false, false, param); - } else { - - // add as stand-alone row to filelist - var uniqueName = getUniqueName(data.files[0].name); - var size=t('files','Pending'); - if (data.files[0].size>=0){ - size=data.files[0].size; - } - var date=new Date(); - var param = {}; - if ($('#publicUploadRequestToken').length) { - param.download_url = document.location.href + '&download&path=/' + $('#dir').val() + '/' + uniqueName; - } - - //should the file exist in the list remove it - FileList.remove(file.name); + // update file data + data.context.attr('data-mime',file.mime).attr('data-id',file.id); - // create new file context - data.context = FileList.addFile(file.name, file.size, date, false, false, param); - - // update file data - data.context.attr('data-mime',file.mime).attr('data-id',file.id); - - getMimeIcon(file.mime, function(path){ - data.context.find('td.filename').attr('style','background-image:url('+path+')'); - }); - } + getMimeIcon(file.mime, function(path){ + data.context.find('td.filename').attr('style','background-image:url('+path+')'); + }); } } }); + + file_upload_start.on('fileuploadalways', function(e, data) { + console.log('fileuploadalways ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + }); + file_upload_start.on('fileuploadsend', function(e, data) { + console.log('fileuploadsend ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + + // TODOD add vis + //data.context.element = + }); + file_upload_start.on('fileuploadprogress', function(e, data) { + console.log('fileuploadprogress ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + }); + file_upload_start.on('fileuploadprogressall', function(e, data) { + console.log('fileuploadprogressall ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + }); + file_upload_start.on('fileuploadstop', function(e, data) { + console.log('fileuploadstop ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + }); + file_upload_start.on('fileuploadfail', function(e, data) { + console.log('fileuploadfail ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + }); + /* file_upload_start.on('fileuploadfail', function(e, data) { - // only update the fileList if it exists + console.log('fileuploadfail'+((data.files&&data.files.length>0)?' '+data.files[0].name:'')); + + // if we are uploading to a subdirectory + if (data.context && data.context.data('type') === 'dir') { + + // update upload counter ui + var uploadtext = data.context.find('.uploadtext'); + var currentUploads = parseInt(uploadtext.attr('currentUploads')); + currentUploads -= 1; + uploadtext.attr('currentUploads', currentUploads); + if(currentUploads === 0) { + var img = OC.imagePath('core', 'filetypes/folder.png'); + data.context.find('td.filename').attr('style','background-image:url('+img+')'); + uploadtext.text(''); + uploadtext.hide(); + } else { + uploadtext.text(currentUploads + ' ' + t('files', 'files uploading')); + } + + } + // cleanup files, error notification has been shown by fileupload code var tr = data.context; if (typeof tr === 'undefined') { tr = $('tr').filterAttr('data-file', data.files[0].name); } if (tr.attr('data-type') === 'dir') { + //cleanup uploading to a dir var uploadtext = tr.find('.uploadtext'); var img = OC.imagePath('core', 'filetypes/folder.png'); tr.find('td.filename').attr('style','background-image:url('+img+')'); uploadtext.text(''); uploadtext.hide(); //TODO really hide already + } else { + //TODO add row when sending file //remove file tr.fadeOut(); tr.remove(); } }); - +*/ $('#notification').hide(); $('#notification').on('click', '.undo', function(){ if (FileList.deleteFiles) { diff --git a/apps/files/js/files.js b/apps/files/js/files.js index 3fad3fae7d3..a907aeab1fc 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -1,31 +1,5 @@ var uploadingFiles = {}; Files={ - cancelUpload:function(filename) { - if(uploadingFiles[filename]) { - uploadingFiles[filename].abort(); - delete uploadingFiles[filename]; - return true; - } - return false; - }, - cancelUploads:function() { - $.each(uploadingFiles,function(index,file) { - if(typeof file['abort'] === 'function') { - file.abort(); - var filename = $('tr').filterAttr('data-file',index); - filename.hide(); - filename.find('input[type="checkbox"]').removeAttr('checked'); - filename.removeClass('selected'); - } else { - $.each(file,function(i,f) { - f.abort(); - delete file[i]; - }); - } - delete uploadingFiles[index]; - }); - procesSelection(); - }, updateMaxUploadFilesize:function(response) { if(response == undefined) { return; @@ -117,7 +91,8 @@ $(document).ready(function() { // Trigger cancelling of file upload $('#uploadprogresswrapper .stop').on('click', function() { - Files.cancelUploads(); + OC.Upload.cancelUploads(); + procesSelection(); }); // Show trash bin diff --git a/apps/files_sharing/js/public.js b/apps/files_sharing/js/public.js index 294223aa094..a20b4ae636f 100644 --- a/apps/files_sharing/js/public.js +++ b/apps/files_sharing/js/public.js @@ -62,7 +62,10 @@ $(document).ready(function() { // Add Uploadprogress Wrapper to controls bar $('#controls').append($('#additional_controls div#uploadprogresswrapper')); - // Cancel upload trigger - $('#cancel_upload_button').click(Files.cancelUploads); + // Cancel upload trigger + $('#cancel_upload_button').click(function() { + OC.Upload.cancelUploads(); + procesSelection(); + }); }); diff --git a/core/js/oc-dialogs.js b/core/js/oc-dialogs.js index 88a3f6628cb..ea03ef21455 100644 --- a/core/js/oc-dialogs.js +++ b/core/js/oc-dialogs.js @@ -207,105 +207,142 @@ var OCdialogs = { * @param {object} controller a controller with onCancel, onSkip, onReplace and onRename methods */ fileexists:function(data, original, replacement, controller) { - if (typeof controller !== 'object') { - controller = {}; - } - var self = this; - $.when(this._getFileExistsTemplate()).then(function($tmpl) { - var dialog_name = 'oc-dialog-fileexists-' + OCdialogs.dialogs_counter + '-content'; - var dialog_id = '#' + dialog_name; - var title = t('files','Replace »{filename}«?',{filename: original.name}); - var $dlg = $tmpl.octemplate({ - dialog_name: dialog_name, - title: title, - type: 'fileexists', - - why: t('files','Another file with the same name already exists in "{dir}".',{dir:'somedir'}), - what: t('files','Replacing it will overwrite it\'s contents.'), - original_heading: t('files','Original file'), - original_size: t('files','Size: {size}',{size: original.size}), - original_mtime: t('files','Last changed: {mtime}',{mtime: original.mtime}), + var selection = controller.getSelection(data.originalFiles); + if (selection.defaultAction) { + controller[selection.defaultAction](data); + } else { + $.when(this._getFileExistsTemplate()).then(function($tmpl) { + var dialog_name = 'oc-dialog-fileexists-' + OCdialogs.dialogs_counter + '-content'; + var dialog_id = '#' + dialog_name; + var title = t('files','Replace »{filename}«?',{filename: original.name}); + var original_size= t('files','Size: {size}',{size: original.size}); + var original_mtime = t('files','Last changed: {mtime}',{mtime: original.mtime}); + var replacement_size= t('files','Size: {size}',{size: replacement.size}); + var replacement_mtime = t('files','Last changed: {mtime}',{mtime: replacement.mtime}); + var $dlg = $tmpl.octemplate({ + dialog_name: dialog_name, + title: title, + type: 'fileexists', - replacement_heading: t('files','Replace with'), - replacement_size: t('files','Size: {size}',{size: replacement.size}), - replacement_mtime: t('files','Last changed: {mtime}',{mtime: replacement.mtime}), + why: t('files','Another file with the same name already exists in "{dir}".',{dir:'somedir'}), + what: t('files','Replacing it will overwrite it\'s contents.'), + original_heading: t('files','Original file'), + original_size: original_size, + original_mtime: original_mtime, - new_name_label: t('files','Choose a new name for the target.'), - all_files_label: t('files','Use this action for all files.') - }); - $('body').append($dlg); - - $(dialog_id + ' .original .icon').css('background-image','url('+OC.imagePath('core', 'filetypes/file.png')+')'); - $(dialog_id + ' .replacement .icon').css('background-image','url('+OC.imagePath('core', 'filetypes/file.png')+')'); - $(dialog_id + ' #new-name').val(original.name); - - - $(dialog_id + ' #new-name').on('keyup', function(e){ - if ($(dialog_id + ' #new-name').val() === original.name) { - - $(dialog_id + ' + div .rename').removeClass('primary').hide(); - $(dialog_id + ' + div .replace').addClass('primary').show(); - } else { - $(dialog_id + ' + div .rename').addClass('primary').show(); - $(dialog_id + ' + div .replace').removeClass('primary').hide(); - } - }); + replacement_heading: t('files','Replace with'), + replacement_size: replacement_size, + replacement_mtime: replacement_mtime, - buttonlist = [{ - text: t('core', 'Cancel'), - classes: 'cancel', - click: function(){ - if ( typeof controller.onCancel !== 'undefined') { - controller.onCancel(data); - } - $(dialog_id).ocdialog('close'); + new_name_label: t('files','Choose a new name for the target.'), + all_files_label: t('files','Use this action for all files.') + }); + $('body').append($dlg); + + getMimeIcon(original.type,function(path){ + $(dialog_id + ' .original .icon').css('background-image','url('+path+')'); + }); + getMimeIcon(replacement.type,function(path){ + $(dialog_id + ' .replacement .icon').css('background-image','url('+path+')'); + }); + $(dialog_id + ' #newname').val(original.name); + + + $(dialog_id + ' #newname').on('keyup', function(e){ + if ($(dialog_id + ' #newname').val() === original.name) { + $(dialog_id + ' + div .rename').removeClass('primary').hide(); + $(dialog_id + ' + div .replace').addClass('primary').show(); + } else { + $(dialog_id + ' + div .rename').addClass('primary').show(); + $(dialog_id + ' + div .replace').removeClass('primary').hide(); } - }, - { - text: t('core', 'Skip'), - classes: 'skip', - click: function(){ - if ( typeof controller.onSkip !== 'undefined') { - controller.onSkip(data); + }); + + buttonlist = [{ + text: t('core', 'Cancel'), + classes: 'cancel', + click: function(){ + if ( typeof controller.onCancel !== 'undefined') { + controller.onCancel(data); + } + $(dialog_id).ocdialog('close'); } - $(dialog_id).ocdialog('close'); - } - }, - { - text: t('core', 'Replace'), - classes: 'replace', - click: function(){ - if ( typeof controller.onReplace !== 'undefined') { - controller.onReplace(data); + }, + { + text: t('core', 'Skip'), + classes: 'skip', + click: function(){ + if ( typeof controller.onSkip !== 'undefined') { + if($(dialog_id + ' #allfiles').prop('checked')){ + selection.defaultAction = 'onSkip'; + /*selection.defaultAction = function(){ + controller.onSkip(data); + };*/ + } + controller.onSkip(data); + } + // trigger fileupload done with status skip + //data.result[0].status = 'skip'; + //fileupload._trigger('done', data.e, data); + $(dialog_id).ocdialog('close'); } - $(dialog_id).ocdialog('close'); }, - defaultButton: true - }, - { - text: t('core', 'Rename'), - classes: 'rename', - click: function(){ - if ( typeof controller.onRename !== 'undefined') { - controller.onRename(data, $(dialog_id + ' #new-name').val()); + { + text: t('core', 'Replace'), + classes: 'replace', + click: function(){ + if ( typeof controller.onReplace !== 'undefined') { + if($(dialog_id + ' #allfiles').prop('checked')){ + selection.defaultAction = 'onReplace'; + /*selection.defaultAction = function(){ + controller.onReplace(data); + };*/ + } + controller.onReplace(data); + } + $(dialog_id).ocdialog('close'); + }, + defaultButton: true + }, + { + text: t('core', 'Rename'), + classes: 'rename', + click: function(){ + if ( typeof controller.onRename !== 'undefined') { + //TODO use autorename when repeat is checked + controller.onRename(data, $(dialog_id + ' #newname').val()); + } + $(dialog_id).ocdialog('close'); } - $(dialog_id).ocdialog('close'); - } - }]; + }]; - $(dialog_id).ocdialog({ - closeOnEscape: true, - modal: true, - buttons: buttonlist, - closeButton: null + $(dialog_id).ocdialog({ + width: 500, + closeOnEscape: true, + modal: true, + buttons: buttonlist, + closeButton: null + }); + OCdialogs.dialogs_counter++; + + $(dialog_id + ' + div .rename').hide(); + $(dialog_id + ' #newname').hide(); + + $(dialog_id + ' #newnamecb').on('change', function(){ + if ($(dialog_id + ' #newnamecb').prop('checked')) { + $(dialog_id + ' #newname').fadeIn(); + } else { + $(dialog_id + ' #newname').fadeOut(); + $(dialog_id + ' #newname').val(original.name); + } + }); + + + }) + .fail(function() { + alert(t('core', 'Error loading file exists template')); }); - OCdialogs.dialogs_counter++; - - $(dialog_id + ' + div .rename').hide(); - }) - .fail(function() { - alert(t('core', 'Error loading file exists template')); - }); + } }, _getFilePickerTemplate: function() { var defer = $.Deferred(); -- cgit v1.2.3 From 164502477d8eac293ea002d39378be846bcc733c Mon Sep 17 00:00:00 2001 From: Georg Ehrke Date: Fri, 16 Aug 2013 17:24:45 +0200 Subject: use jQuery.get instead of jQuery.ajax --- apps/files/js/files.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'apps/files/js/files.js') diff --git a/apps/files/js/files.js b/apps/files/js/files.js index 3178ff65af8..fd18cf21ee8 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -821,12 +821,8 @@ function lazyLoadPreview(path, mime, ready) { var x = $('#filestable').data('preview-x'); var y = $('#filestable').data('preview-y'); var previewURL = OC.Router.generate('core_ajax_preview', {file: encodeURIComponent(path), x:x, y:y}); - $.ajax({ - url: previewURL, - type: 'GET', - success: function() { - ready(previewURL); - } + $.get(previewURL, function() { + ready(previewURL); }); } -- cgit v1.2.3 From bf04daff82758fe9913c706eef07aed30c9b35ea Mon Sep 17 00:00:00 2001 From: Jörn Friedrich Dreyer Date: Wed, 21 Aug 2013 14:58:28 +0200 Subject: architecture too complex --- 3rdparty | 2 +- apps/files/js/file-upload.js | 109 +++- apps/files/js/filelist.js | 51 +- apps/files/js/files.js | 7 - apps/files/js/jquery.fileupload.js | 1023 ++++++++++++++++++++++-------- apps/files/js/jquery.iframe-transport.js | 70 +- apps/files/templates/part.list.php | 11 +- 7 files changed, 934 insertions(+), 339 deletions(-) (limited to 'apps/files/js/files.js') diff --git a/3rdparty b/3rdparty index 2f3ae9f56a9..75a05d76ab8 160000 --- a/3rdparty +++ b/3rdparty @@ -1 +1 @@ -Subproject commit 2f3ae9f56a9838b45254393e13c14f8a8c380d6b +Subproject commit 75a05d76ab86ba7454b4312fd0ff2ca5bd5828cf diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index f8899cb07e6..c620942170c 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -1,4 +1,7 @@ /** + * + * use put t ocacnel upload before it starts? use chunked uploads? + * * 1. tracking which file to upload next -> upload queue with data elements added whenever add is called * 2. tracking progress for each folder individually -> track progress in a progress[dirname] object * - every new selection increases the total size and number of files for a directory @@ -63,6 +66,7 @@ OC.Upload = { * @type Array */ _selections: {}, + _selectionCount: 0, /* * queue which progress tracker to use for the next upload * @type Array @@ -77,7 +81,7 @@ OC.Upload = { }, getSelection:function(originalFiles) { if (!originalFiles.selectionKey) { - originalFiles.selectionKey = 'selection-' + $.assocArraySize(this._selections); + originalFiles.selectionKey = 'selection-' + this._selectionCount++; this._selections[originalFiles.selectionKey] = { selectionKey:originalFiles.selectionKey, files:{}, @@ -90,22 +94,41 @@ OC.Upload = { } return this._selections[originalFiles.selectionKey]; }, + deleteSelection:function(selectionKey) { + if (this._selections[selectionKey]) { + jQuery.each(this._selections[selectionKey].uploads, function(i, upload) { + upload.abort(); + }); + delete this._selections[selectionKey]; + } else { + console.log('OC.Upload: selection ' + selectionKey + ' does not exist'); + } + }, + deleteSelectionUpload:function(selection, filename) { + if(selection.uploads[filename]) { + selection.uploads[filename].abort(); + return true; + } else { + console.log('OC.Upload: selection ' + selection.selectionKey + ' does not contain upload for ' + filename); + } + return false; + }, cancelUpload:function(dir, filename) { + var self = this; var deleted = false; jQuery.each(this._selections, function(i, selection) { if (selection.dir === dir && selection.uploads[filename]) { - delete selection.uploads[filename]; - deleted = true; + deleted = self.deleteSelectionUpload(selection, filename); return false; // end searching through selections } }); return deleted; }, cancelUploads:function() { - jQuery.each(this._selections,function(i,selection){ - jQuery.each(selection.uploads, function (j, jqXHR) { - delete jqXHR; - }); + console.log('canceling uploads'); + var self = this; + jQuery.each(this._selections,function(i, selection){ + self.deleteSelection(selection.selectionKey); }); this._queue = []; this._isProcessing = false; @@ -132,7 +155,7 @@ OC.Upload = { } else { //queue is empty, we are done this._isProcessing = false; - //TODO free data + OC.Upload.cancelUploads(); } }, progressBytes: function() { @@ -157,13 +180,13 @@ OC.Upload = { total += selection.totalBytes; }); return total; - }, - handleExists:function(data) { - }, onCancel:function(data){ - //TODO cancel all uploads - OC.Upload.cancelUploads(); + //TODO cancel all uploads of this selection + + var selection = this.getSelection(data.originalFiles); + OC.Upload.deleteSelection(selection.selectionKey); + //FIXME hide progressbar }, onSkip:function(data){ var selection = this.getSelection(data.originalFiles); @@ -171,20 +194,19 @@ OC.Upload = { this.nextUpload(); }, onReplace:function(data){ - //TODO overwrite file data.data.append('replace', true); data.submit(); }, onRename:function(data, newName){ - //TODO rename file in filelist, stop spinner data.data.append('newname', newName); data.submit(); }, - setAction:function(data, action) { - - }, - setDefaultAction:function(action) { - + logStatus:function(caption, e, data) { + console.log(caption+' ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + if (data) { + console.log(data); + } + console.log(e); } }; @@ -195,6 +217,7 @@ $(document).ready(function() { //singleFileUploads is on by default, so the data.files array will always have length 1 add: function(e, data) { + OC.Upload.logStatus('add', e, data); var that = $(this); // lookup selection for dir @@ -267,14 +290,17 @@ $(document).ready(function() { * @param e */ start: function(e) { + OC.Upload.logStatus('start', e, null); //IE < 10 does not fire the necessary events for the progress bar. if($('html.lte9').length > 0) { return true; } + $('#uploadprogresswrapper input.stop').show(); $('#uploadprogressbar').progressbar({value:0}); $('#uploadprogressbar').fadeIn(); }, fail: function(e, data) { + OC.Upload.logStatus('fail', e, data); if (typeof data.textStatus !== 'undefined' && data.textStatus !== 'success' ) { if (data.textStatus === 'abort') { $('#notification').text(t('files', 'Upload cancelled.')); @@ -289,12 +315,26 @@ $(document).ready(function() { }, 5000); } var selection = OC.Upload.getSelection(data.originalFiles); - delete selection.uploads[data.files[0]]; + OC.Upload.deleteSelectionUpload(selection, data.files[0].name); + + //if user pressed cancel hide upload progress bar and cancel button + if (data.errorThrown === 'abort') { + $('#uploadprogresswrapper input.stop').fadeOut(); + $('#uploadprogressbar').fadeOut(); + } }, progress: function(e, data) { + OC.Upload.logStatus('progress', e, data); // TODO: show nice progress bar in file row }, + /** + * + * @param {type} e + * @param {type} data (only has loaded, total and lengthComputable) + * @returns {unresolved} + */ progressall: function(e, data) { + OC.Upload.logStatus('progressall', e, data); //IE < 10 does not fire the necessary events for the progress bar. if($('html.lte9').length > 0) { return; @@ -309,6 +349,7 @@ $(document).ready(function() { * @param data */ done:function(e, data) { + OC.Upload.logStatus('done', e, data); // handle different responses (json or body from iframe for ie) var response; if (typeof data.result === 'string') { @@ -323,7 +364,9 @@ $(document).ready(function() { if(typeof result[0] !== 'undefined' && result[0].status === 'success' ) { - selection.loadedBytes+=data.loaded; + if (selection) { + selection.loadedBytes+=data.loaded; + } OC.Upload.nextUpload(); } else { if (result[0].status === 'existserror') { @@ -333,13 +376,19 @@ $(document).ready(function() { var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); OC.dialogs.fileexists(data, original, replacement, OC.Upload, fu); } else { - delete selection.uploads[data.files[0]]; + OC.Upload.deleteSelectionUpload(selection, data.files[0].name); data.textStatus = 'servererror'; data.errorThrown = t('files', result.data.message); var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); fu._trigger('fail', e, data); } } + + //if user pressed cancel hide upload chrome + if (! OC.Upload.isProcessing()) { + $('#uploadprogresswrapper input.stop').fadeOut(); + $('#uploadprogressbar').fadeOut(); + } }, /** @@ -348,7 +397,10 @@ $(document).ready(function() { * @param data */ stop: function(e, data) { - if(OC.Upload.progressBytes()>=100) { + OC.Upload.logStatus('stop', e, data); + if(OC.Upload.progressBytes()>=100) { //only hide controls when all selections have ended uploading + + OC.Upload.cancelUploads(); //cleanup if(data.dataType !== 'iframe') { $('#uploadprogresswrapper input.stop').hide(); @@ -362,6 +414,11 @@ $(document).ready(function() { $('#uploadprogressbar').progressbar('value', 100); $('#uploadprogressbar').fadeOut(); } + //if user pressed cancel hide upload chrome + if (! OC.Upload.isProcessing()) { + $('#uploadprogresswrapper input.stop').fadeOut(); + $('#uploadprogressbar').fadeOut(); + } } }; @@ -384,8 +441,8 @@ $(document).ready(function() { }; // warn user not to leave the page while upload is in progress - $(window).bind('beforeunload', function(e) { - if ($.assocArraySize(uploadingFiles) > 0) { + $(window).on('beforeunload', function(e) { + if (OC.Upload.isProcessing()) { return t('files', 'File upload is in progress. Leaving the page now will cancel the upload.'); } }); diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 335f81e04b9..eb57672e464 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -409,7 +409,7 @@ $(document).ready(function(){ var file_upload_start = $('#file_upload_start'); file_upload_start.on('fileuploaddrop', function(e, data) { - console.log('fileuploaddrop ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + OC.Upload.logStatus('fileuploaddrop', e, data); var dropTarget = $(e.originalEvent.target).closest('tr'); if(dropTarget && dropTarget.data('type') === 'dir') { // drag&drop upload to folder @@ -448,7 +448,7 @@ $(document).ready(function(){ }); file_upload_start.on('fileuploadadd', function(e, data) { - console.log('fileuploadadd ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + OC.Upload.logStatus('fileuploadadd', e, data); // lookup selection for dir var selection = OC.Upload.getSelection(data.originalFiles); @@ -482,8 +482,11 @@ $(document).ready(function(){ } }); + file_upload_start.on('fileuploadstart', function(e, data) { + OC.Upload.logStatus('fileuploadstart', e, data); + }); file_upload_start.on('fileuploaddone', function(e, data) { - console.log('fileuploaddone ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + OC.Upload.logStatus('fileuploaddone', e, data); var response; if (typeof data.result === 'string') { @@ -545,28 +548,58 @@ $(document).ready(function(){ }); } } + + //if user pressed cancel hide upload chrome + if (! OC.Upload.isProcessing()) { + //cleanup uploading to a dir + var uploadtext = $('tr .uploadtext'); + var img = OC.imagePath('core', 'filetypes/folder.png'); + uploadtext.parents('td.filename').attr('style','background-image:url('+img+')'); + uploadtext.fadeOut(); + uploadtext.attr('currentUploads', 0); + } }); file_upload_start.on('fileuploadalways', function(e, data) { - console.log('fileuploadalways ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + OC.Upload.logStatus('fileuploadalways', e, data); }); file_upload_start.on('fileuploadsend', function(e, data) { - console.log('fileuploadsend ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + OC.Upload.logStatus('fileuploadsend', e, data); // TODOD add vis //data.context.element = }); file_upload_start.on('fileuploadprogress', function(e, data) { - console.log('fileuploadprogress ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + OC.Upload.logStatus('fileuploadprogress', e, data); }); file_upload_start.on('fileuploadprogressall', function(e, data) { - console.log('fileuploadprogressall ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + OC.Upload.logStatus('fileuploadprogressall', e, data); }); file_upload_start.on('fileuploadstop', function(e, data) { - console.log('fileuploadstop ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + OC.Upload.logStatus('fileuploadstop', e, data); + + //if user pressed cancel hide upload chrome + if (! OC.Upload.isProcessing()) { + //cleanup uploading to a dir + var uploadtext = $('tr .uploadtext'); + var img = OC.imagePath('core', 'filetypes/folder.png'); + uploadtext.parents('td.filename').attr('style','background-image:url('+img+')'); + uploadtext.fadeOut(); + uploadtext.attr('currentUploads', 0); + } }); file_upload_start.on('fileuploadfail', function(e, data) { - console.log('fileuploadfail ' +OC.Upload.loadedBytes()+' / '+OC.Upload.totalBytes()); + OC.Upload.logStatus('fileuploadfail', e, data); + + //if user pressed cancel hide upload chrome + if (data.errorThrown === 'abort') { + //cleanup uploading to a dir + var uploadtext = $('tr .uploadtext'); + var img = OC.imagePath('core', 'filetypes/folder.png'); + uploadtext.parents('td.filename').attr('style','background-image:url('+img+')'); + uploadtext.fadeOut(); + uploadtext.attr('currentUploads', 0); + } }); /* file_upload_start.on('fileuploadfail', function(e, data) { diff --git a/apps/files/js/files.js b/apps/files/js/files.js index a907aeab1fc..53405c7fe7e 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -1,4 +1,3 @@ -var uploadingFiles = {}; Files={ updateMaxUploadFilesize:function(response) { if(response == undefined) { @@ -235,12 +234,6 @@ $(document).ready(function() { return size; }; - // warn user not to leave the page while upload is in progress - $(window).bind('beforeunload', function(e) { - if ($.assocArraySize(uploadingFiles) > 0) - return t('files','File upload is in progress. Leaving the page now will cancel the upload.'); - }); - //add multiply file upload attribute to all browsers except konqueror (which crashes when it's used) if(navigator.userAgent.search(/konqueror/i)==-1){ $('#file_upload_start').attr('multiple','multiple') diff --git a/apps/files/js/jquery.fileupload.js b/apps/files/js/jquery.fileupload.js index a89e9dc2c44..f9f6cc3a382 100644 --- a/apps/files/js/jquery.fileupload.js +++ b/apps/files/js/jquery.fileupload.js @@ -1,5 +1,5 @@ /* - * jQuery File Upload Plugin 5.9 + * jQuery File Upload Plugin 5.32.2 * https://github.com/blueimp/jQuery-File-Upload * * Copyright 2010, Sebastian Tschan @@ -10,7 +10,7 @@ */ /*jslint nomen: true, unparam: true, regexp: true */ -/*global define, window, document, Blob, FormData, location */ +/*global define, window, document, location, File, Blob, FormData */ (function (factory) { 'use strict'; @@ -27,12 +27,28 @@ }(function ($) { 'use strict'; + // Detect file input support, based on + // http://viljamis.com/blog/2012/file-upload-support-on-mobile/ + $.support.fileInput = !(new RegExp( + // Handle devices which give false positives for the feature detection: + '(Android (1\\.[0156]|2\\.[01]))' + + '|(Windows Phone (OS 7|8\\.0))|(XBLWP)|(ZuneWP)|(WPDesktop)' + + '|(w(eb)?OSBrowser)|(webOS)' + + '|(Kindle/(1\\.0|2\\.[05]|3\\.0))' + ).test(window.navigator.userAgent) || + // Feature detection for all other devices: + $('').prop('disabled')); + // The FileReader API is not actually used, but works as feature detection, // as e.g. Safari supports XHR file uploads via the FormData API, // but not non-multipart XHR file uploads: $.support.xhrFileUpload = !!(window.XMLHttpRequestUpload && window.FileReader); $.support.xhrFormDataFileUpload = !!window.FormData; + // Detect support for Blob slicing (required for chunked uploads): + $.support.blobSlice = window.Blob && (Blob.prototype.slice || + Blob.prototype.webkitSlice || Blob.prototype.mozSlice); + // The fileupload widget listens for change events on file input fields defined // via fileInput setting and paste or drop events of the given dropZone. // In addition to the default jQuery Widget methods, the fileupload widget @@ -44,17 +60,16 @@ $.widget('blueimp.fileupload', { options: { - // The namespace used for event handler binding on the dropZone and - // fileInput collections. - // If not set, the name of the widget ("fileupload") is used. - namespace: undefined, - // The drop target collection, by the default the complete document. - // Set to null or an empty collection to disable drag & drop support: + // The drop target element(s), by the default the complete document. + // Set to null to disable drag & drop support: dropZone: $(document), - // The file input field collection, that is listened for change events. + // The paste target element(s), by the default the complete document. + // Set to null to disable paste support: + pasteZone: $(document), + // The file input field(s), that are listened to for change events. // If undefined, it is set to the file input fields inside // of the widget element on plugin initialization. - // Set to null or an empty collection to disable the change listener. + // Set to null to disable the change listener. fileInput: undefined, // By default, the file input field is replaced with a clone after // each input field change event. This is required for iframe transport @@ -63,7 +78,8 @@ replaceFileInput: true, // The parameter name for the file form data (the request argument name). // If undefined or empty, the name property of the file input field is - // used, or "files[]" if the file input name property is also empty: + // used, or "files[]" if the file input name property is also empty, + // can be a string or an array of strings: paramName: undefined, // By default, each file of a selection is uploaded using an individual // request for XHR type uploads. Set to false to upload file @@ -108,6 +124,29 @@ // global progress calculation. Set the following option to false to // prevent recalculating the global progress data: recalculateProgress: true, + // Interval in milliseconds to calculate and trigger progress events: + progressInterval: 100, + // Interval in milliseconds to calculate progress bitrate: + bitrateInterval: 500, + // By default, uploads are started automatically when adding files: + autoUpload: true, + + // Error and info messages: + messages: { + uploadedBytes: 'Uploaded bytes exceed file size' + }, + + // Translation function, gets the message key to be translated + // and an object with context specific data as arguments: + i18n: function (message, context) { + message = this.messages[message] || message.toString(); + if (context) { + $.each(context, function (key, value) { + message = message.replace('{' + key + '}', value); + }); + } + return message; + }, // Additional form data to be sent along with the file uploads can be set // using this option, which accepts an array of objects with name and @@ -121,48 +160,81 @@ // The add callback is invoked as soon as files are added to the fileupload // widget (via file input selection, drag & drop, paste or add API call). // If the singleFileUploads option is enabled, this callback will be - // called once for each file in the selection for XHR file uplaods, else + // called once for each file in the selection for XHR file uploads, else // once for each file selection. + // // The upload starts when the submit method is invoked on the data parameter. // The data object contains a files property holding the added files - // and allows to override plugin options as well as define ajax settings. + // and allows you to override plugin options as well as define ajax settings. + // // Listeners for this callback can also be bound the following way: // .bind('fileuploadadd', func); + // // data.submit() returns a Promise object and allows to attach additional // handlers using jQuery's Deferred callbacks: // data.submit().done(func).fail(func).always(func); add: function (e, data) { - data.submit(); + if (data.autoUpload || (data.autoUpload !== false && + $(this).fileupload('option', 'autoUpload'))) { + data.process().done(function () { + data.submit(); + }); + } }, // Other callbacks: + // Callback for the submit event of each file upload: // submit: function (e, data) {}, // .bind('fileuploadsubmit', func); + // Callback for the start of each file upload request: // send: function (e, data) {}, // .bind('fileuploadsend', func); + // Callback for successful uploads: // done: function (e, data) {}, // .bind('fileuploaddone', func); + // Callback for failed (abort or error) uploads: // fail: function (e, data) {}, // .bind('fileuploadfail', func); + // Callback for completed (success, abort or error) requests: // always: function (e, data) {}, // .bind('fileuploadalways', func); + // Callback for upload progress events: // progress: function (e, data) {}, // .bind('fileuploadprogress', func); + // Callback for global upload progress events: // progressall: function (e, data) {}, // .bind('fileuploadprogressall', func); + // Callback for uploads start, equivalent to the global ajaxStart event: // start: function (e) {}, // .bind('fileuploadstart', func); + // Callback for uploads stop, equivalent to the global ajaxStop event: // stop: function (e) {}, // .bind('fileuploadstop', func); - // Callback for change events of the fileInput collection: + + // Callback for change events of the fileInput(s): // change: function (e, data) {}, // .bind('fileuploadchange', func); - // Callback for paste events to the dropZone collection: + + // Callback for paste events to the pasteZone(s): // paste: function (e, data) {}, // .bind('fileuploadpaste', func); - // Callback for drop events of the dropZone collection: + + // Callback for drop events of the dropZone(s): // drop: function (e, data) {}, // .bind('fileuploaddrop', func); - // Callback for dragover events of the dropZone collection: + + // Callback for dragover events of the dropZone(s): // dragover: function (e) {}, // .bind('fileuploaddragover', func); + // Callback for the start of each chunk upload request: + // chunksend: function (e, data) {}, // .bind('fileuploadchunksend', func); + + // Callback for successful chunk uploads: + // chunkdone: function (e, data) {}, // .bind('fileuploadchunkdone', func); + + // Callback for failed (abort or error) chunk uploads: + // chunkfail: function (e, data) {}, // .bind('fileuploadchunkfail', func); + + // Callback for completed (success, abort or error) chunk upload requests: + // chunkalways: function (e, data) {}, // .bind('fileuploadchunkalways', func); + // The plugin options are used as settings object for the ajax calls. // The following are jQuery ajax settings required for the file uploads: processData: false, @@ -170,15 +242,36 @@ cache: false }, - // A list of options that require a refresh after assigning a new value: - _refreshOptionsList: [ - 'namespace', - 'dropZone', + // A list of options that require reinitializing event listeners and/or + // special initialization code: + _specialOptions: [ 'fileInput', + 'dropZone', + 'pasteZone', 'multipart', 'forceIframeTransport' ], + _blobSlice: $.support.blobSlice && function () { + var slice = this.slice || this.webkitSlice || this.mozSlice; + return slice.apply(this, arguments); + }, + + _BitrateTimer: function () { + this.timestamp = ((Date.now) ? Date.now() : (new Date()).getTime()); + this.loaded = 0; + this.bitrate = 0; + this.getBitrate = function (now, loaded, interval) { + var timeDiff = now - this.timestamp; + if (!this.bitrate || !interval || timeDiff > interval) { + this.bitrate = (loaded - this.loaded) * (1000 / timeDiff) * 8; + this.loaded = loaded; + this.timestamp = now; + } + return this.bitrate; + }; + }, + _isXHRUpload: function (options) { return !options.forceIframeTransport && ((!options.multipart && $.support.xhrFileUpload) || @@ -189,9 +282,11 @@ var formData; if (typeof options.formData === 'function') { return options.formData(options.form); - } else if ($.isArray(options.formData)) { + } + if ($.isArray(options.formData)) { return options.formData; - } else if (options.formData) { + } + if ($.type(options.formData) === 'object') { formData = []; $.each(options.formData, function (name, value) { formData.push({name: name, value: value}); @@ -209,28 +304,66 @@ return total; }, + _initProgressObject: function (obj) { + var progress = { + loaded: 0, + total: 0, + bitrate: 0 + }; + if (obj._progress) { + $.extend(obj._progress, progress); + } else { + obj._progress = progress; + } + }, + + _initResponseObject: function (obj) { + var prop; + if (obj._response) { + for (prop in obj._response) { + if (obj._response.hasOwnProperty(prop)) { + delete obj._response[prop]; + } + } + } else { + obj._response = {}; + } + }, + _onProgress: function (e, data) { if (e.lengthComputable) { - var total = data.total || this._getTotal(data.files), - loaded = parseInt( - e.loaded / e.total * (data.chunkSize || total), - 10 - ) + (data.uploadedBytes || 0); - this._loaded += loaded - (data.loaded || data.uploadedBytes || 0); - data.lengthComputable = true; - data.loaded = loaded; - data.total = total; + var now = ((Date.now) ? Date.now() : (new Date()).getTime()), + loaded; + if (data._time && data.progressInterval && + (now - data._time < data.progressInterval) && + e.loaded !== e.total) { + return; + } + data._time = now; + loaded = Math.floor( + e.loaded / e.total * (data.chunkSize || data._progress.total) + ) + (data.uploadedBytes || 0); + // Add the difference from the previously loaded state + // to the global loaded counter: + this._progress.loaded += (loaded - data._progress.loaded); + this._progress.bitrate = this._bitrateTimer.getBitrate( + now, + this._progress.loaded, + data.bitrateInterval + ); + data._progress.loaded = data.loaded = loaded; + data._progress.bitrate = data.bitrate = data._bitrateTimer.getBitrate( + now, + loaded, + data.bitrateInterval + ); // Trigger a custom progress event with a total data property set // to the file size(s) of the current upload and a loaded data // property calculated accordingly: this._trigger('progress', e, data); // Trigger a global progress event for all current file uploads, // including ajax calls queued for sequential file uploads: - this._trigger('progressall', e, { - lengthComputable: true, - loaded: this._loaded, - total: this._total - }); + this._trigger('progressall', e, this._progress); } }, @@ -254,34 +387,30 @@ } }, + _isInstanceOf: function (type, obj) { + // Cross-frame instanceof check + return Object.prototype.toString.call(obj) === '[object ' + type + ']'; + }, + _initXHRData: function (options) { - var formData, + var that = this, + formData, file = options.files[0], // Ignore non-multipart setting if not supported: - multipart = options.multipart || !$.support.xhrFileUpload; - if (!multipart || options.blob) { - // For non-multipart uploads and chunked uploads, - // file meta data is not part of the request body, - // so we transmit this data as part of the HTTP headers. - // For cross domain requests, these headers must be allowed - // via Access-Control-Allow-Headers or removed using - // the beforeSend callback: - options.headers = $.extend(options.headers, { - 'X-File-Name': file.name, - 'X-File-Type': file.type, - 'X-File-Size': file.size - }); - if (!options.blob) { - // Non-chunked non-multipart upload: - options.contentType = file.type; - options.data = file; - } else if (!multipart) { - // Chunked non-multipart upload: - options.contentType = 'application/octet-stream'; - options.data = options.blob; - } + multipart = options.multipart || !$.support.xhrFileUpload, + paramName = options.paramName[0]; + options.headers = options.headers || {}; + if (options.contentRange) { + options.headers['Content-Range'] = options.contentRange; + } + if (!multipart || options.blob || !this._isInstanceOf('File', file)) { + options.headers['Content-Disposition'] = 'attachment; filename="' + + encodeURI(file.name) + '"'; } - if (multipart && $.support.xhrFormDataFileUpload) { + if (!multipart) { + options.contentType = file.type; + options.data = options.blob || file; + } else if ($.support.xhrFormDataFileUpload) { if (options.postMessage) { // window.postMessage does not allow sending FormData // objects, so we just add the File/Blob objects to @@ -290,19 +419,19 @@ formData = this._getFormData(options); if (options.blob) { formData.push({ - name: options.paramName, + name: paramName, value: options.blob }); } else { $.each(options.files, function (index, file) { formData.push({ - name: options.paramName, + name: options.paramName[index] || paramName, value: file }); }); } } else { - if (options.formData instanceof FormData) { + if (that._isInstanceOf('FormData', options.formData)) { formData = options.formData; } else { formData = new FormData(); @@ -311,14 +440,18 @@ }); } if (options.blob) { - formData.append(options.paramName, options.blob, file.name); + formData.append(paramName, options.blob, file.name); } else { $.each(options.files, function (index, file) { - // File objects are also Blob instances. // This check allows the tests to run with // dummy objects: - if (file instanceof Blob) { - formData.append(options.paramName, file, file.name); + if (that._isInstanceOf('File', file) || + that._isInstanceOf('Blob', file)) { + formData.append( + options.paramName[index] || paramName, + file, + file.name + ); } }); } @@ -330,13 +463,13 @@ }, _initIframeSettings: function (options) { + var targetHost = $('').prop('href', options.url).prop('host'); // Setting the dataType to iframe enables the iframe transport: options.dataType = 'iframe ' + (options.dataType || ''); // The iframe transport accepts a serialized array as form data: options.formData = this._getFormData(options); // Add redirect url to form data on cross-domain uploads: - if (options.redirect && $('').prop('href', options.url) - .prop('host') !== location.host) { + if (options.redirect && targetHost && targetHost !== location.host) { options.formData.push({ name: options.redirectParamName || 'redirect', value: options.redirect @@ -358,29 +491,58 @@ options.dataType = 'postmessage ' + (options.dataType || ''); } } else { - this._initIframeSettings(options, 'iframe'); + this._initIframeSettings(options); } }, + _getParamName: function (options) { + var fileInput = $(options.fileInput), + paramName = options.paramName; + if (!paramName) { + paramName = []; + fileInput.each(function () { + var input = $(this), + name = input.prop('name') || 'files[]', + i = (input.prop('files') || [1]).length; + while (i) { + paramName.push(name); + i -= 1; + } + }); + if (!paramName.length) { + paramName = [fileInput.prop('name') || 'files[]']; + } + } else if (!$.isArray(paramName)) { + paramName = [paramName]; + } + return paramName; + }, + _initFormSettings: function (options) { // Retrieve missing options from the input field and the // associated form, if available: if (!options.form || !options.form.length) { options.form = $(options.fileInput.prop('form')); + // If the given file input doesn't have an associated form, + // use the default widget file input's form: + if (!options.form.length) { + options.form = $(this.options.fileInput.prop('form')); + } } - if (!options.paramName) { - options.paramName = options.fileInput.prop('name') || - 'files[]'; - } + options.paramName = this._getParamName(options); if (!options.url) { options.url = options.form.prop('action') || location.href; } // The HTTP request method must be "POST" or "PUT": options.type = (options.type || options.form.prop('method') || '') .toUpperCase(); - if (options.type !== 'POST' && options.type !== 'PUT') { + if (options.type !== 'POST' && options.type !== 'PUT' && + options.type !== 'PATCH') { options.type = 'POST'; } + if (!options.formAcceptCharset) { + options.formAcceptCharset = options.form.attr('accept-charset'); + } }, _getAJAXSettings: function (data) { @@ -390,6 +552,21 @@ return options; }, + // jQuery 1.6 doesn't provide .state(), + // while jQuery 1.8+ removed .isRejected() and .isResolved(): + _getDeferredState: function (deferred) { + if (deferred.state) { + return deferred.state(); + } + if (deferred.isResolved()) { + return 'resolved'; + } + if (deferred.isRejected()) { + return 'rejected'; + } + return 'pending'; + }, + // Maps jqXHR callbacks to the equivalent // methods of the given Promise object: _enhancePromise: function (promise) { @@ -414,24 +591,77 @@ return this._enhancePromise(promise); }, + // Adds convenience methods to the data callback argument: + _addConvenienceMethods: function (e, data) { + var that = this, + getPromise = function (data) { + return $.Deferred().resolveWith(that, [data]).promise(); + }; + data.process = function (resolveFunc, rejectFunc) { + if (resolveFunc || rejectFunc) { + data._processQueue = this._processQueue = + (this._processQueue || getPromise(this)) + .pipe(resolveFunc, rejectFunc); + } + return this._processQueue || getPromise(this); + }; + data.submit = function () { + if (this.state() !== 'pending') { + data.jqXHR = this.jqXHR = + (that._trigger('submit', e, this) !== false) && + that._onSend(e, this); + } + return this.jqXHR || that._getXHRPromise(); + }; + data.abort = function () { + if (this.jqXHR) { + return this.jqXHR.abort(); + } + return that._getXHRPromise(); + }; + data.state = function () { + if (this.jqXHR) { + return that._getDeferredState(this.jqXHR); + } + if (this._processQueue) { + return that._getDeferredState(this._processQueue); + } + }; + data.progress = function () { + return this._progress; + }; + data.response = function () { + return this._response; + }; + }, + + // Parses the Range header from the server response + // and returns the uploaded bytes: + _getUploadedBytes: function (jqXHR) { + var range = jqXHR.getResponseHeader('Range'), + parts = range && range.split('-'), + upperBytesPos = parts && parts.length > 1 && + parseInt(parts[1], 10); + return upperBytesPos && upperBytesPos + 1; + }, + // Uploads a file in multiple, sequential requests // by splitting the file up in multiple blob chunks. // If the second parameter is true, only tests if the file // should be uploaded in chunks, but does not invoke any // upload requests: _chunkedUpload: function (options, testOnly) { + options.uploadedBytes = options.uploadedBytes || 0; var that = this, file = options.files[0], fs = file.size, - ub = options.uploadedBytes = options.uploadedBytes || 0, + ub = options.uploadedBytes, mcs = options.maxChunkSize || fs, - // Use the Blob methods with the slice implementation - // according to the W3C Blob API specification: - slice = file.webkitSlice || file.mozSlice || file.slice, - upload, - n, + slice = this._blobSlice, + dfd = $.Deferred(), + promise = dfd.promise(), jqXHR, - pipe; + upload; if (!(this._isXHRUpload(options) && slice && (ub || mcs < fs)) || options.data) { return false; @@ -440,62 +670,84 @@ return true; } if (ub >= fs) { - file.error = 'uploadedBytes'; + file.error = options.i18n('uploadedBytes'); return this._getXHRPromise( false, options.context, [null, 'error', file.error] ); } - // n is the number of blobs to upload, - // calculated via filesize, uploaded bytes and max chunk size: - n = Math.ceil((fs - ub) / mcs); - // The chunk upload method accepting the chunk number as parameter: - upload = function (i) { - if (!i) { - return that._getXHRPromise(true, options.context); - } - // Upload the blobs in sequential order: - return upload(i -= 1).pipe(function () { - // Clone the options object for each chunk upload: - var o = $.extend({}, options); - o.blob = slice.call( - file, - ub + i * mcs, - ub + (i + 1) * mcs - ); - // Store the current chunk size, as the blob itself - // will be dereferenced after data processing: - o.chunkSize = o.blob.size; - // Process the upload data (the blob and potential form data): - that._initXHRData(o); - // Add progress listeners for this chunk upload: - that._initProgressListener(o); - jqXHR = ($.ajax(o) || that._getXHRPromise(false, o.context)) - .done(function () { - // Create a progress event if upload is done and - // no progress event has been invoked for this chunk: - if (!o.loaded) { - that._onProgress($.Event('progress', { - lengthComputable: true, - loaded: o.chunkSize, - total: o.chunkSize - }), o); - } - options.uploadedBytes = o.uploadedBytes += - o.chunkSize; - }); - return jqXHR; - }); + // The chunk upload method: + upload = function () { + // Clone the options object for each chunk upload: + var o = $.extend({}, options), + currentLoaded = o._progress.loaded; + o.blob = slice.call( + file, + ub, + ub + mcs, + file.type + ); + // Store the current chunk size, as the blob itself + // will be dereferenced after data processing: + o.chunkSize = o.blob.size; + // Expose the chunk bytes position range: + o.contentRange = 'bytes ' + ub + '-' + + (ub + o.chunkSize - 1) + '/' + fs; + // Process the upload data (the blob and potential form data): + that._initXHRData(o); + // Add progress listeners for this chunk upload: + that._initProgressListener(o); + jqXHR = ((that._trigger('chunksend', null, o) !== false && $.ajax(o)) || + that._getXHRPromise(false, o.context)) + .done(function (result, textStatus, jqXHR) { + ub = that._getUploadedBytes(jqXHR) || + (ub + o.chunkSize); + // Create a progress event if no final progress event + // with loaded equaling total has been triggered + // for this chunk: + if (currentLoaded + o.chunkSize - o._progress.loaded) { + that._onProgress($.Event('progress', { + lengthComputable: true, + loaded: ub - o.uploadedBytes, + total: ub - o.uploadedBytes + }), o); + } + options.uploadedBytes = o.uploadedBytes = ub; + o.result = result; + o.textStatus = textStatus; + o.jqXHR = jqXHR; + that._trigger('chunkdone', null, o); + that._trigger('chunkalways', null, o); + if (ub < fs) { + // File upload not yet complete, + // continue with the next chunk: + upload(); + } else { + dfd.resolveWith( + o.context, + [result, textStatus, jqXHR] + ); + } + }) + .fail(function (jqXHR, textStatus, errorThrown) { + o.jqXHR = jqXHR; + o.textStatus = textStatus; + o.errorThrown = errorThrown; + that._trigger('chunkfail', null, o); + that._trigger('chunkalways', null, o); + dfd.rejectWith( + o.context, + [jqXHR, textStatus, errorThrown] + ); + }); }; - // Return the piped Promise object, enhanced with an abort method, - // which is delegated to the jqXHR object of the current upload, - // and jqXHR callbacks mapped to the equivalent Promise methods: - pipe = upload(n); - pipe.abort = function () { + this._enhancePromise(promise); + promise.abort = function () { return jqXHR.abort(); }; - return this._enhancePromise(pipe); + upload(); + return promise; }, _beforeSend: function (e, data) { @@ -504,99 +756,113 @@ // and no other uploads are currently running, // equivalent to the global ajaxStart event: this._trigger('start'); + // Set timer for global bitrate progress calculation: + this._bitrateTimer = new this._BitrateTimer(); + // Reset the global progress values: + this._progress.loaded = this._progress.total = 0; + this._progress.bitrate = 0; } + // Make sure the container objects for the .response() and + // .progress() methods on the data object are available + // and reset to their initial state: + this._initResponseObject(data); + this._initProgressObject(data); + data._progress.loaded = data.loaded = data.uploadedBytes || 0; + data._progress.total = data.total = this._getTotal(data.files) || 1; + data._progress.bitrate = data.bitrate = 0; this._active += 1; // Initialize the global progress values: - this._loaded += data.uploadedBytes || 0; - this._total += this._getTotal(data.files); + this._progress.loaded += data.loaded; + this._progress.total += data.total; }, _onDone: function (result, textStatus, jqXHR, options) { - if (!this._isXHRUpload(options)) { - // Create a progress event for each iframe load: + var total = options._progress.total, + response = options._response; + if (options._progress.loaded < total) { + // Create a progress event if no final progress event + // with loaded equaling total has been triggered: this._onProgress($.Event('progress', { lengthComputable: true, - loaded: 1, - total: 1 + loaded: total, + total: total }), options); } - options.result = result; - options.textStatus = textStatus; - options.jqXHR = jqXHR; + response.result = options.result = result; + response.textStatus = options.textStatus = textStatus; + response.jqXHR = options.jqXHR = jqXHR; this._trigger('done', null, options); }, _onFail: function (jqXHR, textStatus, errorThrown, options) { - options.jqXHR = jqXHR; - options.textStatus = textStatus; - options.errorThrown = errorThrown; - this._trigger('fail', null, options); + var response = options._response; if (options.recalculateProgress) { // Remove the failed (error or abort) file upload from // the global progress calculation: - this._loaded -= options.loaded || options.uploadedBytes || 0; - this._total -= options.total || this._getTotal(options.files); + this._progress.loaded -= options._progress.loaded; + this._progress.total -= options._progress.total; } + response.jqXHR = options.jqXHR = jqXHR; + response.textStatus = options.textStatus = textStatus; + response.errorThrown = options.errorThrown = errorThrown; + this._trigger('fail', null, options); }, _onAlways: function (jqXHRorResult, textStatus, jqXHRorError, options) { - this._active -= 1; - options.textStatus = textStatus; - if (jqXHRorError && jqXHRorError.always) { - options.jqXHR = jqXHRorError; - options.result = jqXHRorResult; - } else { - options.jqXHR = jqXHRorResult; - options.errorThrown = jqXHRorError; - } + // jqXHRorResult, textStatus and jqXHRorError are added to the + // options object via done and fail callbacks this._trigger('always', null, options); - if (this._active === 0) { - // The stop callback is triggered when all uploads have - // been completed, equivalent to the global ajaxStop event: - this._trigger('stop'); - // Reset the global progress values: - this._loaded = this._total = 0; - } }, _onSend: function (e, data) { + if (!data.submit) { + this._addConvenienceMethods(e, data); + } var that = this, jqXHR, + aborted, slot, pipe, options = that._getAJAXSettings(data), - send = function (resolve, args) { + send = function () { that._sending += 1; + // Set timer for bitrate progress calculation: + options._bitrateTimer = new that._BitrateTimer(); jqXHR = jqXHR || ( - (resolve !== false && - that._trigger('send', e, options) !== false && - (that._chunkedUpload(options) || $.ajax(options))) || - that._getXHRPromise(false, options.context, args) + ((aborted || that._trigger('send', e, options) === false) && + that._getXHRPromise(false, options.context, aborted)) || + that._chunkedUpload(options) || $.ajax(options) ).done(function (result, textStatus, jqXHR) { that._onDone(result, textStatus, jqXHR, options); }).fail(function (jqXHR, textStatus, errorThrown) { that._onFail(jqXHR, textStatus, errorThrown, options); }).always(function (jqXHRorResult, textStatus, jqXHRorError) { - that._sending -= 1; that._onAlways( jqXHRorResult, textStatus, jqXHRorError, options ); + that._sending -= 1; + that._active -= 1; if (options.limitConcurrentUploads && options.limitConcurrentUploads > that._sending) { // Start the next queued upload, // that has not been aborted: var nextSlot = that._slots.shift(); while (nextSlot) { - if (!nextSlot.isRejected()) { + if (that._getDeferredState(nextSlot) === 'pending') { nextSlot.resolve(); break; } nextSlot = that._slots.shift(); } } + if (that._active === 0) { + // The stop callback is triggered when all uploads have + // been completed, equivalent to the global ajaxStop event: + that._trigger('stop'); + } }); return jqXHR; }; @@ -609,18 +875,19 @@ this._slots.push(slot); pipe = slot.pipe(send); } else { - pipe = (this._sequence = this._sequence.pipe(send, send)); + this._sequence = this._sequence.pipe(send, send); + pipe = this._sequence; } // Return the piped Promise object, enhanced with an abort method, // which is delegated to the jqXHR object of the current upload, // and jqXHR callbacks mapped to the equivalent Promise methods: pipe.abort = function () { - var args = [undefined, 'abort', 'abort']; + aborted = [undefined, 'abort', 'abort']; if (!jqXHR) { if (slot) { - slot.rejectWith(args); + slot.rejectWith(options.context, aborted); } - return send(false, args); + return send(); } return jqXHR.abort(); }; @@ -634,40 +901,43 @@ result = true, options = $.extend({}, this.options, data), limit = options.limitMultiFileUploads, + paramName = this._getParamName(options), + paramNameSet, + paramNameSlice, fileSet, i; if (!(options.singleFileUploads || limit) || !this._isXHRUpload(options)) { fileSet = [data.files]; + paramNameSet = [paramName]; } else if (!options.singleFileUploads && limit) { fileSet = []; + paramNameSet = []; for (i = 0; i < data.files.length; i += limit) { fileSet.push(data.files.slice(i, i + limit)); + paramNameSlice = paramName.slice(i, i + limit); + if (!paramNameSlice.length) { + paramNameSlice = paramName; + } + paramNameSet.push(paramNameSlice); } + } else { + paramNameSet = paramName; } data.originalFiles = data.files; $.each(fileSet || data.files, function (index, element) { - var files = fileSet ? element : [element], - newData = $.extend({}, data, {files: files}); - newData.submit = function () { - newData.jqXHR = this.jqXHR = - (that._trigger('submit', e, this) !== false) && - that._onSend(e, this); - return this.jqXHR; - }; - return (result = that._trigger('add', e, newData)); + var newData = $.extend({}, data); + newData.files = fileSet ? element : [element]; + newData.paramName = paramNameSet[index]; + that._initResponseObject(newData); + that._initProgressObject(newData); + that._addConvenienceMethods(e, newData); + result = that._trigger('add', e, newData); + return result; }); return result; }, - // File Normalization for Gecko 1.9.1 (Firefox 3.5) support: - _normalizeFile: function (index, file) { - if (file.name === undefined && file.size === undefined) { - file.name = file.fileName; - file.size = file.fileSize; - } - }, - _replaceFileInput: function (input) { var inputClone = input.clone(true); $('
').append(inputClone)[0].reset(); @@ -677,7 +947,7 @@ // Avoid memory leaks with the detached file input: $.cleanData(input.unbind('remove')); // Replace the original file input element in the fileInput - // collection with the clone, which has been copied including + // elements set with the clone, which has been copied including // event handlers: this.options.fileInput = this.options.fileInput.map(function (i, el) { if (el === input[0]) { @@ -692,102 +962,229 @@ } }, - _onChange: function (e) { - var that = e.data.fileupload, - data = { - files: $.each($.makeArray(e.target.files), that._normalizeFile), - fileInput: $(e.target), - form: $(e.target.form) - }; - if (!data.files.length) { + _handleFileTreeEntry: function (entry, path) { + var that = this, + dfd = $.Deferred(), + errorHandler = function (e) { + if (e && !e.entry) { + e.entry = entry; + } + // Since $.when returns immediately if one + // Deferred is rejected, we use resolve instead. + // This allows valid files and invalid items + // to be returned together in one set: + dfd.resolve([e]); + }, + dirReader; + path = path || ''; + if (entry.isFile) { + if (entry._file) { + // Workaround for Chrome bug #149735 + entry._file.relativePath = path; + dfd.resolve(entry._file); + } else { + entry.file(function (file) { + file.relativePath = path; + dfd.resolve(file); + }, errorHandler); + } + } else if (entry.isDirectory) { + dirReader = entry.createReader(); + dirReader.readEntries(function (entries) { + that._handleFileTreeEntries( + entries, + path + entry.name + '/' + ).done(function (files) { + dfd.resolve(files); + }).fail(errorHandler); + }, errorHandler); + } else { + // Return an empy list for file system items + // other than files or directories: + dfd.resolve([]); + } + return dfd.promise(); + }, + + _handleFileTreeEntries: function (entries, path) { + var that = this; + return $.when.apply( + $, + $.map(entries, function (entry) { + return that._handleFileTreeEntry(entry, path); + }) + ).pipe(function () { + return Array.prototype.concat.apply( + [], + arguments + ); + }); + }, + + _getDroppedFiles: function (dataTransfer) { + dataTransfer = dataTransfer || {}; + var items = dataTransfer.items; + if (items && items.length && (items[0].webkitGetAsEntry || + items[0].getAsEntry)) { + return this._handleFileTreeEntries( + $.map(items, function (item) { + var entry; + if (item.webkitGetAsEntry) { + entry = item.webkitGetAsEntry(); + if (entry) { + // Workaround for Chrome bug #149735: + entry._file = item.getAsFile(); + } + return entry; + } + return item.getAsEntry(); + }) + ); + } + return $.Deferred().resolve( + $.makeArray(dataTransfer.files) + ).promise(); + }, + + _getSingleFileInputFiles: function (fileInput) { + fileInput = $(fileInput); + var entries = fileInput.prop('webkitEntries') || + fileInput.prop('entries'), + files, + value; + if (entries && entries.length) { + return this._handleFileTreeEntries(entries); + } + files = $.makeArray(fileInput.prop('files')); + if (!files.length) { + value = fileInput.prop('value'); + if (!value) { + return $.Deferred().resolve([]).promise(); + } // If the files property is not available, the browser does not // support the File API and we add a pseudo File object with // the input value as name with path information removed: - data.files = [{name: e.target.value.replace(/^.*\\/, '')}]; - } - if (that.options.replaceFileInput) { - that._replaceFileInput(data.fileInput); + files = [{name: value.replace(/^.*\\/, '')}]; + } else if (files[0].name === undefined && files[0].fileName) { + // File normalization for Safari 4 and Firefox 3: + $.each(files, function (index, file) { + file.name = file.fileName; + file.size = file.fileSize; + }); } - if (that._trigger('change', e, data) === false || - that._onAdd(e, data) === false) { - return false; + return $.Deferred().resolve(files).promise(); + }, + + _getFileInputFiles: function (fileInput) { + if (!(fileInput instanceof $) || fileInput.length === 1) { + return this._getSingleFileInputFiles(fileInput); } + return $.when.apply( + $, + $.map(fileInput, this._getSingleFileInputFiles) + ).pipe(function () { + return Array.prototype.concat.apply( + [], + arguments + ); + }); + }, + + _onChange: function (e) { + var that = this, + data = { + fileInput: $(e.target), + form: $(e.target.form) + }; + this._getFileInputFiles(data.fileInput).always(function (files) { + data.files = files; + if (that.options.replaceFileInput) { + that._replaceFileInput(data.fileInput); + } + if (that._trigger('change', e, data) !== false) { + that._onAdd(e, data); + } + }); }, _onPaste: function (e) { - var that = e.data.fileupload, - cbd = e.originalEvent.clipboardData, - items = (cbd && cbd.items) || [], + var items = e.originalEvent && e.originalEvent.clipboardData && + e.originalEvent.clipboardData.items, data = {files: []}; - $.each(items, function (index, item) { - var file = item.getAsFile && item.getAsFile(); - if (file) { - data.files.push(file); + if (items && items.length) { + $.each(items, function (index, item) { + var file = item.getAsFile && item.getAsFile(); + if (file) { + data.files.push(file); + } + }); + if (this._trigger('paste', e, data) === false || + this._onAdd(e, data) === false) { + return false; } - }); - if (that._trigger('paste', e, data) === false || - that._onAdd(e, data) === false) { - return false; } }, _onDrop: function (e) { - var that = e.data.fileupload, - dataTransfer = e.dataTransfer = e.originalEvent.dataTransfer, - data = { - files: $.each( - $.makeArray(dataTransfer && dataTransfer.files), - that._normalizeFile - ) - }; - if (that._trigger('drop', e, data) === false || - that._onAdd(e, data) === false) { - return false; + e.dataTransfer = e.originalEvent && e.originalEvent.dataTransfer; + var that = this, + dataTransfer = e.dataTransfer, + data = {}; + if (dataTransfer && dataTransfer.files && dataTransfer.files.length) { + e.preventDefault(); + this._getDroppedFiles(dataTransfer).always(function (files) { + data.files = files; + if (that._trigger('drop', e, data) !== false) { + that._onAdd(e, data); + } + }); } - e.preventDefault(); }, _onDragOver: function (e) { - var that = e.data.fileupload, - dataTransfer = e.dataTransfer = e.originalEvent.dataTransfer; - if (that._trigger('dragover', e) === false) { - return false; - } + e.dataTransfer = e.originalEvent && e.originalEvent.dataTransfer; + var dataTransfer = e.dataTransfer; if (dataTransfer) { - dataTransfer.dropEffect = dataTransfer.effectAllowed = 'copy'; + if (this._trigger('dragover', e) === false) { + return false; + } + if ($.inArray('Files', dataTransfer.types) !== -1) { + dataTransfer.dropEffect = 'copy'; + e.preventDefault(); + } } - e.preventDefault(); }, _initEventHandlers: function () { - var ns = this.options.namespace; if (this._isXHRUpload(this.options)) { - this.options.dropZone - .bind('dragover.' + ns, {fileupload: this}, this._onDragOver) - .bind('drop.' + ns, {fileupload: this}, this._onDrop) - .bind('paste.' + ns, {fileupload: this}, this._onPaste); + this._on(this.options.dropZone, { + dragover: this._onDragOver, + drop: this._onDrop + }); + this._on(this.options.pasteZone, { + paste: this._onPaste + }); + } + if ($.support.fileInput) { + this._on(this.options.fileInput, { + change: this._onChange + }); } - this.options.fileInput - .bind('change.' + ns, {fileupload: this}, this._onChange); }, _destroyEventHandlers: function () { - var ns = this.options.namespace; - this.options.dropZone - .unbind('dragover.' + ns, this._onDragOver) - .unbind('drop.' + ns, this._onDrop) - .unbind('paste.' + ns, this._onPaste); - this.options.fileInput - .unbind('change.' + ns, this._onChange); + this._off(this.options.dropZone, 'dragover drop'); + this._off(this.options.pasteZone, 'paste'); + this._off(this.options.fileInput, 'change'); }, _setOption: function (key, value) { - var refresh = $.inArray(key, this._refreshOptionsList) !== -1; - if (refresh) { + var reinit = $.inArray(key, this._specialOptions) !== -1; + if (reinit) { this._destroyEventHandlers(); } - $.Widget.prototype._setOption.call(this, key, value); - if (refresh) { + this._super(key, value); + if (reinit) { this._initSpecialOptions(); this._initEventHandlers(); } @@ -796,42 +1193,68 @@ _initSpecialOptions: function () { var options = this.options; if (options.fileInput === undefined) { - options.fileInput = this.element.is('input:file') ? - this.element : this.element.find('input:file'); + options.fileInput = this.element.is('input[type="file"]') ? + this.element : this.element.find('input[type="file"]'); } else if (!(options.fileInput instanceof $)) { options.fileInput = $(options.fileInput); } if (!(options.dropZone instanceof $)) { options.dropZone = $(options.dropZone); } + if (!(options.pasteZone instanceof $)) { + options.pasteZone = $(options.pasteZone); + } + }, + + _getRegExp: function (str) { + var parts = str.split('/'), + modifiers = parts.pop(); + parts.shift(); + return new RegExp(parts.join('/'), modifiers); + }, + + _isRegExpOption: function (key, value) { + return key !== 'url' && $.type(value) === 'string' && + /^\/.*\/[igm]{0,3}$/.test(value); + }, + + _initDataAttributes: function () { + var that = this, + options = this.options; + // Initialize options set via HTML5 data-attributes: + $.each( + $(this.element[0].cloneNode(false)).data(), + function (key, value) { + if (that._isRegExpOption(key, value)) { + value = that._getRegExp(value); + } + options[key] = value; + } + ); }, _create: function () { - var options = this.options, - dataOpts = $.extend({}, this.element.data()); - dataOpts[this.widgetName] = undefined; - $.extend(options, dataOpts); - options.namespace = options.namespace || this.widgetName; + this._initDataAttributes(); this._initSpecialOptions(); this._slots = []; this._sequence = this._getXHRPromise(true); - this._sending = this._active = this._loaded = this._total = 0; + this._sending = this._active = 0; + this._initProgressObject(this); this._initEventHandlers(); }, - destroy: function () { - this._destroyEventHandlers(); - $.Widget.prototype.destroy.call(this); - }, - - enable: function () { - $.Widget.prototype.enable.call(this); - this._initEventHandlers(); + // This method is exposed to the widget API and allows to query + // the number of active uploads: + active: function () { + return this._active; }, - disable: function () { - this._destroyEventHandlers(); - $.Widget.prototype.disable.call(this); + // This method is exposed to the widget API and allows to query + // the widget upload progress. + // It returns an object with loaded, total and bitrate properties + // for the running uploads: + progress: function () { + return this._progress; }, // This method is exposed to the widget API and allows adding files @@ -839,21 +1262,65 @@ // must have a files property and can contain additional options: // .fileupload('add', {files: filesList}); add: function (data) { + var that = this; if (!data || this.options.disabled) { return; } - data.files = $.each($.makeArray(data.files), this._normalizeFile); - this._onAdd(null, data); + if (data.fileInput && !data.files) { + this._getFileInputFiles(data.fileInput).always(function (files) { + data.files = files; + that._onAdd(null, data); + }); + } else { + data.files = $.makeArray(data.files); + this._onAdd(null, data); + } }, // This method is exposed to the widget API and allows sending files // using the fileupload API. The data parameter accepts an object which - // must have a files property and can contain additional options: + // must have a files or fileInput property and can contain additional options: // .fileupload('send', {files: filesList}); // The method returns a Promise object for the file upload call. send: function (data) { if (data && !this.options.disabled) { - data.files = $.each($.makeArray(data.files), this._normalizeFile); + if (data.fileInput && !data.files) { + var that = this, + dfd = $.Deferred(), + promise = dfd.promise(), + jqXHR, + aborted; + promise.abort = function () { + aborted = true; + if (jqXHR) { + return jqXHR.abort(); + } + dfd.reject(null, 'abort', 'abort'); + return promise; + }; + this._getFileInputFiles(data.fileInput).always( + function (files) { + if (aborted) { + return; + } + if (!files.length) { + dfd.reject(); + return; + } + data.files = files; + jqXHR = that._onSend(null, data).then( + function (result, textStatus, jqXHR) { + dfd.resolve(result, textStatus, jqXHR); + }, + function (jqXHR, textStatus, errorThrown) { + dfd.reject(jqXHR, textStatus, errorThrown); + } + ); + } + ); + return this._enhancePromise(promise); + } + data.files = $.makeArray(data.files); if (data.files.length) { return this._onSend(null, data); } @@ -863,4 +1330,4 @@ }); -})); +})); \ No newline at end of file diff --git a/apps/files/js/jquery.iframe-transport.js b/apps/files/js/jquery.iframe-transport.js index d85c0c11297..5c9df77976b 100644 --- a/apps/files/js/jquery.iframe-transport.js +++ b/apps/files/js/jquery.iframe-transport.js @@ -1,5 +1,5 @@ /* - * jQuery Iframe Transport Plugin 1.3 + * jQuery Iframe Transport Plugin 1.7 * https://github.com/blueimp/jQuery-File-Upload * * Copyright 2011, Sebastian Tschan @@ -30,27 +30,45 @@ // The iframe transport accepts three additional options: // options.fileInput: a jQuery collection of file input fields // options.paramName: the parameter name for the file form data, - // overrides the name property of the file input field(s) + // overrides the name property of the file input field(s), + // can be a string or an array of strings. // options.formData: an array of objects with name and value properties, // equivalent to the return data of .serializeArray(), e.g.: // [{name: 'a', value: 1}, {name: 'b', value: 2}] $.ajaxTransport('iframe', function (options) { - if (options.async && (options.type === 'POST' || options.type === 'GET')) { + if (options.async) { var form, - iframe; + iframe, + addParamChar; return { send: function (_, completeCallback) { form = $('
'); + form.attr('accept-charset', options.formAcceptCharset); + addParamChar = /\?/.test(options.url) ? '&' : '?'; + // XDomainRequest only supports GET and POST: + if (options.type === 'DELETE') { + options.url = options.url + addParamChar + '_method=DELETE'; + options.type = 'POST'; + } else if (options.type === 'PUT') { + options.url = options.url + addParamChar + '_method=PUT'; + options.type = 'POST'; + } else if (options.type === 'PATCH') { + options.url = options.url + addParamChar + '_method=PATCH'; + options.type = 'POST'; + } // javascript:false as initial iframe src // prevents warning popups on HTTPS in IE6. // IE versions below IE8 cannot set the name property of // elements that have already been added to the DOM, // so we set the name along with the iframe HTML markup: + counter += 1; iframe = $( '' + counter + '">' ).bind('load', function () { - var fileInputClones; + var fileInputClones, + paramNames = $.isArray(options.paramName) ? + options.paramName : [options.paramName]; iframe .unbind('load') .bind('load', function () { @@ -79,7 +97,12 @@ // (happens on form submits to iframe targets): $('') .appendTo(form); - form.remove(); + window.setTimeout(function () { + // Removing the form in a setTimeout call + // allows Chrome's developer tools to display + // the response result + form.remove(); + }, 0); }); form .prop('target', iframe.prop('name')) @@ -101,8 +124,11 @@ return fileInputClones[index]; }); if (options.paramName) { - options.fileInput.each(function () { - $(this).prop('name', options.paramName); + options.fileInput.each(function (index) { + $(this).prop( + 'name', + paramNames[index] || options.paramName + ); }); } // Appending the file input fields to the hidden form @@ -144,22 +170,36 @@ }); // The iframe transport returns the iframe content document as response. - // The following adds converters from iframe to text, json, html, and script: + // The following adds converters from iframe to text, json, html, xml + // and script. + // Please note that the Content-Type for JSON responses has to be text/plain + // or text/html, if the browser doesn't include application/json in the + // Accept header, else IE will show a download dialog. + // The Content-Type for XML responses on the other hand has to be always + // application/xml or text/xml, so IE properly parses the XML response. + // See also + // https://github.com/blueimp/jQuery-File-Upload/wiki/Setup#content-type-negotiation $.ajaxSetup({ converters: { 'iframe text': function (iframe) { - return $(iframe[0].body).text(); + return iframe && $(iframe[0].body).text(); }, 'iframe json': function (iframe) { - return $.parseJSON($(iframe[0].body).text()); + return iframe && $.parseJSON($(iframe[0].body).text()); }, 'iframe html': function (iframe) { - return $(iframe[0].body).html(); + return iframe && $(iframe[0].body).html(); + }, + 'iframe xml': function (iframe) { + var xmlDoc = iframe && iframe[0]; + return xmlDoc && $.isXMLDoc(xmlDoc) ? xmlDoc : + $.parseXML((xmlDoc.XMLDocument && xmlDoc.XMLDocument.xml) || + $(xmlDoc.body).html()); }, 'iframe script': function (iframe) { - return $.globalEval($(iframe[0].body).text()); + return iframe && $.globalEval($(iframe[0].body).text()); } } }); -})); +})); \ No newline at end of file diff --git a/apps/files/templates/part.list.php b/apps/files/templates/part.list.php index 0c7d6936697..39f5ac471e5 100644 --- a/apps/files/templates/part.list.php +++ b/apps/files/templates/part.list.php @@ -1,7 +1,8 @@ +$totalsize = 0; +$pc = 0; ?> $relative_date_color = round((time()-$file['mtime'])/60/60/24*14); if($relative_date_color>160) $relative_date_color = 160; $name = \OCP\Util::encodePath($file['name']); - $directory = \OCP\Util::encodePath($file['directory']); ?> + $directory = \OCP\Util::encodePath($file['directory']); + ?> +
+ + )"> -- cgit v1.2.3 From 13e34649bfb1a7d15833c209d629e3540d3366ef Mon Sep 17 00:00:00 2001 From: Georg Ehrke Date: Fri, 23 Aug 2013 23:19:21 +0200 Subject: move path generation for previews to dedicated function --- apps/files/js/filelist.js | 2 +- apps/files/js/files.js | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) (limited to 'apps/files/js/files.js') diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 41245c00ba6..e3e985af38b 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -185,7 +185,7 @@ var FileList={ if (id != null) { tr.attr('data-id', id); } - var path = $('#dir').val()+'/'+name; + var path = getPathForPreview(name); lazyLoadPreview(path, mime, function(previewpath){ tr.find('td.filename').attr('style','background-image:url('+previewpath+')'); }); diff --git a/apps/files/js/files.js b/apps/files/js/files.js index f88ecd961b1..79fa01aa0aa 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -382,7 +382,7 @@ $(document).ready(function() { tr.attr('data-size',result.data.size); tr.attr('data-id', result.data.id); tr.find('.filesize').text(humanFileSize(result.data.size)); - var path = $('#dir').val() + '/' + name; + var path = getPathForPreview(name); lazyLoadPreview(path, result.data.mime, function(previewpath){ tr.find('td.filename').attr('style','background-image:url('+previewpath+')'); }); @@ -654,7 +654,7 @@ var createDragShadow = function(event){ if (elem.type === 'dir') { newtr.find('td.filename').attr('style','background-image:url('+OC.imagePath('core', 'filetypes/folder.png')+')'); } else { - var path = $('#dir').val()+'/'+elem.name; + var path = getPathForPreview(elem.name); lazyLoadPreview(path, elem.mime, function(previewpath){ newtr.find('td.filename').attr('style','background-image:url('+previewpath+')'); }); @@ -832,6 +832,11 @@ function getMimeIcon(mime, ready){ } getMimeIcon.cache={}; +function getPathForPreview(name) { + var path = $('#dir').val() + '/' + name; + return path; +} + function lazyLoadPreview(path, mime, ready) { getMimeIcon(mime,ready); var x = $('#filestable').data('preview-x'); -- cgit v1.2.3 From 46cbd7cd3b37e073ebb497f34d54ab67e4761969 Mon Sep 17 00:00:00 2001 From: Georg Ehrke Date: Mon, 26 Aug 2013 12:16:51 +0200 Subject: fix preview issue when uploading a file with parentheses --- apps/files/js/files.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'apps/files/js/files.js') diff --git a/apps/files/js/files.js b/apps/files/js/files.js index 79fa01aa0aa..a890da843bb 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -843,7 +843,9 @@ function lazyLoadPreview(path, mime, ready) { var y = $('#filestable').data('preview-y'); var previewURL = OC.Router.generate('core_ajax_preview', {file: encodeURIComponent(path), x:x, y:y}); $.get(previewURL, function() { - ready(previewURL); + previewURL = previewURL.replace('(','%28'); + previewURL = previewURL.replace(')','%29'); + ready(previewURL + '&reload=true'); }); } -- cgit v1.2.3 From bbf8acb383bdcb1dcb53f4b9d5a8d894b17401df Mon Sep 17 00:00:00 2001 From: Jörn Friedrich Dreyer Date: Thu, 5 Sep 2013 10:19:54 +0200 Subject: separate uploading code from progress code, add progress capability detection --- apps/files/js/file-upload.js | 193 +++++++++++++++++++++++-------------------- apps/files/js/filelist.js | 20 ++--- apps/files/js/files.js | 32 +++---- core/js/oc-dialogs.js | 13 ++- 4 files changed, 136 insertions(+), 122 deletions(-) (limited to 'apps/files/js/files.js') diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index be3d7e08aff..bd0ae4db00b 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -14,6 +14,7 @@ * - when only existing -> remember as single skip action * - when only new -> remember as single replace action * - when both -> remember as single autorename action + * - continue -> apply marks, when nothing is marked continue == skip all * - start uploading selection * * on send @@ -96,7 +97,30 @@ * */ +// from https://github.com/New-Bamboo/example-ajax-upload/blob/master/public/index.html +// also see article at http://blog.new-bamboo.co.uk/2012/01/10/ridiculously-simple-ajax-uploads-with-formdata +// Function that will allow us to know if Ajax uploads are supported +function supportAjaxUploadWithProgress() { + return supportFileAPI() && supportAjaxUploadProgressEvents() && supportFormData(); + // Is the File API supported? + function supportFileAPI() { + var fi = document.createElement('INPUT'); + fi.type = 'file'; + return 'files' in fi; + }; + + // Are progress events supported? + function supportAjaxUploadProgressEvents() { + var xhr = new XMLHttpRequest(); + return !! (xhr && ('upload' in xhr) && ('onprogress' in xhr.upload)); + }; + + // Is FormData supported? + function supportFormData() { + return !! window.FormData; + } +} //TODO clean uploads when all progress has completed OC.Upload = { @@ -245,6 +269,7 @@ OC.Upload = { console.log(data); }, checkExistingFiles: function (selection, callbacks){ + // FIXME check filelist before uploading callbacks.onNoConflicts(selection); } }; @@ -327,7 +352,7 @@ $(document).ready(function() { return false; //don't upload anything } - // check existing files whan all is collected + // check existing files when all is collected if ( selection.uploads.length >= selection.filesToUpload ) { //remove our selection hack: @@ -358,11 +383,6 @@ $(document).ready(function() { OC.Upload.checkExistingFiles(selection, callbacks); - //TODO refactor away: - //show cancel button - if($('html.lte9').length === 0 && data.dataType !== 'iframe') { - $('#uploadprogresswrapper input.stop').show(); - } } @@ -389,13 +409,6 @@ $(document).ready(function() { */ start: function(e) { OC.Upload.logStatus('start', e, null); - //IE < 10 does not fire the necessary events for the progress bar. - if($('html.lte9').length > 0) { - return true; - } - $('#uploadprogresswrapper input.stop').show(); - $('#uploadprogressbar').progressbar({value:0}); - $('#uploadprogressbar').fadeIn(); }, fail: function(e, data) { OC.Upload.logStatus('fail', e, data); @@ -414,32 +427,6 @@ $(document).ready(function() { } //var selection = OC.Upload.getSelection(data.originalFiles); //OC.Upload.deleteSelectionUpload(selection, data.files[0].name); - - //if user pressed cancel hide upload progress bar and cancel button - if (data.errorThrown === 'abort') { - $('#uploadprogresswrapper input.stop').fadeOut(); - $('#uploadprogressbar').fadeOut(); - } - }, - progress: function(e, data) { - OC.Upload.logStatus('progress', e, data); - // TODO: show nice progress bar in file row - }, - /** - * - * @param {type} e - * @param {type} data (only has loaded, total and lengthComputable) - * @returns {unresolved} - */ - progressall: function(e, data) { - OC.Upload.logStatus('progressall', e, data); - //IE < 10 does not fire the necessary events for the progress bar. - if($('html.lte9').length > 0) { - return; - } - var progress = (data.loaded/data.total)*100; - //var progress = OC.Upload.progressBytes(); - $('#uploadprogressbar').progressbar('value', progress); }, /** * called for every successful upload @@ -460,33 +447,21 @@ $(document).ready(function() { //var selection = OC.Upload.getSelection(data.originalFiles); if(typeof result[0] !== 'undefined' - && result[0].status === 'success' + && result[0].status === 'existserror' ) { - //if (selection) { - // selection.loadedBytes+=data.loaded; - //} - //OC.Upload.nextUpload(); + //show "file already exists" dialog + var original = result[0]; + var replacement = data.files[0]; + var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); + OC.dialogs.fileexists(data, original, replacement, OC.Upload, fu); } else { - if (result[0].status === 'existserror') { - //show "file already exists" dialog - var original = result[0]; - var replacement = data.files[0]; - var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); - OC.dialogs.fileexists(data, original, replacement, OC.Upload, fu); - } else { - OC.Upload.deleteSelectionUpload(selection, data.files[0].name); - data.textStatus = 'servererror'; - data.errorThrown = t('files', result.data.message); - var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); - fu._trigger('fail', e, data); - } + OC.Upload.deleteSelectionUpload(selection, data.files[0].name); + data.textStatus = 'servererror'; + data.errorThrown = t('files', result.data.message); + var fu = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); + fu._trigger('fail', e, data); } - //if user pressed cancel hide upload chrome - //if (! OC.Upload.isProcessing()) { - // $('#uploadprogresswrapper input.stop').fadeOut(); - // $('#uploadprogressbar').fadeOut(); - //} }, /** @@ -496,36 +471,78 @@ $(document).ready(function() { */ stop: function(e, data) { OC.Upload.logStatus('stop', e, data); - //if(OC.Upload.progressBytes()>=100) { //only hide controls when all selections have ended uploading - - //OC.Upload.cancelUploads(); //cleanup - - // if(data.dataType !== 'iframe') { - // $('#uploadprogresswrapper input.stop').hide(); - // } - - //IE < 10 does not fire the necessary events for the progress bar. - if($('html.lte9').length > 0) { - return; - } - - // $('#uploadprogressbar').progressbar('value', 100); - // $('#uploadprogressbar').fadeOut(); - //} - //if user pressed cancel hide upload chrome - //if (! OC.Upload.isProcessing()) { - // $('#uploadprogresswrapper input.stop').fadeOut(); - // $('#uploadprogressbar').fadeOut(); - //} } }; - - var file_upload_handler = function() { - $('#file_upload_start').fileupload(file_upload_param); - }; if ( document.getElementById('data-upload-form') ) { - $(file_upload_handler); + // initialize jquery fileupload (blueimp) + var fileupload = $('#file_upload_start').fileupload(file_upload_param); + + if(supportAjaxUploadWithProgress()) { + + // add progress handlers + fileupload.on('fileuploadadd', function(e, data) { + OC.Upload.logStatus('progress handle fileuploadadd', e, data); + //show cancel button + //if(data.dataType !== 'iframe') { //FIXME when is iframe used? only for ie? + // $('#uploadprogresswrapper input.stop').show(); + //} + }); + // add progress handlers + fileupload.on('fileuploadstart', function(e, data) { + OC.Upload.logStatus('progress handle fileuploadstart', e, data); + $('#uploadprogresswrapper input.stop').show(); + $('#uploadprogressbar').progressbar({value:0}); + $('#uploadprogressbar').fadeIn(); + }); + fileupload.on('fileuploadprogress', function(e, data) { + OC.Upload.logStatus('progress handle fileuploadprogress', e, data); + //TODO progressbar in row + }); + fileupload.on('fileuploadprogressall', function(e, data) { + OC.Upload.logStatus('progress handle fileuploadprogressall', e, data); + var progress = (data.loaded / data.total) * 100; + $('#uploadprogressbar').progressbar('value', progress); + }); + fileupload.on('fileuploaddone', function(e, data) { + OC.Upload.logStatus('progress handle fileuploaddone', e, data); + //if user pressed cancel hide upload chrome + //if (! OC.Upload.isProcessing()) { + // $('#uploadprogresswrapper input.stop').fadeOut(); + // $('#uploadprogressbar').fadeOut(); + //} + }); + fileupload.on('fileuploadstop', function(e, data) { + OC.Upload.logStatus('progress handle fileuploadstop', e, data); + //if(OC.Upload.progressBytes()>=100) { //only hide controls when all selections have ended uploading + + //OC.Upload.cancelUploads(); //cleanup + + // if(data.dataType !== 'iframe') { + // $('#uploadprogresswrapper input.stop').hide(); + // } + + // $('#uploadprogressbar').progressbar('value', 100); + // $('#uploadprogressbar').fadeOut(); + //} + //if user pressed cancel hide upload chrome + //if (! OC.Upload.isProcessing()) { + // $('#uploadprogresswrapper input.stop').fadeOut(); + // $('#uploadprogressbar').fadeOut(); + //} + }); + fileupload.on('fileuploadfail', function(e, data) { + OC.Upload.logStatus('progress handle fileuploadfail', e, data); + //if user pressed cancel hide upload progress bar and cancel button + if (data.errorThrown === 'abort') { + $('#uploadprogresswrapper input.stop').fadeOut(); + $('#uploadprogressbar').fadeOut(); + } + }); + + } else { + console.log('skipping file progress because your browser is broken'); + } } $.assocArraySize = function(obj) { // http://stackoverflow.com/a/6700/11236 diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 31e2a8300e2..4f20d1940aa 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -517,7 +517,7 @@ $(document).ready(function(){ var file_upload_start = $('#file_upload_start'); file_upload_start.on('fileuploaddrop', function(e, data) { - OC.Upload.logStatus('fileuploaddrop', e, data); + OC.Upload.logStatus('filelist handle fileuploaddrop', e, data); var dropTarget = $(e.originalEvent.target).closest('tr'); if(dropTarget && dropTarget.data('type') === 'dir') { // drag&drop upload to folder @@ -556,7 +556,7 @@ $(document).ready(function(){ }); file_upload_start.on('fileuploadadd', function(e, data) { - OC.Upload.logStatus('fileuploadadd', e, data); + OC.Upload.logStatus('filelist handle fileuploadadd', e, data); // lookup selection for dir var selection = OC.Upload.getSelection(data.originalFiles); @@ -592,10 +592,10 @@ $(document).ready(function(){ }); file_upload_start.on('fileuploadstart', function(e, data) { - OC.Upload.logStatus('fileuploadstart', e, data); + OC.Upload.logStatus('filelist handle fileuploadstart', e, data); }); file_upload_start.on('fileuploaddone', function(e, data) { - OC.Upload.logStatus('fileuploaddone', e, data); + OC.Upload.logStatus('filelist handle fileuploaddone', e, data); var response; if (typeof data.result === 'string') { @@ -672,22 +672,22 @@ $(document).ready(function(){ }); file_upload_start.on('fileuploadalways', function(e, data) { - OC.Upload.logStatus('fileuploadalways', e, data); + OC.Upload.logStatus('filelist handle fileuploadalways', e, data); }); file_upload_start.on('fileuploadsend', function(e, data) { - OC.Upload.logStatus('fileuploadsend', e, data); + OC.Upload.logStatus('filelist handle fileuploadsend', e, data); // TODOD add vis //data.context.element = }); file_upload_start.on('fileuploadprogress', function(e, data) { - OC.Upload.logStatus('fileuploadprogress', e, data); + OC.Upload.logStatus('filelist handle fileuploadprogress', e, data); }); file_upload_start.on('fileuploadprogressall', function(e, data) { - OC.Upload.logStatus('fileuploadprogressall', e, data); + OC.Upload.logStatus('filelist handle fileuploadprogressall', e, data); }); file_upload_start.on('fileuploadstop', function(e, data) { - OC.Upload.logStatus('fileuploadstop', e, data); + OC.Upload.logStatus('filelist handle fileuploadstop', e, data); //if user pressed cancel hide upload chrome if (! OC.Upload.isProcessing()) { @@ -700,7 +700,7 @@ $(document).ready(function(){ } }); file_upload_start.on('fileuploadfail', function(e, data) { - OC.Upload.logStatus('fileuploadfail', e, data); + OC.Upload.logStatus('filelist handle fileuploadfail', e, data); //if user pressed cancel hide upload chrome if (data.errorThrown === 'abort') { diff --git a/apps/files/js/files.js b/apps/files/js/files.js index 9a725fc2077..4a6c9c78900 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -1,6 +1,6 @@ Files={ updateMaxUploadFilesize:function(response) { - if(response == undefined) { + if(response === undefined) { return; } if(response.data !== undefined && response.data.uploadMaxFilesize !== undefined) { @@ -9,7 +9,7 @@ Files={ $('#usedSpacePercent').val(response.data.usedSpacePercent); Files.displayStorageWarnings(); } - if(response[0] == undefined) { + if(response[0] === undefined) { return; } if(response[0].uploadMaxFilesize !== undefined) { @@ -25,7 +25,7 @@ Files={ OC.Notification.show(t('files', '\'.\' is an invalid file name.')); return false; } - if (name.length == 0) { + if (name.length === 0) { OC.Notification.show(t('files', 'File name cannot be empty.')); return false; } @@ -33,7 +33,7 @@ Files={ // check for invalid characters var invalid_characters = ['\\', '/', '<', '>', ':', '"', '|', '?', '*']; for (var i = 0; i < invalid_characters.length; i++) { - if (name.indexOf(invalid_characters[i]) != -1) { + if (name.indexOf(invalid_characters[i]) !== -1) { OC.Notification.show(t('files', "Invalid name, '\\', '/', '<', '>', ':', '\"', '|', '?' and '*' are not allowed.")); return false; } @@ -127,7 +127,7 @@ $(document).ready(function() { var rows = $(this).parent().parent().parent().children('tr'); for (var i = start; i < end; i++) { $(rows).each(function(index) { - if (index == i) { + if (index === i) { var checkbox = $(this).children().children('input:checkbox'); $(checkbox).attr('checked', 'checked'); $(checkbox).parent().parent().addClass('selected'); @@ -145,7 +145,7 @@ $(document).ready(function() { $(checkbox).attr('checked', 'checked'); $(checkbox).parent().parent().toggleClass('selected'); var selectedCount=$('td.filename input:checkbox:checked').length; - if (selectedCount == $('td.filename input:checkbox').length) { + if (selectedCount === $('td.filename input:checkbox').length) { $('#select_all').attr('checked', 'checked'); } } @@ -192,7 +192,7 @@ $(document).ready(function() { var rows = $(this).parent().parent().parent().children('tr'); for (var i = start; i < end; i++) { $(rows).each(function(index) { - if (index == i) { + if (index === i) { var checkbox = $(this).children().children('input:checkbox'); $(checkbox).attr('checked', 'checked'); $(checkbox).parent().parent().addClass('selected'); @@ -205,7 +205,7 @@ $(document).ready(function() { if(!$(this).attr('checked')){ $('#select_all').attr('checked',false); }else{ - if(selectedCount==$('td.filename input:checkbox').length){ + if(selectedCount === $('td.filename input:checkbox').length){ $('#select_all').attr('checked',true); } } @@ -262,9 +262,9 @@ $(document).ready(function() { function resizeBreadcrumbs(firstRun) { var width = $(this).width(); - if (width != lastWidth) { + if (width !== lastWidth) { if ((width < lastWidth || firstRun) && width < breadcrumbsWidth) { - if (hiddenBreadcrumbs == 0) { + if (hiddenBreadcrumbs === 0) { breadcrumbsWidth -= $(breadcrumbs[1]).get(0).offsetWidth; $(breadcrumbs[1]).find('a').hide(); $(breadcrumbs[1]).append('...'); @@ -276,12 +276,12 @@ $(document).ready(function() { breadcrumbsWidth -= $(breadcrumbs[i]).get(0).offsetWidth; $(breadcrumbs[i]).hide(); hiddenBreadcrumbs = i; - i++ + i++; } } else if (width > lastWidth && hiddenBreadcrumbs > 0) { var i = hiddenBreadcrumbs; while (width > breadcrumbsWidth && i > 0) { - if (hiddenBreadcrumbs == 1) { + if (hiddenBreadcrumbs === 1) { breadcrumbsWidth -= $(breadcrumbs[1]).get(0).offsetWidth; $(breadcrumbs[1]).find('span').remove(); $(breadcrumbs[1]).find('a').show(); @@ -382,7 +382,7 @@ scanFiles.scanning=false; function boolOperationFinished(data, callback) { result = jQuery.parseJSON(data.responseText); Files.updateMaxUploadFilesize(result); - if(result.status == 'success'){ + if(result.status === 'success'){ callback.call(); } else { alert(result.data.message); @@ -436,7 +436,7 @@ var createDragShadow = function(event){ }); return dragshadow; -} +}; //options for file drag/drop var dragOptions={ @@ -446,7 +446,7 @@ var dragOptions={ stop: function(event, ui) { $('#fileList tr td.filename').addClass('ui-draggable'); } -} +}; // sane browsers support using the distance option if ( $('html.ie').length === 0) { dragOptions['distance'] = 20; @@ -489,7 +489,7 @@ var folderDropOptions={ }); }, tolerance: 'pointer' -} +}; var crumbDropOptions={ drop: function( event, ui ) { diff --git a/core/js/oc-dialogs.js b/core/js/oc-dialogs.js index 5ed24417264..08afbfd42f2 100644 --- a/core/js/oc-dialogs.js +++ b/core/js/oc-dialogs.js @@ -257,13 +257,7 @@ var OCdialogs = { //TODO add to same size collection? } - //add checkbox toggling actions - conflict.find('.replacement,.original').on('click', function(){ - var checkbox = $(this).find('input[type="checkbox"]'); - checkbox.prop('checkbox', !checkbox.prop('checkbox')); - }).find('input[type="checkbox"]').prop('checkbox',false); - - //TODO show skip action for files with same size and mtime + //TODO show skip action for files with same size and mtime in bottom row }; var selection = controller.getSelection(data.originalFiles); @@ -345,11 +339,14 @@ var OCdialogs = { var checkboxes = $(dialog_id).find('.conflict:not(.template) .original input[type="checkbox"]'); checkboxes.prop('checked', $(this).prop('checked')); }); - $(dialog_id).find('.conflicts').on('click', '.replacement,.original', function() { var checkbox = $(this).find('input[type="checkbox"]'); checkbox.prop('checked', !checkbox.prop('checked')); }); + $(dialog_id).find('.conflicts').on('click', 'input[type="checkbox"]', function() { + var checkbox = $(this); + checkbox.prop('checked', !checkbox.prop('checked')); + }); //update counters $(dialog_id).on('click', '.replacement,.allnewfiles', function() { -- cgit v1.2.3 From 1304b511e9533dee4cf1125e625568c8a74719a1 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Sat, 17 Aug 2013 13:07:18 +0200 Subject: Ajax calls for "files" and "files_trashbin" apps Frontend: - The files app list now uses ajax calls to refresh the list. - Added support the browser back button (history API). - Added mask + spinner while loading file list Backend: - Added utility function in core JS for parsing query strings. - Moved file list + breadcrumb template data code to helper functions - Fixed some file paths in trashbin app to be similar to the files app --- apps/files/ajax/list.php | 38 +++--- apps/files/css/files.css | 22 ++++ apps/files/index.php | 42 +------ apps/files/js/fileactions.js | 5 +- apps/files/js/filelist.js | 137 ++++++++++++++++++++-- apps/files/js/files.js | 48 +++++--- apps/files/lib/helper.php | 65 ++++++++++ apps/files/templates/index.php | 10 +- apps/files/templates/part.list.php | 4 +- apps/files_sharing/js/share.js | 2 +- apps/files_sharing/public.php | 2 +- apps/files_trashbin/ajax/list.php | 51 ++++++++ apps/files_trashbin/index.php | 91 ++------------ apps/files_trashbin/js/filelist.js | 29 +++++ apps/files_trashbin/js/trash.js | 14 ++- apps/files_trashbin/lib/helper.php | 97 +++++++++++++++ apps/files_trashbin/templates/index.php | 3 + apps/files_trashbin/templates/part.breadcrumb.php | 4 +- apps/files_trashbin/templates/part.list.php | 3 +- core/js/js.js | 32 +++++ 20 files changed, 518 insertions(+), 181 deletions(-) create mode 100644 apps/files_trashbin/ajax/list.php create mode 100644 apps/files_trashbin/js/filelist.js create mode 100644 apps/files_trashbin/lib/helper.php (limited to 'apps/files/js/files.js') diff --git a/apps/files/ajax/list.php b/apps/files/ajax/list.php index 14ed43cbb3a..035ffc0e393 100644 --- a/apps/files/ajax/list.php +++ b/apps/files/ajax/list.php @@ -10,36 +10,34 @@ OCP\JSON::checkLoggedIn(); // Load the files $dir = isset( $_GET['dir'] ) ? $_GET['dir'] : ''; + +if (!\OC\Files\Filesystem::is_dir($dir . '/')) { + header("HTTP/1.0 404 Not Found"); + exit(); +} + $doBreadcrumb = isset($_GET['breadcrumb']); $data = array(); +$baseUrl = OCP\Util::linkTo('files', 'index.php') . '?dir='; // Make breadcrumb if($doBreadcrumb) { - $breadcrumb = array(); - $pathtohere = "/"; - foreach( explode( "/", $dir ) as $i ) { - if( $i != "" ) { - $pathtohere .= "$i/"; - $breadcrumb[] = array( "dir" => $pathtohere, "name" => $i ); - } - } - - $breadcrumbNav = new OCP\Template( "files", "part.breadcrumb", "" ); - $breadcrumbNav->assign( "breadcrumb", $breadcrumb, false ); + $breadcrumb = \OCA\files\lib\Helper::makeBreadcrumb($dir); + + $breadcrumbNav = new OCP\Template('files', 'part.breadcrumb', ''); + $breadcrumbNav->assign('breadcrumb', $breadcrumb, false); + $breadcrumbNav->assign('baseURL', $baseUrl); $data['breadcrumb'] = $breadcrumbNav->fetchPage(); } // make filelist -$files = array(); -foreach( \OC\Files\Filesystem::getDirectoryContent( $dir ) as $i ) { - $i["date"] = OCP\Util::formatDate($i["mtime"] ); - $i['icon'] = \OCA\files\lib\Helper::determineIcon($i); - $files[] = $i; -} +$files = \OCA\files\lib\Helper::getFiles($dir); -$list = new OCP\Template( "files", "part.list", "" ); -$list->assign( "files", $files, false ); -$data = array('files' => $list->fetchPage()); +$list = new OCP\Template("files", "part.list", ""); +$list->assign('files', $files, false); +$list->assign('baseURL', $baseUrl, false); +$list->assign('downloadURL', OCP\Util::linkToRoute('download', array('file' => '/'))); +$data['files'] = $list->fetchPage(); OCP\JSON::success(array('data' => $data)); diff --git a/apps/files/css/files.css b/apps/files/css/files.css index 8053649bd55..f506a379477 100644 --- a/apps/files/css/files.css +++ b/apps/files/css/files.css @@ -336,3 +336,25 @@ table.dragshadow td.size { text-align: center; margin-left: -200px; } +.mask { + z-index: 50; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: white; + background-repeat: no-repeat no-repeat; + background-position: 50%; + opacity: 0.7; + filter: alpha(opacity=70); + transition: opacity 100ms; + -moz-transition: opacity 100ms; + -o-transition: opacity 100ms; + -ms-transition: opacity 100ms; + -webkit-transition: opacity 100ms; +} +.mask.transparent{ + opacity: 0; +} + diff --git a/apps/files/index.php b/apps/files/index.php index 4443bf5fde0..ec824f895b6 100644 --- a/apps/files/index.php +++ b/apps/files/index.php @@ -41,62 +41,25 @@ if (!\OC\Files\Filesystem::is_dir($dir . '/')) { exit(); } -function fileCmp($a, $b) { - if ($a['type'] == 'dir' and $b['type'] != 'dir') { - return -1; - } elseif ($a['type'] != 'dir' and $b['type'] == 'dir') { - return 1; - } else { - return strnatcasecmp($a['name'], $b['name']); - } -} - $files = array(); $user = OC_User::getUser(); if (\OC\Files\Cache\Upgrade::needUpgrade($user)) { //dont load anything if we need to upgrade the cache - $content = array(); $needUpgrade = true; $freeSpace = 0; } else { - $content = \OC\Files\Filesystem::getDirectoryContent($dir); + $files = \OCA\files\lib\Helper::getFiles($dir); $freeSpace = \OC\Files\Filesystem::free_space($dir); $needUpgrade = false; } -foreach ($content as $i) { - $i['date'] = OCP\Util::formatDate($i['mtime']); - if ($i['type'] == 'file') { - $fileinfo = pathinfo($i['name']); - $i['basename'] = $fileinfo['filename']; - if (!empty($fileinfo['extension'])) { - $i['extension'] = '.' . $fileinfo['extension']; - } else { - $i['extension'] = ''; - } - } - $i['directory'] = $dir; - $i['isPreviewAvailable'] = \OCP\Preview::isMimeSupported($i['mimetype']); - $i['icon'] = \OCA\files\lib\Helper::determineIcon($i); - $files[] = $i; -} - -usort($files, "fileCmp"); // Make breadcrumb -$breadcrumb = array(); -$pathtohere = ''; -foreach (explode('/', $dir) as $i) { - if ($i != '') { - $pathtohere .= '/' . $i; - $breadcrumb[] = array('dir' => $pathtohere, 'name' => $i); - } -} +$breadcrumb = \OCA\files\lib\Helper::makeBreadcrumb($dir); // make breadcrumb und filelist markup $list = new OCP\Template('files', 'part.list', ''); $list->assign('files', $files); $list->assign('baseURL', OCP\Util::linkTo('files', 'index.php') . '?dir='); $list->assign('downloadURL', OCP\Util::linkToRoute('download', array('file' => '/'))); -$list->assign('disableSharing', false); $list->assign('isPublic', false); $breadcrumbNav = new OCP\Template('files', 'part.breadcrumb', ''); $breadcrumbNav->assign('breadcrumb', $breadcrumb); @@ -154,5 +117,6 @@ if ($needUpgrade) { $tmpl->assign('isPublic', false); $tmpl->assign('publicUploadEnabled', $publicUploadEnabled); $tmpl->assign("encryptedFiles", \OCP\Util::encryptedFiles()); + $tmpl->assign('disableSharing', false); $tmpl->printPage(); } diff --git a/apps/files/js/fileactions.js b/apps/files/js/fileactions.js index 097fe521aa6..330fe86f6b3 100644 --- a/apps/files/js/fileactions.js +++ b/apps/files/js/fileactions.js @@ -196,13 +196,12 @@ FileActions.register('all', 'Rename', OC.PERMISSION_UPDATE, function () { FileList.rename(filename); }); - FileActions.register('dir', 'Open', OC.PERMISSION_READ, '', function (filename) { - var dir = $('#dir').val(); + var dir = $('#dir').val() || '/'; if (dir !== '/') { dir = dir + '/'; } - window.location = OC.linkTo('files', 'index.php') + '?dir=' + encodeURIComponent(dir + filename); + FileList.changeDirectory(dir + filename); }); FileActions.setDefault('dir', 'Open'); diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 29be5e0d362..c205ae32aa9 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -1,7 +1,25 @@ var FileList={ useUndo:true, + postProcessList: function(){ + $('#fileList tr').each(function(){ + //little hack to set unescape filenames in attribute + $(this).attr('data-file',decodeURIComponent($(this).attr('data-file'))); + }); + }, update:function(fileListHtml) { - $('#fileList').empty().html(fileListHtml); + var $fileList = $('#fileList'); + $fileList.empty().html(fileListHtml); + $('#emptycontent').toggleClass('hidden', $fileList.find('tr').length > 0); + $fileList.find('tr').each(function () { + FileActions.display($(this).children('td.filename')); + }); + $fileList.trigger(jQuery.Event("fileActionsReady")); + FileList.postProcessList(); + // "Files" might not be loaded in extending apps + if (window.Files){ + Files.setupDragAndDrop(); + } + $fileList.trigger(jQuery.Event("updated")); }, createRow:function(type, name, iconurl, linktarget, size, lastModified, permissions){ var td, simpleSize, basename, extension; @@ -134,20 +152,83 @@ var FileList={ FileActions.display(tr.find('td.filename')); return tr; }, - refresh:function(data) { - var result = jQuery.parseJSON(data.responseText); + /** + * @brief Changes the current directory and reload the file list. + * @param targetDir target directory (non URL encoded) + * @param changeUrl false if the URL must not be changed (defaults to true) + */ + changeDirectory: function(targetDir, changeUrl){ + var $dir = $('#dir'), + url, + currentDir = $dir.val() || '/'; + targetDir = targetDir || '/'; + if (currentDir === targetDir){ + return; + } + FileList.setCurrentDir(targetDir, changeUrl); + FileList.reload(); + }, + setCurrentDir: function(targetDir, changeUrl){ + $('#dir').val(targetDir); + // Note: IE8 handling ignored for now + if (window.history.pushState && changeUrl !== false){ + url = OC.linkTo('files', 'index.php')+"?dir="+ encodeURIComponent(targetDir).replace(/%2F/g, '/'), + window.history.pushState({dir: targetDir}, '', url); + } + }, + /** + * @brief Reloads the file list using ajax call + */ + reload: function(){ + FileList.showMask(); + if (FileList._reloadCall){ + FileList._reloadCall.abort(); + } + FileList._reloadCall = $.ajax({ + url: OC.filePath('files','ajax','list.php'), + data: { + dir : $('#dir').val(), + breadcrumb: true + }, + error: function(result){ + FileList.reloadCallback(result); + }, + success: function(result) { + FileList.reloadCallback(result); + } + }); + }, + reloadCallback: function(result){ + var $controls = $('#controls'); + + delete FileList._reloadCall; + FileList.hideMask(); + + if (!result || result.status === 'error') { + OC.Notification.show(result.data.message); + return; + } + + if (result.status === 404){ + // go back home + FileList.changeDirectory('/'); + return; + } + if(typeof(result.data.breadcrumb) != 'undefined'){ - updateBreadcrumb(result.data.breadcrumb); + $controls.find('.crumb').remove(); + $controls.prepend(result.data.breadcrumb); + // TODO: might need refactor breadcrumb code into a new file + //resizeBreadcrumbs(true); } FileList.update(result.data.files); - resetFileActionPanel(); }, remove:function(name){ $('tr').filterAttr('data-file',name).find('td.filename').draggable('destroy'); $('tr').filterAttr('data-file',name).remove(); FileList.updateFileSummary(); if($('tr[data-file]').length==0){ - $('#emptycontent').show(); + $('#emptycontent').removeClass('hidden'); } }, insertElement:function(name,type,element){ @@ -177,7 +258,7 @@ var FileList={ }else{ $('#fileList').append(element); } - $('#emptycontent').hide(); + $('#emptycontent').addClass('hidden'); FileList.updateFileSummary(); }, loadingDone:function(name, id){ @@ -508,6 +589,30 @@ var FileList={ $connector.show(); } } + }, + showMask: function(){ + // in case one was shown before + var $mask = $('#content .mask'); + if ($mask.length){ + return; + } + + $mask = $('
'); + + $mask.css('background-image', 'url('+ OC.imagePath('core', 'loading.gif') + ')'); + $('#content').append($mask); + + // block UI, but only make visible in case loading takes longer + FileList._maskTimeout = window.setTimeout(function(){ + // reset opacity + $mask.removeClass('transparent'); + }, 250); + }, + hideMask: function(){ + var $mask = $('#content .mask').remove(); + if (FileList._maskTimeout){ + window.clearTimeout(FileList._maskTimeout); + } } }; @@ -629,8 +734,8 @@ $(document).ready(function(){ } // update folder size - var size = parseInt(data.context.data('size')); - size += parseInt(file.size) ; + var size = parseInt(data.context.data('size')); + size += parseInt(file.size); data.context.attr('data-size', size); data.context.find('td.filesize').text(humanFileSize(size)); @@ -710,5 +815,19 @@ $(document).ready(function(){ $(window).trigger('beforeunload'); }); + window.onpopstate = function(e){ + var targetDir; + if (e.state && e.state.dir){ + targetDir = e.state.dir; + } + else{ + // read from URL + targetDir = (OC.parseQueryString(location.search) || {dir: '/'}).dir || '/'; + } + if (targetDir){ + FileList.changeDirectory(targetDir, false); + } + } + FileList.createFileSummary(); }); diff --git a/apps/files/js/files.js b/apps/files/js/files.js index d729077ea72..ce72c7bcb5a 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -94,29 +94,34 @@ Files={ OC.Notification.show(t('files_encryption', 'Encryption was disabled but your files are still encrypted. Please go to your personal settings to decrypt your files.')); return; } + }, + + setupDragAndDrop: function(){ + var $fileList = $('#fileList'); + + //drag/drop of files + $fileList.find('tr td.filename').each(function(i,e){ + if ($(e).parent().data('permissions') & OC.PERMISSION_DELETE) { + $(e).draggable(dragOptions); + } + }); + + $fileList.find('tr[data-type="dir"] td.filename').each(function(i,e){ + if ($(e).parent().data('permissions') & OC.PERMISSION_CREATE){ + $(e).droppable(folderDropOptions); + } + }); } }; $(document).ready(function() { Files.displayEncryptionWarning(); Files.bindKeyboardShortcuts(document, jQuery); - $('#fileList tr').each(function(){ - //little hack to set unescape filenames in attribute - $(this).attr('data-file',decodeURIComponent($(this).attr('data-file'))); - }); + + FileList.postProcessList(); + Files.setupDragAndDrop(); $('#file_action_panel').attr('activeAction', false); - //drag/drop of files - $('#fileList tr td.filename').each(function(i,e){ - if ($(e).parent().data('permissions') & OC.PERMISSION_DELETE) { - $(e).draggable(dragOptions); - } - }); - $('#fileList tr[data-type="dir"] td.filename').each(function(i,e){ - if ($(e).parent().data('permissions') & OC.PERMISSION_CREATE){ - $(e).droppable(folderDropOptions); - } - }); $('div.crumb:not(.last)').droppable(crumbDropOptions); $('ul#apps>li:first-child').data('dir',''); if($('div.crumb').length){ @@ -335,6 +340,9 @@ $(document).ready(function() { resizeBreadcrumbs(true); + // event handlers for breadcrumb items + $('#controls').delegate('.crumb a', 'click', onClickBreadcrumb); + // display storage warnings setTimeout ( "Files.displayStorageWarnings()", 100 ); OC.Notification.setDefault(Files.displayStorageWarnings); @@ -415,10 +423,6 @@ function boolOperationFinished(data, callback) { } } -function updateBreadcrumb(breadcrumbHtml) { - $('p.nav').empty().html(breadcrumbHtml); -} - var createDragShadow = function(event){ //select dragged file var isDragSelected = $(event.target).parents('tr').find('td input:first').prop('checked'); @@ -681,3 +685,9 @@ function checkTrashStatus() { } }); } + +function onClickBreadcrumb(e){ + var $el = $(e.target).closest('.crumb'); + e.preventDefault(); + FileList.changeDirectory(decodeURIComponent($el.data('dir'))); +} diff --git a/apps/files/lib/helper.php b/apps/files/lib/helper.php index 9170c6e3fc0..282f0678a9a 100644 --- a/apps/files/lib/helper.php +++ b/apps/files/lib/helper.php @@ -45,5 +45,70 @@ class Helper return \OC_Helper::mimetypeIcon($file['mimetype']); } + /** + * Comparator function to sort files alphabetically and have + * the directories appear first + * @param array $a file + * @param array $b file + * @return -1 if $a must come before $b, 1 otherwise + */ + public static function fileCmp($a, $b) { + if ($a['type'] === 'dir' and $b['type'] !== 'dir') { + return -1; + } elseif ($a['type'] !== 'dir' and $b['type'] === 'dir') { + return 1; + } else { + return strnatcasecmp($a['name'], $b['name']); + } + } + + /** + * Retrieves the contents of the given directory and + * returns it as a sorted array. + * @param string $dir path to the directory + * @return array of files + */ + public static function getFiles($dir) { + $content = \OC\Files\Filesystem::getDirectoryContent($dir); + $files = array(); + + foreach ($content as $i) { + $i['date'] = \OCP\Util::formatDate($i['mtime']); + if ($i['type'] === 'file') { + $fileinfo = pathinfo($i['name']); + $i['basename'] = $fileinfo['filename']; + if (!empty($fileinfo['extension'])) { + $i['extension'] = '.' . $fileinfo['extension']; + } else { + $i['extension'] = ''; + } + } + $i['directory'] = $dir; + $i['isPreviewAvailable'] = \OCP\Preview::isMimeSupported($i['mimetype']); + $i['icon'] = \OCA\files\lib\Helper::determineIcon($i); + $files[] = $i; + } + usort($files, array('\OCA\files\lib\Helper', 'fileCmp')); + + return $files; + } + + /** + * Splits the given path into a breadcrumb structure. + * @param string $dir path to process + * @return array where each entry is a hash of the absolute + * directory path and its name + */ + public static function makeBreadcrumb($dir){ + $breadcrumb = array(); + $pathtohere = ''; + foreach (explode('/', $dir) as $i) { + if ($i !== '') { + $pathtohere .= '/' . $i; + $breadcrumb[] = array('dir' => $pathtohere, 'name' => $i); + } + } + return $breadcrumb; + } } diff --git a/apps/files/templates/index.php b/apps/files/templates/index.php index 29cb457cd5a..85e21380c6d 100644 --- a/apps/files/templates/index.php +++ b/apps/files/templates/index.php @@ -2,7 +2,7 @@
-
+
t('New'));?>
    @@ -55,9 +55,9 @@
- -
t('Nothing in here. Upload something!'))?>
- +
0):?>class="hidden">t('Nothing in here. Upload something!'))?>
+ + @@ -82,7 +82,7 @@
t( 'Modified' )); ?> - + t('Unshare'))?> diff --git a/apps/files/templates/part.list.php b/apps/files/templates/part.list.php index 9e1750faddf..1e4d4d11c98 100644 --- a/apps/files/templates/part.list.php +++ b/apps/files/templates/part.list.php @@ -1,4 +1,6 @@ - + assign('mimetype', \OC\Files\Filesystem::getMimeType($path)); $tmpl->assign('fileTarget', basename($linkItem['file_target'])); $tmpl->assign('dirToken', $linkItem['token']); + $tmpl->assign('disableSharing', true); $allowPublicUploadEnabled = (bool) ($linkItem['permissions'] & OCP\PERMISSION_CREATE); if (\OCP\App::isEnabled('files_encryption')) { $allowPublicUploadEnabled = false; @@ -206,7 +207,6 @@ if (isset($path)) { } $list = new OCP\Template('files', 'part.list', ''); $list->assign('files', $files); - $list->assign('disableSharing', true); $list->assign('baseURL', OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&path='); $list->assign('downloadURL', OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&download&path='); diff --git a/apps/files_trashbin/ajax/list.php b/apps/files_trashbin/ajax/list.php new file mode 100644 index 00000000000..e72e67b01d7 --- /dev/null +++ b/apps/files_trashbin/ajax/list.php @@ -0,0 +1,51 @@ +assign('breadcrumb', $breadcrumb, false); + $breadcrumbNav->assign('baseURL', OCP\Util::linkTo('files_trashbin', 'index.php') . '?dir='); + $breadcrumbNav->assign('home', OCP\Util::linkTo('files', 'index.php')); + + $data['breadcrumb'] = $breadcrumbNav->fetchPage(); +} + +// make filelist +$files = \OCA\files_trashbin\lib\Helper::getTrashFiles($dir); + +if ($files === null){ + header("HTTP/1.0 404 Not Found"); + exit(); +} + +$dirlisting = false; +if ($dir && $dir !== '/') { + $dirlisting = true; +} + +$encodedDir = \OCP\Util::encodePath($dir); +$list = new OCP\Template('files_trashbin', 'part.list', ''); +$list->assign('files', $files, false); +$list->assign('baseURL', OCP\Util::linkTo('files_trashbin', 'index.php'). '?dir='.$encodedDir); +$list->assign('downloadURL', OCP\Util::linkToRoute('download', array('file' => '/'))); +$list->assign('dirlisting', $dirlisting); +$list->assign('disableDownloadActions', true); +$data['files'] = $list->fetchPage(); + +OCP\JSON::success(array('data' => $data)); + diff --git a/apps/files_trashbin/index.php b/apps/files_trashbin/index.php index b7d0ef012f4..c28a88d541a 100644 --- a/apps/files_trashbin/index.php +++ b/apps/files_trashbin/index.php @@ -10,92 +10,27 @@ OCP\Util::addScript('files_trashbin', 'disableDefaultActions'); OCP\Util::addScript('files', 'fileactions'); $tmpl = new OCP\Template('files_trashbin', 'index', 'user'); -$user = \OCP\User::getUser(); -$view = new OC_Filesystemview('/'.$user.'/files_trashbin/files'); - OCP\Util::addStyle('files', 'files'); OCP\Util::addScript('files', 'filelist'); +// filelist overrides +OCP\Util::addScript('files_trashbin', 'filelist'); $dir = isset($_GET['dir']) ? stripslashes($_GET['dir']) : ''; -$result = array(); -if ($dir) { - $dirlisting = true; - $dirContent = $view->opendir($dir); - $i = 0; - if(is_resource($dirContent)) { - while(($entryName = readdir($dirContent)) !== false) { - if (!\OC\Files\Filesystem::isIgnoredDir($entryName)) { - $pos = strpos($dir.'/', '/', 1); - $tmp = substr($dir, 0, $pos); - $pos = strrpos($tmp, '.d'); - $timestamp = substr($tmp, $pos+2); - $result[] = array( - 'id' => $entryName, - 'timestamp' => $timestamp, - 'mime' => $view->getMimeType($dir.'/'.$entryName), - 'type' => $view->is_dir($dir.'/'.$entryName) ? 'dir' : 'file', - 'location' => $dir, - ); - } - } - closedir($dirContent); - } -} else { - $dirlisting = false; - $query = \OC_DB::prepare('SELECT `id`,`location`,`timestamp`,`type`,`mime` FROM `*PREFIX*files_trash` WHERE `user` = ?'); - $result = $query->execute(array($user))->fetchAll(); -} +$files = \OCA\files_trashbin\lib\Helper::getTrashFiles($dir); -$files = array(); -foreach ($result as $r) { - $i = array(); - $i['name'] = $r['id']; - $i['date'] = OCP\Util::formatDate($r['timestamp']); - $i['timestamp'] = $r['timestamp']; - $i['mimetype'] = $r['mime']; - $i['type'] = $r['type']; - if ($i['type'] === 'file') { - $fileinfo = pathinfo($r['id']); - $i['basename'] = $fileinfo['filename']; - $i['extension'] = isset($fileinfo['extension']) ? ('.'.$fileinfo['extension']) : ''; - } - $i['directory'] = $r['location']; - if ($i['directory'] === '/') { - $i['directory'] = ''; - } - $i['permissions'] = OCP\PERMISSION_READ; - $i['isPreviewAvailable'] = \OCP\Preview::isMimeSupported($r['mime']); - $i['icon'] = \OCA\files\lib\Helper::determineIcon($i); - $files[] = $i; +// Redirect if directory does not exist +if ($files === null){ + header('Location: ' . OCP\Util::linkTo('files_trashbin', 'index.php')); + exit(); } -function fileCmp($a, $b) { - if ($a['type'] === 'dir' and $b['type'] !== 'dir') { - return -1; - } elseif ($a['type'] !== 'dir' and $b['type'] === 'dir') { - return 1; - } else { - return strnatcasecmp($a['name'], $b['name']); - } +$dirlisting = false; +if ($dir && $dir !== '/') { + $dirlisting = true; } -usort($files, "fileCmp"); - -// Make breadcrumb -$pathtohere = ''; -$breadcrumb = array(); -foreach (explode('/', $dir) as $i) { - if ($i !== '') { - if ( preg_match('/^(.+)\.d[0-9]+$/', $i, $match) ) { - $name = $match[1]; - } else { - $name = $i; - } - $pathtohere .= '/' . $i; - $breadcrumb[] = array('dir' => $pathtohere, 'name' => $name); - } -} +$breadcrumb = \OCA\files_trashbin\lib\Helper::makeBreadcrumb($dir); $breadcrumbNav = new OCP\Template('files_trashbin', 'part.breadcrumb', ''); $breadcrumbNav->assign('breadcrumb', $breadcrumb); @@ -108,7 +43,6 @@ $list->assign('files', $files); $encodedDir = \OCP\Util::encodePath($dir); $list->assign('baseURL', OCP\Util::linkTo('files_trashbin', 'index.php'). '?dir='.$encodedDir); $list->assign('downloadURL', OCP\Util::linkTo('files_trashbin', 'download.php') . '?file='.$encodedDir); -$list->assign('disableSharing', true); $list->assign('dirlisting', $dirlisting); $list->assign('disableDownloadActions', true); @@ -116,6 +50,7 @@ $tmpl->assign('dirlisting', $dirlisting); $tmpl->assign('breadcrumb', $breadcrumbNav->fetchPage()); $tmpl->assign('fileList', $list->fetchPage()); $tmpl->assign('files', $files); -$tmpl->assign('dir', \OC\Files\Filesystem::normalizePath($view->getAbsolutePath())); +$tmpl->assign('dir', $dir); +$tmpl->assign('disableSharing', true); $tmpl->printPage(); diff --git a/apps/files_trashbin/js/filelist.js b/apps/files_trashbin/js/filelist.js new file mode 100644 index 00000000000..ff3a846d860 --- /dev/null +++ b/apps/files_trashbin/js/filelist.js @@ -0,0 +1,29 @@ +// override reload with own ajax call +FileList.reload = function(){ + FileList.showMask(); + if (FileList._reloadCall){ + FileList._reloadCall.abort(); + } + $.ajax({ + url: OC.filePath('files_trashbin','ajax','list.php'), + data: { + dir : $('#dir').val(), + breadcrumb: true + }, + error: function(result) { + FileList.reloadCallback(result); + }, + success: function(result) { + FileList.reloadCallback(result); + } + }); +} + +FileList.setCurrentDir = function(targetDir, changeUrl){ + $('#dir').val(targetDir); + // Note: IE8 handling ignored for now + if (window.history.pushState && changeUrl !== false){ + url = OC.linkTo('files_trashbin', 'index.php')+"?dir="+ encodeURIComponent(targetDir).replace(/%2F/g, '/'), + window.history.pushState({dir: targetDir}, '', url); + } +} diff --git a/apps/files_trashbin/js/trash.js b/apps/files_trashbin/js/trash.js index 40c0bdb3829..d73eadb6011 100644 --- a/apps/files_trashbin/js/trash.js +++ b/apps/files_trashbin/js/trash.js @@ -171,9 +171,15 @@ $(document).ready(function() { action(filename); } } + + // event handlers for breadcrumb items + $('#controls').delegate('.crumb:not(.home) a', 'click', onClickBreadcrumb); }); - FileActions.actions.dir = {}; + FileActions.actions.dir = { + // only keep 'Open' action for navigation + 'Open': FileActions.actions.dir.Open + }; }); function processSelection(){ @@ -246,3 +252,9 @@ function disableActions() { $(".action").css("display", "none"); $(":input:checkbox").css("display", "none"); } +function onClickBreadcrumb(e){ + var $el = $(e.target).closest('.crumb'); + e.preventDefault(); + FileList.changeDirectory(decodeURIComponent($el.data('dir'))); +} + diff --git a/apps/files_trashbin/lib/helper.php b/apps/files_trashbin/lib/helper.php new file mode 100644 index 00000000000..098fc0b54b7 --- /dev/null +++ b/apps/files_trashbin/lib/helper.php @@ -0,0 +1,97 @@ +opendir($dir); + if ($dirContent === false){ + return null; + } + if(is_resource($dirContent)){ + while(($entryName = readdir($dirContent)) !== false) { + if (!\OC\Files\Filesystem::isIgnoredDir($entryName)) { + $pos = strpos($dir.'/', '/', 1); + $tmp = substr($dir, 0, $pos); + $pos = strrpos($tmp, '.d'); + $timestamp = substr($tmp, $pos+2); + $result[] = array( + 'id' => $entryName, + 'timestamp' => $timestamp, + 'mime' => $view->getMimeType($dir.'/'.$entryName), + 'type' => $view->is_dir($dir.'/'.$entryName) ? 'dir' : 'file', + 'location' => $dir, + ); + } + } + closedir($dirContent); + } + } else { + $query = \OC_DB::prepare('SELECT `id`,`location`,`timestamp`,`type`,`mime` FROM `*PREFIX*files_trash` WHERE `user` = ?'); + $result = $query->execute(array($user))->fetchAll(); + } + + $files = array(); + foreach ($result as $r) { + $i = array(); + $i['name'] = $r['id']; + $i['date'] = \OCP\Util::formatDate($r['timestamp']); + $i['timestamp'] = $r['timestamp']; + $i['mimetype'] = $r['mime']; + $i['type'] = $r['type']; + if ($i['type'] === 'file') { + $fileinfo = pathinfo($r['id']); + $i['basename'] = $fileinfo['filename']; + $i['extension'] = isset($fileinfo['extension']) ? ('.'.$fileinfo['extension']) : ''; + } + $i['directory'] = $r['location']; + if ($i['directory'] === '/') { + $i['directory'] = ''; + } + $i['permissions'] = \OCP\PERMISSION_READ; + $i['isPreviewAvailable'] = \OCP\Preview::isMimeSupported($r['mime']); + $i['icon'] = \OCA\files\lib\Helper::determineIcon($i); + $files[] = $i; + } + + usort($files, array('\OCA\files\lib\Helper', 'fileCmp')); + + return $files; + } + + /** + * Splits the given path into a breadcrumb structure. + * @param string $dir path to process + * @return array where each entry is a hash of the absolute + * directory path and its name + */ + public static function makeBreadcrumb($dir){ + // Make breadcrumb + $pathtohere = ''; + $breadcrumb = array(); + foreach (explode('/', $dir) as $i) { + if ($i !== '') { + if ( preg_match('/^(.+)\.d[0-9]+$/', $i, $match) ) { + $name = $match[1]; + } else { + $name = $i; + } + $pathtohere .= '/' . $i; + $breadcrumb[] = array('dir' => $pathtohere, 'name' => $name); + } + } + return $breadcrumb; + } +} diff --git a/apps/files_trashbin/templates/index.php b/apps/files_trashbin/templates/index.php index 88c32b1f3eb..daae7753aeb 100644 --- a/apps/files_trashbin/templates/index.php +++ b/apps/files_trashbin/templates/index.php @@ -9,6 +9,9 @@
t('Nothing in here. Your trash bin is empty!'))?>
+ + + diff --git a/apps/files_trashbin/templates/part.breadcrumb.php b/apps/files_trashbin/templates/part.breadcrumb.php index 8ecab58e5c8..4acc298adbe 100644 --- a/apps/files_trashbin/templates/part.breadcrumb.php +++ b/apps/files_trashbin/templates/part.breadcrumb.php @@ -1,11 +1,11 @@ -
+
'> + data-dir='/'> t("Deleted Files")); ?>
diff --git a/apps/files_trashbin/templates/part.list.php b/apps/files_trashbin/templates/part.list.php index f7cc6b01bbb..78709d986ae 100644 --- a/apps/files_trashbin/templates/part.list.php +++ b/apps/files_trashbin/templates/part.list.php @@ -1,4 +1,3 @@ - ' id="" - data-file="" + data-file="" data-timestamp='' data-dirlisting=1 diff --git a/core/js/js.js b/core/js/js.js index 1999ff73d23..c09f80369f9 100644 --- a/core/js/js.js +++ b/core/js/js.js @@ -321,6 +321,38 @@ var OC={ var date = new Date(1000*mtime); return date.getDate()+'.'+(date.getMonth()+1)+'.'+date.getFullYear()+', '+date.getHours()+':'+date.getMinutes(); }, + /** + * Parses a URL query string into a JS map + * @param queryString query string in the format param1=1234¶m2=abcde¶m3=xyz + * @return map containing key/values matching the URL parameters + */ + parseQueryString:function(queryString){ + var parts, + components, + result = {}, + key, + value; + if (!queryString){ + return null; + } + if (queryString[0] === '?'){ + queryString = queryString.substr(1); + } + parts = queryString.split('&'); + for (var i = 0; i < parts.length; i++){ + components = parts[i].split('='); + if (!components.length){ + continue; + } + key = decodeURIComponent(components[0]); + if (!key){ + continue; + } + value = components[1]; + result[key] = value && decodeURIComponent(value); + } + return result; + }, /** * Opens a popup with the setting for an app. * @param appid String. The ID of the app e.g. 'calendar', 'contacts' or 'files'. -- cgit v1.2.3 From 4e751cbb473b416f693473ae69e7c55e013e854b Mon Sep 17 00:00:00 2001 From: Thomas Müller Date: Tue, 27 Aug 2013 13:13:00 +0200 Subject: fixing breadcrumbs on ajax loading of files --- apps/files/js/filelist.js | 6 +- apps/files/js/files.js | 133 ++++++++++++++++++++++------------------- apps/files/templates/index.php | 4 +- 3 files changed, 78 insertions(+), 65 deletions(-) (limited to 'apps/files/js/files.js') diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 21f713b4a80..b3955b3d22e 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -218,8 +218,10 @@ var FileList={ if(typeof(result.data.breadcrumb) != 'undefined'){ $controls.find('.crumb').remove(); $controls.prepend(result.data.breadcrumb); - // TODO: might need refactor breadcrumb code into a new file - //resizeBreadcrumbs(true); + + var width = $(window).width(); + Files.initBreadCrumbs(); + Files.resizeBreadcrumbs(width, true); // in case svg is not supported by the browser we need to execute the fallback mechanism if(!SVGSupport()) { diff --git a/apps/files/js/files.js b/apps/files/js/files.js index ce72c7bcb5a..1ea2f5fbcc2 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -111,6 +111,72 @@ Files={ $(e).droppable(folderDropOptions); } }); + }, + + lastWidth: 0, + + initBreadCrumbs: function () { + Files.lastWidth = 0; + Files.breadcrumbs = []; + + // initialize with some extra space + Files.breadcrumbsWidth = 64; + if ( document.getElementById("navigation") ) { + Files.breadcrumbsWidth += $('#navigation').get(0).offsetWidth; + } + Files.hiddenBreadcrumbs = 0; + + $.each($('.crumb'), function(index, breadcrumb) { + Files.breadcrumbs[index] = breadcrumb; + Files.breadcrumbsWidth += $(breadcrumb).get(0).offsetWidth; + }); + + + $.each($('#controls .actions>div'), function(index, action) { + Files.breadcrumbsWidth += $(action).get(0).offsetWidth; + }); + }, + + resizeBreadcrumbs: function (width, firstRun) { + if (width != Files.lastWidth) { + if ((width < Files.lastWidth || firstRun) && width < Files.breadcrumbsWidth) { + if (Files.hiddenBreadcrumbs == 0) { + Files.breadcrumbsWidth -= $(Files.breadcrumbs[1]).get(0).offsetWidth; + $(Files.breadcrumbs[1]).find('a').hide(); + $(Files.breadcrumbs[1]).append('...'); + Files.breadcrumbsWidth += $(Files.breadcrumbs[1]).get(0).offsetWidth; + Files.hiddenBreadcrumbs = 2; + } + var i = Files.hiddenBreadcrumbs; + while (width < Files.breadcrumbsWidth && i > 1 && i < Files.breadcrumbs.length - 1) { + Files.breadcrumbsWidth -= $(Files.breadcrumbs[i]).get(0).offsetWidth; + $(Files.breadcrumbs[i]).hide(); + Files.hiddenBreadcrumbs = i; + i++ + } + } else if (width > Files.lastWidth && Files.hiddenBreadcrumbs > 0) { + var i = Files.hiddenBreadcrumbs; + while (width > Files.breadcrumbsWidth && i > 0) { + if (Files.hiddenBreadcrumbs == 1) { + Files.breadcrumbsWidth -= $(Files.breadcrumbs[1]).get(0).offsetWidth; + $(Files.breadcrumbs[1]).find('span').remove(); + $(Files.breadcrumbs[1]).find('a').show(); + Files.breadcrumbsWidth += $(Files.breadcrumbs[1]).get(0).offsetWidth; + } else { + $(Files.breadcrumbs[i]).show(); + Files.breadcrumbsWidth += $(Files.breadcrumbs[i]).get(0).offsetWidth; + if (Files.breadcrumbsWidth > width) { + Files.breadcrumbsWidth -= $(Files.breadcrumbs[i]).get(0).offsetWidth; + $(Files.breadcrumbs[i]).hide(); + break; + } + } + i--; + Files.hiddenBreadcrumbs = i; + } + } + Files.lastWidth = width; + } } }; $(document).ready(function() { @@ -273,72 +339,15 @@ $(document).ready(function() { //do a background scan if needed scanFiles(); - var lastWidth = 0; - var breadcrumbs = []; - var breadcrumbsWidth = 0; - if ( document.getElementById("navigation") ) { - breadcrumbsWidth = $('#navigation').get(0).offsetWidth; - } - var hiddenBreadcrumbs = 0; - - $.each($('.crumb'), function(index, breadcrumb) { - breadcrumbs[index] = breadcrumb; - breadcrumbsWidth += $(breadcrumb).get(0).offsetWidth; - }); - - - $.each($('#controls .actions>div'), function(index, action) { - breadcrumbsWidth += $(action).get(0).offsetWidth; - }); - - function resizeBreadcrumbs(firstRun) { - var width = $(this).width(); - if (width != lastWidth) { - if ((width < lastWidth || firstRun) && width < breadcrumbsWidth) { - if (hiddenBreadcrumbs == 0) { - breadcrumbsWidth -= $(breadcrumbs[1]).get(0).offsetWidth; - $(breadcrumbs[1]).find('a').hide(); - $(breadcrumbs[1]).append('...'); - breadcrumbsWidth += $(breadcrumbs[1]).get(0).offsetWidth; - hiddenBreadcrumbs = 2; - } - var i = hiddenBreadcrumbs; - while (width < breadcrumbsWidth && i > 1 && i < breadcrumbs.length - 1) { - breadcrumbsWidth -= $(breadcrumbs[i]).get(0).offsetWidth; - $(breadcrumbs[i]).hide(); - hiddenBreadcrumbs = i; - i++ - } - } else if (width > lastWidth && hiddenBreadcrumbs > 0) { - var i = hiddenBreadcrumbs; - while (width > breadcrumbsWidth && i > 0) { - if (hiddenBreadcrumbs == 1) { - breadcrumbsWidth -= $(breadcrumbs[1]).get(0).offsetWidth; - $(breadcrumbs[1]).find('span').remove(); - $(breadcrumbs[1]).find('a').show(); - breadcrumbsWidth += $(breadcrumbs[1]).get(0).offsetWidth; - } else { - $(breadcrumbs[i]).show(); - breadcrumbsWidth += $(breadcrumbs[i]).get(0).offsetWidth; - if (breadcrumbsWidth > width) { - breadcrumbsWidth -= $(breadcrumbs[i]).get(0).offsetWidth; - $(breadcrumbs[i]).hide(); - break; - } - } - i--; - hiddenBreadcrumbs = i; - } - } - lastWidth = width; - } - } + Files.initBreadCrumbs(); $(window).resize(function() { - resizeBreadcrumbs(false); + var width = $(this).width(); + Files.resizeBreadcrumbs(width, false); }); - resizeBreadcrumbs(true); + var width = $(this).width(); + Files.resizeBreadcrumbs(width, true); // event handlers for breadcrumb items $('#controls').delegate('.crumb a', 'click', onClickBreadcrumb); diff --git a/apps/files/templates/index.php b/apps/files/templates/index.php index 85e21380c6d..9ca115f7711 100644 --- a/apps/files/templates/index.php +++ b/apps/files/templates/index.php @@ -38,7 +38,9 @@
- > +
-- cgit v1.2.3 From 3cf0820d3507349fa2d518c24579d8605db7cd2e Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Sun, 1 Sep 2013 14:24:01 +0200 Subject: Changed breadcrumb event handling to not use delegate Using delegate might break apps that embed themselves in the files container. When an app embeds itself and the user clicks a breadcrumb, it will simply reload the whole browser page. --- apps/files/js/files.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'apps/files/js/files.js') diff --git a/apps/files/js/files.js b/apps/files/js/files.js index 1ea2f5fbcc2..7aef8ea1d11 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -131,10 +131,12 @@ Files={ Files.breadcrumbsWidth += $(breadcrumb).get(0).offsetWidth; }); - $.each($('#controls .actions>div'), function(index, action) { Files.breadcrumbsWidth += $(action).get(0).offsetWidth; }); + + // event handlers for breadcrumb items + $('#controls .crumb a').on('click', onClickBreadcrumb); }, resizeBreadcrumbs: function (width, firstRun) { @@ -349,9 +351,6 @@ $(document).ready(function() { var width = $(this).width(); Files.resizeBreadcrumbs(width, true); - // event handlers for breadcrumb items - $('#controls').delegate('.crumb a', 'click', onClickBreadcrumb); - // display storage warnings setTimeout ( "Files.displayStorageWarnings()", 100 ); OC.Notification.setDefault(Files.displayStorageWarnings); -- cgit v1.2.3 From 611075bf206555cf011b5fb70e117c93040e9027 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Sun, 1 Sep 2013 14:36:33 +0200 Subject: Fixed JS error in trashbin app --- apps/files/js/files.js | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'apps/files/js/files.js') diff --git a/apps/files/js/files.js b/apps/files/js/files.js index 7aef8ea1d11..c2418cfa751 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -182,6 +182,10 @@ Files={ } }; $(document).ready(function() { + // FIXME: workaround for trashbin app + if (window.trashBinApp){ + return; + } Files.displayEncryptionWarning(); Files.bindKeyboardShortcuts(document, jQuery); -- cgit v1.2.3 From b40925ae1747ae44a52fb1f8dcf7645d022c6f13 Mon Sep 17 00:00:00 2001 From: Jörn Friedrich Dreyer Date: Wed, 31 Jul 2013 22:24:52 +0200 Subject: initial scrollto implementation: use places/folder icon, move link construction to JS, only show icon on hover, use 'searchresult' as css class name, add filter/unfilter methods, highlight searched files in current filelist only filter when correct FileList is present --- apps/files/css/files.css | 3 +++ apps/files/js/filelist.js | 41 ++++++++++++++++++++++++++++++++++----- apps/files/js/files.js | 5 +++++ core/js/js.js | 13 +++++++++++++ lib/search/provider/file.php | 3 ++- lib/search/result.php | 4 +++- search/css/results.css | 22 +++++++++++++++++++-- search/js/result.js | 38 ++++++++++++++++++++++++++++++------ search/templates/part.results.php | 3 ++- 9 files changed, 116 insertions(+), 16 deletions(-) (limited to 'apps/files/js/files.js') diff --git a/apps/files/css/files.css b/apps/files/css/files.css index 41d9808c56b..0acb3c5d829 100644 --- a/apps/files/css/files.css +++ b/apps/files/css/files.css @@ -76,6 +76,9 @@ #filestable tbody tr.selected { background-color: rgb(230,230,230); } +#filestable tbody tr.searchresult { + background-color: rgb(240,240,240); +} tbody a { color:#000; } span.extension, span.uploading, td.date { color:#999; } span.extension { text-transform:lowercase; -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=70)"; filter:alpha(opacity=70); opacity:.7; -webkit-transition:opacity 300ms; -moz-transition:opacity 300ms; -o-transition:opacity 300ms; transition:opacity 300ms; } diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index b50d46c98d3..9a2d39c3652 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -643,6 +643,37 @@ var FileList={ if (FileList._maskTimeout){ window.clearTimeout(FileList._maskTimeout); } + }, + scrollTo:function(file) { + //scroll to and highlight preselected file + var scrolltorow = $('tr[data-file="'+file+'"]'); + if (scrolltorow.length > 0) { + scrolltorow.addClass('searchresult'); + $(window).scrollTop(scrolltorow.position().top); + //remove highlight when hovered over + scrolltorow.one('hover', function(){ + scrolltorow.removeClass('searchresult'); + }); + } + }, + filter:function(query){ + $('#fileList tr:not(.summary)').each(function(i,e){ + if ($(e).data('file').toLowerCase().indexOf(query.toLowerCase()) !== -1) { + $(e).addClass("searchresult"); + } else { + $(e).removeClass("searchresult"); + } + }); + //do not use scrollto to prevent removing searchresult css class + var first = $('#fileList tr.searchresult').first(); + if (first.length !== 0) { + $(window).scrollTop(first.position().top); + } + }, + unfilter:function(){ + $('#fileList tr.searchresult').each(function(i,e){ + $(e).removeClass("searchresult"); + }); } }; @@ -818,16 +849,16 @@ $(document).ready(function(){ FileList.replaceIsNewFile = null; } FileList.lastAction = null; - OC.Notification.hide(); + OC.Notification.hide(); }); $('#notification:first-child').on('click', '.replace', function() { - OC.Notification.hide(function() { - FileList.replace($('#notification > span').attr('data-oldName'), $('#notification > span').attr('data-newName'), $('#notification > span').attr('data-isNewFile')); - }); + OC.Notification.hide(function() { + FileList.replace($('#notification > span').attr('data-oldName'), $('#notification > span').attr('data-newName'), $('#notification > span').attr('data-isNewFile')); + }); }); $('#notification:first-child').on('click', '.suggest', function() { $('tr').filterAttr('data-file', $('#notification > span').attr('data-oldName')).show(); - OC.Notification.hide(); + OC.Notification.hide(); }); $('#notification:first-child').on('click', '.cancel', function() { if ($('#notification > span').attr('data-isNewFile')) { diff --git a/apps/files/js/files.js b/apps/files/js/files.js index c2418cfa751..a4fdf383339 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -384,6 +384,11 @@ $(document).ready(function() { } }); } + + //scroll to and highlight preselected file + if (getURLParameter('scrollto')) { + FileList.scrollTo(getURLParameter('scrollto')); + } }); function scanFiles(force, dir, users){ diff --git a/core/js/js.js b/core/js/js.js index c09f80369f9..c23cf9eebd9 100644 --- a/core/js/js.js +++ b/core/js/js.js @@ -723,11 +723,17 @@ $(document).ready(function(){ } }else if(event.keyCode===27){//esc OC.search.hide(); + if (FileList && typeof FileList.unfilter === 'function') { //TODO add hook system + FileList.unfilter(); + } }else{ var query=$('#searchbox').val(); if(OC.search.lastQuery!==query){ OC.search.lastQuery=query; OC.search.currentResult=-1; + if (FileList && typeof FileList.filter === 'function') { //TODO add hook system + FileList.filter(query); + } if(query.length>2){ OC.search(query); }else{ @@ -840,6 +846,13 @@ function formatDate(date){ return $.datepicker.formatDate(datepickerFormatDate, date)+' '+date.getHours()+':'+((date.getMinutes()<10)?'0':'')+date.getMinutes(); } +// taken from http://stackoverflow.com/questions/1403888/get-url-parameter-with-jquery +function getURLParameter(name) { + return decodeURI( + (RegExp(name + '=' + '(.+?)(&|$)').exec(location.search) || [, null])[1] + ); +} + /** * takes an absolute timestamp and return a string with a human-friendly relative date * @param int a Unix timestamp diff --git a/lib/search/provider/file.php b/lib/search/provider/file.php index 4d88c2a87f1..9bd50931517 100644 --- a/lib/search/provider/file.php +++ b/lib/search/provider/file.php @@ -10,6 +10,7 @@ class OC_Search_Provider_File extends OC_Search_Provider{ $mime = $fileData['mimetype']; $name = basename($path); + $container = dirname($path); $text = ''; $skip = false; if($mime=='httpd/unix-directory') { @@ -37,7 +38,7 @@ class OC_Search_Provider_File extends OC_Search_Provider{ } } if(!$skip) { - $results[] = new OC_Search_Result($name, $text, $link, $type); + $results[] = new OC_Search_Result($name, $text, $link, $type, $container); } } return $results; diff --git a/lib/search/result.php b/lib/search/result.php index 08beaea151c..42275c2df11 100644 --- a/lib/search/result.php +++ b/lib/search/result.php @@ -7,6 +7,7 @@ class OC_Search_Result{ public $text; public $link; public $type; + public $container; /** * create a new search result @@ -15,10 +16,11 @@ class OC_Search_Result{ * @param string $link link for the result * @param string $type the type of result as human readable string ('File', 'Music', etc) */ - public function __construct($name, $text, $link, $type) { + public function __construct($name, $text, $link, $type, $container) { $this->name=$name; $this->text=$text; $this->link=$link; $this->type=$type; + $this->container=$container; } } diff --git a/search/css/results.css b/search/css/results.css index 4ae7d67afb3..8a32b0b995d 100644 --- a/search/css/results.css +++ b/search/css/results.css @@ -14,7 +14,7 @@ position:fixed; right:0; text-overflow:ellipsis; - top:20px; + top:45px; width:380px; z-index:75; } @@ -43,10 +43,16 @@ } #searchresults td { - vertical-align:top; padding:0 .3em; + height: 32px; +} +#searchresults tr.template { + display: none; } +#searchresults td.result { + width:250px; +} #searchresults td.result div.text { padding-left:1em; white-space:nowrap; @@ -56,6 +62,18 @@ cursor:pointer; } +#searchresults td.container { + width:20px; +} + +#searchresults td.container img { + vertical-align: middle; + display:none; +} +#searchresults tr:hover td.container img { + display:inline; +} + #searchresults td.type { border-bottom:none; border-right:1px solid #aaa; diff --git a/search/js/result.js b/search/js/result.js index 78fa8efc8e9..78d9149f220 100644 --- a/search/js/result.js +++ b/search/js/result.js @@ -8,15 +8,23 @@ OC.search.catagorizeResults=function(results){ types[type].push(results[i]); } return types; -} +}; OC.search.hide=function(){ $('#searchresults').hide(); if($('#searchbox').val().length>2){ $('#searchbox').val(''); + if (FileList && typeof FileList.unfilter === 'function') { //TODO add hook system + FileList.unfilter(); + } }; -} + if ($('#searchbox').val().length === 0) { + if (FileList && typeof FileList.unfilter === 'function') { //TODO add hook system + FileList.unfilter(); + } + } +}; OC.search.showResults=function(results){ - if(results.length==0){ + if(results.length === 0){ return; } if(!OC.search.showResults.loaded){ @@ -30,6 +38,9 @@ OC.search.showResults=function(results){ }); $(document).click(function(event){ OC.search.hide(); + if (FileList && typeof FileList.unfilter === 'function') { //TODO add hook system + FileList.unfilter(); + } }); OC.search.lastResults=results; OC.search.showResults(results); @@ -46,12 +57,27 @@ OC.search.showResults=function(results){ var row=$('#searchresults tr.template').clone(); row.removeClass('template'); row.addClass('result'); - if (i == 0){ + if (i === 0){ row.children('td.type').text(name); } row.find('td.result a').attr('href',type[i].link); row.find('td.result div.name').text(type[i].name); row.find('td.result div.text').text(type[i].text); + if (type[i].container) { + var td = row.find('td.container'); + td.append(''); + td.find('img').attr('src',OC.imagePath('core','places/folder')); + var containerName = OC.basename(type[i].container); + if (containerName === '') { + containerName = '/'; + } + var containerLink = OC.linkTo('files','index.php') + +'?dir='+encodeURIComponent(type[i].container) + +'&scrollto='+encodeURIComponent(type[i].name); + row.find('td.container a') + .attr('href',containerLink) + .attr('title',t('core','Show in {folder}',{folder: containerName})); + } row.data('index',index); index++; if(OC.search.customResults[name]){//give plugins the ability to customize the entries in here @@ -62,7 +88,7 @@ OC.search.showResults=function(results){ } } } -} +}; OC.search.showResults.loaded=false; OC.search.renderCurrent=function(){ @@ -71,4 +97,4 @@ OC.search.renderCurrent=function(){ $('#searchresults tr.result').removeClass('current'); $(result).addClass('current'); } -} +}; diff --git a/search/templates/part.results.php b/search/templates/part.results.php index 9e39a1c2c8b..1469e3468d3 100644 --- a/search/templates/part.results.php +++ b/search/templates/part.results.php @@ -1,7 +1,7 @@
- + +
@@ -9,6 +9,7 @@
-- cgit v1.2.3 From 0d81a53e12bed66e5ec9684424519913283110a5 Mon Sep 17 00:00:00 2001 From: Jörn Friedrich Dreyer Date: Thu, 19 Sep 2013 10:00:42 +0200 Subject: use 96x96 as 64x64 thumbnails in conflicts dialog, 64x64 looks very blocky ... maybe something is wrong there --- apps/files/js/files.js | 12 ++++++++---- core/js/oc-dialogs.js | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) (limited to 'apps/files/js/files.js') diff --git a/apps/files/js/files.js b/apps/files/js/files.js index afbb14c5e00..76f19b87cbc 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -627,11 +627,15 @@ function getPathForPreview(name) { return path; } -function lazyLoadPreview(path, mime, ready) { +function lazyLoadPreview(path, mime, ready, width, height) { getMimeIcon(mime,ready); - var x = $('#filestable').data('preview-x'); - var y = $('#filestable').data('preview-y'); - var previewURL = OC.Router.generate('core_ajax_preview', {file: encodeURIComponent(path), x:x, y:y}); + if (!width) { + width = $('#filestable').data('preview-x'); + } + if (!height) { + height = $('#filestable').data('preview-y'); + } + var previewURL = OC.Router.generate('core_ajax_preview', {file: encodeURIComponent(path), x:width, y:height}); $.get(previewURL, function() { previewURL = previewURL.replace('(','%28'); previewURL = previewURL.replace(')','%29'); diff --git a/core/js/oc-dialogs.js b/core/js/oc-dialogs.js index 7ca94dcbaa4..d661a871a5f 100644 --- a/core/js/oc-dialogs.js +++ b/core/js/oc-dialogs.js @@ -296,7 +296,7 @@ var OCdialogs = { var path = getPathForPreview(original.name); lazyLoadPreview(path, original.type, function(previewpath){ conflict.find('.original .icon').css('background-image','url('+previewpath+')'); - }); + }, 96, 96); getCroppedPreview(replacement).then( function(path){ conflict.find('.replacement .icon').css('background-image','url(' + path + ')'); -- cgit v1.2.3 From c30c153ea517403ee479be739a503bd91bab272e Mon Sep 17 00:00:00 2001 From: Thomas Müller Date: Thu, 19 Sep 2013 11:13:11 +0200 Subject: fixing typos and l10n --- apps/files/js/file-upload.js | 2 +- apps/files/js/files.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'apps/files/js/files.js') diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js index 8e9bcb885f6..2c42f29445f 100644 --- a/apps/files/js/file-upload.js +++ b/apps/files/js/file-upload.js @@ -169,7 +169,7 @@ OC.Upload = { }, /** * TODO checks the list of existing files prior to uploading and shows a simple dialog to choose - * skip all, replace all or choosw which files to keep + * skip all, replace all or choose which files to keep * @param array selection of files to upload * @param callbacks to call: * onNoConflicts, diff --git a/apps/files/js/files.js b/apps/files/js/files.js index 76f19b87cbc..ccb40e7216f 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -499,7 +499,7 @@ var folderDropOptions={ $('#notification').fadeIn(); } } else { - OC.dialogs.alert(t('Error moving file'), t('core', 'Error')); + OC.dialogs.alert(t('files', 'Error moving file'), t('files', 'Error')); } }); }); @@ -537,7 +537,7 @@ var crumbDropOptions={ $('#notification').fadeIn(); } } else { - OC.dialogs.alert(t('Error moving file'), t('core', 'Error')); + OC.dialogs.alert(t('files', 'Error moving file'), t('files', 'Error')); } }); }); -- cgit v1.2.3 From 98ff8478301676c99ffefd5756ad22466dfb6acf Mon Sep 17 00:00:00 2001 From: Jörn Friedrich Dreyer Date: Thu, 19 Sep 2013 14:46:33 +0200 Subject: fix race condition in lazy preview loading --- apps/files/js/files.js | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) (limited to 'apps/files/js/files.js') diff --git a/apps/files/js/files.js b/apps/files/js/files.js index ccb40e7216f..5ec65d87457 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -628,18 +628,24 @@ function getPathForPreview(name) { } function lazyLoadPreview(path, mime, ready, width, height) { - getMimeIcon(mime,ready); - if (!width) { - width = $('#filestable').data('preview-x'); - } - if (!height) { - height = $('#filestable').data('preview-y'); - } - var previewURL = OC.Router.generate('core_ajax_preview', {file: encodeURIComponent(path), x:width, y:height}); - $.get(previewURL, function() { - previewURL = previewURL.replace('(','%28'); - previewURL = previewURL.replace(')','%29'); - ready(previewURL + '&reload=true'); + // get mime icon url + getMimeIcon(mime, function(iconURL) { + ready(iconURL); // set mimeicon URL + + // now try getting a preview thumbnail URL + if ( ! width ) { + width = $('#filestable').data('preview-x'); + } + if ( ! height ) { + height = $('#filestable').data('preview-y'); + } + var previewURL = OC.Router.generate('core_ajax_preview', {file: encodeURIComponent(path), x:width, y:height}); + $.get(previewURL, function() { + previewURL = previewURL.replace('(', '%28'); + previewURL = previewURL.replace(')', '%29'); + //set preview thumbnail URL + ready(previewURL + '&reload=true'); + }); }); } -- cgit v1.2.3 From 8a1618bce56a32e311626cd9f0e322dd7cf330c4 Mon Sep 17 00:00:00 2001 From: Georg Ehrke Date: Mon, 23 Sep 2013 12:27:05 +0200 Subject: implement previews for public upload --- apps/files/js/files.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'apps/files/js/files.js') diff --git a/apps/files/js/files.js b/apps/files/js/files.js index 8ccb448abfb..ec688eaf63e 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -644,7 +644,11 @@ function lazyLoadPreview(path, mime, ready, width, height) { if ( ! height ) { height = $('#filestable').data('preview-y'); } - var previewURL = OC.Router.generate('core_ajax_preview', {file: encodeURIComponent(path), x:width, y:height}); + if( $('#publicUploadButtonMock').length ) { + var previewURL = OC.Router.generate('core_ajax_public_preview', {file: encodeURIComponent(path), x:width, y:height, t:$('#dirToken').val()}); + } else { + var previewURL = OC.Router.generate('core_ajax_preview', {file: encodeURIComponent(path), x:width, y:height}); + } $.get(previewURL, function() { previewURL = previewURL.replace('(', '%28'); previewURL = previewURL.replace(')', '%29'); -- cgit v1.2.3