[MERGE] lp:~xrg/openobject-addons/trunk-patch18
[odoo/odoo.git] / addons / document_webdav / webdav.py
index 1fd530c..1b60ade 100644 (file)
 
 import xml.dom.minidom
 domimpl = xml.dom.minidom.getDOMImplementation()
+from xml.dom.minicompat import StringTypes
+
 import urlparse
 import urllib
-from DAV import utils
-from DAV.propfind import PROPFIND
-import tools
-
+from osv import osv
+from tools.translate import _
 
-super_mk_prop_response = PROPFIND.mk_prop_response
-def mk_prop_response(self, uri, good_props, bad_props, doc):
-    """ make a new <prop> result element
+try:
+    from DAV import utils
+    from DAV.propfind import PROPFIND
+    from DAV.report import REPORT
+except ImportError:
+    raise osv.except_osv(_('PyWebDAV Import Error!'), _('Please install PyWebDAV from http://code.google.com/p/pywebdav/downloads/detail?name=PyWebDAV-0.9.4.tar.gz&can=2&q=/'))
 
-    We differ between the good props and the bad ones for
-    each generating an extra <propstat>-Node (for each error
-    one, that means).
+import tools
 
+class Text2(xml.dom.minidom.Text):
+    def writexml(self, writer, indent="", addindent="", newl=""):
+        data = "%s%s%s" % (indent, self.data, newl)
+        data = data.replace("&", "&amp;").replace("<", "&lt;")
+        data = data.replace(">", "&gt;")
+        writer.write(data)
+
+class Prop2xml(object):
+    """ A helper class to convert property structs to DAV:XML
+    
+        Written to generalize the use of _prop_child(), a class is 
+        needed to hold some persistent data accross the recursions 
+        of _prop_elem_child().
     """
-    re=doc.createElement("D:response")
-    # append namespaces to response
-    nsnum=0
-    namespaces = self.namespaces
-    if 'DAV:' in namespaces:
-        namespaces.remove('DAV:')
-    for nsname in namespaces:
-        re.setAttribute("xmlns:ns"+str(nsnum),nsname)
-        nsnum=nsnum+1
-
-    def _prop_child(xnode, ns, prop, value):
+    
+    def __init__(self, doc, namespaces, nsnum):
+        """ Init the structure
+        @param doc the xml doc element
+        @param namespaces a dict of namespaces
+        @param nsnum the next namespace number to define
+        """
+        self.doc = doc
+        self.namespaces = namespaces
+        self.nsnum = nsnum
+
+    def createText2Node(self, data):
+        if not isinstance(data, StringTypes):
+            raise TypeError, "node contents must be a string"
+        t = Text2()
+        t.data = data
+        t.ownerDocument = self.doc
+        return t
+
+    def _prop_child(self, xnode, ns, prop, value):
         """Append a property xml node to xnode, with <prop>value</prop>
-           
+
            And a little smarter than that, it will consider namespace and
            also allow nested properties etc.
-           
+
            :param ns the namespace of the <prop/> node
            :param prop the name of the property
            :param value the value. Can be:
                     string: text node
                     tuple ('elem', 'ns') for empty sub-node <ns:elem />
                     tuple ('elem', 'ns', sub-elems) for sub-node with elements
