from twisted.internet import defer
from twisted.internet.interfaces import IPushProducer
from twisted.python.failure import Failure
-from twisted.web import http, html
+from twisted.web import http
from nevow import url, rend, inevow, tags as T
from nevow.inevow import IRequest
from allmydata.uri import from_string_dirnode
from allmydata.interfaces import IDirectoryNode, IFileNode, IFilesystemNode, \
IImmutableFileNode, IMutableFileNode, ExistingChildError, \
- NoSuchChildError, EmptyPathnameComponentError
+ NoSuchChildError, EmptyPathnameComponentError, SDMF_VERSION, MDMF_VERSION
+from allmydata.blacklist import ProhibitedNode
from allmydata.monitor import Monitor, OperationCancelledError
from allmydata import dirnode
from allmydata.web.common import text_plain, WebError, \
IOpHandleTable, NeedOperationHandleError, \
boolean_of_arg, get_arg, get_root, parse_replace_arg, \
should_create_intermediate_directories, \
- getxmlfile, RenderMixin, humanize_failure, convert_children_json
+ getxmlfile, RenderMixin, humanize_failure, convert_children_json, \
+ get_format, get_mutable_type
from allmydata.web.filenode import ReplaceMeMixin, \
FileNodeHandler, PlaceHolderNodeHandler
-from allmydata.web.check_results import CheckResults, \
- CheckAndRepairResults, DeepCheckResults, DeepCheckAndRepairResults, \
- LiteralCheckResults
+from allmydata.web.check_results import CheckResultsRenderer, \
+ CheckAndRepairResultsRenderer, DeepCheckResultsRenderer, \
+ DeepCheckAndRepairResultsRenderer, LiteralCheckResultsRenderer
from allmydata.web.info import MoreInfo
from allmydata.web.operations import ReloadMixin
from allmydata.web.check_results import json_check_results, \
self.name = name
def childFactory(self, ctx, name):
- req = IRequest(ctx)
name = name.decode("utf-8")
if not name:
raise EmptyPathnameComponentError()
kids_json = req.content.read()
kids = convert_children_json(self.client.nodemaker,
kids_json)
+ file_format = get_format(req, None)
mutable = True
+ mt = get_mutable_type(file_format)
if t == "mkdir-immutable":
mutable = False
+
d = self.node.create_subdirectory(name, kids,
- mutable=mutable)
+ mutable=mutable,
+ mutable_version=mt)
d.addCallback(make_handler_for,
self.client, self.node, name)
return d
req = IRequest(ctx)
# This is where all of the directory-related ?t=* code goes.
t = get_arg(req, "t", "").strip()
+
+ # t=info contains variable ophandles, t=rename-form contains the name
+ # of the child being renamed. Neither is allowed an ETag.
+ FIXED_OUTPUT_TYPES = ["", "json", "uri", "readonly-uri"]
+ if not self.node.is_mutable() and t in FIXED_OUTPUT_TYPES:
+ si = self.node.get_storage_index()
+ if si and req.setETag('DIR:%s-%s' % (base32.b2a(si), t or "")):
+ return ""
+
if not t:
# render the directory as HTML, using the docFactory and Nevow's
# whole templating thing.
- return DirectoryAsHTML(self.node)
+ return DirectoryAsHTML(self.node,
+ self.client.mutable_file_default)
if t == "json":
return DirectoryJSONMetadata(ctx, self.node)
d = self._POST_mkdir_with_children(req)
elif t == "mkdir-immutable":
d = self._POST_mkdir_immutable(req)
- elif t == "mkdir-p":
- # TODO: docs, tests
- d = self._POST_mkdir_p(req)
elif t == "upload":
d = self._POST_upload(ctx) # this one needs the context
elif t == "uri":
d = self._POST_uri(req)
- elif t == "delete":
- d = self._POST_delete(req)
+ elif t == "delete" or t == "unlink":
+ d = self._POST_unlink(req)
elif t == "rename":
d = self._POST_rename(req)
+ elif t == "move":
+ d = self._POST_move(req)
elif t == "check":
d = self._POST_check(req)
elif t == "start-deep-check":
d = self._POST_start_deep_stats(ctx)
elif t == "stream-manifest":
d = self._POST_stream_manifest(ctx)
- elif t == "set_children":
+ elif t == "set_children" or t == "set-children":
d = self._POST_set_children(req)
else:
raise WebError("POST to a directory with bad t=%s" % t)
name = name.decode("utf-8")
replace = boolean_of_arg(get_arg(req, "replace", "true"))
kids = {}
- d = self.node.create_subdirectory(name, kids, overwrite=replace)
+ mt = get_mutable_type(get_format(req, None))
+ d = self.node.create_subdirectory(name, kids, overwrite=replace,
+ mutable_version=mt)
d.addCallback(lambda child: child.get_uri()) # TODO: urlencode
return d
# which created the final directory (i.e. us)
return defer.succeed(self.node.get_uri()) # TODO: urlencode
name = name.decode("utf-8")
- replace = boolean_of_arg(get_arg(req, "replace", "true"))
+ # TODO: decide on replace= behavior, see #903
+ #replace = boolean_of_arg(get_arg(req, "replace", "false"))
req.content.seek(0)
kids_json = req.content.read()
kids = convert_children_json(self.client.nodemaker, kids_json)
- d = self.node.create_subdirectory(name, kids, overwrite=replace)
+ mt = get_mutable_type(get_format(req, None))
+ d = self.node.create_subdirectory(name, kids, overwrite=False,
+ mutable_version=mt)
d.addCallback(lambda child: child.get_uri()) # TODO: urlencode
return d
# which created the final directory (i.e. us)
return defer.succeed(self.node.get_uri()) # TODO: urlencode
name = name.decode("utf-8")
- replace = boolean_of_arg(get_arg(req, "replace", "true"))
+ # TODO: decide on replace= behavior, see #903
+ #replace = boolean_of_arg(get_arg(req, "replace", "false"))
req.content.seek(0)
kids_json = req.content.read()
kids = convert_children_json(self.client.nodemaker, kids_json)
- d = self.node.create_subdirectory(name, kids, mutable=False)
+ d = self.node.create_subdirectory(name, kids, overwrite=False, mutable=False)
d.addCallback(lambda child: child.get_uri()) # TODO: urlencode
return d
- def _POST_mkdir_p(self, req):
- path = get_arg(req, "path")
- if not path:
- raise WebError("mkdir-p requires a path")
- path_ = tuple([seg.decode("utf-8") for seg in path.split('/') if seg ])
- # TODO: replace
- d = self._get_or_create_directories(self.node, path_)
- d.addCallback(lambda node: node.get_uri())
- return d
-
- def _get_or_create_directories(self, node, path):
- if not IDirectoryNode.providedBy(node):
- # unfortunately it is too late to provide the name of the
- # blocking directory in the error message.
- raise BlockingFileError("cannot create directory because there "
- "is a file in the way")
- if not path:
- return defer.succeed(node)
- d = node.get(path[0])
- def _maybe_create(f):
- f.trap(NoSuchChildError)
- return node.create_subdirectory(path[0])
- d.addErrback(_maybe_create)
- d.addCallback(self._get_or_create_directories, path[1:])
- return d
-
def _POST_upload(self, ctx):
req = IRequest(ctx)
charset = get_arg(req, "_charset", "utf-8")
charset = get_arg(req, "_charset", "utf-8")
name = name.decode(charset)
replace = boolean_of_arg(get_arg(req, "replace", "true"))
- d = self.node.set_uri(name, childcap, childcap, overwrite=replace)
+
+ # We mustn't pass childcap for the readcap argument because we don't
+ # know whether it is a read cap. Passing a read cap as the writecap
+ # argument will work (it ends up calling NodeMaker.create_from_cap,
+ # which derives a readcap if necessary and possible).
+ d = self.node.set_uri(name, childcap, None, overwrite=replace)
d.addCallback(lambda res: childcap)
return d
- def _POST_delete(self, req):
+ def _POST_unlink(self, req):
name = get_arg(req, "name")
if name is None:
# apparently an <input type="hidden" name="name" value="">
# won't show up in the resulting encoded form.. the 'name'
- # field is completely missing. So to allow deletion of an
- # empty file, we have to pretend that None means ''. The only
- # downide of this is a slightly confusing error message if
- # someone does a POST without a name= field. For our own HTML
- # thisn't a big deal, because we create the 'delete' POST
- # buttons ourselves.
+ # field is completely missing. So to allow unlinking of a
+ # child with a name that is the empty string, we have to
+ # pretend that None means ''. The only downside of this is
+ # a slightly confusing error message if someone does a POST
+ # without a name= field. For our own HTML this isn't a big
+ # deal, because we create the 'unlink' POST buttons ourselves.
name = ''
charset = get_arg(req, "_charset", "utf-8")
name = name.decode(charset)
d = self.node.delete(name)
- d.addCallback(lambda res: "thing deleted")
+ d.addCallback(lambda res: "thing unlinked")
return d
def _POST_rename(self, req):
d.addCallback(lambda res: "thing renamed")
return d
+ def _POST_move(self, req):
+ charset = get_arg(req, "_charset", "utf-8")
+ from_name = get_arg(req, "from_name")
+ if from_name is not None:
+ from_name = from_name.strip()
+ from_name = from_name.decode(charset)
+ assert isinstance(from_name, unicode)
+ to_name = get_arg(req, "to_name")
+ if to_name is not None:
+ to_name = to_name.strip()
+ to_name = to_name.decode(charset)
+ assert isinstance(to_name, unicode)
+ if not to_name:
+ to_name = from_name
+ to_dir = get_arg(req, "to_dir")
+ if to_dir is not None:
+ to_dir = to_dir.strip()
+ to_dir = to_dir.decode(charset)
+ assert isinstance(to_dir, unicode)
+ 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"))
+
+ # Disallow slashes in both from_name and to_name, that would only
+ # cause confusion. t=move is only for moving things from the
+ # *current* directory into a second directory named by to_dir=
+ if "/" in from_name:
+ raise WebError("from_name= may not contain a slash",
+ http.BAD_REQUEST)
+ if "/" in to_name:
+ raise WebError("to_name= may not contain a slash",
+ http.BAD_REQUEST)
+
+ target_type = get_arg(req, "target_type", "name")
+ if target_type == "name":
+ d = self.node.get_child_at_path(to_dir)
+ elif target_type == "uri":
+ d = defer.succeed(self.client.create_node_from_uri(str(to_dir)))
+ else:
+ raise WebError("invalid target_type parameter", http.BAD_REQUEST)
+
+ def is_target_node_usable(target_node):
+ if not IDirectoryNode.providedBy(target_node):
+ raise WebError("to_dir is not a directory", http.BAD_REQUEST)
+ return target_node
+ d.addCallback(is_target_node_usable)
+ d.addCallback(lambda new_parent:
+ self.node.move_child_to(from_name, new_parent,
+ to_name, replace))
+ d.addCallback(lambda res: "thing moved")
+ return d
+
def _maybe_literal(self, res, Results_Class):
if res:
return Results_Class(self.client, res)
- return LiteralCheckResults(self.client)
+ return LiteralCheckResultsRenderer(self.client)
def _POST_check(self, req):
# check this directory
add_lease = boolean_of_arg(get_arg(req, "add-lease", "false"))
if repair:
d = self.node.check_and_repair(Monitor(), verify, add_lease)
- d.addCallback(self._maybe_literal, CheckAndRepairResults)
+ d.addCallback(self._maybe_literal, CheckAndRepairResultsRenderer)
else:
d = self.node.check(Monitor(), verify, add_lease)
- d.addCallback(self._maybe_literal, CheckResults)
+ d.addCallback(self._maybe_literal, CheckResultsRenderer)
return d
def _start_operation(self, monitor, renderer, ctx):
add_lease = boolean_of_arg(get_arg(ctx, "add-lease", "false"))
if repair:
monitor = self.node.start_deep_check_and_repair(verify, add_lease)
- renderer = DeepCheckAndRepairResults(self.client, monitor)
+ renderer = DeepCheckAndRepairResultsRenderer(self.client, monitor)
else:
monitor = self.node.start_deep_check(verify, add_lease)
- renderer = DeepCheckResults(self.client, monitor)
+ renderer = DeepCheckResultsRenderer(self.client, monitor)
return self._start_operation(monitor, renderer, ctx)
def _POST_stream_deep_check(self, ctx):
u = from_string_dirnode(dirnode.get_uri())
return u.abbrev_si()
+SPACE = u"\u00A0"*2
+
class DirectoryAsHTML(rend.Page):
# The remainder of this class is to render the directory into
# human+browser -oriented HTML.
docFactory = getxmlfile("directory.xhtml")
addSlash = True
- def __init__(self, node):
+ def __init__(self, node, default_mutable_format):
rend.Page.__init__(self)
self.node = node
+ assert default_mutable_format in (MDMF_VERSION, SDMF_VERSION)
+ self.default_mutable_format = default_mutable_format
+
def beforeRender(self, ctx):
# attempt to get the dirnode's children, stashing them (or the
# failure that results) for later use
def render_title(self, ctx, data):
si_s = abbreviated_dirnode(self.node)
header = ["Tahoe-LAFS - Directory SI=%s" % si_s]
- if self.node.is_readonly():
+ if self.node.is_unknown():
+ header.append(" (unknown)")
+ elif not self.node.is_mutable():
+ header.append(" (immutable)")
+ elif self.node.is_readonly():
header.append(" (read-only)")
else:
header.append(" (modifiable)")
def render_header(self, ctx, data):
si_s = abbreviated_dirnode(self.node)
header = ["Tahoe-LAFS Directory SI=", T.span(class_="data-chars")[si_s]]
- if self.node.is_readonly():
+ if self.node.is_unknown():
+ header.append(" (unknown)")
+ elif not self.node.is_mutable():
+ header.append(" (immutable)")
+ elif self.node.is_readonly():
header.append(" (read-only)")
return ctx.tag[header]
def render_welcome(self, ctx, data):
link = get_root(ctx)
- return T.div[T.a(href=link)["Return to Welcome page"]]
+ return ctx.tag[T.a(href=link)["Return to Welcome page"]]
def render_show_readonly(self, ctx, data):
- if self.node.is_readonly():
+ if self.node.is_unknown() or self.node.is_readonly():
return ""
rocap = self.node.get_readonly_uri()
root = get_root(ctx)
root = get_root(ctx)
here = "%s/uri/%s/" % (root, urllib.quote(self.node.get_uri()))
- if self.node.is_readonly():
- delete = "-"
+ if self.node.is_unknown() or self.node.is_readonly():
+ unlink = "-"
rename = "-"
else:
- # this creates a button which will cause our child__delete method
- # to be invoked, which deletes the file and then redirects the
+ # this creates a button which will cause our _POST_unlink method
+ # to be invoked, which unlinks the file and then redirects the
# browser back to this directory
- delete = T.form(action=here, method="post")[
- T.input(type='hidden', name='t', value='delete'),
+ unlink = T.form(action=here, method="post")[
+ T.input(type='hidden', name='t', value='unlink'),
T.input(type='hidden', name='name', value=name),
T.input(type='hidden', name='when_done', value="."),
- T.input(type='submit', value='del', name="del"),
+ T.input(type='submit', value='unlink', name="unlink"),
]
rename = T.form(action=here, method="get")[
T.input(type='hidden', name='t', value='rename-form'),
T.input(type='hidden', name='name', value=name),
T.input(type='hidden', name='when_done', value="."),
- T.input(type='submit', value='rename', name="rename"),
+ T.input(type='submit', value='rename/move', name="rename"),
]
- ctx.fillSlots("delete", delete)
+ ctx.fillSlots("unlink", unlink)
ctx.fillSlots("rename", rename)
times = []
ctx.fillSlots("times", times)
assert IFilesystemNode.providedBy(target), target
- writecap = target.get_uri() or ""
- quoted_uri = urllib.quote(writecap, safe="") # escape slashes too
+ target_uri = target.get_uri() or ""
+ quoted_uri = urllib.quote(target_uri, safe="") # escape slashes too
if IMutableFileNode.providedBy(target):
# to prevent javascript in displayed .html files from stealing a
# page that doesn't know about the directory at all
dlurl = "%s/file/%s/@@named=/%s" % (root, quoted_uri, nameurl)
- ctx.fillSlots("filename",
- T.a(href=dlurl)[html.escape(name)])
+ ctx.fillSlots("filename", T.a(href=dlurl)[name])
ctx.fillSlots("type", "SSK")
ctx.fillSlots("size", "?")
elif IImmutableFileNode.providedBy(target):
dlurl = "%s/file/%s/@@named=/%s" % (root, quoted_uri, nameurl)
- ctx.fillSlots("filename",
- T.a(href=dlurl)[html.escape(name)])
+ ctx.fillSlots("filename", T.a(href=dlurl)[name])
ctx.fillSlots("type", "FILE")
ctx.fillSlots("size", target.get_size())
elif IDirectoryNode.providedBy(target):
# directory
- uri_link = "%s/uri/%s/" % (root, urllib.quote(writecap))
- ctx.fillSlots("filename",
- T.a(href=uri_link)[html.escape(name)])
+ uri_link = "%s/uri/%s/" % (root, urllib.quote(target_uri))
+ ctx.fillSlots("filename", T.a(href=uri_link)[name])
if not target.is_mutable():
dirtype = "DIR-IMM"
elif target.is_readonly():
ctx.fillSlots("size", "-")
info_link = "%s/uri/%s/?t=info" % (root, quoted_uri)
+ elif isinstance(target, ProhibitedNode):
+ ctx.fillSlots("filename", T.strike[name])
+ if IDirectoryNode.providedBy(target.wrapped_node):
+ blacklisted_type = "DIR-BLACKLISTED"
+ else:
+ blacklisted_type = "BLACKLISTED"
+ ctx.fillSlots("type", blacklisted_type)
+ ctx.fillSlots("size", "-")
+ info_link = None
+ ctx.fillSlots("info", ["Access Prohibited:", T.br, target.reason])
+
else:
# unknown
- ctx.fillSlots("filename", html.escape(name))
- ctx.fillSlots("type", "?")
+ ctx.fillSlots("filename", name)
+ if target.get_write_uri() is not None:
+ unknowntype = "?"
+ elif not self.node.is_mutable() or target.is_alleged_immutable():
+ unknowntype = "?-IMM"
+ else:
+ unknowntype = "?-RO"
+ ctx.fillSlots("type", unknowntype)
ctx.fillSlots("size", "-")
# use a directory-relative info link, so we can extract both the
# writecap and the readcap
info_link = "%s?t=info" % urllib.quote(name)
- ctx.fillSlots("info", T.a(href=info_link)["More Info"])
+ if info_link:
+ ctx.fillSlots("info", T.a(href=info_link)["More Info"])
return ctx.tag
+ # XXX: similar to render_upload_form and render_mkdir_form in root.py.
def render_forms(self, ctx, data):
forms = []
if self.dirnode_children is None:
return T.div["No upload forms: directory is unreadable"]
- mkdir = T.form(action=".", method="post",
- enctype="multipart/form-data")[
+ mkdir_sdmf = T.input(type='radio', name='format',
+ value='sdmf', id='mkdir-sdmf',
+ checked='checked')
+ mkdir_mdmf = T.input(type='radio', name='format',
+ value='mdmf', id='mkdir-mdmf')
+
+ mkdir_form = T.form(action=".", method="post",
+ enctype="multipart/form-data")[
T.fieldset[
T.input(type="hidden", name="t", value="mkdir"),
T.input(type="hidden", name="when_done", value="."),
T.legend(class_="freeform-form-label")["Create a new directory in this directory"],
- "New directory name: ",
- T.input(type="text", name="name"), " ",
- T.input(type="submit", value="Create"),
+ "New directory name:"+SPACE,
+ T.input(type="text", name="name"), SPACE,
+ T.input(type="submit", value="Create"), SPACE*2,
+ mkdir_sdmf, T.label(for_='mutable-directory-sdmf')[" SDMF"], SPACE,
+ mkdir_mdmf, T.label(for_='mutable-directory-mdmf')[" MDMF (experimental)"],
]]
- forms.append(T.div(class_="freeform-form")[mkdir])
-
- upload = T.form(action=".", method="post",
- enctype="multipart/form-data")[
+ forms.append(T.div(class_="freeform-form")[mkdir_form])
+
+ upload_chk = T.input(type='radio', name='format',
+ value='chk', id='upload-chk',
+ checked='checked')
+ upload_sdmf = T.input(type='radio', name='format',
+ value='sdmf', id='upload-sdmf')
+ upload_mdmf = T.input(type='radio', name='format',
+ value='mdmf', id='upload-mdmf')
+
+ upload_form = T.form(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="."),
T.legend(class_="freeform-form-label")["Upload a file to this directory"],
- "Choose a file to upload: ",
- T.input(type="file", name="file", class_="freeform-input-file"),
- " ",
- T.input(type="submit", value="Upload"),
- " Mutable?:",
- T.input(type="checkbox", name="mutable"),
+ "Choose a file to upload:"+SPACE,
+ T.input(type="file", name="file", class_="freeform-input-file"), SPACE,
+ T.input(type="submit", value="Upload"), SPACE*2,
+ upload_chk, T.label(for_="upload-chk") [" Immutable"], SPACE,
+ upload_sdmf, T.label(for_="upload-sdmf")[" SDMF"], SPACE,
+ upload_mdmf, T.label(for_="upload-mdmf")[" MDMF (experimental)"],
]]
- forms.append(T.div(class_="freeform-form")[upload])
+ forms.append(T.div(class_="freeform-form")[upload_form])
- mount = T.form(action=".", method="post",
- enctype="multipart/form-data")[
+ attach_form = T.form(action=".", method="post",
+ enctype="multipart/form-data")[
T.fieldset[
T.input(type="hidden", name="t", value="uri"),
T.input(type="hidden", name="when_done", value="."),
T.legend(class_="freeform-form-label")["Add a link to a file or directory which is already in Tahoe-LAFS."],
- "New child name: ",
- T.input(type="text", name="name"), " ",
- "URI of new child: ",
- T.input(type="text", name="uri"), " ",
+ "New child name:"+SPACE,
+ T.input(type="text", name="name"), SPACE*2,
+ "URI of new child:"+SPACE,
+ T.input(type="text", name="uri"), SPACE,
T.input(type="submit", value="Attach"),
]]
- forms.append(T.div(class_="freeform-form")[mount])
+ forms.append(T.div(class_="freeform-form")[attach_form])
return forms
def render_results(self, ctx, data):
kids = {}
for name, (childnode, metadata) in children.iteritems():
assert IFilesystemNode.providedBy(childnode), childnode
- rw_uri = childnode.get_uri()
+ rw_uri = childnode.get_write_uri()
ro_uri = childnode.get_readonly_uri()
if IFileNode.providedBy(childnode):
- if childnode.is_readonly():
- rw_uri = None
kiddata = ("filenode", {'size': childnode.get_size(),
'mutable': childnode.is_mutable(),
})
+ if childnode.is_mutable():
+ mutable_type = childnode.get_version()
+ assert mutable_type in (SDMF_VERSION, MDMF_VERSION)
+ if mutable_type == MDMF_VERSION:
+ file_format = "MDMF"
+ else:
+ file_format = "SDMF"
+ else:
+ file_format = "CHK"
+ kiddata[1]['format'] = file_format
+
elif IDirectoryNode.providedBy(childnode):
- if childnode.is_readonly():
- rw_uri = None
kiddata = ("dirnode", {'mutable': childnode.is_mutable()})
else:
kiddata = ("unknown", {})
+
kiddata[1]["metadata"] = metadata
- if ro_uri:
- kiddata[1]["ro_uri"] = ro_uri
if rw_uri:
kiddata[1]["rw_uri"] = rw_uri
+ if ro_uri:
+ kiddata[1]["ro_uri"] = ro_uri
verifycap = childnode.get_verify_cap()
if verifycap:
kiddata[1]['verify_uri'] = verifycap.to_string()
+
kids[name] = kiddata
- if dirnode.is_readonly():
- drw_uri = None
- dro_uri = dirnode.get_uri()
- else:
- drw_uri = dirnode.get_uri()
- dro_uri = dirnode.get_readonly_uri()
+
+ drw_uri = dirnode.get_write_uri()
+ dro_uri = dirnode.get_readonly_uri()
contents = { 'children': kids }
if dro_uri:
contents['ro_uri'] = dro_uri
contents['verify_uri'] = verifycap.to_string()
contents['mutable'] = dirnode.is_mutable()
data = ("dirnode", contents)
- return simplejson.dumps(data, indent=1) + "\n"
+ json = simplejson.dumps(data, indent=1) + "\n"
+ return json
d.addCallback(_got)
d.addCallback(text_plain, ctx)
return d
-
def DirectoryURI(ctx, dirnode):
return text_plain(dirnode.get_uri(), ctx)
ctx.tag.attributes['value'] = name
return ctx.tag
-
class ManifestResults(rend.Page, ReloadMixin):
docFactory = getxmlfile("manifest.xhtml")
m = self.monitor
s = m.get_status()
+ if m.origin_si:
+ origin_base32 = base32.b2a(m.origin_si)
+ else:
+ origin_base32 = ""
status = { "stats": s["stats"],
"finished": m.is_finished(),
- "origin": base32.b2a(m.origin_si),
+ "origin": origin_base32,
}
if m.is_finished():
# don't return manifest/verifycaps/SIs unless the operation is
return simplejson.dumps(status, indent=1)
def _si_abbrev(self):
- return base32.b2a(self.monitor.origin_si)[:6]
+ si = self.monitor.origin_si
+ if not si:
+ return "<LIT>"
+ return base32.b2a(si)[:6]
def render_title(self, ctx):
return T.title["Manifest of SI=%s" % self._si_abbrev()]
v = node.get_verify_cap()
if v:
v = v.to_string()
- d["verifycap"] = v
+ d["verifycap"] = v or ""
r = node.get_repair_cap()
if r:
r = r.to_string()
- d["repaircap"] = r
+ d["repaircap"] = r or ""
si = node.get_storage_index()
if si:
si = base32.b2a(si)
- d["storage-index"] = si
+ d["storage-index"] = si or ""
j = simplejson.dumps(d, ensure_ascii=True)
assert "\n" not in j
if IDirectoryNode.providedBy(node):
data["type"] = "directory"
- else:
+ elif IFileNode.providedBy(node):
data["type"] = "file"
+ else:
+ data["type"] = "unknown"
v = node.get_verify_cap()
if v:
v = v.to_string()
- data["verifycap"] = v
+ data["verifycap"] = v or ""
r = node.get_repair_cap()
if r:
r = r.to_string()
- data["repaircap"] = r
+ data["repaircap"] = r or ""
si = node.get_storage_index()
if si:
si = base32.b2a(si)
- data["storage-index"] = si
+ data["storage-index"] = si or ""
if self.repair:
d = node.check_and_repair(self.monitor, self.verify, self.add_lease)
self.req.write(j+"\n")
return ""
-class UnknownNodeHandler(RenderMixin, rend.Page):
+class UnknownNodeHandler(RenderMixin, rend.Page):
def __init__(self, client, node, parentnode=None, name=None):
rend.Page.__init__(self)
assert node
self.node = node
+ self.parentnode = parentnode
+ self.name = name
def render_GET(self, ctx):
req = IRequest(ctx)
t = get_arg(req, "t", "").strip()
if t == "info":
return MoreInfo(self.node)
- raise WebError("GET unknown URI type: can only do t=info, not t=%s" % t)
-
-
+ if t == "json":
+ is_parent_known_immutable = self.parentnode and not self.parentnode.is_mutable()
+ if self.parentnode and self.name:
+ d = self.parentnode.get_metadata_for(self.name)
+ else:
+ d = defer.succeed(None)
+ d.addCallback(lambda md: UnknownJSONMetadata(ctx, self.node, md, is_parent_known_immutable))
+ return d
+ raise WebError("GET unknown URI type: can only do t=info and t=json, not t=%s.\n"
+ "Using a webapi server that supports a later version of Tahoe "
+ "may help." % t)
+
+def UnknownJSONMetadata(ctx, node, edge_metadata, is_parent_known_immutable):
+ rw_uri = node.get_write_uri()
+ ro_uri = node.get_readonly_uri()
+ data = ("unknown", {})
+ if ro_uri:
+ data[1]['ro_uri'] = ro_uri
+ if rw_uri:
+ data[1]['rw_uri'] = rw_uri
+ data[1]['mutable'] = True
+ elif is_parent_known_immutable or node.is_alleged_immutable():
+ data[1]['mutable'] = False
+ # else we don't know whether it is mutable.
+
+ if edge_metadata is not None:
+ data[1]['metadata'] = edge_metadata
+ return text_plain(simplejson.dumps(data, indent=1) + "\n", ctx)