~asteam/asdaily/dhinesh

« back to all changes in this revision

Viewing changes to openerp/addons/mail/mail_followers.py

  • Committer: dhinesh
  • Date: 2014-09-09 07:49:53 UTC
  • Revision ID: p.dhinesh1990escet@gmail.com-20140909074953-mjkzeki2uxryewng
by dhinesh

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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',
 
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
    # Modifying followers change access rights to individual documents. As the
 
53
    # cache may contain accessible/inaccessible data, one has to refresh it.
 
54
    #
 
55
    def create(self, cr, uid, vals, context=None):
 
56
        res = super(mail_followers, self).create(cr, uid, vals, context=context)
 
57
        self.invalidate_cache(cr, uid, context=context)
 
58
        return res
 
59
 
 
60
    def write(self, cr, uid, ids, vals, context=None):
 
61
        res = super(mail_followers, self).write(cr, uid, ids, vals, context=context)
 
62
        self.invalidate_cache(cr, uid, context=context)
 
63
        return res
 
64
 
 
65
    def unlink(self, cr, uid, ids, context=None):
 
66
        res = super(mail_followers, self).unlink(cr, uid, ids, context=context)
 
67
        self.invalidate_cache(cr, uid, context=context)
 
68
        return res
 
69
 
 
70
    _sql_constraints = [('mail_followers_res_partner_res_model_id_uniq','unique(res_model,res_id,partner_id)','Error, a partner cannot follow twice the same object.')]
 
71
 
 
72
class mail_notification(osv.Model):
 
73
    """ Class holding notifications pushed to partners. Followers and partners
 
74
        added in 'contacts to notify' receive notifications. """
 
75
    _name = 'mail.notification'
 
76
    _rec_name = 'partner_id'
 
77
    _log_access = False
 
78
    _description = 'Notifications'
 
79
 
 
80
    _columns = {
 
81
        'partner_id': fields.many2one('res.partner', string='Contact',
 
82
                        ondelete='cascade', required=True, select=1),
 
83
        'is_read': fields.boolean('Read', select=1, oldname='read'),
 
84
        'starred': fields.boolean('Starred', select=1,
 
85
            help='Starred message that goes into the todo mailbox'),
 
86
        'message_id': fields.many2one('mail.message', string='Message',
 
87
                        ondelete='cascade', required=True, select=1),
 
88
    }
 
89
 
 
90
    _defaults = {
 
91
        'is_read': False,
 
92
        'starred': False,
 
93
    }
 
94
 
 
95
    def init(self, cr):
 
96
        cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = %s', ('mail_notification_partner_id_read_starred_message_id',))
 
97
        if not cr.fetchone():
 
98
            cr.execute('CREATE INDEX mail_notification_partner_id_read_starred_message_id ON mail_notification (partner_id, is_read, starred, message_id)')
 
99
 
 
100
    def get_partners_to_email(self, cr, uid, ids, message, context=None):
 
101
        """ Return the list of partners to notify, based on their preferences.
 
102
 
 
103
            :param browse_record message: mail.message to notify
 
104
            :param list partners_to_notify: optional list of partner ids restricting
 
105
                the notifications to process
 
106
        """
 
107
        notify_pids = []
 
108
        for notification in self.browse(cr, uid, ids, context=context):
 
109
            if notification.is_read:
 
110
                continue
 
111
            partner = notification.partner_id
 
112
            # Do not send to partners without email address defined
 
113
            if not partner.email:
 
114
                continue
 
115
            # Do not send to partners having same email address than the author (can cause loops or bounce effect due to messy database)
 
116
            if message.author_id and message.author_id.email == partner.email:
 
117
                continue
 
118
            # Partner does not want to receive any emails or is opt-out
 
119
            if partner.notify_email == 'none':
 
120
                continue
 
121
            notify_pids.append(partner.id)
 
122
        return notify_pids
 
123
 
 
124
    def get_signature_footer(self, cr, uid, user_id, res_model=None, res_id=None, context=None, user_signature=True):
 
125
        """ Format a standard footer for notification emails (such as pushed messages
 
126
            notification or invite emails).
 
127
            Format:
 
128
                <p>--<br />
 
129
                    Administrator
 
130
                </p>
 
131
                <div>
 
132
                    <small>Sent from <a ...>Your Company</a> using <a ...>OpenERP</a>.</small>
 
133
                </div>
 
134
        """
 
135
        footer = ""
 
136
        if not user_id:
 
137
            return footer
 
138
 
 
139
        # add user signature
 
140
        user = self.pool.get("res.users").browse(cr, SUPERUSER_ID, [user_id], context=context)[0]
 
141
        if user_signature:
 
142
            if user.signature:
 
143
                signature = user.signature
 
144
            else:
 
145
                signature = "--<br />%s" % user.name
 
146
            footer = tools.append_content_to_html(footer, signature, plaintext=False)
 
147
 
 
148
        # add company signature
 
149
        if user.company_id.website:
 
150
            website_url = ('http://%s' % user.company_id.website) if not user.company_id.website.lower().startswith(('http:', 'https:')) \
 
151
                else user.company_id.website
 
