~unifield-team/unifield-wm/us-826

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
# -*- coding: utf-8 -*-
##############################################################################
#
#    OpenERP, Open Source Management Solution
#    Copyright (C) 2011 MSF, TeMPO consulting
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero General Public License as
#    published by the Free Software Foundation, either version 3 of the
#    License, or (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

from osv import osv, fields
from tools.translate import _
import base64
from os.path import join as opj
import tools

from spreadsheet_xml.spreadsheet_xml import SpreadsheetXML
from msf_supply_doc_import.check_line import *

RESULT_MODELS_SELECTION = [('group', 'Group'), ('user', 'User'), ('menu', 'Menu')]
RESULT_TYPES_SELECTION = [('error', 'Error'), ('warning', 'Warning'), ('created', 'Created'), ('activated', 'Activated'), ('deactivated', 'Deactivated')]


class user_access_configurator(osv.osv_memory):
    _name = 'user.access.configurator'
    _columns = {'file_to_import_uac': fields.binary(string='File to import', filters='*.xml', help='You can use the template of the export for the format that you need to use. \n The file should be in XML Spreadsheet 2003 format. \n The columns should be in this order : Product Code*, Product Description*, Initial Average Cost, Location*, Batch, Expiry Date, Quantity'),
                'number_of_non_group_columns_uac': fields.integer(string='Number of columns not containing group name')}
    _defaults = {'number_of_non_group_columns_uac': 4}
    
    def _row_is_empty(self, cr, uid, ids, context=None, *args, **kwargs):
        """
        return True if row is empty
        """
        row = kwargs['row']
        return all([not cell.data for cell in row.cells])
    
    def _cell_is_true(self, cr, uid, ids, context=None, *args, **kwargs):
        """
        return True if row is empty
        """
        cell = kwargs['cell']
        if cell.data and cell.data.upper() == 'YES':
            return True
        return False
    
    def _get_ids_from_group_names(self, cr, uid, context=None, *args, **kwargs):
        '''
        return ids corresponding to group names
        '''
        # objects
        group_obj = self.pool.get('res.groups')
        # group names
        group_names = kwargs['group_names']
        # additional search criteria
        additional_criterias = kwargs.get('additional_criterias', [])
        if not isinstance(group_names, list):
            group_names = [group_names]
        
        # add additional criterias
        criterias = [('name', 'in', group_names)]
        criterias.extend(additional_criterias)
        group_ids = group_obj.search(cr, uid, criterias, context=context)
        return group_ids
    
    def _get_all_access_objects_ids(self, cr, uid, context=None, *args, **kwargs):
        '''
        return ids of object which need full access even if originally admin rights only
        '''
        # objects
        model_obj = self.pool.get('ir.model')
        # group names
        all_access_group_names = self._get_all_access_objects_name(cr, uid, context=context)
        all_access_group_ids = model_obj.search(cr, uid, [('model', 'in', all_access_group_names)], context=context)
        
        return all_access_group_ids
    
    def _get_all_access_objects_name(self, cr, uid, context=None, *args, **kwargs):
        '''
        return names of object which need full access even if originally admin rights only
        '''
        all_access_object_names = ['ir.model.data']
        return all_access_object_names
    
    def _get_admin_user_rights_group_name(self, cr, uid, context=None, *args, **kwargs):
        '''
        return admin_user_rights_group
        '''
        admin_user_rights_group = 'Administration / Access Rights'
        return admin_user_rights_group
    
    def _get_admin_user_rights_group_id(self, cr, uid, context=None, *args, **kwargs):
        '''
        return admin_user_rights_group id
        '''
        admin_group_name = self._get_admin_user_rights_group_name(cr, uid, context=context)
        admin_group_ids = self._get_ids_from_group_names(cr, uid, context=context, group_names=[admin_group_name])
        return admin_group_ids[0]
    
    def _get_DNCGL_name(self, cr, uid, ids, context=None, *args, **kwargs):
        '''
        return do not change groups
        '''
        group_immunity_list = [u'Useability / No One', u'Useability / Multi Companies']
        return group_immunity_list
    
    def _get_DNCGL_ids(self, cr, uid, ids, context=None, *args, **kwargs):
        '''
        return do not change groups ids
        '''
        group_names = self._get_DNCGL_name(cr, uid, ids, context=context)
        return self._get_ids_from_group_names(cr, uid, context=context, group_names=group_names)
    
    def _get_IGL_name(self, cr, uid, ids, context=None, *args, **kwargs):
        '''
        return immunity groups
        '''
        group_immunity_list = [u'Administration / Access Rights']
        # CNCGL names are temporarily added as non active groups are considered to be part of no groups, and therefore always displayed?
