a5a890dbc558311e02cf0275dcb88395e9557dd3
[odoo/odoo.git] / addons / email / email_message.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 from osv import osv, fields
23 import time
24 import tools
25 import binascii
26 import email
27 from email.header import decode_header
28 from email.utils import parsedate
29 import base64
30 import re
31 from tools.translate import _
32 import logging
33 import xmlrpclib
34
35 _logger = logging.getLogger('mailgate')
36
37 def format_date_tz(date, tz=None):
38     if not date:
39         return 'n/a'
40     format = tools.DEFAULT_SERVER_DATETIME_FORMAT
41     return tools.server_to_local_timestamp(date, format, format, tz)
42
43 class email_message(osv.osv):
44     '''
45     Email Message
46     '''
47     _name = 'email.message'
48     _description = 'Email Message'
49     _order = 'date desc'
50
51     def open_document(self, cr, uid, ids, context=None):
52         """ To Open Document
53         @param self: The object pointer.
54         @param cr: A database cursor
55         @param uid: ID of the user currently logged in
56         @param ids: the ID of messages
57         @param context: A standard dictionary
58         """
59         action_data = False
60         if ids:
61             message_id = ids[0]
62             mailgate_data = self.browse(cr, uid, message_id, context=context)
63             model = mailgate_data.model
64             res_id = mailgate_data.res_id
65
66             action_pool = self.pool.get('ir.actions.act_window')
67             action_ids = action_pool.search(cr, uid, [('res_model', '=', model)])
68             if action_ids:
69                 action_data = action_pool.read(cr, uid, action_ids[0], context=context)
70                 action_data.update({
71                     'domain' : "[('id','=',%d)]"%(res_id),
72                     'nodestroy': True,
73                     'context': {}
74                     })
75         return action_data
76
77     def open_attachment(self, cr, uid, ids, context=None):
78         """ To Open attachments
79         @param self: The object pointer.
80         @param cr: A database cursor
81         @param uid: ID of the user currently logged in
82         @param ids: the ID of messages
83         @param context: A standard dictionary
84         """
85         action_data = False
86         action_pool = self.pool.get('ir.actions.act_window')
87         message_pool = self.browse(cr ,uid, ids, context=context)[0]
88         att_ids = [x.id for x in message_pool.attachment_ids]
89         action_ids = action_pool.search(cr, uid, [('res_model', '=', 'ir.attachment')])
90         if action_ids:
91             action_data = action_pool.read(cr, uid, action_ids[0], context=context)
92             action_data.update({
93                 'domain': [('id','in',att_ids)],
94                 'nodestroy': True
95                 })
96         return action_data
97
98     def truncate_data(self, cr, uid, data, context=None):
99         data_list = data and data.split('\n') or []
100         if len(data_list) > 3:
101             res = '\n\t'.join(data_list[:3]) + '...'
102         else:
103             res = '\n\t'.join(data_list)
104         return res
105
106     def _get_display_text(self, cr, uid, ids, name, arg, context=None):
107         if context is None:
108             context = {}
109         tz = context.get('tz')
110         result = {}
111         for message in self.browse(cr, uid, ids, context=context):
112             msg_txt = ''
113             if message.history:
114                 msg_txt += (message.email_from or '/') + _(' wrote on ') + format_date_tz(message.date, tz) + ':\n\t'
115                 if message.description:
116                     msg_txt += self.truncate_data(cr, uid, message.description, context=context)
117             else:
118                 msg_txt = (message.user_id.name or '/') + _(' on ') + format_date_tz(message.date, tz) + ':\n\t'
119                 if message.name == _('Opportunity'):
120                     msg_txt += _("Converted to Opportunity")
121                 elif message.name == _('Note'):
122                     msg_txt = (message.user_id.name or '/') + _(' added note on ') + format_date_tz(message.date, tz) + ':\n\t'
123                     msg_txt += self.truncate_data(cr, uid, message.description, context=context)
124                 elif message.name == _('Stage'):
125                     msg_txt += _("Changed Stage to: ") + message.description
126                 else:
127                     msg_txt += _("Changed Status to: ") + message.name
128             result[message.id] = msg_txt
129         return result
130
131     _columns = {
132         'name':fields.text('Subject', readonly=True),
133         'model': fields.char('Object Name', size=128, select=1, readonly=True),
134         'res_id': fields.integer('Resource ID', select=1, readonly=True),
135         'ref_id': fields.char('Reference Id', size=256, readonly=True, help="Message Id in Email Server.", select=True),
136         'date': fields.datetime('Date', readonly=True),
137         'history': fields.boolean('Is History?', readonly=True),
138         'user_id': fields.many2one('res.users', 'User Responsible', readonly=True),
139         'message': fields.text('Description', readonly=True),
140         'email_from': fields.char('From', size=128, help="Email From", readonly=True),
141         'email_to': fields.char('To', help="Email Recipients", size=256, readonly=True),
142         'email_cc': fields.char('Cc', help="Carbon Copy Email Recipients", size=256, readonly=True),
143         'email_bcc': fields.char('Bcc', help='Blind Carbon Copy Email Recipients', size=256, readonly=True),
144         'message_id': fields.char('Message Id', size=1024, readonly=True, help="Message Id on Email.", select=True),
145         'references': fields.text('References', readonly=True, help="References emails."),
146         'description': fields.text('Description', readonly=True),
147         'partner_id': fields.many2one('res.partner', 'Partner', required=False),
148         'attachment_ids': fields.many2many('ir.attachment', 'message_attachment_rel', 'message_id', 'attachment_id', 'Attachments', readonly=True),
149         'display_text': fields.function(_get_display_text, method=True, type='text', size="512", string='Display Text'),
150         'reply_to':fields.char('Reply-To', size=250, readonly=True),
151         'account_id' :fields.many2one('email.smtp_server', 'User account', required=True, readonly=True),
152         #I like GMAIL which allows putting same mail in many folders
153         #Lets plan it for 0.9
154         'folder':fields.selection([
155                         ('drafts', 'Drafts'),
156                         ('outbox', 'Outbox'),
157                         ('trash', 'Trash'),
158                         ('sent', 'Sent Items'),
159                         ], 'Folder', required=True, readonly=True),
160         'state':fields.selection([
161                         ('na', 'Not Applicable'),
162                         ('sending', 'Sending'),
163                         ('wait', 'Waiting'),
164                         ], 'Status', required=True, readonly=True),
165     }
166
167     _defaults = {
168         'state': lambda * a: 'na',
169         'folder': lambda * a: 'outbox',
170     }
171
172     def unlink(self, cr, uid, ids, context=None):
173         """
174         It just changes the folder of the item to "Trash", if it is no in Trash folder yet,
175         or completely deletes it if it is already in Trash.
176         """
177         to_update = []
178         to_remove = []
179         for mail in self.browse(cr, uid, ids, context=context):
180             if mail.folder == 'trash':
181                 to_remove.append(mail.id)
182             else:
183                 to_update.append(mail.id)
184         # Changes the folder to trash
185         self.write(cr, uid, to_update, {'folder': 'trash'}, context=context)
186         return super(email_template_mailbox, self).unlink(cr, uid, to_remove, context=context)
187
188     def init(self, cr):
189         cr.execute("""SELECT indexname
190                       FROM pg_indexes
191                       WHERE indexname = 'email_message_res_id_model_idx'""")
192         if not cr.fetchone():
193             cr.execute("""CREATE INDEX email_message_res_id_model_idx
194                           ON email_message (model, res_id)""")
195
196     def run_mail_scheduler(self, cursor, user, context=None):
197         """
198         This method is called by OpenERP Scheduler
199         to periodically send emails
200         """
201         try:
202             self.send_all_mail(cursor, user, context=context)
203         except Exception, e:
204             LOGGER.notifyChannel(
205                                  "Email Template",
206                                  netsvc.LOG_ERROR,
207                                  _("Error sending mail: %s") % e)
208
209     def send_all_mail(self, cr, uid, ids=None, context=None):
210         if ids is None:
211             ids = []
212         if context is None:
213             context = {}
214         filters = [('folder', '=', 'outbox'), ('state', '!=', 'sending')]
215         if 'filters' in context.keys():
216             for each_filter in context['filters']:
217                 filters.append(each_filter)
218         ids = self.search(cr, uid, filters, context=context)
219         self.write(cr, uid, ids, {'state':'sending'}, context)
220         self.send_this_mail(cr, uid, ids, context)
221         return True
222
223     def send_this_mail(self, cr, uid, ids=None, context=None):
224         #previous method to send email (link with email account can be found at the revision 4172 and below
225         result = True
226         attachment_pool = self.pool.get('ir.attachment')
227         for id in (ids or []):
228             try:
229                 account_obj = self.pool.get('email.smtp_server')
230                 values = self.read(cr, uid, id, [], context)
231                 payload = {}
232                 if values['attachments_ids']:
233                     for attid in values['attachments_ids']:
234                         attachment = attachment_pool.browse(cr, uid, attid, context)#,['datas_fname','datas'])
235                         payload[attachment.datas_fname] = attachment.datas
236                 result = account_obj.send_email(cr, uid,
237                               [values['account_id'][0]],
238                               {'To':values.get('email_to') or u'',
239                                'CC':values.get('email_cc') or u'',
240                                'BCC':values.get('email_bcc') or u'',
241                                'Reply-To':values.get('reply_to') or u''},
242                               values['subject'] or u'',
243                               {'text':values.get('body_text') or u'', 'html':values.get('body_html') or u''},
244                               payload=payload,
245                               message_id=values['message_id'],
246                               context=context)
247                 if result == True:
248                     account = account_obj.browse(cr, uid, values['account_id'][0], context=context)
249                     if account.auto_delete:
250                         self.write(cr, uid, id, {'folder': 'trash'}, context=context)
251                         self.unlink(cr, uid, [id], context=context)
252                         # Remove attachments for this mail
253                         attachment_pool.unlink(cr, uid, values['attachments_ids'], context=context)
254                     else:
255                         self.write(cr, uid, id, {'folder':'sent', 'state':'na', 'date_mail':time.strftime("%Y-%m-%d %H:%M:%S")}, context)
256                 else:
257                     error = result['error_msg']
258
259             except Exception, error:
260                 logger = netsvc.Logger()
261                 logger.notifyChannel("email-template", netsvc.LOG_ERROR, _("Sending of Mail %s failed. Probable Reason:Could not login to server\nError: %s") % (id, error))
262             self.write(cr, uid, id, {'state':'na'}, context)
263         return result
264
265 email_message()
266
267 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: