diff --git a/copyparty/httpcli.py b/copyparty/httpcli.py
index c658edef..e4f3d961 100644
--- a/copyparty/httpcli.py
+++ b/copyparty/httpcli.py
@@ -1560,8 +1560,8 @@ class HttpCli(object):
raise Pebkac(403, "disabled by argv")
# full path of new loc (incl filename)
- dst = self.uparam.get("to")
- if dst is None:
+ dst = self.uparam.get("move")
+ if not dst:
raise Pebkac(400, "need dst vpath")
x = self.conn.hsrv.broker.put(
diff --git a/copyparty/up2k.py b/copyparty/up2k.py
index 98b5646b..737c7274 100644
--- a/copyparty/up2k.py
+++ b/copyparty/up2k.py
@@ -1302,9 +1302,12 @@ class Up2k(object):
# dbv, vrem = dbv.get_dbv(vrem)
_ = dbv.get(vrem, uname, *permsets[0])
with self.mutex:
- ptop = dbv.realpath
- cur, wark = self._find_from_vpath(ptop, vrem)
- self._forget_file(ptop, vpath, cur, wark)
+ try:
+ ptop = dbv.realpath
+ cur, wark = self._find_from_vpath(ptop, vrem)
+ self._forget_file(ptop, vpath, cur, wark)
+ finally:
+ cur.connection.commit()
bos.unlink(abspath)
@@ -1318,7 +1321,7 @@ class Up2k(object):
return "deleted {} files (and {}/{} folders)".format(n_files, n_dirs, len(dirs))
- def _handle_mv(self, uname, svp, dvp):
+ def handle_mv(self, uname, svp, dvp):
svn, srem = self.asrv.vfs.get(svp, uname, True, False, True)
dvn, drem = self.asrv.vfs.get(dvp, uname, False, True)
sabs = svn.canonical(srem)
@@ -1335,15 +1338,16 @@ class Up2k(object):
c2 = self.cur.get(dvn.realpath)
if c1 and c2:
q = "select rd, fn from up where substr(w,1,16)=? and w=?"
- hit = c2.execute(q, (w[:16], w)).fetchone()
- if hit:
- # found in dest vol, just need a symlink
- rd, fn = hit
+ for rd, fn in c2.execute(q, (w[:16], w)):
if rd.startswith("//") or fn.startswith("//"):
rd, fn = s3dec(rd, fn)
- slabs = "{}/{}".join(rd, fn).strip("/")
+ slabs = "{}/{}".format(rd, fn).strip("/")
slabs = absreal(os.path.join(dvn.realpath, slabs))
+ if slabs == sabs:
+ # hit is src
+ continue
+
if bos.path.exists(slabs):
self.log("mv: quick relink, nice")
self._symlink(slabs, dabs)
@@ -1355,6 +1359,8 @@ class Up2k(object):
bos.rename(sabs, slabs)
self._forget_file(svn.realpath, srem, c1, w)
+ c1.connection.commit()
+ c2.connection.commit()
return "k"
# not found in dst db; copy info
@@ -1366,9 +1372,11 @@ class Up2k(object):
if c1:
self._forget_file(svn.realpath, srem, c1, w)
+ c1.connection.commit()
if c2:
self.db_add(c2, w, drd, dfn, st.st_mtime, st.st_size)
+ c2.connection.commit()
bos.rename(sabs, dabs)
return "k"
@@ -1402,14 +1410,12 @@ class Up2k(object):
def _forget_file(self, ptop, vrem, cur, wark):
"""forgets file in db, fixes symlinks, does not delete"""
- fn = vrem.split("/")[-1]
- wark = None
+ srd, sfn = vsplit(vrem)
dupes = []
self.log("forgetting {}".format(vrem))
if wark:
# found in db; find dupes
- wark = wark[0]
self.log("found {} in db".format(wark))
q = "select rd, fn from up where substr(w,1,16)=? and w=?"
@@ -1425,10 +1431,11 @@ class Up2k(object):
if dupes:
# fix symlinks
self._relink(ptop, dupes, vrem, None)
- else:
- # drop tags
+ elif wark:
+ # thus cur; drop tags
q = "delete from mt where w=?"
cur.execute(q, (wark[:16],))
+ self.db_rm(cur, srd, sfn)
reg = self.registry.get(ptop)
if reg:
diff --git a/copyparty/web/browser.js b/copyparty/web/browser.js
index 34055a6c..6d5ca600 100644
--- a/copyparty/web/browser.js
+++ b/copyparty/web/browser.js
@@ -1546,7 +1546,7 @@ var fileman = (function () {
r.paste = function (e) {
ev(e);
if (!r.clip.length)
- return alert('first cut some items to paste\n\nnote: you can cut/paste across different tabs');
+ return alert('first cut some items to paste\n\nnote: you can cut/paste across different browser tabs');
var req = [],
exists = [],
@@ -1574,6 +1574,36 @@ var fileman = (function () {
if (!confirm('paste these ' + req.length + ' items here?\n\n' + req.join('\n')))
return;
+ function paster() {
+ var xhr = new XMLHttpRequest(),
+ vp = req.shift();
+
+ if (!vp) {
+ toast.show('paste OK', 2000);
+ treectl.goto(get_evpath());
+ return;
+ }
+ toast.show('pasting ' + (req.length + 1) + ' items
' + vp, 2000);
+
+ var dst = get_evpath() + vp.split('/').slice(-1)[0];
+
+ xhr.open('GET', vp + '?move=' + dst, true);
+ xhr.onreadystatechange = paste_cb;
+ xhr.send();
+ }
+ function paste_cb() {
+ if (this.readyState != XMLHttpRequest.DONE)
+ return;
+
+ if (this.status !== 200) {
+ var msg = this.responseText;
+ toast.show('paste failed:
' + msg, 2000);
+ return;
+ }
+ paster();
+ }
+ paster();
+
jwrite('fman_clip', []);
r.tx();
};