Revision: 860 http://trac.macosforge.org/projects/calendarserver/changeset/860 Author: cdaboo@apple.com Date: 2006-12-19 11:22:56 -0800 (Tue, 19 Dec 2006) Log Message: ----------- Principal resources are now collections. Reworked directory listing code into modular api so that the directory table can be embedded in other HTML rendered elsewehere. Modified Paths: -------------- CalendarServer/branches/users/cdaboo/cuproxy-857/twistedcaldav/directory/principal.py CalendarServer/branches/users/cdaboo/cuproxy-857/twistedcaldav/extensions.py CalendarServer/branches/users/cdaboo/cuproxy-857/twistedcaldav/resource.py Modified: CalendarServer/branches/users/cdaboo/cuproxy-857/twistedcaldav/directory/principal.py =================================================================== --- CalendarServer/branches/users/cdaboo/cuproxy-857/twistedcaldav/directory/principal.py 2006-12-19 16:46:17 UTC (rev 859) +++ CalendarServer/branches/users/cdaboo/cuproxy-857/twistedcaldav/directory/principal.py 2006-12-19 19:22:56 UTC (rev 860) @@ -234,6 +234,8 @@ self.record = record self.parent = parent self._url = joinURL(parent.principalCollectionURL(), record.shortName) + if self.isCollection(): + self._url += "/" # Provision in __init__() because principals are used prior to request # lookups. @@ -258,7 +260,23 @@ yield " ** %s **: %s\n" % (e.__class__.__name__, e) return "".join(genlist()) - output = ("".join(( + output = [ + """<html>""" + """<head>""" + """<title>%(title)s</title>""" + """<style>%(style)s</style>""" + """</head>""" + """<body>""" + """<div class="directory-listing">""" + """<h1>Principal Details</h1>""" + """<pre><blockquote>""" + % { + "title": unquote(request.uri), + "style": self.directoryStyleSheet(), + } + ] + + output.append("".join(( "Directory Information\n" "---------------------\n" "Directory GUID: %s\n" % (self.record.service.guid,), @@ -279,6 +297,15 @@ "\nCalendar user addresses:\n" , format_list(self.calendarUserAddresses), ))) + output.append( + """</pre></blockquote></div>""" + ) + + output.append(self.getDirectoryTable("Collection Listing")) + + output.append("</body></html>") + + output = "".join(output) if type(output) == unicode: output = output.encode("utf-8") mime_params = {"charset": "utf-8"} @@ -286,7 +313,7 @@ mime_params = {} response = Response(code=responsecode.OK, stream=output) - response.headers.setHeader("content-type", MimeType("text", "plain", mime_params)) + response.headers.setHeader("content-type", MimeType("text", "html", mime_params)) return response @@ -300,9 +327,6 @@ else: return self.record.shortName - def isCollection(self): - return False - ## # ACL ## Modified: CalendarServer/branches/users/cdaboo/cuproxy-857/twistedcaldav/extensions.py =================================================================== --- CalendarServer/branches/users/cdaboo/cuproxy-857/twistedcaldav/extensions.py 2006-12-19 16:46:17 UTC (rev 859) +++ CalendarServer/branches/users/cdaboo/cuproxy-857/twistedcaldav/extensions.py 2006-12-19 19:22:56 UTC (rev 860) @@ -131,7 +131,7 @@ """ Render a directory listing. """ - title = "Directory listing for %s" % urllib.unquote(request.path) + pagetitle = "Directory listing for %s" % urllib.unquote(request.path) output = [ """<html>""" @@ -140,51 +140,41 @@ """<style>%(style)s</style>""" """</head>""" """<body>""" + % { + "title": pagetitle, + "style": self.directoryStyleSheet(), + } + ] + + output.append(self.getDirectoryTable(urllib.unquote(request.uri))) + + output.append("</body></html>") + + response = Response(200, {}, "".join(output)) + response.headers.setHeader("content-type", MimeType("text", "html")) + return response + + def getDirectoryTable(self, title): + """ + Generate a directory listing table in HTML. + """ + + output = [ """<div class="directory-listing">""" """<h1>%(title)s</h1>""" """<table>""" """<tr><th>Name</th> <th>Size</th> <th>Last Modified</th> <th>MIME Type</th></tr>""" % { - "title": urllib.unquote(request.uri), - "style": self.directoryStyleSheet(), + "title": title, } ] - def orNone(value, default="?", f=None): - if value is None: - return default - elif f is not None: - return f(value) - else: - return value - even = False for name in sorted(self.listChildren()): child = self.getChild(name) - url = urllib.quote(name, '/') - if isinstance(child, SuperDAVFile) and child.isCollection(): - url += "/" - name += "/" + url, name, size, lastModified, contentType = self.getChildDirectoryEntry(child, name) - if isinstance(child, MetaDataMixin): - size = child.contentLength() - lastModified = child.lastModified() - contentType = child.contentType() - else: - size = None - lastModified = None - contentType = None - - if self.fp.isdir(): - contentType = "(collection)" - else: - contentType = orNone( - contentType, - default="-", - f=lambda m: "%s/%s %s" % (m.mediaType, m.mediaSubtype, m.params) - ) - # FIXME: gray out resources that are not readable output.append( """<tr class="%(even)s">""" @@ -197,22 +187,61 @@ "even": even and "even" or "odd", "url": url, "name": name, - "size": orNone(size), - "lastModified": orNone( - lastModified, - default="", - f=lambda t: time.strftime("%Y-%b-%d %H:%M", time.localtime(t)) - ), + "size": size, + "lastModified": lastModified, "type": contentType, } ) even = not even - output.append("</table></div></body></html>") + output.append("</table></div>") - response = Response(200, {}, "".join(output)) - response.headers.setHeader("content-type", MimeType("text", "html")) - return response + return "".join(output) + def getChildDirectoryEntry(self, child, name): + + def orNone(value, default="?", f=None): + if value is None: + return default + elif f is not None: + return f(value) + else: + return value + + url = urllib.quote(name, '/') + if isinstance(child, SuperDAVFile) and child.isCollection(): + url += "/" + name += "/" + + if isinstance(child, MetaDataMixin): + size = child.contentLength() + lastModified = child.lastModified() + contentType = child.contentType() + else: + size = None + lastModified = None + contentType = None + + if self.fp.isdir(): + contentType = "(collection)" + else: + contentType = self._orNone( + contentType, + default="-", + f=lambda m: "%s/%s %s" % (m.mediaType, m.mediaSubtype, m.params) + ) + + return ( + url, + name, + orNone(size), + orNone( + lastModified, + default="", + f=lambda t: time.strftime("%Y-%b-%d %H:%M", time.localtime(t)) + ), + contentType, + ) + class ReadOnlyResourceMixIn (object): """ Read only resource. Modified: CalendarServer/branches/users/cdaboo/cuproxy-857/twistedcaldav/resource.py =================================================================== --- CalendarServer/branches/users/cdaboo/cuproxy-857/twistedcaldav/resource.py 2006-12-19 16:46:17 UTC (rev 859) +++ CalendarServer/branches/users/cdaboo/cuproxy-857/twistedcaldav/resource.py 2006-12-19 19:22:56 UTC (rev 860) @@ -551,14 +551,15 @@ (calendarserver_namespace, "notifications-URL"), ) + def isCollection(self): + return True + def readProperty(self, property, request): def defer(): if type(property) is tuple: qname = property - sname = "{%s}%s" % property else: qname = property.qname() - sname = property.sname() namespace, name = qname