[WIP] Followers rewrite: in create/write, the purpose is to add the field in the...
[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 openerp
24 import openerp.tools as tools
25 from operator import itemgetter
26 from osv import osv
27 from osv import fields
28 import tools
29 from tools.translate import _
30 from lxml import etree
31
32 class mail_group(osv.Model):
33     """
34     A mail_group is a collection of users sharing messages in a discussion
35     group. Group users are users that follow the mail group, using the
36     subscription/follow mechanism of OpenSocial. A mail group has nothing
37     in common with res.users.group.
38     """
39     
40     _description = 'Discussion group'
41     _name = 'mail.group'
42     _inherit = ['mail.thread']
43     _inherits = {'mail.alias': 'alias_id', 'ir.ui.menu': 'menu_id'}
44
45     def _get_image(self, cr, uid, ids, name, args, context=None):
46         result = dict.fromkeys(ids, False)
47         for obj in self.browse(cr, uid, ids, context=context):
48             result[obj.id] = tools.image_get_resized_images(obj.image)
49         return result
50     
51     def _set_image(self, cr, uid, id, name, value, args, context=None):
52         return self.write(cr, uid, [id], {'image': tools.image_resize_image_big(value)}, context=context)
53     
54     def _get_last_month_msg_nbr(self, cr, uid, ids, name, args, context=None):
55         result = {}
56         message_obj = self.pool.get('mail.message')
57         for id in ids:
58             lower_date = (DT.datetime.now() - DT.timedelta(days=30)).strftime(tools.DEFAULT_SERVER_DATE_FORMAT)
59             result[id] = self.message_search(cr, uid, [id], limit=None, domain=[('date', '>=', lower_date)], count=True, context=context)
60         return result
61     
62     def _get_default_image(self, cr, uid, context=None):
63         image_path = openerp.modules.get_module_resource('mail', 'static/src/img', 'groupdefault.png')
64         return tools.image_resize_image_big(open(image_path, 'rb').read().encode('base64'))
65     
66     _columns = {
67         'description': fields.text('Description'),
68         'menu_id': fields.many2one('ir.ui.menu', string='Related Menu', required=True, ondelete="cascade"),
69         'responsible_id': fields.many2one('res.users', string='Responsible',
70             ondelete='set null', required=True, select=1,
71             help="Responsible of the group that has all rights on the record."),
72         'public': fields.selection([('public', 'Public'), ('private', 'Private'), ('groups', 'Selected Group Only')],
73             string='Privacy', required=True,
74             help='This group is visible by non members. '\
75                  'Invisible groups can add members through the invite button.'),
76         'group_public_id': fields.many2one('res.groups', string='Authorized Group'),
77         'group_ids': fields.many2many('res.groups', rel='mail_group_res_group_rel',
78             id1='mail_group_id', id2='groups_id', string='Auto Subscription',
79             help="Members of those groups will automatically added as followers. "\
80                  "Note that they will be able to manage their subscription manually "\
81                  "if necessary."),
82         'image': fields.binary("Photo",
83             help="This field holds the image used as photo for the "\
84                  "user. The image is base64 encoded, and PIL-supported. "\
85                  "It is limited to a 12024x1024 px image."),
86         'image_medium': fields.function(_get_image, fnct_inv=_set_image,
87             string="Medium-sized photo", type="binary", multi="_get_image",
88             store = {
89                 'mail.group': (lambda self, cr, uid, ids, c={}: ids, ['image'], 10),
90             },
91             help="Medium-sized photo of the group. It is automatically "\
92                  "resized as a 180x180px image, with aspect ratio preserved. "\
93                  "Use this field in form views or some kanban views."),
94         'image_small': fields.function(_get_image, fnct_inv=_set_image,
95             string="Small-sized photo", type="binary", multi="_get_image",
96             store = {
97                 'mail.group': (lambda self, cr, uid, ids, c={}: ids, ['image'], 10),
98             },
99             help="Small-sized photo of the group. It is automatically "\
100                  "resized as a 50x50px image, with aspect ratio preserved. "\
101                  "Use this field anywhere a small image is required."),
102         'last_month_msg_nbr': fields.function(_get_last_month_msg_nbr, type='integer',
103             string='Messages count for last month'),
104         'alias_id': fields.many2one('mail.alias', 'Alias', ondelete="cascade", 
105                                     help="The email address associated with this group. New emails received will automatically "
106                                          "create new topics."),
107     }
108
109     def _get_default_employee_group(self, cr, uid, context=None):
110         ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'base', 'group_user')
111         return ref and ref[1] or False
112
113     def _get_menu_parent(self, cr, uid, context=None):
114         ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'mail', 'mail_group_root')
115         return ref and ref[1] or False
116
117     _defaults = {
118         'public': 'groups',
119         'group_public_id': _get_default_employee_group,
120         'responsible_id': (lambda s, cr, uid, ctx: uid),
121         'image': _get_default_image,
122         'parent_id': _get_menu_parent,
123     }
124
125     def _subscribe_user_with_group_m2m_command(self, cr, uid, ids, group_ids_command, context=None):
126         # form: {'group_ids': [(3, 10), (3, 3), (4, 10), (4, 3)]} or {'group_ids': [(6, 0, [ids]}
127         user_group_ids = [command[1] for command in group_ids_command if command[0] == 4]
128         user_group_ids += [id for command in group_ids_command if command[0] == 6 for id in command[2]]
129         # retrieve the user member of those groups
130         user_ids = []
131         res_groups_obj = self.pool.get('res.groups')
132         for group in res_groups_obj.browse(cr, uid, user_group_ids, context=context):
133             user_ids += [user.id for user in group.users]
134         # subscribe the users
135         return self.message_subscribe(cr, uid, ids, user_ids, context=context)
136
137     def create(self, cr, uid, vals, context=None):
138         alias_pool = self.pool.get('mail.alias')
139         if not vals.get('alias_id'):
140             name = vals.get('alias_name') or vals['name']
141             alias_id = alias_pool.create_unique_alias(cr, uid, 
142                     {'alias_name': "group_"+name}, 
143                     model_name=self._name, context=context)
144             vals['alias_id'] = alias_id
145
146         mail_group_id = super(mail_group, self).create(cr, uid, vals, context)
147
148         # Create client action for this group and link the menu to it
149         ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'mail', 'action_mail_group_feeds')
150         if ref:
151             search_ref = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'mail', 'view_message_search_wall')
152             params = {
153                 'search_view_id': search_ref and search_ref[1] or False,
154                 'domain': [('model','=','mail.group'),('res_id','=',mail_group_id)],
155                 'res_model': 'mail.group',
156                 'res_id': mail_group_id,
157                 'thread_level': 2
158             }
159             cobj = self.pool.get('ir.actions.client')
160             newref = cobj.copy(cr, uid, ref[1], default={'params': str(params), 'name': vals['name']}, context=context)
161             self.write(cr, uid, [mail_group_id], {'action': 'ir.actions.client,'+str(newref), 'mail_group_id': mail_group_id}, context=context)
162
163         alias_pool.write(cr, uid, [vals['alias_id']], {"alias_force_thread_id": mail_group_id}, context)
164         if vals.get('group_ids'):
165             self._subscribe_user_with_group_m2m_command(cr, uid, [mail_group_id], vals.get('group_ids'), context=context)
166
167         return mail_group_id
168
169     def unlink(self, cr, uid, ids, context=None):
170         # Cascade-delete mail aliases as well, as they should not exist without the mail group.
171         mail_alias = self.pool.get('mail.alias')
172         alias_ids = [group.alias_id.id for group in self.browse(cr, uid, ids, context=context) if group.alias_id]
173         res = super(mail_group, self).unlink(cr, uid, ids, context=context)
174         mail_alias.unlink(cr, uid, alias_ids, context=context)
175         return res
176
177     def write(self, cr, uid, ids, vals, context=None):
178         if vals.get('group_ids'):
179             self._subscribe_user_with_group_m2m_command(cr, uid, ids, vals.get('group_ids'), context=context)
180         return super(mail_group, self).write(cr, uid, ids, vals, context=context)
181
182     def action_group_join(self, cr, uid, ids, context=None):
183         return self.message_subscribe(cr, uid, ids, context=context)
184
185     def action_group_leave(self, cr, uid, ids, context=None):
186         return self.message_unsubscribe(cr, uid, ids, context=context)
187
188     # ----------------------------------------
189     # OpenChatter methods and notifications
190     # ----------------------------------------
191
192     def message_get_monitored_follower_fields(self, cr, uid, ids, context=None):
193         """ Add 'responsible_id' to the monitored fields """
194         res = super(mail_group, self).message_get_monitored_follower_fields(cr, uid, ids, context=context)
195         return res + ['responsible_id']