changeset 8e374f951fa2 in sao:default
details: https://hg.tryton.org/sao?cmd=changeset;node=8e374f951fa2
description:
        Send client email via server

        issue9449
        review292001002
diffstat:

 CHANGELOG              |    1 +
 images/tryton-send.svg |    1 +
 src/action.js          |    7 -
 src/common.js          |  147 +++++++++++++++++++++++++++++-
 src/sao.js             |    8 +-
 src/sao.less           |   12 ++
 src/tab.js             |   41 +++++++-
 src/theme.less         |    3 +
 src/view/form.js       |  151 +----------------------------
 src/window.js          |  242 +++++++++++++++++++++++++++++++++++++++++++++++++
 10 files changed, 454 insertions(+), 159 deletions(-)

diffs (797 lines):

diff -r 5247d0b71e4d -r 8e374f951fa2 CHANGELOG
--- a/CHANGELOG Sat Sep 12 18:30:11 2020 +0200
+++ b/CHANGELOG Thu Sep 17 17:18:02 2020 +0200
@@ -1,3 +1,4 @@
+* Support e-mail template
 * Always encode CSV export in UTF-8
 * Use tempusdominus and Popper for date time picker
 * Support PYSON comparison of date and datetime
diff -r 5247d0b71e4d -r 8e374f951fa2 images/tryton-send.svg
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/images/tryton-send.svg    Thu Sep 17 17:18:02 2020 +0200
@@ -0,0 +1,1 @@
+<svg xmlns="http://www.w3.org/2000/svg"; height="24" viewBox="0 0 24 24" 
width="24"><path d="M0 0h24v24H0z" fill="none"/><path d="M2.01 21L23 12 2.01 3 
2 10l15 2-15 2z"/></svg>
\ No newline at end of file
diff -r 5247d0b71e4d -r 8e374f951fa2 src/action.js
--- a/src/action.js     Sat Sep 12 18:30:11 2020 +0200
+++ b/src/action.js     Thu Sep 17 17:18:02 2020 +0200
@@ -132,8 +132,6 @@
                 params.name = action.report_name;
                 params.data = data;
                 params.direct_print = action.direct_print;
-                params.email_print = action.email_print;
-                params.email = action.email;
                 params.context = context;
                 Sao.Action.exec_report(params);
                 return;
@@ -183,15 +181,10 @@
         if (!attributes.context) {
             attributes.context = {};
         }
-        if (!attributes.email) {
-            attributes.email = {};
-        }
         var data = jQuery.extend({}, attributes.data);
         var context = jQuery.extend({}, Sao.Session.current_session.context);
         jQuery.extend(context, attributes.context);
         context.direct_print = attributes.direct_print;
