- The PosBox software installed on this posbox is version 15,
+ If you need to display the current customer basket on another device, you can do it here.
+
+
+ The PosBox software installed on this posbox is version 16,
the posbox version number is independent from Odoo. You can upgrade
the software on the upgrade page.
diff --git a/addons/hw_screen/__init__.py b/addons/hw_screen/__init__.py
new file mode 100644
index 00000000000..502f47ce57c
--- /dev/null
+++ b/addons/hw_screen/__init__.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2004-2015 Tiny SPRL ().
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+##############################################################################
+
+import controllers
diff --git a/addons/hw_screen/__openerp__.py b/addons/hw_screen/__openerp__.py
new file mode 100644
index 00000000000..1e6438bf25c
--- /dev/null
+++ b/addons/hw_screen/__openerp__.py
@@ -0,0 +1,42 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2004-2015 Tiny SPRL ().
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+##############################################################################
+
+
+{
+ 'name': 'Screen Driver',
+ 'version': '1.0',
+ 'category': 'Hardware Drivers',
+ 'sequence': 6,
+ 'summary': 'Provides support for customer facing displays',
+ 'website': 'https://www.odoo.com/page/point-of-sale',
+ 'description': """
+Screen Driver
+=============
+
+This module allows the POS client to send rendered HTML to a remotely
+installed screen. This module then displays this HTML using a web
+browser.
+""",
+ 'author': 'OpenERP SA',
+ 'depends': ['hw_proxy'],
+ 'installable': False,
+ 'auto_install': False,
+}
diff --git a/addons/hw_screen/controllers/__init__.py b/addons/hw_screen/controllers/__init__.py
new file mode 100644
index 00000000000..517985a53ef
--- /dev/null
+++ b/addons/hw_screen/controllers/__init__.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2004-2015 Tiny SPRL ().
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+##############################################################################
+
+import main
diff --git a/addons/hw_screen/controllers/main.py b/addons/hw_screen/controllers/main.py
new file mode 100644
index 00000000000..aa4b8a1a729
--- /dev/null
+++ b/addons/hw_screen/controllers/main.py
@@ -0,0 +1,185 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2004-2015 Tiny SPRL ().
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+##############################################################################
+
+
+from openerp import http
+from openerp.tools import config
+from openerp.addons.web.controllers import main as web
+
+import logging
+import netifaces as ni
+import os
+from subprocess import call
+import time
+import threading
+
+self_port = str(config['xmlrpc_port'] or 8069)
+
+_logger = logging.getLogger(__name__)
+
+
+class HardwareScreen(web.Home):
+
+ event_data = threading.Event()
+ pos_client_data = {'rendered_html': False,
+ 'ip_from': False}
+ display_in_use = ''
+ failure_count = {}
+
+ def _call_xdotools(self, keystroke):
+ os.environ['DISPLAY'] = ":0.0"
+ os.environ['XAUTHORITY'] = "/run/lightdm/pi/xauthority"
+ try:
+ call(['xdotool', 'key', keystroke])
+ return "xdotool succeeded in stroking " + keystroke
+ except:
+ return "xdotool threw an error, maybe it is not installed on the posbox"
+
+ @http.route('/hw_proxy/display_refresh', type='json', auth='none', cors='*')
+ def display_refresh(self):
+ return self._call_xdotools('F5')
+
+ # POS CASHIER'S ROUTES
+ @http.route('/hw_proxy/customer_facing_display', type='json', auth='none', cors='*')
+ def update_user_facing_display(self, html=None):
+ request_ip = http.request.httprequest.remote_addr
+ if request_ip == HardwareScreen.pos_client_data.get('ip_from', ''):
+ HardwareScreen.pos_client_data['rendered_html'] = html
+ HardwareScreen.event_data.set()
+
+ return {'status': 'updated'}
+ else:
+ return {'status': 'failed'}
+
+ @http.route('/hw_proxy/take_control', type='json', auth='none', cors='*')
+ def take_control(self, html=None):
+ # ALLOW A CASHIER TO TAKE CONTROL OVER THE POSBOX, IN CASE OF MULTIPLE CASHIER PER POSBOX
+ HardwareScreen.pos_client_data['rendered_html'] = html
+ HardwareScreen.pos_client_data['ip_from'] = http.request.httprequest.remote_addr
+ HardwareScreen.event_data.set()
+
+ return {'status': 'success',
+ 'message': 'You now have access to the display'}
+
+ @http.route('/hw_proxy/test_ownership', type='json', auth='none', cors='*')
+ def test_ownership(self):
+ if HardwareScreen.pos_client_data.get('ip_from') == http.request.httprequest.remote_addr:
+ return {'status': 'OWNER'}
+ else:
+ return {'status': 'NOWNER'}
+
+ # POSBOX ROUTES (SELF)
+ @http.route('/point_of_sale/display', type='http', auth='none')
+ def render_main_display(self):
+ return self._get_html()
+
+ @http.route('/point_of_sale/get_serialized_order', type='json', auth='none')
+ def get_serialized_order(self):
+ request_addr = http.request.httprequest.remote_addr
+ result = HardwareScreen.pos_client_data
+ if HardwareScreen.display_in_use and request_addr != HardwareScreen.display_in_use:
+ if not HardwareScreen.failure_count.get(request_addr):
+ HardwareScreen.failure_count[request_addr] = 0
+ if HardwareScreen.failure_count[request_addr] > 0:
+ time.sleep(10)
+ HardwareScreen.failure_count[request_addr] += 1
+ return {'rendered_html': """
Not Authorized. Another browser is in use to display for the client. Please refresh.
""",
+ 'stop_longpolling': True,
+ 'ip_from': request_addr}
+
+ # IMPLEMENTATION OF LONGPOLLING
+ # Times out 2 seconds before the JS request does
+ if HardwareScreen.event_data.wait(28):
+ HardwareScreen.event_data.clear()
+ HardwareScreen.failure_count[request_addr] = 0
+ return result
+ return {'rendered_html': False,
+ 'ip_from': HardwareScreen.pos_client_data['ip_from']}
+
+ def _get_html(self):
+ cust_js = None
+ interfaces = ni.interfaces()
+ my_ip = '127.0.0.1'
+ HardwareScreen.display_in_use = http.request.httprequest.remote_addr
+
+ with open(os.path.join(os.path.dirname(__file__), "../static/src/js/worker.js")) as js:
+ cust_js = js.read()
+
+ with open(os.path.join(os.path.dirname(__file__), "../static/src/css/cust_css.css")) as css:
+ cust_css = css.read()
+
+ display_ifaces = ""
+ for iface_id in interfaces:
+ iface_obj = ni.ifaddresses(iface_id)
+ ifconfigs = iface_obj.get(ni.AF_INET, [])
+ for conf in ifconfigs:
+ if conf.get('addr'):
+ display_ifaces += "
" + iface_id + "
"
+ display_ifaces += "
" + conf.get('addr') + "
"
+ display_ifaces += "
" + conf.get('netmask') + "
"
+ # What is my external IP ?
+ if iface_id != 'lo':
+ my_ip = conf.get('addr')
+
+ my_ip_port = my_ip + ":" + self_port
+
+ html = """
+
+
+
+ Odoo -- Point of Sale
+
+
+
+
+
+
+
+
+
+
+
+
+
Odoo Point of Sale
+
POSBox Client display
+
My IPs
+
+
+
Interface
+
IP
+
Netmask
+
+ """ + display_ifaces + """
+
+
The customer cart will be displayed here once a Point of Sale session is started.
+
Odoo version 11 or above is required.
+
+
+
+
+
+ """
+ return html
diff --git a/addons/hw_screen/static/src/css/cust_css.css b/addons/hw_screen/static/src/css/cust_css.css
new file mode 100644
index 00000000000..69fddb25457
--- /dev/null
+++ b/addons/hw_screen/static/src/css/cust_css.css
@@ -0,0 +1,21 @@
+html {
+ width: 100%;
+ height: 100%;
+ font-size: 11px;
+}
+
+body {
+ width: 100%;
+ height: 100%;
+ font-size: 14px;
+ margin: 0;
+}
+
+.original_body {
+ background-color: #797083;
+ color: white;
+}
+
+.ajax_got_body {
+ color: black;
+}
\ No newline at end of file
diff --git a/addons/hw_screen/static/src/js/worker.js b/addons/hw_screen/static/src/js/worker.js
new file mode 100644
index 00000000000..55e389e78ab
--- /dev/null
+++ b/addons/hw_screen/static/src/js/worker.js
@@ -0,0 +1,60 @@
+ $(function() {
+ "use strict";
+ // mergedHead will be turned to true the first time we receive something from a new host
+ // It allows to transform the only once
+ var mergedHead = false;
+ var current_client_url = "";
+ var stop_longpolling = false;
+
+ function longpolling() {
+ $.ajax({
+ type: 'POST',
+ url: 'http://'+window.location.host+'/point_of_sale/get_serialized_order',
+ dataType: 'json',
+ beforeSend: function(xhr){xhr.setRequestHeader('Content-Type', 'application/json');},
+ data: JSON.stringify({jsonrpc: '2.0'}),
+
+ success: function(data) {
+ if (typeof data.result.stop_longpolling !== 'undefined') {
+ stop_longpolling = data.result.stop_longpolling;
+ }
+ if (data.result.ip_from && data.result.rendered_html) {
+ var trimmed = $.trim(data.result.rendered_html);
+ var $parsedHTML = $('
').html($.parseHTML(trimmed,true)); // WARNING: the true here will executes any script present in the string to parse
+ var new_client_url = $parsedHTML.find(".resources > base").attr('href');
+
+ if (!mergedHead || (current_client_url !== new_client_url)) {
+
+ mergedHead = true;
+ current_client_url = new_client_url;
+ $("body").removeClass('original_body').addClass('ajax_got_body');
+ $("head").children().not('.origin').remove();
+ $("head").append($parsedHTML.find(".resources").html());
+ }
+
+ $(".container").html($parsedHTML.find('.pos-customer_facing_display').html());
+ $(".container").attr('class', 'container').addClass($parsedHTML.find('.pos-customer_facing_display').attr('class'));
+
+ var d = $('.pos_orderlines_list');
+ d.scrollTop(d.prop("scrollHeight"));
+
+ // Here we execute the code coming from the pos, apparently $.parseHTML() executes scripts right away,
+ // Since we modify the dom afterwards, the script might not have any effect
+ if (typeof foreign_js !== 'undefined' && $.isFunction(foreign_js)) {
+ foreign_js();
+ }
+ }
+ },
+
+ complete: function(jqXHR,err) {
+ if (!stop_longpolling) {
+ longpolling();
+ }
+ },
+
+ timeout: 30000,
+ });
+ };
+
+ longpolling();
+ });
\ No newline at end of file
diff --git a/addons/point_of_sale/tools/posbox/configuration/setup_ramdisks.sh b/addons/point_of_sale/tools/posbox/configuration/setup_ramdisks.sh
index ce2f2e633e6..618c4b1fd48 100755
--- a/addons/point_of_sale/tools/posbox/configuration/setup_ramdisks.sh
+++ b/addons/point_of_sale/tools/posbox/configuration/setup_ramdisks.sh
@@ -11,7 +11,7 @@ create_ramdisk () {
echo "Creating ramdisk for ${1} of size ${SIZE}..."
mount -t tmpfs -o size="${SIZE}" tmpfs "${RAMDISK}"
- rsync -a --exclude="swap" --exclude="apt" --exclude="dpkg" "${ORIGINAL}/" "${RAMDISK}/"
+ rsync -a --exclude="swap" --exclude="apt" --exclude="dpkg" --exclude=".mozilla" "${ORIGINAL}/" "${RAMDISK}/"
mount --bind "${RAMDISK}" "${ORIGINAL}"
}
diff --git a/addons/point_of_sale/tools/posbox/overwrite_after_init/etc/lightdm/lightdm.conf b/addons/point_of_sale/tools/posbox/overwrite_after_init/etc/lightdm/lightdm.conf
new file mode 100644
index 00000000000..977903c1c90
--- /dev/null
+++ b/addons/point_of_sale/tools/posbox/overwrite_after_init/etc/lightdm/lightdm.conf
@@ -0,0 +1,8 @@
+
+[LightDM]
+user-authority-in-system-dir=true
+
+[SeatDefaults]
+xserver-command=/usr/bin/X -s 0 dpms -nolisten tcp
+greeter-hide-users=false
+autologin-user=pi
\ No newline at end of file
diff --git a/addons/point_of_sale/tools/posbox/overwrite_after_init/etc/xdg/openbox/autostart b/addons/point_of_sale/tools/posbox/overwrite_after_init/etc/xdg/openbox/autostart
new file mode 100755
index 00000000000..e2b61e2f591
--- /dev/null
+++ b/addons/point_of_sale/tools/posbox/overwrite_after_init/etc/xdg/openbox/autostart
@@ -0,0 +1,6 @@
+#!/bin/bash
+xset s off
+xset -dpms
+
+export HOME=/tmp
+/usr/bin/firefox http://localhost:8069/point_of_sale/display &
diff --git a/addons/point_of_sale/tools/posbox/overwrite_after_init/usr/lib/firefox-esr/browser/extensions/{4D498D0A-05AD-4fdb-97B5-8A0AABC1FC5B}/META-INF/manifest.mf b/addons/point_of_sale/tools/posbox/overwrite_after_init/usr/lib/firefox-esr/browser/extensions/{4D498D0A-05AD-4fdb-97B5-8A0AABC1FC5B}/META-INF/manifest.mf
new file mode 100644
index 00000000000..63a9098e00d
--- /dev/null
+++ b/addons/point_of_sale/tools/posbox/overwrite_after_init/usr/lib/firefox-esr/browser/extensions/{4D498D0A-05AD-4fdb-97B5-8A0AABC1FC5B}/META-INF/manifest.mf
@@ -0,0 +1,32 @@
+Manifest-Version: 1.0
+
+Name: install.rdf
+Digest-Algorithms: MD5 SHA1
+MD5-Digest: xnFKNbJgc33dy/FJ+4cmHA==
+SHA1-Digest: 4YLgHgdgCw4lGCfsX7+r4zVmKJ4=
+
+Name: chrome.manifest
+Digest-Algorithms: MD5 SHA1
+MD5-Digest: O5ce4bajAqUxG6iVul0K2w==
+SHA1-Digest: wb6VleWnV5GgLypmg1ij9TfmPLE=
+
+Name: content/rkioskbrowser.js
+Digest-Algorithms: MD5 SHA1
+MD5-Digest: /cF15tfVnbkfbxRVTL5N+g==
+SHA1-Digest: lvbY92PbsjwY+0TguEYoXWw0lxQ=
+
+Name: content/rkioskbrowser.xul
+Digest-Algorithms: MD5 SHA1
+MD5-Digest: NFOIpAmZtn20Y+XfTo4cpw==
+SHA1-Digest: VVIo7GdG+4mvAOtgR0DJUsSc/yg=
+
+Name: content/rkioskunknownContentType.xul
+Digest-Algorithms: MD5 SHA1
+MD5-Digest: 6cNGoHJS9Xvm+wmvo4IesQ==
+SHA1-Digest: T8i1MkMKjmn5HICqWv+iXSPRKIU=
+
+Name: content/rkioskxpinstallConfirm.xul
+Digest-Algorithms: MD5 SHA1
+MD5-Digest: SxN1YnRJiuood2WhU85v9g==
+SHA1-Digest: Msj21u4bTAKUi496xld5YCGdjiA=
+
diff --git a/addons/point_of_sale/tools/posbox/overwrite_after_init/usr/lib/firefox-esr/browser/extensions/{4D498D0A-05AD-4fdb-97B5-8A0AABC1FC5B}/META-INF/mozilla.rsa b/addons/point_of_sale/tools/posbox/overwrite_after_init/usr/lib/firefox-esr/browser/extensions/{4D498D0A-05AD-4fdb-97B5-8A0AABC1FC5B}/META-INF/mozilla.rsa
new file mode 100644
index 0000000000000000000000000000000000000000..521b08d182a938ae7a831df4a347f00b9f42e54d
GIT binary patch
literal 4197
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
literal 0
HcmV?d00001
diff --git a/addons/point_of_sale/tools/posbox/overwrite_after_init/usr/lib/firefox-esr/browser/extensions/{4D498D0A-05AD-4fdb-97B5-8A0AABC1FC5B}/META-INF/mozilla.sf b/addons/point_of_sale/tools/posbox/overwrite_after_init/usr/lib/firefox-esr/browser/extensions/{4D498D0A-05AD-4fdb-97B5-8A0AABC1FC5B}/META-INF/mozilla.sf
new file mode 100644
index 00000000000..37eae3e21fb
--- /dev/null
+++ b/addons/point_of_sale/tools/posbox/overwrite_after_init/usr/lib/firefox-esr/browser/extensions/{4D498D0A-05AD-4fdb-97B5-8A0AABC1FC5B}/META-INF/mozilla.sf
@@ -0,0 +1,4 @@
+Signature-Version: 1.0
+MD5-Digest-Manifest: 7hFyhNW67+H+rf9OhWGDkA==
+SHA1-Digest-Manifest: HvfAQQLXphSZ9m8KBorJMX8MUnE=
+
diff --git a/addons/point_of_sale/tools/posbox/overwrite_after_init/usr/lib/firefox-esr/browser/extensions/{4D498D0A-05AD-4fdb-97B5-8A0AABC1FC5B}/README b/addons/point_of_sale/tools/posbox/overwrite_after_init/usr/lib/firefox-esr/browser/extensions/{4D498D0A-05AD-4fdb-97B5-8A0AABC1FC5B}/README
new file mode 100644
index 00000000000..c8c1ce58823
--- /dev/null
+++ b/addons/point_of_sale/tools/posbox/overwrite_after_init/usr/lib/firefox-esr/browser/extensions/{4D498D0A-05AD-4fdb-97B5-8A0AABC1FC5B}/README
@@ -0,0 +1,3 @@
+The R-kiosk module has been downloaded from https://addons.mozilla.org/en-US/firefox/addon/r-kiosk/
+It is in the public domain as stated on https://addons.mozilla.org/en-US/firefox/addon/r-kiosk/eula/
+This mozilla module is shipped by Odoo to provide out of the box Client facing display to Odoo's Posbox
\ No newline at end of file
diff --git a/addons/point_of_sale/tools/posbox/overwrite_after_init/usr/lib/firefox-esr/browser/extensions/{4D498D0A-05AD-4fdb-97B5-8A0AABC1FC5B}/chrome.manifest b/addons/point_of_sale/tools/posbox/overwrite_after_init/usr/lib/firefox-esr/browser/extensions/{4D498D0A-05AD-4fdb-97B5-8A0AABC1FC5B}/chrome.manifest
new file mode 100644
index 00000000000..e4957b8d5e8
--- /dev/null
+++ b/addons/point_of_sale/tools/posbox/overwrite_after_init/usr/lib/firefox-esr/browser/extensions/{4D498D0A-05AD-4fdb-97B5-8A0AABC1FC5B}/chrome.manifest
@@ -0,0 +1,4 @@
+content RKiosk content/
+overlay chrome://browser/content/browser.xul chrome://rkiosk/content/rkioskbrowser.xul
+overlay chrome://mozapps/content/xpinstall/xpinstallConfirm.xul chrome://rkiosk/content/rkioskxpinstallConfirm.xul
+overlay chrome://mozapps/content/downloads/unknownContentType.xul chrome://rkiosk/content/rkioskunknownContentType.xul
diff --git a/addons/point_of_sale/tools/posbox/overwrite_after_init/usr/lib/firefox-esr/browser/extensions/{4D498D0A-05AD-4fdb-97B5-8A0AABC1FC5B}/content/rkioskbrowser.js b/addons/point_of_sale/tools/posbox/overwrite_after_init/usr/lib/firefox-esr/browser/extensions/{4D498D0A-05AD-4fdb-97B5-8A0AABC1FC5B}/content/rkioskbrowser.js
new file mode 100644
index 00000000000..5ece9a9e06b
--- /dev/null
+++ b/addons/point_of_sale/tools/posbox/overwrite_after_init/usr/lib/firefox-esr/browser/extensions/{4D498D0A-05AD-4fdb-97B5-8A0AABC1FC5B}/content/rkioskbrowser.js
@@ -0,0 +1,34 @@
+function Rkiosk_donothing()
+{
+
+
+}
+
+function rkioskclose()
+{
+ close();
+}
+
+function Rkiosk_navbar_setting()
+{
+ var rkiosk_navbar_enable="true";
+ var prefs = Components.classes["@mozilla.org/preferences-service;1"].
+ getService(Components.interfaces.nsIPrefBranch);
+ if (prefs.getPrefType("rkiosk.navbar") == prefs.PREF_BOOL){
+ if (prefs.getBoolPref("rkiosk.navbar")) rkiosk_navbar_enable = "false";
+ }
+ var rkiosk_element = document.getElementById("navigator-toolbox");
+ rkiosk_element.setAttribute("hidden", rkiosk_navbar_enable);
+}
+
+function RkioskBrowserStartup()
+{
+ Rkiosk_navbar_setting();
+ BrowserStartup();
+ setTimeout(RkioskdelayedStartup, 1000);
+}
+
+function RkioskdelayedStartup()
+{
+ window.fullScreen = true;
+}
diff --git a/addons/point_of_sale/tools/posbox/overwrite_after_init/usr/lib/firefox-esr/browser/extensions/{4D498D0A-05AD-4fdb-97B5-8A0AABC1FC5B}/content/rkioskbrowser.xul b/addons/point_of_sale/tools/posbox/overwrite_after_init/usr/lib/firefox-esr/browser/extensions/{4D498D0A-05AD-4fdb-97B5-8A0AABC1FC5B}/content/rkioskbrowser.xul
new file mode 100644
index 00000000000..d5fb0c140d1
--- /dev/null
+++ b/addons/point_of_sale/tools/posbox/overwrite_after_init/usr/lib/firefox-esr/browser/extensions/{4D498D0A-05AD-4fdb-97B5-8A0AABC1FC5B}/content/rkioskbrowser.xul
@@ -0,0 +1,175 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/addons/point_of_sale/tools/posbox/overwrite_after_init/usr/lib/firefox-esr/browser/extensions/{4D498D0A-05AD-4fdb-97B5-8A0AABC1FC5B}/content/rkioskunknownContentType.xul b/addons/point_of_sale/tools/posbox/overwrite_after_init/usr/lib/firefox-esr/browser/extensions/{4D498D0A-05AD-4fdb-97B5-8A0AABC1FC5B}/content/rkioskunknownContentType.xul
new file mode 100644
index 00000000000..faf2381d1fa
--- /dev/null
+++ b/addons/point_of_sale/tools/posbox/overwrite_after_init/usr/lib/firefox-esr/browser/extensions/{4D498D0A-05AD-4fdb-97B5-8A0AABC1FC5B}/content/rkioskunknownContentType.xul
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/addons/point_of_sale/tools/posbox/overwrite_after_init/usr/lib/firefox-esr/browser/extensions/{4D498D0A-05AD-4fdb-97B5-8A0AABC1FC5B}/content/rkioskxpinstallConfirm.xul b/addons/point_of_sale/tools/posbox/overwrite_after_init/usr/lib/firefox-esr/browser/extensions/{4D498D0A-05AD-4fdb-97B5-8A0AABC1FC5B}/content/rkioskxpinstallConfirm.xul
new file mode 100644
index 00000000000..b96deadeee6
--- /dev/null
+++ b/addons/point_of_sale/tools/posbox/overwrite_after_init/usr/lib/firefox-esr/browser/extensions/{4D498D0A-05AD-4fdb-97B5-8A0AABC1FC5B}/content/rkioskxpinstallConfirm.xul
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/addons/point_of_sale/tools/posbox/overwrite_after_init/usr/lib/firefox-esr/browser/extensions/{4D498D0A-05AD-4fdb-97B5-8A0AABC1FC5B}/install.rdf b/addons/point_of_sale/tools/posbox/overwrite_after_init/usr/lib/firefox-esr/browser/extensions/{4D498D0A-05AD-4fdb-97B5-8A0AABC1FC5B}/install.rdf
new file mode 100644
index 00000000000..5be400e52e8
--- /dev/null
+++ b/addons/point_of_sale/tools/posbox/overwrite_after_init/usr/lib/firefox-esr/browser/extensions/{4D498D0A-05AD-4fdb-97B5-8A0AABC1FC5B}/install.rdf
@@ -0,0 +1,19 @@
+
+
+
+
+
+ {4D498D0A-05AD-4fdb-97B5-8A0AABC1FC5B}
+ R-kiosk
+ 0.9.0.1-signed.1-signed
+ RKiosk (Real Kiosk), fullscreen kiosk mode: all menus, keys etc. disabled
+ Kimmo Heinaaro
+
+
+ {ec8030f7-c20a-464f-9b0e-13a3a9e97384}
+ 2.0
+ 6.*
+
+
+
+
\ No newline at end of file
diff --git a/addons/point_of_sale/tools/posbox/overwrite_after_init/usr/share/firefox-esr/browser/defaults/preferences/all-posbox.js b/addons/point_of_sale/tools/posbox/overwrite_after_init/usr/share/firefox-esr/browser/defaults/preferences/all-posbox.js
new file mode 100644
index 00000000000..8de5d5d60a4
--- /dev/null
+++ b/addons/point_of_sale/tools/posbox/overwrite_after_init/usr/share/firefox-esr/browser/defaults/preferences/all-posbox.js
@@ -0,0 +1,7 @@
+// Preferences to allow unattended install of R-Kiosk extension
+// Needed for Odoo posbox Client display
+pref("app.update.checkInstallTime", false);
+pref("devtools.webide.widget.autoinstall", false);
+pref("xpinstall.customConfirmationUI", false);
+pref("xpinstall.signatures.required", false);
+pref("browser.shell.checkDefaultBrowser",false)
diff --git a/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/init.d/odoo b/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/init.d/odoo
index 30be4bd26d3..4ab50a456a1 100755
--- a/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/init.d/odoo
+++ b/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/init.d/odoo
@@ -26,7 +26,7 @@ test -x $DAEMON || exit 0
set -e
function _start() {
- start-stop-daemon --start --quiet --pidfile $PIDFILE --chuid $USER:$USER --background --make-pidfile --exec $DAEMON -- --config $CONFIG --logfile $LOGFILE --load=web,hw_proxy,hw_posbox_homepage,hw_posbox_upgrade,hw_scale,hw_scanner,hw_escpos,hw_blackbox_be
+ start-stop-daemon --start --quiet --pidfile $PIDFILE --chuid $USER:$USER --background --make-pidfile --exec $DAEMON -- --config $CONFIG --logfile $LOGFILE --load=web,hw_proxy,hw_posbox_homepage,hw_posbox_upgrade,hw_scale,hw_scanner,hw_escpos,hw_blackbox_be,hw_screen
}
function _stop() {
diff --git a/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/init_posbox_image.sh b/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/init_posbox_image.sh
index 5c7895822a3..a9dfda63070 100755
--- a/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/init_posbox_image.sh
+++ b/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/init_posbox_image.sh
@@ -8,17 +8,25 @@ __dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
__file="${__dir}/$(basename "${BASH_SOURCE[0]}")"
__base="$(basename ${__file} .sh)"
+# Since we are emulating, the real /boot is not mounted,
+# leading to mismatch between kernel image and modules.
+mount /dev/sda1 /boot
+
# Recommends: antiword, graphviz, ghostscript, postgresql, python-gevent, poppler-utils
export DEBIAN_FRONTEND=noninteractive
+echo "nameserver 8.8.8.8" >> /etc/resolv.conf
-mount /dev/sda1 /boot
apt-get update
apt-get -y dist-upgrade
+# Do not be too fast to upgrade to more recent firmware and kernel than 4.38
+# Firmware 4.44 seems to prevent the LED mechanism from working
-PKGS_TO_INSTALL="adduser postgresql-client python python-dateutil python-decorator python-docutils python-feedparser python-imaging python-jinja2 python-ldap python-libxslt1 python-lxml python-mako python-mock python-openid python-passlib python-psutil python-psycopg2 python-pybabel python-pychart python-pydot python-pyparsing python-pypdf python-reportlab python-requests python-simplejson python-tz python-unittest2 python-vatnumber python-vobject python-werkzeug python-xlwt python-yaml postgresql python-gevent python-serial python-pip python-dev localepurge vim mc mg screen iw hostapd isc-dhcp-server git rsync console-data"
+PKGS_TO_INSTALL="adduser postgresql-client python python-dateutil python-decorator python-docutils python-feedparser python-imaging python-jinja2 python-ldap python-libxslt1 python-lxml python-mako python-mock python-openid python-passlib python-psutil python-psycopg2 python-pybabel python-pychart python-pydot python-pyparsing python-pypdf python-reportlab python-requests python-simplejson python-tz python-unittest2 python-vatnumber python-vobject python-werkzeug python-xlwt python-yaml postgresql python-gevent python-serial python-pip python-dev localepurge vim mc mg screen iw hostapd isc-dhcp-server git rsync console-data lightdm xserver-xorg-video-fbdev xserver-xorg-input-evdev iceweasel xdotool unclutter x11-utils openbox python-netifaces rpi-update"
-apt-get -y install ${PKGS_TO_INSTALL}
+# KEEP OWN CONFIG FILES DURING PACKAGE CONFIGURATION
+# http://serverfault.com/questions/259226/automatically-keep-current-version-of-config-files-when-apt-get-install
+apt-get -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" --force-yes install ${PKGS_TO_INSTALL}
apt-get clean
localepurge
@@ -32,13 +40,20 @@ pip install pyusb==1.0.0b1
pip install qrcode
pip install evdev
+# --upgrade because websocket_client in wheezy is bad:
+# https://github.com/docker/compose/issues/1288
+pip install --upgrade websocket_client
+
groupadd usbusers
usermod -a -G usbusers pi
usermod -a -G lp pi
+usermod -a -G input lightdm
sudo -u postgres createuser -s pi
mkdir /var/log/odoo
chown pi:pi /var/log/odoo
+chown pi:pi -R /home/pi/odoo/
+chmod 770 -R /home/pi/odoo/
# logrotate is very picky when it comes to file permissions
chown -R root:root /etc/logrotate.d/
@@ -54,6 +69,31 @@ update-rc.d -f isc-dhcp-server remove
systemctl daemon-reload
systemctl enable ramdisks.service
systemctl disable dphys-swapfile.service
+systemctl enable ssh
+
+# USER PI AUTO LOGIN (from nano raspi-config)
+# We take the whole algorithm from raspi-config in order to stay compatible with raspbian infrastructure
+if command -v systemctl > /dev/null && systemctl | grep -q '\-\.mount'; then
+ SYSTEMD=1
+elif [ -f /etc/init.d/cron ] && [ ! -h /etc/init.d/cron ]; then
+ SYSTEMD=0
+else
+ echo "Unrecognised init system"
+ return 1
+fi
+if [ $SYSTEMD -eq 1 ]; then
+ systemctl set-default graphical.target
+ ln -fs /etc/systemd/system/autologin@.service /etc/systemd/system/getty.target.wants/getty@tty1.service
+else
+ update-rc.d lightdm enable 2
+fi
+
+# disable overscan in /boot/config.txt, we can't use
+# overwrite_after_init because it's on a different device
+# (/dev/mmcblk0p1) and we don't mount that afterwards.
+# This option disables any black strips around the screen
+# cf: https://www.raspberrypi.org/documentation/configuration/raspi-config.md
+echo "disable_overscan=1" >> /boot/config.txt
# https://www.raspberrypi.org/forums/viewtopic.php?p=79249
# to not have "setting up console font and keymap" during boot take ages
@@ -68,5 +108,6 @@ create_ramdisk_dir "/var"
create_ramdisk_dir "/etc"
create_ramdisk_dir "/tmp"
mkdir /root_bypass_ramdisks
+umount /dev/sda1
reboot
diff --git a/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/udev/rules.d/99-z-input.rules b/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/udev/rules.d/99-z-input.rules
new file mode 100644
index 00000000000..aab19f2d1c7
--- /dev/null
+++ b/addons/point_of_sale/tools/posbox/overwrite_before_init/etc/udev/rules.d/99-z-input.rules
@@ -0,0 +1,2 @@
+SUBSYSTEM=="input", GROUP="input", MODE="0660"
+KERNEL=="tty[0-9]*", GROUP="tty", MODE="0660"
\ No newline at end of file
diff --git a/addons/point_of_sale/tools/posbox/posbox_create_image.sh b/addons/point_of_sale/tools/posbox/posbox_create_image.sh
index 1e4c9d1277c..a9ec8bc102d 100755
--- a/addons/point_of_sale/tools/posbox/posbox_create_image.sh
+++ b/addons/point_of_sale/tools/posbox/posbox_create_image.sh
@@ -36,20 +36,25 @@ fi
cp -a *raspbian*.img posbox.img
CLONE_DIR="${OVERWRITE_FILES_BEFORE_INIT_DIR}/home/pi/odoo"
+
rm -rf "${CLONE_DIR}"
-mkdir "${CLONE_DIR}"
-git clone -b 8.0 --no-checkout --depth 1 https://github.com/odoo/odoo.git "${CLONE_DIR}"
-cd "${CLONE_DIR}"
-git config core.sparsecheckout true
-echo "addons/web
+
+if [ ! -d $CLONE_DIR ]; then
+ echo "Clone Github repo"
+ mkdir -p "${CLONE_DIR}"
+ git clone -b 8.0 --no-local --no-checkout --depth 1 https://github.com/odoo/odoo.git "${CLONE_DIR}"
+ cd "${CLONE_DIR}"
+ git config core.sparsecheckout true
+ echo "addons/web
addons/web_kanban
addons/hw_*
addons/point_of_sale/tools/posbox/configuration
openerp/
odoo.py" | tee --append .git/info/sparse-checkout > /dev/null
-git read-tree -mu HEAD
-cd "${__dir}"
+ git read-tree -mu HEAD
+fi
+cd "${__dir}"
USR_BIN="${OVERWRITE_FILES_BEFORE_INIT_DIR}/usr/bin/"
mkdir -p "${USR_BIN}"
cd "/tmp"
@@ -60,9 +65,11 @@ cd "${__dir}"
mv /tmp/ngrok "${USR_BIN}"
# zero pad the image to be around 3.5 GiB, by default the image is only ~1.3 GiB
+echo "Enlarging the image..."
dd if=/dev/zero bs=1M count=2048 >> posbox.img
# resize partition table
+echo "Fdisking"
START_OF_ROOT_PARTITION=$(fdisk -l posbox.img | tail -n 1 | awk '{print $2}')
(echo 'p'; # print
echo 'd'; # delete
@@ -75,7 +82,7 @@ START_OF_ROOT_PARTITION=$(fdisk -l posbox.img | tail -n 1 | awk '{print $2}')
echo 'p'; # print
echo 'w') | fdisk posbox.img # write and quit
-LOOP_MAPPER_PATH=$(kpartx -av posbox.img | tail -n 1 | cut -d ' ' -f 3)
+LOOP_MAPPER_PATH=$(kpartx -avs posbox.img | tail -n 1 | cut -d ' ' -f 3)
LOOP_MAPPER_PATH="/dev/mapper/${LOOP_MAPPER_PATH}"
sleep 5
From 5761b9a388edf0cd5d92ab280f4b78c6e84867aa Mon Sep 17 00:00:00 2001
From: Goffin Simon
Date: Tue, 21 Mar 2017 16:22:58 +0100
Subject: [PATCH 4/9] [FIX] http: setup_lang in Safari When not logged in the
webstie on Safari and clicking on "Have a Question? Chat with us", it creates
a mail.channel from get_mail_channel and it also creates a translation. But
with Safari, the accept_languages is set with the value 'fr-fr', and this
value was set in the context as the lang='fr_fr'. So when the translation was
created, a bad insert query was raised in sql because the lang didn't exist
in the res.lang table. When a translation is created, the function
_get_languages checked that the language is in the table. So it was
impossible to use the chatter when the user is not logged.
NB: interseting functions to see:
-setup_lang in odoo/http.py
-_dispatch in addons/website/models/ir_http.py
-get_mail_channel in addons/im_livechat/models/im_livechat_channel.py
opw:716519
---
openerp/http.py | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/openerp/http.py b/openerp/http.py
index 4900e140ce0..c0ca2489afc 100644
--- a/openerp/http.py
+++ b/openerp/http.py
@@ -1357,9 +1357,16 @@ class Root(object):
httprequest.session.db = db_monodb(httprequest)
def setup_lang(self, httprequest):
- if not "lang" in httprequest.session.context:
- lang = httprequest.accept_languages.best or "en_US"
- lang = babel.core.LOCALE_ALIASES.get(lang, lang).replace('-', '_')
+ if "lang" not in httprequest.session.context:
+ alang = httprequest.accept_languages.best or "en-US"
+ try:
+ code, territory, _, _ = babel.core.parse_locale(alang, sep='-')
+ if territory:
+ lang = '%s_%s' % (code, territory)
+ else:
+ lang = babel.code.LOCALE_ALIASES[code]
+ except (ValueError, KeyError):
+ lang = 'en_US'
httprequest.session.context["lang"] = lang
def get_request(self, httprequest):
From 0b529cf3af4e9efc19204478e656aaa2ecaa6547 Mon Sep 17 00:00:00 2001
From: Goffin Simon
Date: Wed, 22 Mar 2017 17:03:23 +0100
Subject: [PATCH 5/9] [FIX] http: setup_lang in Safari
Fine tuning of this commit: 5761b9a388edf0cd5d92ab280f4b78c6e84867aa
opw:716519
---
demo_10.sql | 0
openerp/http.py | 2 +-
2 files changed, 1 insertion(+), 1 deletion(-)
create mode 100644 demo_10.sql
diff --git a/demo_10.sql b/demo_10.sql
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/openerp/http.py b/openerp/http.py
index c0ca2489afc..e3de9c95665 100644
--- a/openerp/http.py
+++ b/openerp/http.py
@@ -1364,7 +1364,7 @@ class Root(object):
if territory:
lang = '%s_%s' % (code, territory)
else:
- lang = babel.code.LOCALE_ALIASES[code]
+ lang = babel.core.LOCALE_ALIASES[code]
except (ValueError, KeyError):
lang = 'en_US'
httprequest.session.context["lang"] = lang
From 874c7aa2dd7708ade32396a4ca4307a6342cfb29 Mon Sep 17 00:00:00 2001
From: Fabien Meghazi
Date: Wed, 21 Dec 2016 11:09:15 +0100
Subject: [PATCH 6/9] [FIX] web/mail: many2many_tags_email widget returns
erratic values (opw: 693067)
The many2many_tags_email's internal value change logic fails to process
all its values. It is not concurrency proof either.
The internal value change logic is now replaced by a mutex and deferreds
are used for the partner's form view popups in order to allow concurrent
events.
---
.../static/src/js/many2many_tags_email.js | 68 ++++++++++---------
1 file changed, 35 insertions(+), 33 deletions(-)
diff --git a/addons/mail/static/src/js/many2many_tags_email.js b/addons/mail/static/src/js/many2many_tags_email.js
index 5d36262f5b9..c3985a3d258 100644
--- a/addons/mail/static/src/js/many2many_tags_email.js
+++ b/addons/mail/static/src/js/many2many_tags_email.js
@@ -9,51 +9,49 @@ var _t = instance.web._t;
instance.web.form.FieldMany2ManyTagsEmail = instance.web.form.FieldMany2ManyTags.extend({
start: function() {
- this.values = [];
- this.values_checking = [];
+ this.mutex = new openerp.Mutex();
+
+ // This widget will indirectly trigger a change:value to it's parent widget
+ // when setting the value of valid partners. For this reason we have to keep an
+ // internal state of the last value in order to compute the effective value changes.
+ this.last_processed_value = [];
this.on("change:value", this, this.on_change_value_check);
- this.trigger("change:value");
-
this._super.apply(this, arguments);
},
on_change_value_check : function () {
- this.values = _.uniq(this.values);
+ var self = this;
+ var values = this.get('value').slice(0); // Clone the array
- // filter for removed values
- var values_removed = _.difference(this.values, this.get('value'));
- if (values_removed.length) {
- this.values = _.difference(this.values, values_removed);
- this.set({'value': this.values});
- return false;
- }
-
- // find not checked values that are not currently on checking
- var not_checked = _.difference(this.get('value'), this.values, this.values_checking);
- if (not_checked.length) {
- // remember values on checking for cheked only one time
- this.values_checking = this.values_checking.concat(not_checked);
- // check values
- this._check_email_popup(not_checked);
+ // We only validate partners emails in case the value is not empty
+ // and is different from the last processed value
+ var effective_change = _.difference(values, self.last_processed_value).length;
+ if (values.length && effective_change) {
+ this.mutex.exec(function() {
+ return self._check_email_popup(values);
+ });
}
},
_check_email_popup: function (ids) {
var self = this;
+ var valid_partners;
+
new instance.web.Model('res.partner').call("search", [[
- ["id", "in", ids],
- ["email", "=", false],
- ["notify_email", "=", 'always'] ]],
+ ["id", "in", ids],
+ ["email", "=", false],
+ ["notify_email", "=", 'always'] ]],
{context: this.build_context()})
.then(function (record_ids) {
- // valid partner
- var valid_partner = _.difference(ids, record_ids);
- self.values = self.values.concat(valid_partner);
- self.values_checking = _.difference(self.values_checking, valid_partner);
+ var popups_deferreds = [];
+ self.valid_partners = _.difference(ids, record_ids);
- // unvalid partner
+ // Propose the user to correct invalid partners
_.each(record_ids, function (id) {
+ var popup_def = $.Deferred();
+ popups_deferreds.push(popup_def);
+
var pop = new instance.web.form.FormOpenPopup(self);
pop.show_element(
'res.partner',
@@ -64,15 +62,19 @@ instance.web.form.FieldMany2ManyTagsEmail = instance.web.form.FieldMany2ManyTags
}
);
pop.on('write_completed', self, function () {
- this.values.push(id);
- this.values_checking = _.without(this.values_checking, id);
- this.set({'value': this.values});
+ self.valid_partners.push(id);
});
pop.on('closed', self, function () {
- this.values_checking = _.without(this.values_checking, id);
- this.set({'value': this.values});
+ popup_def.resolve();
});
});
+ return $.when.apply($, popups_deferreds).then(function() {
+ // All popups have been processed for the given ids
+ // It is now time to set the final value with valid partners ids.
+ var filtered_value = _.uniq(self.valid_partners);
+ self.last_processed_value = filtered_value;
+ self.set({'value': filtered_value});
+ });
});
},
});
From b2d66d0c1468b9d321d9ffa5782e370e92525a43 Mon Sep 17 00:00:00 2001
From: qsm-odoo
Date: Wed, 15 Mar 2017 15:19:46 +0100
Subject: [PATCH 7/9] [FIX] web: properly combine domains with "OR" in
CompoundDomain
The CompoundDomain class allows to regroup several domains with an
implicit "AND"; these domains can be either a string, an array or
another CompoundDomain. A CompoundDomain can then be converted to
an array thanks to pyeval.js.
For example, if a CompoundDomain is initialized with `[A, B]` and
`[C]`, the array conversion gave `[A, B, C]` (which is expected).
However, a hackish method was used with CompoundDomain. If one of the
domain of a CompoundDomain is equal to `["|"]` (an array with the
OR operator in it), the two next subdomains were supposed to be joined
by a OR operator. Indeed, for the case of a CompoundDomain initialized
with `["|"]`, `[A]` and `[B]`, the array conversion gave `["|", A, B]`
(which is expected). However, if initialized with `["|"]`, `[A, B]` and
`[C]`, the array conversion gave `["|", A, B, C]` which is very wrong
as what was expected is `["|", "&", A, B, C]`. The problem is that the
given `[A, B]` contains implicit "&" operators.
This commit fixes the problem by normalizing only if the CompoundDomain
starts with a ["|"] or ["!"] array which is the standard odoo case.
This allows to limit breaking custom code (e.g we want a simple "AND"
of `[A]` and `[B]` to stay `[A, B]`, not become `["&", A, B]`).
The commit also modifies a test so that it checks that the problem is
properly solved.
---
addons/web/static/src/js/pyeval.js | 47 +++++++++++++++++++++++++-----
addons/web/static/test/evals.js | 5 ++--
2 files changed, 42 insertions(+), 10 deletions(-)
diff --git a/addons/web/static/src/js/pyeval.js b/addons/web/static/src/js/pyeval.js
index c132b8365fb..a651c7d8bfa 100644
--- a/addons/web/static/src/js/pyeval.js
+++ b/addons/web/static/src/js/pyeval.js
@@ -852,29 +852,60 @@
var eval_domains = function (domains, evaluation_context) {
evaluation_context = _.extend(instance.web.pyeval.context(), evaluation_context || {});
var result_domain = [];
+ // Normalize only if the first domain is the array ["|"] or ["!"]
+ var need_normalization = (
+ domains.length > 0
+ && domains[0].length === 1
+ && (domains[0][0] === "|" || domains[0][0] === "!")
+ );
_(domains).each(function (domain) {
if (_.isString(domain)) {
// wrap raw strings in domain
domain = { __ref: 'domain', __debug: domain };
}
+ var domain_array_to_combine;
switch(domain.__ref) {
case 'domain':
evaluation_context.context = evaluation_context;
- result_domain.push.apply(
- result_domain, py.eval(domain.__debug, wrap_context(evaluation_context)));
+ domain_array_to_combine = py.eval(domain.__debug, wrap_context(evaluation_context));
break;
case 'compound_domain':
var eval_context = eval_contexts([domain.__eval_context]);
- result_domain.push.apply(
- result_domain, eval_domains(
- domain.__domains, _.extend(
- {}, evaluation_context, eval_context)));
+ domain_array_to_combine = eval_domains(
+ domain.__domains,
+ _.extend({}, evaluation_context, eval_context)
+ );
break;
default:
- result_domain.push.apply(result_domain, domain);
+ domain_array_to_combine = domain;
}
+ if (need_normalization) {
+ domain_array_to_combine = get_normalized_domain(domain_array_to_combine);
+ }
+ result_domain.push.apply(result_domain, domain_array_to_combine);
});
return result_domain;
+
+ /**
+ * Returns a normalized copy of the given domain array. Normalization is
+ * is making the implicit "&" at the start of the domain explicit, e.g.
+ * [A, B, C] would become ["&", "&", A, B, C].
+ *
+ * @param {Array} domain_array
+ * @returns {Array} normalized copy of the given array
+ */
+ function get_normalized_domain(domain_array) {
+ var expected = 1; // Holds the number of expected domain expressions
+ _.each(domain_array, function (item) {
+ if (item === "&" || item === "|") {
+ expected++;
+ } else if (item !== "!") {
+ expected--;
+ }
+ });
+ var new_explicit_ands = _.times(-expected, _.constant("&"));
+ return new_explicit_ands.concat(domain_array);
+ }
};
var eval_groupbys = function (contexts, evaluation_context) {
evaluation_context = _.extend(instance.web.pyeval.context(), evaluation_context || {});
@@ -995,7 +1026,7 @@
instance.web._t("Local evaluation failure\n%s\n\n%s"),
e.message, JSON.stringify(source))
}
- }};
+ }};
}
d.resolve(result);
}, 0); });
diff --git a/addons/web/static/test/evals.js b/addons/web/static/test/evals.js
index ff97086b019..8dd9a62497e 100644
--- a/addons/web/static/test/evals.js
+++ b/addons/web/static/test/evals.js
@@ -500,7 +500,7 @@ openerp.testing.section('eval.edc.nonliterals', {
test('domain with time', {asserts: 1}, function (instance) {
return instance.edc([
[['type', '=', 'contract']],
- { "__domains": [["|"], [["state", "in", ["open", "draft"]]], [["state", "=", "pending"]]],
+ { "__domains": [["|"], [["state", "in", ["open", "draft"]]], [["type", "=", "contract"], ["state", "=", "pending"]]],
"__eval_context": null,
"__ref": "compound_domain"
},
@@ -513,7 +513,8 @@ openerp.testing.section('eval.edc.nonliterals', {
deepEqual(result.domain, [
["type", "=", "contract"],
"|", ["state", "in", ["open", "draft"]],
- ["state", "=", "pending"],
+ "&", ["type", "=", "contract"],
+ ["state", "=", "pending"],
"|",
"&", ["date", "!=", false],
["date", "<=", today],
From 5847de4aaf94eb5d69a4cf9aba9ceb74a7b9ab43 Mon Sep 17 00:00:00 2001
From: Christophe Simonis
Date: Thu, 23 Mar 2017 16:43:37 +0100
Subject: [PATCH 8/9] [FIX] web_calendar: remove attendee icons from event
title
---
addons/web_calendar/static/src/js/web_calendar.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/addons/web_calendar/static/src/js/web_calendar.js b/addons/web_calendar/static/src/js/web_calendar.js
index d90987413f2..55b22403bca 100644
--- a/addons/web_calendar/static/src/js/web_calendar.js
+++ b/addons/web_calendar/static/src/js/web_calendar.js
@@ -276,7 +276,7 @@ openerp.web_calendar = function(instance) {
self.proxy('update_record')(event._id, data);
},
eventRender: function (event, element, view) {
- element.find('.fc-event-title').html(event.title);
+ element.find('.fc-event-title').html(event.title + event.attendee_avatars);
},
eventAfterRender: function (event, element, view) {
if ((view.name !== 'month') && (((event.end-event.start)/60000)<=30)) {
@@ -567,7 +567,6 @@ openerp.web_calendar = function(instance) {
if (attendee_other.length>2) {
the_title_avatar += '+';
}
- the_title = the_title_avatar + the_title;
}
}
@@ -578,6 +577,7 @@ openerp.web_calendar = function(instance) {
'start': date_start.toString('yyyy-MM-dd HH:mm:ss'),
'end': date_stop.toString('yyyy-MM-dd HH:mm:ss'),
'title': the_title,
+ 'attendee_avatars': the_title_avatar,
'allDay': (this.fields[this.date_start].type == 'date' || (this.all_day && evt[this.all_day]) || false),
'id': evt.id,
'attendees':attendees
From 08213e831ee96fe722c0db97b0174b3aad71b528 Mon Sep 17 00:00:00 2001
From: Christophe Simonis
Date: Fri, 24 Mar 2017 17:03:11 +0100
Subject: [PATCH 9/9] [REM] useless empty sql file wrongly commited by
0b529cf3af4e9efc19204478e656aaa2ecaa6547
---
demo_10.sql | 0
1 file changed, 0 insertions(+), 0 deletions(-)
delete mode 100644 demo_10.sql
diff --git a/demo_10.sql b/demo_10.sql
deleted file mode 100644
index e69de29bb2d..00000000000