diff --git a/apps/files/ajax/delete.php b/apps/files/ajax/delete.php
index 575b8c8d9ea..da7e9d6b2aa 100644
--- a/apps/files/ajax/delete.php
+++ b/apps/files/ajax/delete.php
@@ -12,10 +12,12 @@ $files = isset($_POST["file"]) ? stripslashes($_POST["file"]) : stripslashes($_P
$files = json_decode($files);
$filesWithError = '';
+
$success = true;
+
//Now delete
foreach ($files as $file) {
- if (!OC_Files::delete($dir, $file)) {
+ if (($dir === '' && $file === 'Shared') || !\OC\Files\Filesystem::unlink($dir . '/' . $file)) {
$filesWithError .= $file . "\n";
$success = false;
}
diff --git a/apps/files/ajax/list.php b/apps/files/ajax/list.php
index cade7e872b3..878e4cb2159 100644
--- a/apps/files/ajax/list.php
+++ b/apps/files/ajax/list.php
@@ -32,7 +32,7 @@ if($doBreadcrumb) {
// make filelist
$files = array();
-foreach( OC_Files::getdirectorycontent( $dir ) as $i ) {
+foreach( \OC\Files\Filesystem::getDirectoryContent( $dir ) as $i ) {
$i["date"] = OCP\Util::formatDate($i["mtime"] );
$files[] = $i;
}
diff --git a/apps/files/ajax/move.php b/apps/files/ajax/move.php
index 4ebc3f42d9f..99e43ed23be 100644
--- a/apps/files/ajax/move.php
+++ b/apps/files/ajax/move.php
@@ -7,19 +7,23 @@ OCP\JSON::checkLoggedIn();
OCP\JSON::callCheck();
// Get data
-$dir = stripslashes($_GET["dir"]);
-$file = stripslashes($_GET["file"]);
-$target = stripslashes(rawurldecode($_GET["target"]));
+$dir = stripslashes($_POST["dir"]);
+$file = stripslashes($_POST["file"]);
+$target = stripslashes(rawurldecode($_POST["target"]));
-$l=OC_L10N::get('files');
-
-if(OC_Filesystem::file_exists($target . '/' . $file)) {
- OCP\JSON::error(array("data" => array( "message" => $l->t("Could not move %s - File with this name already exists", array($file)) )));
+if(\OC\Files\Filesystem::file_exists($target . '/' . $file)) {
+ OCP\JSON::error(array("data" => array( "message" => "Could not move $file - File with this name already exists" )));
exit;
}
-if(OC_Files::move($dir, $file, $target, $file)) {
- OCP\JSON::success(array("data" => array( "dir" => $dir, "files" => $file )));
-} else {
- OCP\JSON::error(array("data" => array( "message" => $l->t("Could not move %s", array($file)) )));
+if ($dir != '' || $file != 'Shared') {
+ $targetFile = \OC\Files\Filesystem::normalizePath($dir . '/' . $file);
+ $sourceFile = \OC\Files\Filesystem::normalizePath($target . '/' . $file);
+ if(\OC\Files\Filesystem::rename($sourceFile, $targetFile)) {
+ OCP\JSON::success(array("data" => array( "dir" => $dir, "files" => $file )));
+ } else {
+ OCP\JSON::error(array("data" => array( "message" => "Could not move $file" )));
+ }
+}else{
+ OCP\JSON::error(array("data" => array( "message" => "Could not move $file" )));
}
diff --git a/apps/files/ajax/newfile.php b/apps/files/ajax/newfile.php
index 2bac9bb20ba..38714f34a63 100644
--- a/apps/files/ajax/newfile.php
+++ b/apps/files/ajax/newfile.php
@@ -63,13 +63,12 @@ if($source) {
$ctx = stream_context_create(null, array('notification' =>'progress'));
$sourceStream=fopen($source, 'rb', false, $ctx);
$target=$dir.'/'.$filename;
- $result=OC_Filesystem::file_put_contents($target, $sourceStream);
+ $result=\OC\Files\Filesystem::file_put_contents($target, $sourceStream);
if($result) {
- $target = OC_Filesystem::normalizePath($target);
- $meta = OC_FileCache::get($target);
+ $meta = \OC\Files\Filesystem::getFileInfo($target);
$mime=$meta['mimetype'];
- $id = OC_FileCache::getId($target);
- $eventSource->send('success', array('mime'=>$mime, 'size'=>OC_Filesystem::filesize($target), 'id' => $id));
+ $id = $meta['fileid'];
+ $eventSource->send('success', array('mime'=>$mime, 'size'=>\OC\Files\Filesystem::filesize($target), 'id' => $id));
} else {
$eventSource->send('error', "Error while downloading ".$source. ' to '.$target);
}
@@ -77,15 +76,15 @@ if($source) {
exit();
} else {
if($content) {
- if(OC_Filesystem::file_put_contents($dir.'/'.$filename, $content)) {
- $meta = OC_FileCache::get($dir.'/'.$filename);
- $id = OC_FileCache::getId($dir.'/'.$filename);
+ if(\OC\Files\Filesystem::file_put_contents($dir.'/'.$filename, $content)) {
+ $meta = \OC\Files\Filesystem::getFileInfo($dir.'/'.$filename);
+ $id = $meta['fileid'];
OCP\JSON::success(array("data" => array('content'=>$content, 'id' => $id)));
exit();
}
- }elseif(OC_Files::newFile($dir, $filename, 'file')) {
- $meta = OC_FileCache::get($dir.'/'.$filename);
- $id = OC_FileCache::getId($dir.'/'.$filename);
+ }elseif(\OC\Files\Filesystem::touch($dir . '/' . $filename)) {
+ $meta = \OC\Files\Filesystem::getFileInfo($dir.'/'.$filename);
+ $id = $meta['fileid'];
OCP\JSON::success(array("data" => array('content'=>$content, 'id' => $id)));
exit();
}
diff --git a/apps/files/ajax/newfolder.php b/apps/files/ajax/newfolder.php
index 0f1f2f14eb0..e26e1238bc6 100644
--- a/apps/files/ajax/newfolder.php
+++ b/apps/files/ajax/newfolder.php
@@ -19,13 +19,14 @@ if(strpos($foldername, '/')!==false) {
exit();
}
-if(OC_Files::newFile($dir, stripslashes($foldername), 'dir')) {
+if(\OC\Files\Filesystem::mkdir($dir . '/' . stripslashes($foldername))) {
if ( $dir != '/') {
$path = $dir.'/'.$foldername;
} else {
$path = '/'.$foldername;
}
- $id = OC_FileCache::getId($path);
+ $meta = \OC\Files\Filesystem::getFileInfo($path);
+ $id = $meta['fileid'];
OCP\JSON::success(array("data" => array('id'=>$id)));
exit();
}
diff --git a/apps/files/ajax/rawlist.php b/apps/files/ajax/rawlist.php
index e0aa0bdac52..1cd2944483c 100644
--- a/apps/files/ajax/rawlist.php
+++ b/apps/files/ajax/rawlist.php
@@ -15,7 +15,7 @@ $mimetype = isset($_GET['mimetype']) ? $_GET['mimetype'] : '';
// make filelist
$files = array();
-foreach( OC_Files::getdirectorycontent( $dir, $mimetype ) as $i ) {
+foreach( \OC\Files\Filesystem::getDirectoryContent( $dir, $mimetype ) as $i ) {
$i["date"] = OCP\Util::formatDate($i["mtime"] );
$i['mimetype_icon'] = $i['type'] == 'dir' ? \mimetype_icon('dir'): \mimetype_icon($i['mimetype']);
$files[] = $i;
diff --git a/apps/files/ajax/rename.php b/apps/files/ajax/rename.php
index 89b4d4bba73..970aaa638da 100644
--- a/apps/files/ajax/rename.php
+++ b/apps/files/ajax/rename.php
@@ -11,10 +11,14 @@ $dir = stripslashes($_GET["dir"]);
$file = stripslashes($_GET["file"]);
$newname = stripslashes($_GET["newname"]);
-// Delete
-if( $newname !== '.' and OC_Files::move( $dir, $file, $dir, $newname )) {
- OCP\JSON::success(array("data" => array( "dir" => $dir, "file" => $file, "newname" => $newname )));
-} else {
- $l=OC_L10N::get('files');
- OCP\JSON::error(array("data" => array( "message" => $l->t("Unable to rename file") )));
+if ( $newname !== '.' and ($dir != '' || $file != 'Shared') and $newname !== '.') {
+ $targetFile = \OC\Files\Filesystem::normalizePath($dir . '/' . $newname);
+ $sourceFile = \OC\Files\Filesystem::normalizePath($dir . '/' . $file);
+ if(\OC\Files\Filesystem::rename($sourceFile, $targetFile)) {
+ OCP\JSON::success(array("data" => array( "dir" => $dir, "file" => $file, "newname" => $newname )));
+ } else {
+ OCP\JSON::error(array("data" => array( "message" => "Unable to rename file" )));
+ }
+}else{
+ OCP\JSON::error(array("data" => array( "message" => "Unable to rename file" )));
}
diff --git a/apps/files/ajax/scan.php b/apps/files/ajax/scan.php
index a819578e309..391b98608bd 100644
--- a/apps/files/ajax/scan.php
+++ b/apps/files/ajax/scan.php
@@ -1,44 +1,71 @@
getAbsolutePath($dir);
+
+$mountPoints = \OC\Files\Filesystem::getMountPoints($absolutePath);
+$mountPoints[] = \OC\Files\Filesystem::getMountPoint($absolutePath);
+$mountPoints = array_reverse($mountPoints); //start with the mount point of $dir
+
+foreach ($mountPoints as $mountPoint) {
+ $storage = \OC\Files\Filesystem::getStorage($mountPoint);
+ if ($storage) {
+ ScanListener::$mountPoints[$storage->getId()] = $mountPoint;
+ $scanner = $storage->getScanner();
+ if ($force) {
+ $scanner->scan('');
+ } else {
+ $scanner->backgroundScan();
}
-
- OC_FileCache::scan($dir, $eventSource);
- OC_FileCache::clean();
- OCP\DB::commit();
- $eventSource->send('success', true);
- } else {
- OCP\JSON::success(array('data'=>array('done'=>true)));
- exit;
- }
-} else {
- if($checkOnly) {
- OCP\JSON::success(array('data'=>array('done'=>false)));
- exit;
- }
- if(isset($eventSource)) {
- $eventSource->send('success', false);
- } else {
- exit;
}
}
+
+$eventSource->send('done', ScanListener::$fileCount);
$eventSource->close();
+
+class ScanListener {
+
+ static public $fileCount = 0;
+ static public $lastCount = 0;
+
+ /**
+ * @var \OC\Files\View $view
+ */
+ static public $view;
+
+ /**
+ * @var array $mountPoints map storage ids to mountpoints
+ */
+ static public $mountPoints = array();
+
+ /**
+ * @var \OC_EventSource event source to pass events to
+ */
+ static public $eventSource;
+
+ static function folder($params) {
+ $internalPath = $params['path'];
+ $mountPoint = self::$mountPoints[$params['storage']];
+ $path = self::$view->getRelativePath($mountPoint . $internalPath);
+ self::$eventSource->send('folder', $path);
+ }
+
+ static function file() {
+ self::$fileCount++;
+ if (self::$fileCount > self::$lastCount + 20) { //send a count update every 20 files
+ self::$lastCount = self::$fileCount;
+ self::$eventSource->send('count', self::$fileCount);
+ }
+ }
+}
diff --git a/apps/files/ajax/upgrade.php b/apps/files/ajax/upgrade.php
new file mode 100644
index 00000000000..7237b02c0b0
--- /dev/null
+++ b/apps/files/ajax/upgrade.php
@@ -0,0 +1,44 @@
+hasItems()) {
+ OC_Hook::connect('\OC\Files\Cache\Upgrade', 'migrate_path', $listener, 'upgradePath');
+
+ OC_DB::beginTransaction();
+ $upgrade = new \OC\Files\Cache\Upgrade($legacy);
+ $count = $legacy->getCount();
+ $eventSource->send('total', $count);
+ $upgrade->upgradePath('/' . $user . '/files');
+ OC_DB::commit();
+}
+\OC\Files\Cache\Upgrade::upgradeDone($user);
+$eventSource->send('done', true);
+$eventSource->close();
+
+class UpgradeListener {
+ /**
+ * @var OC_EventSource $eventSource
+ */
+ private $eventSource;
+
+ private $count = 0;
+ private $lastSend = 0;
+
+ public function __construct($eventSource) {
+ $this->eventSource = $eventSource;
+ }
+
+ public function upgradePath($path) {
+ $this->count++;
+ if ($this->count > ($this->lastSend + 5)) {
+ $this->lastSend = $this->count;
+ $this->eventSource->send('count', $this->count);
+ }
+ }
+}
diff --git a/apps/files/ajax/upload.php b/apps/files/ajax/upload.php
index 415524be629..9ecc1a6c2f4 100644
--- a/apps/files/ajax/upload.php
+++ b/apps/files/ajax/upload.php
@@ -21,13 +21,13 @@ if (!isset($_FILES['files'])) {
foreach ($_FILES['files']['error'] as $error) {
if ($error != 0) {
$errors = array(
- UPLOAD_ERR_OK => $l->t('There is no error, the file uploaded with success'),
- UPLOAD_ERR_INI_SIZE => $l->t('The uploaded file exceeds the upload_max_filesize directive in php.ini: ')
+ UPLOAD_ERR_OK => $l->t('There is no error, the file uploaded with success'),
+ UPLOAD_ERR_INI_SIZE => $l->t('The uploaded file exceeds the upload_max_filesize directive in php.ini: ')
. ini_get('upload_max_filesize'),
- UPLOAD_ERR_FORM_SIZE => $l->t('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified'
+ UPLOAD_ERR_FORM_SIZE => $l->t('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified'
. ' in the HTML form'),
- UPLOAD_ERR_PARTIAL => $l->t('The uploaded file was only partially uploaded'),
- UPLOAD_ERR_NO_FILE => $l->t('No file was uploaded'),
+ UPLOAD_ERR_PARTIAL => $l->t('The uploaded file was only partially uploaded'),
+ UPLOAD_ERR_NO_FILE => $l->t('No file was uploaded'),
UPLOAD_ERR_NO_TMP_DIR => $l->t('Missing a temporary folder'),
UPLOAD_ERR_CANT_WRITE => $l->t('Failed to write to disk'),
);
@@ -40,12 +40,17 @@ $files = $_FILES['files'];
$dir = $_POST['dir'];
$error = '';
+$maxUploadFilesize = OCP\Util::maxUploadFilesize($dir);
+$maxHumanFilesize = OCP\Util::humanFileSize($maxUploadFilesize);
+
$totalSize = 0;
foreach ($files['size'] as $size) {
$totalSize += $size;
}
-if ($totalSize > OC_Filesystem::free_space($dir)) {
- OCP\JSON::error(array('data' => array_merge(array('message' => $l->t('Not enough storage available')), $storageStats)));
+if ($totalSize > \OC\Files\Filesystem::free_space($dir)) {
+ OCP\JSON::error(array('data' => array('message' => $l->t('Not enough space available'),
+ 'uploadMaxFilesize' => $maxUploadFilesize,
+ 'maxHumanFilesize' => $maxHumanFilesize)));
exit();
}
@@ -55,19 +60,19 @@ if (strpos($dir, '..') === false) {
for ($i = 0; $i < $fileCount; $i++) {
$target = OCP\Files::buildNotExistingFileName(stripslashes($dir), $files['name'][$i]);
// $path needs to be normalized - this failed within drag'n'drop upload to a sub-folder
- $target = OC_Filesystem::normalizePath($target);
- if (is_uploaded_file($files['tmp_name'][$i]) and OC_Filesystem::fromTmpFile($files['tmp_name'][$i], $target)) {
- $meta = OC_FileCache::get($target);
- $id = OC_FileCache::getId($target);
-
+ $target = \OC\Files\Filesystem::normalizePath($target);
+ if (is_uploaded_file($files['tmp_name'][$i]) and \OC\Files\Filesystem::fromTmpFile($files['tmp_name'][$i], $target)) {
+ $meta = \OC\Files\Filesystem::getFileInfo($target);
// updated max file size after upload
$storageStats = \OCA\files\lib\Helper::buildFileStorageStatistics($dir);
- $result[] = array_merge(array('status' => 'success',
- 'mime' => $meta['mimetype'],
- 'size' => $meta['size'],
- 'id' => $id,
- 'name' => basename($target)), $storageStats
+ $result[] = array('status' => 'success',
+ 'mime' => $meta['mimetype'],
+ 'size' => $meta['size'],
+ 'id' => $meta['fileid'],
+ 'name' => basename($target),
+ 'uploadMaxFilesize' => $maxUploadFilesize,
+ 'maxHumanFilesize' => $maxHumanFilesize
);
}
}
diff --git a/apps/files/appinfo/app.php b/apps/files/appinfo/app.php
index dfab94b15ea..da17a7f2ccd 100644
--- a/apps/files/appinfo/app.php
+++ b/apps/files/appinfo/app.php
@@ -1,5 +1,5 @@
basename($file),
);
diff --git a/apps/files/appinfo/info.xml b/apps/files/appinfo/info.xml
index 0a1b196b06f..7c82c839dab 100644
--- a/apps/files/appinfo/info.xml
+++ b/apps/files/appinfo/info.xml
@@ -5,7 +5,7 @@
';
+ deleteAction[0].outerHTML = newHTML;
+ }
// Finish any existing actions
if (FileList.lastAction) {
FileList.lastAction();
}
- FileList.prepareDeletion(files);
-
- if (!FileList.useUndo) {
- FileList.lastAction();
- } else {
- // NOTE: Temporary fix to change the text to unshared for files in root of Shared folder
- if ($('#dir').val() == '/Shared') {
- OC.Notification.showHtml(t('files', 'unshared {files}', {'files': escapeHTML(files)})+''+t('files', 'undo')+'');
- } else {
- OC.Notification.showHtml(t('files', 'deleted {files}', {'files': escapeHTML(files)})+''+t('files', 'undo')+'');
- }
- }
- },
- finishDelete:function(ready,sync){
- if(!FileList.deleteCanceled && FileList.deleteFiles){
- var fileNames=JSON.stringify(FileList.deleteFiles);
- $.ajax({
- url: OC.filePath('files', 'ajax', 'delete.php'),
- async:!sync,
- type:'post',
- data: {dir:$('#dir').val(),files:fileNames},
- complete: function(data){
- boolOperationFinished(data, function(){
- OC.Notification.hide();
- $.each(FileList.deleteFiles,function(index,file){
- FileList.remove(file);
+ var fileNames = JSON.stringify(files);
+ $.post(OC.filePath('files', 'ajax', 'delete.php'),
+ {dir:$('#dir').val(),files:fileNames},
+ function(result){
+ if (result.status == 'success') {
+ $.each(files,function(index,file){
+ var files = $('tr').filterAttr('data-file',file);
+ files.hide();
+ files.find('input[type="checkbox"]').removeAttr('checked');
+ files.removeClass('selected');
});
- FileList.deleteCanceled=true;
- FileList.deleteFiles=null;
- FileList.lastAction = null;
- if(ready){
- ready();
- }
- });
- }
- });
- }
- },
- prepareDeletion:function(files){
- if(files.substr){
- files=[files];
- }
- $.each(files,function(index,file){
- var files = $('tr').filterAttr('data-file',file);
- files.hide();
- files.find('input[type="checkbox"]').removeAttr('checked');
- files.removeClass('selected');
- });
- procesSelection();
- FileList.deleteCanceled=false;
- FileList.deleteFiles=files;
- FileList.lastAction = function() {
- FileList.finishDelete(null, true);
- };
+ procesSelection();
+ } else {
+ $.each(files,function(index,file) {
+ var deleteAction = $('tr').filterAttr('data-file',file).children("td.date").children(".move2trash");
+ deleteAction[0].outerHTML = oldHTML;
+ });
+ }
+ });
}
};
diff --git a/apps/files/js/files.js b/apps/files/js/files.js
index c817d8431e2..3d09d6aa2b6 100644
--- a/apps/files/js/files.js
+++ b/apps/files/js/files.js
@@ -114,6 +114,11 @@ $(document).ready(function() {
$(this).parent().children('#file_upload_start').trigger('click');
return false;
});
+
+ // Show trash bin
+ $('#trash a').live('click', function() {
+ window.location=OC.filePath('files_trashbin', '', 'index.php');
+ });
var lastChecked;
@@ -670,12 +675,8 @@ $(document).ready(function() {
});
});
- //check if we need to scan the filesystem
- $.get(OC.filePath('files','ajax','scan.php'),{checkonly:'true'}, function(response) {
- if(response.data.done){
- scanFiles();
- }
- }, "json");
+ //do a background scan if needed
+ scanFiles();
var lastWidth = 0;
var breadcrumbs = [];
@@ -774,27 +775,23 @@ $(document).ready(function() {
}
});
-function scanFiles(force,dir){
+function scanFiles(force, dir){
if(!dir){
- dir='';
+ dir = '';
}
- force=!!force; //cast to bool
- scanFiles.scanning=true;
- $('#scanning-message').show();
- $('#fileList').remove();
- var scannerEventSource=new OC.EventSource(OC.filePath('files','ajax','scan.php'),{force:force,dir:dir});
- scanFiles.cancel=scannerEventSource.close.bind(scannerEventSource);
- scannerEventSource.listen('scanning',function(data){
- $('#scan-count').text(t('files', '{count} files scanned', {count: data.count}));
- $('#scan-current').text(data.file+'/');
+ force = !!force; //cast to bool
+ scanFiles.scanning = true;
+ var scannerEventSource = new OC.EventSource(OC.filePath('files','ajax','scan.php'),{force:force,dir:dir});
+ scanFiles.cancel = scannerEventSource.close.bind(scannerEventSource);
+ scannerEventSource.listen('count',function(count){
+ console.log(count + 'files scanned')
});
- scannerEventSource.listen('success',function(success){
+ scannerEventSource.listen('folder',function(path){
+ console.log('now scanning ' + path)
+ });
+ scannerEventSource.listen('done',function(count){
scanFiles.scanning=false;
- if(success){
- window.location.reload();
- }else{
- alert(t('files', 'error while scanning'));
- }
+ console.log('done after ' + count + 'files');
});
}
scanFiles.scanning=false;
@@ -813,32 +810,101 @@ function updateBreadcrumb(breadcrumbHtml) {
$('p.nav').empty().html(breadcrumbHtml);
}
-//options for file drag/dropp
+var createDragShadow = function(event){
+ //select dragged file
+ var isDragSelected = $(event.target).parents('tr').find('td input:first').prop('checked');
+ if (!isDragSelected) {
+ //select dragged file
+ $(event.target).parents('tr').find('td input:first').prop('checked',true);
+ }
+
+ var selectedFiles = getSelectedFiles();
+
+ if (!isDragSelected && selectedFiles.length == 1) {
+ //revert the selection
+ $(event.target).parents('tr').find('td input:first').prop('checked',false);
+ }
+
+ //also update class when we dragged more than one file
+ if (selectedFiles.length > 1) {
+ $(event.target).parents('tr').addClass('selected');
+ }
+
+ // build dragshadow
+ var dragshadow = $('
';
+ var undeleteAction = $('tr').filterAttr('data-file',filename).children("td.date");
+ undeleteAction[0].innerHTML = undeleteAction[0].innerHTML+spinner;
+ $.post(OC.filePath('files_trashbin','ajax','undelete.php'),
+ {files:tr.attr('data-file'), dirlisting:tr.attr('data-dirlisting') },
+ function(result){
+ for (var i = 0; i < result.data.success.length; i++) {
+ var row = document.getElementById(result.data.success[i].filename);
+ row.parentNode.removeChild(row);
+ }
+ if (result.status != 'success') {
+ OC.dialogs.alert(result.data.message, 'Error');
+ }
+ });
+
+ });
+ };
+
+ // Sets the select_all checkbox behaviour :
+ $('#select_all').click(function() {
+ if($(this).attr('checked')){
+ // Check all
+ $('td.filename input:checkbox').attr('checked', true);
+ $('td.filename input:checkbox').parent().parent().addClass('selected');
+ }else{
+ // Uncheck all
+ $('td.filename input:checkbox').attr('checked', false);
+ $('td.filename input:checkbox').parent().parent().removeClass('selected');
+ }
+ processSelection();
+ });
+
+ $('td.filename input:checkbox').live('change',function(event) {
+ if (event.shiftKey) {
+ var last = $(lastChecked).parent().parent().prevAll().length;
+ var first = $(this).parent().parent().prevAll().length;
+ var start = Math.min(first, last);
+ var end = Math.max(first, last);
+ var rows = $(this).parent().parent().parent().children('tr');
+ for (var i = start; i < end; i++) {
+ $(rows).each(function(index) {
+ if (index == i) {
+ var checkbox = $(this).children().children('input:checkbox');
+ $(checkbox).attr('checked', 'checked');
+ $(checkbox).parent().parent().addClass('selected');
+ }
+ });
+ }
+ }
+ var selectedCount=$('td.filename input:checkbox:checked').length;
+ $(this).parent().parent().toggleClass('selected');
+ if(!$(this).attr('checked')){
+ $('#select_all').attr('checked',false);
+ }else{
+ if(selectedCount==$('td.filename input:checkbox').length){
+ $('#select_all').attr('checked',true);
+ }
+ }
+ processSelection();
+ });
+
+ $('.undelete').click('click',function(event) {
+ var spinner = '
';
+ var files=getSelectedFiles('file');
+ var fileslist=files.join(';');
+ var dirlisting=getSelectedFiles('dirlisting')[0];
+
+ for (var i in files) {
+ var undeleteAction = $('tr').filterAttr('data-file',files[i]).children("td.date");
+ undeleteAction[0].innerHTML = undeleteAction[0].innerHTML+spinner;
+ }
+
+ $.post(OC.filePath('files_trashbin','ajax','undelete.php'),
+ {files:fileslist, dirlisting:dirlisting},
+ function(result){
+ for (var i = 0; i < result.data.success.length; i++) {
+ var row = document.getElementById(result.data.success[i].filename);
+ row.parentNode.removeChild(row);
+ }
+ if (result.status != 'success') {
+ OC.dialogs.alert(result.data.message, 'Error');
+ }
+ });
+ });
+
+
+});
+
+function processSelection(){
+ var selected=getSelectedFiles();
+ var selectedFiles=selected.filter(function(el){return el.type=='file'});
+ var selectedFolders=selected.filter(function(el){return el.type=='dir'});
+ if(selectedFiles.length==0 && selectedFolders.length==0) {
+ $('#headerName>span.name').text(t('files','Name'));
+ $('#modified').text(t('files','Deleted'));
+ $('table').removeClass('multiselect');
+ $('.selectedActions').hide();
+ }
+ else {
+ $('.selectedActions').show();
+ var selection='';
+ if(selectedFolders.length>0){
+ if(selectedFolders.length==1){
+ selection+=t('files','1 folder');
+ }else{
+ selection+=t('files','{count} folders',{count: selectedFolders.length});
+ }
+ if(selectedFiles.length>0){
+ selection+=' & ';
+ }
+ }
+ if(selectedFiles.length>0){
+ if(selectedFiles.length==1){
+ selection+=t('files','1 file');
+ }else{
+ selection+=t('files','{count} files',{count: selectedFiles.length});
+ }
+ }
+ $('#headerName>span.name').text(selection);
+ $('#modified').text('');
+ $('table').addClass('multiselect');
+ }
+}
+
+/**
+ * @brief get a list of selected files
+ * @param string property (option) the property of the file requested
+ * @return array
+ *
+ * possible values for property: name, mime, size and type
+ * if property is set, an array with that property for each file is returnd
+ * if it's ommited an array of objects with all properties is returned
+ */
+function getSelectedFiles(property){
+ var elements=$('td.filename input:checkbox:checked').parent().parent();
+ var files=[];
+ elements.each(function(i,element){
+ var file={
+ name:$(element).attr('data-filename'),
+ file:$(element).attr('data-file'),
+ timestamp:$(element).attr('data-timestamp'),
+ type:$(element).attr('data-type'),
+ dirlisting:$(element).attr('data-dirlisting')
+ };
+ if(property){
+ files.push(file[property]);
+ }else{
+ files.push(file);
+ }
+ });
+ return files;
+}
\ No newline at end of file
diff --git a/apps/files_trashbin/lib/hooks.php b/apps/files_trashbin/lib/hooks.php
new file mode 100644
index 00000000000..d3bee105b51
--- /dev/null
+++ b/apps/files_trashbin/lib/hooks.php
@@ -0,0 +1,45 @@
+.
+ *
+ */
+
+/**
+ * This class contains all hooks.
+ */
+
+namespace OCA_Trash;
+
+class Hooks {
+
+ /**
+ * @brief Copy files to trash bin
+ * @param array
+ *
+ * This function is connected to the delete signal of OC_Filesystem
+ * to copy the file to the trash bin
+ */
+ public static function remove_hook($params) {
+
+ if ( \OCP\App::isEnabled('files_trashbin') ) {
+ $path = $params['path'];
+ Trashbin::move2trash($path);
+ }
+ }
+}
diff --git a/apps/files_trashbin/lib/trash.php b/apps/files_trashbin/lib/trash.php
new file mode 100644
index 00000000000..a7eff3d44e0
--- /dev/null
+++ b/apps/files_trashbin/lib/trash.php
@@ -0,0 +1,264 @@
+.
+ *
+ */
+
+namespace OCA_Trash;
+
+class Trashbin {
+
+ const DEFAULT_RETENTION_OBLIGATION=180; // how long do we keep files in the trash bin if no other value is defined in the config file (unit: days)
+ /**
+ * move file to the trash bin
+ *
+ * @param $file_path path to the deleted file/directory relative to the files root directory
+ */
+ public static function move2trash($file_path) {
+ $user = \OCP\User::getUser();
+ $view = new \OC_FilesystemView('/'. $user);
+ if (!$view->is_dir('files_trashbin')) {
+ $view->mkdir('files_trashbin');
+ $view->mkdir("versions_trashbin");
+ }
+
+ $path_parts = pathinfo($file_path);
+
+ $deleted = $path_parts['basename'];
+ $location = $path_parts['dirname'];
+ $timestamp = time();
+ $mime = $view->getMimeType('files'.$file_path);
+
+ if ( $view->is_dir('files'.$file_path) ) {
+ $type = 'dir';
+ } else {
+ $type = 'file';
+ }
+
+ self::copy_recursive($file_path, 'files_trashbin/'.$deleted.'.d'.$timestamp, $view);
+
+ if ( $view->file_exists('files_trashbin/'.$deleted.'.d'.$timestamp) ) {
+ $query = \OC_DB::prepare("INSERT INTO *PREFIX*files_trash (id,timestamp,location,type,mime,user) VALUES (?,?,?,?,?,?)");
+ $result = $query->execute(array($deleted, $timestamp, $location, $type, $mime, $user));
+ if ( !$result ) { // if file couldn't be added to the database than also don't store it in the trash bin.
+ $view->deleteAll('files_trashbin/'.$deleted.'.d'.$timestamp);
+ \OC_Log::write('files_trashbin', 'trash bin database couldn\'t be updated', \OC_log::ERROR);
+ return;
+ }
+
+ if ( \OCP\App::isEnabled('files_versions') ) {
+ if ( $view->is_dir('files_versions'.$file_path) ) {
+ $view->rename('files_versions'.$file_path, 'versions_trashbin/'. $deleted.'.d'.$timestamp);
+ } else if ( $versions = \OCA_Versions\Storage::getVersions($file_path) ) {
+ foreach ($versions as $v) {
+ $view->rename('files_versions'.$v['path'].'.v'.$v['version'], 'versions_trashbin/'. $deleted.'.v'.$v['version'].'.d'.$timestamp);
+ }
+ }
+ }
+ } else {
+ \OC_Log::write('files_trashbin', 'Couldn\'t move '.$file_path.' to the trash bin' , \OC_log::ERROR);
+ }
+
+ self::expire();
+ }
+
+
+ /**
+ * restore files from trash bin
+ * @param $file path to the deleted file
+ * @param $filename name of the file
+ * @param $timestamp time when the file was deleted
+ */
+ public static function restore($file, $filename, $timestamp) {
+
+ $user = \OCP\User::getUser();
+ $view = new \OC_FilesystemView('/'.$user);
+
+ if ( $timestamp ) {
+ $query = \OC_DB::prepare('SELECT location,type FROM *PREFIX*files_trash WHERE user=? AND id=? AND timestamp=?');
+ $result = $query->execute(array($user,$filename,$timestamp))->fetchAll();
+ if ( count($result) != 1 ) {
+ \OC_Log::write('files_trashbin', 'trash bin database inconsistent!', \OC_Log::ERROR);
+ return false;
+ }
+
+ // if location no longer exists, restore file in the root directory
+ $location = $result[0]['location'];
+ if ( $result[0]['location'] != '/' &&
+ (!$view->is_dir('files'.$result[0]['location']) ||
+ !$view->isUpdatable('files'.$result[0]['location'])) ) {
+ $location = '';
+ }
+ } else {
+ $path_parts = pathinfo($filename);
+ $result[] = array(
+ 'location' => $path_parts['dirname'],
+ 'type' => $view->is_dir('/files_trashbin/'.$file) ? 'dir' : 'files',
+ );
+ $location = '';
+ }
+
+ $source = \OC_Filesystem::normalizePath('files_trashbin/'.$file);
+ $target = \OC_Filesystem::normalizePath('files/'.$location.'/'.$filename);
+
+ // we need a extension in case a file/dir with the same name already exists
+ $ext = self::getUniqueExtension($location, $filename, $view);
+ $mtime = $view->filemtime($source);
+ if( $view->rename($source, $target.$ext) ) {
+ $view->touch($target.$ext, $mtime);
+ // if versioning app is enabled, copy versions from the trash bin back to the original location
+ if ( \OCP\App::isEnabled('files_versions') ) {
+ if ( $result[0]['type'] == 'dir' ) {
+ $view->rename(\OC_Filesystem::normalizePath('versions_trashbin/'. $file), \OC_Filesystem::normalizePath('files_versions/'.$location.'/'.$filename.$ext));
+ } else if ( $versions = self::getVersionsFromTrash($file, $timestamp) ) {
+ foreach ($versions as $v) {
+ if ($timestamp ) {
+ $view->rename('versions_trashbin/'.$filename.'.v'.$v.'.d'.$timestamp, 'files_versions/'.$location.'/'.$filename.$ext.'.v'.$v);
+ } else {
+ $view->rename('versions_trashbin/'.$file.'.v'.$v, 'files_versions/'.$location.'/'.$filename.$ext.'.v'.$v);
+ }
+ }
+ }
+ }
+
+ if ( $timestamp ) {
+ $query = \OC_DB::prepare('DELETE FROM *PREFIX*files_trash WHERE user=? AND id=? AND timestamp=?');
+ $query->execute(array($user,$filename,$timestamp));
+ }
+
+ return true;
+ } else {
+ \OC_Log::write('files_trashbin', 'Couldn\'t restore file from trash bin, '.$filename , \OC_log::ERROR);
+ }
+
+ return false;
+ }
+
+ /**
+ * clean up the trash bin
+ */
+ private static function expire() {
+
+ $view = new \OC_FilesystemView('/'.\OCP\User::getUser());
+ $user = \OCP\User::getUser();
+
+ $query = \OC_DB::prepare('SELECT location,type,id,timestamp FROM *PREFIX*files_trash WHERE user=?');
+ $result = $query->execute(array($user))->fetchAll();
+
+ $retention_obligation = \OC_Config::getValue('trashbin_retention_obligation', self::DEFAULT_RETENTION_OBLIGATION);
+
+ $limit = time() - ($retention_obligation * 86400);
+
+ foreach ( $result as $r ) {
+ $timestamp = $r['timestamp'];
+ $filename = $r['id'];
+ if ( $r['timestamp'] < $limit ) {
+ $view->unlink('files_trashbin/'.$filename.'.d'.$timestamp);
+ if ($r['type'] == 'dir') {
+ $view->unlink('versions_trashbin/'.$filename.'.d'.$timestamp);
+ } else if ( $versions = self::getVersionsFromTrash($filename, $timestamp) ) {
+ foreach ($versions as $v) {
+ $view->unlink('versions_trashbin/'.$filename.'.v'.$v.'.d'.$timestamp);
+ }
+ }
+ }
+ }
+
+ $query = \OC_DB::prepare('DELETE FROM *PREFIX*files_trash WHERE user=? AND timestamp');
+ $query->execute(array($user,$limit));
+ }
+
+ /**
+ * recursive copy to copy a whole directory
+ *
+ * @param $source source path, relative to the users files directory
+ * @param $destination destination path relative to the users root directoy
+ * @param $view file view for the users root directory
+ */
+ private static function copy_recursive( $source, $destination, $view ) {
+ if ( $view->is_dir( 'files'.$source ) ) {
+ $view->mkdir( $destination );
+ $view->touch($destination, $view->filemtime('files'.$source));
+ foreach ( \OC_Files::getDirectoryContent($source) as $i ) {
+ $pathDir = $source.'/'.$i['name'];
+ if ( $view->is_dir('files'.$pathDir) ) {
+ self::copy_recursive($pathDir, $destination.'/'.$i['name'], $view);
+ } else {
+ $view->copy( 'files'.$pathDir, $destination . '/' . $i['name'] );
+ $view->touch($destination . '/' . $i['name'], $view->filemtime('files'.$pathDir));
+ }
+ }
+ } else {
+ $view->copy( 'files'.$source, $destination );
+ $view->touch($destination, $view->filemtime('files'.$source));
+ }
+ }
+
+ /**
+ * find all versions which belong to the file we want to restore
+ * @param $filename name of the file which should be restored
+ * @param $timestamp timestamp when the file was deleted
+ */
+ private static function getVersionsFromTrash($filename, $timestamp) {
+ $view = new \OC_FilesystemView('/'.\OCP\User::getUser().'/versions_trashbin');
+ $versionsName = \OCP\Config::getSystemValue('datadirectory').$view->getAbsolutePath($filename);
+ $versions = array();
+
+ if ($timestamp ) {
+ // fetch for old versions
+ $matches = glob( $versionsName.'.v*.d'.$timestamp );
+ $offset = -strlen($timestamp)-2;
+ } else {
+ $matches = glob( $versionsName.'.v*' );
+ }
+
+ foreach( $matches as $ma ) {
+ if ( $timestamp ) {
+ $parts = explode( '.v', substr($ma, 0, $offset) );
+ $versions[] = ( end( $parts ) );
+ } else {
+ $parts = explode( '.v', $ma );
+ $versions[] = ( end( $parts ) );
+ }
+ }
+ return $versions;
+ }
+
+ /**
+ * find unique extension for restored file if a file with the same name already exists
+ * @param $location where the file should be restored
+ * @param $filename name of the file
+ * @param $view filesystem view relative to users root directory
+ * @return string with unique extension
+ */
+ private static function getUniqueExtension($location, $filename, $view) {
+ $ext = '';
+ if ( $view->file_exists('files'.$location.'/'.$filename) ) {
+ $tmpext = '.restored';
+ $ext = $tmpext;
+ $i = 1;
+ while ( $view->file_exists('files'.$location.'/'.$filename.$ext) ) {
+ $ext = $tmpext.$i;
+ $i++;
+ }
+ }
+ return $ext;
+ }
+
+}
diff --git a/apps/files_trashbin/templates/index.php b/apps/files_trashbin/templates/index.php
new file mode 100644
index 00000000000..c3e51b4becd
--- /dev/null
+++ b/apps/files_trashbin/templates/index.php
@@ -0,0 +1,34 @@
+
+|
+
+ t( 'Name' ); ?>
+
+
+ |
+ + t( 'Deleted' ); ?> + | +
|---|