first version of the trash bin app

This commit is contained in:
Björn Schießle 2013-01-18 13:11:29 +01:00
parent 8259768732
commit d605228937
13 changed files with 690 additions and 0 deletions

View File

@ -0,0 +1,13 @@
<?php
if(!OC_User::isLoggedIn()) {
exit;
}
$timestamp = isset( $_REQUEST['timestamp'] ) ? $_REQUEST['timestamp'] : '';
$filename = isset( $_REQUEST['filename'] ) ? trim($_REQUEST['filename'], '/\\') : '';
OCA_Trash\Trashbin::restore($filename, $timestamp);
//TODO: return useful data after succsessful restore operation and remove restored files from the list view
OCP\JSON::success(array("data" => array('content'=>'foo', 'id' => 'bar')));

View File

@ -0,0 +1,7 @@
<?php
OC::$CLASSPATH['OCA_Trash\Hooks'] = 'apps/files_trashbin/lib/hooks.php';
OC::$CLASSPATH['OCA_Trash\Trashbin'] = 'apps/files_trashbin/lib/trash.php';
OCP\Util::connectHook('OC_Filesystem', 'delete', "OCA_Trash\Hooks", "remove_hook");

View File

@ -0,0 +1,92 @@
<?xml version="1.0" encoding="ISO-8859-1" ?>
<database>
<name>*dbname*</name>
<create>true</create>
<overwrite>false</overwrite>
<charset>utf8</charset>
<table>
<name>*dbprefix*files_trash</name>
<declaration>
<field>
<name>id</name>
<type>text</type>
<default></default>
<notnull>true</notnull>
<length>50</length>
</field>
<field>
<name>user</name>
<type>text</type>
<default></default>
<notnull>true</notnull>
<length>50</length>
</field>
<field>
<name>timestamp</name>
<type>text</type>
<default></default>
<notnull>true</notnull>
<length>12</length>
</field>
<field>
<name>location</name>
<type>text</type>
<default></default>
<notnull>true</notnull>
<length>200</length>
</field>
<field>
<name>type</name>
<type>text</type>
<default></default>
<notnull>true</notnull>
<length>4</length>
</field>
<field>
<name>mime</name>
<type>text</type>
<default></default>
<notnull>true</notnull>
<length>30</length>
</field>
<index>
<name>id_index</name>
<field>
<name>id</name>
<sorting>ascending</sorting>
</field>
</index>
<index>
<name>timestamp_index</name>
<field>
<name>timestamp</name>
<sorting>ascending</sorting>
</field>
</index>
<index>
<name>user_index</name>
<field>
<name>user</name>
<sorting>ascending</sorting>
</field>
</index>
</declaration>
</table>
</database>

View File

@ -0,0 +1,14 @@
<?xml version="1.0"?>
<info>
<id>files_trashbin</id>
<name>Trash</name>
<description>Trash bin</description>
<licence>AGPL</licence>
<author>Bjoern Schiessle</author>
<shipped>true</shipped>
<require>4.9</require>
<default_enable/>
<types>
<filesystem/>
</types>
</info>

View File

@ -0,0 +1 @@
0.1

View File

@ -0,0 +1,78 @@
body {
background:#ddd;
}
#header {
background:#1d2d44;
box-shadow:0 0 10px rgba(0,0,0,.5), inset 0 -2px 10px #222;
height:2.5em;
left:0;
line-height:2.5em;
position:fixed;
right:0;
top:0;
z-index:100;
padding:.5em;
}
#details {
color:#fff;
}
#header #download {
font-weight:700;
margin-left:2em;
}
#header #download img {
padding-left:.1em;
padding-right:.3em;
vertical-align:text-bottom;
}
#preview {
background:#eee;
border-bottom:1px solid #f8f8f8;
min-height:30em;
padding-top:2em;
text-align:center;
margin:50px auto;
}
#noPreview {
display:none;
padding-top:5em;
}
p.info {
color:#777;
text-align:center;
text-shadow:#fff 0 1px 0;
width:22em;
margin:2em auto;
}
p.info a {
color:#777;
font-weight:700;
}
#imgframe {
height:75%;
padding-bottom:2em;
width:80%;
margin:0 auto;
}
#imgframe img {
max-height:100%;
max-width:100%;
}
table td.filename .name {
display:block;
height:1.5em;
vertical-align:middle;
margin-left:3em;
/*font-weight:bold;*/
}

