[MERGE+IMP] crm: utf-8 problems in mailagte
authorHarry (Open ERP) <hmo@tinyerp.com>
Wed, 31 Mar 2010 14:14:42 +0000 (19:44 +0530)
committerHarry (Open ERP) <hmo@tinyerp.com>
Wed, 31 Mar 2010 14:14:42 +0000 (19:44 +0530)
bzr revid: hmo@tinyerp.com-20100331141442-wsdldsd0ckja2c6i

1  2 
addons/crm/crm.py
addons/crm/crm_action_rule.py
addons/crm/crm_mailgate.py
addons/crm/scripts/openerp_mailgate/openerp_mailgate.py
addons/crm/wizard/crm_send_email.py
addons/mail_gateway/mail_gateway.py

@@@ -340,15 -356,12 +340,17 @@@ class crm_case(osv.osv)
                  'section_id': case.section_id.id
              }
              obj = self.pool.get('crm.case.log')
 -            if history and case.description:
 +            if history:
                  obj = self.pool.get('crm.case.history')
                  data['description'] = details or case.description
 -                data['email'] = email or \
 +                data['email_to'] = email or \
++                        (case.section_id and case.section_id.reply_to) or \
 +                        (case.user_id and case.user_id.address_id and \
-                             case.user_id.address_id.email) or False
++                            case.user_id.address_id.email) or tools.config.get('email_from',False)
 +                data['email_from'] = email_from or \
++                        (case.section_id and case.section_id.reply_to) or \
                          (case.user_id and case.user_id.address_id and \
--                            case.user_id.address_id.email) or False
++                            case.user_id.address_id.email) or tools.config.get('email_from',False)
              res = obj.create(cr, uid, data, context)            
          return True
      _history = __history
@@@ -55,11 -55,12 +55,14 @@@ class case(osv.osv)
              if case.section_id.reply_to and case.email_from:
                  src = case.email_from
                  dest = case.section_id.reply_to
-                 body = case.email_last or case.description
+                 body = ""
 -                body = case.email_last or case.description
++                body = case.email_last or case.description               
                  if not destination:
                      src, dest = dest, src
-                     if case.user_id.signature:
-                         body += '\n\n%s' % (case.user_id.signature or '')
+                     if body and case.user_id.signature:
 -                        body += '\n\n%s' % (case.user_id.signature).encode('utf8')
++                        body += '\n\n%s' % (case.user_id.signature)
++
++                body = self.format_body(body)
                  dest = [dest]
  
                  attach_to_send = None
                      src,
                      dest,
                      "Reminder: [%s] %s" % (str(case.id), case.name, ),
--                    self.format_body(body),
++                    body,
                      reply_to=case.section_id.reply_to,
                      openobject_id=str(case.id),
                      attach=attach_to_send
                  )
-                 if flag:
-                     raise osv.except_osv(_('Email!'),("Email Successfully Sent"))
-                 else:
-                     raise osv.except_osv(_('Email Fail!'),("Email is not sent successfully"))
++                self._history(cr, uid, [case], _('Send'), history=True, email=dest, details=body, email_from=src)
+                 #if flag:
+                 #    raise osv.except_osv(_('Email!'),("Email Successfully Sent"))
+                 #else:
+                 #    raise osv.except_osv(_('Email Fail!'),("Email is not sent successfully"))
          return True    
  
      def _check(self, cr, uid, ids=False, context={}):
