b35dd1ed1f2bcd18e2ab42d3800b11d12b54f05a
[odoo/odoo.git] / addons / document / nodes.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #    
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
6 #
7 #    This program is free software: you can redistribute it and/or modify
8 #    it under the terms of the GNU Affero General Public License as
9 #    published by the Free Software Foundation, either version 3 of the
10 #    License, or (at your option) any later version.
11 #
12 #    This program is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU Affero General Public License for more details.
16 #
17 #    You should have received a copy of the GNU Affero General Public License
18 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.     
19 #
20 ##############################################################################
21
22 import base64
23 import StringIO
24 from osv import osv, fields
25 from osv.orm import except_orm
26 import urlparse
27 import pooler
28 from tools.safe_eval import safe_eval
29
30 import os
31 import time
32
33 #
34 # An object that represent an uri
35 #   path: the uri of the object
36 #   content: the Content it belongs to (_print.pdf)
37 #   type: content or collection
38 #       content: objct = res.partner
39 #       collection: object = directory, object2 = res.partner
40 #       file: objct = ir.attachement
41 #   root: if we are at the first directory of a ressource
42 #
43
44 def get_node_context(cr, uid, context):
45     return node_context(cr,uid,context)
46
47 class node_context(object):
48     """ This is the root node, representing access to some particular
49         context """
50     cached_roots = {}
51
52     def __init__(self, cr, uid, context=None):        
53         self.dbname = cr.dbname
54         self.uid = uid
55         self.context = context
56         self._dirobj = pooler.get_pool(cr.dbname).get('document.directory')
57         assert self._dirobj
58         self.rootdir = False #self._dirobj._get_root_directory(cr,uid,context)
59
60     def get_uri(self, cr,  uri):
61         """ Although this fn passes back to doc.dir, it is needed since
62         it is a potential caching point """            
63         (ndir, duri) =  self._dirobj._locate_child(cr,self.uid, self.rootdir,uri, None, self)          
64         while duri:                    
65             ndir = ndir.child(cr, duri[0])            
66             if not ndir:
67                 return False            
68             duri = duri[1:] 
69         return ndir
70
71
72
73
74 class node_class(object):
75     """ this is a superclass for our inodes
76         It is an API for all code that wants to access the document files. 
77         Nodes have attributes which contain usual file properties
78         """
79     our_type = 'baseclass'
80     def __init__(self, path, parent, context):
81         assert isinstance(context,node_context)
82         assert (not parent ) or isinstance(parent,node_class)
83         self.path = path
84         self.context = context
85         self.type=self.our_type
86         self.parent = parent
87         self.mimetype = 'application/octet-stream'
88         self.create_date = None
89         self.write_date = None
90         self.content_length = 0
91         # dynamic context:
92         self.dctx = {}
93         if parent:
94             self.dctx = parent.dctx.copy()
95         self.displayname = 'Object'
96     
97     def full_path(self):
98         """ Return the components of the full path for some
99             node. 
100             The returned list only contains the names of nodes.
101         """
102         if self.parent:
103             s = self.parent.full_path()
104         else:
105             s = []
106         if isinstance(self.path,list):
107             s+=self.path
108         else:
109             s.append(self.path)
110         return s #map(lambda x: '/' +x, s)
111
112     def children(self, cr):
113         print "node_class.children()"
114         return [] #stub
115
116     def child(self,cr, name):
117         print "node_class.child()"
118         return None
119
120     def path_get(self):
121         print "node_class.path_get()"
122         return False
123     
124     def get_data(self,cr):
125         raise TypeError('no data for %s'% self.type)
126
127     def _get_storage(self,cr):
128         raise RuntimeError("no storage for base class")
129
130     def get_etag(self,cr):
131         """ Get a tag, unique per object + modification.
132     
133             see. http://tools.ietf.org/html/rfc2616#section-13.3.3 """
134         return self._get_ttag(cr) + ':' + self._get_wtag(cr)
135
136     def _get_wtag(self,cr):
137         """ Return the modification time as a unique, compact string """
138         if self.write_date:
139             wtime = time.mktime(time.strptime(self.write_date,'%Y-%m-%d %H:%M:%S'))
140         else: wtime = time.time()
141         return str(wtime)
142     
143     def _get_ttag(self,cr):
144         """ Get a unique tag for this type/id of object.
145             Must be overriden, so that each node is uniquely identified.
146         """
147         print "node_class.get_ttag()",self
148         raise RuntimeError("get_etag stub()")
149     
150     def get_dav_props(self, cr):
151         """ If this class has special behaviour for GroupDAV etc, export
152         its capabilities """
153         return {}
154
155     def get_dav_eprop(self,cr,ns,prop):
156         return None
157
158 class node_database(node_class):
159     """ A node representing the database directory
160         
161     """
162     our_type = 'database'
163     def __init__(self, path=[], parent=False, context=None):
164         super(node_database,self).__init__(path, parent, context)
165
166     def children(self,cr):
167         res = self._child_get(cr) + self._file_get(cr)        
168         return res
169
170     def child(self, cr, name):        
171         res = self._child_get(cr,name)
172         if res:
173             return res[0]
174         res = self._file_get(cr,name)
175         if res:
176             return res[0]
177         return None
178
179     def _child_get(self, cr, name=False, parent_id=False, domain=None):
180         dirobj = self.context._dirobj
181         uid = self.context.uid
182         ctx = self.context.context.copy()
183         ctx.update(self.dctx)
184         where = [('parent_id','=',parent_id)]                              
185         if name:
186             where.append(('name','=',name))
187         if not domain:
188             domain = []
189
190         where2 = where + domain + [('type', '=', 'directory')]
191         ids = dirobj.search(cr, uid, where2, context=ctx)
192         res = []        
193         for dirr in dirobj.browse(cr,uid,ids,context=ctx):            
194             res.append(node_dir(dirr.name,self,self.context,dirr))
195         
196         where2 = where + domain + [('type', '=', 'ressource'), ('ressource_parent_type_id','=',False)]
197         ids = dirobj.search(cr, uid, where2, context=ctx)        
198         for dirr in dirobj.browse(cr,uid,ids,context=ctx):                       
199             res.append(node_res_dir(dirr.name,self,self.context,dirr))
200         
201         fil_obj = dirobj.pool.get('ir.attachment')        
202         ids = fil_obj.search(cr,uid,where,context=ctx)
203         if ids:
204             for fil in fil_obj.browse(cr,uid,ids,context=ctx):
205                 res.append(node_file(fil.name,self,self.context,fil))
206         return res
207
208     def _file_get(self,cr, nodename=False, directory_id=False):
209         res = []
210         cntobj = self.context._dirobj.pool.get('document.directory.content')
211         uid = self.context.uid
212         ctx = self.context.context.copy()
213         ctx.update(self.dctx)
214         where = [('directory_id','=',directory_id) ]
215         ids = cntobj.search(cr, uid, where, context=ctx)
216         for content in cntobj.browse(cr, uid, ids, context=ctx):
217             res3 = cntobj._file_get(cr, self, nodename, content)
218             if res3:
219                 res.extend(res3)
220
221         return res
222
223     def _get_ttag(self,cr):
224         return 'db-%s' % cr.dbname
225         
226
227 class node_dir(node_database):
228     our_type = 'collection'
229     def __init__(self,path, parent, context, dirr, dctx=None):
230         super(node_dir,self).__init__(path, parent,context)
231         self.dir_id = dirr and dirr.id or False
232         #todo: more info from dirr
233         self.mimetype = 'application/x-directory'
234             # 'httpd/unix-directory'
235         self.create_date = dirr and dirr.create_date or False
236         self.domain = dirr and dirr.domain or []
237         self.res_model = dirr and dirr.ressource_type_id and dirr.ressource_type_id.model or False
238         # TODO: the write date should be MAX(file.write)..
239         self.write_date = dirr and (dirr.write_date or dirr.create_date) or False
240         self.content_length = 0
241         if dctx:
242             self.dctx.update(dctx)
243         dc2 = self.context.context
244         dc2.update(self.dctx)
245         dc2['dir_id'] = self.dir_id
246         self.displayname = dirr and dirr.name or False
247         if dirr and dirr.dctx_ids:
248             for dfld in dirr.dctx_ids:
249                 try:
250                     self.dctx['dctx_' + dfld.field] = safe_eval(dfld.expr,dc2)
251                 except Exception,e:
252                     print "Cannot eval %s" % dfld.expr
253                     print e
254                     pass
255
256     
257     #def get_data(self,cr):
258     #    return ''
259
260     
261
262     def _file_get(self,cr, nodename=False):
263         return super(node_dir,self)._file_get(cr, nodename, self.dir_id)       
264
265     
266
267     def _child_get(self, cr, name = None):
268         return super(node_dir,self)._child_get(cr, name, self.dir_id)
269
270     def create_child_collection(self, cr, objname):
271         object2 = False            
272         dirobj = self.context._dirobj
273         uid = self.context.uid
274         ctx = self.context.context.copy()
275         ctx.update(self.dctx)
276         obj = dirobj.browse(cr, uid, self.dir_id)            
277         if obj and (obj.type == 'ressource') and not object2:
278             raise OSError(1, 'Operation not permited.')
279
280         #objname = uri2[-1]
281         val = {
282                 'name': objname,
283                 'ressource_parent_type_id': obj and obj.ressource_type_id.id or False,
284                 'ressource_id': object2 and object2.id or False,
285                 'parent_id' : obj and obj.id or False
286         }                 
287         
288         return dirobj.create(cr, uid, val)
289         
290     
291     def create_child(self,cr,path,data):
292         """ API function to create a child file object and node
293             Return the node_* created
294         """
295         dirobj = self.context._dirobj
296         uid = self.context.uid
297         ctx = self.context.context.copy()
298         ctx.update(self.dctx)
299         fil_obj=dirobj.pool.get('ir.attachment')
300         val = {
301             'name': path,
302             'datas_fname': path,
303             'parent_id': self.dir_id,
304             # Datas are not set here
305         }
306
307         fil_id = fil_obj.create(cr,uid, val, context=ctx)
308         fil = fil_obj.browse(cr,uid,fil_id,context=ctx)
309         fnode = node_file(path,self,self.context,fil)
310         fnode.set_data(cr,data,fil)
311         return fnode
312
313     def get_etag(self, cr):
314         """ Get a tag, unique per object + modification.
315
316             see. http://tools.ietf.org/html/rfc2616#section-13.3.3 """
317         return self._get_ttag(cr) + ':' + self._get_wtag(cr)
318
319     def _get_wtag(self, cr):
320         """ Return the modification time as a unique, compact string """
321         if self.write_date:
322             wtime = time.mktime(time.strptime(self.write_date, '%Y-%m-%d %H:%M:%S'))
323         else: wtime = time.time()
324         return str(wtime)    
325     
326     def _get_ttag(self,cr):
327         return 'dir-%d' % self.dir_id
328
329 class node_res_dir(node_class):
330     """ A special sibling to node_dir, which does only contain dynamically
331         created folders foreach resource in the foreign model.
332         All folders should be of type node_res_obj and merely behave like
333         node_dirs (with limited domain).
334     """
335     our_type = 'collection'
336     def __init__(self,path, parent, context, dirr, dctx=None ):
337         super(node_res_dir,self).__init__(path, parent,context)
338         self.dir_id = dirr.id
339         #todo: more info from dirr
340         self.mimetype = 'application/x-directory'
341                         # 'httpd/unix-directory'
342         self.create_date = dirr.create_date
343         # TODO: the write date should be MAX(file.write)..
344         self.write_date = dirr.write_date or dirr.create_date
345         self.content_length = 0
346         self.res_model = dirr.ressource_type_id and dirr.ressource_type_id.model or False
347         self.resm_id = dirr.ressource_id
348         self.namefield = dirr.resource_field or 'name'
349         self.displayname = dirr.name
350         # Important: the domain is evaluated using the *parent* dctx!        
351         self.domain = dirr.domain
352         self.ressource_tree = dirr.ressource_tree
353         # and then, we add our own vars in the dctx:
354         if dctx:
355             self.dctx.update(dctx)
356     
357         # and then, we prepare a dctx dict, for deferred evaluation:
358         self.dctx_dict = {}
359         for dfld in dirr.dctx_ids:
360             self.dctx_dict['dctx_' + dfld.field] = dfld.expr
361
362     def children(self,cr):
363         return self._child_get(cr)
364
365     def child(self,cr, name):        
366         res = self._child_get(cr,name)
367         if res:
368             return res[0]
369         return None
370
371     def _child_get(self,cr,name = None):
372         """ return virtual children of resource, based on the
373             foreign object.
374         
375             Note that many objects use NULL for a name, so we should
376             better call the name_search(),name_get() set of methods
377         """
378         obj = self.context._dirobj.pool.get(self.res_model)
379         if not obj:
380             print "couldn't find model", self.res_model
381             return []
382         dirobj = self.context._dirobj
383         uid = self.context.uid
384         ctx = self.context.context.copy()
385         ctx.update(self.dctx)
386         where = []
387         if self.domain:
388             where += safe_eval(self.domain, self.dctx)
389         if self.resm_id:
390             where.append(('id','=',self.resm_id))
391     
392         if name:
393             where.append((self.namefield,'=',name))
394         
395         # print "Where clause for %s" % self.res_model, where
396         if self.ressource_tree:
397             object2 = False
398             if self.resm_id:
399                 object2 = dirobj.pool.get(self.res_model).browse(cr, uid, self.resm_id) or False                
400             if obj._parent_name in obj.fields_get(cr, uid):                    
401                 where.append((obj._parent_name,'=',object2 and object2.id or False))
402                 
403         resids = obj.search(cr,uid, where, context=ctx)
404         res = []        
405         for bo in obj.browse(cr,uid,resids,context=ctx):
406             if not bo:
407                 continue
408             name = getattr(bo,self.namefield)
409             if not name:
410                 continue
411                 # Yes! we can't do better but skip nameless records.     
412              
413             res.append(node_res_obj(name, self.dir_id, self, self.context, self.res_model, bo))
414         return res
415
416     def _get_ttag(self,cr):
417         return 'rdir-%d' % self.dir_id
418
419 class node_res_obj(node_class):
420     """ A special sibling to node_dir, which does only contain dynamically
421         created folders foreach resource in the foreign model.
422         All folders should be of type node_res_obj and merely behave like
423         node_dirs (with limited domain).
424         """
425     our_type = 'collection'
426     def __init__(self, path, dir_id, parent, context, res_model, res_bo, res_id = None):
427         super(node_res_obj,self).__init__(path, parent,context)
428         assert parent
429         #todo: more info from dirr            
430         self.dir_id = dir_id
431         self.mimetype = 'application/x-directory'
432                         # 'httpd/unix-directory'
433         self.create_date = parent.create_date
434         # TODO: the write date should be MAX(file.write)..
435         self.write_date = parent.write_date
436         self.content_length = 0
437         self.res_model = res_model
438         self.domain = parent.domain
439         self.displayname = path  
440         self.dctx_dict = parent.dctx_dict      
441         if res_bo:            
442             self.res_id = res_bo.id            
443             dc2 = self.context.context
444             dc2.update(self.dctx)
445             dc2['res_model'] = res_model
446             dc2['res_id'] = res_bo.id
447             dc2['this'] = res_bo            
448             for fld,expr in self.dctx_dict.items():
449                 try:
450                     self.dctx[fld] = safe_eval(expr,dc2)
451                 except Exception,e:
452                     print "Cannot eval %s for %s" % (expr, fld)
453                     print e
454                     pass
455         else:
456             self.res_id = res_id
457
458     def children(self,cr):        
459         return self._child_get(cr) + self._file_get(cr)
460
461     def child(self,cr, name):            
462         res = self._child_get(cr,name)                
463         if res:
464             return res[0]
465         res = self._file_get(cr,name)
466         if res:
467             return res[0]
468         return None
469
470     def _file_get(self,cr, nodename=False):
471         res = []
472         cntobj = self.context._dirobj.pool.get('document.directory.content')
473         uid = self.context.uid
474         ctx = self.context.context.copy()
475         ctx.update(self.dctx)
476         where = [('directory_id','=',self.dir_id) ]
477         #if self.domain:
478         #    where.extend(self.domain)
479         # print "res_obj file_get clause", where
480         ids = cntobj.search(cr,uid,where,context=ctx)
481         for content in cntobj.browse(cr,uid,ids,context=ctx):
482             res3 = cntobj._file_get(cr,self,nodename,content, context=ctx)
483             if res3:
484                 res.extend(res3)
485
486         return res
487
488     def get_dav_props(self, cr):
489         res = {}
490         cntobj = self.context._dirobj.pool.get('document.directory.content')
491         uid = self.context.uid
492         ctx = self.context.context.copy()
493         ctx.update(self.dctx)
494         where = [('directory_id','=',self.dir_id) ]
495         ids = cntobj.search(cr,uid,where,context=ctx)
496         for content in cntobj.browse(cr,uid,ids,context=ctx):
497             if content.extension == '.ics': # FIXME: call the content class!
498                 res['http://groupdav.org/'] = ('resourcetype',)
499         return res
500
501     def get_dav_eprop(self,cr,ns,prop):
502         if ns != 'http://groupdav.org/' or prop != 'resourcetype':
503             print "Who asked for %s:%s?" % (ns,prop)
504             return None
505         res = {}
506         cntobj = self.context._dirobj.pool.get('document.directory.content')
507         uid = self.context.uid
508         ctx = self.context.context.copy()
509         ctx.update(self.dctx)
510         where = [('directory_id','=',self.dir_id) ]
511         ids = cntobj.search(cr,uid,where,context=ctx)
512         for content in cntobj.browse(cr,uid,ids,context=ctx):
513             if content.extension == '.ics': # FIXME: call the content class!
514                 return ('vevent-collection','http://groupdav.org/')
515         return None
516
517     def _child_get(self,cr,name = None):        
518         dirobj = self.context._dirobj
519         uid = self.context.uid
520         ctx = self.context.context.copy()
521         ctx.update(self.dctx)
522         directory = dirobj.browse(cr, uid, self.dir_id)
523         obj = dirobj.pool.get(self.res_model)
524         where = []
525         res = []        
526         if name:
527             where.append(('name','=',name))   
528        
529         # Directory Structure display in tree structure
530         if self.res_id and directory.ressource_tree:  
531             where1 = []                 
532             if obj._parent_name in obj.fields_get(cr, uid):                    
533                 where1 = where + [(obj._parent_name, '=', self.res_id)]
534             resids = obj.search(cr,uid, where1, context=ctx)                        
535             for bo in obj.browse(cr,uid,resids,context=ctx):
536                 namefield = directory.resource_field or 'name'
537                 if not bo:
538                     continue
539                 res_name = getattr(bo, namefield)
540                 if not res_name:
541                     continue                
542                 res.append(node_res_obj(res_name, self.dir_id, self, self.context, self.res_model, res_bo = bo))        
543         
544         
545         where2 = where + [('parent_id','=',self.dir_id) ]        
546         ids = dirobj.search(cr, uid, where2, context=ctx) 
547         for dirr in dirobj.browse(cr, uid, ids, context=ctx):
548             if dirr.type == 'directory':
549                 res.append(node_res_obj(dirr.name, dirr.id, self, self.context, self.res_model, res_bo = None, res_id = self.res_id))
550             elif dirr.type == 'ressource':
551                 # child resources can be controlled by properly set dctx
552                 res.append(node_res_dir(dirr.name,self,self.context, dirr, {'active_id': self.res_id}))
553         
554
555        
556         
557         fil_obj = dirobj.pool.get('ir.attachment')
558         where3 = where2  + [('res_model', '=', self.res_model), ('res_id','=',self.res_id)]
559         # print "where clause for dir_obj", where2
560         ids = fil_obj.search(cr, uid, where3, context=ctx)
561         if ids:
562             for fil in fil_obj.browse(cr, uid, ids, context=ctx):
563                 res.append(node_file(fil.name, self, self.context, fil))
564     
565         
566         # Get Child Ressource Directories                
567         if directory.ressource_type_id and directory.ressource_type_id.id:              
568             where4 = where + [('ressource_parent_type_id','=',directory.ressource_type_id.id)]            
569             where5 = where4 + [('ressource_id','=',0)]
570             dirids = dirobj.search(cr,uid, where5)            
571             where5 = where4 + [('ressource_id','=',self.res_id)]
572             dirids = dirids + dirobj.search(cr,uid, where5)            
573             for dirr in dirobj.browse(cr, uid, dirids, context=ctx):
574                 if dirr.type == 'directory' and not dirr.parent_id:
575                     res.append(node_res_obj(dirr.name, dirr.id, self, self.context, self.res_model, res_bo = None, res_id = self.res_id))
576                 if dirr.type == 'ressource':
577                     res.append(node_res_dir(dirr.name, self, self.context, dirr, {'active_id': self.res_id}))                
578         return res
579
580     def create_child_collection(self, cr, objname):
581         dirobj = self.context._dirobj
582         uid = self.context.uid
583         ctx = self.context.context.copy()
584         ctx.update(self.dctx)
585         res_obj = dirobj.pool.get(self.context.context['res_model'])           
586         
587         object2 = res_obj.browse(cr, uid, self.context.context['res_id']) or False            
588         
589         obj = dirobj.browse(cr, uid, self.dir_id)            
590         if obj and (obj.type == 'ressource') and not object2:
591             raise OSError(1, 'Operation not permited.')
592
593         
594         val = {
595                 'name': objname,
596                 'ressource_parent_type_id': obj and obj.ressource_type_id.id or False,
597                 'ressource_id': object2 and object2.id or False,
598                 'parent_id' : False
599         }        
600         if (obj and (obj.type in ('directory'))) or not object2:                
601             val['parent_id'] =  obj and obj.id or False            
602         
603         return dirobj.create(cr, uid, val)
604     
605     def create_child(self,cr,path,data):
606         """ API function to create a child file object and node
607             Return the node_* created
608         """
609         dirobj = self.context._dirobj
610         uid = self.context.uid
611         ctx = self.context.context.copy()
612         ctx.update(self.dctx)
613         fil_obj=dirobj.pool.get('ir.attachment')
614         val = {
615             'name': path,
616             'datas_fname': path,
617             'parent_id': self.dir_id,
618             'res_model': self.res_model,
619             'res_id': self.res_id,
620             # Datas are not set here
621         }
622
623         fil_id = fil_obj.create(cr,uid, val, context=ctx)
624         fil = fil_obj.browse(cr,uid,fil_id,context=ctx)
625         fnode = node_file(path,self,self.context,fil)
626         fnode.set_data(cr,data,fil)
627         return fnode
628
629     def _get_ttag(self,cr):
630         return 'rodir-%d-%d' % (self.dir_id,self.res_id)
631
632 class node_file(node_class):
633     our_type = 'file'
634     def __init__(self,path, parent, context, fil):
635         super(node_file,self).__init__(path, parent,context)
636         self.file_id = fil.id
637         #todo: more info from ir_attachment
638         if fil.file_type and '/' in fil.file_type:
639             self.mimetype = fil.file_type
640         self.create_date = fil.create_date
641         self.write_date = fil.write_date or fil.create_date
642         self.content_length = fil.file_size
643         self.displayname = fil.name
644         
645         # This only propagates the problem to get_data. Better
646         # fix those files to point to the root dir.
647         if fil.parent_id:
648             self.storage_id = fil.parent_id.storage_id.id
649         else:
650             self.storage_id = None    
651
652     def open(self, cr, mode=False):
653         uid = self.context.uid
654         if self.type in ('collection','database'):
655             return False            
656         fobj = self.context._dirobj.pool.get('ir.attachment').browse(cr, uid, self.file_id, context=self.context.context)
657         if fobj.store_method and fobj.store_method== 'fs' :
658             s = StringIO.StringIO(self.get_data(cr, fobj))
659         else:
660             s = StringIO.StringIO(base64.decodestring(fobj.db_datas or ''))
661         s.name = self
662         return s                   
663
664     def fix_ppath(self, cr, fbro):
665         """Sometimes we may init this w/o path, parent.
666         This function fills the missing path from the file browse object
667         
668         Note: this may be an expensive operation, do on demand. However,
669         once caching is in, we might want to do that at init time and keep
670         this object anyway
671         """
672         if self.path or self.parent:
673             return
674         assert fbro
675         uid = self.context.uid
676         
677         dirpath = []
678         if fbro.parent_id:
679             dirobj = self.context._dirobj.pool.get('document.directory')
680             dirpath = dirobj.get_full_path(cr, uid, fbro.parent_id.id, context=self.context.context)
681         if fbro.datas_fname:
682             dirpath.append(fbro.datas_fname)
683         else:
684             dirpath.append(fbro.name)
685         
686         if len(dirpath)>1:
687             self.path = dirpath
688         else:
689             self.path = dirpath[0]   
690     
691     def get_data(self, cr, fil_obj = None):
692         """ Retrieve the data for some file. 
693             fil_obj may optionally be specified, and should be a browse object
694             for the file. This is useful when the caller has already initiated
695             the browse object. """
696         # this is where storage kicks in..
697         stor = self.storage_id
698         if not stor:
699             data_obj = self.context._dirobj.pool.get('ir.model.data')
700             data_id = data_obj._get_id(cr, self.context.uid, 'document', 'storage_db')
701             if data_id:
702                 stor = data_obj.browse(cr, self.context.uid, data_id, context=self.context.context).res_id 
703         assert stor
704         stobj = self.context._dirobj.pool.get('document.storage')
705         return stobj.get_data(cr,self.context.uid,stor, self,self.context.context, fil_obj)
706
707     def get_data_len(self, cr, fil_obj = None):
708         # TODO: verify with the storage object!
709         bin_size = self.context.context.get('bin_size', False)
710         if bin_size and not self.content_length:
711             self.content_length = fil_obj.db_datas
712         return self.content_length
713
714     def set_data(self, cr, data, fil_obj = None):
715         """ Store data at some file. 
716             fil_obj may optionally be specified, and should be a browse object
717             for the file. This is useful when the caller has already initiated
718             the browse object. """
719         # this is where storage kicks in..
720         stor = self.storage_id
721         if not stor:
722             data_obj = self.context._dirobj.pool.get('ir.model.data')
723             data_id = data_obj._get_id(cr, self.context.uid, 'document', 'storage_db')
724             if data_id:
725                 stor = data_obj.browse(cr, self.context.uid, data_id, context=self.context.context).res_id 
726         assert stor
727         stobj = self.context._dirobj.pool.get('document.storage')
728         return stobj.set_data(cr,self.context.uid,stor, self, data, self.context.context, fil_obj)
729
730     def _get_ttag(self,cr):
731         return 'file-%d' % self.file_id
732
733 class node_content(node_class):
734     our_type = 'content'
735     def __init__(self,path, parent, context, cnt, dctx = None, act_id=None):
736         super(node_content,self).__init__(path, parent,context)
737         self.cnt_id = cnt.id
738         self.create_date = False
739         self.write_date = False
740         self.content_length = False
741         self.extension = cnt.extension
742         self.report_id = cnt.report_id and cnt.report_id.id
743         #self.mimetype = cnt.extension.
744         self.displayname = path
745         if dctx:
746            self.dctx.update(dctx)
747         self.act_id = act_id
748
749     def open(self, cr, mode=False):
750         uid = self.context.uid
751         if self.type in ('collection','database'):
752             return False                    
753         pool = self.context._dirobj.pool
754         res = getattr(pool.get('document.directory.content'), 'process_read')(cr, uid, self)
755         res = StringIO.StringIO(res)
756         res.name = self
757         return res     
758     
759     def fill_fields(self,cr,dctx = None):
760         """ Try to read the object and fill missing fields, like mimetype,
761             dates etc.
762             This function must be different from the constructor, because
763             it uses the db cursor.
764         """
765         
766         cr.execute('SELECT DISTINCT mimetype FROM document_directory_content_type WHERE active AND code = %s;',
767                 (self.extension,))
768         res = cr.fetchall()
769         if res and res[0][0]:
770             self.mimetype = res[0][0]
771
772
773     def get_data(self, cr, fil_obj = None):
774         cntobj = self.context._dirobj.pool.get('document.directory.content')
775         ctx = self.context.context.copy()
776         ctx.update(self.dctx)
777         data = cntobj.process_read(cr,self.context.uid,self,ctx)
778         if data:
779             self.content_length = len(data)
780         return data
781
782     def get_data_len(self, cr, fil_obj = None):
783         if not self.content_length:
784             self.get_data(cr,fil_obj)
785         return self.content_length
786
787     def set_data(self, cr, data, fil_obj = None):
788         cntobj = self.context._dirobj.pool.get('document.directory.content')
789         ctx = self.context.context.copy()
790         ctx.update(self.dctx)
791         return cntobj.process_write(cr,self.context.uid,self, data,ctx)
792
793     def _get_ttag(self,cr):
794         return 'cnt-%d%s' % (self.cnt_id,(self.act_id and ('-' + str(self.act_id))) or '')
795
796 class old_class():
797     # the old code, remove..
798     def __init__(self, cr, uid, path, object, object2=False, context={}, content=False, type='collection', root=False):
799         self.cr = cr
800     def _file_get(self, nodename=False):
801         if not self.object:
802             return []
803         pool = pooler.get_pool(self.cr.dbname)
804         fobj = pool.get('ir.attachment')
805         res2 = []
806         where = []
807         if self.object2:
808             where.append( ('res_model','=',self.object2._name) )
809             where.append( ('res_id','=',self.object2.id) )
810         else:
811             where.append( ('parent_id','=',self.object.id) )
812             where.append( ('res_id','=',False) )
813         if nodename:
814             where.append( (fobj._rec_name,'=',nodename) )
815         for content in self.object.content_ids:
816             res3 = content._table._file_get(self,nodename,content)
817             if res3:
818                 res2.extend(res3)
819
820         ids = fobj.search(self.cr, self.uid, where+[ ('parent_id','=',self.object and self.object.id or False) ])
821         if self.object and self.root and (self.object.type=='ressource'):
822             ids += fobj.search(self.cr, self.uid, where+[ ('parent_id','=',False) ])
823         res = fobj.browse(self.cr, self.uid, ids, context=self.context)
824         return map(lambda x: node_class(self.cr, self.uid, self.path+'/'+eval('x.'+fobj._rec_name), x, False, context=self.context, type='file', root=False), res) + res2
825     
826     def get_translation(self,value,lang):
827         # Must go, it works on arbitrary models and could be ambiguous.
828         result = value
829         pool = pooler.get_pool(self.cr.dbname)        
830         translation_ids = pool.get('ir.translation').search(self.cr, self.uid, [('value','=',value),('lang','=',lang),('type','=','model')])
831         if len(translation_ids):
832             tran_id = translation_ids[0]
833             translation = pool.get('ir.translation').read(self.cr, self.uid, tran_id, ['res_id','name'])
834             res_model,field_name = tuple(translation['name'].split(','))  
835             res_id = translation['res_id']        
836             res = pool.get(res_model).read(self.cr, self.uid, res_id, [field_name])
837             if res:
838                 result = res[field_name]
839         return result 
840     
841     def directory_list_for_child(self,nodename,parent=False):
842         pool = pooler.get_pool(self.cr.dbname)
843         where = []
844         if nodename:
845             nodename = self.get_translation(nodename, self.context['lang'])
846             where.append(('name','=',nodename))
847         if (self.object and self.object.type=='directory') or not self.object2:
848             where.append(('parent_id','=',self.object and self.object.id or False))
849         else:
850             where.append(('parent_id','=',False))
851         if self.object:
852             where.append(('ressource_parent_type_id','=',self.object.ressource_type_id.id))
853         else:
854             where.append(('ressource_parent_type_id','=',False))
855
856         ids = pool.get('document.directory').search(self.cr, self.uid, where+[('ressource_id','=',0)])
857         if self.object2:
858             ids += pool.get('document.directory').search(self.cr, self.uid, where+[('ressource_id','=',self.object2.id)])        
859         res = pool.get('document.directory').browse(self.cr, self.uid, ids, self.context)
860         return res
861
862     def _child_get(self, nodename=False):
863         if self.type not in ('collection','database'):
864             return []
865         res = self.directory_list_for_child(nodename)
866         result= map(lambda x: node_class(self.cr, self.uid, self.path+'/'+x.name, x, x.type=='directory' and self.object2 or False, context=self.context, root=self.root), res)
867         if self.type=='database':
868             pool = pooler.get_pool(self.cr.dbname)
869             fobj = pool.get('ir.attachment')
870             vargs = [('parent_id','=',False),('res_id','=',False)]
871             if nodename:
872                 vargs.append((fobj._rec_name,'=',nodename))
873             file_ids=fobj.search(self.cr,self.uid,vargs)
874
875             res = fobj.browse(self.cr, self.uid, file_ids, context=self.context)
876             result +=map(lambda x: node_class(self.cr, self.uid, self.path+'/'+eval('x.'+fobj._rec_name), x, False, context=self.context, type='file', root=self.root), res)
877         if self.type=='collection' and self.object.type=="ressource":
878             where = self.object.domain and eval(self.object.domain, {'active_id':self.root, 'uid':self.uid}) or []
879             pool = pooler.get_pool(self.cr.dbname)
880             obj = pool.get(self.object.ressource_type_id.model)
881             _dirname_field = obj._rec_name
882             if len(obj.fields_get(self.cr, self.uid, ['dirname'])):
883                 _dirname_field = 'dirname'
884
885             name_for = obj._name.split('.')[-1]
886             if nodename  and nodename.find(name_for) == 0  :
887                 id = int(nodename.replace(name_for,''))
888                 where.append(('id','=',id))
889             elif nodename:
890                 if nodename.find('__') :
891                     nodename=nodename.replace('__','/')
892                 for invalid in INVALID_CHARS:
893                     if nodename.find(INVALID_CHARS[invalid]) :
894                         nodename=nodename.replace(INVALID_CHARS[invalid],invalid)
895                 nodename = self.get_translation(nodename, self.context['lang'])
896                 where.append((_dirname_field,'=',nodename))
897
898             if self.object.ressource_tree:
899                 if obj._parent_name in obj.fields_get(self.cr,self.uid):
900                     where.append((obj._parent_name,'=',self.object2 and self.object2.id or False))
901                     ids = obj.search(self.cr, self.uid, where)
902                     res = obj.browse(self.cr, self.uid, ids,self.context)
903                     result+= map(lambda x: node_class(self.cr, self.uid, self.path+'/'+x.name.replace('/','__'), self.object, x, context=self.context, root=x.id), res)
904                     return result
905                 else :
906                     if self.object2:
907                         return result
908             else:
909                 if self.object2:
910                     return result
911
912             
913             ids = obj.search(self.cr, self.uid, where)
914             res = obj.browse(self.cr, self.uid, ids,self.context)
915             for r in res:
916                 if len(obj.fields_get(self.cr, self.uid, [_dirname_field])):
917                     r.name = eval('r.'+_dirname_field)
918                 else:
919                     r.name = False
920                 if not r.name:
921                     r.name = name_for + '%d'%r.id
922                 for invalid in INVALID_CHARS:
923                     if r.name.find(invalid) :
924                         r.name = r.name.replace(invalid,INVALID_CHARS[invalid])
925             result2 = map(lambda x: node_class(self.cr, self.uid, self.path+'/'+x.name.replace('/','__'), self.object, x, context=self.context, root=x.id), res)
926             if result2:
927                 if self.object.ressource_tree:
928                     result += result2
929                 else:
930                     result = result2
931         return result
932
933
934     def path_get(self):
935         path = self.path
936         if self.path[0]=='/':
937             path = self.path[1:]
938         return path