1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
6 # Copyright (c) 1999 Christian Scholz (ruebe@aachen.heimat.de)
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU Affero General Public License as
10 # published by the Free Software Foundation, either version 3 of the
11 # License, or (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU Affero General Public License for more details.
18 # You should have received a copy of the GNU Affero General Public License
19 # along with this program. If not, see <http://www.gnu.org/licenses/>.
21 ##############################################################################
25 _logger = logging.getLogger(__name__)
26 import xml.dom.minidom
27 domimpl = xml.dom.minidom.getDOMImplementation()
28 from xml.dom.minicompat import StringTypes
32 from openerp.osv import osv
33 from openerp.tools.translate import _
36 from pywebdav.lib import utils
37 from pywebdav.lib.propfind import PROPFIND
38 from pywebdav.lib.report import REPORT
41 from DAV.propfind import PROPFIND
42 from DAV.report import REPORT
44 from openerp import tools
46 class Text2(xml.dom.minidom.Text):
47 def writexml(self, writer, indent="", addindent="", newl=""):
48 data = "%s%s%s" % (indent, self.data, newl)
49 data = data.replace("&", "&").replace("<", "<")
50 data = data.replace(">", ">")
53 class Prop2xml(object):
54 """ A helper class to convert property structs to DAV:XML
56 Written to generalize the use of _prop_child(), a class is
57 needed to hold some persistent data accross the recursions
58 of _prop_elem_child().
61 def __init__(self, doc, namespaces, nsnum):
62 """ Init the structure
63 @param doc the xml doc element
64 @param namespaces a dict of namespaces
65 @param nsnum the next namespace number to define
68 self.namespaces = namespaces
71 def createText2Node(self, data):
72 if not isinstance(data, StringTypes):
73 raise TypeError, "Node contents must be a string."
76 t.ownerDocument = self.doc
79 def _prop_child(self, xnode, ns, prop, value):
80 """Append a property xml node to xnode, with <prop>value</prop>
82 And a little smarter than that, it will consider namespace and
83 also allow nested properties etc.
85 :param ns the namespace of the <prop/> node
86 :param prop the name of the property
87 :param value the value. Can be:
89 tuple ('elem', 'ns') for empty sub-node <ns:elem />
90 tuple ('elem', 'ns', sub-elems) for sub-node with elements
91 tuple ('elem', 'ns', sub-elems, {attrs}) for sub-node with
92 optional elements and attributes
98 ns_prefix="ns"+str(self.namespaces.index(ns))+":"
100 pe = self.doc.createElement(ns_prefix+str(prop))
101 if hasattr(value, '__class__') and value.__class__.__name__ == 'Element':
102 pe.appendChild(value)
104 if ns == 'DAV:' and prop=="resourcetype" and isinstance(value, int):
107 ve = self.doc.createElement("D:collection")
110 self._prop_elem_child(pe, ns, value, ns_prefix)
112 xnode.appendChild(pe)
114 def _prop_elem_child(self, pnode, pns, v, pns_prefix):
116 if isinstance(v, list):
118 self._prop_elem_child(pnode, pns, vit, pns_prefix)
119 elif isinstance(v,tuple):
122 ns_prefix = pns_prefix
125 elif v[1] in self.namespaces:
126 ns_prefix="ns"+str(self.namespaces.index(v[1]))+":"
128 ns_prefix="ns"+str(self.nsnum)+":"
131 ve = self.doc.createElement(ns_prefix+v[0])
133 ve.setAttribute("xmlns:ns"+str(self.nsnum), v[1])
134 if len(v) > 2 and v[2] is not None:
135 if isinstance(v[2], (list, tuple)):
136 # support nested elements like:
137 # ( 'elem', 'ns:', [('sub-elem1', 'ns1'), ...]
138 self._prop_elem_child(ve, v[1], v[2], ns_prefix)
140 vt = self.createText2Node(tools.ustr(v[2]))
142 if len(v) > 3 and v[3]:
143 assert isinstance(v[3], dict)
144 for ak, av in v[3].items():
145 ve.setAttribute(ak, av)
146 pnode.appendChild(ve)
148 ve = self.createText2Node(tools.ustr(v))
149 pnode.appendChild(ve)
152 super_mk_prop_response = PROPFIND.mk_prop_response
153 def mk_prop_response(self, uri, good_props, bad_props, doc):
154 """ make a new <prop> result element
156 We differ between the good props and the bad ones for
157 each generating an extra <propstat>-Node (for each error
161 re=doc.createElement("D:response")
162 # append namespaces to response
164 namespaces = self.namespaces[:]
165 if 'DAV:' in namespaces:
166 namespaces.remove('DAV:')
167 for nsname in namespaces:
168 re.setAttribute("xmlns:ns"+str(nsnum),nsname)
171 propgen = Prop2xml(doc, namespaces, nsnum)
172 # write href information
173 uparts=urlparse.urlparse(uri)
176 fileloc += ';' + uparts[3]
177 if isinstance(fileloc, unicode):
178 fileloc = fileloc.encode('utf-8')
179 href=doc.createElement("D:href")
180 davpath = self._dataclass.parent.get_davpath()
181 if uparts[0] and uparts[1]:
182 hurl = '%s://%s%s%s' % (uparts[0], uparts[1], davpath, urllib.quote(fileloc))
184 # When the request has been relative, we don't have enough data to
185 # reply with absolute url here.
186 hurl = '%s%s' % (davpath, urllib.quote(fileloc))
187 huri=doc.createTextNode(hurl)
188 href.appendChild(huri)
191 # write good properties
192 ps=doc.createElement("D:propstat")
195 s=doc.createElement("D:status")
196 t=doc.createTextNode("HTTP/1.1 200 OK")
200 gp=doc.createElement("D:prop")
201 for ns in good_props.keys():
205 ns_prefix="ns"+str(namespaces.index(ns))+":"
206 for p,v in good_props[ns].items():
209 propgen._prop_child(gp, ns, p, v)
214 # now write the errors!
215 if len(bad_props.items()):
217 # write a propstat for each error code
218 for ecode in bad_props.keys():
219 ps=doc.createElement("D:propstat")
221 s=doc.createElement("D:status")
222 t=doc.createTextNode(utils.gen_estring(ecode))
225 bp=doc.createElement("D:prop")
228 for ns in bad_props[ecode].keys():
232 ns_prefix="ns"+str(self.namespaces.index(ns))+":"
234 for p in bad_props[ecode][ns]:
235 pe=doc.createElement(ns_prefix+str(p))
240 # return the new response element
244 def mk_propname_response(self, uri, propnames, doc):
245 """ make a new <prop> result element for a PROPNAME request
247 This will simply format the propnames list.
248 propnames should have the format {NS1 : [prop1, prop2, ...], NS2: ...}
251 re=doc.createElement("D:response")
253 # write href information
254 uparts=urlparse.urlparse(uri)
257 fileloc += ';' + uparts[3]
258 if isinstance(fileloc, unicode):
259 fileloc = fileloc.encode('utf-8')
260 href=doc.createElement("D:href")
261 davpath = self._dataclass.parent.get_davpath()
262 if uparts[0] and uparts[1]:
263 hurl = '%s://%s%s%s' % (uparts[0], uparts[1], davpath, urllib.quote(fileloc))
265 # When the request has been relative, we don't have enough data to
266 # reply with absolute url here.
267 hurl = '%s%s' % (davpath, urllib.quote(fileloc))
268 huri=doc.createTextNode(hurl)
269 href.appendChild(huri)
272 ps=doc.createElement("D:propstat")
275 for ns,plist in propnames.items():
277 pr=doc.createElement("D:prop")
282 ps.setAttribute("xmlns:"+nsp,ns)
285 # write propertynames
287 pe=doc.createElement(nsp+":"+p)
296 PROPFIND.mk_prop_response = mk_prop_response
297 PROPFIND.mk_propname_response = mk_propname_response
299 def mk_lock_response(self, uri, props):
300 """ Prepare the data response to a DAV LOCK command
302 This function is here, merely to be in the same file as the
303 ones above, that have similar code.
305 doc = domimpl.createDocument('DAV:', "D:prop", None)
306 ms = doc.documentElement
307 ms.setAttribute("xmlns:D", "DAV:")
308 # ms.tagName = 'D:multistatus'
311 propgen = Prop2xml(doc, namespaces, nsnum)
312 # write href information
313 uparts=urlparse.urlparse(uri)
316 fileloc += ';' + uparts[3]
317 if isinstance(fileloc, unicode):
318 fileloc = fileloc.encode('utf-8')
319 davpath = self.parent.get_davpath()
320 if uparts[0] and uparts[1]:
321 hurl = '%s://%s%s%s' % (uparts[0], uparts[1], davpath, urllib.quote(fileloc))
323 # When the request has been relative, we don't have enough data to
324 # reply with absolute url here.
325 hurl = '%s%s' % (davpath, urllib.quote(fileloc))
327 props.append( ('lockroot', 'DAV:', ('href', 'DAV:', (hurl))))
328 pld = doc.createElement('D:lockdiscovery')
330 propgen._prop_child(pld, 'DAV:', 'activelock', props)
332 return doc.toxml(encoding="utf-8")
334 super_create_prop = REPORT.create_prop
336 def create_prop(self):
338 if (self.filter is not None) and self._depth == "0":
339 hrefs = self.filter.getElementsByTagNameNS('DAV:', 'href')
344 return super_create_prop(self)
346 REPORT.create_prop = create_prop
350 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: