diff --git a/addons/mrp_subcontracting/__manifest__.py b/addons/mrp_subcontracting/__manifest__.py
index 4c4ab41f3ad..42d89893290 100644
--- a/addons/mrp_subcontracting/__manifest__.py
+++ b/addons/mrp_subcontracting/__manifest__.py
@@ -56,7 +56,6 @@
'web/static/src/legacy/scss/ui.scss',
'web/static/src/legacy/scss/modal.scss',
'web/static/src/views/fields/translation_dialog.scss',
- 'web/static/src/legacy/scss/name_and_signature.scss',
'web/static/src/scss/fontawesome_overridden.scss',
'web/static/src/legacy/js/promise_extension.js',
@@ -104,8 +103,6 @@
'web/static/src/legacy/js/core/widget.js',
'web/static/src/legacy/js/services/core.js',
'web/static/src/legacy/js/common_env.js',
- 'web/static/src/legacy/js/widgets/name_and_signature.js',
- 'web/static/src/legacy/xml/name_and_signature.xml',
('include', 'web._assets_bootstrap'),
diff --git a/addons/portal/__manifest__.py b/addons/portal/__manifest__.py
index e7e50e6aff3..ab007883e02 100644
--- a/addons/portal/__manifest__.py
+++ b/addons/portal/__manifest__.py
@@ -43,11 +43,10 @@ a dependency towards website editing and customization capabilities.""",
'portal/static/src/xml/portal_chatter.xml',
'portal/static/src/js/portal_composer.js',
'portal/static/src/js/portal_security.js',
- 'portal/static/src/js/portal_signature.js',
- 'portal/static/src/xml/portal_signature.xml',
'portal/static/src/js/portal_sidebar.js',
'portal/static/src/xml/portal_security.xml',
'portal/static/src/js/components/**/*',
+ 'portal/static/src/signature_form/**/*',
],
'web.assets_tests': [
'portal/static/tests/**/*',
diff --git a/addons/portal/static/src/js/portal_signature.js b/addons/portal/static/src/js/portal_signature.js
deleted file mode 100644
index ddbd7fb9f25..00000000000
--- a/addons/portal/static/src/js/portal_signature.js
+++ /dev/null
@@ -1,192 +0,0 @@
-/** @odoo-module **/
-
-import { _t } from "@web/core/l10n/translation";
-import { renderToElement } from "@web/core/utils/render";
-import publicWidget from "@web/legacy/js/public/public_widget";
-import { NameAndSignature } from "@web/legacy/js/widgets/name_and_signature";
-
-/**
- * This widget is a signature request form. It uses
- * @see NameAndSignature for the input fields, adds a submit
- * button, and handles the RPC to save the result.
- */
-var SignatureForm = publicWidget.Widget.extend({
- template: 'portal.portal_signature',
- events: {
- 'click .o_portal_sign_submit': 'async _onClickSignSubmit',
- },
- custom_events: {
- 'signature_changed': '_onChangeSignature',
- },
-
- /**
- * Overridden to allow options.
- *
- * @constructor
- * @param {Widget} parent
- * @param {Object} options
- * @param {string} options.callUrl - make RPC to this url
- * @param {string} [options.sendLabel='Accept & Sign'] - label of the
- * send button
- * @param {Object} [options.rpcParams={}] - params for the RPC
- * @param {Object} [options.nameAndSignatureOptions={}] - options for
- * @see NameAndSignature.init()
- */
- init: function (parent, options) {
- this._super.apply(this, arguments);
-
- this.csrf_token = odoo.csrf_token;
-
- this.callUrl = options.callUrl || '';
- this.rpcParams = options.rpcParams || {};
- this.sendLabel = options.sendLabel || _t("Accept & Sign");
-
- this.nameAndSignature = new NameAndSignature(this,
- options.nameAndSignatureOptions || {});
-
- this.rpc = this.bindService("rpc");
- },
- /**
- * Overridden to get the DOM elements
- * and to insert the name and signature.
- *
- * @override
- */
- start: function () {
- var self = this;
- this.$confirm_btn = this.$('.o_portal_sign_submit');
- this.$controls = this.$('.o_portal_sign_controls');
- var subWidgetStart = this.nameAndSignature.replace(this.$('.o_web_sign_name_and_signature'));
- return Promise.all([subWidgetStart, this._super.apply(this, arguments)]).then(function () {
- self.nameAndSignature.resetSignature();
- });
- },
-
- //----------------------------------------------------------------------
- // Public
- //----------------------------------------------------------------------
-
- /**
- * Focuses the name.
- *
- * @see NameAndSignature.focusName();
- */
- focusName: function () {
- this.nameAndSignature.focusName();
- },
- /**
- * Resets the signature.
- *
- * @see NameAndSignature.resetSignature();
- */
- resetSignature: function () {
- return this.nameAndSignature.resetSignature();
- },
-
- //----------------------------------------------------------------------
- // Handlers
- //----------------------------------------------------------------------
-
- /**
- * Handles click on the submit button.
- *
- * This will get the current name and signature and validate them.
- * If they are valid, they are sent to the server, and the reponse is
- * handled. If they are invalid, it will display the errors to the user.
- *
- * @private
- * @param {Event} ev
- * @returns {Deferred}
- */
- _onClickSignSubmit: function (ev) {
- var self = this;
- ev.preventDefault();
-
- if (!this.nameAndSignature.validateSignature()) {
- return;
- }
-
- var name = this.nameAndSignature.getName();
- var signature = this.nameAndSignature.getSignatureImage()[1];
-
- return this.rpc(
- this.callUrl,
- Object.assign(this.rpcParams, {
- 'name': name,
- 'signature': signature,
- })
- ).then(function (data) {
- if (data.error) {
- self.$('.o_portal_sign_error_msg').remove();
- self.$controls.prepend(renderToElement('portal.portal_signature_error', {widget: data}));
- } else if (data.success) {
- var $success = renderToElement('portal.portal_signature_success', {widget: data});
- self.$el.empty().append($success);
- }
- if (data.force_refresh) {
- if (data.redirect_url) {
- window.location = data.redirect_url;
- } else {
- window.location.reload();
- }
- // no resolve if we reload the page
- return new Promise(function () { });
- }
- });
- },
- /**
- * Toggles the submit button depending on the signature state.
- *
- * @private
- */
- _onChangeSignature: function () {
- var isEmpty = this.nameAndSignature.isSignatureEmpty();
- this.$confirm_btn.prop('disabled', isEmpty);
- },
-});
-
-publicWidget.registry.SignatureForm = publicWidget.Widget.extend({
- selector: '.o_portal_signature_form',
-
- /**
- * @private
- */
- start: function () {
- var hasBeenReset = false;
-
- var callUrl = this.$el.data('call-url');
- var nameAndSignatureOptions = {
- defaultName: this.$el.data('default-name'),
- mode: this.$el.data('mode'),
- displaySignatureRatio: this.$el.data('signature-ratio'),
- signatureType: this.$el.data('signature-type'),
- fontColor: this.$el.data('font-color') || 'black',
- };
- var sendLabel = this.$el.data('send-label');
-
- var form = new SignatureForm(this, {
- callUrl: callUrl,
- nameAndSignatureOptions: nameAndSignatureOptions,
- sendLabel: sendLabel,
- });
-
- // Correctly set up the signature area if it is inside a modal
- this.$el.closest('.modal').on('shown.bs.modal', function (ev) {
- if (!hasBeenReset) {
- // Reset it only the first time it is open to get correct
- // size. After we want to keep its content on reopen.
- hasBeenReset = true;
- form.resetSignature();
- } else {
- form.focusName();
- }
- });
-
- return Promise.all([
- this._super.apply(this, arguments),
- form.appendTo(this.$el)
- ]);
- },
-});
-
-export default SignatureForm;
diff --git a/addons/portal/static/src/signature_form/signature_form.js b/addons/portal/static/src/signature_form/signature_form.js
new file mode 100644
index 00000000000..5da333a1387
--- /dev/null
+++ b/addons/portal/static/src/signature_form/signature_form.js
@@ -0,0 +1,86 @@
+/** @odoo-module **/
+
+import { Component, onMounted, useRef, useState } from "@odoo/owl";
+import { _t } from "@web/core/l10n/translation";
+import { registry } from "@web/core/registry";
+import { redirect } from "@web/core/utils/urls";
+import { NameAndSignature } from "@web/core/signature/name_and_signature";
+import { useService } from "@web/core/utils/hooks";
+
+/**
+ * This Component is a signature request form. It uses
+ * @see NameAndSignature for the input fields, adds a submit
+ * button, and handles the RPC to save the result.
+ */
+class SignatureForm extends Component {
+ static template = "portal.SignatureForm"
+ static components = { NameAndSignature }
+
+ setup() {
+ this.rootRef = useRef("root");
+ this.rpc = useService("rpc");
+
+ this.csrfToken = odoo.csrf_token;
+ this.state = useState({
+ error: false,
+ success: false,
+ });
+ this.signature = useState({ name: this.props.defaultName });
+ this.nameAndSignatureProps = {
+ signature: this.signature,
+ fontColor: this.props.fontColor || "black",
+ };
+ if (this.props.signatureRatio) {
+ this.nameAndSignatureProps.displaySignatureRatio = this.props.signatureRatio;
+ }
+ if (this.props.signatureType) {
+ this.nameAndSignatureProps.signatureType = this.props.signatureType;
+ }
+ if (this.props.mode) {
+ this.nameAndSignatureProps.mode = this.props.mode;
+ }
+
+ // Correctly set up the signature area if it is inside a modal
+ onMounted(() => {
+ this.rootRef.el.closest('.modal').addEventListener('shown.bs.modal', () => {
+ this.signature.resetSignature();
+ });
+ });
+ }
+
+ get sendLabel() {
+ return this.props.sendLabel || _t("Accept & Sign");
+ }
+
+ /**
+ * Handles click on the submit button.
+ *
+ * This will get the current name and signature and validate them.
+ * If they are valid, they are sent to the server, and the reponse is
+ * handled. If they are invalid, it will display the errors to the user.
+ *
+ * @returns {Promise}
+ */
+ async onClickSubmit() {
+ const name = this.signature.name;
+ const signature = this.signature.getSignatureImage()[1];
+ const data = await this.rpc(this.props.callUrl, { name, signature });
+ if (data.force_refresh) {
+ if (data.redirect_url) {
+ redirect(data.redirect_url);
+ } else {
+ window.location.reload();
+ }
+ // do not resolve if we reload the page
+ return new Promise(() => {});
+ }
+ this.state.error = data.error || false;
+ this.state.success = !data.error && {
+ message: data.message,
+ redirectUrl: data.redirect_url,
+ redirectMessage: data.redirect_message,
+ };
+ }
+}
+
+registry.category("public_components").add("portal.signature_form", SignatureForm);
diff --git a/addons/portal/static/src/signature_form/signature_form.xml b/addons/portal/static/src/signature_form/signature_form.xml
new file mode 100644
index 00000000000..d0af26a32d7
--- /dev/null
+++ b/addons/portal/static/src/signature_form/signature_form.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
diff --git a/addons/portal/static/src/xml/portal_signature.xml b/addons/portal/static/src/xml/portal_signature.xml
deleted file mode 100644
index ccd33541559..00000000000
--- a/addons/portal/static/src/xml/portal_signature.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/addons/portal/views/portal_templates.xml b/addons/portal/views/portal_templates.xml
index 58f7207040b..cd6737b3cba 100644
--- a/addons/portal/views/portal_templates.xml
+++ b/addons/portal/views/portal_templates.xml
@@ -738,19 +738,19 @@
- {number} signature_ratio: ratio of the signature area
- {string} signature_type: 'signature' or 'initial'
- For the default values and more information, see init() of the widgets
- SignatureForm and NameAndSignature.
+ For default values and more information, see components SignatureForm and NameAndSignature.
-->
-
+
+