def set_value(self, value): if value: self.relation, self.default = value.split(",") self.text = rpc.name_get(self.relation, self.default, rpc.session.context) else: self.relation = '' self.default = '' self.text = ''
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 set_value(self, value): if value and isinstance(value, (tuple, list)) and len(value) == 2: self.default, self.text = value elif value and isinstance(value, basestring): self.text = value else: self.default = value self.text = rpc.name_get(self.relation, self.default, rpc.session.context)
def get_text(self): self.refrel = False self.refid = False if isinstance(self.value, dict): self.value = self.value.get('selection',"") if self.value and ',' in self.value: self.refrel, self.refid = self.value.split(',', 2) self.value = rpc.name_get(self.refrel, self.refid, rpc.session.context) return self.value return ''
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 get_text(self): if isinstance(self.value, (int, long)): self.value = self.value, rpc.name_get(self.attrs['relation'], self.value, rpc.session.context) if self.value and len(self.value) > 0: if isinstance(self.value, (tuple, list)): return self.value[-1] else: return self.value return ''
def set_value(self, value): if isinstance(value, dict): if value.get('options'): self.options = value['options'] value = value.get('selection',"") if value: self.relation, self.default = value.split(",") self.text = rpc.name_get(self.relation, self.default, rpc.session.context) else: self.relation = '' self.default = '' self.text = ''
def get_name(self, model, id): return dict(name=rpc.name_get(model, id, rpc.session.context))
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, 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)
class Form(SecuredController): _cp_path = "/openerp/form" def create_form(self, params, tg_errors=None): if tg_errors: return cherrypy.request.terp_form cherrypy.session['params'] = params params.offset = params.offset or 0 params.limit = params.limit or 50 params.count = params.count or 0 params.view_type = params.view_type or params.view_mode[0] return tw.form_view.ViewForm(params, name="view_form", action="/openerp/form/save") @expose(template="/openerp/controllers/templates/form.mako") def create(self, params, tg_errors=None): params.view_type = params.view_type or params.view_mode[0] if params.view_type == 'tree': params.editable = True form = self.create_form(params, tg_errors) if not tg_errors: try: cherrypy.session.pop('remember_notebooks') except: self.reset_notebooks() editable = form.screen.editable mode = form.screen.view_type id = form.screen.id buttons = TinyDict() # toolbar buttons.new = (not editable or mode == 'tree') and mode != 'diagram' buttons.edit = not editable and (mode == 'form' or mode == 'diagram') buttons.save = editable and mode == 'form' buttons.cancel = editable and mode == 'form' buttons.delete = not editable and mode == 'form' buttons.pager = mode == 'form' or mode == 'diagram' # Pager will visible in edit and non-edit mode in form view. buttons.can_attach = id and mode == 'form' buttons.i18n = not editable and mode == 'form' buttons.show_grid = mode == 'diagram' buttons.create_node = mode == 'diagram' and editable from openerp.widgets import get_registered_views buttons.views = [] for kind, view in get_registered_views(): buttons.views.append( dict(kind=kind, name=view.name, desc=view.desc)) target = getattr(cherrypy.request, '_terp_view_target', None) buttons.toolbar = (target != 'new' and not form.is_dashboard) or mode == 'diagram' pager = None if buttons.pager: pager = tw.pager.Pager(id=form.screen.id, ids=form.screen.ids, offset=form.screen.offset, limit=form.screen.limit, count=form.screen.count, view_type=params.view_type) can_shortcut = self.can_shortcut_create() shortcut_ids = [] if cherrypy.session.get('terp_shortcuts'): for sc in cherrypy.session['terp_shortcuts']: if isinstance(sc['res_id'], tuple): shortcut_ids.append(sc['res_id'][0]) else: shortcut_ids.append(sc['res_id']) title = form.screen.string or '' display_name = {} if params.view_type == 'form': if params.id: if form.screen.view.get( 'fields') and form.screen.view['fields'].get('name'): display_name = { 'field': form.screen.view['fields']['name']['string'], 'value': ustr(form.screen.view['fields']['name']['value']) } title = ustr(display_name['field']) + ':' + ustr( display_name['value']) elif params.view_type == 'diagram': display_name = { 'field': form.screen.view['fields']['name']['string'], 'value': rpc.RPCProxy(params.model).name_get(form.screen.id, rpc.session.context)[0][1] } # For Corporate Intelligence visibility. obj_process = rpc.RPCProxy('ir.model').search( [('model', '=', 'process.process')]) or None tips = params.display_menu_tip if params.view_type == params.view_mode[0] and tips: tips = tips is_dashboard = form.screen.is_dashboard or False return dict(form=form, pager=pager, buttons=buttons, path=self.path, can_shortcut=can_shortcut, shortcut_ids=shortcut_ids, display_name=display_name, title=title, tips=tips, obj_process=obj_process, is_dashboard=is_dashboard) @expose('json', methods=('POST', )) def close_or_disable_tips(self): rpc.RPCProxy('res.users').write(rpc.session.uid, {'menu_tips': False}, rpc.session.context) def _read_form(self, context, count, domain, filter_domain, id, ids, kw, limit, model, offset, search_data, search_domain, source, view_ids, view_mode, view_type, notebook_tab, o2m_edit=False, editable=False): """ Extract parameters for form reading/creation common to both self.edit and self.view """ params, data = TinyDict.split({ '_terp_model': model, '_terp_id': id, '_terp_ids': ids, '_terp_view_ids': view_ids, '_terp_view_mode': view_mode, '_terp_view_type': view_type, '_terp_source': source, '_terp_domain': domain, '_terp_context': context, '_terp_offset': offset, '_terp_limit': limit, '_terp_count': count, '_terp_search_domain': search_domain, '_terp_search_data': search_data, '_terp_filter_domain': filter_domain, '_terp_notebook_tab': notebook_tab }) params.o2m_edit = o2m_edit params.editable = editable params.action_id = kw.get('action_id') if kw.get('default_date'): params.context.update({'default_date': kw['default_date']}) cherrypy.request._terp_view_target = kw.get('target') if params.view_mode and 'form' not in params.view_mode: params.view_type = params.view_mode[-1] if params.view_type == 'tree': params.view_type = 'form' if not params.ids: params.offset = 0 return params @expose() def edit(self, model, id=False, ids=None, view_ids=None, view_mode=['form', 'tree'], view_type='form', source=None, domain=[], context={}, offset=0, limit=50, count=0, search_domain=None, search_data=None, filter_domain=None, o2m_edit=False, **kw): notebook_tab = kw.get('notebook_tab') or 0 params = self._read_form(context, count, domain, filter_domain, id, ids, kw, limit, model, offset, search_data, search_domain, source, view_ids, view_mode, view_type, notebook_tab, o2m_edit=o2m_edit, editable=True) if not params.ids: params.count = 0 # On New O2M if params.source: current = TinyDict() current.id = False params[params.source] = current return self.create(params) @expose() def view(self, model, id, ids=None, view_ids=None, view_mode=['form', 'tree'], view_type='form', source=None, domain=[], context={}, offset=0, limit=50, count=0, search_domain=None, search_data=None, filter_domain=None, **kw): notebook_tab = kw.get('notebook_tab') or 0 params = self._read_form(context, count, domain, filter_domain, id, ids, kw, limit, model, offset, search_data, search_domain, source, view_ids, view_mode, view_type, notebook_tab) if not params.ids: params.count = 1 return self.create(params) @expose() def cancel(self, **kw): params, data = TinyDict.split(kw) if params.button: res = self.button_action(params) if res: return res raise redirect('/') if not params.id and params.ids: params.id = params.ids[0] if params.id and params.editable: raise redirect(self.path + "/view", model=params.model, id=params.id, ids=ustr(params.ids), view_ids=ustr(params.view_ids), view_mode=ustr(params.view_mode), domain=ustr(params.domain), context=ustr(params.context), offset=params.offset, limit=params.limit, count=params.count, search_domain=ustr(params.search_domain), search_data=ustr(params.search_data), filter_domain=ustr(params.filter_domain)) params.view_type = 'tree' return self.create(params) @expose(methods=('POST', )) @validate(form=get_validation_schema) @error_handler(default_error_handler) @exception_handler(default_exception_handler) def save(self, terp_save_only=False, **kw): """Controller method to save/button actions... @param tg_errors: TG special arg, used durring validation @param kw: keyword arguments @return: form view """ params, data = TinyDict.split(kw) # remember the current page (tab) of notebooks cherrypy.session['remember_notebooks'] = True Model = rpc.RPCProxy(params.model) # bypass save, for button action in non-editable view if params.editable: if not params.id: if params.default_o2m: data.update(params.default_o2m) ctx = dict((params.context or {}), **rpc.session.context) params.id = int(Model.create(data, ctx)) params.ids = (params.ids or []) + [params.id] params.count += 1 else: original_data = Model.read(params.id, data.keys()) modified = {} if original_data and isinstance(original_data, dict): for field, original_value in original_data.iteritems(): if isinstance(original_value, tuple): original_data[field] = original_value[0] if field in data and data[field] != original_data[ field]: #When field is many2many at that time following code will be applied if isinstance(data[field], list) and isinstance( data[field][0][2], list): if sorted(data[field][0][2]) != sorted( original_data[field]): modified[field] = data[field] else: modified[field] = data[field] ctx = utils.context_with_concurrency_info( params.context, params.concurrency_info) Model.write([params.id], modified, ctx) else: ctx = utils.context_with_concurrency_info( params.context, params.concurrency_info) Model.write([params.id], data, ctx) tw.ConcurrencyInfo.update( params.model, Model.read([params.id], ['__last_update'], ctx)) cherrypy.request.params = params button = params.button # perform button action if params.button: res = self.button_action(params) if res: return res current = params.chain_get(params.source or '') if current: current.id = None elif not button: params.editable = False if terp_save_only: return dict(params=params, data=data) def get_params(p, f): pp = p.chain_get(f) px = rpc.RPCProxy(p.model) _ids = pp.ids _all = px.read([p.id], [f])[0][f] _new = [i for i in _all if i not in _ids] pp.ids = _all if _new: pp.id = _new[0] return pp if params.source and len(params.source.split("/")) > 1: path = params.source.split("/") p = params for f in path: p = get_params(p, f) return self.create(params) args = { 'model': params.model, 'id': params.id, 'ids': ustr(params.ids), 'view_ids': ustr(params.view_ids), 'view_mode': ustr(params.view_mode), 'domain': ustr(params.domain), 'context': ustr(params.context), 'offset': params.offset, 'limit': params.limit, 'count': params.count, 'search_domain': ustr(params.search_domain), 'search_data': ustr(params.search_data), 'filter_domain': ustr(params.filter_domain), 'notebook_tab': params.notebook_tab } if params.o2m_edit: # hack to avoid creating new record line when editing o2m inline: # by default one2many.mako is going to fetch a new line (.create) # on /edit args['o2m_edit'] = "1" if params.editable or params.source or params.return_edit: raise redirect(self.path + '/edit', source=params.source, **args) raise redirect(self.path + '/view', **args) def button_action_cancel(self, name, params): if name: params.button.btype = "object" params.id = False res = self.button_action(params) if res: return res import actions return actions.close_popup(reload=False) def button_action_save(self, _, params): params.id = False params.button = None def button_action_workflow(self, name, params): model, id, _, _ = self._get_button_infos(params) res = rpc.session.execute('object', 'exec_workflow', model, name, id) if isinstance(res, dict): import actions return actions.execute(res, ids=[id]) params.button = None def button_action_object(self, name, params): model, id, ids, ctx = self._get_button_infos(params) res = rpc.session.execute('object', 'execute', model, name, ids, ctx) # after installation of modules (esp. initial) we may # need values from the global context for some contexts & domains (e.g. # leads) => installer wizards are generally postfixed by '.installer' # so use this characteristic to setup context reloads if model.endswith('.installer'): rpc.session.context_reload() if isinstance(res, dict): import actions return actions.execute(res, ids=[id], context=ctx) params.button = None def button_action_action(self, name, params): model, id, ids, ctx = self._get_button_infos(params) import actions action_id = int(name) action_type = actions.get_action_type(action_id) if action_type == 'ir.actions.wizard': cherrypy.session['wizard_parent_form'] = self.path cherrypy.session[ 'wizard_parent_params'] = params.parent_params or params res = actions.execute_by_id(action_id, type=action_type, model=model, id=id, ids=ids, context=ctx or {}) if res: return res params.button = None BUTTON_ACTIONS_BY_BTYPE = { 'action': button_action_action, 'cancel': button_action_cancel, 'object': button_action_object, 'save': button_action_save, 'workflow': button_action_workflow, } def _get_button_infos(self, params): model = params.button.model id = params.button.id or params.id id = (id or False) and (id) ids = (id or []) and [id] ctx = dict((params.context or {}), **rpc.session.context) ctx.update(params.button.context or {}) return model, id, ids, ctx def button_action(self, params): button_name = openobject.ustr(params.button.name) button_name = button_name.rsplit('/', 1)[-1] btype = params.button.btype try: return self.BUTTON_ACTIONS_BY_BTYPE[btype](self, button_name, params) except KeyError: raise common.warning(_('Invalid button type "%s"') % btype) @expose() def duplicate(self, **kw): params, data = TinyDict.split(kw) id = params.id ctx = params.context model = params.model proxy = rpc.RPCProxy(model) new_id = proxy.copy(id, {}, ctx) if new_id: params.id = new_id params.ids += [int(new_id)] params.count += 1 args = { 'model': params.model, 'id': params.id, 'ids': ustr(params.ids), 'view_ids': ustr(params.view_ids), 'view_mode': ustr(params.view_mode), 'domain': ustr(params.domain), 'context': ustr(params.context), 'offset': params.offset, 'limit': params.limit, 'count': params.count, 'search_domain': ustr(params.search_domain), 'filter_domain': ustr(params.filter_domain) } if new_id: raise redirect(self.path + '/edit', **args) raise redirect(self.path + '/view', **args) @expose() def delete(self, **kw): params, data = TinyDict.split(kw) current = params.chain_get(params.source or '') or params proxy = rpc.RPCProxy(current.model) idx = -1 if current.id: ctx = utils.context_with_concurrency_info(current.context, params.concurrency_info) res = proxy.unlink([current.id], ctx) if current.ids: idx = current.ids.index(current.id) if idx >= 0: current.ids.remove(current.id) params.count -= 1 if not len(current.ids) and params.count > 0: params.offset = params.offset - params.limit current.ids = proxy.search([], params.offset, params.limit, 0, ctx) idx = -1 if idx == len(current.ids): idx = -1 current.id = (current.ids or None) and current.ids[idx] self.reset_notebooks() args = { 'model': params.model, 'id': params.id, 'ids': ustr(params.ids), 'view_ids': ustr(params.view_ids), 'view_mode': ustr(params.view_mode), 'domain': ustr(params.domain), 'context': ustr(params.context), 'offset': params.offset, 'limit': params.limit, 'count': params.count, 'search_domain': ustr(params.search_domain), 'filter_domain': ustr(params.filter_domain) } if not params.id: raise redirect(self.path + '/edit', **args) raise redirect(self.path + '/view', **args) @expose(content_type='application/octet-stream') def save_binary_data(self, _fname='file.dat', **kw): params, data = TinyDict.split(kw) cherrypy.response.headers[ 'Content-Disposition'] = 'attachment; filename="%s"' % _fname if params.datas: form = params.datas['form'] res = form.get(params.field) return base64.decodestring(res) elif params.id: proxy = rpc.RPCProxy(params.model) res = proxy.read([params.id], [params.field], rpc.session.context) return base64.decodestring(res[0][params.field]) else: return base64.decodestring(data[params.field]) @expose() def clear_binary_data(self, **kw): params, data = TinyDict.split(kw) proxy = rpc.RPCProxy(params.model) ctx = utils.context_with_concurrency_info(params.context, params.concurrency_info) if params.fname: proxy.write([params.id], { params.field: False, params.fname: False }, ctx) else: proxy.write([params.id], {params.field: False}, ctx) args = { 'model': params.model, 'id': params.id, 'ids': ustr(params.ids), 'view_ids': ustr(params.view_ids), 'view_mode': ustr(params.view_mode), 'domain': ustr(params.domain), 'context': ustr(params.context), 'offset': params.offset, 'limit': params.limit, 'count': params.count, 'search_domain': ustr(params.search_domain), 'filter_domain': ustr(params.filter_domain) } raise redirect(self.path + '/edit', **args) @expose(content_type='image/png') def binary_image_get_image(self, **kw): model = kw.get('model') field = kw.get('field') id = kw.get('id') proxy = rpc.RPCProxy(model) if id == 'None': # FIXME: doesnt honor the context res = proxy.default_get([field]).get(field, '') else: res = proxy.read([int(id)], [field])[0].get(field) if res: return base64.decodestring(res) else: return open( openobject.paths.addons('openerp', 'static', 'images', 'placeholder.png'), 'rb').read() @expose("json") def binary_image_delete(self, **kw): saved = kw.get('saved') or None model = kw.get('model') id = kw.get('id') if id: id = int(id) field = kw.get('field') if id: proxy = rpc.RPCProxy(model) proxy.write([id], {field: False}) return {} @expose() def b64(self, **kw): #idea from http://dean.edwards.name/weblog/2005/06/base64-ie/ try: qs = cherrypy.request.query_string content_type, data = qs.split(';') data_type, data = data.split(',') assert (data_type == 'base64') cherrypy.response.headers['Content-Type'] = content_type return base64.decodestring(data) except: raise cherrypy.HTTPError(400) # Bad request @expose() @validate(form=get_validation_schema) @error_handler(default_error_handler) @exception_handler(default_exception_handler) def filter(self, **kw): params, data = TinyDict.split(kw) if params.get('_terp_save_current_id'): ctx = dict((params.context or {}), **rpc.session.context) if params.id: rpc.RPCProxy(params.model).write([params.id], data, ctx) else: id = rpc.RPCProxy(params.model).create(data, ctx) params.ids.append(id) params.count += 1 l = params.limit or 50 o = params.offset or 0 c = params.count or 0 id = params.id or False ids = params.ids or [] filter_action = params.filter_action if ids and filter_action == 'FIRST': o = 0 id = ids[0] if ids and filter_action == 'LAST': o = c - c % l id = ids[-1] if ids and filter_action == 'PREV': if id == ids[0]: o -= l elif id in ids: id = ids[ids.index(id) - 1] if ids and filter_action == 'NEXT': if id == ids[-1]: o += l elif id in ids: id = ids[ids.index(id) + 1] elif id is False: o = 0 id = ids[0] if filter_action: # remember the current page (tab) of notebooks cherrypy.session['remember_notebooks'] = True if params.offset != o: domain = params.domain if params.search_domain is not None: domain = params.search_domain data = params.search_data ctx = params.context or {} ctx.update(rpc.session.context.copy()) res = search(params.model, o, l, domain=domain, context=ctx, data=data) o = res['offset'] l = res['limit'] if not c: c = res['count'] params.search_domain = res['search_domain'] params.search_data = res['search_data'] ids = res['ids'] id = False if ids and filter_action in ('FIRST', 'NEXT'): id = ids[0] if ids and filter_action in ('LAST', 'PREV'): id = ids[-1] params.id = id params.ids = ids params.offset = o params.limit = l params.count = c return self.create(params) @expose() def find(self, **kw): kw['_terp_offset'] = None kw['_terp_limit'] = None kw['_terp_search_domain'] = None kw['_terp_search_data'] = None kw['_terp_filter_action'] = 'FIND' return self.filter(**kw) @expose() def first(self, **kw): kw['_terp_filter_action'] = 'FIRST' return self.filter(**kw) @expose() def last(self, **kw): kw['_terp_filter_action'] = 'LAST' return self.filter(**kw) @expose() def previous(self, **kw): if '_terp_source' in kw: return self.previous_o2m(**kw) kw['_terp_filter_action'] = 'PREV' return self.filter(**kw) @expose() def next(self, **kw): if '_terp_source' in kw: return self.next_o2m(**kw) kw['_terp_filter_action'] = 'NEXT' return self.filter(**kw) @expose() @validate(form=get_validation_schema) @error_handler(default_error_handler) @exception_handler(default_exception_handler) def previous_o2m(self, **kw): params, data = TinyDict.split(kw) if params.get('_terp_save_current_id'): ctx = dict((params.context or {}), **rpc.session.context) if params.id: rpc.RPCProxy(params.model).write([params.id], data, ctx) else: id = rpc.RPCProxy(params.model).create(data, ctx) params.ids.append(id) params.count += 1 current = params.chain_get(params.source or '') or params idx = -1 if current.id: # save current record if params.editable: self.save(terp_save_only=True, **kw) idx = current.ids.index(current.id) idx = idx - 1 if idx == len(current.ids): idx = len(current.ids) - 1 if current.ids: current.id = current.ids[idx] return self.create(params) @expose() def next_o2m(self, **kw): params, data = TinyDict.split(kw) c = params.count or 0 current = params.chain_get(params.source or '') or params idx = 0 if current.id: # save current record if params.editable: self.save(terp_save_only=True, **kw) idx = current.ids.index(current.id) idx = idx + 1 if idx == len(current.ids): idx = 0 if current.ids: current.id = current.ids[idx] return self.create(params) @expose() @validate(form=get_validation_schema) def switch(self, **kw): params, data = TinyDict.split(kw) if params.get('_terp_save_current_id'): ctx = dict((params.context or {}), **rpc.session.context) if params.id: rpc.RPCProxy(params.model).write([params.id], data, ctx) else: id = rpc.RPCProxy(params.model).create(data, ctx) params.ids.append(id) params.count += 1 # switch the view params.view_type = params.source_view_type return self.create(params) def do_action(self, name, adds={}, datas={}): params, data = TinyDict.split(datas) model = params.model id = params.id or False ids = params.selection or params.ids or [] if params.view_type == 'form': #TODO: save current record ids = (id or []) and [id] if id and not ids: ids = [id] if len(ids): import actions return actions.execute_by_keyword(name, adds=adds, model=model, id=id, ids=ids, report_type='pdf') else: raise common.message(_("No record selected")) @expose() def report(self, **kw): return self.do_action('client_print_multi', adds={ 'Print Screen': { 'report_name': 'printscreen.list', 'name': _('Print Screen'), 'type': 'ir.actions.report.xml' } }, datas=kw) @expose() 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) 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) @expose() def dashlet(self, **kw): params, data = TinyDict.split(kw) current = params.chain_get(str(params.source) or '') or params return self.create(current) @expose('json') 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()) if response is False: # response is False when creating new record for inherited view. response = {} if 'value' not in response: response['value'] = {} result.update(response) # apply validators (transform values from python) values = result['value'] values2 = {} for k, v in values.items(): key = ((prefix or '') and prefix + '/') + k kind = data.get(key, {}).get('type', '') if key in data and key != 'id': values2[k] = data[key] values2[k]['value'] = v else: values2[k] = {'value': v} if kind == 'float': field = proxy.fields_get([k], ctx2) digit = field[k].get('digits') if digit: digit = digit[1] values2[k]['digit'] = digit or 2 values = TinyForm(**values2).from_python().make_plain() # get name of m2o and reference fields for k, v in values2.items(): kind = v.get('type') relation = v.get('relation') if relation and kind in ('many2one', 'reference') and values.get(k): values[k] = [ values[k], rpc.name_get(relation, values[k], context) ] result['value'] = values # convert domains in string to prevent them being converted in JSON if 'domain' in result: for k in result['domain']: result['domain'][k] = ustr(result['domain'][k]) if change_default: value = data.get('_terp_value') proxy = rpc.RPCProxy('ir.values') values = proxy.get('default', '%s=%s' % (caller, value), [(model, False)], False, context) for index, fname, value in values: if fname not in result['value']: result['value'][fname] = value return result