diff --git a/addons/auth_totp/static/tests/totp_flow.js b/addons/auth_totp/static/tests/totp_flow.js index aad2e2050d8..f61ddcfcba7 100644 --- a/addons/auth_totp/static/tests/totp_flow.js +++ b/addons/auth_totp/static/tests/totp_flow.js @@ -1,6 +1,6 @@ /** @odoo-module **/ -import ajax from "@web/legacy/js/core/ajax"; +import { jsonrpc } from "@web/core/network/rpc_service"; import { registry } from "@web/core/registry"; import { stepUtils } from "@web_tour/tour_service/tour_utils"; @@ -98,7 +98,7 @@ registry.category("web_tour.tours").add('totp_tour_setup', { const $secret = this.$anchor.closest('div').find('[name=secret] span:first-child'); const $copyBtn = $secret.find('button'); $copyBtn.remove(); - const token = await ajax.jsonRpc('/totphook', 'call', { + const token = await jsonrpc('/totphook', { secret: $secret.text() }); helpers.text(token, '[name=code] input'); @@ -148,7 +148,7 @@ registry.category("web_tour.tours").add('totp_login_enabled', { // content of the HTML element, not the JS value property. We // could set a class but that's really no better than // procedurally clicking the button after we've set the input. - const token = await ajax.jsonRpc('/totphook', 'call', {}); + const token = await jsonrpc('/totphook'); helpers.text(token); helpers.click('button:contains("Log in")'); } @@ -185,7 +185,7 @@ registry.category("web_tour.tours").add('totp_login_device', { content: "input code", trigger: 'input[name=totp_token]', async run(helpers) { - const token = await ajax.jsonRpc('/totphook', 'call', {}) + const token = await jsonrpc('/totphook') helpers.text(token); helpers.click('button:contains("Log in")'); } diff --git a/addons/auth_totp_portal/static/src/js/totp_frontend.js b/addons/auth_totp_portal/static/src/js/totp_frontend.js index d0c16108745..fe0ce43118c 100644 --- a/addons/auth_totp_portal/static/src/js/totp_frontend.js +++ b/addons/auth_totp_portal/static/src/js/totp_frontend.js @@ -139,14 +139,20 @@ publicWidget.registry.TOTPButton = publicWidget.Widget.extend({ click: '_onClick', }, + init() { + this._super(...arguments); + this.orm = this.bindService("orm"); + this.dialog = this.bindService("dialog"); + }, + async _onClick(e) { e.preventDefault(); - const w = await handleCheckIdentity(this.proxy('_rpc'), this._rpc({ - model: 'res.users', - method: 'action_totp_enable_wizard', - args: [session.user_id] - }), (...args) => this.call("dialog", "add", ...args)); + const w = await handleCheckIdentity( + this.orm.call("res.users", "action_totp_enable_wizard", [session.user_id]), + this.orm, + this.dialog + ); if (!w) { // TOTP probably already enabled, just reload page @@ -156,9 +162,7 @@ publicWidget.registry.TOTPButton = publicWidget.Widget.extend({ const {res_model: model, res_id: wizard_id} = w; - const record = await this._rpc({ - model, method: 'read', args: [wizard_id, []] - }).then(ar => ar[0]); + const record = await this.orm.read(model, [wizard_id], []).then(ar => ar[0]); const doc = new DOMParser().parseFromString( document.getElementById('totp_wizard_view').textContent, @@ -181,15 +185,11 @@ publicWidget.registry.TOTPButton = publicWidget.Widget.extend({ } try { - await this._rpc({ - model, - method: "write", - args: [record.id, { code: inputEl.value }], - }); + await this.orm.write(model, [record.id], { code: inputEl.value }); await handleCheckIdentity( - this.proxy("_rpc"), - this._rpc({ model, method: "enable", args: [record.id] }), - (...args) => this.call("dialog", "add", ...args) + this.orm.call(model, "enable", [record.id]), + this.orm, + this.dialog ); } catch (e) { const errorMessage = ( @@ -216,12 +216,18 @@ publicWidget.registry.DisableTOTPButton = publicWidget.Widget.extend({ click: '_onClick' }, + init() { + this._super(...arguments); + this.orm = this.bindService("orm"); + this.dialog = this.bindService("dialog"); + }, + async _onClick(e) { e.preventDefault(); await handleCheckIdentity( - this.proxy('_rpc'), - this._rpc({model: 'res.users', method: 'action_totp_disable', args: [session.user_id]}), - (...args) => this.call("dialog", "add", ...args) + this.orm.call("res.users", "action_totp_disable", [session.user_id]), + this.orm, + this.dialog ) window.location = window.location; } @@ -235,13 +241,9 @@ publicWidget.registry.RevokeTrustedDeviceButton = publicWidget.Widget.extend({ async _onClick(e){ e.preventDefault(); await handleCheckIdentity( - this.proxy('_rpc'), - this._rpc({ - model: 'auth_totp.device', - method: 'remove', - args: [parseInt(this.el.id)] - }), - (...args) => this.call("dialog", "add", ...args) + this.orm.call("auth_totp.device", "remove", [parseInt(this.el.id)]), + this.orm, + this.dialog ); window.location = window.location; } @@ -252,16 +254,18 @@ publicWidget.registry.RevokeAllTrustedDevicesButton = publicWidget.Widget.extend click: '_onClick' }, + init() { + this._super(...arguments); + this.orm = this.bindService("orm"); + this.dialog = this.bindService("dialog"); + }, + async _onClick(e){ e.preventDefault(); await handleCheckIdentity( - this.proxy('_rpc'), - this._rpc({ - model: 'res.users', - method: 'revoke_all_devices', - args: [session.user_id] - }), - (...args) => this.call("dialog", "add", ...args) + this.orm.call("res.users", "revoke_all_devices", [session.user_id]), + this.orm, + this.dialog ); window.location = window.location; } diff --git a/addons/auth_totp_portal/static/tests/totp_portal.js b/addons/auth_totp_portal/static/tests/totp_portal.js index 7331bc15d9c..461b3be027a 100644 --- a/addons/auth_totp_portal/static/tests/totp_portal.js +++ b/addons/auth_totp_portal/static/tests/totp_portal.js @@ -1,7 +1,7 @@ /** @odoo-module **/ -import ajax from "@web/legacy/js/core/ajax"; import { registry } from "@web/core/registry"; +import { jsonrpc } from "@web/core/network/rpc_service"; registry.category("web_tour.tours").add('totportal_tour_setup', { test: true, @@ -29,7 +29,7 @@ registry.category("web_tour.tours").add('totportal_tour_setup', { trigger: 'a:contains("Cannot scan it?")', run: async function(helpers) { const secret = this.$anchor.closest('div').find('span[name="secret"]').text(); - const token = await ajax.jsonRpc('/totphook', 'call', { + const token = await jsonrpc('/totphook', { secret }); helpers._text(helpers._get_action_values('input[name=code]'), token); @@ -65,7 +65,7 @@ registry.category("web_tour.tours").add('totportal_login_enabled', { content: "input code", trigger: 'input[name=totp_token]', run: async function (helpers) { - const token = await ajax.jsonRpc('/totphook', 'call', {}); + const token = await jsonrpc('/totphook'); helpers._text(helpers._get_action_values(), token); // FIXME: is there a way to put the button as its own step trigger without // the tour straight blowing through and not waiting for this? diff --git a/addons/im_livechat/static/src/embed/external/boot.js b/addons/im_livechat/static/src/embed/external/boot.js index 027ed02bd88..8e92ae75e54 100644 --- a/addons/im_livechat/static/src/embed/external/boot.js +++ b/addons/im_livechat/static/src/embed/external/boot.js @@ -30,12 +30,11 @@ import { session } from "@web/session"; { async: true, start(env) { - let rpcId = 0; - return function rpc(route, params = {}, settings) { + return function rpc(route, params = {}, settings = {}) { if (!route.match(/^(?:https?:)?\/\//)) { route = session.origin + route; } - return jsonrpc(env, rpcId++, route, params, settings); + return jsonrpc(route, params, { bus: env.bus, ...settings }); }; }, }, diff --git a/addons/mail_group/static/src/js/mail_group.js b/addons/mail_group/static/src/js/mail_group.js index 9119a70437a..ac1e6f7ea5a 100644 --- a/addons/mail_group/static/src/js/mail_group.js +++ b/addons/mail_group/static/src/js/mail_group.js @@ -9,6 +9,11 @@ publicWidget.registry.MailGroup = publicWidget.Widget.extend({ 'click .o_mg_subscribe_btn': '_onSubscribeBtnClick', }, + init() { + this._super(...arguments); + this.rpc = this.bindService("rpc"); + }, + /** * @override */ @@ -42,13 +47,10 @@ publicWidget.registry.MailGroup = publicWidget.Widget.extend({ const action = (this.isMember || this.forceUnsubscribe) ? 'unsubscribe' : 'subscribe'; - const response = await this._rpc({ - route: '/group/' + action, - params: { - 'group_id': this.mailgroupId, - 'email': email, - 'token': this.token, - }, + const response = await this.rpc('/group/' + action, { + 'group_id': this.mailgroupId, + 'email': email, + 'token': this.token, }); this.$el.find('.o_mg_alert').remove(); diff --git a/addons/mail_group/static/src/js/mail_group_message.js b/addons/mail_group/static/src/js/mail_group_message.js index 47e657b36cf..b900f05094d 100644 --- a/addons/mail_group/static/src/js/mail_group_message.js +++ b/addons/mail_group/static/src/js/mail_group_message.js @@ -10,6 +10,11 @@ publicWidget.registry.MailGroupMessage = publicWidget.Widget.extend({ 'click button.o_mg_read_more': '_onReadMoreClick', }, + init() { + this._super(...arguments); + this.rpc = this.bindService("rpc"); + }, + /** * @override */ @@ -63,11 +68,8 @@ publicWidget.registry.MailGroupMessage = publicWidget.Widget.extend({ */ _onReadMoreClick: function (ev) { const $link = $(ev.target); - this._rpc({ - route: $link.data('href'), - params: { - last_displayed_id: $link.data('last-displayed-id'), - }, + this.rpc($link.data('href'), { + last_displayed_id: $link.data('last-displayed-id'), }).then(function (data) { if (!data) { return; diff --git a/addons/mass_mailing/static/src/js/mailing_portal.js b/addons/mass_mailing/static/src/js/mailing_portal.js index d9c1cbcaf10..7be8aafd2f7 100644 --- a/addons/mass_mailing/static/src/js/mailing_portal.js +++ b/addons/mass_mailing/static/src/js/mailing_portal.js @@ -1,9 +1,9 @@ /** @odoo-module **/ -import ajax from "@web/legacy/js/core/ajax"; import { escape } from "@web/core/utils/strings"; import { whenReady } from "@odoo/owl"; import { _t } from "@web/core/l10n/translation"; +import { jsonrpc } from "@web/core/network/rpc_service"; whenReady(() => { var email = $("input[name='email']").val(); @@ -13,7 +13,7 @@ whenReady(() => { if ($('.o_unsubscribe_form').length) { if (email != '' && email != undefined){ - ajax.jsonRpc('/mailing/blacklist/check', 'call', {'email': email, 'mailing_id': mailing_id, 'res_id': res_id, 'token': token}) + jsonrpc('/mailing/blacklist/check', {'email': email, 'mailing_id': mailing_id, 'res_id': res_id, 'token': token}) .then(function (result) { if (result == 'unauthorized'){ $('#button_add_blacklist').hide(); @@ -67,7 +67,7 @@ whenReady(() => { unchecked_ids[i] = parseInt($(this).val()); }); - ajax.jsonRpc('/mailing/list/update', 'call', {'opt_in_ids': checked_ids, 'opt_out_ids': unchecked_ids, 'email': email, 'mailing_id': mailing_id, 'res_id': res_id, 'token': token}) + jsonrpc('/mailing/list/update', {'opt_in_ids': checked_ids, 'opt_out_ids': unchecked_ids, 'email': email, 'mailing_id': mailing_id, 'res_id': res_id, 'token': token}) .then(function (result) { if (result == 'unauthorized'){ $('#subscription_info').text(_t('You are not authorized to do this!')); @@ -94,7 +94,7 @@ whenReady(() => { $('#button_add_blacklist').click(function (e) { e.preventDefault(); - ajax.jsonRpc('/mailing/blacklist/add', 'call', {'email': email, 'mailing_id': mailing_id, 'res_id': res_id, 'token': token}) + jsonrpc('/mailing/blacklist/add', {'email': email, 'mailing_id': mailing_id, 'res_id': res_id, 'token': token}) .then(function (result) { if (result == 'unauthorized'){ $('#subscription_info').text(_t('You are not authorized to do this!')); @@ -126,7 +126,7 @@ whenReady(() => { $('#button_remove_blacklist').click(function (e) { e.preventDefault(); - ajax.jsonRpc('/mailing/blacklist/remove', 'call', {'email': email, 'mailing_id': mailing_id, 'res_id': res_id, 'token': token}) + jsonrpc('/mailing/blacklist/remove', {'email': email, 'mailing_id': mailing_id, 'res_id': res_id, 'token': token}) .then(function (result) { if (result == 'unauthorized'){ $('#subscription_info').text(_t('You are not authorized to do this!')); @@ -161,7 +161,7 @@ whenReady(() => { $('#button_feedback').click(function (e) { var feedback = $("textarea[name='opt_out_feedback']").val(); e.preventDefault(); - ajax.jsonRpc('/mailing/feedback', 'call', {'mailing_id': mailing_id, 'res_id': res_id, 'email': email, 'feedback': feedback, 'token': token}) + jsonrpc('/mailing/feedback', {'mailing_id': mailing_id, 'res_id': res_id, 'email': email, 'feedback': feedback, 'token': token}) .then(function (result) { if (result == 'unauthorized'){ $('#subscription_info').text(_t('You are not authorized to do this!')); diff --git a/addons/mass_mailing/static/src/js/mass_mailing_html_field.js b/addons/mass_mailing/static/src/js/mass_mailing_html_field.js index 24e8f24d42e..8675d31d331 100644 --- a/addons/mass_mailing/static/src/js/mass_mailing_html_field.js +++ b/addons/mass_mailing/static/src/js/mass_mailing_html_field.js @@ -9,7 +9,6 @@ import { toInline, getCSSRules } from "@web_editor/js/backend/convert_inline"; import { loadBundle } from "@web/core/assets"; import { renderToElement } from "@web/core/utils/render"; import { useService } from "@web/core/utils/hooks"; -import { buildQuery } from "@web/legacy/js/core/rpc"; import { HtmlField, htmlField } from "@web_editor/js/backend/html_field"; import { MassMailingMobilePreviewDialog } from "./mass_mailing_mobile_preview"; import { getRangePosition } from '@web_editor/js/editor/odoo-editor/src/utils/utils'; @@ -39,6 +38,7 @@ export class MassMailingHtmlField extends HtmlField { }); this.action = useService('action'); this.rpc = useService('rpc'); + this.orm = useService('orm'); this.dialog = useService('dialog'); useRecordObserver((record) => { @@ -217,13 +217,8 @@ export class MassMailingHtmlField extends HtmlField { ? [[['mailing_model_id', '=', this.props.record.data.mailing_model_id[0]]]] : []; - const rpcQuery = buildQuery({ - model: 'mailing.mailing', - method: 'action_fetch_favorites', - args: args, - }) // Templates taken from old mailings - const result = await this.rpc(rpcQuery.route, rpcQuery.params); + const result = await this.orm.call('mailing.mailing', 'action_fetch_favorites', args); if (status(this) === 'destroyed') return; const templatesParams = result.map(values => { return { @@ -416,13 +411,7 @@ export class MassMailingHtmlField extends HtmlField { const $target = $(ev.currentTarget); const mailingId = $target.data('id'); - const rpcQuery = buildQuery({ - model: 'mailing.mailing', - method: 'action_remove_favorite', - args: [mailingId], - }) - const action = await this.rpc(rpcQuery.route, rpcQuery.params); - + const action = await this.orm.call('mailing.mailing', 'action_remove_favorite', [mailingId]); this.action.doAction(action); $target.parents('.o_mail_template_preview').remove(); diff --git a/addons/mrp_subcontracting/__manifest__.py b/addons/mrp_subcontracting/__manifest__.py index 03f3a03d1fb..8a70866eb43 100644 --- a/addons/mrp_subcontracting/__manifest__.py +++ b/addons/mrp_subcontracting/__manifest__.py @@ -96,14 +96,12 @@ 'web/static/src/legacy/js/libs/bootstrap.js', 'web/static/src/legacy/js/libs/jquery.js', 'web/static/src/legacy/js/libs/jSignatureCustom.js', - 'web/static/src/legacy/js/core/ajax.js', 'web/static/src/legacy/js/core/bus.js', 'web/static/src/legacy/js/core/class.js', 'web/static/src/legacy/js/core/dialog.js', 'web/static/src/legacy/xml/dialog.xml', 'web/static/src/legacy/js/core/dom.js', 'web/static/src/legacy/js/core/mixins.js', - 'web/static/src/legacy/js/core/rpc.js', 'web/static/src/legacy/js/core/service_mixins.js', 'web/static/src/legacy/js/core/time.js', 'web/static/src/legacy/js/core/widget.js', diff --git a/addons/payment/static/src/js/payment_form.js b/addons/payment/static/src/js/payment_form.js index ea7d4c59c89..fbe1697816e 100644 --- a/addons/payment/static/src/js/payment_form.js +++ b/addons/payment/static/src/js/payment_form.js @@ -17,6 +17,15 @@ publicWidget.registry.PaymentForm = publicWidget.Widget.extend({ // #=== WIDGET LIFECYCLE ===# + /** + * @override + */ + init() { + this._super(...arguments); + this.rpc = this.bindService("rpc"); + this.orm = this.bindService("orm"); + }, + /** * @override */ @@ -76,11 +85,11 @@ publicWidget.registry.PaymentForm = publicWidget.Widget.extend({ const linkedRadio = document.getElementById(ev.currentTarget.dataset['linkedRadio']); const tokenId = this._getPaymentOptionId(linkedRadio); - this._rpc({ - model: 'payment.token', - method: 'get_linked_records_info', - args: [tokenId], - }).then(linkedRecordsInfo => { + this.orm.call( + 'payment.token', + 'get_linked_records_info', + [tokenId], + ).then(linkedRecordsInfo => { this._challengeTokenDeletion(tokenId, linkedRecordsInfo); }).guardedCatch(error => { error.event.preventDefault(); @@ -342,12 +351,9 @@ publicWidget.registry.PaymentForm = publicWidget.Widget.extend({ * @return {void} */ async _assignToken(tokenId) { - this._rpc({ - route: this.paymentContext['assignTokenRoute'], - params: { - 'token_id': tokenId, - 'access_token': this.paymentContext['accessToken'], - } + this.rpc(this.paymentContext['assignTokenRoute'], { + 'token_id': tokenId, + 'access_token': this.paymentContext['accessToken'], }).then(() => { window.location = this.paymentContext['landingRoute']; }).guardedCatch(error => { @@ -376,10 +382,10 @@ publicWidget.registry.PaymentForm = publicWidget.Widget.extend({ */ async _initiatePaymentFlow(providerCode, paymentOptionId, paymentMethodCode, flow) { // Create a transaction and retrieve its processing values. - this._rpc({ - route: this.paymentContext['transactionRoute'], - params: this._prepareTransactionRouteParams(), - }).then(processingValues => { + this.rpc( + this.paymentContext['transactionRoute'], + this._prepareTransactionRouteParams(), + ).then(processingValues => { if (flow === 'redirect') { this._processRedirectFlow( providerCode, paymentOptionId, paymentMethodCode, processingValues @@ -490,11 +496,8 @@ publicWidget.registry.PaymentForm = publicWidget.Widget.extend({ * @return {void} */ _archiveToken(tokenId) { - this._rpc({ - route: '/payment/archive_token', - params: { - 'token_id': tokenId, - }, + this.rpc('/payment/archive_token', { + 'token_id': tokenId, }).then(() => { browser.location.reload(); }).guardedCatch(error => { diff --git a/addons/payment/static/src/js/post_processing.js b/addons/payment/static/src/js/post_processing.js index c7f141a526f..83b383d36f4 100644 --- a/addons/payment/static/src/js/post_processing.js +++ b/addons/payment/static/src/js/post_processing.js @@ -11,6 +11,11 @@ publicWidget.registry.PaymentPostProcessing = publicWidget.Widget.extend({ timeout: 0, pollCount: 0, + init() { + this._super(...arguments); + this.rpc = this.bindService("rpc"); + }, + async start() { this.call('ui', 'block', { 'message': _t("We are processing your payment. Please wait."), @@ -24,11 +29,8 @@ publicWidget.registry.PaymentPostProcessing = publicWidget.Widget.extend({ setTimeout(() => { // Fetch the post-processing values from the server. const self = this; - this._rpc({ - route: '/payment/status/poll', - params: { - 'csrf_token': odoo.csrf_token, - } + this.rpc('/payment/status/poll', { + 'csrf_token': odoo.csrf_token, }).then(postProcessingValues => { let { state, display_message, landing_route } = postProcessingValues; diff --git a/addons/payment_adyen/static/src/js/payment_form.js b/addons/payment_adyen/static/src/js/payment_form.js index 3dfe4372430..d19ebd586b1 100644 --- a/addons/payment_adyen/static/src/js/payment_form.js +++ b/addons/payment_adyen/static/src/js/payment_form.js @@ -50,18 +50,15 @@ paymentForm.include({ // Create the checkout object if not already done for another payment method. if (!this.adyenCheckout) { - await this._rpc({ // Await the RPC to let it create AdyenCheckout before using it. - route: '/payment/adyen/payment_methods', - params: { - 'provider_id': providerId, - 'partner_id': parseInt(this.paymentContext['partnerId']), - 'amount': this.paymentContext['amount'] - ? parseFloat(this.paymentContext['amount']) - : undefined, - 'currency_id': this.paymentContext['currencyId'] - ? parseInt(this.paymentContext['currencyId']) - : undefined, - }, + await this.rpc('/payment/adyen/payment_methods', { // Await the RPC to let it create AdyenCheckout before using it. + 'provider_id': providerId, + 'partner_id': parseInt(this.paymentContext['partnerId']), + 'amount': this.paymentContext['amount'] + ? parseFloat(this.paymentContext['amount']) + : undefined, + 'currency_id': this.paymentContext['currencyId'] + ? parseInt(this.paymentContext['currencyId']) + : undefined, }).then(async response => { // Create the Adyen Checkout SDK. const providerState = this._getProviderState(radio); @@ -161,24 +158,21 @@ paymentForm.include({ */ _adyenOnSubmit(state, component) { // Create the transaction and retrieve the processing values. - this._rpc({ - route: this.paymentContext['transactionRoute'], - params: this._prepareTransactionRouteParams(), - }).then(processingValues => { + this.rpc( + this.paymentContext['transactionRoute'], + this._prepareTransactionRouteParams(), + ).then(processingValues => { component.reference = processingValues.reference; // Store final reference. // Initiate the payment. - return this._rpc({ - route: '/payment/adyen/payments', - params: { - 'provider_id': processingValues.provider_id, - 'reference': processingValues.reference, - 'converted_amount': processingValues.converted_amount, - 'currency_id': processingValues.currency_id, - 'partner_id': processingValues.partner_id, - 'payment_method': state.data.paymentMethod, - 'access_token': processingValues.access_token, - 'browser_info': state.data.browserInfo, - }, + return this.rpc('/payment/adyen/payments', { + 'provider_id': processingValues.provider_id, + 'reference': processingValues.reference, + 'converted_amount': processingValues.converted_amount, + 'currency_id': processingValues.currency_id, + 'partner_id': processingValues.partner_id, + 'payment_method': state.data.paymentMethod, + 'access_token': processingValues.access_token, + 'browser_info': state.data.browserInfo, }); }).then(paymentResponse => { if (paymentResponse.action) { // An additional action is required from the shopper. @@ -204,13 +198,10 @@ paymentForm.include({ * @return {void} */ _adyenOnSubmitAdditionalDetails(state, component) { - this._rpc({ - route: '/payment/adyen/payments/details', - params: { - 'provider_id': this.paymentContext['providerId'], - 'reference': component.reference, - 'payment_details': state.data, - }, + this.rpc('/payment/adyen/payments/details', { + 'provider_id': this.paymentContext['providerId'], + 'reference': component.reference, + 'payment_details': state.data, }).then(paymentDetails => { if (paymentDetails.action) { // Additional action required from the shopper. component.handleAction(paymentDetails.action); diff --git a/addons/payment_authorize/static/src/js/payment_form.js b/addons/payment_authorize/static/src/js/payment_form.js index 9e139a31aa6..4f85bc07198 100644 --- a/addons/payment_authorize/static/src/js/payment_form.js +++ b/addons/payment_authorize/static/src/js/payment_form.js @@ -138,14 +138,11 @@ paymentForm.include({ } // Initiate the payment - this._rpc({ - route: '/payment/authorize/payment', - params: { - 'reference': processingValues.reference, - 'partner_id': processingValues.partner_id, - 'opaque_data': response.opaqueData, - 'access_token': processingValues.access_token, - } + this.rpc('/payment/authorize/payment', { + 'reference': processingValues.reference, + 'partner_id': processingValues.partner_id, + 'opaque_data': response.opaqueData, + 'access_token': processingValues.access_token, }).then(() => { window.location = '/payment/status'; }).guardedCatch((error) => { diff --git a/addons/payment_demo/static/src/js/payment_form.js b/addons/payment_demo/static/src/js/payment_form.js index 4f38d68d243..d252a9fc947 100644 --- a/addons/payment_demo/static/src/js/payment_form.js +++ b/addons/payment_demo/static/src/js/payment_form.js @@ -51,13 +51,10 @@ paymentForm.include({ const customerInput = document.getElementById('customer_input').value; const simulatedPaymentState = document.getElementById('simulated_payment_state').value; - this._rpc({ - route: '/payment/demo/simulate_payment', - params: { - 'reference': processingValues.reference, - 'payment_details': customerInput, - 'simulated_state': simulatedPaymentState, - }, + this.rpc('/payment/demo/simulate_payment', { + 'reference': processingValues.reference, + 'payment_details': customerInput, + 'simulated_state': simulatedPaymentState, }).then(() => { window.location = '/payment/status'; }).guardedCatch(error => { diff --git a/addons/payment_stripe/static/src/js/express_checkout_form.js b/addons/payment_stripe/static/src/js/express_checkout_form.js index ab70d63af8a..dcaeb8789aa 100644 --- a/addons/payment_stripe/static/src/js/express_checkout_form.js +++ b/addons/payment_stripe/static/src/js/express_checkout_form.js @@ -6,6 +6,10 @@ import { paymentExpressCheckoutForm } from '@payment/js/express_checkout_form'; import { StripeOptions } from '@payment_stripe/js/stripe_options'; paymentExpressCheckoutForm.include({ + init() { + this._super(...arguments); + this.rpc = this.bindService("rpc"); + }, /** * Get the order details to display on the payment form. @@ -123,14 +127,15 @@ paymentExpressCheckoutForm.include({ addresses.shipping_option = ev.shippingOption; } // Update the customer addresses on the related document. - this.paymentContext.partnerId = parseInt(await this._rpc({ - route: this.paymentContext['expressCheckoutRoute'], params: addresses, - })); + this.paymentContext.partnerId = parseInt(await this.rpc( + this.paymentContext['expressCheckoutRoute'], + addresses, + )); // Call the transaction route to create the transaction and retrieve the client secret. - const { client_secret } = await this._rpc({ - route: this.paymentContext['transactionRoute'], - params: this._prepareTransactionRouteParams(providerData.providerId), - }); + const { client_secret } = await this.rpc( + this.paymentContext['transactionRoute'], + this._prepareTransactionRouteParams(providerData.providerId), + ); // Confirm the PaymentIntent without handling eventual next actions (e.g. 3DS). const { paymentIntent, error: confirmError } = await stripeJS.confirmCardPayment( client_secret, {payment_method: ev.paymentMethod.id}, {handleActions: false} @@ -157,9 +162,9 @@ paymentExpressCheckoutForm.include({ // shipping address, the shipping options need to be fetched again. paymentRequest.on('shippingaddresschange', async (ev) => { // Call the shipping address update route to fetch the shipping options. - const availableCarriers = await this._rpc({ - route: this.paymentContext['shippingAddressUpdateRoute'], - params: { + const availableCarriers = await this.rpc( + this.paymentContext['shippingAddressUpdateRoute'], + { partial_shipping_address: { zip: ev.shippingAddress.postalCode, city: ev.shippingAddress.city, @@ -167,7 +172,7 @@ paymentExpressCheckoutForm.include({ state: ev.shippingAddress.region, }, }, - }); + ); if (availableCarriers.length === 0) { ev.updateWith({status: 'invalid_shipping_address'}); } else { diff --git a/addons/point_of_sale/static/src/app/store/pos_store.js b/addons/point_of_sale/static/src/app/store/pos_store.js index 1d6cf5e8f63..ad3d4f09059 100644 --- a/addons/point_of_sale/static/src/app/store/pos_store.js +++ b/addons/point_of_sale/static/src/app/store/pos_store.js @@ -1602,13 +1602,8 @@ export class PosStore extends Reactive { } const payload = ev.data; if (ev.name === "call_service") { - let args = payload.args || []; - if (payload.service === "ajax" && payload.method === "rpc") { - // ajax service uses an extra 'target' argument for rpc - args = args.concat(ev.target); - } const service = this.env.services[payload.service]; - const result = service[payload.method].apply(service, args); + const result = service[payload.method].apply(service, ev.data.args || []); payload.callback(result); } } diff --git a/addons/portal/static/src/js/portal.js b/addons/portal/static/src/js/portal.js index cbe7e65e430..bec36fb9bd3 100644 --- a/addons/portal/static/src/js/portal.js +++ b/addons/portal/static/src/js/portal.js @@ -52,6 +52,11 @@ publicWidget.registry.portalDetails = publicWidget.Widget.extend({ export const PortalHomeCounters = publicWidget.Widget.extend({ selector: '.o_portal_my_home', + init() { + this._super(...arguments); + this.rpc = this.bindService("rpc"); + }, + /** * @override */ @@ -86,11 +91,8 @@ export const PortalHomeCounters = publicWidget.Widget.extend({ const countersAlwaysDisplayed = this._getCountersAlwaysDisplayed(); const proms = [...Array(Math.min(numberRpc, needed.length)).keys()].map(async i => { - const documentsCountersData = await this._rpc({ - route: "/my/counters", - params: { - counters: needed.slice(i * counterByRpc, (i + 1) * counterByRpc) - }, + const documentsCountersData = await this.rpc("/my/counters", { + counters: needed.slice(i * counterByRpc, (i + 1) * counterByRpc) }); Object.keys(documentsCountersData).forEach(counterName => { const documentsCounterEl = this.el.querySelector(`[data-placeholder_count='${counterName}']`); diff --git a/addons/portal/static/src/js/portal_chatter.js b/addons/portal/static/src/js/portal_chatter.js index 231cb84efa8..4126b99dcf9 100644 --- a/addons/portal/static/src/js/portal_chatter.js +++ b/addons/portal/static/src/js/portal_chatter.js @@ -37,6 +37,8 @@ var PortalChatter = publicWidget.Widget.extend({ this.set('pager', {}); this.set('domain', this.options['domain']); this._currentPage = this.options['pager_start']; + + this.rpc = this.bindService("rpc"); }, /** * @override @@ -83,10 +85,7 @@ var PortalChatter = publicWidget.Widget.extend({ */ messageFetch: function (domain) { var self = this; - return this._rpc({ - route: '/mail/chatter_fetch', - params: self._messageFetchPrepareParams(), - }).then(function (result) { + return this.rpc('/mail/chatter_fetch', self._messageFetchPrepareParams()).then(function (result) { self.set('messages', self.preprocessMessages(result['messages'])); self.set('message_count', result['message_count']); return result; @@ -177,10 +176,7 @@ var PortalChatter = publicWidget.Widget.extend({ */ _chatterInit: function () { var self = this; - return this._rpc({ - route: '/mail/chatter_init', - params: this._messageFetchPrepareParams() - }).then(function (result) { + return this.rpc('/mail/chatter_init', this._messageFetchPrepareParams()).then(function (result) { self.result = result; self.options = Object.assign(self.options, self.result['options'] || {}); return result; @@ -300,12 +296,9 @@ var PortalChatter = publicWidget.Widget.extend({ ev.preventDefault(); var $elem = $(ev.currentTarget); - return this._rpc({ - route: '/mail/update_is_internal', - params: { - message_id: $elem.data('message-id'), - is_internal: ! $elem.data('is-internal'), - }, + return this.rpc('/mail/update_is_internal', { + message_id: $elem.data('message-id'), + is_internal: ! $elem.data('is-internal'), }).then(function (result) { $elem.data('is-internal', result); if (result === true) { diff --git a/addons/portal/static/src/js/portal_composer.js b/addons/portal/static/src/js/portal_composer.js index e3c02c1e21d..be898b87aa2 100644 --- a/addons/portal/static/src/js/portal_composer.js +++ b/addons/portal/static/src/js/portal_composer.js @@ -2,10 +2,10 @@ import { _t } from "@web/core/l10n/translation"; import { escape } from "@web/core/utils/strings"; -import ajax from "@web/legacy/js/core/ajax"; import core from "@web/legacy/js/services/core"; import { renderToElement } from "@web/core/utils/render"; import publicWidget from "@web/legacy/js/public/public_widget"; +import { post } from "@web/core/network/http_service"; /** * Widget PortalComposer @@ -36,6 +36,7 @@ var PortalComposer = publicWidget.Widget.extend({ 'res_id': false, }, options || {}); this.attachments = []; + this.rpc = this.bindService("rpc"); }, /** * @override @@ -84,12 +85,9 @@ var PortalComposer = publicWidget.Widget.extend({ this.$sendButton.prop('disabled', true); - return this._rpc({ - route: '/portal/attachment/remove', - params: { - 'attachment_id': attachmentId, - 'access_token': accessToken, - }, + return this.rpc('/portal/attachment/remove', { + 'attachment_id': attachmentId, + 'access_token': accessToken, }).then(function () { self.attachments = self.attachments.filter(attachment => attachment.id !== attachmentId); self._updateAttachments(); @@ -117,7 +115,10 @@ var PortalComposer = publicWidget.Widget.extend({ return Promise.all([...this.$fileInput[0].files].map((file) => { return new Promise(function (resolve, reject) { var data = self._prepareAttachmentData(file); - ajax.post('/portal/attachment/add', data).then(function (attachment) { + if (odoo.csrf_token) { + data.csrf_token = odoo.csrf_token; + } + post('/portal/attachment/add', data).then(function (attachment) { attachment.state = 'pending'; self.attachments.push(attachment); self._updateAttachments(); @@ -195,10 +196,7 @@ var PortalComposer = publicWidget.Widget.extend({ * @returns {Promise} */ _chatterPostMessage: async function (route) { - const result = await this._rpc({ - route: route, - params: this._prepareMessageData(), - }); + const result = await this.rpc(route, this._prepareMessageData()); core.bus.trigger('reload_chatter_content', result); return result; }, diff --git a/addons/portal/static/src/js/portal_security.js b/addons/portal/static/src/js/portal_security.js index f9586293989..5c88d52f9e5 100644 --- a/addons/portal/static/src/js/portal_security.js +++ b/addons/portal/static/src/js/portal_security.js @@ -13,6 +13,12 @@ publicWidget.registry.NewAPIKeyButton = publicWidget.Widget.extend({ click: '_onClick' }, + init() { + this._super(...arguments); + this.orm = this.bindService("orm"); + this.dialog = this.bindService("dialog"); + }, + async _onClick(e){ e.preventDefault(); // This call is done just so it asks for the password confirmation before starting displaying the @@ -20,11 +26,11 @@ publicWidget.registry.NewAPIKeyButton = publicWidget.Widget.extend({ // displaying the wizard. // The result of the call is unused. But it's required to call a method with the decorator `@check_identity` // in order to use `handleCheckIdentity`. - await handleCheckIdentity(this.proxy('_rpc'), this._rpc({ - model: 'res.users', - method: 'api_key_wizard', - args: [session.user_id], - }), (...args) => this.call("dialog", "add", ...args)); + await handleCheckIdentity( + this.orm.call("res.users", "api_key_wizard", [session.user_id]), + this.orm, + this.dialog + ); this.call("dialog", "add", InputConfirmationDialog, { title: _t("New API Key"), @@ -32,19 +38,11 @@ publicWidget.registry.NewAPIKeyButton = publicWidget.Widget.extend({ confirmLabel: _t("Confirm"), confirm: async ({ inputEl }) => { const description = inputEl.value; - const wizard_id = await this._rpc({ - model: "res.users.apikeys.description", - method: "create", - args: [{ name: description }], - }); + const wizard_id = await this.orm.create("res.users.apikeys.description", [{ name: description }]); const res = await handleCheckIdentity( - this.proxy('_rpc'), - this._rpc({ - model: 'res.users.apikeys.description', - method: 'make_key', - args: [wizard_id], - }), - (...args) => this.call("dialog", "add", ...args) + this.orm.call("res.users.apikeys.description", "make_key", [wizard_id]), + this.orm, + this.dialog ); this.call("dialog", "add", ConfirmationDialog, { @@ -67,16 +65,18 @@ publicWidget.registry.RemoveAPIKeyButton = publicWidget.Widget.extend({ click: '_onClick' }, + init() { + this._super(...arguments); + this.orm = this.bindService("orm"); + this.dialog = this.bindService("dialog"); + }, + async _onClick(e){ e.preventDefault(); await handleCheckIdentity( - this.proxy('_rpc'), - this._rpc({ - model: 'res.users.apikeys', - method: 'remove', - args: [parseInt(this.el.id)] - }), - (...args) => this.call("dialog", "add", ...args) + this.orm.call("res.users.apikeys", "remove", [parseInt(this.el.id)]), + this.orm, + this.dialog ); window.location = window.location; } @@ -116,12 +116,15 @@ publicWidget.registry.RevokeSessionsButton = publicWidget.Widget.extend({ click: '_onClick', }, + init() { + this._super(...arguments); + this.orm = this.bindService("orm"); + }, + async _onClick() { - const { res_id: checkId } = await this._rpc({ - model: 'res.users', - method: 'api_key_wizard', - args: [session.user_id], - }); + const { res_id: checkId } = await this.orm.call("res.users", "api_key_wizard", [ + session.user_id, + ]); this.call("dialog", "add", InputConfirmationDialog, { title: _t("Log out from all devices?"), body: renderToMarkup("portal.revoke_all_devices_popup_template"), @@ -132,17 +135,13 @@ publicWidget.registry.RevokeSessionsButton = publicWidget.Widget.extend({ return false; } - await this._rpc({ - model: "res.users.identitycheck", - method: "write", - args: [checkId, { password: inputEl.value }], - }); + await this.orm.write("res.users.identitycheck", [checkId], { password: inputEl.value }); try { - await this._rpc({ - model: "res.users.identitycheck", - method: "revoke_all_devices", - args: [checkId], - }); + await this.orm.call( + "res.users.identitycheck", + "revoke_all_devices", + [checkId] + ); } catch { inputEl.classList.add("is-invalid"); inputEl.setCustomValidity(_t("Check failed")); @@ -171,19 +170,19 @@ publicWidget.registry.RevokeSessionsButton = publicWidget.Widget.extend({ * Warning: does not in and of itself trigger an identity check, a promise which * never triggers and identity check internally will do nothing of use. * - * @param {Function} rpc Widget#_rpc bound do the widget * @param {Promise} wrapped promise to check for an identity check request - * @param {Function} addDialog add a dialog to the dialog service + * @param {Function} ormService bound do the widget + * @param {Function} dialogService dialog service * @returns {Promise} result of the original call */ -export async function handleCheckIdentity(rpc, wrapped, addDialog) { +export async function handleCheckIdentity(wrapped, ormService, dialogService) { return wrapped.then((r) => { if (!(r.type === "ir.actions.act_window" && r.res_model === "res.users.identitycheck")) { return r; } const checkId = r.res_id; return new Promise((resolve) => { - addDialog(InputConfirmationDialog, { + dialogService.add(InputConfirmationDialog, { title: _t("Security Control"), body: renderToMarkup("portal.identitycheck"), confirmLabel: _t("Confirm Password"), @@ -193,17 +192,9 @@ export async function handleCheckIdentity(rpc, wrapped, addDialog) { return false; } let result; - await rpc({ - model: "res.users.identitycheck", - method: "write", - args: [checkId, { password: inputEl.value }], - }); + await ormService.write("res.users.identitycheck", [checkId], { password: inputEl.value }); try { - result = await rpc({ - model: "res.users.identitycheck", - method: "run_check", - args: [checkId], - }); + result = await ormService.call("res.users.identitycheck", "run_check", [checkId]); } catch { inputEl.classList.add("is-invalid"); inputEl.setCustomValidity(_t("Check failed")); diff --git a/addons/portal/static/src/js/portal_signature.js b/addons/portal/static/src/js/portal_signature.js index 2bcc20c0b14..ddbd7fb9f25 100644 --- a/addons/portal/static/src/js/portal_signature.js +++ b/addons/portal/static/src/js/portal_signature.js @@ -43,6 +43,8 @@ var SignatureForm = publicWidget.Widget.extend({ this.nameAndSignature = new NameAndSignature(this, options.nameAndSignatureOptions || {}); + + this.rpc = this.bindService("rpc"); }, /** * Overridden to get the DOM elements @@ -107,13 +109,13 @@ var SignatureForm = publicWidget.Widget.extend({ var name = this.nameAndSignature.getName(); var signature = this.nameAndSignature.getSignatureImage()[1]; - return this._rpc({ - route: this.callUrl, - params: Object.assign(this.rpcParams, { + return this.rpc( + this.callUrl, + Object.assign(this.rpcParams, { 'name': name, 'signature': signature, - }), - }).then(function (data) { + }) + ).then(function (data) { if (data.error) { self.$('.o_portal_sign_error_msg').remove(); self.$controls.prepend(renderToElement('portal.portal_signature_error', {widget: data})); diff --git a/addons/portal_rating/static/src/js/portal_chatter.js b/addons/portal_rating/static/src/js/portal_chatter.js index 8067acaf30a..c2d68fc1a13 100644 --- a/addons/portal_rating/static/src/js/portal_chatter.js +++ b/addons/portal_rating/static/src/js/portal_chatter.js @@ -293,12 +293,9 @@ PortalChatter.include({ var messageIndex = $source.data("mes_index"); var ratingId = this.messages[messageIndex].rating.id; - this._rpc({ - route: '/website/rating/comment', - params: { - "rating_id": ratingId, - "publisher_comment": '' // Empty publisher comment means no comment - } + this.rpc('/website/rating/comment', { + "rating_id": ratingId, + "publisher_comment": '' // Empty publisher comment means no comment }).then(function (res) { self.messages[messageIndex].rating = self._preprocessCommentData(res, messageIndex); self._getCommentButton($source).removeClass("d-none"); @@ -318,12 +315,9 @@ PortalChatter.include({ var comment = this._getCommentTextarea($source).val(); var ratingId = this.messages[messageIndex].rating.id; - this._rpc({ - route: '/website/rating/comment', - params: { - "rating_id": ratingId, - "publisher_comment": comment - } + this.rpc('/website/rating/comment', { + "rating_id": ratingId, + "publisher_comment": comment }).then(function (res) { // Modify the related message diff --git a/addons/project/__manifest__.py b/addons/project/__manifest__.py index 3b295d44b82..d7fd3286d0e 100644 --- a/addons/project/__manifest__.py +++ b/addons/project/__manifest__.py @@ -146,14 +146,12 @@ 'web/static/src/legacy/js/libs/bootstrap.js', 'web/static/src/legacy/js/libs/jquery.js', 'web/static/src/legacy/js/libs/jSignatureCustom.js', - 'web/static/src/legacy/js/core/ajax.js', 'web/static/src/legacy/js/core/bus.js', 'web/static/src/legacy/js/core/class.js', 'web/static/src/legacy/js/core/dialog.js', 'web/static/src/legacy/xml/dialog.xml', 'web/static/src/legacy/js/core/dom.js', 'web/static/src/legacy/js/core/mixins.js', - 'web/static/src/legacy/js/core/rpc.js', 'web/static/src/legacy/js/core/service_mixins.js', 'web/static/src/legacy/js/core/time.js', 'web/static/src/legacy/js/core/widget.js', diff --git a/addons/purchase/static/src/js/purchase_datetimepicker.js b/addons/purchase/static/src/js/purchase_datetimepicker.js index 25baf927a5b..36e22927b7f 100644 --- a/addons/purchase/static/src/js/purchase_datetimepicker.js +++ b/addons/purchase/static/src/js/purchase_datetimepicker.js @@ -3,16 +3,17 @@ import PublicWidget from "@web/legacy/js/public/public_widget"; export const PurchaseDatePicker = PublicWidget.Widget.extend({ selector: ".o-purchase-datetimepicker", + init() { + this._super(...arguments); + this.rpc = this.bindService("rpc"); + }, start() { this.call("datetime_picker", "create", { target: this.el, onChange: (newDate) => { const { accessToken, orderId, lineId } = this.el.dataset; - this._rpc({ - route: `/my/purchase/${orderId}/update?access_token=${accessToken}`, - params: { - [lineId]: newDate.toISODate(), - }, + this.rpc(`/my/purchase/${orderId}/update?access_token=${accessToken}`, { + [lineId]: newDate.toISODate(), }); }, pickerProps: { diff --git a/addons/sale_management/static/src/js/sale_management.js b/addons/sale_management/static/src/js/sale_management.js index a0c9ab18e0e..678300b2c11 100644 --- a/addons/sale_management/static/src/js/sale_management.js +++ b/addons/sale_management/static/src/js/sale_management.js @@ -10,6 +10,11 @@ publicWidget.registry.SaleUpdateLineButton = publicWidget.Widget.extend({ 'change .js_quantity': '_onChangeOptionQuantity', }, + init() { + this._super(...arguments); + this.rpc = this.bindService("rpc"); + }, + /** * @override */ @@ -28,10 +33,7 @@ publicWidget.registry.SaleUpdateLineButton = publicWidget.Widget.extend({ * @return {Deferred} */ _callUpdateLineRoute(order_id, params) { - return this._rpc({ - route: "/my/orders/" + order_id + "/update_line_dict", - params: params, - }); + return this.rpc("/my/orders/" + order_id + "/update_line_dict", params); }, /** @@ -98,10 +100,10 @@ publicWidget.registry.SaleUpdateLineButton = publicWidget.Widget.extend({ // to avoid double click on link with href. $target.css('pointer-events', 'none'); - this._rpc({ - route: "/my/orders/" + self.orderDetail.orderId + "/add_option/" + $target.data('optionId'), - params: {access_token: self.orderDetail.token} - }).then((data) => { + this.rpc( + "/my/orders/" + self.orderDetail.orderId + "/add_option/" + $target.data('optionId'), + {access_token: self.orderDetail.token} + ).then((data) => { this._refreshOrderUI(data); }); }, diff --git a/addons/survey/static/src/js/survey_form.js b/addons/survey/static/src/js/survey_form.js index 7f1f1158dab..e72170a92fe 100644 --- a/addons/survey/static/src/js/survey_form.js +++ b/addons/survey/static/src/js/survey_form.js @@ -39,6 +39,11 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend(SurveyPreloa // Widget //-------------------------------------------------------------------------- + init() { + this._super(...arguments); + this.rpc = this.bindService("rpc"); + }, + /** * @override */ @@ -326,9 +331,9 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend(SurveyPreloa this.preventEnterSubmit = false; this.readonly = false; this._nextScreen( - this._rpc({ - route: `/survey/next_question/${this.options.surveyToken}/${this.options.answerToken}`, - }), { + this.rpc( + `/survey/next_question/${this.options.surveyToken}/${this.options.answerToken}` + ), { initTimer: true, isFinish: nextPageEvent.type === 'end_session' } @@ -392,10 +397,10 @@ publicWidget.registry.SurveyFormWidget = publicWidget.Widget.extend(SurveyPreloa this.readonly = true; } - var submitPromise = self._rpc({ - route: `${route}/${self.options.surveyToken}/${self.options.answerToken}`, - params: params, - }); + var submitPromise = self.rpc( + `${route}/${self.options.surveyToken}/${self.options.answerToken}`, + params + ); this._nextScreen(submitPromise, options); }, diff --git a/addons/survey/static/src/js/survey_quick_access.js b/addons/survey/static/src/js/survey_quick_access.js index 653cec8aff6..9e5caf438f9 100644 --- a/addons/survey/static/src/js/survey_quick_access.js +++ b/addons/survey/static/src/js/survey_quick_access.js @@ -14,6 +14,12 @@ publicWidget.registry.SurveyQuickAccessWidget = publicWidget.Widget.extend({ // Widget //-------------------------------------------------------------------------- + init() { + this._super(...arguments); + this.rpc = this.bindService("rpc"); + this.orm = this.bindService("orm"); + }, + /** * @override */ @@ -35,11 +41,11 @@ publicWidget.registry.SurveyQuickAccessWidget = publicWidget.Widget.extend({ // ------------------------------------------------------------------------- _onLaunchSessionClick: async function () { - const sessionResult = await this._rpc({ - 'model': 'survey.survey', - 'method': 'action_start_session', - 'args': [[this.$('.o_survey_launch_session').data('surveyId')]], - }); + const sessionResult = await this.orm.call( + "survey.survey", + "action_start_session", + [[this.$(".o_survey_launch_session").data("surveyId")]] + ); window.location = sessionResult.url; }, @@ -69,9 +75,7 @@ publicWidget.registry.SurveyQuickAccessWidget = publicWidget.Widget.extend({ self.$('.o_survey_session_error_invalid_code').removeClass('d-none'); return; } - this._rpc({ - route: `/survey/check_session_code/${sessionCodeInputVal}`, - }).then(function (response) { + this.rpc(`/survey/check_session_code/${sessionCodeInputVal}`).then(function (response) { if (response.survey_url) { window.location = response.survey_url; } else { diff --git a/addons/survey/static/src/js/survey_session_leaderboard.js b/addons/survey/static/src/js/survey_session_leaderboard.js index 090a9080b77..5903e8ccfa4 100644 --- a/addons/survey/static/src/js/survey_session_leaderboard.js +++ b/addons/survey/static/src/js/survey_session_leaderboard.js @@ -13,6 +13,8 @@ publicWidget.registry.SurveySessionLeaderboard = publicWidget.Widget.extend({ this.BAR_MIN_WIDTH = '3rem'; this.BAR_WIDTH = '24rem'; this.BAR_HEIGHT = '3.8rem'; + + this.rpc = this.bindService("rpc"); }, //-------------------------------------------------------------------------- @@ -43,9 +45,7 @@ publicWidget.registry.SurveySessionLeaderboard = publicWidget.Widget.extend({ self.$('.o_survey_session_leaderboard_container').empty(); } - var leaderboardPromise = this._rpc({ - route: `/survey/session/leaderboard/${this.surveyAccessToken}`, - }); + var leaderboardPromise = this.rpc(`/survey/session/leaderboard/${this.surveyAccessToken}`); Promise.all([fadeOutPromise, leaderboardPromise]).then(function (results) { var leaderboardResults = results[1]; diff --git a/addons/survey/static/src/js/survey_session_manage.js b/addons/survey/static/src/js/survey_session_manage.js index 5b02e601816..5c942936afa 100644 --- a/addons/survey/static/src/js/survey_session_manage.js +++ b/addons/survey/static/src/js/survey_session_manage.js @@ -26,6 +26,12 @@ publicWidget.registry.SurveySessionManage = publicWidget.Widget.extend(SurveyPre 'click .o_survey_session_close': '_onEndSessionClick', }, + init() { + this._super(...arguments); + this.rpc = this.bindService("rpc"); + this.orm = this.bindService("orm"); + }, + /** * Overridden to set a few properties that come from the python template rendering. * @@ -243,11 +249,11 @@ publicWidget.registry.SurveySessionManage = publicWidget.Widget.extend(SurveyPre var self = this; ev.preventDefault(); - this._rpc({ - model: 'survey.survey', - method: 'action_end_session', - args: [[this.surveyId]], - }).then(function () { + this.orm.call( + "survey.survey", + "action_end_session", + [[this.surveyId]] + ).then(function () { if ($(ev.currentTarget).data('showResults')) { document.location = `/survey/results/${encodeURIComponent(self.surveyId)}`; } else { @@ -354,12 +360,12 @@ publicWidget.registry.SurveySessionManage = publicWidget.Widget.extend(SurveyPre delete this.resultsRefreshInterval; } - var nextQuestionPromise = this._rpc({ - route: `/survey/session/next_question/${self.surveyAccessToken}`, - params: { + var nextQuestionPromise = this.rpc( + `/survey/session/next_question/${self.surveyAccessToken}`, + { 'go_back': goBack, } - }).then(function (result) { + ).then(function (result) { self.nextQuestion = result; if (self.refreshBackground && result.background_image_url) { return self._preloadBackground(result.background_image_url); @@ -467,9 +473,9 @@ publicWidget.registry.SurveySessionManage = publicWidget.Widget.extend(SurveyPre _refreshResults: function () { var self = this; - return this._rpc({ - route: `/survey/session/results/${self.surveyAccessToken}`, - }).then(function (questionResults) { + return this.rpc( + `/survey/session/results/${self.surveyAccessToken}` + ).then(function (questionResults) { if (questionResults) { self.attendeesCount = questionResults.attendees_count; @@ -507,11 +513,10 @@ publicWidget.registry.SurveySessionManage = publicWidget.Widget.extend(SurveyPre _refreshAttendeesCount: function () { var self = this; - return self._rpc({ - model: 'survey.survey', - method: 'read', - args: [[self.surveyId], ['session_answer_count']], - }).then(function (result) { + return self.orm.read( + "survey.survey", + [[self.surveyId], ['session_answer_count']] + ).then(function (result) { if (result && result.length === 1){ self.$('.o_survey_session_attendees_count').text( result[0].session_answer_count diff --git a/addons/test_website/static/src/js/test_error.js b/addons/test_website/static/src/js/test_error.js index 14adac770f3..d6bef09b68a 100644 --- a/addons/test_website/static/src/js/test_error.js +++ b/addons/test_website/static/src/js/test_error.js @@ -8,6 +8,11 @@ publicWidget.registry.testError = publicWidget.Widget.extend({ 'click a': '_onRpcErrorClick', }, + init() { + this._super(...arguments); + this.rpc = this.bindService("rpc"); + }, + //---------------------------------------------------------------------- // Handlers //---------------------------------------------------------------------- @@ -21,8 +26,6 @@ publicWidget.registry.testError = publicWidget.Widget.extend({ _onRpcErrorClick: function (ev) { ev.preventDefault(); var $link = $(ev.currentTarget); - return this._rpc({ - route: $link.attr('href'), - }); + return this.rpc($link.attr('href')); } }); diff --git a/addons/test_website/static/tests/tours/json_auth.js b/addons/test_website/static/tests/tours/json_auth.js index ef97473f020..b640cbb11b4 100644 --- a/addons/test_website/static/tests/tours/json_auth.js +++ b/addons/test_website/static/tests/tours/json_auth.js @@ -1,15 +1,15 @@ /** @odoo-module **/ import { registry } from "@web/core/registry"; -import ajax from "@web/legacy/js/core/ajax"; +import { jsonrpc } from "@web/core/network/rpc_service"; registry.category("web_tour.tours").add('test_json_auth', { test: true, steps: () => [{ trigger: 'body', run: async function () { - await ajax.rpc('/test_get_dbname').then( function (result){ - return ajax.rpc("/web/session/authenticate", { + await jsonrpc('/test_get_dbname').then( function (result){ + return jsonrpc("/web/session/authenticate", { db: result, login: 'admin', password: 'admin' diff --git a/addons/web/__manifest__.py b/addons/web/__manifest__.py index e4f82818899..f04ec889ea4 100644 --- a/addons/web/__manifest__.py +++ b/addons/web/__manifest__.py @@ -97,14 +97,12 @@ This module provides the core of the Odoo Web Client. 'web/static/src/legacy/js/libs/bootstrap.js', 'web/static/src/legacy/js/libs/jquery.js', 'web/static/src/legacy/js/libs/jSignatureCustom.js', - 'web/static/src/legacy/js/core/ajax.js', 'web/static/src/legacy/js/core/bus.js', 'web/static/src/legacy/js/core/class.js', 'web/static/src/legacy/js/core/dialog.js', 'web/static/src/legacy/xml/dialog.xml', 'web/static/src/legacy/js/core/dom.js', 'web/static/src/legacy/js/core/mixins.js', - 'web/static/src/legacy/js/core/rpc.js', 'web/static/src/legacy/js/core/service_mixins.js', 'web/static/src/legacy/js/core/time.js', 'web/static/src/legacy/js/core/widget.js', @@ -234,14 +232,12 @@ This module provides the core of the Odoo Web Client. 'web/static/src/legacy/js/libs/bootstrap.js', 'web/static/src/legacy/js/libs/jquery.js', 'web/static/src/legacy/js/libs/jSignatureCustom.js', - 'web/static/src/legacy/js/core/ajax.js', 'web/static/src/legacy/js/core/bus.js', 'web/static/src/legacy/js/core/class.js', 'web/static/src/legacy/js/core/dialog.js', 'web/static/src/legacy/xml/dialog.xml', 'web/static/src/legacy/js/core/dom.js', 'web/static/src/legacy/js/core/mixins.js', - 'web/static/src/legacy/js/core/rpc.js', 'web/static/src/legacy/js/core/service_mixins.js', 'web/static/src/legacy/js/core/time.js', 'web/static/src/legacy/js/core/widget.js', diff --git a/addons/web/static/src/core/network/http_service.js b/addons/web/static/src/core/network/http_service.js index 13f25142ebe..560b524e831 100644 --- a/addons/web/static/src/core/network/http_service.js +++ b/addons/web/static/src/core/network/http_service.js @@ -1,5 +1,6 @@ /** @odoo-module **/ +import { browser } from "@web/core/browser/browser"; import { registry } from "../registry"; function checkResponseStatus(response) { @@ -8,35 +9,28 @@ function checkResponseStatus(response) { } } +export async function get(route, readMethod = "json") { + const response = await browser.fetch(route, { method: "GET" }); + checkResponseStatus(response); + return response[readMethod](); +} + +export async function post(route, params = {}, readMethod = "json") { + const formData = new FormData(); + for (const key in params) { + formData.append(key, params[key]); + } + const response = await browser.fetch(route, { + body: formData, + method: "POST", + }); + checkResponseStatus(response); + return response[readMethod](); +} + export const httpService = { start() { - return { - async get(route, readMethod = "json") { - const response = await fetch(route, { method: "GET" }); - checkResponseStatus(response); - return response[readMethod](); - }, - async post(route, params = {}, readMethod = "json") { - const formData = new FormData(); - for (const key in params) { - const value = params[key]; - if (Array.isArray(value) && value.length) { - for (const val of value) { - formData.append(key, val); - } - } else { - formData.append(key, value); - } - } - const info = { - body: formData, - method: "POST", - }; - const response = await fetch(route, info); - checkResponseStatus(response); - return response[readMethod](); - }, - }; + return { get, post }; }, }; diff --git a/addons/web/static/src/core/network/rpc_service.js b/addons/web/static/src/core/network/rpc_service.js index 3b534d2e0d6..55a9461577d 100644 --- a/addons/web/static/src/core/network/rpc_service.js +++ b/addons/web/static/src/core/network/rpc_service.js @@ -43,11 +43,12 @@ export function makeErrorFromResponse(reponse) { return error; } -export function jsonrpc(env, rpcId, url, params, settings = {}) { - const bus = env.bus; +let rpcId = 0; +export function jsonrpc(url, params = {}, settings = {}) { + const bus = settings.bus; const XHR = browser.XMLHttpRequest; const data = { - id: rpcId, + id: rpcId++, jsonrpc: "2.0", method: "call", params: params, @@ -56,13 +57,13 @@ export function jsonrpc(env, rpcId, url, params, settings = {}) { let rejectFn; const promise = new Promise((resolve, reject) => { rejectFn = reject; - bus.trigger("RPC:REQUEST", { data, settings }); + bus?.trigger("RPC:REQUEST", { data, settings }); // handle success request.addEventListener("load", () => { if (request.status === 502) { // If Odoo is behind another server (eg.: nginx) const error = new ConnectionLostError(url); - bus.trigger("RPC:RESPONSE", { data, settings, error }); + bus?.trigger("RPC:RESPONSE", { data, settings, error }); reject(error); return; } @@ -73,22 +74,22 @@ export function jsonrpc(env, rpcId, url, params, settings = {}) { // the response isn't json parsable, which probably means that the rpc request could // not be handled by the server, e.g. PoolError('The Connection Pool Is Full') const error = new ConnectionLostError(url); - bus.trigger("RPC:RESPONSE", { data, settings, error }); + bus?.trigger("RPC:RESPONSE", { data, settings, error }); return reject(error); } const { error: responseError, result: responseResult } = params; if (!params.error) { - bus.trigger("RPC:RESPONSE", { data, settings, result: params.result }); + bus?.trigger("RPC:RESPONSE", { data, settings, result: params.result }); return resolve(responseResult); } const error = makeErrorFromResponse(responseError); - bus.trigger("RPC:RESPONSE", { data, settings, error }); + bus?.trigger("RPC:RESPONSE", { data, settings, error }); reject(error); }); // handle failure request.addEventListener("error", () => { const error = new ConnectionLostError(url); - bus.trigger("RPC:RESPONSE", { data, settings, error }); + bus?.trigger("RPC:RESPONSE", { data, settings, error }); reject(error); }); // configure and send request @@ -105,7 +106,7 @@ export function jsonrpc(env, rpcId, url, params, settings = {}) { request.abort(); } const error = new ConnectionAbortedError("XmlHttpRequestError abort"); - bus.trigger("RPC:RESPONSE", { data, settings, error }); + bus?.trigger("RPC:RESPONSE", { data, settings, error }); if (rejectError) { rejectFn(error); } @@ -119,7 +120,6 @@ export function jsonrpc(env, rpcId, url, params, settings = {}) { export const rpcService = { async: true, start(env) { - let rpcId = 0; /** * @param {string} route * @param {Object} params @@ -127,8 +127,8 @@ export const rpcService = { * @param {boolean} settings.silent * @param {XMLHttpRequest} settings.xhr */ - return function rpc(route, params = {}, settings) { - return jsonrpc(env, rpcId++, route, params, settings); + return function rpc(route, params = {}, settings = {}) { + return jsonrpc(route, params, { bus: env.bus, ...settings }); }; }, }; diff --git a/addons/web/static/src/legacy/js/core/ajax.js b/addons/web/static/src/legacy/js/core/ajax.js deleted file mode 100644 index 78c80d2720f..00000000000 --- a/addons/web/static/src/legacy/js/core/ajax.js +++ /dev/null @@ -1,225 +0,0 @@ -/** @odoo-module **/ - -import core from "@web/legacy/js/services/core"; -import time from "@web/legacy/js/core/time"; -import { session } from "@web/session"; - -import { markup } from "@odoo/owl"; - -// Create the final object containing all the functions first to allow monkey -// patching them correctly if ever needed. -var ajax = {}; - -function _genericJsonRpc (fct_name, params, settings, fct) { - var shadow = settings.shadow || false; - delete settings.shadow; - var data = { - jsonrpc: "2.0", - method: fct_name, - params: params, - id: Math.floor(Math.random() * 1000 * 1000 * 1000) - }; - - if (!shadow) { - core.bus.trigger('rpc_request', data.id); - } - - var xhr = fct(data); - var result = xhr.then(function(result) { - core.bus.trigger('rpc:result', data, result); - if (result.error !== undefined) { - console.debug( - "Server application error\n", - "Error code:", result.error.code, "\n", - "Error message:", result.error.message, "\n", - "Error data message:\n", result.error.data.message, "\n", - "Error data debug:\n", result.error.data.debug - ); - return Promise.reject({type: "server", error: result.error}); - } else { - return result.result; - } - }, function() { - //console.error("JsonRPC communication error", [...arguments]); - var reason = { - type: 'communication', - error: arguments[0], - textStatus: arguments[1], - errorThrown: arguments[2], - }; - return Promise.reject(reason); - }); - - var rejection; - var promise = new Promise(function (resolve, reject) { - rejection = reject; - - result.then(function (result) { - if (!shadow) { - core.bus.trigger('rpc_response', data.id); - } - resolve(result); - }, function (reason) { - var type = reason.type; - var error = reason.error; - var textStatus = reason.textStatus; - var errorThrown = reason.errorThrown; - if (type === "server") { - if (!shadow) { - core.bus.trigger('rpc_response', data.id); - } - reject({message: error, event: $.Event()}); - } else { - if (!shadow) { - core.bus.trigger('rpc_response_failed', data.id); - } - var nerror = { - code: -32098, - message: "XmlHttpRequestError " + errorThrown, - data: { - type: "xhr"+textStatus, - debug: error.responseText, - objects: [error, errorThrown], - arguments: [reason || textStatus] - }, - }; - reject({message: nerror, event: $.Event()}); - } - }); - }); - - // FIXME: jsonp? - promise.abort = function () { - rejection({ - message: "XmlHttpRequestError abort", - event: $.Event('abort') - }); - - if (!shadow) { - core.bus.trigger('rpc_response'); - } - - if (xhr.abort) { - xhr.abort(); - } - }; - promise.guardedCatch(function (reason) { // Allow promise user to disable rpc_error call in case of failure - setTimeout(function () { - // we want to execute this handler after all others (hence - // setTimeout) to let the other handlers prevent the event - if (!reason.event.isDefaultPrevented()) { - core.bus.trigger('rpc_error', reason.message, reason.event); - } - }, 0); - }); - return promise; -}; - -export function jsonRpc(url, fct_name, params, settings) { - settings = settings || {}; - return _genericJsonRpc(fct_name, params, settings, function(data) { - return $.ajax(url, Object.assign({}, settings, { - url: url, - dataType: 'json', - type: 'POST', - data: JSON.stringify(data, time.date_to_utc), - contentType: 'application/json' - })); - }); -} - -// helper function to make a rpc with a function name hardcoded to 'call' -function rpc(url, params, settings) { - return jsonRpc(url, 'call', params, settings); -} - -function post (controller_url, data) { - var postData = new FormData(); - - $.each(data, function(i,val) { - postData.append(i, val); - }); - if (odoo.csrf_token) { - postData.append('csrf_token', odoo.csrf_token); - } - - return new Promise(function (resolve, reject) { - $.ajax(controller_url, { - data: postData, - processData: false, - contentType: false, - type: 'POST' - }).then(resolve).fail(reject); - }); -} - - -/** - * Loads a template file according to the given xmlId. - * - * @param {string} [xmlId] - the template xmlId - * @param {Object} [context] - * additionnal rpc context to be merged with the default one - * @param {string} [tplRoute='/web/dataset/call_kw/'] - * @returns {Deferred} resolved with an object - * cssLibs: list of css files - * cssContents: list of style tag contents - * jsLibs: list of JS files - * jsContents: list of script tag contents - */ -var loadAsset = (function () { - var cache = {}; - - var load = function loadAsset(xmlId, context, tplRoute = '/web/dataset/call_kw/') { - if (cache[xmlId]) { - return cache[xmlId]; - } - context = Object.assign({}, session.user_context, context); - const params = { - args: [xmlId, { - debug: !!odoo.debug - }], - kwargs: { - context: context, - }, - }; - if (tplRoute === '/web/dataset/call_kw/') { - Object.assign(params, { - model: 'ir.ui.view', - method: 'render_public_asset', - }); - } - cache[xmlId] = rpc(tplRoute, params).then(function (xml) { - var $xml = $(xml); - return { - cssLibs: $xml.filter('link[href]:not([type="image/x-icon"])').map(function () { - return $(this).attr('href'); - }).get(), - cssContents: $xml.filter('style').map(function () { - return markup($(this).html()); - }).get(), - jsLibs: $xml.filter('script[src]').map(function () { - return $(this).attr('src'); - }).get(), - jsContents: $xml.filter('script:not([src])').map(function () { - return markup($(this).html()); - }).get(), - }; - }).guardedCatch(reason => { - reason.event.preventDefault(); - throw `Unable to render the required templates for the assets to load: ${reason.message.message}`; - }); - return cache[xmlId]; - }; - - return load; -})(); - -Object.assign(ajax, { - jsonRpc: jsonRpc, - rpc: rpc, - loadAsset: loadAsset, - post: post, -}); - -export default ajax; diff --git a/addons/web/static/src/legacy/js/core/rpc.js b/addons/web/static/src/legacy/js/core/rpc.js deleted file mode 100644 index 2a843c3bae1..00000000000 --- a/addons/web/static/src/legacy/js/core/rpc.js +++ /dev/null @@ -1,107 +0,0 @@ -/** @odoo-module **/ - -import ajax from "@web/legacy/js/core/ajax"; - -const rpc = { - /** - * Perform a RPC. Please note that this is not the preferred way to do a - * rpc if you are in the context of a widget. In that case, you should use - * the this._rpc method. - * - * @param {Object} params @see buildQuery for a description - * @param {Object} options - * @returns {Promise} - */ - query: function (params, options) { - var query = rpc.buildQuery(params); - return ajax.rpc(query.route, query.params, options); - }, - /** - * @param {Object} options - * @param {any[]} [options.args] - * @param {Object} [options.context] - * @param {any[]} [options.domain] - * @param {string[]} [options.fields] - * @param {string[]} [options.groupBy] - * @param {Object} [options.kwargs] - * @param {integer|false} [options.limit] - * @param {string} [options.method] - * @param {string} [options.model] - * @param {integer} [options.offset] - * @param {string[]} [options.orderBy] - * @param {Object} [options.params] - * @param {string} [options.route] - * @returns {Object} with 2 keys: route and params - */ - buildQuery: function (options) { - var route; - var params = options.params || {}; - var orderBy; - if (options.route) { - route = options.route; - } else if (options.model && options.method) { - route = '/web/dataset/call_kw/' + options.model + '/' + options.method; - } - if (options.method) { - params.args = options.args || []; - params.model = options.model; - params.method = options.method; - params.kwargs = Object.assign(params.kwargs || {}, options.kwargs); - params.kwargs.context = options.context || params.context || params.kwargs.context; - } - - if (options.method === 'read_group' || options.method === 'web_read_group') { - if (!(params.args && params.args[0] !== undefined)) { - params.kwargs.domain = options.domain || params.domain || params.kwargs.domain || []; - } - if (!(params.args && params.args[1] !== undefined)) { - params.kwargs.fields = options.fields || params.fields || params.kwargs.fields || []; - } - if (!(params.args && params.args[2] !== undefined)) { - params.kwargs.groupby = options.groupBy || params.groupBy || params.kwargs.groupby || []; - } - params.kwargs.offset = options.offset || params.offset || params.kwargs.offset; - params.kwargs.limit = options.limit || params.limit || params.kwargs.limit; - // In kwargs, we look for "orderby" rather than "orderBy" (note the absence of capital B), - // since the Python argument to the actual function is "orderby". - orderBy = options.orderBy || params.orderBy || params.kwargs.orderby; - params.kwargs.orderby = orderBy ? rpc._serializeSort(orderBy) : orderBy; - params.kwargs.lazy = 'lazy' in options ? options.lazy : params.lazy; - } - - if (options.method === 'search_read') { - // call the model method - params.kwargs.domain = options.domain || params.domain || params.kwargs.domain; - params.kwargs.fields = options.fields || params.fields || params.kwargs.fields; - params.kwargs.offset = options.offset || params.offset || params.kwargs.offset; - params.kwargs.limit = options.limit || params.limit || params.kwargs.limit; - // In kwargs, we look for "order" rather than "orderBy" since the Python - // argument to the actual function is "order". - orderBy = options.orderBy || params.orderBy || params.kwargs.order; - params.kwargs.order = orderBy ? rpc._serializeSort(orderBy) : orderBy; - } - - return { - route: route, - params: JSON.parse(JSON.stringify(params)), - }; - }, - /** - * Helper method, generates a string to describe a ordered by sequence for - * SQL. - * - * For example, [{name: 'foo'}, {name: 'bar', asc: false}] will - * be converted into 'foo ASC, bar DESC' - * - * @param {Object[]} orderBy list of objects {name:..., [asc: ...]} - * @returns {string} - */ - _serializeSort: function (orderBy) { - return orderBy - .map((order) => order.name + (order.asc !== false ? " ASC" : " DESC")) - .join(", "); - }, -}; - -export const buildQuery = rpc.buildQuery -export default rpc; diff --git a/addons/web/static/src/legacy/js/core/service_mixins.js b/addons/web/static/src/legacy/js/core/service_mixins.js index cbd4b1ecc84..e47e4fc7729 100644 --- a/addons/web/static/src/legacy/js/core/service_mixins.js +++ b/addons/web/static/src/legacy/js/core/service_mixins.js @@ -1,12 +1,59 @@ /** @odoo-module **/ -import rpc from "@web/legacy/js/core/rpc"; +import { SERVICES_METADATA } from "@web/env"; +import { + ConnectionAbortedError, + ConnectionLostError, + RPCError, +} from "@web/core/network/rpc_service"; + +function protectMethod(widget, fn) { + return function (...args) { + return new Promise((resolve, reject) => { + Promise.resolve(fn.call(this, ...args)) + .then((result) => { + if (!widget.isDestroyed()) { + resolve(result); + } + }) + .catch((reason) => { + if (!widget.isDestroyed()) { + if (reason instanceof RPCError || reason instanceof ConnectionLostError) { + // we do not reject an error here because we want to pass through + // the legacy guardedCatch code + reject({ message: reason, event: $.Event(), legacy: true }); + } else if (reason instanceof ConnectionAbortedError) { + reject({ message: reason.message, event: $.Event("abort") }); + } else { + reject(reason); + } + } + }); + }); + }; +} -/** - * @mixin - * @name ServicesMixin - */ var ServicesMixin = { + bindService: function (serviceName) { + const { services } = owl.Component.env; + const service = services[serviceName]; + if (!service) { + throw new Error(`Service ${serviceName} is not available`); + } + if (serviceName in SERVICES_METADATA) { + if (service instanceof Function) { + return protectMethod(this, service); + } else { + const methods = SERVICES_METADATA[serviceName]; + const result = Object.create(service); + for (const method of methods) { + result[method] = protectMethod(this, service[method]); + } + return result; + } + } + return service; + }, /** * @param {string} service * @param {string} method @@ -25,97 +72,6 @@ var ServicesMixin = { }); return result; }, - /** - * Builds and executes RPC query. Returns a promise resolved with - * the RPC result. - * - * @param {string} params either a route or a model - * @param {string} options if a model is given, this argument is a method - * @returns {Promise} - */ - _rpc: function (params, options) { - var query = rpc.buildQuery(params); - var prom = this.call('ajax', 'rpc', query.route, query.params, options, this); - if (!prom) { - prom = new Promise(function () {}); - prom.abort = function () {}; - } - var abort = prom.abort ? prom.abort : prom.reject; - if (abort) { - prom.abort = abort.bind(prom); - } - return prom; - }, - loadFieldView: function (modelName, context, view_id, view_type, options) { - return this.loadViews(modelName, context, [[view_id, view_type]], options).then(function (result) { - return result[view_type]; - }); - }, - loadViews: function (modelName, context, views, options) { - var self = this; - return new Promise(function (resolve) { - self.trigger_up('load_views', { - modelName: modelName, - context: context, - views: views, - options: options, - on_success: resolve, - }); - }); - }, - loadFilters: function (modelName, actionId, context) { - var self = this; - return new Promise(function (resolve, reject) { - self.trigger_up('load_filters', { - modelName: modelName, - actionId: actionId, - context: context, - on_success: resolve, - }); - }); - }, - createFilter: function (filter) { - var self = this; - return new Promise(function (resolve, reject) { - self.trigger_up('create_filter', { - filter: filter, - on_success: resolve, - }); - }); - }, - deleteFilter: function (filterId) { - var self = this; - return new Promise(function (resolve, reject) { - self.trigger_up('delete_filter', { - filterId: filterId, - on_success: resolve, - }); - }); - }, - /** - * Informs the action manager to do an action. This supposes that the action - * manager can be found amongst the ancestors of the current widget. - * If that's not the case this method will simply return an unresolved - * promise. - * - * @param {any} action - * @param {any} options - * @returns {Promise} - */ - do_action: function (action, options) { - var self = this; - return new Promise(function (resolve, reject) { - self.trigger_up('do_action', { - action: action, - options: options, - on_success: resolve, - on_fail: (reason) => { - reject(reason); - return "alreadyThrown" - }, - }); - }); - }, /** * Displays a notification. * diff --git a/addons/web/static/src/legacy/js/public/public_root.js b/addons/web/static/src/legacy/js/public/public_root.js index f3965cfa0b5..2535fa4f9ad 100644 --- a/addons/web/static/src/legacy/js/public/public_root.js +++ b/addons/web/static/src/legacy/js/public/public_root.js @@ -11,7 +11,6 @@ import lazyloader from "@web/legacy/js/public/lazyloader"; import { makeLegacyNotificationService, mapLegacyEnvToWowlEnv, - makeLegacyRPCService, createWidgetParent, } from "../../utils"; @@ -22,7 +21,6 @@ import { browser } from '@web/core/browser/browser'; import { jsonrpc } from '@web/core/network/rpc_service'; import { renderToString } from "@web/core/utils/render"; import { _t } from "@web/core/l10n/translation"; -import { omit } from "@web/core/utils/objects"; import { App, whenReady } from "@odoo/owl"; import { getOrigin } from '@web/core/utils/urls'; @@ -227,37 +225,9 @@ export const PublicRoot = publicWidget.RootWidget.extend({ * @param {OdooEvent} event */ _onCallService: function (ev) { - function _computeContext(context, noContextKeys) { - context = Object.assign({}, this._getContext(), context); - if (noContextKeys) { - context = omit(context, ...noContextKeys); - } - return JSON.parse(JSON.stringify(context)); - } - const payload = ev.data; - let args = payload.args || []; - if (payload.service === 'ajax' && payload.method === 'rpc') { - // ajax service uses an extra 'target' argument for rpc - args = args.concat(ev.target); - - var route = args[0]; - if (String(route).startsWith("/web/dataset/call_kw/")) { - var params = args[1]; - var options = args[2]; - var noContextKeys; - if (options) { - noContextKeys = options.noContextKeys; - args[2] = omit(options, 'noContextKeys'); - } - params.kwargs.context = _computeContext.call(this, params.kwargs.context, noContextKeys); - } - } else { - return; - } - const service = this.env.services[payload.service]; - const result = service[payload.method].apply(service, args); + const result = service[payload.method].apply(service, payload.args || []); payload.callback(result); ev.stopPropagation(); }, @@ -358,7 +328,6 @@ export async function createPublicRoot(RootWidget) { await lazyloader.allScriptsLoaded; // add a bunch of mapping services that will redirect service calls from the legacy env // to the wowl env - serviceRegistry.add("legacy_rpc", makeLegacyRPCService(legacyEnv)); serviceRegistry.add("legacy_notification", makeLegacyNotificationService(legacyEnv)); const wowlToLegacyServiceMappers = registry.category('wowlToLegacyServiceMappers').getEntries(); for (const [legacyServiceName, wowlToLegacyServiceMapper] of wowlToLegacyServiceMappers) { @@ -379,12 +348,17 @@ export async function createPublicRoot(RootWidget) { serviceRegistry.add("rpc", { async: true, start(env) { - let rpcId = 0; - return function rpc(route, params = {}, settings) { + return function rpc(route, params = {}, settings = {}) { if (!route.match(/^(?:https?:)?\/\//)) { route = baseUrl + route; } - return jsonrpc(env, rpcId++, route, params, settings); + if (String(route).includes("/web/dataset/call_kw/")) { + params.kwargs.context = { + ...publicRoot._getContext(), + ...params.kwargs.context, + }; + } + return jsonrpc(route, params, { bus: env.bus, ...settings }); }; }, }, { force: true }); diff --git a/addons/web/static/src/legacy/js/widgets/name_and_signature.js b/addons/web/static/src/legacy/js/widgets/name_and_signature.js index 44d89ac0cde..e543e9048b6 100644 --- a/addons/web/static/src/legacy/js/widgets/name_and_signature.js +++ b/addons/web/static/src/legacy/js/widgets/name_and_signature.js @@ -78,6 +78,7 @@ export var NameAndSignature = Widget.extend({ this.drawTimeout = null; this.drawPreviewTimeout = null; this.signatureAreaHidden = false; + this.rpc = this.bindService("rpc"); }, /** * Loads the fonts. @@ -94,7 +95,7 @@ export var NameAndSignature = Widget.extend({ } return Promise.all([ this._super.apply(this, arguments), - this._rpc({route: '/web/sign/get_fonts/' + this.defaultFont}).then(data => { + this.rpc('/web/sign/get_fonts/' + this.defaultFont).then(data => { this.fonts = data; }) ]); diff --git a/addons/web/static/src/legacy/legacy_setup.js b/addons/web/static/src/legacy/legacy_setup.js index 6d9bbaba6c4..b9c0c888df5 100644 --- a/addons/web/static/src/legacy/legacy_setup.js +++ b/addons/web/static/src/legacy/legacy_setup.js @@ -1,10 +1,7 @@ /** @odoo-module alias=web.legacySetup **/ import { registry } from "../core/registry"; -import { - makeLegacyNotificationService, - makeLegacyRPCService, -} from "./utils"; +import { makeLegacyNotificationService } from "./utils"; import { makeLegacyActionManagerService } from "./backend_utils"; import legacyEnv from "@web/legacy/js/env"; import { templates } from "@web/core/assets"; @@ -25,7 +22,6 @@ export const legacySetupProm = new Promise((resolve) => { serviceRegistry.add("legacy_action_manager", legacyActionManagerService); // add a service to redirect rpc events triggered on the bus in the // legacy env on the bus in the wowl env - serviceRegistry.add("legacy_rpc", makeLegacyRPCService(legacyEnv)); serviceRegistry.add("legacy_notification", makeLegacyNotificationService(legacyEnv)); const wowlToLegacyServiceMappers = registry.category("wowlToLegacyServiceMappers").getEntries(); for (const [legacyServiceName, wowlToLegacyServiceMapper] of wowlToLegacyServiceMappers) { diff --git a/addons/web/static/src/legacy/utils.js b/addons/web/static/src/legacy/utils.js index 0472fbac8cf..4e16585ab3e 100644 --- a/addons/web/static/src/legacy/utils.js +++ b/addons/web/static/src/legacy/utils.js @@ -16,11 +16,6 @@ import { } from "@odoo/owl"; import { templates } from "@web/core/assets"; import { _t } from "@web/core/l10n/translation"; -import { - ConnectionAbortedError, - ConnectionLostError, - RPCError, -} from "@web/core/network/rpc_service"; export const wowlServicesSymbol = Symbol("wowlServices"); @@ -116,46 +111,6 @@ export function makeLegacyNotificationService(legacyEnv) { }; } -export function makeLegacyRPC(wowlRPC) { - return function rpc(route, args, options, target) { - let rpcPromise = null; - const promise = new Promise(function (resolve, reject) { - rpcPromise = wowlRPC(route, args, options); - rpcPromise - .then(function (result) { - if (!target.isDestroyed()) { - resolve(result); - } - }) - .catch(function (reason) { - if (!target.isDestroyed()) { - if (reason instanceof RPCError || reason instanceof ConnectionLostError) { - // we do not reject an error here because we want to pass through - // the legacy guardedCatch code - reject({ message: reason, event: $.Event(), legacy: true }); - } else if (reason instanceof ConnectionAbortedError) { - reject({ message: reason.message, event: $.Event("abort") }); - } else { - reject(reason); - } - } - }); - }); - promise.abort = rpcPromise.abort.bind(rpcPromise); - return promise; - }; -} - -export function makeLegacyRPCService(legacyEnv) { - return { - dependencies: ["rpc"], - start(_, { rpc: wowlRPC }) { - const rpc = makeLegacyRPC(wowlRPC); - legacyEnv.services.ajax = { rpc }; - }, - }; -} - /** * This hook allows legacy owl Components to use services coming from the wowl env. * @param {string} serviceName @@ -175,13 +130,8 @@ export function createWidgetParent(env) { env, _trigger_up: (ev) => { if (ev.name === "call_service") { - let args = ev.data.args || []; - if (ev.data.service === "ajax" && ev.data.method === "rpc") { - // ajax service uses an extra 'target' argument for rpc - args = args.concat(ev.target); - } const service = env.services[ev.data.service]; - const result = service[ev.data.method].apply(service, args); + const result = service[ev.data.method].apply(service, ev.data.args || []); ev.data.callback(result); } }, diff --git a/addons/web/static/tests/legacy/core/mixins_tests.js b/addons/web/static/tests/legacy/core/mixins_tests.js deleted file mode 100644 index 1c55691caa0..00000000000 --- a/addons/web/static/tests/legacy/core/mixins_tests.js +++ /dev/null @@ -1,30 +0,0 @@ -/** @odoo-module **/ - -import testUtils from "@web/../tests/legacy/helpers/test_utils"; -import Widget from "@web/legacy/js/core/widget"; - -QUnit.module('core', {}, function () { - - QUnit.module('mixins'); - - QUnit.test('perform a do_action properly', function (assert) { - assert.expect(3); - var done = assert.async(); - - var widget = new Widget(); - - testUtils.mock.intercept(widget, 'do_action', function (event) { - assert.strictEqual(event.data.action, 'test.some_action_id', - "should have sent proper action name"); - assert.deepEqual(event.data.options, {clear_breadcrumbs: true}, - "should have sent proper options"); - event.data.on_success(); - }); - - widget.do_action('test.some_action_id', {clear_breadcrumbs: true}).then(function () { - assert.ok(true, 'deferred should have been resolved'); - widget.destroy(); - done(); - }); - }); -}); diff --git a/addons/web/static/tests/legacy/core/rpc_tests.js b/addons/web/static/tests/legacy/core/rpc_tests.js deleted file mode 100644 index 1389d3bc914..00000000000 --- a/addons/web/static/tests/legacy/core/rpc_tests.js +++ /dev/null @@ -1,277 +0,0 @@ -/** @odoo-module **/ - -import rpc from "@web/legacy/js/core/rpc"; - -QUnit.module('core', {}, function () { - - QUnit.module('RPC Builder'); - - QUnit.test('basic rpc (route)', function (assert) { - assert.expect(1); - - var query = rpc.buildQuery({ - route: '/my/route', - }); - assert.strictEqual(query.route, '/my/route', "should have the proper route"); - }); - - QUnit.test('rpc on route with parameters', function (assert) { - assert.expect(1); - - var query = rpc.buildQuery({ - route: '/my/route', - params: {hey: 'there', model: 'test'}, - }); - - assert.deepEqual(query.params, {hey: 'there', model: 'test'}, - "should transfer the proper parameters"); - }); - - QUnit.test('basic rpc, with no context', function (assert) { - assert.expect(1); - - var query = rpc.buildQuery({ - model: 'partner', - method: 'test', - kwargs: {}, - }); - assert.notOk(query.params.kwargs.context, - "does not automatically add a context"); - }); - - QUnit.test('basic rpc, with context', function (assert) { - assert.expect(1); - - var query = rpc.buildQuery({ - model: 'partner', - method: 'test', - context: {a: 1}, - }); - - assert.deepEqual(query.params.kwargs.context, {a: 1}, - "properly transfer the context"); - }); - - QUnit.test('basic rpc, with context, part 2', function (assert) { - assert.expect(1); - - var query = rpc.buildQuery({ - model: 'partner', - method: 'test', - kwargs: {context: {a: 1}}, - }); - - assert.deepEqual(query.params.kwargs.context, {a: 1}, - "properly transfer the context"); - - }); - - QUnit.test('basic rpc (method of model)', function (assert) { - assert.expect(3); - - var query = rpc.buildQuery({ - model: 'partner', - method: 'test', - kwargs: {context: {a: 1}}, - }); - - assert.strictEqual(query.route, '/web/dataset/call_kw/partner/test', - "should call the proper route"); - assert.strictEqual(query.params.model, 'partner', - "should correctly specify the model"); - assert.strictEqual(query.params.method, 'test', - "should correctly specify the method"); - }); - - QUnit.test('rpc with args and kwargs', function (assert) { - assert.expect(4); - var query = rpc.buildQuery({ - model: 'partner', - method: 'test', - args: ['arg1', 2], - kwargs: {k: 78}, - }); - - assert.strictEqual(query.route, '/web/dataset/call_kw/partner/test', - "should call the proper route"); - assert.strictEqual(query.params.args[0], 'arg1', - "should call with correct args"); - assert.strictEqual(query.params.args[1], 2, - "should call with correct args"); - assert.strictEqual(query.params.kwargs.k, 78, - "should call with correct kargs"); - }); - - QUnit.test('search_read method', function (assert) { - assert.expect(1); - var query = rpc.buildQuery({ - model: 'partner', - method: 'search_read', - domain: ['a', '=', 1], - fields: ['name'], - limit: 32, - offset: 2, - orderBy: [{name: 'yop', asc: true}, {name: 'aa', asc: false}], - }); - assert.deepEqual(query.params, { - args: [], - kwargs: { - domain: ['a', '=', 1], - fields: ['name'], - offset: 2, - limit: 32, - order: 'yop ASC, aa DESC' - }, - method: 'search_read', - model: 'partner' - }, "should have correct kwargs"); - }); - - QUnit.test('search_read with args', function (assert) { - assert.expect(1); - var query = rpc.buildQuery({ - model: 'partner', - method: 'search_read', - args: [ - ['a', '=', 1], - ['name'], - 2, - 32, - 'yop ASC, aa DESC', - ] - }); - assert.deepEqual(query.params, { - args: [['a', '=', 1], ['name'], 2, 32, 'yop ASC, aa DESC'], - kwargs: {}, - method: 'search_read', - model: 'partner' - }, "should have correct args"); - }); - - QUnit.test('read_group', function (assert) { - assert.expect(2); - - var query = rpc.buildQuery({ - model: 'partner', - method: 'read_group', - domain: ['a', '=', 1], - fields: ['name'], - groupBy: ['product_id'], - context: {abc: 'def'}, - lazy: true, - }); - - assert.deepEqual(query.params, { - args: [], - kwargs: { - context: {abc: 'def'}, - domain: ['a', '=', 1], - fields: ['name'], - groupby: ['product_id'], - lazy: true, - }, - method: 'read_group', - model: 'partner', - }, "should have correct args"); - assert.equal(query.route, '/web/dataset/call_kw/partner/read_group', - "should call correct route"); - }); - - QUnit.test('read_group with kwargs', function (assert) { - assert.expect(1); - - var query = rpc.buildQuery({ - model: 'partner', - method: 'read_group', - domain: ['a', '=', 1], - fields: ['name'], - groupBy: ['product_id'], - lazy: false, - kwargs: {context: {abc: 'def'}} - }); - - assert.deepEqual(query.params, { - args: [], - kwargs: { - context: {abc: 'def'}, - domain: ['a', '=', 1], - fields: ['name'], - groupby: ['product_id'], - lazy: false, - }, - method: 'read_group', - model: 'partner', - }, "should have correct args"); - }); - - QUnit.test('read_group with no domain, nor fields', function (assert) { - assert.expect(7); - var query = rpc.buildQuery({ - model: 'partner', - method: 'read_group', - }); - - assert.deepEqual(query.params.kwargs.domain, [], "should have [] as default domain"); - assert.deepEqual(query.params.kwargs.fields, [], "should have false as default fields"); - assert.deepEqual(query.params.kwargs.groupby, [], "should have false as default groupby"); - assert.deepEqual(query.params.kwargs.offset, undefined, "should not enforce a default value for offst"); - assert.deepEqual(query.params.kwargs.limit, undefined, "should not enforce a default value for limit"); - assert.deepEqual(query.params.kwargs.orderby, undefined, "should not enforce a default value for orderby"); - assert.deepEqual(query.params.kwargs.lazy, undefined, "should not enforce a default value for lazy"); - }); - - QUnit.test('read_group with args and kwargs', function (assert) { - assert.expect(9); - var query = rpc.buildQuery({ - model: 'partner', - method: 'read_group', - kwargs: { - domain: ['name', '=', 'saucisse'], - fields: ['category_id'], - groupby: ['country_id'], - }, - }); - - assert.deepEqual(query.params.kwargs.domain, ['name', '=', 'saucisse'], "should have ['name', '=', 'saucisse'] category_id as default domain"); - assert.deepEqual(query.params.kwargs.fields, ['category_id'], "should have category_id as default fields"); - assert.deepEqual(query.params.kwargs.groupby, ['country_id'], "should have country_id as default groupby"); - - var query = rpc.buildQuery({ - model: 'partner', - method: 'read_group', - args: [['name', '=', 'saucisse']], - kwargs: { - fields: ['category_id'], - groupby: ['country_id'], - }, - }); - - assert.deepEqual(query.params.kwargs.domain, undefined, "should not enforce a default value for domain"); - assert.deepEqual(query.params.kwargs.fields, ['category_id'], "should have category_id as default fields"); - assert.deepEqual(query.params.kwargs.groupby, ['country_id'], "should have country_id as default groupby"); - - var query = rpc.buildQuery({ - model: 'partner', - method: 'read_group', - args: [['name', '=', 'saucisse'], ['category_id'], ['country_id']], - }); - - assert.deepEqual(query.params.kwargs.domain, undefined, "should not enforce a default value for domain"); - assert.deepEqual(query.params.kwargs.fields, undefined, "should not enforce a default value for fields"); - assert.deepEqual(query.params.kwargs.groupby, undefined, "should not enforce a default value for groupby"); - }); - - QUnit.test('search_read with no domain, nor fields', function (assert) { - assert.expect(5); - var query = rpc.buildQuery({ - model: 'partner', - method: 'search_read', - }); - - assert.deepEqual(query.params.kwargs.domain, undefined, "should not enforce a default value for domain"); - assert.deepEqual(query.params.kwargs.fields, undefined, "should not enforce a default value for fields"); - assert.deepEqual(query.params.kwargs.offset, undefined, "should not enforce a default value for offset"); - assert.deepEqual(query.params.kwargs.limit, undefined, "should not enforce a default value for limit"); - assert.deepEqual(query.params.kwargs.order, undefined, "should not enforce a default value for orderby"); - }); -}); diff --git a/addons/web/static/tests/legacy/core/widget_tests.js b/addons/web/static/tests/legacy/core/widget_tests.js index 639a8c53a2c..66926827643 100644 --- a/addons/web/static/tests/legacy/core/widget_tests.js +++ b/addons/web/static/tests/legacy/core/widget_tests.js @@ -4,7 +4,8 @@ import Dialog from "@web/legacy/js/core/dialog"; import Widget from "@web/legacy/js/core/widget"; import testUtils from "@web/../tests/legacy/helpers/test_utils"; import { renderToString } from "@web/core/utils/render"; -import { makeLegacyRPC } from "@web/legacy/utils"; +import makeTestEnvironment from "../helpers/test_env"; +import { SERVICES_METADATA } from "@web/env"; QUnit.module('core', {}, function () { @@ -399,31 +400,31 @@ QUnit.module('core', {}, function () { }); QUnit.test("calling _rpc on destroyed widgets", async function (assert) { - assert.expect(3); + assert.expect(2); - const rpc = makeLegacyRPC(() => { + SERVICES_METADATA.rpc = true; + var def; + owl.Component.env = await makeTestEnvironment({}, () => { def = testUtils.makeTestPromise(); - def.abort = def.reject; return def; }); - var def; - var parent = new Widget(); - testUtils.mock.intercept(parent, 'call_service', function ({ data }) { - if (data.service === "ajax" && data.method === "rpc") { - data.callback(rpc(...data.args)); - } + const ChildWidget = Widget.extend({ + init() { + this._super(...arguments); + this.rpc = this.bindService("rpc"); + }, }); - var widget = new Widget(parent); + var widget = new ChildWidget(); - widget._rpc({route: '/a/route'}).then(function () { + widget.rpc('/a/route').then(function () { assert.ok(true, "The ajax call should be resolve"); }); def.resolve(); await testUtils.nextMicrotaskTick(); def = null; - widget._rpc({route: '/a/route'}).then(function () { + widget.rpc('/a/route').then(function () { throw Error("Calling _rpc on a destroyed widget should return a " + "promise that remains pending forever"); }).catch(function () { @@ -435,19 +436,16 @@ QUnit.module('core', {}, function () { await testUtils.nextMicrotaskTick(); def = null; - widget._rpc({route: '/a/route'}).then(function () { + widget.rpc('/a/route').then(function () { throw Error("Calling _rpc on a destroyed widget should return a " + "promise that remains pending forever"); }).catch(function () { throw Error("Calling _rpc on a destroyed widget should return a " + "promise that remains pending forever"); }); - assert.ok(!def, "trigger_up is not performed and the call returns a " + - "promise that remains pending forever"); assert.ok(true, "there should be no crash when calling _rpc on a destroyed widget"); - parent.destroy(); }); QUnit.test("calling do_hide on a widget destroyed before being rendered", async function (assert) { diff --git a/addons/web/static/tests/legacy/helpers/test_env.js b/addons/web/static/tests/legacy/helpers/test_env.js index 39c00445893..83455ea95fd 100644 --- a/addons/web/static/tests/legacy/helpers/test_env.js +++ b/addons/web/static/tests/legacy/helpers/test_env.js @@ -39,7 +39,6 @@ }, hotkey: { add: () => () => {} }, // fake service notification: { notify() {} }, - ajax: { rpc }, rpc, ui: { activeElement: document }, // fake service ...env.services, diff --git a/addons/web/static/tests/legacy/widgets/name_and_signature_tests.js b/addons/web/static/tests/legacy/widgets/name_and_signature_tests.js index dd4eb5638bc..a361a44aa28 100644 --- a/addons/web/static/tests/legacy/widgets/name_and_signature_tests.js +++ b/addons/web/static/tests/legacy/widgets/name_and_signature_tests.js @@ -1,7 +1,8 @@ /** @odoo-module **/ import { NameAndSignature } from "@web/legacy/js/widgets/name_and_signature"; - import testUtils from "@web/../tests/legacy/helpers/test_utils"; + import makeTestEnvironment from "../helpers/test_env"; + import { patchWithCleanup } from "@web/../tests/helpers/utils"; const MockedNameAndSignature = NameAndSignature.extend({ events: { @@ -14,14 +15,14 @@ async function MockedNameAndSignatureGenerator (options) { const parent = $("#qunit-fixture"); - const mockedNameAndSignature = new MockedNameAndSignature(parent, options); - testUtils.mock.intercept(mockedNameAndSignature, 'call_service', function ({ data }) { - if (data.service === "ajax" && data.method === "rpc") { - if (data.args[0] === "/web/sign/get_fonts/") { - data.callback(Promise.resolve()); + patchWithCleanup(owl.Component, { + env: await makeTestEnvironment({}, (route) => { + if (route === "/web/sign/get_fonts/") { + return Promise.resolve(); } - } + }), }); + const mockedNameAndSignature = new MockedNameAndSignature(parent, options); await mockedNameAndSignature.appendTo(parent); await mockedNameAndSignature.resetSignature(); return mockedNameAndSignature; diff --git a/addons/web_editor/__manifest__.py b/addons/web_editor/__manifest__.py index dd468d425fc..c7f2b14b0ec 100644 --- a/addons/web_editor/__manifest__.py +++ b/addons/web_editor/__manifest__.py @@ -85,14 +85,12 @@ Odoo Web Editor widget. 'web/static/src/legacy/js/libs/bootstrap.js', 'web/static/src/legacy/js/libs/jquery.js', 'web/static/src/legacy/js/libs/jSignatureCustom.js', - 'web/static/src/legacy/js/core/ajax.js', 'web/static/src/legacy/js/core/bus.js', 'web/static/src/legacy/js/core/class.js', 'web/static/src/legacy/js/core/dialog.js', 'web/static/src/legacy/xml/dialog.xml', 'web/static/src/legacy/js/core/dom.js', 'web/static/src/legacy/js/core/mixins.js', - 'web/static/src/legacy/js/core/rpc.js', 'web/static/src/legacy/js/core/service_mixins.js', 'web/static/src/legacy/js/core/time.js', 'web/static/src/legacy/js/core/widget.js', diff --git a/addons/web_editor/static/src/js/common/ace.js b/addons/web_editor/static/src/js/common/ace.js index abe9e70383b..45478702c2a 100644 --- a/addons/web_editor/static/src/js/common/ace.js +++ b/addons/web_editor/static/src/js/common/ace.js @@ -7,7 +7,7 @@ import Widget from "@web/legacy/js/core/widget"; import { browser } from "@web/core/browser/browser"; import { debounce } from "@web/core/utils/timing"; import { sortBy } from "@web/core/utils/arrays"; -import { pick } from "@web/core/utils/objects"; +import { omit, pick } from "@web/core/utils/objects"; import { AlertDialog, ConfirmationDialog } from "@web/core/confirmation_dialog/confirmation_dialog"; import { markup } from "@odoo/owl"; @@ -209,6 +209,9 @@ var ViewEditor = Widget.extend({ this.views = this.resources.xml; this.scss = this.resources.scss; this.js = this.resources.js; + + this.rpc = this.bindService("rpc"); + this.orm = this.bindService("orm"); }, /** * Loads everything the ace library needs to work. @@ -459,6 +462,9 @@ var ViewEditor = Widget.extend({ this._showErrorLine(check.error.line, check.error.message, this._getSelectedResource()); } }, + _getContext() { + return {}; + }, /** * Returns the currently selected resource data. * @@ -502,17 +508,14 @@ var ViewEditor = Widget.extend({ this.js = this.resources.js; // Load resources - return this._rpc({ - route: '/web_editor/get_assets_editor_resources', - params: { - key: this.viewKey, - get_views: !this.options.doNotLoadViews, - get_scss: !this.options.doNotLoadSCSS, - get_js: !this.options.doNotLoadJS, - bundles: this.options.includeBundles, - bundles_restriction: this.options.filesFilter === 'all' ? [] : this.options.defaultBundlesRestriction, - only_user_custom_files: this.options.filesFilter === 'custom', - }, + return this.rpc('/web_editor/get_assets_editor_resources', { + key: this.viewKey, + get_views: !this.options.doNotLoadViews, + get_scss: !this.options.doNotLoadSCSS, + get_js: !this.options.doNotLoadJS, + bundles: this.options.includeBundles, + bundles_restriction: this.options.filesFilter === 'all' ? [] : this.options.defaultBundlesRestriction, + only_user_custom_files: this.options.filesFilter === 'custom', }).then((function (resources) { _processViews.call(this, resources.views || []); _processJSorSCSS.call(this, resources.scss || [], 'scss'); @@ -600,10 +603,8 @@ var ViewEditor = Widget.extend({ return Promise.reject(_t("Reseting views is not supported yet")); } else { var resource = type === 'scss' ? this.scss[resID] : this.js[resID]; - return this._rpc({ - model: 'web_editor.assets', - method: 'reset_asset', - args: [resID, resource.bundle], + return this.orm.call("web_editor.assets", "reset_asset", [resID, resource.bundle], { + context: this._getContext(), }); } }, @@ -621,11 +622,17 @@ var ViewEditor = Widget.extend({ var sessionIdEndsWithJS = String(session.id).endsWith(".js"); var bundle = sessionIdEndsWithJS ? this.js[session.id].bundle : this.scss[session.id].bundle; var fileType = sessionIdEndsWithJS ? 'js' : 'scss'; - return self._rpc({ - model: 'web_editor.assets', - method: 'save_asset', - args: [session.id, bundle, session.text, fileType], - }).then(function () { + return this.orm.call( + "web_editor.assets", + "save_asset", + [ + session.id, + bundle, + session.text, + fileType, + ], + { context: this._getContext() } + ).then(function () { self._toggleDirtyInfo(session.id, fileType, false); }); }, @@ -714,13 +721,13 @@ var ViewEditor = Widget.extend({ _saveView: function (session) { var self = this; return new Promise(function (resolve, reject) { - self._rpc({ - model: 'ir.ui.view', - method: 'write', - args: [[session.id], {arch: session.text}], - }, { - noContextKeys: 'lang', - }).then(function () { + self.orm.write( + "ir.ui.view", + [session.id], + { arch: session.text }, + { context: omit(self._getContext(), "lang") } + ).then( + function () { self._toggleDirtyInfo(session.id, 'xml', false); resolve(); }, function (source, error) { diff --git a/addons/web_editor/static/src/js/editor/image_processing.js b/addons/web_editor/static/src/js/editor/image_processing.js index ab7f33a86ae..4c70a827802 100644 --- a/addons/web_editor/static/src/js/editor/image_processing.js +++ b/addons/web_editor/static/src/js/editor/image_processing.js @@ -447,10 +447,7 @@ export async function loadImageInfo(img, rpc, attachmentSrc = '') { return; } - const {original} = await rpc({ - route: '/web_editor/get_image_info', - params: {src: src.split(/[?#]/)[0]}, - }); + const {original} = await rpc('/web_editor/get_image_info', {src: src.split(/[?#]/)[0]}); // Check that url is local. const isLocal = original && new URL(original.image_src, window.location.origin).origin === window.location.origin && !/\/web\/image\/\d+-redirect\//.test(original.image_src); diff --git a/addons/web_editor/static/src/js/editor/snippets.editor.js b/addons/web_editor/static/src/js/editor/snippets.editor.js index d2f50a7d1fa..4f698d6839c 100644 --- a/addons/web_editor/static/src/js/editor/snippets.editor.js +++ b/addons/web_editor/static/src/js/editor/snippets.editor.js @@ -1767,6 +1767,8 @@ var SnippetsMenu = Widget.extend({ this.loadingElements = {}; this._loadingEffectDisabled = false; this._onClick = this._onClick.bind(this); + + this.orm = this.bindService("orm"); }, /** * @override @@ -2090,14 +2092,12 @@ var SnippetsMenu = Widget.extend({ context.lang = this.options.context.user_lang; context.snippet_lang = this.options.context.lang; } - this._defLoadSnippets = this._rpc({ - model: 'ir.ui.view', - method: 'render_public_asset', - args: [this.options.snippets, {}], - kwargs: { - context: context, - }, - }, { shadow: true }); + this._defLoadSnippets = this.orm.silent.call( + "ir.ui.view", + "render_public_asset", + [this.options.snippets, {}], + { context } + ); cacheSnippetTemplate[this.options.snippets] = this._defLoadSnippets; return this._defLoadSnippets; }, @@ -3817,11 +3817,7 @@ var SnippetsMenu = Widget.extend({ classes: 'btn-primary', click: function () { this.$footer.find('.btn').toggleClass('o_hidden'); - this._rpc({ - model: 'ir.module.module', - method: 'button_immediate_install', - args: [[moduleID]], - }).then(() => { + this.orm.call("ir.module.module", "button_immediate_install", [[moduleID]]).then(() => { self.trigger_up('request_save', { invalidateSnippetCache: true, _toMutex: true, @@ -3897,13 +3893,9 @@ var SnippetsMenu = Widget.extend({ close: true, classes: 'btn-primary', click: async () => { - await this._rpc({ - model: 'ir.ui.view', - method: 'delete_snippet', - kwargs: { - 'view_id': snippetId, - 'template_key': this.options.snippets, - }, + await this.orm.call("ir.ui.view", "delete_snippet", [], { + 'view_id': snippetId, + 'template_key': this.options.snippets, }); await this._loadSnippetsTemplates(true); }, @@ -3940,14 +3932,10 @@ var SnippetsMenu = Widget.extend({ const name = $textInput.val(); if (name !== snippetName) { this._execWithLoadingEffect(async () => { - await this._rpc({ - model: 'ir.ui.view', - method: 'rename_snippet', - kwargs: { - 'name': name, - 'view_id': parseInt(ev.target.dataset.snippetId), - 'template_key': this.options.snippets, - }, + await this.orm.call("ir.ui.view", "rename_snippet", [], { + 'name': name, + 'view_id': parseInt(ev.target.dataset.snippetId), + 'template_key': this.options.snippets, }); }, true); } diff --git a/addons/web_editor/static/src/js/editor/snippets.options.js b/addons/web_editor/static/src/js/editor/snippets.options.js index 3ac1b80ddf8..327e4c9dd1b 100644 --- a/addons/web_editor/static/src/js/editor/snippets.options.js +++ b/addons/web_editor/static/src/js/editor/snippets.options.js @@ -4,7 +4,6 @@ import { attachComponent } from "@web/legacy/utils"; import { MediaDialog } from "@web_editor/components/media_dialog/media_dialog"; import Dialog from "@web/legacy/js/core/dialog"; import dom from "@web/legacy/js/core/dom"; -import rpc from "@web/legacy/js/core/rpc"; import { throttleForAnimation, debounce } from "@web/core/utils/timing"; import { clamp } from "@web/core/utils/numbers"; import Widget from "@web/legacy/js/core/widget"; @@ -41,6 +40,7 @@ import { normalizeCSSColor, } from '@web/core/utils/colors'; import { renderToElement } from "@web/core/utils/render"; +import { jsonrpc } from "@web/core/network/rpc_service"; const preserveCursor = OdooEditorLib.preserveCursor; const { DateTime } = luxon; @@ -2552,6 +2552,9 @@ const Many2oneUserValueWidget = SelectUserValueWidget.extend({ options.domainComponents = {}; options.nullText = $target[0].dataset.nullText || JSON.parse($target[0].dataset.oeContactOptions || '{}')['null_text']; + + this.orm = this._withCache(this.bindService("orm")); + return this._super(...arguments); }, /** @@ -2660,12 +2663,11 @@ const Many2oneUserValueWidget = SelectUserValueWidget.extend({ async setFilterInDomainIds(linkedRecordsIds) { const allowedIds = new Set(); if (linkedRecordsIds) { - const parentRecordsData = await this._rpc({ - model: this.options.filterInModel, - method: 'search_read', - fields: [this.options.filterInField], - domain: [['id', 'in', linkedRecordsIds]], - }); + const parentRecordsData = await this.orm.searchRead( + this.options.filterInModel, + [['id', 'in', linkedRecordsIds]], + [this.options.filterInField] + ); parentRecordsData.forEach(record => { record[this.options.filterInField].forEach(item => allowedIds.add(item)); }); @@ -2684,12 +2686,17 @@ const Many2oneUserValueWidget = SelectUserValueWidget.extend({ * * @override */ - async _rpc() { - const cacheId = JSON.stringify(...arguments); - if (!this._rpcCache[cacheId]) { - this._rpcCache[cacheId] = this._super(...arguments); - } - return this._rpcCache[cacheId]; + _withCache(orm) { + const cache = this._rpcCache; + return Object.assign(Object.create(orm), { + call() { + const cacheId = JSON.stringify(arguments); + if (!cache[cacheId]) { + cache[cacheId] = orm.call(...arguments); + } + return cache[cacheId]; + }, + }); }, /** * Searches the database for corresponding records and updates the dropdown @@ -2697,23 +2704,19 @@ const Many2oneUserValueWidget = SelectUserValueWidget.extend({ * @private */ async _search(needle) { - const recTuples = await this._rpc({ - model: this.options.model, - method: 'name_search', - kwargs: { - name: needle, - args: (await this._getSearchDomain()).concat( - Object.values(this.options.domainComponents).filter(item => item !== null) - ), - operator: "ilike", - limit: this.options.limit + 1, - }, - }); - const records = await this._rpc({ - model: this.options.model, - method: 'read', - args: [recTuples.map(([id, _name]) => id), this.options.fields], + const recTuples = await this.orm.call(this.options.model, "name_search", [], { + name: needle, + args: (await this._getSearchDomain()).concat( + Object.values(this.options.domainComponents).filter(item => item !== null) + ), + operator: "ilike", + limit: this.options.limit + 1, }); + const records = await this.orm.read( + this.options.model, + recTuples.map(([id, _name]) => id), + this.options.fields + ); // Remove select options. this._userValueWidgets.filter(widget => { return widget instanceof ButtonUserValueWidget && @@ -2801,11 +2804,7 @@ const Many2oneUserValueWidget = SelectUserValueWidget.extend({ */ async _getDisplayName(recordId) { if (!this.displayNameCache.hasOwnProperty(recordId)) { - this.displayNameCache[recordId] = (await this._rpc({ - model: this.options.model, - method: 'read', - args: [[recordId], ['display_name']], - }))[0].display_name; + this.displayNameCache[recordId] = (await this.orm.read(this.options.model, [recordId], ['display_name']))[0].display_name; } return this.displayNameCache[recordId]; }, @@ -2917,6 +2916,8 @@ const Many2manyUserValueWidget = UserValueWidget.extend({ dataAttributes.filterInModel = options.model; dataAttributes.filterInField = options.m2oField; } + this.orm = this.bindService("orm"); + this.fields = this.bindService("field"); return this._super(...arguments); }, /** @@ -2931,27 +2932,15 @@ const Many2manyUserValueWidget = UserValueWidget.extend({ return; } const { model, recordId, m2oField } = this.options; - const [record] = await this._rpc({ - model: model, - method: 'read', - args: [[parseInt(recordId)], [m2oField]], - }); + const [record] = await this.orm.read(model, [parseInt(recordId)], [m2oField]); const selectedRecordIds = record[m2oField]; // TODO: handle no record - const modelData = await this._rpc({ - model: model, - method: 'fields_get', - args: [[m2oField]], - }); + const modelData = await this.fields.loadFields(model, { fieldNames: [m2oField] }); // TODO: simultaneously fly both RPCs this.m2oModel = modelData[m2oField].relation; this.m2oName = modelData[m2oField].field_description; // Use as string attr? - const selectedRecords = await this._rpc({ - model: this.m2oModel, - method: 'read', - args: [selectedRecordIds, ['display_name']], - }); + const selectedRecords = await this.orm.read(this.m2oModel, selectedRecordIds, ['display_name']); // TODO: reconcile the fact that this widget sets its own initial value // instead of it coming through setValue(_computeWidgetState) this._value = JSON.stringify(selectedRecords); @@ -5603,6 +5592,13 @@ registry.ReplaceMedia = SnippetOptionWidget.extend({ * options that handles all the common parts. */ const ImageHandlerOption = SnippetOptionWidget.extend({ + /** + * @override + */ + init() { + this._super(...arguments); + this.rpc = this.bindService("rpc"); + }, /** * @override */ @@ -5849,7 +5845,7 @@ const ImageHandlerOption = SnippetOptionWidget.extend({ */ async _loadImageInfo(attachmentSrc = '') { const img = this._getImg(); - await loadImageInfo(img, this._rpc.bind(this), attachmentSrc); + await loadImageInfo(img, this.rpc, attachmentSrc); if (!img.dataset.originalId) { this.originalId = null; this.originalSrc = null; @@ -5987,6 +5983,7 @@ registry.ImageTools = ImageHandlerOption.extend({ */ init() { this.shapeCache = {}; + this.rpc = this.bindService("rpc"); return this._super(...arguments); }, /** @@ -6022,7 +6019,7 @@ registry.ImageTools = ImageHandlerOption.extend({ const imageCropWrapperElement = document.createElement('div'); document.body.append(imageCropWrapperElement); const imageCropWrapper = await attachComponent(this, imageCropWrapperElement, ImageCrop, { - rpc: this._rpc.bind(this), + rpc: this.rpc, activeOnStart: true, media: img, mimetype: this._getImageMimetype(img), @@ -6085,7 +6082,7 @@ registry.ImageTools = ImageHandlerOption.extend({ const imageCropWrapperElement = document.createElement('div'); document.body.append(imageCropWrapperElement); const imageCropWrapper = await attachComponent(this, imageCropWrapperElement, ImageCrop, { - rpc: this._rpc.bind(this), + rpc: this.rpc, activeOnStart: true, media: img, mimetype: this._getImageMimetype(img), @@ -8265,6 +8262,11 @@ registry.ContainerWidth = SnippetOptionWidget.extend({ * @todo replace this mechanism with real backend m2o field ? */ registry.many2one = SnippetOptionWidget.extend({ + init() { + this._super(...arguments); + this.orm = this.bindService("orm"); + }, + /** * @override */ @@ -8273,11 +8275,7 @@ registry.many2one = SnippetOptionWidget.extend({ this.fields = ['name', 'display_name']; return Promise.all([ this._super(...arguments), - this._rpc({ - model: oeMany2oneModel, - method: 'read', - args: [[parseInt(oeMany2oneId)], this.fields], - }).then(([initialRecord]) => { + this.orm.read(oeMany2oneModel, [parseInt(oeMany2oneId)], this.fields).then(([initialRecord]) => { this.initialRecord = initialRecord; }), ]); @@ -8344,11 +8342,7 @@ registry.many2one = SnippetOptionWidget.extend({ many2oneWidget.dataset.changeRecord = ''; const model = this.$target[0].dataset.oeMany2oneModel; - const [{name: modelName}] = await this._rpc({ - model: 'ir.model', - method: 'search_read', - args: [[['model', '=', model]], ['name']], - }); + const [{name: modelName}] = await this.orm.searchRead("ir.model", [['model', '=', model]], ['name']); many2oneWidget.setAttribute('String', modelName); many2oneWidget.dataset.model = model; many2oneWidget.dataset.fields = JSON.stringify(this.fields); @@ -8377,12 +8371,12 @@ registry.many2one = SnippetOptionWidget.extend({ .attr('data-oe-many2one-id', contactId).data('oe-many2one-id', contactId) .map(async (i, node) => { if (node.dataset.oeType === 'contact') { - const html = await this._rpc({ - model: 'ir.qweb.field.contact', - method: 'get_record_to_html', - args: [[contactId]], - kwargs: {options: JSON.parse(node.dataset.oeContactOptions)}, - }); + const html = await this.orm.call( + "ir.qweb.field.contact", + "get_record_to_html", + [[contactId]], + {options: JSON.parse(node.dataset.oeContactOptions)} + ); $(node).html(html); } else { node.textContent = defaultText; @@ -8455,9 +8449,10 @@ registry.SnippetSave = SnippetOptionWidget.extend({ // current widget has been destroyed and is orphaned, so this._rpc // will not work as it can't trigger_up. For this reason, we need // to bypass the service provider and use the global RPC directly - await rpc.query({ - model: 'ir.ui.view', - method: 'save_snippet', + await jsonrpc(`/web/dataset/call_kw/ir.ui.view/save_snippet`, { + model: "ir.ui.view", + method: "save_snippet", + args: [], kwargs: { 'name': defaultSnippetName, 'arch': targetCopyEl.outerHTML, @@ -8653,6 +8648,7 @@ registry.SelectTemplate = SnippetOptionWidget.extend({ this._super(...arguments); this.containerSelector = ''; this.selectTemplateWidgetName = ''; + this.orm = this.bindService("orm"); }, /** * @constructor @@ -8725,14 +8721,12 @@ registry.SelectTemplate = SnippetOptionWidget.extend({ */ async _getTemplate(xmlid) { if (!this._templates[xmlid]) { - this._templates[xmlid] = await this._rpc({ - model: 'ir.ui.view', - method: 'render_public_asset', - args: [`${xmlid}`, {}], - kwargs: { - context: this.options.context, - }, - }); + this._templates[xmlid] = await this.orm.call( + "ir.ui.view", + "render_public_asset", + [`${xmlid}`, {}], + { context: this.options.context } + ); } return this._templates[xmlid]; }, diff --git a/addons/web_editor/static/src/js/wysiwyg/wysiwyg.js b/addons/web_editor/static/src/js/wysiwyg/wysiwyg.js index 494bb5ae085..d43dc022a29 100644 --- a/addons/web_editor/static/src/js/wysiwyg/wysiwyg.js +++ b/addons/web_editor/static/src/js/wysiwyg/wysiwyg.js @@ -3058,31 +3058,9 @@ export class Wysiwyg extends Component { } } _callService(payload) { - let args = payload.args || []; - if (payload.service === 'ajax' && payload.method === 'rpc') { - // ajax service uses an extra 'target' argument for rpc - const [route, params, settings = {}] = args; - if (params && params.kwargs) { - params.kwargs.context = { - ...this.env.services.user.context, - ...params.kwargs.context, - }; - } - const result = this._serviceRpc(route, params, settings); - payload.callback(result); - return; - } const service = this.env.services[payload.service]; - // If the service doesn't exist it means that it was translated to Owl - if (service) { - const result = service[payload.method].apply(service, args); - payload.callback(result); - } else { - throw new Error( - `The service "${payload.service}" is not present in the legacy owl environment. - You should probably create a mapper in @web/legacy/utils` - ); - } + const result = service[payload.method].apply(service, payload.args || []); + payload.callback(result); } _serviceRpc(route, params, settings = {}) { if (params && params.kwargs) { diff --git a/addons/web_unsplash/static/src/js/unsplash_beacon.js b/addons/web_unsplash/static/src/js/unsplash_beacon.js index dc2fef5dcc1..cecdb1960c6 100644 --- a/addons/web_unsplash/static/src/js/unsplash_beacon.js +++ b/addons/web_unsplash/static/src/js/unsplash_beacon.js @@ -6,6 +6,11 @@ publicWidget.registry.UnsplashBeacon = publicWidget.Widget.extend({ // /!\ To adapt the day the beacon makes sense for backend customizations selector: '#wrapwrap', + init() { + this._super(...arguments); + this.rpc = this.bindService("rpc"); + }, + /** * @override */ @@ -15,9 +20,7 @@ publicWidget.registry.UnsplashBeacon = publicWidget.Widget.extend({ return img.src.split('/unsplash/')[1].split('/')[0]; }); if (unsplashImages.length) { - this._rpc({ - route: '/web_unsplash/get_app_id', - }).then(function (appID) { + this.rpc('/web_unsplash/get_app_id').then(function (appID) { if (!appID) { return; } diff --git a/addons/website/static/src/components/ace_editor/ace_editor.js b/addons/website/static/src/components/ace_editor/ace_editor.js index 3e13b257c59..b698ffe9314 100644 --- a/addons/website/static/src/components/ace_editor/ace_editor.js +++ b/addons/website/static/src/components/ace_editor/ace_editor.js @@ -23,6 +23,12 @@ export const WebsiteAceEditor = AceEditor.extend({ // Private //-------------------------------------------------------------------------- + /** + * @override + */ + _getContext() { + return this.options.getContext(); + }, /** * @override */ @@ -38,12 +44,17 @@ export const WebsiteAceEditor = AceEditor.extend({ // got copy/unlink). const selectedView = Object.values(this.views).find(view => view.id === this._getSelectedResource()); const context = this.options.getContext(); - defs.push(this._rpc({ - model: 'ir.ui.view', - method: 'search_read', - fields: ['id'], - domain: [['key', '=', selectedView.key], ['website_id', '=', context.website_id]], - })); + defs.push( + this.orm.searchRead( + "ir.ui.view", + [ + ["key", "=", selectedView.key], + ["website_id", "=", context.website_id], + ], + ["id"], + { context: this.options.getContext() } + ) + ); } return Promise.all(defs).then((async () => { await this._updateEditor(); @@ -88,12 +99,6 @@ export const WebsiteAceEditor = AceEditor.extend({ return this._displayResource(this._getSelectedResource()); } }, - /** - * @override - */ - _rpc(options) { - return this._super({ ...options, context: this.options.getContext() }); - }, }); export class AceEditorAdapterComponent extends Component { diff --git a/addons/website/static/src/components/dialog/edit_menu.js b/addons/website/static/src/components/dialog/edit_menu.js index e80fdf10880..faac6e278cd 100644 --- a/addons/website/static/src/components/dialog/edit_menu.js +++ b/addons/website/static/src/components/dialog/edit_menu.js @@ -44,21 +44,16 @@ export class MenuDialog extends Component { useEffect(() => { const $input = $(this.urlInputRef.el); - // wUtils.autocompleteWithPages rely on a widget that has a _rpc and - // trigger_up method. - const fakeWidget = { - _rpc: ({ route, params }) => this.rpc(route, params), - trigger_up: () => { - this.url.input.value = this.urlInputRef.el.value; - }, - }; const options = { body: this.website.pageDocument.body, classes: { 'ui-autocomplete': 'o_edit_menu_autocomplete' }, + urlChosen: () => { + this.url.input.value = this.urlInputRef.el.value; + }, }; - wUtils.autocompleteWithPages(fakeWidget, $input, options); + wUtils.autocompleteWithPages(this.rpc.bind(this), $input, options); return () => $input.urlcomplete('destroy'); }, () => []); } diff --git a/addons/website/static/src/components/wysiwyg_adapter/wysiwyg_adapter.js b/addons/website/static/src/components/wysiwyg_adapter/wysiwyg_adapter.js index 1d24a369252..69696619cb5 100644 --- a/addons/website/static/src/components/wysiwyg_adapter/wysiwyg_adapter.js +++ b/addons/website/static/src/components/wysiwyg_adapter/wysiwyg_adapter.js @@ -787,22 +787,9 @@ export class WysiwygAdapterComponent extends Wysiwyg { if (evType in triggers) { triggers[evType](ev); } else if (evType === 'call_service') { - let args = payload.args || []; - if (payload.service === 'ajax' && payload.method === 'rpc') { - // ajax service uses an extra 'target' argument for rpc - args = args.concat(ev.target); - } const service = legacyEnv.services[payload.service]; - //If the service doesn't exist it means that it was translated to Owl - if (service) { - const result = service[payload.method].apply(service, args); - payload.callback(result); - } else { - throw new Error( - `The service "${payload.service}" is not present in the legacy owl environment. - You should probably create a mapper in @web/legacy/utils` - ); - } + const result = service[payload.method].apply(service, payload.args || []); + payload.callback(result); } else { super._trigger_up(...arguments); } diff --git a/addons/website/static/src/js/content/website_root.js b/addons/website/static/src/js/content/website_root.js index df4e33944a9..f8f79d41020 100644 --- a/addons/website/static/src/js/content/website_root.js +++ b/addons/website/static/src/js/content/website_root.js @@ -28,6 +28,7 @@ export const WebsiteRoot = publicRootData.PublicRoot.extend({ */ init() { this.isFullscreen = false; + this.rpc = this.bindService("rpc"); return this._super(...arguments); }, /** @@ -71,9 +72,7 @@ export const WebsiteRoot = publicRootData.PublicRoot.extend({ async _getGMapAPIKey(refetch) { if (refetch || !this._gmapAPIKeyProm) { this._gmapAPIKeyProm = new Promise(async resolve => { - const data = await this._rpc({ - route: '/website/google_maps_api_key', - }); + const data = await this.rpc('/website/google_maps_api_key'); resolve(JSON.parse(data).google_maps_api_key || ''); }); } @@ -227,12 +226,9 @@ export const WebsiteRoot = publicRootData.PublicRoot.extend({ } var $data = $(ev.currentTarget).parents(".js_publish_management:first"); - this._rpc({ - route: $data.data('controller') || '/website/publish', - params: { - id: +$data.data('id'), - object: $data.data('object'), - }, + this.rpc($data.data('controller') || '/website/publish', { + id: +$data.data('id'), + object: $data.data('object'), }) .then(function (result) { $data.toggleClass("css_published", result).toggleClass("css_unpublished", !result); diff --git a/addons/website/static/src/js/editor/editor.js b/addons/website/static/src/js/editor/editor.js index 9e6834d8817..37608087fdf 100644 --- a/addons/website/static/src/js/editor/editor.js +++ b/addons/website/static/src/js/editor/editor.js @@ -20,13 +20,7 @@ patch(LinkDialog.prototype, { body: this.$link && this.$link[0].ownerDocument.body, }; const result = await super.start(...arguments); - // wUtils.autocompleteWithPages rely on a widget that has a _rpc and - // trigger_up method. - const fakeWidget = { - _rpc: ({ route, params }) => this.rpc(route, params), - trigger_up: () => {}, - }; - wUtils.autocompleteWithPages(fakeWidget, this.$el.find('input[name="url"]'), options); + wUtils.autocompleteWithPages(this.rpc.bind(this), this.$el.find('input[name="url"]'), options); return result; }, }); diff --git a/addons/website/static/src/js/editor/snippets.editor.js b/addons/website/static/src/js/editor/snippets.editor.js index b0d668cd3bd..10717d05656 100644 --- a/addons/website/static/src/js/editor/snippets.editor.js +++ b/addons/website/static/src/js/editor/snippets.editor.js @@ -144,14 +144,7 @@ const wSnippetMenu = weSnippetEditor.SnippetsMenu.extend({ $button.prop('disabled', true); const res = await this._validateGMapAPIKey(valueAPIKey); if (res.isValid) { - await this._rpc({ - model: 'website', - method: 'write', - args: [ - [websiteId], - {google_maps_api_key: valueAPIKey}, - ], - }); + await this.orm.write("website", [websiteId], { google_maps_api_key: valueAPIKey }); invalidated = true; dialog.close(); } else { diff --git a/addons/website/static/src/js/editor/snippets.options.js b/addons/website/static/src/js/editor/snippets.options.js index 35ac1079920..3a398417b45 100644 --- a/addons/website/static/src/js/editor/snippets.options.js +++ b/addons/website/static/src/js/editor/snippets.options.js @@ -47,6 +47,11 @@ options.UserValueWidget.include({ }); Many2oneUserValueWidget.include({ + init() { + this._super(...arguments); + this.fields = this.bindService("field"); + }, + /** * @override */ @@ -54,10 +59,8 @@ Many2oneUserValueWidget.include({ // Add the current website's domain if the model has a website_id field. // Note that the `_rpc` method is cached in Many2X user value widget, // see `_rpcCache`. - const websiteIdField = await this._rpc({ - model: this.options.model, - method: "fields_get", - args: [["website_id"]], + const websiteIdField = await this.fields.loadFields(this.options.model, { + fieldNames: ["website_id"], }); const modelHasWebsiteId = !!websiteIdField["website_id"]; if (modelHasWebsiteId && !this.options.domain.find(arr => arr[0] === "website_id")) { @@ -69,13 +72,15 @@ Many2oneUserValueWidget.include({ }); const UrlPickerUserValueWidget = InputUserValueWidget.extend({ - custom_events: Object.assign({}, InputUserValueWidget.prototype.custom_events || {}, { - 'website_url_chosen': '_onWebsiteURLChosen', - }), events: Object.assign({}, InputUserValueWidget.prototype.events || {}, { 'click .o_we_redirect_to': '_onRedirectTo', }), + init() { + this._super(...arguments); + this.rpc = this.bindService("rpc"); + }, + /** * @override */ @@ -98,8 +103,9 @@ const UrlPickerUserValueWidget = InputUserValueWidget.extend({ "ui-autocomplete": 'o_website_ui_autocomplete' }, body: this.getParent().$target[0].ownerDocument.body, + urlChosen: this._onWebsiteURLChosen.bind(this), }; - wUtils.autocompleteWithPages(this, $(this.inputEl), options); + wUtils.autocompleteWithPages(this.rpc.bind(this), $(this.inputEl), options); }, //-------------------------------------------------------------------------- @@ -627,6 +633,9 @@ options.Class.include({ // triggers a custom event, only that same jQuery instance will // trigger handlers set with `.on`. this.$bsTarget = this.ownerDocument.defaultView.$(this.$target[0]); + + this.rpc = this.bindService("rpc"); + this.orm = this.bindService("orm"); }, //-------------------------------------------------------------------------- @@ -831,14 +840,11 @@ options.Class.include({ const disableDataKeys = allDataKeys.filter(value => !enableDataKeys.includes(value)); const resetViewArch = !!params.resetViewArch; - return this._rpc({ - route: '/website/theme_customize_data', - params: { - 'is_view_data': isViewData, - 'enable': enableDataKeys, - 'disable': disableDataKeys, - 'reset_view_arch': resetViewArch, - }, + return this.rpc('/website/theme_customize_data', { + 'is_view_data': isViewData, + 'enable': enableDataKeys, + 'disable': disableDataKeys, + 'reset_view_arch': resetViewArch, }); }, /** @@ -859,12 +865,9 @@ options.Class.include({ */ async _getEnabledCustomizeValues(possibleValues, isViewData) { const allDataKeys = this._getDataKeysFromPossibleValues(possibleValues); - const enabledValues = await this._rpc({ - route: '/website/theme_customize_data_get', - params: { - 'keys': allDataKeys, - 'is_view_data': isViewData, - }, + const enabledValues = await this.rpc('/website/theme_customize_data_get', { + 'keys': allDataKeys, + 'is_view_data': isViewData, }); let mostValuesStr = ''; let mostValuesNb = 0; @@ -885,11 +888,7 @@ options.Class.include({ Object.keys(values).forEach((key) => { values[key] = values[key] || defaultValue; }); - return this._rpc({ - model: 'web_editor.assets', - method: 'make_scss_customization', - args: [url, values], - }); + return this.orm.call("web_editor.assets", "make_scss_customization", [url, values]); }, /** * Refreshes all public widgets related to the given element. @@ -1206,6 +1205,7 @@ options.registry.OptionsTab = options.Class.extend({ this._super(...arguments); this.grayParams = {}; this.grays = {}; + this.orm = this.bindService("orm"); }, //-------------------------------------------------------------------------- @@ -1369,11 +1369,11 @@ options.registry.OptionsTab = options.Class.extend({ }); let website; - const dataProm = this._rpc({ - model: 'website', - method: 'read', - args: [[websiteId], ['custom_code_head', 'custom_code_footer']], - }).then(websites => { + const dataProm = this.orm.read( + "website", + [websiteId], + ['custom_code_head', 'custom_code_footer'] + ).then(websites => { website = websites[0]; }); @@ -1403,13 +1403,8 @@ options.registry.OptionsTab = options.Class.extend({ text: _t("Save"), classes: 'btn-primary', click: async () => { - await this._rpc({ - model: 'website', - method: 'write', - args: [ - [websiteId], - {[fieldName]: aceEditor.getValue()}, - ], + await this.orm.write("website", [websiteId], { + [fieldName]: aceEditor.getValue(), }); }, close: true, @@ -1657,6 +1652,12 @@ options.registry.ThemeColors = options.registry.OptionsTab.extend({ }); options.registry.menu_data = options.Class.extend({ + init() { + this._super(...arguments); + this.user = this.bindService("user"); + this.orm = this.bindService("orm"); + }, + /** * When the users selects a menu, a popover is shown with 4 possible * actions: follow the link in a new tab, copy the menu link, edit the menu, @@ -1674,11 +1675,7 @@ options.registry.menu_data = options.Class.extend({ wysiwyg, container: popoverContainer, notify: this.displayNotification.bind(this), - checkIsWebsiteDesigner: () => this._rpc({ - 'model': 'res.users', - 'method': 'has_group', - 'args': ['website.group_website_designer'], - }), + checkIsWebsiteDesigner: () => this.user.hasGroup("website.group_website_designer"), onEditLinkClick: (widget) => { var $menu = widget.$target.find('[data-oe-id]'); this.trigger_up('menu_dialog', { @@ -1694,11 +1691,11 @@ options.registry.menu_data = options.Class.extend({ name, url, }; - return this._rpc({ - model: 'website.menu', - method: 'save', - args: [websiteId, {'data': [data]}], - }).then(function () { + return this.orm.call( + "website.menu", + "save", + [websiteId, {'data': [data]}] + ).then(function () { widget.wysiwyg.odooEditor.observerUnactive(); widget.$target.attr('href', url); $menu.text(name); @@ -1731,6 +1728,12 @@ options.registry.menu_data = options.Class.extend({ }); options.registry.company_data = options.Class.extend({ + init() { + this._super(...arguments); + this.rpc = this.bindService("rpc"); + this.orm = this.bindService("orm"); + }, + /** * Fetches data to determine the URL where the user can edit its company * data. Saves the info in the prototype to do this only once. @@ -1742,12 +1745,8 @@ options.registry.company_data = options.Class.extend({ var prom; var self = this; if (proto.__link === undefined) { - prom = this._rpc({route: '/web/session/get_session_info'}).then(function (session) { - return self._rpc({ - model: 'res.users', - method: 'read', - args: [session.uid, ['company_id']], - }); + prom = this.rpc('/web/session/get_session_info').then(function (session) { + return self.orm.read("res.users", [session.uid], ["company_id"]); }).then(function (res) { proto.__link = '/web#action=base.action_res_company_form&view_type=form&id=' + encodeURIComponent(res && res[0] && res[0].company_id[0] || 1); }); diff --git a/addons/website/static/src/js/editor/widget_link.js b/addons/website/static/src/js/editor/widget_link.js index 98711705178..201b1642a67 100644 --- a/addons/website/static/src/js/editor/widget_link.js +++ b/addons/website/static/src/js/editor/widget_link.js @@ -38,15 +38,10 @@ patch(LinkTools.prototype, { "ui-autocomplete": 'o_website_ui_autocomplete' }, body: this.$editable[0].ownerDocument.body, - }; - // wUtils.autocompleteWithPages rely on a widget that has a _rpc and - // trigger_up method. - const fakeWidget = { - _rpc: ({ route, params }) => this.rpc(route, params), - trigger_up: this._onAutocompleteClose.bind(this), + urlChosen: this._onAutocompleteClose.bind(this), isDestroyed: () => status(this) === 'destroyed', }; - wUtils.autocompleteWithPages(fakeWidget, this.$el.find('input[name="url"]'), options); + wUtils.autocompleteWithPages(this.rpc.bind(this), this.$el.find('input[name="url"]'), options); this._adaptPageAnchor(); return def; }, diff --git a/addons/website/static/src/js/utils.js b/addons/website/static/src/js/utils.js index 87dbc6f2b7a..3d1eb03bef4 100644 --- a/addons/website/static/src/js/utils.js +++ b/addons/website/static/src/js/utils.js @@ -45,10 +45,10 @@ function loadAnchors(url, body) { /** * Allows the given input to propose existing website URLs. * - * @param {ServicesMixin|Widget} self - an element capable to trigger an RPC + * @param {Function} rpc * @param {jQuery} $input */ -function autocompleteWithPages(self, $input, options) { +function autocompleteWithPages(rpc, $input, options) { $.widget("website.urlcomplete", $.ui.autocomplete, { options: options || {}, _create: function () { @@ -88,15 +88,12 @@ function autocompleteWithPages(self, $input, options) { // avoid useless call to /website/get_suggested_links response(); } else { - if (self.isDestroyed?.()) { - return ; + if (options.isDestroyed?.()) { + return; } - return self._rpc({ - route: '/website/get_suggested_links', - params: { - needle: request.term, - limit: 15, - } + return rpc('/website/get_suggested_links', { + needle: request.term, + limit: 15, }).then(function (res) { let choices = res.matching_pages; res.others.forEach(other => { @@ -115,7 +112,7 @@ function autocompleteWithPages(self, $input, options) { // choose url in dropdown with arrow change ev.target.value without trigger_up // so cannot check here if value has been updated ev.target.value = ui.item.value; - self.trigger_up('website_url_chosen'); + options?.urlChosen(); ev.preventDefault(); }, }); diff --git a/addons/website/static/src/snippets/s_dynamic_snippet/000.js b/addons/website/static/src/snippets/s_dynamic_snippet/000.js index c93cd409287..4154ec6b9d3 100644 --- a/addons/website/static/src/snippets/s_dynamic_snippet/000.js +++ b/addons/website/static/src/snippets/s_dynamic_snippet/000.js @@ -35,6 +35,8 @@ const DynamicSnippet = publicWidget.Widget.extend({ this.isDesplayedAsMobile = uiUtils.isSmall(); this.unique_id = uniqueId("s_dynamic_snippet_"); this.template_key = 'website.s_dynamic_snippet.grid'; + + this.rpc = this.bindService("rpc"); }, /** * @@ -117,16 +119,16 @@ const DynamicSnippet = publicWidget.Widget.extend({ async _fetchData() { if (this._isConfigComplete()) { const nodeData = this.el.dataset; - const filterFragments = await this._rpc({ - 'route': '/website/snippet/filters', - 'params': Object.assign({ + const filterFragments = await this.rpc( + '/website/snippet/filters', + Object.assign({ 'filter_id': parseInt(nodeData.filterId), 'template_key': nodeData.templateKey, 'limit': parseInt(nodeData.numberOfRecords), 'search_domain': this._getSearchDomain(), 'with_sample': this.editableMode, - }, this._getRpcParameters()), - }); + }, this._getRpcParameters()) + ); this.data = filterFragments.map(markup); } else { this.data = []; diff --git a/addons/website/static/src/snippets/s_dynamic_snippet/options.js b/addons/website/static/src/snippets/s_dynamic_snippet/options.js index 24951d3e39e..2d24c87912b 100644 --- a/addons/website/static/src/snippets/s_dynamic_snippet/options.js +++ b/addons/website/static/src/snippets/s_dynamic_snippet/options.js @@ -33,6 +33,8 @@ const dynamicSnippetOptions = options.Class.extend({ this.dynamicFilterTemplates = {}; // Indicates that some current options are a default selection. this.isOptionDefault = {}; + + this.rpc = this.bindService("rpc"); }, /** * @override @@ -149,10 +151,10 @@ const dynamicSnippetOptions = options.Class.extend({ * @returns {Promise} */ async _fetchDynamicFilters() { - const dynamicFilters = await this._rpc({route: '/website/snippet/options_filters', params: { + const dynamicFilters = await this.rpc('/website/snippet/options_filters', { model_name: this.modelNameFilter, search_domain: this.contextualFilterDomain, - }}); + }); if (!dynamicFilters.length) { // Additional modules are needed for dynamic filters to be defined. return; @@ -174,9 +176,9 @@ const dynamicSnippetOptions = options.Class.extend({ if (!filter) { return []; } - const dynamicFilterTemplates = await this._rpc({route: '/website/snippet/filter_templates', params: { + const dynamicFilterTemplates = await this.rpc('/website/snippet/filter_templates', { filter_name: filter.model_name.replaceAll('.', '_'), - }}); + }); for (let index in dynamicFilterTemplates) { this.dynamicFilterTemplates[dynamicFilterTemplates[index].key] = dynamicFilterTemplates[index]; } diff --git a/addons/website/static/src/snippets/s_facebook_page/options.js b/addons/website/static/src/snippets/s_facebook_page/options.js index 600782f1a01..6c0ade9f8df 100644 --- a/addons/website/static/src/snippets/s_facebook_page/options.js +++ b/addons/website/static/src/snippets/s_facebook_page/options.js @@ -4,6 +4,11 @@ import { pick } from "@web/core/utils/objects"; import options from "@web_editor/js/editor/snippets.options"; options.registry.facebookPage = options.Class.extend({ + init() { + this._super(...arguments); + this.orm = this.bindService("orm"); + }, + /** * Initializes the required facebook page data to create the iframe. * @@ -25,10 +30,7 @@ options.registry.facebookPage = options.Class.extend({ if (!this.fbData.href) { // Fetches the default url for facebook page from website config var self = this; - defs.push(this._rpc({ - model: 'website', - method: 'search_read', - args: [[], ['social_facebook']], + defs.push(this.orm.searchRead("website", [], ["social_facebook"], { limit: 1, }).then(function (res) { if (res) { diff --git a/addons/website/static/src/snippets/s_searchbar/000.js b/addons/website/static/src/snippets/s_searchbar/000.js index dbc12e031e9..21d39f46c1e 100644 --- a/addons/website/static/src/snippets/s_searchbar/000.js +++ b/addons/website/static/src/snippets/s_searchbar/000.js @@ -28,6 +28,8 @@ publicWidget.registry.searchBar = publicWidget.Widget.extend({ this._onInput = debounce(this._onInput, 400); this._onFocusOut = debounce(this._onFocusOut, 100); + + this.rpc = this.bindService("rpc"); }, /** * @override @@ -111,16 +113,13 @@ publicWidget.registry.searchBar = publicWidget.Widget.extend({ * @private */ async _fetch() { - const res = await this._rpc({ - route: '/website/snippet/autocomplete', - params: { - 'search_type': this.searchType, - 'term': this.$input.val(), - 'order': this.order, - 'limit': this.limit, - 'max_nb_chars': Math.round(Math.max(this.autocompleteMinWidth, parseInt(this.$el.width())) * 0.22), - 'options': this.options, - }, + const res = await this.rpc('/website/snippet/autocomplete', { + 'search_type': this.searchType, + 'term': this.$input.val(), + 'order': this.order, + 'limit': this.limit, + 'max_nb_chars': Math.round(Math.max(this.autocompleteMinWidth, parseInt(this.$el.width())) * 0.22), + 'options': this.options, }); const fieldNames = this._getFieldsNames(); res.results.forEach(record => { diff --git a/addons/website/static/src/snippets/s_social_media/options.js b/addons/website/static/src/snippets/s_social_media/options.js index 99e4b137b24..63e4b07f6b7 100644 --- a/addons/website/static/src/snippets/s_social_media/options.js +++ b/addons/website/static/src/snippets/s_social_media/options.js @@ -13,6 +13,11 @@ const clearDbSocialValuesCache = () => { }; options.registry.SocialMedia = options.Class.extend({ + init() { + this._super(...arguments); + this.orm = this.bindService("orm"); + }, + /** * @override */ @@ -55,11 +60,7 @@ options.registry.SocialMedia = options.Class.extend({ websiteId = ctx['website_id']; }, }); - await this._rpc({ - model: 'website', - method: 'write', - args: [[websiteId], dbSocialValues], - }); + await this.orm.write("website", [websiteId], dbSocialValues); }, /** * @override @@ -251,12 +252,15 @@ options.registry.SocialMedia = options.Class.extend({ }, }); // Fetch URLs for db links. - dbSocialValuesProm = this._rpc({ - model: 'website', - method: 'read', - args: [websiteId, ['social_facebook', 'social_twitter', 'social_linkedin', - 'social_youtube', 'social_instagram', 'social_github', 'social_tiktok']], - }).then(function (values) { + dbSocialValuesProm = this.orm.read("website", [websiteId], [ + "social_facebook", + "social_twitter", + "social_linkedin", + "social_youtube", + "social_instagram", + "social_github", + "social_tiktok", + ]).then(function (values) { [dbSocialValues] = values; delete dbSocialValues.id; }); diff --git a/addons/website/static/src/snippets/s_website_form/000.js b/addons/website/static/src/snippets/s_website_form/000.js index 877b069bc92..cb151cefe44 100644 --- a/addons/website/static/src/snippets/s_website_form/000.js +++ b/addons/website/static/src/snippets/s_website_form/000.js @@ -2,13 +2,13 @@ import {ReCaptcha} from "@google_recaptcha/js/recaptcha"; import { session } from "@web/session"; - import ajax from "@web/legacy/js/core/ajax"; import publicWidget from "@web/legacy/js/public/public_widget"; import dom from "@web/legacy/js/core/dom"; import { delay } from "@web/core/utils/concurrency"; import { debounce } from "@web/core/utils/timing"; import { _t } from "@web/core/l10n/translation"; import { renderToElement } from "@web/core/utils/render"; + import { post } from "@web/core/network/http_service"; import { formatDate, formatDateTime, @@ -62,6 +62,7 @@ const { DateTime } = luxon; this._visibilityFunctionByFieldName = new Map(); this._visibilityFunctionByFieldEl = new Map(); this.__started = new Promise(resolve => this.__startResolve = resolve); + this.orm = this.bindService("orm"); }, willStart: async function () { const res = this._super(...arguments); @@ -72,11 +73,11 @@ const { DateTime } = luxon; // fetch user data (required by fill-with behavior) this.preFillValues = {}; if (session.user_id) { - this.preFillValues = (await this._rpc({ - model: 'res.users', - method: 'read', - args: [session.user_id, this._getUserPreFillFields()], - }))[0] || {}; + this.preFillValues = (await this.orm.read( + "res.users", + [session.user_id], + this._getUserPreFillFields() + ))[0] || {}; } return res; }, @@ -351,14 +352,17 @@ const { DateTime } = luxon; } } + if (odoo.csrf_token) { + form_values.csrf_token = odoo.csrf_token; + } + // Post form and handle result - ajax.post(this.$el.attr('action') + (this.$el.data('force_action') || this.$el.data('model_name')), form_values) + post(this.$el.attr('action') + (this.$el.data('force_action') || this.$el.data('model_name')), form_values) .then(async function (result_data) { // Restore send button behavior self.$el.find('.s_website_form_send, .o_website_form_send') .removeAttr('disabled') .removeClass('disabled'); // !compatibility - result_data = JSON.parse(result_data); if (!result_data.id) { // Failure, the server didn't return the created record ID self.update_status('error', result_data.error ? result_data.error : false); diff --git a/addons/website/static/src/snippets/s_website_form/options.js b/addons/website/static/src/snippets/s_website_form/options.js index 2af191a778d..252a11767ae 100644 --- a/addons/website/static/src/snippets/s_website_form/options.js +++ b/addons/website/static/src/snippets/s_website_form/options.js @@ -39,6 +39,11 @@ function _getDomain(formEl, name, type, relation) { } const FormEditor = options.Class.extend({ + init() { + this._super(...arguments); + this.orm = this.bindService("orm"); + }, + //---------------------------------------------------------------------- // Private //---------------------------------------------------------------------- @@ -66,14 +71,7 @@ const FormEditor = options.Class.extend({ display_name: el[1], })); } else if (field.relation && field.relation !== 'ir.attachment') { - field.records = await this._rpc({ - model: field.relation, - method: 'search_read', - args: [ - field.domain, - ['display_name'] - ], - }); + field.records = await this.orm.searchRead(field.relation, field.domain, ["display_name"]); } return field.records; }, @@ -376,10 +374,7 @@ options.registry.WebsiteFormEditor = FormEditor.extend({ } // Get list of website_form compatible models. - this.models = await this._rpc({ - model: 'ir.model', - method: 'get_compatible_form_models', - }); + this.models = await this.orm.call("ir.model", "get_compatible_form_models"); const targetModelName = this.$target[0].dataset.model_name || 'mail.mail'; this.activeForm = this.models.find(m => m.model === targetModelName); @@ -430,11 +425,7 @@ options.registry.WebsiteFormEditor = FormEditor.extend({ if (fields.length) { // ideally we'd only do this if saving the form // succeeds... but no idea how to do that - this._rpc({ - model: 'ir.model.fields', - method: 'formbuilder_whitelist', - args: [model, unique(fields)], - }); + this.orm.call("ir.model.fields", "formbuilder_whitelist", [model, unique(fields)]); } } if (this.$message.length) { @@ -887,11 +878,7 @@ options.registry.WebsiteFieldEditor = FieldEditor.extend({ if (model in authorizedFieldsCache) { getFields = authorizedFieldsCache[model]; } else { - getFields = this._rpc({ - model: "ir.model", - method: "get_authorized_fields", - args: [model], - }); + getFields = this.orm.call("ir.model", "get_authorized_fields", [model]); authorizedFieldsCache[model] = getFields; } diff --git a/addons/website/static/tests/tours/website_form_editor_frontend.js b/addons/website/static/tests/tours/website_form_editor_frontend.js index 2771649595f..96fa22cd1f5 100644 --- a/addons/website/static/tests/tours/website_form_editor_frontend.js +++ b/addons/website/static/tests/tours/website_form_editor_frontend.js @@ -1,6 +1,6 @@ /** @odoo-module **/ +import { jsonrpc } from "@web/core/network/rpc_service"; import { registry } from "@web/core/registry"; -import rpc from '@web/legacy/js/core/rpc'; registry.category("web_tour.tours").add("website_form_editor_tour_submit", { test: true, @@ -165,17 +165,18 @@ registry.category("web_tour.tours").add("website_form_editor_tour_results", { content: "Check mail.mail records have been created", trigger: "body", run: function () { - var mailDef = rpc.query({ - model: 'mail.mail', - method: 'search_count', - args: [[ - ['email_to', '=', 'test@test.test'], - ['body_html', 'like', 'A useless message'], - ['body_html', 'like', 'Service : Development Service'], - ['body_html', 'like', 'State : 44 - UK'], - ['body_html', 'like', 'Products : Xperia,Wiko Stairway'] - ]], - }); + var mailDef = jsonrpc(`/web/dataset/call_kw/mail.mail/search_count`, { + model: "mail.mail", + method: "search_count", + args: [[ + ['email_to', '=', 'test@test.test'], + ['body_html', 'like', 'A useless message'], + ['body_html', 'like', 'Service : Development Service'], + ['body_html', 'like', 'State : 44 - UK'], + ['body_html', 'like', 'Products : Xperia,Wiko Stairway'] + ]], + kwargs: {}, + }); var success = function(model, count) { if (count > 0) { $('body').append('
'); diff --git a/addons/website_blog/static/src/js/options.js b/addons/website_blog/static/src/js/options.js index 0cb7c051900..871b2e58ef4 100644 --- a/addons/website_blog/static/src/js/options.js +++ b/addons/website_blog/static/src/js/options.js @@ -58,6 +58,11 @@ options.registry.CoverProperties.include({ }); options.registry.BlogPostTagSelection = options.Class.extend({ + init() { + this._super(...arguments); + this.orm = this.bindService("orm"); + }, + /** * @override */ @@ -66,11 +71,11 @@ options.registry.BlogPostTagSelection = options.Class.extend({ this.blogPostID = parseInt(this.$target[0].dataset.blogId); this.isEditingTags = false; - const tags = await this._rpc({ - model: 'blog.tag', - method: 'search_read', - args: [[], ['id', 'name', 'display_name', 'post_ids']], - }); + const tags = await this.orm.searchRead( + "blog.tag", + [], + ["id", "name", "display_name", "post_ids"] + ); this.allTagsByID = {}; this.tagIDs = []; for (const tag of tags) { diff --git a/addons/website_blog/static/src/snippets/s_blog_posts/options.js b/addons/website_blog/static/src/snippets/s_blog_posts/options.js index 85a94158521..169e9aacc52 100644 --- a/addons/website_blog/static/src/snippets/s_blog_posts/options.js +++ b/addons/website_blog/static/src/snippets/s_blog_posts/options.js @@ -37,14 +37,7 @@ const dynamicSnippetBlogPostsOptions = dynamicSnippetOptions.extend({ * @returns {Promise} */ _fetchBlogs: function () { - return this._rpc({ - model: 'blog.blog', - method: 'search_read', - kwargs: { - domain: wUtils.websiteDomain(this), - fields: ['id', 'name'], - } - }); + return this.orm.searchRead("blog.blog", wUtils.websiteDomain(this), ["id", "name"]); }, /** * diff --git a/addons/website_crm_partner_assign/static/src/js/crm_partner_assign.js b/addons/website_crm_partner_assign/static/src/js/crm_partner_assign.js index 2353b81fca7..55251d906e8 100644 --- a/addons/website_crm_partner_assign/static/src/js/crm_partner_assign.js +++ b/addons/website_crm_partner_assign/static/src/js/crm_partner_assign.js @@ -19,6 +19,11 @@ publicWidget.registry.crmPartnerAssign = publicWidget.Widget.extend({ 'change #new-opp-dialog .contact_name': '_onChangeContactName', }, + init() { + this._super(...arguments); + this.orm = this.bindService("orm"); + }, + //-------------------------------------------------------------------------- // Private //-------------------------------------------------------------------------- @@ -41,14 +46,10 @@ publicWidget.registry.crmPartnerAssign = publicWidget.Widget.extend({ * @returns {Promise} */ _confirmInterestedPartner: function () { - return this._rpc({ - model: 'crm.lead', - method: 'partner_interested', - args: [ - [parseInt($('.interested_partner_assign_form .assign_lead_id').val())], - $('.interested_partner_assign_form .comment_interested').val() - ], - }).then(function () { + return this.orm.call("crm.lead", "partner_interested", [ + [parseInt($('.interested_partner_assign_form .assign_lead_id').val())], + $('.interested_partner_assign_form .comment_interested').val() + ]).then(function () { window.location.href = '/my/leads'; }); }, @@ -57,16 +58,12 @@ publicWidget.registry.crmPartnerAssign = publicWidget.Widget.extend({ * @returns {Promise} */ _confirmDesinterestedPartner: function () { - return this._rpc({ - model: 'crm.lead', - method: 'partner_desinterested', - args: [ - [parseInt($('.desinterested_partner_assign_form .assign_lead_id').val())], - $('.desinterested_partner_assign_form .comment_desinterested').val(), - $('.desinterested_partner_assign_form .contacted_desinterested').prop('checked'), - $('.desinterested_partner_assign_form .customer_mark_spam').prop('checked'), - ], - }).then(function () { + return this.orm.call("crm.lead", "partner_desinterested", [ + [parseInt($('.desinterested_partner_assign_form .assign_lead_id').val())], + $('.desinterested_partner_assign_form .comment_desinterested').val(), + $('.desinterested_partner_assign_form .contacted_desinterested').prop('checked'), + $('.desinterested_partner_assign_form .customer_mark_spam').prop('checked'), + ]).then(function () { window.location.href = '/my/leads'; }); }, @@ -76,12 +73,7 @@ publicWidget.registry.crmPartnerAssign = publicWidget.Widget.extend({ * @returns {Promise} */ _changeOppStage: function (leadID, stageID) { - return this._rpc({ - model: 'crm.lead', - method: 'write', - args: [[leadID], { - stage_id: stageID, - }], + return this.orm.write("crm.lead", [leadID], { stage_id: stageID }, { context: Object.assign({website_partner_assign: 1}), }).then(function () { window.location.reload(); @@ -92,10 +84,9 @@ publicWidget.registry.crmPartnerAssign = publicWidget.Widget.extend({ * @returns {Promise} */ _editContact: function () { - return this._rpc({ - model: 'crm.lead', - method: 'update_contact_details_from_portal', - args: [[parseInt($('.edit_contact_form .opportunity_id').val())], { + return this.orm.call("crm.lead", "update_contact_details_from_portal", [ + [parseInt($('.edit_contact_form .opportunity_id').val())], + { partner_name: $('.edit_contact_form .partner_name').val(), phone: $('.edit_contact_form .phone').val(), mobile: $('.edit_contact_form .mobile').val(), @@ -106,8 +97,8 @@ publicWidget.registry.crmPartnerAssign = publicWidget.Widget.extend({ zip: $('.edit_contact_form .zip').val(), state_id: parseInt($('.edit_contact_form .state_id').find(':selected').attr('value')), country_id: parseInt($('.edit_contact_form .country_id').find(':selected').attr('value')), - }], - }).then(function () { + }, + ]).then(function () { window.location.reload(); }); }, @@ -116,15 +107,11 @@ publicWidget.registry.crmPartnerAssign = publicWidget.Widget.extend({ * @returns {Promise} */ _createOpportunity: function () { - return this._rpc({ - model: 'crm.lead', - method: 'create_opp_portal', - args: [{ - contact_name: $('.new_opp_form .contact_name').val(), - title: $('.new_opp_form .title').val(), - description: $('.new_opp_form .description').val(), - }], - }).then(function (response) { + return this.orm.call("crm.lead", "create_opp_portal", [{ + contact_name: $('.new_opp_form .contact_name').val(), + title: $('.new_opp_form .title').val(), + description: $('.new_opp_form .description').val(), + }]).then(function (response) { if (response.errors) { $('#new-opp-dialog .alert').remove(); $('#new-opp-dialog div:first').prepend('
' + response.errors + '
'); @@ -139,10 +126,9 @@ publicWidget.registry.crmPartnerAssign = publicWidget.Widget.extend({ * @returns {Promise} */ _editOpportunity: function () { - return this._rpc({ - model: 'crm.lead', - method: 'update_lead_portal', - args: [[parseInt($('.edit_opp_form .opportunity_id').val())], { + return this.orm.call("crm.lead", "update_lead_portal", [ + [parseInt($('.edit_opp_form .opportunity_id').val())], + { date_deadline: this._parse_date($('.edit_opp_form .date_deadline').val()), expected_revenue: parseFloat($('.edit_opp_form .expected_revenue').val()), probability: parseFloat($('.edit_opp_form .probability').val()), @@ -150,8 +136,8 @@ publicWidget.registry.crmPartnerAssign = publicWidget.Widget.extend({ activity_summary: $('.edit_opp_form .activity_summary').val(), activity_date_deadline: this._parse_date($('.edit_opp_form .activity_date_deadline').val()), priority: $('input[name="PriorityRadioOptions"]:checked').val(), - }], - }).then(function () { + }, + ]).then(function () { window.location.reload(); }); }, diff --git a/addons/website_event/static/src/js/website_event.js b/addons/website_event/static/src/js/website_event.js index 754b82fc2cd..dfea191294b 100644 --- a/addons/website_event/static/src/js/website_event.js +++ b/addons/website_event/static/src/js/website_event.js @@ -1,7 +1,7 @@ /** @odoo-module **/ -import ajax from "@web/legacy/js/core/ajax"; import publicWidget from "@web/legacy/js/public/public_widget"; +import { jsonrpc } from "@web/core/network/rpc_service"; // Catch registration form event, because of JS for attendee details var EventRegistrationForm = publicWidget.Widget.extend({ @@ -47,7 +47,7 @@ var EventRegistrationForm = publicWidget.Widget.extend({ var $button = $(ev.currentTarget).closest('[type="submit"]'); const post = this._getPost(); $button.attr('disabled', true); - return ajax.jsonRpc($form.attr('action'), 'call', post).then(function (modal) { + return jsonrpc($form.attr('action'), post).then(function (modal) { var $modal = $(modal); $modal.find('.modal-body > div').removeClass('container'); // retrocompatibility - REMOVE ME in master / saas-19 $modal.appendTo(document.body); diff --git a/addons/website_event/static/src/snippets/options.js b/addons/website_event/static/src/snippets/options.js index 6150eb51970..8234fb33d83 100644 --- a/addons/website_event/static/src/snippets/options.js +++ b/addons/website_event/static/src/snippets/options.js @@ -3,6 +3,10 @@ import options from '@web_editor/js/editor/snippets.options'; options.registry.WebsiteEvent = options.Class.extend({ + init() { + this._super(...arguments); + this.orm = this.bindService("orm"); + }, /** * @override @@ -13,14 +17,7 @@ options.registry.WebsiteEvent = options.Class.extend({ this.eventId = this._getEventObjectId(); // Only need for one RPC request as the option will be destroyed if a // change is made. - const rpcData = await this._rpc({ - model: 'event.event', - method: 'read', - args: [ - [this.eventId], - ['website_menu'], - ], - }); + const rpcData = await this.orm.read("event.event", [this.eventId], ["website_menu"]); this.data.reload = this.currentWebsiteUrl; this.websiteMenu = rpcData[0]['website_menu']; return res; @@ -34,11 +31,7 @@ options.registry.WebsiteEvent = options.Class.extend({ * @see this.selectClass for parameters */ displaySubmenu(previewMode, widgetValue, params) { - return this._rpc({ - model: 'event.event', - method: 'toggle_website_menu', - args: [[this.eventId], widgetValue], - }); + return this.orm.call("event.event", "toggle_website_menu", [[this.eventId], widgetValue]); }, //-------------------------------------------------------------------------- diff --git a/addons/website_event_booth/static/src/js/booth_register.js b/addons/website_event_booth/static/src/js/booth_register.js index de7d4ab10cb..7d29e40924c 100644 --- a/addons/website_event_booth/static/src/js/booth_register.js +++ b/addons/website_event_booth/static/src/js/booth_register.js @@ -13,6 +13,11 @@ publicWidget.registry.boothRegistration = publicWidget.Widget.extend({ 'click .o_wbooth_registration_confirm': '_onConfirmRegistrationClick', }, + init() { + this._super(...arguments); + this.rpc = this.bindService("rpc"); + }, + start() { this.eventId = parseInt(this.el.dataset.eventId); this.activeBoothCategoryId = false; @@ -35,11 +40,8 @@ publicWidget.registry.boothRegistration = publicWidget.Widget.extend({ _check_booths_availability(eventBoothIds) { const self = this; - return this._rpc({ - route: "/event/booth/check_availability", - params: { - event_booth_ids: eventBoothIds, - }, + return this.rpc("/event/booth/check_availability", { + event_booth_ids: eventBoothIds, }).then(function (result) { if (result.unavailable_booths.length) { self.$('input[name="event_booth_ids"]').each(function (i, el) { @@ -162,12 +164,9 @@ publicWidget.registry.boothRegistration = publicWidget.Widget.extend({ _fetchBoothsAndUpdateUI() { if (this.boothCache[this.activeBoothCategoryId] === undefined) { var self = this; - this._rpc({ - route: '/event/booth_category/get_available_booths', - params: { - event_id: this.eventId, - booth_category_id: this.activeBoothCategoryId, - }, + this.rpc('/event/booth_category/get_available_booths', { + event_id: this.eventId, + booth_category_id: this.activeBoothCategoryId, }).then(function (result) { self.boothCache[self.activeBoothCategoryId] = result; self._updateUiAfterBoothCategoryChange(); diff --git a/addons/website_event_meet/static/src/js/snippets/options.js b/addons/website_event_meet/static/src/js/snippets/options.js index c74ddd458a7..29e18044806 100644 --- a/addons/website_event_meet/static/src/js/snippets/options.js +++ b/addons/website_event_meet/static/src/js/snippets/options.js @@ -12,12 +12,8 @@ options.registry.WebsiteEvent.include({ * @see this.selectClass for parameters */ allowRoomCreation(previewMode, widgetValue, params) { - this._rpc({ - model: this.modelName, - method: 'write', - args: [[this.eventId], { - meeting_room_allow_creation: widgetValue - }], + this.orm.write(this.modelName, [this.eventId], { + meeting_room_allow_creation: widgetValue, }).then(() => this.trigger_up('request_save', {reload: true, optionSelector: this.data.selector})); }, diff --git a/addons/website_event_meet/static/src/js/website_event_create_meeting_room_button.js b/addons/website_event_meet/static/src/js/website_event_create_meeting_room_button.js index 1546103cd98..abe36df17d3 100644 --- a/addons/website_event_meet/static/src/js/website_event_create_meeting_room_button.js +++ b/addons/website_event_meet/static/src/js/website_event_create_meeting_room_button.js @@ -9,15 +9,18 @@ publicWidget.registry.websiteEventCreateMeetingRoom = publicWidget.Widget.extend 'click': '_onClickCreate', }, + init() { + this._super(...arguments); + this.rpc = this.bindService("rpc"); + }, + //-------------------------------------------------------------------------- // Handlers //-------------------------------------------------------------------------- _onClickCreate: async function () { if (!this.$createModal) { - const langs = await this._rpc({ - route: "/event/active_langs", - }); + const langs = await this.rpc("/event/active_langs"); this.$createModal = $(renderToElement( 'event_meet_create_room_modal', diff --git a/addons/website_event_meet/static/src/js/website_event_meeting_room.js b/addons/website_event_meet/static/src/js/website_event_meeting_room.js index 29e4589d08d..0945d100d0a 100644 --- a/addons/website_event_meet/static/src/js/website_event_meeting_room.js +++ b/addons/website_event_meet/static/src/js/website_event_meeting_room.js @@ -12,6 +12,11 @@ publicWidget.registry.websiteEventMeetingRoom = publicWidget.Widget.extend({ 'click .o_wevent_meeting_room_is_pinned': '_onPinClick', }, + init() { + this._super(...arguments); + this.orm = this.bindService("orm"); + }, + start: function () { this._super.apply(this, arguments); this.csrf_token = odoo.csrf_token; @@ -34,12 +39,12 @@ publicWidget.registry.websiteEventMeetingRoom = publicWidget.Widget.extend({ this.call("dialog", "add", ConfirmationDialog, { body: _t("Are you sure you want to close this room?"), confirm: async () => { - await this._rpc({ - model: "event.meeting.room", - method: "write", - args: [this.meetingRoomId, { is_published: false }], - context: this.context, - }); + await this.orm.write( + "event.meeting.room", + [this.meetingRoomId], + { is_published: false }, + { context: this.context } + ); // remove the element so we do not need to refresh the page this.$el.remove(); @@ -58,10 +63,7 @@ publicWidget.registry.websiteEventMeetingRoom = publicWidget.Widget.extend({ this.call("dialog", "add", ConfirmationDialog, { body: _t("Are you sure you want to duplicate this room?"), confirm: async () => { - await this._rpc({ - model: "event.meeting.room", - method: "copy", - args: [this.meetingRoomId], + await this.orm.call("event.meeting.room", "copy", [this.meetingRoomId], { context: this.context, }); @@ -82,12 +84,12 @@ publicWidget.registry.websiteEventMeetingRoom = publicWidget.Widget.extend({ const pinnedButtonClass = "o_wevent_meeting_room_pinned"; const isPinned = event.currentTarget.classList.contains(pinnedButtonClass); - await this._rpc({ - model: 'event.meeting.room', - method: 'write', - args: [this.meetingRoomId, {is_pinned: !isPinned}], - context: this.context, - }); + await this.orm.write( + "event.meeting.room", + [this.meetingRoomId], + { is_pinned: !isPinned }, + { context: this.context } + ); // TDE FIXME: addclass ? if (isPinned) { diff --git a/addons/website_event_track/static/src/js/event_track_reminder.js b/addons/website_event_track/static/src/js/event_track_reminder.js index bf2b4447eb3..a9582fb1f87 100644 --- a/addons/website_event_track/static/src/js/event_track_reminder.js +++ b/addons/website_event_track/static/src/js/event_track_reminder.js @@ -17,6 +17,7 @@ publicWidget.registry.websiteEventTrackReminder = publicWidget.Widget.extend({ init: function () { this._super.apply(this, arguments); this._onReminderToggleClick = debounce(this._onReminderToggleClick, 500, true); + this.rpc = this.bindService("rpc"); }, //-------------------------------------------------------------------------- @@ -39,12 +40,9 @@ publicWidget.registry.websiteEventTrackReminder = publicWidget.Widget.extend({ var reminderOnValue = !this.reminderOn; - this._rpc({ - route: '/event/track/toggle_reminder', - params: { - track_id: $trackLink.data('trackId'), - set_reminder_on: reminderOnValue, - }, + this.rpc('/event/track/toggle_reminder', { + track_id: $trackLink.data('trackId'), + set_reminder_on: reminderOnValue, }).then(function (result) { if (result.error && result.error === 'ignored') { self.displayNotification({ diff --git a/addons/website_event_track/static/src/js/website_event_track_proposal_form_tags.js b/addons/website_event_track/static/src/js/website_event_track_proposal_form_tags.js index 15270cd4221..3b8f2f8e4b3 100644 --- a/addons/website_event_track/static/src/js/website_event_track_proposal_form_tags.js +++ b/addons/website_event_track/static/src/js/website_event_track_proposal_form_tags.js @@ -6,6 +6,11 @@ import publicWidget from "@web/legacy/js/public/public_widget"; publicWidget.registry.websiteEventTrackProposalFormTags = publicWidget.Widget.extend({ selector: '.o_website_event_track_proposal_form_tags', + init() { + this._super(...arguments); + this.rpc = this.bindService("rpc"); + }, + start: function () { var self = this; return this._super.apply(this, arguments).then(function () { @@ -22,12 +27,9 @@ publicWidget.registry.websiteEventTrackProposalFormTags = publicWidget.Widget.ex var self = this; this.$('.o_wetrack_select2_tags').select2(this._select2Wrapper(_t('Select categories'), function () { - return self._rpc({ - route: "/event/track_tag/search_read", - params: { - fields: ['name', 'category_id'], - domain: [], - } + return self.rpc("/event/track_tag/search_read", { + fields: ['name', 'category_id'], + domain: [], }); }) ); diff --git a/addons/website_event_track_live/static/src/js/website_event_track_live.js b/addons/website_event_track_live/static/src/js/website_event_track_live.js index 3edef242cef..e3a343f38e3 100644 --- a/addons/website_event_track_live/static/src/js/website_event_track_live.js +++ b/addons/website_event_track_live/static/src/js/website_event_track_live.js @@ -11,6 +11,11 @@ publicWidget.registry.websiteEventTrackLive = publicWidget.Widget.extend({ 'video-ended': '_onVideoEnded' }), + init() { + this._super(...arguments); + this.rpc = this.bindService("rpc"); + }, + start: function () { var self = this; return this._super(...arguments).then(function () { @@ -45,11 +50,8 @@ publicWidget.registry.websiteEventTrackLive = publicWidget.Widget.extend({ class: 'owevent_track_suggestion_loading position-absolute w-100' })); var self = this; - this._rpc({ - route: '/event_track/get_track_suggestion', - params: { - track_id: this.$el.data('trackId'), - } + this.rpc('/event_track/get_track_suggestion', { + track_id: this.$el.data('trackId'), }).then(function (suggestion) { self.nextSuggestion = suggestion; self._showSuggestion(); diff --git a/addons/website_event_track_live_quiz/static/src/js/event_quiz.js b/addons/website_event_track_live_quiz/static/src/js/event_quiz.js index 36d3c794c07..8f3b6b60410 100644 --- a/addons/website_event_track_live_quiz/static/src/js/event_quiz.js +++ b/addons/website_event_track_live_quiz/static/src/js/event_quiz.js @@ -36,11 +36,8 @@ var WebsiteEventTrackSuggestionQuiz = Quiz.include({ _getTrackSuggestion: function () { var self = this; - return this._rpc({ - route: '/event_track/get_track_suggestion', - params: { - track_id: this.track.id, - } + return this.rpc('/event_track/get_track_suggestion', { + track_id: this.track.id, }).then(function (suggestion) { self.nextSuggestion = suggestion; return Promise.resolve(); diff --git a/addons/website_event_track_quiz/static/src/js/event_quiz.js b/addons/website_event_track_quiz/static/src/js/event_quiz.js index f4f593cf0a1..259505086c7 100644 --- a/addons/website_event_track_quiz/static/src/js/event_quiz.js +++ b/addons/website_event_track_quiz/static/src/js/event_quiz.js @@ -45,6 +45,8 @@ var Quiz = publicWidget.Widget.extend({ this.isMember = data.isMember || false; this.userId = session.user_id; this.redirectURL = encodeURIComponent(document.URL); + + this.rpc = this.bindService("rpc"); }, /** @@ -208,13 +210,10 @@ var Quiz = publicWidget.Widget.extend({ _submitQuiz: function () { var self = this; - return this._rpc({ - route: '/event_track/quiz/submit', - params: { - event_id: self.track.eventId, - track_id: self.track.id, - answer_ids: this._getQuizAnswers(), - } + return this.rpc('/event_track/quiz/submit', { + event_id: self.track.eventId, + track_id: self.track.id, + answer_ids: this._getQuizAnswers(), }).then(function (data) { if (data.error) { self._alertShow(data.error); @@ -257,12 +256,9 @@ var Quiz = publicWidget.Widget.extend({ * @private */ _onClickReset: function () { - this._rpc({ - route: '/event_track/quiz/reset', - params: { - event_id: this.track.eventId, - track_id: this.track.id - } + this.rpc('/event_track/quiz/reset', { + event_id: this.track.eventId, + track_id: this.track.id }).then(this._resetQuiz.bind(this)); }, diff --git a/addons/website_forum/static/src/js/website_forum.js b/addons/website_forum/static/src/js/website_forum.js index 9ecb2d218b8..60f0085fea7 100644 --- a/addons/website_forum/static/src/js/website_forum.js +++ b/addons/website_forum/static/src/js/website_forum.js @@ -33,6 +33,12 @@ publicWidget.registry.websiteForum = publicWidget.Widget.extend({ 'submit .js_wforum_submit_form:has(:not(.karma_required).o_wforum_submit_post)': '_onSubmitForm', }, + init() { + this._super(...arguments); + this.rpc = this.bindService("rpc"); + this.orm = this.bindService("orm"); + }, + /** * @override */ @@ -323,9 +329,9 @@ publicWidget.registry.websiteForum = publicWidget.Widget.extend({ _onFlagAlertClick: function (ev) { ev.preventDefault(); const elem = ev.currentTarget; - this._rpc({ - route: elem.dataset.href || (elem.getAttribute('href') !== '#' && elem.getAttribute('href')) || elem.closest('form').getAttribute('action'), - }).then(data => { + this.rpc( + elem.dataset.href || (elem.getAttribute('href') !== '#' && elem.getAttribute('href')) || elem.closest('form').getAttribute('action'), + ).then(data => { if (data.error) { const message = data.error === 'anonymous_user' ? _t("Sorry you must be logged to flag a post") @@ -365,9 +371,7 @@ publicWidget.registry.websiteForum = publicWidget.Widget.extend({ _onVotePostClick: function (ev) { ev.preventDefault(); var $btn = $(ev.currentTarget); - this._rpc({ - route: $btn.data('href'), - }).then(data => { + this.rpc($btn.data('href')).then(data => { if (data.error) { const message = data.error === 'own_post' ? _t('Sorry, you cannot vote for your own posts') @@ -465,7 +469,7 @@ publicWidget.registry.websiteForum = publicWidget.Widget.extend({ ev.preventDefault(); const link = ev.currentTarget; const target = link.dataset.target; - const data = await this._rpc({ route: link.dataset.href }); + const data = await this.rpc(link.dataset.href); if (data.error) { const message = data.error === 'anonymous_user' ? _t('Sorry, anonymous users cannot choose correct answers.') @@ -500,7 +504,7 @@ publicWidget.registry.websiteForum = publicWidget.Widget.extend({ _onFavoriteQuestionClick: async function (ev) { ev.preventDefault(); const link = ev.currentTarget; - const data = await this._rpc({ route: link.dataset.href }); + const data = await this.rpc(link.dataset.href); link.classList.toggle('opacity-50', !data); link.classList.toggle('opacity-100-hover', !data); const link_icon = link.querySelector('.fa'); @@ -517,9 +521,7 @@ publicWidget.registry.websiteForum = publicWidget.Widget.extend({ var $link = $(ev.currentTarget); var $container = $link.closest('.o_wforum_post_comments_container'); - this._rpc({ - route: $link.closest('form').attr('action'), - }).then(function () { + this.rpc($link.closest('form').attr('action')).then(function () { $link.closest('.o_wforum_post_comment').remove(); var count = $container.find('.o_wforum_post_comment').length; @@ -547,11 +549,9 @@ publicWidget.registry.websiteForum = publicWidget.Widget.extend({ async _onFlagValidatorClick(ev) { ev.preventDefault(); const currentTarget = ev.currentTarget; - await this._rpc({ - model: 'forum.post', - method: currentTarget.dataset.action, - args: [parseInt(currentTarget.dataset.postId)], - }); + await this.orm.call("forum.post", currentTarget.dataset.action, [ + parseInt(currentTarget.dataset.postId), + ]); this._findParent(currentTarget, '.o_wforum_flag_alert')?.classList.toggle('d-none'); const flaggedButton = currentTarget.parentElement.firstElementChild, child = flaggedButton.firstElementChild, @@ -571,9 +571,7 @@ publicWidget.registry.websiteForum = publicWidget.Widget.extend({ */ async _onFlagMarkAsOffensiveClick(ev) { ev.preventDefault(); - const template = await this._rpc({ - route: $(ev.currentTarget).data('action'), - }); + const template = await this.rpc($(ev.currentTarget).data('action')); this.call("dialog", "add", FlagMarkAsOffensiveDialog, { title: _t("Offensive Post"), body: markup(template), @@ -597,6 +595,11 @@ publicWidget.registry.websiteForumSpam = publicWidget.Widget.extend({ 'input #spamSearch': '_onSpamSearchInput', }, + init() { + this._super(...arguments); + this.orm = this.bindService("orm"); + }, + /** * @override */ @@ -625,18 +628,14 @@ publicWidget.registry.websiteForumSpam = publicWidget.Widget.extend({ _onSpamSearchInput: function (ev) { var self = this; var toSearch = $(ev.currentTarget).val(); - return this._rpc({ - model: 'forum.post', - method: 'search_read', - args: [ - [['id', 'in', self.spamIDs], - '|', - ['name', 'ilike', toSearch], - ['content', 'ilike', toSearch]], - ['name', 'content'] - ], - kwargs: {} - }).then(function (o) { + return this.orm.searchRead( + "forum.post", + [['id', 'in', self.spamIDs], + '|', + ['name', 'ilike', toSearch], + ['content', 'ilike', toSearch]], + ['name', 'content'] + ).then(function (o) { Object.values(o).forEach((r) => { r.content = $('

' + $(r.content).html() + '

').text().substring(0, 250); }); @@ -654,10 +653,11 @@ publicWidget.registry.websiteForumSpam = publicWidget.Widget.extend({ var key = this.$('.modal .tab-pane.active').data('key'); var $inputs = this.$('.modal .tab-pane.active input.form-check-input:checked'); var values = Array.from($inputs).map((o) => parseInt(o.value)); - return this._rpc({model: 'forum.post', - method: 'mark_as_offensive_batch', - args: [this.spamIDs, key, values], - }).then(function () { + return this.orm.call("forum.post", "mark_as_offensive_batch", [ + this.spamIDs, + key, + values, + ]).then(function () { window.location.reload(); }); }, diff --git a/addons/website_jitsi/static/src/js/chat_room.js b/addons/website_jitsi/static/src/js/chat_room.js index 6d14308a6f5..881570863fe 100644 --- a/addons/website_jitsi/static/src/js/chat_room.js +++ b/addons/website_jitsi/static/src/js/chat_room.js @@ -10,6 +10,11 @@ publicWidget.registry.ChatRoom = publicWidget.Widget.extend({ 'click .o_wjitsi_room_link': '_onChatRoomClick', }, + init() { + this._super(...arguments); + this.rpc = this.bindService("rpc"); + }, + /** * Manage the chat room (Jitsi), update the participant count... * @@ -59,12 +64,7 @@ publicWidget.registry.ChatRoom = publicWidget.Widget.extend({ if (this.checkFull) { // maybe we didn't refresh the page for a while and so we might join a room // which is full, so we perform a RPC call to verify that we can really join - let isChatRoomFull = await this._rpc({ - route: '/jitsi/is_full', - params: { - room_name: this.roomName, - }, - }); + let isChatRoomFull = await this.rpc('/jitsi/is_full', { room_name: this.roomName }); if (isChatRoomFull) { window.location.reload(); @@ -204,13 +204,10 @@ publicWidget.registry.ChatRoom = publicWidget.Widget.extend({ * @param {boolean} joined, true if someone joined the room */ _updateParticipantCount: async function (count, joined) { - await this._rpc({ - route: '/jitsi/update_status', - params: { - room_name: this.roomName, - participant_count: count, - joined: joined, - }, + await this.rpc('/jitsi/update_status', { + room_name: this.roomName, + participant_count: count, + joined: joined, }); }, diff --git a/addons/website_links/static/src/js/website_links.js b/addons/website_links/static/src/js/website_links.js index bb76cc14a36..4188fadc63e 100644 --- a/addons/website_links/static/src/js/website_links.js +++ b/addons/website_links/static/src/js/website_links.js @@ -18,6 +18,8 @@ var SelectBox = publicWidget.Widget.extend({ this._super.apply(this, arguments); this.obj = obj; this.placeholder = placeholder; + + this.orm = this.bindService("orm"); }, /** * @override @@ -25,13 +27,7 @@ var SelectBox = publicWidget.Widget.extend({ willStart: function () { var self = this; var defs = [this._super.apply(this, arguments)]; - defs.push(this._rpc({ - model: this.obj, - method: 'search_read', - kwargs: { - fields: ['id', 'name'], - }, - }).then(function (result) { + defs.push(this.orm.searchRead(this.obj, [], ["id", "name"]).then(function (result) { self.objects = result.map((val) => { return {id: val.id, text: val.name}; }); @@ -82,11 +78,7 @@ var SelectBox = publicWidget.Widget.extend({ if (this.obj === "utm.campaign"){ args.is_auto_campaign = true; } - return this._rpc({ - model: this.obj, - method: 'create', - args: [args], - }).then(function (record) { + return this.orm.create(this.obj, [args]).then(function (record) { self.$el.attr('value', record); self.objects.push({'id': record, 'text': name}); }); @@ -127,6 +119,7 @@ var RecentLinkBox = publicWidget.Widget.extend({ this._super.apply(this, arguments); this.link_obj = obj; this.animating_copy = false; + this.rpc = this.bindService("rpc"); }, /** * @override @@ -236,12 +229,9 @@ var RecentLinkBox = publicWidget.Widget.extend({ if (initCode === newCode) { showNewCode(newCode); } else { - this._rpc({ - route: '/website_links/add_code', - params: { - init_code: initCode, - new_code: newCode, - }, + this.rpc('/website_links/add_code', { + init_code: initCode, + new_code: newCode, }).then(function (result) { showNewCode(result[0].code); }, function () { @@ -282,6 +272,10 @@ var RecentLinkBox = publicWidget.Widget.extend({ }); var RecentLinks = publicWidget.Widget.extend({ + init() { + this._super(...arguments); + this.rpc = this.bindService("rpc"); + }, //-------------------------------------------------------------------------- // Private @@ -292,12 +286,9 @@ var RecentLinks = publicWidget.Widget.extend({ */ getRecentLinks: function (filter) { var self = this; - return this._rpc({ - route: '/website_links/recent_links', - params: { - filter: filter, - limit: 20, - }, + return this.rpc('/website_links/recent_links', { + filter: filter, + limit: 20, }).then(function (result) { result.reverse().forEach((link) => { self._addLink(link); @@ -354,6 +345,11 @@ publicWidget.registry.websiteLinks = publicWidget.Widget.extend({ 'submit #o_website_links_link_tracker_form': '_onFormSubmit', }, + init() { + this._super(...arguments); + this.rpc = this.bindService("rpc"); + }, + /** * @override */ @@ -493,10 +489,7 @@ publicWidget.registry.websiteLinks = publicWidget.Widget.extend({ $('#btn_shorten_url').text(_t("Generating link...")); - this._rpc({ - route: '/website_links/new', - params: params, - }).then(function (result) { + this.rpc('/website_links/new', params).then(function (result) { if ('error' in result) { // Handle errors if (result.error === 'empty_url') { diff --git a/addons/website_links/static/src/js/website_links_charts.js b/addons/website_links/static/src/js/website_links_charts.js index 0f945b11d49..bd2d2270277 100644 --- a/addons/website_links/static/src/js/website_links_charts.js +++ b/addons/website_links/static/src/js/website_links_charts.js @@ -126,6 +126,11 @@ publicWidget.registry.websiteLinksCharts = publicWidget.Widget.extend({ 'click .copy-to-clipboard': '_onCopyToClipboardClick', }, + init() { + this._super(...arguments); + this.orm = this.bindService("orm"); + }, + /** * @override */ @@ -221,33 +226,29 @@ publicWidget.registry.websiteLinksCharts = publicWidget.Widget.extend({ * @private */ _totalClicks: function () { - return this._rpc({ - model: 'link.tracker.click', - method: 'search_count', - args: [[this.links_domain]], - }); + return this.orm.searchCount("link.tracker.click", [this.links_domain]); }, /** * @private */ _clicksByDay: function () { - return this._rpc({ - model: 'link.tracker.click', - method: 'read_group', - args: [[this.links_domain], ['create_date']], - kwargs: {groupby: 'create_date:day'}, - }); + return this.orm.readGroup( + "link.tracker.click", + [this.links_domain], + ["create_date"], + ["create_date:day"] + ); }, /** * @private */ _clicksByCountry: function () { - return this._rpc({ - model: 'link.tracker.click', - method: 'read_group', - args: [[this.links_domain], ['country_id']], - kwargs: {groupby: 'country_id'}, - }); + return this.orm.readGroup( + "link.tracker.click", + [this.links_domain], + ["country_id"], + ["country_id"] + ); }, /** * @private @@ -257,12 +258,12 @@ publicWidget.registry.websiteLinksCharts = publicWidget.Widget.extend({ const aWeekAgoDate = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000); // get the date in the format YYYY-MM-DD. const aWeekAgoString = aWeekAgoDate.toISOString().split("T")[0]; - return this._rpc({ - model: 'link.tracker.click', - method: 'read_group', - args: [[this.links_domain, ["create_date", ">", aWeekAgoString]], ["country_id"]], - kwargs: {groupby: 'country_id'}, - }); + return this.orm.readGroup( + "link.tracker.click", + [this.links_domain, ["create_date", ">", aWeekAgoString]], + ["country_id"], + ["country_id"] + ); }, /** * @private @@ -272,12 +273,12 @@ publicWidget.registry.websiteLinksCharts = publicWidget.Widget.extend({ const aMonthAgoDate = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000); // get the date in the format YYYY-MM-DD. const aMonthAgoString = aMonthAgoDate.toISOString().split("T")[0]; - return this._rpc({ - model: 'link.tracker.click', - method: 'read_group', - args: [[this.links_domain, ["create_date", ">", aMonthAgoString]], ["country_id"]], - kwargs: {groupby: 'country_id'}, - }); + return this.orm.readGroup( + "link.tracker.click", + [this.links_domain, ["create_date", ">", aMonthAgoString]], + ["country_id"], + ["country_id"] + ); }, //-------------------------------------------------------------------------- diff --git a/addons/website_links/static/src/js/website_links_code_editor.js b/addons/website_links/static/src/js/website_links_code_editor.js index 192651f93ce..be44ed274aa 100644 --- a/addons/website_links/static/src/js/website_links_code_editor.js +++ b/addons/website_links/static/src/js/website_links_code_editor.js @@ -12,6 +12,11 @@ publicWidget.registry.websiteLinksCodeEditor = publicWidget.Widget.extend({ 'click .o_website_links_ok_edit': '_onEditCodeFormSubmit', }, + init() { + this._super(...arguments); + this.rpc = this.bindService("rpc"); + }, + //-------------------------------------------------------------------------- // Private //-------------------------------------------------------------------------- @@ -58,12 +63,9 @@ publicWidget.registry.websiteLinksCodeEditor = publicWidget.Widget.extend({ if (initCode === newCode) { this._showNewCode(newCode); } else { - return this._rpc({ - route: '/website_links/add_code', - params: { - init_code: initCode, - new_code: newCode, - }, + return this.rpc('/website_links/add_code', { + init_code: initCode, + new_code: newCode, }).then(function (result) { self._showNewCode(result[0].code); }, function () { diff --git a/addons/website_mail/static/src/js/follow.js b/addons/website_mail/static/src/js/follow.js index 1c8770a8dd9..ee95dce05f0 100644 --- a/addons/website_mail/static/src/js/follow.js +++ b/addons/website_mail/static/src/js/follow.js @@ -6,6 +6,11 @@ publicWidget.registry.follow = publicWidget.Widget.extend({ selector: '#wrapwrap:has(.js_follow)', disabledInEditableMode: false, + init() { + this._super(...arguments); + this.rpc = this.bindService("rpc"); + }, + /** * @override */ @@ -34,11 +39,8 @@ publicWidget.registry.follow = publicWidget.Widget.extend({ records[model].push(parseInt(el.dataset.id)); } - this._rpc({ - route: '/website_mail/is_follower', - params: { - records: records, - }, + this.rpc('/website_mail/is_follower', { + records: records, }).then(always).guardedCatch(always); // not if editable mode to allow designer to edit @@ -111,14 +113,11 @@ publicWidget.registry.follow = publicWidget.Widget.extend({ var email = $email.length ? $email.val() : false; if (email || this.isUser) { - this._rpc({ - route: '/website_mail/follow', - params: { - 'id': +$jsFollow.data('id'), - 'object': $jsFollow.data('object'), - 'message_is_follower': $jsFollow.attr("data-follow") || "off", - 'email': email, - }, + this.rpc('/website_mail/follow', { + 'id': +$jsFollow.data('id'), + 'object': $jsFollow.data('object'), + 'message_is_follower': $jsFollow.attr("data-follow") || "off", + 'email': email, }).then(function (follow) { self._toggleSubscription(follow, email, $jsFollow); }); diff --git a/addons/website_mail_group/static/src/snippets/s_group/000.js b/addons/website_mail_group/static/src/snippets/s_group/000.js index 39f7b74de96..7ec266968dd 100644 --- a/addons/website_mail_group/static/src/snippets/s_group/000.js +++ b/addons/website_mail_group/static/src/snippets/s_group/000.js @@ -12,13 +12,10 @@ MailGroup.include({ // Because it's rendered only once when the admin add the snippets // for the first time, we make a RPC call to setup the widget properly const email = (new URL(document.location.href)).searchParams.get('email'); - const response = await this._rpc({ - route: '/group/is_member', - params: { - 'group_id': this.mailgroupId, - 'email': email, - 'token': this.token, - }, + const response = await this.rpc('/group/is_member', { + 'group_id': this.mailgroupId, + 'email': email, + 'token': this.token, }); if (!response) { diff --git a/addons/website_mail_group/static/src/snippets/s_group/options.js b/addons/website_mail_group/static/src/snippets/s_group/options.js index b47e69ab777..abc826ae3ac 100644 --- a/addons/website_mail_group/static/src/snippets/s_group/options.js +++ b/addons/website_mail_group/static/src/snippets/s_group/options.js @@ -5,6 +5,11 @@ import options from "@web_editor/js/editor/snippets.options"; import wUtils from "@website/js/utils"; options.registry.Group = options.Class.extend({ + init() { + this._super(...arguments); + this.orm = this.bindService("orm"); + }, + /** * @override */ @@ -57,13 +62,7 @@ options.registry.Group = options.Class.extend({ return; } - const groupId = await this._rpc({ - model: 'mail.group', - method: 'create', - args: [{ - name: name, - }], - }); + const groupId = await this.orm.create("mail.group", [{ name: name }]); this.$target.attr("data-id", groupId); return this._rerenderXML(); @@ -91,10 +90,6 @@ options.registry.Group = options.Class.extend({ * @return {Promise} */ _getMailGroups() { - return this._rpc({ - model: 'mail.group', - method: 'name_search', - args: [''], - }); + return this.orm.call("mail.group", "name_search", [""]); }, }); diff --git a/addons/website_mass_mailing/static/src/js/website_mass_mailing.editor.js b/addons/website_mass_mailing/static/src/js/website_mass_mailing.editor.js index ebfb294a328..42412c80f02 100644 --- a/addons/website_mass_mailing/static/src/js/website_mass_mailing.editor.js +++ b/addons/website_mass_mailing/static/src/js/website_mass_mailing.editor.js @@ -6,6 +6,11 @@ import { renderToElement } from "@web/core/utils/render"; import options from "@web_editor/js/editor/snippets.options"; options.registry.mailing_list_subscribe = options.Class.extend({ + init() { + this._super(...arguments); + this.orm = this.bindService("orm"); + }, + /** * @override */ @@ -80,12 +85,12 @@ options.registry.mailing_list_subscribe = options.Class.extend({ * @override */ async _renderCustomXML(uiFragment) { - this.mailingLists = await this._rpc({ - model: 'mailing.list', - method: 'name_search', - args: ['', [['is_public', '=', true]]], - context: this.options.recordInfo.context, - }); + this.mailingLists = await this.orm.call( + "mailing.list", + "name_search", + ["", [["is_public", "=", true]]], + { context: this.options.recordInfo.context } + ); if (this.mailingLists.length) { const selectEl = uiFragment.querySelector('we-select[data-attribute-name="listId"]'); for (const mailingList of this.mailingLists) { diff --git a/addons/website_mass_mailing/static/src/js/website_mass_mailing.js b/addons/website_mass_mailing/static/src/js/website_mass_mailing.js index 8765e524dd6..2b55dbd1bad 100644 --- a/addons/website_mass_mailing/static/src/js/website_mass_mailing.js +++ b/addons/website_mass_mailing/static/src/js/website_mass_mailing.js @@ -17,6 +17,7 @@ publicWidget.registry.subscribe = publicWidget.Widget.extend({ init: function () { this._super(...arguments); this._recaptcha = new ReCaptcha(); + this.rpc = this.bindService("rpc"); }, /** * @override @@ -38,12 +39,9 @@ publicWidget.registry.subscribe = publicWidget.Widget.extend({ } const always = this._updateView.bind(this); const inputName = this.el.querySelector('input').name; - return Promise.all([def, this._rpc({ - route: '/website_mass_mailing/is_subscriber', - params: { - 'list_id': this._getListId(), - 'subscription_type': inputName, - }, + return Promise.all([def, this.rpc('/website_mass_mailing/is_subscriber', { + 'list_id': this._getListId(), + 'subscription_type': inputName, }).then(always).guardedCatch(always)]); }, /** @@ -109,14 +107,11 @@ publicWidget.registry.subscribe = publicWidget.Widget.extend({ }); return false; } - this._rpc({ - route: '/website_mass_mailing/subscribe', - params: { - 'list_id': this._getListId(), - 'value': $input.length ? $input.val() : false, - 'subscription_type': inputName, - recaptcha_token_response: tokenObj.token, - }, + this.rpc('/website_mass_mailing/subscribe', { + 'list_id': this._getListId(), + 'value': $input.length ? $input.val() : false, + 'subscription_type': inputName, + recaptcha_token_response: tokenObj.token, }).then(function (result) { let toastType = result.toast_type; if (toastType === 'success') { diff --git a/addons/website_payment/static/src/snippets/s_donation/000.js b/addons/website_payment/static/src/snippets/s_donation/000.js index 98728d3cd58..fbd816d31e9 100644 --- a/addons/website_payment/static/src/snippets/s_donation/000.js +++ b/addons/website_payment/static/src/snippets/s_donation/000.js @@ -13,6 +13,13 @@ publicWidget.registry.DonationSnippet = publicWidget.Widget.extend({ 'click .s_donation_donate_btn': '_onClickDonateNowButton', 'input #s_donation_range_slider': '_onInputRangeSlider', }, + /** + * @override + */ + init() { + this._super(...arguments); + this.rpc = this.bindService("rpc"); + }, /** * @override @@ -81,9 +88,7 @@ publicWidget.registry.DonationSnippet = publicWidget.Widget.extend({ * @private */ _displayCurrencies() { - return this._rpc({ - route: '/website/get_current_currency', - }).then((result) => { + return this.rpc('/website/get_current_currency').then((result) => { this.currency = result; this.$('.s_donation_currency').remove(); const $prefilledButtons = this.$('.s_donation_btn, .s_range_bubble'); diff --git a/addons/website_profile/static/src/js/website_profile.js b/addons/website_profile/static/src/js/website_profile.js index d17dd5b72a9..4c078fd78b1 100644 --- a/addons/website_profile/static/src/js/website_profile.js +++ b/addons/website_profile/static/src/js/website_profile.js @@ -10,6 +10,11 @@ publicWidget.registry.websiteProfile = publicWidget.Widget.extend({ 'click .validated_email_close': '_onCloseValidatedEmailClick', }, + init() { + this._super(...arguments); + this.rpc = this.bindService("rpc"); + }, + //-------------------------------------------------------------------------- // Handlers //-------------------------------------------------------------------------- @@ -20,9 +25,8 @@ publicWidget.registry.websiteProfile = publicWidget.Widget.extend({ _onSendValidationEmailClick: function (ev) { ev.preventDefault(); var $element = $(ev.currentTarget); - this._rpc({ - route: '/profile/send_validation_email', - params: {'redirect_url': $element.data('redirect_url')}, + this.rpc('/profile/send_validation_email', { + 'redirect_url': $element.data('redirect_url'), }).then(function (data) { if (data) { window.location = $element.data('redirect_url'); @@ -34,9 +38,7 @@ publicWidget.registry.websiteProfile = publicWidget.Widget.extend({ * @private */ _onCloseValidatedEmailClick: function () { - this._rpc({ - route: '/profile/validate_email/close', - }); + this.rpc('/profile/validate_email/close'); }, }); diff --git a/addons/website_sale/static/src/js/sale_variant_mixin.js b/addons/website_sale/static/src/js/sale_variant_mixin.js index 16c4b58fa4c..88769fe2e55 100644 --- a/addons/website_sale/static/src/js/sale_variant_mixin.js +++ b/addons/website_sale/static/src/js/sale_variant_mixin.js @@ -1,12 +1,12 @@ /** @odoo-module **/ import { KeepLast } from "@web/core/utils/concurrency"; -import ajax from "@web/legacy/js/core/ajax"; import { memoize, uniqueId } from "@web/core/utils/functions"; import { throttleForAnimation } from "@web/core/utils/timing"; import { insertThousandsSep } from "@web/core/utils/numbers"; import { _t } from "@web/core/l10n/translation"; import { localization } from "@web/core/l10n/localization"; +import { jsonrpc } from "@web/core/network/rpc_service"; var VariantMixin = { events: { @@ -70,7 +70,7 @@ var VariantMixin = { const $currentOptionalProduct = $(optionalProduct); const childCombination = this.getSelectedVariantValues($currentOptionalProduct); const productTemplateId = parseInt($currentOptionalProduct.find('.product_template_id').val()); - ajax.jsonRpc('/website_sale/get_combination_info', 'call', { + jsonrpc('/website_sale/get_combination_info', { 'product_template_id': productTemplateId, 'product_id': this._getProductId($currentOptionalProduct), 'combination': childCombination, @@ -89,7 +89,7 @@ var VariantMixin = { ); } - return ajax.jsonRpc('/website_sale/get_combination_info', 'call', { + return jsonrpc('/website_sale/get_combination_info', { 'product_template_id': parseInt($parent.find('.product_template_id').val()), 'product_id': this._getProductId($parent), 'combination': combination, @@ -317,7 +317,7 @@ var VariantMixin = { * @param {$.Element} $container the container to look into * @param {integer} productId the product id * @param {integer} productTemplateId the corresponding product template id - * @param {boolean} useAjax wether the rpc call should be done using ajax.jsonRpc or using _rpc + * @param {boolean} useAjax wether the rpc call should be done using jsonrpc or using _rpc * @returns {Promise} the promise that will be resolved with a {integer} productId */ selectOrCreateProduct: function ($container, productId, productTemplateId, useAjax) { @@ -335,10 +335,7 @@ var VariantMixin = { var route = '/sale/create_product_variant'; if (useAjax) { - productReady = ajax.jsonRpc(route, 'call', params); - } else if (Boolean(this._rpc)) { - // HACK to combine owl and non owl calls - productReady = this._rpc({route: route, params: params}); + productReady = jsonrpc(route, params); } else { productReady = this.rpc(route, params); } diff --git a/addons/website_sale/static/src/js/website_sale.editor.js b/addons/website_sale/static/src/js/website_sale.editor.js index 66c25ba86d8..f4876c90348 100644 --- a/addons/website_sale/static/src/js/website_sale.editor.js +++ b/addons/website_sale/static/src/js/website_sale.editor.js @@ -8,6 +8,10 @@ import "@website/js/editor/snippets.options"; import { renderToElement } from "@web/core/utils/render"; options.registry.WebsiteSaleGridLayout = options.Class.extend({ + init() { + this._super(...arguments); + this.rpc = this.bindService("rpc"); + }, /** * @override @@ -40,36 +44,21 @@ options.registry.WebsiteSaleGridLayout = options.Class.extend({ return false; } this.ppg = Math.min(ppg, PPG_LIMIT); - return this._rpc({ - route: '/shop/config/website', - params: { - 'shop_ppg': this.ppg, - }, - }); + return this.rpc('/shop/config/website', { 'shop_ppg': this.ppg }); }, /** * @see this.selectClass for params */ setPpr: function (previewMode, widgetValue, params) { this.ppr = parseInt(widgetValue); - this._rpc({ - route: '/shop/config/website', - params: { - 'shop_ppr': this.ppr, - }, - }); + this.rpc('/shop/config/website', { 'shop_ppr': this.ppr }); }, /** * @see this.selectClass for params */ setDefaultSort: function (previewMode, widgetValue, params) { this.default_sort = widgetValue; - this._rpc({ - route: '/shop/config/website', - params: { - 'shop_default_sort': this.default_sort, - }, - }); + this.rpc('/shop/config/website', { 'shop_default_sort': this.default_sort }); }, //-------------------------------------------------------------------------- @@ -116,6 +105,11 @@ options.registry.WebsiteSaleProductsItem = options.Class.extend({ 'click .o_wsale_soptions_menu_sizes td': '_onTableItemClick', }), + init() { + this._super(...arguments); + this.rpc = this.bindService("rpc"); + }, + /** * @override */ @@ -234,12 +228,9 @@ options.registry.WebsiteSaleProductsItem = options.Class.extend({ * @see this.selectClass for params */ changeSequence: function (previewMode, widgetValue, params) { - this._rpc({ - route: '/shop/config/product', - params: { - product_id: this.productTemplateID, - sequence: widgetValue, - }, + this.rpc('/shop/config/product', { + product_id: this.productTemplateID, + sequence: widgetValue, }).then(() => this._reloadEditable()); }, @@ -437,13 +428,10 @@ options.registry.WebsiteSaleProductsItem = options.Class.extend({ var $td = $(ev.currentTarget); var x = $td.index() + 1; var y = $td.parent().index() + 1 - this._rpc({ - route: '/shop/config/product', - params: { - product_id: this.productTemplateID, - x: x, - y: y, - }, + this.rpc('/shop/config/product', { + product_id: this.productTemplateID, + x: x, + y: y, }).then(() => this._reloadEditable()); }, _reloadEditable() { @@ -467,6 +455,11 @@ class AttachmentMediaDialog extends MediaDialog { } options.registry.WebsiteSaleProductPage = options.Class.extend({ + init() { + this._super(...arguments); + this.rpc = this.bindService("rpc"); + this.orm = this.bindService("orm"); + }, /** * @override @@ -489,10 +482,7 @@ options.registry.WebsiteSaleProductPage = options.Class.extend({ }, _updateWebsiteConfig(params) { - this._rpc({ - route: '/shop/config/website', - params, - }).then(() => this.trigger_up('request_save', {reload: true, optionSelector: this.data.selector})); + this.rpc('/shop/config/website', params).then(() => this.trigger_up('request_save', {reload: true, optionSelector: this.data.selector})); }, _getZoomOptionData() { @@ -593,14 +583,11 @@ options.registry.WebsiteSaleProductPage = options.Class.extend({ await this._convertAttachmentToWebp(attachment, extraImageEls[index]); } } - this._rpc({ - route: `/shop/product/extra-images`, - params: { - images: attachments, - product_product_id: this.productProductID, - product_template_id: this.productTemplateID, - combination_ids: this._getSelectedVariantValues(this.$target.find('.js_add_cart_variants')), - } + this.rpc(`/shop/product/extra-images`, { + images: attachments, + product_product_id: this.productProductID, + product_template_id: this.productTemplateID, + combination_ids: this._getSelectedVariantValues(this.$target.find('.js_add_cart_variants')), }).then(() => { this.trigger_up('request_save', {reload: true, optionSelector: this.data.selector}); }); @@ -629,18 +616,14 @@ options.registry.WebsiteSaleProductPage = options.Class.extend({ ctx.fillStyle = "rgb(255, 255, 255)"; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.drawImage(imgEl, 0, 0, imgEl.width, imgEl.height, 0, 0, canvas.width, canvas.height); - const [resizedId] = await this._rpc({ - model: "ir.attachment", - method: "create_unique", - args: [[{ - name: webpName, - description: size === originalSize ? "" : `resize: ${size}`, - datas: canvas.toDataURL("image/webp", 0.75).split(",")[1], - res_id: referenceId, - res_model: "ir.attachment", - mimetype: "image/webp", - }]], - }); + const [resizedId] = await this.orm.call("ir.attachment", "create_unique", [[{ + name: webpName, + description: size === originalSize ? "" : `resize: ${size}`, + datas: canvas.toDataURL("image/webp", 0.75).split(",")[1], + res_id: referenceId, + res_model: "ir.attachment", + mimetype: "image/webp", + }]]); if (size === originalSize) { attachment.original_id = attachment.id; attachment.id = resizedId; @@ -648,18 +631,14 @@ options.registry.WebsiteSaleProductPage = options.Class.extend({ attachment.mimetype = "image/webp"; } referenceId = referenceId || resizedId; // Keep track of original. - await this._rpc({ - model: "ir.attachment", - method: "create_unique", - args: [[{ - name: webpName.replace(/\.webp$/, ".jpg"), - description: "format: jpeg", - datas: canvas.toDataURL("image/jpeg", 0.75).split(",")[1], - res_id: resizedId, - res_model: "ir.attachment", - mimetype: "image/jpeg", - }]], - }); + await this.orm.call("ir.attachment", "create_unique", [[{ + name: webpName.replace(/\.webp$/, ".jpg"), + description: "format: jpeg", + datas: canvas.toDataURL("image/jpeg", 0.75).split(",")[1], + res_id: resizedId, + res_model: "ir.attachment", + mimetype: "image/jpeg", + }]]); } }, @@ -667,14 +646,11 @@ options.registry.WebsiteSaleProductPage = options.Class.extend({ * Removes all extra-images from the product. */ clearImages: function () { - this._rpc({ - route: `/shop/product/clear-images`, - params: { - model: this.mode, - product_product_id: this.productProductID, - product_template_id: this.productTemplateID, - combination_ids: this._getSelectedVariantValues(this.$target.find('.js_add_cart_variants')), - } + this.rpc(`/shop/product/clear-images`, { + model: this.mode, + product_product_id: this.productProductID, + product_template_id: this.productTemplateID, + combination_ids: this._getSelectedVariantValues(this.$target.find('.js_add_cart_variants')), }).then(() => { this.trigger_up('request_save', {reload: true, optionSelector: this.data.selector}); }); @@ -690,21 +666,15 @@ options.registry.WebsiteSaleProductPage = options.Class.extend({ 2: 'medium', 3: 'big', }[widgetValue]; - this._rpc({ - route: '/shop/config/website', - params: { - 'product_page_image_spacing': spacing, - }, + this.rpc('/shop/config/website', { + 'product_page_image_spacing': spacing, }).then(() => this.trigger_up('request_save', {reload: true, optionSelector: this.data.selector})); this.productPageGrid.dataset.image_spacing = spacing; }, setColumns(previewMode, widgetValue, params) { - this._rpc({ - route: '/shop/config/website', - params: { - 'product_page_grid_columns': widgetValue, - }, + this.rpc('/shop/config/website', { + 'product_page_grid_columns': widgetValue, }).then(() => this.trigger_up('request_save', {reload: true, optionSelector: this.data.selector})); this.productPageGrid.dataset.grid_columns = widgetValue; }, @@ -758,6 +728,11 @@ options.registry.WebsiteSaleProductPage = options.Class.extend({ }); options.registry.WebsiteSaleProductAttribute = options.Class.extend({ + init() { + this._super(...arguments); + this.rpc = this.bindService("rpc"); + }, + /** * @override */ @@ -770,12 +745,9 @@ options.registry.WebsiteSaleProductAttribute = options.Class.extend({ * @see this.selectClass for params */ setDisplayType: function (previewMode, widgetValue, params) { - this._rpc({ - route: '/shop/config/attribute', - params: { - attribute_id: this.attributeID, - display_type: widgetValue, - }, + this.rpc('/shop/config/attribute', { + attribute_id: this.attributeID, + display_type: widgetValue, }).then(() => this.trigger_up('request_save', {reload: true, optionSelector: this.data.selector})); }, @@ -803,6 +775,10 @@ options.registry.SnippetSave.include({ }); options.registry.ReplaceMedia.include({ + init() { + this._super(...arguments); + this.orm = this.bindService("orm"); + }, /** * @override */ @@ -821,11 +797,7 @@ options.registry.ReplaceMedia.include({ if (this.recordModel === "product.image") { // Unlink the "product.image" record as it is not the main product // image. - await this._rpc({ - model: "product.image", - method: "unlink", - args: [[this.recordId]], - }); + await this.orm.unlink("product.image", [this.recordId]); } this.$target[0].remove(); this.trigger_up("request_save", {reload: true, optionSelector: "#product_detail_main"}); @@ -835,13 +807,10 @@ options.registry.ReplaceMedia.include({ * */ async setPosition(previewMode, widgetValue, params) { - this._rpc({ - route: '/shop/product/resequence-image', - params: { - image_res_model: this.recordModel, - image_res_id: this.recordId, - move: widgetValue, - }, + this.rpc('/shop/product/resequence-image', { + image_res_model: this.recordModel, + image_res_id: this.recordId, + move: widgetValue, }).then(() => this.trigger_up('request_save', {reload: true, optionSelector: '#product_detail_main'})); }, /** diff --git a/addons/website_sale/static/src/js/website_sale.js b/addons/website_sale/static/src/js/website_sale.js index 772795cbf8f..10ded89a840 100644 --- a/addons/website_sale/static/src/js/website_sale.js +++ b/addons/website_sale/static/src/js/website_sale.js @@ -60,6 +60,8 @@ export const WebsiteSale = publicWidget.Widget.extend(VariantMixin, cartHandlerM delete this.events['change .main_product:not(.in_cart) input.js_quantity']; delete this.events['change [data-attribute_exclusions]']; + + this.rpc = this.bindService("rpc"); }, /** * @override @@ -202,14 +204,11 @@ export const WebsiteSale = publicWidget.Widget.extend(VariantMixin, cartHandlerM }); $input.data('update_change', true); - this._rpc({ - route: "/shop/cart/update_json", - params: { - line_id: line_id, - product_id: parseInt($input.data('product-id'), 10), - set_qty: value, - display: true, - }, + this.rpc("/shop/cart/update_json", { + line_id: line_id, + product_id: parseInt($input.data('product-id'), 10), + set_qty: value, + display: true, }).then(function (data) { $input.data('update_change', false); var check_value = parseInt($input.val() || 0, 10); @@ -239,11 +238,8 @@ export const WebsiteSale = publicWidget.Widget.extend(VariantMixin, cartHandlerM if (!$("#country_id").val()) { return; } - this._rpc({ - route: "/shop/country_infos/" + $("#country_id").val(), - params: { - mode: $("#country_id").attr('mode'), - }, + this.rpc("/shop/country_infos/" + $("#country_id").val(), { + mode: $("#country_id").attr('mode'), }).then(function (data) { // placeholder phone_code $("input[name='phone']").attr('placeholder', data.phone_code !== 0 ? '+'+ data.phone_code : ''); @@ -772,11 +768,8 @@ publicWidget.registry.WebsiteSaleLayout = publicWidget.Widget.extend({ var clickedValue = $(ev.target).val(); var isList = clickedValue === 'list'; if (!this.editableMode) { - this._rpc({ - route: '/shop/save_shop_layout_mode', - params: { - 'layout_mode': isList ? 'list' : 'grid', - }, + this.rpc('/shop/save_shop_layout_mode', { + 'layout_mode': isList ? 'list' : 'grid', }); } diff --git a/addons/website_sale/static/src/js/website_sale_delivery.js b/addons/website_sale/static/src/js/website_sale_delivery.js index 0dccb5e5b39..25d8b2de34f 100644 --- a/addons/website_sale/static/src/js/website_sale_delivery.js +++ b/addons/website_sale/static/src/js/website_sale_delivery.js @@ -17,6 +17,11 @@ publicWidget.registry.websiteSaleDelivery = publicWidget.Widget.extend({ "click .o_payment_option_card": "_onClickPaymentMethod" }, + init() { + this._super(...arguments); + this.rpc = this.bindService("rpc"); + }, + /** * @override */ @@ -53,9 +58,7 @@ publicWidget.registry.websiteSaleDelivery = publicWidget.Widget.extend({ * @private */ _getCurrentLocation: async function () { - const data = await this._rpc({ - route: "/shop/access_point/get", - }) + const data = await this.rpc("/shop/access_point/get"); const carriers = document.querySelectorAll('.o_delivery_carrier_select') for (let carrier of carriers) { const deliveryType = carrier.querySelector('input[type="radio"]').getAttribute("delivery_type"); @@ -125,11 +128,8 @@ publicWidget.registry.websiteSaleDelivery = publicWidget.Widget.extend({ * @params {Object} carrier: The carrier element */ _getCarrierRateShipment: async function(carrierInput) { - const result = await this._rpc({ - route: '/shop/carrier_rate_shipment', - params: { - 'carrier_id': carrierInput.value, - } + const result = await this.rpc('/shop/carrier_rate_shipment', { + 'carrier_id': carrierInput.value, }); this._handleCarrierUpdateResultBadge(result); }, @@ -138,11 +138,8 @@ publicWidget.registry.websiteSaleDelivery = publicWidget.Widget.extend({ * @param {Object} result */ _handleCarrierUpdateResult: async function (carrierInput) { - const result = await this._rpc({ - route: '/shop/update_carrier', - params: { - 'carrier_id': carrierInput.value, - }, + const result = await this.rpc('/shop/update_carrier', { + 'carrier_id': carrierInput.value, }) this.result = result; this._handleCarrierUpdateResultBadge(result); @@ -216,11 +213,8 @@ publicWidget.registry.websiteSaleDelivery = publicWidget.Widget.extend({ _checkCarrier: async function (ev, carrier_id) { ev.stopPropagation(); - await this.keepLast.add(this._rpc({ - route: '/shop/update_carrier', - params: { - carrier_id: carrier_id, - }, + await this.keepLast.add(this.rpc('/shop/update_carrier', { + carrier_id: carrier_id, })) var closestDocElement = ev.currentTarget.closest('.o_delivery_carrier_select'); var radio = closestDocElement.querySelector('input[type="radio"]'); @@ -240,11 +234,8 @@ publicWidget.registry.websiteSaleDelivery = publicWidget.Widget.extend({ return; } const carrier_id = carrierChecked?.querySelector('input')?.value; - const result = await this._rpc({ - route: '/shop/update_carrier', - params: { - 'carrier_id': carrier_id, - }, + const result = await this.rpc('/shop/update_carrier', { + 'carrier_id': carrier_id, }) this._enableButton(result.status); }, @@ -311,11 +302,8 @@ publicWidget.registry.websiteSaleDelivery = publicWidget.Widget.extend({ */ _onClickRemoveLocation: async function (ev) { ev.stopPropagation(); - await this._rpc({ - route: "/shop/access_point/set", - params: { - access_point_encoded: null, - }, + await this.rpc("/shop/access_point/set", { + access_point_encoded: null, }) const deliveryTypeInput = ev.currentTarget.closest(".o_delivery_carrier_select").querySelector('input[name="delivery_type"]'); const deliveryTypeId = deliveryTypeInput.value; @@ -346,9 +334,7 @@ publicWidget.registry.websiteSaleDelivery = publicWidget.Widget.extend({ const deliveryTypeId = deliveryTypeInput.value; await this._checkCarrier(ev,deliveryTypeId) $(renderToElement(deliveryType + "_pickup_location_loading")).appendTo($(modal)); - const data = await this._rpc({ - route: "/shop/access_point/close_locations", - }) + const data = await this.rpc("/shop/access_point/close_locations"); if (modal.firstChild){ modal.firstChild.remove(); } @@ -398,23 +384,17 @@ publicWidget.registry.websiteSaleDelivery = publicWidget.Widget.extend({ await this._checkCarrier(ev,carrierId) const modal = ev.target.closest(".o_list_pickup_locations"); const encodedLocation = ev.target.previousElementSibling.innerText; - await this._rpc({ - route: "/shop/access_point/set", - params: { - access_point_encoded: encodedLocation, - }, + await this.rpc("/shop/access_point/set", { + access_point_encoded: encodedLocation, }) while (modal.firstChild) { modal.lastChild.remove(); } await this._getCurrentLocation(); document.querySelectorAll('.error_no_pick_up_point').forEach(el => el.remove()); - const result = await this._rpc({ - route: '/shop/update_carrier', - params: { - 'carrier_id': carrierId, - 'no_reset_access_point_address': true, - }, + const result = await this.rpc('/shop/update_carrier', { + 'carrier_id': carrierId, + 'no_reset_access_point_address': true, }) this._enableButton(result.status); }, diff --git a/addons/website_sale/static/src/js/website_sale_recently_viewed.js b/addons/website_sale/static/src/js/website_sale_recently_viewed.js index 5a933c3c45a..9a034c2659a 100644 --- a/addons/website_sale/static/src/js/website_sale_recently_viewed.js +++ b/addons/website_sale/static/src/js/website_sale_recently_viewed.js @@ -17,6 +17,7 @@ publicWidget.registry.productsRecentlyViewedUpdate = publicWidget.Widget.extend( init: function () { this._super.apply(this, arguments); this._onProductChange = debounce(this._onProductChange, this.debounceValue); + this.rpc = this.bindService("rpc"); }, //-------------------------------------------------------------------------- @@ -40,11 +41,8 @@ publicWidget.registry.productsRecentlyViewedUpdate = publicWidget.Widget.extend( if ($(this.el).find('.js_product.css_not_available').length) { return; // Variant not possible } - this._rpc({ - route: '/shop/products/recently_viewed_update', - params: { - product_id: productId, - } + this.rpc('/shop/products/recently_viewed_update', { + product_id: productId, }).then(function (res) { setCookie(cookieName, productId, 30 * 60, 'optional'); }); diff --git a/addons/website_sale/static/src/js/website_sale_utils.js b/addons/website_sale/static/src/js/website_sale_utils.js index bbedc921518..9a35736b4ef 100644 --- a/addons/website_sale/static/src/js/website_sale_utils.js +++ b/addons/website_sale/static/src/js/website_sale_utils.js @@ -28,14 +28,11 @@ export const cartHandlerMixin = { * @private */ async _addToCartInPage(params) { - const data = await this._rpc({ - route: "/shop/cart/update_json", - params: { - ...params, - display: false, - force_create: true, - }, - }) + const data = await this.rpc("/shop/cart/update_json", { + ...params, + display: false, + force_create: true, + }); if (data.cart_quantity && (data.cart_quantity !== parseInt($(".my_cart_quantity").text()))) { updateCartNavBar(data); showCartNotification(this.call.bind(this), data.notification_info); diff --git a/addons/website_sale/static/src/snippets/s_add_to_cart/000.js b/addons/website_sale/static/src/snippets/s_add_to_cart/000.js index 726c5a8337d..911f020236b 100644 --- a/addons/website_sale/static/src/snippets/s_add_to_cart/000.js +++ b/addons/website_sale/static/src/snippets/s_add_to_cart/000.js @@ -25,11 +25,8 @@ publicWidget.registry.AddToCartSnippet = WebsiteSale.extend(cartHandlerMixin, { if (visitorChoice) { this._handleAdd($(ev.currentTarget.closest('div'))); } else { - const isAddToCartAllowed = await this._rpc({ - route: `/shop/product/is_add_to_cart_allowed`, - params: { - product_id: productId, - }, + const isAddToCartAllowed = await this.rpc(`/shop/product/is_add_to_cart_allowed`, { + product_id: productId, }); if (!isAddToCartAllowed) { this.displayNotification({ diff --git a/addons/website_sale/static/src/snippets/s_add_to_cart/options.js b/addons/website_sale/static/src/snippets/s_add_to_cart/options.js index e2afac2c4ba..c50c8ed30c6 100644 --- a/addons/website_sale/static/src/snippets/s_add_to_cart/options.js +++ b/addons/website_sale/static/src/snippets/s_add_to_cart/options.js @@ -30,6 +30,11 @@ options.registry.AddToCart = options.Class.extend({ 'click .reset-product-picker': '_onClickResetProductPicker', }), + init() { + this._super(...arguments); + this.orm = this.bindService("orm"); + }, + async updateUI() { if (this.rerender) { this.rerender = false; @@ -92,13 +97,9 @@ options.registry.AddToCart = options.Class.extend({ * Fetches the variants ids from the server */ async _fetchVariants(productTemplateId) { - const response = await this._rpc({ - model: 'product.product', - method: 'search_read', - domain: [ - ["product_tmpl_id", "=", parseInt(productTemplateId)], - ], - }); + const response = await this.orm.searchRead("product.product", [ + ["product_tmpl_id", "=", parseInt(productTemplateId)], + ]); this.$target[0].dataset.variants = response.map(variant => variant.id); }, diff --git a/addons/website_sale/static/src/snippets/s_dynamic_snippet_products/000.js b/addons/website_sale/static/src/snippets/s_dynamic_snippet_products/000.js index a683f1dca19..0e09194c777 100644 --- a/addons/website_sale/static/src/snippets/s_dynamic_snippet_products/000.js +++ b/addons/website_sale/static/src/snippets/s_dynamic_snippet_products/000.js @@ -115,6 +115,7 @@ const DynamicSnippetProductsCard = publicWidget.Widget.extend({ init(root, options) { const parent = options.parent || root; this._super(parent, options); + this.rpc = this.bindService("rpc"); }, start() { @@ -132,13 +133,10 @@ const DynamicSnippetProductsCard = publicWidget.Widget.extend({ */ async _onClickAddToCart(ev) { const $card = $(ev.currentTarget).closest('.card'); - const data = await this._rpc({ - route: "/shop/cart/update_json", - params: { - product_id: $card.find('input[data-product-id]').data('product-id'), - add_qty: 1, - display: false, - }, + const data = await this.rpc("/shop/cart/update_json", { + product_id: $card.find('input[data-product-id]').data('product-id'), + add_qty: 1, + display: false, }); wSaleUtils.updateCartNavBar(data); wSaleUtils.showCartNotification(this.call.bind(this), data.notification_info); @@ -156,11 +154,8 @@ const DynamicSnippetProductsCard = publicWidget.Widget.extend({ */ async _onRemoveFromRecentlyViewed(ev) { const $card = $(ev.currentTarget).closest('.card'); - await this._rpc({ - route: "/shop/products/recently_viewed_delete", - params: { - product_id: $card.find('input[data-product-id]').data('product-id'), - }, + await this.rpc("/shop/products/recently_viewed_delete", { + product_id: $card.find('input[data-product-id]').data('product-id'), }); this.trigger_up('widgets_start_request', { $target: this.$el.closest('.s_dynamic'), diff --git a/addons/website_sale/static/src/snippets/s_dynamic_snippet_products/options.js b/addons/website_sale/static/src/snippets/s_dynamic_snippet_products/options.js index a488b2eee4c..b6f6533f6a8 100644 --- a/addons/website_sale/static/src/snippets/s_dynamic_snippet_products/options.js +++ b/addons/website_sale/static/src/snippets/s_dynamic_snippet_products/options.js @@ -27,6 +27,8 @@ const dynamicSnippetProductsOptions = s_dynamic_snippet_carousel_options.extend( } this.productCategories = {}; this.isAlternativeProductSnippet = this.$target.hasClass('o_wsale_alternative_products'); + + this.orm = this.bindService("orm"); }, //-------------------------------------------------------------------------- // Private @@ -48,14 +50,7 @@ const dynamicSnippetProductsOptions = s_dynamic_snippet_carousel_options.extend( * @returns {Promise} */ _fetchProductCategories: function () { - return this._rpc({ - model: 'product.public.category', - method: 'search_read', - kwargs: { - domain: wUtils.websiteDomain(this), - fields: ['id', 'name'], - } - }); + return this.orm.searchRead("product.public.category", wUtils.websiteDomain(this), ["id", "name"]); }, /** * diff --git a/addons/website_sale/static/tests/tours/website_sale_complete_flow.js b/addons/website_sale/static/tests/tours/website_sale_complete_flow.js index 4264d760247..a2f158f036a 100644 --- a/addons/website_sale/static/tests/tours/website_sale_complete_flow.js +++ b/addons/website_sale/static/tests/tours/website_sale_complete_flow.js @@ -1,7 +1,7 @@ /** @odoo-module **/ + import { jsonrpc } from "@web/core/network/rpc_service"; import { registry } from "@web/core/registry"; - import rpc from "@web/legacy/js/core/rpc"; import tourUtils from "@website_sale/js/tours/tour_utils"; registry.category("web_tour.tours").add('website_sale_tour_1', { @@ -173,19 +173,21 @@ extra_trigger: '.o_frontend_to_backend_nav', // Check if the user is connected trigger: '#wrapwrap', run: function () { - var def1 = rpc.query({ - model: 'res.config.settings', - method: 'create', + var def1 = jsonrpc(`/web/dataset/call_kw/res.config.settings/create`, { + model: "res.config.settings", + method: "create", args: [{ 'auth_signup_uninvited': 'b2b', 'show_line_subtotals_tax_selection': 'tax_included', }], + kwargs: {}, }); var def2 = def1.then(function (resId) { - return rpc.query({ - model: 'res.config.settings', - method: 'execute', + return jsonrpc(`/web/dataset/call_kw/res.config.settings/execute`, { + model: "res.config.settings", + method: "execute", args: [[resId]], + kwargs: {}, }); }); def2.then(function () { diff --git a/addons/website_sale/static/tests/tours/website_sale_shop_mail.js b/addons/website_sale/static/tests/tours/website_sale_shop_mail.js index 72c0cc1414e..d0586ee02a7 100644 --- a/addons/website_sale/static/tests/tours/website_sale_shop_mail.js +++ b/addons/website_sale/static/tests/tours/website_sale_shop_mail.js @@ -1,8 +1,8 @@ /** @odoo-module **/ -import rpc from "@web/legacy/js/core/rpc"; import { registry } from "@web/core/registry"; import tourUtils from "@website_sale/js/tours/tour_utils"; +import { jsonrpc } from "@web/core/network/rpc_service"; registry.category("web_tour.tours").add('shop_mail', { test: true, @@ -14,22 +14,24 @@ registry.category("web_tour.tours").add('shop_mail', { run: function () { // We change the domain of the website to test that the email that // will be sent uses the correct domain for its links. - var def1 = rpc.query({ + var def1 = jsonrpc("/web/dataset/call_kw/website/write", { 'model': 'website', 'method': 'write', 'args': [[1], { 'domain': "my-test-domain.com", }], + kwargs: {}, }); // We need to change the domain of all the websites otherwise the // website selector will return the website 2 since the domain we // set on website 1 doesn't actually match our test server. - var def2 = rpc.query({ + var def2 = jsonrpc("/web/dataset/call_kw/website/write", { 'model': 'website', 'method': 'write', 'args': [[2], { 'domain': "https://domain-not-used.fr", }], + kwargs: {}, }); Promise.all([def1, def2]).then(function (data) { window.location.href = '/shop?search=Acoustic Bloc Screens'; diff --git a/addons/website_sale_autocomplete/static/src/js/address_form.js b/addons/website_sale_autocomplete/static/src/js/address_form.js index 010dd372e99..d9ca6b0cc74 100644 --- a/addons/website_sale_autocomplete/static/src/js/address_form.js +++ b/addons/website_sale_autocomplete/static/src/js/address_form.js @@ -22,6 +22,8 @@ publicWidget.registry.AddressForm = publicWidget.Widget.extend({ this._onChangeStreet = debounce(this._onChangeStreet, 200); this._super.apply(this, arguments); + + this.rpc = this.bindService("rpc"); }, /** @@ -47,12 +49,9 @@ publicWidget.registry.AddressForm = publicWidget.Widget.extend({ const inputContainer = ev.currentTarget.parentNode; if (ev.currentTarget.value.length >= 5) { this.keepLast.add( - this._rpc({ - route: '/autocomplete/address', - params: { - partial_address: ev.currentTarget.value, - session_id: this.sessionId || null - } + this.rpc('/autocomplete/address', { + partial_address: ev.currentTarget.value, + session_id: this.sessionId || null })).then((response) => { this._hideAutocomplete(inputContainer); inputContainer.appendChild(renderToElement("website_sale_autocomplete.AutocompleteDropDown", { @@ -77,13 +76,10 @@ publicWidget.registry.AddressForm = publicWidget.Widget.extend({ spinner.classList.add('spinner-border', 'text-warning', 'text-center', 'm-auto'); dropDown.appendChild(spinner); - const address = await this._rpc({ - route: '/autocomplete/address_full', - params: { - address: ev.currentTarget.innerText, - google_place_id: ev.currentTarget.dataset.googlePlaceId, - session_id: this.sessionId || null - } + const address = await this.rpc('/autocomplete/address_full', { + address: ev.currentTarget.innerText, + google_place_id: ev.currentTarget.dataset.googlePlaceId, + session_id: this.sessionId || null }); if (address.formatted_street_number) { this.streetAndNumberInput.value = address.formatted_street_number; diff --git a/addons/website_sale_comparison/static/src/js/website_sale_comparison.js b/addons/website_sale_comparison/static/src/js/website_sale_comparison.js index 1d3aab74c1c..15b9c0a8fc4 100644 --- a/addons/website_sale_comparison/static/src/js/website_sale_comparison.js +++ b/addons/website_sale_comparison/static/src/js/website_sale_comparison.js @@ -29,6 +29,7 @@ var ProductComparison = publicWidget.Widget.extend(VariantMixin, { this.comparelist_product_ids = JSON.parse(getCookie('comparelist_product_ids') || '[]'); this.product_compare_limit = 4; this.guard = new Mutex(); + this.rpc = this.bindService("rpc"); }, /** * @override @@ -135,12 +136,9 @@ var ProductComparison = publicWidget.Widget.extend(VariantMixin, { */ _loadProducts: function (product_ids) { var self = this; - return this._rpc({ - route: '/shop/get_product_data', - params: { - product_ids: product_ids, - cookies: JSON.parse(getCookie('comparelist_product_ids') || '[]'), - }, + return this.rpc('/shop/get_product_data', { + product_ids: product_ids, + cookies: JSON.parse(getCookie('comparelist_product_ids') || '[]'), }).then(function (data) { self.comparelist_product_ids = JSON.parse(data.cookies); delete data.cookies; @@ -267,6 +265,10 @@ publicWidget.registry.ProductComparison = publicWidget.Widget.extend(cartHandler 'submit .o_add_cart_form_compare': '_onFormSubmit', }, + init() { + this._super(...arguments); + this.rpc = this.bindService("rpc"); + }, /** * @override */ diff --git a/addons/website_sale_loyalty/static/tests/tours/test_promo_main_tour.js b/addons/website_sale_loyalty/static/tests/tours/test_promo_main_tour.js index 9f918e55548..7867c943f7c 100644 --- a/addons/website_sale_loyalty/static/tests/tours/test_promo_main_tour.js +++ b/addons/website_sale_loyalty/static/tests/tours/test_promo_main_tour.js @@ -1,7 +1,7 @@ /** @odoo-module **/ +import { jsonrpc } from "@web/core/network/rpc_service"; import { registry } from "@web/core/registry"; -import ajax from '@web/legacy/js/core/ajax'; import tourUtils from '@website_sale/js/tours/tour_utils'; registry.category("web_tour.tours").add('shop_sale_loyalty', { @@ -49,7 +49,7 @@ registry.category("web_tour.tours").add('shop_sale_loyalty', { content: "go to shop", trigger: '.td-product_name:contains("10.0% discount on total amount")', run: function () { - ajax.jsonRpc('/web/dataset/call_kw', 'call', { + jsonrpc('/web/dataset/call_kw/account.tax/create', { model: 'account.tax', method: 'create', args: [{ @@ -58,7 +58,7 @@ registry.category("web_tour.tours").add('shop_sale_loyalty', { }], kwargs: {}, }).then(function (tax_id) { - ajax.jsonRpc('/web/dataset/call_kw', 'call', { + jsonrpc('/web/dataset/call_kw/product.template/create', { model: 'product.template', method: 'create', args: [{ diff --git a/addons/website_sale_mondialrelay/static/src/js/website_sale_mondialrelay.js b/addons/website_sale_mondialrelay/static/src/js/website_sale_mondialrelay.js index d8a8b624f45..d32d81e36de 100644 --- a/addons/website_sale_mondialrelay/static/src/js/website_sale_mondialrelay.js +++ b/addons/website_sale_mondialrelay/static/src/js/website_sale_mondialrelay.js @@ -110,11 +110,8 @@ WebsiteSaleDeliveryWidget.include({ if (!this.lastRelaySelected) { return; } - this._rpc({ - route: '/website_sale_mondialrelay/update_shipping', - params: { - ...this.lastRelaySelected, - }, + this.rpc('/website_sale_mondialrelay/update_shipping', { + ...this.lastRelaySelected, }).then((o) => { $('#address_on_payment').html(o.address); this.$modal_mondialrelay.modal('hide'); diff --git a/addons/website_sale_product_configurator/static/src/js/sale_product_configurator_modal.js b/addons/website_sale_product_configurator/static/src/js/sale_product_configurator_modal.js index 1474c846708..a1cbea722e5 100644 --- a/addons/website_sale_product_configurator/static/src/js/sale_product_configurator_modal.js +++ b/addons/website_sale_product_configurator/static/src/js/sale_product_configurator_modal.js @@ -1,12 +1,11 @@ /** @odoo-module */ -import ajax from '@web/legacy/js/core/ajax'; import Dialog from '@web/legacy/js/core/dialog'; -import ServicesMixin from '@web/legacy/js/core/service_mixins'; import VariantMixin from '@website_sale/js/sale_variant_mixin'; import { uniqueId } from '@web/core/utils/functions'; +import { jsonrpc } from '@web/core/network/rpc_service'; -export const OptionalProductsModal = Dialog.extend(ServicesMixin, VariantMixin, { +export const OptionalProductsModal = Dialog.extend(VariantMixin, { events: Object.assign({}, Dialog.prototype.events, VariantMixin.events, { 'click a.js_add, a.js_remove': '_onAddOrRemoveOption', 'click button.js_add_cart_json': 'onClickAddCartJSON', @@ -73,6 +72,8 @@ export const OptionalProductsModal = Dialog.extend(ServicesMixin, VariantMixin, self.$el.closest('.modal-content').css('min-height', self.previousModalHeight + 'px'); } }); + + this.rpc = this.bindService("rpc"); }, /** * @override @@ -80,22 +81,19 @@ export const OptionalProductsModal = Dialog.extend(ServicesMixin, VariantMixin, willStart: function () { var self = this; - var getModalContent = ajax.jsonRpc( - '/sale_product_configurator/show_advanced_configurator', - 'call', - { - mode: self.mode, - product_id: self.rootProduct.product_id, - variant_values: self.rootProduct.variant_values, - product_custom_attribute_values: self.rootProduct.product_custom_attribute_values, - pricelist_id: self.pricelistId || false, - add_qty: self.rootProduct.quantity, - force_dialog: self.forceDialog, - no_attribute: self.rootProduct.no_variant_attribute_values, - custom_attribute: self.rootProduct.product_custom_attribute_values, - context: Object.assign({'quantity': self.rootProduct.quantity}, this.context), - } - ).then(function (modalContent) { + var getModalContent = jsonrpc("/sale_product_configurator/show_advanced_configurator", { + mode: self.mode, + product_id: self.rootProduct.product_id, + variant_values: self.rootProduct.variant_values, + product_custom_attribute_values: self.rootProduct.product_custom_attribute_values, + pricelist_id: self.pricelistId || false, + add_qty: self.rootProduct.quantity, + force_dialog: self.forceDialog, + no_attribute: self.rootProduct.no_variant_attribute_values, + custom_attribute: self.rootProduct.product_custom_attribute_values, + context: Object.assign({'quantity': self.rootProduct.quantity}, this.context), + }) + .then(function (modalContent) { if (modalContent) { var $modalContent = $(modalContent); $modalContent = self._postProcessContent($modalContent); @@ -394,14 +392,10 @@ export const OptionalProductsModal = Dialog.extend(ServicesMixin, VariantMixin, ).then(function (productId) { $parent.find('.product_id').val(productId); - ajax.jsonRpc( - '/sale_product_configurator/optional_product_items', - 'call', - { - 'product_id': productId, - 'pricelist_id': self.pricelistId || false, - } - ).then(function (addedItem) { + jsonrpc("/sale_product_configurator/optional_product_items", { + 'product_id': productId, + 'pricelist_id': self.pricelistId || false, + }).then(function (addedItem) { var $addedItem = $(addedItem); $modal.find('tr:last').after($addedItem); diff --git a/addons/website_sale_product_configurator/static/src/js/website_sale_options.js b/addons/website_sale_product_configurator/static/src/js/website_sale_options.js index c0a5ac300a2..f347732d667 100644 --- a/addons/website_sale_product_configurator/static/src/js/website_sale_options.js +++ b/addons/website_sale_product_configurator/static/src/js/website_sale_options.js @@ -104,12 +104,9 @@ publicWidget.registry.WebsiteSale.include({ this.optionalProductsModal.getAndCreateSelectedProducts() .then((products) => { const productAndOptions = JSON.stringify(products); - this._rpc({ - route: '/shop/cart/update_option', - params: { - product_and_options: productAndOptions, - ...this._getOptionalCombinationInfoParam(), - }, + this.rpc('/shop/cart/update_option', { + product_and_options: productAndOptions, + ...this._getOptionalCombinationInfoParam(), }).then(function (values) { if (goToShop) { window.location.pathname = "/shop/cart"; @@ -119,7 +116,7 @@ publicWidget.registry.WebsiteSale.include({ } }).then(() => { this._getCombinationInfo($.Event('click', {target: $("#add_to_cart")})); - });; + }); }); }, }); diff --git a/addons/website_sale_stock/static/src/js/website_sale.js b/addons/website_sale_stock/static/src/js/website_sale.js index 5c978b09431..7bcf188bd0a 100644 --- a/addons/website_sale_stock/static/src/js/website_sale.js +++ b/addons/website_sale_stock/static/src/js/website_sale.js @@ -38,12 +38,9 @@ WebsiteSale.include({ return this._displayEmailIncorrectMessage(stockNotificationEl); } - this._rpc({ - route: "/shop/add/stock_notification", - params: { - product_id: productId, - email, - }, + this.rpc("/shop/add/stock_notification", { + product_id: productId, + email, }).then((data) => { const message = stockNotificationEl.querySelector('#stock_notification_success_message'); diff --git a/addons/website_sale_wishlist/static/src/js/website_sale_wishlist.js b/addons/website_sale_wishlist/static/src/js/website_sale_wishlist.js index df8f8c8e08c..f30a99878dc 100644 --- a/addons/website_sale_wishlist/static/src/js/website_sale_wishlist.js +++ b/addons/website_sale_wishlist/static/src/js/website_sale_wishlist.js @@ -24,6 +24,7 @@ publicWidget.registry.ProductWishlist = publicWidget.Widget.extend(VariantMixin, init: function (parent) { this._super.apply(this, arguments); this.wishlistProductIDs = JSON.parse(sessionStorage.getItem('website_sale_wishlist_product_ids') || '[]'); + this.rpc = this.bindService("rpc"); }, /** * Gets the current wishlist items. @@ -97,11 +98,8 @@ publicWidget.registry.ProductWishlist = publicWidget.Widget.extend(VariantMixin, productId = parseInt(productId, 10); if (productId && !self.wishlistProductIDs.includes(productId)) { - return self._rpc({ - route: '/shop/wishlist/add', - params: { - product_id: productId, - }, + return self.rpc('/shop/wishlist/add', { + product_id: productId, }).then(function () { var $navButton = $('header .o_wsale_my_wish').first(); self.wishlistProductIDs.push(productId); @@ -145,9 +143,7 @@ publicWidget.registry.ProductWishlist = publicWidget.Widget.extend(VariantMixin, var product = tr.data('product-id'); var self = this; - this._rpc({ - route: '/shop/wishlist/remove/' + wish, - }).then(function () { + this.rpc('/shop/wishlist/remove/' + wish).then(function () { $(tr).hide(); }); @@ -189,12 +185,9 @@ publicWidget.registry.ProductWishlist = publicWidget.Widget.extend(VariantMixin, $tr.trigger('add_to_cart_event', [productTrackingInfo]); } const callService = this.call.bind(this) - return this._rpc({ - route: "/shop/cart/update_json", - params: { - ...this._getCartUpdateJsonParams(productID, qty), - display: false, - }, + return this.rpc("/shop/cart/update_json", { + ...this._getCartUpdateJsonParams(productID, qty), + display: false, }).then(function (data) { wSaleUtils.updateCartNavBar(data); wSaleUtils.showCartNotification(callService, data.notification_info); diff --git a/addons/website_sale_wishlist/static/tests/tours/website_sale_wishlist.js b/addons/website_sale_wishlist/static/tests/tours/website_sale_wishlist.js index fa8d52583be..d25a61c0c8e 100644 --- a/addons/website_sale_wishlist/static/tests/tours/website_sale_wishlist.js +++ b/addons/website_sale_wishlist/static/tests/tours/website_sale_wishlist.js @@ -1,7 +1,7 @@ /** @odoo-module **/ -import rpc from "@web/legacy/js/core/rpc"; import { registry } from "@web/core/registry"; +import { jsonrpc } from "@web/core/network/rpc_service"; registry.category("web_tour.tours").add('shop_wishlist', { test: true, @@ -128,7 +128,7 @@ registry.category("web_tour.tours").add('shop_wishlist', { content: "Create a product with dynamic attribute and its values.", trigger: 'body', run: function () { - rpc.query({ + jsonrpc("/web/dataset/call_kw/product.attribute/create", { model: 'product.attribute', method: 'create', args: [{ @@ -136,8 +136,9 @@ registry.category("web_tour.tours").add('shop_wishlist', { 'display_type': 'color', 'create_variant': 'dynamic' }], + kwargs: {}, }).then(function (attributeId) { - return rpc.query({ + return jsonrpc("/web/dataset/call_kw/product.template/create", { model: 'product.template', method: 'create', args: [{ @@ -161,6 +162,7 @@ registry.category("web_tour.tours").add('shop_wishlist', { ] }]], }], + kwargs: {}, }); }).then(function () { window.location.href = '/web/session/logout?redirect=/shop?search=Bottle'; @@ -239,16 +241,18 @@ registry.category("web_tour.tours").add('shop_wishlist', { content: "Archive the first variant", trigger: '#top_menu:contains("Mitchell Admin")', run: function () { - rpc.query({ + jsonrpc("/web/dataset/call_kw/product.product/search", { model: 'product.product', method: 'search', args: [[['name', '=', "Bottle"]]], + kwargs: {}, }) .then(function (productIds) { - return rpc.query({ + return jsonrpc("/web/dataset/call_kw/product.product/write", { model: 'product.product', method: 'write', args: [productIds[0], {active: false}], + kwargs: {}, }); }) .then(function () { @@ -305,16 +309,18 @@ registry.category("web_tour.tours").add('shop_wishlist', { content: "Archive all variants", trigger: '#top_menu:contains("Mitchell Admin")', run: function () { - rpc.query({ + jsonrpc("/web/dataset/call_kw/product.product/search", { model: 'product.product', method: 'search', args: [[['name', '=', "Bottle"]]], + kwargs: {}, }) .then(function (productIds) { - return rpc.query({ + return jsonrpc("/web/dataset/call_kw/product.product/write", { model: 'product.product', method: 'write', args: [productIds, {active: false}], + kwargs: {}, }); }) .then(function () { diff --git a/addons/website_slides/__manifest__.py b/addons/website_slides/__manifest__.py index e19a470513e..6a418d386fe 100644 --- a/addons/website_slides/__manifest__.py +++ b/addons/website_slides/__manifest__.py @@ -186,14 +186,12 @@ Featuring 'web/static/src/legacy/js/libs/jquery.js', 'website/static/src/libs/zoomodoo/zoomodoo.js', 'web/static/src/legacy/js/libs/jSignatureCustom.js', - 'web/static/src/legacy/js/core/ajax.js', 'web/static/src/legacy/js/core/bus.js', 'web/static/src/legacy/js/core/class.js', 'web/static/src/legacy/js/core/dialog.js', 'web/static/src/legacy/xml/dialog.xml', 'web/static/src/legacy/js/core/dom.js', 'web/static/src/legacy/js/core/mixins.js', - 'web/static/src/legacy/js/core/rpc.js', 'web/static/src/legacy/js/core/service_mixins.js', 'web/static/src/legacy/js/core/time.js', 'web/static/src/legacy/js/core/widget.js', diff --git a/addons/website_slides/static/src/js/slides_category_delete.js b/addons/website_slides/static/src/js/slides_category_delete.js index 700d9c36517..6a8e70200b4 100644 --- a/addons/website_slides/static/src/js/slides_category_delete.js +++ b/addons/website_slides/static/src/js/slides_category_delete.js @@ -10,6 +10,11 @@ publicWidget.registry.websiteSlidesCategoryDelete = publicWidget.Widget.extend({ click: "_onClickDeleteCateogry", }, + init() { + this._super(...arguments); + this.orm = this.bindService("orm"); + }, + //-------------------------------------------------------------------------- // Handlers //-------------------------------------------------------------------------- @@ -29,11 +34,7 @@ publicWidget.registry.websiteSlidesCategoryDelete = publicWidget.Widget.extend({ * Calls 'unlink' method on slides.slide to delete the category and * reloads page after deletion to re-arrange the content on UI */ - await this._rpc({ - model: "slide.slide", - method: "unlink", - args: [categoryId], - }); + await this.orm.unlink("slide.slide", [categoryId]); window.location.reload(); }, cancel: () => {}, diff --git a/addons/website_slides/static/src/js/slides_course_enroll_email.js b/addons/website_slides/static/src/js/slides_course_enroll_email.js index cc71b4b2d16..ad9b8732f2b 100644 --- a/addons/website_slides/static/src/js/slides_course_enroll_email.js +++ b/addons/website_slides/static/src/js/slides_course_enroll_email.js @@ -10,6 +10,10 @@ export const WebsiteSlidesEnroll = publicWidget.Widget.extend({ events: { "click .o_wslides_js_channel_enroll": "_onSendRequestClick", }, + init() { + this._super(...arguments); + this.orm = this.bindService("orm"); + }, async _onSendRequestClick(ev) { ev.preventDefault(); const clickedEl = ev.currentTarget; @@ -23,11 +27,11 @@ export const WebsiteSlidesEnroll = publicWidget.Widget.extend({ cancel: () => {}, // show cancel button }) ); - const { error, done } = await this._rpc({ - model: "slide.channel", - method: "action_request_access", - args: [channelId], - }); + const { error, done } = await this.orm.call( + "slide.channel", + "action_request_access", + [channelId], + ); const $alert = $(clickedEl.closest(".alert")); const message = done ? _t("Request sent!") : error || _t("Unknown error, try again."); $alert.replaceWith(` diff --git a/addons/website_slides/static/src/js/slides_course_fullscreen_player.js b/addons/website_slides/static/src/js/slides_course_fullscreen_player.js index 7c0374fbd88..0d88771ded7 100644 --- a/addons/website_slides/static/src/js/slides_course_fullscreen_player.js +++ b/addons/website_slides/static/src/js/slides_course_fullscreen_player.js @@ -383,6 +383,8 @@ this._super(parent, options); this.slide = slide; this.session = session; + + this.rpc = this.bindService("rpc"); }, //-------------------------------------------------------------------------- @@ -408,13 +410,10 @@ if (input.val()) { form.removeClass('o_has_error').find('.form-control, .form-select').removeClass('is-invalid'); var slideID = form.find('button').data('slide-id'); - this._rpc({ - route: '/slides/slide/send_share_email', - params: { - slide_id: slideID, - emails: input.val(), - fullscreen: true - }, + this.rpc('/slides/slide/send_share_email', { + slide_id: slideID, + emails: input.val(), + fullscreen: true }).then((action) => { if (action) { form.find('.alert-info').removeClass('d-none'); @@ -565,11 +564,8 @@ _fetchHtmlContent: function (){ var self = this; var currentSlide = this.get('slide'); - return self._rpc({ - route:"/slides/slide/get_html_content", - params: { - 'slide_id': currentSlide.id - } + return self.rpc("/slides/slide/get_html_content", { + 'slide_id': currentSlide.id }).then(function (data){ if (data.html_content) { currentSlide.htmlContent = data.html_content; diff --git a/addons/website_slides/static/src/js/slides_course_join.js b/addons/website_slides/static/src/js/slides_course_join.js index 7c76e1f33d1..0f861409ef4 100644 --- a/addons/website_slides/static/src/js/slides_course_join.js +++ b/addons/website_slides/static/src/js/slides_course_join.js @@ -48,6 +48,7 @@ var CourseJoinWidget = publicWidget.Widget.extend({ this.joinMessage = options.joinMessage || _t('Join this Course'); this.beforeJoin = options.beforeJoin || function () {return Promise.resolve();}; this.afterJoin = options.afterJoin || function () {document.location.reload();}; + this.rpc = this.bindService("rpc"); }, //-------------------------------------------------------------------------- @@ -125,11 +126,8 @@ var CourseJoinWidget = publicWidget.Widget.extend({ */ joinChannel: function (channelId) { var self = this; - this._rpc({ - route: '/slides/channel/join', - params: { - channel_id: channelId, - }, + this.rpc('/slides/channel/join', { + channel_id: channelId, }).then(function (data) { if (!data.error) { self.afterJoin(); diff --git a/addons/website_slides/static/src/js/slides_course_page.js b/addons/website_slides/static/src/js/slides_course_page.js index 150d5c5a299..e0d6bc5d50e 100644 --- a/addons/website_slides/static/src/js/slides_course_page.js +++ b/addons/website_slides/static/src/js/slides_course_page.js @@ -19,6 +19,11 @@ export const SlideCoursePage = publicWidget.Widget.extend({ 'slide_mark_completed': '_onSlideMarkCompleted', }, + init() { + this._super(...arguments); + this.rpc = this.bindService("rpc"); + }, + /** * Collapse the next category when the current one has just been completed */ @@ -116,10 +121,10 @@ export const SlideCoursePage = publicWidget.Widget.extend({ return; } - const data = await this._rpc({ - route: `/slides/slide/${completed ? 'set_completed' : 'set_uncompleted'}`, - params: {slide_id: slide.id}, - }); + const data = await this.rpc( + `/slides/slide/${completed ? 'set_completed' : 'set_uncompleted'}`, + {slide_id: slide.id}, + ); this.toggleCompletionButton(slide, completed); this.updateProgressbar(data.channel_completion); diff --git a/addons/website_slides/static/src/js/slides_course_quiz.js b/addons/website_slides/static/src/js/slides_course_quiz.js index 1f39f7c5d0f..230d4a1b6e6 100644 --- a/addons/website_slides/static/src/js/slides_course_quiz.js +++ b/addons/website_slides/static/src/js/slides_course_quiz.js @@ -72,6 +72,9 @@ this.userId = session.user_id; this.redirectURL = encodeURIComponent(document.URL); this.channel = channel_data; + + this.rpc = this.bindService("rpc"); + this.orm = this.bindService("orm"); }, /** @@ -175,12 +178,9 @@ * @private */ _reorderQuestions: function () { - this._rpc({ - route: '/web/dataset/resequence', - params: { - model: "slide.question", - ids: this._getQuestionsIds() - } + this.rpc('/web/dataset/resequence', { + model: "slide.question", + ids: this._getQuestionsIds() }).then(this._modifyQuestionsSequence.bind(this)) }, /* @@ -189,11 +189,8 @@ */ _fetchQuiz: function () { var self = this; - return self._rpc({ - route:'/slides/slide/quiz/get', - params: { - 'slide_id': self.slide.id, - } + return self.rpc('/slides/slide/quiz/get', { + 'slide_id': self.slide.id, }).then(function (quiz_data) { self.slide.sessionAnswers = quiz_data.session_answers; self.quiz = { @@ -357,12 +354,9 @@ * @private */ async _submitQuiz() { - const data = await this._rpc({ - route: '/slides/slide/quiz/submit', - params: { - slide_id: this.slide.id, - answer_ids: this._getQuizAnswers(), - } + const data = await this.rpc('/slides/slide/quiz/submit', { + slide_id: this.slide.id, + answer_ids: this._getQuizAnswers(), }); if (data.error) { this._showErrorMessage(data.error); @@ -470,11 +464,8 @@ * @private */ _onClickReset: function () { - this._rpc({ - route: '/slides/slide/quiz/reset', - params: { - slide_id: this.slide.id - } + this.rpc('/slides/slide/quiz/reset', { + slide_id: this.slide.id }).then(function () { window.location.reload(); }); @@ -488,11 +479,8 @@ _saveQuizAnswersToSession: function () { this._hideErrorMessage(); - return this._rpc({ - route: '/slides/slide/quiz/save_to_session', - params: { - 'quiz_answers': {'slide_id': this.slide.id, 'slide_answers': this._getQuizAnswers()}, - } + return this.rpc('/slides/slide/quiz/save_to_session', { + 'quiz_answers': {'slide_id': this.slide.id, 'slide_answers': this._getQuizAnswers()}, }); }, /** @@ -665,11 +653,7 @@ */ _onConfirmClick: function () { var self = this; - this._rpc({ - model: 'slide.question', - method: 'unlink', - args: [this.questionId], - }).then(function () { + this.orm.unlink("slide.question", [this.questionId]).then(function () { self.trigger_up('delete_question', { questionId: self.questionId }); self.close(); }); diff --git a/addons/website_slides/static/src/js/slides_course_quiz_question_form.js b/addons/website_slides/static/src/js/slides_course_quiz_question_form.js index 9fe2d1ed83a..368dc3c8273 100644 --- a/addons/website_slides/static/src/js/slides_course_quiz_question_form.js +++ b/addons/website_slides/static/src/js/slides_course_quiz_question_form.js @@ -33,6 +33,7 @@ var QuestionFormWidget = publicWidget.Widget.extend({ this.sequence = options.sequence; this.slideId = options.slideId; this._super.apply(this, arguments); + this.rpc = this.bindService("rpc"); }, /** @@ -150,10 +151,7 @@ var QuestionFormWidget = publicWidget.Widget.extend({ if (this._isValidForm($form)) { var values = this._serializeForm($form); - var renderedQuestion = await this._rpc({ - route: '/slides/slide/quiz/question_add_or_update', - params: values - }); + var renderedQuestion = await this.rpc('/slides/slide/quiz/question_add_or_update', values); if (typeof renderedQuestion === 'object' && renderedQuestion.error) { this.$('.o_wslides_js_quiz_validation_error') diff --git a/addons/website_slides/static/src/js/slides_course_slides_list.js b/addons/website_slides/static/src/js/slides_course_slides_list.js index 866e6d24722..000fdc44659 100644 --- a/addons/website_slides/static/src/js/slides_course_slides_list.js +++ b/addons/website_slides/static/src/js/slides_course_slides_list.js @@ -77,12 +77,9 @@ publicWidget.registry.websiteSlidesCourseSlidesList = SlideCoursePage.extend({ }, _reorderSlides: function (){ var self = this; - self._rpc({ - route: '/web/dataset/resequence', - params: { - model: "slide.slide", - ids: self._getSlides() - } + self.rpc('/web/dataset/resequence', { + model: "slide.slide", + ids: self._getSlides(), }).then(function (res) { self._checkForEmptySections(); }); diff --git a/addons/website_slides/static/src/js/slides_course_tag_add.js b/addons/website_slides/static/src/js/slides_course_tag_add.js index a4d19ffb292..7bb8ab34284 100644 --- a/addons/website_slides/static/src/js/slides_course_tag_add.js +++ b/addons/website_slides/static/src/js/slides_course_tag_add.js @@ -36,6 +36,7 @@ var TagCourseDialog = Dialog.extend({ // Open with a tag name as default this.defaultTag = options.defaultTag; this._super(parent, options); + this.rpc = this.bindService("rpc"); }, start: function () { var self = this; @@ -61,23 +62,17 @@ var TagCourseDialog = Dialog.extend({ var self = this; this.$('#tag_id').select2(this._select2Wrapper(_t('Tag'), function () { - return self._rpc({ - route: '/slides/channel/tag/search_read', - params: { - fields: ['name'], - domain: [['id', 'not in', self.tagIds], ['color', '!=', 0]], - } + return self.rpc('/slides/channel/tag/search_read', { + fields: ['name'], + domain: [['id', 'not in', self.tagIds], ['color', '!=', 0]], }); }) ); this.$('#tag_group_id').select2(this._select2Wrapper(_t('Tag Group (required for new tags)'), function () { - return self._rpc({ - route: '/slides/channel/tag/group/search_read', - params: { - fields: ['name'], - domain: [], - } + return self.rpc('/slides/channel/tag/group/search_read', { + fields: ['name'], + domain: [], }); }) ); @@ -286,11 +281,10 @@ var TagCourseDialog = Dialog.extend({ var $form = this.$('#slides_channel_tag_add_form'); if (this._formValidate($form)) { var values = this._getSelect2DropdownValues(); - return this._rpc({ - route: '/slides/channel/tag/add', - params: {'channel_id': this.channelID, - 'tag_id': values.tag_id, - 'group_id': values.group_id}, + return this.rpc('/slides/channel/tag/add', { + 'channel_id': this.channelID, + 'tag_id': values.tag_id, + 'group_id': values.group_id, }).then(function (data) { if (data.error) { self._alertDisplay(data.error); @@ -309,12 +303,9 @@ var TagCourseDialog = Dialog.extend({ this.$('#tag_id').select2('readonly', true); if (valid) { var values = this._getSelect2DropdownValues(); - return this._rpc({ - route: '/slide_channel_tag/add', - params: { - 'tag_id': values.tag_id, - 'group_id': values.group_id - }, + return this.rpc('/slide_channel_tag/add', { + 'tag_id': values.tag_id, + 'group_id': values.group_id }).then(function (data) { self.trigger_up('tag_refresh', { tag_id: data.tag_id }); self.close(); diff --git a/addons/website_slides/static/src/js/slides_share.js b/addons/website_slides/static/src/js/slides_share.js index 7589eb919d9..132710a21ff 100644 --- a/addons/website_slides/static/src/js/slides_share.js +++ b/addons/website_slides/static/src/js/slides_share.js @@ -10,6 +10,11 @@ export const ShareMail = publicWidget.Widget.extend({ 'keypress input': '_onKeypress', }, + init() { + this._super(...arguments); + this.rpc = this.bindService("rpc"); + }, + //-------------------------------------------------------------------------- // Private //-------------------------------------------------------------------------- @@ -47,10 +52,7 @@ export const ShareMail = publicWidget.Widget.extend({ params.channel_id = channelID; } this.$el.removeClass('o_has_error').find('.form-control, .form-select').removeClass('is-invalid'); - this._rpc({ - route, - params - }).then((action) => { + this.rpc(route, params).then((action) => { if (action) { this.$('.alert-info').removeClass('d-none'); this.$('.input-group').addClass('d-none'); diff --git a/addons/website_slides/static/src/js/slides_slide_archive.js b/addons/website_slides/static/src/js/slides_slide_archive.js index 0d1bebe1fb2..b490c1eb20b 100644 --- a/addons/website_slides/static/src/js/slides_slide_archive.js +++ b/addons/website_slides/static/src/js/slides_slide_archive.js @@ -10,6 +10,11 @@ publicWidget.registry.websiteSlidesSlideArchive = publicWidget.Widget.extend({ click: "_onArchiveSlideClick", }, + init() { + this._super(...arguments); + this.rpc = this.bindService("rpc"); + }, + //-------------------------------------------------------------------------- // Private //-------------------------------------------------------------------------- @@ -24,11 +29,8 @@ publicWidget.registry.websiteSlidesSlideArchive = publicWidget.Widget.extend({ /** * Calls 'archive' on slide controller and then visually removes the slide dom element */ - const isArchived = await this._rpc({ - route: "/slides/slide/archive", - params: { - slide_id: slideId, - }, + const isArchived = await this.rpc("/slides/slide/archive", { + slide_id: slideId, }); if (isArchived) { $slideTarget.closest(".o_wslides_slides_list_slide").remove(); diff --git a/addons/website_slides/static/src/js/slides_slide_like.js b/addons/website_slides/static/src/js/slides_slide_like.js index 45b66d24ccf..d27063efba0 100644 --- a/addons/website_slides/static/src/js/slides_slide_like.js +++ b/addons/website_slides/static/src/js/slides_slide_like.js @@ -11,6 +11,11 @@ var SlideLikeWidget = publicWidget.Widget.extend({ 'click .o_wslides_js_slide_like_down': '_onClickDown', }, + init() { + this._super(...arguments); + this.rpc = this.bindService("rpc"); + }, + //-------------------------------------------------------------------------- // Private //-------------------------------------------------------------------------- @@ -42,12 +47,9 @@ var SlideLikeWidget = publicWidget.Widget.extend({ */ _onClick: function (slideId, voteType) { var self = this; - this._rpc({ - route: '/slides/slide/like', - params: { - slide_id: slideId, - upvote: voteType === 'like', - }, + this.rpc('/slides/slide/like', { + slide_id: slideId, + upvote: voteType === 'like', }).then(function (data) { if (! data.error) { const $likesBtn = self.$('span.o_wslides_js_slide_like_up'); diff --git a/addons/website_slides/static/src/js/slides_slide_toggle_is_preview.js b/addons/website_slides/static/src/js/slides_slide_toggle_is_preview.js index b51d2746ef9..636153b4310 100644 --- a/addons/website_slides/static/src/js/slides_slide_toggle_is_preview.js +++ b/addons/website_slides/static/src/js/slides_slide_toggle_is_preview.js @@ -8,12 +8,14 @@ 'click': '_onPreviewSlideClick', }, + init() { + this._super(...arguments); + this.rpc = this.bindService("rpc"); + }, + _toggleSlidePreview: function($slideTarget) { - this._rpc({ - route: '/slides/slide/toggle_is_preview', - params: { - slide_id: $slideTarget.data('slideId') - }, + this.rpc('/slides/slide/toggle_is_preview', { + slide_id: $slideTarget.data('slideId') }).then(function (isPreview) { if (isPreview) { $slideTarget.removeClass('bg-light bg-hide border'); diff --git a/addons/website_slides/static/src/js/slides_upload.js b/addons/website_slides/static/src/js/slides_upload.js index da7c1fe4d5b..0ab8b3768d2 100644 --- a/addons/website_slides/static/src/js/slides_upload.js +++ b/addons/website_slides/static/src/js/slides_upload.js @@ -65,6 +65,9 @@ var SlideUploadDialog = Dialog.extend({ this.file = {}; this.isValidUrl = true; + + this.rpc = this.bindService("rpc"); + this.orm = this.bindService("orm"); }, start: function () { var self = this; @@ -105,33 +108,24 @@ var SlideUploadDialog = Dialog.extend({ var self = this; this.$('#category_id').select2(this._select2Wrapper(_t('Section'), false, function () { - return self._rpc({ - route: '/slides/category/search_read', - params: { - fields: ['name'], - domain: [['channel_id', '=', self.channelID]], - } + return self.rpc('/slides/category/search_read', { + fields: ['name'], + domain: [['channel_id', '=', self.channelID]], }); }) ); this.$('#tag_ids').select2(this._select2Wrapper(_t('Tags'), true, function () { - return self._rpc({ - route: '/slides/tag/search_read', - params: { - fields: ['name'], - domain: [], - } + return self.rpc('/slides/tag/search_read', { + fields: ['name'], + domain: [], }); })); }, _fetchUrlPreview: function (url, slideCategory) { - return this._rpc({ - route: '/slides/prepare_preview/', - params: { - 'url': url, - 'slide_category': slideCategory, - 'channel_id': this.channelID - }, + return this.rpc('/slides/prepare_preview/', { + 'url': url, + 'slide_category': slideCategory, + 'channel_id': this.channelID }); }, _formSetFieldValue: function (fieldId, value) { @@ -680,11 +674,10 @@ var SlideUploadDialog = Dialog.extend({ $el.text(_t('Installing "%s".', this.modulesToInstallStatus.name)); this.modulesToInstallStatus.installing = true; this._resetModalButton(); - this._rpc({ - model: 'ir.module.module', - method: 'button_immediate_install', - args: [[this.modulesToInstallStatus.id]], - }).then(function () { + this.orm.call("ir.module.module", "button_immediate_install", [ + [this.modulesToInstallStatus.id], + ]).then( + function () { let redirectUrl = window.location.origin + window.location.pathname + '?enable_slide_upload'; if (self.modulesToInstallStatus.default_slide_category) { redirectUrl += '='; @@ -722,10 +715,7 @@ var SlideUploadDialog = Dialog.extend({ var oldType = this.get('state'); this.set('state', '_upload'); - const data = await this._rpc({ - route: '/slides/add_slide', - params: values, - }); + const data = await this.rpc('/slides/add_slide', values); this._onFormSubmitDone(data, oldType); }, diff --git a/addons/website_slides_survey/static/src/js/slides_upload.js b/addons/website_slides_survey/static/src/js/slides_upload.js index 84563f6e835..75fe31cbfaa 100644 --- a/addons/website_slides_survey/static/src/js/slides_upload.js +++ b/addons/website_slides_survey/static/src/js/slides_upload.js @@ -59,11 +59,8 @@ SlidesUpload.SlideUploadDialog.include({ var self = this; this.$('#certification_id').select2(this._select2Wrapper(_t('Certification'), false, function () { - return self._rpc({ - route: '/slides_survey/certification/search_read', - params: { - fields: ['title'], - } + return self.rpc('/slides_survey/certification/search_read', { + fields: ['title'], }); }, 'title') ); diff --git a/addons/website_twitter/static/src/js/website.twitter.animation.js b/addons/website_twitter/static/src/js/website.twitter.animation.js index 21e62c565f3..15267e1e4ce 100644 --- a/addons/website_twitter/static/src/js/website.twitter.animation.js +++ b/addons/website_twitter/static/src/js/website.twitter.animation.js @@ -15,6 +15,11 @@ publicWidget.registry.twitter = publicWidget.Widget.extend({ 'click .twitter_timeline .tweet': '_onTweetClick', }, + init() { + this._super(...arguments); + this.rpc = this.bindService("rpc"); + }, + /** * @override */ @@ -23,7 +28,7 @@ publicWidget.registry.twitter = publicWidget.Widget.extend({ var $timeline = this.$('.twitter_timeline'); $timeline.append('
'); - var def = this._rpc({route: '/website_twitter/get_favorites'}).then(function (data) { + var def = this.rpc('/website_twitter/get_favorites').then(function (data) { $timeline.empty(); if (data.error) { diff --git a/addons/website_twitter/static/src/js/website.twitter.editor.js b/addons/website_twitter/static/src/js/website.twitter.editor.js index 85721670b1d..60cbd720cfc 100644 --- a/addons/website_twitter/static/src/js/website.twitter.editor.js +++ b/addons/website_twitter/static/src/js/website.twitter.editor.js @@ -5,6 +5,10 @@ import dom from "@web/legacy/js/core/dom"; import sOptions from "@web_editor/js/editor/snippets.options"; sOptions.registry.twitter = sOptions.Class.extend({ + init() { + this._super(...arguments); + this.rpc = this.bindService("rpc"); + }, /** * @override */ @@ -22,7 +26,7 @@ sOptions.registry.twitter = sOptions.Class.extend({ $configuration.appendTo(div).on('click', function (ev) { ev.preventDefault(); ev.stopPropagation(); - self._rpc({route: '/website_twitter/reload'}); + self.rpc('/website_twitter/reload'); }); this.$target.on('mouseover.website_twitter', function () { var $selected = $(this); diff --git a/odoo/addons/test_apikeys/static/tests/apikey_flow.js b/odoo/addons/test_apikeys/static/tests/apikey_flow.js index 48842227c2f..a14ec708e7e 100644 --- a/odoo/addons/test_apikeys/static/tests/apikey_flow.js +++ b/odoo/addons/test_apikeys/static/tests/apikey_flow.js @@ -1,7 +1,7 @@ /** @odoo-module **/ +import { jsonrpc } from "@web/core/network/rpc_service"; import { registry } from "@web/core/registry"; -import ajax from "@web/legacy/js/core/ajax"; registry.category("web_tour.tours").add('apikeys_tour_setup', { test: true, @@ -48,7 +48,7 @@ registry.category("web_tour.tours").add('apikeys_tour_setup', { trigger: 'p:contains("Here is your new API key")', run: async () => { const key = $('code [name=key] span').text(); - await ajax.jsonRpc('/web/dataset/call_kw', 'call', { + await jsonrpc('/web/dataset/call_kw', { model: 'ir.logging', method: 'send_key', args: [key], kwargs: {},