1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2009-today OpenERP SA (<http://www.openerp.com>)
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 ##############################################################################
21 from openerp.osv import osv, fields
22 from openerp import tools, SUPERUSER_ID
23 from openerp.tools.translate import _
24 from openerp.tools.mail import plaintext2html
26 class mail_followers(osv.Model):
27 """ mail_followers holds the data related to the follow mechanism inside
28 OpenERP. Partners can choose to follow documents (records) of any kind
29 that inherits from mail.thread. Following documents allow to receive
30 notifications for new messages.
31 A subscription is characterized by:
32 :param: res_model: model of the followed objects
33 :param: res_id: ID of resource (may be 0 for every objects)
35 _name = 'mail.followers'
36 _rec_name = 'partner_id'
38 _description = 'Document Followers'
40 'res_model': fields.char('Related Document Model', size=128,
41 required=True, select=1,
42 help='Model of the followed resource'),
43 'res_id': fields.integer('Related Document ID', select=1,
44 help='Id of the followed resource'),
45 'partner_id': fields.many2one('res.partner', string='Related Partner',
46 ondelete='cascade', required=True, select=1),
47 'subtype_ids': fields.many2many('mail.message.subtype', string='Subtype',
48 help="Message subtypes followed, meaning subtypes that will be pushed onto the user's Wall."),
52 class mail_notification(osv.Model):
53 """ Class holding notifications pushed to partners. Followers and partners
54 added in 'contacts to notify' receive notifications. """
55 _name = 'mail.notification'
56 _rec_name = 'partner_id'
58 _description = 'Notifications'
61 'partner_id': fields.many2one('res.partner', string='Contact',
62 ondelete='cascade', required=True, select=1),
63 'read': fields.boolean('Read', select=1),
64 'starred': fields.boolean('Starred', select=1,
65 help='Starred message that goes into the todo mailbox'),
66 'message_id': fields.many2one('mail.message', string='Message',
67 ondelete='cascade', required=True, select=1),
76 cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = %s', ('mail_notification_partner_id_read_starred_message_id',))
78 cr.execute('CREATE INDEX mail_notification_partner_id_read_starred_message_id ON mail_notification (partner_id, read, starred, message_id)')
80 def get_partners_to_notify(self, cr, uid, message, partners_to_notify=None, context=None):
81 """ Return the list of partners to notify, based on their preferences.
83 :param browse_record message: mail.message to notify
84 :param list partners_to_notify: optional list of partner ids restricting
85 the notifications to process
88 for notification in message.notification_ids:
91 partner = notification.partner_id
92 # If partners_to_notify specified: restrict to them
93 if partners_to_notify is not None and partner.id not in partners_to_notify:
95 # Do not send to partners without email address defined
98 # Do not send to partners having same email address than the author (can cause loops or bounce effect due to messy database)
99 if message.author_id and message.author_id.email == partner.email:
101 # Partner does not want to receive any emails or is opt-out
102 if partner.notification_email_send == 'none':
104 # Partner wants to receive only emails and comments
105 if partner.notification_email_send == 'comment' and message.type not in ('email', 'comment'):
107 # Partner wants to receive only emails
108 if partner.notification_email_send == 'email' and message.type != 'email':
110 notify_pids.append(partner.id)
113 def get_signature_footer(self, cr, uid, user_id, res_model=None, res_id=None, context=None):
114 """ Format a standard footer for notification emails (such as pushed messages
115 notification or invite emails).
121 <small>Sent by <a ...>Your Company</a> using <a ...>OpenERP</a>.</small> OR
122 <small>Sent by Administrator using <a ...>OpenERP</a>.</small>
130 user = self.pool.get("res.users").browse(cr, SUPERUSER_ID, [user_id], context=context)[0]
132 signature = plaintext2html(user.signature)
134 signature = "--<br />%s" % user.name
135 footer = tools.append_content_to_html(footer, signature, plaintext=False, container_tag='p')
137 # add company signature
138 if user.company_id.website:
139 website_url = ('http://%s' % user.company_id.website) if not user.company_id.website.lower().startswith(('http:', 'https:')) \
140 else user.company_id.website
141 company = "<a style='color:inherit' href='%s'>%s</a>" % (website_url, user.company_id.name)
143 company = user.company_id.name
144 sent_by = _('Sent by %(company)s using %(openerp)s.')
145 signature_company = '<small>%s</small>' % (sent_by % {
147 'openerp': "<a style='color:inherit' href='https://www.openerp.com/'>OpenERP</a>"
149 footer = tools.append_content_to_html(footer, signature_company, plaintext=False, container_tag='div')
153 def _notify(self, cr, uid, msg_id, partners_to_notify=None, context=None,
154 force_send=False, user_signature=True):
155 """ Send by email the notification depending on the user preferences
157 :param list partners_to_notify: optional list of partner ids restricting
158 the notifications to process
159 :param bool force_send: if True, the generated mail.mail is
160 immediately sent after being created, as if the scheduler
161 was executed for this message only.
162 :param bool user_signature: if True, the generated mail.mail body is
163 the body of the related mail.message with the author's signature
167 mail_message_obj = self.pool.get('mail.message')
169 # optional list of partners to notify: subscribe them if not already done or update the notification
170 if partners_to_notify:
171 notifications_to_update = []
172 notified_partners = []
173 notif_ids = self.search(cr, SUPERUSER_ID, [('message_id', '=', msg_id), ('partner_id', 'in', partners_to_notify)], context=context)
174 for notification in self.browse(cr, SUPERUSER_ID, notif_ids, context=context):
175 notified_partners.append(notification.partner_id.id)
176 notifications_to_update.append(notification.id)
177 partners_to_notify = filter(lambda item: item not in notified_partners, partners_to_notify)
178 if notifications_to_update:
179 self.write(cr, SUPERUSER_ID, notifications_to_update, {'read': False}, context=context)
180 mail_message_obj.write(cr, uid, msg_id, {'notified_partner_ids': [(4, id) for id in partners_to_notify]}, context=context)
182 # mail_notify_noemail (do not send email) or no partner_ids: do not send, return
183 if context.get('mail_notify_noemail'):
185 # browse as SUPERUSER_ID because of access to res_partner not necessarily allowed
186 msg = self.pool.get('mail.message').browse(cr, SUPERUSER_ID, msg_id, context=context)
187 notify_partner_ids = self.get_partners_to_notify(cr, uid, msg, partners_to_notify=partners_to_notify, context=context)
188 if not notify_partner_ids:
191 # add the context in the email
192 # TDE FIXME: commented, to be improved in a future branch
193 # quote_context = self.pool.get('mail.message').message_quote_context(cr, uid, msg_id, context=context)
197 user_id = msg.author_id and msg.author_id.user_ids and msg.author_id.user_ids[0] and msg.author_id.user_ids[0].id or None
199 signature_company = self.get_signature_footer(cr, uid, user_id, res_model=msg.model, res_id=msg.res_id, context=context)
200 body_html = tools.append_content_to_html(body_html, signature_company, plaintext=False, container_tag='div')
204 references = msg.parent_id.message_id
207 'mail_message_id': msg.id,
209 'body_html': body_html,
210 'recipient_ids': [(4, id) for id in notify_partner_ids],
211 'references': references,
213 mail_mail = self.pool.get('mail.mail')
214 email_notif_id = mail_mail.create(cr, uid, mail_values, context=context)
217 mail_mail.send(cr, uid, [email_notif_id], context=context)