diff --git a/addons/crm/static/src/js/tour.js b/addons/crm/static/src/js/tour.js index 66db8f9d43e..5895c8f14b9 100644 --- a/addons/crm/static/src/js/tour.js +++ b/addons/crm/static/src/js/tour.js @@ -71,7 +71,7 @@ tour.STEPS.MENU_MORE, { content: _t("Configuration options are available in the Settings app."), position: "bottom" }, { - trigger: ".o_web_settings_dashboard textarea#user_emails", + trigger: ".o_web_settings_dashboard .o_user_emails", content: _t("Invite coworkers via email.
Enter one email per line."), position: "right" }, tour.STEPS.TOGGLE_HOME_MENU, diff --git a/addons/project/static/src/js/tour.js b/addons/project/static/src/js/tour.js index 270206c80d7..c0aeaa15924 100644 --- a/addons/project/static/src/js/tour.js +++ b/addons/project/static/src/js/tour.js @@ -94,7 +94,7 @@ tour.STEPS.MENU_MORE, { content: _t("Configuration options are available in the Settings app."), position: "bottom" }, { - trigger: ".o_web_settings_dashboard textarea#user_emails", + trigger: ".o_web_settings_dashboard .o_user_emails", content: _t("Invite coworkers via email.
Enter one email per line."), position: "right" }, tour.STEPS.TOGGLE_HOME_MENU, diff --git a/addons/web_settings_dashboard/controllers/main.py b/addons/web_settings_dashboard/controllers/main.py index fdda07c5090..21423ec902d 100644 --- a/addons/web_settings_dashboard/controllers/main.py +++ b/addons/web_settings_dashboard/controllers/main.py @@ -32,6 +32,7 @@ class WebSettingsDashboard(http.Controller): SELECT count(u.*) FROM res_users u WHERE active=true AND + share=false AND NOT exists(SELECT 1 FROM res_users_log WHERE create_uid=u.id) """) pending_count = cr.dictfetchall()[0].get('count') @@ -39,8 +40,9 @@ class WebSettingsDashboard(http.Controller): cr.execute(""" SELECT id, login FROM res_users u - WHERE active=true - AND NOT exists(SELECT 1 FROM res_users_log WHERE create_uid=u.id) + WHERE active=true AND + share=false AND + NOT exists(SELECT 1 FROM res_users_log WHERE create_uid=u.id) ORDER BY id desc LIMIT 10 """) diff --git a/addons/web_settings_dashboard/static/src/js/dashboard.js b/addons/web_settings_dashboard/static/src/js/dashboard.js index 06f24ab8735..d5705c80e42 100644 --- a/addons/web_settings_dashboard/static/src/js/dashboard.js +++ b/addons/web_settings_dashboard/static/src/js/dashboard.js @@ -61,55 +61,86 @@ var Dashboard = AbstractAction.extend({ load_company: function (data) { return new DashboardCompany(this, data.company).replace(this.$('.o_web_settings_dashboard_company')); - } + }, }); var DashboardInvitations = Widget.extend({ template: 'DashboardInvitations', events: { - 'click .o_web_settings_dashboard_invitations': 'send_invitations', + 'click .o_web_settings_dashboard_invite': '_onClickInvite', 'click .o_web_settings_dashboard_access_rights': 'on_access_rights_clicked', 'click .o_web_settings_dashboard_user': 'on_user_clicked', 'click .o_web_settings_dashboard_more': 'on_more', + 'click .o_badge_remove': '_onClickBadgeRemove', + 'keydown .o_user_emails': '_onKeydownUserEmails', }, - init: function(parent, data){ + init: function(parent, data) { this.data = data; this.parent = parent; + this.emails = []; return this._super.apply(this, arguments); }, - send_invitations: function(e){ - var self = this; - var $target = $(e.currentTarget); - var user_emails = _.filter($(e.delegateTarget).find('#user_emails').val().split(/[\n, ]/), function(email){ - return email !== ""; - }); - var re = /^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,63}(?:\.[a-z]{2})?)$/i; - var is_valid_emails = _.every(user_emails, function(email) { - return re.test(email); - }); - if (is_valid_emails) { - // Disable button - $target.prop('disabled', true); - $target.find('i.fa-cog').removeClass('hidden'); - // Try to create user accountst - this._rpc({ - model: 'res.users', - method: 'web_dashboard_create_users', - args: [user_emails], - }) - .then(function() { - self.reload(); - }) - .always(function() { - // Re-enable button - $(e.delegateTarget).find('.o_web_settings_dashboard_invitations').prop('disabled', false); - $(e.delegateTarget).find('i.fa-cog').addClass('hidden'); - }); + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * Creates and appends badges for valid and unique email addresses + * + * @private + */ + _createBadges: function () { + var $userEmails = this.$('.o_user_emails'); + var value = $userEmails.val().trim(); + if (value) { + // filter out duplicates + var emails = _.uniq(value.split(/[ ,;\n]+/)); + + // filter out invalid email addresses + var invalidEmails = _.reject(emails, this._validateEmail.bind(this)); + if (invalidEmails.length) { + this.do_warn(_.str.sprintf(_t('The following email addresses are invalid: %s.'), invalidEmails.join(', '))); + } + emails = _.difference(emails, invalidEmails); + + // filter out already processed or pending addresses + var pendingEmails = _.map(this.data.pending_users, function (info) { + return info[1]; + }); + var existingEmails = _.intersection(emails, this.emails.concat(pendingEmails)); + if (existingEmails.length) { + this.do_warn(_.str.sprintf(_t('The following email addresses already exist: %s.'), existingEmails.join(', '))); + } + emails = _.difference(emails, existingEmails); + + // add valid email addresses, if any + if (emails.length) { + this.emails = this.emails.concat(emails); + $userEmails.before(QWeb.render('EmailBadge', {emails: emails})); + $userEmails.val(''); + } } - else { - this.do_warn(_t("Please provide valid email addresses"), ""); - } + }, + /** + * Removes a given badge from the DOM, and its associated email address + * + * @private + * @param {jQueryElement} $badge + */ + _removeBadge: function ($badge) { + var email = $badge.text().trim(); + this.emails = _.without(this.emails, email); + $badge.remove(); + }, + /** + * @private + * @param {string} email + * @returns {boolean} true iff the given email address is valid + */ + _validateEmail: function (email) { + var re = /^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,63}(?:\.[a-z]{2})?)$/i; + return re.test(email); }, on_access_rights_clicked: function (e) { var self = this; @@ -138,11 +169,13 @@ var DashboardInvitations = Widget.extend({ var self = this; e.preventDefault(); var action = { + name: _t('Users'), type: 'ir.actions.act_window', view_type: 'form', view_mode: 'tree,form', res_model: 'res.users', domain: [['log_ids', '=', false]], + context: {search_default_no_share: true}, views: [[false, 'list'], [false, 'form']], }; this.do_action(action,{ @@ -152,6 +185,58 @@ var DashboardInvitations = Widget.extend({ reload:function(){ return this.parent.load(['invitations']); }, + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + /** + * @private + * @param {MouseEvent} ev + */ + _onClickBadgeRemove: function (ev) { + var $badge = $(ev.target).closest('.badge'); + this._removeBadge($badge); + }, + /** + * @private + * @param {MouseEvent} ev + */ + _onClickInvite: function (ev) { + var self = this; + this._createBadges(); + if (this.emails.length) { + var $button = $(ev.target); + $button.button('loading'); + this._rpc({ + model: 'res.users', + method: 'web_dashboard_create_users', + args: [this.emails], + }) + .then(function () { + self.reload(); + }) + .fail(function () { + $button.button('reset'); + }); + } + }, + /** + * @private + * @param {KeyboardEvent} ev + */ + _onKeydownUserEmails: function (ev) { + var $userEmails = this.$('.o_user_emails'); + var keyCodes = [$.ui.keyCode.TAB, $.ui.keyCode.COMMA, $.ui.keyCode.ENTER, $.ui.keyCode.SPACE]; + if (_.contains(keyCodes, ev.which)) { + ev.preventDefault(); + this._createBadges(); + } + // remove last badge on backspace + if (ev.which === $.ui.keyCode.BACKSPACE && this.emails.length && !$userEmails.val()) { + this._removeBadge(this.$('.o_web_settings_dashboard_invitation_form .badge:last')); + } + }, }); var DashboardApps = Widget.extend({ diff --git a/addons/web_settings_dashboard/static/src/less/dashboard.less b/addons/web_settings_dashboard/static/src/less/dashboard.less index f14b7164197..06e19f02095 100644 --- a/addons/web_settings_dashboard/static/src/less/dashboard.less +++ b/addons/web_settings_dashboard/static/src/less/dashboard.less @@ -68,9 +68,6 @@ width: 100%; } } - a.o_web_settings_dashboard_user { - line-height: 24px; - } } .o_web_settings_dashboard_planner { .progress { diff --git a/addons/web_settings_dashboard/static/src/xml/dashboard.xml b/addons/web_settings_dashboard/static/src/xml/dashboard.xml index 8c90f99de8a..f7ccf053e8d 100644 --- a/addons/web_settings_dashboard/static/src/xml/dashboard.xml +++ b/addons/web_settings_dashboard/static/src/xml/dashboard.xml @@ -46,6 +46,16 @@
+ + + + + + + + + +
@@ -63,24 +73,21 @@

- Invite new users: - -