diff options
Diffstat (limited to 'addons/web/static/src/js/web_client.js')
| -rw-r--r-- | addons/web/static/src/js/web_client.js | 368 |
1 files changed, 368 insertions, 0 deletions
diff --git a/addons/web/static/src/js/web_client.js b/addons/web/static/src/js/web_client.js new file mode 100644 index 00000000000..e4d943ce6bf --- /dev/null +++ b/addons/web/static/src/js/web_client.js @@ -0,0 +1,368 @@ +odoo.define('web.WebClient', ['web.ActionManager', 'web.core', 'web.crash_manager', 'web.data', 'web.Loading', 'web.Menu', 'web.Model', 'web.Notification', 'web.session', 'web.SystrayMenu', 'web.UserMenu', 'web.utils', 'web.Widget'], function (require) { +"use strict"; + +var ActionManager = require('web.ActionManager'); +var core = require('web.core'); +var crash_manager = require('web.crash_manager'); +var data = require('web.data'); +var Loading = require('web.Loading'); +var Menu = require('web.Menu'); +var Model = require('web.Model'); +var Notification = require('web.Notification'); +var session = require('web.session'); +var SystrayMenu = require('web.SystrayMenu'); +var UserMenu = require('web.UserMenu'); +var utils = require('web.utils'); +var Widget = require('web.Widget'); + +var QWeb = core.qweb; +var _t = core._t; + + +var WebClient = Widget.extend({ + events: { + 'click .oe_logo_edit_admin': 'logo_edit', + 'click .oe_logo img': 'on_logo_click', + }, + + init: function(parent, client_options) { + this.client_options = {}; + this._super(parent); + this.origin = undefined; + if (client_options) { + _.extend(this.client_options, client_options); + } + this._current_state = null; + this.menu_dm = new utils.DropMisordered(); + this.action_mutex = new utils.Mutex(); + this.set('title_part', {"zopenerp": "Odoo"}); + }, + start: function() { + var self = this; + this.on("change:title_part", this, this._title_changed); + this._title_changed(); + + core.bus.on('web_client_toggle_bars', this, function () { + this.toggle_bars.apply(this, arguments); + }); + + return session.session_bind(this.origin).then(function() { + self.bind_events(); + return self.show_common(); + }).then(function() { + if (session.session_is_valid()) { + self.show_application(); + } + if (self.client_options.action) { + self.action_manager.do_action(self.client_options.action); + delete(self.client_options.action); + } + self.trigger('web_client_ready'); + }); + }, + bind_events: function() { + var self = this; + $('.oe_systray').show(); + this.$el.on('mouseenter', '.oe_systray > div:not([data-toggle=tooltip])', function() { + $(this).attr('data-toggle', 'tooltip').tooltip().trigger('mouseenter'); + }); + this.$el.on('click', '.oe_dropdown_toggle', function(ev) { + ev.preventDefault(); + var $toggle = $(this); + var doc_width = $(document).width(); + var $menu = $toggle.siblings('.oe_dropdown_menu'); + $menu = $menu.size() >= 1 ? $menu : $toggle.find('.oe_dropdown_menu'); + var state = $menu.is('.oe_opened'); + setTimeout(function() { + // Do not alter propagation + $toggle.add($menu).toggleClass('oe_opened', !state); + if (!state) { + // Move $menu if outside window's edge + var offset = $menu.offset(); + var menu_width = $menu.width(); + var x = doc_width - offset.left - menu_width - 2; + if (x < 0) { + $menu.offset({ left: offset.left + x }).width(menu_width); + } + } + }, 0); + }); + core.bus.on('click', this, function(ev) { + $('.tooltip').remove(); + if (!$(ev.target).is('input[type=file]')) { + self.$el.find('.oe_dropdown_menu.oe_opened, .oe_dropdown_toggle.oe_opened').removeClass('oe_opened'); + } + }); + core.bus.on('set_full_screen', this, function (full_screen) { + this.set_content_full_screen(full_screen); + }); + }, + on_logo_click: function(ev){ + if (!this.has_uncommitted_changes()) { + return; + } else { + ev.preventDefault(); + } + }, + show_common: function() { + var self = this; + session.on('error', crash_manager, crash_manager.rpc_error); + self.notification = new Notification(this); + self.notification.appendTo(self.$('.openerp')); + self.loading = new Loading(self); + self.loading.appendTo(self.$('.openerp_webclient_container')); + self.action_manager = new ActionManager(self); + self.action_manager.replace(self.$('.oe_application')); + + core.bus.on('display_notification_warning', this, function (title, message) { + self.notification.warn(title, message); + }); + window.onerror = function (message, file, line) { + crash_manager.show_error({ + type: _t("Client Error"), + message: message, + data: {debug: file + ':' + line} + }); + }; + + }, + toggle_bars: function(value) { + this.$('tr:has(td.navbar),.oe_leftbar').toggle(value); + }, + has_uncommitted_changes: function() { + var $e = $.Event('clear_uncommitted_changes'); + core.bus.trigger('clear_uncommitted_changes', $e); + return $e.isDefaultPrevented(); + }, + /** + Sets the first part of the title of the window, dedicated to the current action. + */ + set_title: function(title) { + this.set_title_part("action", title); + }, + /** + Sets an arbitrary part of the title of the window. Title parts are identified by strings. Each time + a title part is changed, all parts are gathered, ordered by alphabetical order and displayed in the + title of the window separated by '-'. + */ + set_title_part: function(part, title) { + var tmp = _.clone(this.get("title_part")); + tmp[part] = title; + this.set("title_part", tmp); + }, + _title_changed: function() { + var parts = _.sortBy(_.keys(this.get("title_part")), function(x) { return x; }); + var tmp = ""; + _.each(parts, function(part) { + var str = this.get("title_part")[part]; + if (str) { + tmp = tmp ? tmp + " - " + str : str; + } + }, this); + document.title = tmp; + }, + show_application: function() { + var self = this; + self.toggle_bars(true); + + self.update_logo(); + + // Menu is rendered server-side thus we don't want the widget to create any dom + self.menu = new Menu(self); + self.menu.setElement(this.$el.parents().find('.oe_application_menu_placeholder')); + self.menu.on('menu_click', this, this.on_menu_action); + + // Create the user menu (rendered client-side) + self.user_menu = new UserMenu(self); + var user_menu_loaded = self.user_menu.appendTo(this.$el.parents().find('.oe_user_menu_placeholder')); + self.user_menu.on('user_logout', self, self.on_logout); + self.user_menu.do_update(); + + // Create the systray menu (rendered server-side) + self.systray_menu = new SystrayMenu(self); + self.systray_menu.setElement(this.$el.parents().find('.oe_systray')); + var systray_menu_loaded = self.systray_menu.start(); + + // Start the menu once both systray and user menus are rendered + // to prevent overflows while loading + $.when(systray_menu_loaded, user_menu_loaded).done(function() { + self.menu.start(); + }); + + self.bind_hashchange(); + self.set_title(); + self.check_timezone(); + if (self.client_options.action_post_login) { + self.action_manager.do_action(self.client_options.action_post_login); + delete(self.client_options.action_post_login); + } + }, + update_logo: function() { + var company = session.company_id; + var img = session.url('/web/binary/company_logo' + '?db=' + session.db + (company ? '&company=' + company : '')); + this.$('.oe_logo img').attr('src', '').attr('src', img); + this.$('.oe_logo_edit').toggleClass('oe_logo_edit_admin', session.uid === 1); + }, + logo_edit: function(ev) { + var self = this; + ev.preventDefault(); + self.alive(new Model("res.users").get_func("read")(session.uid, ["company_id"])).then(function(res) { + self.rpc("/web/action/load", { action_id: "base.action_res_company_form" }).done(function(result) { + result.res_id = res.company_id[0]; + result.target = "new"; + result.views = [[false, 'form']]; + result.flags = { + action_buttons: true, + headless: true, + }; + self.action_manager.do_action(result); + var form = self.action_manager.dialog_widget.views.form.controller; + form.on("on_button_cancel", self.action_manager, self.action_manager.dialog_stop); + form.on('record_saved', self, function() { + self.action_manager.dialog_stop(); + self.update_logo(); + }); + }); + }); + return false; + }, + check_timezone: function() { + var self = this; + return self.alive(new Model('res.users').call('read', [[session.uid], ['tz_offset']])).then(function(result) { + var user_offset = result[0]['tz_offset']; + var offset = -(new Date().getTimezoneOffset()); + // _.str.sprintf()'s zero front padding is buggy with signed decimals, so doing it manually + var browser_offset = (offset < 0) ? "-" : "+"; + browser_offset += _.str.sprintf("%02d", Math.abs(offset / 60)); + browser_offset += _.str.sprintf("%02d", Math.abs(offset % 60)); + if (browser_offset !== user_offset) { + var $icon = $(QWeb.render('WebClient.timezone_systray')); + $icon.on('click', function() { + var notification = self.do_warn(_t("Timezone Mismatch"), QWeb.render('WebClient.timezone_notification', { + user_timezone: session.user_context.tz || 'UTC', + user_offset: user_offset, + browser_offset: browser_offset, + }), true); + notification.element.find('.oe_webclient_timezone_notification').on('click', function() { + notification.close(); + }).find('a').on('click', function() { + notification.close(); + self.user_menu.on_menu_settings(); + return false; + }); + }); + $icon.prependTo(window.$('.oe_systray')); + } + }); + }, + destroy_content: function() { + _.each(_.clone(this.getChildren()), function(el) { + el.destroy(); + }); + this.$el.children().remove(); + }, + do_reload: function() { + var self = this; + return this.session.session_reload().then(function () { + session.load_modules(true).then( + self.menu.proxy('do_reload')); }); + }, + do_notify: function() { + var n = this.notification; + return n.notify.apply(n, arguments); + }, + do_warn: function() { + var n = this.notification; + return n.warn.apply(n, arguments); + }, + on_logout: function() { + var self = this; + if (!this.has_uncommitted_changes()) { + self.action_manager.do_action('logout'); + } + }, + bind_hashchange: function() { + var self = this; + $(window).bind('hashchange', this.on_hashchange); + + var state = $.bbq.getState(true); + if (_.isEmpty(state) || state.action == "login") { + self.menu.is_bound.done(function() { + new Model("res.users").call("read", [session.uid, ["action_id"]]).done(function(data) { + if(data.action_id) { + self.action_manager.do_action(data.action_id[0]); + self.menu.open_action(data.action_id[0]); + } else { + var first_menu_id = self.menu.$el.find("a:first").data("menu"); + if(first_menu_id) { + self.menu.menu_click(first_menu_id); + } } + }); + }); + } else { + $(window).trigger('hashchange'); + } + }, + on_hashchange: function(event) { + var self = this; + var stringstate = event.getState(false); + if (!_.isEqual(this._current_state, stringstate)) { + var state = event.getState(true); + if(!state.action && state.menu_id) { + self.menu.is_bound.done(function() { + self.menu.menu_click(state.menu_id); + }); + } else { + state._push_me = false; // no need to push state back... + this.action_manager.do_load_state(state, !!this._current_state); + } + } + this._current_state = stringstate; + }, + do_push_state: function(state) { + this.set_title(state.title); + delete state.title; + var url = '#' + $.param(state); + this._current_state = $.deparam($.param(state), false); // stringify all values + $.bbq.pushState(url); + this.trigger('state_pushed', state); + }, + on_menu_action: function(options) { + var self = this; + return this.menu_dm.add(this.rpc("/web/action/load", { action_id: options.action_id })) + .then(function (result) { + return self.action_mutex.exec(function() { + if (options.needaction) { + result.context = new data.CompoundContext(result.context, { + search_default_message_unread: true, + search_disable_custom_filters: true, + }); + } + var completed = $.Deferred(); + $.when(self.action_manager.do_action(result, { + clear_breadcrumbs: true, + action_menu_id: self.menu.current_menu, + })).fail(function() { + self.menu.open_menu(options.previous_menu_id); + }).always(function() { + completed.resolve(); + }); + setTimeout(function() { + completed.resolve(); + }, 2000); + // We block the menu when clicking on an element until the action has correctly finished + // loading. If something crash, there is a 2 seconds timeout before it's unblocked. + return completed; + }); + }); + }, + set_content_full_screen: function(fullscreen) { + $(document.body).css('overflow-y', fullscreen ? 'hidden' : 'scroll'); + this.$('.oe_webclient').toggleClass( + 'oe_content_full_screen', fullscreen); + }, +}); + +return WebClient; + +}); |