#        group_immunity_list.extend(self._get_DNCGL_name(cr, uid, ids, context=context))
        
        return group_immunity_list
    
    def _group_name_is_immunity(self, cr, uid, ids, context=None, *args, **kwargs):
        '''
        return True if group_name is immune
        '''
        group_immunity_list = self._get_IGL_name(cr, uid, ids, context=context)
        group_name = kwargs['group_name']
        return group_name in group_immunity_list
        
    def _remove_group_immune(self, cr, uid, ids, context=None, *args, **kwargs):
        '''
        clear groups of immune groups
        '''
        group_names = kwargs['group_names']
        return [group_name for group_name in group_names if not self._group_name_is_immunity(cr, uid, ids, context=context, group_name=group_name)]
    
    def _import_data_uac(self, cr, uid, ids, context=None):
        '''
        import data and generate data structure
        
        {id: {
              'group_name_list': [group_names],
              'menus_groups': {'menu_id': [group_names]}, - we only take the group_name into account if True - if the same group is defined multiple times, it will be deleted at the end of import function
              'group': {'activated': [group_names], 'deactivated': [group_names], 'created': [group_names], 'warning': [], 'error': []},
              'user': {'activated': [user_names], 'deactivated': [user_names], 'created': [user_names], 'warning': [], 'error': []},
              'menu': {'warning': [], 'error': []},
              }
        '''
        # Some verifications
        if context is None:
            context = {}
        if isinstance(ids, (int, long)):
            ids = [ids]
        
        # data structure returned with processed data from file
        data_structure = {}
        
        for obj in self.browse(cr, uid, ids, context=context):
            # data structure returned with processed data from file
            data_structure.update({obj.id: {'group_name_list': [],
                                            'menus_groups': {},
                                            'group': {'activated': [], 'deactivated': [], 'created': [], 'warning': [], 'error': []},
                                            'user': {'activated': [], 'deactivated': [], 'created': [], 'warning': [], 'error': []},
                                            'menu': {'warning': [], 'error': []},
                                            }})
            # file to process
            file = obj.file_to_import_uac
            # file is mandatory for import process
            if not file:
                raise osv.except_osv(_('Warning'), _('No File Selected.'))
            # load the selected file according to XML OUTPUT
            fileobj = SpreadsheetXML(xmlstring=base64.decodestring(file))
            # iterator on rows
            rows = fileobj.getRows()
            # first row flag
            first_row = True
            # loop the rows for groups-menus relation
            for row in rows:
                # skip empty lines
                if self._row_is_empty(cr, uid, ids, context=context, row=row):
                    continue
                
                # first row, gather group names
                if first_row:
                    first_row = False
                    # skip information rows
                    for i in range(obj.number_of_non_group_columns_uac, len(row)):
                        group_name = False
                        # if no name of the group, create a new group No Name
                        if not row.cells[i].data or row.cells[i].data == '':
                            group_name = 'No Name'
                        else:
                            group_name = row.cells[i].data
                        # if the same group is defined multiple times
                        if group_name in data_structure[obj.id]['group_name_list']:
                            # display a warning, the same group is displayed multiple times, columns values are aggregated (OR)
                            data_structure[obj.id]['group']['warning'].append('The group %s is defined multiple times. Values from all these groups are aggregated (OR function).'%group_name)
                        # we add the column, even if defined multiple times, as we need it for name matching when setting menu rights
                        data_structure[obj.id]['group_name_list'].append(group_name)
                else:
                    # information rows
                    try:
                        menu_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, row.cells[0], row.cells[1])[1]
                    except ValueError:
                        # menu is in the file but not in the database
                        data_structure[obj.id]['menu']['error'].append('The menu %s (%s.%s) is defined in the file but is missing in the database.'%(row.cells[3], row.cells[0], row.cells[1]))
                        continue
                    
                    # test if a menu is defined multiple times
                    if menu_id in data_structure[obj.id]['menus_groups']:
                        data_structure[obj.id]['menu']['warning'].append('The menu %s (%s.%s) is defined multiple times. Groups from all these rows are aggregated (OR function).'%(row.cells[3], row.cells[0], row.cells[1]))
                    
                    # skip information rows - find related groups
                    for i in range(obj.number_of_non_group_columns_uac, len(row)):
                        # group is true for this menu
                        if self._cell_is_true(cr, uid, ids, context=context, cell=row.cells[i]):
                            # name of group
                            group_name = data_structure[obj.id]['group_name_list'][i - obj.number_of_non_group_columns_uac]
                            menu_group_list = data_structure[obj.id]['menus_groups'].setdefault(menu_id, [])
                            # if the column is defined multiple times, we only add one time the name, but the access selection is aggregated from all related columns
                            if group_name not in menu_group_list:
                                menu_group_list.append(group_name)
            
            # all rows have been treated, the order of group_name_list is not important anymore, we can now exclude groups which are defined multiple times
            data_structure[obj.id]['group_name_list'] = list(set(data_structure[obj.id]['group_name_list']))
                    
        return data_structure
    
    def _activate_immunity_groups(self, cr, uid, ids, context=None):
        '''
        activate immunity groups
        
        return immunity group
        '''
        # objects
        group_obj = self.pool.get('res.groups')
        
        group_immunity_name_list = self._get_IGL_name(cr, uid, ids, context=context)
        return self._set_active_group_name(cr, uid, ids, context=context, group_names=group_immunity_name_list, active_value=True)
    
    def _set_active_group_name(self, cr, uid, ids, context=None, *args, **kwargs):
        '''
        activate groups names
        
        return activated groups names
        '''
        # objects
        group_obj = self.pool.get('res.groups')
        # data structure
        data_structure = context['data_structure']
        
        group_names = kwargs['group_names']
        active_value = kwargs['active_value']
        set_active_ids = self._get_ids_from_group_names(cr, uid, context=context, group_names=group_names, additional_criterias=[('visible_res_groups', '=', not active_value)])
        group_obj.write(cr, uid, set_active_ids, {'visible_res_groups': active_value}, context=context)
        
        # info logging - activated/deactivated groups
        set_active_names = [x['name'] for x in group_obj.read(cr, uid, set_active_ids, ['name'], context=context)]
        for id in ids:
            if active_value:
                data_structure[id]['group']['activated'].extend(set_active_names)
            else:
                data_structure[id]['group']['deactivated'].extend(set_active_names)
        
        return group_names
    
    def _process_groups_uac(self, cr, uid, ids, context=None):
        '''
        create / active / deactivate groups according to policy defined in the file
        '''
        # Some verifications
        if context is None:
            context = {}
        if isinstance(ids, (int, long)):
            ids = [ids]
        
        # objects
        group_obj = self.pool.get('res.groups')
        # data structure
        data_structure = context['data_structure']
        # load all groups from database
        group_ids = group_obj.search(cr, uid, [], context=context)
        group_names = group_obj.read(cr, uid, group_ids, ['name'], context=context)
        group_names = [x['name'] for x in group_names]
        
        # IGL groups are activated
        self._activate_immunity_groups(cr, uid, ids, context=context)
        
        for obj in self.browse(cr, uid, ids, context=context):
            # work copy of groups present in the file - will represent the missing groups to be created
            missing_group_names = list(data_structure[obj.id]['group_name_list'])
            # all groups from file are activated (in case a new group in the file which was deactivated previously)
            self._set_active_group_name(cr, uid, ids, context=context, group_names=missing_group_names, active_value=True)
            # will represent the groups present in the database but not in the file, to be deactivated
            deactivate_group_names = []
            # loop through groups in the database - pop from file list if already exist
            for group_name in group_names:
                if group_name in missing_group_names:
                    # the group from file already exists
                    missing_group_names.remove(group_name)
                elif not self._group_name_is_immunity(cr, uid, ids, context=context, group_name=group_name):
                    # the group from database is not immune and not in the file
                    deactivate_group_names.append(group_name)
            
            # create the new groups from the file
            for missing_group_name in missing_group_names:
                new_group_id = group_obj.create(cr, uid, {'name': missing_group_name, 'from_file_import_res_groups': True}, context=context)
                # info logging - created groups
                data_structure[obj.id]['group']['created'].append(missing_group_name)
                
            # deactivate the groups not present in the file
            self._set_active_group_name(cr, uid, ids, context=context, group_names=deactivate_group_names, active_value=False)
        
        return True
    
    def _set_active_user_ids(self, cr, uid, ids, context=None, *args, **kwargs):
        '''
        change active value of user ids
        
        return modified user ids
        '''
        # objects
        user_obj = self.pool.get('res.users')
        # data structure
        data_structure = context['data_structure']
    
        user_ids = kwargs['user_ids']
        active_value = kwargs['active_value']
        # only with active not active_value
        set_active_ids = user_obj.search(cr, uid, [('id', 'in', user_ids), ('active', '=', not active_value)], context=context)
        user_obj.write(cr, uid, set_active_ids, {'active': active_value}, context=context)
        
        # info logging - activated/deactivated users
        set_active_names = [x['name'] for x in user_obj.read(cr, uid, set_active_ids, ['name'], context=context)]
        for id in ids:
            if active_value:
                data_structure[id]['user']['activated'].extend(set_active_names)
            else:
                data_structure[id]['user']['deactivated'].extend(set_active_names)
        
        return set_active_ids
    
    def _process_users_uac(self, cr, uid, ids, context=None):
        '''
        create user corresponding to file groups if not already present
        
        default values for users
        
        res_user:
        'groups_id': fields.many2many('res.groups', 'res_groups_users_rel', 'uid', 'gid', 'Groups'),
        '''
        # Some verifications
        if context is None:
            context = {}
        if isinstance(ids, (int, long)):
            ids = [ids]
        
        # objects
        user_obj = self.pool.get('res.users')
        # data structure
        data_structure = context['data_structure']
        # default password value
        default_password_value = 'temp'
        
        for obj in self.browse(cr, uid, ids, context=context):
            # get admin user id
            admin_ids = user_obj.search(cr, uid, [('login', '=', 'admin')], context=context)
            if not admin_ids:
                # log error and return
                data_structure[obj.id]['user']['error'].append('The Administrator user does not exist. This is a big issue.')
                return
            # group ids - used to set all groups to admin user
            group_ids_list = []
            # user ids - used to deactivate users for which group is not in the file
            # we do not want to deactivate admin user (even if not in the file)
            user_ids_list = [admin_ids[0]]
            for group_name in data_structure[obj.id]['group_name_list']:
                # login format from group_name
                login_name = '_'.join(group_name.lower().split())
                # check if a user already exist
                user_ids = user_obj.search(cr, uid, [('login', '=', login_name)], context=context)
                if not user_ids:
                    # create a new user, copied from admin user
                    user_ids = [user_obj.copy(cr, uid, admin_ids[0], {'name': group_name,
                                                                      'login': login_name,
                                                                      'password': default_password_value,
                                                                      'date': False}, context=context)]
                    # info logging - created users
                    data_structure[obj.id]['user']['created'].append(group_name)
        
                else:
                    # we make sure that the user name is up to date, as Manager gives the same login name as mAnAgER.
                    user_obj.write(cr, uid, user_ids, {'name': group_name}, context=context)
                # update the group of the user with (6, 0, 0) resetting the data
                group_ids = self._get_ids_from_group_names(cr, uid, context=context, group_names=[group_name])
                user_obj.write(cr, uid, user_ids, {'groups_id': [(6, 0, group_ids)]}, context=context)
                # keep group_id for updating admin user
                group_ids_list.extend(group_ids)
                # keep user_id for deactivate users not present in the file
                user_ids_list.extend(user_ids)
                
            # get all users
            all_user_ids = user_obj.search(cr, uid, [], context=context)
            # deactivate user not present in the file and not ADMIN
            deactivate_user_ids = [x for x in all_user_ids if x not in user_ids_list]
            self._set_active_user_ids(cr, uid, ids, context=context, user_ids=deactivate_user_ids, active_value=False)
            # activate user from the file (could have been deactivate previously)
            self._set_active_user_ids(cr, uid, ids, context=context, user_ids=user_ids_list, active_value=True)
            # get admin group id
            group_ids_list.append(self._get_admin_user_rights_group_id(cr, uid, context=context))
            # for admin user, set all unifield groups + admin group (only user to have this group)
            user_obj.write(cr, uid, admin_ids, {'groups_id': [(6, 0, group_ids_list)]}, context=context)
        
        return True
    
    def _process_menus_uac(self, cr, uid, ids, context=None):
        '''
        set menus group relation as specified in file
        
        ir.ui.menu: groups_id
        '''
        # Some verifications
        if context is None:
            context = {}
        if isinstance(ids, (int, long)):
            ids = [ids]
        
        # objects
        menu_obj = self.pool.get('ir.ui.menu')
        # data structure
        data_structure = context['data_structure']
        # get all menus from database
        all_menus_context = dict(context)
        all_menus_context.update({'ir.ui.menu.full_list': True})
        db_menu_ids = menu_obj.search(cr, uid, [], context=all_menus_context)
        
        for obj in self.browse(cr, uid, ids, context=context):
            # check each menus from database
            for db_menu_id in db_menu_ids:
                # group ids to be linked to
                group_ids = []
                if db_menu_id not in data_structure[obj.id]['menus_groups'] or not data_structure[obj.id]['menus_groups'][db_menu_id]:
                    # we modify the groups to admin only if the menu is not linked to one of the group of DNCGL
                    skip_update = False
                    db_menu = menu_obj.browse(cr, uid, db_menu_id, context=context)
                    dncgl_ids = self._get_DNCGL_ids(cr, uid, ids, context=context)
                    for group in db_menu.groups_id:
                        if group.id in dncgl_ids:
                            skip_update = True
                    # the menu does not exist in the file OR the menu does not belong to any group
                    # link (6,0,[id]) to administration / access rights
                    if not skip_update:
                        admin_group_id = self._get_admin_user_rights_group_id(cr, uid, context=context)
                        group_ids = [admin_group_id]
                else:
                    # find the id of corresponding groups, and write (6,0, ids) in groups_id
                    group_ids = self._get_ids_from_group_names(cr, uid, context=context, group_names=data_structure[obj.id]['menus_groups'][db_menu_id])
                # link the menu to selected group ids
                if group_ids:
                    menu_obj.write(cr, uid, [db_menu_id], {'groups_id': [(6, 0, group_ids)]}, context=context)
        
        return True
    
    def _process_objects_uac(self, cr, uid, context=None):
        '''
        reset ACL lines
        
        
        ir.model:
        'access_ids': fields.one2many('ir.model.access', 'model_id', 'Access'),
        
        ir.model.access:
        'model_id': fields.many2one('ir.model', 'Object', required=True, domain=[('osv_memory','=', False)], select=True, ondelete='cascade'),
        'group_id': fields.many2one('res.groups', 'Group', ondelete='cascade', select=True),
        'perm_read': fields.boolean('Read Access'),
        'perm_write': fields.boolean('Write Access'),
        'perm_create': fields.boolean('Create Access'),
        'perm_unlink': fields.boolean('Delete Access'),
        '''
        # Some verifications
        if context is None:
            context = {}
            
        # objects
        model_obj = self.pool.get('ir.model')
        access_obj = self.pool.get('ir.model.access')
        # list all ids of objects from the database
        model_ids = model_obj.search(cr, uid, [('osv_memory', '=', False)], context=context)
        # list all ids of acl
        access_ids = access_obj.search(cr, uid, [], context=context)
        # admin user group id
        admin_group_user_rights_id = self._get_admin_user_rights_group_id(cr, uid, context=context)
        # list of objects which have wild card for all access
        all_access_object_ids = self._get_all_access_objects_ids(cr, uid, context=context)
        # list of all acl linked to Administration / Access Rights -> two lines for those models - ONLY IF NOT PART OF ALL ACCESS OBJECTS
        admin_group_access_ids = access_obj.search(cr, uid, [('group_id', '=', admin_group_user_rights_id),
                                                             ('model_id', 'not in', all_access_object_ids)], context=context)
        # get the list of corresponding model ids
        data = access_obj.read(cr, uid, admin_group_access_ids, ['model_id'], context=context)
        # we only keep one ACL with link to admin for one model thanks to dictionary structure
        two_lines_ids = dict((x['model_id'][0], x['id']) for x in data if x['model_id'])
        # drop all ACL
        access_obj.unlink(cr, uid, access_ids, context=context)
        # one line data
        acl_one_line_read_no_group_values = {'name': 'not admin',
                                             'group_id': False,
                                             'perm_read': True,
                                             'perm_write': True,
                                             'perm_create': True,
                                             'perm_unlink': True,
                                             }
        # create one line for all objects no linked to admin
        no_linked_to_admin_ids = [x for x in model_ids if x not in two_lines_ids.keys()]
        model_obj.write(cr, uid, no_linked_to_admin_ids, {'access_ids' : [(0, 0, acl_one_line_read_no_group_values)]}, context=context)
        # first line, for admin group, all access
        acl_admin_values = {'name': 'admin',
                            'group_id': admin_group_user_rights_id,
                            'perm_read': True,
                            'perm_write': True,
                            'perm_create': True,
                            'perm_unlink': True,
                            }
        acl_read_values = {'name': 'admin',
                           'group_id': False,
                           'perm_read': True,
                           'perm_write': False,
                           'perm_create': False,
                           'perm_unlink': False,
                           }
        # create lines for theses models with deletion of existing ACL
        # [(0, 0, {'field_name':field_value_record1, ...}), (0, 0, {'field_name':field_value_record2, ...})]
        model_obj.write(cr, uid, two_lines_ids.keys(), {'access_ids' : [(0, 0, acl_admin_values), (0, 0, acl_read_values)]}, context=context)
        
        return True
    
    def _process_record_rules_uac(self, cr, uid, context=None):
        '''
        drop all
        '''
        # Some verifications
        if context is None:
            context = {}
        
        # objects
        rule_obj = self.pool.get('ir.rule')
        all_ids = rule_obj.search(cr, uid, [], context=context)
        rule_obj.unlink(cr, uid, all_ids, context=context)
        
        return False
    
    def _do_process_uac(self, cr, uid, ids, context=None):
        '''
        private method, used in the yaml tests to get data_structure
        '''
        # Some verifications
        if context is None:
            context = {}
        if isinstance(ids, (int, long)):
            ids = [ids]
        
        # we need to take inactive groups and users into acount, in order to reactivate them and avoid creation of the same group multiple time
        context=dict(context, active_test=False)
        # gather data structure corresponding to selected file
        data_structure = self._import_data_uac(cr, uid, ids, context=context)
        # process the groups
        self._process_groups_uac(cr, uid, ids, context=dict(context, data_structure=data_structure))
        # process users
        self._process_users_uac(cr, uid, ids, context=dict(context, data_structure=data_structure))
        # process menus - groups relation
        self._process_menus_uac(cr, uid, ids, context=dict(context, data_structure=data_structure))
        # process ACL
        self._process_objects_uac(cr, uid, context=context)
        # process rules
        self._process_record_rules_uac(cr, uid, context=context)
        return data_structure
    
    def do_process_uac(self, cr, uid, ids, context=None):
        '''
        main function called from wizard
        '''
        # Some verifications
        if context is None:
            context = {}
        if isinstance(ids, (int, long)):
            ids = [ids]
            
        data_structure = self._do_process_uac(cr, uid, ids, context=context)
        # data
        name = _("Results from User Rights Import")
        model = 'user.access.results'
        step = 'default'
        wiz_obj = self.pool.get('wizard')
        # open the selected wizard
        res = wiz_obj.open_wizard(cr, uid, ids, name=name, model=model, step=step, context=dict(context,
                                                                                                data_structure=data_structure))
        return res
    
    def do_update_after_module_install(self, cr, uid, context=None):
        '''
        special method called after module install
        
        reset data potentially reset to default values
        '''
        # process ACL
        self._process_objects_uac(cr, uid, context=context)
        # process rules
        self._process_record_rules_uac(cr, uid, context=context)
        
        return True