+                    tuple ('elem', 'ns', sub-elems, {attrs}) for sub-node with 
+                            optional elements and attributes
                     list, of above tuples
         """
         if ns == 'DAV:':
             ns_prefix = 'D:'
         else:
-            ns_prefix="ns"+str(namespaces.index(ns))+":"
+            ns_prefix="ns"+str(self.namespaces.index(ns))+":"
 
-        pe=doc.createElement(ns_prefix+str(prop))
+        pe = self.doc.createElement(ns_prefix+str(prop))
         if hasattr(value, '__class__') and value.__class__.__name__ == 'Element':
             pe.appendChild(value)
         else:
             if ns == 'DAV:' and prop=="resourcetype" and isinstance(value, int):
                 # hack, to go..
                 if value == 1:
-                    ve=doc.createElement("D:collection")
+                    ve = self.doc.createElement("D:collection")
                     pe.appendChild(ve)
             else:
-                _prop_elem_child(pe, ns, value, ns_prefix)
+                self._prop_elem_child(pe, ns, value, ns_prefix)
 
             xnode.appendChild(pe)
 
-    def _prop_elem_child(pnode, pns, v, pns_prefix):
-        
+    def _prop_elem_child(self, pnode, pns, v, pns_prefix):
+
         if isinstance(v, list):
             for vit in v:
-                _prop_elem_child(pnode, pns, vit, pns_prefix)
+                self._prop_elem_child(pnode, pns, vit, pns_prefix)
         elif isinstance(v,tuple):
             need_ns = False
             if v[1] == pns:
                 ns_prefix = pns_prefix
             elif v[1] == 'DAV:':
                 ns_prefix = 'D:'
-            elif v[1] in namespaces:
-                ns_prefix="ns"+str(namespaces.index(v[1]))+":"
+            elif v[1] in self.namespaces:
+                ns_prefix="ns"+str(self.namespaces.index(v[1]))+":"
             else:
-                # namespaces.append(v[1])
-                # nsnum += 1
-                ns_prefix="ns"+str(nsnum)+":"
+                ns_prefix="ns"+str(self.nsnum)+":"
                 need_ns = True
 
-            ve=doc.createElement(ns_prefix+v[0])
+            ve = self.doc.createElement(ns_prefix+v[0])
             if need_ns:
-                ve.setAttribute("xmlns:ns"+str(nsnum), v[1])
-            if len(v) > 2 and isinstance(v[2], list):
-                # support nested elements like:
-                # ( 'elem', 'ns:', [('sub-elem1', 'ns1'), ...]
-                _prop_elem_child(ve, v[1], v[2], ns_prefix)
+                ve.setAttribute("xmlns:ns"+str(self.nsnum), v[1])
+            if len(v) > 2 and v[2] is not None:
+                if isinstance(v[2], (list, tuple)):
+                    # support nested elements like:
+                    # ( 'elem', 'ns:', [('sub-elem1', 'ns1'), ...]
+                    self._prop_elem_child(ve, v[1], v[2], ns_prefix)
+                else:
+                    vt = self.createText2Node(tools.ustr(v[2]))
+                    ve.appendChild(vt)
+            if len(v) > 3 and v[3]:
+                assert isinstance(v[3], dict)
+                for ak, av in v[3].items():
+                    ve.setAttribute(ak, av)
             pnode.appendChild(ve)
         else:
-            ve=doc.createTextNode(tools.ustr(v))
+            ve = self.createText2Node(tools.ustr(v))
             pnode.appendChild(ve)
 
+
+super_mk_prop_response = PROPFIND.mk_prop_response
+def mk_prop_response(self, uri, good_props, bad_props, doc):
+    """ make a new <prop> result element
+
+    We differ between the good props and the bad ones for
+    each generating an extra <propstat>-Node (for each error
+    one, that means).
+
+    """
+    re=doc.createElement("D:response")
+    # append namespaces to response
+    nsnum=0
+    namespaces = self.namespaces[:]
+    if 'DAV:' in namespaces:
+        namespaces.remove('DAV:')
+    for nsname in namespaces:
+        re.setAttribute("xmlns:ns"+str(nsnum),nsname)
+        nsnum=nsnum+1
+
+    propgen = Prop2xml(doc, namespaces, nsnum)
     # write href information
     uparts=urlparse.urlparse(uri)
     fileloc=uparts[2]
@@ -119,7 +171,12 @@ def mk_prop_response(self, uri, good_props, bad_props, doc):
         fileloc = fileloc.encode('utf-8')
     href=doc.createElement("D:href")
     davpath = self._dataclass.parent.get_davpath()
-    hurl = '%s://%s%s%s' % (uparts[0], uparts[1], davpath, urllib.quote(fileloc))
+    if uparts[0] and uparts[1]:
+        hurl = '%s://%s%s%s' % (uparts[0], uparts[1], davpath, urllib.quote(fileloc))
+    else:
+        # When the request has been relative, we don't have enough data to
+        # reply with absolute url here.
+        hurl = '%s%s' % (davpath, urllib.quote(fileloc))
     huri=doc.createTextNode(hurl)
     href.appendChild(huri)
     re.appendChild(href)
@@ -128,6 +185,10 @@ def mk_prop_response(self, uri, good_props, bad_props, doc):
     ps=doc.createElement("D:propstat")
     if good_props:
         re.appendChild(ps)
+    s=doc.createElement("D:status")
+    t=doc.createTextNode("HTTP/1.1 200 OK")
+    s.appendChild(t)
+    ps.appendChild(s)
 
     gp=doc.createElement("D:prop")
     for ns in good_props.keys():
@@ -136,15 +197,11 @@ def mk_prop_response(self, uri, good_props, bad_props, doc):
         else:
             ns_prefix="ns"+str(namespaces.index(ns))+":"
         for p,v in good_props[ns].items():
-            if not v:
+            if v is None:
                 continue
-            _prop_child(gp, ns, p, v)
+            propgen._prop_child(gp, ns, p, v)
 
     ps.appendChild(gp)
-    s=doc.createElement("D:status")
-    t=doc.createTextNode("HTTP/1.1 200 OK")
-    s.appendChild(t)
-    ps.appendChild(s)
     re.appendChild(ps)
 
     # now write the errors!
@@ -154,6 +211,10 @@ def mk_prop_response(self, uri, good_props, bad_props, doc):
         for ecode in bad_props.keys():
             ps=doc.createElement("D:propstat")
             re.appendChild(ps)
+            s=doc.createElement("D:status")
+            t=doc.createTextNode(utils.gen_estring(ecode))
+            s.appendChild(t)
+            ps.appendChild(s)
             bp=doc.createElement("D:prop")
             ps.appendChild(bp)
 
@@ -167,10 +228,6 @@ def mk_prop_response(self, uri, good_props, bad_props, doc):
                 pe=doc.createElement(ns_prefix+str(p))
                 bp.appendChild(pe)
 
-            s=doc.createElement("D:status")
-            t=doc.createTextNode(utils.gen_estring(ecode))
-            s.appendChild(t)
-            ps.appendChild(s)
             re.appendChild(ps)
 
     # return the new response element
@@ -193,7 +250,12 @@ def mk_propname_response(self,uri,propnames,doc):
         fileloc = fileloc.encode('utf-8')
     href=doc.createElement("D:href")
     davpath = self._dataclass.parent.get_davpath()
-    hurl = '%s://%s%s%s' % (uparts[0], uparts[1], davpath, urllib.quote(fileloc))
+    if uparts[0] and uparts[1]:
+        hurl = '%s://%s%s%s' % (uparts[0], uparts[1], davpath, urllib.quote(fileloc))
+    else:
+        # When the request has been relative, we don't have enough data to
+        # reply with absolute url here.
+        hurl = '%s%s' % (davpath, urllib.quote(fileloc))
     huri=doc.createTextNode(hurl)
     href.appendChild(huri)
     re.appendChild(href)
@@ -225,3 +287,51 @@ def mk_propname_response(self,uri,propnames,doc):
 PROPFIND.mk_prop_response = mk_prop_response
 PROPFIND.mk_propname_response = mk_propname_response
 
+def mk_lock_response(self, uri, props):
+    """ Prepare the data response to a DAV LOCK command
+    
+    This function is here, merely to be in the same file as the
+    ones above, that have similar code.
+    """
+    doc = domimpl.createDocument('DAV:', "D:prop", None)
+    ms = doc.documentElement
+    ms.setAttribute("xmlns:D", "DAV:")
+    # ms.tagName = 'D:multistatus'
+    namespaces = []
+    nsnum = 0
+    propgen = Prop2xml(doc, namespaces, nsnum)
+    # write href information
+    uparts=urlparse.urlparse(uri)
+    fileloc=uparts[2]
+    if isinstance(fileloc, unicode):
+        fileloc = fileloc.encode('utf-8')
+    davpath = self.parent.get_davpath()
+    if uparts[0] and uparts[1]:
+        hurl = '%s://%s%s%s' % (uparts[0], uparts[1], davpath, urllib.quote(fileloc))
+    else:
+        # When the request has been relative, we don't have enough data to
+        # reply with absolute url here.
+        hurl = '%s%s' % (davpath, urllib.quote(fileloc))
+        
+    props.append( ('lockroot', 'DAV:', ('href', 'DAV:', (hurl))))
+    pld = doc.createElement('D:lockdiscovery')
+    ms.appendChild(pld)
+    propgen._prop_child(pld, 'DAV:', 'activelock', props)
+
+    return doc.toxml(encoding="utf-8")
+
+super_create_prop = REPORT.create_prop
+
+def create_prop(self):
+    try:
+        if (self.filter is not None) and self._depth == "0":
+            hrefs = self.filter.getElementsByTagNameNS('DAV:', 'href')
+            if hrefs:
+                self._depth = "1"
+    except Exception:
+        pass
+    return super_create_prop(self)
+
+REPORT.create_prop = create_prop
+
+#eof