[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:
committed by
Géry Debongnie
parent
e1bfd3fbda
commit
977db823c7
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"directory": "static/lib/"
|
||||
}
|
||||
@@ -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']);
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
+750
-570
File diff suppressed because it is too large
Load Diff
+721
-547
File diff suppressed because it is too large
Load Diff
+720
-546
File diff suppressed because it is too large
Load Diff
+747
-569
File diff suppressed because it is too large
Load Diff
+712
-538
File diff suppressed because it is too large
Load Diff
+748
-570
File diff suppressed because it is too large
Load Diff
+745
-567
File diff suppressed because it is too large
Load Diff
+746
-568
File diff suppressed because it is too large
Load Diff
+750
-572
File diff suppressed because it is too large
Load Diff
+732
-554
File diff suppressed because it is too large
Load Diff
+746
-568
File diff suppressed because it is too large
Load Diff
+737
-559
File diff suppressed because it is too large
Load Diff
+749
-571
File diff suppressed because it is too large
Load Diff
+710
-536
File diff suppressed because it is too large
Load Diff
+741
-559
File diff suppressed because it is too large
Load Diff
+720
-546
File diff suppressed because it is too large
Load Diff
+740
-559
File diff suppressed because it is too large
Load Diff
+735
-557
File diff suppressed because it is too large
Load Diff
+734
-556
File diff suppressed because it is too large
Load Diff
+709
-535
File diff suppressed because it is too large
Load Diff
+719
-545
File diff suppressed because it is too large
Load Diff
+710
-536
File diff suppressed because it is too large
Load Diff
+728
-551
File diff suppressed because it is too large
Load Diff
+749
-571
File diff suppressed because it is too large
Load Diff
+743
-561
File diff suppressed because it is too large
Load Diff
+720
-546
File diff suppressed because it is too large
Load Diff
+711
-537
File diff suppressed because it is too large
Load Diff
+727
-553
File diff suppressed because it is too large
Load Diff
+709
-535
File diff suppressed because it is too large
Load Diff
+750
-572
File diff suppressed because it is too large
Load Diff
+742
-558
File diff suppressed because it is too large
Load Diff
+750
-576
File diff suppressed because it is too large
Load Diff
+740
-560
File diff suppressed because it is too large
Load Diff
+736
-558
File diff suppressed because it is too large
Load Diff
+726
-552
File diff suppressed because it is too large
Load Diff
+723
-549
File diff suppressed because it is too large
Load Diff
+749
-571
File diff suppressed because it is too large
Load Diff
+872
-504
File diff suppressed because it is too large
Load Diff
+735
-561
File diff suppressed because it is too large
Load Diff
+710
-536
File diff suppressed because it is too large
Load Diff
+749
-571
File diff suppressed because it is too large
Load Diff
+749
-571
File diff suppressed because it is too large
Load Diff
+747
-569
File diff suppressed because it is too large
Load Diff
+739
-557
File diff suppressed because it is too large
Load Diff
+725
-551
File diff suppressed because it is too large
Load Diff
+751
-573
File diff suppressed because it is too large
Load Diff
+744
-566
File diff suppressed because it is too large
Load Diff
+747
-565
File diff suppressed because it is too large
Load Diff
+749
-571
File diff suppressed because it is too large
Load Diff
+737
-559
File diff suppressed because it is too large
Load Diff
+740
-558
File diff suppressed because it is too large
Load Diff
+749
-571
File diff suppressed because it is too large
Load Diff
+709
-535
File diff suppressed because it is too large
Load Diff
+720
-546
File diff suppressed because it is too large
Load Diff
+735
-561
File diff suppressed because it is too large
Load Diff
+747
-567
File diff suppressed because it is too large
Load Diff
+737
-559
File diff suppressed because it is too large
Load Diff
+739
-557
File diff suppressed because it is too large
Load Diff
+724
-550
File diff suppressed because it is too large
Load Diff
+700
-510
File diff suppressed because it is too large
Load Diff
+751
-567
File diff suppressed because it is too large
Load Diff
+728
-550
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -1,5 +0,0 @@
|
||||
repo: 076b192d0d8ab2b92d1dbcfa3da055382f30eaea
|
||||
node: 7ee0bc7b4e9138f485cdc9ec791961d8ef452f17
|
||||
branch: default
|
||||
latesttag: 0.7
|
||||
latesttagdistance: 11
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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%;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
});
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
});
|
||||
|
||||
@@ -199,7 +199,6 @@
|
||||
process_jobs: function (jobs, services) {
|
||||
var job;
|
||||
var require;
|
||||
var time;
|
||||
|
||||
function process_job (job) {
|
||||
var require = make_require(job);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -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 = [];
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
},
|
||||
/**
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user