diff --git a/addons/web/models/models.py b/addons/web/models/models.py index fe01ffe6938..31e5834d200 100644 --- a/addons/web/models/models.py +++ b/addons/web/models/models.py @@ -1,11 +1,14 @@ # -*- coding: utf-8 -*- -from datetime import datetime import babel.dates import pytz -from odoo.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT, DEFAULT_SERVER_DATE_FORMAT from odoo import _, api, fields, models +from odoo.tools import lazy +class IrActionsActWindowView(models.Model): + _inherit = 'ir.actions.act_window.view' + + view_mode = fields.Selection(selection_add=[('qweb', 'QWeb')]) class Base(models.AbstractModel): _inherit = 'base' @@ -78,3 +81,35 @@ class Base(models.AbstractModel): data[group_by_value][field_value] += 1 return data + + ##### qweb view hooks ##### + @api.model + def qweb_render_view(self, view_id, domain): + assert view_id + return self.env['ir.qweb'].render( + view_id, { + **self.env['ir.ui.view']._prepare_qcontext(), + **self._qweb_prepare_qcontext(view_id, domain), + }) + + def _qweb_prepare_qcontext(self, view_id, domain): + """ + Base qcontext for rendering qweb views bound to this model + """ + return { + 'model': self, + 'domain': domain, + # not necessarily necessary as env is already part of the + # non-minimal qcontext + 'context': self.env.context, + 'records': lazy(self.search, domain), + } + + @api.model + def fields_view_get(self, view_id=None, view_type='form', toolbar=False, submenu=False): + r = super().fields_view_get(view_id, view_type, toolbar, submenu) + # avoid leaking the raw (un-rendered) template, also avoids bloating + # the response payload for no reason + if r['type'] == 'qweb': + r['arch'] = '' + return r diff --git a/addons/web/static/src/js/views/abstract_controller.js b/addons/web/static/src/js/views/abstract_controller.js index 57b3771427b..ba00b4dbab7 100644 --- a/addons/web/static/src/js/views/abstract_controller.js +++ b/addons/web/static/src/js/views/abstract_controller.js @@ -456,7 +456,10 @@ var AbstractController = mvc.Controller.extend(ActionMixin, { } }); } else if ($target.attr('name')) { - this.do_action($target.attr('name')); + this.do_action( + $target.attr('name'), + data.context && {additional_context: data.context} + ); } else { this.do_action({ name: $target.attr('title') || _.str.strip($target.text()), diff --git a/addons/web/static/src/js/views/qweb/qweb_view.js b/addons/web/static/src/js/views/qweb/qweb_view.js new file mode 100644 index 00000000000..9e79ecf19d2 --- /dev/null +++ b/addons/web/static/src/js/views/qweb/qweb_view.js @@ -0,0 +1,202 @@ +/** + * Client-side implementation of a qweb view. + */ +odoo.define('web.qweb', function (require) { +"use strict"; + +var core = require('web.core'); +var AbstractView = require('web.AbstractView'); +var AbstractModel = require('web.AbstractModel'); +var AbstractRenderer = require('web.AbstractRenderer'); +var AbstractController = require('web.AbstractController'); +var registry = require('web.view_registry'); + +var _lt = core._lt; + +/** + * model + */ +var Model = AbstractModel.extend({ + /** + * init + */ + init: function () { + this._super.apply(this, arguments); + this._state = { + viewId: false, + modelName: false, + body: '', + context: {}, + domain: [], + }; + }, + /** + * fetches the rendered qweb view + */ + _fetch: function () { + var state = this._state; + return this._rpc({ + model: state.modelName, + method: 'qweb_render_view', + kwargs: { + view_id: state.viewId, + domain: state.domain, + context: state.context + } + }).then(function (r) { + state.body = r; + return state.viewId; + }); + }, + /** + * get + */ + get: function () { + return this._state; + }, + /** + * load + */ + load: function (params) { + _.extend(this._state, _.pick(params, ['viewId', 'modelName', 'domain', 'context'])); + + return this._fetch(); + }, + /** + * reload + */ + reload: function (_id, _params) { + // FIXME: update this._state or something? + return this._fetch(); + } +}); +/** + * renderer + */ +var Renderer = AbstractRenderer.extend({ + /** + * render + */ + _render: function () { + var self = this; + return this._super.apply(this, arguments).then(function () { + self.$el.html(self.state.body); + }); + } +}); +/** + * controller + */ +var Controller = AbstractController.extend({ + events: _.extend({}, AbstractController.prototype.events, { + 'click [type="toggle"]': '_onLazyToggle', + }), + + init: function () { + this._super.apply(this, arguments); + this._$buttons = $('