View File

@ -0,0 +1,52 @@
<?php
/**
* ownCloud - trash bin
*
* @author Bjoern Schiessle
* @copyright 2013 Bjoern Schiessle schiessle@owncloud.com
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
// Check if we are a user
OCP\User::checkLoggedIn();
$filename = $_GET["file"];
$view = new OC_FilesystemView('/'.\OCP\User::getUser().'/files_trashbin');
if(!$view->file_exists($filename)) {
error_log("file does not exist... " . $view->getInternalPath($filename));
header("HTTP/1.0 404 Not Found");
$tmpl = new OCP\Template( '', '404', 'guest' );
$tmpl->assign('file', $filename);
$tmpl->printPage();
exit;
}
$ftype=$view->getMimeType( $filename );
header('Content-Type:'.$ftype);if ( preg_match( "/MSIE/", $_SERVER["HTTP_USER_AGENT"] ) ) {
header( 'Content-Disposition: attachment; filename="' . rawurlencode( basename($filename) ) . '"' );
} else {
header( 'Content-Disposition: attachment; filename*=UTF-8\'\'' . rawurlencode( basename($filename) )
. '; filename="' . rawurlencode( basename($filename) ) . '"' );
}
OCP\Response::disableCaching();
header('Content-Length: '. $view->filesize($filename));
OC_Util::obEnd();
$view->readfile( $filename );

View File

@ -0,0 +1,55 @@
<?php
// Check if we are a user
OCP\User::checkLoggedIn();
OCP\Util::addStyle('files_trashbin', 'trash');
OCP\Util::addScript('files_trashbin', 'trash');
OCP\Util::addScript('files', 'fileactions');
$tmpl = new OCP\Template('files_trashbin', 'index', 'user');
$user = \OCP\User::getUser();
$view = new OC_Filesystemview('/'.$user.'/files_trashbin');
OCP\Util::addStyle('files', 'files');
OCP\Util::addScript('files', 'filelist');
$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;
$files[] = $i;
}
$breadcrumbNav = new OCP\Template('files', 'part.breadcrumb', '');
$breadcrumbNav->assign('breadcrumb', array(array('dir' => '', 'name' => 'Trash')), false);
$breadcrumbNav->assign('baseURL', OCP\Util::linkTo('files_trashbin', 'index.php') . '?dir=', false);
$list = new OCP\Template('files_trashbin', 'part.list', '');
$list->assign('files', $files, false);
$list->assign('baseURL', OCP\Util::linkTo('files_trashbin', 'index.php'). '?dir=', false);
$list->assign('downloadURL', OCP\Util::linkTo('files_trashbin', 'download.php') . '?file=', false);
$list->assign('disableSharing', true);
$list->assign('disableDownloadActions', true);
$tmpl->assign('breadcrumb', $breadcrumbNav->fetchPage(), false);
$tmpl->assign('fileList', $list->fetchPage(), false);
$tmpl->assign('dir', OC_Filesystem::normalizePath($view->getAbsolutePath()));
$tmpl->printPage();

View File

@ -0,0 +1,37 @@
// Override download path to files_sharing/public.php
function fileDownloadPath(dir, file) {
var url = $('#downloadURL').val();
if (url.indexOf('&path=') != -1) {
url += '/'+file;
}
return url;
}
$(document).ready(function() {
if (typeof FileActions !== 'undefined') {
FileActions.register('all', 'Undelete', OC.PERMISSION_READ, '', function(filename) {
var tr=$('tr').filterAttr('data-file', filename);
console.log("tr: " + tr.attr('data-timestamp') + " name: " + name);
$.post(OC.filePath('files_trashbin','ajax','undelete.php'),
{timestamp:tr.attr('data-timestamp'),filename:tr.attr('data-filename')},
function(result){
if (result.status == 'success') {
return;
var date=new Date();
FileList.addFile(name,0,date,false,hidden);
var tr=$('tr').filterAttr('data-file',name);
tr.data('mime','text/plain').data('id',result.data.id);
tr.attr('data-id', result.data.id);
getMimeIcon('text/plain',function(path){
tr.find('td.filename').attr('style','background-image:url('+path+')');
});
} else {
OC.dialogs.alert(result.data.message, 'Error');
}
});
});
};
});

View File

@ -0,0 +1,45 @@
<?php
/**
* ownCloud - trash bin
*
* @author Bjoern Schiessle
* @copyright 2013 Bjoern Schiessle schiessle@owncloud.com
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
/**
* 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);
}
}
}

View File

@ -0,0 +1,190 @@
<?php
/**
* ownCloud - trash bin
*
* @author Bjoern Schiessle
* @copyright 2013 Bjoern Schiessle schiessle@owncloud.com
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA_Trash;
class Trashbin {
/**
* 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);
$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 ( \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);
}
}
}
self::expire();
}
/**
* restore files from trash bin
* @param $filename name of the file
* @param $timestamp time when the file was deleted
*/
public static function restore($filename, $timestamp) {
$user = \OCP\User::getUser();
$view = new \OC_FilesystemView('/'.$user);
$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;
}
$location = $result[0]['location'];
if ( $result[0]['location'] != '/' && !$view->is_dir('files'.$result[0]['location']) ) {
$location = '/';
}
$source = 'files_trashbin/'.$filename.'.d'.$timestamp;
$target = \OC_Filesystem::normalizePath('files/'.$location.'/'.$filename);
$ext = self::getUniqueExtension($location, $filename, $view);
$view->rename($source, $target.$ext);
if ( \OCP\App::isEnabled('files_versions') ) {
if ( $result[0][type] == 'dir' ) {
$view->rename('versions_trashbin/'. $filename.'.d'.$timestamp, 'files_versions/'.$location.'/'.$filename.$ext);
} else if ( $versions = self::getVersionsFromTrash($filename, $timestamp) ) {
foreach ($versions as $v) {
$view->rename('versions_trashbin/'.$filename.'.v'.$v.'.d'.$timestamp, 'files_versions/'.$location.'/'.$filename.$ext.'.v'.$v);
}
}
}
$query = \OC_DB::prepare('DELETE FROM *PREFIX*files_trash WHERE user=? AND id=? AND timestamp=?');
$query->execute(array($user,$filename,$timestamp));
}
/**
* clean up the trash bin
*/
private static function expire() {
//TODO: implement expire function
return true;
}
/**
* 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
* @param $location location of the source files, either "fscache" or "local"
*/
private static function copy_recursive( $source, $destination, $view, $location='fscache' ) {
if ( $view->is_dir( 'files'.$source ) ) {
$view->mkdir( $destination );
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'] );
}
}
} else {
$view->copy( 'files'.$source, $destination );
}
}
/**
* 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();
// fetch for old versions
$matches = glob( $versionsName.'.v*.d'.$timestamp );
foreach( $matches as $ma ) {
$parts = explode( '.v', substr($ma, 0, -strlen($timestamp)-2) );
$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;
}
}

View File

@ -0,0 +1,36 @@
<!--[if IE 8]><style>input[type="checkbox"]{padding:0;}table td{position:static !important;}</style><![endif]-->
<div id="controls">
<?php echo($_['breadcrumb']); ?>
<div id="file_action_panel"></div>
</div>
<div id='notification'></div>
<?php if (isset($_['files']) and $_['isCreatable'] and count($_['files'])==0):?>
<div id="emptyfolder"><?php echo $l->t('Nothing in here. Upload something!')?></div>
<?php endif; ?>
<table>
<thead>
<tr>
<th id='headerName'>
<input type="checkbox" id="select_all" />
<span class='name'><?php echo $l->t( 'Name' ); ?></span>
<span class='selectedActions'>
<?php if($_['allowZipDownload']) : ?>
<a href="" class="download">
<img class="svg" alt="Download"
src="<?php echo OCP\image_path("core", "actions/download.svg"); ?>" />
<?php echo $l->t('Download')?>
</a>
<?php endif; ?>
</span>
</th>
<th id="headerDate">
<span id="modified"><?php echo $l->t( 'Deleted' ); ?></span>
</th>
</tr>
</thead>
<tbody id="fileList">
<?php echo($_['fileList']); ?>
</tbody>
</table>

View File

@ -0,0 +1,70 @@
<script type="text/javascript">
<?php if ( array_key_exists('disableSharing', $_) && $_['disableSharing'] == true ) :?>
var disableSharing = true;
<?php else: ?>
var disableSharing = false;
<?php endif; ?>
<?php if ( array_key_exists('disableDownloadActions', $_) && $_['disableDownloadActions'] == true ) :?>
var disableDownloadActions = true;
<?php else: ?>
var disableDownloadActions = false;
<?php endif; ?>
</script>
<?php foreach($_['files'] as $file):
$simple_file_size = OCP\simple_file_size($file['size']);
// the bigger the file, the darker the shade of grey; megabytes*2
$simple_size_color = intval(200-$file['size']/(1024*1024)*2);
if($simple_size_color<0) $simple_size_color = 0;
$relative_deleted_date = OCP\relative_modified_date($file['mtime']);
// the older the file, the brighter the shade of grey; days*14
$relative_date_color = round((time()-$file['mtime'])/60/60/24*14);
if($relative_date_color>200) $relative_date_color = 200;
$name = str_replace('+', '%20', urlencode($file['name']));
$name = str_replace('%2F', '/', $name);
$directory = str_replace('+', '%20', urlencode($file['directory']));
$directory = str_replace('%2F', '/', $directory); ?>
<tr data-file="<?php echo $file['name'].'.d'.$file['timestamp'];?>"
data-filename="<?php echo $file['name'];?>"
data-type="<?php echo ($file['type'] == 'dir')?'dir':'file'?>"
data-mime="<?php echo $file['mimetype']?>"
data-timestamp='<?php echo $file['timestamp'];?>'
data-permissions='<?php echo $file['permissions']; ?>'>
<td class="filename svg"
<?php if($file['type'] == 'dir'): ?>
style="background-image:url(<?php echo OCP\mimetype_icon('dir'); ?>)"
<?php else: ?>
style="background-image:url(<?php echo OCP\mimetype_icon($file['mimetype']); ?>)"
<?php endif; ?>
>
<?php if(!isset($_['readonly']) || !$_['readonly']): ?><input type="checkbox" /><?php endif; ?>
<?php if($file['type'] == 'dir'): ?>
<a class="name" href="<?php $_['baseURL'].'/'.$name.'.d'.$file['timestamp']; ?>)" title="">
<?php else: ?>
<a class="name" href="<?php echo $_['downloadURL'].'/'.$name.'.d'.$file['timestamp']; ?>" title="">
<?php endif; ?>
<span class="nametext">
<?php if($file['type'] == 'dir'):?>
<?php echo htmlspecialchars($file['name']);?>
<?php else:?>
<?php echo htmlspecialchars($file['basename']);?><span
class='extension'><?php echo $file['extension'];?></span>
<?php endif;?>
</span>
<?php if($file['type'] == 'dir'):?>
<span class="uploadtext" currentUploads="0">
</span>
<?php endif;?>
</a>
</td>
<td class="date">
<span class="modified"
title="<?php echo $file['date']; ?>"
style="color:rgb(<?php echo $relative_date_color.','
.$relative_date_color.','
.$relative_date_color ?>)">
<?php echo $relative_deleted_date; ?>
</span>
</td>
</tr>
<?php endforeach;