Files
tryton/trytond/ir/ui/menu.py
T
2016-03-03 21:51:57 +01:00

330 lines
10 KiB
Python

# 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 itertools import groupby
from sql import Null
from sql.conditionals import Case
from trytond.model import ModelView, ModelSQL, fields
from trytond.transaction import Transaction
from trytond.pool import Pool
from trytond.rpc import RPC
__all__ = [
'UIMenu', 'UIMenuFavorite',
]
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-attachment-hi',
'tryton-attachment',
'tryton-cancel',
'tryton-clear',
'tryton-close',
'tryton-connect',
'tryton-copy',
'tryton-delete',
'tryton-dialog-error',
'tryton-dialog-information',
'tryton-dialog-warning',
'tryton-disconnect',
'tryton-executable',
'tryton-find-replace',
'tryton-find',
'tryton-folder-new',
'tryton-fullscreen',
'tryton-go-home',
'tryton-go-jump',
'tryton-go-next',
'tryton-go-previous',
'tryton-help',
'tryton-icon',
'tryton-list-add',
'tryton-list-remove',
'tryton-locale',
'tryton-lock',
'tryton-log-out',
'tryton-mail-message-new',
'tryton-mail-message',
'tryton-new',
'tryton-ok',
'tryton-open',
'tryton-preferences-system-session',
'tryton-preferences-system',
'tryton-preferences',
'tryton-print',
'tryton-refresh',
'tryton-save-as',
'tryton-save',
'tryton-start-here',
'tryton-system-file-manager',
'tryton-system',
'tryton-undo',
'tryton-web-browser')]
SEPARATOR = ' / '
class UIMenu(ModelSQL, ModelView):
"UI menu"
__name__ = 'ir.ui.menu'
name = fields.Char('Menu', required=True, translate=True)
sequence = fields.Integer('Sequence', required=True)
childs = fields.One2Many('ir.ui.menu', 'parent', 'Children')
parent = fields.Many2One('ir.ui.menu', 'Parent Menu', select=True,
ondelete='CASCADE')
groups = fields.Many2Many('ir.ui.menu-res.group',
'menu', 'group', 'Groups')
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'),
]), 'get_action', setter='set_action')
action_keywords = fields.One2Many('ir.action.keyword', 'model',
'Action Keywords')
active = fields.Boolean('Active')
favorite = fields.Function(fields.Boolean('Favorite'), 'get_favorite')
@classmethod
def __setup__(cls):
super(UIMenu, cls).__setup__()
cls._order.insert(0, ('sequence', 'ASC'))
cls._error_messages.update({
'wrong_name': ('"%%s" is not a valid menu name because it is '
'not allowed to contain "%s".' % SEPARATOR),
})
@classmethod
def order_complete_name(cls, tables):
return cls.name.convert_order('name', tables, cls)
@staticmethod
def default_icon():
return 'tryton-open'
@staticmethod
def default_sequence():
return 10
@staticmethod
def default_active():
return True
@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 validate(cls, menus):
super(UIMenu, cls).validate(menus)
cls.check_recursion(menus)
for menu in menus:
menu.check_name()
def check_name(self):
if SEPARATOR in self.name:
self.raise_user_error('wrong_name', (self.name,))
def get_rec_name(self, name):
parent = self.parent
name = self.name
while parent:
name = parent.name + SEPARATOR + name
parent = parent.parent
return name
@classmethod
def search_rec_name(cls, name, clause):
if isinstance(clause[2], basestring):
values = clause[2].split(SEPARATOR.strip())
values.reverse()
domain = []
field = 'name'
for name in values:
domain.append((field, clause[1], name.strip()))
field = 'parent.' + field
else:
domain = [('name',) + tuple(clause[1:])]
ids = [m.id for m in cls.search(domain, order=[])]
return [('parent', 'child_of', ids)]
@classmethod
def search_global(cls, text):
# TODO improve search clause
for record in cls.search([
('rec_name', 'ilike', '%%%s%%' % text),
]):
if record.action:
yield record, record.rec_name, record.icon
@classmethod
def search(cls, domain, offset=0, limit=None, order=None, count=False,
query=False):
menus = super(UIMenu, cls).search(domain, offset=offset, limit=limit,
order=order, count=False, query=query)
if query:
return menus
if menus:
parent_ids = [x.parent.id for x in menus if x.parent]
parents = cls.search([
('id', 'in', parent_ids),
])
menus = [x for x in menus
if (x.parent and x.parent in parents) or not x.parent]
if count:
return len(menus)
return menus
@classmethod
def get_action(cls, menus, name):
pool = Pool()
actions = dict((m.id, None) for m in menus)
with Transaction().set_context(active_test=False):
menus = cls.browse(menus)
action_keywords = sum((list(m.action_keywords) for m in menus), [])
key = lambda k: k.action.type
action_keywords.sort(key=key)
for type, action_keywords in groupby(action_keywords, key=key):
action_keywords = list(action_keywords)
for action_keyword in action_keywords:
model = action_keyword.model
actions[model.id] = '%s,-1' % type
Action = pool.get(type)
action2keyword = {k.action.id: k for k in action_keywords}
with Transaction().set_context(active_test=False):
factions = Action.search([
('action', 'in', action2keyword.keys()),
])
for action in factions:
model = action2keyword[action.id].model
actions[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, basestring):
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 = dict((m.id, False if m.action else None)
for m in menus)
menu2favorite.update(dict((f.menu.id, True) for f in favorites))
return menu2favorite
class UIMenuFavorite(ModelSQL, ModelView):
"Menu Favorite"
__name__ = 'ir.ui.menu.favorite'
menu = fields.Many2One('ir.ui.menu', 'Menu', required=True,
ondelete='CASCADE')
sequence = fields.Integer('Sequence')
user = fields.Many2One('res.user', 'User', required=True,
ondelete='CASCADE')
@classmethod
def __setup__(cls):
super(UIMenuFavorite, cls).__setup__()
cls.__rpc__.update({
'get': RPC(),
'set': RPC(readonly=False),
'unset': RPC(readonly=False),
})
cls._order = [
('sequence', 'ASC'),
('id', 'DESC'),
]
@staticmethod
def order_sequence(tables):
table, _ = tables[None]
return [Case((table.sequence == Null, 0), else_=1), table.sequence]
@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)