# along with this program. If not, see <http://www.gnu.org/licenses/>
#
##############################################################################
-
-from openerp import SUPERUSER_ID
-from openerp.osv import osv
-from openerp.osv import fields
-from openerp import tools
-
+from openerp.osv import osv, fields
+from openerp import tools, SUPERUSER_ID
+from openerp.tools.translate import _
+from openerp.tools.mail import plaintext2html
class mail_followers(osv.Model):
""" mail_followers holds the data related to the follow mechanism inside
'partner_id': fields.many2one('res.partner', string='Contact',
ondelete='cascade', required=True, select=1),
'read': fields.boolean('Read', select=1),
+ 'starred': fields.boolean('Starred', select=1,
+ help='Starred message that goes into the todo mailbox'),
'message_id': fields.many2one('mail.message', string='Message',
ondelete='cascade', required=True, select=1),
}
_defaults = {
'read': False,
+ 'starred': False,
}
def init(self, cr):
- cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = %s', ('mail_notification_partner_id_read_message_id',))
+ cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = %s', ('mail_notification_partner_id_read_starred_message_id',))
if not cr.fetchone():
- cr.execute('CREATE INDEX mail_notification_partner_id_read_message_id ON mail_notification (partner_id, read, message_id)')
-
- def create(self, cr, uid, vals, context=None):
- """ Override of create to check that we can not create a notification
- for a message the user can not read. """
- if self.pool.get('mail.message').check_access_rights(cr, uid, 'read'):
- return super(mail_notification, self).create(cr, uid, vals, context=context)
- return False
-
- def set_message_read(self, cr, uid, msg_ids, read=None, context=None):
- """ Set messages as (un)read. Technically, the notifications related
- to uid are set to (un)read. If for some msg_ids there are missing
- notifications (i.e. due to load more or thread parent fetching),
- they are created.
-
- :param bool read: (un)read notification
- """
- user_pid = self.pool.get('res.users').read(cr, uid, uid, ['partner_id'], context=context)['partner_id'][0]
- notif_ids = self.search(cr, uid, [
- ('partner_id', '=', user_pid),
- ('message_id', 'in', msg_ids)
- ], context=context)
-
- # all message have notifications: already set them as (un)read
- if len(notif_ids) == len(msg_ids):
- return self.write(cr, uid, notif_ids, {'read': read}, context=context)
-
- # some messages do not have notifications: find which one, create notification, update read status
- notified_msg_ids = [notification.message_id.id for notification in self.browse(cr, uid, notif_ids, context=context)]
- to_create_msg_ids = list(set(msg_ids) - set(notified_msg_ids))
- for msg_id in to_create_msg_ids:
- self.create(cr, uid, {'partner_id': user_pid, 'read': read, 'message_id': msg_id}, context=context)
- return self.write(cr, uid, notif_ids, {'read': read}, context=context)
-
- def get_partners_to_notify(self, cr, uid, message, context=None):
+ cr.execute('CREATE INDEX mail_notification_partner_id_read_starred_message_id ON mail_notification (partner_id, read, starred, message_id)')
+
+ def get_partners_to_email(self, cr, uid, ids, message, context=None):
""" Return the list of partners to notify, based on their preferences.
:param browse_record message: mail.message to notify
+ :param list partners_to_notify: optional list of partner ids restricting
+ the notifications to process
"""
notify_pids = []
- for notification in message.notification_ids:
+ for notification in self.browse(cr, uid, ids, context=context):
if notification.read:
continue
partner = notification.partner_id
- # Do not send an email to the writer
- if partner.user_ids and partner.user_ids[0].id == uid:
- continue
# Do not send to partners without email address defined
if not partner.email:
continue
- # Partner does not want to receive any emails
+ # Do not send to partners having same email address than the author (can cause loops or bounce effect due to messy database)
+ if message.author_id and message.author_id.email == partner.email:
+ continue
+ # Partner does not want to receive any emails or is opt-out
if partner.notification_email_send == 'none':
continue
# Partner wants to receive only emails and comments
notify_pids.append(partner.id)
return notify_pids
- def _notify(self, cr, uid, msg_id, context=None):
- """ Send by email the notification depending on the user preferences """
- if context is None:
- context = {}
- # mail_noemail (do not send email) or no partner_ids: do not send, return
- if context.get('mail_noemail'):
- return True
- msg = self.pool.get('mail.message').browse(cr, uid, msg_id, context=context)
-
- notify_partner_ids = self.get_partners_to_notify(cr, uid, msg, context=context)
- if not notify_partner_ids:
+ def get_signature_footer(self, cr, uid, user_id, res_model=None, res_id=None, context=None):
+ """ Format a standard footer for notification emails (such as pushed messages
+ notification or invite emails).
+ Format:
+ <p>--<br />
+ Administrator
+ </p>
+ <div>
+ <small>Sent by <a ...>Your Company</a> using <a ...>OpenERP</a>.</small> OR
+ <small>Sent by Administrator using <a ...>OpenERP</a>.</small>
+ </div>
+ """
+ footer = ""
+ if not user_id:
+ return footer
+
+ # add user signature
+ user = self.pool.get("res.users").browse(cr, SUPERUSER_ID, [user_id], context=context)[0]
+ if user.signature:
+ signature = plaintext2html(user.signature)
+ else:
+ signature = "--<br />%s" % user.name
+ footer = tools.append_content_to_html(footer, signature, plaintext=False, container_tag='p')
+
+ # add company signature
+ if user.company_id.website:
+ website_url = ('http://%s' % user.company_id.website) if not user.company_id.website.lower().startswith(('http:', 'https:')) \
+ else user.company_id.website
+ company = "<a style='color:inherit' href='%s'>%s</a>" % (website_url, user.company_id.name)
+ else:
+ company = user.company_id.name
+ sent_by = _('Sent by %(company)s using %(openerp)s.')
+ signature_company = '<small>%s</small>' % (sent_by % {
+ 'company': company,
+ 'openerp': "<a style='color:inherit' href='https://www.openerp.com/'>OpenERP</a>"
+ })
+ footer = tools.append_content_to_html(footer, signature_company, plaintext=False, container_tag='div')
+
+ return footer
+
+ def update_message_notification(self, cr, uid, ids, message_id, partner_ids, context=None):
+ existing_pids = set()
+ new_pids = set()
+ new_notif_ids = []
+
+ for notification in self.browse(cr, uid, ids, context=context):
+ existing_pids.add(notification.partner_id.id)
+
+ # update existing notifications
+ self.write(cr, uid, ids, {'read': False}, context=context)
+
+ # create new notifications
+ new_pids = set(partner_ids) - existing_pids
+ for new_pid in new_pids:
+ new_notif_ids.append(self.create(cr, uid, {'message_id': message_id, 'partner_id': new_pid, 'read': False}, context=context))
+ return new_notif_ids
+
+ def _notify_email(self, cr, uid, ids, message_id, force_send=False, user_signature=True, context=None):
+ message = self.pool['mail.message'].browse(cr, SUPERUSER_ID, message_id, context=context)
+
+ # compute partners
+ email_pids = self.get_partners_to_email(cr, uid, ids, message, context=None)
+ if not email_pids:
return True
- # add the context in the email
- # TDE FIXME: commented, to be improved in a future branch
- # quote_context = self.pool.get('mail.message').message_quote_context(cr, uid, msg_id, context=context)
+ # compute email body (signature, company data)
+ body_html = message.body
+ user_id = message.author_id and message.author_id.user_ids and message.author_id.user_ids[0] and message.author_id.user_ids[0].id or None
+ if user_signature:
+ signature_company = self.get_signature_footer(cr, uid, user_id, res_model=message.model, res_id=message.res_id, context=context)
+ body_html = tools.append_content_to_html(body_html, signature_company, plaintext=False, container_tag='div')
- mail_mail = self.pool.get('mail.mail')
- # add signature
- body_html = msg.body
- # if quote_context:
- # body_html = tools.append_content_to_html(body_html, quote_context, plaintext=False)
- signature = msg.author_id and msg.author_id.user_ids and msg.author_id.user_ids[0].signature or ''
- if signature:
- body_html = tools.append_content_to_html(body_html, signature, plaintext=True, container_tag='div')
+ # compute email references
+ references = message.parent_id.message_id if message.parent_id else False
+ # create email values
mail_values = {
- 'mail_message_id': msg.id,
- 'email_to': [],
+ 'mail_message_id': message.id,
'auto_delete': True,
'body_html': body_html,
- 'state': 'outgoing',
+ 'recipient_ids': [(4, id) for id in email_pids],
+ 'references': references,
}
- mail_values['email_to'] = ', '.join(mail_values['email_to'])
- email_notif_id = mail_mail.create(cr, uid, mail_values, context=context)
- return mail_mail.send(cr, uid, [email_notif_id], recipient_ids=notify_partner_ids, context=context)
+ email_notif_id = self.pool.get('mail.mail').create(cr, uid, mail_values, context=context)
+ if force_send:
+ self.pool.get('mail.mail').send(cr, uid, [email_notif_id], context=context)
+ return True
+
+ def _notify(self, cr, uid, message_id, partners_to_notify=None, context=None,
+ force_send=False, user_signature=True):
+ """ Send by email the notification depending on the user preferences
+
+ :param list partners_to_notify: optional list of partner ids restricting
+ the notifications to process
+ :param bool force_send: if True, the generated mail.mail is
+ immediately sent after being created, as if the scheduler
+ was executed for this message only.
+ :param bool user_signature: if True, the generated mail.mail body is
+ the body of the related mail.message with the author's signature
+ """
+ notif_ids = self.search(cr, SUPERUSER_ID, [('message_id', '=', message_id), ('partner_id', 'in', partners_to_notify)], context=context)
+
+ # update or create notifications
+ new_notif_ids = self.update_message_notification(cr, SUPERUSER_ID, notif_ids, message_id, partners_to_notify, context=context)
+
+ # mail_notify_noemail (do not send email) or no partner_ids: do not send, return
+ if context and context.get('mail_notify_noemail'):
+ return True
+
+ # browse as SUPERUSER_ID because of access to res_partner not necessarily allowed
+ self._notify_email(cr, SUPERUSER_ID, new_notif_ids, message_id, force_send, user_signature, context=context)