diff --git a/docs/frontends/webapi.rst b/docs/frontends/webapi.rst
index 5f1060b1a..11cee7229 100644
--- a/docs/frontends/webapi.rst
+++ b/docs/frontends/webapi.rst
@@ -1281,16 +1281,21 @@ Renaming A Child
Moving A Child
----------------
-``POST /uri/$DIRCAP/[SUBDIRS../]?t=rename&from_name=OLD&to_dir=TARGET[&to_name=NEW]``
+``POST /uri/$DIRCAP/[SUBDIRS../]?t=move&from_name=OLD&to_dir=TARGET``
This instructs the node to move a child of the given directory to a
- different directory, both of which must be mutable. The child can also be
- renamed in the process. The to_dir parameter can be either the name of a
- subdirectory of the dircap from which the child is being moved (multiple
- levels of descent are supported) or the writecap of an unrelated directory.
+ different directory, both of which must be mutable. The child can also
+ be renamed in the process. The to_dir parameter should contain the name
+ of a subdirectory of the dircap from which the child is being moved
+ (multiple levels of descent are supported), unless the file is to be
+ moved to an unrelated directory. In the latter case, this can be
+ specified by passing a target_type=uri argument and the target URI in
+ to_dir=. If the target is the name of a subdirectory, this can be
+ signified by passing target_type=name. A new name for the child can
+ also be specified using the to_name= parameter.
- This operation will replace any existing child of the new name, making it
- behave like the UNIX "``mv -f``" command. The original child is not
+ This operation will replace any existing child of the new name, making
+ it behave like the UNIX "``mv -f``" command. The original child is not
unlinked until it is linked into the target directory.
Other Utilities
diff --git a/src/allmydata/test/test_web.py b/src/allmydata/test/test_web.py
index a03d56c0b..baf001784 100644
--- a/src/allmydata/test/test_web.py
+++ b/src/allmydata/test/test_web.py
@@ -3247,7 +3247,6 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
return d
def test_POST_move_file(self):
- """"""
d = self.POST(self.public_url + "/foo", t="move",
from_name="bar.txt", to_dir="sub")
d.addCallback(lambda res:
@@ -3331,6 +3330,16 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
d.addCallback(self.failUnlessIsBazDotTxt)
return d
+ def test_POST_move_file_bad_target_type(self):
+ d = self.POST(self.public_url + "/foo", t="move", target_type="*D",
+ from_name="bar.txt", to_dir="sub")
+ d.addBoth(self.shouldFail, error.Error,
+ "test_POST_rename_file_slash_fail",
+ "400 Bad Request",
+ "invalid target_type parameter",
+ )
+ return d
+
def test_POST_move_file_multi_level(self):
d = self.POST(self.public_url + "/foo/sub/level2?t=mkdir", "")
d.addCallback(lambda res: self.POST(self.public_url + "/foo", t="move",
@@ -3344,7 +3353,7 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
return d
def test_POST_move_file_to_uri(self):
- d = self.POST(self.public_url + "/foo", t="move",
+ d = self.POST(self.public_url + "/foo", t="move", target_type="uri",
from_name="bar.txt", to_dir=self._sub_uri)
d.addCallback(lambda res:
self.failIfNodeHasChild(self._foo_node, u"bar.txt"))
@@ -3379,7 +3388,8 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
return d
def test_POST_move_file_to_bad_uri(self):
- d = self.POST(self.public_url + "/foo", t="move", from_name="bar.txt",
+ d = self.POST(self.public_url + "/foo", t="move",
+ from_name="bar.txt", target_type="uri",
to_dir="URI:DIR2:mn5jlyjnrjeuydyswlzyui72i:rmneifcj6k6sycjljjhj3f6majsq2zqffydnnul5hfa4j577arma")
d.addBoth(self.shouldFail, error.Error,
"POST_move_file_to_bad_uri",
diff --git a/src/allmydata/uri.py b/src/allmydata/uri.py
index dfa6cfa94..ee9c86aaa 100644
--- a/src/allmydata/uri.py
+++ b/src/allmydata/uri.py
@@ -934,13 +934,6 @@ def is_literal_file_uri(s):
s.startswith(ALLEGED_READONLY_PREFIX + 'URI:LIT:') or
s.startswith(ALLEGED_IMMUTABLE_PREFIX + 'URI:LIT:'))
-def is_writeable_directory_uri(s):
- if not isinstance(s, str):
- return False
- return (s.startswith('URI:DIR2:') or
- s.startswith(ALLEGED_READONLY_PREFIX + 'URI:DIR2:') or
- s.startswith(ALLEGED_IMMUTABLE_PREFIX + 'URI:DIR2:'))
-
def has_uri_prefix(s):
if not isinstance(s, str):
return False
diff --git a/src/allmydata/web/directory.py b/src/allmydata/web/directory.py
index d4f88de29..c7f9af2d3 100644
--- a/src/allmydata/web/directory.py
+++ b/src/allmydata/web/directory.py
@@ -13,7 +13,7 @@ from nevow.inevow import IRequest
from foolscap.api import fireEventually
from allmydata.util import base32, time_format
-from allmydata.uri import from_string_dirnode, is_writeable_directory_uri
+from allmydata.uri import from_string_dirnode
from allmydata.interfaces import IDirectoryNode, IFileNode, IFilesystemNode, \
IImmutableFileNode, IMutableFileNode, ExistingChildError, \
NoSuchChildError, EmptyPathnameComponentError, SDMF_VERSION, MDMF_VERSION
@@ -444,23 +444,30 @@ class DirectoryNodeHandler(RenderMixin, rend.Page, ReplaceMeMixin):
if not from_name or not to_dir:
raise WebError("move requires from_name and to_dir")
replace = boolean_of_arg(get_arg(req, "replace", "true"))
+ target_type = get_arg(req, "target_type", "name")
+ if not target_type in ["name", "uri"]:
+ raise WebError("invalid target_type parameter",
+ http.BAD_REQUEST)
# allow from_name to contain slashes, so they can fix names that
# were accidentally created with them. But disallow them in to_name
# (if it's specified), to discourage the practice.
if to_name and "/" in to_name:
- raise WebError("to_name= may not contain a slash", http.BAD_REQUEST)
+ raise WebError("to_name= may not contain a slash",
+ http.BAD_REQUEST)
- d = self.node.has_child(to_dir.split('/')[0])
- def get_target_node(isname):
- if isname or not is_writeable_directory_uri(str(to_dir)):
+ d = defer.Deferred()
+ def get_target_node(target_type):
+ if target_type == "name":
return self.node.get_child_at_path(to_dir)
- else:
+ elif target_type == "uri":
return self.client.create_node_from_uri(str(to_dir))
d.addCallback(get_target_node)
+ d.callback(target_type)
def is_target_node_usable(target_node):
if not IDirectoryNode.providedBy(target_node):
- raise WebError("to_dir is not a usable directory", http.GONE)
+ raise WebError("to_dir is not a usable directory",
+ http.GONE)
return target_node
d.addCallback(is_target_node_usable)
d.addCallback(lambda new_parent: self.node.move_child_to(
diff --git a/src/allmydata/web/move-form.xhtml b/src/allmydata/web/move-form.xhtml
index 51baae7dd..1df427cc1 100644
--- a/src/allmydata/web/move-form.xhtml
+++ b/src/allmydata/web/move-form.xhtml
@@ -17,12 +17,18 @@
Move child:
-
+
to
+
+
+
+