[MERGE] trunk
bzr revid: al@openerp.com-20120725082646-ff6ljt0pquvm0dup
This commit is contained in:
commit
3235ed8a18
File diff suppressed because it is too large
Load Diff
|
@ -1,4 +1,4 @@
|
|||
@charset "UTF-8";
|
||||
@charset "utf-8";
|
||||
@font-face {
|
||||
font-family: "mnmliconsRegular";
|
||||
src: url("/web/static/src/font/mnmliconsv21-webfont.eot") format("eot");
|
||||
|
@ -267,6 +267,45 @@
|
|||
color: black;
|
||||
text-decoration: none;
|
||||
}
|
||||
.openerp.ui-dialog .oe_about {
|
||||
background-color: white;
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAGCAYAAADgzO9IAAAAKUlEQVQIHWO8e/fufwYsgAUkJigoiCIF5DMyoYggcUiXgNnBiGQKmAkARpcEQeriln4AAAAASUVORK5CYII=);
|
||||
-moz-border-radius: 0 0 2px 2px;
|
||||
-webkit-border-radius: 0 0 2px 2px;
|
||||
border-radius: 0 0 2px 2px;
|
||||
}
|
||||
.openerp.ui-dialog .oe_about a {
|
||||
color: #8a89ba;
|
||||
}
|
||||
.openerp.ui-dialog .oe_about a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.openerp.ui-dialog .oe_about .oe_logo {
|
||||
margin-left: -6px;
|
||||
}
|
||||
.openerp.ui-dialog .oe_about .oe_bottom {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
text-shadow: 0 1px 1px #999999;
|
||||
background-color: #b41616;
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#b41616), to(#600606));
|
||||
background-image: -webkit-linear-gradient(top, #b41616, #600606);
|
||||
background-image: -moz-linear-gradient(top, #b41616, #600606);
|
||||
background-image: -ms-linear-gradient(top, #b41616, #600606);
|
||||
background-image: -o-linear-gradient(top, #b41616, #600606);
|
||||
background-image: linear-gradient(to bottom, #b41616, #600606);
|
||||
color: #eeeeee;
|
||||
padding: 0 16px;
|
||||
-moz-border-radius: 0 0 2px 2px;
|
||||
-webkit-border-radius: 0 0 2px 2px;
|
||||
border-radius: 0 0 2px 2px;
|
||||
}
|
||||
.openerp.ui-dialog .oe_about .oe_bottom a {
|
||||
color: #eeeeee;
|
||||
}
|
||||
.openerp.ui-dialog.oe_act_window .ui-dialog-content {
|
||||
padding: 0px;
|
||||
}
|
||||
|
@ -584,7 +623,7 @@
|
|||
z-index: 1;
|
||||
border: 1px solid #afafb6;
|
||||
background: white;
|
||||
padding: 6px 0;
|
||||
padding: 4px 0;
|
||||
min-width: 140px;
|
||||
text-align: left;
|
||||
-moz-border-radius: 3px;
|
||||
|
@ -599,6 +638,7 @@
|
|||
float: none;
|
||||
display: block;
|
||||
position: relative;
|
||||
padding: 2px 8px;
|
||||
}
|
||||
.openerp .oe_dropdown_menu > li:hover {
|
||||
background-color: #f0f0fa;
|
||||
|
@ -615,7 +655,6 @@
|
|||
.openerp .oe_dropdown_menu > li > a {
|
||||
white-space: nowrap;
|
||||
display: block;
|
||||
padding: 4px 15px;
|
||||
color: #4c4c4c;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
@ -711,7 +750,7 @@
|
|||
z-index: 1050;
|
||||
}
|
||||
.openerp .oe_login {
|
||||
background: url("/web/static/src/img/pattern.png") repeat;
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAGCAYAAADgzO9IAAAAKUlEQVQIHWO8e/fufwYsgAUkJigoiCIF5DMyoYggcUiXgNnBiGQKmAkARpcEQeriln4AAAAASUVORK5CYII=);
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
height: 100%;
|
||||
|
@ -945,11 +984,12 @@
|
|||
}
|
||||
.openerp .oe_topbar .oe_dropdown_menu li {
|
||||
float: none;
|
||||
padding: 3px 12px;
|
||||
}
|
||||
.openerp .oe_topbar .oe_dropdown_menu li a {
|
||||
color: #eeeeee;
|
||||
}
|
||||
.openerp .oe_topbar .oe_dropdown_menu li a:hover {
|
||||
.openerp .oe_topbar .oe_dropdown_menu li:hover {
|
||||
background-color: #292929;
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, from(#292929), to(#191919));
|
||||
background-image: -webkit-linear-gradient(top, #292929, #191919);
|
||||
|
@ -1408,20 +1448,31 @@
|
|||
filter: alpha(opacity=50);
|
||||
opacity: 0.5;
|
||||
}
|
||||
.openerp .oe_searchview .oe_searchview_search {
|
||||
font-size: 1px;
|
||||
letter-spacing: -1px;
|
||||
color: transparent;
|
||||
-moz-box-shadow: none;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
-moz-border-radius: 0;
|
||||
-webkit-border-radius: 0;
|
||||
border-radius: 0;
|
||||
position: absolute;
|
||||
left: 3px;
|
||||
top: 1px;
|
||||
padding: 0;
|
||||
border: none;
|
||||
background: transparent;
|
||||
}
|
||||
.openerp .oe_searchview .oe_searchview_search:before {
|
||||
font: 21px "mnmliconsRegular";
|
||||
content: "r";
|
||||
color: #a3a3a3;
|
||||
}
|
||||
.openerp .oe_searchview .oe_searchview_facets {
|
||||
min-height: 22px;
|
||||
}
|
||||
.openerp .oe_searchview .oe_searchview_facets:before {
|
||||
color: #cccccc;
|
||||
font-family: "mnmliconsRegular";
|
||||
content: "r";
|
||||
font-size: 130%;
|
||||
display: inline;
|
||||
position: relative;
|
||||
left: 6px;
|
||||
top: 2px;
|
||||
color: #a3a3a3;
|
||||
padding-right: 4px;
|
||||
margin-left: 15px;
|
||||
}
|
||||
.openerp .oe_searchview .oe_searchview_facets * {
|
||||
vertical-align: top;
|
||||
|
@ -1437,7 +1488,7 @@
|
|||
outline: none;
|
||||
}
|
||||
.openerp .oe_searchview .oe_searchview_facets .oe_searchview_input {
|
||||
padding: 0 3px;
|
||||
padding: 0 0 0 6px;
|
||||
}
|
||||
.openerp .oe_searchview .oe_searchview_facets .oe_searchview_facet {
|
||||
position: relative;
|
||||
|
@ -1600,14 +1651,14 @@
|
|||
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_section li:hover {
|
||||
background-color: #f0f0fa;
|
||||
}
|
||||
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_section form {
|
||||
.openerp .oe_searchview .oe_searchview_drawer form {
|
||||
margin-left: 12px;
|
||||
}
|
||||
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_section form p {
|
||||
.openerp .oe_searchview .oe_searchview_drawer form p {
|
||||
margin: 4px 0;
|
||||
line-height: 18px;
|
||||
}
|
||||
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_section form button {
|
||||
.openerp .oe_searchview .oe_searchview_drawer form button {
|
||||
margin: 0 0 8px 0;
|
||||
}
|
||||
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_custom {
|
||||
|
@ -2180,9 +2231,33 @@
|
|||
height: auto;
|
||||
line-height: 16px;
|
||||
}
|
||||
.openerp .oe_form_field_one2many .oe_list_buttons.oe_editing .oe_list_save, .openerp .oe_form_field_many2many .oe_list_buttons.oe_editing .oe_list_save {
|
||||
visibility: hidden;
|
||||
}
|
||||
.openerp .oe_form .oe_form_field_many2many > .oe_list .oe_list_pager_single_page {
|
||||
display: none;
|
||||
}
|
||||
.openerp .oe_list_buttons .oe_list_save, .openerp .oe_list_buttons .oe_list_discard {
|
||||
display: none;
|
||||
}
|
||||
.openerp .oe_list_buttons.oe_editing .oe_list_add, .openerp .oe_list_buttons.oe_editing .oe_list_button_import {
|
||||
display: none;
|
||||
}
|
||||
.openerp .oe_list_buttons.oe_editing .oe_list_save {
|
||||
display: inline-block;
|
||||
}
|
||||
.openerp .oe_list_buttons.oe_editing .oe_list_discard {
|
||||
display: inline;
|
||||
}
|
||||
.openerp .oe_list {
|
||||
position: relative;
|
||||
}
|
||||
.openerp .oe_list .oe_form .oe_form_field {
|
||||
width: auto;
|
||||
position: absolute;
|
||||
margin: 0 !important;
|
||||
padding: 0;
|
||||
}
|
||||
.openerp .oe_list_content {
|
||||
width: 100%;
|
||||
}
|
||||
|
@ -2240,9 +2315,7 @@
|
|||
}
|
||||
.openerp .oe_list_content > tbody > tr > td.oe_list_field_cell {
|
||||
padding: 3px 6px;
|
||||
}
|
||||
.openerp .oe_list_content > tbody > tr > td.oe_list_field_cell progress {
|
||||
width: 100%;
|
||||
white-space: pre-line;
|
||||
}
|
||||
.openerp .oe_list_content > tbody > tr > td > button, .openerp .oe_list_content > tbody > tr > th > button {
|
||||
border: none;
|
||||
|
@ -2290,9 +2363,6 @@
|
|||
.openerp .oe_list_content .numeric input {
|
||||
text-align: right;
|
||||
}
|
||||
.openerp .oe_list_content .oe_list_edit_row_save:before {
|
||||
content: "S";
|
||||
}
|
||||
.openerp .oe_trad_field.touched {
|
||||
border: 1px solid green !important;
|
||||
}
|
||||
|
|
|
@ -92,9 +92,8 @@ $sheet-max-width: 860px
|
|||
letter-spacing: -1px
|
||||
color: transparent
|
||||
&:before
|
||||
font-family: "mnmliconsRegular"
|
||||
font: 21px "mnmliconsRegular"
|
||||
content: $icon-name
|
||||
font-size: 20px
|
||||
color: $color
|
||||
|
||||
// }}}
|
||||
|
@ -256,6 +255,29 @@ $sheet-max-width: 860px
|
|||
&:hover
|
||||
color: black
|
||||
text-decoration: none
|
||||
.oe_about
|
||||
background-color: white
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAGCAYAAADgzO9IAAAAKUlEQVQIHWO8e/fufwYsgAUkJigoiCIF5DMyoYggcUiXgNnBiGQKmAkARpcEQeriln4AAAAASUVORK5CYII=)
|
||||
@include radius(0 0 2px 2px)
|
||||
a
|
||||
color: #8A89BA
|
||||
&:hover
|
||||
text-decoration: underline
|
||||
.oe_logo
|
||||
margin-left: -6px
|
||||
.oe_bottom
|
||||
position: absolute
|
||||
top: 50%
|
||||
left: 0
|
||||
right: 0
|
||||
bottom: 0
|
||||
text-shadow: 0 1px 1px #999999
|
||||
@include vertical-gradient(#b41616, #600606)
|
||||
color: #eee
|
||||
padding: 0 16px
|
||||
@include radius(0 0 2px 2px)
|
||||
a
|
||||
color: #eee
|
||||
|
||||
&.ui-dialog.oe_act_window
|
||||
.ui-dialog-content
|
||||
|
@ -470,7 +492,7 @@ $sheet-max-width: 860px
|
|||
z-index: 1
|
||||
border: 1px solid #afafb6
|
||||
background: white
|
||||
padding: 6px 0
|
||||
padding: 4px 0
|
||||
min-width: 140px
|
||||
text-align: left
|
||||
@include radius(3px)
|
||||
|
@ -483,10 +505,10 @@ $sheet-max-width: 860px
|
|||
float: none
|
||||
display: block
|
||||
position: relative
|
||||
padding: 2px 8px
|
||||
> a
|
||||
white-space: nowrap
|
||||
display: block
|
||||
padding: 4px 15px
|
||||
color: #4c4c4c
|
||||
text-decoration: none
|
||||
&:hover
|
||||
|
@ -567,7 +589,7 @@ $sheet-max-width: 860px
|
|||
// }}}
|
||||
// Login {{{
|
||||
.oe_login
|
||||
background: url("/web/static/src/img/pattern.png") repeat
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAGCAYAAADgzO9IAAAAKUlEQVQIHWO8e/fufwYsgAUkJigoiCIF5DMyoYggcUiXgNnBiGQKmAkARpcEQeriln4AAAAASUVORK5CYII=)
|
||||
text-align: center
|
||||
font-size: 14px
|
||||
height: 100%
|
||||
|
@ -735,11 +757,12 @@ $sheet-max-width: 860px
|
|||
@include background-clip()
|
||||
li
|
||||
float: none
|
||||
padding: 3px 12px
|
||||
a
|
||||
color: #eee
|
||||
&:hover
|
||||
@include vertical-gradient(#292929, #191919)
|
||||
@include box-shadow(none)
|
||||
&:hover
|
||||
@include vertical-gradient(#292929, #191919)
|
||||
@include box-shadow(none)
|
||||
|
||||
// }}}
|
||||
// Webclient.leftbar {{{
|
||||
|
@ -1102,19 +1125,20 @@ $sheet-max-width: 860px
|
|||
border-right: 5px solid transparent
|
||||
@include opacity()
|
||||
|
||||
.oe_searchview_search
|
||||
@include text-to-icon("r", #a3a3a3)
|
||||
@include box-shadow(none)
|
||||
@include radius(0)
|
||||
position: absolute
|
||||
left: 3px
|
||||
top: 1px
|
||||
padding: 0
|
||||
border: none
|
||||
background: transparent
|
||||
|
||||
.oe_searchview_facets
|
||||
min-height: 22px
|
||||
&:before
|
||||
color: #ccc
|
||||
font-family: "mnmliconsRegular"
|
||||
content: "r"
|
||||
font-size: 130%
|
||||
display: inline
|
||||
position: relative
|
||||
left: 6px
|
||||
top: 2px
|
||||
color: #a3a3a3
|
||||
padding-right: 4px
|
||||
margin-left: 15px
|
||||
*
|
||||
vertical-align: top
|
||||
display: inline-block
|
||||
|
@ -1126,7 +1150,7 @@ $sheet-max-width: 860px
|
|||
&:focus
|
||||
outline: none
|
||||
.oe_searchview_input
|
||||
padding: 0 3px
|
||||
padding: 0 0 0 6px
|
||||
.oe_searchview_facet
|
||||
position: relative
|
||||
cursor: pointer
|
||||
|
@ -1248,13 +1272,13 @@ $sheet-max-width: 860px
|
|||
// after oe_selected so background color is not overridden
|
||||
&:hover
|
||||
background-color: $hover-background
|
||||
form
|
||||
margin-left: 12px
|
||||
p
|
||||
margin: 4px 0
|
||||
line-height: 18px
|
||||
button
|
||||
margin: 0 0 8px 0
|
||||
form
|
||||
margin-left: 12px
|
||||
p
|
||||
margin: 4px 0
|
||||
line-height: 18px
|
||||
button
|
||||
margin: 0 0 8px 0
|
||||
.oe_searchview_custom
|
||||
padding: 0 8px 8px 8px
|
||||
form
|
||||
|
@ -1721,6 +1745,9 @@ $sheet-max-width: 860px
|
|||
li
|
||||
height: auto
|
||||
line-height: 16px
|
||||
.oe_list_buttons.oe_editing .oe_list_save
|
||||
// keep "save row" button hidden in o2m
|
||||
visibility: hidden
|
||||
// }}}
|
||||
// FormView.many2many {{{
|
||||
.oe_form .oe_form_field_many2many > .oe_list
|
||||
|
@ -1728,6 +1755,25 @@ $sheet-max-width: 860px
|
|||
display: none
|
||||
// }}}
|
||||
// ListView {{{
|
||||
.oe_list_buttons
|
||||
.oe_list_save, .oe_list_discard
|
||||
display: none
|
||||
&.oe_editing
|
||||
.oe_list_add, .oe_list_button_import
|
||||
display: none
|
||||
.oe_list_save
|
||||
display: inline-block
|
||||
.oe_list_discard
|
||||
display: inline
|
||||
|
||||
.oe_list
|
||||
position: relative
|
||||
.oe_form .oe_form_field
|
||||
width: auto
|
||||
position: absolute
|
||||
margin: 0 !important // dammit
|
||||
padding: 0
|
||||
|
||||
.oe_list_content
|
||||
width: 100%
|
||||
td:first-child, th:first-child
|
||||
|
@ -1771,8 +1817,7 @@ $sheet-max-width: 860px
|
|||
border-top: 1px solid #ddd
|
||||
> td.oe_list_field_cell
|
||||
padding: 3px 6px
|
||||
progress
|
||||
width: 100%
|
||||
white-space: pre-line
|
||||
> td, > th
|
||||
> button
|
||||
border: none
|
||||
|
@ -1800,8 +1845,6 @@ $sheet-max-width: 860px
|
|||
width: 82px
|
||||
input
|
||||
text-align: right
|
||||
.oe_list_edit_row_save:before
|
||||
content: "S"
|
||||
// }}}
|
||||
// Translation {{{
|
||||
.oe_trad_field.touched
|
||||
|
@ -1910,6 +1953,7 @@ $sheet-max-width: 860px
|
|||
background-attachment: fixed
|
||||
>*
|
||||
opacity: 0.70
|
||||
|
||||
// }}}
|
||||
|
||||
div.ui-widget-overlay
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 98 B |
Binary file not shown.
Before Width: | Height: | Size: 673 B After Width: | Height: | Size: 7.4 KiB |
Binary file not shown.
Before Width: | Height: | Size: 7.0 KiB |
|
@ -135,8 +135,12 @@ instance.web.Dialog = instance.web.Widget.extend({
|
|||
this.$element.dialog('close');
|
||||
},
|
||||
on_close: function() {
|
||||
if (this.__tmp_dialog_destroying)
|
||||
return;
|
||||
if (this.dialog_options.destroy_on_close) {
|
||||
this.__tmp_dialog_closing = true;
|
||||
this.destroy();
|
||||
this.__tmp_dialog_closing = undefined;
|
||||
}
|
||||
},
|
||||
on_resized: function() {
|
||||
|
@ -145,6 +149,11 @@ instance.web.Dialog = instance.web.Widget.extend({
|
|||
_.each(this.getChildren(), function(el) {
|
||||
el.destroy();
|
||||
});
|
||||
if (! this.__tmp_dialog_closing) {
|
||||
this.__tmp_dialog_destroying = true;
|
||||
this.close();
|
||||
this.__tmp_dialog_destroying = undefined;
|
||||
}
|
||||
if (! this.isDestroyed()) {
|
||||
this.$element.dialog('destroy');
|
||||
}
|
||||
|
@ -248,13 +257,17 @@ instance.web.Loading = instance.web.Widget.extend({
|
|||
// Block UI after 3s
|
||||
this.long_running_timer = setTimeout(function () {
|
||||
self.blocked_ui = true;
|
||||
$.blockUI();
|
||||
instance.web.blockUI();
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
this.count += increment;
|
||||
if (this.count > 0) {
|
||||
this.$element.text(_.str.sprintf( _t("Loading (%d)"), this.count));
|
||||
if (instance.connection.debug) {
|
||||
this.$element.text(_.str.sprintf( _t("Loading (%d)"), this.count));
|
||||
} else {
|
||||
this.$element.text(_t("Loading"));
|
||||
}
|
||||
this.$element.show();
|
||||
this.getParent().$element.addClass('oe_wait');
|
||||
} else {
|
||||
|
@ -263,7 +276,7 @@ instance.web.Loading = instance.web.Widget.extend({
|
|||
// Don't unblock if blocked by somebody else
|
||||
if (self.blocked_ui) {
|
||||
this.blocked_ui = false;
|
||||
$.unblockUI();
|
||||
instance.web.unblockUI();
|
||||
}
|
||||
this.$element.fadeOut();
|
||||
this.getParent().$element.removeClass('oe_wait');
|
||||
|
@ -274,7 +287,7 @@ instance.web.Loading = instance.web.Widget.extend({
|
|||
instance.web.DatabaseManager = instance.web.Widget.extend({
|
||||
init: function(parent) {
|
||||
this._super(parent);
|
||||
this.unblockUIFunction = $.unblockUI;
|
||||
this.unblockUIFunction = instance.web.unblockUI;
|
||||
$.validator.addMethod('matches', function (s, _, re) {
|
||||
return new RegExp(re).test(s);
|
||||
}, _t("Invalid database name"));
|
||||
|
@ -341,16 +354,16 @@ instance.web.DatabaseManager = instance.web.Widget.extend({
|
|||
* from unblocking the UI
|
||||
*/
|
||||
blockUI: function () {
|
||||
$.blockUI();
|
||||
$.unblockUI = function () {};
|
||||
instance.web.blockUI();
|
||||
instance.web.unblockUI = function () {};
|
||||
},
|
||||
/**
|
||||
* Reinstates $.unblockUI so third parties can play with blockUI, and
|
||||
* unblocks the UI
|
||||
*/
|
||||
unblockUI: function () {
|
||||
$.unblockUI = this.unblockUIFunction;
|
||||
$.unblockUI();
|
||||
instance.web.unblockUI = this.unblockUIFunction;
|
||||
instance.web.unblockUI();
|
||||
},
|
||||
/**
|
||||
* Displays an error dialog resulting from the various RPC communications
|
||||
|
@ -851,7 +864,7 @@ instance.web.UserMenu = instance.web.Widget.extend({
|
|||
window.location.href, 'debug');
|
||||
});
|
||||
instance.web.dialog($help, {autoOpen: true,
|
||||
modal: true, width: 960, title: _t("About")});
|
||||
modal: true, width: 580, height: 290, resizable: false, title: _t("About")});
|
||||
});
|
||||
},
|
||||
});
|
||||
|
@ -890,7 +903,7 @@ instance.web.Client = instance.web.Widget.extend({
|
|||
var doc_width = $(document).width();
|
||||
var offset = $menu.offset();
|
||||
var menu_width = $menu.width();
|
||||
var x = doc_width - offset.left - menu_width - 15;
|
||||
var x = doc_width - offset.left - menu_width - 2;
|
||||
if (x < 0) {
|
||||
$menu.offset({ left: offset.left + x }).width(menu_width);
|
||||
}
|
||||
|
@ -957,10 +970,12 @@ instance.web.WebClient = instance.web.Client.extend({
|
|||
},
|
||||
show_login: function() {
|
||||
var self = this;
|
||||
self.$('.oe_topbar').hide();
|
||||
self.login.appendTo(self.$element);
|
||||
},
|
||||
show_application: function() {
|
||||
var self = this;
|
||||
self.$('.oe_topbar').show();
|
||||
self.login.$element.hide();
|
||||
self.menu = new instance.web.Menu(self);
|
||||
self.menu.replace(this.$element.find('.oe_menu_placeholder'));
|
||||
|
|
|
@ -550,7 +550,15 @@ $.async_when = function() {
|
|||
/** Setup blockui */
|
||||
if ($.blockUI) {
|
||||
$.blockUI.defaults.baseZ = 1100;
|
||||
$.blockUI.defaults.message = '<img src="/web/static/src/img/throbber2.gif">';
|
||||
$.blockUI.defaults.message = '<img src="/web/static/src/img/throbber.gif">';
|
||||
$.blockUI.defaults.css.border = '0';
|
||||
$.blockUI.defaults.css["background-color"] = '';
|
||||
}
|
||||
instance.web.blockUI = function() {
|
||||
$.blockUI.apply($, arguments);
|
||||
}
|
||||
instance.web.unblockUI = function() {
|
||||
return $.unblockUI.apply($, arguments);
|
||||
}
|
||||
|
||||
/** Setup default session */
|
||||
|
|
|
@ -376,7 +376,7 @@ instance.web.DataExport = instance.web.Dialog.extend({
|
|||
|
||||
exported_fields.unshift({name: 'id', label: 'External ID'});
|
||||
var export_format = this.$element.find("#export_format").val();
|
||||
$.blockUI();
|
||||
instance.web.blockUI();
|
||||
this.session.get_file({
|
||||
url: '/web/export/' + export_format,
|
||||
data: {data: JSON.stringify({
|
||||
|
@ -387,7 +387,7 @@ instance.web.DataExport = instance.web.Dialog.extend({
|
|||
import_compat: Boolean(
|
||||
this.$element.find("#import_compat").val())
|
||||
})},
|
||||
complete: $.unblockUI
|
||||
complete: instance.web.unblockUI
|
||||
});
|
||||
},
|
||||
close: function() {
|
||||
|
|
|
@ -330,6 +330,12 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
|
|||
});
|
||||
}
|
||||
|
||||
// Launch a search on clicking the oe_searchview_search button
|
||||
this.$element.on('click', 'button.oe_searchview_search', function (e) {
|
||||
e.stopImmediatePropagation();
|
||||
self.do_search();
|
||||
});
|
||||
|
||||
this.$element.on('keydown',
|
||||
'.oe_searchview_input, .oe_searchview_facet', function (e) {
|
||||
switch(e.which) {
|
||||
|
@ -1475,7 +1481,17 @@ instance.web.search.ManyToOneField = instance.web.search.CharField.extend({
|
|||
facet_for: function (value) {
|
||||
var self = this;
|
||||
if (value instanceof Array) {
|
||||
return $.when(facet_from(this, value));
|
||||
if (value.length === 2 && _.isString(value[1])) {
|
||||
return $.when(facet_from(this, value));
|
||||
}
|
||||
if (value.length > 1) {
|
||||
// more than one search_default m2o id? Should we OR them?
|
||||
throw new Error(
|
||||
_("M2O search fields do not currently handle multiple default values"));
|
||||
}
|
||||
// there are many cases of {search_default_$m2ofield: [id]}, need
|
||||
// to handle this as if it were a single value.
|
||||
value = value[0];
|
||||
}
|
||||
return this.model.call('name_get', [value], {}).pipe(function (names) {
|
||||
if (_(names).isEmpty()) { return null; }
|
||||
|
@ -1669,6 +1685,7 @@ instance.web.search.AddToDashboard = instance.web.Widget.extend({
|
|||
return $.when(this.load_data(),this.data_loaded).pipe(this.proxy("render_data"));
|
||||
},
|
||||
load_data:function(){
|
||||
if (!instance.webclient) { return $.Deferred().reject(); }
|
||||
var self = this,dashboard_menu = instance.webclient.menu.data.data.children;
|
||||
var ir_model_data = new instance.web.Model('ir.model.data',{},[['name','=','menu_reporting_dashboard']]).query(['res_id']);
|
||||
var map_data = function(result){
|
||||
|
|
|
@ -239,6 +239,13 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {Object} [options]
|
||||
* @param {Boolean} [editable=false] whether the form should be switched to edition mode. A value of ``false`` will keep the current mode.
|
||||
* @param {Boolean} [reload=true] whether the form should reload its content on show, or use the currently loaded record
|
||||
* @return {$.Deferred}
|
||||
*/
|
||||
do_show: function (options) {
|
||||
var self = this;
|
||||
options = options || {};
|
||||
|
@ -253,23 +260,24 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
}
|
||||
this.$element.show().css('visibility', 'hidden');
|
||||
this.$element.add(this.$buttons).removeClass('oe_form_dirty');
|
||||
return this.has_been_loaded.pipe(function() {
|
||||
var result;
|
||||
if (self.dataset.index === null) {
|
||||
// null index means we should start a new record
|
||||
result = self.on_button_new();
|
||||
} else {
|
||||
result = self.dataset.read_index(_.keys(self.fields_view.fields), {
|
||||
context : { 'bin_size' : true }
|
||||
}).pipe(self.on_record_loaded);
|
||||
}
|
||||
result.pipe(function() {
|
||||
if (options.editable) {
|
||||
self.set({mode: "edit"});
|
||||
|
||||
var shown = this.has_been_loaded;
|
||||
if (options.reload !== false) {
|
||||
shown = shown.pipe(function() {
|
||||
if (self.dataset.index === null) {
|
||||
// null index means we should start a new record
|
||||
return self.on_button_new();
|
||||
}
|
||||
self.$element.css('visibility', 'visible');
|
||||
return self.dataset.read_index(_.keys(self.fields_view.fields), {
|
||||
context: { 'bin_size': true }
|
||||
}).pipe(self.on_record_loaded);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
return shown.pipe(function() {
|
||||
if (options.editable) {
|
||||
self.set({mode: "edit"});
|
||||
}
|
||||
self.$element.css('visibility', 'visible');
|
||||
});
|
||||
},
|
||||
do_hide: function () {
|
||||
|
@ -330,6 +338,20 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
self.$element.add(self.$buttons).removeClass('oe_form_dirty');
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Loads and sets up the default values for the model as the current
|
||||
* record
|
||||
*
|
||||
* @return {$.Deferred}
|
||||
*/
|
||||
load_defaults: function () {
|
||||
var keys = _.keys(this.fields_view.fields);
|
||||
if (keys.length) {
|
||||
return this.dataset.default_get(keys)
|
||||
.pipe(this.on_record_loaded);
|
||||
}
|
||||
return this.on_record_loaded({});
|
||||
},
|
||||
on_form_changed: function() {
|
||||
this.trigger("view_content_has_changed");
|
||||
},
|
||||
|
@ -600,22 +622,11 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
on_button_new: function() {
|
||||
var self = this;
|
||||
this.set({mode: "edit"});
|
||||
var def = $.Deferred();
|
||||
$.when(this.has_been_loaded).then(function() {
|
||||
return $.when(this.has_been_loaded).pipe(function() {
|
||||
if (self.can_be_discarded()) {
|
||||
var keys = _.keys(self.fields_view.fields);
|
||||
if (keys.length) {
|
||||
self.dataset.default_get(keys).pipe(self.on_record_loaded).then(function() {
|
||||
def.resolve();
|
||||
});
|
||||
} else {
|
||||
self.on_record_loaded({}).then(function() {
|
||||
def.resolve();
|
||||
});
|
||||
}
|
||||
return self.load_defaults();
|
||||
}
|
||||
});
|
||||
return def.promise();
|
||||
},
|
||||
on_button_edit: function() {
|
||||
return this.set({mode: "edit"});
|
||||
|
@ -674,6 +685,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
values = {},
|
||||
first_invalid_field = null;
|
||||
for (var f in self.fields) {
|
||||
if (!self.fields.hasOwnProperty(f)) { continue; }
|
||||
f = self.fields[f];
|
||||
if (!f.is_valid()) {
|
||||
form_invalid = true;
|
||||
|
@ -689,8 +701,9 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
}
|
||||
if (form_invalid) {
|
||||
self.set({'display_invalid_fields': true});
|
||||
for (var f in self.fields) {
|
||||
self.fields[f]._check_css_flags();
|
||||
for (var g in self.fields) {
|
||||
if (!self.fields.hasOwnProperty(g)) { continue; }
|
||||
self.fields[g]._check_css_flags();
|
||||
}
|
||||
first_invalid_field.focus();
|
||||
self.on_invalid();
|
||||
|
@ -722,14 +735,15 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
});});
|
||||
},
|
||||
on_invalid: function() {
|
||||
var msg = "<ul>";
|
||||
_.each(this.fields, function(f) {
|
||||
if (!f.is_valid()) {
|
||||
msg += "<li>" + f.string + "</li>";
|
||||
}
|
||||
});
|
||||
msg += "</ul>";
|
||||
this.do_warn("The following fields are invalid :", msg);
|
||||
var warnings = _(this.fields).chain()
|
||||
.filter(function (f) { return !f.is_valid(); })
|
||||
.map(function (f) {
|
||||
return _.str.sprintf('<li>%s</li>',
|
||||
_.escape(f.string));
|
||||
}).value();
|
||||
warnings.unshift('<ul>');
|
||||
warnings.push('</ul>');
|
||||
this.do_warn("The following fields are invalid :", warnings.join(''));
|
||||
},
|
||||
on_saved: function(r, success) {
|
||||
if (!r.result) {
|
||||
|
@ -807,10 +821,10 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
var ids = this.get_selected_ids();
|
||||
values["id"] = ids.length > 0 ? ids[0] : false;
|
||||
_.each(this.fields, function(value_, key) {
|
||||
if (_.include(blacklist, key))
|
||||
if (_.include(blacklist, key)) {
|
||||
return;
|
||||
var val = value_.get_value();
|
||||
values[key] = val;
|
||||
}
|
||||
values[key] = value_.get_value();
|
||||
});
|
||||
return values;
|
||||
},
|
||||
|
@ -945,6 +959,9 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|
|||
is_create_mode: function() {
|
||||
return !this.datarecord.id;
|
||||
},
|
||||
open_translate_dialog: function(field) {
|
||||
return this._super(field);
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
|
@ -988,7 +1005,6 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
|
|||
}
|
||||
});
|
||||
}
|
||||
selector = 'form[version!="7.0"] page,form[version!="7.0"]';
|
||||
},
|
||||
render_to: function($target) {
|
||||
var self = this;
|
||||
|
@ -1122,7 +1138,7 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
|
|||
if (found)
|
||||
return;
|
||||
|
||||
$label = $('<label/>').attr({
|
||||
var $label = $('<label/>').attr({
|
||||
'for' : name,
|
||||
"modifiers": JSON.stringify({invisible: field_modifiers.invisible}),
|
||||
"string": $field.attr('string'),
|
||||
|
@ -1336,7 +1352,7 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
|
|||
return $new_label;
|
||||
},
|
||||
handle_common_properties: function($new_element, $node) {
|
||||
var str_modifiers = $node.attr("modifiers") || "{}"
|
||||
var str_modifiers = $node.attr("modifiers") || "{}";
|
||||
var modifiers = JSON.parse(str_modifiers);
|
||||
var ic = null;
|
||||
if (modifiers.invisible !== undefined)
|
||||
|
@ -1455,24 +1471,27 @@ instance.web.form.compute_domain = function(expr, fields) {
|
|||
*/
|
||||
instance.web.form.InvisibilityChangerMixin = {
|
||||
init: function(field_manager, invisible_domain) {
|
||||
this._ic_field_manager = field_manager
|
||||
var self = this;
|
||||
this._ic_field_manager = field_manager;
|
||||
this._ic_invisible_modifier = invisible_domain;
|
||||
this._ic_field_manager.on("view_content_has_changed", this, function() {
|
||||
var result = this._ic_invisible_modifier === undefined ? false :
|
||||
instance.web.form.compute_domain(this._ic_invisible_modifier, this._ic_field_manager.fields);
|
||||
this.set({"invisible": result});
|
||||
var result = self._ic_invisible_modifier === undefined ? false :
|
||||
instance.web.form.compute_domain(
|
||||
self._ic_invisible_modifier,
|
||||
self._ic_field_manager.fields);
|
||||
self.set({"invisible": result});
|
||||
});
|
||||
this.set({invisible: this._ic_invisible_modifier === true, force_invisible: false});
|
||||
var check = function() {
|
||||
if (this.get("invisible") || this.get('force_invisible')) {
|
||||
this.set({"effective_invisible": true});
|
||||
if (self.get("invisible") || self.get('force_invisible')) {
|
||||
self.set({"effective_invisible": true});
|
||||
} else {
|
||||
this.set({"effective_invisible": false});
|
||||
self.set({"effective_invisible": false});
|
||||
}
|
||||
};
|
||||
this.on('change:invisible', this, check);
|
||||
this.on('change:force_invisible', this, check);
|
||||
_.bind(check, this)();
|
||||
check.call(this);
|
||||
},
|
||||
start: function() {
|
||||
this.on("change:effective_invisible", this, this._check_visibility);
|
||||
|
@ -1536,6 +1555,7 @@ instance.web.form.FormWidget = instance.web.Widget.extend(instance.web.form.Invi
|
|||
var compute_domain = instance.web.form.compute_domain;
|
||||
var to_set = {};
|
||||
for (var a in this.modifiers) {
|
||||
if (!this.modifiers.hasOwnProperty(a)) { continue; }
|
||||
if (!_.include(["invisible"], a)) {
|
||||
var val = compute_domain(this.modifiers[a], this.view.fields);
|
||||
to_set[a] = val;
|
||||
|
@ -1682,11 +1702,7 @@ instance.web.form.WidgetButton = instance.web.form.FormWidget.extend({
|
|||
on_confirmed: function() {
|
||||
var self = this;
|
||||
|
||||
var context = this.node.attrs.context;
|
||||
if (context && context.__ref) {
|
||||
context = new instance.web.CompoundContext(context);
|
||||
context.set_eval_context(this._build_eval_context());
|
||||
}
|
||||
var context = this.build_context();
|
||||
|
||||
return this.view.do_execute_action(
|
||||
_.extend({}, this.node.attrs, {context: context}),
|
||||
|
@ -1739,18 +1755,18 @@ instance.web.form.FieldInterface = {
|
|||
/**
|
||||
* Get the current value of the widget.
|
||||
*
|
||||
* Must always return a syntaxically correct value to be passed to the "write" method of the osv class in
|
||||
* Must always return a syntactically correct value to be passed to the "write" method of the osv class in
|
||||
* the OpenERP server, although it is not assumed to respect the constraints applied to the field.
|
||||
* For example if the field is marqued as "required", a call to get_value() can return false.
|
||||
* For example if the field is marked as "required", a call to get_value() can return false.
|
||||
*
|
||||
* get_value() can also be called *before* a call to set_value() and, in that case, is supposed to
|
||||
* return a defaut value according to the type of field.
|
||||
* return a default value according to the type of field.
|
||||
*
|
||||
* This method is always assumed to perform synchronously, it can not return a promise.
|
||||
*
|
||||
* If there was no user interaction to modify the value of the field, it is always assumed that
|
||||
* get_value() return the same semantic value than the one passed in the last call to set_value(),
|
||||
* altough the syntax can be different. This can be the case for type of fields that have a different
|
||||
* although the syntax can be different. This can be the case for type of fields that have a different
|
||||
* syntax for "read" and "write" (example: m2o: set_value([0, "Administrator"]), get_value() => 0).
|
||||
*/
|
||||
get_value: function() {},
|
||||
|
@ -1765,7 +1781,7 @@ instance.web.form.FieldInterface = {
|
|||
*/
|
||||
is_valid: function() {},
|
||||
/**
|
||||
* Returns true if the field holds a value which is syntaxically correct, ignoring
|
||||
* Returns true if the field holds a value which is syntactically correct, ignoring
|
||||
* the potential semantic restrictions applied to the field.
|
||||
*/
|
||||
is_syntax_valid: function() {},
|
||||
|
@ -1795,6 +1811,7 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.w
|
|||
* @param node
|
||||
*/
|
||||
init: function(field_manager, node) {
|
||||
var self = this
|
||||
this._super(field_manager, node);
|
||||
this.field_manager = field_manager;
|
||||
this.name = this.node.attrs.name;
|
||||
|
@ -1809,12 +1826,11 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.w
|
|||
this.set({"readonly": this.modifiers['readonly'] === true});
|
||||
this.set({"force_readonly": false});
|
||||
var test_effective_readonly = function() {
|
||||
this.set({"effective_readonly": this.get("readonly") || !!this.get("force_readonly")});
|
||||
self.set({"effective_readonly": self.get("readonly") || !!self.get("force_readonly")});
|
||||
};
|
||||
this.on("change:readonly", this, test_effective_readonly);
|
||||
this.on("change:force_readonly", this, test_effective_readonly);
|
||||
_.bind(test_effective_readonly, this)();
|
||||
|
||||
test_effective_readonly.call(this);
|
||||
this.on("change:value", this, function() {
|
||||
if (! this._inhibit_on_change)
|
||||
this.trigger('changed_value');
|
||||
|
@ -1891,7 +1907,7 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.w
|
|||
*/
|
||||
delay_focus: function($elem) {
|
||||
setTimeout(function() {
|
||||
$elem.focus();
|
||||
$elem[0].focus();
|
||||
}, 50);
|
||||
},
|
||||
/**
|
||||
|
@ -2234,6 +2250,11 @@ instance.web.form.FieldText = instance.web.form.AbstractField.extend(instance.we
|
|||
} else {
|
||||
this.$textarea.attr('disabled', 'disabled');
|
||||
}
|
||||
this.$element.keyup(function (e) {
|
||||
if (e.which === $.ui.keyCode.ENTER) {
|
||||
e.stopPropagation();
|
||||
}
|
||||
});
|
||||
this.setupFocus(this.$textarea);
|
||||
},
|
||||
set_value: function(value_) {
|
||||
|
@ -2337,6 +2358,7 @@ instance.web.form.FieldTextHtml = instance.web.form.FieldText.extend({
|
|||
instance.web.form.FieldBoolean = instance.web.form.AbstractField.extend({
|
||||
template: 'FieldBoolean',
|
||||
start: function() {
|
||||
var self = this;
|
||||
this._super.apply(this, arguments);
|
||||
this.$checkbox = $("input", this.$element);
|
||||
this.setupFocus(this.$checkbox);
|
||||
|
@ -2344,10 +2366,10 @@ instance.web.form.FieldBoolean = instance.web.form.AbstractField.extend({
|
|||
this.set({'value': this.$checkbox.is(':checked')});
|
||||
}, this));
|
||||
var check_readonly = function() {
|
||||
this.$checkbox.prop('disabled', this.get("effective_readonly"));
|
||||
self.$checkbox.prop('disabled', self.get("effective_readonly"));
|
||||
};
|
||||
this.on("change:effective_readonly", this, check_readonly);
|
||||
_.bind(check_readonly, this)();
|
||||
check_readonly.call(this);
|
||||
},
|
||||
set_value: function(value_) {
|
||||
this._super.apply(this, arguments);
|
||||
|
@ -2960,6 +2982,7 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
|
|||
selectable: self.multi_selection,
|
||||
sortable: false,
|
||||
import_enabled: false,
|
||||
deletable: true
|
||||
});
|
||||
if (self.get("effective_readonly")) {
|
||||
_.extend(view.options, {
|
||||
|
@ -3004,8 +3027,11 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
|
|||
this.viewmanager.on_controller_inited.add_last(function(view_type, controller) {
|
||||
controller.o2m = self;
|
||||
if (view_type == "list") {
|
||||
if (self.get("effective_readonly"))
|
||||
controller.set_editable(false);
|
||||
if (self.get("effective_readonly")) {
|
||||
controller.on('edit:before', self, function (e) {
|
||||
e.cancel = true;
|
||||
});
|
||||
}
|
||||
} else if (view_type === "form") {
|
||||
if (self.get("effective_readonly")) {
|
||||
$(".oe_form_buttons", controller.$element).children().remove();
|
||||
|
@ -3189,6 +3215,7 @@ instance.web.form.One2ManyViewManager = instance.web.ViewManager.extend({
|
|||
form: 'instance.web.form.One2ManyFormView',
|
||||
kanban: 'instance.web.form.One2ManyKanbanView',
|
||||
});
|
||||
this.__ignore_blur = false;
|
||||
},
|
||||
switch_view: function(mode, unused) {
|
||||
if (mode !== 'form') {
|
||||
|
@ -3238,19 +3265,34 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({
|
|||
this._super(parent, dataset, view_id, _.extend(options || {}, {
|
||||
ListType: instance.web.form.One2ManyList
|
||||
}));
|
||||
this.on('edit:before', this, this.proxy('_before_edit'));
|
||||
this.on('save:before cancel:before', this, this.proxy('_before_unedit'));
|
||||
|
||||
this.records
|
||||
.bind('add', this.proxy("changed_records"))
|
||||
.bind('edit', this.proxy("changed_records"))
|
||||
.bind('remove', this.proxy("changed_records"));
|
||||
},
|
||||
start: function () {
|
||||
var ret = this._super();
|
||||
this.$element
|
||||
.off('mousedown.handleButtons')
|
||||
.on('mousedown.handleButtons', 'table button', this.proxy('_button_down'));
|
||||
return ret;
|
||||
},
|
||||
changed_records: function () {
|
||||
this.o2m.trigger_on_change();
|
||||
},
|
||||
is_valid: function () {
|
||||
var form;
|
||||
// A list not being edited is always valid
|
||||
if (!(form = this.first_edition_form())) {
|
||||
return true;
|
||||
}
|
||||
var form = this.editor.form;
|
||||
|
||||
// If the form has not been modified, the view can only be valid
|
||||
// NB: is_dirty will also be set on defaults/onchanges/whatever?
|
||||
// oe_form_dirty seems to only be set on actual user actions
|
||||
if (!form.$element.is('.oe_form_dirty')) {
|
||||
return true;
|
||||
}
|
||||
this.o2m._dirty_flag = true;
|
||||
|
||||
// Otherwise validate internal form
|
||||
return _(form.fields).chain()
|
||||
|
@ -3261,21 +3303,8 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({
|
|||
.all(_.identity)
|
||||
.value();
|
||||
},
|
||||
first_edition_form: function () {
|
||||
var get_form = function (group_or_list) {
|
||||
if (group_or_list.edition) {
|
||||
return group_or_list.edition_form;
|
||||
}
|
||||
return _(group_or_list.children).chain()
|
||||
.map(get_form)
|
||||
.compact()
|
||||
.first()
|
||||
.value();
|
||||
};
|
||||
return get_form(this.groups);
|
||||
},
|
||||
do_add_record: function () {
|
||||
if (this.options.editable) {
|
||||
if (this.editable()) {
|
||||
this._super.apply(this, arguments);
|
||||
} else {
|
||||
var self = this;
|
||||
|
@ -3328,55 +3357,58 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({
|
|||
});
|
||||
},
|
||||
do_button_action: function (name, id, callback) {
|
||||
var _super = _.bind(this._super, this);
|
||||
|
||||
this.o2m.view.do_save().then(function () {
|
||||
_super(name, id, callback);
|
||||
});
|
||||
}
|
||||
});
|
||||
instance.web.form.One2ManyList = instance.web.ListView.List.extend({
|
||||
KEY_RETURN: 13,
|
||||
// blurring caused by hitting the [Return] key, should skip the
|
||||
// autosave-on-blur and let the handler for [Return] do its thing
|
||||
__return_blur: false,
|
||||
render_row_as_form: function () {
|
||||
if (!_.isNumber(id)) {
|
||||
instance.webclient.notification.warn(
|
||||
_t("Action Button"),
|
||||
_t("The o2m record must be saved before an action can be used"));
|
||||
return;
|
||||
}
|
||||
var parent_form = this.o2m.view;
|
||||
var self = this;
|
||||
return this._super.apply(this, arguments).then(function () {
|
||||
// Replace the "Save Row" button with "Cancel Edition"
|
||||
self.edition_form.$element
|
||||
.undelegate('button.oe_list_edit_row_save', 'click')
|
||||
.delegate('button.oe_list_edit_row_save', 'click', function () {
|
||||
self.cancel_pending_edition();
|
||||
});
|
||||
|
||||
// Overload execute_action on the edition form to perform a simple
|
||||
// reload_record after the action is done, rather than fully
|
||||
// reload the parent view (or something)
|
||||
var _execute_action = self.edition_form.do_execute_action;
|
||||
self.edition_form.do_execute_action = function (action, dataset, record_id, _callback) {
|
||||
return _execute_action.call(this, action, dataset, record_id, function () {
|
||||
self.view.reload_record(
|
||||
self.view.records.get(record_id));
|
||||
});
|
||||
};
|
||||
|
||||
self.edition_form.on('blurred', null, function () {
|
||||
if (self.__return_blur) {
|
||||
delete self.__return_blur;
|
||||
return;
|
||||
}
|
||||
if (!self.edition_form.widget_is_stopped) {
|
||||
self.view.ensure_saved();
|
||||
}
|
||||
});
|
||||
this.ensure_saved().pipe(function () {
|
||||
return parent_form.do_save();
|
||||
}).then(function () {
|
||||
self.handle_button(name, id, callback);
|
||||
});
|
||||
},
|
||||
on_row_keyup: function (e) {
|
||||
if (e.which === this.KEY_RETURN) {
|
||||
this.__return_blur = true;
|
||||
|
||||
_before_edit: function () {
|
||||
this.__ignore_blur = false;
|
||||
this.editor.form.on('blurred', this, this._on_form_blur);
|
||||
},
|
||||
_before_unedit: function () {
|
||||
this.editor.form.off('blurred', this, this._on_form_blur);
|
||||
},
|
||||
_button_down: function () {
|
||||
// If a button is clicked (usually some sort of action button), it's
|
||||
// the button's responsibility to ensure the editable list is in the
|
||||
// correct state -> ignore form blurring
|
||||
this.__ignore_blur = true;
|
||||
},
|
||||
/**
|
||||
* Handles blurring of the nested form (saves the currently edited row),
|
||||
* unless the flag to ignore the event is set to ``true``
|
||||
*
|
||||
* Makes the internal form go away
|
||||
*/
|
||||
_on_form_blur: function () {
|
||||
if (this.__ignore_blur) {
|
||||
this.__ignore_blur = false;
|
||||
return;
|
||||
}
|
||||
this._super(e);
|
||||
// FIXME: why isn't there an API for this?
|
||||
if (this.editor.form.$element.hasClass('oe_form_dirty')) {
|
||||
this.save_edition();
|
||||
return;
|
||||
}
|
||||
this.cancel_edition();
|
||||
},
|
||||
keyup_ENTER: function () {
|
||||
// blurring caused by hitting the [Return] key, should skip the
|
||||
// autosave-on-blur and let the handler for [Return] do its thing (save
|
||||
// the current row *anyway*, then create a new one/edit the next one)
|
||||
this.__ignore_blur = true;
|
||||
this._super.apply(this, arguments);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -3545,7 +3577,7 @@ instance.web.form.FieldMany2Many = instance.web.form.AbstractField.extend({
|
|||
},
|
||||
start: function() {
|
||||
this._super.apply(this, arguments);
|
||||
this.$element.addClass('oe_form_field_many2many');
|
||||
this.$element.addClass('oe_form_field oe_form_field_many2many');
|
||||
|
||||
var self = this;
|
||||
|
||||
|
@ -4079,10 +4111,12 @@ instance.web.form.SelectCreatePopup = instance.web.form.AbstractFormPopup.extend
|
|||
self.dataset, false,
|
||||
_.extend({'deletable': false,
|
||||
'selectable': !self.options.disable_multiple_selection,
|
||||
'read_only': true,
|
||||
'import_enabled': false,
|
||||
'$buttons': self.$buttonpane,
|
||||
}, self.options.list_view_options || {}));
|
||||
self.view_list.on('edit:before', self, function (e) {
|
||||
e.cancel = true;
|
||||
});
|
||||
self.view_list.popup = self;
|
||||
self.view_list.appendTo($(".oe_popup_list", self.$element)).pipe(function() {
|
||||
self.view_list.do_show();
|
||||
|
@ -4314,7 +4348,7 @@ instance.web.form.FieldBinary = instance.web.form.AbstractField.extend(instance.
|
|||
//link.target = '_blank';
|
||||
link.href = "data:application/octet-stream;base64," + value;
|
||||
} else {
|
||||
$.blockUI();
|
||||
instance.web.blockUI();
|
||||
this.session.get_file({
|
||||
url: '/web/binary/saveas_ajax',
|
||||
data: {data: JSON.stringify({
|
||||
|
@ -4324,7 +4358,7 @@ instance.web.form.FieldBinary = instance.web.form.AbstractField.extend(instance.
|
|||
filename_field: (this.node.attrs.filename || ''),
|
||||
context: this.view.dataset.get_context()
|
||||
})},
|
||||
complete: $.unblockUI,
|
||||
complete: instance.web.unblockUI,
|
||||
error: instance.webclient.crashmanager.on_rpc_error
|
||||
});
|
||||
ev.stopPropagation();
|
||||
|
|
|
@ -21,9 +21,6 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
|
|||
// whether the view rows can be reordered (via vertical drag & drop)
|
||||
'reorderable': true,
|
||||
'action_buttons': true,
|
||||
// if true, the view can't be editable, ignoring the view's and the context's
|
||||
// instructions
|
||||
'read_only': false,
|
||||
// if true, the 'Import', 'Export', etc... buttons will be shown
|
||||
'import_enabled': true,
|
||||
},
|
||||
|
@ -142,7 +139,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
|
|||
});
|
||||
},
|
||||
/**
|
||||
* View startup method, the default behavior is to set the ``oe_listw``
|
||||
* View startup method, the default behavior is to set the ``oe_list``
|
||||
* class on its root element and to perform an RPC load call.
|
||||
*
|
||||
* @returns {$.Deferred} loading promise
|
||||
|
@ -288,7 +285,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
|
|||
}
|
||||
this.$buttons.find('.oe_list_add')
|
||||
.click(this.proxy('do_add_record'))
|
||||
.prop('disabled', grouped && this.options.editable);
|
||||
.prop('disabled', grouped);
|
||||
this.$buttons.on('click', '.oe_list_button_import', function() {
|
||||
self.on_sidebar_import();
|
||||
return false;
|
||||
|
@ -411,20 +408,27 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
|
|||
if (column.modifiers) {
|
||||
var modifiers = JSON.parse(column.modifiers);
|
||||
column.modifiers_for = function (fields) {
|
||||
if (!modifiers.invisible) {
|
||||
return {};
|
||||
var out = {};
|
||||
|
||||
for (var attr in modifiers) {
|
||||
if (!modifiers.hasOwnProperty(attr)) { continue; }
|
||||
var modifier = modifiers[attr];
|
||||
out[attr] = _.isBoolean(modifier)
|
||||
? modifier
|
||||
: domain_computer(modifier, fields);
|
||||
}
|
||||
return {
|
||||
'invisible': domain_computer(modifiers.invisible, fields)
|
||||
};
|
||||
|
||||
return out;
|
||||
};
|
||||
if (modifiers['tree_invisible']) {
|
||||
column.invisible = '1';
|
||||
} else {
|
||||
delete column.invisible;
|
||||
}
|
||||
column.modifiers = modifiers;
|
||||
} else {
|
||||
column.modifiers_for = noop;
|
||||
column.modifiers = {};
|
||||
}
|
||||
return column;
|
||||
};
|
||||
|
@ -436,10 +440,12 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
|
|||
if (grouped) {
|
||||
this.columns.unshift({
|
||||
id: '_group', tag: '', string: _t("Group"), meta: true,
|
||||
modifiers_for: function () { return {}; }
|
||||
modifiers_for: function () { return {}; },
|
||||
modifiers: {}
|
||||
}, {
|
||||
id: '_count', tag: '', string: '#', meta: true,
|
||||
modifiers_for: function () { return {}; }
|
||||
modifiers_for: function () { return {}; },
|
||||
modifiers: {}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -667,6 +673,19 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
|
|||
* @param {Function} callback should be called after the action is executed, if non-null
|
||||
*/
|
||||
do_button_action: function (name, id, callback) {
|
||||
this.handle_button(name, id, callback);
|
||||
},
|
||||
/**
|
||||
* Base handling of buttons, can be called when overriding do_button_action
|
||||
* in order to bypass parent overrides.
|
||||
*
|
||||
* This method should not be overridden.
|
||||
*
|
||||
* @param {String} name action name
|
||||
* @param {Object} id id of the record the action should be called on
|
||||
* @param {Function} callback should be called after the action is executed, if non-null
|
||||
*/
|
||||
handle_button: function (name, id, callback) {
|
||||
var action = _.detect(this.columns, function (field) {
|
||||
return field.name === name;
|
||||
});
|
||||
|
@ -911,27 +930,42 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.
|
|||
|
||||
this.record_callbacks = {
|
||||
'remove': function (event, record) {
|
||||
var $row = self.$current.find(
|
||||
'[data-id=' + record.get('id') + ']');
|
||||
var $row = self.$current.children(
|
||||
'[data-id=' + record.get('id') + ']');
|
||||
var index = $row.data('index');
|
||||
$row.remove();
|
||||
self.refresh_zebra(index);
|
||||
},
|
||||
'reset': function () { return self.on_records_reset(); },
|
||||
'change': function (event, record) {
|
||||
var $row = self.$current.find('[data-id=' + record.get('id') + ']');
|
||||
'change': function (event, record, attribute, value, old_value) {
|
||||
var $row;
|
||||
if (attribute === 'id') {
|
||||
if (old_value) {
|
||||
throw new Error("Setting 'id' attribute on existing record "
|
||||
+ JSON.stringify(record.attributes));
|
||||
}
|
||||
if (!_.contains(self.dataset.ids, value)) {
|
||||
// add record to dataset if not already in (added by
|
||||
// the form view?)
|
||||
self.dataset.ids.splice(
|
||||
self.records.indexOf(record), 0, value);
|
||||
}
|
||||
// Set id on new record
|
||||
$row = self.$current.children('[data-id=false]');
|
||||
} else {
|
||||
$row = self.$current.children(
|
||||
'[data-id=' + record.get('id') + ']');
|
||||
}
|
||||
$row.replaceWith(self.render_record(record));
|
||||
},
|
||||
'add': function (ev, records, record, index) {
|
||||
var $new_row = $('<tr>').attr({
|
||||
'data-id': record.get('id')
|
||||
});
|
||||
var $new_row = $(self.render_record(record));
|
||||
|
||||
if (index === 0) {
|
||||
$new_row.prependTo(self.$current);
|
||||
} else {
|
||||
var previous_record = records.at(index-1),
|
||||
$previous_sibling = self.$current.find(
|
||||
$previous_sibling = self.$current.children(
|
||||
'[data-id=' + previous_record.get('id') + ']');
|
||||
$new_row.insertAfter($previous_sibling);
|
||||
}
|
||||
|
@ -975,11 +1009,11 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.
|
|||
e.stopPropagation();
|
||||
})
|
||||
.delegate('tr', 'click', function (e) {
|
||||
e.stopPropagation();
|
||||
var row_id = self.row_id(e.currentTarget);
|
||||
if (row_id !== undefined) {
|
||||
if (row_id) {
|
||||
e.stopPropagation();
|
||||
if (!self.dataset.select_id(row_id)) {
|
||||
throw "Could not find id in dataset"
|
||||
throw new Error("Could not find id in dataset");
|
||||
}
|
||||
self.row_clicked(e);
|
||||
}
|
||||
|
@ -1139,6 +1173,7 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.
|
|||
* @returns {String} QWeb rendering of the selected record
|
||||
*/
|
||||
render_record: function (record) {
|
||||
var self = this;
|
||||
var index = this.records.indexOf(record);
|
||||
return QWeb.render('ListView.row', {
|
||||
columns: this.columns,
|
||||
|
@ -1147,7 +1182,7 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.
|
|||
row_parity: (index % 2 === 0) ? 'even' : 'odd',
|
||||
view: this.view,
|
||||
render_cell: function () {
|
||||
return this.render_cell.apply(this, arguments); }
|
||||
return self.render_cell.apply(self, arguments); }
|
||||
});
|
||||
},
|
||||
/**
|
||||
|
@ -1831,7 +1866,7 @@ var Collection = instance.web.Class.extend(/** @lends Collection# */{
|
|||
* @returns this
|
||||
*/
|
||||
remove: function (record) {
|
||||
var index = _(this.records).indexOf(record);
|
||||
var index = this.indexOf(record);
|
||||
if (index === -1) {
|
||||
_(this._proxies).each(function (proxy) {
|
||||
proxy.remove(record);
|
||||
|
@ -1847,13 +1882,42 @@ var Collection = instance.web.Class.extend(/** @lends Collection# */{
|
|||
return this;
|
||||
},
|
||||
|
||||
_onRecordEvent: function (event, record, options) {
|
||||
_onRecordEvent: function (event) {
|
||||
switch(event) {
|
||||
// don't propagate reset events
|
||||
if (event === 'reset') { return; }
|
||||
case 'reset': return;
|
||||
case 'change:id':
|
||||
var record = arguments[1];
|
||||
var new_value = arguments[2];
|
||||
var old_value = arguments[3];
|
||||
// [change:id, record, new_value, old_value]
|
||||
if (this._byId[old_value] === record) {
|
||||
delete this._byId[old_value];
|
||||
this._byId[new_value] = record;
|
||||
}
|
||||
break;
|
||||
}
|
||||
this.trigger.apply(this, arguments);
|
||||
},
|
||||
|
||||
// underscore-type methods
|
||||
find: function (callback) {
|
||||
var record;
|
||||
for(var section in this._proxies) {
|
||||
if (!this._proxies.hasOwnProperty(section)) {
|
||||
continue
|
||||
}
|
||||
if ((record = this._proxies[section].find(callback))) {
|
||||
return record;
|
||||
}
|
||||
}
|
||||
for(var i=0; i<this.length; ++i) {
|
||||
record = this.records[i];
|
||||
if (callback(record)) {
|
||||
return record;
|
||||
}
|
||||
}
|
||||
},
|
||||
each: function (callback) {
|
||||
for(var section in this._proxies) {
|
||||
if (this._proxies.hasOwnProperty(section)) {
|
||||
|
@ -1878,6 +1942,46 @@ var Collection = instance.web.Class.extend(/** @lends Collection# */{
|
|||
},
|
||||
indexOf: function (record) {
|
||||
return _(this.records).indexOf(record);
|
||||
},
|
||||
succ: function (record, options) {
|
||||
options = options || {wraparound: false};
|
||||
var result;
|
||||
for(var section in this._proxies) {
|
||||
if (!this._proxies.hasOwnProperty(section)) {
|
||||
continue;
|
||||
}
|
||||
if ((result = this._proxies[section].succ(record, options))) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
var index = this.indexOf(record);
|
||||
if (index === -1) { return null; }
|
||||
var next_index = index + 1;
|
||||
if (options.wraparound && (next_index === this.length)) {
|
||||
return this.at(0);
|
||||
}
|
||||
return this.at(next_index);
|
||||
},
|
||||
pred: function (record, options) {
|
||||
options = options || {wraparound: false};
|
||||
|
||||
var result;
|
||||
for (var section in this._proxies) {
|
||||
if (!this._proxies.hasOwnProperty(section)) {
|
||||
continue;
|
||||
}
|
||||
if ((result = this._proxies[section].pred(record, options))) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
var index = this.indexOf(record);
|
||||
if (index === -1) { return null; }
|
||||
var next_index = index - 1;
|
||||
if (options.wraparound && (next_index === -1)) {
|
||||
return this.at(this.length - 1);
|
||||
}
|
||||
return this.at(next_index);
|
||||
}
|
||||
});
|
||||
Collection.include(Events);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -274,7 +274,7 @@ instance.web.ActionManager = instance.web.Widget.extend({
|
|||
},
|
||||
ir_actions_report_xml: function(action, on_closed) {
|
||||
var self = this;
|
||||
$.blockUI();
|
||||
instance.web.blockUI();
|
||||
self.rpc("/web/session/eval_domain_and_context", {
|
||||
contexts: [action.context],
|
||||
domains: []
|
||||
|
@ -284,7 +284,7 @@ instance.web.ActionManager = instance.web.Widget.extend({
|
|||
self.session.get_file({
|
||||
url: '/web/report',
|
||||
data: {action: JSON.stringify(action)},
|
||||
complete: $.unblockUI,
|
||||
complete: instance.web.unblockUI,
|
||||
success: function(){
|
||||
if (!self.dialog && on_closed) {
|
||||
on_closed();
|
||||
|
@ -841,7 +841,7 @@ instance.web.Sidebar = instance.web.Widget.extend({
|
|||
} else {
|
||||
self.do_attachement_update(self.dataset, self.model_id);
|
||||
}
|
||||
$.unblockUI();
|
||||
instance.web.unblockUI();
|
||||
});
|
||||
},
|
||||
start: function() {
|
||||
|
@ -980,7 +980,7 @@ instance.web.Sidebar = instance.web.Widget.extend({
|
|||
$e.parent().find('input[type=file]').prop('disabled', true);
|
||||
$e.parent().find('button').prop('disabled', true).find('img, span').toggle();
|
||||
this.$('.oe_sidebar_add_attachment span').text(_t('Uploading...'));
|
||||
$.blockUI();
|
||||
instance.web.blockUI();
|
||||
}
|
||||
},
|
||||
on_attachment_delete: function(e) {
|
||||
|
@ -1074,11 +1074,13 @@ instance.web.TranslateDialog = instance.web.Dialog.extend({
|
|||
if (self.view.translatable_fields && self.view.translatable_fields.length) {
|
||||
self.do_load_fields_values(function() {
|
||||
sup.call(self);
|
||||
// desactivated because it created an exception, plus it does not seem very useful
|
||||
/*
|
||||
if (field) {
|
||||
var $field_input = self.$element.find('tr[data-field="' + field.name + '"] td:nth-child(2) *:first-child');
|
||||
self.$element.scrollTo($field_input);
|
||||
$field_input.focus();
|
||||
}
|
||||
}*/
|
||||
});
|
||||
} else {
|
||||
sup.call(self);
|
||||
|
@ -1346,7 +1348,9 @@ instance.web.json_node_to_xml = function(node, human_readable, indent) {
|
|||
if (typeof(node) === 'string') {
|
||||
return sindent + node;
|
||||
} else if (typeof(node.tag) !== 'string' || !node.children instanceof Array || !node.attrs instanceof Object) {
|
||||
throw("Node a json node");
|
||||
throw new Error(
|
||||
_.str.sprintf("Node [%s] is not a JSONified XML node",
|
||||
JSON.stringify(node)));
|
||||
}
|
||||
for (var attr in node.attrs) {
|
||||
var vattr = node.attrs[attr];
|
||||
|
|
|
@ -329,21 +329,20 @@
|
|||
</span>
|
||||
</t>
|
||||
<t t-name="UserMenu.about">
|
||||
<div>
|
||||
<a class="oe_activate_debug_mode" href="?debug" style="float:right; font-size: 80%;">Activate the developer mode</a>
|
||||
<h1 style="margin:0;">OpenERP</h1>
|
||||
<h3 style="margin:15px 0;padding:0;">Version <t t-esc="version_info.version"/></h3>
|
||||
<p>
|
||||
Copyright © 2004-TODAY OpenERP SA. All Rights Reserved.<br />
|
||||
OpenERP is a trademark of the <a target="_blank" href="http://openerp.com/" style="text-decoration: underline;">OpenERP SA Company</a>.
|
||||
</p>
|
||||
<p>
|
||||
Licenced under the terms of <a target="_blank" href="http://www.gnu.org/licenses/agpl.html" style="text-decoration: underline;">GNU Affero General Public License</a>
|
||||
</p>
|
||||
<p>
|
||||
For more information visit <a target="_blank" href="http://openerp.com/" style="text-decoration: underline;">OpenERP.com</a>
|
||||
</p>
|
||||
|
||||
<div class="oe_about">
|
||||
<a class="oe_activate_debug_mode oe_right" href="?debug">Activate the developer mode</a>
|
||||
<img class="oe_logo" src="/web/static/src/img/logo2.png"/>
|
||||
<h3>Version <t t-esc="version_info.version"/></h3>
|
||||
|
||||
<div class="oe_bottom">
|
||||
<p>Copyright © 2004-TODAY OpenERP SA. All Rights Reserved.<br />
|
||||
OpenERP is a trademark of the <a target="_blank" href="http://openerp.com/" style="text-decoration: underline;">OpenERP SA Company</a>.</p>
|
||||
<p>Licenced under the terms of <a target="_blank" href="http://www.gnu.org/licenses/agpl.html" style="text-decoration: underline;">GNU Affero General Public License</a></p>
|
||||
<p>For more information visit <a target="_blank" href="http://openerp.com/" style="text-decoration: underline;">OpenERP.com</a></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</t>
|
||||
<t t-name="UserMenu.password">
|
||||
<form name="change_password_form" method="POST">
|
||||
|
@ -630,9 +629,9 @@
|
|||
<button type="button" class="oe_button oe_list_add oe_highlight">
|
||||
<t t-esc="widget.options.addable"/>
|
||||
</button>
|
||||
<t t-if="widget.options.import_enabled">
|
||||
<span class="oe_alternative" t-if="widget.options.import_enabled">
|
||||
<span class="oe_fade">or</span> <a href="#" class="oe_bold oe_list_button_import">Import</a>
|
||||
</t>
|
||||
</span>
|
||||
</t>
|
||||
</div>
|
||||
<t t-name="ListView.pager">
|
||||
|
@ -652,6 +651,7 @@
|
|||
<tr t-name="ListView.row" t-att-class="row_parity"
|
||||
t-att-data-id="record.get('id')"
|
||||
t-att-style="view.style_for(record)">
|
||||
<t t-set="asData" t-value="record.toForm().data"/>
|
||||
<t t-foreach="columns" t-as="column">
|
||||
<td t-if="column.meta">
|
||||
|
||||
|
@ -663,23 +663,26 @@
|
|||
<input t-if="!options.radio" type="checkbox" name="radiogroup" t-att-checked="checked"/>
|
||||
</th>
|
||||
<t t-foreach="columns" t-as="column">
|
||||
<t t-set="align" t-value="column.type === 'integer' or column.type == 'float'"/>
|
||||
<t t-set="number" t-value="column.type === 'integer' or column.type == 'float'"/>
|
||||
<t t-set="modifiers" t-value="column.modifiers_for(asData)"/>
|
||||
<td t-if="!column.meta and column.invisible !== '1'" t-att-title="column.help"
|
||||
t-att-class="'oe_list_field_cell' + (align ? ' oe_number' : '')
|
||||
+ (column.tag === 'button' ? ' oe_button' : '')"
|
||||
t-att-data-field="column.id">
|
||||
<t t-raw="render_cell(record, column)"/>
|
||||
</td>
|
||||
t-attf-class="oe_list_field_cell oe_list_field_#{column.widget or column.type} #{number ? 'oe_number' : ''} #{column.tag === 'button' ? 'oe-button' : ''} #{modifiers.readonly ? 'oe_readonly' : ''}"
|
||||
t-att-data-field="column.id"
|
||||
><t t-raw="render_cell(record, column)"/></td>
|
||||
</t>
|
||||
<td t-if="options.deletable" class='oe_list_record_delete' width="1">
|
||||
<button type="button" name="delete" class="oe_i">d</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<t t-name="ListView.row.save">
|
||||
<td>
|
||||
<button class='oe_i oe_list_edit_row_save' type='button' name='save'/>
|
||||
</td>
|
||||
<t t-extend="ListView.buttons">
|
||||
<t t-jquery="button.oe_list_add" t-operation="after">
|
||||
<button class="oe_button oe_list_save oe_highlight"
|
||||
type="button">Save</button>
|
||||
</t>
|
||||
<t t-jquery="a.oe_list_button_import" t-operation="after">
|
||||
<a href="#" class="oe_bold oe_list_discard">discard</a>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<t t-name="FormView">
|
||||
|
@ -956,7 +959,7 @@
|
|||
<span class="oe_form_m2o_follow"/>
|
||||
</t>
|
||||
<t t-if="!widget.get('effective_readonly')">
|
||||
<a href="#" class="oe_m2o_cm_button oe_e oe_right">/</a>
|
||||
<a href="#" tabindex="-1" class="oe_m2o_cm_button oe_e oe_right">/</a>
|
||||
<div>
|
||||
<input type="text"
|
||||
t-att-id="widget.id_for_label"
|
||||
|
@ -1216,6 +1219,8 @@
|
|||
<div class="oe_searchview_clear"/>
|
||||
<div class="oe_searchview_unfold_drawer" title="Advanced Search..."/>
|
||||
<div class="oe_searchview_drawer"/>
|
||||
<button type="button" class="oe_searchview_search"
|
||||
title="Search Again">Search</button>
|
||||
</div>
|
||||
|
||||
<div t-name="SearchView.InputView"
|
||||
|
@ -1403,8 +1408,8 @@
|
|||
<div t-name="SearchView.addtodashboard" class="oe_searchview_dashboard">
|
||||
<h4>Add to Dashboard</h4>
|
||||
<form>
|
||||
<input placeholder ="Title of new Dashboard item" title = "Title of new Dashboard item" type="text"/>
|
||||
<button class="oe_apply" type="submit">save</button>
|
||||
<p><input placeholder ="Title of new Dashboard item" title = "Title of new Dashboard item" type="text"/></p>
|
||||
<button class="oe_apply" type="submit">Save</button>
|
||||
</form>
|
||||
</div>
|
||||
<t t-name="SearchView.addtodashboard.selection">
|
||||
|
@ -1498,7 +1503,7 @@
|
|||
missing columns
|
||||
-->
|
||||
<t t-jquery="> :last" t-operation="after">
|
||||
<td t-if="edited and !options.deletable" class="oe_list_padding"/>
|
||||
<td t-if="edited and !options.deletable" class="oe-listview-padding"/>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
|
|
|
@ -0,0 +1,352 @@
|
|||
$(document).ready(function () {
|
||||
var $fix = $('#qunit-fixture');
|
||||
|
||||
var instance;
|
||||
var baseSetup = function () {
|
||||
instance = openerp.testing.instanceFor('list_editable');
|
||||
|
||||
openerp.testing.loadTemplate(instance);
|
||||
|
||||
openerp.testing.mockifyRPC(instance);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {Object} [attrs]
|
||||
* @param {String} [attrs.type="char"]
|
||||
* @param {Boolean} [attrs.required]
|
||||
* @param {Boolean} [attrs.invisible]
|
||||
* @param {Boolean} [attrs.readonly]
|
||||
* @return {Object}
|
||||
*/
|
||||
function field(name, attrs) {
|
||||
attrs = attrs || {};
|
||||
attrs.name = name;
|
||||
return _.defaults(attrs, {
|
||||
type: 'char'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array} fields
|
||||
* @return {Object}
|
||||
*/
|
||||
function makeFormView(fields) {
|
||||
var fobj = {};
|
||||
_(fields).each(function (field) {
|
||||
fobj[field.name] = {
|
||||
type: field.type,
|
||||
string: field.string
|
||||
};
|
||||
});
|
||||
var children = _(fields).map(function (field) {
|
||||
return {
|
||||
tag: 'field',
|
||||
attrs: {
|
||||
name: field.name,
|
||||
modifiers: JSON.stringify({
|
||||
required: field.required,
|
||||
invisible: field.invisible,
|
||||
readonly: field.readonly
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
return {
|
||||
arch: {
|
||||
tag: 'form',
|
||||
attrs: {
|
||||
version: '7.0',
|
||||
'class': 'oe_form_container'
|
||||
},
|
||||
children: children
|
||||
},
|
||||
fields: fobj
|
||||
};
|
||||
}
|
||||
|
||||
module('editor', {
|
||||
setup: baseSetup
|
||||
});
|
||||
asyncTest('base-state', 2, function () {
|
||||
var e = new instance.web.list.Editor({
|
||||
dataset: {},
|
||||
edition_view: function () {
|
||||
return makeFormView();
|
||||
}
|
||||
});
|
||||
e.appendTo($fix)
|
||||
.always(start)
|
||||
.fail(function (error) { ok(false, error && error.message); })
|
||||
.done(function () {
|
||||
ok(!e.is_editing(), "should not be editing");
|
||||
ok(e.form instanceof instance.web.FormView,
|
||||
"should use default form type");
|
||||
});
|
||||
});
|
||||
asyncTest('toggle-edition-save', 4, function () {
|
||||
instance.connection.responses['/web/dataset/call_kw:create'] = function () {
|
||||
return { result: 42 };
|
||||
};
|
||||
instance.connection.responses['/web/dataset/call_kw:read'] = function () {
|
||||
return { result: [{
|
||||
id: 42,
|
||||
a: false,
|
||||
b: false,
|
||||
c: false
|
||||
}]};
|
||||
};
|
||||
var e = new instance.web.list.Editor({
|
||||
dataset: new instance.web.DataSetSearch(),
|
||||
prepends_on_create: function () { return false; },
|
||||
edition_view: function () {
|
||||
return makeFormView([ field('a'), field('b'), field('c') ]);
|
||||
}
|
||||
});
|
||||
var counter = 0;
|
||||
e.appendTo($fix)
|
||||
.pipe(function () {
|
||||
return e.edit({}, function () {
|
||||
++counter;
|
||||
});
|
||||
})
|
||||
.pipe(function (form) {
|
||||
ok(e.is_editing(), "should be editing");
|
||||
equal(counter, 3, "should have configured all fields");
|
||||
return e.save();
|
||||
})
|
||||
.always(start)
|
||||
.fail(function (error) { ok(false, error && error.message); })
|
||||
.done(function (record) {
|
||||
ok(!e.is_editing(), "should have stopped editing");
|
||||
equal(record.id, 42, "should have newly created id");
|
||||
})
|
||||
});
|
||||
asyncTest('toggle-edition-cancel', 2, function () {
|
||||
instance.connection.responses['/web/dataset/call_kw:create'] = function () {
|
||||
return { result: 42 };
|
||||
};
|
||||
var e = new instance.web.list.Editor({
|
||||
dataset: new instance.web.DataSetSearch(),
|
||||
prepends_on_create: function () { return false; },
|
||||
edition_view: function () {
|
||||
return makeFormView([ field('a'), field('b'), field('c') ]);
|
||||
}
|
||||
});
|
||||
var counter = 0;
|
||||
e.appendTo($fix)
|
||||
.pipe(function () {
|
||||
return e.edit({}, function () {
|
||||
++counter;
|
||||
});
|
||||
})
|
||||
.pipe(function (form) {
|
||||
return e.cancel();
|
||||
})
|
||||
.always(start)
|
||||
.fail(function (error) { ok(false, error && error.message); })
|
||||
.done(function (record) {
|
||||
ok(!e.is_editing(), "should have stopped editing");
|
||||
ok(!record.id, "should have no id");
|
||||
})
|
||||
});
|
||||
asyncTest('toggle-save-required', 2, function () {
|
||||
instance.connection.responses['/web/dataset/call_kw:create'] = function () {
|
||||
return { result: 42 };
|
||||
};
|
||||
var e = new instance.web.list.Editor({
|
||||
do_warn: function () {
|
||||
warnings++;
|
||||
},
|
||||
dataset: new instance.web.DataSetSearch(),
|
||||
prepends_on_create: function () { return false; },
|
||||
edition_view: function () {
|
||||
return makeFormView([
|
||||
field('a', {required: true}), field('b'), field('c') ]);
|
||||
}
|
||||
});
|
||||
var counter = 0;
|
||||
var warnings = 0;
|
||||
e.appendTo($fix)
|
||||
.pipe(function () {
|
||||
return e.edit({}, function () {
|
||||
++counter;
|
||||
});
|
||||
})
|
||||
.pipe(function (form) {
|
||||
return e.save();
|
||||
})
|
||||
.always(start)
|
||||
.done(function () { ok(false, "cancel should not succeed"); })
|
||||
.fail(function () {
|
||||
equal(warnings, 1, "should have been warned");
|
||||
ok(e.is_editing(), "should have kept editing");
|
||||
})
|
||||
});
|
||||
|
||||
module('list-edition', {
|
||||
setup: function () {
|
||||
baseSetup();
|
||||
|
||||
var records = {};
|
||||
_.extend(instance.connection.responses, {
|
||||
'/web/listview/load': function () {
|
||||
return {result: {
|
||||
type: 'tree',
|
||||
fields: {
|
||||
a: {type: 'char', string: "A"},
|
||||
b: {type: 'char', string: "B"},
|
||||
c: {type: 'char', string: "C"}
|
||||
},
|
||||
arch: {
|
||||
tag: 'tree',
|
||||
attrs: {},
|
||||
children: [
|
||||
{tag: 'field', attrs: {name: 'a'}},
|
||||
{tag: 'field', attrs: {name: 'b'}},
|
||||
{tag: 'field', attrs: {name: 'c'}}
|
||||
]
|
||||
}
|
||||
}};
|
||||
},
|
||||
'/web/dataset/call_kw:create': function (params) {
|
||||
records[42] = _.extend({}, params.params.args[0]);
|
||||
return {result: 42};
|
||||
},
|
||||
'/web/dataset/call_kw:read': function (params) {
|
||||
var id = params.params.args[0][0];
|
||||
if (id in records) {
|
||||
return {result: [records[id]]};
|
||||
}
|
||||
return {result: []};
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
asyncTest('newrecord', 6, function () {
|
||||
var got_defaults = false;
|
||||
instance.connection.responses['/web/dataset/call_kw:default_get'] = function (params) {
|
||||
var fields = params.params.args[0];
|
||||
deepEqual(
|
||||
fields, ['a', 'b', 'c'],
|
||||
"should ask defaults for all fields");
|
||||
got_defaults = true;
|
||||
return {result: {
|
||||
a: "qux",
|
||||
b: "quux"
|
||||
}};
|
||||
};
|
||||
|
||||
var ds = new instance.web.DataSetStatic(null, 'demo', null, [1]);
|
||||
var l = new instance.web.ListView({}, ds);
|
||||
l.set_editable(true);
|
||||
|
||||
l.appendTo($fix)
|
||||
.pipe(l.proxy('reload_content'))
|
||||
.pipe(function () {
|
||||
return l.start_edition();
|
||||
})
|
||||
.always(start)
|
||||
.pipe(function () {
|
||||
ok(got_defaults, "should have fetched default values for form");
|
||||
return l.save_edition();
|
||||
})
|
||||
.pipe(function (result) {
|
||||
ok(result.created, "should yield newly created record");
|
||||
equal(result.record.get('a'), "qux",
|
||||
"should have used default values");
|
||||
equal(result.record.get('b'), "quux",
|
||||
"should have used default values");
|
||||
ok(!result.record.get('c'),
|
||||
"should have no value if there was no default");
|
||||
})
|
||||
.fail(function (e) { ok(false, e && e.message || e); });
|
||||
});
|
||||
|
||||
module('list-edition-events', {
|
||||
setup: function () {
|
||||
baseSetup();
|
||||
_.extend(instance.connection.responses, {
|
||||
'/web/listview/load': function () {
|
||||
return {result: {
|
||||
type: 'tree',
|
||||
fields: {
|
||||
a: {type: 'char', string: "A"},
|
||||
b: {type: 'char', string: "B"},
|
||||
c: {type: 'char', string: "C"}
|
||||
},
|
||||
arch: {
|
||||
tag: 'tree',
|
||||
attrs: {},
|
||||
children: [
|
||||
{tag: 'field', attrs: {name: 'a'}},
|
||||
{tag: 'field', attrs: {name: 'b'}},
|
||||
{tag: 'field', attrs: {name: 'c'}}
|
||||
]
|
||||
}
|
||||
}};
|
||||
},
|
||||
'/web/dataset/call_kw:read': function (params) {
|
||||
return {result: [{
|
||||
id: 1,
|
||||
a: 'foo',
|
||||
b: 'bar',
|
||||
c: 'baz'
|
||||
}]};
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
asyncTest('edition events', 4, function () {
|
||||
var ds = new instance.web.DataSetStatic(null, 'demo', null, [1]);
|
||||
var o = {
|
||||
counter: 0,
|
||||
onEvent: function (e) { this.counter++; }
|
||||
};
|
||||
var l = new instance.web.ListView({}, ds);
|
||||
l.set_editable(true);
|
||||
l.on('edit:before edit:after', o, o.onEvent);
|
||||
l.appendTo($fix)
|
||||
.pipe(l.proxy('reload_content'))
|
||||
.always(start)
|
||||
.pipe(function () {
|
||||
ok(l.options.editable, "should be editable");
|
||||
equal(o.counter, 0, "should have seen no event yet");
|
||||
return l.start_edition(l.records.get(1));
|
||||
})
|
||||
.pipe(function () {
|
||||
ok(l.editor.is_editing(), "should be editing");
|
||||
equal(o.counter, 2, "should have seen two edition events");
|
||||
})
|
||||
.fail(function (e) { ok(false, e && e.message); });
|
||||
});
|
||||
|
||||
asyncTest('edition events: cancelling', 3, function () {
|
||||
var edit_after = false;
|
||||
var ds = new instance.web.DataSetStatic(null, 'demo', null, [1]);
|
||||
var l = new instance.web.ListView({}, ds);
|
||||
l.set_editable(true);
|
||||
l.on('edit:before', {}, function (e) {
|
||||
e.cancel = true;
|
||||
});
|
||||
l.on('edit:after', {}, function () {
|
||||
edit_after = true;
|
||||
});
|
||||
l.appendTo($fix)
|
||||
.pipe(l.proxy('reload_content'))
|
||||
.always(start)
|
||||
.pipe(function () {
|
||||
ok(l.options.editable, "should be editable");
|
||||
return l.start_edition();
|
||||
})
|
||||
// cancelling an event rejects the deferred
|
||||
.pipe($.Deferred().reject(), function () {
|
||||
ok(!l.editor.is_editing(), "should not be editing");
|
||||
ok(!edit_after, "should not have fired the edit:after event");
|
||||
return $.when();
|
||||
})
|
||||
.fail(function (e) { ok(false, e && e.message || e); });
|
||||
});
|
||||
});
|
|
@ -133,7 +133,7 @@ $(document).ready(function () {
|
|||
strictEqual(changed, 1);
|
||||
});
|
||||
|
||||
module('list-collections-degenerate', {
|
||||
module('list-collections', {
|
||||
setup: function () {
|
||||
openerp = window.openerp.init([]);
|
||||
window.openerp.web.corelib(openerp);
|
||||
|
@ -145,7 +145,7 @@ $(document).ready(function () {
|
|||
window.openerp.web.list(openerp);
|
||||
}
|
||||
});
|
||||
test('Fetch from collection', function () {
|
||||
test('degenerate-fetch', function () {
|
||||
var c = new openerp.web.list.Collection();
|
||||
strictEqual(c.length, 0);
|
||||
c.add({id: 1, value: 2});
|
||||
|
@ -163,7 +163,7 @@ $(document).ready(function () {
|
|||
strictEqual(r2.get('id'), 1);
|
||||
strictEqual(r2.get('value'), 2);
|
||||
});
|
||||
test('Add at index', function () {
|
||||
test('degenerate-indexed-add', function () {
|
||||
var c = new openerp.web.list.Collection([
|
||||
{id: 1, value: 5},
|
||||
{id: 2, value: 10},
|
||||
|
@ -175,7 +175,7 @@ $(document).ready(function () {
|
|||
strictEqual(c.at(1).get('value'), 55);
|
||||
strictEqual(c.at(3).get('value'), 20);
|
||||
});
|
||||
test('Remove record', function () {
|
||||
test('degenerate-remove', function () {
|
||||
var c = new openerp.web.list.Collection([
|
||||
{id: 1, value: 5},
|
||||
{id: 2, value: 10},
|
||||
|
@ -188,7 +188,7 @@ $(document).ready(function () {
|
|||
equal(c.get(2), undefined);
|
||||
strictEqual(c.at(1).get('value'), 20);
|
||||
});
|
||||
test('Remove unbind', function () {
|
||||
test('degenerate-remove-bound', function () {
|
||||
var changed = false,
|
||||
c = new openerp.web.list.Collection([ {id: 1, value: 5} ]);
|
||||
c.bind('change', function () { changed = true; });
|
||||
|
@ -198,7 +198,7 @@ $(document).ready(function () {
|
|||
ok(!changed, 'removed records should not trigger events in their ' +
|
||||
'parent collection');
|
||||
});
|
||||
test('Reset', function () {
|
||||
test('degenerate-reset', function () {
|
||||
var event, obj, c = new openerp.web.list.Collection([
|
||||
{id: 1, value: 5},
|
||||
{id: 2, value: 10},
|
||||
|
@ -218,7 +218,7 @@ $(document).ready(function () {
|
|||
strictEqual(c.length, 1);
|
||||
strictEqual(c.get(42).get('value'), 55);
|
||||
});
|
||||
test('Reset unbind', function () {
|
||||
test('degenerate-reset-bound', function () {
|
||||
var changed = false,
|
||||
c = new openerp.web.list.Collection([ {id: 1, value: 5} ]);
|
||||
c.bind('change', function () { changed = true; });
|
||||
|
@ -229,7 +229,7 @@ $(document).ready(function () {
|
|||
'parent collection');
|
||||
});
|
||||
|
||||
test('Events propagation', function () {
|
||||
test('degenerate-propagations', function () {
|
||||
var values = [];
|
||||
var c = new openerp.web.list.Collection([
|
||||
{id: 1, value: 5},
|
||||
|
@ -260,6 +260,82 @@ $(document).ready(function () {
|
|||
c.at(1).set('wealth', 5);
|
||||
strictEqual(total, 47);
|
||||
});
|
||||
test('degenerate-successor', function () {
|
||||
var root = new openerp.web.list.Collection([
|
||||
{id: 1, value: 1},
|
||||
{id: 2, value: 2},
|
||||
{id: 3, value: 3},
|
||||
{id: 4, value: 5},
|
||||
{id: 5, value: 8}
|
||||
]);
|
||||
|
||||
deepEqual(root.succ(root.at(2)).attributes,
|
||||
root.at(3).attributes,
|
||||
"should return the record at (index + 1) from the pivot");
|
||||
equal(root.succ(root.at(4)), null,
|
||||
"should return null as successor to last record");
|
||||
deepEqual(root.succ(root.at(4), {wraparound: true}).attributes,
|
||||
root.at(0).attributes,
|
||||
"should return index 0 as successor to last record if" +
|
||||
" wraparound is set");
|
||||
deepEqual(root.succ(root.at(2), {wraparound: true}).attributes,
|
||||
root.at(3).attributes,
|
||||
"wraparound should have no effect if not succ(last_record)");
|
||||
});
|
||||
test('successor', function () {
|
||||
var root = new openerp.web.list.Collection();
|
||||
root.proxy('first').add([{id: 1, value: 1}, {id: 2, value: 2}]);
|
||||
root.proxy('second').add([{id: 3, value: 3}, {id: 4, value: 5}]);
|
||||
root.proxy('third').add([{id: 5, value: 8}, {id: 6, value: 13}]);
|
||||
|
||||
deepEqual(root.succ(root.get(3)).attributes,
|
||||
root.get(4).attributes,
|
||||
"should get successor");
|
||||
equal(root.succ(root.get(4)),
|
||||
null,
|
||||
"successors do not cross collections");
|
||||
deepEqual(root.succ(root.get(4), {wraparound: true}).attributes,
|
||||
root.get(3).attributes,
|
||||
"should wraparound within a collection");
|
||||
});
|
||||
test('degenerate-predecessor', function () {
|
||||
var root = new openerp.web.list.Collection([
|
||||
{id: 1, value: 1},
|
||||
{id: 2, value: 2},
|
||||
{id: 3, value: 3},
|
||||
{id: 4, value: 5},
|
||||
{id: 5, value: 8}
|
||||
]);
|
||||
|
||||
deepEqual(root.pred(root.at(2)).attributes,
|
||||
root.at(1).attributes,
|
||||
"should return the record at (index - 1) from the pivot");
|
||||
equal(root.pred(root.at(0)), null,
|
||||
"should return null as predecessor to first record");
|
||||
deepEqual(root.pred(root.at(0), {wraparound: true}).attributes,
|
||||
root.at(4).attributes,
|
||||
"should return last record as predecessor to first record" +
|
||||
" if wraparound is set");
|
||||
deepEqual(root.pred(root.at(1), {wraparound: true}).attributes,
|
||||
root.at(0).attributes,
|
||||
"wraparound should have no effect if not pred(first_record)");
|
||||
});
|
||||
test('predecessor', function () {
|
||||
var root = new openerp.web.list.Collection();
|
||||
root.proxy('first').add([{id: 1, value: 1}, {id: 2, value: 2}]);
|
||||
root.proxy('second').add([{id: 3, value: 3}, {id: 4, value: 5}]);
|
||||
root.proxy('third').add([{id: 5, value: 8}, {id: 6, value: 13}]);
|
||||
|
||||
deepEqual(root.pred(root.get(4)).attributes,
|
||||
root.get(3).attributes,
|
||||
"should get predecessor");
|
||||
equal(root.pred(root.get(3)),
|
||||
null,
|
||||
"predecessor do not cross collections");
|
||||
deepEqual(root.pred(root.get(3), {wraparound: true}).attributes,
|
||||
root.get(4).attributes,
|
||||
"should wraparound within a collection");
|
||||
});
|
||||
|
||||
module('list-hofs', {
|
||||
setup: function () {
|
||||
|
@ -338,4 +414,33 @@ $(document).ready(function () {
|
|||
ids, [1, 2, 3, 10, 20, 30],
|
||||
'tree collections should be deeply iterated');
|
||||
});
|
||||
|
||||
module("list-weirds", {
|
||||
setup: function () {
|
||||
openerp = window.openerp.init([]);
|
||||
window.openerp.web.corelib(openerp);
|
||||
window.openerp.web.coresetup(openerp);
|
||||
window.openerp.web.chrome(openerp);
|
||||
// views loader stuff
|
||||
window.openerp.web.data(openerp);
|
||||
window.openerp.web.views(openerp);
|
||||
window.openerp.web.list(openerp);
|
||||
}
|
||||
});
|
||||
test('set-from-noid', function () {
|
||||
var root = new openerp.web.list.Collection();
|
||||
root.add({v: 3});
|
||||
root.at(0).set('id', 42);
|
||||
var record = root.get(42);
|
||||
equal(root.length, 1);
|
||||
equal(record.get('v'), 3, "should have fetched the original record");
|
||||
});
|
||||
test('set-from-previd', function () {
|
||||
var root = new openerp.web.list.Collection();
|
||||
root.add({id: 1, v: 2});
|
||||
root.get(1).set('id', 42);
|
||||
var record = root.get(42);
|
||||
equal(root.length, 1);
|
||||
equal(record.get('v'), 2, "should have fetched the original record");
|
||||
});
|
||||
});
|
||||
|
|
|
@ -9,21 +9,6 @@ $(document).ready(function () {
|
|||
openerp.web.Foo2 = {};
|
||||
}
|
||||
});
|
||||
test('key fetch', function () {
|
||||
var reg = new openerp.web.Registry({
|
||||
foo: 'openerp.web.Foo',
|
||||
bar: 'openerp.web.Bar',
|
||||
quux: 'openerp.web.Quux'
|
||||
});
|
||||
|
||||
strictEqual(reg.get_object('foo'), openerp.web.Foo);
|
||||
raises(function () { reg.get_object('qux'); },
|
||||
openerp.web.KeyNotFound,
|
||||
"Unknown keys should raise KeyNotFound");
|
||||
raises(function () { reg.get_object('quux'); },
|
||||
openerp.web.ObjectNotFound,
|
||||
"Incorrect file paths should raise ObjectNotFound");
|
||||
});
|
||||
test('key set', function () {
|
||||
var reg = new openerp.web.Registry();
|
||||
|
||||
|
|
|
@ -1,37 +1,8 @@
|
|||
$(document).ready(function () {
|
||||
var xhr = QWeb2.Engine.prototype.get_xhr();
|
||||
xhr.open('GET', '/web/static/src/xml/base.xml', false);
|
||||
xhr.send(null);
|
||||
var doc = xhr.responseXML;
|
||||
|
||||
var noop = function () {};
|
||||
/**
|
||||
* Make connection RPC responses mockable by setting keys on the
|
||||
* Connection#responses object (key is the URL, value is the function to
|
||||
* call with the RPC request payload)
|
||||
*
|
||||
* @param {openerp.web.Connection} connection connection instance to mockify
|
||||
* @param {Object} [responses] url:function mapping to seed the mock connection
|
||||
*/
|
||||
var mockifyRPC = function (connection, responses) {
|
||||
connection.responses = responses || {};
|
||||
connection.rpc_function = function (url, payload) {
|
||||
if (!(url.url in this.responses)) {
|
||||
return $.Deferred().reject({}, 'failed', _.str.sprintf("Url %s not found in mock responses", url.url)).promise();
|
||||
}
|
||||
return $.when(this.responses[url.url](payload));
|
||||
};
|
||||
};
|
||||
|
||||
var instance;
|
||||
module('query', {
|
||||
setup: function () {
|
||||
instance = window.openerp.init([]);
|
||||
window.openerp.web.corelib(instance);
|
||||
window.openerp.web.coresetup(instance);
|
||||
window.openerp.web.chrome(instance);
|
||||
window.openerp.web.data(instance);
|
||||
window.openerp.web.search(instance);
|
||||
instance = openerp.testing.instanceFor('search');
|
||||
}
|
||||
});
|
||||
test('Adding a facet to the query creates a facet and a value', function () {
|
||||
|
@ -167,16 +138,11 @@ $(document).ready(function () {
|
|||
|
||||
module('defaults', {
|
||||
setup: function () {
|
||||
instance = window.openerp.init([]);
|
||||
window.openerp.web.corelib(instance);
|
||||
window.openerp.web.coresetup(instance);
|
||||
window.openerp.web.chrome(instance);
|
||||
window.openerp.web.data(instance);
|
||||
window.openerp.web.search(instance);
|
||||
instance = openerp.testing.instanceFor('search');
|
||||
|
||||
instance.web.qweb.add_template(doc);
|
||||
openerp.testing.loadTemplate(instance);
|
||||
|
||||
mockifyRPC(instance.connection);
|
||||
openerp.testing.mockifyRPC(instance);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -404,18 +370,11 @@ $(document).ready(function () {
|
|||
|
||||
module('completions', {
|
||||
setup: function () {
|
||||
instance = window.openerp.init([]);
|
||||
window.openerp.web.corelib(instance);
|
||||
window.openerp.web.coresetup(instance);
|
||||
window.openerp.web.chrome(instance);
|
||||
window.openerp.web.data(instance);
|
||||
// date complete
|
||||
window.openerp.web.formats(instance);
|
||||
window.openerp.web.search(instance);
|
||||
instance = openerp.testing.instanceFor('search');
|
||||
|
||||
instance.web.qweb.add_template(doc);
|
||||
openerp.testing.loadTemplate(instance);
|
||||
|
||||
mockifyRPC(instance.connection);
|
||||
openerp.testing.mockifyRPC(instance);
|
||||
}
|
||||
});
|
||||
asyncTest('calling', 4, function () {
|
||||
|
@ -432,10 +391,7 @@ $(document).ready(function () {
|
|||
}
|
||||
});
|
||||
view.appendTo($('#qunit-fixture'))
|
||||
.always(start)
|
||||
.fail(function (error) { ok(false, error.message); })
|
||||
.done(function () {
|
||||
stop();
|
||||
view.complete_global_search({term: "dum"}, function (completions) {
|
||||
start();
|
||||
equal(completions.length, 1, "should have a single completion");
|
||||
|
@ -454,7 +410,11 @@ $(document).ready(function () {
|
|||
var completion = {
|
||||
label: "Dummy",
|
||||
facet: {
|
||||
field: {get_domain: noop, get_context: noop, get_groupby: noop},
|
||||
field: {
|
||||
get_domain: openerp.testing.noop,
|
||||
get_context: openerp.testing.noop,
|
||||
get_groupby: openerp.testing.noop
|
||||
},
|
||||
category: 'Dummy',
|
||||
values: [{label: 'dummy', value: 42}]
|
||||
}
|
||||
|
@ -476,7 +436,11 @@ $(document).ready(function () {
|
|||
});
|
||||
});
|
||||
asyncTest('facet selection: new value existing facet', 3, function () {
|
||||
var field = {get_domain: noop, get_context: noop, get_groupby: noop};
|
||||
var field = {
|
||||
get_domain: openerp.testing.noop,
|
||||
get_context: openerp.testing.noop,
|
||||
get_groupby: openerp.testing.noop
|
||||
};
|
||||
var completion = {
|
||||
label: "Dummy",
|
||||
facet: {
|
||||
|
@ -663,16 +627,11 @@ $(document).ready(function () {
|
|||
|
||||
module('search-serialization', {
|
||||
setup: function () {
|
||||
instance = window.openerp.init([]);
|
||||
window.openerp.web.corelib(instance);
|
||||
window.openerp.web.coresetup(instance);
|
||||
window.openerp.web.chrome(instance);
|
||||
window.openerp.web.data(instance);
|
||||
window.openerp.web.search(instance);
|
||||
instance = openerp.testing.instanceFor('search');
|
||||
|
||||
instance.web.qweb.add_template(doc);
|
||||
openerp.testing.loadTemplate(instance);
|
||||
|
||||
mockifyRPC(instance.connection);
|
||||
openerp.testing.mockifyRPC(instance);
|
||||
}
|
||||
});
|
||||
asyncTest('No facet, no call', 6, function () {
|
||||
|
@ -940,16 +899,11 @@ $(document).ready(function () {
|
|||
|
||||
module('removal', {
|
||||
setup: function () {
|
||||
instance = window.openerp.init([]);
|
||||
window.openerp.web.corelib(instance);
|
||||
window.openerp.web.coresetup(instance);
|
||||
window.openerp.web.chrome(instance);
|
||||
window.openerp.web.data(instance);
|
||||
window.openerp.web.search(instance);
|
||||
instance = openerp.testing.instanceFor('search');
|
||||
|
||||
instance.web.qweb.add_template(doc);
|
||||
openerp.testing.loadTemplate(instance);
|
||||
|
||||
mockifyRPC(instance.connection);
|
||||
openerp.testing.mockifyRPC(instance);
|
||||
}
|
||||
});
|
||||
asyncTest('clear button', function () {
|
||||
|
@ -975,16 +929,11 @@ $(document).ready(function () {
|
|||
|
||||
module('drawer', {
|
||||
setup: function () {
|
||||
instance = window.openerp.init([]);
|
||||
window.openerp.web.corelib(instance);
|
||||
window.openerp.web.coresetup(instance);
|
||||
window.openerp.web.chrome(instance);
|
||||
window.openerp.web.data(instance);
|
||||
window.openerp.web.search(instance);
|
||||
instance = openerp.testing.instanceFor('search');
|
||||
|
||||
instance.web.qweb.add_template(doc);
|
||||
openerp.testing.loadTemplate(instance);
|
||||
|
||||
mockifyRPC(instance.connection);
|
||||
openerp.testing.mockifyRPC(instance);
|
||||
}
|
||||
});
|
||||
asyncTest('is-drawn', 2, function () {
|
||||
|
@ -1003,16 +952,11 @@ $(document).ready(function () {
|
|||
|
||||
module('filters', {
|
||||
setup: function () {
|
||||
instance = window.openerp.init([]);
|
||||
window.openerp.web.corelib(instance);
|
||||
window.openerp.web.coresetup(instance);
|
||||
window.openerp.web.chrome(instance);
|
||||
window.openerp.web.data(instance);
|
||||
window.openerp.web.search(instance);
|
||||
instance = openerp.testing.instanceFor('search');
|
||||
|
||||
instance.web.qweb.add_template(doc);
|
||||
openerp.testing.loadTemplate(instance);
|
||||
|
||||
mockifyRPC(instance.connection, {
|
||||
openerp.testing.mockifyRPC(instance, {
|
||||
'/web/searchview/load': function () {
|
||||
// view with a single group of filters
|
||||
return {result: {fields_view: {
|
||||
|
@ -1117,17 +1061,11 @@ $(document).ready(function () {
|
|||
|
||||
module('saved_filters', {
|
||||
setup: function () {
|
||||
instance = window.openerp.init([]);
|
||||
window.openerp.web.corelib(instance);
|
||||
window.openerp.web.coresetup(instance);
|
||||
window.openerp.web.chrome(instance);
|
||||
window.openerp.web.data(instance);
|
||||
window.openerp.web.formats(instance);
|
||||
window.openerp.web.search(instance);
|
||||
instance = openerp.testing.instanceFor('search');
|
||||
|
||||
instance.web.qweb.add_template(doc);
|
||||
openerp.testing.loadTemplate(instance);
|
||||
|
||||
mockifyRPC(instance.connection);
|
||||
openerp.testing.mockifyRPC(instance);
|
||||
}
|
||||
});
|
||||
asyncTest('checkboxing', 6, function () {
|
||||
|
@ -1183,17 +1121,11 @@ $(document).ready(function () {
|
|||
|
||||
module('advanced', {
|
||||
setup: function () {
|
||||
instance = window.openerp.init([]);
|
||||
window.openerp.web.corelib(instance);
|
||||
window.openerp.web.coresetup(instance);
|
||||
window.openerp.web.chrome(instance);
|
||||
window.openerp.web.data(instance);
|
||||
window.openerp.web.formats(instance);
|
||||
window.openerp.web.search(instance);
|
||||
instance = openerp.testing.instanceFor('search');
|
||||
|
||||
instance.web.qweb.add_template(doc);
|
||||
openerp.testing.loadTemplate(instance);
|
||||
|
||||
mockifyRPC(instance.connection);
|
||||
openerp.testing.mockifyRPC(instance);
|
||||
}
|
||||
});
|
||||
asyncTest('single-advanced', 6, function () {
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<script src="/web/static/lib/backbone/backbone.js" type="text/javascript"></script>
|
||||
|
||||
<!-- jquery -->
|
||||
<script src="/web/static/lib/jquery/jquery-1.7.2b1.js"></script>
|
||||
<script src="/web/static/lib/jquery/jquery-1.7.2.js"></script>
|
||||
<script src="/web/static/lib/jquery.ui/js/jquery-ui-1.8.17.custom.min.js"></script>
|
||||
<script src="/web/static/lib/jquery.ba-bbq/jquery.ba-bbq.js"></script>
|
||||
|
||||
|
@ -38,6 +38,12 @@
|
|||
<script src="/web/static/src/js/search.js"></script>
|
||||
<script src="/web/static/src/js/view_form.js"></script>
|
||||
<script src="/web/static/src/js/view_list.js"></script>
|
||||
<script src="/web/static/src/js/view_list_editable.js"></script>
|
||||
|
||||
<script src="/web/static/test/testing.js"></script>
|
||||
<script type="text/javascript">
|
||||
QUnit.config.testTimeout = 500;
|
||||
</script>
|
||||
</head>
|
||||
<body id="oe" class="openerp">
|
||||
<h1 id="qunit-header">OpenERP web Test Suite</h1>
|
||||
|
@ -56,4 +62,5 @@
|
|||
<script type="text/javascript" src="/web/static/test/evals.js"></script>
|
||||
<script type="text/javascript" src="/web/static/test/search.js"></script>
|
||||
<script type="text/javascript" src="/web/static/test/Widget.js"></script>
|
||||
<script type="text/javascript" src="/web/static/test/list-editable.js"></script>
|
||||
</html>
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
// Test support structures and methods for OpenERP
|
||||
openerp.testing = (function () {
|
||||
var xhr = QWeb2.Engine.prototype.get_xhr();
|
||||
xhr.open('GET', '/web/static/src/xml/base.xml', false);
|
||||
xhr.send(null);
|
||||
var doc = xhr.responseXML;
|
||||
|
||||
var dependencies = {
|
||||
corelib: [],
|
||||
coresetup: ['corelib'],
|
||||
data: ['corelib', 'coresetup'],
|
||||
dates: [],
|
||||
formats: ['coresetup', 'dates'],
|
||||
chrome: ['corelib', 'coresetup'],
|
||||
views: ['corelib', 'coresetup', 'data', 'chrome'],
|
||||
search: ['data', 'coresetup', 'formats'],
|
||||
list: ['views', 'data'],
|
||||
form: ['data', 'views', 'list', 'formats'],
|
||||
list_editable: ['list', 'form', 'data'],
|
||||
};
|
||||
|
||||
return {
|
||||
/**
|
||||
* Function which does not do anything
|
||||
*/
|
||||
noop: function () { },
|
||||
/**
|
||||
* Loads 'base.xml' template file into qweb for the provided instance
|
||||
*
|
||||
* @param instance openerp instance being initialized, to load the template file in
|
||||
*/
|
||||
loadTemplate: function (instance) {
|
||||
instance.web.qweb.add_template(doc);
|
||||
},
|
||||
/**
|
||||
* Alter provided instance's ``connection`` attribute to make response
|
||||
* mockable:
|
||||
*
|
||||
* * The ``responses`` parameter can be used to provide a map of (RPC)
|
||||
* paths (e.g. ``/web/view/load``) to a function returning a response
|
||||
* to the query.
|
||||
* * ``instance,connection`` grows a ``responses`` attribute which is
|
||||
* a map of the same (and is in fact initialized to the ``responses``
|
||||
* parameter if one is provided)
|
||||
*
|
||||
* Note that RPC requests to un-mocked URLs will be rejected with an
|
||||
* error message: only explicitly specified urls will get a response.
|
||||
*
|
||||
* Mocked connections will *never* perform an actual RPC connection.
|
||||
*
|
||||
* @param instance openerp instance being initialized
|
||||
* @param {Object} [responses]
|
||||
*/
|
||||
mockifyRPC: function (instance, responses) {
|
||||
var connection = instance.connection;
|
||||
connection.responses = responses || {};
|
||||
connection.rpc_function = function (url, payload) {
|
||||
var fn = this.responses[url.url + ':' + payload.params.method]
|
||||
|| this.responses[url.url];
|
||||
|
||||
if (!fn) {
|
||||
return $.Deferred().reject({}, 'failed',
|
||||
_.str.sprintf("Url %s not found in mock responses, with arguments %s",
|
||||
url.url, JSON.stringify(payload.params))
|
||||
).promise();
|
||||
}
|
||||
return $.when(fn(payload));
|
||||
};
|
||||
},
|
||||
/**
|
||||
* Creates an openerp web instance loading the specified module after
|
||||
* all of its dependencies.
|
||||
*
|
||||
* @param {String} module
|
||||
* @returns OpenERP Web instance
|
||||
*/
|
||||
instanceFor: function (module) {
|
||||
var instance = openerp.init([]);
|
||||
this._load(instance, module);
|
||||
return instance;
|
||||
},
|
||||
_load: function (instance, module, loaded) {
|
||||
if (!loaded) { loaded = []; }
|
||||
|
||||
var deps = dependencies[module];
|
||||
if (!deps) { throw new Error("Unknown dependencies for " + module); }
|
||||
|
||||
var to_load = _.difference(deps, loaded);
|
||||
while (!_.isEmpty(to_load)) {
|
||||
this._load(instance, to_load[0], loaded);
|
||||
to_load = _.difference(deps, loaded);
|
||||
}
|
||||
openerp.web[module](instance);
|
||||
loaded.push(module);
|
||||
}
|
||||
}
|
||||
})();
|
|
@ -0,0 +1,63 @@
|
|||
# Ukrainian translation for openerp-web
|
||||
# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012
|
||||
# This file is distributed under the same license as the openerp-web package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, 2012.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-07-02 09:06+0200\n"
|
||||
"PO-Revision-Date: 2012-07-22 09:32+0000\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Ukrainian <uk@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-07-23 05:21+0000\n"
|
||||
"X-Generator: Launchpad (build 15654)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:60
|
||||
msgid "Edit Layout"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/js/dashboard.js:106
|
||||
msgid "Are you sure you want to remove this item ?"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:4
|
||||
msgid "Reset Layout.."
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:6
|
||||
msgid "Reset"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:8
|
||||
msgid "Change Layout.."
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:10
|
||||
msgid "Change Layout"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:27
|
||||
msgid " "
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:28
|
||||
msgid "Create"
|
||||
msgstr ""
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_dashboard/static/src/xml/web_dashboard.xml:39
|
||||
msgid "Choose dashboard layout"
|
||||
msgstr ""
|
|
@ -359,6 +359,9 @@
|
|||
.openerp .oe_kanban_view .oe_kanban_card .oe_dropdown_kanban {
|
||||
margin-top: 4px;
|
||||
}
|
||||
.openerp .oe_kanban_view .oe_kanban_card .oe_dropdown_kanban .oe_kanban_project_times li {
|
||||
float: left;
|
||||
}
|
||||
.openerp .oe_kanban_view .oe_kanban_star {
|
||||
float: left;
|
||||
position: inline-block;
|
||||
|
@ -401,9 +404,6 @@
|
|||
position: relative;
|
||||
top: 2px;
|
||||
}
|
||||
.openerp .oe_kanban_view .oe_kanban_project_times li {
|
||||
float: left;
|
||||
}
|
||||
.openerp .oe_kanban_view .oe_kanban_status {
|
||||
position: relative;
|
||||
top: 4px;
|
||||
|
@ -471,30 +471,25 @@
|
|||
visibility: hidden;
|
||||
}
|
||||
.openerp .oe_kanban_view .oe_kanban_colorpicker {
|
||||
padding: 3px 6px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.openerp .oe_kanban_view .oe_kanban_colorpicker li {
|
||||
float: left;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.openerp .oe_kanban_view .oe_kanban_colorpicker li a {
|
||||
display: inline-block;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border: 1px solid white;
|
||||
}
|
||||
.openerp .oe_kanban_view .oe_kanban_colorpicker li a:hover {
|
||||
border: 1px solid gray !important;
|
||||
}
|
||||
.openerp .oe_kanban_view .oe_kanban_colorpicker li:first-child a {
|
||||
margin-top: 1px;
|
||||
height: 16px;
|
||||
border: 1px solid #cccccc;
|
||||
}
|
||||
.openerp .oe_kanban_view .oe_kanban_colorpicker li:first-child a:hover {
|
||||
margin-top: 0px;
|
||||
height: 18px;
|
||||
}
|
||||
.openerp .oe_kanban_view .oe_kanban_color_0 {
|
||||
background-color: white;
|
||||
}
|
||||
|
|
|
@ -307,6 +307,10 @@
|
|||
text-decoration: none
|
||||
.oe_dropdown_kanban
|
||||
margin-top: 4px
|
||||
.oe_kanban_project_times
|
||||
li
|
||||
float: left
|
||||
|
||||
.oe_kanban_star
|
||||
float: left
|
||||
position: inline-block
|
||||
|
@ -337,9 +341,6 @@
|
|||
position: relative
|
||||
top: 2px
|
||||
|
||||
.oe_kanban_project_times li
|
||||
float: left
|
||||
|
||||
.oe_kanban_status
|
||||
position: relative
|
||||
top: 4px
|
||||
|
@ -387,24 +388,20 @@
|
|||
// }}}
|
||||
// KanbanColorPicker {{{
|
||||
.oe_kanban_colorpicker
|
||||
padding: 3px 6px
|
||||
white-space: nowrap
|
||||
.oe_kanban_colorpicker li
|
||||
float: left
|
||||
margin: 0
|
||||
padding: 0
|
||||
a
|
||||
display: inline-block
|
||||
width: 18px
|
||||
height: 18px
|
||||
width: 16px
|
||||
height: 16px
|
||||
border: 1px solid white
|
||||
a:hover
|
||||
border: 1px solid gray !important
|
||||
.oe_kanban_colorpicker li:first-child a
|
||||
margin-top: 1px
|
||||
height: 16px
|
||||
border: 1px solid #ccc
|
||||
&:hover
|
||||
margin-top: 0px
|
||||
height: 18px
|
||||
// }}}
|
||||
// KanbanColors {{{
|
||||
.oe_kanban_color_0
|
||||
|
|
|
@ -8,14 +8,14 @@ msgstr ""
|
|||
"Project-Id-Version: openerp-web\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-07-02 09:06+0200\n"
|
||||
"PO-Revision-Date: 2012-03-29 11:39+0000\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"PO-Revision-Date: 2012-07-24 06:25+0000\n"
|
||||
"Last-Translator: Tor Syversen <sol-moe@online.no>\n"
|
||||
"Language-Team: Norwegian Bokmal <nb@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-07-03 05:55+0000\n"
|
||||
"X-Generator: Launchpad (build 15531)\n"
|
||||
"X-Launchpad-Export-Date: 2012-07-25 04:52+0000\n"
|
||||
"X-Generator: Launchpad (build 15679)\n"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_process/static/src/js/process.js:261
|
||||
|
@ -85,12 +85,12 @@ msgstr "Notater:"
|
|||
#. openerp-web
|
||||
#: addons/web_process/static/src/xml/web_process.xml:59
|
||||
msgid "Last modified by:"
|
||||
msgstr ""
|
||||
msgstr "Sist revidert av:"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_process/static/src/xml/web_process.xml:59
|
||||
msgid "N/A"
|
||||
msgstr ""
|
||||
msgstr "N/A"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_process/static/src/xml/web_process.xml:62
|
||||
|
@ -105,7 +105,7 @@ msgstr ""
|
|||
#. openerp-web
|
||||
#: addons/web_process/static/src/xml/web_process.xml:88
|
||||
msgid "Select Process"
|
||||
msgstr ""
|
||||
msgstr "Velg prosess"
|
||||
|
||||
#. openerp-web
|
||||
#: addons/web_process/static/src/xml/web_process.xml:98
|
||||
|
@ -115,4 +115,4 @@ msgstr "Velg"
|
|||
#. openerp-web
|
||||
#: addons/web_process/static/src/xml/web_process.xml:109
|
||||
msgid "Edit Process"
|
||||
msgstr ""
|
||||
msgstr "Rediger prosess"
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
Notes on the usage of the Form View as a sub-widget
|
||||
===================================================
|
||||
|
||||
Undocumented stuff
|
||||
------------------
|
||||
|
||||
* ``initial_mode`` *option* defines the starting mode of the form
|
||||
view, one of ``view`` and ``edit`` (?). Default value is ``view``
|
||||
(non-editable form).
|
||||
|
||||
* ``embedded_view`` *attribute* has to be set separately when
|
||||
providing a view directly, no option available for that usage.
|
||||
|
||||
* View arch **must** contain node with
|
||||
``@class="oe_form_container"``, otherwise everything will break
|
||||
without any info
|
||||
|
||||
* Root element of view arch not being ``form`` may or may not work
|
||||
correctly, no idea.
|
||||
|
||||
* Freeform views => ``@version="7.0"``
|
||||
|
||||
* Form is not entirely loaded (some widgets may not appear) unless
|
||||
``on_record_loaded`` is called (or ``do_show``, which itself calls
|
||||
``on_record_loaded``).
|
||||
|
||||
* "Empty" form => ``on_button_new`` (...), or manually call
|
||||
``default_get`` + ``on_record_loaded``
|
||||
|
||||
* Form fields default to width: 100%, padding, !important margin, can
|
||||
be reached via ``.oe_form_field``
|
||||
|
||||
* Form *will* render buttons and a pager, offers options to locate
|
||||
both outside of form itself (``$buttons`` and ``$pager``), providing
|
||||
empty jquery objects (``$()``) seems to stop displaying both but not
|
||||
sure if there are deleterious side-effects.
|
||||
|
||||
Other options:
|
||||
|
||||
* Pass in ``$(document.createDocumentFragment)`` to ensure it's a
|
||||
DOM-compatible tree completely outside of the actual DOM.
|
||||
|
||||
* ???
|
||||
|
||||
* readonly fields probably don't have a background, beware if need of
|
||||
overlay
|
||||
|
||||
* What is the difference between ``readonly`` and
|
||||
``effective_readonly``?
|
||||
|
||||
* No facilities for DOM events handling/delegations e.g. handling
|
||||
keyup/keydown/keypress from a form fields into the form's user.
|
||||
|
||||
* Also no way to reverse from a DOM node (e.g. DOMEvent#target) back to a
|
||||
form view field easily
|
|
@ -19,6 +19,9 @@ Contents:
|
|||
widget
|
||||
search-view
|
||||
|
||||
list-view
|
||||
form-notes
|
||||
|
||||
Older stuff
|
||||
-----------
|
||||
|
||||
|
|
|
@ -0,0 +1,466 @@
|
|||
List View
|
||||
=========
|
||||
|
||||
Style Hooks
|
||||
-----------
|
||||
|
||||
The list view provides a few style hook classes for re-styling of list views in
|
||||
various situations:
|
||||
|
||||
``.oe_list``
|
||||
|
||||
The root element of the list view, styling rules should be rooted
|
||||
on that class.
|
||||
|
||||
``table.oe_list_content``
|
||||
|
||||
The root table for the listview, accessory components may be
|
||||
generated or added outside this section, this is the list view
|
||||
"proper".
|
||||
|
||||
``.oe_list_buttons``
|
||||
|
||||
The action buttons array for the list view, with its sub-elements
|
||||
|
||||
``.oe_list_add``
|
||||
|
||||
The default "Create"/"Add" button of the list view
|
||||
|
||||
``.oe_alternative``
|
||||
|
||||
The "alternative choice" for the list view, by default text
|
||||
along the lines of "or import" with a link.
|
||||
|
||||
``.oe_list_field_cell``
|
||||
|
||||
The cell (``td``) for a given field of the list view, cells which
|
||||
are *not* fields (e.g. name of a group, or number of items in a
|
||||
group) will not have this class. The field cell can be further
|
||||
specified:
|
||||
|
||||
``.oe_number``
|
||||
|
||||
Numeric cell types (integer and float)
|
||||
|
||||
``.oe_button``
|
||||
|
||||
Action button (``button`` tag in the view) inside the cell
|
||||
|
||||
``.oe_readonly``
|
||||
|
||||
Readonly field cell
|
||||
|
||||
``.oe_list_field_$type``
|
||||
|
||||
Additional class for the precise type of the cell, ``$type``
|
||||
is the field's @widget if there is one, otherwise it's the
|
||||
field's type.
|
||||
|
||||
``.oe_list_record_selector``
|
||||
|
||||
Selector cells
|
||||
|
||||
Editable list view
|
||||
++++++++++++++++++
|
||||
|
||||
The editable list view module adds a few supplementary style hook
|
||||
classes, for edition situations:
|
||||
|
||||
``.oe_editing``
|
||||
|
||||
Added to both ``.oe_list`` and ``.oe_list_button`` (as the
|
||||
buttons may be outside of the list view) when a row of the list is
|
||||
currently being edited.
|
||||
|
||||
``tr.oe_edition``
|
||||
|
||||
Class set on the row being edited itself. Note that the edition
|
||||
form is *not* contained within the row, this allows for styling or
|
||||
modifying the row while it's being edited separately. Mostly for
|
||||
fields which can not be edited (e.g. read-only fields).
|
||||
|
||||
|
||||
Editable list view
|
||||
------------------
|
||||
|
||||
List view edition is an extension to the base listview providing the
|
||||
capability of inline record edition by delegating to an embedded form
|
||||
view.
|
||||
|
||||
Editability status
|
||||
++++++++++++++++++
|
||||
|
||||
The editability status of a list view can be queried through the
|
||||
:js:func:`~openerp.web.ListView.editable` method, will return a falsy
|
||||
value if the listview is not currently editable.
|
||||
|
||||
The editability status is based on three flags:
|
||||
|
||||
``tree/@editable``
|
||||
|
||||
If present, can be either ``"top"`` or ``"bottom"``. Either will
|
||||
make the list view editable, with new records being respectively
|
||||
created at the top or at the bottom of the view.
|
||||
|
||||
``context.set_editable``
|
||||
|
||||
Boolean flag extracted from a search context (during the
|
||||
:js:func:`~openerp.web.ListView.do_search`` handler), ``true``
|
||||
will make the view editable (from the top), ``false`` or the
|
||||
absence of the flag is a noop.
|
||||
|
||||
``defaults.editable``
|
||||
|
||||
Like ``tree/@editable``, one of absent (``null``)), ``"top"`` or
|
||||
``"bottom"``, fallback for the list view if none of the previous
|
||||
two flags are set.
|
||||
|
||||
These three flags can only *make* a listview editable, they can *not*
|
||||
override a previously set flag. To do that, a listview user should
|
||||
instead cancel :ref:`the edit:before event <listview-edit-before>`.
|
||||
|
||||
The editable list view module adds a number of methods to the list
|
||||
view, on top of implementing the :js:class:`EditorDelegate` protocol:
|
||||
|
||||
Interaction Methods
|
||||
+++++++++++++++++++
|
||||
|
||||
.. js:function:: openerp.web.ListView.ensure_saved
|
||||
|
||||
Attempts to resolve the pending edition, if any, by saving the
|
||||
edited row's current state.
|
||||
|
||||
:returns: delegate resolving to all editions having been saved, or
|
||||
rejected if a pending edition could not be saved
|
||||
(e.g. validation failure)
|
||||
|
||||
.. js:function:: openerp.web.ListView.start_edition([record][, options])
|
||||
|
||||
Starts editing the provided record inline, through an overlay form
|
||||
view of editable fields in the record.
|
||||
|
||||
If no record is provided, creates a new one according to the
|
||||
editability configuration of the list view.
|
||||
|
||||
This method resolves any pending edition when invoked, before
|
||||
starting a new edition.
|
||||
|
||||
:param record: record to edit, or null to create a new record
|
||||
:type record: :js:class:`~openerp.web.list.Record`
|
||||
:param EditOptions options:
|
||||
:returns: delegate to the form used for the edition
|
||||
|
||||
.. js:function:: openerp.web.ListView.save_edition
|
||||
|
||||
Resolves the pending edition.
|
||||
|
||||
:returns: delegate to the save being completed, resolves to an
|
||||
object with two attributes ``created`` (flag indicating
|
||||
whether the saved record was just created or was
|
||||
updated) and ``record`` the reloaded record having been
|
||||
edited.
|
||||
|
||||
.. js:function:: openerp.web.ListView.cancel_edition
|
||||
|
||||
Cancels pending edition, cleans up the list view in case of
|
||||
creation (removes the empty record being created).
|
||||
|
||||
Utility Methods
|
||||
+++++++++++++++
|
||||
|
||||
.. js:function:: openerp.web.ListView.get_cells_for(row)
|
||||
|
||||
Extracts the cells from a listview row, and puts them in a
|
||||
{fieldname: cell} mapping for analysis and manipulation.
|
||||
|
||||
:param jQuery row:
|
||||
:rtype: Object
|
||||
|
||||
.. js:function:: openerp.web.ListView.with_event(event_name, event, action[, args][, trigger_params])
|
||||
|
||||
Executes ``action`` in the context of the view's editor,
|
||||
bracketing it with cancellable event signals.
|
||||
|
||||
:param String event_name: base name for the bracketing event, will
|
||||
be postfixed by ``:before`` and
|
||||
``:after`` before being called
|
||||
(respectively before and after
|
||||
``action`` is executed)
|
||||
:param Object event: object passed to the ``:before`` event
|
||||
handlers.
|
||||
:param Function action: function called with the view's editor as
|
||||
its ``this``. May return a deferred.
|
||||
:param Array args: arguments passed to ``action``
|
||||
:param Array trigger_params: arguments passed to the ``:after``
|
||||
event handler alongside the results
|
||||
of ``action``
|
||||
|
||||
Behavioral Customizations
|
||||
+++++++++++++++++++++++++
|
||||
|
||||
.. js:function:: openerp.web.ListView.handle_onwrite(record)
|
||||
|
||||
Implements the handling of the ``onwrite`` listview attribute:
|
||||
calls the RPC methods specified by ``@onwrite``, and if that
|
||||
method returns an array of ids loads or reloads the records
|
||||
corresponding to those ids.
|
||||
|
||||
:param record: record being written having triggered the
|
||||
``onwrite`` callback
|
||||
:type record: openerp.web.list.Record
|
||||
:returns: deferred to all reloadings being done
|
||||
|
||||
Events
|
||||
++++++
|
||||
|
||||
For simpler interactions by/with external users of the listview, the
|
||||
view provides a number of dedicated events to its lifecycle.
|
||||
|
||||
.. note:: if an event is defined as *cancellable*, it means its first
|
||||
parameter is an object on which the ``cancel`` attribute can
|
||||
be set. If the ``cancel`` attribute is set, the view will
|
||||
abort its current behavior as soon as possible, and rollback
|
||||
any state modification.
|
||||
|
||||
Generally speaking, an event should only be cancelled (by
|
||||
setting the ``cancel`` flag to ``true``), uncancelling an
|
||||
event is undefined as event handlers are executed on a
|
||||
first-come-first-serve basis and later handlers may
|
||||
re-cancel an uncancelled event.
|
||||
|
||||
.. _listview-edit-before:
|
||||
|
||||
``edit:before`` *cancellable*
|
||||
|
||||
Invoked before the list view starts editing a record.
|
||||
|
||||
Provided with an event object with a single property ``record``,
|
||||
holding the attributes of the record being edited (``record`` is
|
||||
empty *but not null* for a new record)
|
||||
|
||||
``edit:after``
|
||||
|
||||
Invoked after the list view has gone into an edition state,
|
||||
provided with the attributes of the record being edited (see
|
||||
``edit:before``) as first parameter and the form used for the
|
||||
edition as second parameter.
|
||||
|
||||
``save:before`` *cancellable*
|
||||
|
||||
Invoked right before saving a pending edition, provided with an
|
||||
event object holding the listview's editor (``editor``) and the
|
||||
edition form (``form``)
|
||||
|
||||
``save:after``
|
||||
|
||||
Invoked after a save has been completed
|
||||
|
||||
``cancel:before`` *cancellable*
|
||||
|
||||
Invoked before cancelling a pending edition, provided with the
|
||||
same information as ``save:before``.
|
||||
|
||||
``cancel:after``
|
||||
|
||||
Invoked after a pending edition has been cancelled.
|
||||
|
||||
DOM events
|
||||
++++++++++
|
||||
|
||||
The list view has grown hooks for the ``keyup`` event on its edition
|
||||
form (during edition): any such event bubbling out of the edition form
|
||||
will be forwarded to a method ``keyup_EVENTNAME``, where ``EVENTNAME``
|
||||
is the name of the key in ``$.ui.keyCode``.
|
||||
|
||||
The method will also get the event object (originally passed to the
|
||||
``keyup`` handler) as its sole parameter.
|
||||
|
||||
The base editable list view has handlers for the ``ENTER`` and
|
||||
``ESCAPE`` keys.
|
||||
|
||||
Editor
|
||||
------
|
||||
|
||||
The list-edition modules does not generally interact with the embedded
|
||||
formview, delegating instead to its
|
||||
:js:class:`~openerp.web.list.Editor`.
|
||||
|
||||
.. js:class:: openerp.web.list.Editor(parent[, options])
|
||||
|
||||
The editor object provides a more convenient interface to form
|
||||
views, and simplifies the usage of form views for semi-arbitrary
|
||||
edition of stuff.
|
||||
|
||||
However, the editor does *not* task itself with being internally
|
||||
consistent at this point: calling
|
||||
e.g. :js:func:`~openerp.web.list.Editor.edit` multiple times in a
|
||||
row without saving or cancelling each edit is undefined.
|
||||
|
||||
:param parent:
|
||||
:type parent: :js:class:`~openerp.web.Widget`
|
||||
:param EditorOptions options:
|
||||
|
||||
.. js:function:: openerp.web.list.Editor.is_editing([record_state])
|
||||
|
||||
Indicates whether the editor is currently in the process of
|
||||
providing edition for a record.
|
||||
|
||||
Can be filtered by the state of the record being edited
|
||||
(whether it's a record being *created* or a record being
|
||||
*altered*), in which case it asserts both that an edition is
|
||||
underway and that the record being edited respectively does
|
||||
not yet exist in the database or already exists there.
|
||||
|
||||
:param record_state: state of the record being edited.
|
||||
Either ``"new"`` or ``"edit"``.
|
||||
:type record_state: String
|
||||
:rtype: Boolean
|
||||
|
||||
.. js:function:: openerp.web.list.Editor.edit(record, configureField[, options])
|
||||
|
||||
Loads the provided record into the internal form view and
|
||||
displays the form view.
|
||||
|
||||
Will also attempt to focus the first visible field of the form
|
||||
view.
|
||||
|
||||
:param Object record: record to load into the form view
|
||||
(key:value mapping similar to the result
|
||||
of a ``read``)
|
||||
:param configureField: function called with each field of the
|
||||
form view right after the form is
|
||||
displayed, lets whoever called this
|
||||
method do some last-minute
|
||||
configuration of form fields.
|
||||
:type configureField: Function<String, openerp.web.form.Field>
|
||||
:param EditOptions options:
|
||||
:returns: jQuery delegate to the form object
|
||||
|
||||
.. js:function:: openerp.web.list.Editor.save
|
||||
|
||||
Attempts to save the internal form, then hide it
|
||||
|
||||
:returns: delegate to the record under edition (with ``id``
|
||||
added for a creation). The record is not updated
|
||||
from when it was passed in, aside from the ``id``
|
||||
attribute.
|
||||
|
||||
.. js:function:: openerp.web.list.Editor.cancel
|
||||
|
||||
Attemps to cancel the edition of the internal form, then hide
|
||||
the form
|
||||
|
||||
:returns: delegate to the record under edition
|
||||
|
||||
.. js:class:: EditorOptions
|
||||
|
||||
.. js:attribute:: EditorOptions.formView
|
||||
|
||||
Form view (sub)-class to instantiate and delegate edition to.
|
||||
|
||||
By default, :js:class:`~openerp.web.FormView`
|
||||
|
||||
.. js:attribute:: EditorOptions.delegate
|
||||
|
||||
Object used to get various bits of information about how to
|
||||
display stuff.
|
||||
|
||||
By default, uses the editor's parent widget. See
|
||||
:js:class:`EditorDelegate` for the methods and attributes to
|
||||
provide.
|
||||
|
||||
.. js:class:: EditorDelegate
|
||||
|
||||
Informal protocol defining the methods and attributes expected of
|
||||
the :js:class:`~openerp.web.list.Editor`'s delegate.
|
||||
|
||||
.. js:attribute:: EditorDelegate.dataset
|
||||
|
||||
The dataset passed to the form view to synchronize the form
|
||||
view and the outer widget.
|
||||
|
||||
.. js:function:: EditorDelegate.edition_view(editor)
|
||||
|
||||
Called by the :js:class:`~openerp.web.list.Editor` object to
|
||||
get a form view (JSON) to pass along to the form view it
|
||||
created.
|
||||
|
||||
The result should be a valid form view, see :doc:`Form Notes
|
||||
<form-notes>` for various peculiarities of the form view
|
||||
format.
|
||||
|
||||
:param editor: editor object asking for the view
|
||||
:type editor: :js:class:`~openerp.web.list.Editor`
|
||||
:returns: form view
|
||||
:rtype: Object
|
||||
|
||||
.. js:function:: EditorDelegate.prepends_on_create
|
||||
|
||||
By default, the :js:class:`~openerp.web.list.Editor` will
|
||||
append the ids of newly created records to the
|
||||
:js:attr:`EditorDelegate.dataset`. If this method returns
|
||||
``true``, it will prepend these ids instead.
|
||||
|
||||
:returns: whether new records should be prepended to the
|
||||
dataset (instead of appended)
|
||||
:rtype: Boolean
|
||||
|
||||
|
||||
.. js:class:: EditOptions
|
||||
|
||||
Options object optionally passed into a method starting an edition
|
||||
to configure its setup and behavior
|
||||
|
||||
.. js:attribute:: focus_field
|
||||
|
||||
Name of the field to set focus on after setting up the edition
|
||||
of the record.
|
||||
|
||||
If this option is not provided, or the requested field can not
|
||||
be focused (invisible, readonly or not in the view), the first
|
||||
visible non-readonly field is focused.
|
||||
|
||||
Changes from 6.1
|
||||
----------------
|
||||
|
||||
* The editable listview behavior has been rewritten pretty much from
|
||||
scratch, any code touching on editability will have to be modified
|
||||
|
||||
* The overloading of :js:class:`~openerp.web.ListView.Groups` and
|
||||
:js:class:`~openerp.web.ListView.List` for editability has been
|
||||
drastically simplified, and most of the behavior has been moved to
|
||||
the list view itself. Only
|
||||
:js:func:`~openerp.web.ListView.List.row_clicked` is still
|
||||
overridden.
|
||||
|
||||
* A new method ``get_row_for(record) -> jQuery(tr) | null`` has been
|
||||
added to both ListView.List and ListView.Group, it can be called
|
||||
from the list view to get the table row matching a record (if such
|
||||
a row exists).
|
||||
|
||||
* :js:func:`~openerp.web.ListView.do_button_action`'s core behavior
|
||||
has been split away to
|
||||
:js:func:`~openerp.web.ListView.handle_button`. This allows bypassing
|
||||
overrides of :js:func:`~openerp.web.ListView.do_button_action` in a
|
||||
parent class.
|
||||
|
||||
Ideally, :js:func:`~openerp.web.ListView.handle_button` should not be
|
||||
overridden.
|
||||
|
||||
* Modifiers handling has been improved (all modifiers information
|
||||
should now be available through :js:func:`~Column.modifiers_for`,
|
||||
not just ``invisible``)
|
||||
|
||||
* Changed some handling of the list view's record: a record may now
|
||||
have no id, and the listview will handle that correctly (for new
|
||||
records being created) as well as correctly handle the ``id`` being
|
||||
set.
|
||||
|
||||
* Extended the internal collections structure of the list view with
|
||||
`#find`_, `#succ`_ and `#pred`_.
|
||||
|
||||
.. _#find: http://underscorejs.org/#find
|
||||
|
||||
.. _#succ: http://hackage.haskell.org/packages/archive/base/latest/doc/html/Prelude.html#v:succ
|
||||
|
||||
.. _#pred: http://hackage.haskell.org/packages/archive/base/latest/doc/html/Prelude.html#v:pred
|
Loading…
Reference in New Issue