user_access_configurator()


class user_access_results(osv.osv_memory):
    _name = 'user.access.results'
    _columns = {'group_ids_user_access_results': fields.one2many('user.access.results.groups.line', 'wizard_id_user_access_results_line', string='Groups Info'),
                'user_ids_user_access_results': fields.one2many('user.access.results.users.line', 'wizard_id_user_access_results_line', string='Users Info'),
                'menu_ids_user_access_results': fields.one2many('user.access.results.menus.line', 'wizard_id_user_access_results_line', string='Menus Info'),
                }
    
    def default_get(self, cr, uid, fields, context=None):
        '''
        fill the lines with default create_values
        '''
        # Some verifications
        if context is None:
            context = {}
        # objects
        move_obj = self.pool.get('stock.move')
        # data structure
        data_structure = context['data_structure']
        # super
        res = super(user_access_results, self).default_get(cr, uid, fields, context=context)
        # wizard id
        wizard_id = context['active_ids'][0]
        # model
        for model_t in RESULT_MODELS_SELECTION:
            # model
            model = model_t[0]
            # values
            result = []
            # type
            for type_t in RESULT_TYPES_SELECTION:
                # type
                type = type_t[0]
                # fill groups data
                if type in data_structure[wizard_id][model]:
                    datas = data_structure[wizard_id][model][type]
                    # errors
                    for value in datas:
                        create_values  = {'model_user_access_results_line': model,
                                          'type_user_access_results_line': type,
                                          'value_user_access_results_line': value}
                        result.append(create_values)
            
            # add to one2many
            # kit list
            if '%s_ids_user_access_results'%model in fields:
                res.update({'%s_ids_user_access_results'%model: result})
                     
        return res
    
    def close_results_uar(self, cr, uid, ids, context=None):
        '''
        close wizard
        '''
        return {'type' : 'ir.actions.act_window_close'}