152
            company = "<a style='color:inherit' href='%s'>%s</a>" % (website_url, user.company_id.name)
 
153
        else:
 
154
            company = user.company_id.name
 
155
        sent_by = _('Sent by %(company)s using %(odoo)s')
 
156
 
 
157
        signature_company = '<br /><small>%s</small>' % (sent_by % {
 
158
            'company': company,
 
159
            'odoo': "<a style='color:inherit' href='https://www.odoo.com/'>Odoo</a>"
 
160
        })
 
161
        footer = tools.append_content_to_html(footer, signature_company, plaintext=False, container_tag='div')
 
162
 
 
163
        return footer
 
164
 
 
165
    def update_message_notification(self, cr, uid, ids, message_id, partner_ids, context=None):
 
166
        existing_pids = set()
 
167
        new_pids = set()
 
168
        new_notif_ids = []
 
169
 
 
170
        for notification in self.browse(cr, uid, ids, context=context):
 
171
            existing_pids.add(notification.partner_id.id)
 
172
 
 
173
        # update existing notifications
 
174
        self.write(cr, uid, ids, {'is_read': False}, context=context)
 
175
 
 
176
        # create new notifications
 
177
        new_pids = set(partner_ids) - existing_pids
 
178
        for new_pid in new_pids:
 
179
            new_notif_ids.append(self.create(cr, uid, {'message_id': message_id, 'partner_id': new_pid, 'is_read': False}, context=context))
 
180
        return new_notif_ids
 
181
 
 
182
    def _notify_email(self, cr, uid, ids, message_id, force_send=False, user_signature=True, context=None):
 
183
        message = self.pool['mail.message'].browse(cr, SUPERUSER_ID, message_id, context=context)
 
184
 
 
185
        # compute partners
 
186
        email_pids = self.get_partners_to_email(cr, uid, ids, message, context=None)
 
187
        if not email_pids:
 
188
            return True
 
189
 
 
190
        # compute email body (signature, company data)
 
191
        body_html = message.body
 
192
        # add user signature except for mail groups, where users are usually adding their own signatures already
 
193
        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
 
194
        signature_company = self.get_signature_footer(cr, uid, user_id, res_model=message.model, res_id=message.res_id, context=context, user_signature=(user_signature and message.model != 'mail.group'))
 
195
        if signature_company:
 
196
            body_html = tools.append_content_to_html(body_html, signature_company, plaintext=False, container_tag='div')
 
197
 
 
198
        # compute email references
 
199
        references = message.parent_id.message_id if message.parent_id else False
 
200
 
 
201
        # custom values
 
202
        custom_values = dict()
 
203
        if message.model and message.res_id and self.pool.get(message.model) and hasattr(self.pool[message.model], 'message_get_email_values'):
 
204
            custom_values = self.pool[message.model].message_get_email_values(cr, uid, message.res_id, message, context=context)
 
205
 
 
206
        # create email values
 
207
        max_recipients = 50
 
208
        chunks = [email_pids[x:x + max_recipients] for x in xrange(0, len(email_pids), max_recipients)]
 
209
        email_ids = []
 
210
        for chunk in chunks:
 
211
            mail_values = {
 
212
                'mail_message_id': message.id,
 
213
                'auto_delete': True,
 
214
                'body_html': body_html,
 
215
                'recipient_ids': [(4, id) for id in chunk],
 
216
                'references': references,
 
217
            }
 
218
            mail_values.update(custom_values)
 
219
            email_ids.append(self.pool.get('mail.mail').create(cr, uid, mail_values, context=context))
 
220
        if force_send and len(chunks) < 2:  # for more than 50 followers, use the queue system
 
221
            self.pool.get('mail.mail').send(cr, uid, email_ids, context=context)
 
222
        return True
 
223
 
 
224
    def _notify(self, cr, uid, message_id, partners_to_notify=None, context=None,
 
225
                force_send=False, user_signature=True):
 
226
        """ Send by email the notification depending on the user preferences
 
227
 
 
228
            :param list partners_to_notify: optional list of partner ids restricting
 
229
                the notifications to process
 
230
            :param bool force_send: if True, the generated mail.mail is
 
231
                immediately sent after being created, as if the scheduler
 
232
                was executed for this message only.
 
233
            :param bool user_signature: if True, the generated mail.mail body is
 
234
                the body of the related mail.message with the author's signature
 
235
        """
 
236
        notif_ids = self.search(cr, SUPERUSER_ID, [('message_id', '=', message_id), ('partner_id', 'in', partners_to_notify)], context=context)
 
237
 
 
238
        # update or create notifications
 
239
        new_notif_ids = self.update_message_notification(cr, SUPERUSER_ID, notif_ids, message_id, partners_to_notify, context=context)
 
240
 
 
241
        # mail_notify_noemail (do not send email) or no partner_ids: do not send, return
 
242
        if context and context.get('mail_notify_noemail'):
 
243
            return True
 
244
 
 
245
        # browse as SUPERUSER_ID because of access to res_partner not necessarily allowed
 
246
        self._notify_email(cr, SUPERUSER_ID, new_notif_ids, message_id, force_send, user_signature, context=context)