add wiki module to the addons-wiki
[odoo/odoo.git] / addons / wiki / wiki.py
1 # -*- encoding: utf-8 -*-
2 ##############################################################################
3 #
4 # Copyright (c) 2004-2006 TINY SPRL. (http://axelor.com) All Rights Reserved.
5 #
6 # WARNING: This program as such is intended to be used by professional
7 # programmers who take the whole responsability of assessing all potential
8 # consequences resulting from its eventual inadequacies and bugs
9 # End users who are looking for a ready-to-use solution with commercial
10 # garantees and support are strongly adviced to contract a Free Software
11 # Service Company
12 #
13 # This program is Free Software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; either version 2
16 # of the License, or (at your option) any later version.
17 #
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 # GNU General Public License for more details.
22 #
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
26 #
27 ##############################################################################
28
29 from osv import fields, osv
30 import time
31 from Wiki2Html import Wiki2Html
32 import xmlrpclib
33 import base64
34 import os
35 from StringIO import StringIO
36 import popen2
37
38 class Tag(osv.osv):
39     _name="base.wiki.tag"
40     _description="Wiki"
41     _rec_name="title"
42     _columns={
43        'title':fields.char('Title',size=128),
44     }
45 Tag()
46
47 class Wiki(osv.osv):
48     _name="base.wiki"
49     _description="Wiki"
50     _rec_name="title"
51     _order = 'model_id'
52     _columns={
53        'title':fields.char('Title', size=128, select=True),
54        'last_modify_by':fields.many2one('res.users',"Last Modify By"),
55        'text_area':fields.text("Content", select=True),
56        'wiki_create_uid':fields.many2one('res.users','Authour', select=True),
57        'wiki_create_date':fields.datetime("Created on", select=True),
58        'wiki_write_date':fields.datetime("Last modified", select=True),
59        'tags':fields.many2many("base.wiki.tag","wiki_tag_many_id","wiki_id","tag_id","Tags", select=True),
60        #'forum_id':fields.one2many('base.wiki.forum','wiki_id','Forum Lines'),
61        'history_id':fields.one2many('base.wiki.history','history_wiki_id','History Lines'),
62        'html':fields.text("Html Data", select=True),
63        'path':fields.char('Page Path',size=128),
64        'model_id': fields.many2one('ir.model', 'Model id', select=True, ondelete='cascade'),
65        'res_id': fields.integer("Record Id"),
66        'minor_edit':fields.boolean('Thisd is a minor edit', select=True),
67        'summary':fields.char('Summary',size=256, select=True),
68     }
69     _defaults = {
70         'wiki_create_date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
71         'wiki_create_uid': lambda obj,cr,uid,context: uid,
72     }
73
74     def __init__(self, cr, pool):
75         super(Wiki, self).__init__(cr, pool)
76         self.oldmodel = None
77
78     def read(self, cr, uid, cids, fields=None, context=None, load='_classic_read'):
79         result = super(Wiki, self).read(cr, uid, cids, {}, load='_classic_read')
80
81         if context and context.get('index'):
82             ids = self.search(cr, uid, [('model_id','!=',False), ('id','in',cids)])
83             res = self.read(cr, uid, ids)
84
85             buffer = ''
86             for rs in res:
87                 if self.oldmodel != rs['model_id'][1] or self.oldmodel == None:
88                     self.oldmodel = rs['model_id'][1]
89                     buffer+= '\n==' + self.pool.get('ir.model').browse(cr, uid, rs['model_id'][0]).name + '==\n'
90
91                 rec_url = '/form/view?model=' + rs['model_id'][1] + "&id=" + str(rs['res_id'])
92                 edit_url = '/form/edit?model=' + rs['model_id'][1] + "&id=" + str(rs['res_id'])
93                 buffer+= '#Row\n'
94                 buffer+= '| # | ' + rs['title'] + ' | [' +  rs['path'] + ' -  wiki]' + ' | [' +  rec_url + ' - Browse ] | [' +  edit_url + ' - Edit ]\n'
95
96             ids = self.search(cr, uid, [('model_id','=',False), ('id','in',cids)])
97             res = self.read(cr, uid, ids)
98
99             if res:
100                 buffer+= '\n==Other Pages==\n'
101                 for rs in res:
102
103                     buffer+= '* [' +  rs['path'] + ' - ' + rs['title'] + ']\n'
104
105             return [{'html':self.Wiki2Html(buffer)}]
106         else:
107             return result
108
109     def Wiki2Html(self, wiki):
110         fileName = 'data.txt'
111         file = open(fileName, 'w')
112         file.write(wiki+'\n\n')
113         file.close()
114
115         parser = Wiki2Html()
116         parser.read(fileName)
117         return parser.html
118
119     def create(self, cr, uid, vals, context=None):
120         if vals.get('text_area'):
121             vals['html'] = self.Wiki2Html(vals['text_area'])
122         else:
123             vals['text_area'] = 'Your text goes here'
124             vals['html'] = 'You have not create the page.'
125         if not vals.has_key('minor_edit'):
126             return super(Wiki,self).create(cr, uid, vals, context)
127         vals['history_id']=[[0,0,{'minor_edit':vals['minor_edit'],'text_area':vals['text_area'],'modify_by':uid,'summary':vals['summary']}]]
128         return super(Wiki,self).create(cr, uid, vals, context)
129
130     def write(self, cr, uid, ids, vals, context=None):
131         vals['wiki_write_date']=time.strftime('%Y-%m-%d %H:%M:%S')
132         vals['last_modify_by']=uid
133         wiki_data=self.read(cr,uid,ids,['minor_edit','summary'])[0]
134         if vals.get('text_area'):
135             vals['html'] = self.Wiki2Html(vals['text_area'])
136             if vals.has_key('minor_edit') and vals.has_key('summary'):
137                 vals['history_id']=[[0,0,{'minor_edit':vals['minor_edit'],'text_area':vals['text_area'],'modify_by':uid,'summary':vals['summary']}]]
138             elif vals.has_key('minor_edit'):
139                 vals['history_id']=[[0,0,{'minor_edit':vals['minor_edit'],'text_area':vals['text_area'],'modify_by':uid,'summary':wiki_data['summary']}]]
140             elif vals.has_key('summary'):
141                 vals['history_id']=[[0,0,{'minor_edit':wiki_data['summary'],'text_area':vals['text_area'],'modify_by':uid,'summary':vals['summary']}]]
142             else:
143                 vals['history_id']=[[0,0,{'minor_edit':wiki_data['minor_edit'],'text_area':vals['text_area'],'modify_by':uid,'summary':wiki_data['summary']}]]
144         return super(Wiki,self).write(cr, uid, ids, vals, context)
145 Wiki()
146
147 #class Forum(osv.osv):
148 #    _name="base.wiki.forum"
149 #    _description="Wiki Forum"
150 #    _rec_name="title"
151 #    _columns={
152 #        'title':fields.char('Title',size=128),
153 #        'replies':fields.integer('Replies'),
154 #        'wiki_authour':fields.many2one('res.users','Created By'),
155 #        'last_post':fields.datetime("Last Post",readonly=True),
156 #        'by':fields.many2one('res.users','By',readonly=True),
157 #        'discussion_lines':fields.one2many('base.wiki.discussion','forum_id','Discussion Lines'),
158 #        'wiki_id':fields.many2one('base.wiki','Wiki Id')
159 #    }
160 #Forum()
161 #
162 #class Discussion(osv.osv):
163 #    _name="base.wiki.discussion"
164 #    _description="Wiki Discussion"
165 #    _rec_name="wiki_authour"
166 #    _columns={
167 #        'wiki_authour':fields.many2one('res.users','Authour',readonly=True),
168 #        'posted_date':fields.datetime("Posted Date",readonly=True),
169 #        'message':fields.text('Message'),
170 #        'forum_id':fields.many2one('base.wiki.forum','Forum Id')
171 #    }
172 #Discussion()
173
174 class History(osv.osv):
175     _name="base.wiki.history"
176     _description="Wiki History"
177     _rec_name="date_time"
178     _order = 'id DESC'
179     _columns={
180       'date_time':fields.datetime("Date",select=True),
181       'text_area':fields.text("Text area",select=True),
182       'minor_edit':fields.boolean('This is a major edit ?',select=True),
183       'summary':fields.char('Summary',size=256, select=True),
184       'modify_by':fields.many2one('res.users',"Modify By", select=True),
185       'hist_write_date':fields.datetime("Last modified", select=True),
186       'history_wiki_id':fields.many2one('base.wiki','Wiki Id', select=True)
187     }
188     _defaults = {
189         'hist_write_date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
190         'modify_by': lambda obj,cr,uid,context: uid,
191     }
192     
193     def getDiff(self, cr, uid, v1, v2, context={}):
194         import difflib
195         
196         history_pool = self.pool.get('base.wiki.history')
197         
198         text1 = history_pool.read(cr, uid, [v1], ['text_area'])[0]['text_area']
199         text2 = history_pool.read(cr, uid, [v2], ['text_area'])[0]['text_area']
200         
201         line1 = text1.splitlines(1)
202         line2 = text2.splitlines(1)
203         
204         diff = difflib.HtmlDiff()
205         
206         return diff.make_file(line1, line2, "Revision-%s" % (v1), "Revision-%s" % (v2), context=False)
207     
208 History()
209
210 from StringIO import StringIO
211 from HTMLParser import HTMLParser
212
213 class IndexLine(osv.osv):
214     _name="base.index.line"
215     _description="Index Lines"
216     _columns={
217       'name':fields.text('Content', select=True),
218       'active':fields.boolean('Active', select=True),
219       'model':fields.char('Resource',size=256, select=True),
220       'res_id':fields.integer('Resource Id', select=True),
221     }
222     _defaults = {
223         'active': lambda *a: True,
224     }
225     
226     def __init__(self, cr , pool):
227         super(IndexLine, self).__init__(cr, pool)
228      
229     def reIndex(self, cr, uid, ids, context={}):
230         password = self.pool.get('res.users').browse(cr, uid, uid).password
231         self.server.init(cr.dbname, uid, password, True)
232         self.server.open()
233         
234         self.Index(cr, uid, context)
235         
236         self.server.close()
237         return True
238     
239     def Index(self, cr, uid, ids, context={}):
240         
241         entry_pool = self.pool.get('base.index.line')
242         eids = entry_pool.search(cr, uid, [])
243 #        entry_pool.unlink(cr, uid, eids)
244 #        cr.commit()
245         
246         model_pool = self.pool.get('ir.model')
247         field_pool = self.pool.get('ir.model.fields')
248         
249         ids = model_pool.search(cr, uid, [('model','!=','base.index.line')])
250         models = model_pool.read(cr, uid, ids, ['id', 'model'])
251         
252         for mod in models:
253             res_ids = None
254             
255             if str(mod['model']).startswith('workflow'):
256                 continue
257             
258             if str(mod['model']).startswith('ir'):
259                 continue
260             
261             if str(mod['model']).startswith('base.index.line'):
262                 continue
263             
264             if str(mod['model']).startswith('res.currency'):
265                 continue
266             
267             if str(mod['model']).startswith('base.wiki'):
268                 continue
269             
270             if str(mod['model']).startswith('report'):
271                 continue
272             
273             if str(mod['model']).startswith('account_analytic_analysis.summary.user'):
274                 continue
275             
276             if str(mod['model']).startswith('hr_timesheet_invoice.factor'):
277                 continue
278             
279             try:
280                 fids = field_pool.search(cr, 1, [('model_id','=',mod['id'])])
281                 fdata = field_pool.read(cr, uid, fids, ['name','relation', 'ttype'])
282             except Exception, e:
283                 continue
284 #            
285 #            fkeys = {}
286 #            tkeys = {}
287 #            for fd in fdata:
288 #                fkeys[fd['name']] = fd['relation']
289 #                tkeys[fd['name']] = fd['ttype']
290         
291             res_pool = None
292             res_ids = None
293
294             res_pool = self.pool.get(mod['model'])
295             
296             res_datas = None
297             
298             try:
299                 res_ids = res_pool.search(cr, 1, [])
300                 res_datas = res_pool.read(cr, 1, res_ids)
301                 #cr.execute("select * from %s" % ( mod['model'].replace('.','_')))
302                 #res_datas = cr.dictfetchall()
303             except Exception, e:
304                 print e
305                 continue
306             
307             print 'Indexing : ', mod['model'], ' Auto Status : '
308             
309             if res_datas:
310                 for res_data in res_datas:
311                     
312                     exist_ids = entry_pool.search(cr, uid, [('res_id','=',res_data['id']),('model','=',mod['model'])])
313                     if exist_ids.__len__() > 0:
314                         continue
315
316                     final_res = ''
317 #                    
318 #                    for col in res_data:
319 #                        if fkeys.has_key(col) and fkeys[col] != 'NULL':
320 #                            if tkeys.has_key(col) and tkeys[col] == 'one2many':
321 #                                rel_pool = self.pool.get(fkeys.get(col))
322 #                                try:
323 #                                    foreign_key = res_pool._columns[col]._fields_id
324 #                                except Exception, e:
325 #                                    print e
326 #                                    continue
327 #                                fkids = rel_pool.search(cr, 1, [(foreign_key, '=', res_data['id'])])
328 #                                if fkids.__len__() > 0:
329 #                                    fkdata = rel_pool.read(cr, 1, fkids)
330 #                                    for fkd in fkdata:
331 #                                        final_res += ' '.join(map(str,fkd.values()))
332
333                     final_res += ' '.join(map(str,res_data.values()))
334                     
335                     entry_pool.create(cr, uid, {
336                         'model':mod['model'],
337                         'res_id':res_data['id'],
338                         'name': final_res
339                     })
340                     cr.commit()
341
342         return True
343     
344     def createIndex(self, cr, uid, ids, context={}):
345         return self.Index(cr, uid, ids, context)
346     
347     def getResult(self, cr, uid, key, reSearch=True, context = {}):
348         
349 #        password = self.pool.get('res.users').browse(cr, uid, uid).password
350 #        self.server.init(cr.dbname, uid, password, True)
351         
352         if context and context.get('read_all') and key:
353             try:
354                 #res = self.server.search(key)
355                 ids = self.search(cr, uid, [('name','ilike',key)])
356                 res = self.read(cr, uid, ids, ['model', 'res_id'])
357             except Exception, e:
358                 raise osv.except_osv('Search Error !!', e.faultString)
359             
360             trs = {}
361             for rs in res:
362                 model = rs.get('model')
363                 id = rs.get('res_id')
364                 if trs.has_key(model):
365                     olds = trs[model]
366                     olds.append(id)
367                 else:
368                     trs[model] = [id]
369                     
370             res = trs
371             if res:
372                 buffer = ''
373                 for mod in res.keys():
374                     sbuff = ''
375                     obj_pool = self.pool.get(mod)
376                     header = False
377                     
378                     for id in res[mod]:
379                         name = None
380                         try:
381                             name = obj_pool.name_get(cr, 1, [id])[0][1]
382                             obj_pool.read(cr, uid, [id])
383                             header = True
384                         except Exception, e:
385                             header = False
386                             continue
387
388                         rec_url = '/form/view?model=' + mod + "&id=" + str(id)
389                         edit_url = '/form/edit?model=' + mod + "&id=" + str(id)
390                         sbuff+= '#Row\n'
391                         name = name.replace('/',' ')
392                         name = name.replace('*',' ')
393                         name = name.replace('_',' ')
394                         sbuff+= '| # | ' + name + ' | [' +  mod + '/' + str(id) + ' -  Wiki]' + ' | [' +  rec_url + ' - Browse ] | [' +  edit_url + ' - Edit ]\n'
395                     
396                     if header == True:
397                         mid = self.pool.get('ir.model').search(cr, uid, [('model','=',mod)])[0]
398                         mod_data = self.pool.get('ir.model').browse(cr, uid, mid).name
399                         sbuff = '\n==' + mod_data + '==\n' + sbuff
400                         
401                     buffer += sbuff
402                 html = self.pool.get('base.wiki').Wiki2Html(buffer)
403                 html = html.replace('$s', '<strike>')
404                 html = html.replace('$es', '</strike>')
405                 return [{'html':html}]
406 #            else:
407 #                #TODO: need to create an index on the new records
408 #                if reSearch:
409 #                    self.Index(cr, uid, context)
410 #                    self.getResult(cr, uid, key, False, context)
411
412         return None
413
414 IndexLine()
415
416 class Project(osv.osv):
417     _inherit = 'project.project'
418     _name = 'project.project'
419     _columns = {
420         'wiki':fields.many2one('base.wiki', 'Wiki'),
421         'dwiki':fields.many2one('base.wiki', 'Developer Wiki'),
422     }    
423 Project()