diff options
| author | Naresh Choksy <nch@tinyerp.com> | 2008-11-04 11:16:45 +0530 |
|---|---|---|
| committer | Naresh Choksy <nch@tinyerp.com> | 2008-11-04 11:16:45 +0530 |
| commit | 2a4096b752f3634fe255ecd78e842e11d1801a67 (patch) | |
| tree | 9ed47e680afe293497461adf430f146c60e97da2 /addons/document | |
| parent | 54651e2f34af1412df5ab5d3e371bdc4d0600b23 (diff) | |
| parent | da8aa7411aef2f2b962b2b67b73563dce4c45bee (diff) | |
merges
bzr revid: nch@tinyerp.com-20081104054645-pqtvoho3hiyjb2yf
Diffstat (limited to 'addons/document')
| -rw-r--r-- | addons/document/__init__.py | 34 | ||||
| -rw-r--r-- | addons/document/__terp__.py | 63 | ||||
| -rw-r--r-- | addons/document/content_index.py | 21 | ||||
| -rw-r--r-- | addons/document/document.py | 1229 | ||||
| -rw-r--r-- | addons/document/document_data.xml | 7 | ||||
| -rw-r--r-- | addons/document/document_demo.xml | 133 | ||||
| -rw-r--r-- | addons/document/document_view.xml | 71 | ||||
| -rw-r--r-- | addons/document/ftpserver/abstracted_fs.py | 1488 | ||||
| -rw-r--r-- | addons/document/ftpserver/authorizer.py | 131 | ||||
| -rw-r--r-- | addons/document/ftpserver/ftpserver.py | 11 | ||||
| -rw-r--r-- | addons/document/i18n/document.pot | 392 | ||||
| -rw-r--r-- | addons/document/i18n/fr_FR.po | 392 | ||||
| -rw-r--r-- | addons/document/odt2txt.py | 21 | ||||
| -rw-r--r-- | addons/document/process/document_process.xml | 25 | ||||
| -rw-r--r-- | addons/document/security/ir.model.access.csv | 2 |
15 files changed, 2433 insertions, 1587 deletions
diff --git a/addons/document/__init__.py b/addons/document/__init__.py index dc51bcd02e7..6beb56a4c8d 100644 --- a/addons/document/__init__.py +++ b/addons/document/__init__.py @@ -1,28 +1,22 @@ +# -*- encoding: utf-8 -*- ############################################################################## # -# Copyright (c) 2004 TINY SPRL. (http://tiny.be) All Rights Reserved. -# Fabien Pinckaers <fp@tiny.Be> +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2008 Tiny SPRL (<http://tiny.be>). All Rights Reserved +# $Id$ # -# 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 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 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 General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. # ############################################################################## diff --git a/addons/document/__terp__.py b/addons/document/__terp__.py index aafdbebdb1a..6c6aa6adb24 100644 --- a/addons/document/__terp__.py +++ b/addons/document/__terp__.py @@ -1,26 +1,47 @@ +# -*- encoding: utf-8 -*- +############################################################################## # -# Use the custom module to put your specific code in a separate module. +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2008 Tiny SPRL (<http://tiny.be>). All Rights Reserved +# $Id$ # +# 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 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################## { - "name" : "Integrated Document Management System", - "version" : "1.0", - "author" : "Tiny", - "category" : "Generic Modules/Others", - "website": "http://www.tinyerp.com", - "description": """This is a complete document management system: - * FTP Interface - * User Authentification - * Document Indexation + "name" : "Integrated Document Management System", + "version" : "1.0", + "author" : "Tiny", + "category" : "Generic Modules/Others", + "website": "http://www.tinyerp.com", + "description": """This is a complete document management system: + * FTP Interface + * User Authentification + * Document Indexation """, - "depends" : ["base", "process", "hr"], - "init_xml" : ["document_data.xml"], - "update_xml" : [ - "document_view.xml", - "security/document_security.xml", - "security/ir.model.access.csv", - "process/document_process.xml" - ], - "demo_xml" : ["document_demo.xml"], - "active": False, - "installable": True + "depends" : ["base","process"], + "init_xml" : [ + "document_data.xml", + "document_demo.xml" + ], + "update_xml" : [ + "document_view.xml", + "document_data.xml", + "security/document_security.xml", + "security/ir.model.access.csv", + ], + "demo_xml" : [], + "active": False, + "installable": True } diff --git a/addons/document/content_index.py b/addons/document/content_index.py index c4ec7c2c795..080e055b978 100644 --- a/addons/document/content_index.py +++ b/addons/document/content_index.py @@ -1,3 +1,24 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2008 Tiny SPRL (<http://tiny.be>). All Rights Reserved +# $Id$ +# +# 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 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################## import time import os import StringIO diff --git a/addons/document/document.py b/addons/document/document.py index d7904d31285..79d8960363b 100644 --- a/addons/document/document.py +++ b/addons/document/document.py @@ -1,30 +1,22 @@ # -*- encoding: utf-8 -*- ############################################################################## # -# Copyright (c) 2004-2008 TINY SPRL. (http://tiny.be) All Rights Reserved. +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2008 Tiny SPRL (<http://tiny.be>). All Rights Reserved +# $Id$ # -# $Id$ +# 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 3 of the License, or +# (at your option) any later version. # -# 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 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. # -# 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. +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. # ############################################################################## @@ -38,6 +30,8 @@ import os import pooler from content_index import content_index +import netsvc +import StringIO # Unsupported WebDAV Commands: # label @@ -59,374 +53,405 @@ from content_index import content_index # INVALID_CHARS={'*':str(hash('*')), '|':str(hash('|')) , "\\":str(hash("\\")), '/':'__', ':':str(hash(':')), '"':str(hash('"')), '<':str(hash('<')) , '>':str(hash('>')) , '?':str(hash('?'))} class node_class(object): - def __init__(self, cr, uid, path,object,object2=False, context={}, content=False, type='collection', root=False): - self.cr = cr - self.uid = uid - self.path = path - self.object = object - self.object2 = object2 - self.context = context - self.content = content - self.type=type - self.root=root - - 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) ) - for content in self.object.content_ids: - test_nodename = self.object2.name + (content.suffix or '') + (content.extension or '') - if test_nodename.find('/'): - test_nodename=test_nodename.replace('/', '_') - path = self.path+'/'+test_nodename - #path = self.path+'/'+self.object2.name + (content.suffix or '') + (content.extension or '') - if not nodename: - n = node_class(self.cr, self.uid,path, self.object2, False, content=content, type='content', root=False) - res2.append( n) - else: - if nodename == test_nodename: - n = node_class(self.cr, self.uid, path, self.object2, False, content=content, type='content', root=False) - res2.append(n) - else: - where.append( ('parent_id','=',self.object.id) ) - where.append( ('res_id','=',False) ) - if nodename: - where.append( (fobj._rec_name,'=',nodename) ) - ids = fobj.search(self.cr, self.uid, where+[ ('parent_id','=',self.object and self.object.id or False) ], context=self.context) - if self.object and self.root and (self.object.type=='ressource'): - ids += fobj.search(self.cr, self.uid, where+[ ('parent_id','=',False) ], context=self.context) - res = fobj.browse(self.cr, self.uid, ids, context=self.context) - return map(lambda x: node_class(self.cr, self.uid, self.path+'/'+x.name, x, False, type='file', root=False), res) + res2 - - def directory_list_for_child(self,nodename,parent=False): - pool = pooler.get_pool(self.cr.dbname) - where = [] - if nodename: - 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)], self.context) - if self.object2: - ids += pool.get('document.directory').search(self.cr, self.uid, where+[('ressource_id','=',self.object2.id)], self.context) - 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, 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(('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+'/'+x.name, x, False, 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}) or [] - pool = pooler.get_pool(self.cr.dbname) - obj = pool.get(self.object.ressource_type_id.model) - - 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)) - else : - if self.object2: - return result - else: - if self.object2: - return result - - 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) - where.append(('name','=',nodename)) - ids = obj.search(self.cr, self.uid, where, self.context) - res = obj.browse(self.cr, self.uid, ids,self.context) - for r in res: - 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, root=r.id), res) - if result2: - result = result2 - return result - - def children(self): - return self._child_get() + self._file_get() - - def child(self, name): - res = self._child_get(name) - if res: - return res[0] - res = self._file_get(name) - if res: - return res[0] - return None - - def path_get(self): - path = self.path - if self.path[0]=='/': - path = self.path[1:] - return path + def __init__(self, cr, uid, path,object,object2=False, context={}, content=False, type='collection', root=False): + self.cr = cr + self.uid = uid + self.path = path + self.object = object + self.object2 = object2 + self.context = context + self.content = content + self.type=type + self.root=root + + 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) ) + for content in self.object.content_ids: + if self.object2 or not content.include_name: + if content.include_name: + test_nodename = self.object2.name + (content.suffix or '') + (content.extension or '') + else: + test_nodename = (content.suffix or '') + (content.extension or '') + if test_nodename.find('/'): + test_nodename=test_nodename.replace('/', '_') + path = self.path+'/'+test_nodename + #path = self.path+'/'+self.object2.name + (content.suffix or '') + (content.extension or '') + if not nodename: + n = node_class(self.cr, self.uid,path, self.object2, False, content=content, type='content', root=False) + res2.append( n) + else: + if nodename == test_nodename: + n = node_class(self.cr, self.uid, path, self.object2, False, content=content, type='content', root=False) + res2.append(n) + else: + where.append( ('parent_id','=',self.object.id) ) + where.append( ('res_id','=',False) ) + if nodename: + where.append( (fobj._rec_name,'=',nodename) ) + ids = fobj.search(self.cr, self.uid, where+[ ('parent_id','=',self.object and self.object.id or False) ], context=self.context) + if self.object and self.root and (self.object.type=='ressource'): + ids += fobj.search(self.cr, self.uid, where+[ ('parent_id','=',False) ], context=self.context) + res = fobj.browse(self.cr, self.uid, ids, context=self.context) + return map(lambda x: node_class(self.cr, self.uid, self.path+'/'+x.name, x, False, type='file', root=False), res) + res2 + + def directory_list_for_child(self,nodename,parent=False): + pool = pooler.get_pool(self.cr.dbname) + where = [] + if nodename: + 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)], self.context) + if self.object2: + ids += pool.get('document.directory').search(self.cr, self.uid, where+[('ressource_id','=',self.object2.id)], self.context) + 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, 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(('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+'/'+x.name, x, False, 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}) or [] + pool = pooler.get_pool(self.cr.dbname) + obj = pool.get(self.object.ressource_type_id.model) + + 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)) + else : + if self.object2: + return result + else: + if self.object2: + return result + + 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) + where.append(('name','=',nodename)) + ids = obj.search(self.cr, self.uid, where, self.context) + res = obj.browse(self.cr, self.uid, ids,self.context) + for r in res: + 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, root=r.id), res) + if result2: + result = result2 + return result + + def children(self): + return self._child_get() + self._file_get() + + def child(self, name): + res = self._child_get(name) + if res: + return res[0] + res = self._file_get(name) + if res: + return res[0] + return None + + def path_get(self): + path = self.path + if self.path[0]=='/': + path = self.path[1:] + return path class document_directory(osv.osv): - _name = 'document.directory' - _description = 'Document directory' - _columns = { - 'name': fields.char('Name', size=64, required=True, select=1), - 'write_date': fields.datetime('Date Modified', readonly=True), - 'write_uid': fields.many2one('res.users', 'Last Modification User', readonly=True), - 'create_date': fields.datetime('Date Created', readonly=True), - 'create_uid': fields.many2one('res.users', 'Creator', readonly=True), - 'file_type': fields.char('Content Type', size=32), - 'domain': fields.char('Domain', size=128), - 'user_id': fields.many2one('res.users', 'Owner'), - 'group_ids': fields.many2many('res.groups', 'document_directory_group_rel', 'item_id', 'group_id', 'Groups'), - 'parent_id': fields.many2one('document.directory', 'Parent Item'), - 'child_ids': fields.one2many('document.directory', 'parent_id', 'Childs'), - 'file_ids': fields.one2many('ir.attachment', 'parent_id', 'Files'), - 'content_ids': fields.one2many('document.directory.content', 'directory_id', 'Virtual Files'), - 'type': fields.selection([('directory','Static Directory'),('ressource','Other Ressources')], 'Type', required=True), - 'ressource_type_id': fields.many2one('ir.model', 'Childs Model'), - 'ressource_parent_type_id': fields.many2one('ir.model', 'Linked Model'), - 'ressource_id': fields.integer('Ressource ID'), - 'ressource_tree': fields.boolean('Tree Structure'), - } - _defaults = { - 'user_id': lambda self,cr,uid,ctx: uid, - 'domain': lambda self,cr,uid,ctx: '[]', - 'type': lambda *args: 'directory', - } - _sql_constraints = [ - ('dirname_uniq', 'unique (name,parent_id,ressource_id,ressource_parent_type_id)', 'The directory name must be unique !') - ] - - def get_resource_path(self,cr,uid,dir_id,res_model,res_id): - # this method will be used in process module - # to be need test and Improvement if resource dir has parent resource (link resource) - path=[] - def _parent(dir_id,path): - parent=self.browse(cr,uid,dir_id) - if parent.parent_id and not parent.ressource_parent_type_id: - _parent(parent.parent_id.id,path) - path.append(parent.name) - else: - path.append(parent.name) - return path - - directory=self.browse(cr,uid,dir_id) - model_ids=self.pool.get('ir.model').search(cr,uid,[('model','=',res_model)]) - if directory: - _parent(dir_id,path) - path.append(self.pool.get(directory.ressource_type_id.model).browse(cr,uid,res_id).name) - user=self.pool.get('res.users').browse(cr,uid,uid) - #print "ftp://%s:%s@localhost:8021/%s/%s"%(user.login,user.password,cr.dbname,'/'.join(path)) - return "ftp://%s:%s@localhost:8021/%s/%s"%(user.login,user.password,cr.dbname,'/'.join(path)) - return False - def _check_duplication(self, cr, uid,vals): - if 'name' in vals: - where=" name='%s'"% (vals['name']) - if not 'parent_id' in vals or not vals['parent_id']: - where+=' and parent_id is null' - else: - where+=' and parent_id=%d'%(vals['parent_id']) - if not 'ressource_parent_type_id' in vals or not vals['ressource_parent_type_id']: - where+= ' and ressource_parent_type_id is null' - else: - where+=" and ressource_parent_type_id='%s'"%(vals['ressource_parent_type_id']) -# if not 'ressource_id' in vals or not vals['ressource_id']: -# where+= ' and ressource_id is null' -# else: -# where+=" and ressource_id=%d"%(vals['ressource_id']) - cr.execute("select id from document_directory where" + where) - res = cr.fetchall() - if len(res): - return False - return True - def _check_recursion(self, cr, uid, ids): - level = 100 - while len(ids): - cr.execute('select distinct parent_id from document_directory where id in ('+','.join(map(str,ids))+')') - ids = filter(None, map(lambda x:x[0], cr.fetchall())) - if not level: - return False - level -= 1 - return True - - _constraints = [ - (_check_recursion, 'Error! You can not create recursive Directories.', ['parent_id']) - ] - def __init__(self, *args, **kwargs): - res = super(document_directory, self).__init__(*args, **kwargs) - self._cache = {} - return res - - def onchange_content_id(self, cr, uid, ids, ressource_type_id): - return {} - - def _get_childs(self, cr, uid, node, nodename=False, context={}): - where = [] - if nodename: - where.append(('name','=',nodename)) - if object: - where.append(('parent_id','=',object.id)) - ids = self.search(cr, uid, where, context) - return self.browse(cr, uid, ids, context), False - - """ - PRE: - uri: of the form "Sales Order/SO001" - PORT: - uri - object: the object.directory or object.directory.content - object2: the other object linked (if object.directory.content) - """ - def get_object(self, cr, uid, uri, context={}): - if not uri: - return node_class(cr, uid, '', False, type='database') - turi = tuple(uri) - if False and (turi in self._cache): - (path, oo, oo2, content,type,root) = self._cache[turi] - if oo: - object = self.pool.get(oo[0]).browse(cr, uid, oo[1], context) - else: - object = False - if oo2: - object2 = self.pool.get(oo2[0]).browse(cr, uid, oo2[1], context) - else: - object2 = False - node = node_class(cr, uid, path, object,object2, context, content, type, root) - return node - - node = node_class(cr, uid, '/', False, type='database') - for path in uri[:]: - if path: - node = node.child(path) - if not node: - return False - oo = node.object and (node.object._name, node.object.id) or False - oo2 = node.object2 and (node.object2._name, node.object2.id) or False - self._cache[turi] = (node.path, oo, oo2, node.content,node.type,node.root) - return node - - def get_childs(self, cr, uid, uri, context={}): - node = self.get_object(cr, uid, uri, context) - if uri: - children = node.children() - else: - children= [node] - result = map(lambda node: node.path_get(), children) - #childs,object2 = self._get_childs(cr, uid, object, False, context) - #result = map(lambda x: urlparse.urljoin(path+'/',x.name), childs) - return result - - def write(self, cr, uid, ids, vals, context=None): - # need to make constraints to checking duplicate - #if not self._check_duplication(cr,uid,vals): - # raise except_orm('ValidateError', 'Directory name must be unique!') - return super(document_directory,self).write(cr,uid,ids,vals,context=context) - - def copy(self, cr, uid, id, default=None, context=None): - if not default: - default ={} - name = self.read(cr, uid, [id])[0]['name'] - default.update({'name': name+ " (copy)"}) - return super(document_directory,self).copy(cr,uid,id,default,context) - - def create(self, cr, uid, vals, context=None): - if not self._check_duplication(cr,uid,vals): - raise except_orm('ValidateError', 'Directory name must be unique!') - if vals.get('name',False) and (vals.get('name').find('/')+1 or vals.get('name').find('@')+1 or vals.get('name').find('$')+1 or vals.get('name').find('#')+1) : - raise 'Error' - return super(document_directory,self).create(cr, uid, vals, context) + _name = 'document.directory' + _description = 'Document directory' + _columns = { + 'name': fields.char('Name', size=64, required=True, select=1), + 'write_date': fields.datetime('Date Modified', readonly=True), + 'write_uid': fields.many2one('res.users', 'Last Modification User', readonly=True), + 'create_date': fields.datetime('Date Created', readonly=True), + 'create_uid': fields.many2one('res.users', 'Creator', readonly=True), + 'file_type': fields.char('Content Type', size=32), + 'domain': fields.char('Domain', size=128), + 'user_id': fields.many2one('res.users', 'Owner'), + 'group_ids': fields.many2many('res.groups', 'document_directory_group_rel', 'item_id', 'group_id', 'Groups'), + 'parent_id': fields.many2one('document.directory', 'Parent Item'), + 'child_ids': fields.one2many('document.directory', 'parent_id', 'Childs'), + 'file_ids': fields.one2many('ir.attachment', 'parent_id', 'Files'), + 'content_ids': fields.one2many('document.directory.content', 'directory_id', 'Virtual Files'), + 'type': fields.selection([('directory','Static Directory'),('ressource','Other Ressources')], 'Type', required=True), + 'ressource_type_id': fields.many2one('ir.model', 'Childs Model'), + 'ressource_parent_type_id': fields.many2one('ir.model', 'Linked Model'), + 'ressource_id': fields.integer('Ressource ID'), + 'ressource_tree': fields.boolean('Tree Structure'), + } + _defaults = { + 'user_id': lambda self,cr,uid,ctx: uid, + 'domain': lambda self,cr,uid,ctx: '[]', + 'type': lambda *args: 'directory', + 'ressource_id': lambda *a: 0 + } + _sql_constraints = [ + ('dirname_uniq', 'unique (name,parent_id,ressource_id,ressource_parent_type_id)', 'The directory name must be unique !') + ] + + def get_resource_path(self,cr,uid,dir_id,res_model,res_id): + # this method will be used in process module + # to be need test and Improvement if resource dir has parent resource (link resource) + path=[] + def _parent(dir_id,path): + parent=self.browse(cr,uid,dir_id) + if parent.parent_id and not parent.ressource_parent_type_id: + _parent(parent.parent_id.id,path) + path.append(parent.name) + else: + path.append(parent.name) + return path + + directory=self.browse(cr,uid,dir_id) + model_ids=self.pool.get('ir.model').search(cr,uid,[('model','=',res_model)]) + if directory: + _parent(dir_id,path) + path.append(self.pool.get(directory.ressource_type_id.model).browse(cr,uid,res_id).name) + user=self.pool.get('res.users').browse(cr,uid,uid) + return "ftp://%s:%s@localhost:8021/%s/%s"%(user.login,user.password,cr.dbname,'/'.join(path)) + return False + def _check_duplication(self, cr, uid,vals): + if 'name' in vals: + where=" name='%s'"% (vals['name']) + if not 'parent_id' in vals or not vals['parent_id']: + where+=' and parent_id is null' + else: + where+=' and parent_id=%d'%(vals['parent_id']) + if not 'ressource_parent_type_id' in vals or not vals['ressource_parent_type_id']: + where+= ' and ressource_parent_type_id is null' + else: + where+=" and ressource_parent_type_id='%s'"%(vals['ressource_parent_type_id']) +# if not 'ressource_id' in vals or not vals['ressource_id']: +# where+= ' and ressource_id is null' +# else: +# where+=" and ressource_id=%d"%(vals['ressource_id']) + cr.execute("select id from document_directory where" + where) + res = cr.fetchall() + if len(res): + return False + return True + def _check_recursion(self, cr, uid, ids): + level = 100 + while len(ids): + cr.execute('select distinct parent_id from document_directory where id in ('+','.join(map(str,ids))+')') + ids = filter(None, map(lambda x:x[0], cr.fetchall())) + if not level: + return False + level -= 1 + return True + + _constraints = [ + (_check_recursion, 'Error! You can not create recursive Directories.', ['parent_id']) + ] + def __init__(self, *args, **kwargs): + res = super(document_directory, self).__init__(*args, **kwargs) + self._cache = {} + return res + + def onchange_content_id(self, cr, uid, ids, ressource_type_id): + return {} + + def _get_childs(self, cr, uid, node, nodename=False, context={}): + where = [] + if nodename: + where.append(('name','=',nodename)) + if object: + where.append(('parent_id','=',object.id)) + ids = self.search(cr, uid, where, context) + return self.browse(cr, uid, ids, context), False + + """ + PRE: + uri: of the form "Sales Order/SO001" + PORT: + uri + object: the object.directory or object.directory.content + object2: the other object linked (if object.directory.content) + """ + def get_object(self, cr, uid, uri, context={}): + if not uri: + return node_class(cr, uid, '', False, type='database') + turi = tuple(uri) + if False and (turi in self._cache): + (path, oo, oo2, content,type,root) = self._cache[turi] + if oo: + object = self.pool.get(oo[0]).browse(cr, uid, oo[1], context) + else: + object = False + if oo2: + object2 = self.pool.get(oo2[0]).browse(cr, uid, oo2[1], context) + else: + object2 = False + node = node_class(cr, uid, path, object,object2, context, content, type, root) + return node + + node = node_class(cr, uid, '/', False, type='database') + for path in uri[:]: + if path: + node = node.child(path) + if not node: + return False + oo = node.object and (node.object._name, node.object.id) or False + oo2 = node.object2 and (node.object2._name, node.object2.id) or False + self._cache[turi] = (node.path, oo, oo2, node.content,node.type,node.root) + return node + + def get_childs(self, cr, uid, uri, context={}): + node = self.get_object(cr, uid, uri, context) + if uri: + children = node.children() + else: + children= [node] + result = map(lambda node: node.path_get(), children) + #childs,object2 = self._get_childs(cr, uid, object, False, context) + #result = map(lambda x: urlparse.urljoin(path+'/',x.name), childs) + return result + + def write(self, cr, uid, ids, vals, context=None): + # need to make constraints to checking duplicate + #if not self._check_duplication(cr,uid,vals): + # raise except_orm('ValidateError', 'Directory name must be unique!') + return super(document_directory,self).write(cr,uid,ids,vals,context=context) + + def copy(self, cr, uid, id, default=None, context=None): + if not default: + default ={} + name = self.read(cr, uid, [id])[0]['name'] + default.update({'name': name+ " (copy)"}) + return super(document_directory,self).copy(cr,uid,id,default,context) + + def create(self, cr, uid, vals, context=None): + if not self._check_duplication(cr,uid,vals): + raise except_orm('ValidateError', 'Directory name must be unique!') + if vals.get('name',False) and (vals.get('name').find('/')+1 or vals.get('name').find('@')+1 or vals.get('name').find('$')+1 or vals.get('name').find('#')+1) : + raise 'Error' + return super(document_directory,self).create(cr, uid, vals, context) document_directory() class document_directory_node(osv.osv): - _inherit = 'process.node' - _columns = { - 'directory_id': fields.many2one('document.directory', 'Document directory', ondelete="set null"), - } + _inherit = 'process.node' + _columns = { + 'directory_id': fields.many2one('document.directory', 'Document directory', ondelete="set null"), + } document_directory_node() +class document_directory_content_type(osv.osv): + _name = 'document.directory.content.type' + _description = 'Directory Content Type' + _columns = { + 'name': fields.char('Content Type', size=64, required=True), + 'code': fields.char('Extension', size=4), + 'active': fields.boolean('Active'), + } + _defaults = { + 'active': lambda *args: 1 + } +document_directory_content_type() + class document_directory_content(osv.osv): - _name = 'document.directory.content' - _description = 'Directory Content' - _order = "sequence" - _columns = { - 'name': fields.char('Content Name', size=64, required=True), - 'sequence': fields.integer('Sequence', size=16), - 'suffix': fields.char('Suffix', size=16), - 'versioning': fields.boolean('Versioning'), - 'report_id': fields.many2one('ir.actions.report.xml', 'Report', required=True), - 'extension': fields.selection([('.pdf','.pdf'),('','None')], 'Extension', required=True), - 'directory_id': fields.many2one('document.directory', 'Directory') - } - _defaults = { - 'extension': lambda *args: '', - 'sequence': lambda *args: 1 - } + _name = 'document.directory.content' + _description = 'Directory Content' + _order = "sequence" + def _extension_get(self, cr, uid, context={}): + cr.execute('select code,name from document_directory_content_type where active') + res = cr.fetchall() + return res + _columns = { + 'name': fields.char('Content Name', size=64, required=True), + 'sequence': fields.integer('Sequence', size=16), + 'suffix': fields.char('Suffix', size=16), + 'report_id': fields.many2one('ir.actions.report.xml', 'Report'), + 'extension': fields.selection(_extension_get, 'Document Type', required=True, size=4), + 'include_name': fields.boolean('Include Record Name', help="Check if you cant that the name of the file start by the record name."), + 'directory_id': fields.many2one('document.directory', 'Directory'), + } + _defaults = { + 'extension': lambda *args: '.pdf', + 'sequence': lambda *args: 1, + 'include_name': lambda *args: 1, + } + def process_write_pdf(self, cr, uid, node, context={}): + return True + def process_read_pdf(self, cr, uid, node, context={}): + report = self.pool.get('ir.actions.report.xml').browse(cr, uid, node.content.report_id.id) + srv = netsvc.LocalService('report.'+report.report_name) + pdf,pdftype = srv.create(cr, uid, [node.object.id], {}, {}) + s = StringIO.StringIO(pdf) + s.name = node + return s document_directory_content() class ir_action_report_xml(osv.osv): - _name="ir.actions.report.xml" - _inherit ="ir.actions.report.xml" - - def _model_get(self, cr, uid, ids, name, arg, context): - res = {} - model_pool = self.pool.get('ir.model') - for data in self.read(cr,uid,ids,['model']): - model = data.get('model',False) - if model: - model_id =model_pool.search(cr,uid,[('model','=',model)]) - if model_id: - res[data.get('id')] = model_id[0] - else: - res[data.get('id')] = False - return res - - def _model_search(self, cr, uid, obj, name, args): - if not len(args): - return [] - model_id= args[0][2] - if not model_id: - return [] - model = self.pool.get('ir.model').read(cr,uid,[model_id])[0]['model'] - report_id = self.search(cr,uid,[('model','=',model)]) - if not report_id: - return [('id','=','0')] - return [('id','in',report_id)] - - _columns={ - 'model_id' : fields.function(_model_get,fnct_search=_model_search,method=True,string='Model Id'), - } + _name="ir.actions.report.xml" + _inherit ="ir.actions.report.xml" + + def _model_get(self, cr, uid, ids, name, arg, context): + res = {} + model_pool = self.pool.get('ir.model') + for data in self.read(cr,uid,ids,['model']): + model = data.get('model',False) + if model: + model_id =model_pool.search(cr,uid,[('model','=',model)]) + if model_id: + res[data.get('id')] = model_id[0] + else: + res[data.get('id')] = False + return res + + def _model_search(self, cr, uid, obj, name, args): + if not len(args): + return [] + model_id= args[0][2] + if not model_id: + return [] + model = self.pool.get('ir.model').read(cr,uid,[model_id])[0]['model'] + report_id = self.search(cr,uid,[('model','=',model)]) + if not report_id: + return [('id','=','0')] + return [('id','in',report_id)] + + _columns={ + 'model_id' : fields.function(_model_get,fnct_search=_model_search,method=True,string='Model Id'), + } ir_action_report_xml() @@ -436,174 +461,174 @@ import string def random_name(): - random.seed() - d = [random.choice(string.letters) for x in xrange(10) ] - name = "".join(d) - return name + random.seed() + d = [random.choice(string.letters) for x in xrange(10) ] + name = "".join(d) + return name def create_directory(path): - dir_name = random_name() - path = os.path.join(path,dir_name) - os.mkdir(path) - return dir_name + dir_name = random_name() + path = os.path.join(path,dir_name) + os.mkdir(path) + return dir_name class document_file(osv.osv): - _inherit = 'ir.attachment' - def _data_get(self, cr, uid, ids, name, arg, context): - result = {} - cr.execute('select id,store_method,datas,store_fname,link from ir_attachment where id in ('+','.join(map(str,ids))+')') - for id,m,d,r,l in cr.fetchall(): - if m=='db': - result[id] = d - elif m=='fs': - try: - path = os.path.join(os.getcwd(),'filestore') - value = file(os.path.join(path,r), 'rb').read() - result[id] = base64.encodestring(value) - except: - result[id]='' - else: - result[id] = '' - return result - - # - # This code can be improved - # - def _data_set(self, cr, obj, id, name, value, uid=None, context={}): - if not value: - return True - if (not context) or context.get('store_method','fs')=='fs': - path = os.path.join(os.getcwd(), "filestore") - if not os.path.isdir(path): - os.mkdir(path) - flag = None - # This can be improved - for dirs in os.listdir(path): - if os.path.isdir(os.path.join(path,dirs)) and len(os.listdir(os.path.join(path,dirs)))<4000: - flag = dirs - break - flag = flag or create_directory(path) - filename = random_name() - fname = os.path.join(path, flag, filename) - fp = file(fname,'wb') - v = base64.decodestring(value) - fp.write(v) - filesize = os.stat(fname).st_size - cr.execute('update ir_attachment set store_fname=%s,store_method=%s,file_size=%d where id=%d', (os.path.join(flag,filename),'fs',len(v),id)) - else: - cr.execute('update ir_attachment set datas=%s,store_method=%s where id=%d', (psycopg.Binary(value),'db',id)) - return True - - _columns = { - 'user_id': fields.many2one('res.users', 'Owner', select=1), - 'group_ids': fields.many2many('res.groups', 'document_directory_group_rel', 'item_id', 'group_id', 'Groups'), - 'parent_id': fields.many2one('document.directory', 'Directory', select=1), - 'file_size': fields.integer('File Size', required=True), - 'file_type': fields.char('Content Type', size=32), - 'index_content': fields.text('Indexed Content'), - 'write_date': fields.datetime('Date Modified', readonly=True), - 'write_uid': fields.many2one('res.users', 'Last Modification User', readonly=True), - 'create_date': fields.datetime('Date Created', readonly=True), - 'create_uid': fields.many2one('res.users', 'Creator', readonly=True), - 'store_method': fields.selection([('db','Database'),('fs','Filesystem'),('link','Link')], "Storing Method"), - 'datas': fields.function(_data_get,method=True,store=True,fnct_inv=_data_set,string='File Content',type="binary"), - 'store_fname': fields.char('Stored Filename', size=200), - 'res_model': fields.char('Attached Model', size=64), #res_model - 'res_id': fields.integer('Attached ID'), #res_id - 'partner_id':fields.many2one('res.partner', 'Partner', select=1), - 'title': fields.char('Resource Title',size=64), - } - - _defaults = { - 'user_id': lambda self,cr,uid,ctx:uid, - 'file_size': lambda self,cr,uid,ctx:0, - 'store_method': lambda *args: 'db' - } - _sql_constraints = [ - ('filename_uniq', 'unique (name,parent_id,res_id,res_model)', 'The file name must be unique !') - ] - - def _check_duplication(self, cr, uid,vals): - if 'name' in vals: - res=self.search(cr,uid,[('name','=',vals['name']),('parent_id','=','parent_id' in vals and vals['parent_id'] or False),('res_id','=','res_id' in vals and vals['res_id'] or False),('res_model','=','res_model' in vals and vals['res_model']) or False]) - if len(res): - return False - return True - - def write(self, cr, uid, ids, vals, context=None): - if not self._check_duplication(cr,uid,vals): - raise except_orm('ValidateError', 'File name must be unique!') - result = super(document_file,self).write(cr,uid,ids,vals,context=context) - try: - for f in self.browse(cr, uid, ids, context=context): - if 'datas' not in vals: - vals['datas']=f.datas - res = content_index(base64.decodestring(vals['datas']), f.datas_fname, f.file_type or None) - super(document_file,self).write(cr, uid, ids, { - 'index_content': res - }) - except: - pass - return result - - def create(self, cr, uid, vals, context={}): - vals['title']=vals['name'] - if vals.get('res_id', False) and vals.get('res_model',False): - obj_model=self.pool.get(vals['res_model']) - result = obj_model.read(cr, uid, [vals['res_id']], context=context) - if len(result): - obj=result[0] - vals['title'] = (obj['name'] or '')[:60] - if obj_model._name=='res.partner': - vals['partner_id']=obj['id'] - elif 'address_id' in obj: - address=self.pool.get('res.partner.address').read(cr,uid,[obj['address_id']],context=context) - if len(address): - vals['partner_id']=address[0]['partner_id'] or False - elif 'partner_id' in obj: - if isinstance(obj['partner_id'],tuple) or isinstance(obj['partner_id'],list): - vals['partner_id']=obj['partner_id'][0] - else: - vals['partner_id']=obj['partner_id'] - - datas=None - if 'datas' not in vals: - import urllib - datas=base64.encodestring(urllib.urlopen(vals['link']).read()) - else: - datas=vals['datas'] - vals['file_size']= len(datas) - if not self._check_duplication(cr,uid,vals): - raise except_orm('ValidateError', 'File name must be unique!') - result = super(document_file,self).create(cr, uid, vals, context) - cr.commit() - try: - res = content_index(base64.decodestring(datas), vals['datas_fname'], vals.get('content_type', None)) - super(document_file,self).write(cr, uid, [result], { - 'index_content': res, - }) - cr.commit() - except: - pass - return result - - def unlink(self,cr, uid, ids, context={}): - for f in self.browse(cr, uid, ids, context): - if f.store_method=='fs': - try: - path = os.path.join(os.getcwd(),'filestore',f.store_fname) - os.unlink(path) - except: - pass - return super(document_file, self).unlink(cr, uid, ids, context) + _inherit = 'ir.attachment' + def _data_get(self, cr, uid, ids, name, arg, context): + result = {} + cr.execute('select id,store_method,datas,store_fname,link from ir_attachment where id in ('+','.join(map(str,ids))+')') + for id,m,d,r,l in cr.fetchall(): + if m=='db': + result[id] = d + elif m=='fs': + try: + path = os.path.join(os.getcwd(),'filestore') + value = file(os.path.join(path,r), 'rb').read() + result[id] = base64.encodestring(value) + except: + result[id]='' + else: + result[id] = '' + return result + + # + # This code can be improved + # + def _data_set(self, cr, obj, id, name, value, uid=None, context={}): + if not value: + return True + if (not context) or context.get('store_method','fs')=='fs': + path = os.path.join(os.getcwd(), "filestore") + if not os.path.isdir(path): + os.mkdir(path) + flag = None + # This can be improved + for dirs in os.listdir(path): + if os.path.isdir(os.path.join(path,dirs)) and len(os.listdir(os.path.join(path,dirs)))<4000: + flag = dirs + break + flag = flag or create_directory(path) + filename = random_name() + fname = os.path.join(path, flag, filename) + fp = file(fname,'wb') + v = base64.decodestring(value) + fp.write(v) + filesize = os.stat(fname).st_size + cr.execute('update ir_attachment set store_fname=%s,store_method=%s,file_size=%d where id=%d', (os.path.join(flag,filename),'fs',len(v),id)) + else: + cr.execute('update ir_attachment set datas=%s,store_method=%s where id=%d', (psycopg.Binary(value),'db',id)) + return True + + _columns = { + 'user_id': fields.many2one('res.users', 'Owner', select=1), + 'group_ids': fields.many2many('res.groups', 'document_directory_group_rel', 'item_id', 'group_id', 'Groups'), + 'parent_id': fields.many2one('document.directory', 'Directory', select=1), + 'file_size': fields.integer('File Size', required=True), + 'file_type': fields.char('Content Type', size=32), + 'index_content': fields.text('Indexed Content'), + 'write_date': fields.datetime('Date Modified', readonly=True), + 'write_uid': fields.many2one('res.users', 'Last Modification User', readonly=True), + 'create_date': fields.datetime('Date Created', readonly=True), + 'create_uid': fields.many2one('res.users', 'Creator', readonly=True), + 'store_method': fields.selection([('db','Database'),('fs','Filesystem'),('link','Link')], "Storing Method"), + 'datas': fields.function(_data_get,method=True,store=True,fnct_inv=_data_set,string='File Content',type="binary"), + 'store_fname': fields.char('Stored Filename', size=200), + 'res_model': fields.char('Attached Model', size=64), #res_model + 'res_id': fields.integer('Attached ID'), #res_id + 'partner_id':fields.many2one('res.partner', 'Partner', select=1), + 'title': fields.char('Resource Title',size=64), + } + + _defaults = { + 'user_id': lambda self,cr,uid,ctx:uid, + 'file_size': lambda self,cr,uid,ctx:0, + 'store_method': lambda *args: 'db' + } + _sql_constraints = [ + ('filename_uniq', 'unique (name,parent_id,res_id,res_model)', 'The file name must be unique !') + ] + + def _check_duplication(self, cr, uid,vals): + if 'name' in vals: + res=self.search(cr,uid,[('name','=',vals['name']),('parent_id','=','parent_id' in vals and vals['parent_id'] or False),('res_id','=','res_id' in vals and vals['res_id'] or False),('res_model','=','res_model' in vals and vals['res_model']) or False]) + if len(res): + return False + return True + + def write(self, cr, uid, ids, vals, context=None): + if not self._check_duplication(cr,uid,vals): + raise except_orm('ValidateError', 'File name must be unique!') + result = super(document_file,self).write(cr,uid,ids,vals,context=context) + try: + for f in self.browse(cr, uid, ids, context=context): + if 'datas' not in vals: + vals['datas']=f.datas + res = content_index(base64.decodestring(vals['datas']), f.datas_fname, f.file_type or None) + super(document_file,self).write(cr, uid, ids, { + 'index_content': res + }) + except: + pass + return result + + def create(self, cr, uid, vals, context={}): + vals['title']=vals['name'] + if vals.get('res_id', False) and vals.get('res_model',False): + obj_model=self.pool.get(vals['res_model']) + result = obj_model.read(cr, uid, [vals['res_id']], context=context) + if len(result): + obj=result[0] + vals['title'] = (obj['name'] or '')[:60] + if obj_model._name=='res.partner': + vals['partner_id']=obj['id'] + elif 'address_id' in obj: + address=self.pool.get('res.partner.address').read(cr,uid,[obj['address_id']],context=context) + if len(address): + vals['partner_id']=address[0]['partner_id'] or False + elif 'partner_id' in obj: + if isinstance(obj['partner_id'],tuple) or isinstance(obj['partner_id'],list): + vals['partner_id']=obj['partner_id'][0] + else: + vals['partner_id']=obj['partner_id'] + + datas=None + if 'datas' not in vals: + import urllib + datas=base64.encodestring(urllib.urlopen(vals['link']).read()) + else: + datas=vals['datas'] + vals['file_size']= len(datas) + if not self._check_duplication(cr,uid,vals): + raise except_orm('ValidateError', 'File name must be unique!') + result = super(document_file,self).create(cr, uid, vals, context) + cr.commit() + try: + res = content_index(base64.decodestring(datas), vals['datas_fname'], vals.get('content_type', None)) + super(document_file,self).write(cr, uid, [result], { + 'index_content': res, + }) + cr.commit() + except: + pass + return result + + def unlink(self,cr, uid, ids, context={}): + for f in self.browse(cr, uid, ids, context): + if f.store_method=='fs': + try: + path = os.path.join(os.getcwd(),'filestore',f.store_fname) + os.unlink(path) + except: + pass + return super(document_file, self).unlink(cr, uid, ids, context) document_file() class document_configuration_wizard(osv.osv_memory): _name='document.configuration.wizard' _rec_name = 'Auto Directory configuration' _columns = { - } + } def action_cancel(self,cr,uid,ids,conect=None): return { 'view_type': 'form', @@ -614,79 +639,75 @@ class document_configuration_wizard(osv.osv_memory): } def action_config(self, cr, uid, ids, context=None): - obj=self.pool.get('document.directory') - search_ids=obj.search(cr,uid,[]) - browse_lst=obj.browse(cr,uid,search_ids) - model_obj=self.pool.get('ir.model') - - for doc_obj in browse_lst: - if doc_obj.name in ('Partner','Contacts','Personnal Folders','Partner Category','Sales Order','All Sales Order','Sales by Salesman','Projects'): - res={} - id=[] - if doc_obj.name=='Partner': - id=model_obj.search(cr,uid,[('model','=','res.partner')]) - - if doc_obj.name=='Contacts': - id=model_obj.search(cr,uid,[('model','=','res.partner.address')]) - - if doc_obj.name=='Partner Category': - id=model_obj.search(cr,uid,[('model','=','res.partner.category')]) - - if doc_obj.name=='All Sales Order': - val={} - id=model_obj.search(cr,uid,[('model','=','sale.order')]) - if not len(doc_obj.content_ids): - val['name']='Sale Report' - val['suffix']='_report' - val['report_id']=self.pool.get('ir.actions.report.xml').search(cr,uid,[('report_name','=','sale.order')])[0] - val['extension']='.pdf' - val['directory_id']=doc_obj.id - self.pool.get('document.directory.content').create(cr,uid,val) - - if doc_obj.name=='Sales by Salesman': - id=model_obj.search(cr,uid,[('model','=','res.users')]) - - if doc_obj.name=='Personnal Folders': - id=model_obj.search(cr,uid,[('model','=','res.users')]) - - if doc_obj.name=='Projects': - id=model_obj.search(cr,uid,[('model','=','account.analytic.account')]) - res['ressource_tree']=True - - if id: - res['ressource_type_id']=id[0] - res['type']='ressource' - obj.write(cr,uid,doc_obj.id,res) - self.create_folder(cr, uid, ids, context=None) - return { + obj=self.pool.get('document.directory') + objid=self.pool.get('ir.model.data') + + if self.pool.get('sale.order'): + id = objid._get_id(cr, uid, 'document', 'dir_sale_order_all') + id = objid.browse(cr, uid, id, context=context).res_id + mid = self.pool.get('ir.model').search(cr, uid, [('model','=','sale.order')]) + obj.write(cr, uid, [id], { + 'type':'ressource', + 'ressource_type_id': mid[0], + 'domain': '[]', + }) + aid = objid._get_id(cr, uid, 'sale', 'report_sale_order') + aid = objid.browse(cr, uid, aid, context=context).res_id + + self.pool.get('document.directory.content').create(cr, uid, { + 'name': "Print Order", + 'suffix': "_print", + 'report_id': aid, + 'extension': '.pdf', + 'include_name': 1, + 'directory_id': id, + }) + id = objid._get_id(cr, uid, 'document', 'dir_sale_order_quote') + id = objid.browse(cr, uid, id, context=context).res_id + obj.write(cr, uid, [id], { + 'type':'ressource', + 'ressource_type_id': mid[0], + 'domain': "[('state','=','draft')]", + }) + + if self.pool.get('product.product'): + id = objid._get_id(cr, uid, 'document', 'dir_product') + id = objid.browse(cr, uid, id, context=context).res_id + mid = self.pool.get('ir.model').search(cr, uid, [('model','=','product.product')]) + obj.write(cr, uid, [id], { + 'type':'ressource', + 'ressource_type_id': mid[0], + }) + + if self.pool.get('stock.location'): + aid = objid._get_id(cr, uid, 'stock', 'report_product_history') + aid = objid.browse(cr, uid, aid, context=context).res_id + + self.pool.get('document.directory.content').create(cr, uid, { + 'name': "Product Stock", + 'suffix': "_stock_forecast", + 'report_id': aid, + 'extension': '.pdf', + 'include_name': 1, + 'directory_id': id, + }) + + if self.pool.get('account.analytic.account'): + id = objid._get_id(cr, uid, 'document', 'dir_project') + id = objid.browse(cr, uid, id, context=context).res_id + mid = self.pool.get('ir.model').search(cr, uid, [('model','=','account.analytic.account')]) + obj.write(cr, uid, [id], { + 'type':'ressource', + 'ressource_type_id': mid[0], + 'domain': '[]', + 'ressource_tree': 1 + }) + + return { 'view_type': 'form', "view_mode": 'form', 'res_model': 'ir.actions.configuration.wizard', 'type': 'ir.actions.act_window', 'target':'new', - } - - def create_folder(self, cr, uid, ids, context=None): - doc_obj=self.pool.get('document.directory') - model_obj=self.pool.get('ir.model') - for name in ('Sales','Quotation','Meetings','Analysis Reports'): - res={} - if name=='Sales': - child_model=model_obj.search(cr,uid,[('model','=','sale.order')]) - link_model=model_obj.search(cr,uid,[('model','=','res.users')]) - if child_model: - res['type']='ressource' - res['ressource_type_id']=child_model[0] - res['ressource_parent_type_id']=link_model[0] - res['domain']="[('user_id','=',active_id)]" - else: - link_model=model_obj.search(cr,uid,[('model','=','account.analytic.account')]) - if link_model: - res['ressource_parent_type_id']=link_model[0] - res['ressource_id']=0 - if res: - res['name']=name - doc_obj.create(cr,uid,res) - return True - + } document_configuration_wizard() diff --git a/addons/document/document_data.xml b/addons/document/document_data.xml index 9953d07dd92..33c6f80763a 100644 --- a/addons/document/document_data.xml +++ b/addons/document/document_data.xml @@ -1,6 +1,11 @@ <?xml version="1.0"?> <openerp> -<data noupdate="1"> +<data> + + <record model="document.directory.content.type" id="pdf"> + <field name="code">.pdf</field> + <field name="name">PDF Report</field> + </record> </data> </openerp> diff --git a/addons/document/document_demo.xml b/addons/document/document_demo.xml index 94c52923556..11d1f0a991a 100644 --- a/addons/document/document_demo.xml +++ b/addons/document/document_demo.xml @@ -2,92 +2,71 @@ <openerp> <data noupdate="1"> - <record model="document.directory" id="dir_root"> - <field name="name">Main Repository</field> - <field name="user_id" ref="base.user_admin"/> - <field name="ressource_id" eval="0"/> + <record model="document.directory" id="dir_root"> + <field name="name">Documents</field> + <field name="user_id" ref="base.user_admin"/> + </record> - </record> + <record model="document.directory" id="dir_my_folder"> + <field name="name">My Folder</field> + <field name="parent_id" ref="dir_root"/> + <field name="user_id" ref="base.user_admin"/> + </record> - <record model="document.directory" id="dir_my_folder"> - <field name="name">My Folder</field> - <field name="parent_id" ref="dir_root"/> - <field name="user_id" ref="base.user_admin"/> - <field name="ressource_id" eval="0"/> - </record> + <record model="document.directory" id="dir_partner_category"> + <field name="name">Partners by Category</field> + <field name="parent_id" ref="dir_root"/> + <field name="type">ressource</field> + <field name="ressource_tree">1</field> + <field name="ressource_type_id" search="[('model','=','res.partner.category')]" /> + <field name="user_id" ref="base.user_admin"/> + </record> - <record model="document.directory" id="dir_partner_category"> - <field name="name">Partner Category</field> - <field name="parent_id" ref="dir_root"/> - <field name="type">ressource</field> - <field name="ressource_tree">1</field> - <field name="ressource_type_id" search="[('model','=','res.partner.category')]" /> - <field name="user_id" ref="base.user_admin"/> - <field name="ressource_id" eval="0"/> - </record> + <record model="document.directory" id="dir_partner"> + <field name="name">Partners</field> + <field name="type">ressource</field> + <field name="domain">[('category_id','in',[active_id])]</field> + <field name="ressource_type_id" search="[('model','=','res.partner')]" /> + <field name="ressource_parent_type_id" search="[('model','=','res.partner.category')]" /> + <field name="user_id" ref="base.user_admin"/> + </record> + <record model="document.directory" id="dir_personnal_folder"> + <field name="name">Personnal Folders</field> + <field name="parent_id" ref="dir_root"/> + <field name="type">ressource</field> + <field name="ressource_type_id" ref="base.model_res_users" /> + </record> + <record model="document.directory" id="dir_product"> + <field name="name">Products</field> + <field name="user_id" ref="base.user_admin"/> + <field name="parent_id" ref="dir_root"/> + </record> - <record model="document.directory" id="dir_partner"> - <field name="name">Partner</field> - <field name="type">ressource</field> - <field name="domain">[('category_id','in',[active_id])]</field> - <field name="ressource_type_id" search="[('model','=','res.partner')]" /> - <field name="ressource_parent_type_id" search="[('model','=','res.partner.category')]" /> - <field name="user_id" ref="base.user_admin"/> - <field name="ressource_id" eval="0"/> - </record> + <record model="document.directory" id="dir_sale_order"> + <field name="name">Sales Order</field> + <field name="user_id" ref="base.user_admin"/> + <field name="parent_id" ref="dir_root"/> + </record> - <record model="document.directory" id="dir_partner_contacts"> - <field name="name">Contacts</field> - <field name="type">ressource</field> - <field name="domain">[('partner_id','=',active_id)]</field> - <field name="ressource_type_id" search="[('model','=','res.partner.address')]" /> - <field name="ressource_parent_type_id" search="[('model','=','res.partner')]" /> - <field name="user_id" ref="base.user_admin"/> - <field name="ressource_id" eval="0"/> - </record> + <record model="document.directory" id="dir_sale_order_all"> + <field name="name">All Sales Order</field> + <field name="user_id" ref="base.user_admin"/> + <field name="parent_id" ref="dir_sale_order"/> + </record> - <record model="document.directory" id="dir_my_partner"> - <field name="name">My Folder</field> - <field name="user_id" ref="base.user_admin"/> - <field name="ressource_parent_type_id" search="[('model','=','res.partner')]" /> - <field name="ressource_id" eval="0"/> - </record> + <record model="document.directory" id="dir_sale_order_quote"> + <field name="name">Quotations</field> + <field name="user_id" ref="base.user_admin"/> + <field name="parent_id" ref="dir_sale_order"/> + </record> - <record model="document.directory" id="dir_personnal_folder"> - <field name="name">Personnal Folders</field> - <field name="parent_id" ref="dir_root"/> - <field name="user_id" ref="base.user_admin"/> - <field name="ressource_id" eval="0"/> - </record> - - <record model="document.directory" id="dir_sale_order"> - <field name="name">Sales Order</field> - <field name="user_id" ref="base.user_admin"/> - <field name="parent_id" ref="dir_root"/> - <field name="ressource_id" eval="0"/> - </record> - - <record model="document.directory" id="dir_sale_order_all"> - <field name="name">All Sales Order</field> - <field name="user_id" ref="base.user_admin"/> - <field name="parent_id" ref="dir_sale_order"/> - <field name="ressource_id" eval="0"/> - </record> - <record model="document.directory" id="dir_sale_order_salesman"> - <field name="name">Sales by Salesman</field> - <field name="user_id" ref="base.user_admin"/> - <field name="parent_id" ref="dir_sale_order"/> - <field name="ressource_id" eval="0"/> - </record> - - <record model="document.directory" id="dir_project"> - <field name="name">Projects</field> - <field name="user_id" ref="base.user_admin"/> - <field name="parent_id" ref="dir_root"/> - <field name="ressource_id" eval="0"/> - </record> + <record model="document.directory" id="dir_project"> + <field name="name">Projects</field> + <field name="user_id" ref="base.user_admin"/> + <field name="parent_id" ref="dir_root"/> + </record> </data> </openerp> diff --git a/addons/document/document_view.xml b/addons/document/document_view.xml index 6b15382af63..434e00aa7d8 100644 --- a/addons/document/document_view.xml +++ b/addons/document/document_view.xml @@ -1,6 +1,7 @@ <openerp> <data> <menuitem name="Document Management" icon="terp-stock" id="menu_document"/> + <menuitem name="Configuration" id="menu_document_configuration" parent="menu_document"/> <record model="ir.ui.view" id="view_document_directory_form"> <field name="name">document.directory</field> @@ -8,11 +9,11 @@ <field name="type">form</field> <field name="arch" type="xml"> <form string="Directories"> - <notebook> + <field name="name" select="1" colspan="4"/> + <field name="user_id"/> + <field name="parent_id"/> + <notebook colspan="4"> <page string="Definition"> - <field name="name" select="1" colspan="4"/> - <field name="user_id"/> - <field name="parent_id"/> <separator string="Directory Type" colspan="4"/> <field name="type"/> <field name="ressource_type_id" on_change="onchange_content_id(ressource_type_id)"/> @@ -29,14 +30,15 @@ <field name="sequence"/> <field name="suffix"/> <field name="extension"/> + <field name="include_name"/> + <separator string="PDF Report" colspan="4"/> <field name="report_id" domain="[('model_id','=',parent.ressource_type_id)]"/> </form> <tree string="Contents"> - <field name="sequence"/> + <field name="sequence" string="Seq."/> <field name="name"/> <field name="suffix"/> <field name="extension"/> - <field name="report_id"/> </tree> </field> </page><page string="Security"> @@ -67,26 +69,37 @@ <field name="res_model">document.directory</field> <field name="view_type">form</field> <field name="view_mode">tree,form</field> - </record> - <menuitem name="Configuration" id="menu_document_configuration" parent="menu_document"/> + </record> <menuitem name="Directories" action="action_document_directory_form" - id="menu_document_directories" - parent="menu_document_configuration"/> + id="menu_document_directories" + parent="menu_document_configuration"/> + <record model="ir.actions.act_window" id="action_document_directory_tree"> <field name="type">ir.actions.act_window</field> + <field name="name">Directorie's Structure</field> <field name="res_model">document.directory</field> <field name="view_type">tree</field> <field name="domain">[('ressource_parent_type_id','=',False),('parent_id','=',False)]</field> </record> <menuitem - name="Directorie's Structure" action="action_document_directory_tree" - id="menu_document_directories_tree" - parent="menu_document"/> + id="menu_document_directories_tree" + parent="menu_document_configuration"/> + <record model="ir.actions.url" id="action_document_browse"> + <field name="name">Browse Files</field> + <field name="url">ftp://localhost:8021/</field> + </record> + <menuitem + name="Browse Files Using FTP" + action="action_document_browse" + id="menu_document_browse" + type="url" + icon="STOCK_EXECUTE" + parent="menu_document"/> <record model="ir.ui.view" id="view_document_file_form"> <field name="name">ir.attachment</field> @@ -146,8 +159,8 @@ <menuitem name="Search a File" action="action_document_file_form" - id="menu_document_files" - parent="menu_document"/> + id="menu_document_files" + parent="menu_document"/> <record model="ir.actions.act_window" id="action_document_file_directory_form"> <field name="type">ir.actions.act_window</field> @@ -203,33 +216,20 @@ </record> - <record id="view_auto_config_form" model="ir.ui.view"> + <record id="view_auto_config_form" model="ir.ui.view"> <field name="name">Auto Configure Directory</field> <field name="model">document.configuration.wizard</field> <field name="type">form</field> <field name="arch" type="xml"> <form string="Auto Configure"> - <separator string="Connect to Document Management System." colspan="4"/> - <label align="0.0" string="You can use ftp://admin:admin@localhost:8021 " colspan="4"/> - <separator string="Configure Ressources Directory :" colspan="4"/> - <newline/> - <label string="'All Sales Order' -> sale.order" align="0.0" colspan="4"/> - <label string="'Sales by Salesman' -> res.users" align="0.0" colspan="4"/> - <label string="'Personnal Folders' -> res.users" align="0.0" colspan="4"/> - <label string="'Projects' -> account.analytic.account with ressource tree" align="0.0" colspan="4"/> - <separator string="New Directories." colspan="4"/> - <label string="'Sales' -> sale.order which is link directory of 'res.users' model" align="0.0" colspan="4"/> - <label align="0.0" string="'Quotation','Meetings','Analysis Reports' -> static directories which are link directories of 'account.analytic.account' model" colspan="4"/> - <newline/> - <newline/> - <separator string="" colspan="4"/> - <label string="" colspan="2"/> + <separator string="Document Management System." colspan="4"/> + <label string="This wizard will automatically configure the document management system according to modules installed on your system." align="0.0" colspan="4"/> + <separator string="" colspan="4"/> + <label string="" colspan="2"/> <group col="4" colspan="2"> <button special="cancel" string="Cancel" name="action_cancel" type="object" icon='gtk-cancel'/> <button name="action_config" string="Configure" icon='gtk-ok' type="object"/> </group> - <newline/> - </form> </field> </record> @@ -243,10 +243,9 @@ <field name="target">new</field> </record> - <record model="ir.actions.todo" - id="config_auto_directory"> + <record model="ir.actions.todo" id="config_auto_directory"> <field name="name">Auto Configure Directory</field> - <field name="note">This wizard will be configure directory for ressource model.</field> + <field name="note">This wizard will be configure directory for ressource model.</field> <field name="action_id" ref="action_config_auto_directory"/> <field name="state">open</field> </record> diff --git a/addons/document/ftpserver/abstracted_fs.py b/addons/document/ftpserver/abstracted_fs.py index 2ff2bdbb41c..9bdd38b72bd 100644 --- a/addons/document/ftpserver/abstracted_fs.py +++ b/addons/document/ftpserver/abstracted_fs.py @@ -9,743 +9,761 @@ import fnmatch import pooler import netsvc -import posix +import os from service import security +class file_wrapper(StringIO.StringIO): + def __init__(self, sstr='', ressource_id=False, dbname=None, uid=1, name=''): + StringIO.StringIO.__init__(self, sstr) + self.ressource_id = ressource_id + self.name = name + self.dbname = dbname + self.uid = uid + def close(self, *args, **kwargs): + db,pool = pooler.get_db_and_pool(self.dbname) + cr = db.cursor() + uid =self.uid + val = self.getvalue() + val2 = { + 'datas': base64.encodestring(val), + 'file_size': len(val), + } + pool.get('ir.attachment').write(cr, uid, [self.ressource_id], val2) + cr.commit() + StringIO.StringIO.close(self, *args, **kwargs) + +class content_wrapper(StringIO.StringIO): + def __init__(self, dbname, uid, pool, node, name=''): + StringIO.StringIO.__init__(self, '') + self.dbname = dbname + self.uid = uid + self.node = node + self.pool = pool + self.name = name + def close(self, *args, **kwargs): + db,pool = pooler.get_db_and_pool(self.dbname) + cr = db.cursor() + getattr(self.pool.get('document.directory.content'), 'process_write_'+self.node.content.extension[1:])(cr, self.uid, self.node, self.getvalue()) + StringIO.StringIO.close(self, *args, **kwargs) + cr.commit() + cr.close() + + class abstracted_fs: - """A class used to interact with the file system, providing a high - level, cross-platform interface compatible with both Windows and - UNIX style filesystems. - - It provides some utility methods and some wraps around operations - involved in file creation and file system operations like moving - files or removing directories. - - Instance attributes: - - (str) root: the user home directory. - - (str) cwd: the current working directory. - - (str) rnfr: source file to be renamed. - """ - - # Ok - def db_list(self): - s = netsvc.LocalService('db') - result = s.list() - self.db_name_list=[] - for db_name in result: - db = pooler.get_db_only(db_name) - cr = db.cursor() - cr.execute("select id from ir_module_module where name like 'document%' and state='installed' ") - res=cr.fetchone() - if res and len(res): - self.db_name_list.append(db_name) - cr.close() - return self.db_name_list - - # Ok - def __init__(self): - self.root = None - self.cwd = '/' - self.rnfr = None - - # --- Pathname / conversion utilities - - # Ok - def ftpnorm(self, ftppath): - """Normalize a "virtual" ftp pathname (tipically the raw string - coming from client) depending on the current working directory. - - Example (having "/foo" as current working directory): - 'x' -> '/foo/x' - - Note: directory separators are system independent ("/"). - Pathname returned is always absolutized. - """ - if os.path.isabs(ftppath): - p = os.path.normpath(ftppath) - else: - p = os.path.normpath(os.path.join(self.cwd, ftppath)) - # normalize string in a standard web-path notation having '/' - # as separator. - p = p.replace("\\", "/") - # os.path.normpath supports UNC paths (e.g. "//a/b/c") but we - # don't need them. In case we get an UNC path we collapse - # redundant separators appearing at the beginning of the string - while p[:2] == '//': - p = p[1:] - # Anti path traversal: don't trust user input, in the event - # that self.cwd is not absolute, return "/" as a safety measure. - # This is for extra protection, maybe not really necessary. - if not os.path.isabs(p): - p = "/" - return p - - # Ok - def ftp2fs(self, path_orig, data): - path = self.ftpnorm(path_orig) - if path and path=='/': - return None - path2 = filter(None,path.split('/'))[1:] - (cr, uid, pool) = data - res = pool.get('document.directory').get_object(cr, uid, path2[:]) - if not res: - raise OSError(2, 'Not such file or directory.') - return res - - # Ok - def fs2ftp(self, node): - res = node and ('/' + node.cr.dbname + '/' + node.path) or '/' - return res - - # Ok - def validpath(self, path): - """Check whether the path belongs to user's home directory. - Expected argument is a "real" filesystem pathname. - - If path is a symbolic link it is resolved to check its real - destination. - - Pathnames escaping from user's root directory are considered - not valid. - """ - return path and True or False - - # --- Wrapper methods around open() and tempfile.mkstemp - - # Ok - def create(self, node, objname, mode): - try: - class file_wrapper(StringIO.StringIO): - def __init__(self, sstr='', ressource_id=False, dbname=None, uid=1, name=''): - StringIO.StringIO.__init__(self, sstr) - self.ressource_id = ressource_id - self.name = name - self.dbname = dbname - self.uid = uid - def close(self, *args, **kwargs): - db,pool = pooler.get_db_and_pool(self.dbname) - cr = db.cursor() - uid =self.uid - val = self.getvalue() - val2 = { - 'datas': base64.encodestring(val), - 'file_size': len(val), - } - pool.get('ir.attachment').write(cr, uid, [self.ressource_id], val2) - cr.commit() - StringIO.StringIO.close(self, *args, **kwargs) - - cr = node.cr - uid = node.uid - pool = pooler.get_pool(cr.dbname) - - fobj = pool.get('ir.attachment') - ext = objname.find('.') >0 and objname.split('.')[1] or False - - # TODO: test if already exist and modify in this case if node.type=file - ### checked already exits - object2=node and node.object2 or False - object=node and node.object or False - cid=False - - where=[('name','=',objname)] - if object and (object.type in ('directory','ressource')) or object2: - where.append(('parent_id','=',object.id)) - else: - where.append(('parent_id','=',False)) - - if object2: - where +=[('res_id','=',object2.id),('res_model','=',object2._name)] - cids = fobj.search(cr, uid,where) - if len(cids): - cid=cids[0] - - if not cid: - val = { - 'name': objname, - 'datas_fname': objname, - 'datas': '', - 'file_size': 0L, - 'file_type': ext, - } - if object and (object.type in ('directory','ressource')) or not object2: - val['parent_id']= object and object.id or False - partner = False - if object2: - if 'partner_id' in object2 and object2.partner_id.id: - partner = object2.partner_id.id - if object2._name == 'res.partner': - partner = object2.id - val.update( { - 'res_model': object2._name, - 'partner_id': partner, - 'res_id': object2.id - }) - cid = fobj.create(cr, uid, val, context={}) - cr.commit() - - s = file_wrapper('', cid, cr.dbname, uid, ) - return s - except Exception,e: - print e - raise OSError(1, 'Operation not permited.') - - # Ok - def open(self, node, mode): - if not node: - raise OSError(1, 'Operation not permited.') - # Reading operation - if node.type=='file': - if not self.isfile(node): - raise OSError(1, 'Operation not permited.') - s = StringIO.StringIO(base64.decodestring(node.object.datas or '')) - s.name = node - return s - elif node.type=='content': - cr = node.cr - uid = node.uid - pool = pooler.get_pool(cr.dbname) - report = pool.get('ir.actions.report.xml').browse(cr, uid, node.content['report_id']['id']) - srv = netsvc.LocalService('report.'+report.report_name) - pdf,pdftype = srv.create(cr, uid, [node.object.id], {}, {}) - s = StringIO.StringIO(pdf) - s.name = node - return s - else: - raise OSError(1, 'Operation not permited.') - - # ok, but need test more - - def mkstemp(self, suffix='', prefix='', dir=None, mode='wb'): - """A wrap around tempfile.mkstemp creating a file with a unique - name. Unlike mkstemp it returns an object with a file-like - interface. - """ - raise 'Not Yet Implemented' -# class FileWrapper: -# def __init__(self, fd, name): -# self.file = fd -# self.name = name -# def __getattr__(self, attr): -# return getattr(self.file, attr) + """A class used to interact with the file system, providing a high + level, cross-platform interface compatible with both Windows and + UNIX style filesystems. + + It provides some utility methods and some wraps around operations + involved in file creation and file system operations like moving + files or removing directories. + + Instance attributes: + - (str) root: the user home directory. + - (str) cwd: the current working directory. + - (str) rnfr: source file to be renamed. + """ + + # Ok + def db_list(self): + s = netsvc.LocalService('db') + result = s.list() + self.db_name_list=[] + for db_name in result: + db = pooler.get_db_only(db_name) + cr = db.cursor() + cr.execute("select id from ir_module_module where name like 'document%' and state='installed' ") + res=cr.fetchone() + if res and len(res): + self.db_name_list.append(db_name) + cr.close() + return self.db_name_list + + # Ok + def __init__(self): + self.root = None + self.cwd = '/' + self.rnfr = None + + # --- Pathname / conversion utilities + + # Ok + def ftpnorm(self, ftppath): + """Normalize a "virtual" ftp pathname (tipically the raw string + coming from client) depending on the current working directory. + + Example (having "/foo" as current working directory): + 'x' -> '/foo/x' + + Note: directory separators are system independent ("/"). + Pathname returned is always absolutized. + """ + if os.path.isabs(ftppath): + p = os.path.normpath(ftppath) + else: + p = os.path.normpath(os.path.join(self.cwd, ftppath)) + # normalize string in a standard web-path notation having '/' + # as separator. + p = p.replace("\\", "/") + # os.path.normpath supports UNC paths (e.g. "//a/b/c") but we + # don't need them. In case we get an UNC path we collapse + # redundant separators appearing at the beginning of the string + while p[:2] == '//': + p = p[1:] + # Anti path traversal: don't trust user input, in the event + # that self.cwd is not absolute, return "/" as a safety measure. + # This is for extra protection, maybe not really necessary. + if not os.path.isabs(p): + p = "/" + return p + + # Ok + def ftp2fs(self, path_orig, data): + path = self.ftpnorm(path_orig) + if path and path=='/': + return None + path2 = filter(None,path.split('/'))[1:] + (cr, uid, pool) = data + res = pool.get('document.directory').get_object(cr, uid, path2[:]) + if not res: + raise OSError(2, 'Not such file or directory.') + return res + + # Ok + def fs2ftp(self, node): + res = node and ('/' + node.cr.dbname + '/' + node.path) or '/' + return res + + # Ok + def validpath(self, path): + """Check whether the path belongs to user's home directory. + Expected argument is a "real" filesystem pathname. + + If path is a symbolic link it is resolved to check its real + destination. + + Pathnames escaping from user's root directory are considered + not valid. + """ + return path and True or False + + # --- Wrapper methods around open() and tempfile.mkstemp + + # Ok + def create(self, node, objname, mode): + cr = node.cr + uid = node.uid + pool = pooler.get_pool(cr.dbname) + child = node.child(objname) + if child: + if child.type in ('collection','database'): + raise OSError(1, 'Operation not permited.') + if child.type=='content': + s = content_wrapper(cr.dbname, uid, pool, child) + return s + try: + fobj = pool.get('ir.attachment') + ext = objname.find('.') >0 and objname.split('.')[1] or False + + # TODO: test if already exist and modify in this case if node.type=file + ### checked already exits + object2=node and node.object2 or False + object=node and node.object or False + cid=False + + where=[('name','=',objname)] + if object and (object.type in ('directory','ressource')) or object2: + where.append(('parent_id','=',object.id)) + else: + where.append(('parent_id','=',False)) + + if object2: + where +=[('res_id','=',object2.id),('res_model','=',object2._name)] + cids = fobj.search(cr, uid,where) + if len(cids): + cid=cids[0] + + if not cid: + val = { + 'name': objname, + 'datas_fname': objname, + 'datas': '', + 'file_size': 0L, + 'file_type': ext, + } + if object and (object.type in ('directory','ressource')) or not object2: + val['parent_id']= object and object.id or False + partner = False + if object2: + if 'partner_id' in object2 and object2.partner_id.id: + partner = object2.partner_id.id + if object2._name == 'res.partner': + partner = object2.id + val.update( { + 'res_model': object2._name, + 'partner_id': partner, + 'res_id': object2.id + }) + cid = fobj.create(cr, uid, val, context={}) + cr.commit() + + s = file_wrapper('', cid, cr.dbname, uid, ) + return s + except Exception,e: + print e + raise OSError(1, 'Operation not permited.') + + # Ok + def open(self, node, mode): + if not node: + raise OSError(1, 'Operation not permited.') + # Reading operation + if node.type=='file': + if not self.isfile(node): + raise OSError(1, 'Operation not permited.') + s = StringIO.StringIO(base64.decodestring(node.object.datas or '')) + s.name = node + return s + elif node.type=='content': + cr = node.cr + uid = node.uid + pool = pooler.get_pool(cr.dbname) + return getattr(pool.get('document.directory.content'), 'process_read_'+node.content.extension[1:])(cr, uid, node) + else: + raise OSError(1, 'Operation not permited.') + + # ok, but need test more + + def mkstemp(self, suffix='', prefix='', dir=None, mode='wb'): + """A wrap around tempfile.mkstemp creating a file with a unique + name. Unlike mkstemp it returns an object with a file-like + interface. + """ + raise 'Not Yet Implemented' +# class FileWrapper: +# def __init__(self, fd, name): +# self.file = fd +# self.name = name +# def __getattr__(self, attr): +# return getattr(self.file, attr) # -# text = not 'b' in mode -# # max number of tries to find out a unique file name -# tempfile.TMP_MAX = 50 -# fd, name = tempfile.mkstemp(suffix, prefix, dir, text=text) -# file = os.fdopen(fd, mode) -# return FileWrapper(file, name) - - text = not 'b' in mode - # for unique file , maintain version if duplicate file - if dir: - cr = dir.cr - uid = dir.uid - pool = pooler.get_pool(cr.dbname) - object=dir and dir.object or False - object2=dir and dir.object2 or False - res=pool.get('ir.attachment').search(cr,uid,[('name','like',prefix),('parent_id','=',object and object.type in ('directory','ressource') and object.id or False),('res_id','=',object2 and object2.id or False),('res_model','=',object2 and object2._name or False)]) - if len(res): - pre = prefix.split('.') - prefix=pre[0] + '.v'+str(len(res))+'.'+pre[1] - #prefix = prefix + '.' - return self.create(dir,suffix+prefix,text) - - - - # Ok - def chdir(self, path): - if not path: - self.cwd='/' - return None - if path.type in ('collection','database'): - self.cwd = self.fs2ftp(path) - else: - raise OSError(1, 'Operation not permited.') - - # Ok - def mkdir(self, node, basename): - """Create the specified directory.""" - if not node: - raise OSError(1, 'Operation not permited.') - try: - object2=node and node.object2 or False - object=node and node.object or False - cr = node.cr - uid = node.uid - pool = pooler.get_pool(cr.dbname) - if node.object and (node.object.type=='ressource') and not node.object2: - raise OSError(1, 'Operation not permited.') - val = { - 'name': basename, - 'ressource_parent_type_id': object and object.ressource_type_id.id or False, - 'ressource_id': object2 and object2.id or False - } - if (object and (object.type in ('directory'))) or not object2: - val['parent_id'] = object and object.id or False - # Check if it alreayd exists ! - pool.get('document.directory').create(cr, uid, val) - cr.commit() - except Exception,e: - print e - raise OSError(1, 'Operation not permited.') - - - # Ok - def close_cr(self, data): - if data: - data[0].close() - return True - - def get_cr(self, path): - path = self.ftpnorm(path) - if path=='/': - return None - dbname = path.split('/')[1] - try: - db,pool = pooler.get_db_and_pool(dbname) - except: - raise OSError(1, 'Operation not permited.') - cr = db.cursor() - uid = security.login(dbname, self.username, self.password) - if not uid: - raise OSError(1, 'Operation not permited.') - return cr, uid, pool - - # Ok - def listdir(self, path): - """List the content of a directory.""" - class false_node: - object = None - type = 'database' - def __init__(self, db): - self.path = '/'+db - - if path is None: - result = [] - for db in self.db_list(): - result.append(false_node(db)) - return result - return path.children() - - # Ok - def rmdir(self, node): - """Remove the specified directory.""" - cr = node.cr - uid = node.uid - pool = pooler.get_pool(cr.dbname) - object2=node and node.object2 or False - object=node and node.object or False - if object._table_name=='document.directory': - if node.children(): - raise OSError(39, 'Directory not empty.') - res = pool.get('document.directory').unlink(cr, uid, [object.id]) - else: - raise OSError(39, 'Directory not empty.') - - cr.commit() - - # Ok - def remove(self, node): - """Remove the specified file.""" - cr = node.cr - uid = node.uid - pool = pooler.get_pool(cr.dbname) - object2=node and node.object2 or False - object=node and node.object or False - if not object: - raise OSError(2, 'Not such file or directory.') - if object._table_name=='ir.attachment': - res = pool.get('ir.attachment').unlink(cr, uid, [object.id]) - else: - raise OSError(1, 'Operation not permited.') - cr.commit() - - # Ok - def rename(self, src, dst_basedir,dst_basename): - """ - Renaming operation, the effect depends on the src: - * A file: read, create and remove - * A directory: change the parent and reassign childs to ressource - """ - try: - if src.type=='collection': - if src.object._table_name <> 'document.directory': - raise OSError(1, 'Operation not permited.') - result = { - 'directory': [], - 'attachment': [] - } - # Compute all childs to set the new ressource ID - child_ids = [src] - while len(child_ids): - node = child_ids.pop(0) - child_ids += node.children() - if node.type =='collection': - result['directory'].append(node.object.id) - if (not node.object.ressource_id) and node.object2: - raise OSError(1, 'Operation not permited.') - elif node.type =='file': - result['attachment'].append(node.object.id) - - cr = src.cr - uid = src.uid - pool = pooler.get_pool(cr.dbname) - object2=src and src.object2 or False - object=src and src.object or False - if object2 and not object.ressource_id: - raise OSError(1, 'Operation not permited.') - val = { - 'name':dst_basename, - } - if (dst_basedir.object and (dst_basedir.object.type in ('directory'))) or not dst_basedir.object2: - val['parent_id'] = dst_basedir.object and dst_basedir.object.id or False - else: - val['parent_id'] = False - res = pool.get('document.directory').write(cr, uid, [object.id],val) - - if dst_basedir.object2: - ressource_type_id = pool.get('ir.model').search(cr,uid,[('model','=',dst_basedir.object2._name)])[0] - ressource_id = dst_basedir.object2.id - title = dst_basedir.object2.name - ressource_model = dst_basedir.object2._name - if dst_basedir.object2._name=='res.partner': - partner_id=dst_basedir.object2.id - else: - partner_id= dst_basedir.object2.partner_id and dst_basedir.object2.partner_id.id or False - else: - ressource_type_id = False - ressource_id=False - ressource_model = False - partner_id = False - title = False - - pool.get('document.directory').write(cr, uid, result['directory'], { - 'ressource_id': ressource_id, - 'ressource_parent_type_id': ressource_type_id - }) - val = { - 'res_id': ressource_id, - 'res_model': ressource_model, - 'title': title, - 'partner_id': partner_id - } - pool.get('ir.attachment').write(cr, uid, result['attachment'], val) - if (not val['res_id']) and result['attachment']: - dst_basedir.cr.execute('update ir_attachment set res_id=NULL where id in ('+','.join(map(str,result['attachment']))+')') - - cr.commit() - elif src.type=='file': - pool = pooler.get_pool(src.cr.dbname) - val = { - 'partner_id':False, - #'res_id': False, - 'res_model': False, - 'name': dst_basename, - 'datas_fname': dst_basename, - 'title': dst_basename, - } - - if (dst_basedir.object and (dst_basedir.object.type in ('directory','ressource'))) or not dst_basedir.object2: - val['parent_id'] = dst_basedir.object and dst_basedir.object.id or False - else: - val['parent_id'] = False - - if dst_basedir.object2: - val['res_model'] = dst_basedir.object2._name - val['res_id'] = dst_basedir.object2.id - val['title'] = dst_basedir.object2.name - if dst_basedir.object2._name=='res.partner': - val['partner_id']=dst_basedir.object2.id - else: - val['partner_id']= dst_basedir.object2.partner_id and dst_basedir.object2.partner_id.id or False - elif src.object.res_id: - # I had to do that because writing False to an integer writes 0 instead of NULL - # change if one day we decide to improve osv/fields.py - dst_basedir.cr.execute('update ir_attachment set res_id=NULL where id=%d', (src.object.id,)) - - pool.get('ir.attachment').write(src.cr, src.uid, [src.object.id], val) - src.cr.commit() - elif src.type=='content': - src_file=self.open(src,'r') - dst_file=self.create(dst_basedir,dst_basename,'w') - dst_file.write(src_file.getvalue()) - dst_file.close() - src_file.close() - src.cr.commit() - else: - raise OSError(1, 'Operation not permited.') - except Exception,err: - print err - raise OSError(1,'Operation not permited.') - - - - - # Nearly Ok - def stat(self, node): - r = list(os.stat('/')) - if self.isfile(node): - r[0] = 33188 - r[6] = self.getsize(node) - r[7] = self.getmtime(node) - r[8] = self.getmtime(node) - r[9] = self.getmtime(node) - return posix.stat_result(r) - lstat = stat - - # --- Wrapper methods around os.path.* - - # Ok - def isfile(self, node): - if node and (node.type not in ('collection','database')): - return True - return False - - # Ok - def islink(self, path): - """Return True if path is a symbolic link.""" - return False - - # Ok - def isdir(self, node): - """Return True if path is a directory.""" - if node is None: - return True - if node and (node.type in ('collection','database')): - return True - return False - - # Ok - def getsize(self, node): - """Return the size of the specified file in bytes.""" - result = 0L - if node.type=='file': - result = node.object.file_size or 0L - return result - - # Ok - def getmtime(self, node): - """Return the last modified time as a number of seconds since - the epoch.""" - if node.object and node.type<>'content': - dt = (node.object.write_date or node.object.create_date)[:19] - result = time.mktime(time.strptime(dt, '%Y-%m-%d %H:%M:%S')) - else: - result = time.mktime(time.localtime()) - return result - - # Ok - def realpath(self, path): - """Return the canonical version of path eliminating any - symbolic links encountered in the path (if they are - supported by the operating system). - """ - return path - - # Ok - def lexists(self, path): - """Return True if path refers to an existing path, including - a broken or circular symbolic link. - """ - return path and True or False - exists = lexists - - # Ok, can be improved - def glob1(self, dirname, pattern): - """Return a list of files matching a dirname pattern - non-recursively. - - Unlike glob.glob1 raises exception if os.listdir() fails. - """ - names = self.listdir(dirname) - if pattern[0] != '.': - names = filter(lambda x: x.path[0] != '.', names) - return fnmatch.filter(names, pattern) - - # --- Listing utilities - - # note: the following operations are no more blocking - - # Ok - def get_list_dir(self, path): - """"Return an iterator object that yields a directory listing - in a form suitable for LIST command. - """ - if self.isdir(path): - listing = self.listdir(path) - #listing.sort() - return self.format_list(path and path.path or '/', listing) - # if path is a file or a symlink we return information about it - elif self.isfile(path): - basedir, filename = os.path.split(path.path) - self.lstat(path) # raise exc in case of problems - return self.format_list(basedir, [filename]) - - - # Ok - def get_stat_dir(self, rawline, datacr): - """Return an iterator object that yields a list of files - matching a dirname pattern non-recursively in a form - suitable for STAT command. - - - (str) rawline: the raw string passed by client as command - argument. - """ - ftppath = self.ftpnorm(rawline) - if not glob.has_magic(ftppath): - return self.get_list_dir(self.ftp2fs(rawline, datacr)) - else: - basedir, basename = os.path.split(ftppath) - if glob.has_magic(basedir): - return iter(['Directory recursion not supported.\r\n']) - else: - basedir = self.ftp2fs(basedir, datacr) - listing = self.glob1(basedir, basename) - if listing: - listing.sort() - return self.format_list(basedir, listing) - - # Ok - def format_list(self, basedir, listing, ignore_err=True): - """Return an iterator object that yields the entries of given - directory emulating the "/bin/ls -lA" UNIX command output. - - - (str) basedir: the absolute dirname. - - (list) listing: the names of the entries in basedir - - (bool) ignore_err: when False raise exception if os.lstat() - call fails. - - On platforms which do not support the pwd and grp modules (such - as Windows), ownership is printed as "owner" and "group" as a - default, and number of hard links is always "1". On UNIX - systems, the actual owner, group, and number of links are - printed. - - This is how output appears to client: - - -rw-rw-rw- 1 owner group 7045120 Sep 02 3:47 music.mp3 - drwxrwxrwx 1 owner group 0 Aug 31 18:50 e-books - -rw-rw-rw- 1 owner group 380 Sep 02 3:40 module.py - """ - for file in listing: - try: - st = self.lstat(file) - except os.error: - if ignore_err: - continue - raise - perms = filemode(st.st_mode) # permissions - nlinks = st.st_nlink # number of links to inode - if not nlinks: # non-posix system, let's use a bogus value - nlinks = 1 - size = st.st_size # file size - uname = "owner" - gname = "group" - # stat.st_mtime could fail (-1) if last mtime is too old - # in which case we return the local time as last mtime - try: - mtime = time.strftime("%b %d %H:%M", time.localtime(st.st_mtime)) - except ValueError: - mtime = time.strftime("%b %d %H:%M") - - # formatting is matched with proftpd ls output - yield "%s %3s %-8s %-8s %8s %s %s\r\n" %(perms, nlinks, uname, gname, - size, mtime, file.path.split('/')[-1]) - - # Ok - def format_mlsx(self, basedir, listing, perms, facts, ignore_err=True): - """Return an iterator object that yields the entries of a given - directory or of a single file in a form suitable with MLSD and - MLST commands. - - Every entry includes a list of "facts" referring the listed - element. See RFC-3659, chapter 7, to see what every single - fact stands for. - - - (str) basedir: the absolute dirname. - - (list) listing: the names of the entries in basedir - - (str) perms: the string referencing the user permissions. - - (str) facts: the list of "facts" to be returned. - - (bool) ignore_err: when False raise exception if os.stat() - call fails. - - Note that "facts" returned may change depending on the platform - and on what user specified by using the OPTS command. - - This is how output could appear to the client issuing - a MLSD request: - - type=file;size=156;perm=r;modify=20071029155301;unique=801cd2; music.mp3 - type=dir;size=0;perm=el;modify=20071127230206;unique=801e33; ebooks - type=file;size=211;perm=r;modify=20071103093626;unique=801e32; module.py - """ - permdir = ''.join([x for x in perms if x not in 'arw']) - permfile = ''.join([x for x in perms if x not in 'celmp']) - if ('w' in perms) or ('a' in perms) or ('f' in perms): - permdir += 'c' - if 'd' in perms: - permdir += 'p' - type = size = perm = modify = create = unique = mode = uid = gid = "" - for basename in listing: - file = os.path.join(basedir, basename) - try: - st = self.stat(file) - except OSError: - if ignore_err: - continue - raise - # type + perm - if stat.S_ISDIR(st.st_mode): - if 'type' in facts: - if basename == '.': - type = 'type=cdir;' - elif basename == '..': - type = 'type=pdir;' - else: - type = 'type=dir;' - if 'perm' in facts: - perm = 'perm=%s;' %permdir - else: - if 'type' in facts: - type = 'type=file;' - if 'perm' in facts: - perm = 'perm=%s;' %permfile - if 'size' in facts: - size = 'size=%s;' %st.st_size # file size - # last modification time - if 'modify' in facts: - try: - modify = 'modify=%s;' %time.strftime("%Y%m%d%H%M%S", - time.localtime(st.st_mtime)) - except ValueError: - # stat.st_mtime could fail (-1) if last mtime is too old - modify = "" - if 'create' in facts: - # on Windows we can provide also the creation time - try: - create = 'create=%s;' %time.strftime("%Y%m%d%H%M%S", - time.localtime(st.st_ctime)) - except ValueError: - create = "" - # UNIX only - if 'unix.mode' in facts: - mode = 'unix.mode=%s;' %oct(st.st_mode & 0777) - if 'unix.uid' in facts: - uid = 'unix.uid=%s;' %st.st_uid - if 'unix.gid' in facts: - gid = 'unix.gid=%s;' %st.st_gid - # We provide unique fact (see RFC-3659, chapter 7.5.2) on - # posix platforms only; we get it by mixing st_dev and - # st_ino values which should be enough for granting an - # uniqueness for the file listed. - # The same approach is used by pure-ftpd. - # Implementors who want to provide unique fact on other - # platforms should use some platform-specific method (e.g. - # on Windows NTFS filesystems MTF records could be used). - if 'unique' in facts: - unique = "unique=%x%x;" %(st.st_dev, st.st_ino) - - yield "%s%s%s%s%s%s%s%s%s %s\r\n" %(type, size, perm, modify, create, - mode, uid, gid, unique, basename) +# text = not 'b' in mode +# # max number of tries to find out a unique file name +# tempfile.TMP_MAX = 50 +# fd, name = tempfile.mkstemp(suffix, prefix, dir, text=text) +# file = os.fdopen(fd, mode) +# return FileWrapper(file, name) + + text = not 'b' in mode + # for unique file , maintain version if duplicate file + if dir: + cr = dir.cr + uid = dir.uid + pool = pooler.get_pool(cr.dbname) + object=dir and dir.object or False + object2=dir and dir.object2 or False + res=pool.get('ir.attachment').search(cr,uid,[('name','like',prefix),('parent_id','=',object and object.type in ('directory','ressource') and object.id or False),('res_id','=',object2 and object2.id or False),('res_model','=',object2 and object2._name or False)]) + if len(res): + pre = prefix.split('.') + prefix=pre[0] + '.v'+str(len(res))+'.'+pre[1] + #prefix = prefix + '.' + return self.create(dir,suffix+prefix,text) + + + + # Ok + def chdir(self, path): + if not path: + self.cwd='/' + return None + if path.type in ('collection','database'): + self.cwd = self.fs2ftp(path) + else: + raise OSError(1, 'Operation not permited.') + + # Ok + def mkdir(self, node, basename): + """Create the specified directory.""" + if not node: + raise OSError(1, 'Operation not permited.') + try: + object2=node and node.object2 or False + object=node and node.object or False + cr = node.cr + uid = node.uid + pool = pooler.get_pool(cr.dbname) + if node.object and (node.object.type=='ressource') and not node.object2: + raise OSError(1, 'Operation not permited.') + val = { + 'name': basename, + 'ressource_parent_type_id': object and object.ressource_type_id.id or False, + 'ressource_id': object2 and object2.id or False + } + if (object and (object.type in ('directory'))) or not object2: + val['parent_id'] = object and object.id or False + # Check if it alreayd exists ! + pool.get('document.directory').create(cr, uid, val) + cr.commit() + except Exception,e: + print e + raise OSError(1, 'Operation not permited.') + + + # Ok + def close_cr(self, data): + if data: + data[0].close() + return True + + def get_cr(self, path): + path = self.ftpnorm(path) + if path=='/': + return None + dbname = path.split('/')[1] + try: + db,pool = pooler.get_db_and_pool(dbname) + except: + raise OSError(1, 'Operation not permited.') + cr = db.cursor() + uid = security.login(dbname, self.username, self.password) + if not uid: + raise OSError(2, 'Authentification Required.') + return cr, uid, pool + + # Ok + def listdir(self, path): + """List the content of a directory.""" + class false_node: + object = None + type = 'database' + def __init__(self, db): + self.path = '/'+db + + if path is None: + result = [] + for db in self.db_list(): + result.append(false_node(db)) + return result + return path.children() + + # Ok + def rmdir(self, node): + """Remove the specified directory.""" + cr = node.cr + uid = node.uid + pool = pooler.get_pool(cr.dbname) + object2=node and node.object2 or False + object=node and node.object or False + if object._table_name=='document.directory': + if node.children(): + raise OSError(39, 'Directory not empty.') + res = pool.get('document.directory').unlink(cr, uid, [object.id]) + else: + raise OSError(39, 'Directory not empty.') + + cr.commit() + + # Ok + def remove(self, node): + """Remove the specified file.""" + cr = node.cr + uid = node.uid + pool = pooler.get_pool(cr.dbname) + object2=node and node.object2 or False + object=node and node.object or False + if not object: + raise OSError(2, 'Not such file or directory.') + if object._table_name=='ir.attachment': + res = pool.get('ir.attachment').unlink(cr, uid, [object.id]) + else: + raise OSError(1, 'Operation not permited.') + cr.commit() + + # Ok + def rename(self, src, dst_basedir,dst_basename): + """ + Renaming operation, the effect depends on the src: + * A file: read, create and remove + * A directory: change the parent and reassign childs to ressource + """ + try: + if src.type=='collection': + if src.object._table_name <> 'document.directory': + raise OSError(1, 'Operation not permited.') + result = { + 'directory': [], + 'attachment': [] + } + # Compute all childs to set the new ressource ID + child_ids = [src] + while len(child_ids): + node = child_ids.pop(0) + child_ids += node.children() + if node.type =='collection': + result['directory'].append(node.object.id) + if (not node.object.ressource_id) and node.object2: + raise OSError(1, 'Operation not permited.') + elif node.type =='file': + result['attachment'].append(node.object.id) + + cr = src.cr + uid = src.uid + pool = pooler.get_pool(cr.dbname) + object2=src and src.object2 or False + object=src and src.object or False + if object2 and not object.ressource_id: + raise OSError(1, 'Operation not permited.') + val = { + 'name':dst_basename, + } + if (dst_basedir.object and (dst_basedir.object.type in ('directory'))) or not dst_basedir.object2: + val['parent_id'] = dst_basedir.object and dst_basedir.object.id or False + else: + val['parent_id'] = False + res = pool.get('document.directory').write(cr, uid, [object.id],val) + + if dst_basedir.object2: + ressource_type_id = pool.get('ir.model').search(cr,uid,[('model','=',dst_basedir.object2._name)])[0] + ressource_id = dst_basedir.object2.id + title = dst_basedir.object2.name + ressource_model = dst_basedir.object2._name + if dst_basedir.object2._name=='res.partner': + partner_id=dst_basedir.object2.id + else: + partner_id= dst_basedir.object2.partner_id and dst_basedir.object2.partner_id.id or False + else: + ressource_type_id = False + ressource_id=False + ressource_model = False + partner_id = False + title = False + + pool.get('document.directory').write(cr, uid, result['directory'], { + 'ressource_id': ressource_id, + 'ressource_parent_type_id': ressource_type_id + }) + val = { + 'res_id': ressource_id, + 'res_model': ressource_model, + 'title': title, + 'partner_id': partner_id + } + pool.get('ir.attachment').write(cr, uid, result['attachment'], val) + if (not val['res_id']) and result['attachment']: + dst_basedir.cr.execute('update ir_attachment set res_id=NULL where id in ('+','.join(map(str,result['attachment']))+')') + + cr.commit() + elif src.type=='file': + pool = pooler.get_pool(src.cr.dbname) + val = { + 'partner_id':False, + #'res_id': False, + 'res_model': False, + 'name': dst_basename, + 'datas_fname': dst_basename, + 'title': dst_basename, + } + + if (dst_basedir.object and (dst_basedir.object.type in ('directory','ressource'))) or not dst_basedir.object2: + val['parent_id'] = dst_basedir.object and dst_basedir.object.id or False + else: + val['parent_id'] = False + + if dst_basedir.object2: + val['res_model'] = dst_basedir.object2._name + val['res_id'] = dst_basedir.object2.id + val['title'] = dst_basedir.object2.name + if dst_basedir.object2._name=='res.partner': + val['partner_id']=dst_basedir.object2.id + else: + val['partner_id']= dst_basedir.object2.partner_id and dst_basedir.object2.partner_id.id or False + elif src.object.res_id: + # I had to do that because writing False to an integer writes 0 instead of NULL + # change if one day we decide to improve osv/fields.py + dst_basedir.cr.execute('update ir_attachment set res_id=NULL where id=%d', (src.object.id,)) + + pool.get('ir.attachment').write(src.cr, src.uid, [src.object.id], val) + src.cr.commit() + elif src.type=='content': + src_file=self.open(src,'r') + dst_file=self.create(dst_basedir,dst_basename,'w') + dst_file.write(src_file.getvalue()) + dst_file.close() + src_file.close() + src.cr.commit() + else: + raise OSError(1, 'Operation not permited.') + except Exception,err: + print err + raise OSError(1,'Operation not permited.') + + + + + # Nearly Ok + def stat(self, node): + r = list(os.stat('/')) + if self.isfile(node): + r[0] = 33188 + r[6] = self.getsize(node) + r[7] = self.getmtime(node) + r[8] = self.getmtime(node) + r[9] = self.getmtime(node) + return os.stat_result(r) + lstat = stat + + # --- Wrapper methods around os.path.* + + # Ok + def isfile(self, node): + if node and (node.type not in ('collection','database')): + return True + return False + + # Ok + def islink(self, path): + """Return True if path is a symbolic link.""" + return False + + # Ok + def isdir(self, node): + """Return True if path is a directory.""" + if node is None: + return True + if node and (node.type in ('collection','database')): + return True + return False + + # Ok + def getsize(self, node): + """Return the size of the specified file in bytes.""" + result = 0L + if node.type=='file': + result = node.object.file_size or 0L + return result + + # Ok + def getmtime(self, node): + """Return the last modified time as a number of seconds since + the epoch.""" + if node.object and node.type<>'content': + dt = (node.object.write_date or node.object.create_date)[:19] + result = time.mktime(time.strptime(dt, '%Y-%m-%d %H:%M:%S')) + else: + result = time.mktime(time.localtime()) + return result + + # Ok + def realpath(self, path): + """Return the canonical version of path eliminating any + symbolic links encountered in the path (if they are + supported by the operating system). + """ + return path + + # Ok + def lexists(self, path): + """Return True if path refers to an existing path, including + a broken or circular symbolic link. + """ + return path and True or False + exists = lexists + + # Ok, can be improved + def glob1(self, dirname, pattern): + """Return a list of files matching a dirname pattern + non-recursively. + + Unlike glob.glob1 raises exception if os.listdir() fails. + """ + names = self.listdir(dirname) + if pattern[0] != '.': + names = filter(lambda x: x.path[0] != '.', names) + return fnmatch.filter(names, pattern) + + # --- Listing utilities + + # note: the following operations are no more blocking + + # Ok + def get_list_dir(self, path): + """"Return an iterator object that yields a directory listing + in a form suitable for LIST command. + """ + if self.isdir(path): + listing = self.listdir(path) + #listing.sort() + return self.format_list(path and path.path or '/', listing) + # if path is a file or a symlink we return information about it + elif self.isfile(path): + basedir, filename = os.path.split(path.path) + self.lstat(path) # raise exc in case of problems + return self.format_list(basedir, [filename]) + + + # Ok + def get_stat_dir(self, rawline, datacr): + """Return an iterator object that yields a list of files + matching a dirname pattern non-recursively in a form + suitable for STAT command. + + - (str) rawline: the raw string passed by client as command + argument. + """ + ftppath = self.ftpnorm(rawline) + if not glob.has_magic(ftppath): + return self.get_list_dir(self.ftp2fs(rawline, datacr)) + else: + basedir, basename = os.path.split(ftppath) + if glob.has_magic(basedir): + return iter(['Directory recursion not supported.\r\n']) + else: + basedir = self.ftp2fs(basedir, datacr) + listing = self.glob1(basedir, basename) + if listing: + listing.sort() + return self.format_list(basedir, listing) + + # Ok + def format_list(self, basedir, listing, ignore_err=True): + """Return an iterator object that yields the entries of given + directory emulating the "/bin/ls -lA" UNIX command output. + + - (str) basedir: the absolute dirname. + - (list) listing: the names of the entries in basedir + - (bool) ignore_err: when False raise exception if os.lstat() + call fails. + + On platforms which do not support the pwd and grp modules (such + as Windows), ownership is printed as "owner" and "group" as a + default, and number of hard links is always "1". On UNIX + systems, the actual owner, group, and number of links are + printed. + + This is how output appears to client: + + -rw-rw-rw- 1 owner group 7045120 Sep 02 3:47 music.mp3 + drwxrwxrwx 1 owner group 0 Aug 31 18:50 e-books + -rw-rw-rw- 1 owner group 380 Sep 02 3:40 module.py + """ + for file in listing: + try: + st = self.lstat(file) + except os.error: + if ignore_err: + continue + raise + perms = filemode(st.st_mode) # permissions + nlinks = st.st_nlink # number of links to inode + if not nlinks: # non-posix system, let's use a bogus value + nlinks = 1 + size = st.st_size # file size + uname = "owner" + gname = "group" + # stat.st_mtime could fail (-1) if last mtime is too old + # in which case we return the local time as last mtime + try: + mtime = time.strftime("%b %d %H:%M", time.localtime(st.st_mtime)) + except ValueError: + mtime = time.strftime("%b %d %H:%M") + + # formatting is matched with proftpd ls output + yield "%s %3s %-8s %-8s %8s %s %s\r\n" %(perms, nlinks, uname, gname, + size, mtime, file.path.split('/')[-1]) + + # Ok + def format_mlsx(self, basedir, listing, perms, facts, ignore_err=True): + """Return an iterator object that yields the entries of a given + directory or of a single file in a form suitable with MLSD and + MLST commands. + + Every entry includes a list of "facts" referring the listed + element. See RFC-3659, chapter 7, to see what every single + fact stands for. + + - (str) basedir: the absolute dirname. + - (list) listing: the names of the entries in basedir + - (str) perms: the string referencing the user permissions. + - (str) facts: the list of "facts" to be returned. + - (bool) ignore_err: when False raise exception if os.stat() + call fails. + + Note that "facts" returned may change depending on the platform + and on what user specified by using the OPTS command. + + This is how output could appear to the client issuing + a MLSD request: + + type=file;size=156;perm=r;modify=20071029155301;unique=801cd2; music.mp3 + type=dir;size=0;perm=el;modify=20071127230206;unique=801e33; ebooks + type=file;size=211;perm=r;modify=20071103093626;unique=801e32; module.py + """ + permdir = ''.join([x for x in perms if x not in 'arw']) + permfile = ''.join([x for x in perms if x not in 'celmp']) + if ('w' in perms) or ('a' in perms) or ('f' in perms): + permdir += 'c' + if 'd' in perms: + permdir += 'p' + type = size = perm = modify = create = unique = mode = uid = gid = "" + for basename in listing: + file = os.path.join(basedir, basename) + try: + st = self.stat(file) + except OSError: + if ignore_err: + continue + raise + # type + perm + if stat.S_ISDIR(st.st_mode): + if 'type' in facts: + if basename == '.': + type = 'type=cdir;' + elif basename == '..': + type = 'type=pdir;' + else: + type = 'type=dir;' + if 'perm' in facts: + perm = 'perm=%s;' %permdir + else: + if 'type' in facts: + type = 'type=file;' + if 'perm' in facts: + perm = 'perm=%s;' %permfile + if 'size' in facts: + size = 'size=%s;' %st.st_size # file size + # last modification time + if 'modify' in facts: + try: + modify = 'modify=%s;' %time.strftime("%Y%m%d%H%M%S", + time.localtime(st.st_mtime)) + except ValueError: + # stat.st_mtime could fail (-1) if last mtime is too old + modify = "" + if 'create' in facts: + # on Windows we can provide also the creation time + try: + create = 'create=%s;' %time.strftime("%Y%m%d%H%M%S", + time.localtime(st.st_ctime)) + except ValueError: + create = "" + # UNIX only + if 'unix.mode' in facts: + mode = 'unix.mode=%s;' %oct(st.st_mode & 0777) + if 'unix.uid' in facts: + uid = 'unix.uid=%s;' %st.st_uid + if 'unix.gid' in facts: + gid = 'unix.gid=%s;' %st.st_gid + # We provide unique fact (see RFC-3659, chapter 7.5.2) on + # posix platforms only; we get it by mixing st_dev and + # st_ino values which should be enough for granting an + # uniqueness for the file listed. + # The same approach is used by pure-ftpd. + # Implementors who want to provide unique fact on other + # platforms should use some platform-specific method (e.g. + # on Windows NTFS filesystems MTF records could be used). + if 'unique' in facts: + unique = "unique=%x%x;" %(st.st_dev, st.st_ino) + + yield "%s%s%s%s%s%s%s%s%s %s\r\n" %(type, size, perm, modify, create, + mode, uid, gid, unique, basename) diff --git a/addons/document/ftpserver/authorizer.py b/addons/document/ftpserver/authorizer.py index d33dc5d7234..fa45f0c3183 100644 --- a/addons/document/ftpserver/authorizer.py +++ b/addons/document/ftpserver/authorizer.py @@ -1,70 +1,71 @@ #import pooler class authorizer: - read_perms = "elr" - write_perms = "adfmw" - - def __init__(self): - self.password = '' - - def validate_authentication(self, username, password): - """Return True if the supplied username and password match the - stored credentials.""" - self.password = password - return True - - def impersonate_user(self, username, password): - """Impersonate another user (noop). - - It is always called before accessing the filesystem. - By default it does nothing. The subclass overriding this - method is expected to provide a mechanism to change the - current user. - """ - - def terminate_impersonation(self): - """Terminate impersonation (noop). - - It is always called after having accessed the filesystem. - By default it does nothing. The subclass overriding this - method is expected to provide a mechanism to switch back - to the original user. - """ - - def has_user(self, username): - """Whether the username exists in the virtual users table.""" - return True - - def has_perm(self, username, perm, path=None): - """Whether the user has permission over path (an absolute - pathname of a file or a directory). - - Expected perm argument is one of the following letters: - "elradfmw". - """ - return True - paths = path.split('/') - if not len(paths)>2: - return True - db_name = paths[1] - db,pool = pooler.get_db_and_pool(db_name) - res = security.login(db_name, username, self.password) - return bool(res) - - def get_perms(self, username): - """Return current user permissions.""" - return 'elr' - - def get_home_dir(self, username): - """Return the user's home directory.""" - return '/' - - def get_msg_login(self, username): - """Return the user's login message.""" - return 'Welcome on OpenERP document management system.' - - def get_msg_quit(self, username): - """Return the user's quitting message.""" - return 'Bye.' + read_perms = "elr" + write_perms = "adfmw" + + def __init__(self): + self.password = '' + + def validate_authentication(self, username, password): + """Return True if the supplied username and password match the + stored credentials.""" + self.password = password + return True + + def impersonate_user(self, username, password): + """Impersonate another user (noop). + + It is always called before accessing the filesystem. + By default it does nothing. The subclass overriding this + method is expected to provide a mechanism to change the + current user. + """ + + def terminate_impersonation(self): + """Terminate impersonation (noop). + + It is always called after having accessed the filesystem. + By default it does nothing. The subclass overriding this + method is expected to provide a mechanism to switch back + to the original user. + """ + + def has_user(self, username): + """Whether the username exists in the virtual users table.""" + if username=='anonymous': + return False + return True + + def has_perm(self, username, perm, path=None): + """Whether the user has permission over path (an absolute + pathname of a file or a directory). + + Expected perm argument is one of the following letters: + "elradfmw". + """ + paths = path.split('/') + if not len(paths)>2: + return True + db_name = paths[1] + db,pool = pooler.get_db_and_pool(db_name) + res = security.login(db_name, username, self.password) + return bool(res) + + def get_perms(self, username): + """Return current user permissions.""" + return 'elr' + + def get_home_dir(self, username): + """Return the user's home directory.""" + return '/' + + def get_msg_login(self, username): + """Return the user's login message.""" + return 'Welcome on OpenERP document management system.' + + def get_msg_quit(self, username): + """Return the user's quitting message.""" + return 'Bye.' diff --git a/addons/document/ftpserver/ftpserver.py b/addons/document/ftpserver/ftpserver.py index 4ca5b06bb39..99b1c43abb3 100644 --- a/addons/document/ftpserver/ftpserver.py +++ b/addons/document/ftpserver/ftpserver.py @@ -2512,9 +2512,14 @@ class FTPHandler(asynchat.async_chat): path = self.fs.ftp2fs(line, datacr) self.run_as_current_user(self.fs.chdir, path) except OSError, err: - why = _strerror(err) - self.log('FAIL CWD "%s". %s.' %(self.fs.ftpnorm(line), why)) - self.respond('550 %s.' %why) + if err.errno==2: + why = 'Authentication Required or Failed' + self.log('FAIL CWD "%s". %s.' %(self.fs.ftpnorm(line), why)) + self.respond('530 %s.' %why) + else: + why = _strerror(err) + self.log('FAIL CWD "%s". %s.' %(self.fs.ftpnorm(line), why)) + self.respond('550 %s.' %why) else: self.log('OK CWD "%s".' %self.fs.cwd) self.respond('250 "%s" is the current directory.' %self.fs.cwd) diff --git a/addons/document/i18n/document.pot b/addons/document/i18n/document.pot new file mode 100644 index 00000000000..f109560fa3b --- /dev/null +++ b/addons/document/i18n/document.pot @@ -0,0 +1,392 @@ +# Translation of OpenERP Server. +# This file containt the translation of the following modules: +# * document +# +msgid "" +msgstr "" +"Project-Id-Version: OpenERP Server 4.3.99\n" +"Report-Msgid-Bugs-To: support@openerp.com\n" +"POT-Creation-Date: 2008-10-28 10:07:49+0000\n" +"PO-Revision-Date: 2008-10-28 10:07:49+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: document +#: field:document.directory,create_date:0 +#: field:ir.attachment,create_date:0 +msgid "Date Created" +msgstr "" + +#. module: document +#: view:document.configuration.wizard:0 +msgid "'Projects' -> account.analytic.account with ressource tree" +msgstr "" + +#. module: document +#: field:document.directory.content,include_name:0 +msgid "Include Record Name" +msgstr "" + +#. module: document +#: constraint:ir.model:0 +msgid "The Object name must start with x_ and not contain any special character !" +msgstr "" + +#. module: document +#: constraint:document.directory:0 +msgid "Error! You can not create recursive Directories." +msgstr "" + +#. module: document +#: view:document.configuration.wizard:0 +msgid "Configure Ressources Directory :" +msgstr "" + +#. module: document +#: field:ir.attachment,store_method:0 +msgid "Storing Method" +msgstr "" + +#. module: document +#: model:ir.actions.act_window,name:document.action_config_auto_directory +msgid "Auto Configure Directory" +msgstr "" + +#. module: document +#: field:ir.attachment,file_size:0 +msgid "File Size" +msgstr "" + +#. module: document +#: field:document.directory.content,extension:0 +msgid "Report Type" +msgstr "" + +#. module: document +#: view:document.configuration.wizard:0 +msgid "You can use ftp://admin:admin@localhost:8021 " +msgstr "" + +#. module: document +#: field:ir.actions.report.xml,model_id:0 +msgid "Model Id" +msgstr "" + +#. module: document +#: selection:document.directory,type:0 +msgid "Static Directory" +msgstr "" + +#. module: document +#: model:ir.model,name:document.model_document_directory_content_type +msgid "Directory Content Type" +msgstr "" + +#. module: document +#: field:document.directory,ressource_type_id:0 +msgid "Childs Model" +msgstr "" + +#. module: document +#: view:document.configuration.wizard:0 +msgid "New Directories." +msgstr "" + +#. module: document +#: field:document.directory,type:0 +msgid "Type" +msgstr "" + +#. module: document +#: field:document.directory,user_id:0 +#: field:ir.attachment,user_id:0 +msgid "Owner" +msgstr "" + +#. module: document +#: field:document.directory,parent_id:0 +msgid "Parent Item" +msgstr "" + +#. module: document +#: view:document.directory:0 +msgid "Seq." +msgstr "" + +#. module: document +#: field:document.directory,file_ids:0 +#: view:ir.attachment:0 +msgid "Files" +msgstr "" + +#. module: document +#: view:document.directory:0 +msgid "Directory Type" +msgstr "" + +#. module: document +#: view:document.configuration.wizard:0 +msgid "Connect to Document Management System." +msgstr "" + +#. module: document +#: view:document.configuration.wizard:0 +msgid "Configure" +msgstr "" + +#. module: document +#: model:ir.model,name:document.model_document_directory_content +msgid "Directory Content" +msgstr "" + +#. module: document +#: field:ir.attachment,store_fname:0 +msgid "Stored Filename" +msgstr "" + +#. module: document +#: view:document.configuration.wizard:0 +msgid "Cancel" +msgstr "" + +#. module: document +#: model:ir.ui.menu,name:document.menu_document +msgid "Document Management" +msgstr "" + +#. module: document +#: selection:ir.attachment,store_method:0 +msgid "Link" +msgstr "" + +#. module: document +#: selection:document.directory,type:0 +msgid "Other Ressources" +msgstr "" + +#. module: document +#: field:document.directory,group_ids:0 +#: field:ir.attachment,group_ids:0 +msgid "Groups" +msgstr "" + +#. module: document +#: field:document.directory.content,report_id:0 +msgid "Report" +msgstr "" + +#. module: document +#: model:ir.ui.menu,name:document.menu_document_configuration +msgid "Configuration" +msgstr "" + +#. module: document +#: field:document.directory.content.type,name:0 +#: field:document.directory,file_type:0 +#: field:ir.attachment,file_type:0 +msgid "Content Type" +msgstr "" + +#. module: document +#: view:document.directory:0 +#: view:ir.attachment:0 +msgid "Definition" +msgstr "" + +#. module: document +#: constraint:ir.ui.view:0 +msgid "Invalid XML for View Architecture!" +msgstr "" + +#. module: document +#: field:document.directory,name:0 +msgid "Name" +msgstr "" + +#. module: document +#: field:document.directory.content.type,code:0 +msgid "Extension" +msgstr "" + +#. module: document +#: selection:ir.attachment,store_method:0 +msgid "Database" +msgstr "" + +#. module: document +#: field:document.directory,content_ids:0 +msgid "Virtual Files" +msgstr "" + +#. module: document +#: model:ir.ui.menu,name:document.menu_document_directories +#: view:document.directory:0 +msgid "Directories" +msgstr "" + +#. module: document +#: field:ir.attachment,title:0 +msgid "Resource Title" +msgstr "" + +#. module: document +#: field:document.directory.content.type,active:0 +msgid "Active" +msgstr "" + +#. module: document +#: view:document.configuration.wizard:0 +msgid "'All Sales Order' -> sale.order" +msgstr "" + +#. module: document +#: model:ir.model,name:document.model_document_configuration_wizard +msgid "document.configuration.wizard" +msgstr "" + +#. module: document +#: model:ir.ui.menu,name:document.menu_document_files +msgid "Search a File" +msgstr "" + +#. module: document +#: selection:ir.attachment,store_method:0 +msgid "Filesystem" +msgstr "" + +#. module: document +#: field:document.directory.content,directory_id:0 +#: field:ir.attachment,parent_id:0 +msgid "Directory" +msgstr "" + +#. module: document +#: view:document.directory:0 +#: view:ir.attachment:0 +msgid "Security" +msgstr "" + +#. module: document +#: field:document.directory,write_uid:0 +#: field:ir.attachment,write_uid:0 +msgid "Last Modification User" +msgstr "" + +#. module: document +#: view:document.configuration.wizard:0 +msgid "'Quotation','Meetings','Analysis Reports' -> static directories which are link directories of 'account.analytic.account' model" +msgstr "" + +#. module: document +#: help:document.directory.content,include_name:0 +msgid "Check if you cant that the name of the file start by the record name." +msgstr "" + +#. module: document +#: view:document.configuration.wizard:0 +msgid "'Sales by Salesman' -> res.users" +msgstr "" + +#. module: document +#: field:document.directory,domain:0 +msgid "Domain" +msgstr "" + +#. module: document +#: field:document.directory,child_ids:0 +msgid "Childs" +msgstr "" + +#. module: document +#: field:document.directory,write_date:0 +#: field:ir.attachment,write_date:0 +msgid "Date Modified" +msgstr "" + +#. module: document +#: field:document.directory.content,suffix:0 +msgid "Suffix" +msgstr "" + +#. module: document +#: view:document.configuration.wizard:0 +msgid "'Personnal Folders' -> res.users" +msgstr "" + +#. module: document +#: field:document.directory,create_uid:0 +#: field:ir.attachment,create_uid:0 +msgid "Creator" +msgstr "" + +#. module: document +#: field:document.directory.content,name:0 +msgid "Content Name" +msgstr "" + +#. module: document +#: field:document.directory,ressource_parent_type_id:0 +msgid "Linked Model" +msgstr "" + +#. module: document +#: field:document.directory.content,sequence:0 +msgid "Sequence" +msgstr "" + +#. module: document +#: view:document.configuration.wizard:0 +msgid "'Sales' -> sale.order which is link directory of 'res.users' model" +msgstr "" + +#. module: document +#: field:document.directory,ressource_tree:0 +msgid "Tree Structure" +msgstr "" + +#. module: document +#: model:ir.ui.menu,name:document.menu_document_directories_tree +msgid "Directorie's Structure" +msgstr "" + +#. module: document +#: view:document.configuration.wizard:0 +msgid "Auto Configure" +msgstr "" + +#. module: document +#: field:document.directory,ressource_id:0 +msgid "Ressource ID" +msgstr "" + +#. module: document +#: field:ir.attachment,partner_id:0 +msgid "Partner" +msgstr "" + +#. module: document +#: view:document.directory:0 +msgid "PDF Report" +msgstr "" + +#. module: document +#: field:ir.attachment,index_content:0 +#: view:ir.attachment:0 +msgid "Indexed Content" +msgstr "" + +#. module: document +#: model:ir.model,name:document.model_document_directory +#: field:process.node,directory_id:0 +msgid "Document directory" +msgstr "" + +#. module: document +#: view:document.directory:0 +msgid "Contents" +msgstr "" + diff --git a/addons/document/i18n/fr_FR.po b/addons/document/i18n/fr_FR.po new file mode 100644 index 00000000000..1b52b4bf0c5 --- /dev/null +++ b/addons/document/i18n/fr_FR.po @@ -0,0 +1,392 @@ +# Translation of OpenERP Server. +# This file containt the translation of the following modules: +# * document +# +msgid "" +msgstr "" +"Project-Id-Version: OpenERP Server 4.3.99\n" +"Report-Msgid-Bugs-To: support@openerp.com\n" +"POT-Creation-Date: 2008-10-28 10:07:49+0000\n" +"PO-Revision-Date: 2008-11-02 17:56+0100\n" +"Last-Translator: Christophe Chauvet <christophe.chauvet@syleam.fr>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: \n" + +#. module: document +#: field:document.directory,create_date:0 +#: field:ir.attachment,create_date:0 +msgid "Date Created" +msgstr "Date de Création" + +#. module: document +#: view:document.configuration.wizard:0 +msgid "'Projects' -> account.analytic.account with ressource tree" +msgstr "'Projets' -> account.analytic.account avec un arbre des ressources" + +#. module: document +#: field:document.directory.content,include_name:0 +msgid "Include Record Name" +msgstr "Inclure le Nom de l'Enregistrement" + +#. module: document +#: constraint:ir.model:0 +msgid "The Object name must start with x_ and not contain any special character !" +msgstr "Le nom de l'objet doit commencer avec x_ et ne pas contenir de charactères spéciaux !" + +#. module: document +#: constraint:document.directory:0 +msgid "Error! You can not create recursive Directories." +msgstr "Erreur ! Vous ne pouvez pas créer de Répertoires récursivement." + +#. module: document +#: view:document.configuration.wizard:0 +msgid "Configure Ressources Directory :" +msgstr "Configurer le Répertoire des Ressources :" + +#. module: document +#: field:ir.attachment,store_method:0 +msgid "Storing Method" +msgstr "Méthode de Stockage" + +#. module: document +#: model:ir.actions.act_window,name:document.action_config_auto_directory +msgid "Auto Configure Directory" +msgstr "Configurer le Répertoire" + +#. module: document +#: field:ir.attachment,file_size:0 +msgid "File Size" +msgstr "Taille du Fichier" + +#. module: document +#: field:document.directory.content,extension:0 +msgid "Report Type" +msgstr "Type de Rapport" + +#. module: document +#: view:document.configuration.wizard:0 +msgid "You can use ftp://admin:admin@localhost:8021 " +msgstr "Vous pouvez utiliser ftp://admin:admin@localhost:8021 " + +#. module: document +#: field:ir.actions.report.xml,model_id:0 +msgid "Model Id" +msgstr "Id du Modèle" + +#. module: document +#: selection:document.directory,type:0 +msgid "Static Directory" +msgstr "Répertoire Statique" + +#. module: document +#: model:ir.model,name:document.model_document_directory_content_type +msgid "Directory Content Type" +msgstr "Type de Contenu du Répertoire" + +#. module: document +#: field:document.directory,ressource_type_id:0 +msgid "Childs Model" +msgstr "Modèle des Enfants" + +#. module: document +#: view:document.configuration.wizard:0 +msgid "New Directories." +msgstr "Nouveaux Répertoires" + +#. module: document +#: field:document.directory,type:0 +msgid "Type" +msgstr "Type" + +#. module: document +#: field:document.directory,user_id:0 +#: field:ir.attachment,user_id:0 +msgid "Owner" +msgstr "Propriétaire" + +#. module: document +#: field:document.directory,parent_id:0 +msgid "Parent Item" +msgstr "Objet Parent" + +#. module: document +#: view:document.directory:0 +msgid "Seq." +msgstr "Séq." + +#. module: document +#: field:document.directory,file_ids:0 +#: view:ir.attachment:0 +msgid "Files" +msgstr "Fichiers" + +#. module: document +#: view:document.directory:0 +msgid "Directory Type" +msgstr "Type de Répertoire" + +#. module: document +#: view:document.configuration.wizard:0 +msgid "Connect to Document Management System." +msgstr "Connecter au Système de Gestion Documentaire" + +#. module: document +#: view:document.configuration.wizard:0 +msgid "Configure" +msgstr "Configurer" + +#. module: document +#: model:ir.model,name:document.model_document_directory_content +msgid "Directory Content" +msgstr "Contenu du Répertoire" + +#. module: document +#: field:ir.attachment,store_fname:0 +msgid "Stored Filename" +msgstr "Nom du Fichier Enregistré" + +#. module: document +#: view:document.configuration.wizard:0 +msgid "Cancel" +msgstr "Annuler" + +#. module: document +#: model:ir.ui.menu,name:document.menu_document +msgid "Document Management" +msgstr "Gestion Documentaire" + +#. module: document +#: selection:ir.attachment,store_method:0 +msgid "Link" +msgstr "Lien" + +#. module: document +#: selection:document.directory,type:0 +msgid "Other Ressources" +msgstr "Autres Ressources" + +#. module: document +#: field:document.directory,group_ids:0 +#: field:ir.attachment,group_ids:0 +msgid "Groups" +msgstr "Groupes" + +#. module: document +#: field:document.directory.content,report_id:0 +msgid "Report" +msgstr "Rapport" + +#. module: document +#: model:ir.ui.menu,name:document.menu_document_configuration +msgid "Configuration" +msgstr "Configuration" + +#. module: document +#: field:document.directory.content.type,name:0 +#: field:document.directory,file_type:0 +#: field:ir.attachment,file_type:0 +msgid "Content Type" +msgstr "Type de Contenu" + +#. module: document +#: view:document.directory:0 +#: view:ir.attachment:0 +msgid "Definition" +msgstr "Définition" + +#. module: document +#: constraint:ir.ui.view:0 +msgid "Invalid XML for View Architecture!" +msgstr "XML non valide pour l'architecture de la vue!" + +#. module: document +#: field:document.directory,name:0 +msgid "Name" +msgstr "Nom" + +#. module: document +#: field:document.directory.content.type,code:0 +msgid "Extension" +msgstr "Extension" + +#. module: document +#: selection:ir.attachment,store_method:0 +msgid "Database" +msgstr "Base de données" + +#. module: document +#: field:document.directory,content_ids:0 +msgid "Virtual Files" +msgstr "Fichiers Virtuels" + +#. module: document +#: model:ir.ui.menu,name:document.menu_document_directories +#: view:document.directory:0 +msgid "Directories" +msgstr "Répertoires" + +#. module: document +#: field:ir.attachment,title:0 +msgid "Resource Title" +msgstr "Titre de la Ressource" + +#. module: document +#: field:document.directory.content.type,active:0 +msgid "Active" +msgstr "Actif" + +#. module: document +#: view:document.configuration.wizard:0 +msgid "'All Sales Order' -> sale.order" +msgstr "'Toutes les Commandes Clients' -> sale.order" + +#. module: document +#: model:ir.model,name:document.model_document_configuration_wizard +msgid "document.configuration.wizard" +msgstr "" + +#. module: document +#: model:ir.ui.menu,name:document.menu_document_files +msgid "Search a File" +msgstr "Chercher un Fichier" + +#. module: document +#: selection:ir.attachment,store_method:0 +msgid "Filesystem" +msgstr "Système de Fichiers" + +#. module: document +#: field:document.directory.content,directory_id:0 +#: field:ir.attachment,parent_id:0 +msgid "Directory" +msgstr "Répertoire" + +#. module: document +#: view:document.directory:0 +#: view:ir.attachment:0 +msgid "Security" +msgstr "Sécurité" + +#. module: document +#: field:document.directory,write_uid:0 +#: field:ir.attachment,write_uid:0 +msgid "Last Modification User" +msgstr "Utilisateur de la Dernière Modification" + +#. module: document +#: view:document.configuration.wizard:0 +msgid "'Quotation','Meetings','Analysis Reports' -> static directories which are link directories of 'account.analytic.account' model" +msgstr "\"Devis\",\"Rendez-vous\",\"Rapports d'Analyse\" -> répertoires statiques qui sont des répertoires liens du modèle 'account.analytic.account'" + +#. module: document +#: help:document.directory.content,include_name:0 +msgid "Check if you cant that the name of the file start by the record name." +msgstr "" + +#. module: document +#: view:document.configuration.wizard:0 +msgid "'Sales by Salesman' -> res.users" +msgstr "'Ventes par Vendeur' -> res.users" + +#. module: document +#: field:document.directory,domain:0 +msgid "Domain" +msgstr "Domaine" + +#. module: document +#: field:document.directory,child_ids:0 +msgid "Childs" +msgstr "Enfants" + +#. module: document +#: field:document.directory,write_date:0 +#: field:ir.attachment,write_date:0 +msgid "Date Modified" +msgstr "Date de Modification" + +#. module: document +#: field:document.directory.content,suffix:0 +msgid "Suffix" +msgstr "Suffixe" + +#. module: document +#: view:document.configuration.wizard:0 +msgid "'Personnal Folders' -> res.users" +msgstr "'Dossier Personel' -> res.users" + +#. module: document +#: field:document.directory,create_uid:0 +#: field:ir.attachment,create_uid:0 +msgid "Creator" +msgstr "Créateur" + +#. module: document +#: field:document.directory.content,name:0 +msgid "Content Name" +msgstr "Nom du Contenu" + +#. module: document +#: field:document.directory,ressource_parent_type_id:0 +msgid "Linked Model" +msgstr "Modèle Lié" + +#. module: document +#: field:document.directory.content,sequence:0 +msgid "Sequence" +msgstr "Séquence" + +#. module: document +#: view:document.configuration.wizard:0 +msgid "'Sales' -> sale.order which is link directory of 'res.users' model" +msgstr "'Ventes' -> sale.order qui est un répertoire lien du modèle 'res.users'" + +#. module: document +#: field:document.directory,ressource_tree:0 +msgid "Tree Structure" +msgstr "Structure en Arbre" + +#. module: document +#: model:ir.ui.menu,name:document.menu_document_directories_tree +msgid "Directorie's Structure" +msgstr "Structure du Répertoire " + +#. module: document +#: view:document.configuration.wizard:0 +msgid "Auto Configure" +msgstr "Configuration Automatique" + +#. module: document +#: field:document.directory,ressource_id:0 +msgid "Ressource ID" +msgstr "ID de la Ressource" + +#. module: document +#: field:ir.attachment,partner_id:0 +msgid "Partner" +msgstr "Partenaire" + +#. module: document +#: view:document.directory:0 +msgid "PDF Report" +msgstr "Rapport PDF" + +#. module: document +#: field:ir.attachment,index_content:0 +#: view:ir.attachment:0 +msgid "Indexed Content" +msgstr "Contenu Indexé" + +#. module: document +#: model:ir.model,name:document.model_document_directory +#: field:process.node,directory_id:0 +msgid "Document directory" +msgstr "Répertoire du Document" + +#. module: document +#: view:document.directory:0 +msgid "Contents" +msgstr "Contenus" + diff --git a/addons/document/odt2txt.py b/addons/document/odt2txt.py index 07f792a830e..29714a94929 100644 --- a/addons/document/odt2txt.py +++ b/addons/document/odt2txt.py @@ -1,4 +1,25 @@ #!/usr/bin/python +# -*- encoding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-2008 Tiny SPRL (<http://tiny.be>). All Rights Reserved +# $Id$ +# +# 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 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################## import sys, zipfile, xml.dom.minidom import StringIO diff --git a/addons/document/process/document_process.xml b/addons/document/process/document_process.xml deleted file mode 100644 index 3c37c793ab4..00000000000 --- a/addons/document/process/document_process.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" ?> -<openerp> - <data> - - <record id="process_node_papers0" model="process.node"> - <field name="menu_id" ref="document.menu_document_directories"/> - <field name="model_id" ref="document.model_document_directory"/> - <field eval=""""state"""" name="kind"/> - <field eval=""""Papers"""" name="name"/> - <field eval=""""Employee get papers and matrieal"""" name="note"/> - <field name="process_id" ref="hr.process_process_employeecontractworkflow0"/> - <field eval="0" name="flow_start"/> - </record> - - <record id="process_transition_papersandmaterials0" model="process.transition"> - <field eval="[(6,0,[])]" name="role_ids"/> - <field eval="[(6,0,[])]" name="transition_ids"/> - <field eval=""""Papers and materials"""" name="name"/> - <field eval=""""Employee get papers and necessary materials"""" name="note"/> - <field model="process.node" name="target_node_id" ref="process_node_papers0"/> - <field model="process.node" name="source_node_id" ref="hr.process_node_employee0"/> - </record> - - </data> -</openerp>
\ No newline at end of file diff --git a/addons/document/security/ir.model.access.csv b/addons/document/security/ir.model.access.csv index dfe882e49d8..889ec8781f6 100644 --- a/addons/document/security/ir.model.access.csv +++ b/addons/document/security/ir.model.access.csv @@ -7,3 +7,5 @@ "access_document_directory_content_group_system","document.directory.content group system","model_document_directory_content","base.group_system",1,1,1,1 "access_document_configuation_wizard","document.configuration.wizard document manager","model_document_configuration_wizard","group_document_manager",1,1,1,1 "access_document_configuation_wizard_sytem","document.configuration.wizard group system","model_document_configuration_wizard","base.group_system",1,1,1,1 +"access_document_directory_content_type_group_document_manager","document.directory.content.type document manager","model_document_directory_content_type","group_document_manager",1,1,1,1 +"access_document_directory_content_type_group_system","document.directory.content.type group system","model_document_directory_content_type","base.group_user",1,0,0,0 |
