1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>)
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
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
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/>
20 ##############################################################################
22 from osv import osv, fields
27 from email.header import decode_header
28 from email.utils import parsedate
31 from tools.translate import _
35 _logger = logging.getLogger('mailgate')
37 def format_date_tz(date, tz=None):
40 format = tools.DEFAULT_SERVER_DATETIME_FORMAT
41 return tools.server_to_local_timestamp(date, format, format, tz)
43 class email_message(osv.osv):
47 _name = 'email.message'
48 _description = 'Email Message'
51 def open_document(self, cr, uid, ids, context=None):
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
62 mailgate_data = self.browse(cr, uid, message_id, context=context)
63 model = mailgate_data.model
64 res_id = mailgate_data.res_id
66 action_pool = self.pool.get('ir.actions.act_window')
67 action_ids = action_pool.search(cr, uid, [('res_model', '=', model)])
69 action_data = action_pool.read(cr, uid, action_ids[0], context=context)
71 'domain' : "[('id','=',%d)]"%(res_id),
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
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')])
91 action_data = action_pool.read(cr, uid, action_ids[0], context=context)
93 'domain': [('id','in',att_ids)],
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]) + '...'
103 res = '\n\t'.join(data_list)
106 def _get_display_text(self, cr, uid, ids, name, arg, context=None):
109 tz = context.get('tz')
111 for message in self.browse(cr, uid, ids, context=context):
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)
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
127 msg_txt += _("Changed Status to: ") + message.name
128 result[message.id] = msg_txt
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'),
158 ('sent', 'Sent Items'),
159 ], 'Folder', required=True, readonly=True),
160 'state':fields.selection([
161 ('na', 'Not Applicable'),
162 ('sending', 'Sending'),
164 ], 'Status', required=True, readonly=True),
168 'state': lambda * a: 'na',
169 'folder': lambda * a: 'outbox',
172 def unlink(self, cr, uid, ids, context=None):
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.
179 for mail in self.browse(cr, uid, ids, context=context):
180 if mail.folder == 'trash':
181 to_remove.append(mail.id)
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)
189 cr.execute("""SELECT indexname
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)""")
196 def run_mail_scheduler(self, cursor, user, context=None):
198 This method is called by OpenERP Scheduler
199 to periodically send emails
202 self.send_all_mail(cursor, user, context=context)
204 LOGGER.notifyChannel(
207 _("Error sending mail: %s") % e)
209 def send_all_mail(self, cr, uid, ids=None, context=None):
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)
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
226 attachment_pool = self.pool.get('ir.attachment')
227 for id in (ids or []):
229 account_obj = self.pool.get('email.smtp_server')
230 values = self.read(cr, uid, id, [], context)
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''},
245 message_id=values['message_id'],
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)
255 self.write(cr, uid, id, {'folder':'sent', 'state':'na', 'date_mail':time.strftime("%Y-%m-%d %H:%M:%S")}, context)
257 error = result['error_msg']
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)
267 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: