new portal users get the portal menu and groups as defaults
[odoo/odoo.git] / addons / portal / portal.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2011 Tiny SPRL (<http://tiny.be>).
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 from osv import osv, fields
23 import random
24
25 class portal(osv.osv):
26     _name = 'res.portal'
27     _description = 'Portal'
28     _columns = {
29         'name': fields.char(string='Name', size=64, required=True),
30         'menu_id': fields.many2one('ir.actions.actions', required="True",
31             string='Menu Action',
32             help="The customized menu of the portal's users"),
33         'user_ids': fields.one2many('res.users', 'portal_id',
34             string='Users',
35             help='Gives the set of users associated to this portal'),
36         'group_ids': fields.many2many('res.groups', 'res_portals_groups_rel', 'pid', 'gid',
37             string='Groups',
38             help='Users of this portal automatically belong to those groups'),
39     }
40     _sql_constraints = [
41         ('unique_name', 'UNIQUE(name)', 'Portals must have different names.')
42     ]
43     
44     def copy(self, cr, uid, id, defaults, context=None):
45         """ override copy() to not copy the portal users """
46         # find an unused name of the form "old_name [N]" for some random N
47         old_name = self.browse(cr, uid, id, context).name
48         new_name = copy_random(old_name)
49         while self.search(cr, uid, [('name', '=', new_name)], limit=1, context=context):
50             new_name = copy_random(old_name)
51         
52         defaults['name'] = new_name
53         defaults['user_ids'] = []
54         return super(portal, self).copy(cr, uid, id, defaults, context)
55     
56     def create(self, cr, uid, values, context=None):
57         """ extend create() to assign the portal menu and groups to users """
58         # as 'user_ids' is a many2one relation, values['user_ids'] must be a
59         # list of tuples of the form (0, 0, {values})
60         for op, _, user_values in values['user_ids']:
61             assert op == 0
62             user_values['menu_id'] = values['menu_id']
63             user_values['groups_id'] = values['group_ids']
64         
65         return super(portal, self).create(cr, uid, values, context)
66     
67     def write(self, cr, uid, ids, values, context=None):
68         """ extend write() to reflect menu and groups changes on users """
69         
70         # analyse groups changes, and determine how to change users
71         groups_diff = []
72         for change in values.get('group_ids', []):
73             if change[0] in [0, 5, 6]:          # change creates or sets groups,
74                 groups_diff = None              # must compute per-portal diff
75                 break
76             if change[0] in [3, 4]:             # change add or remove group,
77                 groups_diff.append(change)      # add or remove group on users
78         
79         if groups_diff is None:
80             return self._write_compute_diff(cr, uid, ids, values, context)
81         else:
82             return self._write_diff(cr, uid, ids, values, groups_diff, context)
83     
84     def _write_diff(self, cr, uid, ids, values, groups_diff, context=None):
85         """ perform write() and apply groups_diff on users """
86         # first apply portal changes
87         super(portal, self).write(cr, uid, ids, values, context)
88         
89         # then apply menu and group changes on their users
90         user_values = {}
91         if 'menu_id' in values:
92             user_values['menu_id'] = values['menu_id']
93         if groups_diff:
94             user_values['groups_id'] = groups_diff
95         
96         if user_values:
97             user_ids = []
98             for p in self.browse(cr, uid, ids, context):
99                 user_ids += get_browse_ids(p.user_ids)
100             self.pool.get('res.users').write(cr, uid, user_ids, user_values, context)
101         
102         return True
103     
104     def _write_compute_diff(self, cr, uid, ids, values, context=None):
105         """ perform write(), then compute and apply groups_diff on each portal """
106         # read group_ids before write() to compute groups_diff
107         old_group_ids = {}
108         for p in self.browse(cr, uid, ids, context):
109             old_group_ids[p.id] = get_browse_ids(p.group_ids)
110         
111         # apply portal changes
112         super(portal, self).write(cr, uid, ids, values, context)
113         
114         # the changes to apply on users
115         user_object = self.pool.get('res.users')
116         user_values = {}
117         if 'menu_id' in values:
118             user_values['menu_id'] = values['menu_id']
119         
120         # compute groups_diff on each portal, and apply them on users
121         for p in self.browse(cr, uid, ids, context):
122             old_groups = set(old_group_ids[p.id])
123             new_groups = set(get_browse_ids(p.group_ids))
124             # groups_diff: [(3, UNLINKED_ID), ..., (4, LINKED_ID), ...]
125             user_values['groups_id'] = \
126                 [(3, g) for g in (old_groups - new_groups)] + \
127                 [(4, g) for g in (new_groups - old_groups)]
128             user_ids = get_browse_ids(p.user_ids)
129             user_object.write(cr, uid, user_ids, user_values, context)
130         
131         return True
132
133 portal()
134
135 class users(osv.osv):
136     _name = 'res.users'
137     _inherit = 'res.users'
138     _columns = {
139         'portal_id': fields.many2one('res.portal', string='Portal',
140             help='If given, the portal defines customized menu and access rules'),
141     }
142     
143     def default_get(self, cr, uid, fields, context=None):
144         """ override default values of menu_id and groups_id for portal users """
145         others = {}
146         # How it works: the values of 'menu_id' and 'groups_id' are passed in
147         # context by the portal form view
148         if ('menu_id' in context) and ('menu_id' in fields):
149             fields.remove('menu_id')
150             others['menu_id'] = context['menu_id']
151         if ('groups_id' in context) and ('groups_id' in fields):
152             fields.remove('groups_id')
153             others['groups_id'] = get_many2many(context['groups_id'])
154         # the remaining fields use inherited defaults
155         defs = super(users, self).default_get(cr, uid, fields, context)
156         defs.update(others)
157         return defs
158
159 users()
160
161 # utils
162 def get_browse_id(obj):
163     """ return the id of a browse() object, or None """
164     return (obj and obj.id or None)
165
166 def get_browse_ids(objs):
167     """ return the ids of a list of browse() objects """
168     return map(get_browse_id, objs)
169
170 def get_many2many(arg):
171     """ get the list of ids from a many2many 'values' field """
172     assert len(arg) == 1 and arg[0][0] == 6             # arg = [(6, _, IDs)]
173     return arg[0][2]
174
175 def copy_random(name):
176     """ return "name [N]" for some random integer N """
177     return "%s [%s]" % (name, random.choice(xrange(1000000)))
178