user_access_results()


class user_access_results_groups_line(osv.osv_memory):
    _name = 'user.access.results.groups.line'
    
    def _vals_get_user_access_results_line(self, cr, uid, ids, fields, arg, context=None):
        '''
        multi fields function method
        '''
        # Some verifications
        if context is None:
            context = {}
        if isinstance(ids, (int, long)):
            ids = [ids]
            
        result = {}
        for obj in self.browse(cr, uid, ids, context=context):
            result[obj.id] = {}
            for f in fields:
                result[obj.id].update({f:False})
            # name
            string_value = False
            if obj.type_user_access_results_line in ['created', 'activated', 'deactivated']:
                string_value = 'The %s %s has been %s.'%(obj.model_user_access_results_line, obj.value_user_access_results_line, obj.type_user_access_results_line)
            elif obj.type_user_access_results_line in ['warning', 'error']:
                string_value = '%s.'%(obj.value_user_access_results_line)
            result[obj.id].update({'name': string_value})
            
        return result
    
    _columns = {'wizard_id_user_access_results_line': fields.many2one('user.access.results', string='Wizard'),
                'name': fields.function(_vals_get_user_access_results_line, method=True, type='char', size=1024, string='Name', multi='get_vals_user_access_results_line', readonly=True),
                'model_user_access_results_line': fields.selection(RESULT_MODELS_SELECTION, string='Model', readonly=True),
                'type_user_access_results_line': fields.selection(RESULT_TYPES_SELECTION, string='Type', readonly=True),
                'value_user_access_results_line': fields.char(string='Value', size=1024, readonly=True),
                }

