[MERGE] Merged OpenChatter part3: The Return of the Composer Reloaded Strikes Back...
[odoo/odoo.git] / addons / mail / mail_group.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2010-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
22 import datetime as DT
23 import io
24 import openerp
25 import openerp.tools as tools
26 from operator import itemgetter
27 from osv import osv
28 from osv import fields
29 from PIL import Image
30 import StringIO
31 import tools
32 from tools.translate import _
33
34 class mail_group(osv.osv):
35     """
36     A mail_group is a collection of users sharing messages in a discussion
37     group. Group users are users that follow the mail group, using the
38     subscription/follow mechanism of OpenSocial. A mail group has nothing
39     in common with res.users.group.
40     Additional information on fields:
41         - ``member_ids``: user member of the groups are calculated with
42           ``message_get_subscribers`` method from mail.thread
43         - ``member_count``: calculated with member_ids
44         - ``is_subscriber``: calculated with member_ids
45         
46     """
47     
48     _description = 'Discussion group'
49     _name = 'mail.group'
50     _inherit = ['mail.thread']
51
52     def onchange_photo(self, cr, uid, ids, value, context=None):
53         if not value:
54             return {'value': {'avatar_big': value, 'avatar': value} }
55         return {'value': {'photo_big': value, 'photo': self._photo_resize(cr, uid, value) } }
56     
57     def _set_photo(self, cr, uid, id, name, value, args, context=None):
58         if value:
59             return self.write(cr, uid, [id], {'photo_big': value}, context=context)
60         else:
61             return self.write(cr, uid, [id], {'photo_big': value}, context=context)
62     
63     def _photo_resize(self, cr, uid, photo, width=128, height=128, context=None):
64         image_stream = io.BytesIO(photo.decode('base64'))
65         img = Image.open(image_stream)
66         img.thumbnail((width, height), Image.ANTIALIAS)
67         img_stream = StringIO.StringIO()
68         img.save(img_stream, "JPEG")
69         return img_stream.getvalue().encode('base64')
70         
71     def _get_photo(self, cr, uid, ids, name, args, context=None):
72         result = dict.fromkeys(ids, False)
73         for group in self.browse(cr, uid, ids, context=context):
74             if group.photo_big:
75                 result[group.id] = self._photo_resize(cr, uid, group.photo_big, context=context)
76         return result
77     
78     def get_member_ids(self, cr, uid, ids, field_names, args, context=None):
79         if context is None:
80             context = {}
81         result = dict.fromkeys(ids)
82         for id in ids:
83             result[id] = {}
84             result[id]['member_ids'] = self.message_get_subscribers(cr, uid, [id], context=context)
85             result[id]['member_count'] = len(result[id]['member_ids'])
86             result[id]['is_subscriber'] = uid in result[id]['member_ids']
87         return result
88     
89     def search_member_ids(self, cr, uid, obj, name, args, context=None):
90         if context is None:
91             context = {}
92         sub_obj = self.pool.get('mail.subscription')
93         sub_ids = sub_obj.search(cr, uid, ['&', ('res_model', '=', obj._name), ('user_id', '=', args[0][2])], context=context)
94         subs = sub_obj.read(cr, uid, sub_ids, context=context)
95         return [('id', 'in', map(itemgetter('res_id'), subs))]
96     
97     def get_last_month_msg_nbr(self, cr, uid, ids, name, args, context=None):
98         result = {}
99         message_obj = self.pool.get('mail.message')
100         for id in ids:
101             lower_date = (DT.datetime.now() - DT.timedelta(days=30)).strftime(tools.DEFAULT_SERVER_DATE_FORMAT)
102             result[id] = self.message_search(cr, uid, [id], limit=None, domain=[('date', '>=', lower_date)], count=True, context=context)
103         return result
104     
105     def _get_default_photo(self, cr, uid, context=None):
106         avatar_path = openerp.modules.get_module_resource('mail', 'static/src/img', 'groupdefault.png')
107         return self._photo_resize(cr, uid, open(avatar_path, 'rb').read().encode('base64'), context=context)
108     
109     _columns = {
110         'name': fields.char('Name', size=64, required=True),
111         'description': fields.text('Description'),
112         'responsible_id': fields.many2one('res.users', string='Responsible',
113             ondelete='set null', required=True, select=1,
114             help="Responsible of the group that has all rights on the record."),
115         'public': fields.boolean('Visible by non members', help='This group is visible by non members. \
116             Invisible groups can add members through the invite button.'),
117         'group_ids': fields.many2many('res.groups', rel='mail_group_res_group_rel',
118             id1='mail_group_id', id2='groups_id', string='Linked groups',
119             help="Members of those groups will automatically added as followers. "\
120                     "Note that they will be able to manage their subscription manually "\
121                     "if necessary."),
122         'photo_big': fields.binary('Full-size photo',
123             help='Field holding the full-sized PIL-supported and base64 encoded "\
124                     version of the group image. The photo field is used as an "\
125                     interface for this field.'),
126         'photo': fields.function(_get_photo, fnct_inv=_set_photo,
127             string='Photo', type="binary",
128             store = {
129                 'mail.group': (lambda self, cr, uid, ids, c={}: ids, ['photo_big'], 10),
130             },
131             help='Field holding the automatically resized (128x128) PIL-supported and base64 encoded version of the group image.'),
132         'member_ids': fields.function(get_member_ids, fnct_search=search_member_ids,
133             type='many2many', relation='res.users', string='Group members', multi='get_member_ids'),
134         'member_count': fields.function(get_member_ids, type='integer',
135             string='Member count', multi='get_member_ids'),
136         'is_subscriber': fields.function(get_member_ids, type='boolean',
137             string='Joined', multi='get_member_ids'),
138         'last_month_msg_nbr': fields.function(get_last_month_msg_nbr, type='integer',
139             string='Messages count for last month'),
140     }
141
142     _defaults = {
143         'public': True,
144         'responsible_id': (lambda s, cr, uid, ctx: uid),
145         'photo': _get_default_photo,
146     }
147
148     def _subscribe_user_with_group_m2m_command(self, cr, uid, ids, group_ids_command, context=None):
149         # form: {'group_ids': [(3, 10), (3, 3), (4, 10), (4, 3)]} or {'group_ids': [(6, 0, [ids]}
150         user_group_ids = [command[1] for command in group_ids_command if command[0] == 4]
151         user_group_ids += [id for command in group_ids_command if command[0] == 6 for id in command[2]]
152         # retrieve the user member of those groups
153         user_ids = []
154         res_groups_obj = self.pool.get('res.groups')
155         for group in res_groups_obj.browse(cr, uid, user_group_ids, context=context):
156             user_ids += [user.id for user in group.users]
157         # subscribe the users
158         return self.message_subscribe(cr, uid, ids, user_ids, context=context)
159
160     def create(self, cr, uid, vals, context=None):
161         mail_group_id = super(mail_group, self).create(cr, uid, vals, context=context)
162         if vals.get('group_ids'):
163             self._subscribe_user_with_group_m2m_command(cr, uid, [mail_group_id], vals.get('group_ids'), context=context)
164         return mail_group_id
165
166     def write(self, cr, uid, ids, vals, context=None):
167         if vals.get('group_ids'):
168             self._subscribe_user_with_group_m2m_command(cr, uid, ids, vals.get('group_ids'), context=context)
169         return super(mail_group, self).write(cr, uid, ids, vals, context=context)
170
171     def action_group_join(self, cr, uid, ids, context=None):
172         return self.message_subscribe(cr, uid, ids, context=context)
173     
174     def action_group_leave(self, cr, uid, ids, context=None):
175         return self.message_unsubscribe(cr, uid, ids, context=context)