@@@ -120,6 -121,23 +124,23 @@@ case(
  class base_action_rule(osv.osv):
      _inherit = 'base.action.rule'
      _description = 'Action Rules'
+     def email_send(self, cr, uid, obj, emails, body, emailfrom=tools.config.get('email_from',False), context={}):
+         body = self.format_mail(obj, body)
+         if not emailfrom:
+             if hasattr(obj, 'user_id')  and obj.user_id and obj.user_id.address_id and obj.user_id.address_id.email:
+                 emailfrom = obj.user_id.address_id.email
+             
+         name = '[%d] %s' % (obj.id, tools.ustr(obj.name))
+         emailfrom = tools.ustr(emailfrom)
 -        if obj.section_id and obj.section_id.reply_to:
++        if hasattr(obj, 'section_id') and obj.section_id and obj.section_id.reply_to:
+             reply_to = obj.section_id.reply_to
+         else:
+             reply_to = emailfrom
+         if not emailfrom:
+             raise osv.except_osv(_('Error!'),
+                     _("No E-Mail ID Found for your Company address!"))
+         return tools.email_send(emailfrom, emails, name, body, reply_to=reply_to, openobject_id=str(obj.id))
      
      def do_check(self, cr, uid, action, obj, context={}):
          ok = super(base_action_rule, self).do_check(cr, uid, action, obj, context=context)
          res = super(base_action_rule, self).do_action(cr, uid, action, model_obj, obj, context=context)         
          write = {}
          
--        if action.act_section_id:
++        if hasattr(action, act_section_id) and action.act_section_id:
              obj.section_id = action.act_section_id
              write['section_id'] = action.act_section_id.id        
          
@@@ -34,24 -34,29 +34,28 @@@ from osv.orm import except_or
  
  class crm_cases(osv.osv):
      _name = "crm.case"
--    _inherit = "crm.case"
 -
 -    def _decode_header(self, s):
 -        from email.Header import decode_header
 -        s = decode_header(s)
 -        return ''.join(map(lambda x:x[0].decode(x[1] or 'ascii', 'replace'), s))
++    _inherit = "crm.case"    
  
      def msg_new(self, cr, uid, msg):                
          mailgate_obj = self.pool.get('mail.gateway')
          msg_body = mailgate_obj.msg_body_get(msg)
++        msg_subject = mailgate_obj._decode_header(msg['Subject'])
++        msg_from = mailgate_obj._decode_header(msg['From'])
++        msg_cc = mailgate_obj._decode_header(msg['Cc'])
++        body = self.format_body(msg_body['body'])
          data = {   
-             'name': msg['Subject'],         
-             'email_from': msg['From'],
-             'email_cc': msg['Cc'],            
 -            'name': self._decode_header(msg['Subject']),         
 -            'email_from': self._decode_header(msg['From']),
 -            'email_cc': msg['Cc'] and self._decode_header(msg['Cc']),            
++            'name': msg_subject,         
++            'email_from': msg_from,
++            'email_cc': msg_cc,            
              'user_id': False,
--            'description': msg_body['body'],            
++            'description': body,            
          }
--        res = mailgate_obj.partner_get(cr, uid, msg['From'])
++        res = mailgate_obj.partner_get(cr, uid, msg_from)
          if res:
              data.update(res)
          res = self.create(cr, uid, data)        
          cases = self.browse(cr, uid, [res])       
-         self._history(cr, uid, cases, _('Receive'), history=True, email=msg['From'])
 -        self._history(cr, uid, cases, _('Receive'), history=True, email=self._decode_header(msg['From']))
++        self._history(cr, uid, cases, _('Receive'), history=True, details=body, email_from=msg_from)
          return res
      
      def msg_update(self, cr, uid, ids, msg, data={}, default_act='pending'):
  
          if 'partner' in msg_actions:
              data['email_from'] = msg_actions['partner'][:128]        
--
++        msg_from = self._decode_header(msg['From'])
          res = self.write(cr, uid, select, data)
          cases = self.browse(cr, uid, select)       
--        self._history(cr, uid, cases, _('Receive'), history=True, email=msg['From'])        
++        self._history(cr, uid, cases, _('Receive'), history=True, details=body_data, email_from=msg['From'])        
          getattr(self,act)(cr, uid, select)
          return res
  
@@@ -31,10 -30,17 +31,10 @@@ import binasci
  import time, socket
  
  
 -email_re = re.compile(r"""
 -    ([a-zA-Z][\w\.-]*[a-zA-Z0-9]     # username part
 -    @                                # mandatory @ sign
 -    [a-zA-Z0-9][\w\.-]*              # domain must start with a letter ... Ged> why do we include a 0-9 then?
 -     \.
 -     [a-z]{2,3}                      # TLD
 -    )
 -    """, re.VERBOSE)
 +email_re = re.compile(r"([A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,6})")
  case_re = re.compile(r"\[([0-9]+)\]", re.UNICODE)
  command_re = re.compile("^Set-([a-z]+) *: *(.+)$", re.I + re.UNICODE)
--reference_re = re.compile("<.*-tinycrm-(\\d+)@(.*)>", re.UNICODE)
++reference_re = re.compile("<.*-openobject-(\\d+)@(.*)>", re.UNICODE)
  
  priorities = {
      '1': '1 (Highest)', 
@@@ -157,21 -163,26 +157,37 @@@ class email_parser(object)
              'partner_id': adr[0].get('partner_id', False) and adr[0]['partner_id'][0] or False
          }
  
++    def _to_decode(self, s, charsets):
++       for charset in charsets:
++           if charset:
++               try:
++                   return s.decode(charset)
++               except UnicodeError:  
++                    pass         
++       try:
++           return s.decode('ascii')
++       except UnicodeError:
++           return s 
++
      def _decode_header(self, s):
          from email.Header import decode_header
--        s = decode_header(s)
--        return ''.join(map(lambda x:x[0].decode(x[1] or 'ascii', 'replace'), s))
++        s = decode_header(s)        
++        return ''.join(map(lambda x:self._to_decode(x[0], [x[1]]), s))
  
      def msg_new(self, msg):
          message = self.msg_body_get(msg)
++        msg_subject = self._decode_header(msg['Subject'])
++        msg_from = self._decode_header(msg['From'])
++        msg_cc = self._decode_header(msg['Cc'] or '')
++        
          data = {
--            'name': self._decode_header(msg['Subject']), 
--            'email_from': self._decode_header(msg['From']), 
-             'email_cc': self._decode_header(msg['Cc'] or ''),             
 -            'email_cc': self._decode_header(msg['Cc'] or ''), 
 -            'canal_id': self.canal_id, 
++            'name': msg_subject, 
++            'email_from': msg_from, 
++            'email_cc': msg_cc,             
              'user_id': False, 
              'description': message['body'], 
          }
-         data.update(self.partner_get(self._decode_header(msg['From'])))
 -        try:
 -            data.update(self.partner_get(self._decode_header(msg['From'])))
 -        except Exception, e:
 -            import netsvc
 -            netsvc.Logger().notifyChannel('mailgate', netsvc.LOG_ERROR, "%s" % e)
++        data.update(self.partner_get(msg_from))
  
          try:
              id = self.rpc(self.model, 'create', data)
              if part.get_content_maintype()=='text':
                  buf = part.get_payload(decode=True)
                  if buf:
--                    txt = buf.decode(part.get_charsets()[0] or 'ascii', 'replace')
++                    txt = self._to_decode(buf, part.get_charsets())
                      txt = re.sub("<(\w)>", replace, txt)
                      txt = re.sub("<\/(\w)>", replace, txt)
                  if txt and part.get_content_subtype() == 'plain':
index 45f85cd,0000000..1f1e12d
mode 100644,000000..100644
--- /dev/null
@@@ -1,209 -1,0 +1,215 @@@
 +# -*- coding: utf-8 -*-
 +##############################################################################
 +#
 +#    OpenERP, Open Source Management Solution
 +#    Copyright (C) 2004-2010 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/>.
 +#
 +##############################################################################
 +
 +from osv import osv, fields
 +from tools.translate import _
 +import base64
 +import tools
 +from crm import crm
 +
 +class crm_send_new_email(osv.osv_memory):
 +    """ Sends new email for the case"""
 +    _name = "crm.send.mail"
 +    _description = "Case Send new email"
 +
 +    _columns = {
 +                'email_to' : fields.char('To', size=64, required=True),
 +                'email_from' : fields.char('From', size=64, required=True),
 +                'email_cc' : fields.char('CC', size=128),
 +                'subject': fields.char('Subject', size=128, required=True),
 +                'text': fields.text('Message', required=True),
 +                'state': fields.selection(crm.AVAILABLE_STATES, string='State'),
 +                'doc1': fields.binary("Attachment1"),
 +                'doc2': fields.binary("Attachment2"),
 +                'doc3': fields.binary("Attachment3"),
 +                }
 +
 +    def action_cancel(self, cr, uid, ids, context=None):
 +        """ Closes Phonecall to Opportunity form
 +        """
 +        return {'type':'ir.actions.act_window_close'}
 +
 +    def action_send(self, cr, uid, ids, context=None):
 +        """ This sends an email to ALL the addresses of the selected partners.
 +        """
 +        if not context:
 +            context = {}
 +
 +        if not context.get('model'):
 +            raise osv.except_osv(_('Error'), _('Can not send mail!'))
 +
 +        model = context.get('model')
 +        case_pool = self.pool.get(model)
 +        res_id = context and context.get('active_id', False) or False
 +
 +        for data in self.read(cr, uid, ids, context=context):
 +            attach = filter(lambda x: x, [data['doc1'], data['doc2'], data['doc3']])
 +            attach = map(lambda x: x and ('Attachment'+str(attach.index(x)+1), base64.decodestring(x)), attach)
 +
 +            if context.get('mail', 'new') == 'new':
 +                case = case_pool.browse(cr, uid, res_id)
 +            else:
 +                hist_obj = self.pool.get('crm.case.history')
 +                hist = hist_obj.browse(cr, uid, res_id)
 +                model = hist.log_id.model_id.model
 +                model_pool = self.pool.get(model)
 +                case = model_pool.browse(cr, uid, hist.log_id.res_id)
 +            emails = [data['email_to']] + (data['email_cc'] or '').split(',')
 +            emails = filter(None, emails)
 +            body = data['text']
 +
 +            if case.user_id.signature:
 +                body += '\n\n%s' % (case.user_id.signature)
-             case_pool._history(cr, uid, [case], _('Send'), history=True, email=data['email_to'], details=body)
++            body = case_pool.format_body(body)
 +            email_from = data.get('email_from', False)
++            case_pool._history(cr, uid, [case], _('Send'), history=True, email=data['email_to'], details=body, email_from=email_from)
++            
 +            flag = tools.email_send(
 +                email_from,
 +                emails,
 +                data['subject'],
-                 case_pool.format_body(body),
++                body,
 +                attach=attach,
 +                reply_to=case.section_id.reply_to,
 +                openobject_id=str(case.id),
 +            )
 +            if flag:                
 +                if data['state'] == 'unchanged':
 +                    pass
 +                elif data['state'] == 'done':
 +                    case_pool.case_close(cr, uid, [case.id])
 +                elif data['state'] == 'draft':
 +                    case_pool.case_reset(cr, uid, [case.id])                
 +                elif data['state'] in ['cancel', 'open', 'pending']:
 +                    act = 'case_' + data['state']
 +                    getattr(case_pool, act)(cr, uid, [case.id])
 +                cr.commit()
 +
 +#            Commented because form does not close due to raise
 +#                raise osv.except_osv(_('Email!'), ("Email Successfully Sent"))
 +#            else:
 +#                raise osv.except_osv(_('Warning!'), _("Email not sent !"))
++
 +        return {}
 +
 +    def default_get(self, cr, uid, fields, context=None):
 +        """
 +        This function gets default values
 +        """
 +        if not context:
 +            context = {}
 +
 +        if not context.get('model'):
 +            raise osv.except_osv(_('Error'), _('Can not send mail!'))
 +
 +        res = super(crm_send_new_email, self).default_get(cr, uid, fields, context=context)
 +
 +        if context.get('mail') == 'reply':
 +            res.update(self.get_reply_defaults(cr, uid, fields, context=context))
 +            return res
 +
 +        model = context.get('model')
 +        mod_obj = self.pool.get(model)
 +        res_id = context and context.get('active_ids', []) or []
 +
 +        for case in mod_obj.browse(cr, uid, res_id):
 +            if 'email_to' in fields:
 +                res.update({'email_to': case.email_from})
 +            if 'email_from' in fields:
-                 res.update({'email_from': (case.user_id and case.user_id.address_id and case.user_id.address_id.email) or tools.config.get('email_from',False)})
++                res.update({'email_from': (case.section_id and case.section_id.reply_to) or \
++                            (case.user_id and case.user_id.address_id and \
++                            case.user_id.address_id.email) or tools.config.get('email_from',False)})
 +            if 'subject' in fields:
 +                res.update({'subject': '[%s] %s' %(str(case.id), case.name or '')}) 
 +            if 'email_cc' in fields:
 +                res.update({'email_cc': case.email_cc or ''})
 +            if 'text' in fields:
 +                res.update({'text': case.description or ''})
 +            if 'state' in fields:
 +                res.update({'state': 'pending'})
 +        return res
 +
 +    def get_reply_defaults(self, cr, uid, fields, context=None):
 +        """
 +        This function gets default values for reply mail
 +        """
 +        hist_obj = self.pool.get('crm.case.history')
 +        res_ids = context and context.get('active_ids', []) or []
 +        res = {}
 +        for hist in hist_obj.browse(cr, uid, res_ids):
 +            model = hist.log_id.model_id.model
 +            model_pool = self.pool.get(model)
 +            case = model_pool.browse(cr, uid, hist.log_id.res_id)
 +            if 'email_to' in fields:
-                 res['email_to']=case.email_from or hist.email_from or ''
++                res.update({'email_to': case.email_from or hist.email_from or False})
 +            if 'email_from' in fields:
-                 res['email_from']=(case.user_id and case.user_id.address_id and case.user_id.address_id.email) or hist.email_to  or tools.config.get('email_from','')
++                res.update({'email_from': (case.section_id and case.section_id.reply_to) or \
++                            (case.user_id and case.user_id.address_id and \
++                            case.user_id.address_id.email) or hist.email_to or tools.config.get('email_from',False)})
 +            if 'text' in fields:
-                 header = '\n\n-------- Original Message --------'
-                 sender = 'From: %s' % (hist.email_from or '')
++                header = '-------- Original Message --------'                
++                sender = 'From: %s' %(hist.email_from or '')                
 +                to = 'To: %s' % (hist.email_to or '')
 +                sentdate = 'Date: %s' % (hist.date)
 +                desc = '\n%s'%(hist.description)
 +                original = [header, sender, to, sentdate, desc]
 +                original = '\n'.join(original)
 +                res['text']=original
 +            if 'subject' in fields:
 +                res.update({'subject': '[%s] %s' %(str(case.id), case.name or '')}) 
 +            if 'state' in fields:
 +                res['state']='pending'
 +        return res
 +
 +    def view_init(self, cr, uid, fields_list, context=None):
 +        """
 +        This function checks for precondition before wizard executes
 +        @param self: The object pointer
 +        @param cr: the current row, from the database cursor,
 +        @param uid: the current user’s ID for security checks,
 +        @param fields: List of fields for default value
 +        @param context: A standard dictionary for contextual values
 +
 +        """
 +        if not context:
 +            context = {}
 +
 +        if not context.get('model'):
 +            raise osv.except_osv(_('Error'), _('Can not send mail!'))
 +        model = context.get('model')
 +        mod_obj = self.pool.get(model)
 +        if context.get('mail') == 'reply':
 +            return True
 +        if tools.config.get('email_from'):
 +            return True
 +
 +        for case in mod_obj.browse(cr, uid, context.get('active_ids', [])):
 +            if not case.user_id:
 +                raise osv.except_osv(_('Error'), _('You must define a responsible user for this case in order to use this action!'))
 +            if not case.user_id.address_id.email:
 +                raise osv.except_osv(_('Warning!'), _("Please specify user's email address !"))
 +
 +        return True
 +
 +crm_send_new_email()
 +
 +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@@ -210,10 -210,10 +210,22 @@@ class mail_gateway(osv.osv)
              }
          return res
  
--    def _decode_header(self, s):
++    def _to_decode(self, s, charsets):
++       for charset in charsets:
++           if charset:
++               try:
++                   return s.decode(charset)
++               except UnicodeError:  
++                    pass         
++       try:
++           return s.decode('ascii')
++       except UnicodeError:
++           return s 
++
++    def _decode_header(self, s):        
          from email.Header import decode_header
          s = decode_header(s)
--        return ''.join(map(lambda x:x[0].decode(x[1] or 'ascii', 'replace'), s))
++        return ''.join(map(lambda x:self._to_decode(x[0], x[1]), s))
  
      def msg_new(self, cr, uid, msg, model):
          message = self.msg_body_get(msg)
              if part.get_content_maintype()=='text':
                  buf = part.get_payload(decode=True)
                  if buf:
--                    txt = buf.decode(part.get_charsets()[0] or 'ascii', 'replace')
++                    txt = self._to_decode(buf, part.get_charsets)
                      txt = re.sub("<(\w)>", replace, txt)
                      txt = re.sub("<\/(\w)>", replace, txt)
                  if txt and part.get_content_subtype() == 'plain':