Changeset b29d092 in trunk
- Timestamp:
- 2012-05-09T20:07:13Z (13 years ago)
- Branches:
- master
- Children:
- e58a012
- Parents:
- 8aa690b
- git-author:
- Marcus Wanner <marcus@…> (2011-11-10 08:00:11)
- git-committer:
- Brian Warner <warner@…> (2012-05-09 20:07:13)
- Files:
-
- 1 added
- 5 edited
Legend:
- Unmodified
- Added
- Removed
-
TabularUnified docs/frontends/webapi.rst ¶
r8aa690b rb29d092 30 30 7. `Unlinking A Child`_ 31 31 8. `Renaming A Child`_ 32 9. `Other Utilities`_ 33 10. `Debugging and Testing Features`_ 32 9. `Moving A Child`_ 33 10. `Other Utilities`_ 34 11. `Debugging and Testing Features`_ 34 35 35 36 7. `Other Useful Pages`_ … … 1278 1279 behave like the UNIX "``mv -f``" command. 1279 1280 1281 Moving A Child 1282 ---------------- 1283 1284 ``POST /uri/$DIRCAP/[SUBDIRS../]?t=rename&from_name=OLD&to_dir=TARGET[&to_name=NEW]`` 1285 1286 This instructs the node to move a child of the given directory to a 1287 different directory, both of which must be mutable. The child can also be 1288 renamed in the process. The to_dir parameter can be either the name of a 1289 subdirectory of the dircap from which the child is being moved (multiple 1290 levels of descent are supported) or the writecap of an unrelated directory. 1291 1292 This operation will replace any existing child of the new name, making it 1293 behave like the UNIX "``mv -f``" command. The original child is not 1294 unlinked until it is linked into the target directory. 1295 1280 1296 Other Utilities 1281 1297 --------------- … … 1299 1315 'from_name' field of that form. I.e. this presents a form offering to 1300 1316 rename $CHILDNAME, requesting the new name, and submitting POST rename. 1317 This same URL format can also be used with "move-form" with the expected 1318 results. 1301 1319 1302 1320 ``GET /uri/$DIRCAP/[SUBDIRS../]CHILDNAME?t=uri`` -
TabularUnified src/allmydata/test/test_web.py ¶
r8aa690b rb29d092 246 246 foo.set_uri(u"sub", sub_uri, sub_uri) 247 247 sub = self.s.create_node_from_uri(sub_uri) 248 self._sub_node = sub 248 249 249 250 _ign, n, blocking_uri = self.makefile(1) … … 255 256 foo.set_uri(unicode_filename, self._bar_txt_uri, self._bar_txt_uri) 256 257 257 _ign, n, baz_file = self.makefile(2)258 self.SUBBAZ_CONTENTS, n, baz_file = self.makefile(2) 258 259 self._baz_file_uri = baz_file 259 260 sub.set_uri(u"baz.txt", baz_file, baz_file) … … 309 310 def failUnlessIsBazDotTxt(self, res): 310 311 self.failUnlessReallyEqual(res, self.BAZ_CONTENTS, res) 312 313 def failUnlessIsSubBazDotTxt(self, res): 314 self.failUnlessReallyEqual(res, self.SUBBAZ_CONTENTS, res) 311 315 312 316 def failUnlessIsBarJSON(self, res): … … 1259 1263 ]) 1260 1264 self.failUnless(re.search(get_bar, res), res) 1261 for label in ['unlink', 'rename' ]:1265 for label in ['unlink', 'rename', 'move']: 1262 1266 for line in res.split("\n"): 1263 1267 # find the line that contains the relevant button for bar.txt … … 3243 3247 return d 3244 3248 3249 def test_POST_move_file(self): 3250 """""" 3251 d = self.POST(self.public_url + "/foo", t="move", 3252 from_name="bar.txt", to_dir="sub") 3253 d.addCallback(lambda res: 3254 self.failIfNodeHasChild(self._foo_node, u"bar.txt")) 3255 d.addCallback(lambda res: 3256 self.failUnlessNodeHasChild(self._sub_node, u"bar.txt")) 3257 d.addCallback(lambda res: self.GET(self.public_url + "/foo/sub/bar.txt")) 3258 d.addCallback(self.failUnlessIsBarDotTxt) 3259 d.addCallback(lambda res: self.GET(self.public_url + "/foo/sub/bar.txt?t=json")) 3260 d.addCallback(self.failUnlessIsBarJSON) 3261 return d 3262 3263 def test_POST_move_file_new_name(self): 3264 d = self.POST(self.public_url + "/foo", t="move", 3265 from_name="bar.txt", to_name="wibble.txt", to_dir="sub") 3266 d.addCallback(lambda res: 3267 self.failIfNodeHasChild(self._foo_node, u"bar.txt")) 3268 d.addCallback(lambda res: 3269 self.failIfNodeHasChild(self._sub_node, u"bar.txt")) 3270 d.addCallback(lambda res: 3271 self.failUnlessNodeHasChild(self._sub_node, u"wibble.txt")) 3272 d.addCallback(lambda res: self.GET(self.public_url + "/foo/sub/wibble.txt")) 3273 d.addCallback(self.failUnlessIsBarDotTxt) 3274 d.addCallback(lambda res: self.GET(self.public_url + "/foo/sub/wibble.txt?t=json")) 3275 d.addCallback(self.failUnlessIsBarJSON) 3276 return d 3277 3278 def test_POST_move_file_replace(self): 3279 d = self.POST(self.public_url + "/foo", t="move", 3280 from_name="bar.txt", to_name="baz.txt", to_dir="sub") 3281 d.addCallback(lambda res: 3282 self.failIfNodeHasChild(self._foo_node, u"bar.txt")) 3283 d.addCallback(lambda res: self.GET(self.public_url + "/foo/sub/baz.txt")) 3284 d.addCallback(self.failUnlessIsBarDotTxt) 3285 d.addCallback(lambda res: self.GET(self.public_url + "/foo/sub/baz.txt?t=json")) 3286 d.addCallback(self.failUnlessIsBarJSON) 3287 return d 3288 3289 def test_POST_move_file_no_replace(self): 3290 d = self.POST(self.public_url + "/foo", t="move", replace="false", 3291 from_name="bar.txt", to_name="baz.txt", to_dir="sub") 3292 d.addBoth(self.shouldFail, error.Error, 3293 "POST_move_file_no_replace", 3294 "409 Conflict", 3295 "There was already a child by that name, and you asked me " 3296 "to not replace it") 3297 d.addCallback(lambda res: self.GET(self.public_url + "/foo/bar.txt")) 3298 d.addCallback(self.failUnlessIsBarDotTxt) 3299 d.addCallback(lambda res: self.GET(self.public_url + "/foo/bar.txt?t=json")) 3300 d.addCallback(self.failUnlessIsBarJSON) 3301 d.addCallback(lambda res: self.GET(self.public_url + "/foo/sub/baz.txt")) 3302 d.addCallback(self.failUnlessIsSubBazDotTxt) 3303 return d 3304 3305 def test_POST_move_file_slash_fail(self): 3306 d = self.POST(self.public_url + "/foo", t="move", 3307 from_name="bar.txt", to_name="slash/fail.txt", to_dir="sub") 3308 d.addBoth(self.shouldFail, error.Error, 3309 "test_POST_rename_file_slash_fail", 3310 "400 Bad Request", 3311 "to_name= may not contain a slash", 3312 ) 3313 d.addCallback(lambda res: 3314 self.failUnlessNodeHasChild(self._foo_node, u"bar.txt")) 3315 d.addCallback(lambda res: 3316 self.failIfNodeHasChild(self._sub_node, u"slash/fail.txt")) 3317 return d 3318 3319 def test_POST_move_file_no_target(self): 3320 d = self.POST(self.public_url + "/foo", t="move", 3321 from_name="bar.txt", to_name="baz.txt") 3322 d.addBoth(self.shouldFail, error.Error, 3323 "POST_move_file_no_target", 3324 "400 Bad Request", 3325 "move requires from_name and to_dir") 3326 d.addCallback(lambda res: self.GET(self.public_url + "/foo/bar.txt")) 3327 d.addCallback(self.failUnlessIsBarDotTxt) 3328 d.addCallback(lambda res: self.GET(self.public_url + "/foo/bar.txt?t=json")) 3329 d.addCallback(self.failUnlessIsBarJSON) 3330 d.addCallback(lambda res: self.GET(self.public_url + "/foo/baz.txt")) 3331 d.addCallback(self.failUnlessIsBazDotTxt) 3332 return d 3333 3334 def test_POST_move_file_multi_level(self): 3335 d = self.POST(self.public_url + "/foo/sub/level2?t=mkdir", "") 3336 d.addCallback(lambda res: self.POST(self.public_url + "/foo", t="move", 3337 from_name="bar.txt", to_dir="sub/level2")) 3338 d.addCallback(lambda res: self.failIfNodeHasChild(self._foo_node, u"bar.txt")) 3339 d.addCallback(lambda res: self.failIfNodeHasChild(self._sub_node, u"bar.txt")) 3340 d.addCallback(lambda res: self.GET(self.public_url + "/foo/sub/level2/bar.txt")) 3341 d.addCallback(self.failUnlessIsBarDotTxt) 3342 d.addCallback(lambda res: self.GET(self.public_url + "/foo/sub/level2/bar.txt?t=json")) 3343 d.addCallback(self.failUnlessIsBarJSON) 3344 return d 3345 3346 def test_POST_move_file_to_uri(self): 3347 d = self.POST(self.public_url + "/foo", t="move", 3348 from_name="bar.txt", to_dir=self._sub_uri) 3349 d.addCallback(lambda res: 3350 self.failIfNodeHasChild(self._foo_node, u"bar.txt")) 3351 d.addCallback(lambda res: self.GET(self.public_url + "/foo/sub/bar.txt")) 3352 d.addCallback(self.failUnlessIsBarDotTxt) 3353 d.addCallback(lambda res: self.GET(self.public_url + "/foo/sub/bar.txt?t=json")) 3354 d.addCallback(self.failUnlessIsBarJSON) 3355 return d 3356 3357 def test_POST_move_file_to_nonexist_dir(self): 3358 d = self.POST(self.public_url + "/foo", t="move", 3359 from_name="bar.txt", to_dir="notchucktesta") 3360 d.addBoth(self.shouldFail, error.Error, 3361 "POST_move_file_to_nonexist_dir", 3362 "404 Not Found", 3363 "No such child: notchucktesta") 3364 return d 3365 3366 def test_POST_move_file_into_file(self): 3367 d = self.POST(self.public_url + "/foo", t="move", 3368 from_name="bar.txt", to_dir="baz.txt") 3369 d.addBoth(self.shouldFail, error.Error, 3370 "POST_move_file_into_file", 3371 "410 Gone", 3372 "to_dir is not a usable directory") 3373 d.addCallback(lambda res: self.GET(self.public_url + "/foo/baz.txt")) 3374 d.addCallback(self.failUnlessIsBazDotTxt) 3375 d.addCallback(lambda res: self.GET(self.public_url + "/foo/bar.txt")) 3376 d.addCallback(self.failUnlessIsBarDotTxt) 3377 d.addCallback(lambda res: self.GET(self.public_url + "/foo/bar.txt?t=json")) 3378 d.addCallback(self.failUnlessIsBarJSON) 3379 return d 3380 3381 def test_POST_move_file_to_bad_uri(self): 3382 d = self.POST(self.public_url + "/foo", t="move", from_name="bar.txt", 3383 to_dir="URI:DIR2:mn5jlyjnrjeuydyswlzyui72i:rmneifcj6k6sycjljjhj3f6majsq2zqffydnnul5hfa4j577arma") 3384 d.addBoth(self.shouldFail, error.Error, 3385 "POST_move_file_to_bad_uri", 3386 "410 Gone", 3387 "to_dir is not a usable directory") 3388 d.addCallback(lambda res: self.GET(self.public_url + "/foo/bar.txt")) 3389 d.addCallback(self.failUnlessIsBarDotTxt) 3390 d.addCallback(lambda res: self.GET(self.public_url + "/foo/bar.txt?t=json")) 3391 d.addCallback(self.failUnlessIsBarJSON) 3392 return d 3393 3245 3394 def shouldRedirect(self, res, target=None, statuscode=None, which=""): 3246 3395 """ If target is not None then the redirection has to go to target. If … … 3297 3446 self.failUnless(re.search(r'name="from_name" value="bar\.txt"', res)) 3298 3447 self.failUnlessIn(FAVICON_MARKUP, res) 3448 d.addCallback(_check) 3449 return d 3450 3451 def test_GET_move_form(self): 3452 d = self.GET(self.public_url + "/foo?t=move-form&name=bar.txt", 3453 followRedirect=True) 3454 def _check(res): 3455 self.failUnless('name="when_done" value="."' in res, res) 3456 self.failUnless(re.search(r'name="from_name" value="bar\.txt"', res)) 3299 3457 d.addCallback(_check) 3300 3458 return d -
TabularUnified src/allmydata/uri.py ¶
r8aa690b rb29d092 935 935 s.startswith(ALLEGED_IMMUTABLE_PREFIX + 'URI:LIT:')) 936 936 937 def is_writeable_directory_uri(s): 938 if not isinstance(s, str): 939 return False 940 return (s.startswith('URI:DIR2:') or 941 s.startswith(ALLEGED_READONLY_PREFIX + 'URI:DIR2:') or 942 s.startswith(ALLEGED_IMMUTABLE_PREFIX + 'URI:DIR2:')) 943 937 944 def has_uri_prefix(s): 938 945 if not isinstance(s, str): -
TabularUnified src/allmydata/web/directory.py ¶
r8aa690b rb29d092 14 14 15 15 from allmydata.util import base32, time_format 16 from allmydata.uri import from_string_dirnode 16 from allmydata.uri import from_string_dirnode, is_writeable_directory_uri 17 17 from allmydata.interfaces import IDirectoryNode, IFileNode, IFilesystemNode, \ 18 18 IImmutableFileNode, IMutableFileNode, ExistingChildError, \ … … 170 170 if t == 'rename-form': 171 171 return RenameForm(self.node) 172 if t == 'move-form': 173 return MoveForm(self.node) 172 174 173 175 raise WebError("GET directory: bad t=%s" % t) … … 214 216 elif t == "rename": 215 217 d = self._POST_rename(req) 218 elif t == "move": 219 d = self._POST_move(req) 216 220 elif t == "check": 217 221 d = self._POST_check(req) … … 417 421 d = self.node.move_child_to(from_name, self.node, to_name, replace) 418 422 d.addCallback(lambda res: "thing renamed") 423 return d 424 425 def _POST_move(self, req): 426 charset = get_arg(req, "_charset", "utf-8") 427 from_name = get_arg(req, "from_name") 428 if from_name is not None: 429 from_name = from_name.strip() 430 from_name = from_name.decode(charset) 431 assert isinstance(from_name, unicode) 432 to_name = get_arg(req, "to_name") 433 if to_name is not None: 434 to_name = to_name.strip() 435 to_name = to_name.decode(charset) 436 assert isinstance(to_name, unicode) 437 if not to_name: 438 to_name = from_name 439 to_dir = get_arg(req, "to_dir") 440 if to_dir is not None: 441 to_dir = to_dir.strip() 442 to_dir = to_dir.decode(charset) 443 assert isinstance(to_dir, unicode) 444 if not from_name or not to_dir: 445 raise WebError("move requires from_name and to_dir") 446 replace = boolean_of_arg(get_arg(req, "replace", "true")) 447 448 # allow from_name to contain slashes, so they can fix names that 449 # were accidentally created with them. But disallow them in to_name 450 # (if it's specified), to discourage the practice. 451 if to_name and "/" in to_name: 452 raise WebError("to_name= may not contain a slash", http.BAD_REQUEST) 453 454 d = self.node.has_child(to_dir.split('/')[0]) 455 def get_target_node(isname): 456 if isname or not is_writeable_directory_uri(str(to_dir)): 457 return self.node.get_child_at_path(to_dir) 458 else: 459 return self.client.create_node_from_uri(str(to_dir)) 460 d.addCallback(get_target_node) 461 def is_target_node_usable(target_node): 462 if not IDirectoryNode.providedBy(target_node): 463 raise WebError("to_dir is not a usable directory", http.GONE) 464 return target_node 465 d.addCallback(is_target_node_usable) 466 d.addCallback(lambda new_parent: self.node.move_child_to( 467 from_name, new_parent, to_name, replace)) 468 d.addCallback(lambda res: "thing moved") 419 469 return d 420 470 … … 663 713 unlink = "-" 664 714 rename = "-" 715 move = "-" 665 716 else: 666 717 # this creates a button which will cause our _POST_unlink method … … 681 732 ] 682 733 734 move = T.form(action=here, method="get")[ 735 T.input(type='hidden', name='t', value='move-form'), 736 T.input(type='hidden', name='name', value=name), 737 T.input(type='hidden', name='when_done', value="."), 738 T.input(type='submit', value='move', name="move"), 739 ] 740 683 741 ctx.fillSlots("unlink", unlink) 684 742 ctx.fillSlots("rename", rename) 743 ctx.fillSlots("move", move) 685 744 686 745 times = [] … … 928 987 header = ["Rename " 929 988 "in directory SI=%s" % abbreviated_dirnode(self.original), 989 ] 990 991 if self.original.is_readonly(): 992 header.append(" (readonly!)") 993 header.append(":") 994 return ctx.tag[header] 995 996 def render_when_done(self, ctx, data): 997 return T.input(type="hidden", name="when_done", value=".") 998 999 def render_get_name(self, ctx, data): 1000 req = IRequest(ctx) 1001 name = get_arg(req, "name", "") 1002 ctx.tag.attributes['value'] = name 1003 return ctx.tag 1004 1005 class MoveForm(rend.Page): 1006 addSlash = True 1007 docFactory = getxmlfile("move-form.xhtml") 1008 1009 def render_title(self, ctx, data): 1010 return ctx.tag["Directory SI=%s" % abbreviated_dirnode(self.original)] 1011 1012 def render_header(self, ctx, data): 1013 header = ["Move " 1014 "from directory SI=%s" % abbreviated_dirnode(self.original), 930 1015 ] 931 1016 -
TabularUnified src/allmydata/web/directory.xhtml ¶
r8aa690b rb29d092 34 34 <td><n:slot name="unlink"/></td> 35 35 <td><n:slot name="rename"/></td> 36 <td><n:slot name="move"/></td> 36 37 <td><n:slot name="info"/></td> 37 38 </tr>
Note: See TracChangeset
for help on using the changeset viewer.