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 foolscap.api import fireEventually
-from allmydata.util import base32, time_format
+from allmydata.util import base32
+from allmydata.util.encodingutil import to_str
from allmydata.uri import from_string_dirnode
from allmydata.interfaces import IDirectoryNode, IFileNode, IFilesystemNode, \
IImmutableFileNode, IMutableFileNode, ExistingChildError, \
boolean_of_arg, get_arg, get_root, parse_replace_arg, \
should_create_intermediate_directories, \
getxmlfile, RenderMixin, humanize_failure, convert_children_json, \
- get_format, get_mutable_type
+ get_format, get_mutable_type, get_filenode_metadata, render_time
from allmydata.web.filenode import ReplaceMeMixin, \
FileNodeHandler, PlaceHolderNodeHandler
from allmydata.web.check_results import CheckResultsRenderer, \
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 DirectoryReadonlyURI(ctx, self.node)
if t == 'rename-form':
return RenameForm(self.node)
- if t == 'move-form':
- return MoveForm(self.node)
raise WebError("GET directory: bad t=%s" % t)
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_unlink(req)
elif t == "rename":
d = self._POST_rename(req)
- elif t == "move":
- d = self._POST_move(req)
+ elif t == "relink":
+ d = self._POST_relink(req)
elif t == "check":
d = self._POST_check(req)
elif t == "start-deep-check":
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")
raise WebError("set-uri requires a name")
charset = get_arg(req, "_charset", "utf-8")
name = name.decode(charset)
- replace = boolean_of_arg(get_arg(req, "replace", "true"))
+ replace = parse_replace_arg(get_arg(req, "replace", "true"))
# 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
return d
def _POST_rename(self, req):
+ # rename is identical to relink, but to_dir is not allowed
+ # and to_name is required.
+ if get_arg(req, "to_dir") is not None:
+ raise WebError("to_dir= is not valid for rename")
+ if get_arg(req, "to_name") is None:
+ raise WebError("to_name= is required for rename")
+ return self._POST_relink(req)
+
+ def _POST_relink(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 from_name or not to_name:
- raise WebError("rename requires from_name and to_name")
- if from_name == to_name:
- return defer.succeed("redundant rename")
-
- # allow from_name to contain slashes, so they can fix names that were
- # accidentally created with them. But disallow them in to_name, to
- # discourage the practice.
- if "/" in to_name:
- raise WebError("to_name= may not contain a slash", http.BAD_REQUEST)
-
- replace = boolean_of_arg(get_arg(req, "replace", "true"))
- d = self.node.move_child_to(from_name, self.node, to_name, replace)
- d.addCallback(lambda res: "thing renamed")
- return d
+ replace = parse_replace_arg(get_arg(req, "replace", "true"))
- 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)
+ else:
+ raise WebError("from_name= is required")
+
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:
+ else:
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=
+ # cause confusion.
if "/" in from_name:
raise WebError("from_name= may not contain a slash",
http.BAD_REQUEST)
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)))
+ to_dir = get_arg(req, "to_dir")
+ if to_dir is not None and to_dir != self.node.get_write_uri():
+ to_dir = to_dir.strip()
+ to_dir = to_dir.decode(charset)
+ assert isinstance(to_dir, unicode)
+ to_path = to_dir.split(u"/")
+ to_root = self.client.nodemaker.create_from_cap(to_str(to_path[0]))
+ if not IDirectoryNode.providedBy(to_root):
+ raise WebError("to_dir is not a directory", http.BAD_REQUEST)
+ d = to_root.get_child_at_path(to_path[1:])
else:
- raise WebError("invalid target_type parameter", http.BAD_REQUEST)
+ d = defer.succeed(self.node)
- def is_target_node_usable(target_node):
- if not IDirectoryNode.providedBy(target_node):
+ def _got_new_parent(new_parent):
+ if not IDirectoryNode.providedBy(new_parent):
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))
+
+ return self.node.move_child_to(from_name, new_parent,
+ to_name, replace)
+ d.addCallback(_got_new_parent)
d.addCallback(lambda res: "thing moved")
return d
return d
def _POST_set_children(self, req):
- replace = boolean_of_arg(get_arg(req, "replace", "true"))
+ replace = parse_replace_arg(get_arg(req, "replace", "true"))
req.content.seek(0)
body = req.content.read()
try:
if self.node.is_unknown() or self.node.is_readonly():
unlink = "-"
rename = "-"
- move = "-"
else:
# this creates a button which will cause our _POST_unlink method
# to be invoked, which unlinks the file and then redirects the
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='unlink', name="unlink"),
+ T.input(type='submit', _class='btn', 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"),
- ]
-
- move = T.form(action=here, method="get")[
- T.input(type='hidden', name='t', value='move-form'),
- T.input(type='hidden', name='name', value=name),
- T.input(type='hidden', name='when_done', value="."),
- T.input(type='submit', value='move', name="move"),
+ T.input(type='submit', _class='btn', value='rename/relink', name="rename"),
]
ctx.fillSlots("unlink", unlink)
ctx.fillSlots("rename", rename)
- ctx.fillSlots("move", move)
times = []
linkcrtime = metadata.get('tahoe', {}).get("linkcrtime")
if linkcrtime is not None:
- times.append("lcr: " + time_format.iso_local(linkcrtime))
+ times.append("lcr: " + render_time(linkcrtime))
else:
# For backwards-compatibility with links last modified by Tahoe < 1.4.0:
if "ctime" in metadata:
- ctime = time_format.iso_local(metadata["ctime"])
+ ctime = render_time(metadata["ctime"])
times.append("c: " + ctime)
linkmotime = metadata.get('tahoe', {}).get("linkmotime")
if linkmotime is not None:
if times:
times.append(T.br())
- times.append("lmo: " + time_format.iso_local(linkmotime))
+ times.append("lmo: " + render_time(linkmotime))
else:
# For backwards-compatibility with links last modified by Tahoe < 1.4.0:
if "mtime" in metadata:
- mtime = time_format.iso_local(metadata["mtime"])
+ mtime = render_time(metadata["mtime"])
if times:
times.append(T.br())
times.append("m: " + mtime)
# 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(target_uri))
- ctx.fillSlots("filename",
- T.a(href=uri_link)[html.escape(name)])
+ ctx.fillSlots("filename", T.a(href=uri_link)[name])
if not target.is_mutable():
dirtype = "DIR-IMM"
elif target.is_readonly():
else:
# unknown
- ctx.fillSlots("filename", html.escape(name))
+ ctx.fillSlots("filename", name)
if target.get_write_uri() is not None:
unknowntype = "?"
elif not self.node.is_mutable() or target.is_alleged_immutable():
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:"+SPACE,
+ "New directory name:"+SPACE, T.br,
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)"],
+ T.div(class_="form-inline")[
+ mkdir_sdmf, T.label(for_='mutable-directory-sdmf')[SPACE, "SDMF"], SPACE*2,
+ mkdir_mdmf, T.label(for_='mutable-directory-mdmf')[SPACE, "MDMF (experimental)"]
+ ],
+ T.input(type="submit", class_="btn", value="Create")
]]
forms.append(T.div(class_="freeform-form")[mkdir_form])
T.legend(class_="freeform-form-label")["Upload a file to this directory"],
"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)"],
+ T.div(class_="form-inline")[
+ upload_chk, T.label(for_="upload-chk") [SPACE, "Immutable"], SPACE*2,
+ upload_sdmf, T.label(for_="upload-sdmf")[SPACE, "SDMF"], SPACE*2,
+ upload_mdmf, T.label(for_="upload-mdmf")[SPACE, "MDMF (experimental)"]
+ ],
+ T.input(type="submit", class_="btn", value="Upload"), SPACE*2,
]]
forms.append(T.div(class_="freeform-form")[upload_form])
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:"+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"),
- ]]
+ T.fieldset[ T.div(class_="form-inline")[
+ 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:"+SPACE,
+ T.input(type="text", name="name"), SPACE*2, T.br,
+ "URI of new child:"+SPACE,
+ T.input(type="text", name="uri"), SPACE,
+ T.input(type="submit", class_="btn", value="Attach"),
+ ]]]
forms.append(T.div(class_="freeform-form")[attach_form])
return forms
req = IRequest(ctx)
return get_arg(req, "results", "")
-
def DirectoryJSONMetadata(ctx, dirnode):
d = dirnode.list()
def _got(children):
rw_uri = childnode.get_write_uri()
ro_uri = childnode.get_readonly_uri()
if IFileNode.providedBy(childnode):
- 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
-
+ kiddata = ("filenode", get_filenode_metadata(childnode))
elif IDirectoryNode.providedBy(childnode):
kiddata = ("dirnode", {'mutable': childnode.is_mutable()})
else:
ctx.tag.attributes['value'] = name
return ctx.tag
-class MoveForm(rend.Page):
- addSlash = True
- docFactory = getxmlfile("move-form.xhtml")
-
- def render_title(self, ctx, data):
- return ctx.tag["Directory SI=%s" % abbreviated_dirnode(self.original)]
-
- def render_header(self, ctx, data):
- header = ["Move "
- "from directory SI=%s" % abbreviated_dirnode(self.original),
- ]
-
- if self.original.is_readonly():
- header.append(" (readonly!)")
- header.append(":")
- return ctx.tag[header]
-
- def render_when_done(self, ctx, data):
- return T.input(type="hidden", name="when_done", value=".")
-
- def render_get_name(self, ctx, data):
- req = IRequest(ctx)
- name = get_arg(req, "name", "")
- ctx.tag.attributes['value'] = name
- return ctx.tag
-
-
class ManifestResults(rend.Page, ReloadMixin):
docFactory = getxmlfile("manifest.xhtml")