mirror of
https://github.com/tahoe-lafs/tahoe-lafs.git
synced 2025-01-18 18:56:28 +00:00
web: add 'more info' pages for files and directories, move URI/checker-buttons/deep-size/etc off to them
This commit is contained in:
parent
dde7d67498
commit
99d5a8d8b9
@ -459,6 +459,25 @@ GET /named/$FILECAP/FILENAME
|
||||
this form can *only* be used with file caps; it is an error to use a
|
||||
directory cap after the /named/ prefix.
|
||||
|
||||
=== Get Information About A File Or Directory (as HTML) ===
|
||||
|
||||
GET /uri/$FILECAP?t=info
|
||||
GET /uri/$DIRCAP/?t=info
|
||||
GET /uri/$DIRCAP/[SUBDIRS../]SUBDIR/?t=info
|
||||
GET /uri/$DIRCAP/[SUBDIRS../]FILENAME?t=info
|
||||
|
||||
This returns a human-oriented HTML page with more detail about the selected
|
||||
file or directory object. This page contains the following items:
|
||||
|
||||
object size
|
||||
storage index
|
||||
JSON representation
|
||||
raw contents (text/plain)
|
||||
access caps (URIs): verify-cap, read-cap, write-cap (for mutable objects)
|
||||
check/verify/repair form
|
||||
deep-check/deep-size/deep-stats/manifest (for directories)
|
||||
replace-conents form (for mutable files)
|
||||
|
||||
=== Creating a Directory ===
|
||||
|
||||
POST /uri?t=mkdir
|
||||
|
@ -1986,10 +1986,10 @@ class MutableChecker(SystemTestMixin, unittest.TestCase):
|
||||
|
||||
return d
|
||||
|
||||
class DeepCheck(SystemTestMixin, unittest.TestCase):
|
||||
class DeepCheckWeb(SystemTestMixin, unittest.TestCase):
|
||||
# construct a small directory tree (with one dir, one immutable file, one
|
||||
# mutable file, one LIT file, and a loop), and then check it in various
|
||||
# ways.
|
||||
# mutable file, one LIT file, and a loop), and then check/examine it in
|
||||
# various ways.
|
||||
|
||||
def set_up_tree(self, ignored):
|
||||
# 2.9s
|
||||
@ -2176,19 +2176,23 @@ class DeepCheck(SystemTestMixin, unittest.TestCase):
|
||||
|
||||
def web_json(self, n, **kwargs):
|
||||
kwargs["output"] = "json"
|
||||
return self.web(n, "POST", **kwargs)
|
||||
d = self.web(n, "POST", **kwargs)
|
||||
d.addCallback(self.decode_json)
|
||||
return d
|
||||
|
||||
def decode_json(self, (s,url)):
|
||||
try:
|
||||
data = simplejson.loads(s)
|
||||
except ValueError:
|
||||
self.fail("%s: not JSON: '%s'" % (url, s))
|
||||
return data
|
||||
|
||||
def web(self, n, method="GET", **kwargs):
|
||||
# returns (data, url)
|
||||
url = (self.webish_url + "uri/%s" % urllib.quote(n.get_uri())
|
||||
+ "?" + "&".join(["%s=%s" % (k,v) for (k,v) in kwargs.items()]))
|
||||
d = getPage(url, method=method)
|
||||
def _decode(s):
|
||||
try:
|
||||
data = simplejson.loads(s)
|
||||
except ValueError:
|
||||
self.fail("%s: not JSON: '%s'" % (url, s))
|
||||
return data
|
||||
d.addCallback(_decode)
|
||||
d.addCallback(lambda data: (data,url))
|
||||
return d
|
||||
|
||||
def json_check_is_healthy(self, data, n, where, incomplete=False):
|
||||
@ -2276,6 +2280,7 @@ class DeepCheck(SystemTestMixin, unittest.TestCase):
|
||||
|
||||
# stats
|
||||
d.addCallback(lambda ign: self.web(self.root, t="deep-stats"))
|
||||
d.addCallback(self.decode_json)
|
||||
d.addCallback(self.json_check_stats, "deep-stats")
|
||||
|
||||
# check, no verify
|
||||
@ -2344,4 +2349,11 @@ class DeepCheck(SystemTestMixin, unittest.TestCase):
|
||||
self.web_json(self.root, t="deep-check", verify="true", repair="true"))
|
||||
d.addCallback(self.json_full_deepcheck_and_repair_is_healthy, self.root, "root")
|
||||
|
||||
# now look at t=info
|
||||
d.addCallback(lambda ign: self.web(self.root, t="info"))
|
||||
# TODO: examine the output
|
||||
d.addCallback(lambda ign: self.web(self.mutable, t="info"))
|
||||
d.addCallback(lambda ign: self.web(self.large, t="info"))
|
||||
d.addCallback(lambda ign: self.web(self.small, t="info"))
|
||||
|
||||
return d
|
||||
|
@ -1200,51 +1200,17 @@ class Web(WebMixin, unittest.TestCase):
|
||||
NEW2_CONTENTS))
|
||||
|
||||
# finally list the directory, since mutable files are displayed
|
||||
# differently
|
||||
# slightly differently
|
||||
|
||||
d.addCallback(lambda res:
|
||||
self.GET(self.public_url + "/foo/",
|
||||
followRedirect=True))
|
||||
def _check_page(res):
|
||||
# TODO: assert more about the contents
|
||||
self.failUnless("Overwrite" in res)
|
||||
self.failUnless("Choose new file:" in res)
|
||||
self.failUnless("SSK" in res)
|
||||
return res
|
||||
d.addCallback(_check_page)
|
||||
|
||||
# test that clicking on the "overwrite" button works
|
||||
EVEN_NEWER_CONTENTS = NEWER_CONTENTS + "even newer\n"
|
||||
def _parse_overwrite_form_and_submit(res):
|
||||
|
||||
OVERWRITE_FORM_RE=re.compile('<form action="([^"]*)" method="post" .*<input type="hidden" name="t" value="upload" /><input type="hidden" name="when_done" value="([^"]*)" />', re.I)
|
||||
mo = OVERWRITE_FORM_RE.search(res)
|
||||
self.failUnless(mo, "overwrite form not found in '" + res +
|
||||
"', in which the overwrite form was not found")
|
||||
formaction=mo.group(1)
|
||||
formwhendone=mo.group(2)
|
||||
|
||||
fileurl = "../../../uri/" + urllib.quote(self._mutable_uri)
|
||||
self.failUnlessEqual(formaction, fileurl)
|
||||
# to POST, we need to absoluteify the URL
|
||||
new_formaction = "/uri/%s" % urllib.quote(self._mutable_uri)
|
||||
self.failUnlessEqual(formwhendone,
|
||||
"../uri/%s/" % urllib.quote(self._foo_uri))
|
||||
return self.POST(new_formaction,
|
||||
t="upload",
|
||||
file=("new.txt", EVEN_NEWER_CONTENTS),
|
||||
when_done=formwhendone,
|
||||
followRedirect=False)
|
||||
d.addCallback(_parse_overwrite_form_and_submit)
|
||||
# This will redirect us to ../uri/$FOOURI, rather than
|
||||
# ../uri/$PARENT/foo, but apparently twisted.web.client absolutifies
|
||||
# the redirect for us, and remember that shouldRedirect prepends
|
||||
# self.webish_url for us.
|
||||
d.addBoth(self.shouldRedirect,
|
||||
"/uri/%s/" % urllib.quote(self._foo_uri),
|
||||
which="test_POST_upload_mutable.overwrite")
|
||||
d.addCallback(lambda res:
|
||||
self.failUnlessMutableChildContentsAre(fn, u"new.txt",
|
||||
EVEN_NEWER_CONTENTS))
|
||||
d.addCallback(lambda res: self._foo_node.get(u"new.txt"))
|
||||
def _got3(newnode):
|
||||
self.failUnless(IMutableFileNode.providedBy(newnode))
|
||||
@ -1297,14 +1263,14 @@ class Web(WebMixin, unittest.TestCase):
|
||||
d.addCallback(lambda res:
|
||||
self.GET("/uri/%s" % urllib.quote(self._mutable_uri)))
|
||||
d.addCallback(lambda res:
|
||||
self.failUnlessEqual(res, EVEN_NEWER_CONTENTS))
|
||||
self.failUnlessEqual(res, NEW2_CONTENTS))
|
||||
|
||||
# and that HEAD computes the size correctly
|
||||
d.addCallback(lambda res:
|
||||
self.HEAD(self.public_url + "/foo/new.txt"))
|
||||
def _got_headers(headers):
|
||||
self.failUnlessEqual(headers["content-length"][0],
|
||||
str(len(EVEN_NEWER_CONTENTS)))
|
||||
str(len(NEW2_CONTENTS)))
|
||||
self.failUnlessEqual(headers["content-type"], ["text/plain"])
|
||||
d.addCallback(_got_headers)
|
||||
|
||||
|
@ -23,6 +23,7 @@ from allmydata.web.filenode import ReplaceMeMixin, \
|
||||
FileNodeHandler, PlaceHolderNodeHandler
|
||||
from allmydata.web.checker_results import CheckerResults, \
|
||||
CheckAndRepairResults, DeepCheckResults, DeepCheckAndRepairResults
|
||||
from allmydata.web.info import MoreInfo
|
||||
|
||||
class BlockingFileError(Exception):
|
||||
# TODO: catch and transform
|
||||
@ -131,6 +132,8 @@ class DirectoryNodeHandler(RenderMixin, rend.Page, ReplaceMeMixin):
|
||||
|
||||
if t == "json":
|
||||
return DirectoryJSONMetadata(ctx, self.node)
|
||||
if t == "info":
|
||||
return MoreInfo(self.node)
|
||||
if t == "uri":
|
||||
return DirectoryURI(ctx, self.node)
|
||||
if t == "readonly-uri":
|
||||
@ -467,20 +470,6 @@ class DirectoryAsHTML(rend.Page):
|
||||
|
||||
ctx.fillSlots("delete", delete)
|
||||
ctx.fillSlots("rename", rename)
|
||||
if IDirectoryNode.providedBy(target):
|
||||
check_url = "%s/uri/%s/" % (root, urllib.quote(target.get_uri()))
|
||||
check_done_url = "../../uri/%s/" % urllib.quote(self.node.get_uri())
|
||||
else:
|
||||
check_url = "%s/uri/%s" % (root, urllib.quote(target.get_uri()))
|
||||
check_done_url = "../uri/%s/" % urllib.quote(self.node.get_uri())
|
||||
check = T.form(action=check_url, method="post")[
|
||||
T.input(type='hidden', name='t', value='check'),
|
||||
T.input(type='hidden', name='return_to', value=check_done_url),
|
||||
T.input(type='submit', value='check', name="check"),
|
||||
]
|
||||
ctx.fillSlots("overwrite",
|
||||
self.build_overwrite_form(ctx, name, target))
|
||||
ctx.fillSlots("check", check)
|
||||
|
||||
times = []
|
||||
TIME_FORMAT = "%H:%M:%S %d-%b-%Y"
|
||||
@ -515,7 +504,7 @@ class DirectoryAsHTML(rend.Page):
|
||||
ctx.fillSlots("size", "?")
|
||||
|
||||
text_plain_url = "%s/file/%s/@@named=/foo.txt" % (root, quoted_uri)
|
||||
text_plain_tag = T.a(href=text_plain_url)["text/plain"]
|
||||
info_link = "%s?t=info" % name
|
||||
|
||||
elif IFileNode.providedBy(target):
|
||||
dlurl = "%s/file/%s/@@named=/%s" % (root, quoted_uri, urllib.quote(name))
|
||||
@ -527,8 +516,7 @@ class DirectoryAsHTML(rend.Page):
|
||||
ctx.fillSlots("size", target.get_size())
|
||||
|
||||
text_plain_url = "%s/file/%s/@@named=/foo.txt" % (root, quoted_uri)
|
||||
text_plain_tag = T.a(href=text_plain_url)["text/plain"]
|
||||
|
||||
info_link = "%s?t=info" % name
|
||||
|
||||
elif IDirectoryNode.providedBy(target):
|
||||
# directory
|
||||
@ -541,45 +529,14 @@ class DirectoryAsHTML(rend.Page):
|
||||
dirtype = "DIR"
|
||||
ctx.fillSlots("type", dirtype)
|
||||
ctx.fillSlots("size", "-")
|
||||
text_plain_tag = None
|
||||
info_link = "%s/?t=info" % name
|
||||
|
||||
childdata = [T.a(href="%s?t=json" % name)["JSON"], ", ",
|
||||
T.a(href="%s?t=uri" % name)["URI"], ", ",
|
||||
T.a(href="%s?t=readonly-uri" % name)["readonly-URI"],
|
||||
]
|
||||
if text_plain_tag:
|
||||
childdata.extend([", ", text_plain_tag])
|
||||
|
||||
ctx.fillSlots("data", childdata)
|
||||
|
||||
results = "--"
|
||||
# TODO: include a link to see more results, including timestamps
|
||||
# TODO: use a sparkline
|
||||
ctx.fillSlots("checker_results", results)
|
||||
ctx.fillSlots("info", T.a(href=info_link)["More Info"])
|
||||
|
||||
return ctx.tag
|
||||
|
||||
def render_forms(self, ctx, data):
|
||||
forms = []
|
||||
deep_check = T.form(action=".", method="post",
|
||||
enctype="multipart/form-data")[
|
||||
T.fieldset[
|
||||
T.input(type="hidden", name="t", value="deep-check"),
|
||||
T.input(type="hidden", name="return_to", value="."),
|
||||
T.legend(class_="freeform-form-label")["Run a deep-check operation (EXPENSIVE)"],
|
||||
T.div[
|
||||
"Verify every bit? (EVEN MORE EXPENSIVE):",
|
||||
T.input(type="checkbox", name="verify"),
|
||||
],
|
||||
T.div["Repair any problems?: ",
|
||||
T.input(type="checkbox", name="repair")],
|
||||
T.div["Emit results in JSON format?: ",
|
||||
T.input(type="checkbox", name="output", value="JSON")],
|
||||
|
||||
T.input(type="submit", value="Deep-Check"),
|
||||
|
||||
]]
|
||||
forms.append(T.div(class_="freeform-form")[deep_check])
|
||||
|
||||
if self.node.is_readonly():
|
||||
forms.append(T.div["No upload forms: directory is read-only"])
|
||||
@ -629,26 +586,6 @@ class DirectoryAsHTML(rend.Page):
|
||||
forms.append(T.div(class_="freeform-form")[mount])
|
||||
return forms
|
||||
|
||||
def build_overwrite_form(self, ctx, name, target):
|
||||
if IMutableFileNode.providedBy(target) and not target.is_readonly():
|
||||
root = self.get_root(ctx)
|
||||
action = "%s/uri/%s" % (root, urllib.quote(target.get_uri()))
|
||||
done_url = "../uri/%s/" % urllib.quote(self.node.get_uri())
|
||||
overwrite = T.form(action=action, method="post",
|
||||
enctype="multipart/form-data")[
|
||||
T.fieldset[
|
||||
T.input(type="hidden", name="t", value="upload"),
|
||||
T.input(type='hidden', name='when_done', value=done_url),
|
||||
T.legend(class_="freeform-form-label")["Overwrite"],
|
||||
"Choose new file: ",
|
||||
T.input(type="file", name="file", class_="freeform-input-file"),
|
||||
" ",
|
||||
T.input(type="submit", value="Overwrite")
|
||||
]]
|
||||
return [T.div(class_="freeform-form")[overwrite],]
|
||||
else:
|
||||
return []
|
||||
|
||||
def render_results(self, ctx, data):
|
||||
req = IRequest(ctx)
|
||||
return get_arg(req, "results", "")
|
||||
@ -697,6 +634,8 @@ def DirectoryJSONMetadata(ctx, dirnode):
|
||||
d.addCallback(text_plain, ctx)
|
||||
return d
|
||||
|
||||
|
||||
|
||||
def DirectoryURI(ctx, dirnode):
|
||||
return text_plain(dirnode.get_uri(), ctx)
|
||||
|
||||
|
@ -14,13 +14,7 @@
|
||||
<div><a href=".">Refresh this view</a></div>
|
||||
<div n:render="welcome" />
|
||||
|
||||
<div>Other representations of this directory:
|
||||
<a href="?t=manifest">manifest</a>,
|
||||
<a href="?t=deep-size">total size</a>,
|
||||
<a href="?t=uri">URI</a>,
|
||||
<a href="?t=readonly-uri">read-only URI</a>,
|
||||
<a href="?t=json">JSON</a>
|
||||
</div>
|
||||
<div><a href="?t=info">More info on this directory</a></div>
|
||||
|
||||
<div>
|
||||
<table n:render="sequence" n:data="children" border="1">
|
||||
@ -29,25 +23,18 @@
|
||||
<td>Type</td>
|
||||
<td>Size</td>
|
||||
<td>Times</td>
|
||||
<td>other representations</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
<td>Checker Results</td>
|
||||
</tr>
|
||||
<tr n:pattern="item" n:render="row">
|
||||
<td><n:slot name="filename"/></td>
|
||||
<td><n:slot name="type"/></td>
|
||||
<td><n:slot name="size"/></td>
|
||||
<td><n:slot name="times"/></td>
|
||||
<td><n:slot name="data"/></td>
|
||||
<td><n:slot name="delete"/></td>
|
||||
<td><n:slot name="overwrite"/></td>
|
||||
<td><n:slot name="rename"/></td>
|
||||
|
||||
<td><n:slot name="check"/></td>
|
||||
<td><n:slot name="checker_results"/></td>
|
||||
<td><n:slot name="info"/></td>
|
||||
</tr>
|
||||
|
||||
<tr n:pattern="empty"><td>directory is empty!</td></tr>
|
||||
|
@ -17,6 +17,7 @@ from allmydata.web.common import text_plain, WebError, IClient, RenderMixin, \
|
||||
boolean_of_arg, get_arg, should_create_intermediate_directories
|
||||
from allmydata.web.checker_results import CheckerResults, \
|
||||
CheckAndRepairResults, LiteralCheckerResults
|
||||
from allmydata.web.info import MoreInfo
|
||||
|
||||
class ReplaceMeMixin:
|
||||
|
||||
@ -174,6 +175,8 @@ class FileNodeHandler(RenderMixin, rend.Page, ReplaceMeMixin):
|
||||
return FileDownloader(self.node, filename, save_to_file)
|
||||
if t == "json":
|
||||
return FileJSONMetadata(ctx, self.node)
|
||||
if t == "info":
|
||||
return MoreInfo(self.node)
|
||||
if t == "uri":
|
||||
return FileURI(ctx, self.node)
|
||||
if t == "readonly-uri":
|
||||
|
238
src/allmydata/web/info.py
Normal file
238
src/allmydata/web/info.py
Normal file
@ -0,0 +1,238 @@
|
||||
|
||||
import urllib
|
||||
|
||||
from twisted.internet import defer
|
||||
from nevow import rend, tags as T
|
||||
from nevow.inevow import IRequest
|
||||
|
||||
from allmydata.util import base32
|
||||
from allmydata.interfaces import IDirectoryNode
|
||||
from allmydata.web.common import getxmlfile
|
||||
|
||||
class MoreInfo(rend.Page):
|
||||
addSlash = False
|
||||
docFactory = getxmlfile("info.xhtml")
|
||||
|
||||
def abbrev(self, storage_index_or_none):
|
||||
if storage_index_or_none:
|
||||
return base32.b2a(storage_index_or_none)[:6]
|
||||
return "LIT file"
|
||||
|
||||
def get_type(self):
|
||||
node = self.original
|
||||
si = node.get_storage_index()
|
||||
if IDirectoryNode.providedBy(node):
|
||||
return "directory"
|
||||
if si:
|
||||
if node.is_mutable():
|
||||
return "mutable file"
|
||||
return "immutable file"
|
||||
return "LIT file"
|
||||
|
||||
def render_title(self, ctx, data):
|
||||
node = self.original
|
||||
si = node.get_storage_index()
|
||||
t = "More Info for %s" % self.get_type()
|
||||
if si:
|
||||
t += " (SI=%s)" % self.abbrev(si)
|
||||
return ctx.tag[t]
|
||||
|
||||
def render_header(self, ctx, data):
|
||||
return self.render_title(ctx, data)
|
||||
|
||||
def render_type(self, ctx, data):
|
||||
return ctx.tag[self.get_type()]
|
||||
|
||||
def render_si(self, ctx, data):
|
||||
si = self.original.get_storage_index()
|
||||
if not si:
|
||||
return "None"
|
||||
return ctx.tag[base32.b2a(si)]
|
||||
|
||||
def render_size(self, ctx, data):
|
||||
node = self.original
|
||||
si = node.get_storage_index()
|
||||
if IDirectoryNode.providedBy(node):
|
||||
d = node._node.get_size_of_best_version()
|
||||
elif node.is_mutable():
|
||||
d = node.get_size_of_best_version()
|
||||
else:
|
||||
# for immutable files and LIT files, we get the size from the URI
|
||||
d = defer.succeed(node.get_size())
|
||||
d.addCallback(lambda size: ctx.tag[size])
|
||||
return d
|
||||
|
||||
def render_directory_writecap(self, ctx, data):
|
||||
node = self.original
|
||||
if node.is_readonly():
|
||||
return ""
|
||||
if not IDirectoryNode.providedBy(node):
|
||||
return ""
|
||||
return ctx.tag[node.get_uri()]
|
||||
|
||||
def render_directory_readcap(self, ctx, data):
|
||||
node = self.original
|
||||
if not IDirectoryNode.providedBy(node):
|
||||
return ""
|
||||
return ctx.tag[node.get_readonly_uri()]
|
||||
|
||||
def render_directory_verifycap(self, ctx, data):
|
||||
node = self.original
|
||||
if not IDirectoryNode.providedBy(node):
|
||||
return ""
|
||||
return ctx.tag[node.get_verifier().to_string()]
|
||||
|
||||
|
||||
def render_file_writecap(self, ctx, data):
|
||||
node = self.original
|
||||
if IDirectoryNode.providedBy(node):
|
||||
node = node._node
|
||||
if node.is_readonly():
|
||||
return ""
|
||||
return ctx.tag[node.get_uri()]
|
||||
|
||||
def render_file_readcap(self, ctx, data):
|
||||
node = self.original
|
||||
if IDirectoryNode.providedBy(node):
|
||||
node = node._node
|
||||
return ctx.tag[node.get_readonly_uri()]
|
||||
|
||||
def render_file_verifycap(self, ctx, data):
|
||||
node = self.original
|
||||
if IDirectoryNode.providedBy(node):
|
||||
node = node._node
|
||||
verifier = node.get_verifier()
|
||||
if verifier:
|
||||
return ctx.tag[node.get_verifier().to_string()]
|
||||
return ""
|
||||
|
||||
def get_root(self, ctx):
|
||||
req = IRequest(ctx)
|
||||
# the addSlash=True gives us one extra (empty) segment
|
||||
depth = len(req.prepath) + len(req.postpath) - 1
|
||||
link = "/".join([".."] * depth)
|
||||
return link
|
||||
|
||||
def render_raw_link(self, ctx, data):
|
||||
node = self.original
|
||||
if IDirectoryNode.providedBy(node):
|
||||
node = node._node
|
||||
root = self.get_root(ctx)
|
||||
quoted_uri = urllib.quote(node.get_uri())
|
||||
text_plain_url = "%s/file/%s/@@named=/raw.txt" % (root, quoted_uri)
|
||||
return ctx.tag[text_plain_url]
|
||||
|
||||
def render_is_checkable(self, ctx, data):
|
||||
node = self.original
|
||||
si = node.get_storage_index()
|
||||
if si:
|
||||
return ctx.tag
|
||||
# don't show checker button for LIT files
|
||||
return ""
|
||||
|
||||
def render_check_form(self, ctx, data):
|
||||
check = T.form(action=".", method="post",
|
||||
enctype="multipart/form-data")[
|
||||
T.fieldset[
|
||||
T.input(type="hidden", name="t", value="check"),
|
||||
T.input(type="hidden", name="return_to", value="."),
|
||||
T.legend(class_="freeform-form-label")["Check on this object"],
|
||||
T.div[
|
||||
"Verify every bit? (EXPENSIVE):",
|
||||
T.input(type="checkbox", name="verify"),
|
||||
],
|
||||
T.div["Repair any problems?: ",
|
||||
T.input(type="checkbox", name="repair")],
|
||||
T.div["Emit results in JSON format?: ",
|
||||
T.input(type="checkbox", name="output", value="JSON")],
|
||||
|
||||
T.input(type="submit", value="Check"),
|
||||
|
||||
]]
|
||||
return ctx.tag[check]
|
||||
|
||||
def render_is_mutable_file(self, ctx, data):
|
||||
node = self.original
|
||||
if IDirectoryNode.providedBy(node):
|
||||
return ""
|
||||
if node.is_mutable() and not node.is_readonly():
|
||||
return ctx.tag
|
||||
return ""
|
||||
|
||||
def render_overwrite_form(self, ctx, data):
|
||||
node = self.original
|
||||
root = self.get_root(ctx)
|
||||
action = "%s/uri/%s" % (root, urllib.quote(node.get_uri()))
|
||||
done_url = "%s/uri/%s?t=info" % (root, urllib.quote(node.get_uri()))
|
||||
overwrite = T.form(action=action, method="post",
|
||||
enctype="multipart/form-data")[
|
||||
T.fieldset[
|
||||
T.input(type="hidden", name="t", value="upload"),
|
||||
T.input(type='hidden', name='when_done', value=done_url),
|
||||
T.legend(class_="freeform-form-label")["Overwrite"],
|
||||
"Upload new contents: ",
|
||||
T.input(type="file", name="file"),
|
||||
" ",
|
||||
T.input(type="submit", value="Replace Contents")
|
||||
]]
|
||||
return ctx.tag[overwrite]
|
||||
|
||||
def render_is_directory(self, ctx, data):
|
||||
node = self.original
|
||||
if IDirectoryNode.providedBy(node):
|
||||
return ctx.tag
|
||||
return ""
|
||||
|
||||
def render_deep_check_form(self, ctx, data):
|
||||
deep_check = T.form(action=".", method="post",
|
||||
enctype="multipart/form-data")[
|
||||
T.fieldset[
|
||||
T.input(type="hidden", name="t", value="deep-check"),
|
||||
T.input(type="hidden", name="return_to", value="."),
|
||||
T.legend(class_="freeform-form-label")["Run a deep-check operation (EXPENSIVE)"],
|
||||
T.div[
|
||||
"Verify every bit? (EVEN MORE EXPENSIVE):",
|
||||
T.input(type="checkbox", name="verify"),
|
||||
],
|
||||
T.div["Repair any problems?: ",
|
||||
T.input(type="checkbox", name="repair")],
|
||||
T.div["Emit results in JSON format?: ",
|
||||
T.input(type="checkbox", name="output", value="JSON")],
|
||||
|
||||
T.input(type="submit", value="Deep-Check"),
|
||||
|
||||
]]
|
||||
return ctx.tag[deep_check]
|
||||
|
||||
def render_deep_size_form(self, ctx, data):
|
||||
deep_size = T.form(action=".", method="get",
|
||||
enctype="multipart/form-data")[
|
||||
T.fieldset[
|
||||
T.input(type="hidden", name="t", value="deep-size"),
|
||||
T.legend(class_="freeform-form-label")["Run a deep-size operation (EXPENSIVE)"],
|
||||
T.input(type="submit", value="Deep-Size"),
|
||||
]]
|
||||
return ctx.tag[deep_size]
|
||||
|
||||
def render_deep_stats_form(self, ctx, data):
|
||||
deep_stats = T.form(action=".", method="get",
|
||||
enctype="multipart/form-data")[
|
||||
T.fieldset[
|
||||
T.input(type="hidden", name="t", value="deep-stats"),
|
||||
T.legend(class_="freeform-form-label")["Run a deep-stats operation (EXPENSIVE)"],
|
||||
T.input(type="submit", value="Deep-Stats"),
|
||||
]]
|
||||
return ctx.tag[deep_stats]
|
||||
|
||||
def render_manifest_form(self, ctx, data):
|
||||
manifest = T.form(action=".", method="get",
|
||||
enctype="multipart/form-data")[
|
||||
T.fieldset[
|
||||
T.input(type="hidden", name="t", value="manifest"),
|
||||
T.legend(class_="freeform-form-label")["Run a manifest operation (EXPENSIVE)"],
|
||||
T.input(type="submit", value="Manifest"),
|
||||
]]
|
||||
return ctx.tag[manifest]
|
||||
|
||||
|
||||
# TODO: edge metadata
|
72
src/allmydata/web/info.xhtml
Normal file
72
src/allmydata/web/info.xhtml
Normal file
@ -0,0 +1,72 @@
|
||||
<html xmlns:n="http://nevow.com/ns/nevow/0.1">
|
||||
<head>
|
||||
<title n:render="title"></title>
|
||||
<!-- <link href="http://www.allmydata.com/common/css/styles.css"
|
||||
rel="stylesheet" type="text/css"/> -->
|
||||
<link href="/webform_css" rel="stylesheet" type="text/css"/>
|
||||
<link href="/tahoe_css" rel="stylesheet" type="text/css"/>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h2 n:render="header"></h2>
|
||||
|
||||
<ul>
|
||||
<li>Object Type: <span n:render="type" /></li>
|
||||
<li>Storage Index: <tt n:render="si" /></li>
|
||||
<li>Object Size: <span n:render="size" /></li>
|
||||
<li>Access Caps (URIs):
|
||||
<table border="1">
|
||||
<span n:render="is_directory">
|
||||
<tr>
|
||||
<td>Directory writecap</td>
|
||||
<td><tt n:render="directory_writecap" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Directory readcap</td>
|
||||
<td><tt n:render="directory_readcap" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Directory verifycap</td>
|
||||
<td><tt n:render="directory_verifycap" /></td>
|
||||
</tr>
|
||||
</span>
|
||||
|
||||
<tr>
|
||||
<td>File writecap</td>
|
||||
<td><tt n:render="file_writecap" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>File readcap</td>
|
||||
<td><tt n:render="file_readcap" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>File verifycap</td>
|
||||
<td><tt n:render="file_verifycap" /></td>
|
||||
</tr>
|
||||
</table></li>
|
||||
<li><a href="?t=json">JSON</a></li>
|
||||
<li>Raw data as <a><n:attr name="href" n:render="raw_link" />text/plain</a></li>
|
||||
</ul>
|
||||
|
||||
<div n:render="is_checkable">
|
||||
<h2>Checker Operations</h2>
|
||||
<div n:render="check_form" />
|
||||
</div>
|
||||
|
||||
<div n:render="is_mutable_file">
|
||||
<h2>Mutable File Operations</h2>
|
||||
<div n:render="overwrite_form" />
|
||||
</div>
|
||||
|
||||
<div n:render="is_directory">
|
||||
<h2>Directory Operations</h2>
|
||||
<div n:render="deep_check_form" />
|
||||
<div n:render="deep_size_form" />
|
||||
<div n:render="deep_stats_form" />
|
||||
<div n:render="manifest_form" />
|
||||
</div>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user