user_access_results_groups_line()


class user_access_results_users_line(osv.osv_memory):
    _name = 'user.access.results.users.line'
    _inherit = 'user.access.results.groups.line'
    
user_access_results_users_line()


class user_access_results_menus_line(osv.osv_memory):
    _name = 'user.access.results.menus.line'
    _inherit = 'user.access.results.groups.line'
    
user_access_results_menus_line()


class res_groups(osv.osv):
    '''
    add an active column
    '''
    _inherit = 'res.groups'
    _columns = {'visible_res_groups': fields.boolean('Visible', readonly=True),
                'from_file_import_res_groups': fields.boolean('From file Import', readonly=True),
                }
    _defaults = {'visible_res_groups': True,
                 'from_file_import_res_groups': False,
                 }

res_groups()


class ir_model_access(osv.osv):
    _inherit = 'ir.model.access'
    
    def _ir_model_access_check_groups_hook(self, cr, uid, context=None, *args, **kwargs):
        '''
        Please copy this to your module's method also.
        This hook belongs to the check_groups method from server/bin/addons/base/ir>ir_model.py>ir_model_access
        
        - allow to modify the criteria for group display
        '''
        if context is None:
            context = {}
        
        never_displayed_groups = {'group_multi_company': 'base',
                                  'group_no_one': 'base',
                                  'group_product_variant': 'product'}
        
        # original criteria is not used at all -> no link with groups of the user as groups= stay in original openERP modules - only call if some // modifications are executed by the hooks
        super(ir_model_access, self)._ir_model_access_check_groups_hook(cr, uid, context=context, *args, **kwargs)
        group = kwargs['group']
        
        grouparr  = group.split('.')
        if not grouparr:
            return False
        # if the group belongs to group not to display, we return False
        # we check module *and* group name
        if grouparr[1] in never_displayed_groups.keys() and grouparr[0] == never_displayed_groups[grouparr[1]]:
            return False
        return True
    
ir_model_access()