-        context.email_print = attributes.email_print;
-        context.email = attributes.email;
 
         var prm = Sao.rpc({
             'method': 'report.' + attributes.name + '.execute',
diff -r 5247d0b71e4d -r 8e374f951fa2 src/common.js
--- a/src/common.js     Sat Sep 12 18:30:11 2020 +0200
+++ b/src/common.js     Thu Sep 17 17:18:02 2020 +0200
@@ -133,7 +133,7 @@
 
         keys.forEach(function(k, i) {
             jQuery('<div/>', {
-                'class': 'checkbox'
+                'class': 'radio'
             }).append(jQuery('<label/>')
                 .text(' ' + k)
                 .prepend(jQuery('<input/>', {
@@ -150,7 +150,7 @@
             'type': 'button'
         }).text(Sao.i18n.gettext('Cancel')).click(function() {
             dialog.modal.modal('hide');
-            prm.fail();
+            prm.reject();
         }).appendTo(dialog.footer);
         jQuery('<button/>', {
             'class': 'btn btn-primary',
@@ -2723,6 +2723,7 @@
         'tryton-remove',
         'tryton-save',
         'tryton-search',
+        'tryton-send',
         'tryton-star-border',
         'tryton-star',
         'tryton-switch',
@@ -3435,7 +3436,9 @@
                     this.input.focus();
                 }.bind(this)).prependTo(this.menu);
             }, this);
-            if (!this.input.val()) {
+            if (!this.input.val() || (
+                !this.menu.find('li.completion').length &&
+                !this.menu.find('li.action').length)) {
                 if (this.dropdown.hasClass('open')) {
                     this.menu.dropdown('toggle');
                 }
@@ -3770,4 +3773,142 @@
         return colors;
     };
 
+    Sao.common.richtext_toolbar = function() {
+        var toolbar = jQuery('<div/>', {
+            'class': 'btn-toolbar',
+            'role': 'toolbar',
+        });
+
+        var button_apply_command = function(evt) {
+            document.execCommand(evt.data);
+        };
+
+        var add_buttons = function(buttons) {
+            var group = jQuery('<div/>', {
+                'class': 'btn-group',
+                'role': 'group'
+            }).appendTo(toolbar);
+            for (var i in buttons) {
+                var properties = buttons[i];
+                var button = jQuery('<button/>', {
+                    'class': 'btn btn-default',
+                    'type': 'button'
+                }).append(Sao.common.ICONFACTORY.get_icon_img(
+                    'tryton-format-' + properties.icon)
+                ).appendTo(group);
+                button.click(properties.command, button_apply_command);
+            }
+        };
+
+        add_buttons([
+            {
+                'icon': 'bold',
+                'command': 'bold'
+            }, {
+                'icon': 'italic',
+                'command': 'italic'
+            }, {
+                'icon': 'underline',
+                'command': 'underline'
+            }]);
+
+        var selections = [
+            {
+                'heading': Sao.i18n.gettext('Font'),
+                'options': ['Normal', 'Serif', 'Sans', 'Monospace'],  // XXX
+                'command': 'fontname'
+            }, {
+                'heading': Sao.i18n.gettext('Size'),
+                'options': [1, 2, 3, 4, 5, 6, 7],
+                'command': 'fontsize'
+            }];
+        var add_option = function(dropdown, properties) {
+            return function(option) {
+                dropdown.append(jQuery('<li/>').append(jQuery('<a/>', {
+                    'href': '#'
+                }).text(option).click(function(evt) {
+                    evt.preventDefault();
+                    document.execCommand(properties.command, false, option);
+                })));
+            };
+        };
+        for (var i in selections) {
+            var properties = selections[i];
+            var group = jQuery('<div/>', {
+                'class': 'btn-group',
+                'role': 'group'
+            }).appendTo(toolbar);
+            var button = jQuery('<button/>', {
+                'class': 'btn btn-default dropdown-toggle',
+                'type': 'button',
+                'data-toggle': 'dropdown',
+                'aria-expanded': false,
+                'aria-haspopup': true
+            }).append(properties.heading)
+                .append(jQuery('<span/>', {
+                    'class': 'caret'
+                })).appendTo(group);
+            var dropdown = jQuery('<ul/>', {
+                'class': 'dropdown-menu'
+            }).appendTo(group);
+            properties.options.forEach(add_option(dropdown, properties));
+        }
+
+        add_buttons([
+            {
+                'icon': 'align-left',
+                'command': Sao.i18n.rtl? 'justifyRight' : 'justifyLeft',
+            }, {
+                'icon': 'align-center',
+                'command': 'justifyCenter'
+            }, {
+                'icon': 'align-right',
+                'command': Sao.i18n.rtl? 'justifyLeft': 'justifyRight',
+            }, {
+                'icon': 'align-justify',
+                'command': 'justifyFull'
+            }]);
+
+        // TODO backColor
+        [['foreColor', '#000000']].forEach(
+            function(e) {
+                var command = e[0];
+                var color = e[1];
+                jQuery('<input/>', {
+                    'class': 'btn btn-default',
+                    'type': 'color'
+                }).appendTo(toolbar)
+                    .change(function() {
+                        document.execCommand(command, false, 
jQuery(this).val());
+                    }).focusin(function() {
+                        document.execCommand(command, false, 
jQuery(this).val());
+                    }).val(color);
+            });
+        return toolbar;
+    };
+
+    Sao.common.richtext_normalize = function(html) {
+        var el = jQuery('<div/>').html(html);
+        // TODO order attributes
+        el.find('div').each(function(i, el) {
+            el = jQuery(el);
+            // Not all browsers respect the styleWithCSS
+            if (el.css('text-align')) {
+                // Remove browser specific prefix
+                var align = el.css('text-align').split('-').pop();
+                el.attr('align', align);
+                el.css('text-align', '');
+            }
+            // Some browsers set start as default align
+            if (el.attr('align') == 'start') {
+                if (Sao.i18n.rtl) {
+                    el.attr('align', 'right');
+                } else {
+                    el.attr('align', 'left');
+                }
+            }
+        });
+        return el.html();
+    };
+
 }());
diff -r 5247d0b71e4d -r 8e374f951fa2 src/sao.js
--- a/src/sao.js        Sat Sep 12 18:30:11 2020 +0200
+++ b/src/sao.js        Thu Sep 17 17:18:02 2020 +0200
@@ -488,8 +488,6 @@
             try {
                 attributes.data = loads(params.data || '{}');
                 attributes.direct_print = loads(params.direct_print || 
'false');
-                attributes.email_print = loads(params.email_print || 'false');
-                attributes.email = loads(params.email || 'null');
                 attributes.name = loads(params.name || '""');
                 attributes.window = loads(params.window || 'false');
                 attributes.context = loads(params.context || '{}');
@@ -512,8 +510,6 @@
             try {
                 attributes.data = loads(params.data || '{}');
                 attributes.direct_print = loads(params.direct_print || 
'false');
-                attributes.email_print = loads(params.email_print || 'false');
-                attributes.email = loads(params.email || 'null');
                 attributes.context = loads(params.context || '{}');
             } catch (e) {
                 return;
@@ -1015,6 +1011,10 @@
                 label: Sao.i18n.gettext('Print'),
                 id: 'print',
             }, {
+                shortcut: 'ctrl+shift+e',
+                label: Sao.i18n.gettext('E-Mail'),
+                id: 'email',
+            }, {
                 shortcut: 'alt+shift+tab',
                 label: Sao.i18n.gettext('Previous tab'),
                 callback: function() {
diff -r 5247d0b71e4d -r 8e374f951fa2 src/sao.less
--- a/src/sao.less      Sat Sep 12 18:30:11 2020 +0200
+++ b/src/sao.less      Thu Sep 17 17:18:02 2020 +0200
@@ -681,6 +681,18 @@
     }
 }
 
+.email {
+    .email-richtext {
+        min-height: 10em;
+    }
+    input[type='file'] {
+        display: inline-block;
+    }
+    .close {
+        float: none;
+    }
+}
+
 .bootstrap-datetimepicker-widget.dropdown-menu {
     z-index: 2000;
 }
diff -r 5247d0b71e4d -r 8e374f951fa2 src/tab.js
--- a/src/tab.js        Sat Sep 12 18:30:11 2020 +0200
+++ b/src/tab.js        Thu Sep 17 17:18:02 2020 +0200
@@ -91,6 +91,11 @@
                     id: 'print',
                     icon: 'tryton-print',
                     label: Sao.i18n.gettext('Print'),
+                }, {
+                    id: 'email',
+                    icon: 'tryton-email',
+                    label: Sao.i18n.gettext('E-Mail...'),
+                    tooltip: Sao.i18n.gettext('Send an e-mail using the 
record'),
                 }, null, {
                     id: 'export',
                     icon: 'tryton-export',
@@ -522,7 +527,7 @@
                         'role': 'menu',
                         'aria-labelledby': menu_action[0]
                     }))
-                    .appendTo(toolbar.find('.btn-toolbar > 
.btn-group').last());
+                    .insertBefore(toolbar.find('button#email'));
                     buttons[menu_action[0]] = button;
                     var dropdown = button
                         .on('show.bs.dropdown', function() {
@@ -643,8 +648,6 @@
                                 .selected_records.map(function(record) {
                                     return record.id;
                                 });
-                                exec_action = Sao.Action.evaluate(exec_action,
-                                    menu_action[0], screen.current_record);
                                 var data = {
                                     model: screen.model_name,
                                     id: record_id,
@@ -1173,6 +1176,38 @@
                 this.refresh_resources(true);
             }.bind(this));
         },
+        email: function() {
+            function is_report(action) {
+                return action.type == 'ir.action.report';
+            }
+            if (!this.buttons.email.hasClass('disabled')) {
+                var record = this.screen.current_record;
+                if (!record || (record.id < 0)) {
+                    return;
+                }
+                var title = this.title.text();
+                this.screen.model.execute(
+                    'view_toolbar_get', [], this.screen.context)
+                    .then(function(toolbars) {
+                        var prints = toolbars.print.filter(is_report);
+                        var emails = {};
+                        for (var i = 0; i < toolbars.emails.length; i++) {
+                            var email = toolbars.emails[i];
+                            emails[email.name] = email.id;
+                        }
+                        record.rec_name().then(function(rec_name) {
+                            function email(template) {
+                                new Sao.Window.Email(
+                                    title + ': ' + rec_name, record,
+                                    prints, template);
+                            }
+                            Sao.common.selection(
+                                Sao.i18n.gettext("Template"), emails, true)
+                                .then(email, email);
+                        });
+                    });
+            }
+        },
         refresh_resources: function(reload) {
             var record = this.screen.current_record;
             if (record) {
diff -r 5247d0b71e4d -r 8e374f951fa2 src/theme.less
--- a/src/theme.less    Sat Sep 12 18:30:11 2020 +0200
+++ b/src/theme.less    Thu Sep 17 17:18:02 2020 +0200
@@ -187,6 +187,9 @@
 .checkbox-inline {
     label {
         padding-left: 25px;
+        input {
+            margin-right: 4px;
+        }
     }
 
     input[type="radio"],
diff -r 5247d0b71e4d -r 8e374f951fa2 src/view/form.js
--- a/src/view/form.js  Sat Sep 12 18:30:11 2020 +0200
+++ b/src/view/form.js  Thu Sep 17 17:18:02 2020 +0200
@@ -2100,7 +2100,10 @@
                 'class': this.class_ + ' panel panel-default'
             });
             if (parseInt(attributes.toolbar || '1', 10)) {
-                this.toolbar = this.get_toolbar().appendTo(this.el);
+                this.toolbar = Sao.common.richtext_toolbar().appendTo(
+                    jQuery('<div/>', {
+                        'class': 'panel-heading',
+                    }).appendTo(this.el));
             }
             this.input = this.labelled = jQuery('<div/>', {
                 'class': 'richtext mousetrap',
@@ -2122,122 +2125,6 @@
                 button.click(this.translate.bind(this));
             }
         },
-        get_toolbar: function() {
-            var i, properties, button;
-            var toolbar = jQuery('<div/>', {
-                'class': 'btn-toolbar',
-                'role': 'toolbar'
-            }).appendTo(jQuery('<div/>', {
-                'class': 'panel-heading'
-            }));
-
-            var button_apply_command = function(evt) {
-                document.execCommand(evt.data);
-            };
-
-            var add_buttons = function(buttons) {
-                var group = jQuery('<div/>', {
-                    'class': 'btn-group',
-                    'role': 'group'
-                }).appendTo(toolbar);
-                for (i in buttons) {
-                    properties = buttons[i];
-                    button = jQuery('<button/>', {
-                        'class': 'btn btn-default',
-                        'type': 'button'
-                    }).append(Sao.common.ICONFACTORY.get_icon_img(
-                        'tryton-format-' + properties.icon)
-                    ).appendTo(group);
-                    button.click(properties.command, button_apply_command);
-                }
-            };
-
-            add_buttons([
-                    {
-                        'icon': 'bold',
-                        'command': 'bold'
-                    }, {
-                        'icon': 'italic',
-                        'command': 'italic'
-                    }, {
-                        'icon': 'underline',
-                        'command': 'underline'
-                    }]);
-
-            var selections = [
-            {
-                'heading': Sao.i18n.gettext('Font'),
-                'options': ['Normal', 'Serif', 'Sans', 'Monospace'],  // XXX
-                'command': 'fontname'
-            }, {
-                'heading': Sao.i18n.gettext('Size'),
-                'options': [1, 2, 3, 4, 5, 6, 7],
-                'command': 'fontsize'
-            }];
-            var add_option = function(dropdown, properties) {
-                return function(option) {
-                    dropdown.append(jQuery('<li/>').append(jQuery('<a/>', {
-                        'href': '#'
-                    }).text(option).click(function(evt) {
-                        evt.preventDefault();
-                        document.execCommand(properties.command, false, 
option);
-                    })));
-                };
-            };
-            for (i in selections) {
-                properties = selections[i];
-                var group = jQuery('<div/>', {
-                    'class': 'btn-group',
-                    'role': 'group'
-                }).appendTo(toolbar);
-                button = jQuery('<button/>', {
-                    'class': 'btn btn-default dropdown-toggle',
-                    'type': 'button',
-                    'data-toggle': 'dropdown',
-                    'aria-expanded': false,
-                    'aria-haspopup': true
-                }).append(properties.heading)
-                .append(jQuery('<span/>', {
-                    'class': 'caret'
-                })).appendTo(group);
-                var dropdown = jQuery('<ul/>', {
-                    'class': 'dropdown-menu'
-                }).appendTo(group);
-                properties.options.forEach(add_option(dropdown, properties));
-            }
-
-            add_buttons([
-                    {
-                        'icon': 'align-left',
-                        'command': Sao.i18n.rtl? 'justifyRight' : 
'justifyLeft',
-                    }, {
-                        'icon': 'align-center',
-                        'command': 'justifyCenter'
-                    }, {
-                        'icon': 'align-right',
-                        'command': Sao.i18n.rtl? 'justifyLeft': 'justifyRight',
-                    }, {
-                        'icon': 'align-justify',
-                        'command': 'justifyFull'
-                    }]);
-
-            // TODO backColor
-            [['foreColor', '#000000']].forEach(
-                    function(e) {
-                        var command = e[0];
-                        var color = e[1];
-                        jQuery('<input/>', {
-                            'class': 'btn btn-default',
-                            'type': 'color'
-                        }).appendTo(toolbar)
-                        .change(function() {
-                            document.execCommand(command, false, 
jQuery(this).val());
-                        }).focusin(function() {
-                            document.execCommand(command, false, 
jQuery(this).val());
-                        }).val(color);
-            });
-            return toolbar;
-        },
         focus_out: function() {
             // Let browser set the next focus before testing
             // if it moved out of the widget
@@ -2277,31 +2164,8 @@
             this.field.set_client(this.record, value);
         },
         _normalize_markup: function(content) {
-            var el = jQuery('<div/>').html(
+            return Sao.common.richtext_normalize(
                 Sao.HtmlSanitizer.sanitize(content || ''));
-            this._normalize(el);
-            return el.html();
-        },
-        _normalize: function(el) {
-            // TODO order attributes
-            el.find('div').each(function(i, el) {
-                el = jQuery(el);
-                // Not all browsers respect the styleWithCSS
-                if (el.css('text-align')) {
-                    // Remove browser specific prefix
-                    var align = el.css('text-align').split('-').pop();
-                    el.attr('align', align);
-                    el.css('text-align', '');
-                }
-                // Some browsers set start as default align
-                if (el.attr('align') == 'start') {
-                    if (Sao.i18n.rtl) {
-                        el.attr('align', 'right');
-                    } else {
-                        el.attr('align', 'left');
-                    }
-                }
-            });
         },
         get modified() {
             if (this.record && this.field) {
@@ -2323,7 +2187,10 @@
                 'class': this.class_ + ' panel panel-default',
             });
             if (parseInt(this.attributes.toolbar || '1', 10)) {
-                this.get_toolbar().appendTo(widget);
+                Sao.common.richtext_toolbar().appendTo(
+                    jQuery('<div/>', {
+                        'class': 'panel-heading',
+                    }).appendTo(this.el));
             }
             var input = jQuery('<div/>', {
                 'class': 'richtext mousetrap',
diff -r 5247d0b71e4d -r 8e374f951fa2 src/window.js
--- a/src/window.js     Sat Sep 12 18:30:11 2020 +0200
+++ b/src/window.js     Thu Sep 17 17:18:02 2020 +0200
@@ -1972,4 +1972,246 @@
         return row;
     };
 
+    Sao.Window.EmailEntry = Sao.class_(Sao.common.InputCompletion, {
+        init: function(el, session) {
+            this.session = session;
+            Sao.Window.EmailEntry._super.init.call(
+                this, el,
+                this._email_source,
+                this._email_match_selected,
+                this._email_format);
+        },
+        _email_match_selected: function(value) {
+            this.input.val(value[2]);
+        },
+        _email_source: function(text) {
+            if (this.input[0].selectionStart < this.input.val().length) {
+                return jQuery.when([]);
+            }
+            return Sao.rpc({
+                'method': 'model.ir.email.complete',
+                'params': [text, Sao.config.limit, {}],
+            }, this.session);
+        },
+        _email_format: function(value) {
+            return value[1];
+        },
+    });
+
+    Sao.Window.Email = Sao.class_(Object, {
+        init: function(name, record, prints, template) {
+            this.record = record;
+            this.dialog = new Sao.Dialog(
+                Sao.i18n.gettext('E-mail %1', name), 'email', 'lg');
+            this.el = this.dialog.modal;
+            this.dialog.content.addClass('form-horizontal');
+
+            var body = this.dialog.body;
+            function add_group(name, label, required) {
+                var group = jQuery('<div/>', {
+                    'class': 'form-group',
+                }).appendTo(body);
+                jQuery('<label/>', {
+                    'class': 'control-label col-sm-1',
+                    'text': label,
+                    'for': 'email-' + name,
+                }).appendTo(group);
+                var input = jQuery('<input/>', {
+                    'type': 'text',
+                    'class':'form-control input-sm',
+                    'id': 'email-' + name,
+                }).appendTo(jQuery('<div/>', {
+                    'class': 'col-sm-11',
+                }).appendTo(group));
+                if (required) {
+                    input.attr('required', true);
+                }
+                return input;
+            }
+
+            this.to = add_group('to', Sao.i18n.gettext('To:'), true);
+            this.cc = add_group('cc', Sao.i18n.gettext('Cc:'));
+            this.bcc = add_group('bcc', Sao.i18n.gettext('Bcc:'));
+            [this.to, this.cc, this.bcc].forEach(function(input) {
+                new Sao.Window.EmailEntry(input, this.record.model.session);
+            }.bind(this));
+            this.subject = add_group(
+                'subject', Sao.i18n.gettext('Subject:'), true);
+
+            var panel = jQuery('<div/>', {
+                'class': 'panel panel-default',
+            }).appendTo(body
+            ).append(jQuery('<div/>', {
+                'class': 'panel-heading',
+            }).append(Sao.common.richtext_toolbar()));
+            this.body = jQuery('<div>', {
+                'class': 'email-richtext form-control input-sm mousetrap',
+                'contenteditable': true,
+                'spellcheck': true,
+                'id': 'email-body',
+            }).appendTo(jQuery('<div/>', {
+                'class': 'panel-body',
+            }).appendTo(panel));
+
+            var print_frame = jQuery('<div/>', {
+                'class': 'col-md-6',
+            }).appendTo(body);
+            jQuery('<label/>', {
+                'text': Sao.i18n.gettext("Reports"),
+            }).appendTo(print_frame);
+            this.print_actions = {};
+            for (var i = 0; i < prints.length; i++) {
+                var print = prints[i];
+                var print_check = jQuery('<input/>', {
+                    'type': 'checkbox',
+                });
+                jQuery('<div/>', {
+                    'class': 'checkbox',
+                }).append(jQuery('<label/>'
+                ).text(Sao.i18n.gettext(print.name)
+                ).prepend(print_check)).appendTo(print_frame);
+                this.print_actions[print.id] = print_check;
+            }
+
+            this.files = jQuery('<div/>', {
+                'class': 'col-md-6',
+            }).appendTo(body);
+            jQuery('<label/>', {
+                'text': Sao.i18n.gettext("Attachments"),
+            }).appendTo(this.files);
+            this._add_file_button();
+
+            jQuery('<button/>', {
+                'class': 'btn btn-link',
+                'type': 'button',
+            }).text(' ' + Sao.i18n.gettext('Cancel')).prepend(
+                Sao.common.ICONFACTORY.get_icon_img('tryton-cancel')
+            ).click(function() {
+                this.response('RESPONSE_CANCEL');
+            }.bind(this)).appendTo(this.dialog.footer);
+
+            jQuery('<button/>', {
+                'class': 'btn btn-primary',
+                'type': 'submit',
+            }).text(' ' + Sao.i18n.gettext('Send')).prepend(
+                Sao.common.ICONFACTORY.get_icon_img('tryton-send')
+            ).appendTo(this.dialog.footer);
+            this.dialog.content.submit(function(e) {
+                e.preventDefault();
+                this.response('RESPONSE_OK');
+            }.bind(this));
+
+            this._fill_with(template);
+
+            this.el.modal('show');
+            this.el.on('hidden.bs.modal', function() {
+                jQuery(this).remove();
+            });
+        },
+        _add_file_button: function() {
+            var row = jQuery('<div/>').appendTo(this.files);
+            var file = jQuery('<input/>', {
+                'type': 'file',
+            }).appendTo(row);
+            var button = jQuery('<a/>', {
+                'class': 'close',
+                'title': Sao.i18n.gettext("Remove attachment"),
+            }).append(jQuery('<span/>', {
+                'aria-hidden': true,
+                'text': 'x',
+            })).append(jQuery('<span/>', {
+                'class': 'sr-only',
+            }).text(Sao.i18n.gettext("Remove")));
+            button.hide();
+            button.appendTo(row);
+
+            file.on('change', function() {
+                button.show();
+                this._add_file_button();
+            }.bind(this));
+            button.click(function() {
+                row.remove();
+            });
+        },
+        get_files: function() {
+            var prms = [];
+            var files = [];
+            this.files.find('input[type=file]').each(function(i, input) {
+                if (input.files.length) {
+                    var dfd = jQuery.Deferred();
+                    prms.push(dfd);
+                    Sao.common.get_file_data(
+                        input.files[0], function(data, filename) {
+                            files.push([filename, data]);
+                            dfd.resolve();
+                        });
+                }
+            });
+            return jQuery.when.apply(jQuery, prms).then(function() {
+                return files;
+            });
+        },
+        _fill_with: function(template) {
+            var prm;
+            if (template) {
+                prm = Sao.rpc({
+                    'method': 'model.ir.email.template.get',
+                    'params': [template, this.record.id, {}],
+                }, this.record.model.session);
+            } else {
+                prm = Sao.rpc({
+                    'method': 'model.ir.email.template.get_default',
+                    'params': [this.record.model.name, this.record.id, {}],
+                }, this.record.model.session);
+            }
+            prm.then(function(values) {
+                this.to.val((values.to || []).join(', '));
+                this.cc.val((values.cc || []).join(', '));
+                this.bcc.val((values.bcc || []).join(', '));
+                this.subject.val(values.subject || '');
+                this.body.html(Sao.HtmlSanitizer.sanitize(values.body || ''));
+                var print_ids = (values.reports || []);
+                for (var print_id in this.print_actions) {
+                    var check = this.print_actions[print_id];
+                    check.prop(
+                        'checked', ~print_ids.indexOf(parseInt(print_id, 10)));
+                }
+            }.bind(this));
+        },
+        response: function(response_id) {
+            if (response_id == 'RESPONSE_OK') {
+                var to = this.to.val();
+                var cc = this.cc.val();
+                var bcc = this.bcc.val();
+                var subject = this.subject.val();
+                var body = Sao.common.richtext_normalize(this.body.html());
+                var reports = [];
+                for (var id in this.print_actions) {
+                    var check = this.print_actions[id];
+                    if (check.prop('checked')) {
+                        reports.push(id);
+                    }
+                }
+                var record = this.record;
+                this.get_files().then(function(attachments) {
+                    return Sao.rpc({
+                        'method': 'model.ir.email.send',
+                        'params': [
+                            to, cc, bcc, subject, body,
+                            attachments,
+                            [record.model.name, record.id],
+                            reports, {}],
+                    }, record.model.session);
+                }).then(function() {
+                    this.destroy();
+                }.bind(this));
+            } else {
+                this.destroy();
+            }
+        },
+        destroy: function() {
+            this.el.modal('hide');
+        },
+    });
+
 }());

Reply via email to