[IMP] *: remove legacy rpc

This commit removes the files ajax.js and rpc.js then adapts all the
places where their exports were used. For most of the changes, it's a
replace of `this._rpc({...})` by a new `useService("rpc|orm")` like
pattern in the widgets.

closes odoo/odoo#136271

Task: 3439226
Related: odoo/enterprise#47775
Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
This commit is contained in:
Michael (mcm)
2023-09-27 11:33:01 +00:00
parent 6ff40c0f9f
commit 7422eb643c
136 changed files with 1366 additions and 2384 deletions
+4 -4
View File
@@ -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")');
}
@@ -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;
}
@@ -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?
+2 -3
View File
@@ -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 });
};
},
},
@@ -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();
@@ -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;
@@ -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!'));
@@ -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();
@@ -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',
+23 -20
View File
@@ -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 => {
@@ -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;
@@ -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);
@@ -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) => {
@@ -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 => {
@@ -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 {
@@ -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);
}
}
+7 -5
View File
@@ -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}']`);
+7 -14
View File
@@ -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) {
+10 -12
View File
@@ -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;
},
+44 -53
View File
@@ -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"));
@@ -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}));
@@ -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
-2
View File
@@ -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',
@@ -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: {
@@ -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);
});
},
+12 -7
View File
@@ -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);
},
@@ -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 {
@@ -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];
@@ -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
@@ -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'));
}
});
@@ -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'
-4
View File
@@ -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',
@@ -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 };
},
};
@@ -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 });
};
},
};
@@ -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;
-107
View File
@@ -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<any>}
*/
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;
@@ -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.
*
@@ -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 });
@@ -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;
})
]);
+1 -5
View File
@@ -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) {
+1 -51
View File
@@ -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);
}
},
@@ -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();
});
});
});
@@ -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");
});
});
@@ -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) {
@@ -39,7 +39,6 @@
},
hotkey: { add: () => () => {} }, // fake service
notification: { notify() {} },
ajax: { rpc },
rpc,
ui: { activeElement: document }, // fake service
...env.services,
@@ -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;
-2
View File
@@ -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',
+35 -28
View File
@@ -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) {
@@ -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);
@@ -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);
}
@@ -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];
},
@@ -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) {
@@ -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;
}
@@ -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 {
@@ -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');
}, () => []);
}
@@ -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);
}
@@ -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);
@@ -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;
},
});
@@ -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 {
@@ -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);
});
@@ -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;
},
+8 -11
View File
@@ -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();
},
});
@@ -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 = [];
@@ -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];
}
@@ -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) {
@@ -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 => {
@@ -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;
});
@@ -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);
@@ -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;
}
@@ -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('<div id="website_form_editor_success_test_tour_'+model+'"></div>');
+10 -5
View File
@@ -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) {
@@ -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"]);
},
/**
*
@@ -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('<div class="alert alert-danger">' + response.errors + '</div>');
@@ -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();
});
},
@@ -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);
@@ -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]);
},
//--------------------------------------------------------------------------
@@ -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();
@@ -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}));
},
@@ -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',
@@ -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) {
@@ -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({
@@ -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: [],
});
})
);
@@ -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();
@@ -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();
@@ -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));
},
@@ -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 = $('<p>' + $(r.content).html() + '</p>').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();
});
},
+10 -13
View File
@@ -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,
});
},
@@ -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') {
@@ -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"]
);
},
//--------------------------------------------------------------------------
@@ -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 () {
+12 -13
View File
@@ -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);
});
@@ -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) {
@@ -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", [""]);
},
});
@@ -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) {
@@ -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') {
@@ -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');
@@ -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');
},
});
@@ -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);
}
@@ -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'}));
},
/**
@@ -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',
});
}
@@ -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);
},

Some files were not shown because too many files have changed in this diff Show More