Initial import from Docker volume
This commit is contained in:
308
ir/ui/menu.py
Executable file
308
ir/ui/menu.py
Executable file
@@ -0,0 +1,308 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
from collections import defaultdict
|
||||
from itertools import groupby
|
||||
|
||||
from trytond.model import (
|
||||
DeactivableMixin, ModelSQL, ModelView, fields, sequence_ordered, tree)
|
||||
from trytond.pool import Pool
|
||||
from trytond.rpc import RPC
|
||||
from trytond.tools import grouped_slice
|
||||
from trytond.transaction import Transaction, inactive_records
|
||||
|
||||
|
||||
def one_in(i, j):
|
||||
"""Check the presence of an element of setA in setB
|
||||
"""
|
||||
for k in i:
|
||||
if k in j:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
CLIENT_ICONS = [(x, x) for x in [
|
||||
'tryton-add',
|
||||
'tryton-archive',
|
||||
'tryton-attach',
|
||||
'tryton-back',
|
||||
'tryton-barcode-scanner',
|
||||
'tryton-bookmark-border',
|
||||
'tryton-bookmark',
|
||||
'tryton-bookmarks',
|
||||
'tryton-cancel',
|
||||
'tryton-clear',
|
||||
'tryton-close',
|
||||
'tryton-copy',
|
||||
'tryton-create',
|
||||
'tryton-date',
|
||||
'tryton-delete',
|
||||
'tryton-download',
|
||||
'tryton-email',
|
||||
'tryton-error',
|
||||
'tryton-exit',
|
||||
'tryton-export',
|
||||
'tryton-filter',
|
||||
'tryton-format-align-center',
|
||||
'tryton-format-align-justify',
|
||||
'tryton-format-align-left',
|
||||
'tryton-format-align-right',
|
||||
'tryton-format-bold',
|
||||
'tryton-format-color-text',
|
||||
'tryton-format-italic',
|
||||
'tryton-format-underline',
|
||||
'tryton-forward',
|
||||
'tryton-history',
|
||||
'tryton-import',
|
||||
'tryton-info',
|
||||
'tryton-launch',
|
||||
'tryton-link',
|
||||
'tryton-log',
|
||||
'tryton-menu',
|
||||
'tryton-note',
|
||||
'tryton-ok',
|
||||
'tryton-open',
|
||||
'tryton-print',
|
||||
'tryton-public',
|
||||
'tryton-refresh',
|
||||
'tryton-remove',
|
||||
'tryton-save',
|
||||
'tryton-search',
|
||||
'tryton-sound-off',
|
||||
'tryton-sound-on',
|
||||
'tryton-star-border',
|
||||
'tryton-star',
|
||||
'tryton-switch',
|
||||
'tryton-translate',
|
||||
'tryton-unarchive',
|
||||
'tryton-undo',
|
||||
'tryton-warning',
|
||||
]]
|
||||
|
||||
|
||||
class UIMenu(
|
||||
DeactivableMixin,
|
||||
sequence_ordered(order='ASC NULLS LAST'),
|
||||
tree(separator=' / '),
|
||||
ModelSQL, ModelView):
|
||||
"UI menu"
|
||||
__name__ = 'ir.ui.menu'
|
||||
|
||||
name = fields.Char('Menu', required=True, translate=True)
|
||||
childs = fields.One2Many('ir.ui.menu', 'parent', 'Children')
|
||||
parent = fields.Many2One('ir.ui.menu', 'Parent Menu', ondelete='CASCADE')
|
||||
complete_name = fields.Function(fields.Char('Complete Name'),
|
||||
'get_rec_name', searcher='search_rec_name')
|
||||
icon = fields.Selection('list_icons', 'Icon', translate=False)
|
||||
action = fields.Function(fields.Reference('Action',
|
||||
selection=[
|
||||
('', ''),
|
||||
('ir.action.report', 'ir.action.report'),
|
||||
('ir.action.act_window', 'ir.action.act_window'),
|
||||
('ir.action.wizard', 'ir.action.wizard'),
|
||||
('ir.action.url', 'ir.action.url'),
|
||||
], translate=False), 'get_action', setter='set_action')
|
||||
action_keywords = fields.One2Many(
|
||||
'ir.action.keyword', 'model', "Action Keywords",
|
||||
filter=[
|
||||
('keyword', '=', 'tree_open'),
|
||||
])
|
||||
favorite = fields.Function(fields.Boolean('Favorite'), 'get_favorite')
|
||||
|
||||
@classmethod
|
||||
def order_complete_name(cls, tables):
|
||||
return cls.name.convert_order('name', tables, cls)
|
||||
|
||||
@staticmethod
|
||||
def default_icon():
|
||||
return 'tryton-folder'
|
||||
|
||||
@classmethod
|
||||
def default_sequence(cls):
|
||||
return 50
|
||||
|
||||
@staticmethod
|
||||
def list_icons():
|
||||
pool = Pool()
|
||||
Icon = pool.get('ir.ui.icon')
|
||||
return sorted(CLIENT_ICONS
|
||||
+ [(name, name) for _, name in Icon.list_icons()])
|
||||
|
||||
@classmethod
|
||||
def search_global(cls, text):
|
||||
# TODO improve search clause
|
||||
for record in cls.search([
|
||||
('rec_name', 'ilike', '%%%s%%' % text),
|
||||
]):
|
||||
if record.action_keywords:
|
||||
yield record, record.rec_name, record.icon
|
||||
|
||||
@classmethod
|
||||
def search(cls, domain, offset=0, limit=None, order=None, count=False,
|
||||
query=False):
|
||||
pool = Pool()
|
||||
ModelAccess = pool.get('ir.model.access')
|
||||
transaction = Transaction()
|
||||
|
||||
def has_action_access(menu):
|
||||
if menu.action_keywords and all(
|
||||
k.action.type == 'ir.action.act_window'
|
||||
for k in menu.action_keywords):
|
||||
for keyword in menu.action_keywords:
|
||||
res_model = keyword.action.action.res_model
|
||||
if not res_model:
|
||||
break
|
||||
if ModelAccess.check(res_model, raise_exception=False):
|
||||
break
|
||||
else:
|
||||
return False
|
||||
return True
|
||||
|
||||
menus = super(UIMenu, cls).search(domain, offset=offset, limit=limit,
|
||||
order=order, count=False, query=query)
|
||||
if query:
|
||||
return menus
|
||||
|
||||
if transaction.check_access and menus:
|
||||
|
||||
menus = list(filter(has_action_access, menus))
|
||||
|
||||
parent_ids = {x.parent.id for x in menus if x.parent}
|
||||
parents = set()
|
||||
for sub_parent_ids in grouped_slice(parent_ids):
|
||||
parents.update(cls.search([
|
||||
('id', 'in', list(sub_parent_ids)),
|
||||
]))
|
||||
# Re-browse to avoid side-cache access
|
||||
menus = cls.browse([x.id for x in menus
|
||||
if (x.parent and x.parent in parents) or not x.parent])
|
||||
|
||||
if count:
|
||||
return len(menus)
|
||||
return menus
|
||||
|
||||
@classmethod
|
||||
@inactive_records
|
||||
def get_action(cls, menus, name):
|
||||
pool = Pool()
|
||||
actions = dict((m.id, None) for m in menus)
|
||||
menus = cls.browse(menus)
|
||||
action_keywords = sum((list(m.action_keywords) for m in menus), [])
|
||||
|
||||
def action_type(keyword):
|
||||
return keyword.action.type
|
||||
action_keywords.sort(key=action_type)
|
||||
for type, action_keywords in groupby(action_keywords, key=action_type):
|
||||
action_keywords = list(action_keywords)
|
||||
action2keywords = defaultdict(list)
|
||||
for action_keyword in action_keywords:
|
||||
model = action_keyword.model
|
||||
actions[model.id] = '%s,-1' % type
|
||||
action2keywords[action_keyword.action.id].append(
|
||||
action_keyword)
|
||||
|
||||
Action = pool.get(type)
|
||||
factions = Action.search([
|
||||
('action', 'in', list(action2keywords.keys())),
|
||||
])
|
||||
for action in factions:
|
||||
for action_keyword in action2keywords[action.id]:
|
||||
actions[action_keyword.model.id] = str(action)
|
||||
return actions
|
||||
|
||||
@classmethod
|
||||
def set_action(cls, menus, name, value):
|
||||
pool = Pool()
|
||||
ActionKeyword = pool.get('ir.action.keyword')
|
||||
action_keywords = []
|
||||
transaction = Transaction()
|
||||
for i in range(0, len(menus), transaction.database.IN_MAX):
|
||||
sub_menus = menus[i:i + transaction.database.IN_MAX]
|
||||
action_keywords += ActionKeyword.search([
|
||||
('keyword', '=', 'tree_open'),
|
||||
('model', 'in', [str(menu) for menu in sub_menus]),
|
||||
])
|
||||
if action_keywords:
|
||||
with Transaction().set_context(_timestamp=False):
|
||||
ActionKeyword.delete(action_keywords)
|
||||
if not value:
|
||||
return
|
||||
if isinstance(value, str):
|
||||
action_type, action_id = value.split(',')
|
||||
else:
|
||||
action_type, action_id = value
|
||||
if int(action_id) <= 0:
|
||||
return
|
||||
Action = pool.get(action_type)
|
||||
action = Action(int(action_id))
|
||||
to_create = []
|
||||
for menu in menus:
|
||||
with Transaction().set_context(_timestamp=False):
|
||||
to_create.append({
|
||||
'keyword': 'tree_open',
|
||||
'model': str(menu),
|
||||
'action': action.action.id,
|
||||
})
|
||||
if to_create:
|
||||
ActionKeyword.create(to_create)
|
||||
|
||||
@classmethod
|
||||
def get_favorite(cls, menus, name):
|
||||
pool = Pool()
|
||||
Favorite = pool.get('ir.ui.menu.favorite')
|
||||
user = Transaction().user
|
||||
favorites = Favorite.search([
|
||||
('menu', 'in', [m.id for m in menus]),
|
||||
('user', '=', user),
|
||||
])
|
||||
menu2favorite = {
|
||||
m.id: False if m.action_keywords else None for m in menus}
|
||||
menu2favorite.update(dict((f.menu.id, True) for f in favorites))
|
||||
return menu2favorite
|
||||
|
||||
|
||||
class UIMenuFavorite(sequence_ordered(), ModelSQL, ModelView):
|
||||
"Menu Favorite"
|
||||
__name__ = 'ir.ui.menu.favorite'
|
||||
|
||||
menu = fields.Many2One('ir.ui.menu', 'Menu', required=True,
|
||||
ondelete='CASCADE')
|
||||
user = fields.Many2One('res.user', 'User', required=True,
|
||||
ondelete='CASCADE')
|
||||
|
||||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(UIMenuFavorite, cls).__setup__()
|
||||
cls.__rpc__.update({
|
||||
'get': RPC(check_access=False),
|
||||
'set': RPC(check_access=False, readonly=False),
|
||||
'unset': RPC(check_access=False, readonly=False),
|
||||
})
|
||||
|
||||
@staticmethod
|
||||
def default_user():
|
||||
return Transaction().user
|
||||
|
||||
@classmethod
|
||||
def get(cls):
|
||||
user = Transaction().user
|
||||
favorites = cls.search([
|
||||
('user', '=', user),
|
||||
])
|
||||
return [(f.menu.id, f.menu.rec_name, f.menu.icon) for f in favorites]
|
||||
|
||||
@classmethod
|
||||
def set(cls, menu_id):
|
||||
user = Transaction().user
|
||||
cls.create([{
|
||||
'menu': menu_id,
|
||||
'user': user,
|
||||
}])
|
||||
|
||||
@classmethod
|
||||
def unset(cls, menu_id):
|
||||
user = Transaction().user
|
||||
favorites = cls.search([
|
||||
('menu', '=', menu_id),
|
||||
('user', '=', user),
|
||||
])
|
||||
cls.delete(favorites)
|
||||
Reference in New Issue
Block a user