Added achtung images
[odoo/odoo.git] / addons / mail / mail_followers.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2009-today OpenERP SA (<http://www.openerp.com>)
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 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
25
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)
34     """
35     _name = 'mail.followers'
36     _rec_name = 'partner_id'
37     _log_access = False
38     _description = 'Document Followers'
39     _columns = {
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."),
49     }
50
51
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'
57     _log_access = False
58     _description = 'Notifications'
59
60     _columns = {
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),
68     }
69
70     _defaults = {
71         'read': False,
72         'starred': False,
73     }
74
75     def init(self, cr):
76         cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = %s', ('mail_notification_partner_id_read_starred_message_id',))
77         if not cr.fetchone():
78             cr.execute('CREATE INDEX mail_notification_partner_id_read_starred_message_id ON mail_notification (partner_id, read, starred, message_id)')
79
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.
82
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
86         """
87         notify_pids = []
88         for notification in message.notification_ids:
89             if notification.read:
90                 continue
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:
94                 continue
95             # Do not send to partners without email address defined
96             if not partner.email:
97                 continue
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:
100                 continue
101             # Partner does not want to receive any emails or is opt-out
102             if partner.notification_email_send == 'none':
103                 continue
104             # Partner wants to receive only emails and comments
105             if partner.notification_email_send == 'comment' and message.type not in ('email', 'comment'):
106                 continue
107             # Partner wants to receive only emails
108             if partner.notification_email_send == 'email' and message.type != 'email':
109                 continue
110             notify_pids.append(partner.id)
111         return notify_pids
112
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).
116             Format:
117                 <p>--<br />
118                     Administrator
119                 </p>
120                 <div>
121                     <small>Sent by <a ...>Your Company</a> using <a ...>OpenERP</a>.</small> OR
122                     <small>Sent by Administrator using <a ...>OpenERP</a>.</small>
123                 </div>
124         """
125         footer = ""
126         if not user_id:
127             return footer
128
129         # add user signature
130         user = self.pool.get("res.users").browse(cr, SUPERUSER_ID, [user_id], context=context)[0]
131         if user.signature:
132             signature = plaintext2html(user.signature)
133         else:
134             signature = "--<br />%s" % user.name
135         footer = tools.append_content_to_html(footer, signature, plaintext=False, container_tag='p')
136
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)
142         else:
143             company = user.company_id.name
144         sent_by = _('Sent by %(company)s using %(openerp)s.')
145         signature_company = '<small>%s</small>' % (sent_by % {
146                 'company': company,
147                 'openerp': "<a style='color:inherit' href='https://www.openerp.com/'>OpenERP</a>"
148             })
149         footer = tools.append_content_to_html(footer, signature_company, plaintext=False, container_tag='div')
150
151         return footer
152
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
156
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
164         """
165         if context is None:
166             context = {}
167         mail_message_obj = self.pool.get('mail.message')
168
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)
181
182         # mail_notify_noemail (do not send email) or no partner_ids: do not send, return
183         if context.get('mail_notify_noemail'):
184             return True
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:
189             return True
190
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)
194
195         # add signature
196         body_html = msg.body
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
198         if user_signature:
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')
201
202         references = False
203         if msg.parent_id:
204             references = msg.parent_id.message_id
205
206         mail_values = {
207             'mail_message_id': msg.id,
208             'auto_delete': True,
209             'body_html': body_html,
210             'recipient_ids': [(4, id) for id in notify_partner_ids],
211             'references': references,
212         }
213         mail_mail = self.pool.get('mail.mail')
214         email_notif_id = mail_mail.create(cr, uid, mail_values, context=context)
215
216         if force_send:
217             mail_mail.send(cr, uid, [email_notif_id], context=context)
218         return True