summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js/fields/signature.js
diff options
context:
space:
mode:
authorjbm-odoo <jbm@odoo.com>2019-01-14 14:22:03 +0000
committerjbm-odoo <jbm@odoo.com>2019-02-14 12:59:10 +0000
commit4960453db91b465c9429453ef3a0692a604c8ef9 (patch)
tree3b02bb92556b523e91a949817e31da970b6f2a98 /addons/web/static/src/js/fields/signature.js
parent7410de111fe33731ba79f68f53e13dd3d511fb43 (diff)
[IMP] web Add widget signature on binary field
Add a new widget for binary fields. It open a dialog box and the user can sign manually, or an signature can be draw automatically or he can upload a picture of his signature. Move fonts, controller, scss,templates about signature from portal to web to avoid redundance closes odoo/odoo#30222
Diffstat (limited to 'addons/web/static/src/js/fields/signature.js')
-rw-r--r--addons/web/static/src/js/fields/signature.js272
1 files changed, 272 insertions, 0 deletions
diff --git a/addons/web/static/src/js/fields/signature.js b/addons/web/static/src/js/fields/signature.js
new file mode 100644
index 00000000000..186c8fd764c
--- /dev/null
+++ b/addons/web/static/src/js/fields/signature.js
@@ -0,0 +1,272 @@
+odoo.define('web.Signature', function (require) {
+ "use strict";
+
+ var AbstractFieldBinary = require('web.basic_fields').AbstractFieldBinary;
+ var core = require('web.core');
+ var Dialog = require('web.Dialog');
+ var field_utils = require('web.field_utils');
+ var NameAndSignature = require('web.name_and_signature').NameAndSignature;
+ var registry = require('web.field_registry');
+ var session = require('web.session');
+ var utils = require('web.utils');
+
+
+ var qweb = core.qweb;
+ var _t = core._t;
+
+// The goal of this dialog is to ask the user a signature request.
+// It uses @see SignNameAndSignature for the name and signature fields.
+var SignatureDialog = Dialog.extend({
+ template: 'web.signature_dialog',
+ xmlDependencies: Dialog.prototype.xmlDependencies.concat(
+ ['/web/static/src/xml/name_and_signature.xml']
+ ),
+ custom_events: {
+ 'signature_changed': '_onChangeSignature',
+ },
+
+ /**
+ * @constructor
+ * @param {Widget} parent
+ * @param {Object} options
+ * @param {string} [options.title='Adopt Your Signature'] - modal title
+ * @param {string} [options.size='medium'] - modal size
+ * @param {Object} [options.nameAndSignatureOptions={}] - options for
+ * @see NameAndSignature.init()
+ */
+ init: function (parent, options) {
+ var self = this;
+ options = options || {};
+
+ options.title = options.title || _t("Adopt Your Signature");
+ options.size = options.size || 'medium';
+ options.technical = false;
+
+ if (!options.buttons) {
+ options.buttons = [];
+ options.buttons.push({text: _t("Adopt and Sign"), classes: "btn-primary", disabled: true, click: function (e) {
+ self._onConfirm();
+ }});
+ options.buttons.push({text: _t("Cancel"), close: true});
+ }
+
+ this._super(parent, options);
+
+ this.nameAndSignature = new NameAndSignature(this, options.nameAndSignatureOptions);
+ },
+ /**
+ * Inserts the name and signature.
+ *
+ * @override
+ */
+ start: function () {
+ var self = this;
+ this.$primaryButton = this.$footer.find('.btn-primary');
+
+ this.opened().then(function () {
+ self.nameAndSignature.replace(self.$('.o_web_sign_name_and_signature'));
+ });
+
+ return this._super.apply(this, arguments);
+ },
+
+ //----------------------------------------------------------------------
+ // Public
+ //----------------------------------------------------------------------
+
+ /**
+ * Returns whether the drawing area is currently empty.
+ *
+ * @see NameAndSignature.isSignatureEmpty()
+ * @returns {boolean} Whether the drawing area is currently empty.
+ */
+ isSignatureEmpty: function () {
+ return this.nameAndSignature.isSignatureEmpty();
+ },
+
+ //----------------------------------------------------------------------
+ // Handlers
+ //----------------------------------------------------------------------
+
+ /**
+ * Toggles the submit button depending on the signature state.
+ *
+ * @private
+ */
+ _onChangeSignature: function () {
+ var isEmpty = this.nameAndSignature.isSignatureEmpty();
+ this.$primaryButton.prop('disabled', isEmpty);
+ },
+ /**
+ * Upload the signature image when confirm.
+ *
+ * @private
+ */
+ _onConfirm: function (fct) {
+ this.trigger_up('upload_signature', {
+ name: this.nameAndSignature.getName(),
+ signatureImage: this.nameAndSignature.getSignatureImage(),
+ });
+ },
+});
+
+var FieldBinarySignature = AbstractFieldBinary.extend({
+ fieldDependencies: _.extend({}, AbstractFieldBinary.prototype.fieldDependencies, {
+ __last_update: {type: 'datetime'},
+ }),
+ custom_events: _.extend({}, AbstractFieldBinary.prototype.custom_events, {
+ upload_signature: '_onUploadSignature',
+ }),
+ events: _.extend({}, AbstractFieldBinary.prototype.events, {
+ 'click .o_signature': '_onClickSignature',
+ }),
+ template: null,
+ supportedFieldTypes: ['binary'],
+ file_type_magic_word: {
+ '/': 'jpg',
+ 'R': 'gif',
+ 'i': 'png',
+ 'P': 'svg+xml',
+ },
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ /**
+ * This widget must always have render even if there are no signature.
+ * In edit mode, the real value is return to manage required fields.
+ *
+ * @override
+ */
+ isSet: function () {
+ if (this.mode === 'edit') {
+ return this.value;
+ }
+ return true;
+ },
+
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+
+ /**
+ * Renders an empty signature or the saved signature. Both must have the same size.
+ *
+ * @override
+ * @private
+ */
+
+ _render: function () {
+ var self = this;
+ var displaySignatureRatio = 3;
+ var url;
+ var $img;
+ var width = this.nodeOptions.size ? this.nodeOptions.size[0] : this.attrs.width;
+ var height = this.nodeOptions.size ? this.nodeOptions.size[1] : this.attrs.height;
+ if (this.value) {
+ if (!utils.is_bin_size(this.value)) {
+ // Use magic-word technique for detecting image type
+ url = 'data:image/' + (this.file_type_magic_word[this.value[0]] || 'png') + ';base64,' + this.value;
+ } else {
+ url = session.url('/web/image', {
+ model: this.model,
+ id: JSON.stringify(this.res_id),
+ field: this.nodeOptions.preview_image || this.name,
+ // unique forces a reload of the image when the record has been updated
+ unique: field_utils.format.datetime(this.recordData.__last_update).replace(/[^0-9]/g, ''),
+ });
+ }
+ $img = $(qweb.render("FieldBinarySignature-img", {widget: this, url: url}));
+ } else {
+ $img = $('<div class="o_signature o_signature_empty"><svg></svg><p>' + _t('SIGNATURE') + '</p></div>');
+ if (width && height) {
+ width = Math.min(width, displaySignatureRatio * height);
+ height = width / displaySignatureRatio;
+ } else if (width) {
+ height = width / displaySignatureRatio;
+ } else if (height) {
+ width = height * displaySignatureRatio;
+ }
+ }
+ if (width) {
+ $img.attr('width', width);
+ $img.css('max-width', width + 'px');
+ }
+ if (height) {
+ $img.attr('height', height);
+ $img.css('max-height', height + 'px');
+ }
+ this.$('> div').remove();
+ this.$('> img').remove();
+
+ this.$el.prepend($img);
+
+ $img.on('error', function () {
+ self._clearFile();
+ $img.attr('src', self.placeholder);
+ self.do_warn(_t("Image"), _t("Could not display the selected image."));
+ });
+ },
+
+ //--------------------------------------------------------------------------
+ // Handlers
+ //--------------------------------------------------------------------------
+
+ /**
+ * If the view is in edit mode, open dialog to sign.
+ *
+ * @private
+ */
+ _onClickSignature: function () {
+ var self = this;
+ if (this.mode === 'edit') {
+
+ var nameAndSignatureOptions = {
+ mode: 'draw',
+ displaySignatureRatio: 3,
+ signatureType: 'signature',
+ noInputName: true,
+ };
+
+ if (this.nodeOptions.full_name) {
+ var $search = $("[name='" + this.nodeOptions.full_name + "']");
+ // If full_name correspond to field in read-only mode
+ var signName = $($search).text();
+
+ // If full_name correspond to field to fill
+ if (!signName && $($search)[0]) {
+ signName = $($search)[0].value;
+ }
+ if (!signName) {
+ // If full_name is in the recordData. Last option because this value might not be the last value.
+ signName = this.recordData[this.nodeOptions.full_name] || '';
+ }
+ nameAndSignatureOptions.defaultName = (signName === '') ? undefined : signName;
+ }
+
+ nameAndSignatureOptions.defaultFont = this.nodeOptions.default_font || '';
+ this.signDialog = new SignatureDialog(self, {nameAndSignatureOptions: nameAndSignatureOptions});
+
+ this.signDialog.open();
+ }
+ },
+
+ /**
+ * Upload the signature image if valid and close the dialog.
+ *
+ * @private
+ */
+ _onUploadSignature: function (ev) {
+ var signatureImage = ev.data.signatureImage;
+ if (signatureImage !== this.signDialog.emptySignature) {
+ var data = signatureImage[1];
+ var type = signatureImage[0].split('/')[1];
+ this.on_file_uploaded(data.length, ev.data.name, type, data);
+ }
+ this.signDialog.close();
+ }
+});
+
+registry.add('signature', FieldBinarySignature);
+
+});