def get_search_default(attrs, screen_context=None, default_domain=[]): screen_context = screen_context or {} if 'context' in attrs: ctx = expr_eval(attrs.get('context', "{}"), {'self':attrs.get('name', False)}) group_by = ctx.get('group_by') if group_by: if isinstance(group_by, list): str_ctx = map(lambda x: 'group_' + x, group_by) else: str_ctx = 'group_' + group_by result = str_ctx in screen_context.get('group_by', []) return result or attrs.get('name') and screen_context.get('search_default_' + attrs.get('name')) if cherrypy.request.path_info != '/openerp/listgrid/get' and 'name' in attrs: search_default = screen_context.get('search_default_' + attrs['name']) if search_default: # The value of search_default_$field can be either a literal # (integer, string, ...) or the name of a search_default context # variable. Try .get()ing the value in case it's a context object, # otherwise return the literal itself # ... # or so I expected, apparently somebody else already did the # resolution of those, so either we have a truthy value and we # return it, or we don't and we don't do anything return search_default return False
def get_search_default(attrs, screen_context=None, default_domain=[]): screen_context = screen_context or {} if 'context' in attrs: ctx = expr_eval(attrs.get('context', "{}"), {'self': attrs.get('name', False)}) group_by = ctx.get('group_by') if group_by: if isinstance(group_by, list): str_ctx = map(lambda x: 'group_' + x, group_by) else: str_ctx = 'group_' + group_by result = str_ctx in screen_context.get('group_by', []) return result or attrs.get('name') and screen_context.get( 'search_default_' + attrs.get('name')) if cherrypy.request.path_info != '/openerp/listgrid/get' and 'name' in attrs: search_default = screen_context.get('search_default_' + attrs['name']) if search_default: # The value of search_default_$field can be either a literal # (integer, string, ...) or the name of a search_default context # variable. Try .get()ing the value in case it's a context object, # otherwise return the literal itself # ... # or so I expected, apparently somebody else already did the # resolution of those, so either we have a truthy value and we # return it, or we don't and we don't do anything return search_default return False
def create_form(self, params, tg_errors=None): params.id = params.o2m_id params.model = params.o2m_model params.view_mode = ['form', 'tree'] params.view_type = 'form' #XXX: dirty hack to fix bug #401700 if not params.get('_terp_view_ids'): params['_terp_view_ids'] = [] # to get proper view, first generate form using the view_params vp = params.view_params form = tw.form_view.ViewForm(vp, name="view_form", action="/openerp/openo2m/save") cherrypy.request.terp_validators = {} if '/' in params.o2m: params.o2m = '.'.join(params.o2m.split('/')[-1:]) wid = form.screen.widget.get_widgets_by_name(params.o2m)[0] # save view_params for later phazes vp = vp.make_plain('_terp_view_params/') hiddens = map(lambda x: tw.form.Hidden(name=x, default=ustr(vp[x])), vp) params.prefix = params.o2m params.views = wid.view params.hide_new_button = False ctx = params.context or {} ctx.update(params.parent_context or {}) ctx.update(params.o2m_context or {}) p, ctx = TinyDict.split(ctx) if ctx.get('default_name'): del ctx['default_name'] arch = params.views.get('form', {}).get('arch', False) if arch: dom = xml.dom.minidom.parseString(arch.encode('utf-8')) form_attribute = node_attributes(dom.childNodes[0]) if form_attribute.get('hide_new_button'): params.hide_new_button = expr_eval(form_attribute.get('hide_new_button', False), {'context': ctx}) params.context = ctx or {} params.hidden_fields = [tw.form.Hidden(name='_terp_parent_model', default=params.parent_model), tw.form.Hidden(name='_terp_parent_id', default=params.parent_id), tw.form.Hidden(name='_terp_parent_context', default=ustr(params.parent_context)), tw.form.Hidden(name='_terp_o2m', default=params.o2m), tw.form.Hidden(name='_terp_o2m_id', default=params.id or None), tw.form.Hidden(name='_terp_o2m_model', default=params.o2m_model), tw.form.Hidden(name='_terp_o2m_context', default=ustr(params.o2m_context or {})), tw.form.Hidden(name=params.prefix + '/__id', default=params.id or None)] + hiddens form = tw.form_view.ViewForm(params, name="view_form", action="/openerp/openo2m/save") form.screen.string = wid.screen.string return form
def expr_eval(self, expr, source=None): if not isinstance(expr, basestring): return expr return expr_eval( expr, dict(self, context=self.params.context or {}, active_id=self.get('id', False)))
def update_params(self, params): super(M2O, self).update_params(params) if params['value'] and not params['text']: try: value = expr_eval(params['value'], {'context':rpc.get_session().context}) except: value = params['value'] params['text'] = rpc.name_get(self.relation, value, rpc.get_session().context)
def __init__(self, **attrs): super(Filter, self).__init__(**attrs) default_domain = attrs.get('default_domain') self.global_domain = [] self.icon = attrs.get('icon') self.filter_domain = attrs.get('domain', []) self.help = attrs.get('help') self.filter_id = 'filter_%s' % (random.randint(0,10000)) filter_context = attrs.get('context') screen_context = attrs.get('screen_context', {}) self.def_checked = False self.groupcontext = [] default_search = get_search_default(attrs, screen_context, default_domain) # context implemented only for group_by. self.group_context = None if filter_context: self.filter_context = eval(filter_context) self.group_context = self.filter_context.get('group_by', False) if self.group_context: if isinstance(self.group_context, list): self.group_context = map(lambda x: 'group_' + x, self.group_context) else: self.group_context = 'group_' + self.group_context if default_search: self.def_checked = True self.global_domain += (expr_eval(self.filter_domain, {'context':screen_context})) if self.group_context: self.groupcontext = self.group_context self.nolabel = True self.readonly = False if self.filter_context: if not self.filter_context.get('group_by'): self.filter_context = self.filter_context else: self.filter_context = {} self.text_val = self.string or self.help if self.icon: self.icon = icons.get_icon(self.icon) if self.string == self.help: self.help = None self.first_box = attrs.get('first_box') self.last_box = attrs.get('last_box') self.first_last_box = attrs.get('first_last_box') if not self.def_checked and attrs.get('group_by_ctx'): if self.group_context in attrs['group_by_ctx']: self.def_checked = True
def update_params(self, params): super(M2O, self).update_params(params) if params['value'] and not params['text']: try: value = expr_eval(params['value'], {'context': rpc.session.context}) except: value = params['value'] params['text'] = rpc.name_get(self.relation, value, rpc.session.context)
def action(self, **kw): params, data = TinyDict.split(kw) context_menu = kw.get('context_menu') or False id = params.id or False ids = params.selection or [] if not ids and id: ids = [id] if not id and ids: id = ids[0] domain = params.domain or [] context = params.context or {} action = {} if data.get('datas'): action = eval(data.get('datas')) type = action.get('type') act_id = params.action if not act_id: return self.do_action('client_action_multi', datas=kw) if type is None: action_type = rpc.RPCProxy('ir.actions.actions').read( act_id, ['type'], context)['type'] tmp_ctx = dict(context) if action_type == 'ir.actions.report.xml': # avoid reading large binary values that we won't even care about tmp_ctx['bin_size'] = True action = rpc.session.execute('object', 'execute', action_type, 'read', act_id, False, tmp_ctx) if domain: if isinstance(domain, basestring): domain = eval(domain) domain.extend(expr_eval(action.get('domain', '[]'), context)) action['domain'] = ustr(domain) if context.get('search_view'): context.pop('search_view') action['form_context'] = context or {} import actions return actions.execute(action, model=params.model, id=id, ids=ids, report_type='pdf', context_menu=context_menu)
def execute(action, **data): """Execute the action with the provided data. for internal use only. @param action: the action @param data: the data @return: mostly XHTML code """ if 'type' not in action: #XXX: in gtk client just returns to the caller #raise common.error('Error', 'Invalid action...') return; data.setdefault('context', {}).update(expr_eval(action.get('form_context', '{}') or action.get('context','{}'), data.get('context', {}))) action_executor = ACTIONS_BY_TYPE[action['type']] return action_executor(action, data)
def action(self, **kw): params, data = TinyDict.split(kw) context_menu = kw.get('context_menu') or False id = params.id or False ids = params.selection or [] if not ids and id: ids = [id] if not id and ids: id = ids[0] domain = params.domain or [] context = params.context or {} action = {} if data.get('datas'): action = eval(data.get('datas')) type = action.get('type') act_id = params.action if not act_id: return self.do_action('client_action_multi', datas=kw) if type is None: action_type = rpc.RPCProxy('ir.actions.actions').read(act_id, ['type'], context)['type'] tmp_ctx = dict(context) if action_type == 'ir.actions.report.xml': # avoid reading large binary values that we won't even care about tmp_ctx['bin_size'] = True action = rpc.session.execute('object', 'execute', action_type, 'read', act_id, False, tmp_ctx) if domain: if isinstance(domain, basestring): domain = eval(domain) domain.extend(expr_eval(action.get('domain', '[]'), context)) action['domain'] = ustr(domain) if context.get('search_view'): context.pop('search_view') action['form_context'] = context or {} import actions return actions.execute(action, model=params.model, id=id, ids=ids, report_type='pdf', context_menu=context_menu)
def execute(action, **data): """Execute the action with the provided data. for internal use only. @param action: the action @param data: the data @return: mostly XHTML code """ if 'type' not in action: #XXX: in gtk client just returns to the caller #raise common.error('Error', 'Invalid action...') return data.setdefault('context', {}).update( expr_eval( action.get('form_context', '{}') or action.get('context', '{}'), data.get('context', {}))) action_executor = ACTIONS_BY_TYPE[action['type']] return action_executor(action, data)
def action(self, **kw): params, data = TinyDict.split(kw) context_menu = kw.get('context_menu') id = params.id or False ids = params.selection or [] if not ids and id: ids = [id] if not id and ids: id = ids[0] domain = params.domain or [] context = params.context or {} if not context and rpc.session.context: context['lang'] = rpc.session.context.get('lang') action = {} if data.get('datas'): action = eval(data.get('datas')) type = action.get('type') act_id = params.action if not act_id: return self.do_action('client_action_multi', datas=kw) if type is None: action_type = rpc.RPCProxy('ir.actions.actions').read(act_id, ['type'], context)['type'] action = rpc.session.execute('object', 'execute', action_type, 'read', act_id, False, context) if domain: if isinstance(domain, basestring): domain = eval(domain) domain.extend(expr_eval(action.get('domain', '[]'), context)) action['domain'] = ustr(domain) action['form_context'] = context or {} import actions return actions.execute(action, model=params.model, id=id, ids=ids, report_type='pdf', context_menu=context_menu)
def do_filter_sc(self, **kw): name = kw.get('name') model = kw.get('model') domain = kw.get('domain') group_by = kw.get('group_by', []) if isinstance(group_by, (str,unicode)): group_by = expr_eval(group_by) if group_by: context = {'group_by': group_by} else: context = {} if name: datas = { 'name':name, 'model_id':model, 'domain':domain, 'context':str(context), 'user_id':rpc.session.uid } result = rpc.session.execute('object', 'execute', 'ir.filters', 'create_or_replace', datas, rpc.session.context) return {'filter': (domain, name, group_by), 'new_id':result} return {}
def build_row(self, row): row = row.copy() # compute color once for whole row rowcolor = None for color, expr in self.colors.items(): try: if expr_eval(expr, dict(row, active_id=rpc.session.active_id or False)): rowcolor = color break except Exception: pass for (name, invisible, cell) in self.build_new_fields(self.fields): if invisible: cell.set_value(row.get(name, False)) else: cell.value = row.get(name, False) cell.text = cell.get_text() cell.link = cell.get_link() cell.color = rowcolor row[name] = cell return row
def build_row(self, row): row = row.copy() # compute color once for whole row rowcolor = None for color, expr in self.colors.items(): try: if expr_eval( expr, dict(row, active_id=rpc.session.active_id or False)): rowcolor = color break except Exception: pass for (name, invisible, cell) in self.build_new_fields(self.fields): if invisible: cell.set_value(row.get(name, False)) else: cell.value = row.get(name, False) cell.text = cell.get_text() cell.link = cell.get_link() cell.color = rowcolor row[name] = cell return row
def do_filter_sc(self, **kw): name = kw.get('name') model = kw.get('model') domain = kw.get('domain') group_by = kw.get('group_by', []) if isinstance(group_by, (str, unicode)): group_by = expr_eval(group_by) if group_by: context = {'group_by': group_by} else: context = {} if name: datas = { 'name': name, 'model_id': model, 'domain': domain, 'context': str(context), 'user_id': rpc.session.uid } result = rpc.session.execute('object', 'execute', 'ir.filters', 'create_or_replace', datas, rpc.session.context) return {'filter': (domain, name, group_by), 'new_id': result} return {}
def data(self, ids, model, fields, field_parent=None, icon_name=None, domain=[], context={}, sort_by=None, sort_order="asc", fields_info=None, colors={}): if ids == 'None' or ids == '': ids = [] if isinstance(ids, basestring): ids = map(int, ids.split(',')) elif isinstance(ids, list): ids = map(int, ids) if isinstance(fields, basestring): fields = eval(fields) if isinstance(domain, basestring): domain = eval(domain) if isinstance(context, basestring): context = eval(context) if isinstance(colors, basestring): colors = eval(colors) if isinstance(fields_info, basestring): fields_info = simplejson.loads(fields_info) if field_parent and field_parent not in fields: fields.append(field_parent) proxy = rpc.RPCProxy(model) ctx = dict(context, **rpc.session.context) if icon_name: fields.append(icon_name) if model == 'ir.ui.menu' and 'action' not in fields: fields.append('action') result = proxy.read(ids, fields, ctx) if sort_by: fields_info_type = simplejson.loads(fields_info[sort_by]) result.sort(lambda a, b: self.sort_callback( a, b, sort_by, sort_order, type=fields_info_type['type'])) for item in result: if colors: for color, expr in colors.items(): try: if expr_eval(expr, item or False): item['color'] = color break except: pass # format the data for field in fields: field_info = simplejson.loads(fields_info[field]) if field_info.get('widget', ''): field_info['type'] = field_info['widget'] formatter = FORMATTERS.get(field_info['type']) for x in result: if x[field] and formatter: x[field] = formatter(x[field], field_info) records = [] for item in result: # empty string instead of False or None for k, v in item.items(): if v is None or v is False: item[k] = '' id = item.pop('id') record = { 'id': id, 'action': url('/openerp/tree/open', model=model, id=id, context=ctx), 'target': None, 'icon': None, 'children': [], 'items': item } if icon_name and item.get(icon_name): icon = item.pop(icon_name) record['icon'] = icons.get_icon(icon) if field_parent and field_parent in item: record['children'] = item.pop(field_parent) or None # For nested menu items, remove void action url # to suppress 'No action defined' error. if (model == 'ir.ui.menu' and record['children'] and not item['action']): record['action'] = None records.append(record) return {'records': records}
def __init__(self, **attrs): super(Action, self).__init__(**attrs) self.nolabel = True self.act_id= self.name proxy = rpc.RPCProxy("ir.actions.actions") res = proxy.read([self.act_id], ['type'], rpc.session.context) if not res: raise common.message(_('Action not found')) _type=res[0]['type'] self.action = rpc.session.execute('object', 'execute', _type, 'read', [self.act_id], False, rpc.session.context)[0] if 'view_mode' in attrs: self.action['view_mode'] = attrs['view_mode'] if self.action['type']=='ir.actions.act_window': if not self.action.get('domain', False): self.action['domain']='[]' ctx = dict(rpc.session.context, active_id=False, active_ids=[]) self.context = expr_eval(self.action.get('context', '{}'), ctx) self.domain = expr_eval(self.action['domain'], ctx) views = dict(map(lambda x: (x[1], x[0]), self.action['views'])) view_mode = self.action.get('view_mode', 'tree,form').split(',') view_ids = map(lambda x: views.get(x, False), view_mode) if views.keys() != view_mode: view_mode = map(lambda x: x[1], self.action['views']) view_ids = map(lambda x: x[0], self.action['views']) if self.action['view_type'] == 'form': params = TinyDict() params.updateAttrs( model=self.action['res_model'], id=False, ids=None, view_ids=view_ids, view_mode=view_mode, context=self.context, domain=self.domain, offset = 0, limit = 50 ) # get pager vars if set if hasattr(cherrypy.request, 'terp_params'): current = cherrypy.request.terp_params current = current.chain_get(self.name or '') or current params.updateAttrs( offset=current.offset, limit=current.limit ) self.screen = screen.Screen(params, prefix=self.name, editable=True, selectable=3) elif self.action['view_type']=='tree': pass #TODO
def __init__(self, **attrs): #FIXME: validation error in `Pricelist Version` attrs['required'] = False super(O2M, self).__init__(**attrs) self.new_attrs = { 'text': _("New"), 'help': _('Create new record.')} self.default_get_ctx = attrs.get('default_get', {}) or attrs.get('context', {}) # get top params dictionary params = cherrypy.request.terp_params self.source = params.source self.edition = params.o2m_edit pprefix = '' if '/' in self.name: pprefix = self.name[:self.name.rindex('/')] pparams = params.chain_get(pprefix) if (pparams and not pparams.id) or (not pparams and not params.id): self.new_attrs = { 'text': _("Save/New"), 'help': _('Save parent record.')} self.parent_id = params.id if pparams: self.parent_id = pparams.id # get params for this field current = params.chain_get(self.name) self.model = attrs['relation'] self.link = attrs.get('link', '') self.onchange = None # override onchange in js code view = attrs.get('views', {}) mode = str(attrs.get('mode', 'tree,form')).split(',') self.view = view view_mode = mode view_type = mode[0] self.view_type = view_type if not current: current = TinyDict() if current.view_mode: view_mode = current.view_mode if current.view_type: view_type = current.view_type self.switch_to = view_mode[-1] if view_type == view_mode[-1]: self.switch_to = view_mode[0] ids = attrs.get('value') or [] if not isinstance(ids, list): ids = [ids] if ids: if isinstance(ids[0], dict): current.default_data = ids for item in current.default_data: self.default_value.append( OneToMany.create(item)) item['id'] = 0 ids = [] elif isinstance(ids[0], tuple): [current_id[1] for current_id in ids] id = (ids or None) and ids[0] if self.name == self.source or self.name == params.source: if params.sort_key and ids: domain = current.domain or [] domain.append(('id', 'in', ids)) ids = rpc.RPCProxy(self.model).search(domain, current.offset, current.limit, params.sort_key + ' '+params.sort_order, current.context) id = ids[0] if current and params.source and self.name in params.source.split('/'): id = current.id id = id or None current.model = self.model current.id = id current.ids = ids current.view_mode = view_mode current.view_type = view_type current.domain = current.domain or [] current.context = current.context or {} group_by_ctx = '' if self.default_get_ctx: ctx = dict(cherrypy.request.terp_record, context=current.context, active_id=self.parent_id or False) ctx[attrs['name']] = ids # XXX: parent record for O2M #if self.parent: # ctx['parent'] = EvalEnvironment(self.parent) try: context = ctx.copy() ctx = expr_eval("dict(%s)" % self.default_get_ctx, context) ctx.update(expr_eval("dict(%s)" % attrs.get('context', '{}'), context)) current.context.update(ctx) except: pass if ctx and ctx.get('group_by'): group_by_ctx = ctx.get('group_by') current.offset = current.offset or 0 current.limit = current.limit or 50 current.count = len(ids or []) # Group By for one2many list. if group_by_ctx: current.group_by_ctx = group_by_ctx current.domain = [('id', 'in', ids)] if current.view_type == 'tree' and self.readonly: self.editable = False if 'default_name' in current.context: del current.context['default_name'] if self.view_type == 'tree' and pparams: self.editable = bool(pparams.id) self.screen = Screen(current, prefix=self.name, views_preloaded=view, editable=self.editable, readonly=self.readonly, selectable=0, nolinks=self.link, _o2m=1) self.id = id self.ids = ids if view_type == 'tree': self.id = None elif view_type == 'form': records_count = len(self.screen.ids or []) current_record = 0 if records_count and self.screen.id in self.screen.ids: current_record = self.screen.ids.index(self.screen.id) + 1 self.pager_info = _('%d of %d') % (current_record, records_count) else: self.pager_info = _('- of %d') % (records_count)
parent_context.update( active_id=params.active_id or False, active_ids=params.active_ids or []) ctx.update( parent=pctx, context=parent_context, active_id=params.active_id or False, active_ids=params.active_ids or [] ) if params.active_id and not params.active_ids: ctx['active_ids'] = [params.active_id] if domain and isinstance(domain, basestring): domain = expr_eval(domain, ctx) if domain and len(domain) >= 2 and domain[-2] in ['&', '|']: # For custom domain ('AND', OR') from search view. dom1 = domain[-1:] dom2 = domain[:-2] domain = dom2 + dom1 if context and isinstance(context, basestring): if not context.startswith('{'): context = "dict(%s)"%context ctx['dict'] = dict # required context = expr_eval(context, ctx) # Fixed many2one pop up in listgrid when value is None. for key, val in context.items():
def act_window(action, data): if not action.get('opened'): action.setdefault('target', 'current') return act_window_opener(action, data) for key in ('res_id', 'res_model', 'view_type', 'view_mode', 'limit', 'search_view'): data[key] = action.get(key, data.get(key)) if not data.get('search_view') and data.get('search_view_id'): data['search_view'] = str( rpc.session.execute('object', 'execute', data['res_model'], 'fields_view_get', data['search_view_id'], 'search', data['context'])) if data.get('limit'): data['limit'] = 20 if action.get('target') and action['target'] == 'popup' and action.get( 'res_model') and isinstance(action.get('context'), dict): search_view_id = rpc.RPCProxy('ir.ui.view').search( [('type', '=', 'search'), ('model', '=', action['res_model'])], 0, 0, 0, rpc.session.context) if search_view_id and action['context'].get('search_view'): action['context']['search_view'] = search_view_id[0] view_ids = False if action.get('views', []): if isinstance(action['views'], list): view_ids = [x[0] for x in action['views']] data['view_mode'] = ",".join([x[1] for x in action['views']]) else: if action.get('view_id'): view_ids = [action['view_id'][0]] elif action.get('view_id'): view_ids = [action['view_id'][0]] if not action.get('domain'): action['domain'] = '[]' ctx = dict(data.get('context', {}), active_id=data.get('id', False), active_ids=data.get('ids', []), active_model=data.get('model', False)) if action.get('context') and isinstance(action['context'], dict): if not action['context'].get('active_ids'): action['context']['active_ids'] = ctx['active_ids'] or [] ctx.update(expr_eval(action.get('context', '{}'), ctx)) search_view = action.get('search_view_id') if search_view: if isinstance(search_view, (list, tuple)): ctx['search_view'] = search_view[0] else: ctx['search_view'] = search_view # save active_id in session rpc.session.active_id = data.get('id') domain = expr_eval(action['domain'], ctx) if data.get('domain'): domain.append(data['domain']) if 'menu' in data['res_model'] and action.get('name') == 'Menu': return close_popup() if action.get('display_menu_tip'): display_menu_tip = action.get('help') else: display_menu_tip = None return execute_window(view_ids, data['res_model'], data['res_id'], domain, action['view_type'], ctx, data['view_mode'], name=action.get('name'), target=action.get('target'), limit=data.get('limit'), search_view=data['search_view'], context_menu=data.get('context_menu'), display_menu_tip=display_menu_tip, action_id=action.get('id'))
def parse(self, model=None, root=None, fields=None, values={}): views = [] search_model = model filters_run = [] for node in root.childNodes: if not node.nodeType==node.ELEMENT_NODE: continue if filters_run and node.localName != 'filter': views.append(FiltersGroup(children=filters_run)) filters_run = [] attrs = node_attributes(node) attrs.update(is_search=True, model=search_model) if 'nolabel' in attrs: attrs['nolabel'] = False if node.localName in ('form', 'tree', 'search', 'group'): if node.localName == 'group': attrs['group_by_ctx'] = values.get('group_by_ctx') attrs['expand'] = expr_eval(attrs.get('expand',False),{'context':self.context}) Element = Group else: Element = Frame views.append(Element(children= self.parse(model=search_model, root=node, fields=fields, values=values), **attrs)) elif node.localName=='newline': views.append(NewLine(**attrs)) elif node.localName=='filter': attrs.update( model=search_model, default_domain=self.domain, screen_context=self.context) if values and values.get('group_by_ctx'): attrs['group_by_ctx'] = values['group_by_ctx'] elif self.groupby: attrs['group_by_ctx'] = self.groupby v = Filter(**attrs) if v.groupcontext and v.groupcontext not in self.groupby: self.groupby.append(v.groupcontext) self.listof_domain.extend(i for i in v.global_domain if not i in self.listof_domain) filters_run.append(v) elif node.localName == 'field': val = attrs.get('select', False) or fields[str(attrs['name'])].get('select', False) or self.view_type == 'search' if val: name = attrs['name'] if name in self.fields_type: continue # in search view fields should be writable attrs.update(readonly=False, required=False, translate=False, disabled=False, visible=True, invisible=False, editable=True, attrs=None) try: fields[name].update(attrs) except: print "-"*30,"\n malformed tag for:", attrs print "-"*30 raise if attrs.get('widget'): if attrs['widget'] == 'one2many_list': fields[name]['widget'] = 'one2many' fields[name]['type2'] = fields[name]['type'] fields[name]['type'] = attrs['widget'] kind = fields[name]['type'] if kind not in WIDGETS: continue if kind == 'many2one': attrs['relation'] = fields[name]['relation'] attrs['type'] = fields[name]['type'] if not attrs.get('string'): attrs['string'] = fields[name]['string'] self.fields_type[name] = kind field = WIDGETS[kind](**fields[name]) field.onchange = None field.callback = None if kind == 'boolean': # '0' in string because 0 is considering as No Value, and we need 0 as some value. field.options = [[1,_('Yes')],['0',_('No')]] field.validator.if_empty = '' default_search = None if name: default_search = get_search_default(fields[name], self.context, self.domain) defval = default_search if defval: model = fields[name].get('relation') type2 = fields[name].get('type2') if kind == 'many2one' and model: try: value = rpc.name_get(model, default_search, self.context) except Exception,e: value = defval defval = value or '' if attrs.get('filter_domain'): domain = expr_eval(attrs['filter_domain'], {'self': defval}) else: if field.kind == 'selection': if type2 == 'many2one': m2nval = defval try: m2nval = int(defval) except: # you see, since defval can basically be anything, including a totally # illogic value, my only possibility is to do return another illogic value, # so it won't crash too much pass domain = [(name, '=', m2nval)] else: domain = [(name, '=', defval)] elif field.kind in ('date','datetime'): domain = [(name, '>=', defval)] elif field.kind == 'boolean': domain = [(name, '=', defval)] else: domain = [(name,fields[name].get('comparator','ilike'), defval)] field.set_value(defval) self.listof_domain += [i for i in domain if not i in self.listof_domain] self.context.update(expr_eval(attrs.get('context',"{}"), {'self': default_search})) if (not default_search) and name in values and isinstance(field, TinyInputWidget): field.set_value(values[name]) views.append(field) for n in node.childNodes: if n.localName=='filter': attrs_child = dict( node_attributes(n), default_domain=self.domain, screen_context=self.context) if 'string' in attrs_child: del attrs_child['string'] if values and values.get('group_by_ctx'): attrs['group_by_ctx'] = values['group_by_ctx'] filter_field = Filter(**attrs_child) filter_field.onchange = None filter_field.callback = None if filter_field.groupcontext and filter_field.groupcontext not in self.groupby: self.groupby.append(filter_field.groupcontext) self.listof_domain.extend(i for i in filter_field.global_domain if i not in self.listof_domain) field.filters.append(filter_field)
def parse(self, root, fields, data=[]): """Parse the given node to generate valid list headers. @param root: the root node of the view @param fields: the fields @return: an instance of List """ headers = [] hiddens = [] buttons = [] field_total = {} values = [row.copy() for row in data] myfields = [] # check for duplicate fields for node in root.childNodes: if node.nodeName == "button": attrs = node_attributes(node) if attrs.get("invisible", False): visible = eval(attrs["invisible"], {"context": self.context}) if visible: continue buttons += [Button(**attrs)] headers.append(("button", len(buttons))) elif node.nodeName == "field": attrs = node_attributes(node) if "name" in attrs: name = attrs["name"] if name in myfields: print "-" * 30 print " malformed view for:", self.model print " duplicate field:", name print "-" * 30 raise common.error(_("Application Error"), _("Invalid view, duplicate field: %s") % name) myfields.append(name) if attrs.get("widget"): if attrs["widget"] == "one2many_list": attrs["widget"] = "one2many" attrs["type"] = attrs["widget"] try: fields[name].update(attrs) except: print "-" * 30, "\n malformed tag for:", attrs print "-" * 30 raise kind = fields[name]["type"] if kind not in CELLTYPES: kind = "char" fields[name].update(attrs) try: visval = fields[name].get("invisible", "False") invisible = eval(visval, {"context": self.context}) except NameError, e: cherrypy.log.error(e, context="listgrid.List.parse") invisible = False if invisible: hiddens += [(name, fields[name])] if "sum" in attrs: field_total[name] = [attrs["sum"], 0.0] for i, row in enumerate(data): row_value = values[i] if invisible: cell = Hidden(**fields[name]) cell.set_value(row_value.get(name, False)) else: cell = CELLTYPES[kind](value=row_value.get(name, False), **fields[name]) for color, expr in self.colors.items(): try: if expr_eval(expr, dict(row_value, active_id=rpc.session.active_id or False)): cell.color = color break except: pass row[name] = cell if invisible: continue headers += [(name, fields[name])]
def __init__(self, **attrs): #FIXME: validation error in `Pricelist Version` attrs['required'] = False super(O2M, self).__init__(**attrs) self.new_attrs = {'text': _("New"), 'help': _('Create new record.')} self.default_get_ctx = attrs.get('default_get', {}) or attrs.get( 'context', {}) # get top params dictionary params = cherrypy.request.terp_params self.source = params.source self.edition = params.o2m_edit pprefix = '' if '/' in self.name: pprefix = self.name[:self.name.rindex('/')] pparams = params.chain_get(pprefix) if (pparams and not pparams.id) or (not pparams and not params.id): self.new_attrs = { 'text': _("Save/New"), 'help': _('Save parent record.') } self.parent_id = params.id if pparams: self.parent_id = pparams.id # get params for this field current = params.chain_get(self.name) self.model = attrs['relation'] self.link = attrs.get('link', '') self.onchange = None # override onchange in js code view = attrs.get('views', {}) mode = str(attrs.get('mode', 'tree,form')).split(',') self.view = view view_mode = mode view_type = mode[0] self.view_type = view_type if not current: current = TinyDict() if current.view_mode: view_mode = current.view_mode if current.view_type: view_type = current.view_type self.switch_to = view_mode[-1] if view_type == view_mode[-1]: self.switch_to = view_mode[0] ids = attrs.get('value') or [] if not isinstance(ids, list): ids = [ids] current.offset = current.offset or 0 current.limit = current.limit or 50 current.count = len(ids or []) if current.limit != -1 and not params.sort_key: ids = ids[current.offset:current.offset + current.limit] if ids: if isinstance(ids[0], dict): current.default_data = ids for item in current.default_data: self.default_value.append(OneToMany.create(item)) item['id'] = 0 ids = [] elif isinstance(ids[0], tuple): [current_id[1] for current_id in ids] id = (ids or None) and ids[0] if self.name == self.source or self.name == params.source: if params.sort_key and ids: domain = current.domain or [] domain.append(('id', 'in', ids)) limit = current.limit if current.limit == -1: limit = 0 ids = rpc.RPCProxy(self.model).search( domain, current.offset, limit, params.sort_key + ' ' + params.sort_order, current.context) id = ids[0] if current and params.source and isinstance( params.source, basestring) and self.name in params.source.split('/'): id = current.id id = id or None current.model = self.model current.id = id current.ids = ids current.view_mode = view_mode current.view_type = view_type current.domain = current.domain or [] current.context = current.context or {} group_by_ctx = '' if self.default_get_ctx: ctx = dict(cherrypy.request.terp_record, context=current.context, active_id=self.parent_id or False) ctx[attrs['name']] = ids # XXX: parent record for O2M #if self.parent: # ctx['parent'] = EvalEnvironment(self.parent) try: context = ctx.copy() ctx = expr_eval("dict(%s)" % self.default_get_ctx, context) ctx.update( expr_eval("dict(%s)" % attrs.get('context', '{}'), context)) current.context.update(ctx) except: pass if ctx and ctx.get('group_by'): group_by_ctx = ctx.get('group_by') # Group By for one2many list. if group_by_ctx: current.group_by_ctx = group_by_ctx current.domain = [('id', 'in', ids)] if current.view_type == 'tree' and self.readonly: self.editable = False if 'default_name' in current.context: del current.context['default_name'] self.screen = Screen(current, prefix=self.name, views_preloaded=view, editable=self.editable, readonly=self.readonly, selectable=0, nolinks=self.link, _o2m=1) self.id = id self.ids = ids if view_type == 'tree': self.id = None elif view_type == 'form': records_count = len(self.screen.ids or []) current_record = 0 if records_count and self.screen.id in self.screen.ids: current_record = self.screen.ids.index(self.screen.id) + 1 self.pager_info = _('%d of %d') % (current_record, records_count) else: self.pager_info = _('- of %d') % (records_count)
def __init__(self, **attrs): super(M2M, self).__init__(**attrs) ids = None params = getattr(cherrypy.request, "terp_params", None) if not params: params = TinyDict() params.model = attrs.get("relation", "model") params.ids = attrs.get("value", []) params.name = attrs.get("name", "") current = params.chain_get(self.name) if current and params.source == self.name: ids = current.ids self.model = attrs.get("relation", "model") self.link = attrs.get("link", None) self.onchange = None # override onchange in js code self.relation = attrs.get("relation", "") self.domain = attrs.get("domain", []) self.context = attrs.get("context", {}) or {} view = attrs.get("views", {}) mode = str(attrs.get("mode", "tree,form")).split(",") self.view = view view_mode = mode view_type = mode[0] self.switch_to = view_mode[-1] if view_type == view_mode[-1]: self.switch_to = view_mode[0] if ids is None: ids = attrs.get("value") or [] id = (ids or None) and ids[0] pprefix = "" if "/" in self.name: pprefix = self.name[: self.name.rindex("/")] current = params.chain_get(self.name) if not current: current = TinyDict() current.offset = current.offset or 0 current.limit = current.limit or 50 current.count = len(ids or []) if isinstance(ids, tuple): ids = list(ids) if self.name == params.source and params.sort_key and ids: # reorder ids based on supplier criteria (sort_key, sort_order) domain = current.domain or [] domain.append(("id", "in", ids)) ids = rpc.RPCProxy(self.model).search( domain, 0, 0, params.sort_key + " " + params.sort_order, current.context ) id = ids[0] if current.view_mode: view_mode = current.view_mode if current.view_type: view_type = current.view_type if current and params.source == self.name: id = current.id id = id or None current.model = self.model current.id = id current.ids = ids or [] current.view_mode = view_mode current.view_type = view_type current.domain = current.domain or [] current.context = current.context or {} if isinstance(self.context, basestring): # XXX: parent record for O2M # if self.parent: # ctx['parent'] = EvalEnvironment(self.parent) try: ctx = expr_eval( self.context, dict(cherrypy.request.terp_record, context=current.context, active_id=current.id or False), ) current.context.update(ctx) except: pass if current.view_type == "tree" and self.readonly: self.editable = False if self.editable is False: selectable = 0 else: selectable = 2 # try to get original input values if creating validation form if not params.filter_action: try: current.ids = eval(cherrypy.request.terp_data.get(self.name)) except: pass self.screen = Screen( current, prefix=self.name, views_preloaded=view, editable=self.editable, readonly=self.editable, selectable=selectable, nolinks=self.link, **{"_m2m": 1} ) self.screen.widget.checkbox_name = False self.screen.widget.m2m = True self.validator = validators.many2many()
def act_window(action, data): if not action.get('opened'): action.setdefault('target', 'current') return act_window_opener(action, data) for key in ('res_id', 'res_model', 'view_type', 'view_mode', 'limit', 'search_view'): data[key] = action.get(key, data.get(key)) if not data.get('search_view') and data.get('search_view_id'): data['search_view'] = str(rpc.session.execute( 'object', 'execute', data['res_model'], 'fields_view_get', data['search_view_id'], 'search', data['context'])) # store action limit within request and set it as None for action # so that view specific can differenciate between defaults (i.e this # act_window limit) and user's choosen value if data.get('limit'): # TODO: we're conservative here - so we set limit to 20, # but we should really use act_window's limit (i.e data['limit']) # once we're sure there is *no* performance impact. cherrypy.request.action_limit = 20 data['limit'] = None if action.get('target') and action['target'] == 'popup' and action.get('res_model') and isinstance(action.get('context'), dict): search_view_id = rpc.RPCProxy('ir.ui.view').search([('type','=', 'search'), ('model','=',action['res_model'])], 0, 0, 0, rpc.session.context) if search_view_id and action['context'].get('search_view'): action['context']['search_view'] = search_view_id[0] view_ids = False if action.get('views', []): if isinstance(action['views'], list): view_ids = [x[0] for x in action['views']] data['view_mode'] = ",".join([x[1] for x in action['views']]) else: if action.get('view_id'): view_ids = [action['view_id'][0]] elif action.get('view_id'): view_ids = [action['view_id'][0]] if not action.get('domain'): action['domain'] = '[]' ctx = dict(data.get('context', {}), active_id=data.get('id', False), active_ids=data.get('ids', []), active_model=data.get('model', False) ) if action.get('context') and isinstance(action['context'], dict): if not action['context'].get('active_ids'): action['context']['active_ids'] = ctx['active_ids'] or [] ctx.update(expr_eval(action.get('context', '{}'), ctx)) search_view = action.get('search_view_id') if search_view: if isinstance(search_view, (list, tuple)): ctx['search_view'] = search_view[0] else: ctx['search_view'] = search_view # save active_id in session rpc.session.active_id = data.get('id') domain = expr_eval(action['domain'], ctx) if data.get('domain'): domain.append(data['domain']) if 'menu' in data['res_model'] and action.get('name') == 'Menu': return close_popup() if action.get('display_menu_tip'): display_menu_tip = action.get('help') else: display_menu_tip = None return execute_window(view_ids, data['res_model'], data['res_id'], domain, action['view_type'], ctx, data['view_mode'], name=action.get('name'), target=action.get('target'), limit=data.get('limit'), search_view=data['search_view'], context_menu=data.get('context_menu'), display_menu_tip=display_menu_tip, action_id=action.get('id'))
def __init__(self, **attrs): super(M2M, self).__init__(**attrs) ids = None params = getattr(cherrypy.request, 'terp_params', None) if not params: params = TinyDict() params.model = attrs.get('relation', 'model') params.ids = attrs.get('value', []) params.name = attrs.get('name', '') current = params.chain_get(self.name) if current and params.source == self.name: ids = current.ids self.model = attrs.get('relation', 'model') self.link = attrs.get('link', None) self.onchange = None # override onchange in js code self.relation = attrs.get('relation', '') self.domain = attrs.get('domain', []) self.context = attrs.get('context', {}) or {} view = attrs.get('views', {}) mode = str(attrs.get('mode', 'tree,form')).split(',') self.view = view view_mode = mode view_type = mode[0] self.switch_to = view_mode[-1] if view_type == view_mode[-1]: self.switch_to = view_mode[0] if ids is None: ids = attrs.get('value', []) id = (ids or None) and ids[0] pprefix = '' if '/' in self.name: pprefix = self.name[:self.name.rindex('/')] if self.name == params.source and params.sort_key and ids: self.domain.append(('id', 'in', ids)) ids = rpc.RPCProxy(self.model).search( self.domain, 0, 0, params.sort_key + ' ' + params.sort_order, self.context) id = ids[0] current = params.chain_get(self.name) if not current: current = TinyDict() current.offset = current.offset or 0 current.limit = current.limit or 50 current.count = len(ids or []) if current.view_mode: view_mode = current.view_mode if current.view_type: view_type = current.view_type if current and params.source == self.name: id = current.id id = id or None current.model = self.model current.id = id if isinstance(ids, tuple): ids = list(ids) current.ids = ids or [] current.view_mode = view_mode current.view_type = view_type current.domain = current.domain or [] current.context = current.context or {} if isinstance(self.context, basestring): # XXX: parent record for O2M #if self.parent: # ctx['parent'] = EvalEnvironment(self.parent) try: ctx = expr_eval( self.context, dict(cherrypy.request.terp_record, context=current.context, active_id=current.id or False)) current.context.update(ctx) except: pass if current.view_type == 'tree' and self.readonly: self.editable = False if self.editable is False: selectable = 0 else: selectable = 2 # try to get original input values if creating validation form if not params.filter_action: try: current.ids = eval(cherrypy.request.terp_data.get(self.name)) except: pass self.screen = Screen(current, prefix=self.name, views_preloaded=view, editable=self.editable, readonly=self.editable, selectable=selectable, nolinks=self.link, **{'_m2m': 1}) self.screen.widget.checkbox_name = False self.screen.widget.m2m = True self.validator = validators.many2many()
def __init__(self, **attrs): super(Filter, self).__init__(**attrs) default_domain = attrs.get('default_domain') self.global_domain = [] self.icon = attrs.get('icon') self.filter_domain = attrs.get('domain', []) self.help = attrs.get('help') self.filter_id = 'filter_%s' % (random.randint(0,10000)) self.filter_name = attrs.get('name', attrs.get('string', '')).replace('/', '-') self.filter_status = attrs.get('filter_status', {}).get(self.filter_name) filter_context = attrs.get('context') screen_context = attrs.get('screen_context', {}) self.def_checked = False self.groupcontext = [] default_search = get_search_default(attrs, screen_context, default_domain) # context implemented only for group_by. self.group_context = None if filter_context: self.filter_context = eval(filter_context) self.group_context = self.filter_context.get('group_by', False) if self.group_context: if isinstance(self.group_context, list): self.group_context = map(lambda x: 'group_' + x, self.group_context) else: self.group_context = 'group_' + self.group_context if default_search: self.def_checked = True self.global_domain += (expr_eval(self.filter_domain, {'context':screen_context})) if self.group_context: self.groupcontext = self.group_context self.nolabel = True self.readonly = False if self.filter_context: if not self.filter_context.get('group_by'): self.filter_context = self.filter_context else: self.filter_context = {} self.text_val = self.string or self.help if self.icon: self.icon = icons.get_icon(self.icon) if self.string == self.help: self.help = None self.first_box = attrs.get('first_box') self.last_box = attrs.get('last_box') self.first_last_box = attrs.get('first_last_box') if not self.def_checked and attrs.get('group_by_ctx'): if self.group_context in attrs['group_by_ctx']: self.def_checked = True if self.filter_status is not None: # if filter_status is defined that means user have manually # check/unchecked the filter, so we override default choice # by user's choice. self.def_checked = bool(self.filter_status)
def parse(self, root, fields, data=[]): """Parse the given node to generate valid list headers. @param root: the root node of the view @param fields: the fields @return: an instance of List """ headers = [] hiddens = [] buttons = [] field_total = {} field_real_total = {} values = [row.copy() for row in data] myfields = [] # check for duplicate fields for node in root.childNodes: if node.nodeName == 'button': if self.force_readonly: continue attrs = node_attributes(node) if attrs.get('invisible', False): visible = eval(attrs['invisible'], {'context':self.context}) if visible: continue buttons += [Button(**attrs)] headers.append(("button", len(buttons))) elif node.nodeName == 'separator': headers += [("separator", {'type': 'separator', 'string': '|', 'not_sortable': 1})] elif node.nodeName == 'field': attrs = node_attributes(node) if 'name' in attrs: name = attrs['name'] if name in myfields: print "-"*30 print " malformed view for:", self.model print " duplicate field:", name print "-"*30 raise common.error(_('Application Error'), _('Invalid view, duplicate field: %s') % name) myfields.append(name) if attrs.get('widget'): if attrs['widget']=='one2many_list': attrs['widget']='one2many' attrs['type'] = attrs['widget'] try: fields[name].update(attrs) except: print "-"*30,"\n malformed tag for:", attrs print "-"*30 raise kind = fields[name]['type'] if kind not in CELLTYPES: kind = 'char' fields[name].update(attrs) try: visval = fields[name].get('invisible', 'False') invisible = visval if isinstance(visval, bool) \ else eval(visval, {'context': self.context}) except NameError, e: cherrypy.log.error(e, context='listgrid.List.parse') invisible = False if invisible: hiddens += [(name, fields[name])] if 'sum' in attrs: field_total[name] = [attrs['sum'], 0.0] if 'real_sum' in attrs: field_real_total[name] = [attrs['real_sum'], 0.0] for i, row in enumerate(data): row_value = values[i] if invisible: cell = Hidden(**fields[name]) cell.set_value(row_value.get(name, False)) else: cell = CELLTYPES[kind](value=row_value.get(name, False), **fields[name]) for color, expr in self.colors.items(): try: if expr_eval(expr, dict(row_value, active_id=rpc.session.active_id or False)): cell.color = color break except: pass row[name] = cell if invisible: continue headers += [(name, fields[name])]
def __init__(self, name, model, view, ids=[], domain=[], context={}, **kw): super(List, self).__init__(name=name, model=model, ids=ids) self.context = context or {} self.domain = domain or [] custom_search_domain = getattr(cherrypy.request, 'custom_search_domain', []) custom_filter_domain = getattr(cherrypy.request, 'custom_filter_domain', []) if name.endswith('/'): self._name = name[:-1] if name != '_terp_list': self.source = self.name.replace('/', '/') or None self.sort_order = kw.get('sort_order', '') self.sort_key = kw.get('sort_key', '') #this Condition is for Dashboard to avoid new, edit, delete operation self.dashboard = 0 self.noteditable = [] self.notselectable = [] self.selectable = kw.get('selectable', 0) self.editable = kw.get('editable', False) self.pageable = kw.get('pageable', True) self.view_mode = kw.get('view_mode', []) self.offset = kw.get('offset', 0) self.limit = None self.count = kw.get('count', 0) self.link = kw.get('nolinks') self.m2m = kw.get('m2m', 0) self.o2m = kw.get('o2m', 0) self.concurrency_info = None self.selector = None self.force_readonly = kw.get('force_readonly', False) terp_params = getattr(cherrypy.request, 'terp_params', {}) if terp_params: if terp_params.get('_terp_model'): if terp_params['_terp_model'] == 'board.board' and terp_params.view_type == 'form': self.dashboard = 1 if terp_params.get('_terp_source'): if (str(terp_params.source) == self.source) or (terp_params.source == '_terp_list' and terp_params.sort_key): self.sort_key = terp_params.sort_key self.sort_order = terp_params.sort_order if self.selectable == 1: self.selector = 'radio' if self.selectable == 2: self.selector = 'checkbox' fields = view['fields'] dom = xml.dom.minidom.parseString(view['arch'].encode('utf-8')) root = dom.childNodes[0] attrs = node_attributes(root) # Get the hide status of some buttons - by default buttons are shown self.hide_new_button = False self.hide_delete_button = False self.hide_edit_button = False try: self.hide_new_button = expr_eval(attrs.get('hide_new_button', False), {'context': context}) self.hide_delete_button = expr_eval(attrs.get('hide_delete_button', False), {'context': context}) self.hide_edit_button = expr_eval(attrs.get('hide_edit_button', False), {'context': context}) except: pass self.string = attrs.get('string','') search_param = copy.deepcopy(domain) or [] if custom_search_domain: for elem in custom_search_domain: if elem not in self.domain: search_param.append(elem) for elem in custom_filter_domain: if elem not in self.domain: search_param.append(elem) # -- Limits -- # 1. use action limit or default global listview limit: 20 # 2. apply tree view limit if defined in attrs: <tree limit='..'>...</tree> # 3. apply user limit if changed self.limit = getattr(cherrypy.request, 'action_limit', 20) if attrs.get('limit'): try: self.limit = int(attrs.get('limit')) except Exception: pass if kw.get('limit') is not None: self.limit = int(kw.get('limit')) self.colors = {} for color_spec in attrs.get('colors', '').split(';'): if color_spec: colour, test = color_spec.split(':') self.colors[colour] = test proxy = rpc.RPCProxy(model) default_data = kw.get('default_data', []) search_text = terp_params.get('_terp_search_text', False) approximation = False if not self.source: self.source = terp_params.get('_terp_source', None) if not default_data and not self.o2m and not self.m2m: if self.limit > 0: if self.sort_key: ids = proxy.search(search_param, self.offset, self.limit, self.sort_key + ' ' +self.sort_order + ',id', context) else: if search_text: if self.source == '_terp_list': ids = proxy.search(search_param, self.offset, self.limit, False, context) else: ids = proxy.search(search_param, self.offset, self.limit, False, context) else: ids = proxy.search(search_param, 0, 0, 0, context) if len(ids) < self.limit: if self.offset > 0: self.count = len(ids) + self.offset else: self.count = len(ids) else: self.count, approximation = proxy.approximate_search_count(search_param, context) if not default_data and self.m2m: # prefilter datas for m2m # XXX: make it explicit about how 'sum' are computed as this is going to change default behaviour if ids and self.limit not in (0, -1): ids = self.ids[self.offset:self.offset+self.limit] self.data_dict = {} data = [] if ids and not isinstance(ids, list): ids = [ids] if ids and len(ids) > 0: ctx = rpc.session.context.copy() ctx.update(context) try: data = proxy.read(ids, fields.keys() + ['__last_update'], ctx) except: pass ConcurrencyInfo.update(self.model, data) cherrypy.response.headers.pop('X-Concurrency-Info', None) self.concurrency_info = ConcurrencyInfo(self.model, ids) order_data = [(d['id'], d) for d in data] orderer = dict(zip(ids, count())) ordering = sorted(order_data, key=lambda object: orderer[object[0]]) data = [i[1] for i in ordering] for item in data: self.data_dict[item['id']] = item.copy() if not self.m2m: self.ids = ids elif kw.get('default_data', []): data = kw['default_data'] self.values = copy.deepcopy(data) self.headers, self.hiddens, self.data, self.field_total, self.field_real_total, self.buttons = self.parse(root, fields, data) for k, v in self.field_total.items(): if(len([test[0] for test in self.hiddens if test[0] == k])) <= 0: self.field_total[k][1] = self.do_sum(self.data, k) for k, v in self.field_real_total.items(): if(len([test[0] for test in self.hiddens if test[0] == k])) <= 0: self.field_real_total[k][1] = self.do_real_sum(self.data, k) self.columns = len(self.headers) self.columns += (self.selectable or 0) and 1 self.columns += (self.editable or 0) and 2 self.columns += (self.buttons or 0) and 1 if self.pageable: self.pager = Pager(ids=self.ids, offset=self.offset, limit=self.limit, count=self.count, approximation=approximation) self.pager._name = self.name if self.editable and context.get('set_editable'):#Treeview editable by default or set_editable in context attrs['editable'] = "bottom" if self.selectable and attrs.get('notselectable'): for x in self.values: try: if expr_eval(attrs.get('notselectable'), x): self.notselectable.append(x['id']) except: pass self.resequencable = expr_eval(attrs.get('resequencable') or '1') # self.resequencable = True # uncomment this if you want to disable this globally # make editors if self.editable and attrs.get('noteditable'): for x in self.values: try: if expr_eval(attrs.get('noteditable'), x): self.noteditable.append(x['id']) except: pass if self.editable and attrs.get('editable') in ('top', 'bottom'): for f, fa in self.headers: if not isinstance(fa, int): fa['prefix'] = '_terp_listfields' + ((self.name != '_terp_list' or '') and '/' + self.name) fa['inline'] = True if fa.get('type') == 'one2many': self.edit_inline = False self.editors = {} break Widget = get_widget(fa.get('type', 'char')) or get_widget('char') self.editors[f] = Widget(**fa) # generate hidden fields if self.editors: for f, fa in self.hiddens: fa['prefix'] = '_terp_listfields' + ((self.name != '_terp_list' or '') and '/' + self.name) self.editors[f] = form.Hidden(**fa) # limit the data if self.pageable and len(self.data) > self.limit and self.limit != -1: self.data = self.data[self.offset:] self.data = self.data[:min(self.limit, len(self.data))]
def parse(self, root, fields, data=[]): """Parse the given node to generate valid list headers. @param root: the root node of the view @param fields: the fields @return: an instance of List """ headers = [] hiddens = [] buttons = [] field_total = {} values = [row.copy() for row in data] myfields = [] # check for duplicate fields for node in root.childNodes: if node.nodeName == 'button': attrs = node_attributes(node) if attrs.get('invisible', False): visible = eval(attrs['invisible'], {'context':self.context}) if visible: continue buttons += [Button(**attrs)] headers.append(("button", len(buttons))) elif node.nodeName == 'field': attrs = node_attributes(node) if 'name' in attrs: name = attrs['name'] if name in myfields: raise common.error( _('Application Error'), _("Invalid view for model '%(model)s', duplicate field: %(field)s", model=self.model, name=name)) myfields.append(name) if attrs.get('widget'): if attrs['widget']=='one2many_list': attrs['widget']='one2many' attrs['type'] = attrs['widget'] try: fields[name].update(attrs) except: parse_logger.debug( "Malformed tag for field %s, with %s", name, attrs) raise kind = fields[name]['type'] if kind not in CELLTYPES: kind = 'char' fields[name].update(attrs) try: visval = fields[name].get('invisible', 'False') invisible = eval(visval, {'context': self.context}) except NameError, e: parse_logger.info(e) invisible = False if invisible: hiddens += [(name, fields[name])] if 'sum' in attrs: field_total[name] = [attrs['sum'], 0.0] for i, row in enumerate(data): row_value = values[i] if invisible: cell = Hidden(**fields[name]) cell.set_value(row_value.get(name, False)) else: cell = CELLTYPES[kind](value=row_value.get(name, False), **fields[name]) for color, expr in self.colors.items(): try: if expr_eval(expr, dict(row_value, active_id=rpc.get_session().active_id or False)): cell.color = color break except: pass row[name] = cell if invisible: continue headers += [(name, fields[name])]
def __init__(self, **attrs): super(M2M, self).__init__(**attrs) ids = None params = getattr(cherrypy.request, 'terp_params', None) if not params: params = TinyDict() params.model = attrs.get('relation', 'model') params.ids = attrs.get('value', []) params.name = attrs.get('name', '') current = params.chain_get(self.name) if current and params.source == self.name: ids = current.ids self.model = attrs.get('relation', 'model') self.link = attrs.get('link', None) self.onchange = None # override onchange in js code self.relation = attrs.get('relation', '') self.domain = attrs.get('domain', []) self.context = attrs.get('context', {}) or {} view = attrs.get('views', {}) mode = str(attrs.get('mode', 'tree,form')).split(',') self.view = view view_mode = mode view_type = mode[0] self.switch_to = view_mode[-1] if view_type == view_mode[-1]: self.switch_to = view_mode[0] if ids is None: ids = attrs.get('value', []) id = (ids or None) and ids[0] pprefix = '' if '/' in self.name: pprefix = self.name[:self.name.rindex('/')] if self.name == params.source and params.sort_key and ids: self.domain.append(('id', 'in', ids)) ids = rpc.RPCProxy(self.model).search(self.domain, 0, 0, params.sort_key+ ' '+params.sort_order, self.context) id = ids[0] current = params.chain_get(self.name) if not current: current = TinyDict() current.offset = current.offset or 0 current.limit = current.limit or 50 current.count = len(ids or []) if current.view_mode: view_mode = current.view_mode if current.view_type: view_type = current.view_type if current and params.source == self.name: id = current.id id = id or None current.model = self.model current.id = id if isinstance(ids, tuple): ids = list(ids) current.ids = ids or [] current.view_mode = view_mode current.view_type = view_type current.domain = current.domain or [] current.context = current.context or {} if isinstance(self.context, basestring): # XXX: parent record for O2M #if self.parent: # ctx['parent'] = EvalEnvironment(self.parent) try: ctx = expr_eval( self.context, dict(cherrypy.request.terp_record, context=current.context, active_id=current.id or False)) current.context.update(ctx) except: pass if current.view_type == 'tree' and self.readonly: self.editable = False if self.editable is False: selectable = 0 else: selectable = 2 # try to get original input values if creating validation form if not params.filter_action: try: current.ids = eval(cherrypy.request.terp_data.get(self.name)) except: pass self.screen = Screen(current, prefix=self.name, views_preloaded=view, editable=self.editable, readonly=self.editable, selectable=selectable, nolinks=self.link, **{'_m2m': 1}) self.screen.widget.checkbox_name = False self.screen.widget.m2m = True self.validator = validators.many2many()
def act_window(action, data): if not action.get('opened'): action.setdefault('target', 'current') return act_window_opener(action, data) for key in ('res_id', 'res_model', 'view_type', 'view_mode', 'limit', 'search_view'): data[key] = action.get(key, data.get(key)) if not data.get('search_view') and data.get('search_view_id'): data['search_view'] = str(rpc.RPCProxy(data['res_model']).fields_view_get( data['search_view_id'], 'search', data['context'])) if data.get('limit'): data['limit'] = 20 if action.get('target') and action['target'] == 'popup' and action.get('res_model') and isinstance(action.get('context'), dict): search_view_id = rpc.RPCProxy('ir.ui.view').search([('type','=', 'search'), ('model','=',action['res_model'])], 0, 0, 0, rpc.get_session().context) if search_view_id and action['context'].get('search_view'): action['context']['search_view'] = search_view_id[0] view_ids = False if action.get('views', []): if isinstance(action['views'], list): view_ids = [x[0] for x in action['views']] data['view_mode'] = ",".join([x[1] for x in action['views']]) else: if action.get('view_id'): view_ids = [action['view_id'][0]] elif action.get('view_id'): view_ids = [action['view_id'][0]] if not action.get('domain'): action['domain'] = '[]' ctx = dict(data.get('context', {}), active_id=data.get('id', False), active_ids=data.get('ids', []), active_model=data.get('model', False) ) if action.get('context') and isinstance(action['context'], dict): if not action['context'].get('active_ids'): action['context']['active_ids'] = ctx['active_ids'] or [] ctx.update(expr_eval(action.get('context', '{}'), ctx)) search_view = action.get('search_view_id') if search_view: if isinstance(search_view, (list, tuple)): ctx['search_view'] = search_view[0] else: ctx['search_view'] = search_view # save active_id in session rpc.get_session().active_id = data.get('id') domain = expr_eval(action['domain'], ctx) if data.get('domain'): domain.append(data['domain']) if 'menu' in data['res_model'] and action.get('name') == 'Menu': return close_popup() if action.get('display_menu_tip'): display_menu_tip = action.get('help') else: display_menu_tip = None return execute_window(view_ids, data['res_model'], data['res_id'], domain, action['view_type'], ctx, data['view_mode'], name=action.get('name'), target=action.get('target'), limit=data.get('limit'), search_view=data['search_view'], context_menu=data.get('context_menu'), display_menu_tip=display_menu_tip, action_id=action.get('id'))
def __init__(self, **attrs): super(Filter, self).__init__(**attrs) default_domain = attrs.get('default_domain') self.global_domain = [] self.icon = attrs.get('icon') self.filter_domain = attrs.get('domain', []) self.help = attrs.get('help') self.filter_id = 'filter_%s' % (random.randint(0, 10000)) self.filter_name = attrs.get('name', '') self.filter_status = attrs.get('filter_status', {}).get(self.name) filter_context = attrs.get('context') screen_context = attrs.get('screen_context', {}) self.def_checked = False self.groupcontext = [] default_search = get_search_default(attrs, screen_context, default_domain) # context implemented only for group_by. self.group_context = None if filter_context: self.filter_context = eval(filter_context) self.group_context = self.filter_context.get('group_by', False) if self.group_context: if isinstance(self.group_context, list): self.group_context = map(lambda x: 'group_' + x, self.group_context) else: self.group_context = 'group_' + self.group_context if default_search: self.def_checked = True self.global_domain += (expr_eval(self.filter_domain, {'context': screen_context})) if self.group_context: self.groupcontext = self.group_context self.nolabel = True self.readonly = False if self.filter_context: if not self.filter_context.get('group_by'): self.filter_context = self.filter_context else: self.filter_context = {} self.text_val = self.string or self.help if self.icon: self.icon = icons.get_icon(self.icon) if self.string == self.help: self.help = None self.first_box = attrs.get('first_box') self.last_box = attrs.get('last_box') self.first_last_box = attrs.get('first_last_box') if not self.def_checked and attrs.get('group_by_ctx'): if self.group_context in attrs['group_by_ctx']: self.def_checked = True if self.filter_status is not None: # if filter_status is defined that means user have manually # check/unchecked the filter, so we override default choice # by user's choice. self.def_checked = bool(self.filter_status)
def parse(self, root, fields, data=[]): """Parse the given node to generate valid list headers. @param root: the root node of the view @param fields: the fields @return: an instance of List """ headers = [] hiddens = [] buttons = [] field_total = {} values = [row.copy() for row in data] myfields = [] # check for duplicate fields for node in root.childNodes: if node.nodeName == 'button': attrs = node_attributes(node) if attrs.get('invisible', False): visible = eval(attrs['invisible'], {'context': self.context}) if visible: continue buttons += [Button(**attrs)] headers.append(("button", len(buttons))) elif node.nodeName == 'field': attrs = node_attributes(node) if 'name' in attrs: name = attrs['name'] if name in myfields: print "-" * 30 print " malformed view for:", self.model print " duplicate field:", name print "-" * 30 raise common.error( _('Application Error'), _('Invalid view, duplicate field: %s') % name) myfields.append(name) if attrs.get('widget'): if attrs['widget'] == 'one2many_list': attrs['widget'] = 'one2many' attrs['type'] = attrs['widget'] try: fields[name].update(attrs) except: print "-" * 30, "\n malformed tag for:", attrs print "-" * 30 raise kind = fields[name]['type'] if kind not in CELLTYPES: kind = 'char' fields[name].update(attrs) try: visval = fields[name].get('invisible', 'False') invisible = eval(visval, {'context': self.context}) except NameError, e: cherrypy.log.error(e, context='listgrid.List.parse') invisible = False if invisible: hiddens += [(name, fields[name])] if 'sum' in attrs: field_total[name] = [attrs['sum'], 0.0] for i, row in enumerate(data): row_value = values[i] if invisible: cell = Hidden(**fields[name]) cell.set_value(row_value.get(name, False)) else: cell = CELLTYPES[kind](value=row_value.get( name, False), **fields[name]) for color, expr in self.colors.items(): try: if expr_eval( expr, dict(row_value, active_id=rpc.session.active_id or False)): cell.color = color break except: pass row[name] = cell if invisible: continue headers += [(name, fields[name])]
def parse(self, model=None, root=None, fields=None, values={}): views = [] search_model = model filters_run = [] for node in root.childNodes: if not node.nodeType == node.ELEMENT_NODE: continue if filters_run and node.localName != 'filter': views.append(FiltersGroup(children=filters_run)) filters_run = [] attrs = node_attributes(node) attrs.update(is_search=True, model=search_model) if 'nolabel' in attrs: attrs['nolabel'] = False if node.localName in ('form', 'tree', 'search', 'group'): if node.localName == 'group': attrs['group_by_ctx'] = values.get('group_by_ctx') attrs['expand'] = expr_eval(attrs.get('expand', False), {'context': self.context}) Element = Group else: Element = Frame views.append( Element(children=self.parse(model=search_model, root=node, fields=fields, values=values), **attrs)) elif node.localName == 'newline': views.append(NewLine(**attrs)) elif node.localName == 'filter': attrs.update(model=search_model, default_domain=self.domain, screen_context=self.context) if values and values.get('group_by_ctx'): attrs['group_by_ctx'] = values['group_by_ctx'] elif self.groupby: attrs['group_by_ctx'] = self.groupby if values and values.get('filter_status'): attrs['filter_status'] = values['filter_status'] v = Filter(**attrs) if v.groupcontext and v.groupcontext not in self.groupby: self.groupby.append(v.groupcontext) self.listof_domain.extend(i for i in v.global_domain if not i in self.listof_domain) filters_run.append(v) elif node.localName == 'field': val = attrs.get( 'select', False) or fields[str(attrs['name'])].get( 'select', False) or self.view_type == 'search' if val: name = attrs['name'] if name in self.fields_type: continue # in search view fields should be writable attrs.update(readonly=False, required=False, translate=False, disabled=False, visible=True, invisible=False, editable=True, attrs=None) try: fields[name].update(attrs) except: print "-" * 30, "\n malformed tag for:", attrs print "-" * 30 raise if attrs.get('widget'): if attrs['widget'] == 'one2many_list': fields[name]['widget'] = 'one2many' fields[name]['type2'] = fields[name]['type'] fields[name]['type'] = attrs['widget'] kind = fields[name]['type'] if kind not in WIDGETS: continue if kind == 'many2one': attrs['relation'] = fields[name]['relation'] attrs['type'] = fields[name]['type'] if not attrs.get('string'): attrs['string'] = fields[name]['string'] self.fields_type[name] = kind field = WIDGETS[kind](**fields[name]) field.onchange = None field.callback = None if kind == 'boolean': # '0' in string because 0 is considering as No Value, and we need 0 as some value. field.options = [[1, _('Yes')], ['0', _('No')]] field.validator.if_empty = '' default_search = None if name: default_search = get_search_default( fields[name], self.context, self.domain) defval = default_search if defval: model = fields[name].get('relation') type2 = fields[name].get('type2') if kind == 'many2one' and model: try: value = rpc.name_get( model, default_search, self.context) except Exception, e: value = defval defval = value or '' if attrs.get('filter_domain'): domain = expr_eval(attrs['filter_domain'], {'self': defval}) else: if field.kind == 'selection': if type2 == 'many2one': m2nval = defval try: m2nval = int(defval) except: # you see, since defval can basically be anything, including a totally # illogic value, my only possibility is to do return another illogic value, # so it won't crash too much pass domain = [(name, '=', m2nval)] else: domain = [(name, '=', defval)] elif field.kind in ('date', 'datetime'): domain = [(name, '>=', defval)] elif field.kind == 'boolean': domain = [(name, '=', defval)] else: domain = [(name, fields[name].get( 'comparator', 'ilike'), defval)] field.set_value(defval) self.listof_domain += [ i for i in domain if not i in self.listof_domain ] self.context.update( expr_eval(attrs.get('context', "{}"), {'self': default_search})) if (not default_search) and name in values and isinstance( field, TinyInputWidget): field.set_value(values[name]) views.append(field) for n in node.childNodes: if n.localName == 'filter': attrs_child = dict(node_attributes(n), default_domain=self.domain, screen_context=self.context) if 'string' in attrs_child: del attrs_child['string'] if values and values.get('group_by_ctx'): attrs['group_by_ctx'] = values['group_by_ctx'] filter_field = Filter(**attrs_child) filter_field.onchange = None filter_field.callback = None if filter_field.groupcontext and filter_field.groupcontext not in self.groupby: self.groupby.append(filter_field.groupcontext) self.listof_domain.extend( i for i in filter_field.global_domain if i not in self.listof_domain) field.filters.append(filter_field)
def data(self, ids, model, fields, field_parent=None, icon_name=None, domain=[], context={}, sort_by=None, sort_order="asc", fields_info=None, colors={}): if ids == 'None' or ids == '': ids = [] if isinstance(ids, basestring): ids = map(int, ids.split(',')) elif isinstance(ids, list): ids = map(int, ids) if isinstance(fields, basestring): fields = eval(fields) if isinstance(domain, basestring): domain = eval(domain) if isinstance(context, basestring): context = eval(context) if isinstance(colors, basestring): colors = eval(colors) if isinstance(fields_info, basestring): fields_info = simplejson.loads(fields_info) if field_parent and field_parent not in fields: fields.append(field_parent) proxy = rpc.RPCProxy(model) ctx = dict(context, **rpc.session.context) if icon_name: fields.append(icon_name) if model == 'ir.ui.menu' and 'action' not in fields: fields.append('action') result = proxy.read(ids, fields, ctx) if sort_by: fields_info_type = simplejson.loads(fields_info[sort_by]) result.sort(lambda a,b: self.sort_callback(a, b, sort_by, sort_order, type=fields_info_type['type'])) for item in result: if colors: for color, expr in colors.items(): try: if expr_eval(expr,item or False): item['color'] = color break except: pass # format the data for field in fields: field_info = simplejson.loads(fields_info[field]) if field_info.get('widget',''): field_info['type'] = field_info['widget'] formatter = FORMATTERS.get(field_info['type']) for x in result: if x[field] and formatter: x[field] = formatter(x[field], field_info) records = [] for item in result: # empty string instead of False or None for k, v in item.items(): if v is None or v is False: item[k] = '' id = item.pop('id') record = { 'id': id, 'action': url('/openerp/tree/open', model=model, id=id, context=ctx), 'target': None, 'icon': None, 'children': [], 'items': item } if icon_name and item.get(icon_name): icon = item.pop(icon_name) record['icon'] = icons.get_icon(icon) if field_parent and field_parent in item: record['children'] = item.pop(field_parent) or None # For nested menu items, remove void action url # to suppress 'No action defined' error. if (model == 'ir.ui.menu' and record['children'] and not item['action']): record['action'] = None records.append(record) return {'records': records}
def on_change(self, **kw): data = kw.copy() callback = data.pop('_terp_callback') caller = data.pop('_terp_caller') model = data.pop('_terp_model') context = data.pop('_terp_context') try: context = eval(context) # convert to python dict except: context = {} match = re.match('^(.*?)\((.*)\)$', callback) if not match: raise common.error(_('Application Error'), _('Wrong on_change trigger: %s') % callback) for k, v in data.items(): try: data[k] = eval(v) except: pass result = {} prefix = '' if '/' in caller: prefix = caller.rsplit('/', 1)[0] ctx = TinyForm(**kw).to_python(safe=True) pctx = ctx if prefix: ctx = ctx.chain_get(prefix) if '/' in prefix: pprefix = prefix.rsplit('/', 1)[0] pctx = pctx.chain_get(pprefix) ctx2 = dict(rpc.get_session().context, **context or {}) ctx['parent'] = pctx ctx['context'] = ctx2 func_name = match.group(1) arg_names = [n.strip() for n in match.group(2).split(',')] args = [utils.expr_eval(arg, ctx) for arg in arg_names] # TODO: If the eval fails in expr_eval (because `arg` does not exist in `ctx`), it returns `{}` # This is a value we don't want, but not sure where that behavior # comes from/is used so in order not to risk breakage throughout # patch it here args = [(False if arg == {} else arg) for arg in args] proxy = rpc.RPCProxy(model) ids = ctx.id and [ctx.id] or [] try: response = getattr(proxy, func_name)(ids, *args) except Exception, e: return dict(error=_ep.render())
def eval_domain_filter(self, **kw): all_domains = kw.get('all_domains') custom_domains = kw.get('custom_domain') all_domains = eval(all_domains) domains = all_domains.get('domains') selection_domain = all_domains.get('selection_domain') search_context = all_domains.get('search_context') group_by_ctx = kw.get('group_by_ctx', []) if isinstance(group_by_ctx, str): group_by_ctx = group_by_ctx.split(',') if domains: domains = eval(domains) c = search_context.get('context', {}) v = search_context.get('value') if v and isinstance(v, basestring) and '__' in v: value, operator = v.split('__') v = int(value) ctx = expr_eval(c, {'self':v}) context = rpc.session.context if ctx: ctx.update(context) domain = [] check_domain = all_domains.get('check_domain') if check_domain and isinstance(check_domain, basestring): domain = expr_eval(check_domain, context) or [] search_data = {} model = kw.get('model') proxy = rpc.RPCProxy(model) res = proxy.fields_get(False, context) all_error = [] fld = {} if domains: for field, value in domains.iteritems(): if '/' in field: fieldname, bound = field.split('/') else: fieldname = field bound = '' data = {} fld['type'] = res[fieldname].get('type') if fld['type'] == 'many2many': fld['type'] = 'char' fld['value'] = value data[field] = fld try: frm = TinyForm(**data).to_python() except TinyFormError, e: error_field = e.field error = ustr(e) all_error.append(dict(error=error, error_field=error_field)) continue if bound in ('from', 'to'): if bound == 'from': test = '>=' else: test = '<=' convert_format = openobject.i18n.format.convert_date_format_in_domain([(fieldname, test, value)], res, context) domain.append(convert_format[0]) search_data.setdefault(fieldname, {})[bound] = convert_format[0][2] elif isinstance(value, bool) and value: search_data[field] = 1 elif isinstance(value, int) and not isinstance(value, bool): domain.append((field, '=', value)) search_data[field] = value elif 'selection_' in value: domain.append((field, '=', value.split('selection_')[1])) search_data[field] = value.split('selection_')[1] elif fld['type'] == 'selection': domain.append((field, '=', value)) search_data[field] = value else: if not 'm2o_' in value: operator = 'ilike' if '__' in value: value, operator = value.split('__') value = int(value) domain.append((field, operator, value)) search_data[field] = value else: search_data[field] = value.split('m2o_')[1] if all_error: return dict(all_error=all_error)
def on_change(self, **kw): data = kw.copy() callback = data.pop('_terp_callback') caller = data.pop('_terp_caller') model = data.pop('_terp_model') context = data.pop('_terp_context') change_default = False if '_terp_change_default' in data: change_default = data.pop('_terp_change_default') try: context = eval(context) # convert to python dict except: context = {} match = re.match('^(.*?)\((.*)\)$', callback) if not match: raise common.error(_('Application Error'), _('Wrong on_change trigger: %s') % callback) for k, v in data.items(): try: data[k] = eval(v) except: pass result = {} prefix = '' if '/' in caller: prefix = caller.rsplit('/', 1)[0] ctx = TinyForm(**kw).to_python(safe=True) pctx = ctx if prefix: ctx = ctx.chain_get(prefix) if '/' in prefix: pprefix = prefix.rsplit('/', 1)[0] pctx = pctx.chain_get(pprefix) ctx2 = dict(rpc.session.context, **context or {}) ctx['parent'] = pctx ctx['context'] = ctx2 func_name = match.group(1) arg_names = [n.strip() for n in match.group(2).split(',')] args = [utils.expr_eval(arg, ctx) for arg in arg_names] # TODO: If the eval fails in expr_eval (because `arg` does not exist in `ctx`), it returns `{}` # This is a value we don't want, but not sure where that behavior # comes from/is used so in order not to risk breakage throughout # patch it here args = [(False if arg == {} else arg) for arg in args] proxy = rpc.RPCProxy(model) ids = ctx.id and [ctx.id] or [] try: response = getattr(proxy, func_name)(ids, *args) except Exception, e: return dict(error=_ep.render())