[REF] web: stop duplicating web

Avoid duplicating web addon in enterprise by extracting a common basis.
Enterprise features stay in enterprise, but use that common basis.

Mainly:
 - JS refactoring and linting
 - Conversion of .sass into .less split into multiple files
 - Templates cleaning and DOM simplification
 - Re-generation of web.pot, and update of .po files
This commit is contained in:
Aaron Bohy
2016-04-21 15:21:39 +02:00
committed by Géry Debongnie
parent e1bfd3fbda
commit 977db823c7
162 changed files with 53672 additions and 40386 deletions
-3
View File
@@ -1,3 +0,0 @@
{
"directory": "static/lib/"
}
-20
View File
@@ -9,30 +9,10 @@ module.exports = function(grunt) {
laxbreak: true, //unsafe line breaks
},
},
sass: {
dev: {
options: {
style: "expanded",
},
files: {
"static/src/css/base.css": "static/src/css/base.sass",
}
}
},
watch: {
sass: {
files: ["static/src/css/base.sass"],
tasks: ['sass']
},
}
});
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-sass');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.registerTask('gen', ["sass"]);
grunt.registerTask('watcher', ["gen", "watch"]);
grunt.registerTask('test', []);
grunt.registerTask('default', ['jshint']);
+2 -2
View File
@@ -14,8 +14,8 @@ This module provides the core of the Odoo Web Client.
'data': [
'views/webclient_templates.xml',
],
'qweb' : [
'qweb': [
"static/src/xml/*.xml",
],
'bootstrap': True, # load translations for login screen
'bootstrap': True, # load translations for login screen
}
+8 -8
View File
@@ -427,13 +427,6 @@ def binary_content(xmlid=None, model='ir.attachment', id=None, field='datas', un
xmlid=xmlid, model=model, id=id, field=field, unique=unique, filename=filename, filename_field=filename_field,
download=download, mimetype=mimetype, default_mimetype=default_mimetype, env=env)
def db_info():
version_info = openerp.service.common.exp_version()
return {
'server_version': version_info.get('server_version'),
'server_version_info': version_info.get('server_version_info'),
}
#----------------------------------------------------------
# OpenERP Web web Controllers
#----------------------------------------------------------
@@ -454,7 +447,14 @@ class Home(http.Controller):
request.uid = request.session.uid
menu_data = request.registry['ir.ui.menu'].load_menus(request.cr, request.uid, request.debug, context=request.context)
return request.render('web.webclient_bootstrap', qcontext={'menu_data': menu_data, 'db_info': json.dumps(db_info())})
version_info = openerp.service.common.exp_version()
db_info = {
'server_version': version_info.get('server_version'),
'server_version_info': version_info.get('server_version_info'),
}
return request.render('web.webclient_bootstrap', qcontext={'menu_data': menu_data, 'db_info': json.dumps(db_info)})
@http.route('/web/dbredirect', type='http', auth="none")
def web_db_redirect(self, redirect='/', **kw):
+2 -2
View File
@@ -46,7 +46,7 @@ various situations:
Action button (``button`` tag in the view) inside the cell
``.oe_readonly``
``.o_readonly``
Readonly field cell
@@ -66,7 +66,7 @@ Editable list view
The editable list view module adds a few supplementary style hook
classes, for edition situations:
``.oe_list_editable``
``.o_list_editable``
Added to the ``.oe_list`` when the list is editable (however that
was done). The class may be removed on-the-fly if the list becomes
+712 -538
View File
File diff suppressed because it is too large Load Diff
+750 -570
View File
File diff suppressed because it is too large Load Diff
+721 -547
View File
File diff suppressed because it is too large Load Diff
+720 -546
View File
File diff suppressed because it is too large Load Diff
+747 -569
View File
File diff suppressed because it is too large Load Diff
+712 -538
View File
File diff suppressed because it is too large Load Diff
+748 -570
View File
File diff suppressed because it is too large Load Diff
+745 -567
View File
File diff suppressed because it is too large Load Diff
+746 -568
View File
File diff suppressed because it is too large Load Diff
+750 -572
View File
File diff suppressed because it is too large Load Diff
+732 -554
View File
File diff suppressed because it is too large Load Diff
+746 -568
View File
File diff suppressed because it is too large Load Diff
+737 -559
View File
File diff suppressed because it is too large Load Diff
+749 -571
View File
File diff suppressed because it is too large Load Diff
+710 -536
View File
File diff suppressed because it is too large Load Diff
+741 -559
View File
File diff suppressed because it is too large Load Diff
+720 -546
View File
File diff suppressed because it is too large Load Diff
+740 -559
View File
File diff suppressed because it is too large Load Diff
+735 -557
View File
File diff suppressed because it is too large Load Diff
+734 -556
View File
File diff suppressed because it is too large Load Diff
+709 -535
View File
File diff suppressed because it is too large Load Diff
+719 -545
View File
File diff suppressed because it is too large Load Diff
+710 -536
View File
File diff suppressed because it is too large Load Diff
+728 -551
View File
File diff suppressed because it is too large Load Diff
+749 -571
View File
File diff suppressed because it is too large Load Diff
+743 -561
View File
File diff suppressed because it is too large Load Diff
+720 -546
View File
File diff suppressed because it is too large Load Diff
+711 -537
View File
File diff suppressed because it is too large Load Diff
+727 -553
View File
File diff suppressed because it is too large Load Diff
+709 -535
View File
File diff suppressed because it is too large Load Diff
+750 -572
View File
File diff suppressed because it is too large Load Diff
+742 -558
View File
File diff suppressed because it is too large Load Diff
+750 -576
View File
File diff suppressed because it is too large Load Diff
+740 -560
View File
File diff suppressed because it is too large Load Diff
+736 -558
View File
File diff suppressed because it is too large Load Diff
+726 -552
View File
File diff suppressed because it is too large Load Diff
+723 -549
View File
File diff suppressed because it is too large Load Diff
+749 -571
View File
File diff suppressed because it is too large Load Diff
+872 -504
View File
File diff suppressed because it is too large Load Diff
+735 -561
View File
File diff suppressed because it is too large Load Diff
+710 -536
View File
File diff suppressed because it is too large Load Diff
+749 -571
View File
File diff suppressed because it is too large Load Diff
+749 -571
View File
File diff suppressed because it is too large Load Diff
+747 -569
View File
File diff suppressed because it is too large Load Diff
+739 -557
View File
File diff suppressed because it is too large Load Diff
+725 -551
View File
File diff suppressed because it is too large Load Diff
+751 -573
View File
File diff suppressed because it is too large Load Diff
+744 -566
View File
File diff suppressed because it is too large Load Diff
+747 -565
View File
File diff suppressed because it is too large Load Diff
+749 -571
View File
File diff suppressed because it is too large Load Diff
+737 -559
View File
File diff suppressed because it is too large Load Diff
+740 -558
View File
File diff suppressed because it is too large Load Diff
+749 -571
View File
File diff suppressed because it is too large Load Diff
+709 -535
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+735 -561
View File
File diff suppressed because it is too large Load Diff
+747 -567
View File
File diff suppressed because it is too large Load Diff
+737 -559
View File
File diff suppressed because it is too large Load Diff
+739 -557
View File
File diff suppressed because it is too large Load Diff
+724 -550
View File
File diff suppressed because it is too large Load Diff
+700 -510
View File
File diff suppressed because it is too large Load Diff
+751 -567
View File
File diff suppressed because it is too large Load Diff
+728 -550
View File
File diff suppressed because it is too large Load Diff
-587
View File
@@ -1,587 +0,0 @@
/*!
* Bootstrap v3.3.5 (http://getbootstrap.com)
* Copyright 2011-2015 Twitter, Inc.
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
*/
.btn-default,
.btn-primary,
.btn-success,
.btn-info,
.btn-warning,
.btn-danger {
text-shadow: 0 -1px 0 rgba(0, 0, 0, .2);
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
}
.btn-default:active,
.btn-primary:active,
.btn-success:active,
.btn-info:active,
.btn-warning:active,
.btn-danger:active,
.btn-default.active,
.btn-primary.active,
.btn-success.active,
.btn-info.active,
.btn-warning.active,
.btn-danger.active {
-webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
}
.btn-default.disabled,
.btn-primary.disabled,
.btn-success.disabled,
.btn-info.disabled,
.btn-warning.disabled,
.btn-danger.disabled,
.btn-default[disabled],
.btn-primary[disabled],
.btn-success[disabled],
.btn-info[disabled],
.btn-warning[disabled],
.btn-danger[disabled],
fieldset[disabled] .btn-default,
fieldset[disabled] .btn-primary,
fieldset[disabled] .btn-success,
fieldset[disabled] .btn-info,
fieldset[disabled] .btn-warning,
fieldset[disabled] .btn-danger {
-webkit-box-shadow: none;
box-shadow: none;
}
.btn-default .badge,
.btn-primary .badge,
.btn-success .badge,
.btn-info .badge,
.btn-warning .badge,
.btn-danger .badge {
text-shadow: none;
}
.btn:active,
.btn.active {
background-image: none;
}
.btn-default {
text-shadow: 0 1px 0 #fff;
background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%);
background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0));
background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #dbdbdb;
border-color: #ccc;
}
.btn-default:hover,
.btn-default:focus {
background-color: #e0e0e0;
background-position: 0 -15px;
}
.btn-default:active,
.btn-default.active {
background-color: #e0e0e0;
border-color: #dbdbdb;
}
.btn-default.disabled,
.btn-default[disabled],
fieldset[disabled] .btn-default,
.btn-default.disabled:hover,
.btn-default[disabled]:hover,
fieldset[disabled] .btn-default:hover,
.btn-default.disabled:focus,
.btn-default[disabled]:focus,
fieldset[disabled] .btn-default:focus,
.btn-default.disabled.focus,
.btn-default[disabled].focus,
fieldset[disabled] .btn-default.focus,
.btn-default.disabled:active,
.btn-default[disabled]:active,
fieldset[disabled] .btn-default:active,
.btn-default.disabled.active,
.btn-default[disabled].active,
fieldset[disabled] .btn-default.active {
background-color: #e0e0e0;
background-image: none;
}
.btn-primary {
background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #265a88 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88));
background-image: linear-gradient(to bottom, #337ab7 0%, #265a88 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #245580;
}
.btn-primary:hover,
.btn-primary:focus {
background-color: #265a88;
background-position: 0 -15px;
}
.btn-primary:active,
.btn-primary.active {
background-color: #265a88;
border-color: #245580;
}
.btn-primary.disabled,
.btn-primary[disabled],
fieldset[disabled] .btn-primary,
.btn-primary.disabled:hover,
.btn-primary[disabled]:hover,
fieldset[disabled] .btn-primary:hover,
.btn-primary.disabled:focus,
.btn-primary[disabled]:focus,
fieldset[disabled] .btn-primary:focus,
.btn-primary.disabled.focus,
.btn-primary[disabled].focus,
fieldset[disabled] .btn-primary.focus,
.btn-primary.disabled:active,
.btn-primary[disabled]:active,
fieldset[disabled] .btn-primary:active,
.btn-primary.disabled.active,
.btn-primary[disabled].active,
fieldset[disabled] .btn-primary.active {
background-color: #265a88;
background-image: none;
}
.btn-success {
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);
background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641));
background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #3e8f3e;
}
.btn-success:hover,
.btn-success:focus {
background-color: #419641;
background-position: 0 -15px;
}
.btn-success:active,
.btn-success.active {
background-color: #419641;
border-color: #3e8f3e;
}
.btn-success.disabled,
.btn-success[disabled],
fieldset[disabled] .btn-success,
.btn-success.disabled:hover,
.btn-success[disabled]:hover,
fieldset[disabled] .btn-success:hover,
.btn-success.disabled:focus,
.btn-success[disabled]:focus,
fieldset[disabled] .btn-success:focus,
.btn-success.disabled.focus,
.btn-success[disabled].focus,
fieldset[disabled] .btn-success.focus,
.btn-success.disabled:active,
.btn-success[disabled]:active,
fieldset[disabled] .btn-success:active,
.btn-success.disabled.active,
.btn-success[disabled].active,
fieldset[disabled] .btn-success.active {
background-color: #419641;
background-image: none;
}
.btn-info {
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2));
background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #28a4c9;
}
.btn-info:hover,
.btn-info:focus {
background-color: #2aabd2;
background-position: 0 -15px;
}
.btn-info:active,
.btn-info.active {
background-color: #2aabd2;
border-color: #28a4c9;
}
.btn-info.disabled,
.btn-info[disabled],
fieldset[disabled] .btn-info,
.btn-info.disabled:hover,
.btn-info[disabled]:hover,
fieldset[disabled] .btn-info:hover,
.btn-info.disabled:focus,
.btn-info[disabled]:focus,
fieldset[disabled] .btn-info:focus,
.btn-info.disabled.focus,
.btn-info[disabled].focus,
fieldset[disabled] .btn-info.focus,
.btn-info.disabled:active,
.btn-info[disabled]:active,
fieldset[disabled] .btn-info:active,
.btn-info.disabled.active,
.btn-info[disabled].active,
fieldset[disabled] .btn-info.active {
background-color: #2aabd2;
background-image: none;
}
.btn-warning {
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316));
background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #e38d13;
}
.btn-warning:hover,
.btn-warning:focus {
background-color: #eb9316;
background-position: 0 -15px;
}
.btn-warning:active,
.btn-warning.active {
background-color: #eb9316;
border-color: #e38d13;
}
.btn-warning.disabled,
.btn-warning[disabled],
fieldset[disabled] .btn-warning,
.btn-warning.disabled:hover,
.btn-warning[disabled]:hover,
fieldset[disabled] .btn-warning:hover,
.btn-warning.disabled:focus,
.btn-warning[disabled]:focus,
fieldset[disabled] .btn-warning:focus,
.btn-warning.disabled.focus,
.btn-warning[disabled].focus,
fieldset[disabled] .btn-warning.focus,
.btn-warning.disabled:active,
.btn-warning[disabled]:active,
fieldset[disabled] .btn-warning:active,
.btn-warning.disabled.active,
.btn-warning[disabled].active,
fieldset[disabled] .btn-warning.active {
background-color: #eb9316;
background-image: none;
}
.btn-danger {
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a));
background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-color: #b92c28;
}
.btn-danger:hover,
.btn-danger:focus {
background-color: #c12e2a;
background-position: 0 -15px;
}
.btn-danger:active,
.btn-danger.active {
background-color: #c12e2a;
border-color: #b92c28;
}
.btn-danger.disabled,
.btn-danger[disabled],
fieldset[disabled] .btn-danger,
.btn-danger.disabled:hover,
.btn-danger[disabled]:hover,
fieldset[disabled] .btn-danger:hover,
.btn-danger.disabled:focus,
.btn-danger[disabled]:focus,
fieldset[disabled] .btn-danger:focus,
.btn-danger.disabled.focus,
.btn-danger[disabled].focus,
fieldset[disabled] .btn-danger.focus,
.btn-danger.disabled:active,
.btn-danger[disabled]:active,
fieldset[disabled] .btn-danger:active,
.btn-danger.disabled.active,
.btn-danger[disabled].active,
fieldset[disabled] .btn-danger.active {
background-color: #c12e2a;
background-image: none;
}
.thumbnail,
.img-thumbnail {
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
}
.dropdown-menu > li > a:hover,
.dropdown-menu > li > a:focus {
background-color: #e8e8e8;
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
background-repeat: repeat-x;
}
.dropdown-menu > .active > a,
.dropdown-menu > .active > a:hover,
.dropdown-menu > .active > a:focus {
background-color: #2e6da4;
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
background-repeat: repeat-x;
}
.navbar-default {
background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%);
background-image: -o-linear-gradient(top, #fff 0%, #f8f8f8 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8));
background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-radius: 4px;
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
}
.navbar-default .navbar-nav > .open > a,
.navbar-default .navbar-nav > .active > a {
background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
background-image: -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2));
background-image: linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);
background-repeat: repeat-x;
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
}
.navbar-brand,
.navbar-nav > li > a {
text-shadow: 0 1px 0 rgba(255, 255, 255, .25);
}
.navbar-inverse {
background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%);
background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222));
background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
background-repeat: repeat-x;
border-radius: 4px;
}
.navbar-inverse .navbar-nav > .open > a,
.navbar-inverse .navbar-nav > .active > a {
background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);
background-image: -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f));
background-image: linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);
background-repeat: repeat-x;
-webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
}
.navbar-inverse .navbar-brand,
.navbar-inverse .navbar-nav > li > a {
text-shadow: 0 -1px 0 rgba(0, 0, 0, .25);
}
.navbar-static-top,
.navbar-fixed-top,
.navbar-fixed-bottom {
border-radius: 0;
}
@media (max-width: 767px) {
.navbar .navbar-nav .open .dropdown-menu > .active > a,
.navbar .navbar-nav .open .dropdown-menu > .active > a:hover,
.navbar .navbar-nav .open .dropdown-menu > .active > a:focus {
color: #fff;
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
background-repeat: repeat-x;
}
}
.alert {
text-shadow: 0 1px 0 rgba(255, 255, 255, .2);
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
}
.alert-success {
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc));
background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);
background-repeat: repeat-x;
border-color: #b2dba1;
}
.alert-info {
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0));
background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);
background-repeat: repeat-x;
border-color: #9acfea;
}
.alert-warning {
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0));
background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);
background-repeat: repeat-x;
border-color: #f5e79e;
}
.alert-danger {
background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3));
background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);
background-repeat: repeat-x;
border-color: #dca7a7;
}
.progress {
background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5));
background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);
background-repeat: repeat-x;
}
.progress-bar {
background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #286090 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090));
background-image: linear-gradient(to bottom, #337ab7 0%, #286090 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);
background-repeat: repeat-x;
}
.progress-bar-success {
background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);
background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44));
background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);
background-repeat: repeat-x;
}
.progress-bar-info {
background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5));
background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);
background-repeat: repeat-x;
}
.progress-bar-warning {
background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f));
background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);
background-repeat: repeat-x;
}
.progress-bar-danger {
background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);
background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c));
background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);
background-repeat: repeat-x;
}
.progress-bar-striped {
background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
}
.list-group {
border-radius: 4px;
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
}
.list-group-item.active,
.list-group-item.active:hover,
.list-group-item.active:focus {
text-shadow: 0 -1px 0 #286090;
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a));
background-image: linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);
background-repeat: repeat-x;
border-color: #2b669a;
}
.list-group-item.active .badge,
.list-group-item.active:hover .badge,
.list-group-item.active:focus .badge {
text-shadow: none;
}
.panel {
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
}
.panel-default > .panel-heading {
background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
background-repeat: repeat-x;
}
.panel-primary > .panel-heading {
background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
background-image: linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
background-repeat: repeat-x;
}
.panel-success > .panel-heading {
background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6));
background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);
background-repeat: repeat-x;
}
.panel-info > .panel-heading {
background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3));
background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);
background-repeat: repeat-x;
}
.panel-warning > .panel-heading {
background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc));
background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);
background-repeat: repeat-x;
}
.panel-danger > .panel-heading {
background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc));
background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);
background-repeat: repeat-x;
}
.well {
background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5));
background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);
background-repeat: repeat-x;
border-color: #dcdcdc;
-webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
}
/*# sourceMappingURL=bootstrap-theme.css.map */
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Vendored Executable → Regular
View File
@@ -1,5 +0,0 @@
repo: 076b192d0d8ab2b92d1dbcfa3da055382f30eaea
node: 7ee0bc7b4e9138f485cdc9ec791961d8ef452f17
branch: default
latesttag: 0.7
latesttagdistance: 11
-89
View File
@@ -1,89 +0,0 @@
.openerp .oe_export {
width: 100%;
}
/* Customize according bootstrap3 */
.openerp .oe_export label {
font-weight: normal;
}
/* End of customize */
.openerp .oe_export_row tr{
background-color: #FFFFFF;
font-size: 0.8em;
height: 22px;
}
.openerp tr.ui-selected td {
background-color: #CCCCCC;
}
.openerp .oe_export_requiredfield {
background-color: #D2D2FF;
}
.openerp .oe_export_readonlyfield{
background-color: #999999;
}
.openerp .oe_export_row:hover{
background-color: #F3F3F3;
}
.openerp .oe_export_fields_selector_left {
width: 50%;
}
.openerp div#left_field_panel {
overflow: scroll;
width: 100%;
height: 400px;
border: solid #999999 1px;
}
.openerp div#left_field_panel table {
width: 100%;
}
.openerp .oe_export_fields_selector_center {
text-align: center;
}
.openerp .oe_export_fields_selector_center button {
white-space: nowrap;
}
.openerp .oe_export_fields_selector_right {
width: 45%;
height: 400px;
}
.openerp .oe_export_fields_selector_right select{
width: 100%;
height: 100%;
}
.openerp .oe_export_tree_header{
border: 0.5px solid #E3E3E3;
text-align: left;
white-space: nowrap;
padding: 4px 5px;
background: url(/web/static/src/img/header.gif);
}
.openerp table.tree_grid{
border: 1px solid #E3E3E3;
text-align: left;
white-space: nowrap;
background-color:#E3E3E3;
border-collapse: collapse;
width: 100%;
}
.openerp table.tree_grid a:hover {
color: blue;
border: none;
}
.openerp table.tree_grid a {
color: #5F5C5C;
border: none;
display: block;
}
-12
View File
@@ -1,12 +0,0 @@
/* This css contains the styling specific to the 'normal mode'. (as opposite to the embedded mode) */
/* In embedded mode [1], this stylesheet won't be loaded. */
/* [1] The web client features an embedded mode in which the webclient */
/* or a part of it can be started in any element of any webpage host. */
body {
padding: 0;
margin: 0;
overflow-y: scroll;
height: 100%;
}
-29
View File
@@ -1,29 +0,0 @@
/** base */
.openerp .oe_form label + .oe_form_field.oe_form_field_one2many {
display: block;
}
.openerp .oe_leftbar > div > div > div > div.oe_secondary_menus_container {
position: static;
}
.openerp .oe_application > div > .oe_view_manager > .oe_view_manager_wrapper > div > .oe_view_manager_body {
position: static;
}
/** web_calendar */
.fc .ui-draggable-dragging .fc-event-bg, /* TODO: something nicer like .fc-opacity */
.fc-select-helper .fc-event-bg {
display: none; /* for IE6/7/8. nested opacity filters while dragging don't work */
}
/** web_graph */
.openerp svg:not(:root) { /* width and height are not take in consideration */
overflow: visible;
}
.graph_main_content.graph_chart_mode > div > svg {
min-height: 521px;
}
/** web_kanban */
.openerp .oe_kanban_column_cards {
min-height: 80px;
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 125 B

@@ -0,0 +1,245 @@
odoo.define('web.AbstractWebClient', function (require) {
"use strict";
var ActionManager = require('web.ActionManager');
var core = require('web.core');
var crash_manager = require('web.crash_manager');
var Loading = require('web.Loading');
var NotificationManager = require('web.notification').NotificationManager;
var session = require('web.session');
var utils = require('web.utils');
var Widget = require('web.Widget');
var _t = core._t;
var WebClient = Widget.extend({
custom_events: {
notification: function (e) {
if(this.notification_manager) {
this.notification_manager.notify(e.data.title, e.data.message, e.data.sticky);
}
},
warning: function (e) {
if(this.notification_manager) {
this.notification_manager.warn(e.data.title, e.data.message, e.data.sticky);
}
},
clear_uncommitted_changes: function (e) {
this.clear_uncommitted_changes().then(e.data.callback);
},
},
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();
return session.session_bind(this.origin)
.then(function () {
self.bind_events();
return $.when(
self.set_action_manager(),
self.set_notification_manager(),
self.set_loading()
);
}).then(function () {
if (session.session_is_valid()) {
return self.show_application();
} else {
// database manager needs the webclient to keep going even
// though it has no valid session
return $.when();
}
}).then(function () {
if (self.client_options.action) {
self.do_action(self.client_options.action);
delete(self.client_options.action);
}
core.bus.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.$('.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);
});
core.bus.on('connection_lost', this, this.on_connection_lost);
core.bus.on('connection_restored', this, this.on_connection_restored);
// crash manager integration
session.on('error', crash_manager, crash_manager.rpc_error);
window.onerror = function (message, file, line, col, error) {
var traceback = error ? error.stack : '';
crash_manager.show_error({
type: _t("Client Error"),
message: message,
data: {debug: file + ':' + line + "\n" + _t('Traceback:') + "\n" + traceback}
});
};
},
set_action_manager: function() {
this.action_manager = new ActionManager(this, {webclient: this});
return this.action_manager.appendTo(this.$('.o_main_content'));
},
set_notification_manager: function() {
this.notification_manager = new NotificationManager(this);
return this.notification_manager.appendTo(this.$el);
},
set_loading: function() {
this.loading = new Loading(this);
return this.loading.appendTo(this.$el);
},
show_application: function() {
},
clear_uncommitted_changes: function() {
var def = $.Deferred().resolve();
core.bus.trigger('clear_uncommitted_changes', function chain_callbacks(callback) {
def = def.then(callback);
});
return def;
},
destroy_content: function() {
_.each(_.clone(this.getChildren()), function(el) {
el.destroy();
});
this.$el.children().remove();
},
// --------------------------------------------------------------
// Window title handling
// --------------------------------------------------------------
/**
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;
},
// --------------------------------------------------------------
// do_*
// --------------------------------------------------------------
/**
* When do_action is performed on the WebClient, forward it to the main ActionManager
* This allows to widgets that are not inside the ActionManager to perform do_action
*/
do_action: function() {
return this.action_manager.do_action.apply(this, arguments);
},
do_reload: function() {
var self = this;
return this.session.session_reload().then(function () {
session.load_modules(true).then(
self.menu.proxy('do_reload'));
});
},
do_push_state: function(state) {
if ('title' in 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);
},
// --------------------------------------------------------------
// Connection notifications
// --------------------------------------------------------------
on_connection_lost: function () {
this.connection_notification = this.notification_manager.notify(
_t('Connection lost'),
_t('Trying to reconnect...'),
true
);
},
on_connection_restored: function () {
if (this.connection_notification) {
this.connection_notification.destroy();
this.notification_manager.notify(
_t('Connection restored'),
_t('You are back online'),
false
);
this.connection_notification = false;
}
},
// --------------------------------------------------------------
// Scrolltop handling
// --------------------------------------------------------------
get_scrollTop: function () {
},
//--------------------------------------------------------------
// Misc.
//--------------------------------------------------------------
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;
});
+112 -100
View File
@@ -22,25 +22,18 @@ var Action = core.Class.extend({
this.action_descr = action;
this.title = action.display_name || action.name;
},
/**
* Not implemented for client actions
* @return {Deferred} a rejected Deferred
*/
appendTo: function() {
return $.Deferred().reject();
},
/**
* This method should restore this previously loaded action
* Calls on_reverse_breadcrumb callback if defined
* Calls on_reverse_breadcrumb_callback if defined
* @return {Deferred} resolved when widget is enabled
*/
restore: function() {
if (this.on_reverse_breadcrumb) {
return this.on_reverse_breadcrumb();
if (this.on_reverse_breadcrumb_callback) {
return this.on_reverse_breadcrumb_callback();
}
},
/**
* Not implemented for functions
* There is nothing to detach in the case of a client function
*/
detach: function() {
},
@@ -50,11 +43,16 @@ var Action = core.Class.extend({
destroy: function() {
},
/**
* Sets the on_reverse_breadcrumb callback to be called when coming back to that action
* @param {Function} [on_reverse_breadcrumb] the callback
* Sets the on_reverse_breadcrumb_callback to be called when coming back to that action
* @param {Function} [callback] the callback
*/
set_on_reverse_breadcrumb: function(on_reverse_breadcrumb) {
this.on_reverse_breadcrumb = on_reverse_breadcrumb;
set_on_reverse_breadcrumb: function(callback) {
this.on_reverse_breadcrumb_callback = callback;
},
/**
* Not implemented for client actions
*/
set_scrollTop: function() {
},
/**
* Stores the DOM fragment of the action
@@ -65,8 +63,10 @@ var Action = core.Class.extend({
},
/**
* Not implemented for client actions
* @return {int} the number of pixels the webclient is scrolled when leaving the action
*/
set_is_in_DOM: function() {
get_scrollTop: function() {
return 0;
},
/**
* @return {Object} the description of the action
@@ -112,16 +112,6 @@ var WidgetAction = Action.extend({
this.title = widget.get('title');
});
},
/**
* Wraps the action's widget in a container and appends it to el
* @param {DocumentFragment} [el] where to append the widget
* @return {Deferred} resolved when the widget is appended
*/
appendTo: function(el) {
this.$client_action_container = $('<div>').addClass('oe_client_action');
this.$client_action_container.appendTo(el);
return this.widget.appendTo(this.$client_action_container);
},
/**
* Restores WidgetAction by calling do_show on its widget
*/
@@ -132,32 +122,26 @@ var WidgetAction = Action.extend({
});
},
/**
* Detaches the client action's container from the DOM
* @return {jQuery} the action's container
* Detaches the action's widget from the DOM
* @return the widget's $el
*/
detach: function() {
return this.$client_action_container.detach();
// Hack to remove badly inserted nvd3 tooltips ; should be removed when upgrading nvd3 lib
$('body > .nvtooltip').remove();
return framework.detach([{widget: this.widget}]);
},
/**
* Destroys the widget
*/
destroy: function() {
this.widget.destroy();
this.detach();
},
});
/**
* Specialization of WidgetAction for window actions (i.e. ViewManagers)
*/
var ViewManagerAction = WidgetAction.extend({
/**
* Appends the action's widget to el
* @param {DocumentFragment} [el] where to append the widget
* @return {Deferred} resolved when the widget is appended
*/
appendTo: function(el) {
return this.widget.appendTo(el);
},
/**
* Restores a ViewManagerAction
* Switches to the requested view by calling select_view on the ViewManager
@@ -170,27 +154,27 @@ var ViewManagerAction = WidgetAction.extend({
});
},
/**
* Sets is_in_DOM on this.widget
* @param {Boolean} [is_in_DOM] true iff the widget is attached in the DOM
* Sets the on_reverse_breadcrumb_callback and the scrollTop to apply when
* coming back to that action
* @param {Function} [callback] the callback
* @param {int} [scrollTop] the number of pixels to scroll
*/
set_is_in_DOM: function(is_in_DOM) {
this.widget.is_in_DOM = is_in_DOM;
set_on_reverse_breadcrumb: function(callback, scrollTop) {
this._super(callback);
this.set_scrollTop(scrollTop);
},
/**
* Detaches the view_manager from the DOM
* @return {jQuery} the view_manager's $el
* Sets the scroll position of the widgets's active_view
* @param {int} [scrollTop] the number of pixels to scroll
*/
detach: function() {
// Hack to remove badly inserted nvd3 tooltips ; should be removed when upgrading nvd3 lib
$('body > .nvtooltip').remove();
return this.widget.$el.detach();
set_scrollTop: function(scrollTop) {
this.widget.active_view.controller.set_scrollTop(scrollTop);
},
/**
* Destroys the widget
* @return {int} the number of pixels the webclient is scrolled when leaving the action
*/
destroy: function() {
this.widget.destroy();
get_scrollTop: function() {
return this.widget.active_view.controller.get_scrollTop();
},
/**
* @return {Array} array of Objects that will be interpreted to display the breadcrumbs
@@ -214,13 +198,31 @@ var ViewManagerAction = WidgetAction.extend({
});
var ActionManager = Widget.extend({
template: "ActionManager",
init: function(parent) {
template: 'ActionManager',
/**
* Called each time the action manager is attached into the DOM
*/
on_attach_callback: function() {
this.is_in_DOM = true;
if (this.inner_widget && this.inner_widget.on_attach_callback) {
this.inner_widget.on_attach_callback();
}
},
/**
* Called each time the action manager is detached from the DOM
*/
on_detach_callback: function() {
this.is_in_DOM = false;
if (this.inner_widget && this.inner_widget.on_detach_callback) {
this.inner_widget.on_detach_callback();
}
},
init: function(parent, options) {
this._super(parent);
this.action_stack = [];
this.inner_action = null;
this.inner_widget = null;
this.webclient = parent;
this.webclient = options && options.webclient;
this.dialog = null;
this.dialog_widget = null;
this.on('history_back', this, this.proxy('history_back'));
@@ -236,8 +238,15 @@ var ActionManager = Widget.extend({
this.select_action(action, index);
}, 200, true));
// Append the main control panel to the DOM (inside the ActionManager jQuery element)
this.main_control_panel.appendTo(this.$el);
// Listen to event "DOM_updated" to restore the scroll position
core.bus.on('DOM_updated', this, function() {
if (this.inner_action) {
this.trigger_up('scrollTo', {offset: this.inner_action.get_scrollTop() || 0});
}
});
// Insert the main control panel into the DOM
return this.main_control_panel.insertBefore(this.$el);
},
dialog_stop: function (reason) {
if (this.dialog) {
@@ -256,9 +265,9 @@ var ActionManager = Widget.extend({
*/
push_action: function(widget, action_descr, options) {
var self = this;
var old_widget = this.inner_widget;
var old_action = this.inner_action;
var old_action_stack = this.action_stack;
var old_action = this.inner_action;
var old_widget = this.inner_widget;
options = options || {};
// Empty action_stack if requested
@@ -277,8 +286,8 @@ var ActionManager = Widget.extend({
}
// Set on_reverse_breadcrumb callback on previous inner_action
if (old_action) {
old_action.set_on_reverse_breadcrumb(options.on_reverse_breadcrumb);
if (this.webclient && old_action) {
old_action.set_on_reverse_breadcrumb(options.on_reverse_breadcrumb, this.webclient.get_scrollTop());
}
// Update action_stack (must be done before appendTo to properly
@@ -295,25 +304,19 @@ var ActionManager = Widget.extend({
// render the inner_widget in a fragment, and append it to the
// document only when it's ready
var new_widget_fragment = document.createDocumentFragment();
return $.when(this.inner_action.appendTo(new_widget_fragment)).done(function() {
return $.when(this.inner_widget.appendTo(new_widget_fragment)).done(function() {
// Detach the fragment of the previous action and store it within the action
if (old_action) {
old_action.set_fragment(old_action.detach());
old_action.set_is_in_DOM(false);
}
if (!widget.need_control_panel) {
// Hide the main ControlPanel for widgets that do not use it
self.main_control_panel.do_hide();
}
framework.append(self.$el, new_widget_fragment, true);
self.inner_action.set_is_in_DOM(true);
// Hide the old_widget as it will be removed from the DOM when it
// is destroyed
if (old_widget) {
old_widget.do_hide();
}
framework.append(self.$el, new_widget_fragment, {
in_DOM: self.is_in_DOM,
callbacks: [{widget: self.inner_widget}],
});
if (options.clear_breadcrumbs) {
self.clear_action_stack(old_action_stack);
}
@@ -325,6 +328,11 @@ var ActionManager = Widget.extend({
self.inner_widget = old_widget;
});
},
set_scrollTop: function(scrollTop) {
if (this.inner_action) {
this.inner_action.set_scrollTop(scrollTop);
}
},
get_breadcrumbs: function () {
return _.flatten(_.map(this.action_stack, function (action) {
return action.get_breadcrumbs();
@@ -368,7 +376,9 @@ var ActionManager = Widget.extend({
},
select_action: function(action, index) {
var self = this;
return this.webclient.clear_uncommitted_changes().then(function() {
var def = this.webclient && this.webclient.clear_uncommitted_changes() || $.when();
return def.then(function() {
// Set the new inner_action/widget and update the action stack
var old_action = self.inner_action;
var action_index = self.action_stack.indexOf(action);
@@ -376,19 +386,21 @@ var ActionManager = Widget.extend({
self.inner_action = action;
self.inner_widget = action.widget;
// Hide the ControlPanel if the widget doesn't use it
if (!self.inner_widget.need_control_panel) {
self.main_control_panel.do_hide();
}
return $.when(action.restore(index)).done(function() {
// Hide the ControlPanel if the widget doesn't use it
if (!self.inner_widget.need_control_panel) {
self.main_control_panel.do_hide();
}
// Attach the DOM of the action and restore the scroll position only if necessary
if (action !== old_action) {
// Clear the action stack (this also removes the current action from the DOM)
self.clear_action_stack(to_destroy);
// Append the fragment of the action to restore to self.$el
framework.append(self.$el, action.get_fragment(), true);
self.inner_action.set_is_in_DOM(true);
framework.append(self.$el, action.get_fragment(), {
in_DOM: self.is_in_DOM,
callbacks: [{widget: action.widget}],
});
}
});
}).fail(function() {
@@ -406,7 +418,7 @@ var ActionManager = Widget.extend({
}
},
do_push_state: function(state) {
if (!this.webclient || !this.webclient.do_push_state || this.dialog) {
if (!this.webclient || this.dialog) {
return;
}
state = state || {};
@@ -491,13 +503,6 @@ var ActionManager = Widget.extend({
res_id: state.id,
view_type: state.view_type,
});
$.when(action_loaded || null).done(function() {
self.webclient.menu.is_bound.done(function() {
if (self.inner_action && self.inner_action.get_action_descr().id) {
self.webclient.menu.open_action(self.inner_action.get_action_descr().id);
}
});
});
}
}
} else if (state.model && state.id) {
@@ -524,9 +529,9 @@ var ActionManager = Widget.extend({
});
}
$.when(action_loaded || null).done(function() {
return $.when(action_loaded || null).done(function() {
if (self.inner_widget && self.inner_widget.do_load_state) {
self.inner_widget.do_load_state(state, warm);
return self.inner_widget.do_load_state(state, warm);
}
});
},
@@ -628,7 +633,9 @@ var ActionManager = Widget.extend({
});
}
return this[type](action, options);
return $.when(this[type](action, options)).then(function() {
return action;
});
},
null_action: function() {
this.dialog_stop();
@@ -645,6 +652,7 @@ var ActionManager = Widget.extend({
* @return {*}
*/
ir_actions_common: function(executor, options) {
var self = this;
if (executor.action.target === 'new') {
var pre_dialog = (this.dialog && !this.dialog.isDestroyed()) ? this.dialog : null;
if (pre_dialog){
@@ -693,13 +701,16 @@ var ActionManager = Widget.extend({
this.dialog_widget.set_cp_bus(new core.Bus());
}
this.dialog_widget.setParent(this.dialog);
this.dialog.open();
return this.dialog_widget.appendTo(this.dialog.$el);
var fragment = document.createDocumentFragment();
return this.dialog_widget.appendTo(fragment).then(function() {
self.dialog.open().$el.append(fragment);
if(options.state && self.dialog_widget.do_load_state) {
return self.dialog_widget.do_load_state(options.state);
}
}).then(function () { return executor.action; });
}
// Clear uncommitted changes on the current inner widget if there is one
var self = this;
var def = (this.inner_widget && this.webclient.clear_uncommitted_changes()) || $.when();
var def = this.inner_action && this.webclient && this.webclient.clear_uncommitted_changes() || $.when();
return def.then(function() {
self.dialog_stop(executor.action);
return self.push_action(executor.widget(), executor.action, options);
@@ -764,11 +775,11 @@ var ActionManager = Widget.extend({
},
ir_actions_server: function (action, options) {
var self = this;
this.rpc('/web/action/run', {
return this.rpc('/web/action/run', {
action_id: action.id,
context: action.context || {}
}).done(function (action) {
self.do_action(action, options);
}).then(function (action) {
return self.do_action(action, options);
});
},
ir_actions_report_xml: function(action, options) {
@@ -818,6 +829,7 @@ var ActionManager = Widget.extend({
if (action.target === 'self') {
framework.redirect(url);
return $.Deferred(); // The action is finished only when the redirection is done
} else {
window.open(url, '_blank');
}
+90 -83
View File
@@ -1,4 +1,4 @@
odoo.define('base.apps', function (require) {
odoo.define('web.Apps', function (require) {
"use strict";
var core = require('web.core');
@@ -9,10 +9,9 @@ var web_client = require('web.web_client');
var Widget = require('web.Widget');
var _t = core._t;
var qweb = core.qweb;
var apps_client = null;
var qweb = core.qweb;
var Apps = Widget.extend({
template: 'EmptyComponent',
@@ -106,92 +105,98 @@ var Apps = Widget.extend({
}
},
_on_update_count: function(m) {
var self = this;
var count = m.count;
var get_upd_menu_id = function() {
if (_.isUndefined(self._upd_menu_id)) {
var IMD = new Model('ir.model.data');
return IMD.call('get_object_reference', ['base', 'menu_module_updates']).then(function(r) {
var mid = r[1];
if(r[0] !== 'ir.ui.menu') {
// invalid reference, return null
mid = null;
}
self._upd_menu_id = mid;
return mid;
});
} else {
return $.Deferred().resolve(self._upd_menu_id).promise();
}
};
$.when(get_upd_menu_id()).done(function(menu_id) {
if (_.isNull(menu_id)) {
return;
}
var $menu = web_client.menu.$secondary_menus.find(_.str.sprintf('a[data-menu=%s]', menu_id));
if ($menu.length === 0) {
return;
}
if (_.isUndefined(count)) {
count = 0;
}
var needupdate = $menu.find('#menu_counter');
if (needupdate && needupdate.length !== 0) {
if (count > 0) {
needupdate.text(count);
} else {
needupdate.remove();
}
} else if (count > 0) {
$menu.append(qweb.render("Menu.needaction_counter", {widget: {needaction_counter: count}}));
}
});
},
start: function() {
var self = this;
return self.get_client().
done(function(client) {
self.client = client;
var def = $.Deferred();
self.get_client().then(function(client) {
self.client = client;
var qs = (session.debug ? 'debug&' : '') + 'db=' + client.dbname;
var u = client.origin + '/apps/embed/client?' + qs;
var css = {width: '100%', height: '400px'};
self.$ifr = $('<iframe>').attr('src', u);
var qs = (session.debug ? 'debug&' : '') + 'db=' + client.dbname;
var u = client.origin + '/apps/embed/client?' + qs;
var css = {width: '100%', height: '400px'};
self.$ifr = $('<iframe>').attr('src', u);
$(window).on("message.apps", self.proxy('_on_message'));
$(window).on("message.apps", self.proxy('_on_message'));
self.on('message:ready', self, function(m) {
var w = this.$ifr[0].contentWindow;
var act = {
type: 'ir.actions.client',
tag: this.remote_action_tag,
params: _.extend({}, this.params, {
db: this.session.db,
origin: this.session.origin,
})
};
w.postMessage({type:'action', action: act}, client.origin);
});
self.on('message:set_height', self, function(m) {
this.$ifr.height(m.height);
});
self.on('message:update_count', self, function(m) {
var count = m.count;
var get_upd_menu_id = function() {
if (_.isUndefined(self._upd_menu_id)) {
var IMD = new Model('ir.model.data');
return IMD.call('get_object_reference', ['base', 'menu_module_updates']).then(function(r) {
var mid = r[1];
if(r[0] !== 'ir.ui.menu') {
// invalid reference, return null
mid = null;
}
self._upd_menu_id = mid;
return mid;
});
} else {
return $.Deferred().resolve(self._upd_menu_id).promise();
}
};
$.when(get_upd_menu_id()).done(function(menu_id) {
if (_.isNull(menu_id)) {
return;
}
var $menu = web_client.menu.$secondary_menus.find(_.str.sprintf('a[data-menu=%s]', menu_id));
if ($menu.length === 0) {
return;
}
if (_.isUndefined(count)) {
count = 0;
}
var needupdate = $menu.find('#menu_counter');
if (needupdate && needupdate.length !== 0) {
if (count > 0) {
needupdate.text(count);
} else {
needupdate.remove();
}
} else if (count > 0) {
$menu.append(qweb.render("Menu.needaction_counter", {widget: {needaction_counter: count}}));
}
});
});
self.on('message:blockUI', self, function() { framework.blockUI(); });
self.on('message:unblockUI', self, function() { framework.unblockUI(); });
self.on('message:warn', self, function(m) {self.do_warn(m.title, m.message, m.sticky); });
self.$ifr.appendTo(self.$el).css(css).addClass('apps-client');
}).
fail(function(client) {
self.do_warn(_t('Odoo Apps will be available soon'), _t('Showing locally available modules'), true);
self.rpc('/web/action/load', {action_id: self.failback_action_id}).done(function(action) {
self.do_action(action);
web_client.menu.open_action(action.id);
});
self.on('message:ready', self, function(m) {
var w = this.$ifr[0].contentWindow;
var act = {
type: 'ir.actions.client',
tag: this.remote_action_tag,
params: _.extend({}, this.params, {
db: this.session.db,
origin: this.session.origin,
})
};
w.postMessage({type:'action', action: act}, client.origin);
});
self.on('message:set_height', self, function(m) {
this.$ifr.height(m.height);
});
self.on('message:update_count', self, self._on_update_count);
self.on('message:blockUI', self, function() { framework.blockUI(); });
self.on('message:unblockUI', self, function() { framework.unblockUI(); });
self.on('message:warn', self, function(m) {self.do_warn(m.title, m.message, m.sticky); });
self.$ifr.appendTo(self.$el).css(css).addClass('apps-client');
def.resolve();
}, function() {
self.do_warn(_t('Odoo Apps will be available soon'), _t('Showing locally available modules'), true);
return session.rpc('/web/action/load', {action_id: self.failback_action_id}).then(function(action) {
return self.do_action(action);
}).always(function () {
def.reject();
});
});
return def;
},
});
@@ -202,4 +207,6 @@ var AppsUpdates = Apps.extend({
core.action_registry.add("apps", Apps);
core.action_registry.add("apps.updates", AppsUpdates);
return Apps;
});
-1
View File
@@ -199,7 +199,6 @@
process_jobs: function (jobs, services) {
var job;
var require;
var time;
function process_job (job) {
var require = make_require(job);
+1 -3
View File
@@ -22,7 +22,6 @@ var form_relational = require('web.form_relational'); // necessary
var form_widgets = require('web.form_widgets'); // necessary
var framework = require('web.framework');
var ListView = require('web.ListView');
var Menu = require('web.Menu');
var Model = require('web.DataModel');
var pyeval = require('web.pyeval');
var Registry = require('web.Registry');
@@ -96,7 +95,6 @@ openerp.web.FormView = FormView;
openerp.web.json_node_to_xml = utils.json_node_to_xml;
openerp.web.ListView = ListView;
openerp.web.Menu = Menu;
openerp.web.Model = Model;
openerp.web.normalize_format = time.strftime_to_moment_format;
openerp.web.py_eval = pyeval.py_eval;
@@ -208,7 +206,7 @@ ListView.include({
}).value();
}
return this._super(data);
return this._super();
},
/**
* Returns the style for the provided record in the current view (from the
+27 -24
View File
@@ -54,17 +54,15 @@ var ControlPanel = Widget.extend({
* @return {jQuery.Deferred}
*/
start: function() {
this.$title_col = this.$('.oe-cp-title');
// Exposed jQuery nodesets
this.nodes = {
$breadcrumbs: this.$('.oe-view-title'),
$buttons: this.$('.oe-cp-buttons'),
$pager: this.$('.oe-cp-pager'),
$searchview: this.$('.oe-cp-search-view'),
$searchview_buttons: this.$('.oe-search-options'),
$sidebar: this.$('.oe-cp-sidebar'),
$switch_buttons: this.$('.oe-cp-switch-buttons'),
$breadcrumbs: this.$('.breadcrumb'),
$buttons: this.$('.o_cp_buttons'),
$pager: this.$('.o_cp_pager'),
$searchview: this.$('.o_cp_searchview'),
$searchview_buttons: this.$('.o_search_options'),
$sidebar: this.$('.o_cp_sidebar'),
$switch_buttons: this.$('.o_cp_switch_buttons'),
};
// By default, hide the ControlPanel and remove its contents from the DOM
@@ -120,7 +118,7 @@ var ControlPanel = Widget.extend({
// Update the searchview and switch buttons
this._update_search_view(status.searchview, status.search_view_hidden);
if (status.active_view_selector) {
if (status.active_view_selector) {
this._update_switch_buttons(status.active_view_selector);
}
}
@@ -182,22 +180,25 @@ var ControlPanel = Widget.extend({
*/
_render_breadcrumbs: function (breadcrumbs) {
var self = this;
return breadcrumbs.map(function (bc, index) {
return make_breadcrumb(bc, index === breadcrumbs.length - 1);
return self._render_breadcrumbs_li(bc, index, breadcrumbs.length);
});
function make_breadcrumb (bc, is_last) {
var $bc = $('<li>')
.append(is_last ? _.escape(bc.title) : $('<a>').text(bc.title))
.toggleClass('active', is_last);
if (!is_last) {
$bc.click(function () {
self.trigger("on_breadcrumb_click", bc.action, bc.index);
});
}
return $bc;
},
/**
* Private function that renders a breadcrumbs' li Jquery element
*/
_render_breadcrumbs_li: function (bc, index, length) {
var self = this;
var is_last = (index === length-1);
var $bc = $('<li>')
.append(is_last ? _.escape(bc.title) : $('<a>').text(bc.title))
.toggleClass('active', is_last);
if (!is_last) {
$bc.click(function () {
self.trigger("on_breadcrumb_click", bc.action, bc.index);
});
}
return $bc;
},
/**
* Private function that updates the SearchView's visibility and extend the
@@ -211,8 +212,10 @@ var ControlPanel = Widget.extend({
// have been appended to a jQuery node not in the DOM at SearchView initialization
searchview.$buttons = this.nodes.$searchview_buttons;
searchview.toggle_visibility(!is_hidden);
this.$title_col.toggleClass('col-md-6', !is_hidden).toggleClass('col-md-12', is_hidden);
}
this.nodes.$searchview.toggle(!is_hidden);
this.$el.toggleClass('o_breadcrumb_full', !!is_hidden);
},
});
+8 -9
View File
@@ -2,8 +2,8 @@ odoo.define('web.ajax', function (require) {
"use strict";
var core = require('web.core');
var time = require('web.time');
var utils = require('web.utils');
var time = require('web.time');
function genericJsonRpc (fct_name, params, fct) {
var data = {
@@ -14,6 +14,7 @@ function genericJsonRpc (fct_name, params, fct) {
};
var xhr = fct(data);
var result = xhr.pipe(function(result) {
core.bus.trigger('rpc:result', data, result);
if (result.error !== undefined) {
if (result.error.data.arguments[0] !== "bus.Bus not available in test mode") {
console.error("Server application error", JSON.stringify(result.error));
@@ -42,7 +43,7 @@ function jsonRpc(url, fct_name, params, settings) {
contentType: 'application/json'
}));
});
};
}
function jsonpRpc(url, fct_name, params, settings) {
settings = settings || {};
@@ -116,7 +117,7 @@ function jsonpRpc(url, fct_name, params, settings) {
return deferred;
}
});
};
}
// helper
function realSetTimeout (fct, millis) {
@@ -140,7 +141,7 @@ function loadCSS(url) {
'type': 'text/css'
}));
}
};
}
function loadJS(url) {
var def = $.Deferred();
@@ -165,7 +166,7 @@ function loadJS(url) {
head.appendChild(script);
}
return def;
};
}
/**
* Cooperative file download implementation, for ajaxy APIs.
@@ -301,7 +302,7 @@ function post (controller_url, data) {
var Def = $.Deferred();
var postData = new FormData();
$.each(data, function(i,val) {
postData.append(i, val);
});
@@ -311,7 +312,7 @@ function post (controller_url, data) {
var xhr = new XMLHttpRequest();
if(xhr.upload) xhr.upload.addEventListener('progress', progressHandler(Def), false);
var ajaxDef = $.ajax(controller_url, {
xhr: function() {return xhr;},
data: postData,
@@ -324,8 +325,6 @@ function post (controller_url, data) {
return Def;
}
var loadXML = (function () {
var loading = false;
var urls = [];
+6 -10
View File
@@ -177,20 +177,16 @@ return {
_lt: _lt,
// registries
view_registry: new Registry(),
crash_registry: new Registry(),
action_registry : new Registry(),
form_widget_registry: new Registry(),
form_tag_registry: new Registry(),
crash_registry: new Registry(),
form_custom_registry: new Registry(),
form_tag_registry: new Registry(),
form_widget_registry: new Registry(),
list_widget_registry: new Registry(),
search_widgets_registry: new Registry(),
search_filters_registry: new Registry(),
// necessary to make the kanban view compatible between
// community and enterprise edition
one2many_view_registry: new Registry(),
search_filters_registry: new Registry(),
search_widgets_registry: new Registry(),
view_registry: new Registry(),
};
@@ -40,22 +40,17 @@ var CrashManager = core.Class.extend({
if (!this.active) {
return;
}
if (this.$indicator){
if (this.connection_lost) {
return;
}
if (error.code == -32098) {
$.blockUI({ message: '' , overlayCSS: {'z-index': 9999, backgroundColor: '#FFFFFF', opacity: 0.0, cursor: 'wait'}});
this.$indicator = $('<div class="oe_indicator">' + _t("Trying to reconnect... ") + '<i class="fa fa-refresh"></i></div>');
this.$indicator.prependTo("body");
var timeinterval = setInterval(function(){
core.bus.trigger('connection_lost');
this.connection_lost = true;
var timeinterval = setInterval(function() {
ajax.jsonRpc('/web/webclient/version_info').then(function() {
clearInterval(timeinterval);
self.$indicator.html(_t("You are back online"));
self.$indicator.delay(2000).fadeOut('slow',function(){
$(this).remove();
self.$indicator.remove();
});
$.unblockUI();
core.bus.trigger('connection_restored');
self.connection_lost = false;
});
}, 2000);
return;
+4 -8
View File
@@ -320,6 +320,7 @@ var DataSet = Class.extend(mixins.PropertiesMixin, {
this.index = null;
this._sort = [];
this._model = new Model(model, context);
this.orderer = new utils.DropMisordered();
},
previous: function () {
this.index -= 1;
@@ -408,10 +409,11 @@ var DataSet = Class.extend(mixins.PropertiesMixin, {
read_slice: function (fields, options) {
var self = this;
options = options || {};
return this._model.query(fields)
var query = this._model.query(fields)
.limit(options.limit || false)
.offset(options.offset || 0)
.all().done(function (records) {
.all();
return this.orderer.add(query).done(function (records) {
self.ids = _(records).pluck('id');
});
},
@@ -761,12 +763,6 @@ var BufferedDataSet = DataSetStatic.extend({
_.extend(cached.from_read, options.from_read);
_.extend(cached.changes, options.changes);
_.extend(cached.readonly_fields, options.readonly_fields);
// discard values from cached.changes that are in cached.from_read
_.each(cached.changes, function (v, k) {
if (cached.from_read[k] === v) {
delete cached.changes[k];
}
});
if (options.to_create !== undefined) cached.to_create = options.to_create;
if (options.to_delete !== undefined) cached.to_delete = options.to_delete;
}
+54 -10
View File
@@ -211,6 +211,16 @@ if ('nv' in window) {
nv.tooltip.cleanup = function () {
$('.nvtooltip').remove();
};
// monkey patch nvd3 to prevent it to display a tooltip (position: absolute) with
// a negative `top`; with this patch the highest tooltip's position is still in the
// graph
var originalCalcTooltipPosition = nv.tooltip.calcTooltipPosition;
nv.tooltip.calcTooltipPosition = function () {
var container = originalCalcTooltipPosition.apply(this, arguments);
container.style.top = container.style.top.split('px')[0] < 0 ? 0 + 'px' : container.style.top;
return container;
};
}
// Bootstrap customization
@@ -303,11 +313,18 @@ $.extend( proto, {
});
/**
* Private function that triggers an event on core.bus
* Private function to notify that something has been attached in the DOM
* @param {htmlString or Element or Array or jQuery} [content] the content that
* has been attached in the DOM
* @params {Array} [callbacks] array of {widget: w, callback_args: args} such
* that on_attach_callback() will be called on each w with arguments args
*/
function _notify (content) {
function _notify (content, callbacks) {
_.each(callbacks, function(c) {
if (c.widget && c.widget.on_attach_callback) {
c.widget.on_attach_callback(c.callback_args);
}
});
core.bus.trigger('DOM_updated', content);
}
/**
@@ -315,12 +332,14 @@ function _notify (content) {
* @param {jQuery} [$target] the node where content will be appended
* @param {htmlString or Element or Array or jQuery} [content] DOM element,
* array of elements, HTML string or jQuery object to append to $target
* @param {jQuery} [trigger] true to trigger an event, false otherwise
* @param {Boolean} [options.in_DOM] true if $target is in the DOM
* @param {Array} [options.callbacks] array of objects describing the callbacks
* to perform (see _notify for a complete description)
*/
function append ($target, content, trigger) {
function append ($target, content, options) {
$target.append(content);
if (trigger) {
_notify(content);
if (options && options.in_DOM) {
_notify(content, options.callbacks);
}
}
/**
@@ -328,15 +347,39 @@ function append ($target, content, trigger) {
* @param {jQuery} [$target] the node where content will be prepended
* @param {htmlString or Element or Array or jQuery} [content] DOM element,
* array of elements, HTML string or jQuery object to prepend to $target
* @param {jQuery} [trigger] true to trigger an event, false otherwise
* @param {Boolean} [options.in_DOM] true if $target is in the DOM
* @param {Array} [options.callbacks] array of objects describing the callbacks
* to perform (see _notify for a complete description)
*/
function prepend ($target, content, trigger) {
function prepend ($target, content, options) {
$target.prepend(content);
if (trigger) {
_notify(content);
if (options && options.in_DOM) {
_notify(content, options.callbacks);
}
}
/**
* Detaches widgets from the DOM and performs their on_detach_callback()
* @param {Array} [to_detach] array of {widget: w, callback_args: args} such
* that w.$el will be detached and w.on_detach_callback(args) will be called
* @param {jQuery} [options.$to_detach] if given, detached instead of widgets' $el
* @return {jQuery} the detached elements
*/
function detach (to_detach, options) {
_.each(to_detach, function(d) {
if (d.widget.on_detach_callback) {
d.widget.on_detach_callback(d.callback_args);
}
});
var $to_detach = options && options.$to_detach;
if (!$to_detach) {
$to_detach = $(_.map(to_detach, function(d) {
return d.widget.el;
}));
}
return $to_detach.detach();
}
/**
* Returns the distance between a DOM element and the top-left corner of the window
* @param {element} [e] the DOM element
@@ -358,6 +401,7 @@ return {
redirect: redirect,
append: append,
prepend: prepend,
detach: detach,
getPosition: getPosition,
};
+2 -4
View File
@@ -6,14 +6,12 @@ var session = require('web.session');
var Model = Class.extend({
/**
new openerp.web.Model([session,] model_name[, context[, domain]])
new openerp.web.Model(name[, context[, domain]])
@constructs instance.web.Model
@extends instance.web.Class
@param {openerp.web.Session} [session] The session object used to communicate with
the server.
@param {String} model_name name of the OpenERP model this object is bound to
@param {String} name name of the OpenERP model this object is bound to
@param {Object} [context]
@param {Array} [domain]
*/
@@ -236,7 +236,7 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, {
return d;
},
load_qweb: function(mods) {
this.qweb_mutex.exec(function() {
this.qweb_mutex.exec(function () {
return $.get('/web/webclient/qweb?mods=' + mods).then(function (doc) {
if (!doc) { return; }
qweb.add_template(doc);
@@ -423,7 +423,6 @@ function swap(array, elem1, elem2) {
array[i1] = elem2;
}
return {
divmod: divmod,
modf: modf,
+19 -17
View File
@@ -27,6 +27,8 @@ var View = Widget.extend({
// multi_record is used to distinguish views displaying a single record
// (e.g. FormView) from those that display several records (e.g. ListView)
multi_record: true,
// indicates whether or not the view is mobile-friendly
mobile_friendly: false,
// icon is the font-awesome icon to display in the view switcher
icon: 'fa-question',
// whether or not the view requires the model's fields to render itself
@@ -45,11 +47,8 @@ var View = Widget.extend({
* @return {Deferred}
*/
start: function() {
// add css classes that reflect the (absence of) access rights
this.$el.addClass('oe_view')
.toggleClass('oe_cannot_create', !this.is_action_enabled('create'))
.toggleClass('oe_cannot_edit', !this.is_action_enabled('edit'))
.toggleClass('oe_cannot_delete', !this.is_action_enabled('delete'));
// add classname that reflect the (absence of) access rights
this.$el.toggleClass('o_cannot_create', !this.is_action_enabled('create'));
return this._super().then(this.trigger.bind(this, 'view_loaded'));
},
/**
@@ -76,7 +75,7 @@ var View = Widget.extend({
// response handler
var handler = function (action) {
if (action && action.constructor == Object) {
if (action && action.constructor === Object) {
// filter out context keys that are specific to the current action.
// Wrong default_* and search_default_* values will no give the expected result
// Wrong group_by values will simply fail and forbid rendering of the destination view
@@ -106,7 +105,7 @@ var View = Widget.extend({
if (action_data.special === 'cancel') {
return handler({"type":"ir.actions.act_window_close"});
} else if (action_data.type=="object") {
} else if (action_data.type === "object") {
var args = [[record_id]];
if (action_data.args) {
try {
@@ -122,7 +121,7 @@ var View = Widget.extend({
return dataset.call_button(action_data.name, args).then(handler).then(function () {
core.bus.trigger('do_reload_needaction');
});
} else if (action_data.type=="action") {
} else if (action_data.type === "action") {
return data_manager.load_action(action_data.name, _.extend(pyeval.eval('context', context), {
active_model: dataset.model,
active_ids: dataset.ids,
@@ -144,27 +143,24 @@ var View = Widget.extend({
do_load_state: function (state, warm) {
},
/**
* This function should render the buttons of the view, set this.$buttons to
* the produced jQuery element and define some listeners on it.
* This function should be called after start().
* This function should render the action buttons of the view.
* It should be called after start().
* @param {jQuery} [$node] a jQuery node where the rendered buttons should be inserted
* $node may be undefined, in which case the View can insert the buttons somewhere else
*/
render_buttons: function($node) {
},
/**
* This function should instantiate and render the sidebar of the view, set this.sidebar to
* the instantiated Sidebar Widget and possibly add custom items in it.
* This function should be called after start().
* This function should render the sidebar of the view.
* It should be called after start().
* @param {jQuery} [$node] a jQuery node where the sidebar should be inserted
* $node may be undefined, in which case the View can insert the sidebar somewhere else
*/
render_sidebar: function($node) {
},
/**
* This function should render the pager of the view, set this.$pager to
* the produced jQuery element and define some listeners on it.
* This function should be called after start().
* This function should render the pager of the view.
* It should be called after start().
* @param {jQuery} [$node] a jQuery node where the pager should be inserted
* $node may be undefined, in which case the View can insert the pager somewhere else
*/
@@ -200,6 +196,12 @@ var View = Widget.extend({
get_context: function () {
return {};
},
set_scrollTop: function(scrollTop) {
this.scrollTop = scrollTop;
},
get_scrollTop: function() {
return this.scrollTop;
}
});
return View;
+1 -1
View File
@@ -32,7 +32,7 @@ var mixins = core.mixins;
* },
* start: function() {
* // stuff you want to make after the rendering, `this.$el` holds a correct value
* this.$el.find(".my_button").click(/* an example of event binding * /);
* this.$(".my_button").click(/* an example of event binding * /);
*
* // if you have some asynchronous operations, it's a good idea to return
* // a promise in start()
+3 -3
View File
@@ -15,7 +15,7 @@ var Menu = Widget.extend({
this.data = {data:{children:[]}};
this.on("menu_bound", this, function() {
// launch the fetch of needaction counters, asynchronous
var $all_menus = self.$el.parents('body').find('.oe_webclient').find('[data-menu]');
var $all_menus = self.$el.parents('.o_web_client').find('.o_sub_menu').find('[data-menu]');
var all_menu_ids = _.map($all_menus, function (menu) {return parseInt($(menu).attr('data-menu'), 10);});
if (!_.isEmpty(all_menu_ids)) {
this.do_load_needaction(all_menu_ids);
@@ -34,7 +34,7 @@ var Menu = Widget.extend({
},
bind_menu: function() {
var self = this;
this.$secondary_menus = this.$el.parents().find('.oe_secondary_menus_container');
this.$secondary_menus = this.$el.parents().find('.o_sub_menu');
this.$secondary_menus.on('click', 'a[data-menu]', this.on_menu_click);
this.$el.on('click', 'a[data-menu]', function (event) {
event.preventDefault();
@@ -160,7 +160,7 @@ var Menu = Widget.extend({
$sub_menu.show();
// Hide/Show the leftbar menu depending of the presence of sub-items
this.$secondary_menus.parent('.oe_leftbar').toggle(!!$sub_menu.children().length);
this.$secondary_menus.toggle(!!$sub_menu.children().length);
// Activate current menu item and show parents
this.$secondary_menus.find('.active').removeClass('active');
+90 -52
View File
@@ -14,7 +14,25 @@ var QWeb = core.qweb;
var _t = core._t;
var ViewManager = Widget.extend(ControlPanelMixin, {
template: "ViewManager",
className: "o_view_manager_content",
/**
* Called each time the view manager is attached into the DOM
*/
on_attach_callback: function() {
this.is_in_DOM = true;
if (this.active_view && this.active_view.controller.on_attach_callback) {
this.active_view.controller.on_attach_callback();
}
},
/**
* Called each time the view manager is detached from the DOM
*/
on_detach_callback: function() {
this.is_in_DOM = false;
if (this.active_view && this.active_view.controller.on_detach_callback) {
this.active_view.controller.on_detach_callback();
}
},
/**
* @param {Object} [dataset]
* @param {Array} [views] List of [view_id, view_type[, fields_view]]
@@ -34,12 +52,11 @@ var ViewManager = Widget.extend(ControlPanelMixin, {
this.active_view = null;
this.registry = core.view_registry;
this.title = this.action.name;
this.is_in_DOM = false; // used to know if the view manager is attached in the DOM
_.each(views, function (view) {
var view_type = view[1] || view.view_type;
var View = self.registry.get(view_type);
if (!View) {
console.error("View type", "'"+view[1]+"'", "is not present in the view registry.");
console.error("View type", "'"+view_type+"'", "is not present in the view registry.");
return;
}
var view_label = View.prototype.display_name;
@@ -48,9 +65,9 @@ var ViewManager = Widget.extend(ControlPanelMixin, {
button_label: _.str.sprintf(_t('%(view_type)s view'), {'view_type': (view_label || view_type)}),
controller: null,
fields_view: view[2] || view.fields_view,
embedded_view: view.embedded_view,
icon: View.prototype.icon,
label: view_label,
mobile_friendly: View.prototype.mobile_friendly,
multi_record: View.prototype.multi_record,
options: view.options || {},
require_fields: View.prototype.require_fields,
@@ -81,12 +98,7 @@ var ViewManager = Widget.extend(ControlPanelMixin, {
view.options = _.extend({
action: self.action,
}, self.flags, self.flags[view.type], view.options);
view.$container = self.$(".oe-view-manager-view-" + view.type);
});
this.$el.addClass("oe_view_manager_" + ((this.action && this.action.target) || 'current'));
this.control_elements = {};
if (this.flags.search_view) {
this.search_view_loaded = this.setup_search_view();
}
@@ -224,33 +236,40 @@ var ViewManager = Widget.extend(ControlPanelMixin, {
var view_control_elements = this.render_view_control_elements();
// Show the view
this.active_view.$container.show();
return $.when(view_controller.do_show(view_options)).done(function () {
// Prepare the ControlPanel content and update it
var cp_status = {
active_view_selector: '.oe-cp-switch-' + self.active_view.type,
active_view_selector: '.o_cp_switch_' + self.active_view.type,
breadcrumbs: self.action_manager && self.action_manager.get_breadcrumbs(),
cp_content: _.extend({}, self.control_elements, view_control_elements),
cp_content: _.extend({}, self.searchview_elements, view_control_elements),
hidden: self.flags.headless,
searchview: self.searchview,
search_view_hidden: view_controller.searchable === false || view_controller.searchview_hidden,
};
self.update_control_panel(cp_status);
if (old_view) {
// Detach the old view but not ui-autocomplete elements to let
// jquery-ui garbage-collect them
old_view.$container.contents().not('.ui-autocomplete').detach();
// Hide old view (at first rendering, there is no view to hide)
if (self.active_view !== old_view) {
if (old_view.controller) old_view.controller.do_hide();
if (old_view.$container) old_view.$container.hide();
// Detach the old view and store it
if (old_view && old_view !== self.active_view) {
// Store the scroll position
if (self.action_manager && self.action_manager.webclient) {
old_view.controller.set_scrollTop(self.action_manager.webclient.get_scrollTop());
}
// Do not detach ui-autocomplete elements to let jquery-ui garbage-collect them
var $to_detach = self.$el.contents().not('.ui-autocomplete');
old_view.$fragment = framework.detach([{widget: old_view.controller}], {$to_detach: $to_detach});
}
// Append the view fragment to its $container
framework.append(self.active_view.$container, view_fragment, self.is_in_DOM);
// If the user switches from a multi-record to a mono-record view,
// the action manager should be scrolled to the top.
if (old_view && old_view.controller.multi_record === true && view_controller.multi_record === false) {
view_controller.set_scrollTop(0);
}
// Append the view fragment to self.$el
framework.append(self.$el, view_fragment, {
in_DOM: self.is_in_DOM,
callbacks: [{widget: view_controller}],
});
});
},
create_view: function(view, view_options) {
@@ -282,39 +301,50 @@ var ViewManager = Widget.extend(ControlPanelMixin, {
return this.switch_mode(view_type);
},
/**
* Renders the switch buttons and adds listeners on them but does not append them to the DOM
* Sets $switch_buttons in control_elements to send to the ControlPanel
* @param {Object} [src] the source requesting the switch_buttons
* @param {Array} [views] the array of views
* Renders the switch buttons for multi- and mono-record views and adds
* listeners on them, but does not append them to the DOM
* Sets switch_buttons.$mono and switch_buttons.$multi to send to the ControlPanel
*/
render_switch_buttons: function() {
if (this.flags.views_switcher && this.view_order.length > 1) {
var self = this;
var self = this;
// Render switch buttons but do not append them to the DOM as this will
// be done later, simultaneously to all other ControlPanel elements
this.control_elements.$switch_buttons = $(QWeb.render('ViewManager.switch-buttons', {views: self.view_order}));
// Partition the views according to their multi-/mono-record status
var views = _.partition(this.view_order, function(view) {
return view.multi_record === true;
});
var multi_record_views = views[0];
var mono_record_views = views[1];
// Create bootstrap tooltips
_.each(this.views, function(view) {
self.control_elements.$switch_buttons.siblings('.oe-cp-switch-' + view.type).tooltip();
});
// Inner function to render and prepare switch_buttons
var _render_switch_buttons = function(views) {
if (views.length > 1) {
var $switch_buttons = $(QWeb.render('ViewManager.switch-buttons', {views: views}));
// Create bootstrap tooltips
_.each(views, function(view) {
$switch_buttons.filter('.o_cp_switch_' + view.type).tooltip();
});
// Add onclick event listener
$switch_buttons.filter('button').click(_.debounce(function(event) {
var view_type = $(event.target).data('view-type');
self.switch_mode(view_type);
}, 200, true));
return $switch_buttons;
}
};
// Add onclick event listener
this.control_elements.$switch_buttons.siblings('button').click(_.debounce(function(event) {
var view_type = $(event.target).data('view-type');
self.switch_mode(view_type);
}, 200, true));
}
// Render switch buttons but do not append them to the DOM as this will
// be done later, simultaneously to all other ControlPanel elements
this.switch_buttons = {};
this.switch_buttons.$multi = _render_switch_buttons(multi_record_views);
this.switch_buttons.$mono = _render_switch_buttons(mono_record_views);
},
/**
* Renders the control elements (buttons, sidebar, pager) of the current view
* This must be done when active_search is resolved (for KanbanViews)
* Renders the control elements (buttons, sidebar, pager) of the current view.
* Fills this.active_view.control_elements dictionnary with the rendered
* elements and the adequate view switcher, to send to the ControlPanel
* elements and the adequate view switcher, to send to the ControlPanel.
* Warning: it should be called before calling do_show on the view as the
* sidebar is extended to listen on the load_record event triggered as soon
* as do_show is done (the sidebar should thus be instantiated before)
* as do_show is done (the sidebar should thus be instantiated before).
*/
render_view_control_elements: function() {
if (!this.active_view.control_elements) {
@@ -335,6 +365,15 @@ var ViewManager = Widget.extend(ControlPanelMixin, {
elements = _.mapObject(elements, function($node) {
return $node && $node.contents();
});
// Use the adequate view switcher (mono- or multi-record)
if (this.switch_buttons) {
if (this.active_view.multi_record) {
elements.$switch_buttons = this.switch_buttons.$multi;
} else {
elements.$switch_buttons = this.switch_buttons.$mono;
}
}
// Store the rendered elements in the active_view to allow restoring them later
this.active_view.control_elements = elements;
}
@@ -342,10 +381,7 @@ var ViewManager = Widget.extend(ControlPanelMixin, {
},
/**
* Sets up the current viewmanager's search view.
* Sets $searchview and $searchview_buttons in control_elements to send to the ControlPanel
*
* @param {Number|false} view_id the view to use or false for a default one
* @returns {jQuery.Deferred} search view startup deferred
* Sets $searchview and $searchview_buttons in searchview_elements to send to the ControlPanel
*/
setup_search_view: function() {
var self = this;
@@ -361,6 +397,7 @@ var ViewManager = Widget.extend(ControlPanelMixin, {
search_defaults[match[1]] = value;
}
});
var options = {
hidden: this.flags.search_view === false,
disable_custom_filters: this.flags.search_disable_custom_filters,
@@ -374,8 +411,9 @@ var ViewManager = Widget.extend(ControlPanelMixin, {
this.searchview.on('search_data', this, this.search.bind(this));
return $.when(this.searchview.appendTo($("<div>"))).done(function() {
self.control_elements.$searchview = self.searchview.$el;
self.control_elements.$searchview_buttons = self.searchview.$buttons.contents();
self.searchview_elements = {};
self.searchview_elements.$searchview = self.searchview.$el;
self.searchview_elements.$searchview_buttons = self.searchview.$buttons.contents();
});
},
/**
+46 -29
View File
@@ -215,7 +215,7 @@ var CompletionFieldMixin = {
self._search_create_popup("search", data);
});
},
classname: 'oe_m2o_dropdown_option'
classname: 'o_m2o_dropdown_option'
});
}
// quick create
@@ -228,7 +228,7 @@ var CompletionFieldMixin = {
action: function() {
self._quick_create(search_val);
},
classname: 'oe_m2o_dropdown_option'
classname: 'o_m2o_dropdown_option'
});
}
// create...
@@ -238,14 +238,14 @@ var CompletionFieldMixin = {
action: function() {
self._search_create_popup("form", undefined, self._create_context(search_val));
},
classname: 'oe_m2o_dropdown_option'
classname: 'o_m2o_dropdown_option'
});
}
else if (values.length === 0) {
values.push({
label: _t("No results to show..."),
action: function() {},
classname: 'oe_m2o_dropdown_option'
classname: 'o_m2o_dropdown_option'
});
}
@@ -275,7 +275,7 @@ var CompletionFieldMixin = {
// all search/create popup handling
_search_create_popup: function(view, ids, context) {
var self = this;
new SelectCreateDialog(this, {
new SelectCreateDialog(this, _.extend({}, (this.options || {}), {
res_model: self.field.relation,
domain: self.build_domain(),
context: new data.CompoundContext(self.build_context(), context || {}),
@@ -287,7 +287,7 @@ var CompletionFieldMixin = {
self.add_id(element_ids[0]);
self.focus();
}
}).open();
})).open();
},
/**
* To implement.
@@ -474,7 +474,7 @@ var FormWidget = Widget.extend(InvisibilityChangerMixin, {
debug: session.debug,
widget: widget
});
},
}
}, options || {});
//only show tooltip if we are in debug or if we have a help to show, otherwise it will display
//as empty
@@ -666,16 +666,16 @@ var AbstractField = FormWidget.extend(FieldInterface, {
this._check_css_flags();
});
this.$translate = $(); // Enterprise compatibility
this.$translate = (_t.database.multi_lang && this.field.translate) ? $('<button/>', {
type: 'button',
}).addClass('o_field_translate fa fa-globe btn btn-link') : $();
},
renderElement: function() {
var self = this;
this._super();
if (this.field.translate && this.view) {
this.$el.addClass('oe_form_field_translatable');
this.$el.find('.oe_field_translate').click(this.on_translate);
}
this.$label = this.view ? this.view.$el.find('label[for=' + this.id_for_label + ']') : $();
this.$el.addClass('o_form_field');
this.$label = this.view ? this.view.$('label[for=' + this.id_for_label + ']') : $();
this.do_attach_tooltip(this, this.$label[0] || this.$el);
if (session.debug) {
this.$label.off('dblclick').on('dblclick', function() {
@@ -707,16 +707,23 @@ var AbstractField = FormWidget.extend(FieldInterface, {
});
this.render_value();
this._toggle_label();
if (this.view) {
this.$translate
.insertAfter(this.$el)
.on('click', _.bind(this.on_translate, this));
}
},
_toggle_label: function() {
var empty = this.get('effective_readonly') && this.is_false();
this.$label.toggleClass('o_form_label_empty', empty).toggleClass('o_form_label_false', this.get('effective_readonly') && this.get('value') === false);
this.$el.toggleClass('o_form_field_empty', empty);
},
/**
* Private. Do not use.
*/
_set_required: function() {
this.$el.toggleClass('oe_form_required', this.get("required"));
this.$el.toggleClass('o_form_required', this.get("required"));
},
set_value: function(value_) {
this.set({'value': value_});
@@ -752,10 +759,11 @@ var AbstractField = FormWidget.extend(FieldInterface, {
return this.get('value') === false;
},
_check_css_flags: function() {
if (this.field.translate) {
this.$el.find('.oe_field_translate').toggle(this.field_manager.get('actual_mode') !== "create");
}
this.$el.toggleClass('oe_form_invalid', !this.disable_utility_classes && !!this.field_manager.get('display_invalid_fields') && !this.is_valid());
var show_translate = (!this.get('effective_readonly') && this.field_manager.get('actual_mode') !== "create");
this.$translate.toggleClass('o_translate_active', !!show_translate);
this.$el.add(this.$label)
.toggleClass('o_form_invalid', !this.disable_utility_classes && !!this.field_manager.get('display_invalid_fields') && !this.is_valid());
},
focus: function() {
return false;
@@ -770,11 +778,10 @@ var AbstractField = FormWidget.extend(FieldInterface, {
self.do_action(r);
});
},
set_dimensions: function (height, width) {
set_dimensions: function(height, width) {
this.$el.css({
width: width,
minHeight: height
height: height,
});
},
commit_value: function() {
@@ -802,7 +809,7 @@ var ViewDialog = Dialog.extend({ // FIXME should use ViewManager
options.dialogClass = options.dialogClass || '';
options.dialogClass += ' o_act_window';
this._super(parent, _.clone(options));
this._super(parent, $.extend(true, {}, options));
this.res_model = options.res_model || null;
this.res_id = options.res_id || null;
@@ -864,7 +871,7 @@ var FormViewDialog = ViewDialog.extend({
];
if(!readonly) {
options.buttons.splice(0, 0, {text: _t("Save") + ((multi_select)? " " + _t(" & Close") : ""), classes: "btn-primary o_formdialog_save", click: function() { // o_formdialog_save class for web_tests!
options.buttons.splice(0, 0, {text: _t("Save") + ((multi_select)? " " + _t(" & Close") : ""), classes: "btn-primary", click: function() {
self.view_form.onchanges_mutex.def.then(function() {
if (!self.view_form.warning_displayed) {
$.when(self.view_form.save()).done(function() {
@@ -923,6 +930,7 @@ var FormViewDialog = ViewDialog.extend({
var fragment = document.createDocumentFragment();
self.view_form.appendTo(fragment).then(function () {
self.view_form.do_show().then(function() {
self.view_form.autofocus();
_super().$el.append(fragment);
});
});
@@ -941,7 +949,7 @@ var SelectCreateListView = ListView.extend({
this.popup.close();
},
do_select: function(ids, records) {
this._super(ids, records);
this._super.apply(this, arguments);
this.popup.on_click_element(ids);
}
});
@@ -1022,7 +1030,7 @@ var SelectCreateDialog = ViewDialog.extend({
_.extend({'deletable': false,
'selectable': !self.options.disable_multiple_selection,
'import_enabled': false,
'$buttons': self.$footer,
'$buttons': self.$buttons,
'disable_editable_mode': true,
'pager': true,
}, self.options.list_view_options || {}));
@@ -1030,6 +1038,9 @@ var SelectCreateDialog = ViewDialog.extend({
e.cancel = true;
});
self.view_list.popup = self;
self.view_list.on('list_view_loaded', self, function() {
this.on_view_list_loaded();
});
var buttons = [
{text: _t("Cancel"), classes: "btn-default o_form_button_cancel", close: true}
@@ -1076,6 +1087,7 @@ var SelectCreateDialog = ViewDialog.extend({
this.close();
return new FormViewDialog(this.__parentedParent, this.options).open();
},
on_view_list_loaded: function() {},
});
var DomainEditorDialog = SelectCreateDialog.extend({
@@ -1095,11 +1107,11 @@ var DomainEditorDialog = SelectCreateDialog.extend({
},
get_domain: function (selected_ids) {
var group_domain = [],
domain;
if (this.$('input.oe_list_record_selector').prop('checked')) {
var group_domain = [];
var domain;
if (this.$('.o_list_record_selector input').prop('checked')) {
if (this.view_list.grouped) {
var group_domain = _.chain(_.values(this.view_list.groups.children))
group_domain = _.chain(_.values(this.view_list.groups.children))
.filter(function (child) { return child.records.length; })
.map(function (c) { return c.datagroup.domain;})
.value();
@@ -1118,6 +1130,11 @@ var DomainEditorDialog = SelectCreateDialog.extend({
}
return this.dataset.domain.concat(group_domain).concat(domain || []);
},
on_view_list_loaded: function() {
this.$('.o_list_record_selector input').prop('checked', true);
this.$footer.find(".o_selectcreatepopup_search_select").prop('disabled', false);
},
});
return {
@@ -6,7 +6,6 @@ var core = require('web.core');
var data = require('web.data');
var Dialog = require('web.Dialog');
var common = require('web.form_common');
var FormView = require('web.FormView');
var ListView = require('web.ListView');
var Model = require('web.DataModel');
var session = require('web.session');
@@ -56,16 +55,15 @@ var FieldMany2One = common.AbstractField.extend(common.CompletionFieldMixin, com
events: {
'keydown input': function (e) {
switch (e.which) {
case $.ui.keyCode.UP:
case $.ui.keyCode.DOWN:
e.stopPropagation();
case $.ui.keyCode.UP:
case $.ui.keyCode.DOWN:
e.stopPropagation();
}
},
},
init: function(field_manager, node) {
this._super(field_manager, node);
common.CompletionFieldMixin.init.call(this);
this.set({'value': false});
this.display_value = {};
this.display_value_backup = {};
this.last_search = [];
@@ -91,8 +89,9 @@ var FieldMany2One = common.AbstractField.extend(common.CompletionFieldMixin, com
common.ReinitializeFieldMixin.initialize_field.call(this);
},
initialize_content: function() {
if (!this.get("effective_readonly"))
if (!this.get("effective_readonly")) {
this.render_editable();
}
},
destroy_content: function () {
if (this.$dropdown) {
@@ -125,16 +124,15 @@ var FieldMany2One = common.AbstractField.extend(common.CompletionFieldMixin, com
},
render_editable: function() {
var self = this;
this.$input = this.$el.find("input");
this.$input = this.$("input");
this.init_error_displayer();
self.$input.on('focus', function() {
self.hide_error_displayer();
});
this.$dropdown = this.$el.find(".oe_m2o_drop_down_button");
this.$follow_button = $(".oe_m2o_cm_button", this.$el);
this.$dropdown = this.$(".o_dropdown_button");
this.$follow_button = this.$(".o_external_button");
this.$follow_button.click(function(ev) {
ev.preventDefault();
@@ -177,8 +175,7 @@ var FieldMany2One = common.AbstractField.extend(common.CompletionFieldMixin, com
};
this.$input.keydown(input_changed);
this.$input.change(input_changed);
this.$dropdown.click(function() {
self.$input.focus();
this.$input.on('click', function() {
if (self.$input.autocomplete("widget").is(":visible")) {
self.$input.autocomplete("close");
} else {
@@ -298,7 +295,6 @@ var FieldMany2One = common.AbstractField.extend(common.CompletionFieldMixin, com
});
// set position for list of suggestions box
this.$input.autocomplete( "option", "position", { my : "left top", at: "left bottom" } );
this.$input.autocomplete("widget").openerpClass();
// used to correct a bug when selecting an element by pushing 'enter' in an editable list
this.$input.keyup(function(e) {
if (e.which === 13) { // ENTER
@@ -311,7 +307,7 @@ var FieldMany2One = common.AbstractField.extend(common.CompletionFieldMixin, com
},
render_value: function(no_recurse) {
var self = this;
if (! this.get("value")) {
if (!this.get("value")) {
this.display_string("");
return;
}
@@ -320,7 +316,7 @@ var FieldMany2One = common.AbstractField.extend(common.CompletionFieldMixin, com
this.display_string(display);
return;
}
if (! no_recurse) {
if (!no_recurse) {
var dataset = new data.DataSetStatic(this, this.field.relation, self.build_context());
var def = this.alive(dataset.name_get([self.get("value")])).done(function(data) {
if (!data[0]) {
@@ -341,38 +337,33 @@ var FieldMany2One = common.AbstractField.extend(common.CompletionFieldMixin, com
}
},
display_string: function(str) {
var self = this;
if (!this.get("effective_readonly")) {
this.$input.val(str.split("\n")[0]);
this.current_display = this.$input.val();
if (this.is_false()) {
this.$('.oe_m2o_cm_button').css({'display':'none'});
} else {
this.$('.oe_m2o_cm_button').css({'display':'inline'});
}
this.$follow_button.toggle(!this.is_false());
this.$el.toggleClass('o_with_button', !!this.$follow_button && this.$follow_button.length > 0 && !this.is_false());
} else {
var lines = _.escape(str).split("\n");
var link = "";
var follow = "";
link = lines[0];
follow = _.rest(lines).join("<br />");
if (follow)
link += "<br />";
var $link = this.$el.find('.oe_form_uri')
.unbind('click')
.html(link);
if (! this.options.no_open)
$link.click(function () {
var context = self.build_context().eval();
var model_obj = new Model(self.field.relation);
model_obj.call('get_formview_action', [self.get("value"), context]).then(function(action){
self.do_action(action);
});
return false;
});
$(".oe_form_m2o_follow", this.$el).html(follow);
this.$el.html(_.escape(str).split("\n").join("<br/>"));
// Define callback to perform when clicking on the field
if (!this.options.no_open) {
// Remove potential previously added event handler
this.$el.off('click');
// Ensure that the callback is performed only once even with multiple clicks
var execute_formview_action_once = _.once(this.execute_formview_action.bind(this));
this.$el.click(function (ev) {
ev.preventDefault();
execute_formview_action_once();
});
}
}
},
execute_formview_action: function() {
var self = this;
var context = self.build_context().eval();
(new Model(self.field.relation)).call('get_formview_action', [self.get("value"), context]).then(function(action) {
self.do_action(action);
});
},
set_value: function(value_) {
if (value_ instanceof Array) {
this.display_value = {};
@@ -418,11 +409,6 @@ var FieldMany2One = common.AbstractField.extend(common.CompletionFieldMixin, com
this.no_ed = false;
return res;
},
set_dimensions: function (height, width) {
this._super(height, width);
if (!this.get("effective_readonly") && this.$input)
this.$input.css('height', height);
}
});
/**
@@ -586,40 +572,39 @@ var AbstractManyField = common.AbstractField.extend({
_.each(command_list, function(command) {
self.mutex.exec(function() {
var id = command[1];
if (!id || _.isString(id) || _.isNumber(id)) {
switch (command[0]) {
case COMMANDS.CREATE:
var data = _.clone(command[2]);
delete data.id;
return dataset.create(data, internal_options).then(function (id) {
switch (command[0]) {
case COMMANDS.CREATE:
var data = _.clone(command[2]);
delete data.id;
return dataset.create(data, internal_options).then(function (id) {
dataset.ids.push(id);
res = id;
});
case COMMANDS.UPDATE:
return dataset.write(id, command[2], internal_options).then(function () {
if (dataset.ids.indexOf(id) === -1) {
dataset.ids.push(id);
res = id;
});
case COMMANDS.UPDATE:
return dataset.write(id, command[2], internal_options).then(function () {
if (dataset.ids.indexOf(id) === -1) {
dataset.ids.push(id);
res = id;
}
});
case COMMANDS.FORGET:
return dataset.unlink([id]);
case COMMANDS.DELETE:
return dataset.unlink([id]);
case COMMANDS.LINK_TO:
if (dataset.ids.indexOf(id) === -1) {
return dataset.add_ids([id], internal_options);
}
return;
case COMMANDS.DELETE_ALL:
return dataset.reset_ids([], {keep_read_data: true});
case COMMANDS.REPLACE_WITH:
dataset.ids = [];
return dataset.alter_ids(command[2], internal_options);
}
});
case COMMANDS.FORGET:
return dataset.unlink([id]);
case COMMANDS.DELETE:
return dataset.unlink([id]);
case COMMANDS.LINK_TO:
if (dataset.ids.indexOf(id) === -1) {
return dataset.add_ids([id], internal_options);
}
return;
case COMMANDS.DELETE_ALL:
return dataset.reset_ids([], {keep_read_data: true});
case COMMANDS.REPLACE_WITH:
dataset.ids = [];
return dataset.alter_ids(command[2], internal_options);
default:
throw new Error("send_commands to '"+self.name+"' receive a non command value." +
"\n" + JSON.stringify(command_list));
}
throw new Error("send_commands to '"+self.name+"' receive a non command value." +
"\n" + JSON.stringify(command_list));
});
});
@@ -660,10 +645,6 @@ var AbstractManyField = common.AbstractField.extend({
if (record.to_create) {
command_list.push(COMMANDS.create(values));
} else {
if (index === -1) {
// because the UPDATE below does not imply LINK_TO
command_list.push(COMMANDS.link_to(id));
}
command_list.push(COMMANDS.update(record.id, values));
}
return;
@@ -724,13 +705,12 @@ var FieldX2Many = AbstractManyField.extend({
},
start: function() {
this._super.apply(this, arguments);
this.$el.addClass('oe_form_field');
var self = this;
this.load_views();
var destroy = function() {
self.is_loaded = self.is_loaded.then(function() {
self.renderElement();
self.viewmanager.destroy();
return $.when(self.load_views()).done(function() {
self.reload_current_view();
@@ -828,7 +808,8 @@ var FieldX2Many = AbstractManyField.extend({
});
});
$.async_when().done(function () {
self.alive( self.viewmanager.appendTo(self.$el));
self.$el.addClass('o_view_manager_content');
self.alive(self.viewmanager.attachTo(self.$el));
});
return def;
},
@@ -881,6 +862,9 @@ var FieldX2Many = AbstractManyField.extend({
}
return true;
},
is_false: function() {
return false;
},
});
var X2ManyDataSet = data.BufferedDataSet.extend({
@@ -891,10 +875,15 @@ var X2ManyDataSet = data.BufferedDataSet.extend({
self.context.add(context);
});
return this.context;
}
},
});
var X2ManyViewManager = ViewManager.extend({
custom_events: {
// Catch event scrollTo to prevent scrolling to the top when using the
// pager of List and Kanban views in One2Many fields
'scrollTo': function() {},
},
init: function(parent, dataset, views, flags, x2many_views) {
// By default, render buttons and pager in X2M fields, but no sidebar
flags = _.extend({}, flags, {
@@ -984,6 +973,15 @@ var X2ManyListView = ListView.extend({
});
return valid;
},
render_pager: function($node, options) {
options = _.extend(options || {}, {
single_page_hidden: true,
});
this._super($node, options);
},
display_nocontent_helper: function () {
return false;
},
});
/**
@@ -1008,7 +1006,7 @@ var X2ManyList = ListView.List.extend({
var $cell = $('<td>', {
colspan: columns,
'class': 'oe_form_field_x2many_list_row_add'
'class': 'o_form_field_x2many_list_row_add'
}).append(
$('<a>', {href: '#'}).text(_t("Add an item"))
.click(function (e) {
@@ -1037,7 +1035,6 @@ var X2ManyList = ListView.List.extend({
});
var One2ManyListView = X2ManyListView.extend({
_template: 'One2Many.listview',
init: function () {
this._super.apply(this, arguments);
this.options = _.extend(this.options, {
@@ -1177,7 +1174,7 @@ var One2ManyListView = X2ManyListView.extend({
var click_outside = ($target.closest('.ui-autocomplete,.btn,.modal-backdrop').length === 0);
// Check if click inside the current list editable
var $o2m = $target.closest(".oe_list_editable");
var $o2m = $target.closest(".o_list_editable");
if($o2m.length && $o2m[0] === this.el) {
click_outside = false;
}
@@ -1251,39 +1248,19 @@ var One2ManyGroups = ListView.Groups.extend({
}
});
var One2ManyFormView = FormView.extend({
form_template: 'One2Many.formview',
load_form: function(data) {
this._super(data);
var self = this;
this.$buttons.find('button.oe_form_button_create').click(function() {
self.save().done(self.on_button_new);
});
},
do_notify_change: function() {
if (this.dataset.parent_view) {
this.dataset.parent_view.do_notify_change();
} else {
this._super.apply(this, arguments);
}
}
});
var FieldOne2Many = FieldX2Many.extend({
init: function() {
this._super.apply(this, arguments);
this.x2many_views = {
form: One2ManyFormView,
kanban: core.view_registry.get('one2many_kanban'),
list: One2ManyListView,
};
},
start: function() {
this.$el.addClass('oe_form_field_one2many');
this.$el.addClass('o_form_field_one2many');
return this._super.apply(this, arguments);
},
commit_value: function() {
var self = this;
var view = this.viewmanager.active_view;
if(view.type === "list" && view.controller.editable()) {
return this.mutex.def.then(function () {
@@ -1357,7 +1334,7 @@ var FieldMany2Many = FieldX2Many.extend({
};
},
start: function() {
this.$el.addClass('oe_form_field_many2many');
this.$el.addClass('o_form_field_many2many');
return this._super.apply(this, arguments);
}
});
@@ -1373,7 +1350,7 @@ var FieldMany2ManyKanban = FieldMany2Many.extend({
});
var FieldMany2ManyTags = AbstractManyField.extend(common.CompletionFieldMixin, common.ReinitializeFieldMixin, {
className: "oe_form_field o_form_field_many2manytags",
className: "o_form_field_many2manytags",
tag_template: "FieldMany2ManyTag",
events: {
@@ -1435,7 +1412,6 @@ var FieldMany2ManyTags = AbstractManyField.extend(common.CompletionFieldMixin, c
}
},
get_render_data: function(ids){
var self = this;
this.dataset.cancel_read();
var fields = this.fields.color ? ['display_name', 'name', 'color'] : ['display_name', 'name']; // TODO master: remove useless 'name'
return this.dataset.read_ids(ids, fields);
@@ -1475,26 +1451,30 @@ var FieldMany2ManyTags = AbstractManyField.extend(common.CompletionFieldMixin, c
}
return false;
},
set_dimensions: function (height, width) {
this._super(height, width);
var $input = this.$('input');
if (!this.get("effective_readonly") && $input) {
$input.css('height', height);
set_dimensions: function(height, width) {
if(this.many2one) {
this.many2one.$el.css('height', 'auto');
}
this.$el.css({
width: width,
minHeight: height,
});
if(this.many2one) {
this.many2one.$el.css('height', this.$el.height());
}
},
open_color_picker: function(ev){
this.mutex.exec(function(){
if (this.fields.color) {
this.$color_picker = $(QWeb.render('FieldMany2ManyTag.colorpicker', {
'widget': this,
'tag_id': $(ev.currentTarget).data('id'),
}));
$(ev.currentTarget).append(this.$color_picker);
this.$color_picker.dropdown('toggle');
this.$color_picker.attr("tabindex", 1).focus();
}
}.bind(this));
if (this.fields.color) {
this.$color_picker = $(QWeb.render('FieldMany2ManyTag.colorpicker', {
'widget': this,
'tag_id': $(ev.currentTarget).data('id'),
}));
$(ev.currentTarget).append(this.$color_picker);
this.$color_picker.dropdown('toggle');
this.$color_picker.attr("tabindex", 1).focus();
}
},
close_color_picker: function(){
this.$color_picker.remove();
@@ -1513,24 +1493,76 @@ var FieldMany2ManyTags = AbstractManyField.extend(common.CompletionFieldMixin, c
var old_color = tag.data('color');
tag.removeClass('o_tag_color_' + old_color);
tag.data('color', color);
tag.addClass('o_tag_color_' + color);
tag.addClass('o_tag_color_' + color);
});
},
});
/**
* Widget for (many2many field) to upload one or more file in same time and display in list.
* The user can delete his files.
* Options on attribute ; "blockui" {Boolean} block the UI or not
* during the file is uploading
* Options on attribute ; "blockui" {Boolean} block the UI or not during the file is uploading
*/
var FieldMany2ManyBinaryMultiFiles = AbstractManyField.extend(common.ReinitializeFieldMixin, {
template: "FieldBinaryFileUploader",
events: {
'click .o_attach': function(e) {
this.$('.o_form_input_file').click();
},
'change .o_form_input_file': function(e) {
e.stopPropagation();
var $target = $(e.target);
var value = $target.val();
if(value !== '') {
if(this.data[0] && this.data[0].upload) { // don't upload more of one file in same time
return false;
}
var filename = value.replace(/.*[\\\/]/, '');
for(var id in this.get('value')) {
// if the files exits, delete the file before upload (if it's a new file)
if(this.data[id] && (this.data[id].filename || this.data[id].name) == filename && !this.data[id].no_unlink) {
this.ds_file.unlink([id]);
}
}
if(this.node.attrs.blockui > 0) { // block UI or not
framework.blockUI();
}
// TODO : unactivate send on wizard and form
// submit file
this.$('form.o_form_binary_form').submit();
this.$(".oe_fileupload").hide();
// add file on data result
this.data[0] = {
id: 0,
name: filename,
filename: filename,
url: '',
upload: true,
};
}
},
'click .oe_delete': function(e) {
e.preventDefault();
e.stopPropagation();
var file_id = $(e.currentTarget).data("id");
if(file_id) {
var files = _.without(this.get('value'), file_id);
if(!this.data[file_id].no_unlink) {
this.ds_file.unlink([file_id]);
}
this.set({'value': files});
}
},
},
init: function(field_manager, node) {
this._super(field_manager, node);
this.field_manager = field_manager;
this.node = node;
this._super.apply(this, arguments);
this.session = session;
if(this.field.type != "many2many" || this.field.relation != 'ir.attachment') {
throw _.str.sprintf(_t("The type of the field '%s' must be a many2many field with a relation to 'ir.attachment' model."), this.field.string);
@@ -1541,40 +1573,39 @@ var FieldMany2ManyBinaryMultiFiles = AbstractManyField.extend(common.Reinitializ
this.fileupload_id = _.uniqueId('oe_fileupload_temp');
$(window).on(this.fileupload_id, _.bind(this.on_file_loaded, this));
},
initialize_content: function() {
this.$el.on('change', 'input.o_form_input_file', this.on_file_change );
},
get_file_url: function (attachment) {
get_file_url: function(attachment) {
return '/web/content/' + attachment.id + '?download=true';
},
read_name_values : function () {
read_name_values : function() {
var self = this;
// don't reset know values
var ids = this.get('value');
var _value = _.filter(ids, function (id) { return typeof self.data[id] == 'undefined'; } );
var _value = _.filter(ids, function(id) { return self.data[id] === undefined; });
// send request for get_name
if (_value.length) {
return this.ds_file.call('read', [_value, ['id', 'name', 'datas_fname', 'mimetype']]).then(function (datas) {
_.each(datas, function (data) {
data.no_unlink = true;
data.url = self.get_file_url(data);
self.data[data.id] = data;
});
return ids;
});
if(_value.length) {
return this.ds_file.call('read', [_value, ['id', 'name', 'datas_fname', 'mimetype']])
.then(process_data);
} else {
return $.when(ids);
}
function process_data(datas) {
_.each(datas, function(data) {
data.no_unlink = true;
data.url = self.get_file_url(data);
self.data[data.id] = data;
});
return ids;
}
},
render_value: function () {
render_value: function() {
var self = this;
this.read_name_values().then(function (ids) {
var render = $(QWeb.render('FieldBinaryFileUploader.files', {'widget': self, 'values': ids}));
render.on('click', '.oe_delete', _.bind(self.on_file_delete, self));
self.$('.oe_placeholder_files, .oe_attachments').replaceWith( render );
self.$('.oe_placeholder_files, .oe_attachments')
.replaceWith($(QWeb.render('FieldBinaryFileUploader.files', {'widget': self, 'values': ids})));
// reinit input type file
var $input = self.$('input.o_form_input_file');
var $input = self.$('.o_form_input_file');
$input.after($input.clone(true)).remove();
self.$(".oe_fileupload").show();
@@ -1587,77 +1618,26 @@ var FieldMany2ManyBinaryMultiFiles = AbstractManyField.extend(common.Reinitializ
});
});
},
on_file_change: function (event) {
event.stopPropagation();
var self = this;
var $target = $(event.target);
if ($target.val() !== '') {
var filename = $target.val().replace(/.*[\\\/]/,'');
// don't uplode more of one file in same time
if (self.data[0] && self.data[0].upload ) {
return false;
}
for (var id in this.get('value')) {
// if the files exits, delete the file before upload (if it's a new file)
if (self.data[id] && (self.data[id].filename || self.data[id].name) == filename && !self.data[id].no_unlink ) {
self.ds_file.unlink([id]);
}
}
// block UI or not
if(this.node.attrs.blockui>0) {
framework.blockUI();
}
// TODO : unactivate send on wizard and form
// submit file
this.$('form.o_form_binary_form').submit();
this.$(".oe_fileupload").hide();
// add file on data result
this.data[0] = {
'id': 0,
'name': filename,
'filename': filename,
'url': '',
'upload': true
};
}
},
on_file_loaded: function (event, result) {
var files = this.get('value');
// unblock UI
if(this.node.attrs.blockui>0) {
on_file_loaded: function(e, result) {
if(this.node.attrs.blockui > 0) { // unblock UI
framework.unblockUI();
}
if (result.error || !result.id ) {
this.do_warn( _t('Uploading Error'), result.error);
if(result.error || !result.id) {
this.do_warn(_t('Uploading Error'), result.error);
delete this.data[0];
} else {
if (this.data[0] && this.data[0].filename == result.filename && this.data[0].upload) {
if(this.data[0] && this.data[0].filename === result.filename && this.data[0].upload) {
delete this.data[0];
}
result.url = this.get_file_url(result);
this.data[result.id] = result;
var values = _.clone(this.get('value'));
values.push(result.id);
this.set({'value': values});
this.set({value: values});
}
this.render_value();
},
on_file_delete: function (event) {
event.stopPropagation();
var file_id=$(event.target).closest('.oe_delete').data("id");
if (file_id) {
var files = _.filter(this.get('value'), function (id) {return id != file_id;});
if(!this.data[file_id].no_unlink) {
this.ds_file.unlink([file_id]);
}
this.set({'value': files});
}
},
});
/*
@@ -1672,7 +1652,7 @@ var FieldMany2ManyCheckBoxes = AbstractManyField.extend(common.ReinitializeField
this.set("records", []);
this.field_manager.on("view_content_has_changed", this, function() {
var domain = new data.CompoundDomain(this.build_domain()).eval();
if (! _.isEqual(domain, this.get("domain"))) {
if (!_.isEqual(domain, this.get("domain"))) {
this.set("domain", domain);
}
});
@@ -1694,18 +1674,22 @@ var FieldMany2ManyCheckBoxes = AbstractManyField.extend(common.ReinitializeField
});
},
render_value: function() {
this.$().html(QWeb.render("FieldMany2ManyCheckBoxes", {widget: this}));
var inputs = this.$("input");
inputs.change(_.bind(this.from_dom, this));
if (this.get("effective_readonly"))
inputs.attr("disabled", "true");
this.$el.html(QWeb.render("FieldMany2ManyCheckBoxes", {widget: this}));
var $inputs = this.$("input");
$inputs.change(_.bind(this.from_dom, this));
if (this.get("effective_readonly")) {
$inputs.attr("disabled", "true");
}
},
from_dom: function() {
var new_value = this.$("input:checked").map(function() { return +$(this).data("record-id"); }).get();
if (! _.isEqual(new_value, this.get("value"))) {
if (!_.isEqual(new_value, this.get("value"))) {
this.internal_set_value(new_value);
}
}
},
is_false: function() {
return false;
},
});
core.form_widget_registry
@@ -1718,9 +1702,13 @@ core.form_widget_registry
.add('many2many_binary', FieldMany2ManyBinaryMultiFiles)
.add('many2many_checkboxes', FieldMany2ManyCheckBoxes);
core.one2many_view_registry
.add('list', One2ManyListView);
return {
FieldMany2ManyTags: FieldMany2ManyTags,
AbstractManyField: AbstractManyField,
FieldMany2One: FieldMany2One,
};
});
@@ -0,0 +1,101 @@
odoo.define('web.form_upgrade_widgets', function (require) {
"use strict";
var core = require('web.core');
var Dialog = require('web.Dialog');
var form_widgets = require('web.form_widgets');
var framework = require('web.framework');
var Model = require('web.DataModel');
var _t = core._t;
var QWeb = core.qweb;
/**
* This widget is intended to be used in config settings.
* When checked, an upgrade popup is showed to the user.
*/
var AbstractFieldUpgrade = {
events: {
'click input': 'on_click_input',
},
start: function() {
this._super.apply(this, arguments);
this.get_enterprise_label().after($("<span>", {
text: "Enterprise",
'class': "label label-primary oe_inline"
}));
},
open_dialog: function() {
var message = $(QWeb.render('EnterpriseUpgrade'));
var buttons = [
{
text: _t("Upgrade now"),
classes: 'btn-primary',
close: true,
click: this.confirm_upgrade,
},
{
text: _t("Cancel"),
close: true,
},
];
return new Dialog(this, {
size: 'medium',
buttons: buttons,
$content: $('<div>', {
html: message,
}),
title: _t("Odoo Enterprise"),
}).open();
},
confirm_upgrade: function() {
new Model("res.users").call("search_count", [[["share", "=", false]]]).then(function(data) {
framework.redirect("https://www.odoo.com/odoo-enterprise/upgrade?num_users=" + data);
});
},
get_enterprise_label: function() {},
on_click_input: function() {},
};
var UpgradeBoolean = form_widgets.FieldBoolean.extend(AbstractFieldUpgrade, {
template: "FieldUpgradeBoolean",
get_enterprise_label: function() {
return this.$label;
},
on_click_input: function() {
if(this.$checkbox.prop("checked")) {
this.open_dialog().on('closed', this, function() {
this.$checkbox.prop("checked", false);
});
}
},
});
var UpgradeRadio = form_widgets.FieldRadio.extend(AbstractFieldUpgrade, {
get_enterprise_label: function() {
return this.$('label').last();
},
on_click_input: function(event) {
if($(event.target).val() === 1) {
this.open_dialog().on('closed', this, function() {
this.$('input').first().prop("checked", true);
});
}
},
});
core.form_widget_registry
.add('upgrade_boolean', UpgradeBoolean)
.add('upgrade_radio', UpgradeRadio);
});

Some files were not shown because too many files have changed in this diff Show More