source: main/branches/3D/openPLM/plmapp/mail.py @ 662

Revision 662, 7.1 KB checked in by pcosquer, 9 years ago (diff)

3D branch: merge changes from trunk (rev [661])

Line 
1############################################################################
2# openPLM - open source PLM
3# Copyright 2010 Philippe Joulaud, Pierre Cosquer
4#
5# This file is part of openPLM.
6#
7#    openPLM is free software: you can redistribute it and/or modify
8#    it under the terms of the GNU General Public License as published by
9#    the Free Software Foundation, either version 3 of the License, or
10#    (at your option) any later version.
11#
12#    openPLM 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 General Public License for more details.
16#
17#    You should have received a copy of the GNU General Public License
18#    along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
19#
20# Contact :
21#    Philippe Joulaud : ninoo.fr@gmail.com
22#    Pierre Cosquer : pierre.cosquer@insa-rennes.fr
23################################################################################
24
25"""
26This module contains a function :func:`send_mail` which can be used to notify
27users about a changement in a :class:`.PLMObject`.
28"""
29from collections import Iterable, Mapping
30
31import kjbuckets
32
33from django.conf import settings
34from django.core.mail import EmailMultiAlternatives
35from django.db.models import Model, Q
36from django.template.loader import render_to_string
37from django.db.models.loading import get_model
38from django.contrib.sites.models import Site
39from celery.task import task
40
41from openPLM.plmapp.models import User, DelegationLink, ROLE_OWNER, ROLE_SIGN
42
43
44def get_recipients(obj, roles, users):
45    recipients = set(users)
46    if hasattr(obj, "plmobjectuserlink_plmobject"):
47        manager = obj.plmobjectuserlink_plmobject.order_by()
48        roles_filter = Q()
49
50        for role in roles:
51            if role == ROLE_SIGN:
52                roles_filter |= Q(role__startswith=role)
53            else:
54                roles_filter |= Q(role=role)
55        users = manager.filter(roles_filter).values_list("user", flat=True)
56        recipients.update(users)
57        links = DelegationLink.objects.filter(roles_filter)\
58                        .values_list("delegator", "delegatee")
59        gr = kjbuckets.kjGraph(tuple(links))
60        for u in users:
61            recipients.update(gr.reachable(u).items())
62    elif roles == [ROLE_OWNER]:
63        if hasattr(obj, "owner"):
64            recipients.add(obj.owner_id)
65        elif isinstance(obj, User):
66            recipients.add(obj.id)
67    return recipients
68
69def convert_users(users):
70    if users:
71        r = iter(users).next()
72        if hasattr(r, "id"):
73            users = [x.id for x in users]
74    return users
75
76class CT(object):
77    __slots__ = ("app_label", "module_name", "pk")
78
79    def __init__(self, app_label, module_name, pk):
80        self.app_label = app_label
81        self.module_name = module_name
82        self.pk = pk
83
84    def __getstate__(self):
85        return dict(app_label=self.app_label,
86                    module_name=self.module_name,
87                    pk=self.pk)
88   
89    def __setstate__(self, state):
90        self.app_label = state["app_label"]
91        self.module_name = state["module_name"]
92        self.pk = state["pk"]
93
94    @classmethod
95    def from_object(cls, obj):
96        return cls(obj._meta.app_label, obj._meta.module_name, obj.pk)
97
98    def get_object(self):
99        model_class = get_model(self.app_label, self.module_name)
100        return model_class.objects.get(pk=self.pk)
101
102
103def serialize(obj):
104    if isinstance(obj, basestring):
105        return obj
106    if isinstance(obj, Model):
107        return CT.from_object(obj)
108    elif isinstance(obj, Mapping):
109        new_ctx = {}
110        for key, value in obj.iteritems():
111            new_ctx[key] = serialize(value)
112        return new_ctx
113    elif isinstance(obj, Iterable):
114        return [serialize(o) for o in obj]
115    return obj
116
117def unserialize(obj):
118    if isinstance(obj, basestring):
119        return obj
120    if isinstance(obj, CT):
121        return obj.get_object()
122    elif isinstance(obj, Mapping):
123        new_ctx = {}
124        for key, value in obj.iteritems():
125            new_ctx[key] = unserialize(value)
126        return new_ctx
127    elif isinstance(obj, Iterable):
128        return [unserialize(o) for o in obj]
129    return obj
130
131@task(ignore_result=True)
132def do_send_histories_mail(plmobject, roles, last_action, histories, user, blacklist=(),
133              users=(), template="mails/history"):
134    """
135    Sends a mail to users who have role *role* for *plmobject*.
136
137    :param plmobject: object which was modified
138    :type plmobject: :class:`.PLMObject`
139    :param str roles: list of roles of the users who should be notified
140    :param str last_action: type of modification
141    :param str histories: list of :class:`.AbstractHistory`
142    :param user: user who made the modification
143    :type user: :class:`~django.contrib.auth.models.User`
144    :param blacklist: list of emails whose no mail should be sent (empty by default).
145
146    .. note::
147
148        This function fails silently if it can not send the mail.
149        The mail is sent in a separated thread.
150    """
151    plmobject = unserialize(plmobject)
152    recipients = get_recipients(plmobject, roles, users)
153    if recipients:
154        user = unserialize(user)
155        subject = "[PLM] " + unicode(plmobject)
156        ctx = {
157                "last_action" : last_action,
158                "histories" : histories,
159                "plmobject" : plmobject,
160                "user" : user,
161            }
162        do_send_mail(subject, recipients, ctx, template, blacklist)
163
164@task(ignore_result=True)
165def do_send_mail(subject, recipients, ctx, template, blacklist=()):
166    if recipients:
167        if len(recipients) == 1:
168            email = User.objects.get(id=recipients.pop()).email
169            if not email or email in blacklist:
170                return
171            emails = (email,)
172        else:
173            emails = User.objects.filter(id__in=recipients).exclude(email="")\
174                            .values_list("email", flat=True)
175            emails = set(emails) - set(blacklist)
176            if not emails:
177                return
178        ctx = unserialize(ctx)
179        ctx["site"] = Site.objects.get_current()
180        html_content = render_to_string(template + ".htm", ctx)
181        message = render_to_string(template + ".txt", ctx)
182        msg = EmailMultiAlternatives(subject, message, settings.EMAIL_OPENPLM,
183            emails)
184        msg.attach_alternative(html_content, "text/html")
185        msg.send(fail_silently=True)
186
187def send_mail(subject, recipients, ctx, template, blacklist=()):
188    ctx = serialize(ctx)
189    do_send_mail.delay(subject, convert_users(recipients),
190            ctx, template, blacklist)
191
192def send_histories_mail(plmobject, roles, last_action, histories, user, blacklist=(),
193              users=(), template="mails/history"):
194    if hasattr(plmobject, "object"):
195        plmobject = plmobject.object
196    plmobject = CT.from_object(plmobject)
197    histories = serialize(histories)
198    user = CT.from_object(user)
199    do_send_histories_mail.delay(plmobject, roles, last_action, histories,
200            user, blacklist, convert_users(users), template)
201
202
Note: See TracBrowser for help on using the repository browser.