source file: /home/buildslave/tahoe/edgy/build/src/allmydata/web/directory.py
file stats: 496 lines, 475 executed: 95.8% covered
1.
2. import simplejson
3. import urllib
4. import time
5.
6. from twisted.internet import defer
7. from twisted.python.failure import Failure
8. from twisted.web import http, html
9. from nevow import url, rend, tags as T
10. from nevow.inevow import IRequest
11.
12. from foolscap.eventual import fireEventually
13.
14. from allmydata.util import base32
15. from allmydata.uri import from_string_verifier, from_string_dirnode, \
16. CHKFileVerifierURI
17. from allmydata.interfaces import IDirectoryNode, IFileNode, IMutableFileNode, \
18. ExistingChildError
19. from allmydata.web.common import text_plain, WebError, IClient, \
20. boolean_of_arg, get_arg, should_create_intermediate_directories, \
21. getxmlfile, RenderMixin
22. from allmydata.web.filenode import ReplaceMeMixin, \
23. FileNodeHandler, PlaceHolderNodeHandler
24. from allmydata.web.checker_results import CheckerResults, DeepCheckResults
25.
26. class BlockingFileError(Exception):
27. # TODO: catch and transform
28. """We cannot auto-create a parent directory, because there is a file in
29. the way"""
30.
31. def make_handler_for(node, parentnode=None, name=None):
32. if parentnode:
33. assert IDirectoryNode.providedBy(parentnode)
34. if IFileNode.providedBy(node):
35. return FileNodeHandler(node, parentnode, name)
36. if IMutableFileNode.providedBy(node):
37. return FileNodeHandler(node, parentnode, name)
38. if IDirectoryNode.providedBy(node):
39. return DirectoryNodeHandler(node, parentnode, name)
40. raise WebError("Cannot provide handler for '%s'" % node)
41.
42. class DirectoryNodeHandler(RenderMixin, rend.Page, ReplaceMeMixin):
43. addSlash = True
44.
45. def __init__(self, node, parentnode=None, name=None):
46. rend.Page.__init__(self)
47. assert node
48. self.node = node
49. self.parentnode = parentnode
50. self.name = name
51.
52. def childFactory(self, ctx, name):
53. req = IRequest(ctx)
54. name = name.decode("utf-8")
55. d = self.node.get(name)
56. d.addBoth(self.got_child, ctx, name)
57. # got_child returns a handler resource: FileNodeHandler or
58. # DirectoryNodeHandler
59. return d
60.
61. def got_child(self, node_or_failure, ctx, name):
62. DEBUG = False
63. if DEBUG: print "GOT_CHILD", name, node_or_failure
64. req = IRequest(ctx)
65. method = req.method
66. nonterminal = len(req.postpath) > 1
67. t = get_arg(req, "t", "").strip()
68. if isinstance(node_or_failure, Failure):
69. f = node_or_failure
70. f.trap(KeyError)
71. # No child by this name. What should we do about it?
72. if DEBUG: print "no child", name
73. if DEBUG: print "postpath", req.postpath
74. if nonterminal:
75. if DEBUG: print " intermediate"
76. if should_create_intermediate_directories(req):
77. # create intermediate directories
78. if DEBUG: print " making intermediate directory"
79. d = self.node.create_empty_directory(name)
80. d.addCallback(make_handler_for, self.node, name)
81. return d
82. else:
83. if DEBUG: print " terminal"
84. # terminal node
85. if (method,t) in [ ("POST","mkdir"), ("PUT","mkdir") ]:
86. if DEBUG: print " making final directory"
87. # final directory
88. d = self.node.create_empty_directory(name)
89. d.addCallback(make_handler_for, self.node, name)
90. return d
91. if (method,t) in ( ("PUT",""), ("PUT","uri"), ):
92. if DEBUG: print " PUT, making leaf placeholder"
93. # we were trying to find the leaf filenode (to put a new
94. # file in its place), and it didn't exist. That's ok,
95. # since that's the leaf node that we're about to create.
96. # We make a dummy one, which will respond to the PUT
97. # request by replacing itself.
98. return PlaceHolderNodeHandler(self.node, name)
99. if DEBUG: print " 404"
100. # otherwise, we just return a no-such-child error
101. return rend.FourOhFour()
102.
103. node = node_or_failure
104. if nonterminal and should_create_intermediate_directories(req):
105. if not IDirectoryNode.providedBy(node):
106. # we would have put a new directory here, but there was a
107. # file in the way.
108. if DEBUG: print "blocking"
109. raise WebError("Unable to create directory '%s': "
110. "a file was in the way" % name,
111. http.CONFLICT)
112. if DEBUG: print "good child"
113. return make_handler_for(node, self.node, name)
114.
115. def render_DELETE(self, ctx):
116. assert self.parentnode and self.name
117. d = self.parentnode.delete(self.name)
118. d.addCallback(lambda res: self.node.get_uri())
119. return d
120.
121. def render_GET(self, ctx):
122. client = IClient(ctx)
123. req = IRequest(ctx)
124. # This is where all of the directory-related ?t=* code goes.
125. t = get_arg(req, "t", "").strip()
126. if not t:
127. # render the directory as HTML, using the docFactory and Nevow's
128. # whole templating thing.
129. return DirectoryAsHTML(self.node)
130.
131. if t == "json":
132. return DirectoryJSONMetadata(ctx, self.node)
133. if t == "uri":
134. return DirectoryURI(ctx, self.node)
135. if t == "readonly-uri":
136. return DirectoryReadonlyURI(ctx, self.node)
137. if t == "manifest":
138. return Manifest(self.node)
139. if t == "deep-size":
140. return DeepSize(ctx, self.node)
141. if t == "deep-stats":
142. return DeepStats(ctx, self.node)
143. if t == 'rename-form':
144. return RenameForm(self.node)
145.
146. raise WebError("GET directory: bad t=%s" % t)
147.
148. def render_PUT(self, ctx):
149. req = IRequest(ctx)
150. t = get_arg(req, "t", "").strip()
151. replace = boolean_of_arg(get_arg(req, "replace", "true"))
152. if t == "mkdir":
153. # our job was done by the traversal/create-intermediate-directory
154. # process that got us here.
155. return text_plain(self.node.get_uri(), ctx) # TODO: urlencode
156. if t == "uri":
157. if not replace:
158. # they're trying to set_uri and that name is already occupied
159. # (by us).
160. raise ExistingChildError()
161. d = self.parentnode.replace_me_with_a_childcap(ctx, replace)
162. # TODO: results
163. return d
164.
165. raise WebError("PUT to a directory")
166.
167. def render_POST(self, ctx):
168. req = IRequest(ctx)
169. t = get_arg(req, "t", "").strip()
170. if t == "mkdir":
171. d = self._POST_mkdir(req)
172. elif t == "mkdir-p":
173. # TODO: docs, tests
174. d = self._POST_mkdir_p(req)
175. elif t == "upload":
176. d = self._POST_upload(ctx) # this one needs the context
177. elif t == "uri":
178. d = self._POST_uri(req)
179. elif t == "delete":
180. d = self._POST_delete(req)
181. elif t == "rename":
182. d = self._POST_rename(req)
183. elif t == "check":
184. d = self._POST_check(req)
185. elif t == "deep-check":
186. d = self._POST_deep_check(req)
187. elif t == "set_children":
188. # TODO: docs
189. d = self._POST_set_children(req)
190. else:
191. raise WebError("POST to a directory with bad t=%s" % t)
192.
193. when_done = get_arg(req, "when_done", None)
194. if when_done:
195. d.addCallback(lambda res: url.URL.fromString(when_done))
196. return d
197.
198. def _POST_mkdir(self, req):
199. name = get_arg(req, "name", "")
200. if not name:
201. # our job is done, it was handled by the code in got_child
202. # which created the final directory (i.e. us)
203. return defer.succeed(self.node.get_uri()) # TODO: urlencode
204. name = name.decode("utf-8")
205. replace = boolean_of_arg(get_arg(req, "replace", "true"))
206. d = self.node.create_empty_directory(name, overwrite=replace)
207. d.addCallback(lambda child: child.get_uri()) # TODO: urlencode
208. return d
209.
210. def _POST_mkdir_p(self, req):
211. path = get_arg(req, "path")
212. if not path:
213. raise WebError("mkdir-p requires a path")
214. path_ = tuple([seg.decode("utf-8") for seg in path.split('/') if seg ])
215. # TODO: replace
216. d = self._get_or_create_directories(self.node, path_)
217. d.addCallback(lambda node: node.get_uri())
218. return d
219.
220. def _get_or_create_directories(self, node, path):
221. if not IDirectoryNode.providedBy(node):
222. # unfortunately it is too late to provide the name of the
223. # blocking directory in the error message.
224. raise BlockingFileError("cannot create directory because there "
225. "is a file in the way")
226. if not path:
227. return defer.succeed(node)
228. d = node.get(path[0])
229. def _maybe_create(f):
230. f.trap(KeyError)
231. return node.create_empty_directory(path[0])
232. d.addErrback(_maybe_create)
233. d.addCallback(self._get_or_create_directories, path[1:])
234. return d
235.
236. def _POST_upload(self, ctx):
237. req = IRequest(ctx)
238. charset = get_arg(req, "_charset", "utf-8")
239. contents = req.fields["file"]
240. assert contents.filename is None or isinstance(contents.filename, str)
241. name = get_arg(req, "name")
242. name = name or contents.filename
243. if name is not None:
244. name = name.strip()
245. if not name:
246. # this prohibts empty, missing, and all-whitespace filenames
247. raise WebError("upload requires a name")
248. assert isinstance(name, str)
249. name = name.decode(charset)
250. if "/" in name:
251. raise WebError("name= may not contain a slash", http.BAD_REQUEST)
252. assert isinstance(name, unicode)
253.
254. # since POST /uri/path/file?t=upload is equivalent to
255. # POST /uri/path/dir?t=upload&name=foo, just do the same thing that
256. # childFactory would do. Things are cleaner if we only do a subset of
257. # them, though, so we don't do: d = self.childFactory(ctx, name)
258.
259. d = self.node.get(name)
260. def _maybe_got_node(node_or_failure):
261. if isinstance(node_or_failure, Failure):
262. f = node_or_failure
263. f.trap(KeyError)
264. # create a placeholder which will see POST t=upload
265. return PlaceHolderNodeHandler(self.node, name)
266. else:
267. node = node_or_failure
268. return make_handler_for(node, self.node, name)
269. d.addBoth(_maybe_got_node)
270. # now we have a placeholder or a filenodehandler, and we can just
271. # delegate to it. We could return the resource back out of
272. # DirectoryNodeHandler.renderHTTP, and nevow would recurse into it,
273. # but the addCallback() that handles when_done= would break.
274. d.addCallback(lambda child: child.renderHTTP(ctx))
275. return d
276.
277. def _POST_uri(self, req):
278. childcap = get_arg(req, "uri")
279. if not childcap:
280. raise WebError("set-uri requires a uri")
281. name = get_arg(req, "name")
282. if not name:
283. raise WebError("set-uri requires a name")
284. charset = get_arg(req, "_charset", "utf-8")
285. name = name.decode(charset)
286. replace = boolean_of_arg(get_arg(req, "replace", "true"))
287. d = self.node.set_uri(name, childcap, overwrite=replace)
288. d.addCallback(lambda res: childcap)
289. return d
290.
291. def _POST_delete(self, req):
292. name = get_arg(req, "name")
293. if name is None:
294. # apparently an <input type="hidden" name="name" value="">
295. # won't show up in the resulting encoded form.. the 'name'
296. # field is completely missing. So to allow deletion of an
297. # empty file, we have to pretend that None means ''. The only
298. # downide of this is a slightly confusing error message if
299. # someone does a POST without a name= field. For our own HTML
300. # thisn't a big deal, because we create the 'delete' POST
301. # buttons ourselves.
302. name = ''
303. charset = get_arg(req, "_charset", "utf-8")
304. name = name.decode(charset)
305. d = self.node.delete(name)
306. d.addCallback(lambda res: "thing deleted")
307. return d
308.
309. def _POST_rename(self, req):
310. charset = get_arg(req, "_charset", "utf-8")
311. from_name = get_arg(req, "from_name")
312. if from_name is not None:
313. from_name = from_name.strip()
314. from_name = from_name.decode(charset)
315. assert isinstance(from_name, unicode)
316. to_name = get_arg(req, "to_name")
317. if to_name is not None:
318. to_name = to_name.strip()
319. to_name = to_name.decode(charset)
320. assert isinstance(to_name, unicode)
321. if not from_name or not to_name:
322. raise WebError("rename requires from_name and to_name")
323. for k,v in [ ('from_name', from_name), ('to_name', to_name) ]:
324. if v and "/" in v:
325. raise WebError("%s= may not contain a slash" % k,
326. http.BAD_REQUEST)
327.
328. replace = boolean_of_arg(get_arg(req, "replace", "true"))
329. d = self.node.move_child_to(from_name, self.node, to_name, replace)
330. d.addCallback(lambda res: "thing renamed")
331. return d
332.
333. def _POST_check(self, req):
334. # check this directory
335. d = self.node.check()
336. d.addCallback(lambda res: CheckerResults(res))
337. return d
338.
339. def _POST_deep_check(self, req):
340. # check this directory and everything reachable from it
341. verify = boolean_of_arg(get_arg(req, "verify", "false"))
342. repair = boolean_of_arg(get_arg(req, "repair", "false"))
343. d = self.node.deep_check(verify, repair)
344. d.addCallback(lambda res: DeepCheckResults(res))
345. return d
346.
347. def _POST_set_children(self, req):
348. replace = boolean_of_arg(get_arg(req, "replace", "true"))
349. req.content.seek(0)
350. body = req.content.read()
351. try:
352. children = simplejson.loads(body)
353. except ValueError, le:
354. le.args = tuple(le.args + (body,))
355. # TODO test handling of bad JSON
356. raise
357. cs = []
358. for name, (file_or_dir, mddict) in children.iteritems():
359. cap = str(mddict.get('rw_uri') or mddict.get('ro_uri'))
360. cs.append((name, cap, mddict.get('metadata')))
361. d = self.node.set_children(cs, replace)
362. d.addCallback(lambda res: "Okay so I did it.")
363. # TODO: results
364. return d
365.
366. def abbreviated_dirnode(dirnode):
367. u = from_string_dirnode(dirnode.get_uri())
368. si = u.get_filenode_uri().storage_index
369. si_s = base32.b2a(si)
370. return si_s[:6]
371.
372. class DirectoryAsHTML(rend.Page):
373. # The remainder of this class is to render the directory into
374. # human+browser -oriented HTML.
375. docFactory = getxmlfile("directory.xhtml")
376. addSlash = True
377.
378. def __init__(self, node):
379. rend.Page.__init__(self)
380. self.node = node
381.
382. def render_title(self, ctx, data):
383. si_s = abbreviated_dirnode(self.node)
384. header = ["Directory SI=%s" % si_s]
385. return ctx.tag[header]
386.
387. def render_header(self, ctx, data):
388. si_s = abbreviated_dirnode(self.node)
389. header = ["Directory SI=%s" % si_s]
390. if self.node.is_readonly():
391. header.append(" (readonly)")
392. return ctx.tag[header]
393.
394. def get_root(self, ctx):
395. req = IRequest(ctx)
396. # the addSlash=True gives us one extra (empty) segment
397. depth = len(req.prepath) + len(req.postpath) - 1
398. link = "/".join([".."] * depth)
399. return link
400.
401. def render_welcome(self, ctx, data):
402. link = self.get_root(ctx)
403. return T.div[T.a(href=link)["Return to Welcome page"]]
404.
405. def data_children(self, ctx, data):
406. d = self.node.list()
407. d.addCallback(lambda dict: sorted(dict.items()))
408. def _stall_some(items):
409. # Deferreds don't optimize out tail recursion, and the way
410. # Nevow's flattener handles Deferreds doesn't take this into
411. # account. As a result, large lists of Deferreds that fire in the
412. # same turn (i.e. the output of defer.succeed) will cause a stack
413. # overflow. To work around this, we insert a turn break after
414. # every 100 items, using foolscap's fireEventually(). This gives
415. # the stack a chance to be popped. It would also work to put
416. # every item in its own turn, but that'd be a lot more
417. # inefficient. This addresses ticket #237, for which I was never
418. # able to create a failing unit test.
419. output = []
420. for i,item in enumerate(items):
421. if i % 100 == 0:
422. output.append(fireEventually(item))
423. else:
424. output.append(item)
425. return output
426. d.addCallback(_stall_some)
427. return d
428.
429. def render_row(self, ctx, data):
430. name, (target, metadata) = data
431. name = name.encode("utf-8")
432. assert not isinstance(name, unicode)
433.
434. root = self.get_root(ctx)
435. here = "%s/uri/%s/" % (root, urllib.quote(self.node.get_uri()))
436. if self.node.is_readonly():
437. delete = "-"
438. rename = "-"
439. else:
440. # this creates a button which will cause our child__delete method
441. # to be invoked, which deletes the file and then redirects the
442. # browser back to this directory
443. delete = T.form(action=here, method="post")[
444. T.input(type='hidden', name='t', value='delete'),
445. T.input(type='hidden', name='name', value=name),
446. T.input(type='hidden', name='when_done', value="."),
447. T.input(type='submit', value='del', name="del"),
448. ]
449.
450. rename = T.form(action=here, method="get")[
451. T.input(type='hidden', name='t', value='rename-form'),
452. T.input(type='hidden', name='name', value=name),
453. T.input(type='hidden', name='when_done', value="."),
454. T.input(type='submit', value='rename', name="rename"),
455. ]
456.
457. ctx.fillSlots("delete", delete)
458. ctx.fillSlots("rename", rename)
459. if IDirectoryNode.providedBy(target):
460. check_url = "%s/uri/%s/" % (root, urllib.quote(target.get_uri()))
461. check_done_url = "../../uri/%s/" % urllib.quote(self.node.get_uri())
462. else:
463. check_url = "%s/uri/%s" % (root, urllib.quote(target.get_uri()))
464. check_done_url = "../uri/%s/" % urllib.quote(self.node.get_uri())
465. check = T.form(action=check_url, method="post")[
466. T.input(type='hidden', name='t', value='check'),
467. T.input(type='hidden', name='return_to', value=check_done_url),
468. T.input(type='submit', value='check', name="check"),
469. ]
470. ctx.fillSlots("overwrite",
471. self.build_overwrite_form(ctx, name, target))
472. ctx.fillSlots("check", check)
473.
474. times = []
475. TIME_FORMAT = "%H:%M:%S %d-%b-%Y"
476. if "ctime" in metadata:
477. ctime = time.strftime(TIME_FORMAT,
478. time.localtime(metadata["ctime"]))
479. times.append("c: " + ctime)
480. if "mtime" in metadata:
481. mtime = time.strftime(TIME_FORMAT,
482. time.localtime(metadata["mtime"]))
483. if times:
484. times.append(T.br())
485. times.append("m: " + mtime)
486. ctx.fillSlots("times", times)
487.
488. assert (IFileNode.providedBy(target)
489. or IDirectoryNode.providedBy(target)
490. or IMutableFileNode.providedBy(target)), target
491.
492. quoted_uri = urllib.quote(target.get_uri())
493.
494. if IMutableFileNode.providedBy(target):
495. # to prevent javascript in displayed .html files from stealing a
496. # secret directory URI from the URL, send the browser to a URI-based
497. # page that doesn't know about the directory at all
498. dlurl = "%s/file/%s/@@named=/%s" % (root, quoted_uri, urllib.quote(name))
499.
500. ctx.fillSlots("filename",
501. T.a(href=dlurl)[html.escape(name)])
502. ctx.fillSlots("type", "SSK")
503.
504. ctx.fillSlots("size", "?")
505.
506. text_plain_url = "%s/file/%s/@@named=/foo.txt" % (root, quoted_uri)
507. text_plain_tag = T.a(href=text_plain_url)["text/plain"]
508.
509. elif IFileNode.providedBy(target):
510. dlurl = "%s/file/%s/@@named=/%s" % (root, quoted_uri, urllib.quote(name))
511.
512. ctx.fillSlots("filename",
513. T.a(href=dlurl)[html.escape(name)])
514. ctx.fillSlots("type", "FILE")
515.
516. ctx.fillSlots("size", target.get_size())
517.
518. text_plain_url = "%s/file/%s/@@named=/foo.txt" % (root, quoted_uri)
519. text_plain_tag = T.a(href=text_plain_url)["text/plain"]
520.
521.
522. elif IDirectoryNode.providedBy(target):
523. # directory
524. uri_link = "%s/uri/%s/" % (root, urllib.quote(target.get_uri()))
525. ctx.fillSlots("filename",
526. T.a(href=uri_link)[html.escape(name)])
527. if target.is_readonly():
528. dirtype = "DIR-RO"
529. else:
530. dirtype = "DIR"
531. ctx.fillSlots("type", dirtype)
532. ctx.fillSlots("size", "-")
533. text_plain_tag = None
534.
535. childdata = [T.a(href="%s?t=json" % name)["JSON"], ", ",
536. T.a(href="%s?t=uri" % name)["URI"], ", ",
537. T.a(href="%s?t=readonly-uri" % name)["readonly-URI"],
538. ]
539. if text_plain_tag:
540. childdata.extend([", ", text_plain_tag])
541.
542. ctx.fillSlots("data", childdata)
543.
544. results = "--"
545. # TODO: include a link to see more results, including timestamps
546. # TODO: use a sparkline
547. ctx.fillSlots("checker_results", results)
548.
549. return ctx.tag
550.
551. def render_forms(self, ctx, data):
552. forms = []
553. deep_check = T.form(action=".", method="post",
554. enctype="multipart/form-data")[
555. T.fieldset[
556. T.input(type="hidden", name="t", value="deep-check"),
557. T.input(type="hidden", name="return_to", value="."),
558. T.legend(class_="freeform-form-label")["Run a deep-check operation (EXPENSIVE)"],
559. T.input(type="submit", value="Deep-Check"),
560. " ",
561. "Verify every bit? (EVEN MORE EXPENSIVE):",
562. T.input(type="checkbox", name="verify"),
563. ]]
564. forms.append(T.div(class_="freeform-form")[deep_check])
565.
566. if self.node.is_readonly():
567. forms.append(T.div["No upload forms: directory is read-only"])
568. return forms
569.
570. mkdir = T.form(action=".", method="post",
571. enctype="multipart/form-data")[
572. T.fieldset[
573. T.input(type="hidden", name="t", value="mkdir"),
574. T.input(type="hidden", name="when_done", value="."),
575. T.legend(class_="freeform-form-label")["Create a new directory"],
576. "New directory name: ",
577. T.input(type="text", name="name"), " ",
578. T.input(type="submit", value="Create"),
579. ]]
580. forms.append(T.div(class_="freeform-form")[mkdir])
581.
582. upload = T.form(action=".", method="post",
583. enctype="multipart/form-data")[
584. T.fieldset[
585. T.input(type="hidden", name="t", value="upload"),
586. T.input(type="hidden", name="when_done", value="."),
587. T.legend(class_="freeform-form-label")["Upload a file to this directory"],
588. "Choose a file to upload: ",
589. T.input(type="file", name="file", class_="freeform-input-file"),
590. " ",
591. T.input(type="submit", value="Upload"),
592. " Mutable?:",
593. T.input(type="checkbox", name="mutable"),
594. ]]
595. forms.append(T.div(class_="freeform-form")[upload])
596.
597. mount = T.form(action=".", method="post",
598. enctype="multipart/form-data")[
599. T.fieldset[
600. T.input(type="hidden", name="t", value="uri"),
601. T.input(type="hidden", name="when_done", value="."),
602. T.legend(class_="freeform-form-label")["Attach a file or directory"
603. " (by URI) to this"
604. " directory"],
605. "New child name: ",
606. T.input(type="text", name="name"), " ",
607. "URI of new child: ",
608. T.input(type="text", name="uri"), " ",
609. T.input(type="submit", value="Attach"),
610. ]]
611. forms.append(T.div(class_="freeform-form")[mount])
612. return forms
613.
614. def build_overwrite_form(self, ctx, name, target):
615. if IMutableFileNode.providedBy(target) and not target.is_readonly():
616. root = self.get_root(ctx)
617. action = "%s/uri/%s" % (root, urllib.quote(target.get_uri()))
618. done_url = "../uri/%s/" % urllib.quote(self.node.get_uri())
619. overwrite = T.form(action=action, method="post",
620. enctype="multipart/form-data")[
621. T.fieldset[
622. T.input(type="hidden", name="t", value="upload"),
623. T.input(type='hidden', name='when_done', value=done_url),
624. T.legend(class_="freeform-form-label")["Overwrite"],
625. "Choose new file: ",
626. T.input(type="file", name="file", class_="freeform-input-file"),
627. " ",
628. T.input(type="submit", value="Overwrite")
629. ]]
630. return [T.div(class_="freeform-form")[overwrite],]
631. else:
632. return []
633.
634. def render_results(self, ctx, data):
635. req = IRequest(ctx)
636. return get_arg(req, "results", "")
637.
638.
639. def DirectoryJSONMetadata(ctx, dirnode):
640. d = dirnode.list()
641. def _got(children):
642. kids = {}
643. for name, (childnode, metadata) in children.iteritems():
644. if childnode.is_readonly():
645. rw_uri = None
646. ro_uri = childnode.get_uri()
647. else:
648. rw_uri = childnode.get_uri()
649. ro_uri = childnode.get_readonly_uri()
650. if IFileNode.providedBy(childnode):
651. kiddata = ("filenode", {'size': childnode.get_size(),
652. 'metadata': metadata,
653. })
654. else:
655. assert IDirectoryNode.providedBy(childnode), (childnode,
656. children,)
657. kiddata = ("dirnode", {'metadata': metadata})
658. if ro_uri:
659. kiddata[1]["ro_uri"] = ro_uri
660. if rw_uri:
661. kiddata[1]["rw_uri"] = rw_uri
662. kiddata[1]['mutable'] = childnode.is_mutable()
663. kids[name] = kiddata
664. if dirnode.is_readonly():
665. drw_uri = None
666. dro_uri = dirnode.get_uri()
667. else:
668. drw_uri = dirnode.get_uri()
669. dro_uri = dirnode.get_readonly_uri()
670. contents = { 'children': kids }
671. if dro_uri:
672. contents['ro_uri'] = dro_uri
673. if drw_uri:
674. contents['rw_uri'] = drw_uri
675. contents['mutable'] = dirnode.is_mutable()
676. data = ("dirnode", contents)
677. return simplejson.dumps(data, indent=1)
678. d.addCallback(_got)
679. d.addCallback(text_plain, ctx)
680. return d
681.
682. def DirectoryURI(ctx, dirnode):
683. return text_plain(dirnode.get_uri(), ctx)
684.
685. def DirectoryReadonlyURI(ctx, dirnode):
686. return text_plain(dirnode.get_readonly_uri(), ctx)
687.
688. class RenameForm(rend.Page):
689. addSlash = True
690. docFactory = getxmlfile("rename-form.xhtml")
691.
692. def render_title(self, ctx, data):
693. return ctx.tag["Directory SI=%s" % abbreviated_dirnode(self.original)]
694.
695. def render_header(self, ctx, data):
696. header = ["Rename "
697. "in directory SI=%s" % abbreviated_dirnode(self.original),
698. ]
699.
700. if self.original.is_readonly():
701. header.append(" (readonly!)")
702. header.append(":")
703. return ctx.tag[header]
704.
705. def render_when_done(self, ctx, data):
706. return T.input(type="hidden", name="when_done", value=".")
707.
708. def render_get_name(self, ctx, data):
709. req = IRequest(ctx)
710. name = get_arg(req, "name", "")
711. ctx.tag.attributes['value'] = name
712. return ctx.tag
713.
714.
715. class Manifest(rend.Page):
716. docFactory = getxmlfile("manifest.xhtml")
717.
718. def render_title(self, ctx):
719. return T.title["Manifest of SI=%s" % abbreviated_dirnode(self.original)]
720.
721. def render_header(self, ctx):
722. return T.p["Manifest of SI=%s" % abbreviated_dirnode(self.original)]
723.
724. def data_items(self, ctx, data):
725. return self.original.build_manifest()
726.
727. def render_row(self, ctx, refresh_cap):
728. ctx.fillSlots("refresh_capability", refresh_cap)
729. return ctx.tag
730.
731. def DeepSize(ctx, dirnode):
732. d = dirnode.build_manifest()
733. def _measure_size(manifest):
734. total = 0
735. for verifiercap in manifest:
736. u = from_string_verifier(verifiercap)
737. if isinstance(u, CHKFileVerifierURI):
738. total += u.size
739. return str(total)
740. d.addCallback(_measure_size)
741. d.addCallback(text_plain, ctx)
742. return d
743.
744. def DeepStats(ctx, dirnode):
745. d = dirnode.deep_stats()
746. d.addCallback(simplejson.dumps, indent=1)
747. d.addCallback(text_plain, ctx)
748. return d