diff --git a/addons/base_calendar/base_calendar.py b/addons/base_calendar/base_calendar.py
index f4f1e68a269..7d3b43664ed 100644
--- a/addons/base_calendar/base_calendar.py
+++ b/addons/base_calendar/base_calendar.py
@@ -461,13 +461,13 @@ property or property parameter."),
body = html_invitation % body_vals
if mail_to and email_from:
tools.email_send(
- email_from,
- mail_to,
- sub,
- body,
- subtype='html',
- reply_to=email_from
- )
+ email_from,
+ mail_to,
+ sub,
+ body,
+ subtype='html',
+ reply_to=email_from
+ )
return True
def onchange_user_id(self, cr, uid, ids, user_id, *args, **argv):
@@ -659,41 +659,41 @@ class calendar_alarm(osv.osv):
__attribute__ = {}
_columns = {
- 'alarm_id': fields.many2one('res.alarm', 'Basic Alarm', ondelete='cascade'),
- 'name': fields.char('Summary', size=124, help="""Contains the text to be \
- used as the message subject for email \
- or contains the text to be used for display"""),
- 'action': fields.selection([('audio', 'Audio'), ('display', 'Display'), \
- ('procedure', 'Procedure'), ('email', 'Email') ], 'Action', \
- required=True, help="Defines the action to be invoked when an alarm is triggered"),
- 'description': fields.text('Description', help='Provides a more complete \
- description of the calendar component, than that \
- provided by the "SUMMARY" property'),
- 'attendee_ids': fields.many2many('calendar.attendee', 'alarm_attendee_rel', \
- 'alarm_id', 'attendee_id', 'Attendees', readonly=True),
- 'attach': fields.binary('Attachment', help="""* Points to a sound resource,\
- which is rendered when the alarm is triggered for audio,
- * File which is intended to be sent as message attachments for email,
- * Points to a procedure resource, which is invoked when\
- the alarm is triggered for procedure."""),
- 'res_id': fields.integer('Resource ID'),
- 'model_id': fields.many2one('ir.model', 'Model'),
- 'user_id': fields.many2one('res.users', 'Owner'),
- 'event_date': fields.datetime('Event Date'),
- 'event_end_date': fields.datetime('Event End Date'),
- 'trigger_date': fields.datetime('Trigger Date', readonly="True"),
- 'state':fields.selection([('draft', 'Draft'),
- ('run', 'Run'),
- ('stop', 'Stop'),
- ('done', 'Done'),
- ], 'State', select=True, readonly=True),
- }
+ 'alarm_id': fields.many2one('res.alarm', 'Basic Alarm', ondelete='cascade'),
+ 'name': fields.char('Summary', size=124, help="""Contains the text to be \
+ used as the message subject for email \
+ or contains the text to be used for display"""),
+ 'action': fields.selection([('audio', 'Audio'), ('display', 'Display'), \
+ ('procedure', 'Procedure'), ('email', 'Email') ], 'Action', \
+ required=True, help="Defines the action to be invoked when an alarm is triggered"),
+ 'description': fields.text('Description', help='Provides a more complete \
+ description of the calendar component, than that \
+ provided by the "SUMMARY" property'),
+ 'attendee_ids': fields.many2many('calendar.attendee', 'alarm_attendee_rel', \
+ 'alarm_id', 'attendee_id', 'Attendees', readonly=True),
+ 'attach': fields.binary('Attachment', help="""* Points to a sound resource,\
+ which is rendered when the alarm is triggered for audio,
+ * File which is intended to be sent as message attachments for email,
+ * Points to a procedure resource, which is invoked when\
+ the alarm is triggered for procedure."""),
+ 'res_id': fields.integer('Resource ID'),
+ 'model_id': fields.many2one('ir.model', 'Model'),
+ 'user_id': fields.many2one('res.users', 'Owner'),
+ 'event_date': fields.datetime('Event Date'),
+ 'event_end_date': fields.datetime('Event End Date'),
+ 'trigger_date': fields.datetime('Trigger Date', readonly="True"),
+ 'state':fields.selection([
+ ('draft', 'Draft'),
+ ('run', 'Run'),
+ ('stop', 'Stop'),
+ ('done', 'Done'),
+ ], 'State', select=True, readonly=True),
+ }
_defaults = {
- 'action': lambda *x: 'email',
- 'state': lambda *x: 'run',
- }
-
+ 'action': lambda *x: 'email',
+ 'state': lambda *x: 'run',
+ }
def create(self, cr, uid, vals, context={}):
"""
create new record.
@@ -981,7 +981,7 @@ class calendar_event(osv.osv):
'Show as'),
'base_calendar_url': fields.char('Caldav URL', size=264),
'exdate': fields.text('Exception Date/Times', help="This property \
-defines the list of date/time exceptions for arecurring calendar component."),
+ defines the list of date/time exceptions for arecurring calendar component."),
'exrule': fields.char('Exception Rule', size=352, help="defines a \
rule or repeating pattern for anexception to a recurrence set"),
'rrule': fields.function(_get_rulestring, type='char', size=124, method=True, \
diff --git a/addons/caldav/DAV/BufferingHTTPServer.py b/addons/caldav/DAV/BufferingHTTPServer.py
deleted file mode 100644
index bf2e9e59800..00000000000
--- a/addons/caldav/DAV/BufferingHTTPServer.py
+++ /dev/null
@@ -1,101 +0,0 @@
-#!/usr/bin/env python
-
-"""
- Buffering HTTP Server
- Copyright (C) 1999 Christian Scholz (ruebe@aachen.heimat.de)
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public
- License as published by the Free Software Foundation; either
- version 2 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with this library; if not, write to the Free
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-"""
-
-
-from utils import VERSION, AUTHOR
-__version__ = VERSION
-__author__ = AUTHOR
-
-from BaseHTTPServer import BaseHTTPRequestHandler
-import os
-
-class BufferedHTTPRequestHandler(BaseHTTPRequestHandler):
- """
- Buffering HTTP Request Handler
-
- This class is an extension to the BaseHTTPRequestHandler
- class which buffers the whole output and sends it at once
- after the processing if the request is finished.
-
- This makes it possible to work together with some clients
- which otherwise would break (e.g. cadaver)
-
- """
-
-
- def _init_buffer(self):
- """initialize the buffer.
-
- If you override the handle() method remember to call
- this (see below)
- """
- self.__buffer = ""
- self.__outfp = os.tmpfile()
-
- def _append(self,s):
- """ append a string to the buffer """
- self.__buffer = self.__buffer+s
-
- def _flush(self):
- """ flush the buffer to wfile """
- self.wfile.write(self.__buffer)
- self.__outfp.write(self.__buffer)
- self.__outfp.flush()
- self.wfile.flush()
- self.__buffer = ""
-
- def handle(self):
- """ Handle a HTTP request """
- self._init_buffer()
- BaseHTTPRequestHandler.handle(self)
- self._flush()
-
- def send_header(self, keyword, value):
- """Send a MIME header."""
- if self.request_version != 'HTTP/0.9':
- self._append("%s: %s\r\n" % (keyword, value))
-
- def end_headers(self):
- """Send the blank line ending the MIME headers."""
- if self.request_version != 'HTTP/0.9':
- self._append("\r\n")
-
- def send_response(self, code, message=None):
- self.log_request(code)
-
- if message is None:
- if self.responses.has_key(code):
- message = self.responses[code][0]
- else:
- message = ''
-
- if self.request_version != 'HTTP/0.9':
- self._append("%s %s %s\r\n" %
- (self.protocol_version, str(code), message))
-
- self.send_header('Server', self.version_string())
- self.send_header('Connection', 'close')
- self.send_header('Date', self.date_time_string())
-
- protocol_version="HTTP/1.1"
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
\ No newline at end of file
diff --git a/addons/caldav/DAV/WebDAVServer.py b/addons/caldav/DAV/WebDAVServer.py
deleted file mode 100644
index fa30aa6b82b..00000000000
--- a/addons/caldav/DAV/WebDAVServer.py
+++ /dev/null
@@ -1,381 +0,0 @@
-#!/usr/bin/env python
-
-"""
- Python WebDAV Server.
- Copyright (C) 1999 Christian Scholz (ruebe@aachen.heimat.de)
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public
- License as published by the Free Software Foundation; either
- version 2 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with this library; if not, write to the Free
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-This module builds on AuthServer by implementing the standard DAV
-methods.
-
-Subclass this class and specify an IFACE_CLASS. See example.
-
-"""
-
-DEBUG = None
-
-from utils import VERSION, AUTHOR
-__version__ = VERSION
-__author__ = AUTHOR
-
-from propfind import PROPFIND
-from delete import DELETE
-from davcopy import COPY
-from davmove import MOVE
-
-from string import atoi, split
-from status import STATUS_CODES
-from errors import *
-
-import os
-import sys
-import time
-import socket
-import string
-import posixpath
-import base64
-import urlparse
-import urllib
-import BaseHTTPServer
-
-class DAVRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
- """Simple DAV request handler with
-
- - GET
- - HEAD
- - PUT
- - OPTIONS
- - PROPFIND
- - PROPPATCH
- - MKCOL
-
- It uses the resource/collection classes for serving and
- storing content.
-
- """
-
- server_version = "DAV/" + __version__
- protocol_version = 'HTTP/1.1'
-
- ### utility functions
- def _log(self, message):
- pass
-
- def _append(self, s):
- """ write the string to wfile """
- self.wfile.write(s)
-
- def send_body(self, DATA, code, msg, desc, ctype='application/octet-stream', headers=None):
- """ send a body in one part """
-
- if not headers:
- headers = {}
- self.send_response(code, message=msg)
- self.send_header("Connection", "keep-alive")
- self.send_header("Accept-Ranges", "bytes")
-
- for a, v in headers.items():
- self.send_header(a, v)
-
- if DATA:
- self.send_header("Content-Length", str(len(DATA)))
- self.send_header("Content-Type", ctype)
- else:
- self.send_header("Content-Length", "0")
-
- self.end_headers()
- if DATA:
- self._append(DATA)
-
- def send_body_chunks(self, DATA, code, msg, desc, ctype='text/xml; encoding="utf-8"'):
- """ send a body in chunks """
-
- self.responses[207]=(msg, desc)
- self.send_response(code, message=msg)
- self.send_header("Content-type", ctype)
- self.send_header("Connection", "keep-alive")
- self.send_header("Transfer-Encoding", "chunked")
- self.end_headers()
- self._append(hex(len(DATA))[2:]+"\r\n")
- self._append(DATA)
- self._append("\r\n")
- self._append("0\r\n")
- self._append("\r\n")
-
- ### HTTP METHODS
-
- def do_OPTIONS(self):
- """return the list of capabilities """
- self.send_response(200)
- self.send_header("Allow", "GET, HEAD, COPY, MOVE, POST, PUT, PROPFIND, PROPPATCH, OPTIONS, MKCOL, DELETE, TRACE")
- self.send_header("Content-Type", "text/plain")
- self.send_header("Connection", "keep-alive")
- self.send_header("DAV", "1")
- self.end_headers()
-
- def do_PROPFIND(self):
-
- dc = self.IFACE_CLASS
- # read the body
- body = None
- if self.headers.has_key("Content-Length"):
- l = self.headers['Content-Length']
- body = self.rfile.read(atoi(l))
- alt_body = """
-
-
-
-
-
-
-
-
-
- """
- #self.wfile.write(body)
-
- # which Depth?
- if self.headers.has_key('Depth'):
- d = self.headers['Depth']
- else:
- d = "infinity"
-
- uri = self.geturi()
- pf = PROPFIND(uri, dc, d)
-
- if body:
- pf.read_propfind(body)
-
- try:
- DATA = pf.createResponse()
- DATA = DATA+"\n"
- # print "Data:", DATA
- except DAV_NotFound, (ec, dd):
- return self.send_notFound(dd, uri)
- except DAV_Error, (ec, dd):
- return self.send_error(ec, dd)
-
- self.send_body_chunks(DATA, 207, "Multi-Status", "Multiple responses")
-
- def geturi(self):
- buri = self.IFACE_CLASS.baseuri
- if buri[-1] == '/':
- return urllib.unquote(buri[:-1]+self.path)
- else:
- return urllib.unquote(buri+self.path)
-
- def do_GET(self):
- """Serve a GET request."""
- dc = self.IFACE_CLASS
- uri = self.geturi()
-
- # get the last modified date
- try:
- lm = dc.get_prop(uri, "DAV:", "getlastmodified")
- except:
- lm = "Sun, 01 Dec 2014 00:00:00 GMT" # dummy!
- headers = {"Last-Modified":lm , "Connection": "keep-alive"}
-
- # get the content type
- try:
- ct = dc.get_prop(uri, "DAV:", "getcontenttype")
- except:
- ct = "application/octet-stream"
-
- # get the data
- try:
- data = dc.get_data(uri)
- except DAV_Error, (ec, dd):
- self.send_status(ec)
- return
-
- # send the data
- self.send_body(data, 200, "OK", "OK", ct, headers)
-
- def do_HEAD(self):
- """ Send a HEAD response """
- dc = self.IFACE_CLASS
- uri = self.geturi()
-
- # get the last modified date
- try:
- lm = dc.get_prop(uri, "DAV:", "getlastmodified")
- except:
- lm = "Sun, 01 Dec 2014 00:00:00 GMT" # dummy!
-
- headers = {"Last-Modified":lm, "Connection": "keep-alive"}
-
- # get the content type
- try:
- ct = dc.get_prop(uri, "DAV:", "getcontenttype")
- except:
- ct = "application/octet-stream"
-
- try:
- data = dc.get_data(uri)
- headers["Content-Length"] = str(len(data))
- except DAV_NotFound:
- self.send_body(None, 404, "Not Found", "")
- return
-
- self.send_body(None, 200, "OK", "OK", ct, headers)
-
- def do_POST(self):
- self.send_error(404, "File not found")
-
- def do_MKCOL(self):
- """ create a new collection """
-
- dc = self.IFACE_CLASS
- uri = self.geturi()
- try:
- res = dc.mkcol(uri)
- if res:
- self.send_body(None, 201, "Created", '')
- else:
- self.send_body(None, 415, "Cannot create", '')
- #self.send_header("Connection", "keep-alive")
- # Todo: some content, too
- except DAV_Error, (ec, dd):
- self.send_body(None, int(ec), dd, dd)
-
- def do_DELETE(self):
- """ delete an resource """
- dc = self.IFACE_CLASS
- uri = self.geturi()
- dl = DELETE(uri, dc)
- if dc.is_collection(uri):
- res = dl.delcol()
- else:
- res = dl.delone()
-
- if res:
- self.send_status(207, body=res)
- else:
- self.send_status(204)
-
- def do_PUT(self):
- dc = self.IFACE_CLASS
-
- # read the body
- body = None
- if self.headers.has_key("Content-Length"):
- l = self.headers['Content-Length']
- body = self.rfile.read(atoi(l))
- uri = self.geturi()
-
- ct = None
- if self.headers.has_key("Content-Type"):
- ct = self.headers['Content-Type']
- try:
- dc.put(uri, body, ct)
- except DAV_Error, (ec, dd):
- self.send_status(ec)
- return
- self.send_status(201)
-
- def do_COPY(self):
- """ copy one resource to another """
- try:
- self.copymove(COPY)
- except DAV_Error, (ec, dd):
- self.send_status(ec)
-
- def do_MOVE(self):
- """ move one resource to another """
- try:
- self.copymove(MOVE)
- except DAV_Error, (ec, dd):
- self.send_status(ec)
-
- def copymove(self, CLASS):
- """ common method for copying or moving objects """
- dc = self.IFACE_CLASS
-
- # get the source URI
- source_uri = self.geturi()
-
- # get the destination URI
- dest_uri = self.headers['Destination']
- dest_uri = urllib.unquote(dest_uri)
-
- # Overwrite?
- overwrite = 1
- result_code = 204
- if self.headers.has_key("Overwrite"):
- if self.headers['Overwrite']=="F":
- overwrite=None
- result_code=201
-
- # instanciate ACTION class
- cp = CLASS(dc, source_uri, dest_uri, overwrite)
-
- # Depth?
- d = "infinity"
- if self.headers.has_key("Depth"):
- d = self.headers['Depth']
-
- if d!="0" and d!="infinity":
- self.send_status(400)
- return
-
- if d=="0":
- res = cp.single_action()
- self.send_status(res)
- return
-
- # now it only can be "infinity" but we nevertheless check for a collection
- if dc.is_collection(source_uri):
- try:
- res = cp.tree_action()
- except DAV_Error, (ec, dd):
- self.send_status(ec)
- return
- else:
- try:
- res = cp.single_action()
- except DAV_Error, (ec, dd):
- self.send_status(ec)
- return
-
- if res:
- self.send_body_chunks(res, 207, STATUS_CODES[207], STATUS_CODES[207],
- ctype='text/xml; charset="utf-8"')
- else:
- self.send_status(result_code)
-
- def get_userinfo(self, user, pw):
- """ Dummy method which lets all users in """
-
- return 1
-
- def send_status(self, code=200, mediatype='text/xml; charset="utf-8"', \
- msg=None, body=None):
-
- if not msg: msg = STATUS_CODES[code]
- self.send_body(body, code, STATUS_CODES[code], msg, mediatype)
-
- def send_notFound(self, descr, uri):
- body = """
-
- %s
-
- %s
-
- """
- return self.send_status(404, descr, body=body % (uri, descr))
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/caldav/DAV/__init__.py b/addons/caldav/DAV/__init__.py
deleted file mode 100644
index 10abdb40014..00000000000
--- a/addons/caldav/DAV/__init__.py
+++ /dev/null
@@ -1,20 +0,0 @@
-"""
- python davserver
- Copyright (C) 1999 Christian Scholz (ruebe@aachen.heimat.de)
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public
- License as published by the Free Software Foundation; either
- version 2 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with this library; if not, write to the Free
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-"""
-
-
diff --git a/addons/caldav/DAV/constants.py b/addons/caldav/DAV/constants.py
deleted file mode 100644
index e858ac519c2..00000000000
--- a/addons/caldav/DAV/constants.py
+++ /dev/null
@@ -1,18 +0,0 @@
-"""
-
-constants definition
-
-
-"""
-
-# definition for resourcetype
-COLLECTION=1
-OBJECT=None
-DAV_PROPS=['creationdate', 'displayname', 'getcontentlanguage', 'getcontentlength', 'getcontenttype', 'getetag', 'getlastmodified', 'lockdiscovery', 'resourcetype', 'source', 'supportedlock']
-
-
-# Request classes in propfind
-
-RT_ALLPROP=1
-RT_PROPNAME=2
-RT_PROP=3
diff --git a/addons/caldav/DAV/davcmd.py b/addons/caldav/DAV/davcmd.py
deleted file mode 100644
index 34c061d77ab..00000000000
--- a/addons/caldav/DAV/davcmd.py
+++ /dev/null
@@ -1,219 +0,0 @@
-"""
-
-davcmd.py
----------
-
-containts commands like copy, move, delete for normal
-resources and collections
-
-"""
-
-from string import split,replace,joinfields
-import urlparse
-
-from utils import create_treelist, is_prefix
-from errors import *
-
-def deltree(dc, uri, exclude={}):
- """ delete a tree of resources
-
- dc -- dataclass to use
- uri -- root uri to delete
- exclude -- an optional list of uri:error_code pairs which should not
- be deleted.
-
- returns dict of uri:error_code tuples from which
- another method can create a multistatus xml element.
-
- Also note that we only know Depth=infinity thus we don't have
- to test for it.
-
- """
-
- tlist = create_treelist(dc,uri)
- result = {}
-
- for i in range(len(tlist),0,-1):
- problem_uris = result.keys()
- element = tlist[i-1]
-
- # test here, if an element is a prefix of an uri which
- # generated an error before.
- # note that we walk here from childs to parents, thus
- # we cannot delete a parent if a child made a problem.
- # (see example in 8.6.2.1)
- ok = 1
- for p in problem_uris:
- if is_prefix(element,p):
- ok = None
- break
-
- if not ok: continue
-
- # here we test for the exclude list which is the other way round!
- for p in exclude.keys():
- if is_prefix(p,element):
- ok = None
- break
-
- if not ok: continue
-
- # now delete stuff
- try:
- delone(dc,element)
- except DAV_Error, (ec,dd):
- result[element] = ec
-
- return result
-
-def delone(dc, uri):
- """ delete a single object """
- if dc.is_collection(uri):
- dc.rmcol(uri) # should be empty
- else:
- dc.rm(uri)
-
-###
-### COPY
-###
-
-# helper function
-
-def copy(dc, src, dst):
- """ only copy the element
-
- This is just a helper method factored out from copy and
- copytree. It will not handle the overwrite or depth header.
-
- """
-
- # destination should have been deleted before
- if dc.exists(dst): raise DAV_Error, 412
-
- # source should exist also
- if not dc.exists(src): raise DAV_NotFound
-
- if dc.is_collection(src):
- dc.copycol(src,dst) # an exception will be passed thru
- else:
- dc.copy(src,dst) # an exception will be passed thru
-
-
-# the main functions
-
-def copyone(dc, src, dst, overwrite=None):
- """ copy one resource to a new destination """
-
- if overwrite and dc.exists(dst):
- delres = deltree(dc,dst)
- else:
- delres = {}
-
- # if we cannot delete everything, then do not copy!
- if delres: return delres
-
- try:
- copy(dc,src,dst) # pass thru exceptions
- except DAV_Error, (ec,dd):
- return ec
-
-def copytree(dc, src, dst, overwrite=None):
- """ copy a tree of resources to another location
-
- dc -- dataclass to use
- src -- src uri from where to copy
- dst -- dst uri
- overwrite -- if 1 then delete dst uri before
-
- returns dict of uri:error_code tuples from which
- another method can create a multistatus xml element.
-
- """
-
-
- # first delete the destination resource
- if overwrite and dc.exists(dst):
- delres = deltree(dc,dst)
- else:
- delres = {}
-
- # if we cannot delete everything, then do not copy!
- if delres: return delres
-
- # get the tree we have to copy
- tlist = create_treelist(dc,src)
- result = {}
-
- # prepare destination URIs (get the prefix)
- dpath = urlparse.urlparse(dst)[2]
-
- for element in tlist:
- problem_uris = result.keys()
-
- # now URIs get longer and longer thus we have
- # to test if we had a parent URI which we were not
- # able to copy in problem_uris which is the prefix
- # of the actual element. If it is, then we cannot
- # copy this as well but do not generate another error.
- ok = 1
- for p in problem_uris:
- if is_prefix(p,element):
- ok = None
- break
-
- if not ok: continue
-
- # now create the destination URI which corresponds to
- # the actual source URI. -> actual_dst
- # ("subtract" the base src from the URI and prepend the
- # dst prefix to it.)
- esrc = replace(element,src,"")
- actual_dst = dpath+esrc
-
- # now copy stuff
- try:
- copy(dc,element,actual_dst)
- except DAV_Error, (ec,dd):
- result[element] = ec
-
- return result
-
-
-
-###
-### MOVE
-###
-
-
-def moveone(dc, src, dst, overwrite=None):
- """ move a single resource
-
- This is done by first copying it and then deleting
- the original.
- """
-
- # first copy it
- copyone(dc,src,dst,overwrite)
-
- # then delete it
- dc.rm(src)
-
-def movetree(dc, src, dst, overwrite=None):
- """ move a collection
-
- This is done by first copying it and then deleting
- the original.
-
- PROBLEM: if something did not copy then we have a problem
- when deleting as the original might get deleted!
- """
-
- # first copy it
- res = copytree(dc,src,dst,overwrite)
-
- # then delete it
- res = deltree(dc,src,exclude=res)
-
- return res
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
\ No newline at end of file
diff --git a/addons/caldav/DAV/davcopy.py b/addons/caldav/DAV/davcopy.py
deleted file mode 100755
index 6333aececf6..00000000000
--- a/addons/caldav/DAV/davcopy.py
+++ /dev/null
@@ -1,134 +0,0 @@
-#!/usr/bin/env python
-
-"""
- python davserver
- Copyright (C) 1999 Christian Scholz (ruebe@aachen.heimat.de)
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public
- License as published by the Free Software Foundation; either
- version 2 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with this library; if not, write to the Free
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-
-"""
-
-
-from xml.dom import ext
-from xml.dom.Document import Document
-
-import sys
-import string
-import urlparse
-import urllib
-from StringIO import StringIO
-
-import utils
-from constants import COLLECTION, OBJECT, DAV_PROPS, RT_ALLPROP, RT_PROPNAME, RT_PROP
-from errors import *
-from utils import create_treelist, quote_uri, gen_estring
-
-class COPY:
- """ copy resources and eventually create multistatus responses
-
- This module implements the COPY class which is responsible for
- copying resources. Usually the normal copy work is done in the
- interface class. This class only creates error messages if error
- occur.
-
- """
-
-
- def __init__(self, dataclass, src_uri, dst_uri, overwrite):
- self.__dataclass = dataclass
- self.__src = src_uri
- self.__dst = dst_uri
- self.__overwrite = overwrite
-
-
- def single_action(self):
- """ copy a normal resources.
-
- We try to copy it and return the result code.
- This is for Depth==0
-
- """
-
- dc = self.__dataclass
- base = self.__src
-
- ### some basic tests
- # test if dest exists and overwrite is false
- if dc.exists(self.__dst) and not self.__overwrite: raise DAV_Error, 412
- # test if src and dst are the same
- # (we assume that both uris are on the same server!)
- ps = urlparse.urlparse(self.__src)[2]
- pd = urlparse.urlparse(self.__dst)[2]
- if ps==pd: raise DAV_Error, 403
-
- return dc.copyone(self.__src,self.__dst,self.__overwrite)
-
- #return copyone(dc,self.__src,self.__dst,self.__overwrite)
-
- def tree_action(self):
- """ copy a tree of resources (a collection)
-
- Here we return a multistatus xml element.
-
- """
- dc = self.__dataclass
- base = self.__src
-
- ### some basic tests
- # test if dest exists and overwrite is false
- if dc.exists(self.__dst) and not self.__overwrite: raise DAV_Error, 412
- # test if src and dst are the same
- # (we assume that both uris are on the same server!)
- ps = urlparse.urlparse(self.__src)[2]
- pd = urlparse.urlparse(self.__dst)[2]
- if ps==pd: raise DAV_Error, 403
-
-
- result = dc.copytree(self.__src,self.__dst,self.__overwrite)
- #result=copytree(dc,self.__src,self.__dst,self.__overwrite)
-
- if not result: return None
-
- ###
- ### create the multistatus XML element
- ### (this is also the same as in delete.py.
- ### we might make a common method out of it)
- ###
-
- doc = Document(None)
- ms = doc.createElement("D:multistatus")
- ms.setAttribute("xmlns:D","DAV:")
- doc.appendChild(ms)
-
- for el,ec in result.items():
- re = doc.createElement("D:response")
- hr = doc.createElement("D:href")
- st = doc.createElement("D:status")
- huri = doc.createTextNode(quote_uri(el))
- t = doc.createTextNode(gen_estring(ec))
- st.appendChild(t)
- hr.appendChild(huri)
- re.appendChild(hr)
- re.appendChild(st)
- ms.appendChild(re)
-
- sfile = StringIO()
- ext.PrettyPrint(doc,stream = sfile)
- s = sfile.getvalue()
- sfile.close()
- return s
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
\ No newline at end of file
diff --git a/addons/caldav/DAV/davmove.py b/addons/caldav/DAV/davmove.py
deleted file mode 100755
index 3404f947469..00000000000
--- a/addons/caldav/DAV/davmove.py
+++ /dev/null
@@ -1,103 +0,0 @@
-#!/usr/bin/env python
-
-"""
- python davserver
- Copyright (C) 1999 Christian Scholz (ruebe@aachen.heimat.de)
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public
- License as published by the Free Software Foundation; either
- version 2 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with this library; if not, write to the Free
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-
-"""
-
-
-import sys
-import string
-import urlparse
-import urllib
-from StringIO import StringIO
-
-import utils
-from constants import COLLECTION, OBJECT, DAV_PROPS
-from constants import RT_ALLPROP, RT_PROPNAME, RT_PROP
-from errors import *
-from utils import create_treelist, quote_uri, gen_estring, make_xmlresponse
-from davcmd import moveone, movetree
-
-class MOVE:
- """ move resources and eventually create multistatus responses
-
- This module implements the MOVE class which is responsible for
- moving resources.
-
- MOVE is implemented by a COPY followed by a DELETE of the old
- resource.
-
- """
-
-
- def __init__(self, dataclass, src_uri, dst_uri, overwrite):
- self.__dataclass = dataclass
- self.__src = src_uri
- self.__dst = dst_uri
- self.__overwrite = overwrite
-
-
- def single_action(self):
- """ move a normal resources.
-
- We try to move it and return the result code.
- This is for Depth==0
-
- """
-
- dc = self.__dataclass
- base = self.__src
-
- ### some basic tests
- # test if dest exists and overwrite is false
- if dc.exists(self.__dst) and not self.__overwrite: raise DAV_Error, 412
- # test if src and dst are the same
- # (we assume that both uris are on the same server!)
- ps = urlparse.urlparse(self.__src)[2]
- pd = urlparse.urlparse(self.__dst)[2]
- if ps==pd: raise DAV_Error, 403
-
- return dc.moveone(self.__src,self.__dst,self.__overwrite)
-
- def tree_action(self):
- """ move a tree of resources (a collection)
-
- Here we return a multistatus xml element.
-
- """
- dc = self.__dataclass
- base = self.__src
-
- ### some basic tests
- # test if dest exists and overwrite is false
- if dc.exists(self.__dst) and not self.__overwrite: raise DAV_Error, 412
- # test if src and dst are the same
- # (we assume that both uris are on the same server!)
- ps = urlparse.urlparse(self.__src)[2]
- pd = urlparse.urlparse(self.__dst)[2]
- if ps==pd: raise DAV_Error, 403
-
- result = dc.movetree(self.__src,self.__dst,self.__overwrite)
- if not result: return None
-
- # create the multistatus XML element
- return make_xmlresponse(result)
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
\ No newline at end of file
diff --git a/addons/caldav/DAV/delete.py b/addons/caldav/DAV/delete.py
deleted file mode 100755
index 3e75066f30e..00000000000
--- a/addons/caldav/DAV/delete.py
+++ /dev/null
@@ -1,64 +0,0 @@
-#!/usr/bin/env python
-
-"""
-
- python davserver
- Copyright (C) 1999 Christian Scholz (ruebe@aachen.heimat.de)
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public
- License as published by the Free Software Foundation; either
- version 2 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with this library; if not, write to the Free
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-
-"""
-import os
-import string
-import urllib
-from StringIO import StringIO
-
-from status import STATUS_CODES
-from utils import gen_estring, quote_uri, make_xmlresponse
-from davcmd import deltree
-
-class DELETE:
-
- def __init__(self, uri, dataclass):
- self.__dataclass = dataclass
- self.__uri = uri
-
- def delcol(self):
- """ delete a collection """
-
- dc = self.__dataclass
- result = dc.deltree(self.__uri)
-
- if not len(result.items()):
- return None # everything ok
-
- # create the result element
- return make_xmlresponse(result)
-
- def delone(self):
- """ delete a resource """
-
- dc = self.__dataclass
- result = dc.delone(self.__uri)
-
- if not result: return None
- if not len(result.items()):
- return None # everything ok
-
- # create the result element
- return make_xmlresponse(result)
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
\ No newline at end of file
diff --git a/addons/caldav/DAV/errors.py b/addons/caldav/DAV/errors.py
deleted file mode 100755
index 58d1d3be1d2..00000000000
--- a/addons/caldav/DAV/errors.py
+++ /dev/null
@@ -1,57 +0,0 @@
-#!/usr/bin/env python
-"""
-
- Exceptions for the DAVserver implementation
-
-"""
-
-class DAV_Error(Exception):
- """ in general we can have the following arguments:
-
- 1. the error code
- 2. the error result element, e.g. a element
- """
-
- def __init__(self, *args):
- if len(args)==1:
- self.args = (args[0],"")
- else:
- self.args = args
-
-class DAV_Secret(DAV_Error):
- """ the user is not allowed to know anything about it
-
- returning this for a property value means to exclude it
- from the response xml element.
- """
-
- def __init__(self):
- DAV_Error.__init__(self,0)
- pass
-
-class DAV_NotFound(DAV_Error):
- """ a requested property was not found for a resource """
-
- def __init__(self, *args):
- if len(args):
- if isinstance(args[0], list):
- stre = "Path %s not found!"%('/'.join(args[0]))
- else:
- stre = args[0]
- DAV_Error.__init__(self,404,stre)
- else:
- DAV_Error.__init__(self,404)
-
- pass
-
-class DAV_Forbidden(DAV_Error):
- """ a method on a resource is not allowed """
-
- def __init__(self, *args):
- if len(args):
- DAV_Error.__init__(self,403,args[0])
- else:
- DAV_Error.__init__(self,403)
- pass
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
\ No newline at end of file
diff --git a/addons/caldav/DAV/iface.py b/addons/caldav/DAV/iface.py
deleted file mode 100644
index a723fb9f696..00000000000
--- a/addons/caldav/DAV/iface.py
+++ /dev/null
@@ -1,264 +0,0 @@
-"""
-
-basic interface class
-
-use this for subclassing when writing your own interface
-class.
-
-"""
-
-from errors import *
-
-import time
-from string import lower
-
-class dav_interface:
- """ interface class for implementing DAV servers """
-
- ### defined properties (modify this but let the DAV stuff there!)
- ### the format is namespace: [list of properties]
-
- PROPS={"DAV:" : ('creationdate',
- 'displayname',
- 'getcontentlanguage',
- 'getcontentlength',
- 'getcontenttype',
- 'getetag',
- 'getlastmodified',
- 'lockdiscovery',
- 'resourcetype',
- 'source',
- 'supportedlock'),
- "NS2" : ("p1","p2")
- }
-
- # here we define which methods handle which namespace
- # the first item is the namespace URI and the second one
- # the method prefix
- # e.g. for DAV:getcontenttype we call dav_getcontenttype()
- M_NS = {"DAV:" : "_get_dav",
- "NS2" : "ns2" }
-
- def get_propnames(self, uri):
- """ return the property names allowed for the given URI
-
- In this method we simply return the above defined properties
- assuming that they are valid for any resource.
- You can override this in order to return a different set
- of property names for each resource.
-
- """
- return self.PROPS
-
- def get_prop2(self, uri, ns, pname):
- """ return the value of a property
- """
- if lower(ns)=="dav:": return self.get_dav(uri,pname)
-
- raise DAV_NotFound
-
- def get_prop(self, uri, ns, propname):
- """ return the value of a given property
-
- uri -- uri of the object to get the property of
- ns -- namespace of the property
- pname -- name of the property
- """
- if self.M_NS.has_key(ns):
- prefix = self.M_NS[ns]
- else:
- print "No namespace:",ns, "( for prop:", propname,")"
- raise DAV_NotFound
- mname = prefix+"_"+propname
- if not hasattr(self,mname):
- raise DAV_NotFound
-
- try:
- m = getattr(self,mname)
- r = m(uri)
- return r
- except AttributeError, e:
- print 'Property %s not supported' % propname
- print "Exception:", e
- raise DAV_NotFound
-
- ###
- ### DATA methods (for GET and PUT)
- ###
-
- def get_data(self, uri):
- """ return the content of an object
-
- return data or raise an exception
-
- """
- raise DAV_NotFound
-
- def put(self, uri, data):
- """ write an object to the repository
-
- return a result code or raise an exception
- """
-
- raise DAV_Forbidden
-
- ###
- ### Methods for DAV properties
- ###
-
- def _get_dav_creationdate(self, uri):
- """ return the creationdate of a resource """
- d = self.get_creationdate(uri)
- # format it
- if isinstance(d, int) or isinstance(d, float):
- d = time.localtimetime(d)
- return time.strftime("%Y-%m-%dT%H:%M:%S%Z",d)
-
- def _get_dav_getlastmodified(self, uri):
- """ return the last modified date of a resource """
- d = self.get_lastmodified(uri)
- if isinstance(d, int) or isinstance(d, float):
- d = time.localtime(d)
- # format it
- return time.asctime(d)
-
-
- ###
- ### OVERRIDE THESE!
- ###
-
- def get_creationdate(self, uri):
- """ return the creationdate of the resource """
- return time.time()
-
- def get_lastmodified(self, uri):
- """ return the last modification date of the resource """
- return time.time()
-
-
- ###
- ### COPY MOVE DELETE
- ###
-
- ### methods for deleting a resource
-
- def rmcol(self, uri):
- """ delete a collection
-
- This should not delete any children! This is automatically done
- before by the DELETE class in DAV/delete.py
-
- return a success code or raise an exception
-
- """
- raise DAV_NotFound
-
- def rm(self, uri):
- """ delete a single resource
-
- return a success code or raise an exception
-
- """
- raise DAV_NotFound
-
- """
-
- COPY/MOVE HANDLER
-
- These handler are called when a COPY or MOVE method is invoked by
- a client. In the default implementation it works as follows:
-
- - the davserver receives a COPY/MOVE method
- - the davcopy or davmove module will be loaded and the corresponding
- class will be initialized
- - this class parses the query and decides which method of the interface class
- to call:
-
- copyone for a single resource to copy
- copytree for a tree to copy (collection)
- (the same goes for move of course).
-
- - the interface class has now two options:
- 1. to handle the action directly (e.g. cp or mv on filesystems)
- 2. to let it handle via the copy/move methods in davcmd.
-
- ad 1) The first approach can be used when we know that no error can
- happen inside a tree or when the action can exactly tell which
- element made which error. We have to collect these and return
- it in a dict of the form {uri: error_code, ...}
-
- ad 2) The copytree/movetree/... methods of davcmd.py will do the recursion
- themselves and call for each resource the copy/move method of the
- interface class. Thus method will then only act on a single resource.
- (Thus a copycol on a normal unix filesystem actually only needs to do
- an mkdir as the content will be copied by the davcmd.py function.
- The davcmd.py method will also automatically collect all errors and
- return the dictionary described above.
- When you use 2) you also have to implement the copy() and copycol()
- methods in your interface class. See the example for details.
-
- To decide which approach is the best you have to decide if your application
- is able to generate errors inside a tree. E.g. a function which completely
- fails on a tree if one of the tree's childs fail is not what we need. Then
- 2) would be your way of doing it.
- Actually usually 2) is the better solution and should only be replaced by
- 1) if you really need it.
-
- The remaining question is if we should do the same for the DELETE method.
-
- """
-
- ### MOVE handlers
-
- def moveone(self, src, dst, overwrite):
- """ move one resource with Depth=0 """
- return moveone(self, src, dst, overwrite)
-
- def movetree(self, src, dst, overwrite):
- """ move a collection with Depth=infinity """
- return movetree(self, src, dst, overwrite)
-
- ### COPY handlers
-
- def copyone(self, src, dst, overwrite):
- """ copy one resource with Depth=0 """
- return copyone(self, src, dst, overwrite)
-
- def copytree(self, src, dst, overwrite):
- """ copy a collection with Depth=infinity """
- return copytree(self, src, dst, overwrite)
-
-
- ### low level copy methods (you only need these for method 2)
- def copy(self, src, dst):
- """ copy a resource with depth==0
-
- You don't need to bother about overwrite or not.
- This has been done already.
-
- return a success code or raise an exception if something fails
- """
- return 201
-
-
- def copycol(self, src, dst):
- """ copy a resource with depth==infinity
-
- You don't need to bother about overwrite or not.
- This has been done already.
-
- return a success code or raise an exception if something fails
- """
- return 201
-
- ### some utility functions you need to implement
-
- def exists(self, uri):
- """ return 1 or None depending on if a resource exists """
- return None # no
-
- def is_collection(self, uri):
- """ return 1 or None depending on if a resource is a collection """
- return None # no
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
\ No newline at end of file
diff --git a/addons/caldav/DAV/propfind.py b/addons/caldav/DAV/propfind.py
deleted file mode 100755
index 4d009e7912e..00000000000
--- a/addons/caldav/DAV/propfind.py
+++ /dev/null
@@ -1,373 +0,0 @@
-#!/usr/bin/env python
-
-"""
- python davserver
- Copyright (C) 1999 Christian Scholz (ruebe@aachen.heimat.de)
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public
- License as published by the Free Software Foundation; either
- version 2 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with this library; if not, write to the Free
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-
-"""
-
-from xml.dom import ext
-from xml.dom.Document import Document
-
-import sys
-import string
-import urlparse
-import urllib
-from StringIO import StringIO
-
-import utils
-from constants import COLLECTION, OBJECT, DAV_PROPS, RT_ALLPROP, RT_PROPNAME, RT_PROP
-from errors import *
-
-def utf8str(st):
- if isinstance(st,unicode):
- return st.encode('utf8')
- else:
- return str(st)
-
-class PROPFIND:
- """ parse a propfind xml element and extract props
-
- It will set the following instance vars:
-
- request_class: ALLPROP | PROPNAME | PROP
- proplist: list of properties
- nsmap: map of namespaces
-
- The list of properties will contain tuples of the form
- (element name, ns_prefix, ns_uri)
-
-
- """
-
- def __init__(self, uri, dataclass, depth):
- self.request_type = None
- self.nsmap = {}
- self.proplist = {}
- self.default_ns = None
- self.__dataclass = dataclass
- self.__depth = str(depth)
- self.__uri = uri
- self.use_full_urls = True
- self.__has_body = None # did we parse a body?
-
- def read_propfind(self, xml_doc):
- self.request_type,self.proplist,self.namespaces = utils.parse_propfind(xml_doc)
-
- # a violation of the expected logic: client (korganizer) will ask for DAV:resourcetype
- # but we also have to return the http://groupdav.org/:resourcetype property!
- if self.proplist.has_key('DAV:') and 'resourcetype' in self.proplist['DAV:']:
- if not self.proplist.has_key('http://groupdav.org/'):
- self.proplist['http://groupdav.org/'] = []
- self.proplist['http://groupdav.org/'].append('resourcetype')
- if 'DAV:' in self.namespaces: #TMP
- self.namespaces.append('http://groupdav.org/')
-
- def createResponse(self):
- """ create the multistatus response
-
- This will be delegated to the specific method
- depending on which request (allprop, propname, prop)
- was found.
-
- If we get a PROPNAME then we simply return the list with empty
- values which we get from the interface class
-
- If we get an ALLPROP we first get the list of properties and then
- we do the same as with a PROP method.
-
- If the uri doesn't exist, return an xml response with a 404 status
-
- """
-
- if not self.__dataclass.exists(self.__uri):
- raise DAV_NotFound("Path %s doesn't exist" % self.__uri)
-
- if self.request_type==RT_ALLPROP:
- return self.create_allprop()
-
- if self.request_type==RT_PROPNAME:
- return self.create_propname()
-
- if self.request_type==RT_PROP:
- return self.create_prop()
-
- # no body means ALLPROP!
- return self.create_allprop()
-
- def create_propname(self):
- """ create a multistatus response for the prop names """
-
- dc = self.__dataclass
- # create the document generator
- doc = Document(None)
- ms = doc.createElement("D:multistatus")
- ms.setAttribute("xmlns:D","DAV:")
- doc.appendChild(ms)
-
- if self.__depth=="0":
- pnames = dc.get_propnames(self.__uri)
- re = self.mk_propname_response(self.__uri,pnames,doc)
- ms.appendChild(re)
-
- elif self.__depth=="1":
- pnames = dc.get_propnames(self.__uri)
- re = self.mk_propname_response(self.__uri,pnames,doc)
- ms.appendChild(re)
-
- for newuri in dc.get_childs(self.__uri):
- pnames = dc.get_propnames(newuri)
- re = self.mk_propname_response(newuri,pnames,doc)
- ms.appendChild(re)
- # *** depth=="infinity"
-
- sfile = StringIO()
- ext.PrettyPrint(doc,stream = sfile)
- s = sfile.getvalue()
- sfile.close()
- return s
-
- def create_allprop(self):
- """ return a list of all properties """
- self.proplist = {}
- self.namespaces = []
- for ns,plist in self.__dataclass.get_propnames(self.__uri).items():
- self.proplist[ns] = plist
- self.namespaces.append(ns)
-
- return self.create_prop()
-
- def create_prop(self):
- """ handle a request
-
- This will
-
- 1. set up the -Framework
-
- 2. read the property values for each URI
- (which is dependant on the Depth header)
- This is done by the get_propvalues() method.
-
- 3. For each URI call the append_result() method
- to append the actual -Tag to the result
- document.
-
- We differ between "good" properties, which have been
- assigned a value by the interface class and "bad"
- properties, which resulted in an error, either 404
- (Not Found) or 403 (Forbidden).
-
- """
-
-
- # create the document generator
- doc = Document(None)
- ms = doc.createElement("D:multistatus")
- ms.setAttribute("xmlns:D","DAV:")
- doc.appendChild(ms)
-
- if self.__depth=="0":
- gp,bp = self.get_propvalues(self.__uri)
- res = self.mk_prop_response(self.__uri,gp,bp,doc)
- ms.appendChild(res)
-
- elif self.__depth=="1":
- gp,bp = self.get_propvalues(self.__uri)
- res = self.mk_prop_response(self.__uri,gp,bp,doc)
- ms.appendChild(res)
-
- try:
- for newuri in self.__dataclass.get_childs(self.__uri):
- gp,bp = self.get_propvalues(newuri)
- res = self.mk_prop_response(newuri,gp,bp,doc)
- ms.appendChild(res)
- except DAV_NotFound:
- # If no children, never mind.
- pass
-
- sfile = StringIO()
- ext.PrettyPrint(doc,stream = sfile)
- s = sfile.getvalue()
- sfile.close()
- return s
-
-
- def mk_propname_response(self,uri,propnames,doc):
- """ make a new result element for a PROPNAME request
-
- This will simply format the propnames list.
- propnames should have the format {NS1 : [prop1, prop2, ...], NS2: ...}
-
- """
- re = doc.createElement("D:response")
-
- # write href information
- href = doc.createElement("D:href")
- if self.use_full_urls:
- huri = doc.createTextNode(uri)
- else:
- uparts = urlparse.urlparse(uri)
- fileloc = uparts[2]
- huri = doc.createTextNode(urllib.quote(fileloc.encode('utf8')))
- href.appendChild(huri)
- re.appendChild(href)
-
- ps = doc.createElement("D:propstat")
- nsnum = 0
-
- for ns,plist in propnames.items():
- # write prop element
- pr = doc.createElement("D:prop")
- nsp = "ns"+str(nsnum)
- pr.setAttribute("xmlns:"+nsp,ns)
- nsnum = nsnum+1
-
- # write propertynames
- for p in plist:
- pe = doc.createElement(nsp+":"+p)
- pr.appendChild(pe)
-
- ps.appendChild(pr)
-
- re.appendChild(ps)
-
- return re
-
- def mk_prop_response(self,uri,good_props,bad_props,doc):
- """ make a new result element
-
- We differ between the good props and the bad ones for
- each generating an extra -Node (for each error
- one, that means).
-
- """
- re = doc.createElement("D:response")
- # append namespaces to response
- nsnum = 0
- for nsname in self.namespaces:
- re.setAttribute("xmlns:ns"+str(nsnum),nsname)
- nsnum = nsnum+1
-
- # write href information
- href = doc.createElement("D:href")
- if self.use_full_urls:
- huri = doc.createTextNode(uri)
- else:
- uparts = urlparse.urlparse(uri)
- fileloc = uparts[2]
- huri = doc.createTextNode(urllib.quote(fileloc.encode('utf8')))
- href.appendChild(huri)
- re.appendChild(href)
-
- # write good properties
- if good_props and len(good_props.items()):
- ps = doc.createElement("D:propstat")
-
- gp = doc.createElement("D:prop")
- for ns in good_props.keys():
- ns_prefix="ns"+str(self.namespaces.index(ns))+":"
- for p,v in good_props[ns].items():
- pe = doc.createElement(ns_prefix+str(p))
- if v == None:
- pass
- elif ns=='DAV:' and p=="resourcetype":
- if v == 1:
- ve=doc.createElement("D:collection")
- pe.appendChild(ve)
- elif isinstance(v,tuple) and v[1] == ns:
- ve=doc.createElement(ns_prefix+v[0])
- pe.appendChild(ve)
- else:
- ve = doc.createTextNode(utf8str(v))
- pe.appendChild(ve)
-
- gp.appendChild(pe)
- if gp.hasChildNodes():
- re.appendChild(ps)
- 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!
- if len(bad_props.items()):
-
- # write a propstat for each error code
- for ecode in bad_props.keys():
- ps = doc.createElement("D:propstat")
- re.appendChild(ps)
- bp = doc.createElement("D:prop")
- ps.appendChild(bp)
-
- for ns in bad_props[ecode].keys():
- ns_prefix = "ns"+str(self.namespaces.index(ns))+":"
-
- for p in bad_props[ecode][ns]:
- 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
- return re
-
- def get_propvalues(self,uri):
- """ create lists of property values for an URI
-
- We create two lists for an URI: the properties for
- which we found a value and the ones for which we
- only got an error, either because they haven't been
- found or the user is not allowed to read them.
-
- """
- good_props = {}
- bad_props = {}
-
- for (ns,plist) in self.proplist.items():
- good_props[ns] = {}
- bad_props = {}
- ec = 0
- for prop in plist:
- try:
- ec = 0
- r = self.__dataclass.get_prop(uri,ns,prop)
- good_props[ns][prop] = r
- except DAV_Error, error_code:
- ec = error_code[0]
-
- # ignore props with error_code if 0 (invisible)
- if ec==0: continue
-
- if bad_props.has_key(ec):
- if bad_props[ec].has_key(ns):
- bad_props[ec][ns].append(prop)
- else:
- bad_props[ec][ns] = [prop]
- else:
- bad_props[ec] = {ns:[prop]}
-
- return good_props, bad_props
-
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
\ No newline at end of file
diff --git a/addons/caldav/DAV/status.py b/addons/caldav/DAV/status.py
deleted file mode 100644
index 62cb323c4fb..00000000000
--- a/addons/caldav/DAV/status.py
+++ /dev/null
@@ -1,31 +0,0 @@
-"""
-
-status codes for DAV services
-
-
-"""
-
-
-STATUS_CODES={
- 102: "Processing",
- 200: "Ok",
- 201: "Created",
- 204: "No Content",
- 207: "Multi-Status",
- 201: "Created",
- 400: "Bad Request",
- 403: "Forbidden",
- 404: "Not Found",
- 405: "Method Not Allowed",
- 409: "Conflict",
- 412: "Precondition failed",
- 423: "Locked",
- 415: "Unsupported Media Type",
- 507: "Insufficient Storage",
- 422: "Unprocessable Entity",
- 423: "Locked",
- 424: "Failed Dependency",
- 502: "Bad Gateway",
- 507: "Insufficient Storage",
- 999: "Some error in Create Method please check the data of create method"
-}
diff --git a/addons/caldav/DAV/utils.py b/addons/caldav/DAV/utils.py
deleted file mode 100755
index a4010f27a77..00000000000
--- a/addons/caldav/DAV/utils.py
+++ /dev/null
@@ -1,161 +0,0 @@
-#!/usr/bin/env python
-
-"""
-
-UTILITIES
-
-- parse a propfind request body into a list of props
-
-"""
-
-from xml.dom import ext
-from xml.dom.Document import Document
-from xml.dom.ext.reader import PyExpat
-from xml.dom import Node
-from xml.dom import NodeIterator, NodeFilter
-
-from string import lower, split, atoi, joinfields
-import urlparse
-from StringIO import StringIO
-
-from constants import RT_ALLPROP, RT_PROPNAME, RT_PROP
-from status import STATUS_CODES
-
-VERSION = '0.6'
-AUTHOR = 'Simon Pamies '
-
-
-def gen_estring(ecode):
- """ generate an error string from the given code """
- ec = atoi(str(ecode))
- if STATUS_CODES.has_key(ec):
- return "HTTP/1.1 %s %s" %(ec,STATUS_CODES[ec])
- else:
- return "HTTP/1.1 %s" %(ec)
-
-def parse_propfind(xml_doc):
- """ parse an propfind xml file and return a list of props
-
- returns:
-
- request_type -- ALLPROP, PROPNAME, PROP
- proplist -- list of properties found
- namespaces -- list of namespaces found
-
- """
- doc = PyExpat.Reader().fromString(xml_doc)
- snit = doc.createNodeIterator(doc, NodeFilter.NodeFilter.SHOW_ELEMENT, None, None)
-
- request_type = None
- props = {}
- namespaces = []
-
- while 1:
- curr_elem = snit.nextNode()
- if not curr_elem: break
- ename=fname = lower(curr_elem.nodeName)
- if ":" in fname:
- ename = split(fname,":")[1]
- if ename=="prop": request_type = RT_PROP; continue
- if ename=="propfind": continue
- if ename=="allprop": request_type = RT_ALLPROP; continue
- if ename=="propname": request_type = RT_PROPNAME; continue
-
- # rest should be names of attributes
-
- ns = curr_elem.namespaceURI
- if props.has_key(ns):
- props[ns].append(ename)
- else:
- props[ns] = [ename]
- namespaces.append(ns)
-
- return request_type,props,namespaces
-
-
-def create_treelist(dataclass, uri):
- """ create a list of resources out of a tree
-
- This function is used for the COPY, MOVE and DELETE methods
-
- uri - the root of the subtree to flatten
-
- It will return the flattened tree as list
-
- """
- queue = [uri]
- list = [uri]
- while len(queue):
- element = queue[-1]
- if dataclass.is_collection(element):
- childs = dataclass.get_childs(element)
- else:
- childs = []
- if len(childs):
- list = list+childs
- # update queue
- del queue[-1]
- if len(childs):
- queue = queue+childs
- return list
-
-def is_prefix(uri1, uri2):
- """ returns 1 of uri1 is a prefix of uri2 """
- if uri2[:len(uri1)]==uri1:
- return 1
- else:
- return None
-
-def quote_uri(uri):
- """ quote an URL but not the protocol part """
- import urlparse
- import urllib
-
- up = urlparse.urlparse(uri)
- np = urllib.quote(up[2])
- return urlparse.urlunparse((up[0], up[1], np, up[3], up[4], up[5]))
-
-def get_uriparentpath(uri):
- """ extract the uri path and remove the last element """
- up = urlparse.urlparse(uri)
- return joinfields(split(up[2], "/")[:-1], "/")
-
-def get_urifilename(uri):
- """ extract the uri path and return the last element """
- up = urlparse.urlparse(uri)
- return split(up[2], "/")[-1]
-
-def get_parenturi(uri):
- """ return the parent of the given resource"""
- up = urlparse.urlparse(uri)
- np = joinfields(split(up[2], "/")[:-1], "/")
- return urlparse.urlunparse((up[0], up[1], np, up[3], up[4], up[5]))
-
-### XML utilities
-
-def make_xmlresponse(result):
- """ construct a response from a dict of uri:error_code elements """
- doc = Document(None)
- ms = doc.createElement("D:multistatus")
- ms.setAttribute("xmlns:D", "DAV:")
- doc.appendChild(ms)
-
- for el, ec in result.items():
- re = doc.createElement("D:response")
- hr = doc.createElement("D:href")
- st = doc.createElement("D:status")
- huri = doc.createTextNode(quote_uri(el))
- t = doc.createTextNode(gen_estring(ec))
- st.appendChild(t)
- hr.appendChild(huri)
- re.appendChild(hr)
- re.appendChild(st)
- ms.appendChild(re)
-
- sfile = StringIO()
- ext.PrettyPrint(doc, stream = sfile)
- s = sfile.getvalue()
- sfile.close()
- return s
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
\ No newline at end of file
diff --git a/addons/caldav/__init__.py b/addons/caldav/__init__.py
index 9e222cf99a8..3c7bf49a8fa 100644
--- a/addons/caldav/__init__.py
+++ b/addons/caldav/__init__.py
@@ -19,11 +19,9 @@
#
##############################################################################
+import calendar
+import calendar_collection
import caldav
-import caldav_cache
-import caldav_fs
-import caldav_node
-import webdav_server
import wizard
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/caldav/__openerp__.py b/addons/caldav/__openerp__.py
index 1d25ad8cb77..3717ad32d83 100644
--- a/addons/caldav/__openerp__.py
+++ b/addons/caldav/__openerp__.py
@@ -25,6 +25,7 @@
"version" : "1.0",
"depends" : [
"base",
+ "document_webdav",
],
'description': """
This module Contains basic functionality for caldav system like:
@@ -34,7 +35,7 @@
- Provides iCal Import/Export functionality
To access OpenERP Calendar using caldav to remote site use the URL like:
- http://HOSTNAME:PORT/calendar/DATABASE_NAME/CALENDAR_NAME.ics
+ http://HOSTNAME:PORT/webdav/DATABASE_NAME/Calendars/CALENDAR_NAME.ics
Where,
HOSTNAME: Host on which OpenERP server(With webdav) is running
diff --git a/addons/caldav/caldav.py b/addons/caldav/caldav.py
index c5534afa649..f760421dfe5 100644
--- a/addons/caldav/caldav.py
+++ b/addons/caldav/caldav.py
@@ -19,1009 +19,26 @@
#
##############################################################################
-from datetime import datetime, timedelta
-from dateutil import parser
-from dateutil.rrule import *
-from osv import osv, fields
-from tools.translate import _
-import base64
-import math
-import pooler
-import pytz
-import re
+from document_webdav import webdav
import tools
-import time
-
-
-try:
- import vobject
-except ImportError:
- raise osv.except_osv('vobject Import Error!','Please install python-vobject \
- from http://vobject.skyhouseconsulting.com/')
-
-# O-1 Optional and can come only once
-# O-n Optional and can come more than once
-# R-1 Required and can come only once
-# R-n Required and can come more than once
-
-def uid2openobjectid(cr, uidval, oomodel, rdate):
- """ UID To Open Object Id
- @param cr: the current row, from the database cursor,
- @param uidval: Get USerId vale
- @oomodel: Open Object ModelName
- @param rdate: Get Recurrent Date
- """
- __rege = re.compile(r'OpenObject-([\w|\.]+)_([0-9]+)@(\w+)$')
- wematch = __rege.match(uidval.encode('utf8'))
- if not wematch:
- return (False, None)
- else:
- model, id, dbname = wematch.groups()
- model_obj = pooler.get_pool(cr.dbname).get(model)
- if (not model == oomodel) or (not dbname == cr.dbname):
- return (False, None)
- qry = 'select distinct(id) from %s' % model_obj._table
- if rdate:
- qry += " where recurrent_id='%s'" % (rdate)
- cr.execute(qry)
- r_id = cr.fetchone()
- if r_id:
- return (id, r_id[0])
- cr.execute(qry)
- ids = map(lambda x: str(x[0]), cr.fetchall())
- if id in ids:
- return (id, None)
- return (False, None)
-
-def openobjectid2uid(cr, uidval, oomodel):
- """ Open Object Id To UId
- @param cr: the current row, from the database cursor,
- @param uidval: Get USerId vale
- @oomodel: Open Object ModelName """
-
- value = 'OpenObject-%s_%s@%s' % (oomodel, uidval, cr.dbname)
- return value
-
-def get_attribute_mapping(cr, uid, calname, context={}):
- """ Attribute Mapping with Basic calendar fields and lines
- @param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
- @param calname: Get Calendar name
- @param context: A standard dictionary for contextual values """
-
- if not context:
- context = {}
- pool = pooler.get_pool(cr.dbname)
- field_obj = pool.get('basic.calendar.fields')
- type_obj = pool.get('basic.calendar.lines')
- domain = [('object_id.model', '=', context.get('model'))]
- if context.get('calendar_id'):
- domain.append(('calendar_id', '=', context.get('calendar_id')))
- type_id = type_obj.search(cr, uid, domain)
- fids = field_obj.search(cr, uid, [('type_id', '=', type_id[0])])
- res = {}
- for field in field_obj.browse(cr, uid, fids):
- attr = field.name.name
- res[attr] = {}
- res[attr]['field'] = field.field_id.name
- res[attr]['type'] = field.field_id.ttype
- if field.fn == 'hours':
- res[attr]['type'] = "timedelta"
- if res[attr]['type'] in ('one2many', 'many2many', 'many2one'):
- res[attr]['object'] = field.field_id.relation
- elif res[attr]['type'] in ('selection') and field.mapping:
- res[attr]['mapping'] = eval(field.mapping)
- if not res.get('uid', None):
- res['uid'] = {}
- res['uid']['field'] = 'id'
- res['uid']['type'] = "integer"
+from DAV.propfind import PROPFIND
+import urlparse
+urlparse.uses_netloc.append('caldav')
+urlparse.uses_netloc.append('caldavs')
+super_mk_prop_response = webdav.mk_prop_response
+def mk_prop_response(self,uri,good_props,bad_props,doc):
+ res = super_mk_prop_response(self, uri,good_props,bad_props,doc)
+ uris = uri.split('/')
+ calendar = False
+ if 'http://calendarserver.org/ns/' in good_props or 'http://calendarserver.org/ns/' in bad_props:
+ calendar = True
+ if calendar:
+ ad = doc.createElement('calendar')
+ ad.setAttribute('xmlns', 'urn:ietf:params:xml:ns:caldav')
+ cols = res.getElementsByTagName('D:collection')
+ if cols:
+ cols[0].parentNode.appendChild(ad)
+ #cols[0].parentNode.appendChild(vc)
return res
-def map_data(cr, uid, obj):
- """ Map Data
- @param self: The object pointer
- @param cr: the current row, from the database cursor,"""
-
- vals = {}
- for map_dict in obj.__attribute__:
- map_val = obj.ical_get(map_dict, 'value')
- field = obj.ical_get(map_dict, 'field')
- field_type = obj.ical_get(map_dict, 'type')
- if field:
- if field_type == 'selection':
- if not map_val:
- continue
- mapping = obj.__attribute__[map_dict].get('mapping', False)
- if mapping:
- map_val = mapping[map_val.lower()]
- else:
- map_val = map_val.lower()
- if field_type == 'many2many':
- ids = []
- if not map_val:
- vals[field] = ids
- continue
- model = obj.__attribute__[map_dict].get('object', False)
- modobj = obj.pool.get(model)
- for map_vall in map_val:
- id = modobj.create(cr, uid, map_vall)
- ids.append(id)
- vals[field] = [(6, 0, ids)]
- continue
- if field_type == 'many2one':
- id = None
- if not map_val or not isinstance(map_val, dict):
- vals[field] = id
- continue
- model = obj.__attribute__[map_dict].get('object', False)
- modobj = obj.pool.get(model)
- id = modobj.create(cr, uid, map_val)
- vals[field] = id
- continue
- if field_type == 'timedelta':
- if map_val:
- vals[field] = (map_val.seconds/float(86400) + map_val.days)
- if map_val:
- vals[field] = map_val
- return vals
-
-class CalDAV(object):
- __attribute__ = {}
-
-
-
- def ical_set(self, name, value, type):
- """ set calendar Attribute
- @param self: The object pointer,
- @param name: Get Attribute Name
- @param value: Get Attribute Value
- @param type: Get Attribute Type
- """
- if name in self.__attribute__ and self.__attribute__[name]:
- self.__attribute__[name][type] = value
- return True
-
- def ical_get(self, name, type):
- """ Get calendar Attribute
- @param self: The object pointer,
- @param name: Get Attribute Name
- @param type: Get Attribute Type
- """
-
- if self.__attribute__.get(name):
- val = self.__attribute__.get(name).get(type, None)
- valtype = self.__attribute__.get(name).get('type', None)
- if type == 'value':
- if valtype and valtype == 'datetime' and val:
- if isinstance(val, list):
- val = ','.join(map(lambda x: x.strftime('%Y-%m-%d %H:%M:%S'), val))
- else:
- val = val.strftime('%Y-%m-%d %H:%M:%S')
- return val
- else:
- return self.__attribute__.get(name, None)
-
- def ical_reset(self, type):
- """ Reset Calendar Attribute
- @param self: The object pointer,
- @param type: Get Attribute Type
- """
-
- for name in self.__attribute__:
- if self.__attribute__[name]:
- self.__attribute__[name][type] = None
- return True
-
- def parse_ics(self, cr, uid, child, cal_children=None, context=None):
- """ parse calendaring and scheduling information
- @param self: The object pointer
- @param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
- @param context: A standard dictionary for contextual values """
-
- att_data = []
- for cal_data in child.getChildren():
- if cal_data.name.lower() == 'attendee':
- ctx = context.copy()
- if cal_children:
- ctx.update({'model': cal_children[cal_data.name.lower()]})
- attendee = self.pool.get('basic.calendar.attendee')
- att_data.append(attendee.import_cal(cr, uid, cal_data, context=ctx))
- self.ical_set(cal_data.name.lower(), att_data, 'value')
- continue
- if cal_data.name.lower() == 'valarm':
- alarm = self.pool.get('basic.calendar.alarm')
- ctx = context.copy()
- if cal_children:
- ctx.update({'model': cal_children[cal_data.name.lower()]})
- vals = alarm.import_cal(cr, uid, cal_data, context=ctx)
- self.ical_set(cal_data.name.lower(), vals, 'value')
- continue
- if cal_data.name.lower() == 'exdate':
- exval = map(lambda x: str(x), cal_data.value)
- self.ical_set(cal_data.name.lower(), ','.join(exval), 'value')
- continue
- if cal_data.name.lower() in self.__attribute__:
-
- if cal_data.params.get('X-VOBJ-ORIGINAL-TZID'):
- self.ical_set('vtimezone', cal_data.params.get('X-VOBJ-ORIGINAL-TZID'), 'value')
- self.ical_set(cal_data.name.lower(), cal_data.value, 'value')
- vals = map_data(cr, uid, self)
- return vals
-
- def create_ics(self, cr, uid, datas, name, ical, context=None):
- """ create calendaring and scheduling information
- @param self: The object pointer
- @param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
- @param context: A standard dictionary for contextual values """
-
- if not datas:
- return
- timezones = []
- for data in datas:
- tzval = None
- vevent = ical.add(name)
- for field in self.__attribute__.keys():
- map_field = self.ical_get(field, 'field')
- map_type = self.ical_get(field, 'type')
- if map_field in data.keys():
- if field == 'uid':
- model = context.get('model', None)
- if not model:
- continue
- uidval = openobjectid2uid(cr, data[map_field], model)
- model_obj = self.pool.get(model)
- r_ids = []
- if model_obj._columns.get('recurrent_uid', None):
- cr.execute('select id from %s where recurrent_uid=%s'
- % (model_obj._table, data[map_field]))
- r_ids = map(lambda x: x[0], cr.fetchall())
- if r_ids:
- rdata = self.pool.get(model).read(cr, uid, r_ids)
- event_obj = self.pool.get('basic.calendar.event')
- rcal = event_obj.export_cal(cr, uid, rdata, context=context)
- for revents in rcal.contents['vevent']:
- ical.contents['vevent'].append(revents)
- if data.get('recurrent_uid', None):
- uidval = openobjectid2uid(cr, data['recurrent_uid'], model)
- vevent.add('uid').value = uidval
- elif field == 'attendee' and data[map_field]:
- model = self.__attribute__[field].get('object', False)
- attendee_obj = self.pool.get('basic.calendar.attendee')
- vevent = attendee_obj.export_cal(cr, uid, model, \
- data[map_field], vevent, context=context)
- elif field == 'valarm' and data[map_field]:
- model = self.__attribute__[field].get('object', False)
- ctx = context.copy()
- ctx.update({'model': model})
- alarm_obj = self.pool.get('basic.calendar.alarm')
- vevent = alarm_obj.export_cal(cr, uid, model, \
- data[map_field][0], vevent, context=ctx)
- elif field == 'vtimezone' and data[map_field] and data[map_field] not in timezones:
- tzval = data[map_field]
- tz_obj = self.pool.get('basic.calendar.timezone')
- ical = tz_obj.export_cal(cr, uid, None, \
- data[map_field], ical, context=context)
- timezones.append(data[map_field])
- elif data[map_field]:
- if map_type in ("char", "text"):
- if field in ('exdate'):
- vevent.add(field).value = map(parser.parse, (data[map_field]).split(','))
- else:
- vevent.add(field).value = tools.ustr(data[map_field])
- elif map_type in ('datetime', 'date') and data[map_field]:
- dtfield = vevent.add(field)
- dtfield.value = parser.parse(data[map_field])
- if tzval:
- dtfield.params['TZID'] = [tzval.title()]
- elif map_type == "timedelta":
- vevent.add(field).value = timedelta(hours=data[map_field])
- elif map_type == "many2one":
- vevent.add(field).value = tools.ustr(data.get(map_field)[1])
- elif map_type in ("float", "integer"):
- vevent.add(field).value = str(data.get(map_field))
- elif map_type == "selection":
- if not self.ical_get(field, 'mapping'):
- vevent.add(field).value = (tools.ustr(data[map_field])).upper()
- else:
- for key1, val1 in self.ical_get(field, 'mapping').items():
- if val1 == data[map_field]:
- vevent.add(field).value = key1
- return vevent
-
- def check_import(self, cr, uid, vals, context={}):
- """
- @param self: The object pointer
- @param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
- @param vals: Get Values
- @param context: A standard dictionary for contextual values
- """
-
- ids = []
- model_obj = self.pool.get(context.get('model'))
- recur_pool = {}
- try:
- for val in vals:
- exists, r_id = uid2openobjectid(cr, val['id'], context.get('model'), \
- val.get('recurrent_id'))
- if val.has_key('create_date'): val.pop('create_date')
- u_id = val.get('id', None)
- val.pop('id')
- if exists and r_id:
- val.update({'recurrent_uid': exists})
- model_obj.write(cr, uid, [r_id], val)
- ids.append(r_id)
- elif exists:
- model_obj.write(cr, uid, [exists], val)
- ids.append(exists)
- else:
- if u_id in recur_pool and val.get('recurrent_id'):
- val.update({'recurrent_uid': recur_pool[u_id]})
- revent_id = model_obj.create(cr, uid, val)
- ids.append(revent_id)
- else:
- event_id = model_obj.create(cr, uid, val)
- recur_pool[u_id] = event_id
- ids.append(event_id)
- except Exception, e:
- raise osv.except_osv(('Error !'), (str(e)))
- return ids
-
- def export_cal(self, cr, uid, datas, vobj=None, context={}):
- """ Export Calendar
- @param self: The object pointer
- @param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
- @param datas: Get Data's for caldav
- @param context: A standard dictionary for contextual values
- """
-
- try:
- self.__attribute__ = get_attribute_mapping(cr, uid, self._calname, context)
- ical = vobject.iCalendar()
- self.create_ics(cr, uid, datas, vobj, ical, context=context)
- return ical
- except Exception, e:
- raise osv.except_osv(('Error !'), (str(e)))
-
- def import_cal(self, cr, uid, content, data_id=None, context=None):
- """ Import Calendar
- @param self: The object pointer
- @param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
- @param data_id: Get Data’s ID or False
- @param context: A standard dictionary for contextual values
- """
-
- ical_data = base64.decodestring(content)
- self.__attribute__ = get_attribute_mapping(cr, uid, self._calname, context)
- parsedCal = vobject.readOne(ical_data)
- res = []
- vals = {}
- for child in parsedCal.getChildren():
- if child.name.lower() in ('vevent', 'vtodo'):
- vals = self.parse_ics(cr, uid, child, context=context)
- else:
- vals = {}
- continue
- if vals: res.append(vals)
- self.ical_reset('value')
- return res
-
-
-class Calendar(CalDAV, osv.osv):
- _name = 'basic.calendar'
- _description = 'Calendar'
- _calname = 'calendar'
-
- __attribute__ = {
- 'prodid': None, # Use: R-1, Type: TEXT, Specifies the identifier for the product that created the iCalendar object.
- 'version': None, # Use: R-1, Type: TEXT, Specifies the identifier corresponding to the highest version number
- # or the minimum and maximum range of the iCalendar specification
- # that is required in order to interpret the iCalendar object.
- 'calscale': None, # Use: O-1, Type: TEXT, Defines the calendar scale used for the calendar information specified in the iCalendar object.
- 'method': None, # Use: O-1, Type: TEXT, Defines the iCalendar object method associated with the calendar object.
- 'vevent': None, # Use: O-n, Type: Collection of Event class
- 'vtodo': None, # Use: O-n, Type: Collection of ToDo class
- 'vjournal': None, # Use: O-n, Type: Collection of Journal class
- 'vfreebusy': None, # Use: O-n, Type: Collection of FreeBusy class
- 'vtimezone': None, # Use: O-n, Type: Collection of Timezone class
- }
- _columns = {
- 'name': fields.char("Name", size=64),
- 'line_ids': fields.one2many('basic.calendar.lines', 'calendar_id', 'Calendar Lines'),
- 'active': fields.boolean('Active'),
- 'create_date': fields.datetime('Created Date'),
- 'write_date': fields.datetime('Modifided Date'),
- }
-
- _defaults = {
- 'active': lambda *a: True,
- }
-
- def export_cal(self, cr, uid, ids, vobj='vevent', context={}):
- """ Export Calendar
- @param self: The object pointer
- @param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
- @param ids: List of calendar’s IDs
- @param context: A standard dictionary for contextual values
- """
-
- cal = self.browse(cr, uid, ids[0])
- ical = vobject.iCalendar()
- for line in cal.line_ids:
- if line.name in ('valarm', 'attendee'):
- continue
- mod_obj = self.pool.get(line.object_id.model)
- data_ids = mod_obj.search(cr, uid, eval(line.domain), context=context)
- datas = mod_obj.read(cr, uid, data_ids, context=context)
- context.update({'model': line.object_id.model,
- 'calendar_id': cal.id
- })
- self.__attribute__ = get_attribute_mapping(cr, uid, line.name, context)
- self.create_ics(cr, uid, datas, line.name, ical, context=context)
- return ical.serialize()
-
- def import_cal(self, cr, uid, content, data_id=None, context=None):
- """ Import Calendar
- @param self: The object pointer
- @param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
- @param data_id: Get Data’s ID or False
- @param context: A standard dictionary for contextual values
- """
-
- if not context:
- context = {}
- vals = []
- ical_data = base64.decodestring(content)
- parsedCal = vobject.readOne(ical_data)
- if not data_id:
- data_id = self.search(cr, uid, [])[0]
- cal = self.browse(cr, uid, data_id)
- cal_children = {}
- count = 0
- for line in cal.line_ids:
- cal_children[line.name] = line.object_id.model
- for child in parsedCal.getChildren():
- if child.name.lower() in cal_children:
- context.update({'model': cal_children[child.name.lower()],
- 'calendar_id': cal.id
- })
- self.__attribute__ = get_attribute_mapping(cr, uid, child.name.lower(), context=context)
- val = self.parse_ics(cr, uid, child, cal_children=cal_children, context=context)
- vals.append(val)
- obj = self.pool.get(cal_children[child.name.lower()])
- if hasattr(obj, 'check_import'):
- obj.check_import(cr, uid, vals, context=context)
- else:
- self.check_import(cr, uid, vals, context=context)
- return {}
-Calendar()
-
-
-class basic_calendar_line(osv.osv):
- """ Calendar Lines """
-
- _name = 'basic.calendar.lines'
- _description = 'Calendar Lines'
-
- _columns = {
- 'name': fields.selection([('vevent', 'Event'), ('vtodo', 'TODO'), \
- ('valarm', 'Alarm'), \
- ('attendee', 'Attendee')], \
- string="Type", size=64),
- 'object_id': fields.many2one('ir.model', 'Object'),
- 'calendar_id': fields.many2one('basic.calendar', 'Calendar', \
- required=True, ondelete='cascade'),
- 'domain': fields.char('Domain', size=124),
- 'mapping_ids': fields.one2many('basic.calendar.fields', 'type_id', 'Fields Mapping')
- }
-
- _defaults = {
- 'domain': lambda *a: '[]',
- }
-
- def create(self, cr, uid, vals, context={}):
- """ create calendar's line
- @param self: The object pointer
- @param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
- @param vals: Get the Values
- @param context: A standard dictionary for contextual values
- """
-
- cr.execute("Select count(id) from basic_calendar_lines \
- where name='%s' and calendar_id=%s" % (vals.get('name'), vals.get('calendar_id')))
- res = cr.fetchone()
- if res:
- if res[0] > 0:
- raise osv.except_osv(_('Warning !'), _('Can not create \
-line "%s" more than once' % (vals.get('name'))))
- return super(basic_calendar_line, self).create(cr, uid, vals, context=context)
-
-basic_calendar_line()
-
-
-class basic_calendar_attribute(osv.osv):
- _name = 'basic.calendar.attributes'
- _description = 'Calendar attributes'
- _columns = {
- 'name': fields.char("Name", size=64, required=True),
- 'type': fields.selection([('vevent', 'Event'), ('vtodo', 'TODO'), \
- ('alarm', 'Alarm'), \
- ('attendee', 'Attendee')], \
- string="Type", size=64, required=True),
- }
-
-basic_calendar_attribute()
-
-
-class basic_calendar_fields(osv.osv):
- """ Calendar fields """
-
- _name = 'basic.calendar.fields'
- _description = 'Calendar fields'
-
- _columns = {
- 'field_id': fields.many2one('ir.model.fields', 'OpenObject Field'),
- 'name': fields.many2one('basic.calendar.attributes', 'Name', required=True),
- 'type_id': fields.many2one('basic.calendar.lines', 'Type', \
- required=True, ondelete='cascade'),
- 'expr': fields.char("Expression", size=64),
- 'fn': fields.selection([('field', 'Use the field'),
- ('const', 'Expression as constant'),
- ('hours', 'Interval in hours'),
- ], 'Function'),
- 'mapping': fields.text('Mapping'),
- }
-
- _defaults = {
- 'fn': lambda *a: 'field',
- }
-
- def check_line(self, cr, uid, vals, name, context=None):
- """ check calendar's line
- @param self: The object pointer
- @param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
- @param vals: Get Values
- @param context: A standard dictionary for contextual values
- """
- f_obj = self.pool.get('ir.model.fields')
- field = f_obj.browse(cr, uid, vals['field_id'], context=context)
- relation = field.relation
- line_obj = self.pool.get('basic.calendar.lines')
- l_id = line_obj.search(cr, uid, [('name', '=', name)])
- if l_id:
- line = line_obj.browse(cr, uid, l_id, context=context)[0]
- line_rel = line.object_id.model
- if (relation != 'NULL') and (not relation == line_rel):
- raise osv.except_osv(_('Warning !'), _('Please provide proper configuration of "%s" in Calendar Lines' % (name)))
- return True
-
- def create(self, cr, uid, vals, context={}):
- """ Create Calendar's fields
- @param self: The object pointer
- @param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
- @param vals: Get Values
- @param context: A standard dictionary for contextual values
- """
-
- cr.execute('select name from basic_calendar_attributes \
- where id=%s' % (vals.get('name')))
- name = cr.fetchone()
- name = name[0]
- if name in ('valarm', 'attendee'):
- self.check_line(cr, uid, vals, name, context=context)
- cr.execute("Select count(id) from basic_calendar_fields \
- where name=%s and type_id=%s" % (vals.get('name'), vals.get('type_id')))
- res = cr.fetchone()
- if res:
- if res[0] > 0:
- raise osv.except_osv(_('Warning !'), _('Can not map the field more than once'))
- return super(basic_calendar_fields, self).create(cr, uid, vals, context=context)
-
- def write(self, cr, uid, ids, vals, context=None):
- """ write Calendar's fields
- @param self: The object pointer
- @param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
- @param vals: Get Values
- @param context: A standard dictionary for contextual values
- """
-
- if not vals:
- return
- for id in ids:
- field = self.browse(cr, uid, id, context=context)
- name = field.name.name
- if name in ('valarm', 'attendee'):
- self.check_line(cr, uid, vals, name, context=context)
- qry = "Select count(id) from basic_calendar_fields \
- where name=%s and type_id=%s" % (field.name.id, field.type_id.id)
- cr.execute(qry)
- res = cr.fetchone()
- if res:
- if res[0] > 1:
- raise osv.except_osv(_('Warning !'), _('Can not map same field more than once'))
- return super(basic_calendar_fields, self).write(cr, uid, ids, vals, context)
-
-basic_calendar_fields()
-
-
-class Event(CalDAV, osv.osv_memory):
- _name = 'basic.calendar.event'
- _calname = 'vevent'
- __attribute__ = {
- 'class': None, # Use: O-1, Type: TEXT, Defines the access classification for a calendar component like "PUBLIC" / "PRIVATE" / "CONFIDENTIAL"
- 'created': None, # Use: O-1, Type: DATE-TIME, Specifies the date and time that the calendar information was created by the calendar user agent in the calendar store.
- 'description': None, # Use: O-1, Type: TEXT, Provides a more complete description of the calendar component, than that provided by the "SUMMARY" property.
- 'dtstart': None, # Use: O-1, Type: DATE-TIME, Specifies when the calendar component begins.
- 'geo': None, # Use: O-1, Type: FLOAT, Specifies information related to the global position for the activity specified by a calendar component.
- 'last-mod': None, # Use: O-1, Type: DATE-TIME Specifies the date and time that the information associated with the calendar component was last revised in the calendar store.
- 'location': None, # Use: O-1, Type: TEXT Defines the intended venue for the activity defined by a calendar component.
- 'organizer': None, # Use: O-1, Type: CAL-ADDRESS, Defines the organizer for a calendar component.
- 'priority': None, # Use: O-1, Type: INTEGER, Defines the relative priority for a calendar component.
- 'dtstamp': None, # Use: O-1, Type: DATE-TIME, Indicates the date/time that the instance of the iCalendar object was created.
- 'seq': None, # Use: O-1, Type: INTEGER, Defines the revision sequence number of the calendar component within a sequence of revision.
- 'status': None, # Use: O-1, Type: TEXT, Defines the overall status or confirmation for the calendar component.
- 'summary': None, # Use: O-1, Type: TEXT, Defines a short summary or subject for the calendar component.
- 'transp': None, # Use: O-1, Type: TEXT, Defines whether an event is transparent or not to busy time searches.
- 'uid': None, # Use: O-1, Type: TEXT, Defines the persistent, globally unique identifier for the calendar component.
- 'url': None, # Use: O-1, Type: URL, Defines a Uniform Resource Locator (URL) associated with the iCalendar object.
- 'recurid': None,
- 'attach': None, # Use: O-n, Type: BINARY, Provides the capability to associate a document object with a calendar component.
- 'attendee': None, # Use: O-n, Type: CAL-ADDRESS, Defines an "Attendee" within a calendar component.
- 'categories': None, # Use: O-n, Type: TEXT, Defines the categories for a calendar component.
- 'comment': None, # Use: O-n, Type: TEXT, Specifies non-processing information intended to provide a comment to the calendar user.
- 'contact': None, # Use: O-n, Type: TEXT, Used to represent contact information or alternately a reference to contact information associated with the calendar component.
- 'exdate': None, # Use: O-n, Type: DATE-TIME, Defines the list of date/time exceptions for a recurring calendar component.
- 'exrule': None, # Use: O-n, Type: RECUR, Defines a rule or repeating pattern for an exception to a recurrence set.
- 'rstatus': None,
- 'related': None, # Use: O-n, Specify the relationship of the alarm trigger with respect to the start or end of the calendar component.
- # like A trigger set 5 minutes after the end of the event or to-do.---> TRIGGER;related=END:PT5M
- 'resources': None, # Use: O-n, Type: TEXT, Defines the equipment or resources anticipated for an activity specified by a calendar entity like RESOURCES:EASEL,PROJECTOR,VCR, LANGUAGE=fr:1 raton-laveur
- 'rdate': None, # Use: O-n, Type: DATE-TIME, Defines the list of date/times for a recurrence set.
- 'rrule': None, # Use: O-n, Type: RECUR, Defines a rule or repeating pattern for recurring events, to-dos, or time zone definitions.
- 'x-prop': None,
- 'duration': None, # Use: O-1, Type: DURATION, Specifies a positive duration of time.
- 'dtend': None, # Use: O-1, Type: DATE-TIME, Specifies the date and time that a calendar component ends.
- }
-
- def export_cal(self, cr, uid, datas, vobj='vevent', context={}):
- """ Export calendar
- @param self: The object pointer
- @param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
- @param datas: Get datas
- @param context: A standard dictionary for contextual values
- """
-
- return super(Event, self).export_cal(cr, uid, datas, 'vevent', context=context)
-
-Event()
-
-
-class ToDo(CalDAV, osv.osv_memory):
- _name = 'basic.calendar.todo'
- _calname = 'vtodo'
-
- __attribute__ = {
- 'class': None,
- 'completed': None,
- 'created': None,
- 'description': None,
- 'dtstamp': None,
- 'dtstart': None,
- 'duration': None,
- 'due': None,
- 'geo': None,
- 'last-mod ': None,
- 'location': None,
- 'organizer': None,
- 'percent': None,
- 'priority': None,
- 'recurid': None,
- 'seq': None,
- 'status': None,
- 'summary': None,
- 'uid': None,
- 'url': None,
- 'attach': None,
- 'attendee': None,
- 'categories': None,
- 'comment': None,
- 'contact': None,
- 'exdate': None,
- 'exrule': None,
- 'rstatus': None,
- 'related': None,
- 'resources': None,
- 'rdate': None,
- 'rrule': None,
- }
-
- def export_cal(self, cr, uid, datas, vobj='vevent', context={}):
- """ Export Calendar
- @param self: The object pointer
- @param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
- @param datas: Get datas
- @param context: A standard dictionary for contextual values
- """
-
- return super(ToDo, self).export_cal(cr, uid, datas, 'vtodo', context=context)
-
-ToDo()
-
-
-class Journal(CalDAV):
- __attribute__ = {
- }
-
-
-class FreeBusy(CalDAV):
- __attribute__ = {
- 'contact': None, # Use: O-1, Type: Text, Represent contact information or alternately a reference to contact information associated with the calendar component.
- 'dtstart': None, # Use: O-1, Type: DATE-TIME, Specifies when the calendar component begins.
- 'dtend': None, # Use: O-1, Type: DATE-TIME, Specifies the date and time that a calendar component ends.
- 'duration': None, # Use: O-1, Type: DURATION, Specifies a positive duration of time.
- 'dtstamp': None, # Use: O-1, Type: DATE-TIME, Indicates the date/time that the instance of the iCalendar object was created.
- 'organizer': None, # Use: O-1, Type: CAL-ADDRESS, Defines the organizer for a calendar component.
- 'uid': None, # Use: O-1, Type: Text, Defines the persistent, globally unique identifier for the calendar component.
- 'url': None, # Use: O-1, Type: URL, Defines a Uniform Resource Locator (URL) associated with the iCalendar object.
- 'attendee': None, # Use: O-n, Type: CAL-ADDRESS, Defines an "Attendee" within a calendar component.
- 'comment': None, # Use: O-n, Type: TEXT, Specifies non-processing information intended to provide a comment to the calendar user.
- 'freebusy': None, # Use: O-n, Type: PERIOD, Defines one or more free or busy time intervals.
- 'rstatus': None,
- 'X-prop': None,
- }
-
-
-class Timezone(CalDAV, osv.osv_memory):
- _name = 'basic.calendar.timezone'
- _calname = 'vtimezone'
-
- __attribute__ = {
- 'tzid': {'field': 'tzid'}, # Use: R-1, Type: Text, Specifies the text value that uniquely identifies the "VTIMEZONE" calendar component.
- 'last-mod': None, # Use: O-1, Type: DATE-TIME, Specifies the date and time that the information associated with the calendar component was last revised in the calendar store.
- 'tzurl': None, # Use: O-1, Type: URI, Provides a means for a VTIMEZONE component to point to a network location that can be used to retrieve an up-to-date version of itself.
- 'standardc': {'tzprop': None}, # Use: R-1,
- 'daylightc': {'tzprop': None}, # Use: R-1,
- 'x-prop': None, # Use: O-n, Type: Text,
- }
-
- def get_name_offset(self, cr, uid, tzid, context={}):
- """ Get Name Offset value
- @param self: The object pointer
- @param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
- @param context: A standard dictionary for contextual values
- """
-
- mytz = pytz.timezone(tzid.title())
- mydt = datetime.now(tz=mytz)
- offset = mydt.utcoffset()
- val = offset.days * 24 + float(offset.seconds) / 3600
- realoffset = '%02d%02d' % (math.floor(abs(val)), \
- round(abs(val) % 1 + 0.01, 2) * 60)
- realoffset = (val < 0 and ('-' + realoffset) or ('+' + realoffset))
- return (mydt.tzname(), realoffset)
-
- def export_cal(self, cr, uid, model, tzid, ical, context={}):
- """ Export Calendar
- @param self: The object pointer
- @param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
- @param model: Get Model's name
- @param context: A standard dictionary for contextual values
- """
-
- ctx = context.copy()
- ctx.update({'model': model})
- cal_tz = ical.add('vtimezone')
- cal_tz.add('TZID').value = tzid.title()
- tz_std = cal_tz.add('STANDARD')
- tzname, offset = self.get_name_offset(cr, uid, tzid)
- tz_std.add("TZOFFSETFROM").value = offset
- tz_std.add("TZOFFSETTO").value = offset
- tz_std.add("DTSTART").value = datetime.now() # TODO
- tz_std.add("TZNAME").value = tzname
- return ical
-
- def import_cal(self, cr, uid, ical_data, context=None):
- """ Import Calendar
- @param self: The object pointer
- @param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
- @param ical_data: Get calendar's data
- @param context: A standard dictionary for contextual values
- """
-
- for child in ical_data.getChildren():
- if child.name.lower() == 'tzid':
- tzname = child.value
- self.ical_set(child.name.lower(), tzname, 'value')
- vals = map_data(cr, uid, self)
- return vals
-
-Timezone()
-
-
-class Alarm(CalDAV, osv.osv_memory):
- _name = 'basic.calendar.alarm'
- _calname = 'alarm'
-
- __attribute__ = {
- 'action': None, # Use: R-1, Type: Text, defines the action to be invoked when an alarm is triggered LIKE "AUDIO" / "DISPLAY" / "EMAIL" / "PROCEDURE"
- 'description': None, # Type: Text, Provides a more complete description of the calendar component, than that provided by the "SUMMARY" property. Use:- R-1 for DISPLAY,Use:- R-1 for EMAIL,Use:- R-1 for PROCEDURE
- 'summary': None, # Use: R-1, Type: Text Which contains the text to be used as the message subject. Use for EMAIL
- 'attendee': None, # Use: R-n, Type: CAL-ADDRESS, Contain the email address of attendees to receive the message. It can also include one or more. Use for EMAIL
- 'trigger': None, # Use: R-1, Type: DURATION, The "TRIGGER" property specifies a duration prior to the start of an event or a to-do. The "TRIGGER" edge may be explicitly set to be relative to the "START" or "END" of the event or to-do with the "related" parameter of the "TRIGGER" property. The "TRIGGER" property value type can alternatively be set to an absolute calendar date and time of day value. Use for all action like AUDIO, DISPLAY, EMAIL and PROCEDURE
- 'duration': None, # Type: DURATION, Duration' and 'repeat' are both optional, and MUST NOT occur more than once each, but if one occurs, so MUST the other. Use:- 0-1 for AUDIO, EMAIL and PROCEDURE, Use:- 0-n for DISPLAY
- 'repeat': None, # Type: INTEGER, Duration' and 'repeat' are both optional, and MUST NOT occur more than once each, but if one occurs, so MUST the other. Use:- 0-1 for AUDIO, EMAIL and PROCEDURE, Use:- 0-n for DISPLAY
- 'attach': None, # Use:- O-n: which MUST point to a sound resource, which is rendered when the alarm is triggered for AUDIO, Use:- O-n: which are intended to be sent as message attachments for EMAIL, Use:- R-1:which MUST point to a procedure resource, which is invoked when the alarm is triggered for PROCEDURE.
- 'x-prop': None,
- }
-
- def export_cal(self, cr, uid, model, alarm_id, vevent, context={}):
- """ Export Calendar
- @param self: The object pointer
- @param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
- @param model: Get Model's name
- @param alarm_id: Get Alarm's Id
- @param context: A standard dictionary for contextual values
- """
-
- valarm = vevent.add('valarm')
- alarm_object = self.pool.get(model)
- alarm_data = alarm_object.read(cr, uid, alarm_id, [])
-
- # Compute trigger data
- interval = alarm_data['trigger_interval']
- occurs = alarm_data['trigger_occurs']
- duration = (occurs == 'after' and alarm_data['trigger_duration']) \
- or -(alarm_data['trigger_duration'])
- related = alarm_data['trigger_related']
- trigger = valarm.add('TRIGGER')
- trigger.params['related'] = [related.upper()]
- if interval == 'days':
- delta = timedelta(days=duration)
- if interval == 'hours':
- delta = timedelta(hours=duration)
- if interval == 'minutes':
- delta = timedelta(minutes=duration)
- trigger.value = delta
-
- # Compute other details
- valarm.add('DESCRIPTION').value = alarm_data['name'] or 'OpenERP'
- valarm.add('ACTION').value = alarm_data['action']
- return vevent
-
- def import_cal(self, cr, uid, ical_data, context=None):
- """ Import Calendar
- @param self: The object pointer
- @param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
- @param ical_data: Get calendar's Data
- @param context: A standard dictionary for contextual values
- """
-
- ctx = context.copy()
- ctx.update({'model': context.get('model', None)})
- self.__attribute__ = get_attribute_mapping(cr, uid, self._calname, ctx)
- for child in ical_data.getChildren():
- if child.name.lower() == 'trigger':
- seconds = child.value.seconds
- days = child.value.days
- diff = (days * 86400) + seconds
- interval = 'days'
- related = 'before'
- if not seconds:
- duration = abs(days)
- related = days > 0 and 'after' or 'before'
- elif (abs(diff) / 3600) == 0:
- duration = abs(diff / 60)
- interval = 'minutes'
- related = days >= 0 and 'after' or 'before'
- else:
- duration = abs(diff / 3600)
- interval = 'hours'
- related = days >= 0 and 'after' or 'before'
- self.ical_set('trigger_interval', interval, 'value')
- self.ical_set('trigger_duration', duration, 'value')
- self.ical_set('trigger_occurs', related.lower(), 'value')
- if child.params:
- if child.params.get('related'):
- self.ical_set('trigger_related', child.params.get('related')[0].lower(), 'value')
- else:
- self.ical_set(child.name.lower(), child.value.lower(), 'value')
- vals = map_data(cr, uid, self)
- return vals
-
-Alarm()
-
-
-class Attendee(CalDAV, osv.osv_memory):
- _name = 'basic.calendar.attendee'
- _calname = 'attendee'
-
- __attribute__ = {
- 'cutype': None, # Use: 0-1 Specify the type of calendar user specified by the property like "INDIVIDUAL"/"GROUP"/"RESOURCE"/"ROOM"/"UNKNOWN".
- 'member': None, # Use: 0-1 Specify the group or list membership of the calendar user specified by the property.
- 'role': None, # Use: 0-1 Specify the participation role for the calendar user specified by the property like "CHAIR"/"REQ-PARTICIPANT"/"OPT-PARTICIPANT"/"NON-PARTICIPANT"
- 'partstat': None, # Use: 0-1 Specify the participation status for the calendar user specified by the property. like use for VEVENT:- "NEEDS-ACTION"/"ACCEPTED"/"DECLINED"/"TENTATIVE"/"DELEGATED", use for VTODO:-"NEEDS-ACTION"/"ACCEPTED"/"DECLINED"/"TENTATIVE"/"DELEGATED"/"COMPLETED"/"IN-PROCESS" and use for VJOURNAL:- "NEEDS-ACTION"/"ACCEPTED"/"DECLINED".
- 'rsvp': None, # Use: 0-1 Specify whether there is an expectation of a favor of a reply from the calendar user specified by the property value like TRUE / FALSE.
- 'delegated-to': None, # Use: 0-1 Specify the calendar users to whom the calendar user specified by the property has delegated participation.
- 'delegated-from': None, # Use: 0-1 Specify the calendar users that have delegated their participation to the calendar user specified by the property.
- 'sent-by': None, # Use: 0-1 Specify the calendar user that is acting on behalf of the calendar user specified by the property.
- 'cn': None, # Use: 0-1 Specify the common name to be associated with the calendar user specified by the property.
- 'dir': None, # Use: 0-1 Specify reference to a directory entry associated with the calendar user specified by the property.
- 'language': None, # Use: 0-1 Specify the language for text values in a property or property parameter.
- }
-
- def import_cal(self, cr, uid, ical_data, context=None):
- """ Import Calendar
- @param self: The object pointer
- @param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
- @param ical_data: Get calendar's Data
- @param context: A standard dictionary for contextual values
- """
-
- ctx = context.copy()
- ctx.update({'model': context.get('model', None)})
- self.__attribute__ = get_attribute_mapping(cr, uid, self._calname, ctx)
- for para in ical_data.params:
- if para.lower() == 'cn':
- self.ical_set(para.lower(), ical_data.params[para][0]+':'+ \
- ical_data.value, 'value')
- else:
- self.ical_set(para.lower(), ical_data.params[para][0].lower(), 'value')
- if not ical_data.params.get('CN'):
- self.ical_set('cn', ical_data.value, 'value')
- vals = map_data(cr, uid, self)
- return vals
-
- def export_cal(self, cr, uid, model, attendee_ids, vevent, context={}):
- """ Export Calendar
- @param self: The object pointer
- @param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
- @param model: Get model's name
- @param attendee_ids: Get Attendee's Id
- @param context: A standard dictionary for contextual values
- """
-
- attendee_object = self.pool.get(model)
- ctx = context.copy()
- ctx.update({'model': model})
- self.__attribute__ = get_attribute_mapping(cr, uid, self._calname, ctx)
- for attendee in attendee_object.read(cr, uid, attendee_ids, []):
- attendee_add = vevent.add('attendee')
- cn_val = ''
- for a_key, a_val in self.__attribute__.items():
- if attendee[a_val['field']] and a_val['field'] != 'cn':
- if a_val['type'] in ('text', 'char', 'selection'):
- attendee_add.params[a_key] = [str(attendee[a_val['field']])]
- elif a_val['type'] == 'boolean':
- attendee_add.params[a_key] = [str(attendee[a_val['field']])]
- if a_val['field'] == 'cn' and attendee[a_val['field']]:
- cn_val = [str(attendee[a_val['field']])]
- if cn_val:
- attendee_add.params['CN'] = cn_val
- if not attendee['email']:
- raise osv.except_osv(_('Error !'), _('Attendee must have an Email Id'))
- elif attendee['email']:
- attendee_add.value = 'MAILTO:' + attendee['email']
- return vevent
-
-Attendee()
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+PROPFIND.mk_prop_response = mk_prop_response
diff --git a/addons/caldav/caldav_cache.py b/addons/caldav/caldav_cache.py
deleted file mode 100644
index 4e5768c3445..00000000000
--- a/addons/caldav/caldav_cache.py
+++ /dev/null
@@ -1,62 +0,0 @@
-# -*- coding: utf-8 -*-
-##############################################################################
-#
-# OpenERP, Open Source Management Solution
-# Copyright (C) 2004-2009 Tiny SPRL ().
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-#
-##############################################################################
-
-import time
-import heapq
-
-def memoize(maxsize):
- """decorator to 'memoize' a function - caching its results"""
- def decorating_function(f):
- cache = {} # map from key to value
- heap = [] # list of keys, in LRU heap
- cursize = 0 # because len() is slow
- def wrapper(*args):
- key = repr(args)
- # performance crap
- _cache = cache
- _heap = heap
- _heappop = heapq.heappop
- _heappush = heapq.heappush
- _time = time.time
- _cursize = cursize
- _maxsize = maxsize
- if not _cache.has_key(key):
- if _cursize == _maxsize:
- # pop oldest element
- (_, oldkey) = _heappop(_heap)
- _cache.pop(oldkey)
- else:
- _cursize += 1
- # insert this element
- _cache[key] = f(*args)
- _heappush(_heap, (_time(), key))
- wrapper.misses += 1
- else:
- wrapper.hits += 1
- return cache[key]
- wrapper.__doc__ = f.__doc__
- wrapper.__name__ = f.__name__
- wrapper.hits = wrapper.misses = 0
- return wrapper
- return decorating_function
-
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
\ No newline at end of file
diff --git a/addons/caldav/caldav_data.xml b/addons/caldav/caldav_data.xml
new file mode 100644
index 00000000000..04f5a6004a7
--- /dev/null
+++ b/addons/caldav/caldav_data.xml
@@ -0,0 +1,380 @@
+
+
+
+
+
+ Calendars
+ True
+
+
+
+
+
+
+
+ uid
+ vevent
+
+
+
+ seq
+ vevent
+
+
+
+ recurrence-id
+ vevent
+
+
+
+ transp
+ vevent
+
+
+
+ attendee
+ vevent
+
+
+
+ related
+ vevent
+
+
+
+ rrule
+ vevent
+
+
+
+ dtend
+ vevent
+
+
+
+ valarm
+ vevent
+
+
+
+ vtimezone
+ vevent
+
+
+
+ priority
+ vevent
+
+
+
+ location
+ vevent
+
+
+
+ exrule
+ vevent
+
+
+
+ resources
+ vevent
+
+
+
+ rstatus
+ vevent
+
+
+
+ status
+ vevent
+
+
+
+ exdate
+ vevent
+
+
+
+ dtstamp
+ vevent
+
+
+
+ description
+ vevent
+
+
+
+ rdate
+ vevent
+
+
+
+ dtstart
+ vevent
+
+
+
+ class
+ vevent
+
+
+
+ x-openobject-model
+ vevent
+
+
+
+ created
+ vevent
+
+
+
+ url
+ vevent
+
+
+
+ summary
+ vevent
+
+
+
+ contact
+ vevent
+
+
+
+
+
+
+ status
+ vtodo
+
+
+
+
+
+ attendee
+ vtodo
+
+
+
+ valarm
+ vtodo
+
+
+
+ description
+ vtodo
+
+
+
+ seq
+ vtodo
+
+
+
+ vtimezone
+ vtodo
+
+
+
+ url
+ vtodo
+
+
+
+ completed
+ vtodo
+
+
+
+ percent
+ vtodo
+
+
+
+ due
+ vtodo
+
+
+
+ summary
+ vtodo
+
+
+
+ priority
+ vtodo
+
+
+
+ exdate
+ vtodo
+
+
+
+ location
+ vtodo
+
+
+
+ exrule
+ vtodo
+
+
+
+ duration
+ vtodo
+
+
+
+ organizer
+ vtodo
+
+
+
+ dtstart
+ vtodo
+
+
+
+ rrule
+ vtodo
+
+
+
+ class
+ vtodo
+
+
+
+ uid
+ vtodo
+
+
+
+
+
+ cn
+ attendee
+
+
+
+ sent-by
+ attendee
+
+
+
+ language
+ attendee
+
+
+
+ delegated-from
+ attendee
+
+
+
+ member
+ attendee
+
+
+
+ cutype
+ attendee
+
+
+
+ role
+ attendee
+
+
+
+ partstat
+ attendee
+
+
+
+ delegated-to
+ attendee
+
+
+
+ dir
+ attendee
+
+
+
+ rsvp
+ attendee
+
+
+
+
+
+ attendee
+ alarm
+
+
+
+ trigger_duration
+ alarm
+
+
+
+ description
+ alarm
+
+
+
+ attach
+ alarm
+
+
+
+ trigger_occurs
+ alarm
+
+
+
+ trigger_interval
+ alarm
+
+
+
+ summary
+ alarm
+
+
+
+ duration
+ alarm
+
+
+
+ repeat
+ alarm
+
+
+
+ action
+ alarm
+
+
+
+ trigger_related
+ alarm
+
+
+
+
diff --git a/addons/caldav/caldav_fs.py b/addons/caldav/caldav_fs.py
deleted file mode 100644
index 9ab7a2156d6..00000000000
--- a/addons/caldav/caldav_fs.py
+++ /dev/null
@@ -1,334 +0,0 @@
-# -*- coding: utf-8 -*-
-##############################################################################
-#
-# OpenERP, Open Source Management Solution
-# Copyright (C) 2004-2009 Tiny SPRL ().
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-#
-##############################################################################
-
-import pooler
-
-import base64
-import sys
-import os
-import time
-from string import joinfields, split, lower
-
-from service import security
-
-import netsvc
-import urlparse
-
-from DAV.constants import COLLECTION, OBJECT
-from DAV.errors import *
-from DAV.iface import *
-import urllib
-
-from DAV.davcmd import copyone, copytree, moveone, movetree, delone, deltree
-from caldav_cache import memoize
-from tools import misc
-CACHE_SIZE=20000
-
-
-class tinydav_handler(dav_interface):
- """
- This class models a Tiny ERP interface for the DAV server
- """
- PROPS={'DAV:': dav_interface.PROPS['DAV:'], }
-
- M_NS={ "DAV:": dav_interface.M_NS['DAV:'], }
-
- def __init__(self, parent, verbose=False):
- self.db_name = False
- self.parent = parent
- self.baseuri = parent.baseuri
-
- def get_propnames(self, uri):
- props = self.PROPS
- self.parent.log_message('get propnames: %s' % uri)
- if uri[-1]=='/':uri=uri[:-1]
- cr, uid, pool, dbname, uri2 = self.get_cr(uri)
- if not dbname:
- cr.close()
- return props
- node = self.uri2object(cr, uid, pool, uri2)
- if node:
- props.update(node.get_dav_props(cr))
- cr.close()
- return props
-
- def get_prop(self,uri,ns,propname):
- """ return the value of a given property
-
- uri -- uri of the object to get the property of
- ns -- namespace of the property
- pname -- name of the property
- """
- if self.M_NS.has_key(ns):
- return dav_interface.get_prop(self, uri, ns, propname)
-
- if uri[-1]=='/':uri = uri[:-1]
- cr, uid, pool, dbname, uri2 = self.get_cr(uri)
- if not dbname:
- cr.close()
- raise DAV_NotFound
- node = self.uri2object(cr, uid, pool, uri2)
- if not node:
- cr.close()
- raise DAV_NotFound
- res = node.get_dav_eprop(cr, ns, propname)
- cr.close()
- return res
-
- def urijoin(self,*ajoin):
- """ Return the base URI of this request, or even join it with the
- ajoin path elements
- """
- return self.baseuri+ '/'.join(ajoin)
-
- def uri2local(self, uri):
- uparts = urlparse.urlparse(uri)
- reluri = uparts[2]
- if reluri and reluri[-1]=="/":
- reluri = reluri[:-1]
- return reluri
-
- #
- # pos: -1 to get the parent of the uri
- #
- def get_cr(self, uri):
- pdb = self.parent.auth_proxy.last_auth
- reluri = self.uri2local(uri)
- try:
- dbname = reluri.split('/')[2]
- except:
- dbname = False
- if not dbname:
- return None, None, None, False, None
- if not pdb and dbname:
- # if dbname was in our uri, we should have authenticated
- # against that.
- raise Exception("Programming error")
- assert pdb == dbname, " %s != %s" %(pdb, dbname)
- user, passwd, dbn2, uid = self.parent.auth_proxy.auth_creds[pdb]
- db,pool = pooler.get_db_and_pool(dbname)
- cr = db.cursor()
- uri2 = reluri.split('/')[3:]
- return cr, uid, pool, dbname, uri2
-
- def uri2object(self, cr, uid, pool, uri):
- if not uid:
- return None
- return pool.get('basic.calendar').get_calendar_object(cr, uid, uri)
-
- def get_data(self, uri):
- self.parent.log_message('GET: %s' % uri)
- if uri[-1]=='/':uri=uri[:-1]
- cr, uid, pool, dbname, uri2 = self.get_cr(uri)
- try:
- if not dbname:
- raise DAV_Error, 409
- node = self.uri2object(cr, uid, pool, uri2)
- if not node:
- raise DAV_NotFound(uri2)
- try:
- datas = node.get_data(cr, uid)
- except TypeError, e:
- import traceback
- self.parent.log_error("GET typeError: %s", str(e))
- self.parent.log_message("Exc: %s", traceback.format_exc())
- raise DAV_Forbidden
- except IndexError, e :
- self.parent.log_error("GET IndexError: %s", str(e))
- raise DAV_NotFound(uri2)
- except Exception, e:
- import traceback
- self.parent.log_error("GET exception: %s", str(e))
- self.parent.log_message("Exc: %s", traceback.format_exc())
- raise DAV_Error, 409
- return datas
- finally:
- cr.close()
-
- @memoize(CACHE_SIZE)
- def _get_dav_resourcetype(self, uri):
- """ return type of object """
- self.parent.log_message('get RT: %s' % uri)
- if uri[-1]=='/':uri=uri[:-1]
- cr, uid, pool, dbname, uri2 = self.get_cr(uri)
- try:
- if not dbname:
- return COLLECTION
- node = self.uri2object(cr, uid, pool, uri2)
- if not node:
- raise DAV_NotFound(uri2)
- return OBJECT
- finally:
- cr.close()
-
- def _get_dav_displayname(self, uri):
- self.parent.log_message('get DN: %s' % uri)
- if uri[-1]=='/':uri=uri[:-1]
- cr, uid, pool, dbname, uri2 = self.get_cr(uri)
- if not dbname:
- cr.close()
- return COLLECTION
- node = self.uri2object(cr, uid, pool, uri2)
- if not node:
- cr.close()
- raise DAV_NotFound(uri2)
- cr.close()
- return node.displayname
-
- @memoize(CACHE_SIZE)
- def _get_dav_getcontentlength(self, uri):
- """ return the content length of an object """
- self.parent.log_message('get length: %s' % uri)
- if uri[-1]=='/':uri=uri[:-1]
- result = 0
- cr, uid, pool, dbname, uri2 = self.get_cr(uri)
- if not dbname:
- cr.close()
- return '0'
- node = self.uri2object(cr, uid, pool, uri2)
- if not node:
- cr.close()
- raise DAV_NotFound(uri2)
- result = node.content_length or 0
- cr.close()
- return str(result)
-
- @memoize(CACHE_SIZE)
- def _get_dav_getetag(self, uri):
- """ return the ETag of an object """
- self.parent.log_message('get etag: %s' % uri)
- if uri[-1]=='/':uri=uri[:-1]
- result = 0
- cr, uid, pool, dbname, uri2 = self.get_cr(uri)
- if not dbname:
- cr.close()
- return '0'
- node = self.uri2object(cr, uid, pool, uri2)
- if not node:
- cr.close()
- raise DAV_NotFound(uri2)
- result = node.get_etag(cr)
- cr.close()
- return str(result)
-
- @memoize(CACHE_SIZE)
- def get_lastmodified(self, uri):
- """ return the last modified date of the object """
- if uri[-1]=='/':uri=uri[:-1]
- today = time.time()
- cr, uid, pool, dbname, uri2 = self.get_cr(uri)
- try:
- if not dbname:
- return today
- node = self.uri2object(cr, uid, pool, uri2)
- if not node:
- raise DAV_NotFound(uri2)
- if node.write_date:
- return time.mktime(time.strptime(node.write_date, '%Y-%m-%d %H:%M:%S'))
- else:
- return today
- finally:
- cr.close()
-
- @memoize(CACHE_SIZE)
- def get_creationdate(self, uri):
- """ return the last modified date of the object """
-
- if uri[-1]=='/':uri=uri[:-1]
- cr, uid, pool, dbname, uri2 = self.get_cr(uri)
- try:
- if not dbname:
- raise DAV_Error, 409
- node = self.uri2object(cr, uid, pool, uri2)
- if not node:
- raise DAV_NotFound(uri2)
- if node.create_date:
- result = time.strptime(node.create_date, '%Y-%m-%d %H:%M:%S')
- else:
- result = time.gmtime()
- return result
- finally:
- cr.close()
-
- @memoize(CACHE_SIZE)
- def _get_dav_getcontenttype(self, uri):
- self.parent.log_message('get contenttype: %s' % uri)
- if uri[-1]=='/':uri=uri[:-1]
- cr, uid, pool, dbname, uri2 = self.get_cr(uri)
- try:
- if not dbname:
- return 'httpd/unix-directory'
- node = self.uri2object(cr, uid, pool, uri2)
- if not node:
- raise DAV_NotFound(uri2)
- result = node.mimetype
- return result
- #raise DAV_NotFound, 'Could not find %s' % path
- finally:
- cr.close()
-
-
-
- def put(self, uri, data, content_type=None):
- """ put the object into the filesystem """
- self.parent.log_message('Putting %s (%d), %s'%(misc.ustr(uri), len(data), content_type))
- parent='/'.join(uri.split('/')[:-1])
- cr, uid, pool, dbname, uri2 = self.get_cr(uri)
- if not dbname:
- raise DAV_Forbidden
- try:
- node = self.uri2object(cr, uid, pool, uri2[:])
- except:
- node = False
-
- if not node:
- raise DAV_Forbidden
- else:
- try:
- node.set_data(cr, uid, data)
- except Exception, e:
- import traceback
- self.parent.log_error("Cannot save :%s", str(e))
- self.parent.log_message("Exc: %s", traceback.format_exc())
- raise DAV_Forbidden
-
- cr.commit()
- cr.close()
- return 201
-
- def exists(self, uri):
- """ test if a resource exists """
- result = False
- cr, uid, pool, dbname, uri2 = self.get_cr(uri)
- if not dbname:
- cr.close()
- return True
- try:
- node = self.uri2object(cr, uid, pool, uri2)
- if node:
- result = True
- except:
- pass
- cr.close()
- return result
-
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
\ No newline at end of file
diff --git a/addons/caldav/caldav_node.py b/addons/caldav/caldav_node.py
index fd17b52f478..8ec7422a045 100644
--- a/addons/caldav/caldav_node.py
+++ b/addons/caldav/caldav_node.py
@@ -25,31 +25,104 @@ import pooler
import tools
import time
import base64
+from document import nodes
+import StringIO
+
+class node_database(nodes.node_database):
+ def _child_get(self, cr, name=False, parent_id=False, domain=None):
+ dirobj = self.context._dirobj
+ uid = self.context.uid
+ ctx = self.context.context.copy()
+ ctx.update(self.dctx)
+ if not domain:
+ domain = []
+ domain2 = domain + [('calendar_collection','=', False)]
+ res = super(node_database, self)._child_get(cr, name=name, parent_id=parent_id, domain=domain2)
+ where = [('parent_id','=',parent_id)]
+ domain2 = domain + [('calendar_collection','=', True)]
+ if name:
+ where.append(('name','=',name))
+ if domain2:
+ where += domain2
+
+ where2 = where + [('type', '=', 'directory')]
+ ids = dirobj.search(cr, uid, where2, context=ctx)
+ for dirr in dirobj.browse(cr,uid,ids,context=ctx):
+ res.append(node_calendar_collection(dirr.name,self,self.context,dirr))
+ return res
+
+class node_calendar_collection(nodes.node_dir):
+ PROPS = {
+ "http://calendarserver.org/ns/" : ('getctag'),
+ }
+ M_NS = {
+ "http://calendarserver.org/ns/" : '_get_dav',
+ }
-class node_calendar(object):
- def __init__(self, path, context, calendar):
- self.path = path
- self.context = context
- self.calendar_id = calendar.id
- self.mimetype = 'ics'
- self.create_date = calendar.create_date
- self.write_date = calendar.write_date or calendar.create_date
- self.content_length = 0
- self.displayname = calendar.name
+ def get_dav_props(self, cr):
+ return self.PROPS
+
- def get_data(self, cr, uid):
- calendar_obj = pooler.get_pool(cr.dbname).get('basic.calendar')
- return calendar_obj.export_cal(cr, uid, [self.calendar_id])
+ def get_dav_eprop(self,cr, ns, propname):
+ if self.M_NS.has_key(ns):
+ prefix = self.M_NS[ns]
+ else:
+ print "No namespace:",ns, "( for prop:", propname,")"
+ return None
- def get_data_len(self, cr):
- return self.content_length
+ mname = prefix + "_" + propname
- def set_data(self, cr, uid, data):
- calendar_obj = pooler.get_pool(cr.dbname).get('basic.calendar')
- return calendar_obj.import_cal(cr, uid, base64.encodestring(data), self.calendar_id)
+ if not hasattr(self, mname):
+ return None
+ try:
+ m = getattr(self, mname)
+ r = m(cr)
+ return r
+ except AttributeError, e:
+ print 'Property %s not supported' % propname
+ print "Exception:", e
+ return None
+
+ def _file_get(self,cr, nodename=False):
+ return []
+
+
+
+
+ def _child_get(self, cr, name=False, parent_id=False, domain=None):
+ dirobj = self.context._dirobj
+ uid = self.context.uid
+ ctx = self.context.context.copy()
+ ctx.update(self.dctx)
+ where = [('collection_id','=',self.dir_id)]
+ ext = False
+ if name:
+ res = name.split('.ics')
+ if len(res) > 1:
+ name = res[0]
+ ext = '.ics'
+ if name:
+ where.append(('name','=',name))
+ if not domain:
+ domain = []
+ where = where + domain
+ fil_obj = dirobj.pool.get('basic.calendar')
+ ids = fil_obj.search(cr,uid,where,context=ctx)
+ res = []
+ for calender in fil_obj.browse(cr, uid, ids, context=ctx):
+ if not ext:
+ res.append(node_calendar(calender.name, self, self.context, calender))
+ else:
+ res.append(res_node_calendar(name, self, self.context, calender))
+ return res
+
+ def _get_dav_owner(self, cr):
+ return False
+
+
def get_etag(self, cr):
""" Get a tag, unique per object + modification.
@@ -64,24 +137,385 @@ class node_calendar(object):
return str(wtime)
def _get_ttag(self, cr):
- return 'calendar-%d' % self.calendar_id
+ return 'calendar collection-%d' % self.dir_id
+
+ def _get_dav_getctag(self, cr):
+ result = self.get_etag(cr)
+ return str(result)
+
+
+class node_calendar(nodes.node_class):
+ our_type = 'collection'
+ PROPS = {
+ "http://calendarserver.org/ns/" : ('getctag'),
+ "urn:ietf:params:xml:ns:caldav" : (
+ 'calendar-description',
+ 'calendar-data',
+ 'calendar-home-set',
+ 'calendar-user-address-set',
+ 'schedule-inbox-URL',
+ 'schedule-outbox-URL',)}
+ M_NS = {
+ "DAV:" : '_get_dav',
+ "http://calendarserver.org/ns/" : '_get_dav',
+ "urn:ietf:params:xml:ns:caldav" : '_get_caldav'}
+
+ def __init__(self,path, parent, context, calendar):
+ super(node_calendar,self).__init__(path, parent,context)
+ self.calendar_id = calendar.id
+ self.mimetype = 'application/x-directory'
+ self.create_date = calendar.create_date
+ self.write_date = calendar.write_date or calendar.create_date
+ self.content_length = 0
+ self.displayname = calendar.name
+ self.cal_type = calendar.type
+
+ def _get_dav_getctag(self, cr):
+ result = self._get_ttag(cr) + ':' + str(time.time())
+ return str(result)
+
+ def match_dav_eprop(self, cr, match, ns, prop):
+ if ns == "DAV:" and prop == "getetag":
+ dirobj = self.context._dirobj
+ uid = self.context.uid
+ ctx = self.context.context.copy()
+ tem, dav_time = tuple(match.split(':'))
+ model, res_id = tuple(tem.split('_'))
+ model_obj = dirobj.pool.get(model)
+ model = model_obj.browse(cr, uid, res_id, context=ctx)
+ write_time = model.write_date or model.create_date
+ wtime = time.mktime(time.strptime(write_time,'%Y-%m-%d %H:%M:%S'))
+ if float(dav_time) == float(wtime):
+ return True
+ return False
+ res = super(node_calendar, self).match_dav_eprop(cr, match, ns, prop)
+ return res
+
+
+ def get_domain(self, cr, filters):
+ res = []
+ dirobj = self.context._dirobj
+ uid = self.context.uid
+ ctx = self.context.context.copy()
+ ctx.update(self.dctx)
+ calendar_obj = dirobj.pool.get('basic.calendar')
+ if not filters:
+ return res
+ if filters.localName == 'calendar-query':
+ res = []
+ for filter_child in filters.childNodes:
+ if filter_child.nodeType == filter_child.TEXT_NODE:
+ continue
+ if filter_child.localName == 'filter':
+ for vcalendar_filter in filter_child.childNodes:
+ if vcalendar_filter.nodeType == vcalendar_filter.TEXT_NODE:
+ continue
+ if vcalendar_filter.localName == 'comp-filter':
+ if vcalendar_filter.getAttribute('name') == 'VCALENDAR':
+ for vevent_filter in vcalendar_filter.childNodes:
+ if vevent_filter.nodeType == vevent_filter.TEXT_NODE:
+ continue
+ if vevent_filter.localName == 'comp-filter':
+ if vevent_filter.getAttribute('name') == 'VEVENT':
+ res = [('type','=','vevent')]
+ if vevent_filter.getAttribute('name') == 'VTODO':
+ res = [('type','=','vtodo')]
+ return res
+ elif filters.localName == 'calendar-multiget':
+ names = []
+ for filter_child in filters.childNodes:
+ if filter_child.nodeType == filter_child.TEXT_NODE:
+ continue
+ if filter_child.localName == 'href':
+ if not filter_child.firstChild:
+ continue
+ uri = filter_child.firstChild.data
+ caluri = uri.split('/')
+ if len(caluri):
+ caluri = caluri[-2]
+ if caluri not in names : names.append(caluri)
+ res = [('name','in',names)]
+ return res
+ return res
+
+ def children(self, cr, domain=None):
+ return self._child_get(cr, domain=domain)
+
+ def child(self,cr, name, domain=None):
+ res = self._child_get(cr, name, domain=domain)
+ if res:
+ return res[0]
+ return None
-class Calendar(osv.osv):
- _inherit = 'basic.calendar'
+ def _child_get(self, cr, name=False, parent_id=False, domain=None):
+ dirobj = self.context._dirobj
+ uid = self.context.uid
+ ctx = self.context.context.copy()
+ ctx.update(self.dctx)
+ where = []
+ if name:
+ where.append(('id','=',int(name)))
+ if not domain:
+ domain = []
+ #for opr1, opt, opr2 in domain:
+ # if opr1 == 'type' and opr2 != self.cal_type:
+ # return []
- def get_calendar_object(self, cr, uid, uri, context=None):
- if not uri:
+ fil_obj = dirobj.pool.get('basic.calendar')
+ ids = fil_obj.search(cr, uid, domain)
+ res = []
+ if self.calendar_id in ids:
+ res = fil_obj.get_calendar_objects(cr, uid, [self.calendar_id], self, domain=where, context=ctx)
+ return res
+
+
+
+ def get_dav_props(self, cr):
+ return self.PROPS
+
+ def get_dav_eprop(self,cr, ns, propname):
+ if self.M_NS.has_key(ns):
+ prefix = self.M_NS[ns]
+ else:
+ print "No namespace:",ns, "( for prop:", propname,")"
return None
- if len(uri) > 1:
+ propname = propname.replace('-','_')
+ mname = prefix + "_" + propname
+ if not hasattr(self, mname):
return None
- name, file_type = tuple(uri[0].split('.'))
- res = self.name_search(cr, uid, name)
- if not res:
- return None
- calendar_id, calendar_name = res[0]
- calendar = self.browse(cr, uid, calendar_id)
- return node_calendar(uri, context, calendar)
-Calendar()
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4
\ No newline at end of file
+ try:
+ m = getattr(self, mname)
+ r = m(cr)
+ return r
+ except AttributeError, e:
+ print 'Property %s not supported' % propname
+ print "Exception:", e
+ return None
+
+
+ def create_child(self,cr,path,data):
+ """ API function to create a child file object and node
+ Return the node_* created
+ """
+ return self.set_data(cr, data)
+
+
+ def set_data(self, cr, data, fil_obj = None):
+ uid = self.context.uid
+ calendar_obj = self.context._dirobj.pool.get('basic.calendar')
+ return calendar_obj.import_cal(cr, uid, base64.encodestring(data), self.calendar_id)
+
+ def get_data_len(self, cr, fil_obj = None):
+ return self.content_length
+
+
+ def _get_ttag(self,cr):
+ return 'calendar-%d' % (self.calendar_id,)
+
+
+
+ def get_etag(self, cr):
+ """ Get a tag, unique per object + modification.
+
+ see. http://tools.ietf.org/html/rfc2616#section-13.3.3 """
+ return self._get_ttag(cr) + ':' + self._get_wtag(cr)
+
+ def _get_wtag(self, cr):
+ """ Return the modification time as a unique, compact string """
+ if self.write_date:
+ wtime = time.mktime(time.strptime(self.write_date, '%Y-%m-%d %H:%M:%S'))
+ else: wtime = time.time()
+ return str(wtime)
+
+ def rmcol(self, cr):
+ return False
+
+
+class res_node_calendar(nodes.node_class):
+ our_type = 'file'
+ PROPS = {
+ "http://calendarserver.org/ns/" : ('getctag'),
+ "urn:ietf:params:xml:ns:caldav" : (
+ 'calendar-description',
+ 'calendar-data',
+ 'calendar-home-set',
+ 'calendar-user-address-set',
+ 'schedule-inbox-URL',
+ 'schedule-outbox-URL',)}
+ M_NS = {
+ "http://calendarserver.org/ns/" : '_get_dav',
+ "urn:ietf:params:xml:ns:caldav" : '_get_caldav'}
+
+ def __init__(self,path, parent, context, res_obj, res_model=None, res_id=None):
+ super(res_node_calendar,self).__init__(path, parent, context)
+ self.mimetype = 'text/calendar'
+ self.create_date = parent.create_date
+ self.write_date = parent.write_date or parent.create_date
+ self.calendar_id = hasattr(parent, 'calendar_id') and parent.calendar_id or False
+ if res_obj:
+ if not self.calendar_id: self.calendar_id = res_obj.id
+ self.create_date = res_obj.create_date
+ self.write_date = res_obj.write_date or res_obj.create_date
+ self.displayname = res_obj.name
+
+ self.content_length = 0
+
+ self.model = res_model
+ self.res_id = res_id
+
+ def open(self, cr, mode=False):
+ uid = self.context.uid
+ if self.type in ('collection','database'):
+ return False
+ s = StringIO.StringIO(self.get_data(cr))
+ s.name = self
+ return s
+
+
+
+ def get_dav_props(self, cr):
+ return self.PROPS
+
+ def get_dav_eprop(self,cr, ns, propname):
+ if self.M_NS.has_key(ns):
+ prefix = self.M_NS[ns]
+ else:
+ print "No namespace:",ns, "( for prop:", propname,")"
+ return None
+ propname = propname.replace('-','_')
+ mname = prefix + "_" + propname
+ if not hasattr(self, mname):
+ return None
+
+ try:
+ m = getattr(self, mname)
+ r = m(cr)
+ return r
+ except AttributeError, e:
+ print 'Property %s not supported' % propname
+ print "Exception:", e
+ return None
+
+
+ def get_data(self, cr, fil_obj = None):
+ uid = self.context.uid
+ calendar_obj = self.context._dirobj.pool.get('basic.calendar')
+ context = self.context.context.copy()
+ context.update({'model': self.model, 'res_id':self.res_id})
+ res = calendar_obj.export_cal(cr, uid, [self.calendar_id], context=context)
+ return res
+
+ def get_data_len(self, cr, fil_obj = None):
+ return self.content_length
+
+ def set_data(self, cr, data, fil_obj = None):
+ uid = self.context.uid
+ calendar_obj = self.context._dirobj.pool.get('basic.calendar')
+ return calendar_obj.import_cal(cr, uid, base64.encodestring(data), self.calendar_id)
+
+ def _get_ttag(self,cr):
+ res = False
+ if self.model and self.res_id:
+ res = '%s_%d' % (self.model, self.res_id)
+ elif self.calendar_id:
+ res = '%d' % (self.calendar_id)
+ return res
+
+
+
+ def _get_caldav_calendar_data(self, cr):
+ return self.get_data(cr)
+
+
+ def _get_caldav_calendar_description(self, cr):
+ uid = self.context.uid
+ calendar_obj = self.context._dirobj.pool.get('basic.calendar')
+ ctx = self.context.context.copy()
+ ctx.update(self.dctx)
+ calendar = calendar_obj.browse(cr, uid, self.calendar_id, context=ctx)
+ return calendar.description
+
+
+ def _get_caldav_calendar_home_set(self, cr):
+ import xml.dom.minidom
+ import urllib
+ uid = self.context.uid
+ ctx = self.context.context.copy()
+ ctx.update(self.dctx)
+ doc = xml.dom.minidom.getDOMImplementation().createDocument(None, 'href', None)
+
+ calendar_obj = self.context._dirobj.pool.get('basic.calendar')
+ calendar = calendar_obj.browse(cr, uid, self.calendar_id, context=ctx)
+ huri = doc.createTextNode(urllib.quote('/%s/%s' % (cr.dbname, calendar.collection_id.name)))
+ href = doc.documentElement
+ href.tagName = 'D:href'
+ href.appendChild(huri)
+ return href
+
+ def _get_caldav_calendar_user_address_set(self, cr):
+ import xml.dom.minidom
+ dirobj = self.context._dirobj
+ uid = self.context.uid
+ ctx = self.context.context.copy()
+ ctx.update(self.dctx)
+ user_obj = self.context._dirobj.pool.get('res.users')
+ user = user_obj.browse(cr, uid, uid, context=ctx)
+ doc = xml.dom.minidom.getDOMImplementation().createDocument(None, 'href', None)
+ href = doc.documentElement
+ href.tagName = 'D:href'
+ huri = doc.createTextNode('MAILTO:' + user.email)
+ href.appendChild(huri)
+ return href
+
+
+ def _get_caldav_schedule_inbox_URL(self, cr):
+ import xml.dom.minidom
+ import urllib
+ uid = self.context.uid
+ ctx = self.context.context.copy()
+ ctx.update(self.dctx)
+ calendar_obj = self.context._dirobj.pool.get('basic.calendar')
+ calendar = calendar_obj.browse(cr, uid, self.calendar_id, context=ctx)
+ res = '%s/%s' %(calendar.name, calendar.collection_id.name)
+ doc = xml.dom.minidom.getDOMImplementation().createDocument(None, 'href', None)
+ href = doc.documentElement
+ href.tagName = 'D:href'
+ huri = doc.createTextNode(urllib.quote('/%s/%s' % (cr.dbname, res)))
+ href.appendChild(huri)
+ return href
+
+
+ def rm(self, cr):
+ uid = self.context.uid
+ res = False
+ if self.type in ('collection','database'):
+ return False
+ if self.model and self.res_id:
+ document_obj = self.context._dirobj.pool.get(self.model)
+ if document_obj:
+ res = False
+ #res = document_obj.unlink(cr, uid, [self.res_id]) #TOFIX
+
+ return res
+
+
+
+ def _get_caldav_schedule_outbox_URL(self, cr):
+ return self._get_caldav_schedule_inbox_URL(cr)
+
+
+ def get_etag(self, cr):
+ """ Get a tag, unique per object + modification.
+
+ see. http://tools.ietf.org/html/rfc2616#section-13.3.3 """
+ return self._get_ttag(cr) + ':' + self._get_wtag(cr)
+
+ def _get_wtag(self, cr):
+ """ Return the modification time as a unique, compact string """
+ if self.write_date:
+ wtime = time.mktime(time.strptime(self.write_date, '%Y-%m-%d %H:%M:%S'))
+ else: wtime = time.time()
+ return str(wtime)
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4
diff --git a/addons/caldav/caldav_view.xml b/addons/caldav/caldav_view.xml
index a477eeb282d..4c1003023ae 100644
--- a/addons/caldav/caldav_view.xml
+++ b/addons/caldav/caldav_view.xml
@@ -1,61 +1,151 @@
-
+
+
+ Calendar Collections : Form
+ document.directory
+ form
+
+
+
+
+
+ Calendar Collections : Tree
+ document.directory
+ tree
+
+
+
+
+
+
+
+
+
-
- Basic Calendar
+
+ Calendar Collections
+ ir.actions.act_window
+ document.directory
+ form
+ tree,form
+ [('calendar_collection','=',True)]
+
+
+
+ [('calendar_collection','=',False)]
+
+
+
+
+ tree
+
+
+
+
+
+ form
+
+
+
+
+
+
+
+
+
+ Calendar : Form
basic.calendar
form
-
-
-
+
+
+
-
- Calendar
- ir.actions.act_window
- basic.calendar
- form
- tree,form
-
-
+
+ Calendar : Tree
+ basic.calendar
+ tree
+
+
+
+
+
+
+
+
+
+
-
+
+ Calendars
+ ir.actions.act_window
+ basic.calendar
+ form
+ tree,form
+
-
+
+
+ tree
+
+
+
+
+
+ form
+
+
+
+
+
+
diff --git a/addons/caldav/calendar.py b/addons/caldav/calendar.py
new file mode 100644
index 00000000000..3e4701a08d5
--- /dev/null
+++ b/addons/caldav/calendar.py
@@ -0,0 +1,1058 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2004-2010 Tiny SPRL ().
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+##############################################################################
+
+from datetime import datetime, timedelta
+from dateutil import parser
+from dateutil.rrule import *
+from osv import osv, fields
+from tools.translate import _
+import base64
+import math
+import pooler
+import pytz
+import re
+import tools
+import time
+from caldav_node import res_node_calendar
+
+try:
+ import vobject
+except ImportError:
+ raise osv.except_osv('vobject Import Error!','Please install python-vobject \
+ from http://vobject.skyhouseconsulting.com/')
+
+# O-1 Optional and can come only once
+# O-n Optional and can come more than once
+# R-1 Required and can come only once
+# R-n Required and can come more than once
+
+def uid2openobjectid(cr, uidval, oomodel, rdate):
+ """ UID To Open Object Id
+ @param cr: the current row, from the database cursor,
+ @param uidval: Get USerId vale
+ @oomodel: Open Object ModelName
+ @param rdate: Get Recurrent Date
+ """
+ __rege = re.compile(r'OpenObject-([\w|\.]+)_([0-9]+)@(\w+)$')
+ wematch = __rege.match(uidval.encode('utf8'))
+ if not wematch:
+ return (False, None)
+ else:
+ model, id, dbname = wematch.groups()
+ model_obj = pooler.get_pool(cr.dbname).get(model)
+ if (not model == oomodel) or (not dbname == cr.dbname):
+ return (False, None)
+ qry = 'select distinct(id) from %s' % model_obj._table
+ if rdate:
+ qry += " where recurrent_id='%s'" % (rdate)
+ cr.execute(qry)
+ r_id = cr.fetchone()
+ if r_id:
+ return (id, r_id[0])
+ cr.execute(qry)
+ ids = map(lambda x: str(x[0]), cr.fetchall())
+ if id in ids:
+ return (id, None)
+ return (False, None)
+
+def openobjectid2uid(cr, uidval, oomodel):
+ """ Open Object Id To UId
+ @param cr: the current row, from the database cursor,
+ @param uidval: Get USerId vale
+ @oomodel: Open Object ModelName """
+
+ value = 'OpenObject-%s_%s@%s' % (oomodel, uidval, cr.dbname)
+ return value
+
+def get_attribute_mapping(cr, uid, calname, context={}):
+ """ Attribute Mapping with Basic calendar fields and lines
+ @param cr: the current row, from the database cursor,
+ @param uid: the current user’s ID for security checks,
+ @param calname: Get Calendar name
+ @param context: A standard dictionary for contextual values """
+
+ if not context:
+ context = {}
+ pool = pooler.get_pool(cr.dbname)
+ field_obj = pool.get('basic.calendar.fields')
+ type_obj = pool.get('basic.calendar.lines')
+ domain = [('object_id.model', '=', context.get('model'))]
+ if context.get('calendar_id'):
+ domain.append(('calendar_id', '=', context.get('calendar_id')))
+ type_id = type_obj.search(cr, uid, domain)
+ fids = field_obj.search(cr, uid, [('type_id', '=', type_id[0])])
+ res = {}
+ for field in field_obj.browse(cr, uid, fids):
+ attr = field.name.name
+ res[attr] = {}
+ res[attr]['field'] = field.field_id.name
+ res[attr]['type'] = field.field_id.ttype
+ if field.fn == 'hours':
+ res[attr]['type'] = "timedelta"
+ if res[attr]['type'] in ('one2many', 'many2many', 'many2one'):
+ res[attr]['object'] = field.field_id.relation
+ elif res[attr]['type'] in ('selection') and field.mapping:
+ res[attr]['mapping'] = eval(field.mapping)
+ if not res.get('uid', None):
+ res['uid'] = {}
+ res['uid']['field'] = 'id'
+ res['uid']['type'] = "integer"
+ return res
+
+def map_data(cr, uid, obj):
+ """ Map Data
+ @param self: The object pointer
+ @param cr: the current row, from the database cursor,"""
+
+ vals = {}
+ for map_dict in obj.__attribute__:
+ map_val = obj.ical_get(map_dict, 'value')
+ field = obj.ical_get(map_dict, 'field')
+ field_type = obj.ical_get(map_dict, 'type')
+ if field:
+ if field_type == 'selection':
+ if not map_val:
+ continue
+ mapping = obj.__attribute__[map_dict].get('mapping', False)
+ if mapping:
+ map_val = mapping[map_val.lower()]
+ else:
+ map_val = map_val.lower()
+ if field_type == 'many2many':
+ ids = []
+ if not map_val:
+ vals[field] = ids
+ continue
+ model = obj.__attribute__[map_dict].get('object', False)
+ modobj = obj.pool.get(model)
+ for map_vall in map_val:
+ id = modobj.create(cr, uid, map_vall)
+ ids.append(id)
+ vals[field] = [(6, 0, ids)]
+ continue
+ if field_type == 'many2one':
+ id = None
+ if not map_val or not isinstance(map_val, dict):
+ vals[field] = id
+ continue
+ model = obj.__attribute__[map_dict].get('object', False)
+ modobj = obj.pool.get(model)
+ id = modobj.create(cr, uid, map_val)
+ vals[field] = id
+ continue
+ if field_type == 'timedelta':
+ if map_val:
+ vals[field] = (map_val.seconds/float(86400) + map_val.days)
+ vals[field] = map_val
+ return vals
+
+class CalDAV(object):
+ __attribute__ = {}
+
+
+
+ def ical_set(self, name, value, type):
+ """ set calendar Attribute
+ @param self: The object pointer,
+ @param name: Get Attribute Name
+ @param value: Get Attribute Value
+ @param type: Get Attribute Type
+ """
+ if name in self.__attribute__ and self.__attribute__[name]:
+ self.__attribute__[name][type] = value
+ return True
+
+ def ical_get(self, name, type):
+ """ Get calendar Attribute
+ @param self: The object pointer,
+ @param name: Get Attribute Name
+ @param type: Get Attribute Type
+ """
+
+ if self.__attribute__.get(name):
+ val = self.__attribute__.get(name).get(type, None)
+ valtype = self.__attribute__.get(name).get('type', None)
+ if type == 'value':
+ if valtype and valtype == 'datetime' and val:
+ if isinstance(val, list):
+ val = ','.join(map(lambda x: x.strftime('%Y-%m-%d %H:%M:%S'), val))
+ else:
+ val = val.strftime('%Y-%m-%d %H:%M:%S')
+ return val
+ else:
+ return self.__attribute__.get(name, None)
+
+ def ical_reset(self, type):
+ """ Reset Calendar Attribute
+ @param self: The object pointer,
+ @param type: Get Attribute Type
+ """
+
+ for name in self.__attribute__:
+ if self.__attribute__[name]:
+ self.__attribute__[name][type] = None
+ return True
+
+ def parse_ics(self, cr, uid, child, cal_children=None, context=None):
+ """ parse calendaring and scheduling information
+ @param self: The object pointer
+ @param cr: the current row, from the database cursor,
+ @param uid: the current user’s ID for security checks,
+ @param context: A standard dictionary for contextual values """
+
+ att_data = []
+ for cal_data in child.getChildren():
+ if cal_data.name.lower() == 'attendee':
+ ctx = context.copy()
+ if cal_children:
+ ctx.update({'model': cal_children[cal_data.name.lower()]})
+ attendee = self.pool.get('basic.calendar.attendee')
+ att_data.append(attendee.import_cal(cr, uid, cal_data, context=ctx))
+ self.ical_set(cal_data.name.lower(), att_data, 'value')
+ continue
+ if cal_data.name.lower() == 'valarm':
+ alarm = self.pool.get('basic.calendar.alarm')
+ ctx = context.copy()
+ if cal_children:
+ ctx.update({'model': cal_children[cal_data.name.lower()]})
+ vals = alarm.import_cal(cr, uid, cal_data, context=ctx)
+ self.ical_set(cal_data.name.lower(), vals, 'value')
+ continue
+ if cal_data.name.lower() == 'exdate':
+ exval = map(lambda x: str(x), cal_data.value)
+ self.ical_set(cal_data.name.lower(), ','.join(exval), 'value')
+ continue
+ if cal_data.name.lower() in self.__attribute__:
+
+ if cal_data.params.get('X-VOBJ-ORIGINAL-TZID'):
+ self.ical_set('vtimezone', cal_data.params.get('X-VOBJ-ORIGINAL-TZID'), 'value')
+ self.ical_set(cal_data.name.lower(), cal_data.value, 'value')
+ vals = map_data(cr, uid, self)
+ return vals
+
+ def create_ics(self, cr, uid, datas, name, ical, context=None):
+ """ create calendaring and scheduling information
+ @param self: The object pointer
+ @param cr: the current row, from the database cursor,
+ @param uid: the current user’s ID for security checks,
+ @param context: A standard dictionary for contextual values """
+
+ if not datas:
+ return
+ timezones = []
+ for data in datas:
+ tzval = None
+ vevent = ical.add(name)
+ for field in self.__attribute__.keys():
+ map_field = self.ical_get(field, 'field')
+ map_type = self.ical_get(field, 'type')
+ if map_field in data.keys():
+ if field == 'uid':
+ model = context.get('model', None)
+ if not model:
+ continue
+ uidval = openobjectid2uid(cr, data[map_field], model)
+ model_obj = self.pool.get(model)
+ r_ids = []
+ if model_obj._columns.get('recurrent_uid', None):
+ cr.execute('select id from %s where recurrent_uid=%s'
+ % (model_obj._table, data[map_field]))
+ r_ids = map(lambda x: x[0], cr.fetchall())
+ if r_ids:
+ rdata = self.pool.get(model).read(cr, uid, r_ids)
+ event_obj = self.pool.get('basic.calendar.event')
+ rcal = event_obj.export_cal(cr, uid, rdata, context=context)
+ for revents in rcal.contents['vevent']:
+ ical.contents['vevent'].append(revents)
+ if data.get('recurrent_uid', None):
+ uidval = openobjectid2uid(cr, data['recurrent_uid'], model)
+ vevent.add('uid').value = uidval
+ elif field == 'attendee' and data[map_field]:
+ model = self.__attribute__[field].get('object', False)
+ attendee_obj = self.pool.get('basic.calendar.attendee')
+ vevent = attendee_obj.export_cal(cr, uid, model, \
+ data[map_field], vevent, context=context)
+ elif field == 'valarm' and data[map_field]:
+ model = self.__attribute__[field].get('object', False)
+ ctx = context.copy()
+ ctx.update({'model': model})
+ alarm_obj = self.pool.get('basic.calendar.alarm')
+ vevent = alarm_obj.export_cal(cr, uid, model, \
+ data[map_field][0], vevent, context=ctx)
+ elif field == 'vtimezone' and data[map_field] and data[map_field] not in timezones:
+ tzval = data[map_field]
+ tz_obj = self.pool.get('basic.calendar.timezone')
+ ical = tz_obj.export_cal(cr, uid, None, \
+ data[map_field], ical, context=context)
+ timezones.append(data[map_field])
+ elif data[map_field]:
+ if map_type in ("char", "text"):
+ if field in ('exdate'):
+ vevent.add(field).value = map(parser.parse, (data[map_field]).split(','))
+ else:
+ vevent.add(field).value = tools.ustr(data[map_field])
+ elif map_type in ('datetime', 'date') and data[map_field]:
+ dtfield = vevent.add(field)
+ dtfield.value = parser.parse(data[map_field])
+ if tzval:
+ dtfield.params['TZID'] = [tzval.title()]
+ elif map_type == "timedelta":
+ vevent.add(field).value = timedelta(hours=data[map_field])
+ elif map_type == "many2one":
+ vevent.add(field).value = tools.ustr(data.get(map_field)[1])
+ elif map_type in ("float", "integer"):
+ vevent.add(field).value = str(data.get(map_field))
+ elif map_type == "selection":
+ if not self.ical_get(field, 'mapping'):
+ vevent.add(field).value = (tools.ustr(data[map_field])).upper()
+ else:
+ for key1, val1 in self.ical_get(field, 'mapping').items():
+ if val1 == data[map_field]:
+ vevent.add(field).value = key1
+ return vevent
+
+ def check_import(self, cr, uid, vals, context={}):
+ """
+ @param self: The object pointer
+ @param cr: the current row, from the database cursor,
+ @param uid: the current user’s ID for security checks,
+ @param vals: Get Values
+ @param context: A standard dictionary for contextual values
+ """
+
+ ids = []
+ model_obj = self.pool.get(context.get('model'))
+ recur_pool = {}
+ try:
+ for val in vals:
+ exists, r_id = uid2openobjectid(cr, val['id'], context.get('model'), \
+ val.get('recurrent_id'))
+ if val.has_key('create_date'): val.pop('create_date')
+ u_id = val.get('id', None)
+ val.pop('id')
+ if exists and r_id:
+ val.update({'recurrent_uid': exists})
+ model_obj.write(cr, uid, [r_id], val)
+ ids.append(r_id)
+ elif exists:
+ model_obj.write(cr, uid, [exists], val)
+ ids.append(exists)
+ else:
+ if u_id in recur_pool and val.get('recurrent_id'):
+ val.update({'recurrent_uid': recur_pool[u_id]})
+ revent_id = model_obj.create(cr, uid, val)
+ ids.append(revent_id)
+ else:
+ event_id = model_obj.create(cr, uid, val)
+ recur_pool[u_id] = event_id
+ ids.append(event_id)
+ except Exception, e:
+ raise osv.except_osv(('Error !'), (str(e)))
+ return ids
+
+ def export_cal(self, cr, uid, datas, vobj=None, context={}):
+ """ Export Calendar
+ @param self: The object pointer
+ @param cr: the current row, from the database cursor,
+ @param uid: the current user’s ID for security checks,
+ @param datas: Get Data's for caldav
+ @param context: A standard dictionary for contextual values
+ """
+
+ try:
+ self.__attribute__ = get_attribute_mapping(cr, uid, self._calname, context)
+ ical = vobject.iCalendar()
+ self.create_ics(cr, uid, datas, vobj, ical, context=context)
+ return ical
+ except Exception, e:
+ raise osv.except_osv(('Error !'), (str(e)))
+
+ def import_cal(self, cr, uid, content, data_id=None, context=None):
+ """ Import Calendar
+ @param self: The object pointer
+ @param cr: the current row, from the database cursor,
+ @param uid: the current user’s ID for security checks,
+ @param data_id: Get Data’s ID or False
+ @param context: A standard dictionary for contextual values
+ """
+
+ ical_data = base64.decodestring(content)
+ self.__attribute__ = get_attribute_mapping(cr, uid, self._calname, context)
+ parsedCal = vobject.readOne(ical_data)
+ res = []
+ vals = {}
+ for child in parsedCal.getChildren():
+ if child.name.lower() in ('vevent', 'vtodo'):
+ vals = self.parse_ics(cr, uid, child, context=context)
+ else:
+ vals = {}
+ continue
+ if vals: res.append(vals)
+ self.ical_reset('value')
+ return res
+
+class Calendar(CalDAV, osv.osv):
+ _name = 'basic.calendar'
+ _calname = 'calendar'
+
+ __attribute__ = {
+ 'prodid': None, # Use: R-1, Type: TEXT, Specifies the identifier for the product that created the iCalendar object.
+ 'version': None, # Use: R-1, Type: TEXT, Specifies the identifier corresponding to the highest version number
+ # or the minimum and maximum range of the iCalendar specification
+ # that is required in order to interpret the iCalendar object.
+ 'calscale': None, # Use: O-1, Type: TEXT, Defines the calendar scale used for the calendar information specified in the iCalendar object.
+ 'method': None, # Use: O-1, Type: TEXT, Defines the iCalendar object method associated with the calendar object.
+ 'vevent': None, # Use: O-n, Type: Collection of Event class
+ 'vtodo': None, # Use: O-n, Type: Collection of ToDo class
+ 'vjournal': None, # Use: O-n, Type: Collection of Journal class
+ 'vfreebusy': None, # Use: O-n, Type: Collection of FreeBusy class
+ 'vtimezone': None, # Use: O-n, Type: Collection of Timezone class
+ }
+ _columns = {
+ 'name': fields.char("Name", size=64),
+ 'user_id': fields.many2one('res.users', 'Owner'),
+ 'collection_id': fields.many2one('document.directory', 'Collection', \
+ required=True),
+ 'type': fields.selection([('vevent', 'Event'), ('vtodo', 'TODO')], \
+ string="Type", size=64),
+ 'line_ids': fields.one2many('basic.calendar.lines', 'calendar_id', 'Calendar Lines'),
+ 'create_date': fields.datetime('Created Date'),
+ 'write_date': fields.datetime('Modifided Date'),
+ }
+
+ def get_calendar_objects(self, cr, uid, ids, parent=None, domain=None, context=None):
+ if not context:
+ context = {}
+ if not domain:
+ domain = []
+ res = []
+ ctx_res_id = context.get('res_id', None)
+ ctx_model = context.get('model', None)
+ for cal in self.browse(cr, uid, ids):
+ for line in cal.line_ids:
+ if ctx_model and ctx_model != line.object_id.model:
+ continue
+ if line.name in ('valarm', 'attendee'):
+ continue
+ line_domain = eval(line.domain)
+ line_domain += domain
+ if ctx_res_id:
+ line_domain += [('id','=',ctx_res_id)]
+ mod_obj = self.pool.get(line.object_id.model)
+ data_ids = mod_obj.search(cr, uid, line_domain, context=context)
+ for data in mod_obj.browse(cr, uid, data_ids, context):
+ ctx = parent and parent.context or None
+ node = res_node_calendar('%s' %data.id, parent, ctx, data, line.object_id.model, data.id)
+ res.append(node)
+ return res
+
+ def export_cal(self, cr, uid, ids, vobj='vevent', context=None):
+ """ Export Calendar
+ @param self: The object pointer
+ @param cr: the current row, from the database cursor,
+ @param uid: the current user’s ID for security checks,
+ @param ids: List of calendar’s IDs
+ @param context: A standard dictionary for contextual values
+ """
+ if not context:
+ context = {}
+ ctx_model = context.get('model', None)
+ ctx_res_id = context.get('res_id', None)
+ ical = vobject.iCalendar()
+ for cal in self.browse(cr, uid, ids):
+ for line in cal.line_ids:
+ if ctx_model and ctx_model != line.object_id.model:
+ continue
+ if line.name in ('valarm', 'attendee'):
+ continue
+ domain = eval(line.domain)
+ if ctx_res_id:
+ domain += [('id','=',ctx_res_id)]
+ mod_obj = self.pool.get(line.object_id.model)
+ data_ids = mod_obj.search(cr, uid, domain, context=context)
+ datas = mod_obj.read(cr, uid, data_ids, context=context)
+ context.update({'model': line.object_id.model,
+ 'calendar_id': cal.id
+ })
+ self.__attribute__ = get_attribute_mapping(cr, uid, line.name, context)
+ self.create_ics(cr, uid, datas, line.name, ical, context=context)
+ return ical.serialize()
+
+ def import_cal(self, cr, uid, content, data_id=None, context=None):
+ """ Import Calendar
+ @param self: The object pointer
+ @param cr: the current row, from the database cursor,
+ @param uid: the current user’s ID for security checks,
+ @param data_id: Get Data’s ID or False
+ @param context: A standard dictionary for contextual values
+ """
+
+ if not context:
+ context = {}
+ vals = []
+ ical_data = base64.decodestring(content)
+ parsedCal = vobject.readOne(ical_data)
+ if not data_id:
+ data_id = self.search(cr, uid, [])[0]
+ cal = self.browse(cr, uid, data_id, context=context)
+ cal_children = {}
+ count = 0
+ for line in cal.line_ids:
+ cal_children[line.name] = line.object_id.model
+ for child in parsedCal.getChildren():
+ if child.name.lower() in cal_children:
+ context.update({'model': cal_children[child.name.lower()],
+ 'calendar_id': cal['id']
+ })
+ self.__attribute__ = get_attribute_mapping(cr, uid, child.name.lower(), context=context)
+ val = self.parse_ics(cr, uid, child, cal_children=cal_children, context=context)
+ vals.append(val)
+ obj = self.pool.get(cal_children[child.name.lower()])
+ if hasattr(obj, 'check_import'):
+ obj.check_import(cr, uid, vals, context=context)
+ else:
+ self.check_import(cr, uid, vals, context=context)
+ return {}
+Calendar()
+
+
+class basic_calendar_line(osv.osv):
+ """ Calendar Lines """
+
+ _name = 'basic.calendar.lines'
+ _description = 'Calendar Lines'
+
+ _columns = {
+ 'name': fields.selection([('vevent', 'Event'), ('vtodo', 'TODO'), \
+ ('valarm', 'Alarm'), \
+ ('attendee', 'Attendee')], \
+ string="Type", size=64),
+ 'object_id': fields.many2one('ir.model', 'Object'),
+ 'calendar_id': fields.many2one('basic.calendar', 'Calendar', \
+ required=True, ondelete='cascade'),
+ 'domain': fields.char('Domain', size=124),
+ 'mapping_ids': fields.one2many('basic.calendar.fields', 'type_id', 'Fields Mapping')
+ }
+
+ _defaults = {
+ 'domain': lambda *a: '[]',
+ }
+
+ def create(self, cr, uid, vals, context={}):
+ """ create calendar's line
+ @param self: The object pointer
+ @param cr: the current row, from the database cursor,
+ @param uid: the current user’s ID for security checks,
+ @param vals: Get the Values
+ @param context: A standard dictionary for contextual values
+ """
+
+ cr.execute("Select count(id) from basic_calendar_lines \
+ where name='%s' and calendar_id=%s" % (vals.get('name'), vals.get('calendar_id')))
+ res = cr.fetchone()
+ if res:
+ if res[0] > 0:
+ raise osv.except_osv(_('Warning !'), _('Can not create \
+line "%s" more than once' % (vals.get('name'))))
+ return super(basic_calendar_line, self).create(cr, uid, vals, context=context)
+
+basic_calendar_line()
+
+
+class basic_calendar_attribute(osv.osv):
+ _name = 'basic.calendar.attributes'
+ _description = 'Calendar attributes'
+ _columns = {
+ 'name': fields.char("Name", size=64, required=True),
+ 'type': fields.selection([('vevent', 'Event'), ('vtodo', 'TODO'), \
+ ('alarm', 'Alarm'), \
+ ('attendee', 'Attendee')], \
+ string="Type", size=64, required=True),
+ }
+
+basic_calendar_attribute()
+
+
+class basic_calendar_fields(osv.osv):
+ """ Calendar fields """
+
+ _name = 'basic.calendar.fields'
+ _description = 'Calendar fields'
+
+ _columns = {
+ 'field_id': fields.many2one('ir.model.fields', 'OpenObject Field'),
+ 'name': fields.many2one('basic.calendar.attributes', 'Name', required=True),
+ 'type_id': fields.many2one('basic.calendar.lines', 'Type', \
+ required=True, ondelete='cascade'),
+ 'expr': fields.char("Expression", size=64),
+ 'fn': fields.selection([('field', 'Use the field'),
+ ('const', 'Expression as constant'),
+ ('hours', 'Interval in hours'),
+ ], 'Function'),
+ 'mapping': fields.text('Mapping'),
+ }
+
+ _defaults = {
+ 'fn': lambda *a: 'field',
+ }
+
+ def check_line(self, cr, uid, vals, name, context=None):
+ """ check calendar's line
+ @param self: The object pointer
+ @param cr: the current row, from the database cursor,
+ @param uid: the current user’s ID for security checks,
+ @param vals: Get Values
+ @param context: A standard dictionary for contextual values
+ """
+ f_obj = self.pool.get('ir.model.fields')
+ field = f_obj.browse(cr, uid, vals['field_id'], context=context)
+ relation = field.relation
+ line_obj = self.pool.get('basic.calendar.lines')
+ l_id = line_obj.search(cr, uid, [('name', '=', name)])
+ if l_id:
+ line = line_obj.browse(cr, uid, l_id, context=context)[0]
+ line_rel = line.object_id.model
+ if (relation != 'NULL') and (not relation == line_rel):
+ raise osv.except_osv(_('Warning !'), _('Please provide proper configuration of "%s" in Calendar Lines' % (name)))
+ return True
+
+ def create(self, cr, uid, vals, context={}):
+ """ Create Calendar's fields
+ @param self: The object pointer
+ @param cr: the current row, from the database cursor,
+ @param uid: the current user’s ID for security checks,
+ @param vals: Get Values
+ @param context: A standard dictionary for contextual values
+ """
+
+ cr.execute('select name from basic_calendar_attributes \
+ where id=%s' % (vals.get('name')))
+ name = cr.fetchone()
+ name = name[0]
+ if name in ('valarm', 'attendee'):
+ self.check_line(cr, uid, vals, name, context=context)
+ cr.execute("Select count(id) from basic_calendar_fields \
+ where name=%s and type_id=%s" % (vals.get('name'), vals.get('type_id')))
+ res = cr.fetchone()
+ if res:
+ if res[0] > 0:
+ raise osv.except_osv(_('Warning !'), _('Can not map the field more than once'))
+ return super(basic_calendar_fields, self).create(cr, uid, vals, context=context)
+
+ def write(self, cr, uid, ids, vals, context=None):
+ """ write Calendar's fields
+ @param self: The object pointer
+ @param cr: the current row, from the database cursor,
+ @param uid: the current user’s ID for security checks,
+ @param vals: Get Values
+ @param context: A standard dictionary for contextual values
+ """
+
+ if not vals:
+ return
+ for id in ids:
+ field = self.browse(cr, uid, id, context=context)
+ name = field.name.name
+ if name in ('valarm', 'attendee'):
+ self.check_line(cr, uid, vals, name, context=context)
+ qry = "Select count(id) from basic_calendar_fields \
+ where name=%s and type_id=%s" % (field.name.id, field.type_id.id)
+ cr.execute(qry)
+ res = cr.fetchone()
+ if res:
+ if res[0] > 1:
+ raise osv.except_osv(_('Warning !'), _('Can not map same field more than once'))
+ return super(basic_calendar_fields, self).write(cr, uid, ids, vals, context)
+
+basic_calendar_fields()
+
+
+class Event(CalDAV, osv.osv_memory):
+ _name = 'basic.calendar.event'
+ _calname = 'vevent'
+ __attribute__ = {
+ 'class': None, # Use: O-1, Type: TEXT, Defines the access classification for a calendar component like "PUBLIC" / "PRIVATE" / "CONFIDENTIAL"
+ 'created': None, # Use: O-1, Type: DATE-TIME, Specifies the date and time that the calendar information was created by the calendar user agent in the calendar store.
+ 'description': None, # Use: O-1, Type: TEXT, Provides a more complete description of the calendar component, than that provided by the "SUMMARY" property.
+ 'dtstart': None, # Use: O-1, Type: DATE-TIME, Specifies when the calendar component begins.
+ 'geo': None, # Use: O-1, Type: FLOAT, Specifies information related to the global position for the activity specified by a calendar component.
+ 'last-mod': None, # Use: O-1, Type: DATE-TIME Specifies the date and time that the information associated with the calendar component was last revised in the calendar store.
+ 'location': None, # Use: O-1, Type: TEXT Defines the intended venue for the activity defined by a calendar component.
+ 'organizer': None, # Use: O-1, Type: CAL-ADDRESS, Defines the organizer for a calendar component.
+ 'priority': None, # Use: O-1, Type: INTEGER, Defines the relative priority for a calendar component.
+ 'dtstamp': None, # Use: O-1, Type: DATE-TIME, Indicates the date/time that the instance of the iCalendar object was created.
+ 'seq': None, # Use: O-1, Type: INTEGER, Defines the revision sequence number of the calendar component within a sequence of revision.
+ 'status': None, # Use: O-1, Type: TEXT, Defines the overall status or confirmation for the calendar component.
+ 'summary': None, # Use: O-1, Type: TEXT, Defines a short summary or subject for the calendar component.
+ 'transp': None, # Use: O-1, Type: TEXT, Defines whether an event is transparent or not to busy time searches.
+ 'uid': None, # Use: O-1, Type: TEXT, Defines the persistent, globally unique identifier for the calendar component.
+ 'url': None, # Use: O-1, Type: URL, Defines a Uniform Resource Locator (URL) associated with the iCalendar object.
+ 'recurid': None,
+ 'attach': None, # Use: O-n, Type: BINARY, Provides the capability to associate a document object with a calendar component.
+ 'attendee': None, # Use: O-n, Type: CAL-ADDRESS, Defines an "Attendee" within a calendar component.
+ 'categories': None, # Use: O-n, Type: TEXT, Defines the categories for a calendar component.
+ 'comment': None, # Use: O-n, Type: TEXT, Specifies non-processing information intended to provide a comment to the calendar user.
+ 'contact': None, # Use: O-n, Type: TEXT, Used to represent contact information or alternately a reference to contact information associated with the calendar component.
+ 'exdate': None, # Use: O-n, Type: DATE-TIME, Defines the list of date/time exceptions for a recurring calendar component.
+ 'exrule': None, # Use: O-n, Type: RECUR, Defines a rule or repeating pattern for an exception to a recurrence set.
+ 'rstatus': None,
+ 'related': None, # Use: O-n, Specify the relationship of the alarm trigger with respect to the start or end of the calendar component.
+ # like A trigger set 5 minutes after the end of the event or to-do.---> TRIGGER;related=END:PT5M
+ 'resources': None, # Use: O-n, Type: TEXT, Defines the equipment or resources anticipated for an activity specified by a calendar entity like RESOURCES:EASEL,PROJECTOR,VCR, LANGUAGE=fr:1 raton-laveur
+ 'rdate': None, # Use: O-n, Type: DATE-TIME, Defines the list of date/times for a recurrence set.
+ 'rrule': None, # Use: O-n, Type: RECUR, Defines a rule or repeating pattern for recurring events, to-dos, or time zone definitions.
+ 'x-prop': None,
+ 'duration': None, # Use: O-1, Type: DURATION, Specifies a positive duration of time.
+ 'dtend': None, # Use: O-1, Type: DATE-TIME, Specifies the date and time that a calendar component ends.
+ }
+
+ def export_cal(self, cr, uid, datas, vobj='vevent', context={}):
+ """ Export calendar
+ @param self: The object pointer
+ @param cr: the current row, from the database cursor,
+ @param uid: the current user’s ID for security checks,
+ @param datas: Get datas
+ @param context: A standard dictionary for contextual values
+ """
+
+ return super(Event, self).export_cal(cr, uid, datas, 'vevent', context=context)
+
+Event()
+
+
+class ToDo(CalDAV, osv.osv_memory):
+ _name = 'basic.calendar.todo'
+ _calname = 'vtodo'
+
+ __attribute__ = {
+ 'class': None,
+ 'completed': None,
+ 'created': None,
+ 'description': None,
+ 'dtstamp': None,
+ 'dtstart': None,
+ 'duration': None,
+ 'due': None,
+ 'geo': None,
+ 'last-mod ': None,
+ 'location': None,
+ 'organizer': None,
+ 'percent': None,
+ 'priority': None,
+ 'recurid': None,
+ 'seq': None,
+ 'status': None,
+ 'summary': None,
+ 'uid': None,
+ 'url': None,
+ 'attach': None,
+ 'attendee': None,
+ 'categories': None,
+ 'comment': None,
+ 'contact': None,
+ 'exdate': None,
+ 'exrule': None,
+ 'rstatus': None,
+ 'related': None,
+ 'resources': None,
+ 'rdate': None,
+ 'rrule': None,
+ }
+
+ def export_cal(self, cr, uid, datas, vobj='vevent', context={}):
+ """ Export Calendar
+ @param self: The object pointer
+ @param cr: the current row, from the database cursor,
+ @param uid: the current user’s ID for security checks,
+ @param datas: Get datas
+ @param context: A standard dictionary for contextual values
+ """
+
+ return super(ToDo, self).export_cal(cr, uid, datas, 'vtodo', context=context)
+
+ToDo()
+
+
+class Journal(CalDAV):
+ __attribute__ = {
+ }
+
+
+class FreeBusy(CalDAV):
+ __attribute__ = {
+ 'contact': None, # Use: O-1, Type: Text, Represent contact information or alternately a reference to contact information associated with the calendar component.
+ 'dtstart': None, # Use: O-1, Type: DATE-TIME, Specifies when the calendar component begins.
+ 'dtend': None, # Use: O-1, Type: DATE-TIME, Specifies the date and time that a calendar component ends.
+ 'duration': None, # Use: O-1, Type: DURATION, Specifies a positive duration of time.
+ 'dtstamp': None, # Use: O-1, Type: DATE-TIME, Indicates the date/time that the instance of the iCalendar object was created.
+ 'organizer': None, # Use: O-1, Type: CAL-ADDRESS, Defines the organizer for a calendar component.
+ 'uid': None, # Use: O-1, Type: Text, Defines the persistent, globally unique identifier for the calendar component.
+ 'url': None, # Use: O-1, Type: URL, Defines a Uniform Resource Locator (URL) associated with the iCalendar object.
+ 'attendee': None, # Use: O-n, Type: CAL-ADDRESS, Defines an "Attendee" within a calendar component.
+ 'comment': None, # Use: O-n, Type: TEXT, Specifies non-processing information intended to provide a comment to the calendar user.
+ 'freebusy': None, # Use: O-n, Type: PERIOD, Defines one or more free or busy time intervals.
+ 'rstatus': None,
+ 'X-prop': None,
+ }
+
+
+class Timezone(CalDAV, osv.osv_memory):
+ _name = 'basic.calendar.timezone'
+ _calname = 'vtimezone'
+
+ __attribute__ = {
+ 'tzid': {'field': 'tzid'}, # Use: R-1, Type: Text, Specifies the text value that uniquely identifies the "VTIMEZONE" calendar component.
+ 'last-mod': None, # Use: O-1, Type: DATE-TIME, Specifies the date and time that the information associated with the calendar component was last revised in the calendar store.
+ 'tzurl': None, # Use: O-1, Type: URI, Provides a means for a VTIMEZONE component to point to a network location that can be used to retrieve an up-to-date version of itself.
+ 'standardc': {'tzprop': None}, # Use: R-1,
+ 'daylightc': {'tzprop': None}, # Use: R-1,
+ 'x-prop': None, # Use: O-n, Type: Text,
+ }
+
+ def get_name_offset(self, cr, uid, tzid, context={}):
+ """ Get Name Offset value
+ @param self: The object pointer
+ @param cr: the current row, from the database cursor,
+ @param uid: the current user’s ID for security checks,
+ @param context: A standard dictionary for contextual values
+ """
+
+ mytz = pytz.timezone(tzid.title())
+ mydt = datetime.now(tz=mytz)
+ offset = mydt.utcoffset()
+ val = offset.days * 24 + float(offset.seconds) / 3600
+ realoffset = '%02d%02d' % (math.floor(abs(val)), \
+ round(abs(val) % 1 + 0.01, 2) * 60)
+ realoffset = (val < 0 and ('-' + realoffset) or ('+' + realoffset))
+ return (mydt.tzname(), realoffset)
+
+ def export_cal(self, cr, uid, model, tzid, ical, context={}):
+ """ Export Calendar
+ @param self: The object pointer
+ @param cr: the current row, from the database cursor,
+ @param uid: the current user’s ID for security checks,
+ @param model: Get Model's name
+ @param context: A standard dictionary for contextual values
+ """
+
+ ctx = context.copy()
+ ctx.update({'model': model})
+ cal_tz = ical.add('vtimezone')
+ cal_tz.add('TZID').value = tzid.title()
+ tz_std = cal_tz.add('STANDARD')
+ tzname, offset = self.get_name_offset(cr, uid, tzid)
+ tz_std.add("TZOFFSETFROM").value = offset
+ tz_std.add("TZOFFSETTO").value = offset
+ tz_std.add("DTSTART").value = datetime.now() # TODO
+ tz_std.add("TZNAME").value = tzname
+ return ical
+
+ def import_cal(self, cr, uid, ical_data, context=None):
+ """ Import Calendar
+ @param self: The object pointer
+ @param cr: the current row, from the database cursor,
+ @param uid: the current user’s ID for security checks,
+ @param ical_data: Get calendar's data
+ @param context: A standard dictionary for contextual values
+ """
+
+ for child in ical_data.getChildren():
+ if child.name.lower() == 'tzid':
+ tzname = child.value
+ self.ical_set(child.name.lower(), tzname, 'value')
+ vals = map_data(cr, uid, self)
+ return vals
+
+Timezone()
+
+
+class Alarm(CalDAV, osv.osv_memory):
+ _name = 'basic.calendar.alarm'
+ _calname = 'alarm'
+
+ __attribute__ = {
+ 'action': None, # Use: R-1, Type: Text, defines the action to be invoked when an alarm is triggered LIKE "AUDIO" / "DISPLAY" / "EMAIL" / "PROCEDURE"
+ 'description': None, # Type: Text, Provides a more complete description of the calendar component, than that provided by the "SUMMARY" property. Use:- R-1 for DISPLAY,Use:- R-1 for EMAIL,Use:- R-1 for PROCEDURE
+ 'summary': None, # Use: R-1, Type: Text Which contains the text to be used as the message subject. Use for EMAIL
+ 'attendee': None, # Use: R-n, Type: CAL-ADDRESS, Contain the email address of attendees to receive the message. It can also include one or more. Use for EMAIL
+ 'trigger': None, # Use: R-1, Type: DURATION, The "TRIGGER" property specifies a duration prior to the start of an event or a to-do. The "TRIGGER" edge may be explicitly set to be relative to the "START" or "END" of the event or to-do with the "related" parameter of the "TRIGGER" property. The "TRIGGER" property value type can alternatively be set to an absolute calendar date and time of day value. Use for all action like AUDIO, DISPLAY, EMAIL and PROCEDURE
+ 'duration': None, # Type: DURATION, Duration' and 'repeat' are both optional, and MUST NOT occur more than once each, but if one occurs, so MUST the other. Use:- 0-1 for AUDIO, EMAIL and PROCEDURE, Use:- 0-n for DISPLAY
+ 'repeat': None, # Type: INTEGER, Duration' and 'repeat' are both optional, and MUST NOT occur more than once each, but if one occurs, so MUST the other. Use:- 0-1 for AUDIO, EMAIL and PROCEDURE, Use:- 0-n for DISPLAY
+ 'attach': None, # Use:- O-n: which MUST point to a sound resource, which is rendered when the alarm is triggered for AUDIO, Use:- O-n: which are intended to be sent as message attachments for EMAIL, Use:- R-1:which MUST point to a procedure resource, which is invoked when the alarm is triggered for PROCEDURE.
+ 'x-prop': None,
+ }
+
+ def export_cal(self, cr, uid, model, alarm_id, vevent, context={}):
+ """ Export Calendar
+ @param self: The object pointer
+ @param cr: the current row, from the database cursor,
+ @param uid: the current user’s ID for security checks,
+ @param model: Get Model's name
+ @param alarm_id: Get Alarm's Id
+ @param context: A standard dictionary for contextual values
+ """
+
+ valarm = vevent.add('valarm')
+ alarm_object = self.pool.get(model)
+ alarm_data = alarm_object.read(cr, uid, alarm_id, [])
+
+ # Compute trigger data
+ interval = alarm_data['trigger_interval']
+ occurs = alarm_data['trigger_occurs']
+ duration = (occurs == 'after' and alarm_data['trigger_duration']) \
+ or -(alarm_data['trigger_duration'])
+ related = alarm_data['trigger_related']
+ trigger = valarm.add('TRIGGER')
+ trigger.params['related'] = [related.upper()]
+ if interval == 'days':
+ delta = timedelta(days=duration)
+ if interval == 'hours':
+ delta = timedelta(hours=duration)
+ if interval == 'minutes':
+ delta = timedelta(minutes=duration)
+ trigger.value = delta
+
+ # Compute other details
+ valarm.add('DESCRIPTION').value = alarm_data['name'] or 'OpenERP'
+ valarm.add('ACTION').value = alarm_data['action']
+ return vevent
+
+ def import_cal(self, cr, uid, ical_data, context=None):
+ """ Import Calendar
+ @param self: The object pointer
+ @param cr: the current row, from the database cursor,
+ @param uid: the current user’s ID for security checks,
+ @param ical_data: Get calendar's Data
+ @param context: A standard dictionary for contextual values
+ """
+
+ ctx = context.copy()
+ ctx.update({'model': context.get('model', None)})
+ self.__attribute__ = get_attribute_mapping(cr, uid, self._calname, ctx)
+ for child in ical_data.getChildren():
+ if child.name.lower() == 'trigger':
+ seconds = child.value.seconds
+ days = child.value.days
+ diff = (days * 86400) + seconds
+ interval = 'days'
+ related = 'before'
+ if not seconds:
+ duration = abs(days)
+ related = days > 0 and 'after' or 'before'
+ elif (abs(diff) / 3600) == 0:
+ duration = abs(diff / 60)
+ interval = 'minutes'
+ related = days >= 0 and 'after' or 'before'
+ else:
+ duration = abs(diff / 3600)
+ interval = 'hours'
+ related = days >= 0 and 'after' or 'before'
+ self.ical_set('trigger_interval', interval, 'value')
+ self.ical_set('trigger_duration', duration, 'value')
+ self.ical_set('trigger_occurs', related.lower(), 'value')
+ if child.params:
+ if child.params.get('related'):
+ self.ical_set('trigger_related', child.params.get('related')[0].lower(), 'value')
+ else:
+ self.ical_set(child.name.lower(), child.value.lower(), 'value')
+ vals = map_data(cr, uid, self)
+ return vals
+
+Alarm()
+
+
+class Attendee(CalDAV, osv.osv_memory):
+ _name = 'basic.calendar.attendee'
+ _calname = 'attendee'
+
+ __attribute__ = {
+ 'cutype': None, # Use: 0-1 Specify the type of calendar user specified by the property like "INDIVIDUAL"/"GROUP"/"RESOURCE"/"ROOM"/"UNKNOWN".
+ 'member': None, # Use: 0-1 Specify the group or list membership of the calendar user specified by the property.
+ 'role': None, # Use: 0-1 Specify the participation role for the calendar user specified by the property like "CHAIR"/"REQ-PARTICIPANT"/"OPT-PARTICIPANT"/"NON-PARTICIPANT"
+ 'partstat': None, # Use: 0-1 Specify the participation status for the calendar user specified by the property. like use for VEVENT:- "NEEDS-ACTION"/"ACCEPTED"/"DECLINED"/"TENTATIVE"/"DELEGATED", use for VTODO:-"NEEDS-ACTION"/"ACCEPTED"/"DECLINED"/"TENTATIVE"/"DELEGATED"/"COMPLETED"/"IN-PROCESS" and use for VJOURNAL:- "NEEDS-ACTION"/"ACCEPTED"/"DECLINED".
+ 'rsvp': None, # Use: 0-1 Specify whether there is an expectation of a favor of a reply from the calendar user specified by the property value like TRUE / FALSE.
+ 'delegated-to': None, # Use: 0-1 Specify the calendar users to whom the calendar user specified by the property has delegated participation.
+ 'delegated-from': None, # Use: 0-1 Specify the calendar users that have delegated their participation to the calendar user specified by the property.
+ 'sent-by': None, # Use: 0-1 Specify the calendar user that is acting on behalf of the calendar user specified by the property.
+ 'cn': None, # Use: 0-1 Specify the common name to be associated with the calendar user specified by the property.
+ 'dir': None, # Use: 0-1 Specify reference to a directory entry associated with the calendar user specified by the property.
+ 'language': None, # Use: 0-1 Specify the language for text values in a property or property parameter.
+ }
+
+ def import_cal(self, cr, uid, ical_data, context=None):
+ """ Import Calendar
+ @param self: The object pointer
+ @param cr: the current row, from the database cursor,
+ @param uid: the current user’s ID for security checks,
+ @param ical_data: Get calendar's Data
+ @param context: A standard dictionary for contextual values
+ """
+
+ ctx = context.copy()
+ ctx.update({'model': context.get('model', None)})
+ self.__attribute__ = get_attribute_mapping(cr, uid, self._calname, ctx)
+ for para in ical_data.params:
+ if para.lower() == 'cn':
+ self.ical_set(para.lower(), ical_data.params[para][0]+':'+ \
+ ical_data.value, 'value')
+ else:
+ self.ical_set(para.lower(), ical_data.params[para][0].lower(), 'value')
+ if not ical_data.params.get('CN'):
+ self.ical_set('cn', ical_data.value, 'value')
+ vals = map_data(cr, uid, self)
+ return vals
+
+ def export_cal(self, cr, uid, model, attendee_ids, vevent, context={}):
+ """ Export Calendar
+ @param self: The object pointer
+ @param cr: the current row, from the database cursor,
+ @param uid: the current user’s ID for security checks,
+ @param model: Get model's name
+ @param attendee_ids: Get Attendee's Id
+ @param context: A standard dictionary for contextual values
+ """
+
+ attendee_object = self.pool.get(model)
+ ctx = context.copy()
+ ctx.update({'model': model})
+ self.__attribute__ = get_attribute_mapping(cr, uid, self._calname, ctx)
+ for attendee in attendee_object.read(cr, uid, attendee_ids, []):
+ attendee_add = vevent.add('attendee')
+ cn_val = ''
+ for a_key, a_val in self.__attribute__.items():
+ if attendee[a_val['field']] and a_val['field'] != 'cn':
+ if a_val['type'] in ('text', 'char', 'selection'):
+ attendee_add.params[a_key] = [str(attendee[a_val['field']])]
+ elif a_val['type'] == 'boolean':
+ attendee_add.params[a_key] = [str(attendee[a_val['field']])]
+ if a_val['field'] == 'cn' and attendee[a_val['field']]:
+ cn_val = [str(attendee[a_val['field']])]
+ if cn_val:
+ attendee_add.params['CN'] = cn_val
+ if not attendee['email']:
+ raise osv.except_osv(_('Error !'), _('Attendee must have an Email Id'))
+ elif attendee['email']:
+ attendee_add.value = 'MAILTO:' + attendee['email']
+ return vevent
+
+Attendee()
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/caldav/calendar_collection.py b/addons/caldav/calendar_collection.py
new file mode 100644
index 00000000000..06e799b1686
--- /dev/null
+++ b/addons/caldav/calendar_collection.py
@@ -0,0 +1,80 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2004-2010 Tiny SPRL ().
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+##############################################################################
+from osv import osv, fields
+from tools.translate import _
+import caldav_node
+
+class calendar_collection(osv.osv):
+ _inherit = 'document.directory'
+ _columns = {
+ 'calendar_collection' : fields.boolean('Calendar Collection'),
+ 'calendar_ids': fields.one2many('basic.calendar', 'collection_id', 'Calendars'),
+ }
+ _default = {
+ 'calendar_collection' : False,
+ }
+ def _get_root_calendar_directory(self, cr, uid, context=None):
+ objid = self.pool.get('ir.model.data')
+ try:
+ mid = objid._get_id(cr, uid, 'document', 'dir_calendars')
+ if not mid:
+ return False
+ root_id = objid.read(cr, uid, mid, ['res_id'])['res_id']
+ root_cal_dir = self.browse(cr, uid, root_id, context=context)
+ return root_cal_dir.name
+ except Exception, e:
+ import netsvc
+ logger = netsvc.Logger()
+ logger.notifyChannel("document", netsvc.LOG_WARNING, 'Cannot set root directory for Calendars:'+ str(e))
+ return False
+ return False
+
+ def _locate_child(self, cr, uid, root_id, uri,nparent, ncontext):
+ """ try to locate the node in uri,
+ Return a tuple (node_dir, remaining_path)
+ """
+ return (caldav_node.node_database(context=ncontext), uri)
+
+ def get_description(self, cr, uid, ids, context=None):
+ #TODO : return description of all calendars
+ if not context:
+ context = {}
+ return False
+
+ def get_schedule_inbox_URL(self, cr, uid, ids, context=None):
+ calendar_obj = self.pool.get('basic.calendar')
+
+ calendar_ids = calendar_obj.search(cr, uid, [
+ ('user_id', '=', uid), ('collection_id', 'in', ids)
+ ], limit=1, context=context)
+
+ root_cal_dir = self._get_root_calendar_directory(cr, uid, context=context)
+ if not calendar_ids:
+ return root_cal_dir
+ calendar_id = calendar_ids[0]
+ calendar = calendar_obj.browse(cr, uid, calendar_id,
+ context=context)
+ return '%s/%s' %(root_cal_dir, calendar.name)
+
+ def get_schedule_outbox_URL(self, cr, uid, ids, context=None):
+ return self.get_schedule_inbox_URL(cr, uid, ids, context=context)
+
+calendar_collection()
diff --git a/addons/caldav/security/ir.model.access.csv b/addons/caldav/security/ir.model.access.csv
index 74064f43d75..4dd04fa4c68 100644
--- a/addons/caldav/security/ir.model.access.csv
+++ b/addons/caldav/security/ir.model.access.csv
@@ -1,5 +1,4 @@
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
-"access_basic_calendar_all","basic.calendar","model_basic_calendar","base.group_user",1,1,1,1
"access_basic_calendar_event_all","basic.calendar.event","model_basic_calendar_event","base.group_user",1,1,1,0
"access_basic_calendar_attendee_all","basic.calendar.attendee","model_basic_calendar_attendee","base.group_user",1,1,1,0
"access_calendar_todo_all","basic.calendar.todo","model_basic_calendar_todo","base.group_user",1,1,1,0
diff --git a/addons/caldav/webdav_server.py b/addons/caldav/webdav_server.py
deleted file mode 100644
index 40519ee19a2..00000000000
--- a/addons/caldav/webdav_server.py
+++ /dev/null
@@ -1,78 +0,0 @@
-# -*- encoding: utf-8 -*-
-#
-# Copyright P. Christeas 2008,2009
-#
-# WARNING: This program as such is intended to be used by professional
-# programmers who take the whole responsability of assessing all potential
-# consequences resulting from its eventual inadequacies and bugs
-# End users who are looking for a ready-to-use solution with commercial
-# garantees and support are strongly adviced to contract a Free Software
-# Service Company
-#
-# This program is Free Software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-###############################################################################
-
-
-import netsvc
-from caldav_fs import tinydav_handler
-from tools.config import config
-from DAV.WebDAVServer import DAVRequestHandler
-from service.websrv_lib import HTTPDir,FixSendError
-
-
-class DAVHandler(FixSendError, DAVRequestHandler):
- verbose = False
-
- def get_userinfo(self, user, pw):
- return False
-
- def _log(self, message):
- netsvc.Logger().notifyChannel("webdav", netsvc.LOG_DEBUG, message)
-
- def handle(self):
- pass
-
- def finish(self):
- pass
-
- def setup(self):
- davpath = '/calendar/'
- self.baseuri = "http://%s:%d%s"% (self.server.server_name, self.server.server_port, davpath)
- self.IFACE_CLASS = tinydav_handler(self)
- pass
-
- def log_message(self, format, *args):
- netsvc.Logger().notifyChannel('webdav', netsvc.LOG_DEBUG_RPC, format % args)
-
- def log_error(self, format, *args):
- netsvc.Logger().notifyChannel('webdav', netsvc.LOG_WARNING, format % args)
-
-
-try:
- from service.http_server import reg_http_service, OpenERPAuthProvider
- davpath = '/calendar/'
- handler = DAVHandler
- handler.verbose = config.get_misc('webdav', 'verbose', True)
- handler.debug = config.get_misc('webdav', 'debug', True)
- reg_http_service(HTTPDir(davpath, DAVHandler, OpenERPAuthProvider()))
- netsvc.Logger().notifyChannel('webdav', netsvc.LOG_INFO, "WebDAV service registered at path: %s/ "% davpath)
-except Exception, e:
- logger = netsvc.Logger()
- logger.notifyChannel('webdav', netsvc.LOG_ERROR, 'Cannot launch webdav: %s' % e)
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4
-
-
-
diff --git a/addons/caldav/wizard/calendar_event_import_view.xml b/addons/caldav/wizard/calendar_event_import_view.xml
index 6a697de5fee..7e31d08c1e9 100644
--- a/addons/caldav/wizard/calendar_event_import_view.xml
+++ b/addons/caldav/wizard/calendar_event_import_view.xml
@@ -46,7 +46,7 @@
new
-
diff --git a/addons/caldav/wizard/calendar_event_subscribe_view.xml b/addons/caldav/wizard/calendar_event_subscribe_view.xml
index 43224013268..39eaae28540 100644
--- a/addons/caldav/wizard/calendar_event_subscribe_view.xml
+++ b/addons/caldav/wizard/calendar_event_subscribe_view.xml
@@ -52,4 +52,4 @@
view_mode="form" target="new" view_type="form" />
-
\ No newline at end of file
+
diff --git a/addons/crm/crm.py b/addons/crm/crm.py
index 0c7d2e295ad..7959e32e390 100644
--- a/addons/crm/crm.py
+++ b/addons/crm/crm.py
@@ -80,6 +80,7 @@ class crm_case_section(osv.osv):
]
def _check_recursion(self, cr, uid, ids):
+
"""
Checks for recursion level for sales team
@param self: The object pointer
@@ -138,8 +139,8 @@ class crm_case_categ(osv.osv):
'section_id': fields.many2one('crm.case.section', 'Sales Team'),
'object_id': fields.many2one('ir.model', 'Object Name'),
}
- def _find_object_id(self, cr, uid, context=None):
+ def _find_object_id(self, cr, uid, context=None):
"""Finds id for case object
@param self: The object pointer
@param cr: the current row, from the database cursor,
@@ -153,8 +154,8 @@ class crm_case_categ(osv.osv):
_defaults = {
'object_id' : _find_object_id
- }
+ }
crm_case_categ()
@@ -284,6 +285,7 @@ class crm_case(osv.osv):
@param uid: the current user’s ID for security checks,
@param ids: List of Case IDs
@param context: A standard dictionary for contextual values
+ @return:Dictionary of History Ids
"""
if not context:
context = {}
@@ -526,7 +528,6 @@ class crm_case(osv.osv):
@param context: A standard dictionary for contextual values"""
if not context:
context = {}
-
model_obj = self.pool.get('ir.model')
obj = self.pool.get('crm.case.log')
for case in cases:
diff --git a/addons/crm/crm_action_rule.py b/addons/crm/crm_action_rule.py
index 718b12bb9c3..ff390071456 100644
--- a/addons/crm/crm_action_rule.py
+++ b/addons/crm/crm_action_rule.py
@@ -270,3 +270,5 @@ class base_action_rule_line(osv.osv):
}
base_action_rule_line()
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/crm/crm_fundraising.py b/addons/crm/crm_fundraising.py
index 927a3d05915..afd7f7fbf4d 100644
--- a/addons/crm/crm_fundraising.py
+++ b/addons/crm/crm_fundraising.py
@@ -73,3 +73,5 @@ class crm_fundraising(osv.osv):
}
crm_fundraising()
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py
index 9c4c38875c5..9d50bb0db7b 100644
--- a/addons/crm/crm_lead.py
+++ b/addons/crm/crm_lead.py
@@ -85,8 +85,8 @@ class crm_lead(osv.osv):
resource_id = resource_ids[0]
duration = float(ans.days)
- if lead.section_id.resource_calendar_id:
- duration = float(ans.days) * 24
+ if lead.section_id and lead.section_id.resource_calendar_id:
+ duration = float(ans.days) * 24
new_dates = cal_obj.interval_get(cr,
uid,
lead.section_id.resource_calendar_id and lead.section_id.resource_calendar_id.id or False,
diff --git a/addons/crm/crm_opportunity.py b/addons/crm/crm_opportunity.py
index dec672c3277..dfb202dfa0e 100644
--- a/addons/crm/crm_opportunity.py
+++ b/addons/crm/crm_opportunity.py
@@ -92,8 +92,8 @@ class crm_opportunity(osv.osv):
resource_id = resource_ids[0]
duration = float(ans.days)
- if opportunity.section_id.resource_calendar_id:
- duration = float(ans.days) * 24
+ if opportunity.section_id and opportunity.section_id.resource_calendar_id:
+ duration = float(ans.days) * 24
new_dates = cal_obj.interval_get(cr,
uid,
opportunity.section_id.resource_calendar_id and opportunity.section_id.resource_calendar_id.id or False,
diff --git a/addons/crm_caldav/crm_caldav.py b/addons/crm_caldav/crm_caldav.py
index a3a1054a1ee..ef1f4b020b9 100644
--- a/addons/crm_caldav/crm_caldav.py
+++ b/addons/crm_caldav/crm_caldav.py
@@ -21,8 +21,6 @@
from osv import fields, osv
from crm import crm
-from caldav import caldav
-from base_calendar import base_calendar
class crm_meeting(osv.osv):
_inherit = 'crm.meeting'
diff --git a/addons/crm_caldav/crm_caldav_data.xml b/addons/crm_caldav/crm_caldav_data.xml
index b5e7d183325..0f160bfd85d 100644
--- a/addons/crm_caldav/crm_caldav_data.xml
+++ b/addons/crm_caldav/crm_caldav_data.xml
@@ -6,6 +6,8 @@
Meetings
+
+ vevent
diff --git a/addons/document/document_directory.py b/addons/document/document_directory.py
index 19c4489132c..25e172d2817 100644
--- a/addons/document/document_directory.py
+++ b/addons/document/document_directory.py
@@ -95,8 +95,7 @@ class document_directory(osv.osv):
'user_id': lambda self,cr,uid,ctx: uid,
'domain': lambda self,cr,uid,ctx: '[]',
'type': lambda *args: 'directory',
- 'ressource_id': lambda *a: 0,
- 'parent_id': _get_root_directory,
+ 'ressource_id': lambda *a: 0,
'storage_id': _get_def_storage,
}
_sql_constraints = [
@@ -204,52 +203,11 @@ class document_directory(osv.osv):
raise
- def _locate_child(self, cr,uid, root_id, uri,nparent, ncontext):
+ def _locate_child(self, cr, uid, root_id, uri,nparent, ncontext):
""" try to locate the node in uri,
Return a tuple (node_dir, remaining_path)
"""
- did = root_id
- duri = uri
- path = []
- context = ncontext.context
- while len(duri):
- nid = self.search(cr,uid,[('parent_id','=',did),('name','=',duri[0]),('type','=','directory')], context=context)
- if not nid:
- break
- if len(nid)>1:
- print "Duplicate dir? p= %d, n=\"%s\"" %(did,duri[0])
- path.append(duri[0])
- duri = duri[1:]
- did = nid[0]
- root_node = did and self.browse(cr,uid,did, context) or False
- return (nodes.node_dir(path, nparent,ncontext, root_node), duri)
-
-
- nid = self.search(cr,uid,[('parent_id','=',did),('name','=',duri[0]),('type','=','ressource')], context=context)
- if nid:
- if len(nid)>1:
- print "Duplicate dir? p= %d, n=\"%s\"" %(did,duri[0])
- path.append(duri[0])
- duri = duri[1:]
- did = nid[0]
- return nodes.node_res_dir(path, nparent,ncontext,self.browse(cr,uid,did, context))
-
- # Here, we must find the appropriate non-dir child..
- # Chech for files:
- fil_obj = self.pool.get('ir.attachment')
- nid = fil_obj.search(cr,uid,[('parent_id','=',did),('name','=',duri[0])],context=context)
- if nid:
- if len(duri)>1:
- # cannot treat child as a dir
- return None
- if len(nid)>1:
- print "Duplicate file?",did,duri[0]
- path.append(duri[0])
- return nodes.node_file(path,nparent,ncontext,fil_obj.browse(cr,uid,nid[0],context))
-
- print "nothing found:",did, duri
- #still, nothing found
- return None
+ return (nodes.node_database(context=ncontext), uri)
def old_code():
if not uri:
diff --git a/addons/document/document_view.xml b/addons/document/document_view.xml
index 0185f9fe444..341fe6ceb79 100644
--- a/addons/document/document_view.xml
+++ b/addons/document/document_view.xml
@@ -118,8 +118,7 @@
document.directory
document.directory
- tree
- child_ids
+ tree
diff --git a/addons/document/nodes.py b/addons/document/nodes.py
index f0342fcb885..d0815ef9c74 100644
--- a/addons/document/nodes.py
+++ b/addons/document/nodes.py
@@ -20,7 +20,7 @@
##############################################################################
import base64
-
+import StringIO
from osv import osv, fields
from osv.orm import except_orm
import urlparse
@@ -69,14 +69,6 @@ class node_context(object):
return ndir
-class node_database():
- """ A node representing the database directory
- Useless?
- """
- def __init__(self,ncontext):
- self.nctx = ncontext
-
-
class node_class(object):
@@ -117,11 +109,11 @@ class node_class(object):
s.append(self.path)
return s #map(lambda x: '/' +x, s)
- def children(self, cr):
+ def children(self, cr, domain=None):
print "node_class.children()"
return [] #stub
- def child(self,cr, name):
+ def child(self,cr, name, domain=None):
print "node_class.child()"
return None
@@ -160,10 +152,94 @@ class node_class(object):
its capabilities """
return {}
+ def match_dav_eprop(self, cr, match, ns, prop):
+ res = self.get_dav_eprop(cr, ns, prop)
+ if res == match:
+ return True
+ return False
+
def get_dav_eprop(self,cr,ns,prop):
return None
-class node_dir(node_class):
+ def rm(self, cr):
+ raise RuntimeError("Not Implemented")
+
+ def rmcol(self, cr):
+ raise RuntimeError("Not Implemented")
+
+ def get_domain(self, cr, filters):
+ return []
+
+class node_database(node_class):
+ """ A node representing the database directory
+
+ """
+ our_type = 'database'
+ def __init__(self, path=[], parent=False, context=None):
+ super(node_database,self).__init__(path, parent, context)
+
+ def children(self, cr, domain=None):
+ res = self._child_get(cr, domain=domain) + self._file_get(cr)
+ return res
+
+ def child(self, cr, name, domain=None):
+ res = self._child_get(cr, name, domain=None)
+ if res:
+ return res[0]
+ res = self._file_get(cr,name)
+ if res:
+ return res[0]
+ return None
+
+ def _child_get(self, cr, name=False, parent_id=False, domain=None):
+ dirobj = self.context._dirobj
+ uid = self.context.uid
+ ctx = self.context.context.copy()
+ ctx.update(self.dctx)
+ where = [('parent_id','=',parent_id)]
+ if name:
+ where.append(('name','=',name))
+ if not domain:
+ domain = []
+
+ where2 = where + domain + [('type', '=', 'directory')]
+ ids = dirobj.search(cr, uid, where2, context=ctx)
+ res = []
+ for dirr in dirobj.browse(cr,uid,ids,context=ctx):
+ res.append(node_dir(dirr.name,self,self.context,dirr))
+
+ where2 = where + domain + [('type', '=', 'ressource'), ('ressource_parent_type_id','=',False)]
+ ids = dirobj.search(cr, uid, where2, context=ctx)
+ for dirr in dirobj.browse(cr,uid,ids,context=ctx):
+ res.append(node_res_dir(dirr.name,self,self.context,dirr))
+
+ fil_obj = dirobj.pool.get('ir.attachment')
+ ids = fil_obj.search(cr,uid,where,context=ctx)
+ if ids:
+ for fil in fil_obj.browse(cr,uid,ids,context=ctx):
+ res.append(node_file(fil.name,self,self.context,fil))
+ return res
+
+ def _file_get(self,cr, nodename=False, directory_id=False):
+ res = []
+ cntobj = self.context._dirobj.pool.get('document.directory.content')
+ uid = self.context.uid
+ ctx = self.context.context.copy()
+ ctx.update(self.dctx)
+ where = [('directory_id','=',directory_id) ]
+ ids = cntobj.search(cr, uid, where, context=ctx)
+ for content in cntobj.browse(cr, uid, ids, context=ctx):
+ res3 = cntobj._file_get(cr, self, nodename, content)
+ if res3:
+ res.extend(res3)
+
+ return res
+
+ def _get_ttag(self,cr):
+ return 'db-%s' % cr.dbname
+
+
+class node_dir(node_database):
our_type = 'collection'
def __init__(self,path, parent, context, dirr, dctx=None):
super(node_dir,self).__init__(path, parent,context)
@@ -192,89 +268,57 @@ class node_dir(node_class):
print e
pass
- def children(self,cr):
- return self._child_get(cr) + self._file_get(cr)
+
+ def get_data(self,cr):
+ res = ''
+ for child in self.children(cr):
+ res += child.get_data(cr)
+ return res
- def child(self,cr, name):
- res = self._child_get(cr,name)
- if res:
- return res[0]
- res = self._file_get(cr,name)
- if res:
- return res[0]
- return None
+
def _file_get(self,cr, nodename=False):
- res = []
- cntobj = self.context._dirobj.pool.get('document.directory.content')
- uid = self.context.uid
- ctx = self.context.context.copy()
- ctx.update(self.dctx)
- where = [('directory_id','=',self.dir_id) ]
- ids = cntobj.search(cr, uid, where, context=ctx)
- for content in cntobj.browse(cr, uid, ids, context=ctx):
- res3 = cntobj._file_get(cr, self, nodename, content)
- if res3:
- res.extend(res3)
+ return super(node_dir,self)._file_get(cr, nodename, self.dir_id)
+
+
+ def _child_get(self, cr, name=None, domain=None):
+ return super(node_dir,self)._child_get(cr, name, self.dir_id, domain=domain)
+
+ def rmcol(self, cr):
+ uid = self.context.uid
+ directory = self.context._dirobj.browse(cr, uid, self.dir_id)
+ res = False
+ if not directory:
+ raise OSError(2, 'Not such file or directory.')
+ if directory._table_name=='document.directory':
+ if self.children(cr):
+ raise OSError(39, 'Directory not empty.')
+ res = self.context._dirobj.unlink(cr, uid, [directory.id])
+ else:
+ raise OSError(1, 'Operation not permited.')
return res
- def get_dav_props(self, cr):
- res = {}
- cntobj = self.context._dirobj.pool.get('document.directory.content')
- uid = self.context.uid
- ctx = self.context.context.copy()
- ctx.update(self.dctx)
- where = [('directory_id','=',self.dir_id) ]
- ids = cntobj.search(cr,uid,where,context=ctx)
- for content in cntobj.browse(cr,uid,ids,context=ctx):
- if content.extension == '.ics': # FIXME: call the content class!
- res['http://groupdav.org/'] = ('resourcetype',)
- break
- return res
-
- def get_dav_eprop(self,cr,ns,prop):
- if ns != 'http://groupdav.org/' or prop != 'resourcetype':
- print "Who asked for %s:%s?" % (ns,prop)
- return None
- res = {}
- cntobj = self.context._dirobj.pool.get('document.directory.content')
- uid = self.context.uid
- ctx = self.context.context.copy()
- ctx.update(self.dctx)
- where = [('directory_id','=',self.dir_id) ]
- ids = cntobj.search(cr,uid,where,context=ctx)
- for content in cntobj.browse(cr,uid,ids,context=ctx):
- if content.extension == '.ics': # FIXME: call the content class!
- return ('vevent-collection','http://groupdav.org/')
- return None
-
- def _child_get(self,cr,name = None):
+ def create_child_collection(self, cr, objname):
+ object2 = False
dirobj = self.context._dirobj
uid = self.context.uid
ctx = self.context.context.copy()
ctx.update(self.dctx)
- where = [('parent_id','=',self.dir_id) ]
- if name:
- where.append(('name','=',name))
+ obj = dirobj.browse(cr, uid, self.dir_id)
+ if obj and (obj.type == 'ressource') and not object2:
+ raise OSError(1, 'Operation not permited.')
- ids = dirobj.search(cr, uid, where + [('ressource_parent_type_id','=',False)],context=ctx)
- res = []
- if ids:
- for dirr in dirobj.browse(cr,uid,ids,context=ctx):
- if dirr.type == 'directory':
- res.append(node_dir(dirr.name,self,self.context,dirr))
- elif dirr.type == 'ressource':
- res.append(node_res_dir(dirr.name,self,self.context,dirr))
+ #objname = uri2[-1]
+ val = {
+ 'name': objname,
+ 'ressource_parent_type_id': obj and obj.ressource_type_id.id or False,
+ 'ressource_id': object2 and object2.id or False,
+ 'parent_id' : obj and obj.id or False
+ }
+
+ return dirobj.create(cr, uid, val)
- fil_obj=dirobj.pool.get('ir.attachment')
- #where2 = where # + [('res_model', '=', None)]
- ids = fil_obj.search(cr,uid,where,context=ctx)
- if ids:
- for fil in fil_obj.browse(cr,uid,ids,context=ctx):
- res.append(node_file(fil.name,self,self.context,fil))
-
- return res
def create_child(self,cr,path,data):
""" API function to create a child file object and node
@@ -297,6 +341,19 @@ class node_dir(node_class):
fnode = node_file(path,self,self.context,fil)
fnode.set_data(cr,data,fil)
return fnode
+
+ def get_etag(self, cr):
+ """ Get a tag, unique per object + modification.
+
+ see. http://tools.ietf.org/html/rfc2616#section-13.3.3 """
+ return self._get_ttag(cr) + ':' + self._get_wtag(cr)
+
+ def _get_wtag(self, cr):
+ """ Return the modification time as a unique, compact string """
+ if self.write_date:
+ wtime = time.mktime(time.strptime(self.write_date, '%Y-%m-%d %H:%M:%S'))
+ else: wtime = time.time()
+ return str(wtime)
def _get_ttag(self,cr):
return 'dir-%d' % self.dir_id
@@ -334,16 +391,16 @@ class node_res_dir(node_class):
for dfld in dirr.dctx_ids:
self.dctx_dict['dctx_' + dfld.field] = dfld.expr
- def children(self,cr):
- return self._child_get(cr)
+ def children(self, cr, domain=None):
+ return self._child_get(cr, domain=domain)
- def child(self,cr, name):
- res = self._child_get(cr,name)
+ def child(self,cr, name, domain=None):
+ res = self._child_get(cr, name, domain=domain)
if res:
return res[0]
return None
- def _child_get(self,cr,name = None):
+ def _child_get(self,cr, name = None, domain=None):
""" return virtual children of resource, based on the
foreign object.
@@ -430,11 +487,11 @@ class node_res_obj(node_class):
else:
self.res_id = res_id
- def children(self,cr):
- return self._child_get(cr) + self._file_get(cr)
+ def children(self, cr, domain=None):
+ return self._child_get(cr, domain=domain) + self._file_get(cr)
- def child(self,cr, name):
- res = self._child_get(cr,name)
+ def child(self,cr, name, domain=None):
+ res = self._child_get(cr, name, domain=domain)
if res:
return res[0]
res = self._file_get(cr,name)
@@ -489,7 +546,7 @@ class node_res_obj(node_class):
return ('vevent-collection','http://groupdav.org/')
return None
- def _child_get(self,cr,name = None):
+ def _child_get(self,cr, name=None, domain=None):
dirobj = self.context._dirobj
uid = self.context.uid
ctx = self.context.context.copy()
@@ -551,6 +608,31 @@ class node_res_obj(node_class):
if dirr.type == 'ressource':
res.append(node_res_dir(dirr.name, self, self.context, dirr, {'active_id': self.res_id}))
return res
+
+ def create_child_collection(self, cr, objname):
+ dirobj = self.context._dirobj
+ uid = self.context.uid
+ ctx = self.context.context.copy()
+ ctx.update(self.dctx)
+ res_obj = dirobj.pool.get(self.context.context['res_model'])
+
+ object2 = res_obj.browse(cr, uid, self.context.context['res_id']) or False
+
+ obj = dirobj.browse(cr, uid, self.dir_id)
+ if obj and (obj.type == 'ressource') and not object2:
+ raise OSError(1, 'Operation not permited.')
+
+
+ val = {
+ 'name': objname,
+ 'ressource_parent_type_id': obj and obj.ressource_type_id.id or False,
+ 'ressource_id': object2 and object2.id or False,
+ 'parent_id' : False
+ }
+ if (obj and (obj.type in ('directory'))) or not object2:
+ val['parent_id'] = obj and obj.id or False
+
+ return dirobj.create(cr, uid, val)
def create_child(self,cr,path,data):
""" API function to create a child file object and node
@@ -597,7 +679,30 @@ class node_file(node_class):
if fil.parent_id:
self.storage_id = fil.parent_id.storage_id.id
else:
- self.storage_id = None
+ self.storage_id = None
+
+ def open(self, cr, mode=False):
+ uid = self.context.uid
+ if self.type in ('collection','database'):
+ return False
+ fobj = self.context._dirobj.pool.get('ir.attachment').browse(cr, uid, self.file_id, context=self.context.context)
+ if fobj.store_method and fobj.store_method== 'fs' :
+ s = StringIO.StringIO(self.get_data(cr, fobj))
+ else:
+ s = StringIO.StringIO(base64.decodestring(fobj.db_datas or ''))
+ s.name = self
+ return s
+
+ def rm(self, cr):
+ uid = self.context.uid
+ document_obj = self.context._dirobj.pool.get('ir.attachment')
+ if self.type in ('collection','database'):
+ return False
+ document = document_obj.browse(cr, uid, self.file_id, context=self.context.context)
+ res = False
+ if document and document._table_name == 'ir.attachment':
+ res = document_obj.unlink(cr, uid, [document.id])
+ return res
def fix_ppath(self, cr, fbro):
"""Sometimes we may init this w/o path, parent.
@@ -683,6 +788,16 @@ class node_content(node_class):
if dctx:
self.dctx.update(dctx)
self.act_id = act_id
+
+ def open(self, cr, mode=False):
+ uid = self.context.uid
+ if self.type in ('collection','database'):
+ return False
+ pool = self.context._dirobj.pool
+ res = getattr(pool.get('document.directory.content'), 'process_read')(cr, uid, self)
+ res = StringIO.StringIO(res)
+ res.name = self
+ return res
def fill_fields(self,cr,dctx = None):
""" Try to read the object and fill missing fields, like mimetype,
@@ -720,147 +835,3 @@ class node_content(node_class):
def _get_ttag(self,cr):
return 'cnt-%d%s' % (self.cnt_id,(self.act_id and ('-' + str(self.act_id))) or '')
-
-class old_class():
- # the old code, remove..
- def __init__(self, cr, uid, path, object, object2=False, context={}, content=False, type='collection', root=False):
- self.cr = cr
- def _file_get(self, nodename=False):
- if not self.object:
- return []
- pool = pooler.get_pool(self.cr.dbname)
- fobj = pool.get('ir.attachment')
- res2 = []
- where = []
- if self.object2:
- where.append( ('res_model','=',self.object2._name) )
- where.append( ('res_id','=',self.object2.id) )
- else:
- where.append( ('parent_id','=',self.object.id) )
- where.append( ('res_id','=',False) )
- if nodename:
- where.append( (fobj._rec_name,'=',nodename) )
- for content in self.object.content_ids:
- res3 = content._table._file_get(self,nodename,content)
- if res3:
- res2.extend(res3)
-
- ids = fobj.search(self.cr, self.uid, where+[ ('parent_id','=',self.object and self.object.id or False) ])
- if self.object and self.root and (self.object.type=='ressource'):
- ids += fobj.search(self.cr, self.uid, where+[ ('parent_id','=',False) ])
- res = fobj.browse(self.cr, self.uid, ids, context=self.context)
- return map(lambda x: node_class(self.cr, self.uid, self.path+'/'+eval('x.'+fobj._rec_name), x, False, context=self.context, type='file', root=False), res) + res2
-
- def get_translation(self,value,lang):
- # Must go, it works on arbitrary models and could be ambiguous.
- result = value
- pool = pooler.get_pool(self.cr.dbname)
- translation_ids = pool.get('ir.translation').search(self.cr, self.uid, [('value','=',value),('lang','=',lang),('type','=','model')])
- if len(translation_ids):
- tran_id = translation_ids[0]
- translation = pool.get('ir.translation').read(self.cr, self.uid, tran_id, ['res_id','name'])
- res_model,field_name = tuple(translation['name'].split(','))
- res_id = translation['res_id']
- res = pool.get(res_model).read(self.cr, self.uid, res_id, [field_name])
- if res:
- result = res[field_name]
- return result
-
- def directory_list_for_child(self,nodename,parent=False):
- pool = pooler.get_pool(self.cr.dbname)
- where = []
- if nodename:
- nodename = self.get_translation(nodename, self.context['lang'])
- where.append(('name','=',nodename))
- if (self.object and self.object.type=='directory') or not self.object2:
- where.append(('parent_id','=',self.object and self.object.id or False))
- else:
- where.append(('parent_id','=',False))
- if self.object:
- where.append(('ressource_parent_type_id','=',self.object.ressource_type_id.id))
- else:
- where.append(('ressource_parent_type_id','=',False))
-
- ids = pool.get('document.directory').search(self.cr, self.uid, where+[('ressource_id','=',0)])
- if self.object2:
- ids += pool.get('document.directory').search(self.cr, self.uid, where+[('ressource_id','=',self.object2.id)])
- res = pool.get('document.directory').browse(self.cr, self.uid, ids, self.context)
- return res
-
- def _child_get(self, nodename=False):
- if self.type not in ('collection','database'):
- return []
- res = self.directory_list_for_child(nodename)
- result= map(lambda x: node_class(self.cr, self.uid, self.path+'/'+x.name, x, x.type=='directory' and self.object2 or False, context=self.context, root=self.root), res)
- if self.type=='database':
- pool = pooler.get_pool(self.cr.dbname)
- fobj = pool.get('ir.attachment')
- vargs = [('parent_id','=',False),('res_id','=',False)]
- if nodename:
- vargs.append((fobj._rec_name,'=',nodename))
- file_ids=fobj.search(self.cr,self.uid,vargs)
-
- res = fobj.browse(self.cr, self.uid, file_ids, context=self.context)
- result +=map(lambda x: node_class(self.cr, self.uid, self.path+'/'+eval('x.'+fobj._rec_name), x, False, context=self.context, type='file', root=self.root), res)
- if self.type=='collection' and self.object.type=="ressource":
- where = self.object.domain and eval(self.object.domain, {'active_id':self.root, 'uid':self.uid}) or []
- pool = pooler.get_pool(self.cr.dbname)
- obj = pool.get(self.object.ressource_type_id.model)
- _dirname_field = obj._rec_name
- if len(obj.fields_get(self.cr, self.uid, ['dirname'])):
- _dirname_field = 'dirname'
-
- name_for = obj._name.split('.')[-1]
- if nodename and nodename.find(name_for) == 0 :
- id = int(nodename.replace(name_for,''))
- where.append(('id','=',id))
- elif nodename:
- if nodename.find('__') :
- nodename=nodename.replace('__','/')
- for invalid in INVALID_CHARS:
- if nodename.find(INVALID_CHARS[invalid]) :
- nodename=nodename.replace(INVALID_CHARS[invalid],invalid)
- nodename = self.get_translation(nodename, self.context['lang'])
- where.append((_dirname_field,'=',nodename))
-
- if self.object.ressource_tree:
- if obj._parent_name in obj.fields_get(self.cr,self.uid):
- where.append((obj._parent_name,'=',self.object2 and self.object2.id or False))
- ids = obj.search(self.cr, self.uid, where)
- res = obj.browse(self.cr, self.uid, ids,self.context)
- result+= map(lambda x: node_class(self.cr, self.uid, self.path+'/'+x.name.replace('/','__'), self.object, x, context=self.context, root=x.id), res)
- return result
- else :
- if self.object2:
- return result
- else:
- if self.object2:
- return result
-
-
- ids = obj.search(self.cr, self.uid, where)
- res = obj.browse(self.cr, self.uid, ids,self.context)
- for r in res:
- if len(obj.fields_get(self.cr, self.uid, [_dirname_field])):
- r.name = eval('r.'+_dirname_field)
- else:
- r.name = False
- if not r.name:
- r.name = name_for + '%d'%r.id
- for invalid in INVALID_CHARS:
- if r.name.find(invalid) :
- r.name = r.name.replace(invalid,INVALID_CHARS[invalid])
- result2 = map(lambda x: node_class(self.cr, self.uid, self.path+'/'+x.name.replace('/','__'), self.object, x, context=self.context, root=x.id), res)
- if result2:
- if self.object.ressource_tree:
- result += result2
- else:
- result = result2
- return result
-
-
- def path_get(self):
- path = self.path
- if self.path[0]=='/':
- path = self.path[1:]
- return path
diff --git a/addons/document_ftp/ftpserver/abstracted_fs.py b/addons/document_ftp/ftpserver/abstracted_fs.py
index 0ffa8eb80f2..54b2cb9830f 100644
--- a/addons/document_ftp/ftpserver/abstracted_fs.py
+++ b/addons/document_ftp/ftpserver/abstracted_fs.py
@@ -309,31 +309,18 @@ class abstracted_fs:
def open(self, node, mode):
if not node:
raise OSError(1, 'Operation not permited.')
- # Reading operation
- if node.type == 'file':
- cr = pooler.get_db(node.context.dbname).cursor()
- uid = node.context.uid
- if not self.isfile(node):
- raise OSError(1, 'Operation not permited.')
- fobj = node.context._dirobj.pool.get('ir.attachment').browse(cr, uid, node.file_id, context=node.context.context)
- if fobj.store_method and fobj.store_method== 'fs' :
- s = StringIO.StringIO(node.get_data(cr, fobj))
- else:
- s = StringIO.StringIO(base64.decodestring(fobj.db_datas or ''))
- s.name = node
- cr.close()
- return s
- elif node.type == 'content':
- uid = node.context.uid
- cr = pooler.get_db(node.context.dbname).cursor()
- pool = pooler.get_pool(node.context.dbname)
- res = getattr(pool.get('document.directory.content'), 'process_read')(cr, uid, node)
- res = StringIO.StringIO(res)
- res.name = node
- cr.close()
- return res
- else:
+ # Reading operation
+ cr = pooler.get_db(node.context.dbname).cursor()
+ res = False
+ #try:
+ if node.type not in ('collection','database'):
+ res = node.open(cr, mode)
+ #except:
+ # pass
+ cr.close()
+ if not res:
raise OSError(1, 'Operation not permited.')
+ return res
# ok, but need test more
diff --git a/addons/document_ics/document_demo.xml b/addons/document_ics/document_demo.xml
index a4c892340de..06c72022fe7 100644
--- a/addons/document_ics/document_demo.xml
+++ b/addons/document_ics/document_demo.xml
@@ -7,14 +7,14 @@
shcal
-
+
Calendars
Calendars
meetings
-
+
.ics
diff --git a/addons/document_ics/document_ics.py b/addons/document_ics/document_ics.py
index 9056b3250b4..00f8927eb18 100644
--- a/addons/document_ics/document_ics.py
+++ b/addons/document_ics/document_ics.py
@@ -332,8 +332,8 @@ class document_directory_content(osv.osv):
return s
document_directory_content()
-class crm_case(osv.osv):
- _inherit = 'crm.case'
+class crm_meeting(osv.osv):
+ _inherit = 'crm.meeting'
_columns = {
'code': fields.char('Calendar Code', size=64),
'date_deadline': fields.datetime('Deadline', help="Deadline Date is automatically\
@@ -341,7 +341,7 @@ class crm_case(osv.osv):
}
_defaults = {
- 'code': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'crm.case'),
+ 'code': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').get(cr, uid, 'crm.meeting'),
}
def copy(self, cr, uid, id, default=None, context=None):
@@ -356,32 +356,10 @@ class crm_case(osv.osv):
if not default: default = {}
if not context: context = {}
- default.update({'code': self.pool.get('ir.sequence').get(cr, uid, 'crm.case'), 'id': False})
- return super(crm_case, self).copy(cr, uid, id, default, context)
+ default.update({'code': self.pool.get('ir.sequence').get(cr, uid, 'crm.meeting'), 'id': False})
+ return super(crm_meeting, self).copy(cr, uid, id, default, context)
- def on_change_duration(self, cr, uid, id, date, duration):
- """ Change Duration
- @param self: The object pointer
- @param cr: the current row, from the database cursor,
- @param uid: the current user’s ID for security checks,
- @param id: crm case's ID,
- @param date: Pass the Date,
- @param duration: Pass the duration,
- """
-
- if not date:
- return {}
- start_date = datetime.datetime.fromtimestamp(time.mktime(time.strptime(date, "%Y-%m-%d %H:%M:%S")))
- if duration >= 0 :
- end = start_date + datetime.timedelta(hours=duration)
- if duration < 0:
- raise osv.except_osv(_('Warning !'),
- _('You can not set negative Duration.'))
-
- res = {'value': {'date_deadline' : end.strftime('%Y-%m-%d %H:%M:%S')}}
- return res
-
-crm_case()
+crm_meeting()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/document_ics/document_view.xml b/addons/document_ics/document_view.xml
index ac99f3723db..0cbfb6f81e2 100644
--- a/addons/document_ics/document_view.xml
+++ b/addons/document_ics/document_view.xml
@@ -39,36 +39,16 @@
-
- crm.case.code.form
- crm.case
+
+ crm.meeting.code.form
+ crm.meeting
form
-
+
-
-
-
-
- crm.case.inherit.form1
- crm.case
- form
-
-
-
-
-
-
-
-
-
diff --git a/addons/document_webdav/DAV/BufferingHTTPServer.py b/addons/document_webdav/DAV/BufferingHTTPServer.py
deleted file mode 100644
index 37520421432..00000000000
--- a/addons/document_webdav/DAV/BufferingHTTPServer.py
+++ /dev/null
@@ -1,99 +0,0 @@
-#!/usr/bin/env python
-
-"""
- Buffering HTTP Server
- Copyright (C) 1999 Christian Scholz (ruebe@aachen.heimat.de)
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public
- License as published by the Free Software Foundation; either
- version 2 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with this library; if not, write to the Free
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-"""
-
-
-from utils import VERSION, AUTHOR
-__version__ = VERSION
-__author__ = AUTHOR
-
-from BaseHTTPServer import BaseHTTPRequestHandler
-import os
-class BufferedHTTPRequestHandler(BaseHTTPRequestHandler):
- """
- Buffering HTTP Request Handler
-
- This class is an extension to the BaseHTTPRequestHandler
- class which buffers the whole output and sends it at once
- after the processing if the request is finished.
-
- This makes it possible to work together with some clients
- which otherwise would break (e.g. cadaver)
-
- """
-
-
- def _init_buffer(self):
- """initialize the buffer.
-
- If you override the handle() method remember to call
- this (see below)
- """
- self.__buffer=""
- self.__outfp=os.tmpfile()
-
- def _append(self,s):
- """ append a string to the buffer """
- self.__buffer=self.__buffer+s
-
- def _flush(self):
- """ flush the buffer to wfile """
- self.wfile.write(self.__buffer)
- self.__outfp.write(self.__buffer)
- self.__outfp.flush()
- self.wfile.flush()
- self.__buffer=""
-
- def handle(self):
- """ Handle a HTTP request """
- self._init_buffer()
- BaseHTTPRequestHandler.handle(self)
- self._flush()
-
- def send_header(self, keyword, value):
- """Send a MIME header."""
- if self.request_version != 'HTTP/0.9':
- self._append("%s: %s\r\n" % (keyword, value))
-
- def end_headers(self):
- """Send the blank line ending the MIME headers."""
- if self.request_version != 'HTTP/0.9':
- self._append("\r\n")
-
- def send_response(self, code, message=None):
- self.log_request(code)
-
- if message is None:
- if self.responses.has_key(code):
- message = self.responses[code][0]
- else:
- message = ''
-
- if self.request_version != 'HTTP/0.9':
- self._append("%s %s %s\r\n" %
- (self.protocol_version, str(code), message))
-
- self.send_header('Server', self.version_string())
- self.send_header('Connection', 'close')
- self.send_header('Date', self.date_time_string())
-
- protocol_version="HTTP/1.1"
-
diff --git a/addons/document_webdav/DAV/WebDAVServer.py b/addons/document_webdav/DAV/WebDAVServer.py
deleted file mode 100644
index 5f409773c18..00000000000
--- a/addons/document_webdav/DAV/WebDAVServer.py
+++ /dev/null
@@ -1,379 +0,0 @@
-"""
- Python WebDAV Server.
- Copyright (C) 1999 Christian Scholz (ruebe@aachen.heimat.de)
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public
- License as published by the Free Software Foundation; either
- version 2 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with this library; if not, write to the Free
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-This module builds on AuthServer by implementing the standard DAV
-methods.
-
-Subclass this class and specify an IFACE_CLASS. See example.
-
-"""
-
-DEBUG=None
-
-from utils import VERSION, AUTHOR
-__version__ = VERSION
-__author__ = AUTHOR
-
-
-import os
-import sys
-import time
-import socket
-import string
-import posixpath
-import base64
-import urlparse
-import urllib
-
-from propfind import PROPFIND
-from delete import DELETE
-from davcopy import COPY
-from davmove import MOVE
-
-from string import atoi,split
-from status import STATUS_CODES
-from errors import *
-
-import BaseHTTPServer
-
-class DAVRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
- """Simple DAV request handler with
-
- - GET
- - HEAD
- - PUT
- - OPTIONS
- - PROPFIND
- - PROPPATCH
- - MKCOL
-
- It uses the resource/collection classes for serving and
- storing content.
-
- """
-
- server_version = "DAV/" + __version__
- protocol_version = 'HTTP/1.1'
-
- ### utility functions
- def _log(self, message):
- pass
-
- def _append(self,s):
- """ write the string to wfile """
- self.wfile.write(s)
-
- def send_body(self,DATA,code,msg,desc,ctype='application/octet-stream',headers=None):
- """ send a body in one part """
-
- if not headers:
- headers = {}
- self.send_response(code,message=msg)
- self.send_header("Connection", "keep-alive")
- self.send_header("Accept-Ranges", "bytes")
-
- for a,v in headers.items():
- self.send_header(a,v)
-
- if DATA:
- self.send_header("Content-Length", str(len(DATA)))
- self.send_header("Content-Type", ctype)
- else:
- self.send_header("Content-Length", "0")
-
- self.end_headers()
- if DATA:
- self._append(DATA)
-
- def send_body_chunks(self,DATA,code,msg,desc,ctype='text/xml; encoding="utf-8"'):
- """ send a body in chunks """
-
- self.responses[207]=(msg,desc)
- self.send_response(code,message=msg)
- self.send_header("Content-type", ctype)
- self.send_header("Connection", "keep-alive")
- self.send_header("Transfer-Encoding", "chunked")
- self.end_headers()
- self._append(hex(len(DATA))[2:]+"\r\n")
- self._append(DATA)
- self._append("\r\n")
- self._append("0\r\n")
- self._append("\r\n")
-
- ### HTTP METHODS
-
- def do_OPTIONS(self):
- """return the list of capabilities """
- self.send_response(200)
- self.send_header("Allow", "GET, HEAD, COPY, MOVE, POST, PUT, PROPFIND, PROPPATCH, OPTIONS, MKCOL, DELETE, TRACE")
- self.send_header("Content-Type", "text/plain")
- self.send_header("Connection", "keep-alive")
- self.send_header("DAV", "1")
- self.end_headers()
-
- def do_PROPFIND(self):
-
- dc=self.IFACE_CLASS
- # read the body
- body=None
- if self.headers.has_key("Content-Length"):
- l=self.headers['Content-Length']
- body=self.rfile.read(atoi(l))
- alt_body = """
-
-
-
-
-
-
-
-
-
- """
- #self.wfile.write(body)
-
- # which Depth?
- if self.headers.has_key('Depth'):
- d=self.headers['Depth']
- else:
- d="infinity"
-
- uri=self.geturi()
- pf=PROPFIND(uri,dc,d)
-
- if body:
- pf.read_propfind(body)
-
- try:
- DATA=pf.createResponse()
- DATA=DATA+"\n"
- # print "Data:", DATA
- except DAV_NotFound,(ec,dd):
- return self.send_notFound(dd, uri)
- except DAV_Error, (ec,dd):
- return self.send_error(ec,dd)
-
- self.send_body_chunks(DATA,207,"Multi-Status","Multiple responses")
-
- def geturi(self):
- buri = self.IFACE_CLASS.baseuri
- if buri[-1] == '/':
- return urllib.unquote(buri[:-1]+self.path)
- else:
- return urllib.unquote(buri+self.path)
-
- def do_GET(self):
- """Serve a GET request."""
- dc=self.IFACE_CLASS
- uri=self.geturi()
-
- # get the last modified date
- try:
- lm=dc.get_prop(uri,"DAV:","getlastmodified")
- except:
- lm="Sun, 01 Dec 2014 00:00:00 GMT" # dummy!
- headers={"Last-Modified":lm , "Connection": "keep-alive"}
-
- # get the content type
- try:
- ct=dc.get_prop(uri,"DAV:","getcontenttype")
- except:
- ct="application/octet-stream"
-
- # get the data
- try:
- data=dc.get_data(uri)
- except DAV_Error, (ec,dd):
- self.send_status(ec)
- return
-
- # send the data
- self.send_body(data,200,"OK","OK",ct,headers)
-
- def do_HEAD(self):
- """ Send a HEAD response """
- dc=self.IFACE_CLASS
- uri=self.geturi()
-
- # get the last modified date
- try:
- lm=dc.get_prop(uri,"DAV:","getlastmodified")
- except:
- lm="Sun, 01 Dec 2014 00:00:00 GMT" # dummy!
-
- headers={"Last-Modified":lm, "Connection": "keep-alive"}
-
- # get the content type
- try:
- ct=dc.get_prop(uri,"DAV:","getcontenttype")
- except:
- ct="application/octet-stream"
-
- try:
- data=dc.get_data(uri)
- headers["Content-Length"]=str(len(data))
- except DAV_NotFound:
- self.send_body(None,404,"Not Found","")
- return
-
- self.send_body(None,200,"OK","OK",ct,headers)
-
- def do_POST(self):
- self.send_error(404,"File not found")
-
- def do_MKCOL(self):
- """ create a new collection """
-
- dc=self.IFACE_CLASS
- uri=self.geturi()
- try:
- res = dc.mkcol(uri)
- if res:
- self.send_body(None,201,"Created",'')
- else:
- self.send_body(None,415,"Cannot create",'')
- #self.send_header("Connection", "keep-alive")
- # Todo: some content, too
- except DAV_Error, (ec,dd):
- self.send_body(None,int(ec),dd,dd)
-
- def do_DELETE(self):
- """ delete an resource """
- dc=self.IFACE_CLASS
- uri=self.geturi()
- dl=DELETE(uri,dc)
- if dc.is_collection(uri):
- res=dl.delcol()
- else:
- res=dl.delone()
-
- if res:
- self.send_status(207,body=res)
- else:
- self.send_status(204)
-
- def do_PUT(self):
- dc=self.IFACE_CLASS
-
- # read the body
- body=None
- if self.headers.has_key("Content-Length"):
- l=self.headers['Content-Length']
- body=self.rfile.read(atoi(l))
- uri=self.geturi()
-
- ct=None
- if self.headers.has_key("Content-Type"):
- ct=self.headers['Content-Type']
- try:
- dc.put(uri,body,ct)
- except DAV_Error, (ec,dd):
- self.send_status(ec)
- return
- self.send_status(201)
-
- def do_COPY(self):
- """ copy one resource to another """
- try:
- self.copymove(COPY)
- except DAV_Error, (ec,dd):
- self.send_status(ec)
-
- def do_MOVE(self):
- """ move one resource to another """
- try:
- self.copymove(MOVE)
- except DAV_Error, (ec,dd):
- self.send_status(ec)
-
- def copymove(self,CLASS):
- """ common method for copying or moving objects """
- dc=self.IFACE_CLASS
-
- # get the source URI
- source_uri=self.geturi()
-
- # get the destination URI
- dest_uri=self.headers['Destination']
- dest_uri=urllib.unquote(dest_uri)
-
- # Overwrite?
- overwrite=1
- result_code=204
- if self.headers.has_key("Overwrite"):
- if self.headers['Overwrite']=="F":
- overwrite=None
- result_code=201
-
- # instanciate ACTION class
- cp=CLASS(dc,source_uri,dest_uri,overwrite)
-
- # Depth?
- d="infinity"
- if self.headers.has_key("Depth"):
- d=self.headers['Depth']
-
- if d!="0" and d!="infinity":
- self.send_status(400)
- return
-
- if d=="0":
- res=cp.single_action()
- self.send_status(res)
- return
-
- # now it only can be "infinity" but we nevertheless check for a collection
- if dc.is_collection(source_uri):
- try:
- res=cp.tree_action()
- except DAV_Error, (ec,dd):
- self.send_status(ec)
- return
- else:
- try:
- res=cp.single_action()
- except DAV_Error, (ec,dd):
- self.send_status(ec)
- return
-
- if res:
- self.send_body_chunks(res,207,STATUS_CODES[207],STATUS_CODES[207],
- ctype='text/xml; charset="utf-8"')
- else:
- self.send_status(result_code)
-
- def get_userinfo(self,user,pw):
- """ Dummy method which lets all users in """
-
- return 1
-
- def send_status(self,code=200,mediatype='text/xml; charset="utf-8"', \
- msg=None,body=None):
-
- if not msg: msg=STATUS_CODES[code]
- self.send_body(body,code,STATUS_CODES[code],msg,mediatype)
-
- def send_notFound(self,descr,uri):
- body = """
-
- %s
-
- %s
-
- """
- return self.send_status(404,descr, body=body % (uri,descr))
diff --git a/addons/document_webdav/DAV/__init__.py b/addons/document_webdav/DAV/__init__.py
deleted file mode 100644
index 10abdb40014..00000000000
--- a/addons/document_webdav/DAV/__init__.py
+++ /dev/null
@@ -1,20 +0,0 @@
-"""
- python davserver
- Copyright (C) 1999 Christian Scholz (ruebe@aachen.heimat.de)
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public
- License as published by the Free Software Foundation; either
- version 2 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with this library; if not, write to the Free
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-"""
-
-
diff --git a/addons/document_webdav/DAV/constants.py b/addons/document_webdav/DAV/constants.py
deleted file mode 100644
index e858ac519c2..00000000000
--- a/addons/document_webdav/DAV/constants.py
+++ /dev/null
@@ -1,18 +0,0 @@
-"""
-
-constants definition
-
-
-"""
-
-# definition for resourcetype
-COLLECTION=1
-OBJECT=None
-DAV_PROPS=['creationdate', 'displayname', 'getcontentlanguage', 'getcontentlength', 'getcontenttype', 'getetag', 'getlastmodified', 'lockdiscovery', 'resourcetype', 'source', 'supportedlock']
-
-
-# Request classes in propfind
-
-RT_ALLPROP=1
-RT_PROPNAME=2
-RT_PROP=3
diff --git a/addons/document_webdav/DAV/davcmd.py b/addons/document_webdav/DAV/davcmd.py
deleted file mode 100644
index 5c6be3ff9f7..00000000000
--- a/addons/document_webdav/DAV/davcmd.py
+++ /dev/null
@@ -1,218 +0,0 @@
-"""
-
-davcmd.py
----------
-
-containts commands like copy, move, delete for normal
-resources and collections
-
-"""
-
-from string import split,replace,joinfields
-import urlparse
-
-from utils import create_treelist, is_prefix
-from errors import *
-
-def deltree(dc,uri,exclude={}):
- """ delete a tree of resources
-
- dc -- dataclass to use
- uri -- root uri to delete
- exclude -- an optional list of uri:error_code pairs which should not
- be deleted.
-
- returns dict of uri:error_code tuples from which
- another method can create a multistatus xml element.
-
- Also note that we only know Depth=infinity thus we don't have
- to test for it.
-
- """
-
- tlist=create_treelist(dc,uri)
- result={}
-
- for i in range(len(tlist),0,-1):
- problem_uris=result.keys()
- element=tlist[i-1]
-
- # test here, if an element is a prefix of an uri which
- # generated an error before.
- # note that we walk here from childs to parents, thus
- # we cannot delete a parent if a child made a problem.
- # (see example in 8.6.2.1)
- ok=1
- for p in problem_uris:
- if is_prefix(element,p):
- ok=None
- break
-
- if not ok: continue
-
- # here we test for the exclude list which is the other way round!
- for p in exclude.keys():
- if is_prefix(p,element):
- ok=None
- break
-
- if not ok: continue
-
- # now delete stuff
- try:
- delone(dc,element)
- except DAV_Error, (ec,dd):
- result[element]=ec
-
- return result
-
-def delone(dc,uri):
- """ delete a single object """
- if dc.is_collection(uri):
- dc.rmcol(uri) # should be empty
- else:
- dc.rm(uri)
-
-###
-### COPY
-###
-
-# helper function
-
-def copy(dc,src,dst):
- """ only copy the element
-
- This is just a helper method factored out from copy and
- copytree. It will not handle the overwrite or depth header.
-
- """
-
- # destination should have been deleted before
- if dc.exists(dst): raise DAV_Error, 412
-
- # source should exist also
- if not dc.exists(src): raise DAV_NotFound
-
- if dc.is_collection(src):
- dc.copycol(src,dst) # an exception will be passed thru
- else:
- dc.copy(src,dst) # an exception will be passed thru
-
-
-# the main functions
-
-def copyone(dc,src,dst,overwrite=None):
- """ copy one resource to a new destination """
-
- if overwrite and dc.exists(dst):
- delres=deltree(dc,dst)
- else:
- delres={}
-
- # if we cannot delete everything, then do not copy!
- if delres: return delres
-
- try:
- copy(dc,src,dst) # pass thru exceptions
- except DAV_Error, (ec,dd):
- return ec
-
-def copytree(dc,src,dst,overwrite=None):
- """ copy a tree of resources to another location
-
- dc -- dataclass to use
- src -- src uri from where to copy
- dst -- dst uri
- overwrite -- if 1 then delete dst uri before
-
- returns dict of uri:error_code tuples from which
- another method can create a multistatus xml element.
-
- """
-
-
- # first delete the destination resource
- if overwrite and dc.exists(dst):
- delres=deltree(dc,dst)
- else:
- delres={}
-
- # if we cannot delete everything, then do not copy!
- if delres: return delres
-
- # get the tree we have to copy
- tlist=create_treelist(dc,src)
- result={}
-
- # prepare destination URIs (get the prefix)
- dpath=urlparse.urlparse(dst)[2]
-
- for element in tlist:
- problem_uris=result.keys()
-
- # now URIs get longer and longer thus we have
- # to test if we had a parent URI which we were not
- # able to copy in problem_uris which is the prefix
- # of the actual element. If it is, then we cannot
- # copy this as well but do not generate another error.
- ok=1
- for p in problem_uris:
- if is_prefix(p,element):
- ok=None
- break
-
- if not ok: continue
-
- # now create the destination URI which corresponds to
- # the actual source URI. -> actual_dst
- # ("subtract" the base src from the URI and prepend the
- # dst prefix to it.)
- esrc=replace(element,src,"")
- actual_dst=dpath+esrc
-
- # now copy stuff
- try:
- copy(dc,element,actual_dst)
- except DAV_Error, (ec,dd):
- result[element]=ec
-
- return result
-
-
-
-###
-### MOVE
-###
-
-
-def moveone(dc,src,dst,overwrite=None):
- """ move a single resource
-
- This is done by first copying it and then deleting
- the original.
- """
-
- # first copy it
- copyone(dc,src,dst,overwrite)
-
- # then delete it
- dc.rm(src)
-
-def movetree(dc,src,dst,overwrite=None):
- """ move a collection
-
- This is done by first copying it and then deleting
- the original.
-
- PROBLEM: if something did not copy then we have a problem
- when deleting as the original might get deleted!
- """
-
- # first copy it
- res=copytree(dc,src,dst,overwrite)
-
- # then delete it
- res=deltree(dc,src,exclude=res)
-
- return res
-
diff --git a/addons/document_webdav/DAV/davcopy.py b/addons/document_webdav/DAV/davcopy.py
deleted file mode 100755
index ec44931b107..00000000000
--- a/addons/document_webdav/DAV/davcopy.py
+++ /dev/null
@@ -1,133 +0,0 @@
-#!/usr/bin/env python
-
-"""
- python davserver
- Copyright (C) 1999 Christian Scholz (ruebe@aachen.heimat.de)
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public
- License as published by the Free Software Foundation; either
- version 2 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with this library; if not, write to the Free
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-
-"""
-
-
-from xml.dom import ext
-from xml.dom.Document import Document
-
-import sys
-import string
-import urlparse
-import urllib
-from StringIO import StringIO
-
-import utils
-from constants import COLLECTION, OBJECT, DAV_PROPS, RT_ALLPROP, RT_PROPNAME, RT_PROP
-from errors import *
-from utils import create_treelist, quote_uri, gen_estring
-
-class COPY:
- """ copy resources and eventually create multistatus responses
-
- This module implements the COPY class which is responsible for
- copying resources. Usually the normal copy work is done in the
- interface class. This class only creates error messages if error
- occur.
-
- """
-
-
- def __init__(self,dataclass,src_uri,dst_uri,overwrite):
- self.__dataclass=dataclass
- self.__src=src_uri
- self.__dst=dst_uri
- self.__overwrite=overwrite
-
-
- def single_action(self):
- """ copy a normal resources.
-
- We try to copy it and return the result code.
- This is for Depth==0
-
- """
-
- dc=self.__dataclass
- base=self.__src
-
- ### some basic tests
- # test if dest exists and overwrite is false
- if dc.exists(self.__dst) and not self.__overwrite: raise DAV_Error, 412
- # test if src and dst are the same
- # (we assume that both uris are on the same server!)
- ps=urlparse.urlparse(self.__src)[2]
- pd=urlparse.urlparse(self.__dst)[2]
- if ps==pd: raise DAV_Error, 403
-
- return dc.copyone(self.__src,self.__dst,self.__overwrite)
-
- #return copyone(dc,self.__src,self.__dst,self.__overwrite)
-
- def tree_action(self):
- """ copy a tree of resources (a collection)
-
- Here we return a multistatus xml element.
-
- """
- dc=self.__dataclass
- base=self.__src
-
- ### some basic tests
- # test if dest exists and overwrite is false
- if dc.exists(self.__dst) and not self.__overwrite: raise DAV_Error, 412
- # test if src and dst are the same
- # (we assume that both uris are on the same server!)
- ps=urlparse.urlparse(self.__src)[2]
- pd=urlparse.urlparse(self.__dst)[2]
- if ps==pd: raise DAV_Error, 403
-
-
- result=dc.copytree(self.__src,self.__dst,self.__overwrite)
- #result=copytree(dc,self.__src,self.__dst,self.__overwrite)
-
- if not result: return None
-
- ###
- ### create the multistatus XML element
- ### (this is also the same as in delete.py.
- ### we might make a common method out of it)
- ###
-
- doc = Document(None)
- ms=doc.createElement("D:multistatus")
- ms.setAttribute("xmlns:D","DAV:")
- doc.appendChild(ms)
-
- for el,ec in result.items():
- re=doc.createElement("D:response")
- hr=doc.createElement("D:href")
- st=doc.createElement("D:status")
- huri=doc.createTextNode(quote_uri(el))
- t=doc.createTextNode(gen_estring(ec))
- st.appendChild(t)
- hr.appendChild(huri)
- re.appendChild(hr)
- re.appendChild(st)
- ms.appendChild(re)
-
- sfile=StringIO()
- ext.PrettyPrint(doc,stream=sfile)
- s=sfile.getvalue()
- sfile.close()
- return s
-
diff --git a/addons/document_webdav/DAV/davmove.py b/addons/document_webdav/DAV/davmove.py
deleted file mode 100755
index 78c1e649389..00000000000
--- a/addons/document_webdav/DAV/davmove.py
+++ /dev/null
@@ -1,102 +0,0 @@
-#!/usr/bin/env python
-
-"""
- python davserver
- Copyright (C) 1999 Christian Scholz (ruebe@aachen.heimat.de)
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public
- License as published by the Free Software Foundation; either
- version 2 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with this library; if not, write to the Free
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-
-"""
-
-
-import sys
-import string
-import urlparse
-import urllib
-from StringIO import StringIO
-
-import utils
-from constants import COLLECTION, OBJECT, DAV_PROPS
-from constants import RT_ALLPROP, RT_PROPNAME, RT_PROP
-from errors import *
-from utils import create_treelist, quote_uri, gen_estring, make_xmlresponse
-from davcmd import moveone, movetree
-
-class MOVE:
- """ move resources and eventually create multistatus responses
-
- This module implements the MOVE class which is responsible for
- moving resources.
-
- MOVE is implemented by a COPY followed by a DELETE of the old
- resource.
-
- """
-
-
- def __init__(self,dataclass,src_uri,dst_uri,overwrite):
- self.__dataclass=dataclass
- self.__src=src_uri
- self.__dst=dst_uri
- self.__overwrite=overwrite
-
-
- def single_action(self):
- """ move a normal resources.
-
- We try to move it and return the result code.
- This is for Depth==0
-
- """
-
- dc=self.__dataclass
- base=self.__src
-
- ### some basic tests
- # test if dest exists and overwrite is false
- if dc.exists(self.__dst) and not self.__overwrite: raise DAV_Error, 412
- # test if src and dst are the same
- # (we assume that both uris are on the same server!)
- ps=urlparse.urlparse(self.__src)[2]
- pd=urlparse.urlparse(self.__dst)[2]
- if ps==pd: raise DAV_Error, 403
-
- return dc.moveone(self.__src,self.__dst,self.__overwrite)
-
- def tree_action(self):
- """ move a tree of resources (a collection)
-
- Here we return a multistatus xml element.
-
- """
- dc=self.__dataclass
- base=self.__src
-
- ### some basic tests
- # test if dest exists and overwrite is false
- if dc.exists(self.__dst) and not self.__overwrite: raise DAV_Error, 412
- # test if src and dst are the same
- # (we assume that both uris are on the same server!)
- ps=urlparse.urlparse(self.__src)[2]
- pd=urlparse.urlparse(self.__dst)[2]
- if ps==pd: raise DAV_Error, 403
-
- result=dc.movetree(self.__src,self.__dst,self.__overwrite)
- if not result: return None
-
- # create the multistatus XML element
- return make_xmlresponse(result)
-
diff --git a/addons/document_webdav/DAV/delete.py b/addons/document_webdav/DAV/delete.py
deleted file mode 100755
index ed017e53b6d..00000000000
--- a/addons/document_webdav/DAV/delete.py
+++ /dev/null
@@ -1,63 +0,0 @@
-#!/usr/bin/env python
-
-"""
-
- python davserver
- Copyright (C) 1999 Christian Scholz (ruebe@aachen.heimat.de)
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public
- License as published by the Free Software Foundation; either
- version 2 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with this library; if not, write to the Free
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-
-"""
-import os
-import string
-import urllib
-from StringIO import StringIO
-
-from status import STATUS_CODES
-from utils import gen_estring, quote_uri, make_xmlresponse
-from davcmd import deltree
-
-class DELETE:
-
- def __init__(self,uri,dataclass):
- self.__dataclass=dataclass
- self.__uri=uri
-
- def delcol(self):
- """ delete a collection """
-
- dc=self.__dataclass
- result=dc.deltree(self.__uri)
-
- if not len(result.items()):
- return None # everything ok
-
- # create the result element
- return make_xmlresponse(result)
-
- def delone(self):
- """ delete a resource """
-
- dc=self.__dataclass
- result=dc.delone(self.__uri)
-
- if not result: return None
- if not len(result.items()):
- return None # everything ok
-
- # create the result element
- return make_xmlresponse(result)
-
diff --git a/addons/document_webdav/DAV/errors.py b/addons/document_webdav/DAV/errors.py
deleted file mode 100755
index 9c0e7667913..00000000000
--- a/addons/document_webdav/DAV/errors.py
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/usr/bin/env python
-"""
-
- Exceptions for the DAVserver implementation
-
-"""
-
-class DAV_Error(Exception):
- """ in general we can have the following arguments:
-
- 1. the error code
- 2. the error result element, e.g. a element
- """
-
- def __init__(self,*args):
- if len(args)==1:
- self.args=(args[0],"")
- else:
- self.args=args
-
-class DAV_Secret(DAV_Error):
- """ the user is not allowed to know anything about it
-
- returning this for a property value means to exclude it
- from the response xml element.
- """
-
- def __init__(self):
- DAV_Error.__init__(self,0)
- pass
-
-class DAV_NotFound(DAV_Error):
- """ a requested property was not found for a resource """
-
- def __init__(self,*args):
- if len(args):
- if isinstance(args[0],list):
- stre = "Path %s not found!"%('/'.join(args[0]))
- else:
- stre = args[0]
- DAV_Error.__init__(self,404,stre)
- else:
- DAV_Error.__init__(self,404)
-
- pass
-
-class DAV_Forbidden(DAV_Error):
- """ a method on a resource is not allowed """
-
- def __init__(self,*args):
- if len(args):
- DAV_Error.__init__(self,403,args[0])
- else:
- DAV_Error.__init__(self,403)
- pass
-
diff --git a/addons/document_webdav/DAV/iface.py b/addons/document_webdav/DAV/iface.py
deleted file mode 100644
index c8f5fe2594d..00000000000
--- a/addons/document_webdav/DAV/iface.py
+++ /dev/null
@@ -1,263 +0,0 @@
-"""
-
-basic interface class
-
-use this for subclassing when writing your own interface
-class.
-
-"""
-
-from errors import *
-
-import time
-from string import lower
-
-class dav_interface:
- """ interface class for implementing DAV servers """
-
- ### defined properties (modify this but let the DAV stuff there!)
- ### the format is namespace: [list of properties]
-
- PROPS={"DAV:" : ('creationdate',
- 'displayname',
- 'getcontentlanguage',
- 'getcontentlength',
- 'getcontenttype',
- 'getetag',
- 'getlastmodified',
- 'lockdiscovery',
- 'resourcetype',
- 'source',
- 'supportedlock'),
- "NS2" : ("p1","p2")
- }
-
- # here we define which methods handle which namespace
- # the first item is the namespace URI and the second one
- # the method prefix
- # e.g. for DAV:getcontenttype we call dav_getcontenttype()
- M_NS={"DAV:" : "_get_dav",
- "NS2" : "ns2" }
-
- def get_propnames(self,uri):
- """ return the property names allowed for the given URI
-
- In this method we simply return the above defined properties
- assuming that they are valid for any resource.
- You can override this in order to return a different set
- of property names for each resource.
-
- """
- return self.PROPS
-
- def get_prop2(self,uri,ns,pname):
- """ return the value of a property
- """
- if lower(ns)=="dav:": return self.get_dav(uri,pname)
-
- raise DAV_NotFound
-
- def get_prop(self,uri,ns,propname):
- """ return the value of a given property
-
- uri -- uri of the object to get the property of
- ns -- namespace of the property
- pname -- name of the property
- """
- if self.M_NS.has_key(ns):
- prefix=self.M_NS[ns]
- else:
- print "No namespace:",ns, "( for prop:", propname,")"
- raise DAV_NotFound
- mname=prefix+"_"+propname
- if not hasattr(self,mname):
- raise DAV_NotFound
-
- try:
- m=getattr(self,mname)
- r=m(uri)
- return r
- except AttributeError, e:
- print 'Property %s not supported' % propname
- print "Exception:", e
- raise DAV_NotFound
-
- ###
- ### DATA methods (for GET and PUT)
- ###
-
- def get_data(self,uri):
- """ return the content of an object
-
- return data or raise an exception
-
- """
- raise DAV_NotFound
-
- def put(self,uri,data):
- """ write an object to the repository
-
- return a result code or raise an exception
- """
-
- raise DAV_Forbidden
-
- ###
- ### Methods for DAV properties
- ###
-
- def _get_dav_creationdate(self,uri):
- """ return the creationdate of a resource """
- d=self.get_creationdate(uri)
- # format it
- if isinstance(d, int) or isinstance(d, float):
- d = time.localtimetime(d)
- return time.strftime("%Y-%m-%dT%H:%M:%S%Z",d)
-
- def _get_dav_getlastmodified(self,uri):
- """ return the last modified date of a resource """
- d=self.get_lastmodified(uri)
- if isinstance(d, int) or isinstance(d, float):
- d = time.localtime(d)
- # format it
- return time.asctime(d)
-
-
- ###
- ### OVERRIDE THESE!
- ###
-
- def get_creationdate(self,uri):
- """ return the creationdate of the resource """
- return time.time()
-
- def get_lastmodified(self,uri):
- """ return the last modification date of the resource """
- return time.time()
-
-
- ###
- ### COPY MOVE DELETE
- ###
-
- ### methods for deleting a resource
-
- def rmcol(self,uri):
- """ delete a collection
-
- This should not delete any children! This is automatically done
- before by the DELETE class in DAV/delete.py
-
- return a success code or raise an exception
-
- """
- raise DAV_NotFound
-
- def rm(self,uri):
- """ delete a single resource
-
- return a success code or raise an exception
-
- """
- raise DAV_NotFound
-
- """
-
- COPY/MOVE HANDLER
-
- These handler are called when a COPY or MOVE method is invoked by
- a client. In the default implementation it works as follows:
-
- - the davserver receives a COPY/MOVE method
- - the davcopy or davmove module will be loaded and the corresponding
- class will be initialized
- - this class parses the query and decides which method of the interface class
- to call:
-
- copyone for a single resource to copy
- copytree for a tree to copy (collection)
- (the same goes for move of course).
-
- - the interface class has now two options:
- 1. to handle the action directly (e.g. cp or mv on filesystems)
- 2. to let it handle via the copy/move methods in davcmd.
-
- ad 1) The first approach can be used when we know that no error can
- happen inside a tree or when the action can exactly tell which
- element made which error. We have to collect these and return
- it in a dict of the form {uri: error_code, ...}
-
- ad 2) The copytree/movetree/... methods of davcmd.py will do the recursion
- themselves and call for each resource the copy/move method of the
- interface class. Thus method will then only act on a single resource.
- (Thus a copycol on a normal unix filesystem actually only needs to do
- an mkdir as the content will be copied by the davcmd.py function.
- The davcmd.py method will also automatically collect all errors and
- return the dictionary described above.
- When you use 2) you also have to implement the copy() and copycol()
- methods in your interface class. See the example for details.
-
- To decide which approach is the best you have to decide if your application
- is able to generate errors inside a tree. E.g. a function which completely
- fails on a tree if one of the tree's childs fail is not what we need. Then
- 2) would be your way of doing it.
- Actually usually 2) is the better solution and should only be replaced by
- 1) if you really need it.
-
- The remaining question is if we should do the same for the DELETE method.
-
- """
-
- ### MOVE handlers
-
- def moveone(self,src,dst,overwrite):
- """ move one resource with Depth=0 """
- return moveone(self,src,dst,overwrite)
-
- def movetree(self,src,dst,overwrite):
- """ move a collection with Depth=infinity """
- return movetree(self,src,dst,overwrite)
-
- ### COPY handlers
-
- def copyone(self,src,dst,overwrite):
- """ copy one resource with Depth=0 """
- return copyone(self,src,dst,overwrite)
-
- def copytree(self,src,dst,overwrite):
- """ copy a collection with Depth=infinity """
- return copytree(self,src,dst,overwrite)
-
-
- ### low level copy methods (you only need these for method 2)
- def copy(self,src,dst):
- """ copy a resource with depth==0
-
- You don't need to bother about overwrite or not.
- This has been done already.
-
- return a success code or raise an exception if something fails
- """
- return 201
-
-
- def copycol(self,src,dst):
- """ copy a resource with depth==infinity
-
- You don't need to bother about overwrite or not.
- This has been done already.
-
- return a success code or raise an exception if something fails
- """
- return 201
-
- ### some utility functions you need to implement
-
- def exists(self,uri):
- """ return 1 or None depending on if a resource exists """
- return None # no
-
- def is_collection(self,uri):
- """ return 1 or None depending on if a resource is a collection """
- return None # no
-
diff --git a/addons/document_webdav/DAV/propfind.py b/addons/document_webdav/DAV/propfind.py
deleted file mode 100755
index 0fb9c229c03..00000000000
--- a/addons/document_webdav/DAV/propfind.py
+++ /dev/null
@@ -1,372 +0,0 @@
-#!/usr/bin/env python
-
-"""
- python davserver
- Copyright (C) 1999 Christian Scholz (ruebe@aachen.heimat.de)
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public
- License as published by the Free Software Foundation; either
- version 2 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
-
- You should have received a copy of the GNU Library General Public
- License along with this library; if not, write to the Free
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-"""
-
-
-from xml.dom import ext
-from xml.dom.Document import Document
-
-import sys
-import string
-import urlparse
-import urllib
-from StringIO import StringIO
-
-import utils
-from constants import COLLECTION, OBJECT, DAV_PROPS, RT_ALLPROP, RT_PROPNAME, RT_PROP
-from errors import *
-
-def utf8str(st):
- if isinstance(st,unicode):
- return st.encode('utf8')
- else:
- return str(st)
-
-class PROPFIND:
- """ parse a propfind xml element and extract props
-
- It will set the following instance vars:
-
- request_class : ALLPROP | PROPNAME | PROP
- proplist : list of properties
- nsmap : map of namespaces
-
- The list of properties will contain tuples of the form
- (element name, ns_prefix, ns_uri)
-
-
- """
-
-
- def __init__(self,uri,dataclass,depth):
- self.request_type=None
- self.nsmap={}
- self.proplist={}
- self.default_ns=None
- self.__dataclass=dataclass
- self.__depth=str(depth)
- self.__uri=uri
- self.use_full_urls=True
- self.__has_body=None # did we parse a body?
-
- def read_propfind(self,xml_doc):
- self.request_type,self.proplist,self.namespaces=utils.parse_propfind(xml_doc)
-
- # a violation of the expected logic: client (korganizer) will ask for DAV:resourcetype
- # but we also have to return the http://groupdav.org/:resourcetype property!
- if self.proplist.has_key('DAV:') and 'resourcetype' in self.proplist['DAV:']:
- if not self.proplist.has_key('http://groupdav.org/'):
- self.proplist['http://groupdav.org/'] = []
- self.proplist['http://groupdav.org/'].append('resourcetype')
- if 'DAV:' in self.namespaces: #TMP
- self.namespaces.append('http://groupdav.org/')
-
- def createResponse(self):
- """ create the multistatus response
-
- This will be delegated to the specific method
- depending on which request (allprop, propname, prop)
- was found.
-
- If we get a PROPNAME then we simply return the list with empty
- values which we get from the interface class
-
- If we get an ALLPROP we first get the list of properties and then
- we do the same as with a PROP method.
-
- If the uri doesn't exist, return an xml response with a 404 status
-
- """
-
- if not self.__dataclass.exists(self.__uri):
- raise DAV_NotFound("Path %s doesn't exist" % self.__uri)
-
- if self.request_type==RT_ALLPROP:
- return self.create_allprop()
-
- if self.request_type==RT_PROPNAME:
- return self.create_propname()
-
- if self.request_type==RT_PROP:
- return self.create_prop()
-
- # no body means ALLPROP!
- return self.create_allprop()
-
- def create_propname(self):
- """ create a multistatus response for the prop names """
-
- dc=self.__dataclass
- # create the document generator
- doc = Document(None)
- ms=doc.createElement("D:multistatus")
- ms.setAttribute("xmlns:D","DAV:")
- doc.appendChild(ms)
-
- if self.__depth=="0":
- pnames=dc.get_propnames(self.__uri)
- re=self.mk_propname_response(self.__uri,pnames,doc)
- ms.appendChild(re)
-
- elif self.__depth=="1":
- pnames=dc.get_propnames(self.__uri)
- re=self.mk_propname_response(self.__uri,pnames,doc)
- ms.appendChild(re)
-
- for newuri in dc.get_childs(self.__uri):
- pnames=dc.get_propnames(newuri)
- re=self.mk_propname_response(newuri,pnames,doc)
- ms.appendChild(re)
- # *** depth=="infinity"
-
- sfile=StringIO()
- ext.PrettyPrint(doc,stream=sfile)
- s=sfile.getvalue()
- sfile.close()
- return s
-
- def create_allprop(self):
- """ return a list of all properties """
- self.proplist={}
- self.namespaces=[]
- for ns,plist in self.__dataclass.get_propnames(self.__uri).items():
- self.proplist[ns]=plist
- self.namespaces.append(ns)
-
- return self.create_prop()
-
- def create_prop(self):
- """ handle a request
-
- This will
-
- 1. set up the -Framework
-
- 2. read the property values for each URI
- (which is dependant on the Depth header)
- This is done by the get_propvalues() method.
-
- 3. For each URI call the append_result() method
- to append the actual -Tag to the result
- document.
-
- We differ between "good" properties, which have been
- assigned a value by the interface class and "bad"
- properties, which resulted in an error, either 404
- (Not Found) or 403 (Forbidden).
-
- """
-
-
- # create the document generator
- doc = Document(None)
- ms=doc.createElement("D:multistatus")
- ms.setAttribute("xmlns:D","DAV:")
- doc.appendChild(ms)
-
- if self.__depth=="0":
- gp,bp=self.get_propvalues(self.__uri)
- res=self.mk_prop_response(self.__uri,gp,bp,doc)
- ms.appendChild(res)
-
- elif self.__depth=="1":
- gp,bp=self.get_propvalues(self.__uri)
- res=self.mk_prop_response(self.__uri,gp,bp,doc)
- ms.appendChild(res)
-
- try:
- for newuri in self.__dataclass.get_childs(self.__uri):
- gp,bp=self.get_propvalues(newuri)
- res=self.mk_prop_response(newuri,gp,bp,doc)
- ms.appendChild(res)
- except DAV_NotFound:
- # If no children, never mind.
- pass
-
- sfile=StringIO()
- ext.PrettyPrint(doc,stream=sfile)
- s=sfile.getvalue()
- sfile.close()
- return s
-
-
- def mk_propname_response(self,uri,propnames,doc):
- """ make a new result element for a PROPNAME request
-
- This will simply format the propnames list.
- propnames should have the format {NS1 : [prop1, prop2, ...], NS2: ...}
-
- """
- re=doc.createElement("D:response")
-
- # write href information
- href=doc.createElement("D:href")
- if self.use_full_urls:
- huri=doc.createTextNode(uri)
- else:
- uparts=urlparse.urlparse(uri)
- fileloc=uparts[2]
- huri=doc.createTextNode(urllib.quote(fileloc.encode('utf8')))
- href.appendChild(huri)
- re.appendChild(href)
-
- ps=doc.createElement("D:propstat")
- nsnum=0
-
- for ns,plist in propnames.items():
- # write prop element
- pr=doc.createElement("D:prop")
- nsp="ns"+str(nsnum)
- pr.setAttribute("xmlns:"+nsp,ns)
- nsnum=nsnum+1
-
- # write propertynames
- for p in plist:
- pe=doc.createElement(nsp+":"+p)
- pr.appendChild(pe)
-
- ps.appendChild(pr)
-
- re.appendChild(ps)
-
- return re
-
- def mk_prop_response(self,uri,good_props,bad_props,doc):
- """ make a new result element
-
- We differ between the good props and the bad ones for
- each generating an extra -Node (for each error
- one, that means).
-
- """
- re=doc.createElement("D:response")
- # append namespaces to response
- nsnum=0
- for nsname in self.namespaces:
- re.setAttribute("xmlns:ns"+str(nsnum),nsname)
- nsnum=nsnum+1
-
- # write href information
- href=doc.createElement("D:href")
- if self.use_full_urls:
- huri=doc.createTextNode(uri)
- else:
- uparts=urlparse.urlparse(uri)
- fileloc=uparts[2]
- huri=doc.createTextNode(urllib.quote(fileloc.encode('utf8')))
- href.appendChild(huri)
- re.appendChild(href)
-
- # write good properties
- if good_props and len(good_props.items()):
- ps=doc.createElement("D:propstat")
-
- gp=doc.createElement("D:prop")
- for ns in good_props.keys():
- ns_prefix="ns"+str(self.namespaces.index(ns))+":"
- for p,v in good_props[ns].items():
- pe=doc.createElement(ns_prefix+str(p))
- if v == None:
- pass
- elif ns=='DAV:' and p=="resourcetype":
- if v == 1:
- ve=doc.createElement("D:collection")
- pe.appendChild(ve)
- elif isinstance(v,tuple) and v[1] == ns:
- ve=doc.createElement(ns_prefix+v[0])
- pe.appendChild(ve)
- else:
- ve=doc.createTextNode(utf8str(v))
- pe.appendChild(ve)
-
- gp.appendChild(pe)
- if gp.hasChildNodes():
- re.appendChild(ps)
- 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!
- if len(bad_props.items()):
-
- # write a propstat for each error code
- for ecode in bad_props.keys():
- ps=doc.createElement("D:propstat")
- re.appendChild(ps)
- bp=doc.createElement("D:prop")
- ps.appendChild(bp)
-
- for ns in bad_props[ecode].keys():
- ns_prefix="ns"+str(self.namespaces.index(ns))+":"
-
- for p in bad_props[ecode][ns]:
- 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
- return re
-
- def get_propvalues(self,uri):
- """ create lists of property values for an URI
-
- We create two lists for an URI: the properties for
- which we found a value and the ones for which we
- only got an error, either because they haven't been
- found or the user is not allowed to read them.
-
- """
- good_props={}
- bad_props={}
-
- for (ns,plist) in self.proplist.items():
- good_props[ns]={}
- bad_props={}
- ec = 0
- for prop in plist:
- try:
- ec = 0
- r=self.__dataclass.get_prop(uri,ns,prop)
- good_props[ns][prop]=r
- except DAV_Error, error_code:
- ec=error_code[0]
-
- # ignore props with error_code if 0 (invisible)
- if ec==0: continue
-
- if bad_props.has_key(ec):
- if bad_props[ec].has_key(ns):
- bad_props[ec][ns].append(prop)
- else:
- bad_props[ec][ns]=[prop]
- else:
- bad_props[ec]={ns:[prop]}
-
- return good_props, bad_props
-
diff --git a/addons/document_webdav/DAV/status.py b/addons/document_webdav/DAV/status.py
deleted file mode 100644
index 62cb323c4fb..00000000000
--- a/addons/document_webdav/DAV/status.py
+++ /dev/null
@@ -1,31 +0,0 @@
-"""
-
-status codes for DAV services
-
-
-"""
-
-
-STATUS_CODES={
- 102: "Processing",
- 200: "Ok",
- 201: "Created",
- 204: "No Content",
- 207: "Multi-Status",
- 201: "Created",
- 400: "Bad Request",
- 403: "Forbidden",
- 404: "Not Found",
- 405: "Method Not Allowed",
- 409: "Conflict",
- 412: "Precondition failed",
- 423: "Locked",
- 415: "Unsupported Media Type",
- 507: "Insufficient Storage",
- 422: "Unprocessable Entity",
- 423: "Locked",
- 424: "Failed Dependency",
- 502: "Bad Gateway",
- 507: "Insufficient Storage",
- 999: "Some error in Create Method please check the data of create method"
-}
diff --git a/addons/document_webdav/DAV/utils.py b/addons/document_webdav/DAV/utils.py
deleted file mode 100755
index c4bb4d1a4c0..00000000000
--- a/addons/document_webdav/DAV/utils.py
+++ /dev/null
@@ -1,160 +0,0 @@
-#!/usr/bin/env python
-
-"""
-
-UTILITIES
-
-- parse a propfind request body into a list of props
-
-"""
-
-from xml.dom import ext
-from xml.dom.Document import Document
-from xml.dom.ext.reader import PyExpat
-from xml.dom import Node
-from xml.dom import NodeIterator, NodeFilter
-
-from string import lower, split, atoi, joinfields
-import urlparse
-from StringIO import StringIO
-
-from constants import RT_ALLPROP, RT_PROPNAME, RT_PROP
-from status import STATUS_CODES
-
-VERSION = '0.6'
-AUTHOR = 'Simon Pamies '
-
-
-def gen_estring(ecode):
- """ generate an error string from the given code """
- ec=atoi(str(ecode))
- if STATUS_CODES.has_key(ec):
- return "HTTP/1.1 %s %s" %(ec,STATUS_CODES[ec])
- else:
- return "HTTP/1.1 %s" %(ec)
-
-def parse_propfind(xml_doc):
- """ parse an propfind xml file and return a list of props
-
- returns:
-
- request_type -- ALLPROP, PROPNAME, PROP
- proplist -- list of properties found
- namespaces -- list of namespaces found
-
- """
- doc = PyExpat.Reader().fromString(xml_doc)
- snit = doc.createNodeIterator(doc, NodeFilter.NodeFilter.SHOW_ELEMENT, None, None)
-
- request_type=None
- props={}
- namespaces=[]
-
- while 1:
- curr_elem = snit.nextNode()
- if not curr_elem: break
- ename=fname=lower(curr_elem.nodeName)
- if ":" in fname:
- ename=split(fname,":")[1]
- if ename=="prop": request_type=RT_PROP; continue
- if ename=="propfind": continue
- if ename=="allprop": request_type=RT_ALLPROP; continue
- if ename=="propname": request_type=RT_PROPNAME; continue
-
- # rest should be names of attributes
-
- ns = curr_elem.namespaceURI
- if props.has_key(ns):
- props[ns].append(ename)
- else:
- props[ns]=[ename]
- namespaces.append(ns)
-
- return request_type,props,namespaces
-
-
-def create_treelist(dataclass,uri):
- """ create a list of resources out of a tree
-
- This function is used for the COPY, MOVE and DELETE methods
-
- uri - the root of the subtree to flatten
-
- It will return the flattened tree as list
-
- """
- queue=[uri]
- list=[uri]
- while len(queue):
- element=queue[-1]
- if dataclass.is_collection(element):
- childs=dataclass.get_childs(element)
- else:
- childs=[]
- if len(childs):
- list=list+childs
- # update queue
- del queue[-1]
- if len(childs):
- queue=queue+childs
- return list
-
-def is_prefix(uri1,uri2):
- """ returns 1 of uri1 is a prefix of uri2 """
- if uri2[:len(uri1)]==uri1:
- return 1
- else:
- return None
-
-def quote_uri(uri):
- """ quote an URL but not the protocol part """
- import urlparse
- import urllib
-
- up=urlparse.urlparse(uri)
- np=urllib.quote(up[2])
- return urlparse.urlunparse((up[0],up[1],np,up[3],up[4],up[5]))
-
-def get_uriparentpath(uri):
- """ extract the uri path and remove the last element """
- up=urlparse.urlparse(uri)
- return joinfields(split(up[2],"/")[:-1],"/")
-
-def get_urifilename(uri):
- """ extract the uri path and return the last element """
- up=urlparse.urlparse(uri)
- return split(up[2],"/")[-1]
-
-def get_parenturi(uri):
- """ return the parent of the given resource"""
- up=urlparse.urlparse(uri)
- np=joinfields(split(up[2],"/")[:-1],"/")
- return urlparse.urlunparse((up[0],up[1],np,up[3],up[4],up[5]))
-
-### XML utilities
-
-def make_xmlresponse(result):
- """ construct a response from a dict of uri:error_code elements """
- doc = Document(None)
- ms=doc.createElement("D:multistatus")
- ms.setAttribute("xmlns:D","DAV:")
- doc.appendChild(ms)
-
- for el,ec in result.items():
- re=doc.createElement("D:response")
- hr=doc.createElement("D:href")
- st=doc.createElement("D:status")
- huri=doc.createTextNode(quote_uri(el))
- t=doc.createTextNode(gen_estring(ec))
- st.appendChild(t)
- hr.appendChild(huri)
- re.appendChild(hr)
- re.appendChild(st)
- ms.appendChild(re)
-
- sfile=StringIO()
- ext.PrettyPrint(doc,stream=sfile)
- s=sfile.getvalue()
- sfile.close()
- return s
-
diff --git a/addons/document_webdav/__init__.py b/addons/document_webdav/__init__.py
index 08701e920d2..69312668a74 100644
--- a/addons/document_webdav/__init__.py
+++ b/addons/document_webdav/__init__.py
@@ -1,29 +1,23 @@
+# -*- coding: utf-8 -*-
##############################################################################
#
-# Copyright (c) 2004 TINY SPRL. (http://tiny.be) All Rights Reserved.
-# Fabien Pinckaers
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2004-2010 Tiny SPRL ().
#
-# WARNING: This program as such is intended to be used by professional
-# programmers who take the whole responsability of assessing all potential
-# consequences resulting from its eventual inadequacies and bugs
-# End users who are looking for a ready-to-use solution with commercial
-# garantees and support are strongly adviced to contract a Free Software
-# Service Company
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
#
-# This program is Free Software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
#
##############################################################################
+import webdav
import webdav_server
diff --git a/addons/document_webdav/dav_fs.py b/addons/document_webdav/dav_fs.py
index 1acbfd7d5ec..0c2e52c8df7 100644
--- a/addons/document_webdav/dav_fs.py
+++ b/addons/document_webdav/dav_fs.py
@@ -1,32 +1,23 @@
-# -*- encoding: utf-8 -*-
+# -*- coding: utf-8 -*-
##############################################################################
#
-# Copyright (c) 2004 TINY SPRL. (http://tiny.be) All Rights Reserved.
-# Fabien Pinckaers
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2004-2010 Tiny SPRL ().
#
-# WARNING: This program as such is intended to be used by professional
-# programmers who take the whole responsability of assessing all potential
-# consequences resulting from its eventual inadequacies and bugs
-# End users who are looking for a ready-to-use solution with commercial
-# garantees and support are strongly adviced to contract a Free Software
-# Service Company
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
#
-# This program is Free Software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
#
##############################################################################
-
import pooler
import base64
@@ -55,71 +46,87 @@ CACHE_SIZE=20000
urlparse.uses_netloc.append('webdav')
urlparse.uses_netloc.append('webdavs')
-class tinydav_handler(dav_interface):
+class openerp_dav_handler(dav_interface):
"""
- This class models a Tiny ERP interface for the DAV server
+ This class models a OpenERP interface for the DAV server
"""
- PROPS={'DAV:': dav_interface.PROPS['DAV:'], }
+ PROPS={'DAV:': dav_interface.PROPS['DAV:'],}
- M_NS={ "DAV:" : dav_interface.M_NS['DAV:'], }
+ M_NS={ "DAV:" : dav_interface.M_NS['DAV:'],}
- def __init__(self, parent, verbose=False):
+ def __init__(self, parent, verbose=False):
self.db_name = False
self.directory_id=False
self.db_name_list=[]
self.parent = parent
self.baseuri = parent.baseuri
+ self.verbose = verbose
- def get_propnames(self,uri):
- props = self.PROPS
- self.parent.log_message('get propnames: %s' % uri)
- if uri[-1]=='/':uri=uri[:-1]
- cr, uid, pool, dbname, uri2 = self.get_cr(uri)
- if not dbname:
- cr.close()
- return props
- node = self.uri2object(cr,uid,pool, uri2)
- if node:
- props.update(node.get_dav_props(cr))
- cr.close()
+ def get_propnames(self,uri):
+ props = self.PROPS
+ self.parent.log_message('get propnames: %s' % uri)
+ if uri[-1]=='/':uri=uri[:-1]
+ cr, uid, pool, dbname, uri2 = self.get_cr(uri)
+ if not dbname:
+ if cr: cr.close()
return props
+ node = self.uri2object(cr, uid, pool, uri2)
+ if node:
+ props.update(node.get_dav_props(cr))
+ cr.close()
+ return props
- def get_prop(self,uri,ns,propname):
- """ return the value of a given property
+ def _get_dav_lockdiscovery(self, uri):
+ raise DAV_NotFound
- uri -- uri of the object to get the property of
- ns -- namespace of the property
- pname -- name of the property
- """
- if self.M_NS.has_key(ns):
- return dav_interface.get_prop(self,uri,ns,propname)
+ def _get_dav_supportedlock(self, uri):
+ raise DAV_NotFound
- if uri[-1]=='/':uri=uri[:-1]
- cr, uid, pool, dbname, uri2 = self.get_cr(uri)
- if not dbname:
- cr.close()
- raise DAV_NotFound
- node = self.uri2object(cr,uid,pool, uri2)
- if not node:
- cr.close()
- raise DAV_NotFound
- res = node.get_dav_eprop(cr,ns,propname)
+ def match_prop(self, uri, match, ns, propname):
+ if uri[-1]=='/':uri=uri[:-1]
+ cr, uid, pool, dbname, uri2 = self.get_cr(uri)
+ if not dbname:
+ if cr: cr.close()
+ raise DAV_NotFound
+ node = self.uri2object(cr, uid, pool, uri2)
+ if not node:
cr.close()
- return res
+ raise DAV_NotFound
+ res = node.match_dav_eprop(cr, match, ns, propname)
+ cr.close()
+ return res
-#
-# def get_db(self,uri):
-# names=self.uri2local(uri).split('/')
-# self.db_name=False
-# if len(names) > 1:
-# self.db_name=self.uri2local(uri).split('/')[1]
-# if self.db_name=='':
-# raise Exception,'Plese specify Database name in folder'
-# return self.db_name
-#
+ def get_prop(self, uri, ns, propname):
+ """ return the value of a given property
- def later_get_db_from_path(self,path):
- return "aaa"
+ uri -- uri of the object to get the property of
+ ns -- namespace of the property
+ pname -- name of the property
+ """
+ if self.M_NS.has_key(ns):
+ return dav_interface.get_prop(self,uri,ns,propname)
+ if uri[-1]=='/':uri=uri[:-1]
+ cr, uid, pool, dbname, uri2 = self.get_cr(uri)
+ if not dbname:
+ if cr: cr.close()
+ raise DAV_NotFound
+ node = self.uri2object(cr, uid, pool, uri2)
+ if not node:
+ cr.close()
+ raise DAV_NotFound
+ res = node.get_dav_eprop(cr,ns,propname)
+ cr.close()
+ return res
+
+ def get_db(self,uri):
+ names=self.uri2local(uri).split('/')
+ self.db_name=False
+ if len(names) > 1:
+ self.db_name=self.uri2local(uri).split('/')[1]
+ if self.db_name=='':
+ raise Exception,'Plese specify Database name in folder'
+ return self.db_name
+
def urijoin(self,*ajoin):
""" Return the base URI of this request, or even join it with the
@@ -130,7 +137,7 @@ class tinydav_handler(dav_interface):
@memoize(4)
def db_list(self):
s = netsvc.ExportService.getService('db')
- result = s.list()
+ result = s.exp_list()
self.db_name_list=[]
for db_name in result:
db = pooler.get_db_only(db_name)
@@ -142,20 +149,21 @@ class tinydav_handler(dav_interface):
cr.close()
return self.db_name_list
- def get_childs(self,uri):
- """ return the child objects as self.baseuris for the given URI """
+ def get_childs(self,uri, filters=None):
+ """ return the child objects as self.baseuris for the given URI """
self.parent.log_message('get childs: %s' % uri)
if uri[-1]=='/':uri=uri[:-1]
- cr, uid, pool, dbname, uri2 = self.get_cr(uri)
+ cr, uid, pool, dbname, uri2 = self.get_cr(uri)
- if not dbname:
- s = netsvc.ExportService.getService('db')
- cr.close()
- return map(lambda x: self.urijoin(x), self.db_list())
+ if not dbname:
+ if cr: cr.close()
+ res = map(lambda x: self.urijoin(x), self.db_list())
+ return res
result = []
node = self.uri2object(cr,uid,pool, uri2[:])
+
if not node:
- cr.close()
+ if cr: cr.close()
raise DAV_NotFound(uri2)
else:
fp = node.full_path()
@@ -164,13 +172,16 @@ class tinydav_handler(dav_interface):
fp = '/'.join(fp)
else:
fp = None
- for d in node.children(cr):
- self.parent.log_message('child: %s' % d.path)
+ domain = None
+ if filters:
+ domain = node.get_domain(cr, filters)
+ for d in node.children(cr, domain):
+ self.parent.log_message('child: %s' % d.path)
if fp:
result.append( self.urijoin(dbname,fp,d.path) )
else:
result.append( self.urijoin(dbname,d.path) )
- cr.close()
+ if cr: cr.close()
return result
def uri2local(self, uri):
@@ -183,24 +194,28 @@ class tinydav_handler(dav_interface):
#
# pos: -1 to get the parent of the uri
#
- def get_cr(self, uri):
+ def get_cr(self, uri):
pdb = self.parent.auth_proxy.last_auth
reluri = self.uri2local(uri)
try:
- dbname = reluri.split('/')[2]
+ dbname = reluri.split('/')[1]
except:
- dbname = False
+ dbname = False
if not dbname:
return None, None, None, False, None
if not pdb and dbname:
# if dbname was in our uri, we should have authenticated
# against that.
raise Exception("Programming error")
- assert pdb == dbname, " %s != %s" %(pdb, dbname)
- user, passwd, dbn2, uid = self.parent.auth_proxy.auth_creds[pdb]
+ #assert pdb == dbname, " %s != %s" %(pdb, dbname)
+ res = self.parent.auth_proxy.auth_creds.get(dbname, False)
+ if not res:
+ self.parent.auth_proxy.checkRequest(self.parent, uri, dbname)
+ res = self.parent.auth_proxy.auth_creds[dbname]
+ user, passwd, dbn2, uid = res
db,pool = pooler.get_db_and_pool(dbname)
cr = db.cursor()
- uri2 = reluri.split('/')[3:]
+ uri2 = reluri.split('/')[2:]
return cr, uid, pool, dbname, uri2
def uri2object(self, cr,uid, pool,uri):
@@ -208,20 +223,20 @@ class tinydav_handler(dav_interface):
return None
return pool.get('document.directory').get_object(cr, uid, uri)
- def get_data(self,uri):
+ def get_data(self,uri):
self.parent.log_message('GET: %s' % uri)
if uri[-1]=='/':uri=uri[:-1]
cr, uid, pool, dbname, uri2 = self.get_cr(uri)
try:
if not dbname:
raise DAV_Error, 409
- node = self.uri2object(cr,uid,pool, uri2)
+ node = self.uri2object(cr,uid,pool, uri2)
if not node:
raise DAV_NotFound(uri2)
try:
datas = node.get_data(cr)
except TypeError,e:
- import traceback
+ import traceback
self.parent.log_error("GET typeError: %s", str(e))
self.parent.log_message("Exc: %s",traceback.format_exc())
raise DAV_Forbidden
@@ -235,11 +250,11 @@ class tinydav_handler(dav_interface):
raise DAV_Error, 409
return datas
finally:
- cr.close()
+ if cr: cr.close()
@memoize(CACHE_SIZE)
def _get_dav_resourcetype(self,uri):
- """ return type of object """
+ """ return type of object """
self.parent.log_message('get RT: %s' % uri)
if uri[-1]=='/':uri=uri[:-1]
cr, uid, pool, dbname, uri2 = self.get_cr(uri)
@@ -253,7 +268,7 @@ class tinydav_handler(dav_interface):
return COLLECTION
return OBJECT
finally:
- cr.close()
+ if cr: cr.close()
def _get_dav_displayname(self,uri):
self.parent.log_message('get DN: %s' % uri)
@@ -271,14 +286,14 @@ class tinydav_handler(dav_interface):
@memoize(CACHE_SIZE)
def _get_dav_getcontentlength(self,uri):
- """ return the content length of an object """
+ """ return the content length of an object """
self.parent.log_message('get length: %s' % uri)
if uri[-1]=='/':uri=uri[:-1]
result = 0
- cr, uid, pool, dbname, uri2 = self.get_cr(uri)
+ cr, uid, pool, dbname, uri2 = self.get_cr(uri)
if not dbname:
- cr.close()
- return '0'
+ if cr: cr.close()
+ return str(result)
node = self.uri2object(cr, uid, pool, uri2)
if not node:
cr.close()
@@ -311,9 +326,9 @@ class tinydav_handler(dav_interface):
if uri[-1]=='/':uri=uri[:-1]
today = time.time()
cr, uid, pool, dbname, uri2 = self.get_cr(uri)
- try:
- if not dbname:
- return today
+ if not dbname:
+ return today
+ try:
node = self.uri2object(cr,uid,pool, uri2)
if not node:
raise DAV_NotFound(uri2)
@@ -322,36 +337,35 @@ class tinydav_handler(dav_interface):
else:
return today
finally:
- cr.close()
+ if cr: cr.close()
@memoize(CACHE_SIZE)
def get_creationdate(self,uri):
- """ return the last modified date of the object """
-
+ """ return the last modified date of the object """
if uri[-1]=='/':uri=uri[:-1]
cr, uid, pool, dbname, uri2 = self.get_cr(uri)
- try:
- if not dbname:
- raise DAV_Error, 409
+ if not dbname:
+ raise DAV_Error, 409
+ try:
node = self.uri2object(cr,uid,pool, uri2)
if not node:
- raise DAV_NotFound(uri2)
+ raise DAV_NotFound(uri2)
if node.create_date:
- result = time.strptime(node.create_date,'%Y-%m-%d %H:%M:%S')
+ result = time.mktime(time.strptime(node.create_date,'%Y-%m-%d %H:%M:%S'))
else:
- result = time.gmtime()
+ result = time.time()
return result
finally:
- cr.close()
+ if cr: cr.close()
@memoize(CACHE_SIZE)
def _get_dav_getcontenttype(self,uri):
self.parent.log_message('get contenttype: %s' % uri)
if uri[-1]=='/':uri=uri[:-1]
cr, uid, pool, dbname, uri2 = self.get_cr(uri)
- try:
- if not dbname:
- return 'httpd/unix-directory'
+ if not dbname:
+ return 'httpd/unix-directory'
+ try:
node = self.uri2object(cr,uid,pool, uri2)
if not node:
raise DAV_NotFound(uri2)
@@ -359,49 +373,29 @@ class tinydav_handler(dav_interface):
return result
#raise DAV_NotFound, 'Could not find %s' % path
finally:
- cr.close()
-
+ if cr: cr.close()
+
def mkcol(self,uri):
- """ create a new collection """
+ """ create a new collection """
self.parent.log_message('MKCOL: %s' % uri)
+ uri = self.uri2local(uri)[1:]
if uri[-1]=='/':uri=uri[:-1]
- parent='/'.join(uri.split('/')[:-1])
- if not parent.startswith(self.baseuri):
- parent=self.baseuri + ''.join(parent[1:])
- if not uri.startswith(self.baseuri):
- uri=self.baseuri + ''.join(uri[1:])
-
-
+ parent = '/'.join(uri.split('/')[:-1])
+ parent = self.baseuri + parent
+ uri = self.baseuri + uri
cr, uid, pool,dbname, uri2 = self.get_cr(uri)
if not dbname:
raise DAV_Error, 409
node = self.uri2object(cr,uid,pool, uri2[:-1])
- object2 = False
- if isinstance(node, node_res_obj):
- object2 = node and pool.get(node.context.context['res_model']).browse(cr, uid, node.context.context['res_id']) or False
-
- obj = node.context._dirobj.browse(cr, uid, node.dir_id)
- if obj and (obj.type == 'ressource') and not object2:
- raise OSError(1, 'Operation not permited.')
-
- objname = uri2[-1]
- val = {
- 'name': objname,
- 'ressource_parent_type_id': obj and obj.ressource_type_id.id or False,
- 'ressource_id': object2 and object2.id or False,
- 'parent_id' : False
- }
- if (obj and (obj.type in ('directory'))) or not object2:
- val['parent_id'] = obj and obj.id or False
-
- pool.get('document.directory').create(cr, uid, val)
- cr.commit()
+ if node:
+ node.create_child_collection(cr, uri2[-1])
+ cr.commit()
cr.close()
return True
def put(self,uri,data,content_type=None):
""" put the object into the filesystem """
- self.parent.log_message('Putting %s (%d), %s'%( misc.ustr(uri), len(data), content_type))
+ self.parent.log_message('Putting %s (%d), %s'%( misc.ustr(uri), data and len(data) or 0, content_type))
parent='/'.join(uri.split('/')[:-1])
cr, uid, pool,dbname, uri2 = self.get_cr(uri)
if not dbname:
@@ -410,6 +404,7 @@ class tinydav_handler(dav_interface):
node = self.uri2object(cr,uid,pool, uri2[:])
except:
node = False
+
objname = uri2[-1]
ext = objname.find('.') >0 and objname.split('.')[1] or False
@@ -438,6 +433,7 @@ class tinydav_handler(dav_interface):
return 201
def rmcol(self,uri):
+ print ' REMOVE COLLL :::::', uri
""" delete a collection """
if uri[-1]=='/':uri=uri[:-1]
@@ -445,39 +441,23 @@ class tinydav_handler(dav_interface):
if not dbname: # *-*
raise DAV_Error, 409
node = self.uri2object(cr, uid, pool, uri2)
- object = node.context._dirobj.browse(cr, uid, node.dir_id)
-
- if not object:
- raise OSError(2, 'Not such file or directory.')
- if object._table_name=='document.directory':
- if node.children(cr):
- raise OSError(39, 'Directory not empty.')
- res = pool.get('document.directory').unlink(cr, uid, [object.id])
- else:
- raise OSError(1, 'Operation not permited.')
+ node.rmcol(cr)
cr.commit()
cr.close()
return 204
def rm(self,uri):
- if uri[-1]=='/':uri=uri[:-1]
-
- object=False
+ print ' REMOVE EVENT :::::', uri
+ if uri[-1]=='/':uri=uri[:-1]
cr, uid, pool,dbname, uri2 = self.get_cr(uri)
if not dbname:
cr.close()
raise DAV_Error, 409
node = self.uri2object(cr,uid,pool, uri2)
- object = pool.get('ir.attachment').browse(cr, uid, node.file_id)
- self.parent.log_message(' rm %s "%s"'%(object._table_name,uri))
- if not object:
- raise OSError(2, 'Not such file or directory.')
- if object._table_name == 'ir.attachment':
- res = pool.get('ir.attachment').unlink(cr, uid, [object.id])
- else:
- raise OSError(1, 'Operation not permited.')
-
+ res = node.rm(cr)
+ if not res:
+ raise OSError(1, 'Operation not permited.')
cr.commit()
cr.close()
return 204
@@ -633,7 +613,7 @@ class tinydav_handler(dav_interface):
result = False
cr, uid, pool,dbname, uri2 = self.get_cr(uri)
if not dbname:
- cr.close()
+ if cr: cr.close()
return True
try:
node = self.uri2object(cr,uid,pool, uri2)
diff --git a/addons/document_webdav/webdav.py b/addons/document_webdav/webdav.py
new file mode 100644
index 00000000000..b69c21d62ee
--- /dev/null
+++ b/addons/document_webdav/webdav.py
@@ -0,0 +1,115 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2004-2010 Tiny SPRL ().
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+##############################################################################
+
+import xml.dom.minidom
+domimpl = xml.dom.minidom.getDOMImplementation()
+import urlparse
+import urllib
+from DAV import utils
+from DAV.propfind import PROPFIND
+import tools
+
+
+super_mk_prop_response = PROPFIND.mk_prop_response
+def mk_prop_response(self,uri,good_props,bad_props,doc):
+ """ make a new result element
+
+ We differ between the good props and the bad ones for
+ each generating an extra -Node (for each error
+ one, that means).
+
+ """
+ re=doc.createElement("D:response")
+ # append namespaces to response
+ nsnum=0
+ for nsname in self.namespaces:
+ re.setAttribute("xmlns:ns"+str(nsnum),nsname)
+ nsnum=nsnum+1
+
+ # write href information
+ uparts=urlparse.urlparse(uri)
+ fileloc=uparts[2]
+ href=doc.createElement("D:href")
+ huri=doc.createTextNode(uparts[0]+'://'+'/'.join(uparts[1:2]) + urllib.quote(fileloc))
+ href.appendChild(huri)
+ re.appendChild(href)
+
+ # write good properties
+ ps=doc.createElement("D:propstat")
+ if good_props:
+ re.appendChild(ps)
+
+ gp=doc.createElement("D:prop")
+ for ns in good_props.keys():
+ ns_prefix="ns"+str(self.namespaces.index(ns))+":"
+ for p,v in good_props[ns].items():
+ if not v:
+ pass
+ pe=doc.createElement(ns_prefix+str(p))
+ if hasattr(v, '__class__') and v.__class__.__name__ == 'Element':
+ pe.appendChild(v)
+ else:
+ if p=="resourcetype":
+ if v==1:
+ ve=doc.createElement("D:collection")
+ pe.appendChild(ve)
+ else:
+ ve=doc.createTextNode(tools.ustr(v))
+ pe.appendChild(ve)
+
+ gp.appendChild(pe)
+
+ 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!
+ if len(bad_props.items()):
+
+ # write a propstat for each error code
+ for ecode in bad_props.keys():
+ ps=doc.createElement("D:propstat")
+ re.appendChild(ps)
+ bp=doc.createElement("D:prop")
+ ps.appendChild(bp)
+
+ for ns in bad_props[ecode].keys():
+ ns_prefix="ns"+str(self.namespaces.index(ns))+":"
+
+ for p in bad_props[ecode][ns]:
+ 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
+ return re
+
+
+PROPFIND.mk_prop_response = mk_prop_response
+
diff --git a/addons/document_webdav/webdav_server.py b/addons/document_webdav/webdav_server.py
index 15e8e030824..5f88f90cf11 100644
--- a/addons/document_webdav/webdav_server.py
+++ b/addons/document_webdav/webdav_server.py
@@ -28,10 +28,26 @@
import netsvc
-from dav_fs import tinydav_handler
+import tools
+from dav_fs import openerp_dav_handler
from tools.config import config
from DAV.WebDAVServer import DAVRequestHandler
from service.websrv_lib import HTTPDir,FixSendError
+import urlparse
+import urllib
+from string import atoi,split
+from DAV.errors import *
+
+def OpenDAVConfig(**kw):
+ class OpenDAV:
+ def __init__(self, **kw):
+ self.__dict__.update(**kw)
+
+ class Config:
+ DAV = OpenDAV(**kw)
+
+ return Config()
+
class DAVHandler(FixSendError,DAVRequestHandler):
verbose = False
@@ -43,16 +59,24 @@ class DAVHandler(FixSendError,DAVRequestHandler):
netsvc.Logger().notifyChannel("webdav",netsvc.LOG_DEBUG,message)
def handle(self):
- pass
+ self._init_buffer()
def finish(self):
pass
+ def get_db_from_path(self, uri):
+ if uri or uri == '/':
+ dbs = self.IFACE_CLASS.db_list()
+ res = len(dbs) and dbs[0] or False
+ else:
+ res = self.IFACE_CLASS.get_db(uri)
+ return res
+
def setup(self):
- davpath = '/'+config.get_misc('webdav','vdir','webdav')+'/'
- self.baseuri = "http://%s:%d%s"% (self.server.server_name,self.server.server_port,davpath)
- self.IFACE_CLASS = tinydav_handler(self)
- pass
+ davpath = '/'+config.get_misc('webdav','vdir','webdav')
+ self.baseuri = "http://%s:%d/"% (self.server.server_name,self.server.server_port)
+ self.IFACE_CLASS = openerp_dav_handler(self, self.verbose)
+
def log_message(self, format, *args):
netsvc.Logger().notifyChannel('webdav',netsvc.LOG_DEBUG_RPC,format % args)
@@ -60,16 +84,102 @@ class DAVHandler(FixSendError,DAVRequestHandler):
def log_error(self, format, *args):
netsvc.Logger().notifyChannel('xmlrpc',netsvc.LOG_WARNING,format % args)
+ def do_PUT(self):
+ dc=self.IFACE_CLASS
+ uri=urlparse.urljoin(self.get_baseuri(dc), self.path)
+ uri=urllib.unquote(uri)
+ # Handle If-Match
+ if self.headers.has_key('If-Match'):
+ test = False
+ etag = None
+
+ for match in self.headers['If-Match'].split(','):
+ if match == '*':
+ if dc.exists(uri):
+ test = True
+ break
+ else:
+ if dc.match_prop(uri, match, "DAV:", "getetag"):
+ test = True
+ break
+ if not test:
+ self.send_status(412)
+ return
+
+ # Handle If-None-Match
+ if self.headers.has_key('If-None-Match'):
+ test = True
+ etag = None
+ for match in self.headers['If-None-Match'].split(','):
+ if match == '*':
+ if dc.exists(uri):
+ test = False
+ break
+ else:
+ if dc.match_prop(uri, match, "DAV:", "getetag"):
+ test = False
+ break
+ if not test:
+ self.send_status(412)
+ return
+
+ # Handle expect
+ expect = self.headers.get('Expect', '')
+ if (expect.lower() == '100-continue' and
+ self.protocol_version >= 'HTTP/1.1' and
+ self.request_version >= 'HTTP/1.1'):
+ self.send_status(100)
+ self._flush()
+
+ # read the body
+ body=None
+ if self.headers.has_key("Content-Length"):
+ l=self.headers['Content-Length']
+ body=self.rfile.read(atoi(l))
+
+ # locked resources are not allowed to be overwritten
+ if self._l_isLocked(uri):
+ return self.send_body(None, '423', 'Locked', 'Locked')
+
+ ct=None
+ if self.headers.has_key("Content-Type"):
+ ct=self.headers['Content-Type']
+ try:
+ location = dc.put(uri,body,ct)
+ except DAV_Error, (ec,dd):
+ return self.send_status(ec)
+
+ headers = {}
+ if location:
+ headers['Location'] = location
+
+ try:
+ etag = dc.get_prop(location or uri, "DAV:", "getetag")
+ headers['ETag'] = etag
+ except:
+ pass
+
+ self.send_body(None, '201', 'Created', '', headers=headers)
+
try:
- from service.http_server import reg_http_service,OpenERPAuthProvider
+ from service.http_server import reg_http_service,OpenERPAuthProvider
+
if (config.get_misc('webdav','enable',True)):
- davpath = '/'+config.get_misc('webdav','vdir','webdav')+'/'
+ directory = '/'+config.get_misc('webdav','vdir','webdav')
handler = DAVHandler
- handler.verbose = config.get_misc('webdav','verbose',True)
+ verbose = config.get_misc('webdav','verbose',True)
handler.debug = config.get_misc('webdav','debug',True)
- reg_http_service(HTTPDir(davpath,DAVHandler,OpenERPAuthProvider()))
- netsvc.Logger().notifyChannel('webdav',netsvc.LOG_INFO,"WebDAV service registered at path: %s/ "% davpath)
+ _dc = { 'verbose' : verbose,
+ 'directory' : directory,
+ 'lockemulation' : False,
+
+ }
+
+ conf = OpenDAVConfig(**_dc)
+ handler._config = conf
+ reg_http_service(HTTPDir(directory,DAVHandler,OpenERPAuthProvider()))
+ netsvc.Logger().notifyChannel('webdav',netsvc.LOG_INFO,"WebDAV service registered at path: %s/ "% directory)
except Exception, e:
logger = netsvc.Logger()
logger.notifyChannel('webdav', netsvc.LOG_ERROR, 'Cannot launch webdav: %s' % e)
diff --git a/addons/project_caldav/project_caldav.py b/addons/project_caldav/project_caldav.py
index ff70478298e..62e18784887 100644
--- a/addons/project_caldav/project_caldav.py
+++ b/addons/project_caldav/project_caldav.py
@@ -20,12 +20,14 @@
##############################################################################
from osv import fields, osv
-from caldav import caldav
+from caldav import calendar
class project_task(osv.osv):
_name = "project.task"
_inherit = ["calendar.todo", "project.task"]
_columns = {
+ 'write_date' : fields.datetime('Write Date'),
+ 'create_date' : fields.datetime('Create Date'),
'attendee_ids': fields.many2many('calendar.attendee', \
'task_attendee_rel', 'task_id', 'attendee_id', 'Attendees'),
}
@@ -53,7 +55,7 @@ class project_task(osv.osv):
hours = (val['planned_hours'].seconds / float(3600)) + \
(val['planned_hours'].days * 24)
val['planned_hours'] = hours
- exists, r_id = caldav.uid2openobjectid(cr, val['id'], self._name, val.get('recurrent_id'))
+ exists, r_id = calendar.uid2openobjectid(cr, val['id'], self._name, val.get('recurrent_id'))
val.pop('id')
if exists:
self.write(cr, uid, [exists], val)
diff --git a/addons/project_caldav/project_caldav_data.xml b/addons/project_caldav/project_caldav_data.xml
index 30a200b0315..af0088cf72f 100644
--- a/addons/project_caldav/project_caldav_data.xml
+++ b/addons/project_caldav/project_caldav_data.xml
@@ -1,9 +1,11 @@
-
+
Todo
+
+ vtodo
diff --git a/addons/project_issue/project_issue.py b/addons/project_issue/project_issue.py
index 74e1b5045bf..eaefffc18c4 100755
--- a/addons/project_issue/project_issue.py
+++ b/addons/project_issue/project_issue.py
@@ -84,10 +84,11 @@ class project_issue(osv.osv):
resource_id = False
if issue.user_id:
resource_ids = res_obj.search(cr, uid, [('user_id','=',issue.user_id.id)])
- resource_id = len(resource_ids) and resource_ids[0] or False
+ if resource_ids and len(resource_ids):
+ resource_id = resource_ids[0]
duration = float(ans.days)
- if issue.section_id.resource_calendar_id:
+ if issue.section_id and issue.section_id.resource_calendar_id:
duration = float(ans.days) * 24
new_dates = cal_obj.interval_get(cr,
uid,