Skip to content

Commit

Permalink
[js:core,cmd:rm,VD:trash] fix Studio-42#1065 add "Trash" feature
Browse files Browse the repository at this point in the history
  • Loading branch information
nao-pon committed May 2, 2017
1 parent 5f06c8f commit 1f1e6ea
Show file tree
Hide file tree
Showing 18 changed files with 535 additions and 121 deletions.
10 changes: 10 additions & 0 deletions css/cwd.css
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,16 @@
.elfinder-cwd .elfinder-navbar-root-local.elfinder-droppable-active .elfinder-cwd-icon {
background-position:1px -1px;
}
.elfinder-navbar-root-trash .elfinder-cwd-icon,
.elfinder-cwd .elfinder-navbar-root-trash.elfinder-droppable-active .elfinder-cwd-icon,
.elfinder-cwd-view-list .elfinder-navbar-root-trash td .elfinder-cwd-icon {
background-image:url("../img/volume_icon_trash.png");
background-position:0 0;
background-size: contain;
}
.elfinder-cwd .elfinder-navbar-root-trash.elfinder-droppable-active .elfinder-cwd-icon {
background-position:1px -1px;
}
.elfinder-navbar-root-ftp .elfinder-cwd-icon,
.elfinder-cwd .elfinder-navbar-root-ftp.elfinder-droppable-active .elfinder-cwd-icon,
.elfinder-cwd-view-list .elfinder-navbar-root-ftp td .elfinder-cwd-icon {
Expand Down
1 change: 1 addition & 0 deletions css/navbar.css
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@

/* root icon of each volume */
.elfinder-tree .elfinder-navbar-root-local .elfinder-navbar-icon { background-image:url("../img/volume_icon_local.png"); background-position:0 0; }
.elfinder-tree .elfinder-navbar-root-trash .elfinder-navbar-icon { background-image:url("../img/volume_icon_trash.png"); background-position:0 0; }
.elfinder-tree .elfinder-navbar-root-ftp .elfinder-navbar-icon { background-image:url("../img/volume_icon_ftp.png"); background-position:0 0; }
.elfinder-tree .elfinder-navbar-root-sql .elfinder-navbar-icon { background-image:url("../img/volume_icon_sql.png"); background-position:0 0; }
.elfinder-tree .elfinder-navbar-root-dropbox .elfinder-navbar-icon { background-image:url("../img/volume_icon_dropbox.png"); background-position:0 0; }
Expand Down
Binary file added img/volume_icon_trash.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
318 changes: 255 additions & 63 deletions js/commands/rm.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,51 +4,17 @@
* Delete files
*
* @author Dmitry (dio) Levashov
* @author Naoki Sawada
**/
elFinder.prototype.commands.rm = function() {

this.updateOnSelect = false;
this.shortcuts = [{
pattern : 'delete ctrl+backspace'
}];

this.getstate = function(sel) {
var fm = this.fm;
sel = sel || fm.selected();
return sel.length && $.map(sel, function(h) { var f = fm.file(h); return f && ! f.locked && ! fm.isRoot(f)? h : null }).length == sel.length
? 0 : -1;
}

this.exec = function(hashes) {
var self = this,
fm = this.fm,
dfrd = $.Deferred()
.fail(function(error) {
error && fm.error(error);
}),
files = this.files(hashes),
cnt = files.length,
cwd = fm.cwd().hash,
tpl = '<div class="ui-helper-clearfix elfinder-rm-title"><span class="elfinder-cwd-icon {class} ui-corner-all"/>{title}<div class="elfinder-rm-desc">{desc}</div></div>',
targets, text, f, fname, size, tmb, descs, dialog;

if (! cnt) {
return dfrd.reject();
}

$.each(files, function(i, file) {
if (fm.isRoot(file)) {
return !dfrd.reject(['errRm', file.name, 'errPerm']);
}
if (file.locked) {
return !dfrd.reject(['errLocked', file.name]);
}
});

if (dfrd.state() == 'pending') {
targets = this.hashes(hashes);
cnt = files.length;
descs = [];
var self = this,
tpl = '<div class="ui-helper-clearfix elfinder-rm-title"><span class="elfinder-cwd-icon {class} ui-corner-all"/>{title}<div class="elfinder-rm-desc">{desc}</div></div>',
confirm = function(dfrd, targets, files, tHash, addTexts) {
var fm = self.fm,
cnt = targets.length,
cwd = fm.cwd().hash,
descs = [],
dialog, text, tmb, size, f, fname;

if (cnt > 1) {
if (!$.map(files, function(f) { return f.mime == 'directory' ? 1 : null ; }).length) {
Expand Down Expand Up @@ -76,35 +42,25 @@ elFinder.prototype.commands.rm = function() {
descs.push(fm.i18n('modify')+': '+fm.formatDate(f));
fname = fm.escape(f.i18 || f.name).replace(/([_.])/g, '&#8203;$1');
text = [$(tpl.replace('{class}', fm.mime2class(f.mime)).replace('{title}', '<strong>' + fname + '</strong>').replace('{desc}', descs.join('<br>')))];

}

text.push('confirmRm');
if (addTexts) {
text = text.concat(addTexts);
}

text.push(tHash? 'confirmTrash' : 'confirmRm');

fm.lockfiles({files : targets});
dialog = fm.confirm({
title : self.title,
text : text,
accept : {
label : 'btnRm',
callback : function() {
fm.request({
data : {cmd : 'rm', targets : targets},
notify : {type : 'rm', cnt : cnt},
preventFail : true
})
.fail(function(error) {
dfrd.reject(error);
})
.done(function(data) {
if (data.error || data.warning) {
data.sync = true;
}
dfrd.done(data);
})
.always(function() {
fm.unlockfiles({files : targets});
});
if (tHash) {
toTrash(dfrd, targets, tHash);
} else {
remove(dfrd, targets);
}
}
},
cancel : {
Expand All @@ -126,6 +82,242 @@ elFinder.prototype.commands.rm = function() {
.on('load', function() { dialog.find('.elfinder-cwd-icon').addClass(tmb.className).css('background-image', "url('"+tmb.url+"')"); })
.attr('src', tmb.url);
}
},
toTrash = function(dfrd, targets, tHash) {
var fm = self.fm,
dsts = {},
dirs, cnt;

$.each(targets, function(i, h) {
var path = fm.path(h).replace(/\\/g, '/'),
m = path.match(/^[^\/]+?(\/(?:[^\/]+?\/)*)[^\/]+?$/);

if (m) {
m[1] = m[1].replace(/(^\/.*?)\/?$/, '$1');
if (! dsts[m[1]]) {
dsts[m[1]] = [];
}
dsts[m[1]].push(h);
}
});

dirs = Object.keys(dsts);
cnt = dirs.length;
if (cnt) {
fm.request({
data : {cmd : 'mkdir', target : tHash, dirs : dirs},
notify : {type : 'mkdir', cnt : cnt},
preventFail : true
})
.fail(function(error) {
dfrd.reject(error);
fm.unlockfiles({files : targets});
})
.done(function(data) {
var margeRes = function(data) {
$.each(data, function(k, v) {
if (Array.isArray(v)) {
if (res[k]) {
res[k] = res[k].concat(v);
} else {
res[k] = v;
}
}
});
if (data.sync) {
res.sync = 1;
}
},
err = ['errTrash'],
res = {},
hasNtf = function() {
return fm.ui.notify.children('.elfinder-notify-trash').length;
},
hashes, hasNtf, tm, prg, prgSt;

if (hashes = data.hashes) {
prg = 1 / cnt * 100;
prgSt = 1;
tm = setTimeout(function() {
fm.notify({type : 'trash', cnt : 1, hideCnt : true, progress : prgSt});
}, fm.notifyDelay);
$.each(dsts, function(dir, files) {
if (hashes[dir]) {
fm.request({
data : {cmd : 'paste', dst : hashes[dir], targets : files, cut : 1},
preventDefault : true
})
.fail(function(error) {
if (error) {
err = err.concat(error);
}
})
.done(function(data) {
margeRes(data);
if (data.warning) {
err = err.concat(data.warning);
delete data.warning;
}
// fire some event to update cache/ui
data.removed && data.removed.length && fm.remove(data);
data.added && data.added.length && fm.add(data);
data.changed && data.changed.length && fm.change(data);
// fire event with command name
fm.trigger('paste', data);
// fire event with command name + 'done'
fm.trigger('pastedone');
// force update content
data.sync && fm.sync();
})
.always(function() {
var hashes, addTexts, end = 2;
if (hasNtf()) {
fm.notify({type : 'trash', cnt : 0, hideCnt : true, progress : prg});
} else {
prgSt+= prg;
}
if (--cnt < 1) {
tm && clearTimeout(tm);
hasNtf() && fm.notify({type : 'trash', cnt : -1});
fm.unlockfiles({files : targets});
if (Object.keys(res).length) {
if (err.length > 1) {
if (res.removed || res.removed.length) {
hashes = $.map(targets, function(h) {
return $.inArray(h, res.removed) === -1? h : null;
});
}
if (hashes.length) {
if (err > end) {
end = (fm.messages[err[end-1]] || '').indexOf('$') === -1? end : end + 1;
}
self.exec(hashes, { addTexts: err.slice(0, end) });
} else {
fm.error(err);
}
}
dfrd.done(res);
} else {
dfrd.reject(err);
}
}
});
}
});
} else {
dfrd.reject('errFolderNotFound');
fm.unlockfiles({files : targets});
}
});
} else {
dfrd.reject(['error', 'The folder hierarchy to be deleting can not be determined.']);
fm.unlockfiles({files : targets});
}
},
remove = function(dfrd, targets) {
fm.request({
data : {cmd : 'rm', targets : targets},
notify : {type : 'rm', cnt : targets.length},
preventFail : true
})
.fail(function(error) {
dfrd.reject(error);
})
.done(function(data) {
if (data.error || data.warning) {
data.sync = true;
}
dfrd.done(data);
})
.always(function() {
fm.unlockfiles({files : targets});
});
},
getTHash = function(targets) {
var thash = null,
root1st;

if (targets && targets.length) {
if (targets.length > 1 && fm.searchStatus.state === 2) {
root1st = fm.file(fm.root(targets[0])).volumeid;
if (!$.map(targets, function(h) { return h.indexOf(root1st) !== 0? 1 : null ; }).length) {
thash = fm.option('trashHash', targets[0]);
}
} else {
thash = fm.option('trashHash', targets[0]);
}
}
return thash;
};

this.syncTitleOnChange = true;
this.updateOnSelect = false;
this.shortcuts = [{
pattern : 'delete ctrl+backspace shift+delete'
}];
this.handlers = {
'open' : function() {
self.title = self.fm.i18n(self.fm.option('trashHash')? 'cmdtrash' : 'cmdrm');
self.change();
}
}

this.getstate = function(sel, e) {
if (sel && sel.length) {
this.title = self.fm.i18n(getTHash(sel)? 'cmdtrash' : 'cmdrm');
}
var fm = this.fm;
sel = sel || fm.selected();
return sel.length && $.map(sel, function(h) { var f = fm.file(h); return f && ! f.locked && ! fm.isRoot(f)? h : null }).length == sel.length
? 0 : -1;
}

this.exec = function(hashes, opts) {
var self = this,
fm = this.fm,
dfrd = $.Deferred()
.fail(function(error) {
error && fm.error(error);
}),
files = this.files(hashes),
cnt = files.length,
tHash = null,
addTexts = opts && opts.addTexts? opts.addTexts : null,
targets;

if (! cnt) {
return dfrd.reject();
}

$.each(files, function(i, file) {
if (fm.isRoot(file)) {
return !dfrd.reject(['errRm', file.name, 'errPerm']);
}
if (file.locked) {
return !dfrd.reject(['errLocked', file.name]);
}
});

if (dfrd.state() === 'pending') {
targets = this.hashes(hashes);
cnt = files.length

if (addTexts || (self.event && self.event.originalEvent && self.event.originalEvent.shiftKey)) {
tHash = '';
self.title = fm.i18n('cmdrm');
}

if (tHash === null) {
tHash = getTHash(targets);
}

fm.lockfiles({files : targets});

if (tHash && this.options.quickTrash) {
toTrash(dfrd, targets, tHash);
} else {
confirm(dfrd, targets, files, tHash, addTexts);
}
}

return dfrd;
Expand Down
Loading

0 comments on commit 1f1e6ea

Please sign in to comment.