From d07c7bc4d6f85d593071e1d5a4900771d32545d1 Mon Sep 17 00:00:00 2001 From: "ksa (Open ERP)" Date: Tue, 23 Mar 2010 18:21:25 +0530 Subject: [PATCH 001/593] [IMP]: crm: Apply Doc String bzr revid: ksa@tinyerp.co.in-20100323125125-2ehrrtyh7tvi9vc4 --- addons/crm/crm.py | 234 +++++++++++++--------------------- addons/crm/crm_action_rule.py | 2 + addons/crm/crm_fundraising.py | 2 + 3 files changed, 95 insertions(+), 143 deletions(-) diff --git a/addons/crm/crm.py b/addons/crm/crm.py index a3eb8437546..ab02e943828 100644 --- a/addons/crm/crm.py +++ b/addons/crm/crm.py @@ -19,14 +19,14 @@ # ############################################################################## +import time from datetime import datetime from datetime import timedelta + +import tools from osv import fields from osv import osv from tools.translate import _ -import time -import tools - MAX_LEVEL = 15 AVAILABLE_STATES = [ @@ -82,6 +82,7 @@ class crm_case_section(osv.osv): ] def _check_recursion(self, cr, uid, ids): + """ Checks for recursion level for sections @param self: The object pointer @@ -105,7 +106,8 @@ class crm_case_section(osv.osv): ] def name_get(self, cr, uid, ids, context={}): - """Overrides orm name_get method + + """ Overrides orm name_get method @param self: The object pointer @param cr: the current row, from the database cursor, @param uid: the current user’s ID for security checks, @@ -123,6 +125,7 @@ class crm_case_section(osv.osv): name = record['parent_id'][1] + ' / ' + name res.append((record['id'], name)) return res + crm_case_section() @@ -137,10 +140,10 @@ 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): - """ + """ Find Object's Id for Category @param self: The object pointer @param cr: the current row, from the database cursor, @param uid: the current user’s ID for security checks, @@ -150,11 +153,13 @@ class crm_case_categ(osv.osv): object_id = context and context.get('object_id', False) or False ids = self.pool.get('ir.model').search(cr, uid, [('model', '=', object_id)]) return ids and ids[0] - - _defaults = { + + _defaults = { 'object_id' : _find_object_id + } -# + + crm_case_categ() @@ -170,16 +175,16 @@ class crm_case_resource_type(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): - """ + + """ Find object's Id for resource Type @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 """ - object_id = context and context.get('object_id', False) or False ids = self.pool.get('ir.model').search(cr, uid, [('model', '=', object_id)]) return ids and ids[0] @@ -210,10 +215,10 @@ class crm_case_stage(osv.osv): help="Change Probability on next and previous stages."), 'requirements': fields.text('Requirements') } - + def _find_object_id(self, cr, uid, context=None): - """ + """ Find Object's Id of case stage @param self: The object pointer @param cr: the current row, from the database cursor, @param uid: the current user’s ID for security checks, @@ -233,20 +238,19 @@ class crm_case_stage(osv.osv): crm_case_stage() def _links_get(self, cr, uid, context={}): - """ + + """ Get Link for Object and name @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 """ - obj = self.pool.get('res.request.link') ids = obj.search(cr, uid, []) res = obj.read(cr, uid, ids, ['object', 'name'], context) return [(r['object'], r['name']) for r in res] - class crm_case(osv.osv): """ CRM Case """ @@ -289,12 +293,13 @@ class crm_case(osv.osv): def _get_log_ids(self, cr, uid, ids, field_names, arg, context={}): - """ + """ Get Log's Id for crm case @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 case log’s IDs @param context: A standard dictionary for contextual values + @return:Dictionary of History Ids """ result = {} @@ -360,10 +365,10 @@ class crm_case(osv.osv): \nIf the case needs to be reviewed then the state is set to \'Pending\'.'), 'company_id': fields.many2one('res.company', 'Company'), } - + def _get_default_partner_address(self, cr, uid, context): - """ + """ Get Partner Address for current user @param self: The object pointer @param cr: the current row, from the database cursor, @param uid: the current user’s ID for security checks, @@ -376,7 +381,7 @@ class crm_case(osv.osv): def _get_default_partner(self, cr, uid, context): - """ + """ Get Partner for current user @param self: The object pointer @param cr: the current row, from the database cursor, @param uid: the current user’s ID for security checks, @@ -392,7 +397,7 @@ class crm_case(osv.osv): def _get_default_email(self, cr, uid, context): - """ + """ Get User Email Address @param self: The object pointer @param cr: the current row, from the database cursor, @param uid: the current user’s ID for security checks, @@ -407,7 +412,8 @@ class crm_case(osv.osv): return user.address_id.email def _get_default_user(self, cr, uid, context): - """ + + """ Get Default User @param self: The object pointer @param cr: the current row, from the database cursor, @param uid: the current user’s ID for security checks, @@ -419,7 +425,8 @@ class crm_case(osv.osv): return uid def _get_section(self, cr, uid, context): - """ + + """ Get User's Section @param self: The object pointer @param cr: the current row, from the database cursor, @param uid: the current user’s ID for security checks, @@ -445,7 +452,7 @@ class crm_case(osv.osv): def unlink(self, cr, uid, ids, context={}): - """ + """ Unlink the crm case data @param self: The object pointer @param cr: the current row, from the database cursor, @param uid: the current user’s ID for security checks, @@ -459,7 +466,8 @@ class crm_case(osv.osv): return super(crm_case, self).unlink(cr, uid, ids, context) def stage_next(self, cr, uid, ids, context={}): - """ + + """ Get Next Stage's ID @param self: The object pointer @param cr: the current row, from the database cursor, @param uid: the current user’s ID for security checks, @@ -476,7 +484,8 @@ class crm_case(osv.osv): return True def get_stage_dict(self, cr, uid, ids, context={}): - """ + + """ Gets stage id and maps it to section according to stage sequence @param self: The object pointer @param cr: the current row, from the database cursor, @param uid: the current user’s ID for security checks, @@ -495,12 +504,13 @@ class crm_case(osv.osv): return s def stage_previous(self, cr, uid, ids, context={}): - """ + + """ Get Previous stage's Id @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 stage previous’s IDs - @param context: A standard dictionary for contextual values""" + @param context: A standard dictionary for contextual values """ s = self.get_stage_dict(cr, uid, ids, context=context) for case in self.browse(cr, uid, ids, context): @@ -514,13 +524,15 @@ class crm_case(osv.osv): return True def onchange_case_id(self, cr, uid, ids, case_id, name, partner_id, context={}): - """ + + """ Get Partner Address and email Address depend on the Partner Id @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 case’s IDs @partner_id: Get Partner Id @param context: A standard dictionary for contextual values + """ if not case_id: @@ -540,12 +552,16 @@ class crm_case(osv.osv): return {'value': value} def history(self, cr, uid, ids, keyword, history=False, email=False, details=None, context={}): - """ + + """ Get History For Email @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 History’s IDs - @param context: A standard dictionary for contextual values""" + @param context: A standard dictionary for contextual values + @history: Get the History If pass Value else False + @email: Get email if pass Value else False + @details: Get Detail if pass Value else False """ cases = self.browse(cr, uid, ids, context=context) return self.__history(cr, uid, cases, keyword=keyword, \ @@ -553,16 +569,18 @@ class crm_case(osv.osv): context=context) def __history(self, cr, uid, cases, keyword, history=False, email=False, details=None, context={}): - """ + + """ Get History For Email @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 History’s IDs - @param context: A standard dictionary for contextual values""" - + @param context: A standard dictionary for contextual values + @history: Get the History If pass Value else False + @email: Get email if pass Value else False + @details: Get Detail if pass Value else False""" model_obj = self.pool.get('ir.model') - for case in cases: model_ids = model_obj.search(cr, uid, [('model', '=', case._name)]) data = { @@ -603,7 +621,7 @@ class crm_case(osv.osv): def add_reply(self, cursor, user, ids, context=None): - """ + """ Reply For Particular Email Address @param self: The object pointer @param cr: the current row, from the database cursor, @param uid: the current user’s ID for security checks, @@ -620,12 +638,13 @@ class crm_case(osv.osv): def case_log(self, cr, uid, ids, context={}, email=False, *args): - """ + """ Display case Log For Particular Email Address @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 @param *args: Give Tuple Value + @email: Get Email if pass Value else False """ cases = self.browse(cr, uid, ids) @@ -635,7 +654,7 @@ class crm_case(osv.osv): def case_log_reply(self, cr, uid, ids, context={}, email=False, *args): - """ + """ Give Reply on particular Email Address @param self: The object pointer @param cr: the current row, from the database cursor, @param uid: the current user’s ID for security checks, @@ -660,9 +679,9 @@ class crm_case(osv.osv): self.__history(cr, uid, cases, _('Send'), history=True, email=False) for case in cases: self.write(cr, uid, [case.id], { - 'description': False, - 'som': False, - 'canal_id': False, + 'description': False, + 'som': False, + 'canal_id': False, }) emails = [case.email_from] + (case.email_cc or '').split(',') emails = filter(None, emails) @@ -676,21 +695,22 @@ class crm_case(osv.osv): _("No E-Mail ID Found for your Company address!")) tools.email_send( - emailfrom, - emails, - '[' + str(case.id) + '] ' + case.name, - self.format_body(body), - reply_to = case.section_id.reply_to, + emailfrom, + emails, + '[' + str(case.id) + '] ' + case.name, + self.format_body(body), + reply_to = case.section_id.reply_to, openobject_id = str(case.id) ) return True def onchange_partner_id(self, cr, uid, ids, part, email=False): - """ + """ Get Partner Contact on partner ID @param self: The object pointer @param cr: the current row, from the database cursor, @param uid: the current user’s ID for security checks, + @email: Get email if pass Value else False """ if not part: @@ -704,10 +724,11 @@ class crm_case(osv.osv): def onchange_partner_address_id(self, cr, uid, ids, add, email=False): - """ + """ Get Email Address @param self: The object pointer @param cr: the current row, from the database cursor, @param uid: the current user’s ID for security checks, + @email: Get email if pass Value else False """ data = {} @@ -719,7 +740,7 @@ class crm_case(osv.osv): def case_close(self, cr, uid, ids, *args): - """ + """ case close when state apply done @param self: The object pointer @param cr: the current row, from the database cursor, @param uid: the current user’s ID for security checks, @@ -739,7 +760,7 @@ class crm_case(osv.osv): def case_escalate(self, cr, uid, ids, *args): - """ + """ Escalates case to upper level @param self: The object pointer @param cr: the current row, from the database cursor, @param uid: the current user’s ID for security checks, @@ -766,7 +787,7 @@ class crm_case(osv.osv): def case_open(self, cr, uid, ids, *args): - """ + """ case open when state apply open @param self: The object pointer @param cr: the current row, from the database cursor, @param uid: the current user’s ID for security checks, @@ -787,7 +808,7 @@ class crm_case(osv.osv): def case_cancel(self, cr, uid, ids, *args): - """ + """ case cancel when state apply cancel @param self: The object pointer @param cr: the current row, from the database cursor, @param uid: the current user’s ID for security checks, @@ -804,7 +825,7 @@ class crm_case(osv.osv): def case_pending(self, cr, uid, ids, *args): - """ + """ case pending when state apply pending @param self: The object pointer @param cr: the current row, from the database cursor, @param uid: the current user’s ID for security checks, @@ -821,7 +842,7 @@ class crm_case(osv.osv): def case_reset(self, cr, uid, ids, *args): - """ + """ case reset to draft when state apply draft @param self: The object pointer @param cr: the current row, from the database cursor, @param uid: the current user’s ID for security checks, @@ -846,14 +867,14 @@ class crm_case_log(osv.osv): _order = "id desc" _columns = { - 'name': fields.char('Status', size=64), - 'som': fields.many2one('res.partner.som', 'State of Mind'), - 'date': fields.datetime('Date'), - 'canal_id': fields.many2one('res.partner.canal', 'Channel'), - 'section_id': fields.many2one('crm.case.section', 'Section'), - 'user_id': fields.many2one('res.users', 'User Responsible', readonly=True), - 'model_id': fields.many2one('ir.model', "Model"), - 'res_id': fields.integer('Resource ID'), + 'name': fields.char('Status', size=64), + 'som': fields.many2one('res.partner.som', 'State of Mind'), + 'date': fields.datetime('Date'), + 'canal_id': fields.many2one('res.partner.canal', 'Channel'), + 'section_id': fields.many2one('crm.case.section', 'Section'), + 'user_id': fields.many2one('res.users', 'User Responsible', readonly=True), + 'model_id': fields.many2one('ir.model', "Model"), + 'res_id': fields.integer('Resource ID'), } _defaults = { @@ -872,11 +893,13 @@ class crm_case_history(osv.osv): def _note_get(self, cursor, user, ids, name, arg, context=None): - """ @param self: The object pointer + """ + @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 case History’s IDs @param context: A standard dictionary for contextual values + @return: email address ,date and description """ res = {} @@ -886,89 +909,15 @@ class crm_case_history(osv.osv): return res _columns = { - 'description': fields.text('Description'), - 'note': fields.function(_note_get, method=True, string="Description", type="text"), - 'email': fields.char('Email', size=84), + 'description': fields.text('Description'), + 'note': fields.function(_note_get, method=True, string="Description", type="text"), + 'email': fields.char('Email', size=84), 'email_from' : fields.char('From Email', size=84), - 'log_id': fields.many2one('crm.case.log', 'Log', ondelete='cascade'), + 'log_id': fields.many2one('crm.case.log', 'Log', ondelete='cascade'), } crm_case_history() -class crm_email_add_cc_wizard(osv.osv_memory): - """ Email Add CC""" - - _name = "crm.email.add.cc" - _description = "Email Add CC" - - _columns = { - 'name': fields.selection([('user', 'User'), ('partner', 'Partner'),\ - ('email', 'Email Address')], 'Send to', required=True), - 'user_id': fields.many2one('res.users', "User"), - 'partner_id': fields.many2one('res.partner', "Partner"), - 'email': fields.char('Email', size=32), - 'subject': fields.char('Subject', size=32), - } - - def change_email(self, cr, uid, ids, user, partner): - - """ @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 Mail’s IDs - """ - - if (not partner and not user): - return {'value': {'email': False}} - email = False - if partner: - addr = self.pool.get('res.partner').address_get(cr, uid, [partner], ['contact']) - if addr: - email = self.pool.get('res.partner.address').read(cr, uid, addr['contact'] , ['email'])['email'] - elif user: - addr = self.pool.get('res.users').read(cr, uid, user, ['address_id'])['address_id'] - if addr: - email = self.pool.get('res.partner.address').read(cr, uid, addr[0] , ['email'])['email'] - return {'value': {'email': email}} - - - def add_cc(self, cr, uid, ids, 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 ids: List of create menu’s IDs - @param context: A standard dictionary for contextual values - """ - - data = self.read(cr, uid, ids[0]) - email = data['email'] - subject = data['subject'] - - if not context: - return {} - history_line = self.pool.get('crm.case.history').browse(cr, uid, context['active_id']) - model = history_line.log_id.model_id.model - model_pool = self.pool.get(model) - case = model_pool.browse(cr, uid, history_line.log_id.res_id) - body = history_line.description.replace('\n', '\n> ') - flag = tools.email_send( - case.user_id.address_id.email, - [case.email_from], - subject or '[' + str(case.id) + '] ' + case.name, - model_pool.format_body(body), - email_cc = [email], - openobject_id = str(case.id), - subtype = "html" - ) - - if flag: - model_pool.write(cr, uid, case.id, {'email_cc' : case.email_cc and case.email_cc + ',' + email or email}) - else: - raise osv.except_osv(_('Email Fail!'), ("Lastest Email is not sent successfully")) - return {} - -crm_email_add_cc_wizard() class users(osv.osv): _inherit = 'res.users' @@ -984,7 +933,6 @@ class res_partner(osv.osv): _columns = { 'section_id': fields.many2one('crm.case.section', 'Sales Team'), } - res_partner() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/crm/crm_action_rule.py b/addons/crm/crm_action_rule.py index f2d5a19997d..2de58710f0a 100644 --- a/addons/crm/crm_action_rule.py +++ b/addons/crm/crm_action_rule.py @@ -249,3 +249,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: From aa70854c09a1e287342e830318c873131bf9744f Mon Sep 17 00:00:00 2001 From: "Harry (Open ERP)" Date: Tue, 6 Apr 2010 16:41:32 +0530 Subject: [PATCH 002/593] [IMP] document_webdav: removed DAV and refactore code bzr revid: hmo@tinyerp.com-20100406111132-9bscvnb6aojdvioo --- addons/document/nodes.py | 6 +- .../DAV/BufferingHTTPServer.py | 99 ----- addons/document_webdav/DAV/WebDAVServer.py | 379 ------------------ addons/document_webdav/DAV/__init__.py | 20 - addons/document_webdav/DAV/constants.py | 18 - addons/document_webdav/DAV/davcmd.py | 218 ---------- addons/document_webdav/DAV/davcopy.py | 133 ------ addons/document_webdav/DAV/davmove.py | 102 ----- addons/document_webdav/DAV/delete.py | 63 --- addons/document_webdav/DAV/errors.py | 56 --- addons/document_webdav/DAV/iface.py | 263 ------------ addons/document_webdav/DAV/propfind.py | 372 ----------------- addons/document_webdav/DAV/status.py | 31 -- addons/document_webdav/DAV/utils.py | 160 -------- addons/document_webdav/dav_fs.py | 173 ++++---- addons/document_webdav/webdav_server.py | 140 ++++++- 16 files changed, 214 insertions(+), 2019 deletions(-) delete mode 100644 addons/document_webdav/DAV/BufferingHTTPServer.py delete mode 100644 addons/document_webdav/DAV/WebDAVServer.py delete mode 100644 addons/document_webdav/DAV/__init__.py delete mode 100644 addons/document_webdav/DAV/constants.py delete mode 100644 addons/document_webdav/DAV/davcmd.py delete mode 100755 addons/document_webdav/DAV/davcopy.py delete mode 100755 addons/document_webdav/DAV/davmove.py delete mode 100755 addons/document_webdav/DAV/delete.py delete mode 100755 addons/document_webdav/DAV/errors.py delete mode 100644 addons/document_webdav/DAV/iface.py delete mode 100755 addons/document_webdav/DAV/propfind.py delete mode 100644 addons/document_webdav/DAV/status.py delete mode 100755 addons/document_webdav/DAV/utils.py diff --git a/addons/document/nodes.py b/addons/document/nodes.py index c7eb80c0a45..a8553426d3f 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -193,7 +193,11 @@ class node_dir(node_class): pass def children(self,cr): - return self._child_get(cr) + self._file_get(cr) + res = self._child_get(cr) + self._file_get(cr) + return res + + def get_data(self,cr): + return '' def child(self,cr, name): res = self._child_get(cr,name) 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/dav_fs.py b/addons/document_webdav/dav_fs.py index 1acbfd7d5ec..9ef91e0f9a6 100644 --- a/addons/document_webdav/dav_fs.py +++ b/addons/document_webdav/dav_fs.py @@ -59,67 +59,47 @@ class tinydav_handler(dav_interface): """ This class models a Tiny ERP 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() - return props + def get_propnames(self,uri): + props = self.PROPS + 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 + def get_prop(self,uri,ns,propname): + """ return the value of a given property - 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 + 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) + raise DAV_NotFound -# -# 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 later_get_db_from_path(self,path): - return "aaa" + 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 +110,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) @@ -146,16 +126,17 @@ class tinydav_handler(dav_interface): """ 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() @@ -165,12 +146,13 @@ class tinydav_handler(dav_interface): else: fp = None for d in node.children(cr): - self.parent.log_message('child: %s' % d.path) + 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 +165,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 +194,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 +221,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 +239,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 +257,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 +297,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 +308,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,8 +344,8 @@ 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 """ self.parent.log_message('MKCOL: %s' % uri) @@ -401,7 +386,7 @@ class tinydav_handler(dav_interface): 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: @@ -633,7 +618,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_server.py b/addons/document_webdav/webdav_server.py index 15e8e030824..f2c129dd608 100644 --- a/addons/document_webdav/webdav_server.py +++ b/addons/document_webdav/webdav_server.py @@ -28,10 +28,113 @@ import netsvc +import tools from dav_fs import tinydav_handler from tools.config import config from DAV.WebDAVServer import DAVRequestHandler from service.websrv_lib import HTTPDir,FixSendError +from DAV.propfind import PROPFIND + +import xml.dom.minidom +domimpl = xml.dom.minidom.getDOMImplementation() +import urlparse +import urllib +from DAV import utils + +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 + +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 +146,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 = tinydav_handler(self, self.verbose) + def log_message(self, format, *args): netsvc.Logger().notifyChannel('webdav',netsvc.LOG_DEBUG_RPC,format % args) @@ -62,14 +173,23 @@ class DAVHandler(FixSendError,DAVRequestHandler): 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) From 565d1a18c737021cde9e0faa7b5718ae04992972 Mon Sep 17 00:00:00 2001 From: "Harry (Open ERP)" Date: Mon, 12 Apr 2010 16:22:00 +0530 Subject: [PATCH 003/593] [REF+IMP] caldav bzr revid: hmo@tinyerp.com-20100412105200-c3kvao81ypbh8l0g --- addons/caldav/DAV/BufferingHTTPServer.py | 101 ----- addons/caldav/DAV/WebDAVServer.py | 381 ------------------ addons/caldav/DAV/__init__.py | 20 - addons/caldav/DAV/constants.py | 18 - addons/caldav/DAV/davcmd.py | 219 ---------- addons/caldav/DAV/davcopy.py | 134 ------ addons/caldav/DAV/davmove.py | 103 ----- addons/caldav/DAV/delete.py | 64 --- addons/caldav/DAV/errors.py | 57 --- addons/caldav/DAV/iface.py | 264 ------------ addons/caldav/DAV/propfind.py | 373 ----------------- addons/caldav/DAV/status.py | 31 -- addons/caldav/DAV/utils.py | 161 -------- addons/caldav/__init__.py | 5 +- addons/caldav/__terp__.py | 3 +- addons/caldav/caldav_cache.py | 62 --- .../{caldav.py => caldav_collection.py} | 13 +- addons/caldav/caldav_fs.py | 311 +------------- addons/caldav/caldav_view.xml | 81 ++-- addons/caldav/security/ir.model.access.csv | 1 - addons/caldav/webdav_server.py | 78 ---- addons/caldav/wizard/calendar_event_export.py | 4 +- .../wizard/calendar_event_export_view.xml | 6 +- addons/caldav/wizard/calendar_event_import.py | 2 +- .../wizard/calendar_event_import_view.xml | 6 +- .../caldav/wizard/calendar_event_subscribe.py | 4 +- .../wizard/calendar_event_subscribe_view.xml | 8 +- addons/crm_caldav/crm_caldav.py | 2 - addons/crm_caldav/crm_caldav_data.xml | 2 +- addons/document_webdav/__init__.py | 34 +- addons/document_webdav/dav_fs.py | 39 +- addons/document_webdav/webdav.py | 114 ++++++ addons/document_webdav/webdav_server.py | 96 +---- addons/project_caldav/project_caldav_data.xml | 2 +- 34 files changed, 227 insertions(+), 2572 deletions(-) delete mode 100644 addons/caldav/DAV/BufferingHTTPServer.py delete mode 100644 addons/caldav/DAV/WebDAVServer.py delete mode 100644 addons/caldav/DAV/__init__.py delete mode 100644 addons/caldav/DAV/constants.py delete mode 100644 addons/caldav/DAV/davcmd.py delete mode 100755 addons/caldav/DAV/davcopy.py delete mode 100755 addons/caldav/DAV/davmove.py delete mode 100755 addons/caldav/DAV/delete.py delete mode 100755 addons/caldav/DAV/errors.py delete mode 100644 addons/caldav/DAV/iface.py delete mode 100755 addons/caldav/DAV/propfind.py delete mode 100644 addons/caldav/DAV/status.py delete mode 100755 addons/caldav/DAV/utils.py delete mode 100644 addons/caldav/caldav_cache.py rename addons/caldav/{caldav.py => caldav_collection.py} (99%) delete mode 100644 addons/caldav/webdav_server.py create mode 100644 addons/document_webdav/webdav.py 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..85e6b10a270 100644 --- a/addons/caldav/__init__.py +++ b/addons/caldav/__init__.py @@ -19,11 +19,8 @@ # ############################################################################## -import caldav -import caldav_cache +import caldav_collection 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/__terp__.py b/addons/caldav/__terp__.py index 1d25ad8cb77..3717ad32d83 100644 --- a/addons/caldav/__terp__.py +++ b/addons/caldav/__terp__.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_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.py b/addons/caldav/caldav_collection.py similarity index 99% rename from addons/caldav/caldav.py rename to addons/caldav/caldav_collection.py index c5534afa649..a58b6249cec 100644 --- a/addons/caldav/caldav.py +++ b/addons/caldav/caldav_collection.py @@ -412,8 +412,7 @@ class CalDAV(object): class Calendar(CalDAV, osv.osv): - _name = 'basic.calendar' - _description = 'Calendar' + _inherit = 'document.directory' _calname = 'calendar' __attribute__ = { @@ -431,15 +430,11 @@ class Calendar(CalDAV, osv.osv): } _columns = { 'name': fields.char("Name", size=64), - 'line_ids': fields.one2many('basic.calendar.lines', 'calendar_id', 'Calendar Lines'), - 'active': fields.boolean('Active'), + 'line_ids': fields.one2many('basic.calendar.lines', 'calendar_id', 'Calendar Lines'), '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 @@ -515,7 +510,7 @@ class basic_calendar_line(osv.osv): ('attendee', 'Attendee')], \ string="Type", size=64), 'object_id': fields.many2one('ir.model', 'Object'), - 'calendar_id': fields.many2one('basic.calendar', 'Calendar', \ + 'calendar_id': fields.many2one('document.directory', 'Calendar', \ required=True, ondelete='cascade'), 'domain': fields.char('Domain', size=124), 'mapping_ids': fields.one2many('basic.calendar.fields', 'type_id', 'Fields Mapping') diff --git a/addons/caldav/caldav_fs.py b/addons/caldav/caldav_fs.py index 9ab7a2156d6..1b3d1f4879b 100644 --- a/addons/caldav/caldav_fs.py +++ b/addons/caldav/caldav_fs.py @@ -19,316 +19,7 @@ # ############################################################################## -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 +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/caldav/caldav_view.xml b/addons/caldav/caldav_view.xml index f43328ecc3e..696b1833513 100644 --- a/addons/caldav/caldav_view.xml +++ b/addons/caldav/caldav_view.xml @@ -1,15 +1,15 @@ - - - Basic Calendar - basic.calendar - form - -
- - + + CalDAV Collections : Form + document.directory + form + + + + + @@ -33,29 +33,60 @@ - + - - - + + +
- - Calendar - ir.actions.act_window - basic.calendar - form - tree,form - - + + CalDAV Collections : Tree + document.directory + tree + child_ids + + + + + + + + + - + + CalDAV Collections + ir.actions.act_window + document.directory + form + tree,form + [('line_ids','!=', False)] + + + [('line_ids','=', False)] + + + + + tree + + + + + + form + + + + +
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_export.py b/addons/caldav/wizard/calendar_event_export.py index 856d9e9bc91..79434d3d24d 100644 --- a/addons/caldav/wizard/calendar_event_export.py +++ b/addons/caldav/wizard/calendar_event_export.py @@ -36,7 +36,7 @@ class calendar_event_export(osv.osv_memory): """ Get Default value for file_path field. """ - model = context.get('model', 'basic.calendar') + model = context.get('model', 'document.directory') model_obj = self.pool.get(model) calendar = model_obj.export_cal(cr, uid, context['active_ids'], context) @@ -47,7 +47,7 @@ class calendar_event_export(osv.osv_memory): """ Get Default value for Name field. """ - model = context.get('model', 'basic.calendar') + model = context.get('model', 'document.directory') model_obj = self.pool.get(model) calendar = model_obj.export_cal(cr, uid, context['active_ids'], context) res = super(calendar_event_export, self).default_get( cr, uid, fields, context=context) diff --git a/addons/caldav/wizard/calendar_event_export_view.xml b/addons/caldav/wizard/calendar_event_export_view.xml index 227822966d0..4d3762d2240 100644 --- a/addons/caldav/wizard/calendar_event_export_view.xml +++ b/addons/caldav/wizard/calendar_event_export_view.xml @@ -30,10 +30,10 @@ - + res_model="calendar.event.export" src_model="document.directory" + view_mode="form" target="new" view_type="form" /> --> diff --git a/addons/caldav/wizard/calendar_event_import.py b/addons/caldav/wizard/calendar_event_import.py index 64a1990f00d..a45647a94d6 100644 --- a/addons/caldav/wizard/calendar_event_import.py +++ b/addons/caldav/wizard/calendar_event_import.py @@ -44,7 +44,7 @@ class calendar_event_import(osv.osv_memory): """ for data in self.read(cr, uid, ids): - model = data.get('model', 'basic.calendar') + model = data.get('model', 'document.directory') model_obj = self.pool.get(model) context.update({'model': model}) data_obj = self.pool.get('ir.model.data') diff --git a/addons/caldav/wizard/calendar_event_import_view.xml b/addons/caldav/wizard/calendar_event_import_view.xml index 6a697de5fee..a0135356e71 100644 --- a/addons/caldav/wizard/calendar_event_import_view.xml +++ b/addons/caldav/wizard/calendar_event_import_view.xml @@ -46,10 +46,10 @@ new - + res_model="calendar.event.import" src_model="document.directory" + view_mode="form" target="new" view_type="form" /> --> diff --git a/addons/caldav/wizard/calendar_event_subscribe.py b/addons/caldav/wizard/calendar_event_subscribe.py index de673b5d7c0..57c618af849 100644 --- a/addons/caldav/wizard/calendar_event_subscribe.py +++ b/addons/caldav/wizard/calendar_event_subscribe.py @@ -51,10 +51,10 @@ class calendar_event_subscribe(osv.osv_memory): f.close() except Exception,e: raise osv.except_osv(_('Error!'), _('Please provide Proper URL !')) - model = data.get('model', 'basic.calendar') + model = data.get('model', 'document.directory') model_obj = self.pool.get(model) context.update({'url': data['url_path'], - 'model': data.get('model', 'basic.calendar')}) + 'model': data.get('model', 'document.directory')}) vals = model_obj.import_cal(cr, uid, base64.encodestring(caldata), \ context['active_id'], context) if vals: diff --git a/addons/caldav/wizard/calendar_event_subscribe_view.xml b/addons/caldav/wizard/calendar_event_subscribe_view.xml index 43224013268..2a10fd400f8 100644 --- a/addons/caldav/wizard/calendar_event_subscribe_view.xml +++ b/addons/caldav/wizard/calendar_event_subscribe_view.xml @@ -46,10 +46,10 @@ new - + res_model="calendar.event.subscribe" src_model="document.directory" + view_mode="form" target="new" view_type="form" /> --> - \ No newline at end of file + 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..ccaea831cf9 100644 --- a/addons/crm_caldav/crm_caldav_data.xml +++ b/addons/crm_caldav/crm_caldav_data.xml @@ -4,7 +4,7 @@ - + Meetings 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 9ef91e0f9a6..9be77c50daa 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,9 +46,9 @@ 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:'],} diff --git a/addons/document_webdav/webdav.py b/addons/document_webdav/webdav.py new file mode 100644 index 00000000000..6e816c35e10 --- /dev/null +++ b/addons/document_webdav/webdav.py @@ -0,0 +1,114 @@ +# -*- 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 + + +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 f2c129dd608..6cda2d172ef 100644 --- a/addons/document_webdav/webdav_server.py +++ b/addons/document_webdav/webdav_server.py @@ -29,102 +29,11 @@ import netsvc import tools -from dav_fs import tinydav_handler +from dav_fs import openerp_dav_handler from tools.config import config from DAV.WebDAVServer import DAVRequestHandler from service.websrv_lib import HTTPDir,FixSendError -from DAV.propfind import PROPFIND -import xml.dom.minidom -domimpl = xml.dom.minidom.getDOMImplementation() -import urlparse -import urllib -from DAV import utils - -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 def OpenDAVConfig(**kw): class OpenDAV: @@ -136,6 +45,7 @@ def OpenDAVConfig(**kw): return Config() + class DAVHandler(FixSendError,DAVRequestHandler): verbose = False @@ -162,7 +72,7 @@ class DAVHandler(FixSendError,DAVRequestHandler): def setup(self): davpath = '/'+config.get_misc('webdav','vdir','webdav') self.baseuri = "http://%s:%d/"% (self.server.server_name,self.server.server_port) - self.IFACE_CLASS = tinydav_handler(self, self.verbose) + self.IFACE_CLASS = openerp_dav_handler(self, self.verbose) def log_message(self, format, *args): diff --git a/addons/project_caldav/project_caldav_data.xml b/addons/project_caldav/project_caldav_data.xml index 30a200b0315..5970100800d 100644 --- a/addons/project_caldav/project_caldav_data.xml +++ b/addons/project_caldav/project_caldav_data.xml @@ -2,7 +2,7 @@ - + Todo From 37a0b05a170d0fe454f27cfabe8689d0a08ae552 Mon Sep 17 00:00:00 2001 From: "Harry (Open ERP)" Date: Mon, 12 Apr 2010 17:15:51 +0530 Subject: [PATCH 004/593] [REF] caldav bzr revid: hmo@tinyerp.com-20100412114551-pkrd135oxul8pw1u --- addons/caldav/__init__.py | 2 +- .../{caldav_collection.py => caldav.py} | 16 ++++-- addons/caldav/caldav_data.xml | 3 ++ addons/caldav/caldav_view.xml | 54 +++++++++---------- addons/crm_caldav/crm_caldav_data.xml | 3 +- addons/document_webdav/webdav.py | 1 + addons/project_caldav/project_caldav_data.xml | 5 +- 7 files changed, 46 insertions(+), 38 deletions(-) rename addons/caldav/{caldav_collection.py => caldav.py} (98%) diff --git a/addons/caldav/__init__.py b/addons/caldav/__init__.py index 85e6b10a270..4a870385a68 100644 --- a/addons/caldav/__init__.py +++ b/addons/caldav/__init__.py @@ -19,7 +19,7 @@ # ############################################################################## -import caldav_collection +import caldav import caldav_fs import wizard diff --git a/addons/caldav/caldav_collection.py b/addons/caldav/caldav.py similarity index 98% rename from addons/caldav/caldav_collection.py rename to addons/caldav/caldav.py index a58b6249cec..fb4eb35ca8d 100644 --- a/addons/caldav/caldav_collection.py +++ b/addons/caldav/caldav.py @@ -410,9 +410,15 @@ class CalDAV(object): self.ical_reset('value') return res +class calendar_collection(osv.osv): + _inherit = 'document.directory' + _columns = { + 'calendar_ids': fields.one2many('basic.calendar', 'collection_id', 'Calendars'), + } +calendar_collection() class Calendar(CalDAV, osv.osv): - _inherit = 'document.directory' + _name = 'basic.calendar' _calname = 'calendar' __attribute__ = { @@ -430,11 +436,13 @@ class Calendar(CalDAV, osv.osv): } _columns = { 'name': fields.char("Name", size=64), + 'collection_id': fields.many2one('document.directory', 'Collection', \ + required=True), + 'user_id': fields.many2one('res.users', 'Owner'), 'line_ids': fields.one2many('basic.calendar.lines', 'calendar_id', 'Calendar Lines'), 'create_date': fields.datetime('Created Date'), 'write_date': fields.datetime('Modifided Date'), - } - + } def export_cal(self, cr, uid, ids, vobj='vevent', context={}): """ Export Calendar @@ -510,7 +518,7 @@ class basic_calendar_line(osv.osv): ('attendee', 'Attendee')], \ string="Type", size=64), 'object_id': fields.many2one('ir.model', 'Object'), - 'calendar_id': fields.many2one('document.directory', 'Calendar', \ + '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') diff --git a/addons/caldav/caldav_data.xml b/addons/caldav/caldav_data.xml index 7ebab73aca5..cd8b5918a40 100644 --- a/addons/caldav/caldav_data.xml +++ b/addons/caldav/caldav_data.xml @@ -2,6 +2,9 @@ + + Calendars + diff --git a/addons/caldav/caldav_view.xml b/addons/caldav/caldav_view.xml index 696b1833513..794308daa99 100644 --- a/addons/caldav/caldav_view.xml +++ b/addons/caldav/caldav_view.xml @@ -1,15 +1,15 @@ - - CalDAV Collections : Form - document.directory + + Calendar : Form + basic.calendar form -
- + + - + @@ -43,13 +43,12 @@ - - CalDAV Collections : Tree - document.directory - tree - child_ids + + Calendar : Tree + basic.calendar + tree - + @@ -58,35 +57,30 @@ - - CalDAV Collections + + Calendars ir.actions.act_window - document.directory + basic.calendar form - tree,form - [('line_ids','!=', False)] - + tree,form + - - [('line_ids','=', False)] - - - + tree - - + + - + form - - + + diff --git a/addons/crm_caldav/crm_caldav_data.xml b/addons/crm_caldav/crm_caldav_data.xml index ccaea831cf9..ca5f38f65b8 100644 --- a/addons/crm_caldav/crm_caldav_data.xml +++ b/addons/crm_caldav/crm_caldav_data.xml @@ -4,8 +4,9 @@ - + Meetings + diff --git a/addons/document_webdav/webdav.py b/addons/document_webdav/webdav.py index 6e816c35e10..b69c21d62ee 100644 --- a/addons/document_webdav/webdav.py +++ b/addons/document_webdav/webdav.py @@ -25,6 +25,7 @@ import urlparse import urllib from DAV import utils from DAV.propfind import PROPFIND +import tools super_mk_prop_response = PROPFIND.mk_prop_response diff --git a/addons/project_caldav/project_caldav_data.xml b/addons/project_caldav/project_caldav_data.xml index 5970100800d..59c9e5bd9f7 100644 --- a/addons/project_caldav/project_caldav_data.xml +++ b/addons/project_caldav/project_caldav_data.xml @@ -1,9 +1,10 @@ - + - + Todo + From 9f5aa65624110ebd12027b3041f91335a8cdd86a Mon Sep 17 00:00:00 2001 From: "Harry (Open ERP)" Date: Mon, 12 Apr 2010 17:31:15 +0530 Subject: [PATCH 005/593] [REF] caldav bzr revid: hmo@tinyerp.com-20100412120115-spckeb0vi08x6vvt --- addons/caldav/__init__.py | 2 +- addons/caldav/{caldav.py => calendar.py} | 0 addons/project_caldav/project_caldav.py | 4 ++-- 3 files changed, 3 insertions(+), 3 deletions(-) rename addons/caldav/{caldav.py => calendar.py} (100%) diff --git a/addons/caldav/__init__.py b/addons/caldav/__init__.py index 4a870385a68..0a0787feb89 100644 --- a/addons/caldav/__init__.py +++ b/addons/caldav/__init__.py @@ -19,7 +19,7 @@ # ############################################################################## -import caldav +import calendar import caldav_fs import wizard diff --git a/addons/caldav/caldav.py b/addons/caldav/calendar.py similarity index 100% rename from addons/caldav/caldav.py rename to addons/caldav/calendar.py diff --git a/addons/project_caldav/project_caldav.py b/addons/project_caldav/project_caldav.py index 53bd5545159..9b95ec8e9b8 100644 --- a/addons/project_caldav/project_caldav.py +++ b/addons/project_caldav/project_caldav.py @@ -20,7 +20,7 @@ ############################################################################## from osv import fields, osv -from caldav import caldav +from caldav import calendar class project_task(osv.osv): _name = "project.task" @@ -52,7 +52,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) From dd61fff76064ce4ad8629efce6f75b46d455295d Mon Sep 17 00:00:00 2001 From: "Harry (Open ERP)" Date: Mon, 12 Apr 2010 17:54:23 +0530 Subject: [PATCH 006/593] [IMP] caldav bzr revid: hmo@tinyerp.com-20100412122423-eyc0bs5h1x3mfu9y --- addons/caldav/caldav_data.xml | 1 + addons/caldav/caldav_view.xml | 66 ++++++++++++++++++++++++++++++++++- addons/caldav/calendar.py | 5 +++ 3 files changed, 71 insertions(+), 1 deletion(-) diff --git a/addons/caldav/caldav_data.xml b/addons/caldav/caldav_data.xml index cd8b5918a40..04f5a6004a7 100644 --- a/addons/caldav/caldav_data.xml +++ b/addons/caldav/caldav_data.xml @@ -4,6 +4,7 @@ Calendars + True diff --git a/addons/caldav/caldav_view.xml b/addons/caldav/caldav_view.xml index 794308daa99..152baf6c876 100644 --- a/addons/caldav/caldav_view.xml +++ b/addons/caldav/caldav_view.xml @@ -1,6 +1,70 @@ + + Calendar Collections : Form + document.directory + form + + + + + + + + + + + Calendar Collections : Tree + document.directory + tree + child_ids + + + + + + + + + + + + Calendar Collections + ir.actions.act_window + document.directory + form + tree,form + [('calendar_collection','=',True)] + + + + [('calendar_collection','=',False)] + + + + + tree + + + + + + form + + + + + + + + Calendar : Form basic.calendar @@ -81,6 +145,6 @@ + parent="menu_calendar"/> diff --git a/addons/caldav/calendar.py b/addons/caldav/calendar.py index fb4eb35ca8d..bcf7eb58d76 100644 --- a/addons/caldav/calendar.py +++ b/addons/caldav/calendar.py @@ -413,8 +413,13 @@ class CalDAV(object): 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, + } + calendar_collection() class Calendar(CalDAV, osv.osv): From 522724913410a7aa2cf881f1394a36cbd9a8107e Mon Sep 17 00:00:00 2001 From: "Harry (Open ERP)" Date: Mon, 12 Apr 2010 18:01:10 +0530 Subject: [PATCH 007/593] [IMP] caldav bzr revid: hmo@tinyerp.com-20100412123110-6e76f9icz3up62jm --- addons/caldav/wizard/calendar_event_export.py | 4 ++-- addons/caldav/wizard/calendar_event_export_view.xml | 6 +++--- addons/caldav/wizard/calendar_event_import.py | 2 +- addons/caldav/wizard/calendar_event_import_view.xml | 6 +++--- addons/caldav/wizard/calendar_event_subscribe.py | 4 ++-- addons/caldav/wizard/calendar_event_subscribe_view.xml | 6 +++--- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/addons/caldav/wizard/calendar_event_export.py b/addons/caldav/wizard/calendar_event_export.py index 79434d3d24d..856d9e9bc91 100644 --- a/addons/caldav/wizard/calendar_event_export.py +++ b/addons/caldav/wizard/calendar_event_export.py @@ -36,7 +36,7 @@ class calendar_event_export(osv.osv_memory): """ Get Default value for file_path field. """ - model = context.get('model', 'document.directory') + model = context.get('model', 'basic.calendar') model_obj = self.pool.get(model) calendar = model_obj.export_cal(cr, uid, context['active_ids'], context) @@ -47,7 +47,7 @@ class calendar_event_export(osv.osv_memory): """ Get Default value for Name field. """ - model = context.get('model', 'document.directory') + model = context.get('model', 'basic.calendar') model_obj = self.pool.get(model) calendar = model_obj.export_cal(cr, uid, context['active_ids'], context) res = super(calendar_event_export, self).default_get( cr, uid, fields, context=context) diff --git a/addons/caldav/wizard/calendar_event_export_view.xml b/addons/caldav/wizard/calendar_event_export_view.xml index 4d3762d2240..227822966d0 100644 --- a/addons/caldav/wizard/calendar_event_export_view.xml +++ b/addons/caldav/wizard/calendar_event_export_view.xml @@ -30,10 +30,10 @@ - + res_model="calendar.event.export" src_model="basic.calendar" + view_mode="form" target="new" view_type="form" /> diff --git a/addons/caldav/wizard/calendar_event_import.py b/addons/caldav/wizard/calendar_event_import.py index a45647a94d6..64a1990f00d 100644 --- a/addons/caldav/wizard/calendar_event_import.py +++ b/addons/caldav/wizard/calendar_event_import.py @@ -44,7 +44,7 @@ class calendar_event_import(osv.osv_memory): """ for data in self.read(cr, uid, ids): - model = data.get('model', 'document.directory') + model = data.get('model', 'basic.calendar') model_obj = self.pool.get(model) context.update({'model': model}) data_obj = self.pool.get('ir.model.data') diff --git a/addons/caldav/wizard/calendar_event_import_view.xml b/addons/caldav/wizard/calendar_event_import_view.xml index a0135356e71..7e31d08c1e9 100644 --- a/addons/caldav/wizard/calendar_event_import_view.xml +++ b/addons/caldav/wizard/calendar_event_import_view.xml @@ -46,10 +46,10 @@ new - + res_model="calendar.event.import" src_model="basic.calendar" + view_mode="form" target="new" view_type="form" />
diff --git a/addons/caldav/wizard/calendar_event_subscribe.py b/addons/caldav/wizard/calendar_event_subscribe.py index 57c618af849..de673b5d7c0 100644 --- a/addons/caldav/wizard/calendar_event_subscribe.py +++ b/addons/caldav/wizard/calendar_event_subscribe.py @@ -51,10 +51,10 @@ class calendar_event_subscribe(osv.osv_memory): f.close() except Exception,e: raise osv.except_osv(_('Error!'), _('Please provide Proper URL !')) - model = data.get('model', 'document.directory') + model = data.get('model', 'basic.calendar') model_obj = self.pool.get(model) context.update({'url': data['url_path'], - 'model': data.get('model', 'document.directory')}) + 'model': data.get('model', 'basic.calendar')}) vals = model_obj.import_cal(cr, uid, base64.encodestring(caldata), \ context['active_id'], context) if vals: diff --git a/addons/caldav/wizard/calendar_event_subscribe_view.xml b/addons/caldav/wizard/calendar_event_subscribe_view.xml index 2a10fd400f8..39eaae28540 100644 --- a/addons/caldav/wizard/calendar_event_subscribe_view.xml +++ b/addons/caldav/wizard/calendar_event_subscribe_view.xml @@ -46,10 +46,10 @@ new
- + res_model="calendar.event.subscribe" src_model="basic.calendar" + view_mode="form" target="new" view_type="form" />
From 6a94de909b027dc1259b3fc8b4d1cd16d1d2c467 Mon Sep 17 00:00:00 2001 From: "Harry (Open ERP)" Date: Tue, 13 Apr 2010 16:33:20 +0530 Subject: [PATCH 008/593] [REF+IMP] document bzr revid: hmo@tinyerp.com-20100413110320-9x1fy7l432sinxvd --- addons/caldav/caldav_node.py | 139 +++++++++---- addons/caldav/caldav_view.xml | 3 +- addons/caldav/calendar.py | 22 +- addons/document/document_directory.py | 48 +---- addons/document/document_view.xml | 3 +- addons/document/nodes.py | 192 +++++++++--------- .../document_ftp/ftpserver/abstracted_fs.py | 35 +--- addons/document_ics/document_demo.xml | 4 +- addons/document_ics/document_ics.py | 34 +--- addons/document_ics/document_view.xml | 28 +-- 10 files changed, 253 insertions(+), 255 deletions(-) diff --git a/addons/caldav/caldav_node.py b/addons/caldav/caldav_node.py index fd17b52f478..6ecfcd30027 100644 --- a/addons/caldav/caldav_node.py +++ b/addons/caldav/caldav_node.py @@ -25,31 +25,60 @@ 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 -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 + 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): + def get_dav_props(self, cr): + res = {} + return res - 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,prop): + return None - def get_data_len(self, cr): - return self.content_length - - 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) + 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)] + if name: + where.append(('name','=',name)) + if not domain: + domain = [] + + fil_obj = dirobj.pool.get('basic.calendar') + ids = fil_obj.search(cr,uid,where,context=ctx) + res = [] + if ids: + for fil in fil_obj.browse(cr,uid,ids,context=ctx): + res.append(node_calendar(fil.name,self,self.context,fil)) + return res + def get_etag(self, cr): """ Get a tag, unique per object + modification. @@ -64,24 +93,64 @@ class node_calendar(object): return str(wtime) def _get_ttag(self, cr): + return 'calendar collection-%d' % self.dir_id + +class node_calendar(nodes.node_class): + our_type = 'file' + def __init__(self,path, parent, context, calendar): + super(node_calendar,self).__init__(path, parent,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 open(self, cr, mode=False): + uid = self.context.uid + if self.type in ('collection','database'): + return False + fobj = self.context._dirobj.pool.get('basic.calendar').browse(cr, uid, self.calendar_id, context=self.context.context) + s = StringIO.StringIO(self.get_data(cr, fobj)) + s.name = self + return s + + def get_dav_props(self, cr): + res = {} + return res + + def get_dav_eprop(self,cr,ns,prop): + return None + + + def get_data(self, cr, fil_obj = None): + uid = self.context.uid + calendar_obj = self.context._dirobj.pool.get('basic.calendar') + return calendar_obj.export_cal(cr, uid, [self.calendar_id]) + + 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): return 'calendar-%d' % self.calendar_id + -class Calendar(osv.osv): - _inherit = 'basic.calendar' + def get_etag(self, cr): + """ Get a tag, unique per object + modification. - def get_calendar_object(self, cr, uid, uri, context=None): - if not uri: - return None - if len(uri) > 1: - 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() + see. http://tools.ietf.org/html/rfc2616#section-13.3.3 """ + return self._get_ttag(cr) + ':' + self._get_wtag(cr) -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4 \ No newline at end of file + 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 152baf6c876..c982d695fac 100644 --- a/addons/caldav/caldav_view.xml +++ b/addons/caldav/caldav_view.xml @@ -17,8 +17,7 @@ Calendar Collections : Tree document.directory - tree - child_ids + tree diff --git a/addons/caldav/calendar.py b/addons/caldav/calendar.py index bcf7eb58d76..6ccd5989d5c 100644 --- a/addons/caldav/calendar.py +++ b/addons/caldav/calendar.py @@ -31,7 +31,7 @@ import pytz import re import tools import time - +import caldav_node try: import vobject @@ -418,7 +418,12 @@ class calendar_collection(osv.osv): } _default = { 'calendar_collection' : 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) calendar_collection() @@ -449,6 +454,19 @@ class Calendar(CalDAV, osv.osv): 'write_date': fields.datetime('Modifided Date'), } + def get_calendar_object(self, cr, uid, uri, context=None): + if not uri: + return None + if len(uri) > 1: + 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) + def export_cal(self, cr, uid, ids, vobj='vevent', context={}): """ Export Calendar @param self: The object pointer diff --git a/addons/document/document_directory.py b/addons/document/document_directory.py index 270f2d9353f..b64ee8df9e2 100644 --- a/addons/document/document_directory.py +++ b/addons/document/document_directory.py @@ -93,8 +93,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 = [ @@ -198,52 +197,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 252b9b36a26..3cc3d139b13 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 a8553426d3f..4957078c442 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): @@ -163,7 +155,76 @@ class node_class(object): def get_dav_eprop(self,cr,ns,prop): return None -class node_dir(node_class): +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): + res = self._child_get(cr) + self._file_get(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 _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,93 +253,20 @@ class node_dir(node_class): print e pass - def children(self,cr): - res = self._child_get(cr) + self._file_get(cr) - return res - + def get_data(self,cr): return '' - 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) - 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): - 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)) - - 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)) - - 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 _child_get(self, cr, name = None): + return super(node_dir,self)._child_get(cr, name, self.dir_id) + def create_child(self,cr,path,data): """ API function to create a child file object and node @@ -601,7 +589,19 @@ 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 fix_ppath(self, cr, fbro): """Sometimes we may init this w/o path, parent. @@ -687,6 +687,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, 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 - - - - - - - - - From a3dab7abb8a0a033827a0fc2bc3e1868544c83a8 Mon Sep 17 00:00:00 2001 From: "Harry (Open ERP)" Date: Tue, 13 Apr 2010 19:34:04 +0530 Subject: [PATCH 009/593] [IMP] caldav bzr revid: hmo@tinyerp.com-20100413140404-bku8u3agsv79evyx --- addons/caldav/__init__.py | 1 + addons/caldav/caldav_fs.py | 48 +++++++++++++++++++++++++++++++-- addons/caldav/caldav_node.py | 7 +++++ addons/caldav/caldav_server.py | 49 ++++++++++++++++++++++++++++++++++ addons/document/nodes.py | 13 +++++++++ 5 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 addons/caldav/caldav_server.py diff --git a/addons/caldav/__init__.py b/addons/caldav/__init__.py index 0a0787feb89..95c230e09f8 100644 --- a/addons/caldav/__init__.py +++ b/addons/caldav/__init__.py @@ -21,6 +21,7 @@ import calendar import caldav_fs +import caldav_server import wizard # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/caldav/caldav_fs.py b/addons/caldav/caldav_fs.py index 1b3d1f4879b..c7505b3846b 100644 --- a/addons/caldav/caldav_fs.py +++ b/addons/caldav/caldav_fs.py @@ -2,7 +2,7 @@ ############################################################################## # # OpenERP, Open Source Management Solution -# Copyright (C) 2004-2009 Tiny SPRL (). +# 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 @@ -19,7 +19,51 @@ # ############################################################################## +from document_webdav import dav_fs +from document_webdav.cache import memoize +class openerp_dav_handler(dav_fs.openerp_dav_handler): + PROPS = {'DAV:': dav_fs.openerp_dav_handler.PROPS['DAV:'] + + ('owner',), + 'http://calendarserver.org/ns/': ('getctag')} + + M_NS = { "DAV:" : dav_fs.openerp_dav_handler.M_NS['DAV:'], + "http://calendarserver.org/ns/" : '_get_dav'} + + @memoize(dav_fs.CACHE_SIZE) + def _get_dav_getctag(self,uri): + print """ return the CTag of an object """ + self.parent.log_message('get ctag: %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_ctag(cr) + cr.close() + return str(result) + + @memoize(dav_fs.CACHE_SIZE) + def _get_dav_owner(self,uri): + print """ return the Owner of an object """ + self.parent.log_message('get ctag: %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_owner(cr) + cr.close() + return str(result) -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/caldav/caldav_node.py b/addons/caldav/caldav_node.py index 6ecfcd30027..311e0fef0c8 100644 --- a/addons/caldav/caldav_node.py +++ b/addons/caldav/caldav_node.py @@ -78,6 +78,9 @@ class node_calendar_collection(nodes.node_dir): res.append(node_calendar(fil.name,self,self.context,fil)) return res + def get_owner(self, cr): + return False + def get_etag(self, cr): """ Get a tag, unique per object + modification. @@ -94,6 +97,10 @@ class node_calendar_collection(nodes.node_dir): def _get_ttag(self, cr): return 'calendar collection-%d' % self.dir_id + + def get_ctag(self, cr): + return self.get_etag(cr) + class node_calendar(nodes.node_class): our_type = 'file' diff --git a/addons/caldav/caldav_server.py b/addons/caldav/caldav_server.py new file mode 100644 index 00000000000..77244e5d764 --- /dev/null +++ b/addons/caldav/caldav_server.py @@ -0,0 +1,49 @@ +# -*- 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 service import http_server +from document_webdav import webdav_server +import tools +from caldav_fs import openerp_dav_handler +from tools.config import config + +class DAVHandler(webdav_server.DAVHandler): + def setup(self): + 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) + + + +handler = webdav_server.handler +if handler: + handler = DAVHandler + handler.debug = config.get_misc('webdav','debug',True) + handler._config = webdav_server.conf + +if http_server.httpd and handler: + for vdir in http_server.httpd.server.vdirs: + print vdir.handler , webdav_server.DAVHandler + if vdir.handler == webdav_server.DAVHandler: + vdir.handler = handler +if http_server.httpsd and handler: + for vdir in http_server.httpsd.server.vdirs: + if vdir.handler == webdav_server.DAVHandler: + vdir.handler = handler diff --git a/addons/document/nodes.py b/addons/document/nodes.py index 4957078c442..527fda3f594 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -289,6 +289,19 @@ class node_dir(node_database): 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 From f2311d256ffe78304388988024ce9b8b3cc4dfef Mon Sep 17 00:00:00 2001 From: "Harry (Open ERP)" Date: Wed, 14 Apr 2010 13:06:16 +0530 Subject: [PATCH 010/593] [REF+IMP] caldav bzr revid: hmo@tinyerp.com-20100414073616-i84h1e197yswuv0v --- addons/caldav/__init__.py | 3 +- addons/caldav/caldav_fs.py | 69 -------------- addons/caldav/caldav_node.py | 129 +++++++++++++++++++++++++-- addons/caldav/caldav_server.py | 49 ---------- addons/caldav/calendar.py | 24 +---- addons/caldav/calendar_collection.py | 80 +++++++++++++++++ addons/document_webdav/dav_fs.py | 30 ++++++- 7 files changed, 230 insertions(+), 154 deletions(-) delete mode 100644 addons/caldav/caldav_fs.py delete mode 100644 addons/caldav/caldav_server.py create mode 100644 addons/caldav/calendar_collection.py diff --git a/addons/caldav/__init__.py b/addons/caldav/__init__.py index 95c230e09f8..cb496d12de0 100644 --- a/addons/caldav/__init__.py +++ b/addons/caldav/__init__.py @@ -20,8 +20,7 @@ ############################################################################## import calendar -import caldav_fs -import caldav_server +import calendar_collection import wizard # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/caldav/caldav_fs.py b/addons/caldav/caldav_fs.py deleted file mode 100644 index c7505b3846b..00000000000 --- a/addons/caldav/caldav_fs.py +++ /dev/null @@ -1,69 +0,0 @@ -# -*- 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 document_webdav import dav_fs -from document_webdav.cache import memoize -class openerp_dav_handler(dav_fs.openerp_dav_handler): - PROPS = {'DAV:': dav_fs.openerp_dav_handler.PROPS['DAV:'] + - ('owner',), - 'http://calendarserver.org/ns/': ('getctag')} - - M_NS = { "DAV:" : dav_fs.openerp_dav_handler.M_NS['DAV:'], - "http://calendarserver.org/ns/" : '_get_dav'} - - @memoize(dav_fs.CACHE_SIZE) - def _get_dav_getctag(self,uri): - print """ return the CTag of an object """ - self.parent.log_message('get ctag: %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_ctag(cr) - cr.close() - return str(result) - - @memoize(dav_fs.CACHE_SIZE) - def _get_dav_owner(self,uri): - print """ return the Owner of an object """ - self.parent.log_message('get ctag: %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_owner(cr) - cr.close() - return str(result) - - - diff --git a/addons/caldav/caldav_node.py b/addons/caldav/caldav_node.py index 311e0fef0c8..9c7c73a098d 100644 --- a/addons/caldav/caldav_node.py +++ b/addons/caldav/caldav_node.py @@ -51,12 +51,42 @@ class node_database(nodes.node_database): res.append(node_calendar_collection(dirr.name,self,self.context,dirr)) return res -class node_calendar_collection(nodes.node_dir): - def get_dav_props(self, cr): - res = {} - return res +class node_calendar_collection(nodes.node_dir): + 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 get_dav_props(self, cr): + return self.PROPS - def get_dav_eprop(self,cr,ns,prop): + 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 + + 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 _child_get(self, cr, name=False, parent_id=False, domain=None): @@ -78,7 +108,7 @@ class node_calendar_collection(nodes.node_dir): res.append(node_calendar(fil.name,self,self.context,fil)) return res - def get_owner(self, cr): + def _get_dav_owner(self, cr): return False @@ -97,9 +127,90 @@ class node_calendar_collection(nodes.node_dir): def _get_ttag(self, cr): return 'calendar collection-%d' % self.dir_id - - def get_ctag(self, cr): - return self.get_etag(cr) + + def _get_dav_getctag(self, cr): + result = self.get_etag(cr) + return str(result) + + + def _get_caldav_calendar_description(self, cr): + dirobj = self.context._dirobj + uid = self.context.uid + ctx = self.context.context.copy() + ctx.update(self.dctx) + ids = [self.dir_id] + res = dirobj.get_description(cr, uid, ids, context=ctx) + return res + + def _get_caldav_calendar_data(self, cr): + return self.get_data(cr) + + def _get_caldav_calendar_home_set(self, cr): + import xml.dom.minidom + import urllib + dirobj = self.context._dirobj + uid = self.context.uid + ctx = self.context.context.copy() + ctx.update(self.dctx) + doc = xml.dom.minidom.getDOMImplementation().createDocument(None, 'href', None) + root_cal_dir = dirobj._get_root_calendar_directory(cr, uid, context=ctx) + huri = doc.createTextNode(urllib.quote('/%s/%s' % (cr.dbname, root_cal_dir))) + 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 + dirobj = self.context._dirobj + uid = self.context.uid + ctx = self.context.context.copy() + ctx.update(self.dctx) + ids = [self.dir_id] + res = collection_obj.get_schedule_inbox_URL(cr, uid, ids, context=ctx) + + 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 _get_caldav_schedule_outbox_URL(self, cr): + import xml.dom.minidom + import urllib + dirobj = self.context._dirobj + uid = self.context.uid + ctx = self.context.context.copy() + ctx.update(self.dctx) + ids = [self.dir_id] + res = collection_obj.get_schedule_outbox_URL(cr, uid, ids, context=ctx) + + 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 class node_calendar(nodes.node_class): diff --git a/addons/caldav/caldav_server.py b/addons/caldav/caldav_server.py deleted file mode 100644 index 77244e5d764..00000000000 --- a/addons/caldav/caldav_server.py +++ /dev/null @@ -1,49 +0,0 @@ -# -*- 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 service import http_server -from document_webdav import webdav_server -import tools -from caldav_fs import openerp_dav_handler -from tools.config import config - -class DAVHandler(webdav_server.DAVHandler): - def setup(self): - 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) - - - -handler = webdav_server.handler -if handler: - handler = DAVHandler - handler.debug = config.get_misc('webdav','debug',True) - handler._config = webdav_server.conf - -if http_server.httpd and handler: - for vdir in http_server.httpd.server.vdirs: - print vdir.handler , webdav_server.DAVHandler - if vdir.handler == webdav_server.DAVHandler: - vdir.handler = handler -if http_server.httpsd and handler: - for vdir in http_server.httpsd.server.vdirs: - if vdir.handler == webdav_server.DAVHandler: - vdir.handler = handler diff --git a/addons/caldav/calendar.py b/addons/caldav/calendar.py index 6ccd5989d5c..4f672838155 100644 --- a/addons/caldav/calendar.py +++ b/addons/caldav/calendar.py @@ -31,7 +31,6 @@ import pytz import re import tools import time -import caldav_node try: import vobject @@ -410,23 +409,6 @@ class CalDAV(object): self.ical_reset('value') return res -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 _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) - -calendar_collection() - class Calendar(CalDAV, osv.osv): _name = 'basic.calendar' _calname = 'calendar' @@ -445,10 +427,10 @@ class Calendar(CalDAV, osv.osv): 'vtimezone': None, # Use: O-n, Type: Collection of Timezone class } _columns = { - 'name': fields.char("Name", size=64), - 'collection_id': fields.many2one('document.directory', 'Collection', \ - required=True), + 'name': fields.char("Name", size=64), 'user_id': fields.many2one('res.users', 'Owner'), + 'collection_id': fields.many2one('document.directory', 'Collection', \ + required=True), 'line_ids': fields.one2many('basic.calendar.lines', 'calendar_id', 'Calendar Lines'), 'create_date': fields.datetime('Created Date'), 'write_date': fields.datetime('Modifided Date'), 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/document_webdav/dav_fs.py b/addons/document_webdav/dav_fs.py index 9be77c50daa..41a65c73bfb 100644 --- a/addons/document_webdav/dav_fs.py +++ b/addons/document_webdav/dav_fs.py @@ -63,7 +63,17 @@ class openerp_dav_handler(dav_interface): self.verbose = verbose def get_propnames(self,uri): - props = self.PROPS + 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_dav_lockdiscovery(self, uri): @@ -71,7 +81,8 @@ class openerp_dav_handler(dav_interface): def _get_dav_supportedlock(self, uri): raise DAV_NotFound - def get_prop(self,uri,ns,propname): + + def get_prop(self, uri, ns, propname): """ return the value of a given property uri -- uri of the object to get the property of @@ -79,8 +90,19 @@ class openerp_dav_handler(dav_interface): pname -- name of the property """ if self.M_NS.has_key(ns): - return dav_interface.get_prop(self,uri,ns,propname) - raise DAV_NotFound + 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 get_db(self,uri): names=self.uri2local(uri).split('/') From 4cc0c97a1bfb43a8fed4a22ab38d853af273e0ee Mon Sep 17 00:00:00 2001 From: "Harry (Open ERP)" Date: Wed, 14 Apr 2010 13:09:25 +0530 Subject: [PATCH 011/593] [FIX] caldav bzr revid: hmo@tinyerp.com-20100414073925-3qo19wm9dnv2a34t --- addons/caldav/caldav_node.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/caldav/caldav_node.py b/addons/caldav/caldav_node.py index 9c7c73a098d..365cf6ab1ee 100644 --- a/addons/caldav/caldav_node.py +++ b/addons/caldav/caldav_node.py @@ -184,7 +184,7 @@ class node_calendar_collection(nodes.node_dir): ctx = self.context.context.copy() ctx.update(self.dctx) ids = [self.dir_id] - res = collection_obj.get_schedule_inbox_URL(cr, uid, ids, context=ctx) + res = dirobj.get_schedule_inbox_URL(cr, uid, ids, context=ctx) doc = xml.dom.minidom.getDOMImplementation().createDocument(None, 'href', None) href = doc.documentElement @@ -203,7 +203,7 @@ class node_calendar_collection(nodes.node_dir): ctx = self.context.context.copy() ctx.update(self.dctx) ids = [self.dir_id] - res = collection_obj.get_schedule_outbox_URL(cr, uid, ids, context=ctx) + res = dirobj.get_schedule_outbox_URL(cr, uid, ids, context=ctx) doc = xml.dom.minidom.getDOMImplementation().createDocument(None, 'href', None) href = doc.documentElement From 013abc77091ed4144d95dadfad7655fa8c5c80fe Mon Sep 17 00:00:00 2001 From: "Harry (Open ERP)" Date: Wed, 14 Apr 2010 17:19:40 +0530 Subject: [PATCH 012/593] [FIX] document: MKCOL bzr revid: hmo@tinyerp.com-20100414114940-jkhi3ds5eskjxhej --- addons/document/nodes.py | 49 ++++++++++++++++++++++++++++++-- addons/document_webdav/dav_fs.py | 40 +++++++------------------- 2 files changed, 57 insertions(+), 32 deletions(-) diff --git a/addons/document/nodes.py b/addons/document/nodes.py index 527fda3f594..b35dd1ed1f2 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -254,8 +254,8 @@ class node_dir(node_database): pass - def get_data(self,cr): - return '' + #def get_data(self,cr): + # return '' @@ -266,6 +266,26 @@ class node_dir(node_database): def _child_get(self, cr, name = None): return super(node_dir,self)._child_get(cr, name, self.dir_id) + + 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) + obj = dirobj.browse(cr, uid, self.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' : obj and obj.id or False + } + + return dirobj.create(cr, uid, val) def create_child(self,cr,path,data): @@ -556,6 +576,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 diff --git a/addons/document_webdav/dav_fs.py b/addons/document_webdav/dav_fs.py index 41a65c73bfb..36b7313a0fa 100644 --- a/addons/document_webdav/dav_fs.py +++ b/addons/document_webdav/dav_fs.py @@ -68,7 +68,7 @@ class openerp_dav_handler(dav_interface): if uri[-1]=='/':uri=uri[:-1] cr, uid, pool, dbname, uri2 = self.get_cr(uri) if not dbname: - cr.close() + if cr: cr.close() return props node = self.uri2object(cr, uid, pool, uri2) if node: @@ -94,7 +94,7 @@ class openerp_dav_handler(dav_interface): if uri[-1]=='/':uri=uri[:-1] cr, uid, pool, dbname, uri2 = self.get_cr(uri) if not dbname: - cr.close() + if cr: cr.close() raise DAV_NotFound node = self.uri2object(cr, uid, pool, uri2) if not node: @@ -360,40 +360,20 @@ class openerp_dav_handler(dav_interface): 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 From 7aea663bb63476b3725b4d56cdfffabbfb61aaf2 Mon Sep 17 00:00:00 2001 From: "Harry (Open ERP)" Date: Thu, 15 Apr 2010 13:28:09 +0530 Subject: [PATCH 013/593] [IMP] caldav: supporting caldav protocall bzr revid: hmo@tinyerp.com-20100415075809-edzwop5gzzdi80jj --- addons/caldav/__init__.py | 1 + addons/caldav/caldav.py | 40 +++++ addons/caldav/caldav_node.py | 243 ++++++++++++++++++++----------- addons/caldav/caldav_view.xml | 6 +- addons/caldav/calendar.py | 2 + addons/document/nodes.py | 183 +++-------------------- addons/document_webdav/dav_fs.py | 10 +- 7 files changed, 228 insertions(+), 257 deletions(-) create mode 100644 addons/caldav/caldav.py diff --git a/addons/caldav/__init__.py b/addons/caldav/__init__.py index cb496d12de0..3c7bf49a8fa 100644 --- a/addons/caldav/__init__.py +++ b/addons/caldav/__init__.py @@ -21,6 +21,7 @@ import calendar import calendar_collection +import caldav import wizard # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/caldav/caldav.py b/addons/caldav/caldav.py new file mode 100644 index 00000000000..224d1ba176c --- /dev/null +++ b/addons/caldav/caldav.py @@ -0,0 +1,40 @@ +# -*- 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 document_webdav import webdav +import tools +from DAV.propfind import PROPFIND + + +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('/') + if uri[-1] in ('Calendars'): + 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 + +PROPFIND.mk_prop_response = mk_prop_response diff --git a/addons/caldav/caldav_node.py b/addons/caldav/caldav_node.py index 365cf6ab1ee..ffc300836b7 100644 --- a/addons/caldav/caldav_node.py +++ b/addons/caldav/caldav_node.py @@ -54,16 +54,10 @@ class node_database(nodes.node_database): class node_calendar_collection(nodes.node_dir): 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 get_dav_props(self, cr): return self.PROPS @@ -89,6 +83,59 @@ class node_calendar_collection(nodes.node_dir): print "Exception:", e return None + def _file_get(self,cr, nodename=False): + return [] + + + 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': + ids = [] + 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[-1] + calendar = calendar_obj.name_search(cr, uid, caluri) + if calendar: + calendar_id, calendar_name = calendar[0] + ids.append(calendar_id) + return [('id', 'in', ids)] + return res + def _child_get(self, cr, name=False, parent_id=False, domain=None): dirobj = self.context._dirobj uid = self.context.uid @@ -99,7 +146,7 @@ class node_calendar_collection(nodes.node_dir): 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 = [] @@ -130,31 +177,110 @@ class node_calendar_collection(nodes.node_dir): def _get_dav_getctag(self, cr): result = self.get_etag(cr) - return str(result) + return str(result) + + +class 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, calendar): + super(node_calendar,self).__init__(path, parent,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 open(self, cr, mode=False): + uid = self.context.uid + if self.type in ('collection','database'): + return False + fobj = self.context._dirobj.pool.get('basic.calendar').browse(cr, uid, self.calendar_id, context=self.context.context) + s = StringIO.StringIO(self.get_data(cr, fobj)) + 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') + return calendar_obj.export_cal(cr, uid, [self.calendar_id]) + + 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): + return 'calendar-%d' % self.calendar_id + + + + def _get_caldav_calendar_data(self, cr): + res = self.get_data(cr) def _get_caldav_calendar_description(self, cr): - dirobj = self.context._dirobj uid = self.context.uid + calendar_obj = self.context._dirobj.pool.get('basic.calendar') ctx = self.context.context.copy() - ctx.update(self.dctx) - ids = [self.dir_id] - res = dirobj.get_description(cr, uid, ids, context=ctx) - return res - - def _get_caldav_calendar_data(self, cr): - return self.get_data(cr) + 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 - dirobj = self.context._dirobj + import urllib uid = self.context.uid ctx = self.context.context.copy() ctx.update(self.dctx) doc = xml.dom.minidom.getDOMImplementation().createDocument(None, 'href', None) - root_cal_dir = dirobj._get_root_calendar_directory(cr, uid, context=ctx) - huri = doc.createTextNode(urllib.quote('/%s/%s' % (cr.dbname, root_cal_dir))) + + 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) @@ -178,14 +304,13 @@ class node_calendar_collection(nodes.node_dir): def _get_caldav_schedule_inbox_URL(self, cr): import xml.dom.minidom - import urllib - dirobj = self.context._dirobj + import urllib uid = self.context.uid ctx = self.context.context.copy() ctx.update(self.dctx) - ids = [self.dir_id] - res = dirobj.get_schedule_inbox_URL(cr, uid, ids, context=ctx) - + 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' @@ -196,67 +321,7 @@ class node_calendar_collection(nodes.node_dir): def _get_caldav_schedule_outbox_URL(self, cr): - import xml.dom.minidom - import urllib - dirobj = self.context._dirobj - uid = self.context.uid - ctx = self.context.context.copy() - ctx.update(self.dctx) - ids = [self.dir_id] - res = dirobj.get_schedule_outbox_URL(cr, uid, ids, context=ctx) - - 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 - - -class node_calendar(nodes.node_class): - our_type = 'file' - def __init__(self,path, parent, context, calendar): - super(node_calendar,self).__init__(path, parent,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 open(self, cr, mode=False): - uid = self.context.uid - if self.type in ('collection','database'): - return False - fobj = self.context._dirobj.pool.get('basic.calendar').browse(cr, uid, self.calendar_id, context=self.context.context) - s = StringIO.StringIO(self.get_data(cr, fobj)) - s.name = self - return s - - def get_dav_props(self, cr): - res = {} - return res - - def get_dav_eprop(self,cr,ns,prop): - return None - - - def get_data(self, cr, fil_obj = None): - uid = self.context.uid - calendar_obj = self.context._dirobj.pool.get('basic.calendar') - return calendar_obj.export_cal(cr, uid, [self.calendar_id]) - - 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): - return 'calendar-%d' % self.calendar_id - + return _get_caldav_schedule_inbox_URL(cr) def get_etag(self, cr): @@ -270,5 +335,5 @@ class node_calendar(nodes.node_class): 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) + 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 c982d695fac..4c1003023ae 100644 --- a/addons/caldav/caldav_view.xml +++ b/addons/caldav/caldav_view.xml @@ -70,7 +70,8 @@ form
- + + @@ -112,7 +113,8 @@ tree - + + diff --git a/addons/caldav/calendar.py b/addons/caldav/calendar.py index 4f672838155..52a1e8a56e3 100644 --- a/addons/caldav/calendar.py +++ b/addons/caldav/calendar.py @@ -431,6 +431,8 @@ class Calendar(CalDAV, osv.osv): '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'), diff --git a/addons/document/nodes.py b/addons/document/nodes.py index b35dd1ed1f2..e10d0934eef 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -109,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 @@ -155,6 +155,9 @@ class node_class(object): def get_dav_eprop(self,cr,ns,prop): return None + def get_domain(self, cr, filters): + return [] + class node_database(node_class): """ A node representing the database directory @@ -163,12 +166,12 @@ class node_database(node_class): def __init__(self, path=[], parent=False, context=None): super(node_database,self).__init__(path, parent, context) - def children(self,cr): - res = self._child_get(cr) + self._file_get(cr) + def children(self, cr, domain=None): + res = self._child_get(cr, domain=domain) + self._file_get(cr) return res - 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=None) if res: return res[0] res = self._file_get(cr,name) @@ -264,8 +267,8 @@ class node_dir(node_database): - def _child_get(self, cr, name = None): - return super(node_dir,self)._child_get(cr, name, 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 create_child_collection(self, cr, objname): object2 = False @@ -359,16 +362,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. @@ -455,11 +458,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) @@ -514,7 +517,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() @@ -792,147 +795,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_webdav/dav_fs.py b/addons/document_webdav/dav_fs.py index 36b7313a0fa..1b77ea67832 100644 --- a/addons/document_webdav/dav_fs.py +++ b/addons/document_webdav/dav_fs.py @@ -135,8 +135,8 @@ class openerp_dav_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) @@ -158,14 +158,16 @@ class openerp_dav_handler(dav_interface): fp = '/'.join(fp) else: fp = None - for d in node.children(cr): + 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) ) if cr: cr.close() - return result def uri2local(self, uri): From 8758226edddcceb20617b0d9099fb8b61b4fc374 Mon Sep 17 00:00:00 2001 From: "rpa (Open ERP)" Date: Thu, 15 Apr 2010 15:50:26 +0530 Subject: [PATCH 014/593] [IMP]:caldav: If we clear some value from calendar client it should reflect on OpenERP so removed checking for value availability bzr revid: rpa@tinyerp.com-20100415102026-y1cu3ijg8nqigpa8 --- addons/caldav/calendar.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/addons/caldav/calendar.py b/addons/caldav/calendar.py index 52a1e8a56e3..58a51c11c85 100644 --- a/addons/caldav/calendar.py +++ b/addons/caldav/calendar.py @@ -160,8 +160,7 @@ def map_data(cr, uid, obj): if field_type == 'timedelta': if map_val: vals[field] = (map_val.seconds/float(86400) + map_val.days) - if map_val: - vals[field] = map_val + vals[field] = map_val return vals class CalDAV(object): From 7558802e883a3614f6a07764c7ee9a5723061358 Mon Sep 17 00:00:00 2001 From: "Harry (Open ERP)" Date: Thu, 15 Apr 2010 16:26:05 +0530 Subject: [PATCH 015/593] [IMP] caldav bzr revid: hmo@tinyerp.com-20100415105605-18h6tgny6lu21j85 --- addons/caldav/caldav.py | 5 +++-- addons/caldav/caldav_node.py | 17 +++++++++-------- addons/document/nodes.py | 7 +++++-- addons/document_webdav/dav_fs.py | 4 ++-- 4 files changed, 19 insertions(+), 14 deletions(-) diff --git a/addons/caldav/caldav.py b/addons/caldav/caldav.py index 224d1ba176c..730bfda816a 100644 --- a/addons/caldav/caldav.py +++ b/addons/caldav/caldav.py @@ -22,8 +22,9 @@ from document_webdav import webdav import tools 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) diff --git a/addons/caldav/caldav_node.py b/addons/caldav/caldav_node.py index ffc300836b7..d747078081d 100644 --- a/addons/caldav/caldav_node.py +++ b/addons/caldav/caldav_node.py @@ -62,7 +62,7 @@ class node_calendar_collection(nodes.node_dir): def get_dav_props(self, cr): return self.PROPS - def get_dav_eprop(self,cr, ns, propname): + def get_dav_eprop(self,cr, ns, propname): if self.M_NS.has_key(ns): prefix = self.M_NS[ns] else: @@ -84,7 +84,7 @@ class node_calendar_collection(nodes.node_dir): return None def _file_get(self,cr, nodename=False): - return [] + return [] def get_domain(self, cr, filters): @@ -231,7 +231,7 @@ class node_calendar(nodes.node_class): try: m = getattr(self, mname) - r = m(cr) + r = m(cr) return r except AttributeError, e: print 'Property %s not supported' % propname @@ -239,10 +239,11 @@ class node_calendar(nodes.node_class): return None - def get_data(self, cr, fil_obj = None): + def get_data(self, cr, fil_obj = None): uid = self.context.uid calendar_obj = self.context._dirobj.pool.get('basic.calendar') - return calendar_obj.export_cal(cr, uid, [self.calendar_id]) + res = calendar_obj.export_cal(cr, uid, [self.calendar_id]) + return res def get_data_len(self, cr, fil_obj = None): return self.content_length @@ -257,9 +258,9 @@ class node_calendar(nodes.node_class): - def _get_caldav_calendar_data(self, cr): - res = self.get_data(cr) - + def _get_caldav_calendar_data(self, cr): + return self.get_data(cr) + def _get_caldav_calendar_description(self, cr): uid = self.context.uid diff --git a/addons/document/nodes.py b/addons/document/nodes.py index e10d0934eef..85c71f88b6d 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -257,8 +257,11 @@ class node_dir(node_database): pass - #def get_data(self,cr): - # return '' + def get_data(self,cr): + res = '' + for child in self.children(cr): + res += child.get_data(cr) + return res diff --git a/addons/document_webdav/dav_fs.py b/addons/document_webdav/dav_fs.py index 1b77ea67832..d8b369b5ecf 100644 --- a/addons/document_webdav/dav_fs.py +++ b/addons/document_webdav/dav_fs.py @@ -100,8 +100,8 @@ class openerp_dav_handler(dav_interface): if not node: cr.close() raise DAV_NotFound - res = node.get_dav_eprop(cr,ns,propname) - cr.close() + res = node.get_dav_eprop(cr,ns,propname) + cr.close() return res def get_db(self,uri): From e669746dbea2e0e9a51b4199d6bc0bb7bdf0b61c Mon Sep 17 00:00:00 2001 From: "Harry (Open ERP)" Date: Tue, 20 Apr 2010 11:27:54 +0530 Subject: [PATCH 016/593] [FIX] crm: compute days bzr revid: hmo@tinyerp.com-20100420055754-yjwby6jsw08z9uzp --- addons/crm/crm_lead.py | 2 +- addons/crm/crm_opportunity.py | 2 +- addons/project_issue/project_issue.py | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py index 546431ac7c7..e00813af011 100644 --- a/addons/crm/crm_lead.py +++ b/addons/crm/crm_lead.py @@ -85,7 +85,7 @@ class crm_lead(osv.osv): resource_id = resource_ids[0] duration = float(ans.days) - if lead.section_id.resource_calendar_id: + if lead.section_id and lead.section_id.resource_calendar_id: duration = float(ans.days) * 24 new_dates = cal_obj.interval_get(cr, uid, diff --git a/addons/crm/crm_opportunity.py b/addons/crm/crm_opportunity.py index 3e1fc24a803..4c84774e949 100644 --- a/addons/crm/crm_opportunity.py +++ b/addons/crm/crm_opportunity.py @@ -92,7 +92,7 @@ class crm_opportunity(osv.osv): resource_id = resource_ids[0] duration = float(ans.days) - if opportunity.section_id.resource_calendar_id: + if opportunity.section_id and opportunity.section_id.resource_calendar_id: duration = float(ans.days) * 24 new_dates = cal_obj.interval_get(cr, uid, diff --git a/addons/project_issue/project_issue.py b/addons/project_issue/project_issue.py index 3f35913ae75..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) or resource_ids[0] + 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, From 96ce0830ced03ea5080cc05214900e747c7b584c Mon Sep 17 00:00:00 2001 From: "Harry (Open ERP)" Date: Tue, 20 Apr 2010 11:58:52 +0530 Subject: [PATCH 017/593] [IMP] caldav: add type of event and todo calendars data bzr revid: hmo@tinyerp.com-20100420062852-vnlur0pvgd1wux18 --- addons/crm_caldav/crm_caldav_data.xml | 1 + addons/project_caldav/project_caldav_data.xml | 1 + 2 files changed, 2 insertions(+) diff --git a/addons/crm_caldav/crm_caldav_data.xml b/addons/crm_caldav/crm_caldav_data.xml index ca5f38f65b8..0f160bfd85d 100644 --- a/addons/crm_caldav/crm_caldav_data.xml +++ b/addons/crm_caldav/crm_caldav_data.xml @@ -7,6 +7,7 @@ Meetings + vevent diff --git a/addons/project_caldav/project_caldav_data.xml b/addons/project_caldav/project_caldav_data.xml index 59c9e5bd9f7..af0088cf72f 100644 --- a/addons/project_caldav/project_caldav_data.xml +++ b/addons/project_caldav/project_caldav_data.xml @@ -5,6 +5,7 @@ Todo + vtodo From 5fd39ca156ef78796aa6675041903858a1982189 Mon Sep 17 00:00:00 2001 From: "Rvo (Open ERP)" Date: Wed, 21 Apr 2010 16:44:35 +0530 Subject: [PATCH 018/593] [ADD]: access rights for osv_memory and osv_osv objects bzr revid: rvo@tinyerp.co.in-20100421111435-v4u8cemnfir19kh0 --- addons/account/security/ir.model.access.csv | 10 ++++++++++ addons/auction/security/ir.model.access.csv | 2 ++ addons/delivery/security/ir.model.access.csv | 2 ++ addons/event/security/ir.model.access.csv | 5 +++++ addons/fetchmail/__openerp__.py | 3 ++- addons/fetchmail/security/ir.model.access.csv | 4 ++++ addons/hr_evaluation/security/ir.model.access.csv | 2 ++ addons/hr_expense/security/ir.model.access.csv | 1 + addons/hr_recruitment/security/ir.model.access.csv | 1 + addons/html_view/security/ir.model.access.csv | 2 ++ addons/l10n_be/security/ir.model.access.csv | 4 ++++ addons/membership/security/ir.model.access.csv | 2 ++ addons/mrp/security/ir.model.access.csv | 1 + addons/point_of_sale/security/ir.model.access.csv | 3 ++- addons/product_margin/security/ir.model.access.csv | 2 ++ .../profile_manufacturing/security/ir.model.access.csv | 2 +- addons/profile_tools/security/ir.model.access.csv | 2 ++ addons/project/security/ir.model.access.csv | 1 + addons/project_messages/security/ir.model.access.csv | 2 ++ addons/project_timesheet/security/ir.model.access.csv | 1 - addons/purchase/security/ir.model.access.csv | 1 + addons/smtpclient/security/ir.model.access.csv | 2 ++ addons/stock/security/ir.model.access.csv | 8 +++++--- 23 files changed, 56 insertions(+), 7 deletions(-) create mode 100644 addons/fetchmail/security/ir.model.access.csv create mode 100644 addons/html_view/security/ir.model.access.csv create mode 100644 addons/product_margin/security/ir.model.access.csv create mode 100644 addons/profile_tools/security/ir.model.access.csv create mode 100644 addons/project_messages/security/ir.model.access.csv diff --git a/addons/account/security/ir.model.access.csv b/addons/account/security/ir.model.access.csv index 656e05e2a9b..68292cca244 100644 --- a/addons/account/security/ir.model.access.csv +++ b/addons/account/security/ir.model.access.csv @@ -95,3 +95,13 @@ "access_report_account_type_sales","report.account_type.sales","model_report_account_type_sales","account.group_account_manager",1,0,0,0 "access_report_account_sales","report.account.sales","model_report_account_sales","account.group_account_manager",1,0,0,0 "access_account_invoice_report","account.invoice.report","model_account_invoice_report","account.group_account_manager",1,0,0,0 +"access_project_account_analytic_line","project.account.analytic.line","model_project_account_analytic_line","account.group_account_manager",1,1,1,1 +"access_account_move_line_reconcile_select","account.move.line.reconcile.select","model_account_move_line_reconcile_select","account.group_account_manager",1,1,1,1 +"access_account_move_line_unreconcile_select","account.move.line.unreconcile.select","model_account_move_line_unreconcile_select","account.group_account_manager",1,1,1,1 +"access_account_invoice_refund","account.invoice.refund","model_account_invoice_refund","account.group_account_manager",1,1,1,1 +"access_account_move_journal","account.move.journal","model_account_move_journal","account.group_account_manager",1,1,1,1 +"access_account_move_bank_reconcile","account.move.bank.reconcile","model_account_move_bank_reconcile","account.group_account_manager",1,1,1,1 +"access_account_subscription_generate","account.subscription.generate","model_account_subscription_generate","account.group_account_manager",1,1,1,1 +"access_account_period_close","account.period.close","model_account_period_close","account.group_account_manager",1,1,1,1 +"access_account_fiscalyear_close_state","account.fiscalyear.close.state","model_account_fiscalyear_close_state","account.group_account_manager",1,1,1,1 +"access_account_chart","account.chart","model_account_chart","account.group_account_manager",1,1,1,1 diff --git a/addons/auction/security/ir.model.access.csv b/addons/auction/security/ir.model.access.csv index 1c1df0828ca..cc3a2e304f7 100644 --- a/addons/auction/security/ir.model.access.csv +++ b/addons/auction/security/ir.model.access.csv @@ -32,3 +32,5 @@ "acess_auction_payer","auction.payer","model_auction_payer","base.group_user",1,0,0,0 "access_auction_pay_sel","auction.pay.sel","model_auction_payer_sel","base.group_user",1,0,0,0 "acess_auction_taken","auction.taken","model_auction_taken","base.group_user",1,0,0,0 +"access_auction_catalog_flagey","auction.catalog.flagey","model_auction_catalog_flagey","base.group_user",1,0,0,0 +"access_auction_lots_buyer_map_user","auction_lots_buyer_map_user","model_auction_lots_buyer_map","base.group_user",1,0,0,0 diff --git a/addons/delivery/security/ir.model.access.csv b/addons/delivery/security/ir.model.access.csv index 948a85c35c7..e459c423b59 100644 --- a/addons/delivery/security/ir.model.access.csv +++ b/addons/delivery/security/ir.model.access.csv @@ -7,3 +7,5 @@ "access_delivery_grid_line_manager","delivery.grid.line","model_delivery_grid_line","sale.group_sale_manager",1,1,1,1 "access_delivery_carrier_partner_manager","delivery.carrier partner_manager","model_delivery_carrier","base.group_partner_manager",1,0,0,0 "access_delivery_carrier_stock_worker","delivery.carrier stock_worker","model_delivery_carrier","stock.group_stock_user",1,0,0,0 +"access_delivery_sale_order_manager","delivery.sale.order","model_delivery_sale_order","sale.group_sale_manager",1,1,1,1 +"access_delivery_sale_order","delivery.sale.order","model_delivery_sale_order","sale.group_sale_user",1,0,0,0 diff --git a/addons/event/security/ir.model.access.csv b/addons/event/security/ir.model.access.csv index 98b262ea5f3..9eccb065448 100644 --- a/addons/event/security/ir.model.access.csv +++ b/addons/event/security/ir.model.access.csv @@ -5,3 +5,8 @@ "access_event_registration","event.registration","model_event_registration","crm.group_crm_user",1,1,1,1 "access_report_event_registration","report.event.registration","model_report_event_registration","crm.group_crm_user",1,0,0,0 "access_report_event_type_registration","report.event.type.registration","model_report_event_type_registration","crm.group_crm_user",1,0,0,0 +"access_event_confirm_registration","event.confirm.registration","model_event_confirm_registration","crm.group_crm_user",1,0,0,0 +"access_event_confirm_registration_manager","event.confirm.registration manager","model_event_confirm_registration","crm.group_crm_manager",1,1,1,1 +"access_event_make_invoice","event.make.invoice","model_event_make_invoice","crm.group_crm_user",1,1,1,1 +"access_event_partners_list","event.partners.list","model_event_partners_list","crm.group_crm_user",1,0,0,0 +"access_event_registration_list","event.registration.list","model_event_registration_list","crm.group_crm_user",1,0,0,0 diff --git a/addons/fetchmail/__openerp__.py b/addons/fetchmail/__openerp__.py index fc2b245f11f..fe0608005e3 100644 --- a/addons/fetchmail/__openerp__.py +++ b/addons/fetchmail/__openerp__.py @@ -38,7 +38,8 @@ 'init_xml': [], 'update_xml': [ "fetchmail_view.xml", - "fetchmail_data.xml" + "fetchmail_data.xml", + 'security/ir.model.access.csv', ], 'demo_xml': [ diff --git a/addons/fetchmail/security/ir.model.access.csv b/addons/fetchmail/security/ir.model.access.csv new file mode 100644 index 00000000000..907aa187913 --- /dev/null +++ b/addons/fetchmail/security/ir.model.access.csv @@ -0,0 +1,4 @@ +"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" +"access_email_server","email.server","model_email_server",,1,1,1,1 +"access_mail_server_history","mail.server.history","model_mail_server_history",,1,1,1,1 +"access_email_server_tools","email.server.tools","model_email_server_tools",,1,1,1,1 diff --git a/addons/hr_evaluation/security/ir.model.access.csv b/addons/hr_evaluation/security/ir.model.access.csv index 3f3948c00db..e43c73ed108 100644 --- a/addons/hr_evaluation/security/ir.model.access.csv +++ b/addons/hr_evaluation/security/ir.model.access.csv @@ -8,3 +8,5 @@ "access_hr_evaluation_interview","hr.evaluation.interview","model_hr_evaluation_interview","hr.group_hr_user",1,0,0,0 "access_hr_evaluation_interview_manager","hr.evaluation.interview","model_hr_evaluation_interview","hr.group_hr_manager",1,1,1,1 "access_hr_evaluation_report","hr.evaluation.report","model_hr_evaluation_report",,1,0,0,0 +"access_hr_evaluation_reminder_manager","hr.evaluation.reminder","model_hr_evaluation_reminder","hr.group_hr_manager",1,1,1,1 +"access_hr_evaluation_reminder","hr.evaluation.reminder","model_hr_evaluation_reminder","hr.group_hr_user",1,0,0,0 diff --git a/addons/hr_expense/security/ir.model.access.csv b/addons/hr_expense/security/ir.model.access.csv index aa00dab4fa1..43ae736b9ff 100644 --- a/addons/hr_expense/security/ir.model.access.csv +++ b/addons/hr_expense/security/ir.model.access.csv @@ -1,3 +1,4 @@ "id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" "access_hr_expense_expense","hr.expense.expense","model_hr_expense_expense","hr.group_hr_user",1,1,1,1 "access_hr_expense_line","hr.expense.line","model_hr_expense_line","hr.group_hr_user",1,1,1,1 +"access_hr_expense_report","hr.expense.report","model_hr_expense_report","hr.group_hr_user",1,0,0,0 diff --git a/addons/hr_recruitment/security/ir.model.access.csv b/addons/hr_recruitment/security/ir.model.access.csv index 3e611ae02e8..f8898499c4d 100644 --- a/addons/hr_recruitment/security/ir.model.access.csv +++ b/addons/hr_recruitment/security/ir.model.access.csv @@ -1,2 +1,3 @@ "id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" "access_hr_applicant","hr.applicant","model_hr_applicant","hr.group_hr_manager",1,1,1,1 +"access_hr_recruitment_report","hr.recruitment.report","model_hr_recruitment_report",,1,0,0,0 diff --git a/addons/html_view/security/ir.model.access.csv b/addons/html_view/security/ir.model.access.csv new file mode 100644 index 00000000000..0b1057cfd1e --- /dev/null +++ b/addons/html_view/security/ir.model.access.csv @@ -0,0 +1,2 @@ +"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" +"access_html_view","html.view","model_html_view",,1,0,0,0 diff --git a/addons/l10n_be/security/ir.model.access.csv b/addons/l10n_be/security/ir.model.access.csv index daadf3d4b22..fbb5c74c18c 100644 --- a/addons/l10n_be/security/ir.model.access.csv +++ b/addons/l10n_be/security/ir.model.access.csv @@ -1,2 +1,6 @@ "id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" "access_vat_listing_clients","vat.listing.clients","model_vat_listing_clients",,1,1,1,1 +"access_partner_vat_intra","partner.vat.intra","model_partner_vat_intra",,1,1,1,1 +"access_partner_vat","partner.vat","model_partner_vat",,1,1,1,1 +"access_partner_vat_list","partner.vat.list","model_partner_vat_list",,1,1,1,1 +"access_l1on_be_vat_declaration","l1on_be.vat.declaration","model_l1on_be_vat_declaration",,1,1,1,1 diff --git a/addons/membership/security/ir.model.access.csv b/addons/membership/security/ir.model.access.csv index de35a7c8adc..cb722db567a 100644 --- a/addons/membership/security/ir.model.access.csv +++ b/addons/membership/security/ir.model.access.csv @@ -3,3 +3,5 @@ "access_membership_membership_line","membership.membership_line","model_membership_membership_line",,1,0,0,0 "access_report_partner_member_year","report.partner_member.year","model_report_partner_member_year","base.group_partner_manager",1,0,0,0 "access_report_partner_member_year_new","report.partner_member.year_new","model_report_partner_member_year_new","base.group_partner_manager",1,0,0,0 +"access_membership_invoice","membership.invoice","model_membership_invoice",,1,1,1,1 +"access_membership_unpaid_invoice","membership.unpaid.invoice","model_membership_unpaid_invoice",,1,0,0,0 diff --git a/addons/mrp/security/ir.model.access.csv b/addons/mrp/security/ir.model.access.csv index 48f27679435..b90c2bae7db 100644 --- a/addons/mrp/security/ir.model.access.csv +++ b/addons/mrp/security/ir.model.access.csv @@ -41,3 +41,4 @@ "access_report_mrp_inout","report.mrp.inout","model_report_mrp_inout","mrp.group_mrp_manager",1,0,0,0 "access_mrp_product_price","mrp.product_price","model_mrp_product_price","mrp.group_mrp_manager",1,0,0,0 "access_mrp_workcenter_load","mrp.workcenter.load","model_mrp_workcenter_load","mrp.group_mrp_manager",1,0,0,0 +"access_mrp_procurement_compute","mrp.procurement.compute","model_mrp_procurement_compute","mrp.group_mrp_user",1,0,0,0 diff --git a/addons/point_of_sale/security/ir.model.access.csv b/addons/point_of_sale/security/ir.model.access.csv index 0cfc0fae9b7..a6ec1fa0507 100644 --- a/addons/point_of_sale/security/ir.model.access.csv +++ b/addons/point_of_sale/security/ir.model.access.csv @@ -29,7 +29,7 @@ "access_pos_box_out","pos.box.out","model_pos_box_out","point_of_sale.group_pos_user",1,0,0,0 "access_pos_details ","pos.details ","model_pos_details","point_of_sale.group_pos_user",1,0,0,0 "access_pos_sale_user","pos.sale.user","model_pos_sale_user","point_of_sale.group_pos_user",1,0,0,0 -"access_pos_sale_user","pos.sales.user.today","model_pos_sales_user_today","point_of_sale.group_pos_user",1,0,0,0 +"access_pos_sale_user_today","pos.sales.user.today","model_pos_sales_user_today","point_of_sale.group_pos_user",1,0,0,0 "access_pos_sales_user_today_current_user","pos.sales.user.today.current_user","model_pos_sales_user_today_current_user","point_of_sale.group_pos_user",1,0,0,0 "access_all_closed_cashbox_of_the_day","all.closed.cashbox.of.the.day","model_all_closed_cashbox_of_the_day","point_of_sale.group_pos_user",1,0,0,0 "access_pos_receipt","pos.receipt ","model_pos_receipt","point_of_sale.group_pos_user",1,0,0,0 @@ -37,3 +37,4 @@ "access_pos_payment_report_date ","pos.payment.report.date ","model_pos_payment_report_date","point_of_sale.group_pos_user",1,0,0,0 "access_pos_make_payment","pos.make.payment","model_pos_make_payment","point_of_sale.group_pos_user",1,0,0,0 "access_pos_scan_product","pos.scan.product","model_pos_scan_product","point_of_sale.group_pos_user",1,0,0,0 +"access_pos_payment_report","pos.payment.report","model_pos_payment_report","point_of_sale.group_pos_user",1,0,0,0 diff --git a/addons/product_margin/security/ir.model.access.csv b/addons/product_margin/security/ir.model.access.csv new file mode 100644 index 00000000000..51d88da9d81 --- /dev/null +++ b/addons/product_margin/security/ir.model.access.csv @@ -0,0 +1,2 @@ +"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" +"access_product_margin","product.margin","model_product_margin",,1,0,0,0 diff --git a/addons/profile_manufacturing/security/ir.model.access.csv b/addons/profile_manufacturing/security/ir.model.access.csv index dc32d522e81..892aa4bce00 100644 --- a/addons/profile_manufacturing/security/ir.model.access.csv +++ b/addons/profile_manufacturing/security/ir.model.access.csv @@ -1,2 +1,2 @@ "id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" -"access_profile_manufacturing_wizard_install","profile manufacturing wizard install","model_profile_manufacturing_config_install_modules_wizard","base.group_system",1,1,1,1 +"access_profile_manufacturing_config_install_modules_wizard_all","profile_manufacturing_config_install_modules_wizard_all","model_profile_manufacturing_config_install_modules_wizard","base.group_system",1,1,1,1 diff --git a/addons/profile_tools/security/ir.model.access.csv b/addons/profile_tools/security/ir.model.access.csv new file mode 100644 index 00000000000..dbe55929ecc --- /dev/null +++ b/addons/profile_tools/security/ir.model.access.csv @@ -0,0 +1,2 @@ +"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" +"access_misc_tools_installer","misc_tools.installer","model_misc_tools_installer",,1,1,1,1 diff --git a/addons/project/security/ir.model.access.csv b/addons/project/security/ir.model.access.csv index abedc99cfd6..77f1c066517 100644 --- a/addons/project/security/ir.model.access.csv +++ b/addons/project/security/ir.model.access.csv @@ -14,3 +14,4 @@ "access_project_message","project.message","model_project_message","project.group_project_user",1,0,0,0 "access_project_installer","project.installer","model_project_installer","project.group_project_user",1,0,0,0 "access_report_project_task_user","report.project.task.user","model_report_project_task_user","project.group_project_manager",1,0,0,0 +"access_close_task","close.task","model_close_task","project.group_project_manager",1,1,1,1 diff --git a/addons/project_messages/security/ir.model.access.csv b/addons/project_messages/security/ir.model.access.csv new file mode 100644 index 00000000000..96782d5fc82 --- /dev/null +++ b/addons/project_messages/security/ir.model.access.csv @@ -0,0 +1,2 @@ +"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" +"access_project_messages","project.messages","model_project_messages",,1,1,1,1 diff --git a/addons/project_timesheet/security/ir.model.access.csv b/addons/project_timesheet/security/ir.model.access.csv index 42c3ea0d15c..69075f64e05 100644 --- a/addons/project_timesheet/security/ir.model.access.csv +++ b/addons/project_timesheet/security/ir.model.access.csv @@ -1,3 +1,2 @@ "id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" -"access_task_report","report.task.report","model_task_report","project.group_project_manager",1,0,0,0 "access_report_timesheet_task_user","report.timesheet.task.user","model_report_timesheet_task_user","project.group_project_manager",1,0,0,0 diff --git a/addons/purchase/security/ir.model.access.csv b/addons/purchase/security/ir.model.access.csv index e1eb2cdf199..ea35d9d7258 100644 --- a/addons/purchase/security/ir.model.access.csv +++ b/addons/purchase/security/ir.model.access.csv @@ -9,3 +9,4 @@ "access_purchase_order_line_stock_worker","purchase.order.line stock_worker","model_purchase_order_line","stock.group_stock_user",1,0,0,0 "access_account_tax_purchase_user","account.tax purchase_user","account.model_account_tax","purchase.group_purchase_user",1,0,0,0 "access_report_purchase_order","report.purchase.order","model_report_purchase_order","purchase.group_purchase_manager",1,0,0,0 +"access_purchase_order_group","purchase.order.group","model_purchase_order_group","purchase.group_purchase_user",1,1,1,1 diff --git a/addons/smtpclient/security/ir.model.access.csv b/addons/smtpclient/security/ir.model.access.csv index 6b5df447317..afa2720852e 100644 --- a/addons/smtpclient/security/ir.model.access.csv +++ b/addons/smtpclient/security/ir.model.access.csv @@ -9,3 +9,5 @@ "access_res_company_address_erpmanager","res.company.address","model_res_company_address","base.group_erp_manager",1,1,1,1 "access_email_smtpclient_queue","Smtpclient_queue all","model_email_smtpclient_queue",,1,1,1,0 "access_email_smtpclient_queue_erpmanager","Smtpclient_queue erp_manager","model_email_smtpclient_queue","base.group_erp_manager",1,1,1,1 +"access_email_headers_group_erpmanager","email.headers erp_manager","model_email_headers","base.group_erp_manager",1,1,1,1 +"access_email_headers_all","email.headers all","model_email_headers",,1,0,0,0 diff --git a/addons/stock/security/ir.model.access.csv b/addons/stock/security/ir.model.access.csv index 5622557e834..90d0178ba5a 100644 --- a/addons/stock/security/ir.model.access.csv +++ b/addons/stock/security/ir.model.access.csv @@ -40,8 +40,10 @@ "access_stock_traceability_downstream","stock.traceability.downstream","model_stock_traceability_downstream","stock.group_stock_user",1,0,0,0 "access_stock_traceability_lot_upstream","stock.traceability.lot.upstream","model_stock_traceability_lot_upstream","stock.group_stock_user",1,0,0,0 "access_stock_traceability_lot_downstream","stock.traceability.lot.downstream","model_stock_traceability_lot_downstream","stock.group_stock_user",1,0,0,0 -"access_stock_partial_picking",stock.partial.picking","model_stock_partial_picking","stock.group_stock_user",1,0,0,0 -"access_stock_partial_move","stock.partial.move","model_stock_partial_move","stock.group_stock_user",1,0,0,0 -"access_stock_picking_make","stock.picking.make","model_stock_picking_make","stock.group_stock_user",1,0,0,0 +"access_stock_return_picking","stock.return.picking","model_stock_return_picking","stock.group_stock_user",1,0,0,0 +"access_stock_partial_picking","stock.partial.picking","model_stock_partial_picking","stock.group_stock_user",1,0,0,0 +"acces_stock_partial_move","stock.partial.move","model_stock_partial_move","stock.group_stock_user",1,0,0,0 "access_stock_inventory_merge","stock.inventory.merge","model_stock_inventory_merge","stock.group_stock_user",1,0,0,0 +"access_action_traceability","action.traceability","model_action_traceability","stock.group_stock_user",1,0,0,0 "access_stock_change_standard_price","stock.change.standard.price","model_stock_change_standard_price","stock.group_stock_user",1,0,0,0 +"access_stock_picking_make","stock.picking.make","model_stock_picking_make","stock.group_stock_user",1,0,0,0 From 5c3cf27f2fa72cd64c54e6241f10b86a089d357a Mon Sep 17 00:00:00 2001 From: "Harry (Open ERP)" Date: Wed, 21 Apr 2010 19:04:02 +0530 Subject: [PATCH 019/593] [IMP] caldav bzr revid: hmo@tinyerp.com-20100421133402-191nwwj3nohx6zas --- addons/caldav/caldav_node.py | 62 ++++++++++++++---- addons/caldav/calendar.py | 86 ++++++++++++++++--------- addons/document/nodes.py | 10 ++- addons/document_webdav/dav_fs.py | 19 +++++- addons/document_webdav/webdav_server.py | 82 ++++++++++++++++++++++- 5 files changed, 208 insertions(+), 51 deletions(-) diff --git a/addons/caldav/caldav_node.py b/addons/caldav/caldav_node.py index d747078081d..81135751464 100644 --- a/addons/caldav/caldav_node.py +++ b/addons/caldav/caldav_node.py @@ -58,11 +58,28 @@ class node_calendar_collection(nodes.node_dir): M_NS = { "http://calendarserver.org/ns/" : '_get_dav', } - + headers = None def get_dav_props(self, cr): return self.PROPS - def get_dav_eprop(self,cr, ns, propname): + 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(':')) + title, cal_id, 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 dav_time <= wtime: + return True + return False + res = super(node_calendar_collection, self).match_dav_eprop(cr, match, ns, prop) + return res + + def get_dav_eprop(self,cr, ns, propname): if self.M_NS.has_key(ns): prefix = self.M_NS[ns] else: @@ -86,6 +103,19 @@ class node_calendar_collection(nodes.node_dir): def _file_get(self,cr, nodename=False): return [] + def set_data(self, cr, data, fil_obj = None): + res = None + uid = self.context.uid + calendar_obj = self.context._dirobj.pool.get('basic.calendar') + if self.headers and self.headers.has_key('If-Match'): + for match in self.headers['If-Match'].split(','): + if match != '*': + tem, dav_time = tuple(match.split(':')) + title, cal_id, model, res_id = tuple(tem.split('-')) + res = calendar_obj.import_cal(cr, uid, base64.encodestring(data), cal_id) + + return res + def get_domain(self, cr, filters): res = [] @@ -129,10 +159,11 @@ class node_calendar_collection(nodes.node_dir): caluri = uri.split('/') if len(caluri): caluri = caluri[-1] + caluri, res_id = tuple(caluri.split('_')) calendar = calendar_obj.name_search(cr, uid, caluri) if calendar: calendar_id, calendar_name = calendar[0] - ids.append(calendar_id) + if calendar_id not in ids: ids.append(calendar_id) return [('id', 'in', ids)] return res @@ -143,16 +174,15 @@ class node_calendar_collection(nodes.node_dir): ctx.update(self.dctx) where = [('collection_id','=',self.dir_id)] if name: + name, res_id = tuple(name.split('_')) + ctx.update({'res_id':res_id}) 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 = [] - if ids: - for fil in fil_obj.browse(cr,uid,ids,context=ctx): - res.append(node_calendar(fil.name,self,self.context,fil)) + res = fil_obj.get_calendar_object(cr, uid, ids, parent=self, context=ctx) return res def _get_dav_owner(self, cr): @@ -198,11 +228,13 @@ class node_calendar(nodes.node_class): def __init__(self,path, parent, context, calendar): super(node_calendar,self).__init__(path, parent,context) self.calendar_id = calendar.id - self.mimetype = 'ics' + self.mimetype = 'text/calendar' 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.displayname = calendar.name + self.model = False + self.res_id = False def open(self, cr, mode=False): uid = self.context.uid @@ -218,7 +250,7 @@ class node_calendar(nodes.node_class): def get_dav_props(self, cr): return self.PROPS - def get_dav_eprop(self,cr, ns, propname): + def get_dav_eprop(self,cr, ns, propname): if self.M_NS.has_key(ns): prefix = self.M_NS[ns] else: @@ -239,10 +271,12 @@ class node_calendar(nodes.node_class): return None - def get_data(self, cr, fil_obj = None): + def get_data(self, cr, fil_obj = None): uid = self.context.uid calendar_obj = self.context._dirobj.pool.get('basic.calendar') - res = calendar_obj.export_cal(cr, uid, [self.calendar_id]) + 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): @@ -254,7 +288,7 @@ class node_calendar(nodes.node_class): return calendar_obj.import_cal(cr, uid, base64.encodestring(data), self.calendar_id) def _get_ttag(self,cr): - return 'calendar-%d' % self.calendar_id + return 'calendar-%d-%s-%d' % (self.calendar_id, self.model, self.res_id) @@ -322,7 +356,7 @@ class node_calendar(nodes.node_class): def _get_caldav_schedule_outbox_URL(self, cr): - return _get_caldav_schedule_inbox_URL(cr) + return self._get_caldav_schedule_inbox_URL(cr) def get_etag(self, cr): diff --git a/addons/caldav/calendar.py b/addons/caldav/calendar.py index 58a51c11c85..d657cc4e619 100644 --- a/addons/caldav/calendar.py +++ b/addons/caldav/calendar.py @@ -31,6 +31,7 @@ import pytz import re import tools import time +from caldav_node import node_calendar try: import vobject @@ -435,22 +436,34 @@ class Calendar(CalDAV, osv.osv): '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_object(self, cr, uid, uri, context=None): - if not uri: - return None - if len(uri) > 1: - 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) + def get_calendar_object(self, cr, uid, ids, parent=None, context=None): + if not context: + context = {} + 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 + 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) + for data_id in data_ids: + ctx = parent and parent.context or None + node = node_calendar('%s_%s' %(cal.name, data_id), parent, ctx, cal) + node.model = line.object_id.model + node.res_id = data_id + res.append(node) + return res - def export_cal(self, cr, uid, ids, vobj='vevent', context={}): + 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, @@ -458,20 +471,28 @@ class Calendar(CalDAV, osv.osv): @param ids: List of calendar’s IDs @param context: A standard dictionary for contextual values """ - - cal = self.browse(cr, uid, ids[0]) + if not context: + context = {} + ctx_model = context.get('model', None) + ctx_res_id = context.get('res_id', None) 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) + 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): @@ -485,20 +506,21 @@ class Calendar(CalDAV, osv.osv): if not context: context = {} - vals = [] + 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) + data_id = self.search(cr, uid, [])[0] + + cal = self.read(cr, uid, data_id, context=context)[0] cal_children = {} count = 0 - for line in cal.line_ids: + for line in self.pool.get('basic.calendar.lines').browse(cr, uid, 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 + '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) diff --git a/addons/document/nodes.py b/addons/document/nodes.py index 26f6671f218..e74389dd0d4 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -152,6 +152,12 @@ 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 @@ -259,8 +265,8 @@ class node_dir(node_database): def get_data(self,cr): res = '' - for child in self.children(cr): - res += child.get_data(cr) + for child in self.children(cr): + res += child.get_data(cr) return res diff --git a/addons/document_webdav/dav_fs.py b/addons/document_webdav/dav_fs.py index d8b369b5ecf..d8e654728e2 100644 --- a/addons/document_webdav/dav_fs.py +++ b/addons/document_webdav/dav_fs.py @@ -82,6 +82,20 @@ class openerp_dav_handler(dav_interface): def _get_dav_supportedlock(self, uri): raise DAV_NotFound + 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() + raise DAV_NotFound + res = node.match_dav_eprop(cr, match, ns, propname) + cr.close() + return res + def get_prop(self, uri, ns, propname): """ return the value of a given property @@ -209,14 +223,14 @@ class openerp_dav_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: @@ -390,6 +404,7 @@ class openerp_dav_handler(dav_interface): node = self.uri2object(cr,uid,pool, uri2[:]) except: node = False + node.headers = self.parent.headers objname = uri2[-1] ext = objname.find('.') >0 and objname.split('.')[1] or False diff --git a/addons/document_webdav/webdav_server.py b/addons/document_webdav/webdav_server.py index 6cda2d172ef..5f88f90cf11 100644 --- a/addons/document_webdav/webdav_server.py +++ b/addons/document_webdav/webdav_server.py @@ -33,7 +33,10 @@ 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: @@ -81,6 +84,83 @@ 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 9abbb104d0f97600bd98c895664fb6675ce25848 Mon Sep 17 00:00:00 2001 From: "atp (Open ERP)" Date: Wed, 21 Apr 2010 19:57:13 +0530 Subject: [PATCH 020/593] [IMP]: lunch: Develop Yaml test case and change category_id field type selection to many2one bzr revid: atp@tinyerp.co.in-20100421142713-uyt4qp321a6gmotr --- addons/lunch/__openerp__.py | 5 +- addons/lunch/lunch.py | 14 +--- addons/lunch/lunch_view.xml | 2 +- addons/lunch/test/test_lunch.yml | 122 +++++++++++++++++++++++++++++++ 4 files changed, 128 insertions(+), 15 deletions(-) create mode 100644 addons/lunch/test/test_lunch.yml diff --git a/addons/lunch/__openerp__.py b/addons/lunch/__openerp__.py index 782d5f49df9..efde2f16e61 100644 --- a/addons/lunch/__openerp__.py +++ b/addons/lunch/__openerp__.py @@ -47,7 +47,10 @@ 'lunch_report.xml', #'process/lunch_process.xml' ], - "demo_xml": ['lunch_demo.xml'], + "demo_xml": ['lunch_demo.xml', + ], + "test": ['test/test_lunch.yml' + ], "installable": True, } diff --git a/addons/lunch/lunch.py b/addons/lunch/lunch.py index 207d7ebe940..a986dc25953 100644 --- a/addons/lunch/lunch.py +++ b/addons/lunch/lunch.py @@ -42,21 +42,9 @@ class lunch_product(osv.osv): _name = 'lunch.product' _description = "Lunch Product" - def _category_name_get(self, cr, uid, context={}): - - """ Get category name - @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""" - - obj = self.pool.get('lunch.category') - cat_ids = obj.search(cr, uid, []) - res = obj.read(cr, uid, cat_ids, ['name', 'category']) - return [(str(r['id']), r['name']) for r in res]+ [('0','')] - _columns = { 'name': fields.char('Name', size=50, required=True), - 'category_id': fields.selection(_category_name_get, 'Category', size=32), + 'category_id': fields.many2one('lunch.category', 'Category'), 'description': fields.char('Description', size=128, required=False), 'price': fields.float('Price', digits=(16,2)), 'active': fields.boolean('Active'), diff --git a/addons/lunch/lunch_view.xml b/addons/lunch/lunch_view.xml index 962330c1ea2..ab3d6792683 100644 --- a/addons/lunch/lunch_view.xml +++ b/addons/lunch/lunch_view.xml @@ -195,7 +195,7 @@ - + diff --git a/addons/lunch/test/test_lunch.yml b/addons/lunch/test/test_lunch.yml new file mode 100644 index 00000000000..01ed21b690a --- /dev/null +++ b/addons/lunch/test/test_lunch.yml @@ -0,0 +1,122 @@ +- | + In order to test the Lunch module in OpenERP, + I will create one lunch order and then check the effect on cashboxes and cashmoves + +- | + Given that I have a category of lunch products "Sandwich". +- + !record {model: lunch.category, id: lunch_category_burger0}: + name: Burger +- | + Given that I have a product "Club1" in this category with a price of "2.75". + +- + !record {model: lunch.product, id: lunch_product_club1}: + category_id: lunch_category_burger0 + name: Club1 + price: 2.75 +- | + Given that I have a cashbox "Cashbox" +- + !record {model: lunch.cashbox, id: lunch_cashbox_cashbox0}: + manager: base.user_root + name: CashBox +- | + I create a lunch order "LU001" for the "Club1" product +- | + When I select the product "club1", the price of 2.75 is automatically proposed +- + !record {model: lunch.order, id: lunch_order_0}: + date: '2010-04-19' + product: 'lunch_product_club1' + price: 2.75 + user_id: base.user_root + +- | + I check that lunch order is on draft state after having created it. +- + !assert {model: lunch.order, id: lunch_order_0}: + - state == 'draft' +- | + I confirm the order "LU001" using the "Confirm Order" wizard. +- + !record {model: lunch.order.confirm, id: lunch_order_confirm_0}: + confirm_cashbox: 'lunch_cashbox_cashbox0' +- + !python {model: lunch.order.confirm}: | + self.confirm(cr, uid, [ref('lunch_order_confirm_0')], {'active_ids': [ref('lunch_order_0')]}) +- | + I check that the Cash Moves have been generated with the right box + name +- + # !assert {model: lunch.order, id: lunch_order_0}: + # - cashmove != False + +- | + I check that the Total on the cashbox is -2.75 + +- + !assert {model: lunch.cashbox, id: lunch_cashbox_cashbox0}: + - sum_remain == -2.75 + +- | + I create a new lunch order "LU002" for the "Club" product, at another date. +- + !record {model: lunch.order, id: lunch_order_1}: + date: '2010-04-22' + product: 'lunch_product_club1' + price: 2.75 + user_id: base.user_root + +- | + I confirm this order.open wizard and select cashbox. +- + !record {model: lunch.order.confirm, id: lunch_order_confirm_1}: + confirm_cashbox: 'lunch_cashbox_cashbox0' +- + Now I click on "Confirm Order" button of this wizard. +- + !python {model: lunch.order.confirm}: | + self.confirm(cr, uid, [ref('lunch_order_confirm_1')], {'active_ids': [ref('lunch_order_1')]}) + +- | + I check that the Total on the cashbox is -5.50 +- + !assert {model: lunch.cashbox, id: lunch_cashbox_cashbox0}: + - sum_remain == -5.50 +- | + I reset the cashbox to zero using the "Set CashBox to Zero" wizard. +- + !record {model: lunch.cashbox.clean, id: lunch_cashbox_clean_0}: + {} +- + !python {model: lunch.cashbox.clean, id: lunch_cashbox_clean_0}: | + self.set_to_zero(cr, uid, [ref('lunch_cashbox_clean_0')], {'active_ids': [ref('lunch_cashbox_cashbox0')]}) + +- | + I check that the Total on the cashbox is 0.0 +- + !assert {model: lunch.cashbox, id: lunch_cashbox_cashbox0}: + - sum_remain == 0.00 + +- | + I cancel the order "LU002" +- + !record {model: lunch.order.cancel, id: lunch_order_cancel_0}: + {} +- + !python {model: lunch.order.cancel}: | + self.cancel(cr, uid, [ref('lunch_order_cancel_0')], {'active_ids': [ref('lunch_order_1')]}) + +- | + I test that the Cash Moves revord have been removed for the order LU002. +- + !assert {model: lunch.order, id: lunch_order_1}: + - cashmove == False + +- | + I check that the Total on the cashbox is -2.75 +- + !assert {model: lunch.cashbox, id: lunch_cashbox_cashbox0}: + - sum_remain == -2.75 + \ No newline at end of file From dc456e15857a5a67adfa2a67cfb86cfe0208868e Mon Sep 17 00:00:00 2001 From: "atp (Open ERP)" Date: Wed, 21 Apr 2010 20:09:30 +0530 Subject: [PATCH 021/593] [ADD]: idea: Develop Yaml test case bzr revid: atp@tinyerp.co.in-20100421143930-3tvyfatres2ckp2j --- addons/idea/__openerp__.py | 1 + addons/idea/test/test_idea.yml | 94 ++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 addons/idea/test/test_idea.yml diff --git a/addons/idea/__openerp__.py b/addons/idea/__openerp__.py index c5794346f4d..4730fd4637c 100644 --- a/addons/idea/__openerp__.py +++ b/addons/idea/__openerp__.py @@ -43,6 +43,7 @@ 'security/ir.model.access.csv', ], 'demo_xml': [], + 'test':['test/test_idea.yml'], 'installable': True, 'certificate': '0071515601309', } diff --git a/addons/idea/test/test_idea.yml b/addons/idea/test/test_idea.yml new file mode 100644 index 00000000000..bc67bcd49b4 --- /dev/null +++ b/addons/idea/test/test_idea.yml @@ -0,0 +1,94 @@ +- | + In order to test the Idea module in OpenERP, + we will create a full flow, from the idea definition up to the votes and discussions on the idea. +- | + Given that I have an Idea category "Technical". +- + !record {model: idea.category, id: idea_category_technical0}: + name: Technical +- | + I create a New Idea of "Technical presentation for 1 hours in every day" and specify category "Technical". + +- + !record {model: idea.idea, id: idea_idea_0}: + category_id: idea_category_technical0 + description: I want that Technical presentation are arranged for 1 hours in every + day.\nso, on that presentation, we can know all things what improvement and development + are done in our company.\n\n\n\n\n + title: Technical presentation for 1 hours in every day + user_id: base.user_root +- | + I check that idea on draft state. +- + !assert {model: idea.idea, id: idea_idea_0}: + - state == 'draft' +- | + I open this idea by click on "Open" button. +- + !workflow {model: idea.idea, action: idea_open, ref: idea_idea_0} +- | + In order to check voting statistics in real systems I will create 2 new Users "user1" and user2" +- + !record {model: res.users, id: res_users_user0}: + company_id: base.main_company + context_lang: en_US + login: user1 + name: user1 + password: user1 + +- + !record {model: res.users, id: res_users_user1}: + company_id: base.main_company + context_lang: en_US + login: user2 + name: user2 + password: user2 + +- + In order to post vote I connect as user1 and open the idea page + I click on "Vote" wizard and vote the idea as "Normal" +- + !record {model: idea.post.vote, id: idea_post_vote_0}: + vote: 50 +- + !python {model: idea.post.vote}: | + uid = ref('res_users_user0') + self.do_vote(cr, uid, [ref("idea_post_vote_0")], {'active_id': ref('idea_idea_0')}) +- + To add other vote I connect as user2 and open the idea page + I click on "Vote" wizard and vote the idea as "Very Good" +- + !record {model: idea.post.vote, id: idea_post_vote_1}: + vote: 100 + +- + !python {model: idea.post.vote}: | + uid = ref('res_users_user1') + self.do_vote(cr, uid, [ref("idea_post_vote_1")], {'active_id': ref('idea_idea_0')}) + +- + I can see that the Average score changed in "Average score" field with value 75 +- + !record {model: idea.idea, id: idea_idea_0}: + vote_avg: 75 + +- + I put one comment "We can learn many things from technical presentation" for the idea +- + !record {model: idea.idea, id: idea.idea_idea_0}: + comment_ids: + - content: We can learn many things from technical presentation + idea_id: idea.idea_idea_0 + user_id: base.user_root + +- + I close this idea by click on "Close" button. +- + !workflow {model: idea.idea, action: idea_close, ref: idea_idea_0} + +- + I check that the idea's state is now Accepted +- + !assert {model: idea.idea, id: idea_idea_0}: + - state == 'close' + \ No newline at end of file From 361fe501bc9c6e26b335ee698d18eea8abff9355 Mon Sep 17 00:00:00 2001 From: "atp (Open ERP)" Date: Wed, 21 Apr 2010 20:13:01 +0530 Subject: [PATCH 022/593] [ADD]: crm: Add yaml test case in crm module. bzr revid: atp@tinyerp.co.in-20100421144301-r3prs93ny2jw3qsb --- addons/crm/__openerp__.py | 8 ++ addons/crm/test/test_crm_claim.yml | 38 ++++++++ addons/crm/test/test_crm_fund.yml | 32 +++++++ addons/crm/test/test_crm_helpdesk.yml | 38 ++++++++ addons/crm/test/test_crm_lead.yml | 86 ++++++++++++++++++ addons/crm/test/test_crm_meeting.yml | 109 +++++++++++++++++++++++ addons/crm/test/test_crm_opportunity.yml | 81 +++++++++++++++++ addons/crm/test/test_crm_phonecall.yml | 89 ++++++++++++++++++ 8 files changed, 481 insertions(+) create mode 100644 addons/crm/test/test_crm_claim.yml create mode 100644 addons/crm/test/test_crm_fund.yml create mode 100644 addons/crm/test/test_crm_helpdesk.yml create mode 100644 addons/crm/test/test_crm_lead.yml create mode 100644 addons/crm/test/test_crm_meeting.yml create mode 100644 addons/crm/test/test_crm_opportunity.yml create mode 100644 addons/crm/test/test_crm_phonecall.yml diff --git a/addons/crm/__openerp__.py b/addons/crm/__openerp__.py index b3e45a33978..9d889799127 100644 --- a/addons/crm/__openerp__.py +++ b/addons/crm/__openerp__.py @@ -118,6 +118,14 @@ between mails and Open ERP.""", 'crm_opportunity_demo.xml', 'crm_phonecall_demo.xml' ], + 'test': ['test/test_crm_lead.yml', + 'test/test_crm_meeting.yml', + 'test/test_crm_opportunity.yml', + 'test/test_crm_phonecall.yml', + 'test/test_crm_fund.yml', + 'test/test_crm_claim.yml', + 'test/test_crm_helpdesk.yml', + ], 'installable': True, 'active': False, 'certificate': '0079056041421', diff --git a/addons/crm/test/test_crm_claim.yml b/addons/crm/test/test_crm_claim.yml new file mode 100644 index 00000000000..54ef47a56f5 --- /dev/null +++ b/addons/crm/test/test_crm_claim.yml @@ -0,0 +1,38 @@ +- | + Now I check claims which contain customer claim information about document + related problem, product related problem. +- | + I start by creating new claims for Damaged product as Value Claims with priority High and specify + date of claim at which claim is created. +- + !record {model: crm.claim, id: crm_claim_damagedproduct0}: + categ_id: crm.categ_claim2 + date: '2010-04-21 20:13:00' + email_from: info@balmerinc.be + name: 'Damaged product ' + partner_address_id: base.res_partner_address_1 + partner_id: base.res_partner_9 + priority: '2' + section_id: crm.section_sales_department + +- | + I check that the claims is in 'draft' state. +- + !assert {model: crm.claim, id: crm_claim_damagedproduct0}: + - state == 'draft' +- | + Now I make sure that claim is at "Accepted" stage. +- + !record {model: crm.claim, id: crm.crm_claim_damagedproduct0}: + stage_id: crm.stage_claim1 +- | + I can change that stage by next button right on it +- + !python {model: crm.claim}: | + self.stage_next(cr, uid, [ref('crm_claim_damagedproduct0')]) +- | + I make this claim as Open +- + !python {model: crm.claim}: | + self.case_open(cr, uid, [ref('crm_claim_damagedproduct0')]) + diff --git a/addons/crm/test/test_crm_fund.yml b/addons/crm/test/test_crm_fund.yml new file mode 100644 index 00000000000..4bcacbe148e --- /dev/null +++ b/addons/crm/test/test_crm_fund.yml @@ -0,0 +1,32 @@ +- | + Now I start test Fund Raising which contain information about Donation or charity + given by user. +- | + I start by creating new Funds entry for donation for books to poor school children + with cost 500000.00 and category as "Learning And Education" + I make payment by Cheque so I make sure that the mode on fund is selected as cheque +- + !record {model: crm.fundraising, id: crm_fundraising_donationforbookstopoorschoolchildren0}: + categ_id: crm.categ_fund2 + email_from: info@balmerinc.be + name: Donation for books to poor school children + partner_address_id: base.res_partner_address_1 + partner_id: base.res_partner_9 + planned_cost: 500000.0 + section_id: crm.section_sales_department + type_id: crm.type_fund2 +- | + I check that the Funds is in 'draft' state. +- + !assert {model: crm.fundraising, id: crm_fundraising_donationforbookstopoorschoolchildren0}: + - state == 'draft' +- | + Now I open Funds by click on "Open" button +- + !python {model: crm.fundraising}: | + self.case_open(cr, uid, [ref('crm_fundraising_donationforbookstopoorschoolchildren0')]) +- | + I can close Funds by click on "Done" button. +- + !python {model: crm.fundraising}: | + self.case_close(cr, uid, [ref('crm_fundraising_donationforbookstopoorschoolchildren0')]) diff --git a/addons/crm/test/test_crm_helpdesk.yml b/addons/crm/test/test_crm_helpdesk.yml new file mode 100644 index 00000000000..6d3beb43b41 --- /dev/null +++ b/addons/crm/test/test_crm_helpdesk.yml @@ -0,0 +1,38 @@ +- + | + I start by creating New helpdesk request regarding some functional questions. + I select Date at which helpdesk request is created. +- + !record {model: crm.helpdesk, id: crm_helpdesk_somefunctionalquestion0}: + date: '2010-04-22 10:17:00' + email_from: info@balmerinc.be + name: Some functional question. + partner_address_id: base.res_partner_address_1 + partner_id: base.res_partner_9 + section_id: crm.section_sales_department + +- | + I check that the Helpdesk request is in 'Draft' state. +- + !assert {model: crm.helpdesk, id: crm_helpdesk_somefunctionalquestion0}: + - state == 'draft' +- | + In order to make this helpdesk request to be considered I make it "Open" +- + !python {model: crm.helpdesk}: | + self.case_open(cr, uid, [ref('crm_helpdesk_somefunctionalquestion0')]) +- | + I can see that "Send Reminder" button is visible, which is used to send reminder to partner from + responsible person or send reminder to responsible person from partner. +- + #this is not work for yaml. +- +# !python {model: crm.helpdesk}: | +# self.remind_user(cr, uid, [ref('crm_helpdesk_somefunctionalquestion0')], context={}, attach=False, destination=True) +- | + After a proper communication for the request via email I make sure that the request is fulfilled and + I close this HelpDesk Request by clicking on "Close" button. +- + !python {model: crm.helpdesk}: | + self.case_close(cr, uid, [ref('crm_helpdesk_somefunctionalquestion0')]) +- diff --git a/addons/crm/test/test_crm_lead.yml b/addons/crm/test/test_crm_lead.yml new file mode 100644 index 00000000000..f22adf72e82 --- /dev/null +++ b/addons/crm/test/test_crm_lead.yml @@ -0,0 +1,86 @@ +- | + In order to test the CRM in OpenERP, + I will do a customer qualification process that + starts with a fist contact with a customer (a lead), which will be converted to a + business opportunity and a partner. +- | + In order to test the flow, I create a two new users "user_crm" and + I assign the group "salesman". +- + !record {model: res.users, id: res_users_usercrm0}: + company_id: base.main_company + context_lang: en_US + context_section_id: crm.section_sales_department + groups_id: + - crm.group_crm_user + login: user_crm + name: user_crm + password: user_crm + + +- | + I start by creating a new lead "New Customer" and I provide an address to this + new customer, as well as an email "info@mycustomer.com". +- + !record {model: crm.lead, id: crm_lead_newcustomer0}: + email_from: info@mycustomer.com + name: New Customer + partner_name: Stonage IT + phone: (855) 924-4364 + mobile: (333) 715-1450 + section_id: crm.section_sales_department + title: M. +- | + I check that the lead is in 'draft' state, +- + !assert {model: crm.lead, id: crm_lead_newcustomer0, string: Lead in Draft}: + - state == "draft" +- + I open lead by click on "Open" button, +- + !python {model: crm.lead}: | + self.case_open(cr, uid, [ref("crm_lead_newcustomer0")]) + +- | + As the lead seems to be a real business opportunity, I will convert it to a + partner + and a business opportunity by clicking on the "Convert" button. +- + !python {model: crm.lead}: | + lead = self.browse(cr, uid, ref('crm_lead_newcustomer0')) + if not lead.partner_id: + self.convert_opportunity(cr, uid, [ref("crm_lead_newcustomer0")]) +- + #!act_window {target: new, res_model: crm.lead2opportunity, id: action_crm_lead2opportunity, name: Create Opportunity} +- | + I can check that a lead and a business opportunity is now assigned to this + lead. +- | + I check that the partner associated to this lead as the same country, phone number + and name than the opportunity. +- + #Remaining +- + +# !python {model: crm.lead}: | +# lead = self.browse(cr, uid, ref("crm_lead_newcustomer0")) +# print "lead", lead.partner_name +# obj_opportunity = self.pool.get('crm.opportunity') +# ids = obj_opportunity.search(cr, uid, [('name', '=', lead.partner_name)]) +# opportunity = obj_opportunity.browse(cr, uid, ids)[0] +# assert lead.partner_name == opportunity.partner_id.name +# assert lead.phone == opportunity.phone + +- | + I configure with smtp server. +- | + And I communicate with lead through send New mail to Lead using it email address from user which are loged. +- | + I check that communication history generated when send email to lead. +- | + Then, I add a cc which receive copy of future communication between partner and users by mail. + +- | + I Reply to last Email to lead with some document attached.and check that communication history generated or not. + + \ No newline at end of file diff --git a/addons/crm/test/test_crm_meeting.yml b/addons/crm/test/test_crm_meeting.yml new file mode 100644 index 00000000000..bd8142ecabf --- /dev/null +++ b/addons/crm/test/test_crm_meeting.yml @@ -0,0 +1,109 @@ +- | + Now I will test Meetings which may be customer meeting or phonecall meeting or + internal Meeting. +- + I start by creating a new Meeting. +- + For that, I first create alarm. +- + !record {model: res.alarm, id: res_alarm_minituesbefore0}: + name: 40 minutes before + trigger_duration: 40 + trigger_interval: minutes + trigger_occurs: before + trigger_related: start +- + I create Meeting. +- + !record {model: crm.meeting, id: crm_meeting_regardingpresentation0}: + categ_id: crm.categ_meet2 + date: '2010-04-21 16:04:00' + date_deadline: '2010-04-22 00:04:00' + duration: 8.0 + email_from: info@balmerinc.be + location: Ahmedabad + name: Regarding Presentation + partner_address_id: base.res_partner_address_1 + partner_id: base.res_partner_9 + rrule_type: weekly + section_id: crm.section_sales_department +- | + I check that the Meetings is in 'UnConfirmed' state. +- + !assert {model: crm.meeting, id: crm_meeting_regardingpresentation0}: + - state == "draft" +- | + I can set reminder on meeting if I put reminder "40 minutes before" +- + For that, I first create alarm. +- + !record {model: res.alarm, id: res_alarm_minituesbefore0}: + name: 40 minutes before + trigger_duration: 40 + trigger_interval: minutes + trigger_occurs: before + trigger_related: start +- | + Now I will assign this reminder. + +- !python {model: crm.meeting}: | + self.write(cr, uid, [ref('crm_meeting_regardingpresentation0')], {'alarm_id': ref("res_alarm_minituesbefore0")}) +- | + In order to check recurrence on meetings I will set Recurrency to Custom + and I set the fields so that the meeting will occure weekly on Monday and Friday 10 times +- + !record {model: crm.meeting, id: crm_meeting_regardingpresentation0}: + count: 10 + fr: 1 + freq: weekly + mo: 1 + rrule_type: custom + th: 1 + tu: 1 + we: 1 +- | + I can see from the calendar view that the meeting is scheduled on Monday and Friday + for 10 times, + I will search for one of the recurrent event and count the number of events +- + !python {model: crm.meeting}: | + ids = self.search(cr, uid, [('date', '>=', '2010-05-21 00:00:00'), ('date', '<=', '2010-04-21 00:00:00')] ) + assert len(ids) == 9 + +- | + Now If I want to edit meetings information for all occurence I click on "Edit All" button. +- + !python {model: crm.meeting}: | + self.open_meeting(cr, uid, [ref('crm_meeting_regardingpresentation0')]) +- | + I can see that new meeting form is opened with same value + I change some data for meeting and save it + I can see from meeting's calendar view that all meeting occurences are changed accordingly +- + !record {model: crm.meeting, id: crm.crm_meeting_regardingpresentation0}: + alarm_id: base_calendar.alarm9 + rrule_type: weekly + +- | + In order to invite people for this meetings, I click on "Invite People" button + I can invite internal user. +- + !record {model: base_calendar.invite.attendee, id: base_calendar_invite_attendee_0}: + type: internal + partner_id: base.res_partner_9 + user_ids: + - base.user_demo +- | + If I set Send mail boolean as True I can see that an email is send to + specified email address with proper meeting information. +- + #this is not working for send mail boolean as True. +- + !python {model: base_calendar.invite.attendee}: | + self.do_invite(cr, uid, [ref('base_calendar_invite_attendee_0')], {'active_id': ref('crm_meeting_regardingpresentation0'), 'model' : 'crm.meeting', 'attendee_field':'attendee_ids'}) + +- | + After direct/indirect confirmation for meetings I can confirm meeting +- + !python {model: crm.meeting}: | + self.case_open(cr, uid, [ref('crm_meeting_regardingpresentation0')]) diff --git a/addons/crm/test/test_crm_opportunity.yml b/addons/crm/test/test_crm_opportunity.yml new file mode 100644 index 00000000000..4fd0ddace95 --- /dev/null +++ b/addons/crm/test/test_crm_opportunity.yml @@ -0,0 +1,81 @@ +- | + I start by creating a new Opportunity. And I select partner for opportunity. + I can see that after selecting partner his contact and email is automatically filled. +- + !record {model: crm.opportunity, id: crm_opportunity_abcfuelcounits0}: + email_from: info@balmerinc.be + name: 'ABC FUEL CO 829264 - 10002 units ' + partner_address_id: base.res_partner_address_1 + partner_id: base.res_partner_9 + probability: 1.0 + stage_id: crm.stage_oppor1 + categ_id: crm.categ_oppor2 + section_id: crm.section_sales_department + +- | + I check that the opportunity is in 'New' state. +- + !assert {model: crm.opportunity, id: crm_opportunity_abcfuelcounits0}: + - state == "draft" +- | + I open opportunity by click on "Open" button, +- + !python {model: crm.opportunity}: | + self.case_open(cr, uid, [ref("crm_opportunity_abcfuelcounits0")]) + +- | + I schedule Meeting on this current opportunity by clicking on "schedule + Meeting". +- + !python {model: crm.opportunity}: | + self.action_makeMeeting(cr, uid, [ref("crm_opportunity_abcfuelcounits0")]) + +- | + I can see that Meeting's calendar view is shown. + then I click on the date on which I want schedule meeting. + I fill proper data for that meeting and save it +- + !record {model: crm.meeting, id: crm_meeting_abcfuelcounits0}: + alarm_id: base_calendar.alarm3 + date: '2010-04-16 00:00:00' + date_deadline: '2010-04-16 08:00:00' + duration: 8.0 + email_from: info@balmerinc.be + name: 'ABC FUEL CO 829264 - 10002 units ' + opportunity_id: 'crm_opportunity_abcfuelcounits0' + partner_address_id: base.res_partner_address_1 + partner_id: base.res_partner_9 + rrule_type: weekly + section_id: crm.section_sales_department + state: open +- | + In order to schedule a phonecall to the partner + I click on "schedule call" button and select planned date for the call. +- + !record {model: crm.opportunity2phonecall, id: crm_opportunity2phonecall_abcfuelcounits0}: + date: '2010-04-17 11:15:00' + name: 'ABC FUEL CO 829264 - 10002 units ' + section_id: crm.section_sales_department + user_id: base.user_demo +- | + schedule phonecall by apply (action_apply)function of opportunity2phoncall wizard +- + !python {model: crm.opportunity2phonecall}: | + self.action_apply(cr, uid, [ref('crm_opportunity2phonecall_abcfuelcounits0')], {'active_ids': [ref("crm_opportunity_abcfuelcounits0")]}) +- | + I check that phonecall record is created for that opportunity. +- + !python {model: crm.phonecall}: | + ids = self.search(cr, uid, [('name', '=', 'ABC FUEL CO 829264 - 10002 units')]) + assert len(ids) +- + I can see phonecall record after click on "Schedule call" wizard. +- + !record {model: crm.phonecall, id: crm_phonecall_abcfuelcounits0}: + date: '2010-04-17 11:15:00' + duration: 3.0 + name: 'ABC FUEL CO 829264 - 10002 units' + partner_address_id: base.res_partner_address_1 + partner_id: base.res_partner_9 + section_id: crm.section_sales_department + \ No newline at end of file diff --git a/addons/crm/test/test_crm_phonecall.yml b/addons/crm/test/test_crm_phonecall.yml new file mode 100644 index 00000000000..7e6ed56abc5 --- /dev/null +++ b/addons/crm/test/test_crm_phonecall.yml @@ -0,0 +1,89 @@ +- | + I start by creating a new phonecall. +- + !record {model: crm.phonecall, id: crm_phonecall_interviewcall0}: + date: '2010-04-21 18:59:00' + name: Interview call + partner_address_id: base.res_partner_address_1 + partner_id: base.res_partner_9 + partner_mobile: (+32)2 211 34 83 + section_id: crm.section_sales_department + +- | + As the success of phonecall seems to be a real business opportunity, I will convert + it to opportunity by clicking on the "Convert to Opportunity" button. +- +# !python {model: crm.phonecall2opportunity}: | +# self.action_apply(cr, uid, [1], {'active_id': ref('crm_phonecall_interviewcall0')}) + +- + # This is not working, find a way to do that in YAML + +- | + I can see that a business opportunity is now assigned to this phonecall +- | + I check that the partner associated to this as the same country, phone number + and name than the phonecall. +- | + In order to avoid the duplication of similar partner, OpenERP should be able to + detect if the partner exists or if it should be created from the phonecall during the + conversion. +- | + In order to test this, I log as the user "user_crm2" +- | + Then, I create the same phonecall than the preceeding one but I change the name + of the phonecall. But this customer keeps the same email. +- | + This time OpenERP should detect that this customer already exists in the + partner base and I check that in the wizard, proposes me to link to this existing + partner instead of creating another one. +- | + I confirm the conversion wizard. +- | + And I check that the phonecall and the newly created business opportunity is linked + to the partner +- | + I schedule Meeting on this current phonecall by clicking on "schedule + Meeting" +- + !python {model: crm.phonecall}: | + self.action_make_meeting(cr, uid, [ref('crm_phonecall_interviewcall0')]) + +- + I can see that Meeting's calendar view is shown. + then I click on the date on which I want schedule meeting. + I fill proper data for that meeting and save it +- + !record {model: crm.meeting, id: crm_meeting_interviewcall0}: + alarm_id: base_calendar.alarm3 + date: '2010-04-20 00:00:00' + date_deadline: '2010-04-20 08:00:00' + duration: 8.0 + email_from: info@balmerinc.be + name: Interview call + partner_address_id: base.res_partner_address_1 + partner_id: base.res_partner_9 + phonecall_id: 'crm_phonecall_interviewcall0' + rrule_type: weekly + state: open + +- + #This is not working for yaml +- + I can jump to this meeting by "Meetings" shortcut from my phonecall's form view +- + +- | + In order to schedule other phonecall to the partner + I click on "schedule other call" button. and plan for other call + I can see that it will open other phonecall view with some data same as current + phonecall. +- + !record {model: crm.phonecall2phonecall, id: crm_phonecall2phonecall_interviewcall0}: + date: '2010-04-21 19:49:00' + name: Interview call + section_id: crm.section_sales_department + user_id: base.user_root +- + !python {model: crm.phonecall2phonecall}: | + self.action_apply(cr, uid, [ref('crm_phonecall2phonecall_interviewcall0')], {'active_id': ref('crm_phonecall_interviewcall0')}) From 6ebc3c4453cce48118c89c8198c9ba531dab55fa Mon Sep 17 00:00:00 2001 From: "atp (Open ERP)" Date: Wed, 21 Apr 2010 20:49:11 +0530 Subject: [PATCH 023/593] [IMP]:crm: Improvement in crm meeting yaml bzr revid: atp@tinyerp.co.in-20100421151911-kmach16jn0dhlno4 --- addons/crm/__openerp__.py | 2 +- addons/crm/test/test_crm_meeting.yml | 36 ++++++++++------------------ 2 files changed, 13 insertions(+), 25 deletions(-) diff --git a/addons/crm/__openerp__.py b/addons/crm/__openerp__.py index 9d889799127..e4dea534b65 100644 --- a/addons/crm/__openerp__.py +++ b/addons/crm/__openerp__.py @@ -109,6 +109,7 @@ between mails and Open ERP.""", 'process/crm_configuration_process.xml', ], 'demo_xml': [ + 'test/test_crm_meeting.yml', 'crm_demo.xml', 'crm_claims_demo.xml', 'crm_fund_demo.xml', @@ -119,7 +120,6 @@ between mails and Open ERP.""", 'crm_phonecall_demo.xml' ], 'test': ['test/test_crm_lead.yml', - 'test/test_crm_meeting.yml', 'test/test_crm_opportunity.yml', 'test/test_crm_phonecall.yml', 'test/test_crm_fund.yml', diff --git a/addons/crm/test/test_crm_meeting.yml b/addons/crm/test/test_crm_meeting.yml index bd8142ecabf..6234349a765 100644 --- a/addons/crm/test/test_crm_meeting.yml +++ b/addons/crm/test/test_crm_meeting.yml @@ -1,19 +1,8 @@ - | Now I will test Meetings which may be customer meeting or phonecall meeting or internal Meeting. -- - I start by creating a new Meeting. -- - For that, I first create alarm. -- - !record {model: res.alarm, id: res_alarm_minituesbefore0}: - name: 40 minutes before - trigger_duration: 40 - trigger_interval: minutes - trigger_occurs: before - trigger_related: start -- - I create Meeting. +- + I start by creating a new Meeting. - !record {model: crm.meeting, id: crm_meeting_regardingpresentation0}: categ_id: crm.categ_meet2 @@ -51,20 +40,19 @@ - | In order to check recurrence on meetings I will set Recurrency to Custom and I set the fields so that the meeting will occure weekly on Monday and Friday 10 times -- - !record {model: crm.meeting, id: crm_meeting_regardingpresentation0}: - count: 10 - fr: 1 - freq: weekly - mo: 1 - rrule_type: custom - th: 1 - tu: 1 - we: 1 +- + !python {model: crm.meeting}: | + self.write(cr, uid, [ref("crm_meeting_regardingpresentation0")], {'fr': 1, 'mo': 1, 'th': 1, 'tu': 1, 'we':1, 'count':10, 'interval': 1, 'freq': 'weekly', 'rrule_type': 'custom'}) + - | I can see from the calendar view that the meeting is scheduled on Monday and Friday for 10 times, - I will search for one of the recurrent event and count the number of events + +- + !python {model: crm.meeting}: | + self.fields_view_get(cr, uid, False, 'calendar', context) + + I will search for one of the recurrent event and count the number of meeting. - !python {model: crm.meeting}: | ids = self.search(cr, uid, [('date', '>=', '2010-05-21 00:00:00'), ('date', '<=', '2010-04-21 00:00:00')] ) From 34a3e22250f723efd5603f890304d5229981d17f Mon Sep 17 00:00:00 2001 From: "atp (Open ERP)" Date: Thu, 22 Apr 2010 10:24:38 +0530 Subject: [PATCH 024/593] [IMP]: lunch: Minor improvement in comment of yaml test case. bzr revid: atp@tinyerp.co.in-20100422045438-z7pjermcnnwzwxs2 --- addons/lunch/test/test_lunch.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/lunch/test/test_lunch.yml b/addons/lunch/test/test_lunch.yml index 01ed21b690a..a415731a04a 100644 --- a/addons/lunch/test/test_lunch.yml +++ b/addons/lunch/test/test_lunch.yml @@ -3,7 +3,7 @@ I will create one lunch order and then check the effect on cashboxes and cashmoves - | - Given that I have a category of lunch products "Sandwich". + Given that I have a category of lunch products "Burger". - !record {model: lunch.category, id: lunch_category_burger0}: name: Burger @@ -60,7 +60,7 @@ - sum_remain == -2.75 - | - I create a new lunch order "LU002" for the "Club" product, at another date. + I create a new lunch order "LU002" for the "Club1" product, at another date. - !record {model: lunch.order, id: lunch_order_1}: date: '2010-04-22' From 63de0de17d970d4218a55bc849719e1ca1a0b547 Mon Sep 17 00:00:00 2001 From: "atp (Open ERP)" Date: Thu, 22 Apr 2010 10:39:02 +0530 Subject: [PATCH 025/593] [IMP]: crm: Minor Improvement in meeting yaml fiel bzr revid: atp@tinyerp.co.in-20100422050902-i30v1lylj8rrcpfi --- addons/crm/test/test_crm_meeting.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/crm/test/test_crm_meeting.yml b/addons/crm/test/test_crm_meeting.yml index 6234349a765..55db5e84cc7 100644 --- a/addons/crm/test/test_crm_meeting.yml +++ b/addons/crm/test/test_crm_meeting.yml @@ -51,7 +51,7 @@ - !python {model: crm.meeting}: | self.fields_view_get(cr, uid, False, 'calendar', context) - +- I will search for one of the recurrent event and count the number of meeting. - !python {model: crm.meeting}: | From 166c537f10a269a9581cb2fc0ff681b8c3f57a55 Mon Sep 17 00:00:00 2001 From: "Ysa (Open ERP)" Date: Thu, 22 Apr 2010 11:41:22 +0530 Subject: [PATCH 026/593] [FIX] document :- all record delete in document.directory and create new directory then generate the error. bzr revid: ysa@tinyerp.co.in-20100422061122-jx1q03h95ro3dwsa --- addons/document/document_directory.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/addons/document/document_directory.py b/addons/document/document_directory.py index c9c124cdfe0..19c4489132c 100644 --- a/addons/document/document_directory.py +++ b/addons/document/document_directory.py @@ -105,7 +105,11 @@ class document_directory(osv.osv): ] def name_get(self, cr, uid, ids, context={}): res = [] + all_ids = self.search(cr,uid,[]) for d in self.browse(cr, uid, ids, context=context): + if d.id not in all_ids: + continue + s = '' d2 = d while d2 and d2.parent_id: From 12706188beaaabb04e4ba4813c71cb87aec1b87b Mon Sep 17 00:00:00 2001 From: "ksa (Open ERP)" Date: Thu, 22 Apr 2010 14:27:00 +0530 Subject: [PATCH 027/593] [FIX]:wiki : changes for the wizard file and remove unused file bzr revid: ksa@tinyerp.co.in-20100422085700-vwz7xaam6uqngiel --- addons/wiki/__openerp__.py | 2 +- addons/wiki/wizard/wizard_view.xml | 24 ------------------------ 2 files changed, 1 insertion(+), 25 deletions(-) delete mode 100644 addons/wiki/wizard/wizard_view.xml diff --git a/addons/wiki/__openerp__.py b/addons/wiki/__openerp__.py index 65a4212c289..96c99681b7e 100644 --- a/addons/wiki/__openerp__.py +++ b/addons/wiki/__openerp__.py @@ -33,10 +33,10 @@ 'depends': ['base'], 'init_xml': [], 'update_xml': [ - 'wizard/wizard_view.xml', 'wizard/wiki_wiki_page_open_view.xml', 'wizard/wiki_create_menu_view.xml', 'wizard/wiki_make_index_view.xml', + 'wizard/wiki_show_diff_view.xml', 'wiki_view.xml', 'data/wiki_quickstart.xml', 'data/wiki_main.xml', diff --git a/addons/wiki/wizard/wizard_view.xml b/addons/wiki/wizard/wizard_view.xml deleted file mode 100644 index 2374d22e2dc..00000000000 --- a/addons/wiki/wizard/wizard_view.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - Differences - wizard.wiki.history.show_diff - form - - - - - - - - - - From bf266e764a926d3e47149cb6d47886525317f511 Mon Sep 17 00:00:00 2001 From: "rch (Open ERP)" <> Date: Mon, 26 Apr 2010 12:08:12 +0530 Subject: [PATCH 028/593] [IMP]: Improve SQL view report for crm.lead. bzr revid: rch_open_erp-20100426063812-6e8ldx12o32cdosy --- addons/crm/report/crm_claim_report.py | 2 +- addons/crm/report/crm_lead_report.py | 12 ++++++++++++ addons/crm/report/crm_lead_report_view.xml | 19 +++++++++++++++++++ addons/crm/report/crm_report_view.xml | 7 ++++--- 4 files changed, 36 insertions(+), 4 deletions(-) diff --git a/addons/crm/report/crm_claim_report.py b/addons/crm/report/crm_claim_report.py index 4148a834810..70d6be8d616 100644 --- a/addons/crm/report/crm_claim_report.py +++ b/addons/crm/report/crm_claim_report.py @@ -82,7 +82,7 @@ class crm_claim_report(osv.osv): group by to_char(c.create_date, 'YYYY'), to_char(c.create_date, 'MM'), \ c.state, c.user_id,c.section_id, c.stage_id,\ c.categ_id,c.partner_id,c.company_id,c.create_date,to_char(c.create_date, 'YYYY-MM-DD') - ,c.priority,c.type_id,c.som + ,c.priority,c.type_id )""") crm_claim_report() diff --git a/addons/crm/report/crm_lead_report.py b/addons/crm/report/crm_lead_report.py index d400700c0b5..92aeb7f28ec 100644 --- a/addons/crm/report/crm_lead_report.py +++ b/addons/crm/report/crm_lead_report.py @@ -21,6 +21,7 @@ from osv import fields,osv import tools +import crm_report AVAILABLE_STATES = [ ('draft','Draft'), @@ -47,6 +48,12 @@ class crm_lead_report(osv.osv): ('object_id.model', '=', 'crm.lead')]", readonly=True), 'partner_id': fields.many2one('res.partner', 'Partner' , readonly=True), 'company_id': fields.many2one('res.company', 'Company', readonly=True), + 'priority': fields.selection(crm_report.AVAILABLE_PRIORITIES, 'Priority'), + 'type_id': fields.many2one('crm.case.resource.type', 'Lead Type', \ + domain="[('section_id','=',section_id),\ + ('object_id.model', '=', 'crm.lead')]"), + 'date_closed': fields.datetime('Closed', readonly=True), + 'date_open': fields.datetime('Opened', readonly=True), } def init(self, cr): @@ -74,6 +81,10 @@ class crm_lead_report(osv.osv): 0 as avg_answers, 0.0 as perc_done, 0.0 as perc_cancel, + c.priority as priority, + c.type_id as type_id, + c.date_closed as date_closed, + c.date_open as date_open, date_trunc('day',c.create_date) as create_date, avg(extract('epoch' from (c.date_closed-c.create_date)))/(3600*24) as delay_close from @@ -81,6 +92,7 @@ class crm_lead_report(osv.osv): group by to_char(c.create_date, 'YYYY'), to_char(c.create_date, 'MM'),\ c.state, c.user_id,c.section_id,c.stage_id,categ_id,c.partner_id,c.company_id ,c.create_date,to_char(c.create_date, 'YYYY-MM-DD') + ,c.priority,c.type_id,c.date_closed,c.date_open )""") crm_lead_report() diff --git a/addons/crm/report/crm_lead_report_view.xml b/addons/crm/report/crm_lead_report_view.xml index d6502c190d9..6ef0d90ac97 100644 --- a/addons/crm/report/crm_lead_report_view.xml +++ b/addons/crm/report/crm_lead_report_view.xml @@ -22,6 +22,10 @@ + + + +
@@ -70,6 +74,12 @@ + + + + + + + + + + +
diff --git a/addons/crm/report/crm_report_view.xml b/addons/crm/report/crm_report_view.xml index 97fa4feb6f1..b4e18793af4 100644 --- a/addons/crm/report/crm_report_view.xml +++ b/addons/crm/report/crm_report_view.xml @@ -106,9 +106,10 @@ help="My company"/>
- - - + + + + From b8947954bf8ab4e410272937621815f73b1cdd98 Mon Sep 17 00:00:00 2001 From: "rch (Open ERP)" <> Date: Mon, 26 Apr 2010 12:56:32 +0530 Subject: [PATCH 029/593] [IMP]: Improve SQL view for crm fundraising. bzr revid: rch_open_erp-20100426072632-wcny8y4x5ud18y4t --- addons/crm/report/crm_fundraising_report.py | 8 ++++++-- addons/crm/report/crm_fundraising_report_view.xml | 11 +++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/addons/crm/report/crm_fundraising_report.py b/addons/crm/report/crm_fundraising_report.py index af867ea0008..2b3fbcb4c7a 100644 --- a/addons/crm/report/crm_fundraising_report.py +++ b/addons/crm/report/crm_fundraising_report.py @@ -21,7 +21,7 @@ from osv import fields,osv import tools - +import crm_report class crm_fundraising_report(osv.osv): """CRM Fundraising Report""" @@ -41,6 +41,8 @@ class crm_fundraising_report(osv.osv): 'delay_close': fields.float('Delay to close', digits=(16,2),readonly=True, group_operator="avg",help="Number of Days to close the case"), 'partner_id': fields.many2one('res.partner', 'Partner'), 'company_id': fields.many2one('res.company', 'Company'), + 'priority': fields.selection(crm_report.AVAILABLE_PRIORITIES, 'Priority'), + 'date_closed': fields.datetime('Closed', readonly=True) } def init(self, cr): @@ -67,6 +69,8 @@ class crm_fundraising_report(osv.osv): 0 as avg_answers, 0.0 as perc_done, 0.0 as perc_cancel, + c.priority as priority, + c.date_closed as date_closed, date_trunc('day',c.create_date) as create_date, sum(planned_revenue) as amount_revenue, sum(planned_revenue*probability)::decimal(16,2) as amount_revenue_prob, @@ -76,7 +80,7 @@ class crm_fundraising_report(osv.osv): crm_fundraising c group by to_char(c.create_date, 'YYYY'), to_char(c.create_date, 'MM'),\ c.state, c.user_id,c.section_id,c.categ_id,c.partner_id,c.company_id, - c.create_date,to_char(c.create_date, 'YYYY-MM-DD') + c.create_date,to_char(c.create_date, 'YYYY-MM-DD'),c.priority,c.date_closed )""") crm_fundraising_report() diff --git a/addons/crm/report/crm_fundraising_report_view.xml b/addons/crm/report/crm_fundraising_report_view.xml index e1c32726b4f..1d97b868a34 100644 --- a/addons/crm/report/crm_fundraising_report_view.xml +++ b/addons/crm/report/crm_fundraising_report_view.xml @@ -24,6 +24,8 @@ + +
@@ -73,8 +75,17 @@ + + + + + +
From c70951059ee26059ace2a45e88855d5a56bff2b9 Mon Sep 17 00:00:00 2001 From: apa-tiny Date: Mon, 26 Apr 2010 13:55:43 +0530 Subject: [PATCH 030/593] [IMP]:mrp:Improved SQL report.(Production). bzr revid: apa@tinyerp.com-20100426082543-0yk10z2xhx05rj1t --- addons/mrp/report/mrp_production_order.py | 20 ++-- .../mrp/report/mrp_production_order_view.xml | 91 ++++++++++++------- 2 files changed, 71 insertions(+), 40 deletions(-) diff --git a/addons/mrp/report/mrp_production_order.py b/addons/mrp/report/mrp_production_order.py index 7f48b111d43..96910e205d7 100644 --- a/addons/mrp/report/mrp_production_order.py +++ b/addons/mrp/report/mrp_production_order.py @@ -27,12 +27,14 @@ class mrp_production_order(osv.osv): _description = "Production Order Report" _auto = False _columns = { - 'name': fields.char('Year',size=64,required=False, readonly=True), + 'year': fields.char('Year',size=64,readonly=True), 'month':fields.selection([('01','January'), ('02','February'), ('03','March'), ('04','April'), ('05','May'), ('06','June'), ('07','July'), ('08','August'), ('09','September'), ('10','October'), ('11','November'), ('12','December')],'Month',readonly=True), + 'day': fields.char('Day',size=64,readonly=True), 'origin': fields.char('Source Document', size=64), 'nbr': fields.integer('# of Orders', readonly=True), - 'product_id': fields.many2one('product.product', 'Product', readonly=True, domain=[('type','<>','service')]), + 'date': fields.date('Date', readonly=True), + 'product_id': fields.many2one('product.product', 'Product', readonly=True), 'product_qty': fields.float('Product Qty', readonly=True), 'state': fields.selection([('draft','Draft'), ('picking_except', 'Picking Exception'), @@ -43,13 +45,13 @@ class mrp_production_order(osv.osv): ('done','Done')], 'State', readonly=True), 'date_planned':fields.date('Scheduled Date'), - 'location_src_id': fields.many2one('stock.location', 'Raw Materials Location', required=True), + 'location_src_id': fields.many2one('stock.location', 'Raw Materials Location', readonly=True), 'date_start': fields.datetime('Start Date',readonly=True), 'date_finnished': fields.datetime('End Date',readonly=True), - 'location_dest_id': fields.many2one('stock.location', 'Finished Products Location', required=True), + 'location_dest_id': fields.many2one('stock.location', 'Finished Products Location', readonly=True), 'company_id': fields.many2one('res.company','Company',readonly=True), - 'bom_id': fields.many2one('mrp.bom', 'Bill of Material', domain=[('bom_id','=',False)],readonly=True), - 'routing_id': fields.many2one('mrp.routing', string='Routing', on_delete='set null',readonly=True), + 'bom_id': fields.many2one('mrp.bom', 'Bill of Material',readonly=True), + 'routing_id': fields.many2one('mrp.routing', string='Routing',readonly=True), 'picking_id': fields.many2one('stock.picking', 'Picking list', readonly=True), 'priority': fields.selection([('0','Not urgent'), ('1','Normal'), @@ -65,8 +67,10 @@ class mrp_production_order(osv.osv): create or replace view mrp_production_order as ( select min(l.id) as id, - to_char(s.create_date, 'YYYY') as name, + to_date(to_char(s.create_date, 'MM-dd-YYYY'),'MM-dd-YYYY') as date, + to_char(s.create_date, 'YYYY') as year, to_char(s.create_date, 'MM') as month, + to_char(s.create_date, 'YYYY-MM-DD') as day, l.product_id as product_id, sum(l.product_qty * u.factor) as product_qty, s.company_id as company_id, @@ -90,6 +94,8 @@ class mrp_production_order(osv.osv): group by to_char(s.create_date, 'YYYY'), to_char(s.create_date, 'MM'), + to_char(s.create_date, 'YYYY-MM-DD'), + to_date(to_char(s.create_date, 'MM-dd-YYYY'),'MM-dd-YYYY'), l.product_id, l.product_uom, s.bom_id, diff --git a/addons/mrp/report/mrp_production_order_view.xml b/addons/mrp/report/mrp_production_order_view.xml index a97abe846de..04188f43752 100644 --- a/addons/mrp/report/mrp_production_order_view.xml +++ b/addons/mrp/report/mrp_production_order_view.xml @@ -11,18 +11,21 @@ tree - - - - + + + + - + - + + + + @@ -35,8 +38,9 @@ graph - + +
@@ -48,48 +52,70 @@ - - - - - + + + + + + + + - + + - - + - - - - - - - - - - - - + + - + + + + + + + + + + - + + + - + - + @@ -101,8 +127,7 @@ tree,graph - {'search_default_Product': 1} - + {'search_default_month':1,'search_default_Product':1,'group_by_no_leaf':1,'group_by':[]} From 68ac3e6fa806ea369d0118425f6e53e0c83df36c Mon Sep 17 00:00:00 2001 From: "rch (Open ERP)" <> Date: Mon, 26 Apr 2010 15:31:40 +0530 Subject: [PATCH 031/593] [IMP]: Improve SQL view report of claim,lead and fundraising. bzr revid: rch_open_erp-20100426100140-n0ppf5ddggbungbs --- addons/crm/report/crm_claim_report.py | 8 +++++++- addons/crm/report/crm_claim_report_view.xml | 13 +++++++++++++ addons/crm/report/crm_fundraising_report.py | 7 ++++++- addons/crm/report/crm_fundraising_report_view.xml | 12 +++++++++++- 4 files changed, 37 insertions(+), 3 deletions(-) diff --git a/addons/crm/report/crm_claim_report.py b/addons/crm/report/crm_claim_report.py index 70d6be8d616..74b95864759 100644 --- a/addons/crm/report/crm_claim_report.py +++ b/addons/crm/report/crm_claim_report.py @@ -46,6 +46,9 @@ class crm_claim_report(osv.osv): 'type_id': fields.many2one('crm.case.resource.type', 'Claim Type',\ domain="[('section_id','=',section_id),\ ('object_id.model', '=', 'crm.claim')]"), + 'date_closed': fields.datetime('Closed', readonly=True), + 'canal_id': fields.many2one('res.partner.canal','Channel',domain="[('section_id','=',section_id),('object_id.model', '=', 'crm.claim')]"), + 'som': fields.many2one('res.partner.som', 'State of Mind') } def init(self, cr): @@ -75,6 +78,9 @@ class crm_claim_report(osv.osv): 0.0 as perc_cancel, c.priority as priority, c.type_id as type_id, + c.date_closed as date_closed, + c.canal_id as canal_id, + c.som as som, date_trunc('day',c.create_date) as create_date, avg(extract('epoch' from (c.date_closed-c.create_date)))/(3600*24) as delay_close from @@ -82,7 +88,7 @@ class crm_claim_report(osv.osv): group by to_char(c.create_date, 'YYYY'), to_char(c.create_date, 'MM'), \ c.state, c.user_id,c.section_id, c.stage_id,\ c.categ_id,c.partner_id,c.company_id,c.create_date,to_char(c.create_date, 'YYYY-MM-DD') - ,c.priority,c.type_id + ,c.priority,c.type_id,c.date_closed,c.canal_id,c.som )""") crm_claim_report() diff --git a/addons/crm/report/crm_claim_report_view.xml b/addons/crm/report/crm_claim_report_view.xml index 647a0125179..74ebbd56d0b 100644 --- a/addons/crm/report/crm_claim_report_view.xml +++ b/addons/crm/report/crm_claim_report_view.xml @@ -24,6 +24,9 @@ + + + @@ -78,6 +81,11 @@ + + + + + + + + diff --git a/addons/crm/report/crm_fundraising_report.py b/addons/crm/report/crm_fundraising_report.py index 2b3fbcb4c7a..5765b7367b0 100644 --- a/addons/crm/report/crm_fundraising_report.py +++ b/addons/crm/report/crm_fundraising_report.py @@ -42,7 +42,9 @@ class crm_fundraising_report(osv.osv): 'partner_id': fields.many2one('res.partner', 'Partner'), 'company_id': fields.many2one('res.company', 'Company'), 'priority': fields.selection(crm_report.AVAILABLE_PRIORITIES, 'Priority'), - 'date_closed': fields.datetime('Closed', readonly=True) + 'date_closed': fields.datetime('Closed', readonly=True), + 'canal_id': fields.many2one('res.partner.canal','Channel',domain="[('section_id','=',section_id),('object_id.model', '=', 'crm.fundraising')]"), + 'som': fields.many2one('res.partner.som', 'State of Mind') } def init(self, cr): @@ -71,6 +73,8 @@ class crm_fundraising_report(osv.osv): 0.0 as perc_cancel, c.priority as priority, c.date_closed as date_closed, + c.canal_id as canal_id, + c.som as som, date_trunc('day',c.create_date) as create_date, sum(planned_revenue) as amount_revenue, sum(planned_revenue*probability)::decimal(16,2) as amount_revenue_prob, @@ -81,6 +85,7 @@ class crm_fundraising_report(osv.osv): group by to_char(c.create_date, 'YYYY'), to_char(c.create_date, 'MM'),\ c.state, c.user_id,c.section_id,c.categ_id,c.partner_id,c.company_id, c.create_date,to_char(c.create_date, 'YYYY-MM-DD'),c.priority,c.date_closed + ,c.canal_id,c.som )""") crm_fundraising_report() diff --git a/addons/crm/report/crm_fundraising_report_view.xml b/addons/crm/report/crm_fundraising_report_view.xml index 1d97b868a34..7236df4a8b4 100644 --- a/addons/crm/report/crm_fundraising_report_view.xml +++ b/addons/crm/report/crm_fundraising_report_view.xml @@ -26,6 +26,8 @@ + + @@ -77,7 +79,10 @@ - + + + + + + + From 2eeb2a1f7981a68aec561ade79c12e6ae3e924fd Mon Sep 17 00:00:00 2001 From: "rch (Open ERP)" <> Date: Mon, 26 Apr 2010 16:59:00 +0530 Subject: [PATCH 032/593] [IMP]: Improve SQL view report for crm oppertunities. Add fields in Extended option in search view. bzr revid: rch_open_erp-20100426112900-cfx18360ey1p6zqo --- addons/crm/report/crm_opportunity_report.py | 12 +++++++++++- addons/crm/report/crm_opportunity_report_view.xml | 13 ++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/addons/crm/report/crm_opportunity_report.py b/addons/crm/report/crm_opportunity_report.py index a3af4be8b7c..3e11307ee36 100644 --- a/addons/crm/report/crm_opportunity_report.py +++ b/addons/crm/report/crm_opportunity_report.py @@ -21,6 +21,7 @@ from osv import fields,osv import tools +import crm_report AVAILABLE_STATES = [ ('draft','Draft'), @@ -50,6 +51,9 @@ class crm_opportunity_report(osv.osv): 'partner_id': fields.many2one('res.partner', 'Partner', readonly=True), 'company_id': fields.many2one('res.company', 'Company', readonly=True), 'user_id':fields.many2one('res.users', 'User', readonly=True), + 'date_closed': fields.datetime('Closed', readonly=True), + 'date_open': fields.datetime('Opened', readonly=True), + 'priority': fields.selection(crm_report.AVAILABLE_PRIORITIES, 'Priority') } def init(self, cr): @@ -76,6 +80,9 @@ class crm_opportunity_report(osv.osv): 0 as avg_answers, 0.0 as perc_done, 0.0 as perc_cancel, + c.date_closed as date_closed, + c.date_open as date_open, + c.priority as priority, date_trunc('day',c.create_date) as create_date, sum(planned_revenue) as amount_revenue, sum((planned_revenue*probability)/100.0)::decimal(16,2) as amount_revenue_prob, @@ -94,7 +101,10 @@ class crm_opportunity_report(osv.osv): c.categ_id, c.partner_id, company_id, - create_date + create_date, + c.date_closed, + c.date_open, + c.priority )""") crm_opportunity_report() diff --git a/addons/crm/report/crm_opportunity_report_view.xml b/addons/crm/report/crm_opportunity_report_view.xml index 29a66b838f0..3daa72c717c 100644 --- a/addons/crm/report/crm_opportunity_report_view.xml +++ b/addons/crm/report/crm_opportunity_report_view.xml @@ -25,6 +25,9 @@ + + + @@ -75,7 +78,13 @@ + + + + + + - + + From 97ae945f3a8b81ef5387e8768cf67148c7195f10 Mon Sep 17 00:00:00 2001 From: "rch (Open ERP)" <> Date: Mon, 26 Apr 2010 19:14:14 +0530 Subject: [PATCH 033/593] [IMP]: Improve SQL view report for crm phonecalls. Add fields in Extended Option of search view. bzr revid: rch_open_erp-20100426134414-44oq5wfkhs8y7jc1 --- addons/crm/report/crm_phonecall_report.py | 10 +++++++++ .../crm/report/crm_phonecall_report_view.xml | 21 +++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/addons/crm/report/crm_phonecall_report.py b/addons/crm/report/crm_phonecall_report.py index 40b34779aba..c5109602eb3 100644 --- a/addons/crm/report/crm_phonecall_report.py +++ b/addons/crm/report/crm_phonecall_report.py @@ -21,6 +21,7 @@ from osv import fields,osv import tools +import crm_report class crm_phonecall_report(osv.osv): """ Phone calls by user and section """ @@ -37,6 +38,10 @@ class crm_phonecall_report(osv.osv): ('object_id.model', '=', 'crm.phonecall')]"), 'partner_id': fields.many2one('res.partner', 'Partner' , readonly=True), 'company_id': fields.many2one('res.company', 'Company', readonly=True), + 'priority': fields.selection(crm_report.AVAILABLE_PRIORITIES, 'Priority'), + 'date_closed': fields.datetime('Closed', readonly=True), + 'opportunity_id': fields.many2one ('crm.opportunity', 'Opportunity'), + 'canal_id': fields.many2one('res.partner.canal','Channel',domain="[('section_id','=',section_id),('object_id.model', '=', 'crm.phonecall')]") } def init(self, cr): @@ -63,6 +68,10 @@ class crm_phonecall_report(osv.osv): 0 as avg_answers, 0.0 as perc_done, 0.0 as perc_cancel, + c.priority as priority, + c.date_closed as date_closed, + c.opportunity_id as opportunity_id, + c.canal_id as canal_id, date_trunc('day',c.create_date) as create_date, avg(extract('epoch' from (c.date_closed-c.create_date)))/(3600*24) as delay_close from @@ -70,6 +79,7 @@ class crm_phonecall_report(osv.osv): group by to_char(c.create_date, 'YYYY'), to_char(c.create_date, 'MM'),\ c.state, c.user_id,c.section_id, c.categ_id,c.partner_id,c.company_id ,to_char(c.create_date, 'YYYY-MM-DD'),c.create_date + ,c.priority,c.date_closed,opportunity_id,canal_id )""") crm_phonecall_report() diff --git a/addons/crm/report/crm_phonecall_report_view.xml b/addons/crm/report/crm_phonecall_report_view.xml index 698a9b88fa3..5ad0a63bdd8 100644 --- a/addons/crm/report/crm_phonecall_report_view.xml +++ b/addons/crm/report/crm_phonecall_report_view.xml @@ -21,6 +21,10 @@ + + + + @@ -70,8 +74,25 @@ + + + + + + + + + + + + From bb50f760f7357ae2ca1546969ebd30ac0c09a0e0 Mon Sep 17 00:00:00 2001 From: apa-tiny Date: Tue, 27 Apr 2010 15:13:43 +0530 Subject: [PATCH 034/593] [IMP]:hr_holidays:Improved SQL report.(Leaves). bzr revid: apa@tinyerp.com-20100427094343-mbfa6xv6mgri5xqj --- .../hr_holidays/report/hr_holidays_report.py | 17 ++- .../report/hr_holidays_report_view.xml | 109 +++++++++++------- 2 files changed, 81 insertions(+), 45 deletions(-) diff --git a/addons/hr_holidays/report/hr_holidays_report.py b/addons/hr_holidays/report/hr_holidays_report.py index 435eee71ea5..c060521af0d 100644 --- a/addons/hr_holidays/report/hr_holidays_report.py +++ b/addons/hr_holidays/report/hr_holidays_report.py @@ -30,14 +30,17 @@ class hr_holidays_report(osv.osv): _columns = { 'date': fields.datetime('Date', readonly=True), 'year': fields.char('Year', size=4, readonly=True), + 'day': fields.char('Day', size=15, readonly=True), 'month':fields.selection([('01','January'), ('02','February'), ('03','March'), ('04','April'), ('05','May'), ('06','June'), ('07','July'), ('08','August'), ('09','September'), ('10','October'), ('11','November'), ('12','December')], 'Month',readonly=True), 'date_from' : fields.datetime('Start Date', readonly=True), 'date_to' : fields.datetime('End Date', readonly=True), - 'number_of_days_temp': fields.float('Number of Days', readonly=True), + 'number_of_days_temp': fields.float('#Days', readonly=True), 'employee_id' : fields.many2one('hr.employee', "Employee's Name",readonly=True), 'user_id':fields.many2one('res.users', 'User', readonly=True), + 'holiday_status_id' : fields.many2one("hr.holidays.status", "Leave Type",readonly=True), + 'department_id':fields.many2one('hr.department','Department',readonly=True), 'state': fields.selection([('draft', 'Draft'), ('confirm', 'Waiting Validation'), ('refuse', 'Refused'), @@ -52,21 +55,25 @@ class hr_holidays_report(osv.osv): create or replace view hr_holidays_report as ( select min(s.id) as id, - date_trunc('seconds',s.create_date) as date, + date_trunc('day',s.create_date) as date, date_trunc('day',s.date_from) as date_from, date_trunc('day',s.date_to) as date_to, - s.number_of_days_temp, + sum(s.number_of_days_temp) as number_of_days_temp, s.employee_id, s.user_id as user_id, to_char(s.create_date, 'YYYY') as year, to_char(s.create_date, 'MM') as month, + to_char(s.create_date, 'YYYY-MM-DD') as day, + s.holiday_status_id, + s.department_id, s.state from hr_holidays s where type='remove' group by s.create_date,s.state,s.date_from,s.date_to, - s.number_of_days_temp,s.employee_id,s.user_id + s.employee_id,s.user_id,s.holiday_status_id, + s.department_id ) """) hr_holidays_report() @@ -98,7 +105,7 @@ class hr_holidays_remaining_leaves_user(osv.osv): where hrs.employee_id = hre.id and hre.resource_id = rr.id and - hhs.id = hrs.holiday_status_id + hhs.id = hrs.holiday_status_id group by rr.name,rr.user_id,hhs.name ) diff --git a/addons/hr_holidays/report/hr_holidays_report_view.xml b/addons/hr_holidays/report/hr_holidays_report_view.xml index e9e0e6aae36..c5f2a41afb7 100644 --- a/addons/hr_holidays/report/hr_holidays_report_view.xml +++ b/addons/hr_holidays/report/hr_holidays_report_view.xml @@ -7,14 +7,17 @@ tree - - - - - + + + + + + + + - + @@ -27,6 +30,7 @@ + @@ -37,44 +41,69 @@ search - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + - - + + + @@ -85,7 +114,7 @@ hr.holidays.report form tree,graph - {'search_default_employee': 1} + {'search_default_month':1,'search_default_Employee':1,'group_by_no_leaf':1,'group_by':[]} From 091e1c13c9fc901ffe3876e61cc88c95ff002e07 Mon Sep 17 00:00:00 2001 From: apa-tiny Date: Tue, 27 Apr 2010 15:44:34 +0530 Subject: [PATCH 035/593] [IMP]:hr_holidays:Improved SQL report.(Leaves). bzr revid: apa@tinyerp.com-20100427101434-du3rqytyaiaheq55 --- addons/hr_holidays/report/hr_holidays_report_view.xml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/addons/hr_holidays/report/hr_holidays_report_view.xml b/addons/hr_holidays/report/hr_holidays_report_view.xml index c5f2a41afb7..8c8bc58204c 100644 --- a/addons/hr_holidays/report/hr_holidays_report_view.xml +++ b/addons/hr_holidays/report/hr_holidays_report_view.xml @@ -9,6 +9,7 @@ + @@ -68,7 +69,8 @@ domain="[('state','=','validate')]" help = "Pending Leaves"/> - + + @@ -95,9 +97,10 @@ - - + + + @@ -114,7 +117,7 @@ hr.holidays.report form tree,graph - {'search_default_month':1,'search_default_Employee':1,'group_by_no_leaf':1,'group_by':[]} + {'search_default_month':1,'search_default_User':1,'group_by_no_leaf':1,'group_by':[]} From cfb21e74972a0174dfe552449a0a6e813be6328a Mon Sep 17 00:00:00 2001 From: apa-tiny Date: Tue, 27 Apr 2010 15:53:03 +0530 Subject: [PATCH 036/593] [IMP]:hr_holidays:Improved SQL report.(Leaves). bzr revid: apa@tinyerp.com-20100427102303-98gutwonfqyd4lbu --- addons/hr_holidays/report/available_holidays.py | 2 +- addons/hr_holidays/report/hr_holidays_report.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/addons/hr_holidays/report/available_holidays.py b/addons/hr_holidays/report/available_holidays.py index 4a1972f1f7d..fd9d77a62ff 100644 --- a/addons/hr_holidays/report/available_holidays.py +++ b/addons/hr_holidays/report/available_holidays.py @@ -25,7 +25,7 @@ class available_holidays_report(osv.osv): _name = "available.holidays.report" _auto = False _columns = { - 'date': fields.datetime('Date', readonly=True), + 'date': fields.date('Date', readonly=True), 'year': fields.char('Year', size=4, readonly=True), 'month':fields.selection([('01','January'), ('02','February'), ('03','March'), ('04','April'), ('05','May'), ('06','June'), ('07','July'), ('08','August'), ('09','September'), diff --git a/addons/hr_holidays/report/hr_holidays_report.py b/addons/hr_holidays/report/hr_holidays_report.py index c060521af0d..b44fe041e25 100644 --- a/addons/hr_holidays/report/hr_holidays_report.py +++ b/addons/hr_holidays/report/hr_holidays_report.py @@ -28,14 +28,14 @@ class hr_holidays_report(osv.osv): _auto = False _rec_name = 'date' _columns = { - 'date': fields.datetime('Date', readonly=True), + 'date': fields.date('Date', readonly=True), 'year': fields.char('Year', size=4, readonly=True), 'day': fields.char('Day', size=15, readonly=True), 'month':fields.selection([('01','January'), ('02','February'), ('03','March'), ('04','April'), ('05','May'), ('06','June'), ('07','July'), ('08','August'), ('09','September'), ('10','October'), ('11','November'), ('12','December')], 'Month',readonly=True), - 'date_from' : fields.datetime('Start Date', readonly=True), - 'date_to' : fields.datetime('End Date', readonly=True), + 'date_from' : fields.date('Start Date', readonly=True), + 'date_to' : fields.date('End Date', readonly=True), 'number_of_days_temp': fields.float('#Days', readonly=True), 'employee_id' : fields.many2one('hr.employee', "Employee's Name",readonly=True), 'user_id':fields.many2one('res.users', 'User', readonly=True), From efc69230d36501faf03f9136cf9be40e33ad5941 Mon Sep 17 00:00:00 2001 From: apa-tiny Date: Tue, 27 Apr 2010 16:19:27 +0530 Subject: [PATCH 037/593] [IMP]:hr_holidays:Improved SQL report.(Available Holidays). bzr revid: apa@tinyerp.com-20100427104927-1683smsdb4g9xuvv --- .../hr_holidays/report/available_holidays.py | 7 +- .../report/available_holidays_view.xml | 73 ++++++++++--------- 2 files changed, 44 insertions(+), 36 deletions(-) diff --git a/addons/hr_holidays/report/available_holidays.py b/addons/hr_holidays/report/available_holidays.py index fd9d77a62ff..5499a914f6c 100644 --- a/addons/hr_holidays/report/available_holidays.py +++ b/addons/hr_holidays/report/available_holidays.py @@ -27,6 +27,7 @@ class available_holidays_report(osv.osv): _columns = { 'date': fields.date('Date', readonly=True), 'year': fields.char('Year', size=4, readonly=True), + 'day': fields.char('Day', size=15, readonly=True), 'month':fields.selection([('01','January'), ('02','February'), ('03','March'), ('04','April'), ('05','May'), ('06','June'), ('07','July'), ('08','August'), ('09','September'), ('10','October'), ('11','November'), ('12','December')], 'Month',readonly=True), @@ -35,6 +36,7 @@ class available_holidays_report(osv.osv): 'max_leave': fields.float('Allocated Leaves', readonly=True), 'taken_leaves': fields.float('Taken Leaves', readonly=True), 'remaining_leave': fields.float('Remaining Leaves',readonly=True), + 'department_id':fields.many2one('hr.department','Department',readonly=True), 'user_id':fields.many2one('res.users', 'User', readonly=True), } def init(self, cr): @@ -46,8 +48,10 @@ class available_holidays_report(osv.osv): date_trunc('day',h.create_date) as date, to_char(s.create_date, 'YYYY') as year, to_char(s.create_date, 'MM') as month, + to_char(s.create_date, 'YYYY-MM-DD') as day, h.employee_id as employee_id, h.user_id as user_id, + h.department_id, h.state as state, h.holiday_status_id as holiday_status_id, sum(number_of_days) as remaining_leave, @@ -68,7 +72,8 @@ class available_holidays_report(osv.osv): and s.active <> 'f' group by h.holiday_status_id, h.employee_id, date_trunc('day',h.create_date),to_char(s.create_date, 'YYYY'), - to_char(s.create_date, 'MM'),h.user_id,h.state + to_char(s.create_date, 'MM'),to_char(s.create_date, 'YYYY-MM-DD'),h.user_id, + h.state,h.department_id )""") available_holidays_report() diff --git a/addons/hr_holidays/report/available_holidays_view.xml b/addons/hr_holidays/report/available_holidays_view.xml index 18ea7d58d45..cb610d4214a 100644 --- a/addons/hr_holidays/report/available_holidays_view.xml +++ b/addons/hr_holidays/report/available_holidays_view.xml @@ -10,40 +10,32 @@ tree - - - - + + + + + - + + - - available.holidays.report.form - available.holidays.report - form - - - - - - - - available.holidays.report.graph available.holidays.report graph - + + + - + @@ -55,32 +47,42 @@ - - + + + - + - + + + + + + + - + - - + + + @@ -93,6 +95,7 @@ available.holidays.report form tree,graph + {'search_default_month':1,'search_default_User':1,'group_by_no_leaf':1,'group_by':[]} From c445b5b47185e3039456b784a9ea11500ddcd289 Mon Sep 17 00:00:00 2001 From: "rch (Open ERP)" <> Date: Tue, 27 Apr 2010 18:00:23 +0530 Subject: [PATCH 038/593] [Add]: Add SQL view report for Accounts. bzr revid: rch_open_erp-20100427123023-npkej0o1ed0viscq --- addons/account/__openerp__.py | 1 + addons/account/report/__init__.py | 2 +- .../account/report/account_account_report.py | 77 +++++++++++++++++ .../report/account_account_report_view.xml | 83 +++++++++++++++++++ addons/account/report/account_report_view.xml | 8 +- 5 files changed, 166 insertions(+), 5 deletions(-) create mode 100644 addons/account/report/account_account_report.py create mode 100644 addons/account/report/account_account_report_view.xml diff --git a/addons/account/__openerp__.py b/addons/account/__openerp__.py index abd3d1f459d..e73b3885089 100644 --- a/addons/account/__openerp__.py +++ b/addons/account/__openerp__.py @@ -100,6 +100,7 @@ module named account_voucherss 'report/account_invoice_report_view.xml', 'report/account_report_view.xml', 'report/account_analytic_report_view.xml', + 'report/account_account_report_view.xml' ], 'demo_xml': [ #'demo/price_accuracy00.yml', diff --git a/addons/account/report/__init__.py b/addons/account/report/__init__.py index 09ff866d1a8..959a76013f9 100644 --- a/addons/account/report/__init__.py +++ b/addons/account/report/__init__.py @@ -37,6 +37,6 @@ import compare_account_balance import account_invoice_report import account_report import account_analytic_report - +import account_account_report # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/account/report/account_account_report.py b/addons/account/report/account_account_report.py new file mode 100644 index 00000000000..a872a7173e7 --- /dev/null +++ b/addons/account/report/account_account_report.py @@ -0,0 +1,77 @@ +# -*- 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 tools +from osv import fields,osv + +class account_account_report(osv.osv): + _name = "account.account.report" + _description = "Account Report" + _auto = False + _columns = { + 'name': fields.char('Name', size=128, readonly=True), + 'code': fields.char('Code', size=64, readonly=True), + 'type': fields.selection([ + ('receivable', 'Receivable'), + ('payable', 'Payable'), + ('view', 'View'), + ('consolidation', 'Consolidation'), + ('other', 'Others'), + ('closed', 'Closed'), + ], 'Internal Type', readonly=True), + 'company_id': fields.many2one('res.company', 'Company', required=True), + 'currency_mode': fields.selection([('current', 'At Date'), ('average', 'Average Rate')], 'Outgoing Currencies Rate',readonly=True), + 'user_type': fields.many2one('account.account.type', 'Account Type',readonly=True), + 'credit': fields.float('Credit', readonly=True), + 'debit': fields.float('Debit', readonly=True), + 'balance': fields.float('Balance', readonly=True) + } + def init(self, cr): + tools.drop_view_if_exists(cr, 'account_account_report') + cr.execute(""" + create or replace view account_account_report as ( + select + min(a.id) as id, + a.name, + a.code, + a.type as type, + a.company_id as company_id, + a.currency_mode as currency_mode, + a.user_type as user_type, + sum(m.credit) as credit, + sum(m.debit) as debit, + (sum(m.credit)-sum(m.debit)) as balance + from + account_account as a + left join + account_move_line as m + on m.account_id=a.id + group by + a.name, + a.code, + a.type, + a.company_id, + a.currency_mode, + a.user_type, + m.account_id + ) + """) +account_account_report() diff --git a/addons/account/report/account_account_report_view.xml b/addons/account/report/account_account_report_view.xml new file mode 100644 index 00000000000..a2b4903c8d9 --- /dev/null +++ b/addons/account/report/account_account_report_view.xml @@ -0,0 +1,83 @@ + + + + + account.account.report.tree + account.account.report + tree + + + + + + + + + + + + + + + + + account.account.report.graph + account.account.report + graph + + + + + + + + + + + + account.account.report.search + account.account.report + search + + + + + + + + + + + + + + + + + + + + + + + + + + + + Accounts + account.account.report + form + tree,graph + {"search_default_Company":1,"search_default_At Date":1,'group_by_no_leaf':1,'group_by':[]} + + + + + + + diff --git a/addons/account/report/account_report_view.xml b/addons/account/report/account_report_view.xml index f0f67b477e3..9b1688730aa 100644 --- a/addons/account/report/account_report_view.xml +++ b/addons/account/report/account_report_view.xml @@ -47,7 +47,7 @@ form tree,graph - + @@ -116,7 +116,7 @@ - + report.account.sales.tree @@ -169,7 +169,7 @@ graph,tree - + @@ -237,7 +237,7 @@ graph,tree - + From 33ee081b63216433f93645a132348f22a0c2315e Mon Sep 17 00:00:00 2001 From: "rch (Open ERP)" <> Date: Wed, 28 Apr 2010 10:47:19 +0530 Subject: [PATCH 039/593] [IMP]: Improve SQL view report for account. bzr revid: rch_open_erp-20100428051719-oyb01eo736xz1jot --- .../account/report/account_account_report_view.xml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/addons/account/report/account_account_report_view.xml b/addons/account/report/account_account_report_view.xml index a2b4903c8d9..390fb144b0a 100644 --- a/addons/account/report/account_account_report_view.xml +++ b/addons/account/report/account_account_report_view.xml @@ -7,7 +7,7 @@ tree - + @@ -48,8 +48,8 @@ string="Average Rate" domain="[('currency_mode','=','average')]"/> + - @@ -59,8 +59,11 @@ - - + + + + + @@ -73,7 +76,7 @@ account.account.report form tree,graph - {"search_default_Company":1,"search_default_At Date":1,'group_by_no_leaf':1,'group_by':[]} + {"search_default_Account":1,"search_default_At Date":1,'group_by_no_leaf':1,'group_by':[]} From 2fddb283e50eaf9febd7147d549b196dbb24b9a3 Mon Sep 17 00:00:00 2001 From: apa-tiny Date: Wed, 28 Apr 2010 11:57:43 +0530 Subject: [PATCH 040/593] [IMP]:hr_timesheet_sheet:Improved SQL report.(Timesheet). bzr revid: apa@tinyerp.com-20100428062743-cwkcunmyn2gjojdi --- .../report/hr_timesheet_invoice_report_view.xml | 2 +- addons/hr_timesheet_sheet/report/timesheet_report.py | 11 ++++------- .../report/timesheet_report_view.xml | 12 +++++------- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/addons/hr_timesheet_invoice/report/hr_timesheet_invoice_report_view.xml b/addons/hr_timesheet_invoice/report/hr_timesheet_invoice_report_view.xml index 4de60162d1a..d61203df1ef 100644 --- a/addons/hr_timesheet_invoice/report/hr_timesheet_invoice_report_view.xml +++ b/addons/hr_timesheet_invoice/report/hr_timesheet_invoice_report_view.xml @@ -103,7 +103,7 @@ {'search_default_month':1,'search_default_User':1,'group_by_no_leaf':1,'group_by':[]} - + diff --git a/addons/hr_timesheet_sheet/report/timesheet_report.py b/addons/hr_timesheet_sheet/report/timesheet_report.py index ee016df56d7..7b79470a9b7 100644 --- a/addons/hr_timesheet_sheet/report/timesheet_report.py +++ b/addons/hr_timesheet_sheet/report/timesheet_report.py @@ -45,9 +45,8 @@ class timesheet_report(osv.osv): ('draft','Draft'), ('confirm','Confirmed'), ('done','Done')], 'State', readonly=True), - 'total_att': fields.float('Total Timesheet',readonly=True), - 'total_ts': fields.float('Total Attendance',readonly=True), - 'total_diff': fields.float('Difference', readonly=True), + 'quantity': fields.float('#Quantity',readonly=True), + 'cost': fields.float('#Cost',readonly=True), } def init(self, cr): @@ -64,9 +63,8 @@ class timesheet_report(osv.osv): to_char(htss.date_current,'MM') as month, to_char(htss.date_current, 'YYYY-MM-DD') as day, count(*) as nbr, - sum(day.total_attendance) as total_att, - sum(day.total_timesheet) as total_ts, - sum(day.total_difference) as total_diff, + sum(aal.unit_amount) as quantity, + sum(aal.amount) as cost, aal.account_id, htss.user_id, htss.company_id, @@ -75,7 +73,6 @@ class timesheet_report(osv.osv): from account_analytic_line as aal left join hr_analytic_timesheet as hat ON (hat.line_id=aal.id) left join hr_timesheet_sheet_sheet as htss ON (hat.line_id=htss.id) - left join hr_timesheet_sheet_sheet_day AS day ON (htss.id = day.sheet_id) group by to_char(htss.date_current,'YYYY'), to_char(htss.date_current,'MM'), diff --git a/addons/hr_timesheet_sheet/report/timesheet_report_view.xml b/addons/hr_timesheet_sheet/report/timesheet_report_view.xml index 976b2a6f8ea..2520d5054fb 100644 --- a/addons/hr_timesheet_sheet/report/timesheet_report_view.xml +++ b/addons/hr_timesheet_sheet/report/timesheet_report_view.xml @@ -7,10 +7,9 @@ graph - - - - + + + @@ -25,9 +24,8 @@ - - - + + From f438daad1217c231fdb6a7526a963d0b38b723d5 Mon Sep 17 00:00:00 2001 From: "rch (Open ERP)" <> Date: Wed, 28 Apr 2010 12:24:28 +0530 Subject: [PATCH 041/593] [IMP]: Improve SQL view report for crm lead by adding fields. Also add #Accounts in account report. bzr revid: rch_open_erp-20100428065428-svgm9aq7juqaalqa --- .../account/report/account_account_report.py | 4 +++- .../report/account_account_report_view.xml | 1 + addons/crm/report/crm_lead_report.py | 9 ++++++++ addons/crm/report/crm_lead_report_view.xml | 23 +++++++++++++++++++ 4 files changed, 36 insertions(+), 1 deletion(-) diff --git a/addons/account/report/account_account_report.py b/addons/account/report/account_account_report.py index a872a7173e7..8a18c7d89f1 100644 --- a/addons/account/report/account_account_report.py +++ b/addons/account/report/account_account_report.py @@ -42,7 +42,8 @@ class account_account_report(osv.osv): 'user_type': fields.many2one('account.account.type', 'Account Type',readonly=True), 'credit': fields.float('Credit', readonly=True), 'debit': fields.float('Debit', readonly=True), - 'balance': fields.float('Balance', readonly=True) + 'balance': fields.float('Balance', readonly=True), + 'nbr': fields.integer('#Accounts', readonly=True), } def init(self, cr): tools.drop_view_if_exists(cr, 'account_account_report') @@ -50,6 +51,7 @@ class account_account_report(osv.osv): create or replace view account_account_report as ( select min(a.id) as id, + count(distinct a.id) as nbr, a.name, a.code, a.type as type, diff --git a/addons/account/report/account_account_report_view.xml b/addons/account/report/account_account_report_view.xml index 390fb144b0a..7388beb05f9 100644 --- a/addons/account/report/account_account_report_view.xml +++ b/addons/account/report/account_account_report_view.xml @@ -12,6 +12,7 @@ + diff --git a/addons/crm/report/crm_lead_report.py b/addons/crm/report/crm_lead_report.py index 92aeb7f28ec..fb30dd58182 100644 --- a/addons/crm/report/crm_lead_report.py +++ b/addons/crm/report/crm_lead_report.py @@ -54,6 +54,10 @@ class crm_lead_report(osv.osv): ('object_id.model', '=', 'crm.lead')]"), 'date_closed': fields.datetime('Closed', readonly=True), 'date_open': fields.datetime('Opened', readonly=True), + 'date_deadline': fields.date('Deadline', readonly=True), + 'opportunity_id': fields.many2one('crm.opportunity', 'Opportunity',readonly=True), + 'country_id': fields.many2one('res.country', 'Country' , readonly=True), + 'state_id': fields.many2one('res.country.state', 'State' , readonly=True) } def init(self, cr): @@ -85,6 +89,10 @@ class crm_lead_report(osv.osv): c.type_id as type_id, c.date_closed as date_closed, c.date_open as date_open, + c.opportunity_id as opportunity_id, + c.country_id as country_id, + c.state_id as state_id, + c.date_deadline as date_deadline, date_trunc('day',c.create_date) as create_date, avg(extract('epoch' from (c.date_closed-c.create_date)))/(3600*24) as delay_close from @@ -93,6 +101,7 @@ class crm_lead_report(osv.osv): c.state, c.user_id,c.section_id,c.stage_id,categ_id,c.partner_id,c.company_id ,c.create_date,to_char(c.create_date, 'YYYY-MM-DD') ,c.priority,c.type_id,c.date_closed,c.date_open + ,c.opportunity_id,c.country_id,c.state_id,c.date_deadline )""") crm_lead_report() diff --git a/addons/crm/report/crm_lead_report_view.xml b/addons/crm/report/crm_lead_report_view.xml index 6ef0d90ac97..ab648d75ea2 100644 --- a/addons/crm/report/crm_lead_report_view.xml +++ b/addons/crm/report/crm_lead_report_view.xml @@ -26,6 +26,10 @@ + + + + @@ -78,7 +82,16 @@ + + + + + + + + + @@ -96,6 +109,16 @@ context="{'group_by':'priority'}" /> + + + + + + From 335e59c2904587502c822df35d122356c7ad2554 Mon Sep 17 00:00:00 2001 From: "rch (Open ERP)" <> Date: Wed, 28 Apr 2010 15:34:04 +0530 Subject: [PATCH 042/593] [IMP]: Improve SQL report for crm claim and crm fundraising. bzr revid: rch_open_erp-20100428100404-8rti5e8kzobayv16 --- addons/crm/report/crm_claim_report.py | 9 ++++++++- addons/crm/report/crm_claim_report_view.xml | 15 ++++++++++++++- addons/crm/report/crm_fundraising_report.py | 8 ++++++-- addons/crm/report/crm_fundraising_report_view.xml | 4 ++++ 4 files changed, 32 insertions(+), 4 deletions(-) diff --git a/addons/crm/report/crm_claim_report.py b/addons/crm/report/crm_claim_report.py index 74b95864759..ef769042ea8 100644 --- a/addons/crm/report/crm_claim_report.py +++ b/addons/crm/report/crm_claim_report.py @@ -48,7 +48,10 @@ class crm_claim_report(osv.osv): ('object_id.model', '=', 'crm.claim')]"), 'date_closed': fields.datetime('Closed', readonly=True), 'canal_id': fields.many2one('res.partner.canal','Channel',domain="[('section_id','=',section_id),('object_id.model', '=', 'crm.claim')]"), - 'som': fields.many2one('res.partner.som', 'State of Mind') + 'som': fields.many2one('res.partner.som', 'State of Mind'), + 'claim_date': fields.datetime('Date Of Claim',readonly=True), + 'deadline_date': fields.datetime('Deadline ',readonly=True), + 'partner_address_id': fields.many2one('res.partner.address','Contact ',readonly=True), } def init(self, cr): @@ -81,6 +84,9 @@ class crm_claim_report(osv.osv): c.date_closed as date_closed, c.canal_id as canal_id, c.som as som, + c.create_date as claim_date, + c.date_deadline as deadline_date, + c.partner_address_id as partner_address_id, date_trunc('day',c.create_date) as create_date, avg(extract('epoch' from (c.date_closed-c.create_date)))/(3600*24) as delay_close from @@ -89,6 +95,7 @@ class crm_claim_report(osv.osv): c.state, c.user_id,c.section_id, c.stage_id,\ c.categ_id,c.partner_id,c.company_id,c.create_date,to_char(c.create_date, 'YYYY-MM-DD') ,c.priority,c.type_id,c.date_closed,c.canal_id,c.som + ,c.create_date,c.date_deadline,c.partner_address_id )""") crm_claim_report() diff --git a/addons/crm/report/crm_claim_report_view.xml b/addons/crm/report/crm_claim_report_view.xml index 74ebbd56d0b..66c1c6ff930 100644 --- a/addons/crm/report/crm_claim_report_view.xml +++ b/addons/crm/report/crm_claim_report_view.xml @@ -27,6 +27,9 @@ + + + @@ -81,11 +84,16 @@ - + + + + + + + + + diff --git a/addons/crm/report/crm_fundraising_report.py b/addons/crm/report/crm_fundraising_report.py index 5765b7367b0..9e1a1111253 100644 --- a/addons/crm/report/crm_fundraising_report.py +++ b/addons/crm/report/crm_fundraising_report.py @@ -44,7 +44,10 @@ class crm_fundraising_report(osv.osv): 'priority': fields.selection(crm_report.AVAILABLE_PRIORITIES, 'Priority'), 'date_closed': fields.datetime('Closed', readonly=True), 'canal_id': fields.many2one('res.partner.canal','Channel',domain="[('section_id','=',section_id),('object_id.model', '=', 'crm.fundraising')]"), - 'som': fields.many2one('res.partner.som', 'State of Mind') + 'som': fields.many2one('res.partner.som', 'State of Mind'), + 'type_id': fields.many2one('crm.case.resource.type', 'Fundraising Type', \ + domain="[('section_id','=',section_id),\ + ('object_id.model', '=', 'crm.fundraising')]"), } def init(self, cr): @@ -75,6 +78,7 @@ class crm_fundraising_report(osv.osv): c.date_closed as date_closed, c.canal_id as canal_id, c.som as som, + c.type_id as type_id, date_trunc('day',c.create_date) as create_date, sum(planned_revenue) as amount_revenue, sum(planned_revenue*probability)::decimal(16,2) as amount_revenue_prob, @@ -85,7 +89,7 @@ class crm_fundraising_report(osv.osv): group by to_char(c.create_date, 'YYYY'), to_char(c.create_date, 'MM'),\ c.state, c.user_id,c.section_id,c.categ_id,c.partner_id,c.company_id, c.create_date,to_char(c.create_date, 'YYYY-MM-DD'),c.priority,c.date_closed - ,c.canal_id,c.som + ,c.canal_id,c.som,c.type_id )""") crm_fundraising_report() diff --git a/addons/crm/report/crm_fundraising_report_view.xml b/addons/crm/report/crm_fundraising_report_view.xml index 7236df4a8b4..95410010edf 100644 --- a/addons/crm/report/crm_fundraising_report_view.xml +++ b/addons/crm/report/crm_fundraising_report_view.xml @@ -28,6 +28,7 @@ + @@ -81,6 +82,7 @@ + @@ -95,6 +97,8 @@ context="{'group_by':'canal_id'}" /> + From 1a3e8c3f79542b371506e86018926cc219c1f094 Mon Sep 17 00:00:00 2001 From: apa-tiny Date: Wed, 28 Apr 2010 17:57:46 +0530 Subject: [PATCH 043/593] [IMP]:mrp:Improved SQL report.(Production). bzr revid: apa@tinyerp.com-20100428122746-1w3vuzwglgl22588 --- addons/mrp/report/mrp_production_order.py | 23 +++++++++++++++---- .../mrp/report/mrp_production_order_view.xml | 18 +++++++++++---- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/addons/mrp/report/mrp_production_order.py b/addons/mrp/report/mrp_production_order.py index 96910e205d7..c5bc5020e20 100644 --- a/addons/mrp/report/mrp_production_order.py +++ b/addons/mrp/report/mrp_production_order.py @@ -33,6 +33,8 @@ class mrp_production_order(osv.osv): 'day': fields.char('Day',size=64,readonly=True), 'origin': fields.char('Source Document', size=64), 'nbr': fields.integer('# of Orders', readonly=True), + 'products_to_consumme': fields.integer('Products to Consumme', readonly=True), + 'consummed_products': fields.integer('Consummed Products', readonly=True), 'date': fields.date('Date', readonly=True), 'product_id': fields.many2one('product.product', 'Product', readonly=True), 'product_qty': fields.float('Product Qty', readonly=True), @@ -53,6 +55,7 @@ class mrp_production_order(osv.osv): 'bom_id': fields.many2one('mrp.bom', 'Bill of Material',readonly=True), 'routing_id': fields.many2one('mrp.routing', string='Routing',readonly=True), 'picking_id': fields.many2one('stock.picking', 'Picking list', readonly=True), + 'product_uom': fields.many2one('product.uom', 'Product UOM', readonly=True), 'priority': fields.selection([('0','Not urgent'), ('1','Normal'), ('2','Urgent'), @@ -72,9 +75,20 @@ class mrp_production_order(osv.osv): to_char(s.create_date, 'MM') as month, to_char(s.create_date, 'YYYY-MM-DD') as day, l.product_id as product_id, + l.product_uom, sum(l.product_qty * u.factor) as product_qty, s.company_id as company_id, count(*) as nbr, + (select count(ll.id) from mrp_production_move_ids as mv + left join stock_move as sm ON (sm.id=mv.move_id) + left join mrp_production_product_line as ll ON (ll.id=mv.production_id) + where sm.state not in ('done','cancel') and ll.id=l.id + group by ll.id) as products_to_consumme, + (select count(ll.id) from mrp_production_move_ids as mv + left join stock_move as sm ON (sm.id=mv.move_id) + left join mrp_production_product_line as ll ON (ll.id=mv.production_id) + where sm.state in ('done','cancel') and ll.id=l.id + group by ll.id) as consummed_products, s.location_src_id, s.location_dest_id, s.bom_id, @@ -86,11 +100,9 @@ class mrp_production_order(osv.osv): s.origin, s.priority, s.state - from - mrp_production_product_line l - left join - mrp_production s on (s.id=l.production_id) - left join product_uom u on (u.id=l.product_uom) + from mrp_production_product_line l + left join mrp_production s on (s.id=l.production_id) + left join product_uom u on (u.id=l.product_uom) group by to_char(s.create_date, 'YYYY'), to_char(s.create_date, 'MM'), @@ -98,6 +110,7 @@ class mrp_production_order(osv.osv): to_date(to_char(s.create_date, 'MM-dd-YYYY'),'MM-dd-YYYY'), l.product_id, l.product_uom, + l.id, s.bom_id, s.routing_id, s.picking_id, diff --git a/addons/mrp/report/mrp_production_order_view.xml b/addons/mrp/report/mrp_production_order_view.xml index 04188f43752..acac00d590a 100644 --- a/addons/mrp/report/mrp_production_order_view.xml +++ b/addons/mrp/report/mrp_production_order_view.xml @@ -16,6 +16,8 @@ + + @@ -28,6 +30,8 @@ + + @@ -81,25 +85,30 @@ + - - + + + + + + + - + - @@ -113,6 +122,7 @@ + From 8a54668cba1b026d17c2a8e61413d4558a63cac7 Mon Sep 17 00:00:00 2001 From: "rch (Open ERP)" <> Date: Wed, 28 Apr 2010 19:08:56 +0530 Subject: [PATCH 044/593] [IMP]: Improve SQL report for task and project_issue. bzr revid: rch_open_erp-20100428133856-4lserqno5zsnvjrq --- addons/project/report/project_report_view.xml | 4 +-- .../report/project_issue_report.py | 22 +++++++++++--- .../report/project_issue_report_view.xml | 30 +++++++++++++------ 3 files changed, 41 insertions(+), 15 deletions(-) diff --git a/addons/project/report/project_report_view.xml b/addons/project/report/project_report_view.xml index 9ee400e6472..ef0abd119fa 100644 --- a/addons/project/report/project_report_view.xml +++ b/addons/project/report/project_report_view.xml @@ -88,7 +88,7 @@ help="Non Assigned Tasks to users" domain="[('user_id','=',False)]"/> - + - + diff --git a/addons/project_issue/report/project_issue_report.py b/addons/project_issue/report/project_issue_report.py index 775e4b933f0..a14eb67bab4 100755 --- a/addons/project_issue/report/project_issue_report.py +++ b/addons/project_issue/report/project_issue_report.py @@ -11,16 +11,23 @@ class project_issue_report(osv.osv): 'stage_id': fields.many2one ('crm.case.stage', 'Stage', domain="[('object_id.model', '=', 'project.issue')]"), 'nbr': fields.integer('# of Issues', readonly=True), 'delay_close': fields.float('Avg Closing Delay', digits=(16,2), readonly=True, group_operator="avg", - help="Number of Days to close the project issue"), + help="Number of Days to close the project issue"), + 'delay_open': fields.float('Avg Opening Delay', digits=(16,2), readonly=True, group_operator="avg", + help="Number of Days to open the project issue"), 'company_id' : fields.many2one('res.company', 'Company'), 'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority'), 'project_id':fields.many2one('project.project', 'Project',readonly=True), 'type_id': fields.many2one('crm.case.resource.type', 'Type', domain="[('object_id.model', '=', 'project.issue')]"), 'date_closed': fields.datetime('Close Date', readonly=True), + 'date_open': fields.datetime('Opened', readonly=True), 'assigned_to' : fields.many2one('res.users', 'Assigned to',readonly=True), 'partner_id': fields.many2one('res.partner','Partner',domain="[('object_id.model', '=', 'project.issue')]"), 'canal_id': fields.many2one('res.partner.canal', 'Channel',readonly=True), - 'task_id': fields.many2one('project.task', 'Task',domain="[('object_id.model', '=', 'project.issue')]" ) + 'task_id': fields.many2one('project.task', 'Task',domain="[('object_id.model', '=', 'project.issue')]" ), + 'analytic_account_id' : fields.many2one('account.analytic.account', 'Analytic Account', + domain="[('partner_id', '=', partner_id)]", + required=True), + 'partner_address_id': fields.many2one('res.partner.address','Contact ',readonly=True), } def init(self, cr): tools.drop_view_if_exists(cr, 'project_issue_report') @@ -46,8 +53,12 @@ class project_issue_report(osv.osv): c.partner_id, c.canal_id, c.task_id, + c.analytic_account_id as analytic_account_id, + c.partner_address_id as partner_address_id, + c.date_open as date_open, date_trunc('day',c.create_date) as create_date, - avg(extract('epoch' from (c.date_closed-c.create_date)))/(3600*24) as delay_close + avg(extract('epoch' from (c.date_closed-c.create_date)))/(3600*24) as delay_close, + avg(extract('epoch' from (c.date_open-c.create_date)))/(3600*24) as delay_open from project_issue c left join @@ -70,7 +81,10 @@ class project_issue_report(osv.osv): c.assigned_to, c.partner_id, c.canal_id, - c.task_id + c.task_id, + c.date_open, + c.analytic_account_id, + c.partner_address_id )""") diff --git a/addons/project_issue/report/project_issue_report_view.xml b/addons/project_issue/report/project_issue_report_view.xml index 24290530be7..f075a562ca2 100755 --- a/addons/project_issue/report/project_issue_report_view.xml +++ b/addons/project_issue/report/project_issue_report_view.xml @@ -15,7 +15,6 @@ - @@ -23,11 +22,16 @@ + + + + + @@ -65,17 +69,21 @@ - - - - + - + + + + - + + + + + @@ -90,11 +98,15 @@ + + - - + + + + From 8415f50d3edbe984886139a5d13af338be60b14f Mon Sep 17 00:00:00 2001 From: "nel@tinyerp.com" <> Date: Wed, 28 Apr 2010 16:40:43 +0200 Subject: [PATCH 045/593] [FIX] month field on views bzr revid: nel@tinyerp.com-20100428144043-wl12x361lpzvcqa1 --- addons/account/report/account_invoice_report_view.xml | 2 +- addons/hr_expense/report/hr_expense_report_view.xml | 2 +- addons/hr_holidays/report/available_holidays_view.xml | 2 +- .../hr_recruitment/report/hr_recruitment_report_view.xml | 8 ++++---- addons/sale/report/sale_report_view.xml | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/addons/account/report/account_invoice_report_view.xml b/addons/account/report/account_invoice_report_view.xml index 5a833e454fb..c4832d82309 100644 --- a/addons/account/report/account_invoice_report_view.xml +++ b/addons/account/report/account_invoice_report_view.xml @@ -129,7 +129,7 @@ - + diff --git a/addons/hr_expense/report/hr_expense_report_view.xml b/addons/hr_expense/report/hr_expense_report_view.xml index 972557edd0c..ae111fa6796 100644 --- a/addons/hr_expense/report/hr_expense_report_view.xml +++ b/addons/hr_expense/report/hr_expense_report_view.xml @@ -121,7 +121,7 @@ - + diff --git a/addons/hr_holidays/report/available_holidays_view.xml b/addons/hr_holidays/report/available_holidays_view.xml index cb610d4214a..5d6cc41a730 100644 --- a/addons/hr_holidays/report/available_holidays_view.xml +++ b/addons/hr_holidays/report/available_holidays_view.xml @@ -81,7 +81,7 @@ - + diff --git a/addons/hr_recruitment/report/hr_recruitment_report_view.xml b/addons/hr_recruitment/report/hr_recruitment_report_view.xml index 83f7d2b7602..f9a144dadc4 100644 --- a/addons/hr_recruitment/report/hr_recruitment_report_view.xml +++ b/addons/hr_recruitment/report/hr_recruitment_report_view.xml @@ -119,9 +119,9 @@ - - - + + + @@ -136,7 +136,7 @@ hr.recruitment.report form tree,graph - {'search_default_month':1,'search_default_User':1,'group_by_no_leaf':1,'group_by':[]} + {'search_default_month':1,'search_default_degree':1,'search_default_job':1,'group_by_no_leaf':1,'group_by':[]} diff --git a/addons/sale/report/sale_report_view.xml b/addons/sale/report/sale_report_view.xml index 72707f6e01d..73029cb88fa 100644 --- a/addons/sale/report/sale_report_view.xml +++ b/addons/sale/report/sale_report_view.xml @@ -114,7 +114,7 @@ - + From 29b9372b9cd7214395dc35baf60984dadd917249 Mon Sep 17 00:00:00 2001 From: "nel@tinyerp.com" <> Date: Wed, 28 Apr 2010 16:53:19 +0200 Subject: [PATCH 046/593] rename button bzr revid: nel@tinyerp.com-20100428145319-akcdb0ld4lcz8qtw --- addons/crm/report/crm_phonecall_report_view.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/crm/report/crm_phonecall_report_view.xml b/addons/crm/report/crm_phonecall_report_view.xml index 5ad0a63bdd8..e80cd8f82da 100644 --- a/addons/crm/report/crm_phonecall_report_view.xml +++ b/addons/crm/report/crm_phonecall_report_view.xml @@ -88,7 +88,7 @@ - From a3f82274260ad12288dd7d1df1f82c9b14ff2453 Mon Sep 17 00:00:00 2001 From: apa-tiny Date: Thu, 29 Apr 2010 11:59:53 +0530 Subject: [PATCH 047/593] [IMP]:hr_recruitment:Add Partner and Partner c bzr revid: apa@tinyerp.com-20100429062953-qt8zrs0d1uer5z2w --- .../report/hr_recruitment_report.py | 6 +++++ .../report/hr_recruitment_report_view.xml | 23 +++++++++++++------ 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/addons/hr_recruitment/report/hr_recruitment_report.py b/addons/hr_recruitment/report/hr_recruitment_report.py index f59ea414fec..52fbc94be8a 100644 --- a/addons/hr_recruitment/report/hr_recruitment_report.py +++ b/addons/hr_recruitment/report/hr_recruitment_report.py @@ -44,6 +44,8 @@ class hr_recruitment_report(osv.osv): 'priority': fields.selection(hr_recruitment.AVAILABLE_PRIORITIES, 'Appreciation'), 'salary_prop' : fields.float("Salary Proposed"), 'salary_exp' : fields.float("Salary Expected"), + 'partner_id': fields.many2one('res.partner', 'Partner',readonly=True), + 'partner_address_id': fields.many2one('res.partner.address', 'Partner Contact',readonly=True), 'available' : fields.float("Availability") } @@ -60,7 +62,9 @@ class hr_recruitment_report(osv.osv): to_char(s.create_date, 'MM') as month, to_char(s.create_date, 'YYYY-MM-DD') as day, s.state, + s.partner_id, s.company_id, + s.partner_address_id, s.user_id, s.job_id, s.type_id, @@ -79,6 +83,8 @@ class hr_recruitment_report(osv.osv): date_trunc('day',s.create_date), date_trunc('day',s.date_closed), s.state, + s.partner_id, + s.partner_address_id, s.company_id, s.user_id, s.stage_id, diff --git a/addons/hr_recruitment/report/hr_recruitment_report_view.xml b/addons/hr_recruitment/report/hr_recruitment_report_view.xml index f9a144dadc4..4cdaaca68bc 100644 --- a/addons/hr_recruitment/report/hr_recruitment_report_view.xml +++ b/addons/hr_recruitment/report/hr_recruitment_report_view.xml @@ -13,6 +13,8 @@ + + @@ -100,29 +102,36 @@ string="Hired" name="done" domain="[('state','=','done')]"/> - + + - - + + + + + + - + + - + + From 6460e5ab80a356988ebf38491134613d9c7e9b2c Mon Sep 17 00:00:00 2001 From: "rch (Open ERP)" <> Date: Thu, 29 Apr 2010 12:11:34 +0530 Subject: [PATCH 048/593] [FIX]: Resolve confusion between state and country's state in crm lead report. bzr revid: rch_open_erp-20100429064134-apl3s8gxt6wod3zc --- addons/crm/report/crm_lead_report_view.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/crm/report/crm_lead_report_view.xml b/addons/crm/report/crm_lead_report_view.xml index ab648d75ea2..becf76db72d 100644 --- a/addons/crm/report/crm_lead_report_view.xml +++ b/addons/crm/report/crm_lead_report_view.xml @@ -28,7 +28,7 @@ - + @@ -86,7 +86,7 @@ - + @@ -117,7 +117,7 @@ - From 68137a3117ed1efa3b82deb380f82593b4f70828 Mon Sep 17 00:00:00 2001 From: "rch (Open ERP)" <> Date: Thu, 29 Apr 2010 13:57:53 +0530 Subject: [PATCH 049/593] [IMP]: Improve SQL report for Invoice. bzr revid: rch_open_erp-20100429082753-ao26rotnkwjwmtfh --- .../account/report/account_invoice_report.py | 21 +++++++++-- .../report/account_invoice_report_view.xml | 37 +++++++++++++++---- 2 files changed, 47 insertions(+), 11 deletions(-) diff --git a/addons/account/report/account_invoice_report.py b/addons/account/report/account_invoice_report.py index 36fa5a0d132..5aff8f34f89 100644 --- a/addons/account/report/account_invoice_report.py +++ b/addons/account/report/account_invoice_report.py @@ -61,6 +61,11 @@ class account_invoice_report(osv.osv): ('paid','Done'), ('cancel','Cancelled') ], 'Order State', readonly=True), + 'date_due': fields.date('Due Date', readonly=True), + 'address_contact_id': fields.many2one('res.partner.address', 'Contact Address', readonly=True), + 'address_invoice_id': fields.many2one('res.partner.address', 'Invoice Address', readonly=True), + 'account_id': fields.many2one('account.account', 'Account',readonly=True), + 'partner_bank': fields.many2one('res.partner.bank', 'Bank Account',readonly=True) } _order = 'date desc' def init(self, cr): @@ -87,8 +92,13 @@ class account_invoice_report(osv.osv): (sum(l.quantity*l.price_unit)/sum(l.quantity * u.factor))::decimal(16,2) as price_average, count(*) as nbr, s.type as type, - s.state - from + s.state, + s.date_due as date_due, + s.address_contact_id as address_contact_id, + s.address_invoice_id as address_invoice_id, + s.account_id as account_id, + s.partner_bank as partner_bank + from account_invoice_line l left join account_invoice s on (s.id=l.invoice_id) @@ -106,7 +116,12 @@ class account_invoice_report(osv.osv): s.period_id, s.fiscal_position, s.currency_id, - s.journal_id + s.journal_id, + s.date_due, + s.address_contact_id, + s.address_invoice_id, + s.account_id, + s.partner_bank ) """) account_invoice_report() diff --git a/addons/account/report/account_invoice_report_view.xml b/addons/account/report/account_invoice_report_view.xml index c4832d82309..4764e0a5942 100644 --- a/addons/account/report/account_invoice_report_view.xml +++ b/addons/account/report/account_invoice_report_view.xml @@ -27,6 +27,11 @@ + + + + + @@ -76,7 +81,6 @@ domain="[('state', '=' ,'open')]" help = "In progress tasks"/> - + @@ -102,28 +107,44 @@ domain="[('state', '=' ,'cancel')]" help = "Cancelled tasks"/> + + - + + + + + + - + + + + - + + + + + + - - - - + + + + + From c92db23ececc941655c6b5d8e24d0ccfa981dfaa Mon Sep 17 00:00:00 2001 From: apa-tiny Date: Thu, 29 Apr 2010 14:22:09 +0530 Subject: [PATCH 050/593] [IMP]:hr_expense:Improved SQL report.(Expense). bzr revid: apa@tinyerp.com-20100429085209-lwal4qdyci0eu92p --- addons/hr_expense/report/hr_expense_report.py | 16 +++++++++++----- .../hr_expense/report/hr_expense_report_view.xml | 5 +++++ 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/addons/hr_expense/report/hr_expense_report.py b/addons/hr_expense/report/hr_expense_report.py index cc3f282e9fa..7f0c6cc09a6 100644 --- a/addons/hr_expense/report/hr_expense_report.py +++ b/addons/hr_expense/report/hr_expense_report.py @@ -45,7 +45,9 @@ class hr_expense_report(osv.osv): 'department_id':fields.many2one('hr.department','Department',readonly=True), 'company_id':fields.many2one('res.company', 'Company', readonly=True), 'user_id':fields.many2one('res.users', 'User', readonly=True), + 'currency_id': fields.many2one('res.currency', 'Currency', readonly=True), 'price_total':fields.float('Total Price', readonly=True), + 'analytic_account': fields.many2one('account.analytic.account','Analytic account',readonly=True), 'price_average':fields.float('Average Price', readonly=True), 'nbr':fields.integer('# of Lines', readonly=True), 'state': fields.selection([ @@ -67,6 +69,7 @@ class hr_expense_report(osv.osv): date_trunc('day',s.create_date) as date, s.employee_id, s.journal_id, + s.currency_id, to_date(to_char(s.date_confirm, 'dd-MM-YYYY'),'dd-MM-YYYY') as date_confirm, to_date(to_char(s.date_valid, 'dd-MM-YYYY'),'dd-MM-YYYY') as date_valid, s.invoice_id, @@ -75,6 +78,7 @@ class hr_expense_report(osv.osv): to_char(date_trunc('day',s.create_date), 'MM') as month, to_char(date_trunc('day',s.create_date), 'YYYY-MM-DD') as day, l.product_id as product_id, + l.analytic_account as analytic_account, sum(l.unit_quantity * u.factor) as product_qty, s.user_id as user_id, s.company_id as company_id, @@ -82,18 +86,20 @@ class hr_expense_report(osv.osv): (sum(l.unit_quantity*l.unit_amount)/sum(l.unit_quantity * u.factor))::decimal(16,2) as price_average, count(*) as nbr, s.state - from - hr_expense_line l - left join - hr_expense_expense s on (s.id=l.expense_id) - left join product_uom u on (u.id=l.uom_id) + from hr_expense_line l + left join hr_expense_expense s on (s.id=l.expense_id) + left join product_uom u on (u.id=l.uom_id) group by date_trunc('day',s.create_date), + to_char(date_trunc('day',s.create_date), 'YYYY'), + to_char(date_trunc('day',s.create_date), 'MM'), to_char(date_trunc('day',s.create_date), 'YYYY-MM-DD'), to_date(to_char(s.date_confirm, 'dd-MM-YYYY'),'dd-MM-YYYY'), to_date(to_char(s.date_valid, 'dd-MM-YYYY'),'dd-MM-YYYY'), l.product_id, + l.analytic_account, s.invoice_id, + s.currency_id, s.department_id, l.uom_id, s.user_id, diff --git a/addons/hr_expense/report/hr_expense_report_view.xml b/addons/hr_expense/report/hr_expense_report_view.xml index ae111fa6796..e2dbe60ef4e 100644 --- a/addons/hr_expense/report/hr_expense_report_view.xml +++ b/addons/hr_expense/report/hr_expense_report_view.xml @@ -14,9 +14,11 @@ + + @@ -115,7 +117,10 @@ + + + From c7b027483ee3c78a506cf818999a028e317f2880 Mon Sep 17 00:00:00 2001 From: apa-tiny Date: Thu, 29 Apr 2010 15:22:43 +0530 Subject: [PATCH 051/593] [IMP]:hr_expense:Add #Products in SQL virw report..(Expense). bzr revid: apa@tinyerp.com-20100429095243-p3hq66v38jp6kfdj --- addons/hr_expense/report/hr_expense_report.py | 3 +++ addons/hr_expense/report/hr_expense_report_view.xml | 1 + 2 files changed, 4 insertions(+) diff --git a/addons/hr_expense/report/hr_expense_report.py b/addons/hr_expense/report/hr_expense_report.py index 7f0c6cc09a6..4a983984e57 100644 --- a/addons/hr_expense/report/hr_expense_report.py +++ b/addons/hr_expense/report/hr_expense_report.py @@ -50,6 +50,7 @@ class hr_expense_report(osv.osv): 'analytic_account': fields.many2one('account.analytic.account','Analytic account',readonly=True), 'price_average':fields.float('Average Price', readonly=True), 'nbr':fields.integer('# of Lines', readonly=True), + 'no_of_products':fields.integer('# of Products', readonly=True), 'state': fields.selection([ ('draft', 'Draft'), ('confirm', 'Waiting confirmation'), @@ -85,6 +86,7 @@ class hr_expense_report(osv.osv): sum(l.unit_quantity*l.unit_amount) as price_total, (sum(l.unit_quantity*l.unit_amount)/sum(l.unit_quantity * u.factor))::decimal(16,2) as price_average, count(*) as nbr, + (select unit_quantity from hr_expense_line where id=l.id and product_id is not null) as no_of_products, s.state from hr_expense_line l left join hr_expense_expense s on (s.id=l.expense_id) @@ -102,6 +104,7 @@ class hr_expense_report(osv.osv): s.currency_id, s.department_id, l.uom_id, + l.id, s.user_id, s.state, s.journal_id, diff --git a/addons/hr_expense/report/hr_expense_report_view.xml b/addons/hr_expense/report/hr_expense_report_view.xml index e2dbe60ef4e..d717743ff34 100644 --- a/addons/hr_expense/report/hr_expense_report_view.xml +++ b/addons/hr_expense/report/hr_expense_report_view.xml @@ -22,6 +22,7 @@ + From 9dd2f2d39a794226d9727cfa740b14c53a37b015 Mon Sep 17 00:00:00 2001 From: apa-tiny Date: Thu, 29 Apr 2010 15:54:30 +0530 Subject: [PATCH 052/593] [IMP]:hr_expense:Add #Accounts in SQL virw report..(Expense). bzr revid: apa@tinyerp.com-20100429102430-71xa5n0e2x0o7mx6 --- addons/hr_expense/report/hr_expense_report.py | 2 ++ addons/hr_expense/report/hr_expense_report_view.xml | 1 + 2 files changed, 3 insertions(+) diff --git a/addons/hr_expense/report/hr_expense_report.py b/addons/hr_expense/report/hr_expense_report.py index 4a983984e57..2b0f6b4addf 100644 --- a/addons/hr_expense/report/hr_expense_report.py +++ b/addons/hr_expense/report/hr_expense_report.py @@ -51,6 +51,7 @@ class hr_expense_report(osv.osv): 'price_average':fields.float('Average Price', readonly=True), 'nbr':fields.integer('# of Lines', readonly=True), 'no_of_products':fields.integer('# of Products', readonly=True), + 'no_of_account':fields.integer('# of Accounts', readonly=True), 'state': fields.selection([ ('draft', 'Draft'), ('confirm', 'Waiting confirmation'), @@ -87,6 +88,7 @@ class hr_expense_report(osv.osv): (sum(l.unit_quantity*l.unit_amount)/sum(l.unit_quantity * u.factor))::decimal(16,2) as price_average, count(*) as nbr, (select unit_quantity from hr_expense_line where id=l.id and product_id is not null) as no_of_products, + (select count(analytic_account) from hr_expense_line where id=l.id and analytic_account is not null) as no_of_account, s.state from hr_expense_line l left join hr_expense_expense s on (s.id=l.expense_id) diff --git a/addons/hr_expense/report/hr_expense_report_view.xml b/addons/hr_expense/report/hr_expense_report_view.xml index d717743ff34..0fb5c46c210 100644 --- a/addons/hr_expense/report/hr_expense_report_view.xml +++ b/addons/hr_expense/report/hr_expense_report_view.xml @@ -23,6 +23,7 @@ + From ab94bf1e5f2e224a61041404d0c6edbc0062c398 Mon Sep 17 00:00:00 2001 From: apa-tiny Date: Thu, 29 Apr 2010 16:10:08 +0530 Subject: [PATCH 053/593] [IMP]:hr_expense:improved search view..(Expense). bzr revid: apa@tinyerp.com-20100429104008-x4w9tzkvd698ldoq --- addons/hr_expense/report/hr_expense_report_view.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/addons/hr_expense/report/hr_expense_report_view.xml b/addons/hr_expense/report/hr_expense_report_view.xml index 0fb5c46c210..0259c617836 100644 --- a/addons/hr_expense/report/hr_expense_report_view.xml +++ b/addons/hr_expense/report/hr_expense_report_view.xml @@ -87,6 +87,8 @@ help="Expenses Non Assigned User" domain="[('user_id','=',False)]"/> + + @@ -103,13 +105,11 @@ domain="[('state','=','cancelled')]" help = "Cancelled Expenses"/> - - - + - + From 889d4d7ebc72e5f61a07615f1a4796e524e15d7e Mon Sep 17 00:00:00 2001 From: "rch (Open ERP)" <> Date: Thu, 29 Apr 2010 17:16:49 +0530 Subject: [PATCH 054/593] [IMP]: Improve SQL report for Invoice by adding sum Residual and Delay To Pay fields. bzr revid: rch_open_erp-20100429114649-o46vjjrf11y4ulyx --- addons/account/report/account_invoice_report.py | 15 ++++++++++++--- .../report/account_invoice_report_view.xml | 2 ++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/addons/account/report/account_invoice_report.py b/addons/account/report/account_invoice_report.py index 5aff8f34f89..af8ef08e353 100644 --- a/addons/account/report/account_invoice_report.py +++ b/addons/account/report/account_invoice_report.py @@ -65,7 +65,9 @@ class account_invoice_report(osv.osv): 'address_contact_id': fields.many2one('res.partner.address', 'Contact Address', readonly=True), 'address_invoice_id': fields.many2one('res.partner.address', 'Invoice Address', readonly=True), 'account_id': fields.many2one('account.account', 'Account',readonly=True), - 'partner_bank': fields.many2one('res.partner.bank', 'Bank Account',readonly=True) + 'partner_bank': fields.many2one('res.partner.bank', 'Bank Account',readonly=True), + 'residual':fields.float('Total Residual', readonly=True), + 'delay_to_pay':fields.float('Avg. Delay To Pay', readonly=True, group_operator="avg"), } _order = 'date desc' def init(self, cr): @@ -97,12 +99,19 @@ class account_invoice_report(osv.osv): s.address_contact_id as address_contact_id, s.address_invoice_id as address_invoice_id, s.account_id as account_id, - s.partner_bank as partner_bank + s.partner_bank as partner_bank, + sum(s.residual) as residual, + case when s.state != 'paid' then null else + extract(epoch from avg(am.date_created-l.create_date))/(24*60*60)::decimal(16,2) + end as delay_to_pay from account_invoice_line l left join account_invoice s on (s.id=l.invoice_id) - left join product_uom u on (u.id=l.uos_id) + left join product_uom u on (u.id=l.uos_id), + account_move_line am left join account_invoice i on (i.move_id=am.move_id) + where + am.account_id=i.account_id group by s.type, s.date_invoice, diff --git a/addons/account/report/account_invoice_report_view.xml b/addons/account/report/account_invoice_report_view.xml index 4764e0a5942..fea7dbadd93 100644 --- a/addons/account/report/account_invoice_report_view.xml +++ b/addons/account/report/account_invoice_report_view.xml @@ -32,6 +32,8 @@ + + From 565ba12d6cd0441f2940ea34379d443ef362a2d4 Mon Sep 17 00:00:00 2001 From: "aag (Open ERP)" <> Date: Thu, 29 Apr 2010 18:15:40 +0530 Subject: [PATCH 055/593] [ADD]: Add SQL report for Entries. bzr revid: rch_open_erp-20100429124540-b49s7pibrjk4r4i3 --- addons/account/__openerp__.py | 1 + addons/account/report/__init__.py | 1 + .../account/report/account_entries_report.py | 96 ++++++++++++++++ .../report/account_entries_report_view.xml | 106 ++++++++++++++++++ 4 files changed, 204 insertions(+) create mode 100644 addons/account/report/account_entries_report.py create mode 100644 addons/account/report/account_entries_report_view.xml diff --git a/addons/account/__openerp__.py b/addons/account/__openerp__.py index e73b3885089..e87818d40fc 100644 --- a/addons/account/__openerp__.py +++ b/addons/account/__openerp__.py @@ -98,6 +98,7 @@ module named account_voucherss 'company_view.xml', 'account_installer.xml', 'report/account_invoice_report_view.xml', + 'report/account_entries_report_view.xml', 'report/account_report_view.xml', 'report/account_analytic_report_view.xml', 'report/account_account_report_view.xml' diff --git a/addons/account/report/__init__.py b/addons/account/report/__init__.py index 959a76013f9..51cb0810f43 100644 --- a/addons/account/report/__init__.py +++ b/addons/account/report/__init__.py @@ -38,5 +38,6 @@ import account_invoice_report import account_report import account_analytic_report import account_account_report +import account_entries_report # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/account/report/account_entries_report.py b/addons/account/report/account_entries_report.py new file mode 100644 index 00000000000..83602ded81b --- /dev/null +++ b/addons/account/report/account_entries_report.py @@ -0,0 +1,96 @@ +# -*- 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 tools +from osv import fields,osv + +class account_entries_report(osv.osv): + _name = "account.entries.report" + _description = "Entries" + _auto = False + _rec_name = 'date' + _columns = { + 'date': fields.date('Date', readonly=True), + 'nbr':fields.integer('# of Entries', readonly=True), + 'nbl':fields.integer('# of Lines', readonly=True), + 'amount': fields.float('Amount',readonly=True), + 'year': fields.char('Year', size=4, readonly=True), + 'day': fields.char('Day', size=128, readonly=True), + 'month':fields.selection([('01','January'), ('02','February'), ('03','March'), ('04','April'), + ('05','May'), ('06','June'), ('07','July'), ('08','August'), ('09','September'), + ('10','October'), ('11','November'), ('12','December')], 'Month',readonly=True), + 'ref': fields.char('Reference', size=64,readonly=True), + 'period_id': fields.many2one('account.period', 'Period', readonly=True), + 'journal_id': fields.many2one('account.journal', 'Journal', readonly=True), + 'state': fields.selection([('draft','Draft'), ('posted','Posted')], 'State',readonly=True, + help='When new account move is created the state will be \'Draft\'. When all the payments are done it will be in \'Posted\' state.'), + 'partner_id': fields.many2one('res.partner','Partner', readonly=True), + 'type': fields.selection([ + ('pay_voucher','Cash Payment'), + ('bank_pay_voucher','Bank Payment'), + ('rec_voucher','Cash Receipt'), + ('bank_rec_voucher','Bank Receipt'), + ('cont_voucher','Contra'), + ('journal_sale_vou','Journal Sale'), + ('journal_pur_voucher','Journal Purchase'), + ('journal_voucher','Journal Voucher'), + ],'Type',readonly=True), + 'company_id': fields.many2one('res.company', 'Company', readonly=True), + } + _order = 'date desc' + + def init(self, cr): + tools.drop_view_if_exists(cr, 'account_entries_report') + cr.execute(""" + create or replace view account_entries_report as ( + select + min(l.id) as id, + am.ref as ref, + am.state as state, + am.date as date, + count(l.id) as nbr, + count(distinct am.id) as nbl, + l.debit as amount, + to_char(am.date, 'YYYY') as year, + to_char(am.date, 'MM') as month, + to_char(am.date, 'YYYY-MM-DD') as day, + am.company_id as company_id, + am.journal_id as journal_id, + am.period_id as period_id, + l.partner_id as partner_id, + am.type as type + from + account_move_line l + left join + account_move am on (am.id=l.move_id) + group by am.ref, + am.state, + am.date, + am.company_id, + am.journal_id, + am.period_id, + am.type, + l.partner_id, + l.debit + ) + """) + +account_entries_report() diff --git a/addons/account/report/account_entries_report_view.xml b/addons/account/report/account_entries_report_view.xml new file mode 100644 index 00000000000..46f7ac46b83 --- /dev/null +++ b/addons/account/report/account_entries_report_view.xml @@ -0,0 +1,106 @@ + + + + + account.entries.report.tree + account.entries.report + tree + + + + + + + + + + + + + + + + + + + + + account.entries.report.graph + account.entries.report + graph + + + + + + + + + account.entries.report.search + account.entries.report + search + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Entries + account.entries.report + form + tree,graph + {'search_default_This Month':1,'search_default_Journal':1,'group_by_no_leaf':1,'group_by':[]} + + + + + From 004d37b7a00109b35d676c28966026d828a5b68a Mon Sep 17 00:00:00 2001 From: apa-tiny Date: Thu, 29 Apr 2010 18:51:40 +0530 Subject: [PATCH 056/593] [IMP]:stock:improved search view..(Picking). bzr revid: apa@tinyerp.com-20100429132140-sb1d86mvy6rbj5ii --- addons/stock/report/report_stock_picking.py | 34 ++++++++++++++++--- .../report/report_stock_picking_view.xml | 25 +++++++++++++- 2 files changed, 53 insertions(+), 6 deletions(-) diff --git a/addons/stock/report/report_stock_picking.py b/addons/stock/report/report_stock_picking.py index 7f4ac801399..6e1b778b4b0 100644 --- a/addons/stock/report/report_stock_picking.py +++ b/addons/stock/report/report_stock_picking.py @@ -35,12 +35,22 @@ class report_stock_picking(osv.osv): 'nbp': fields.integer('# of Picking', readonly=True), 'partner_id':fields.many2one('res.partner', 'Partner', readonly=True), 'product_qty': fields.float('# of Products', readonly=True), + 'product_id': fields.many2one('product.product', 'Product', readonly=True), 'date': fields.date('Date', readonly=True), 'avg_days_to_deliver': fields.float('Avg Days to Deliver', digits=(16,2), readonly=True, group_operator="avg", help="Number of Avg Days to deliver"), 'origin': fields.char('Origin', size=64), 'state': fields.selection([('draft', 'Draft'),('auto', 'Waiting'),('confirmed', 'Confirmed'),('assigned', 'Available'),('done', 'Done'),('cancel', 'Cancelled')], 'State'), 'type': fields.selection([('out', 'Sending Goods'), ('in', 'Getting Goods'), ('internal', 'Internal'), ('delivery', 'Delivery')], 'Shipping Type', required=True), + 'company_id': fields.many2one('res.company', 'Company', readonly=True), + 'invoice_state': fields.selection([ + ("invoiced", "Invoiced"), + ("2binvoiced", "To Be Invoiced"), + ("none", "Not from Picking")], "Invoice Status",readonly=True), + 'min_date': fields.date('Expected Date',help="Expected date for Picking. Default it takes current date"), + 'order_date': fields.date('Order Date', help="Date of Order"), + 'date_done': fields.date('Date Done', help="Date of completion"), + 'max_date': fields.date('Max.Expected Date'), } def init(self, cr): tools.drop_view_if_exists(cr, 'report_stock_picking') @@ -48,6 +58,10 @@ class report_stock_picking(osv.osv): create or replace view report_stock_picking as ( select min(sm.id) as id, + date_trunc('day',sp.min_date) as min_date, + date_trunc('day',sp.date) as order_date, + date_trunc('day',sp.date_done) as date_done, + date_trunc('day',sp.max_date) as max_date, to_char(sp.create_date, 'YYYY') as year, to_char(sp.create_date, 'MM') as month, to_char(sp.create_date, 'YYYY-MM-DD') as day, @@ -56,7 +70,10 @@ class report_stock_picking(osv.osv): count(sm.id) as nbr, count(distinct sp.id) as nbp, sum(sm.product_qty) as product_qty, + sm.product_id, sp.type, + sp.invoice_state, + sp.company_id, sp.name as reference, sp.origin, avg(extract('epoch' from (sp.date_done-sp.create_date)))/(3600*24) as avg_days_to_deliver, @@ -64,11 +81,18 @@ class report_stock_picking(osv.osv): from stock_move as sm left join stock_picking as sp ON (sm.picking_id=sp.id) group by sp.type, - sp.create_date, - sp.address_id, - sp.name, - sp.origin, - sp.state + sp.create_date, + sp.address_id, + sp.name, + sm.product_id, + date_trunc('day',sp.min_date), + date_trunc('day',sp.date), + date_trunc('day',sp.date_done), + date_trunc('day',sp.max_date), + sp.invoice_state, + sp.origin, + sp.company_id, + sp.state )""") report_stock_picking() diff --git a/addons/stock/report/report_stock_picking_view.xml b/addons/stock/report/report_stock_picking_view.xml index 5d54634d72b..11a8938bd4b 100644 --- a/addons/stock/report/report_stock_picking_view.xml +++ b/addons/stock/report/report_stock_picking_view.xml @@ -16,9 +16,16 @@ + + + + + + + @@ -63,13 +70,29 @@ - + + + + + + + + + + + + + + + + + From 9833cb0448e299b928d6b2b7f1d6d169122db8e5 Mon Sep 17 00:00:00 2001 From: apa-tiny Date: Thu, 29 Apr 2010 18:54:51 +0530 Subject: [PATCH 057/593] [IMP]:stock:improved search view..(Picking). bzr revid: apa@tinyerp.com-20100429132451-mogw5vr4482m4yyf --- addons/stock/report/report_stock_picking.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/addons/stock/report/report_stock_picking.py b/addons/stock/report/report_stock_picking.py index 6e1b778b4b0..8b464a35762 100644 --- a/addons/stock/report/report_stock_picking.py +++ b/addons/stock/report/report_stock_picking.py @@ -85,6 +85,9 @@ class report_stock_picking(osv.osv): sp.address_id, sp.name, sm.product_id, + to_char(sp.create_date, 'YYYY'), + to_char(sp.create_date, 'MM'), + to_char(sp.create_date, 'YYYY-MM-DD'), date_trunc('day',sp.min_date), date_trunc('day',sp.date), date_trunc('day',sp.date_done), From 6a586ba4a94f74324a82867928068f5c6f80b6d6 Mon Sep 17 00:00:00 2001 From: apa-tiny Date: Fri, 30 Apr 2010 14:47:27 +0530 Subject: [PATCH 058/593] [IMP]:hr_timesheet_sheet:improved search view..(Timesheet). bzr revid: apa@tinyerp.com-20100430091727-mjmivw3t38xb0obh --- .../report/timesheet_report.py | 10 ++++ .../report/timesheet_report_view.xml | 46 +++++++++++++------ 2 files changed, 41 insertions(+), 15 deletions(-) diff --git a/addons/hr_timesheet_sheet/report/timesheet_report.py b/addons/hr_timesheet_sheet/report/timesheet_report.py index 7b79470a9b7..3dd8d161c5f 100644 --- a/addons/hr_timesheet_sheet/report/timesheet_report.py +++ b/addons/hr_timesheet_sheet/report/timesheet_report.py @@ -33,7 +33,11 @@ class timesheet_report(osv.osv): ('10','October'), ('11','November'), ('12','December')], 'Month',readonly=True), 'day': fields.char('Day', size=128, readonly=True), 'name': fields.char('Description', size=64,readonly=True), + 'product_id' : fields.many2one('product.product', 'Product'), + 'general_account_id' : fields.many2one('account.account', 'General Account', readonly=True), 'user_id': fields.many2one('res.users', 'User',readonly=True), + 'to_invoice': fields.many2one('hr_timesheet_invoice.factor', 'Type of Invoicing',readonly=True), + 'account_id': fields.many2one('account.analytic.account', 'Analytic Account',readonly=True), 'nbr': fields.integer('#Nbr',readonly=True), 'company_id': fields.many2one('res.company', 'Company',readonly=True), 'department_id':fields.many2one('hr.department','Department',readonly=True), @@ -66,6 +70,9 @@ class timesheet_report(osv.osv): sum(aal.unit_amount) as quantity, sum(aal.amount) as cost, aal.account_id, + aal.product_id, + aal.to_invoice, + aal.general_account_id, htss.user_id, htss.company_id, htss.department_id, @@ -81,6 +88,9 @@ class timesheet_report(osv.osv): htss.date_from, htss.date_to, htss.date_current, + aal.to_invoice, + aal.product_id, + aal.general_account_id, htss.name, htss.company_id, htss.state, diff --git a/addons/hr_timesheet_sheet/report/timesheet_report_view.xml b/addons/hr_timesheet_sheet/report/timesheet_report_view.xml index 2520d5054fb..57df8dcdf32 100644 --- a/addons/hr_timesheet_sheet/report/timesheet_report_view.xml +++ b/addons/hr_timesheet_sheet/report/timesheet_report_view.xml @@ -29,6 +29,10 @@ + + + + @@ -61,10 +65,8 @@ - + - - - - - - - - + + + + + + + + + + + + + + + - + + + + + + - + From 40e34eb66ea1bd44a9480fbac348350a83edba27 Mon Sep 17 00:00:00 2001 From: "rch (Open ERP)" <> Date: Thu, 22 Apr 2010 15:39:10 +0530 Subject: [PATCH 059/593] [IMP]: Improve sale report search view. bzr revid: rch_open_erp-20100422100910-5e6qqo5f0qmdxvmb --- addons/sale/report/sale_report.py | 22 ++++++++++++++++++++-- addons/sale/report/sale_report_view.xml | 21 +++++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/addons/sale/report/sale_report.py b/addons/sale/report/sale_report.py index d1081a4b21f..0372de7ce42 100644 --- a/addons/sale/report/sale_report.py +++ b/addons/sale/report/sale_report.py @@ -57,6 +57,12 @@ class sale_report(osv.osv): ('done', 'Done'), ('cancel', 'Cancelled') ], 'Order State', readonly=True), + 'partner_invoice_id': fields.many2one('res.partner.address', 'Invoice Address', readonly=True), + 'partner_order_id': fields.many2one('res.partner.address', 'Ordering Contact', readonly=True), + 'partner_shipping_id': fields.many2one('res.partner.address', 'Shipping Address', readonly=True), + 'pricelist_id': fields.many2one('product.pricelist', 'Pricelist', required=True), + 'analytic_account_id': fields.many2one('account.analytic.account', 'Analytic Account', readonly=True), + 'date_confirm': fields.date('Confirmation Date', readonly=True) } _order = 'date desc' def init(self, cr): @@ -81,7 +87,13 @@ class sale_report(osv.osv): sum(l.product_uom_qty*l.price_unit) as price_total, (sum(l.product_uom_qty*l.price_unit)/sum(l.product_uom_qty * u.factor))::decimal(16,2) as price_average, count(*) as nbr, - s.state + s.state, + s.partner_invoice_id as partner_invoice_id, + s.partner_order_id as partner_order_id, + s.partner_shipping_id as partner_shipping_id, + s.pricelist_id as pricelist_id, + s.project_id as analytic_account_id, + s.date_confirm as date_confirm from sale_order_line l left join @@ -97,7 +109,13 @@ class sale_report(osv.osv): s.shop_id, s.company_id, s.fiscal_position, - s.payment_term + s.payment_term, + s.partner_invoice_id, + s.partner_order_id, + s.partner_shipping_id, + s.pricelist_id, + s.project_id, + s.date_confirm ) """) sale_report() diff --git a/addons/sale/report/sale_report_view.xml b/addons/sale/report/sale_report_view.xml index 73029cb88fa..41fe598b597 100644 --- a/addons/sale/report/sale_report_view.xml +++ b/addons/sale/report/sale_report_view.xml @@ -24,6 +24,12 @@ + + + + + + @@ -97,8 +103,16 @@ + + + + + + + + @@ -112,6 +126,13 @@ + + + + + + + From 9b5c16eff3ca6a51b8e1fbbf2b74f4ac894d8e60 Mon Sep 17 00:00:00 2001 From: "Ysa (Open ERP)" Date: Thu, 22 Apr 2010 17:15:02 +0530 Subject: [PATCH 060/593] [ADD] Thuderbird :- add configuration wizard. bzr revid: ysa@tinyerp.co.in-20100422114502-c1j5f8oxkkohl9nm --- addons/base_setup/base_setup_installer.xml | 2 +- addons/base_setup/installer.py | 1 + addons/thunderbird/__init__.py | 2 +- addons/thunderbird/__openerp__.py | 3 +- addons/thunderbird/installer.py | 53 ++++++++++ addons/thunderbird/thunderbird_installer.xml | 103 +++++++++++++++++++ 6 files changed, 161 insertions(+), 3 deletions(-) create mode 100644 addons/thunderbird/installer.py create mode 100644 addons/thunderbird/thunderbird_installer.xml diff --git a/addons/base_setup/base_setup_installer.xml b/addons/base_setup/base_setup_installer.xml index 7a6d61a0e08..594133d3b12 100644 --- a/addons/base_setup/base_setup_installer.xml +++ b/addons/base_setup/base_setup_installer.xml @@ -28,7 +28,7 @@ If you don't think you need any of these right now, you can easily install them - + diff --git a/addons/base_setup/installer.py b/addons/base_setup/installer.py index 2c1ba113e47..9a817eaf10e 100644 --- a/addons/base_setup/installer.py +++ b/addons/base_setup/installer.py @@ -73,6 +73,7 @@ class base_setup_installer(osv.osv_memory): 'report_designer':fields.boolean('Advanced Reporting', help="Lets you install various tools to simplify and enhance " "OpenERP's report creation."), + 'thunderbird' :fields.boolean('Thunderbird'), # Vertical modules 'profile_association':fields.boolean('Associations', help="Installs a preselected set of OpenERP " diff --git a/addons/thunderbird/__init__.py b/addons/thunderbird/__init__.py index 5b7582a98f0..4ab5861f126 100644 --- a/addons/thunderbird/__init__.py +++ b/addons/thunderbird/__init__.py @@ -47,4 +47,4 @@ ############################################################################## import partner - +import installer diff --git a/addons/thunderbird/__openerp__.py b/addons/thunderbird/__openerp__.py index 2cb470bf886..5e60f6508cc 100644 --- a/addons/thunderbird/__openerp__.py +++ b/addons/thunderbird/__openerp__.py @@ -39,7 +39,8 @@ Select a section for which you want to create case.''', "init_xml" : [], "demo_xml" : [], - "update_xml" : ['security/ir.model.access.csv'], + "update_xml" : ['thunderbird_installer.xml', + 'security/ir.model.access.csv'], "active": False, "installable": True } diff --git a/addons/thunderbird/installer.py b/addons/thunderbird/installer.py new file mode 100644 index 00000000000..69576aa2a46 --- /dev/null +++ b/addons/thunderbird/installer.py @@ -0,0 +1,53 @@ +# -*- 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 . +# +############################################################################## + +from osv import fields +from osv import osv +from tools import config + +import base64 + +class thunderbird_installer(osv.osv_memory): + _name = 'thunderbird.installer' + _inherit = 'res.config.installer' + + def process_plugin(self, cr, uid, ids, context): + """ + Default Attach Thunderbird Plug-in File. + """ + data = {} + file = open(config['addons_path'] + "/thunderbird/plugin/tiny_plugin-2.0.xpi", 'r') + data['plugin_file'] = base64.encodestring(file.read()) + self.write(cr, uid, ids, data) + return False + + _columns = { + 'name':fields.char('File name', size=34, required=True, help='Save Thunderbird plugin file.'), + 'thunderbird':fields.boolean('Thunderbird Module ', help="Allows you to select an object that you’d like to add to your email and its attachments."), + 'plugin_file':fields.binary('Thunderbird Plug-in', readonly=True, help="Thunderbird plug-in file. Save as this file and install this plug-in in thunderbir."), + + } + _defaults = { + 'thunderbird' : True, + 'name' : 'tiny_plugin-2.0.xpi', + } + +thunderbird_installer() diff --git a/addons/thunderbird/thunderbird_installer.xml b/addons/thunderbird/thunderbird_installer.xml new file mode 100644 index 00000000000..bd19a780456 --- /dev/null +++ b/addons/thunderbird/thunderbird_installer.xml @@ -0,0 +1,103 @@ + + + + + thunderbird.installer.view + thunderbird.installer + form + + + +
+ Thunderbird Module Installation +
+ + Configure Thunderbird Module + + + This module is required for the thuderbird plug-in to work properly.This allows you to select an object that you had like to add to your email and its attachments. You can select a partner, a task, a project, an analytical account, or any other object and attach selected mail as .eml file in attachment of selected record. + + + + + + + + +