summaryrefslogtreecommitdiff
path: root/addons/web/static/src
diff options
context:
space:
mode:
authorAntony Lesuisse <al@openerp.com>2012-10-03 00:07:20 +0200
committerAntony Lesuisse <al@openerp.com>2012-10-03 00:07:20 +0200
commit4f299e1b1bde579f85df2632882bb0082dffaf1b (patch)
tree37fe6444dbe4a82df1106d269ad58bbdc61c8698 /addons/web/static/src
parent49956d869e5c360b936a0dc0e63e89ffe3b8ef6b (diff)
parent848a08f93463bc187e4ec2e4f73917f6845237fd (diff)
[MERGE] trunk
bzr revid: al@openerp.com-20121002220720-z0rjvmjx8kvy52zh
Diffstat (limited to 'addons/web/static/src')
-rw-r--r--addons/web/static/src/css/base.css334
-rw-r--r--addons/web/static/src/css/base.sass241
-rw-r--r--addons/web/static/src/js/chrome.js20
-rw-r--r--addons/web/static/src/js/corelib.js15
-rw-r--r--addons/web/static/src/js/data.js363
-rw-r--r--addons/web/static/src/js/formats.js3
-rw-r--r--addons/web/static/src/js/search.js9
-rw-r--r--addons/web/static/src/js/view_form.js211
-rw-r--r--addons/web/static/src/js/view_list.js56
-rw-r--r--addons/web/static/src/js/views.js6
-rw-r--r--addons/web/static/src/xml/base.xml33
11 files changed, 652 insertions, 639 deletions
diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css
index 1ec0d534607..aa9d5eab36f 100644
--- a/addons/web/static/src/css/base.css
+++ b/addons/web/static/src/css/base.css
@@ -515,6 +515,28 @@
.openerp .oe_webclient .oe_star_on {
color: gold;
}
+.openerp header {
+ position: relative;
+ border-bottom: 1px solid #cacaca;
+ padding-left: 2px;
+ background-color: #fcfcfc;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#fcfcfc), to(#dedede));
+ background-image: -webkit-linear-gradient(top, #fcfcfc, #dedede);
+ background-image: -moz-linear-gradient(top, #fcfcfc, #dedede);
+ background-image: -ms-linear-gradient(top, #fcfcfc, #dedede);
+ background-image: -o-linear-gradient(top, #fcfcfc, #dedede);
+ background-image: linear-gradient(to bottom, #fcfcfc, #dedede);
+}
+.openerp header > span {
+ margin-left: 4px;
+}
+.openerp header ul {
+ display: inline-block;
+ float: right;
+}
+.openerp header .oe_button {
+ margin: 3px 2px 1px;
+}
.openerp .oe_tag {
border: 1px solid #afafb6;
font-size: 11px;
@@ -546,11 +568,6 @@
font-style: italic;
text-decoration: none;
}
-.openerp .oe_form header .oe_tags {
- margin: 5px 0 0 5px;
- width: 400px;
- padding-bottom: 0;
-}
.openerp.oe_tooltip {
font-size: 12px;
}
@@ -716,7 +733,7 @@
text-indent: -99999px;
vertical-align: top;
margin-top: 8px;
- margin-left: 4px;
+ margin-left: 3px;
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-top: 4px solid #404040;
@@ -967,7 +984,8 @@
}
.openerp .oe_topbar {
width: 100%;
- height: 31px;
+ height: 32px;
+ background-color: #414141;
background-color: #646060;
background-image: -webkit-gradient(linear, left top, left bottom, from(#646060), to(#262626));
background-image: -webkit-linear-gradient(top, #646060, #262626);
@@ -981,16 +999,23 @@
padding: 5px 10px 7px;
line-height: 20px;
height: 20px;
+ text-decoration: none;
color: #eeeeee;
vertical-align: top;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
+ -webkit-transition: all 0.2s ease-out;
+ -moz-transition: all 0.2s ease-out;
+ -ms-transition: all 0.2s ease-out;
+ -o-transition: all 0.2s ease-out;
+ transition: all 0.2s ease-out;
}
.openerp .oe_topbar .oe_topbar_item:hover {
- background: #303030;
+ background: rgba(0, 0, 0, 0.2);
+ text-shadow: black 0px 0px 3px;
color: white;
- -moz-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset;
- -webkit-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset;
- box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset;
+ -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4) inset;
+ -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4) inset;
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4) inset;
}
.openerp .oe_topbar .oe_topbar_avatar {
width: 24px;
@@ -1040,6 +1065,70 @@
-webkit-box-shadow: none;
box-shadow: none;
}
+.openerp .oe_menu {
+ float: left;
+ padding: 0;
+ margin: 0;
+ font-size: 13px;
+}
+.openerp .oe_menu > li {
+ list-style-type: none;
+ padding: 0;
+ margin: 0;
+ float: left;
+ display: block;
+ color: #eeeeee;
+}
+.openerp .oe_menu > li > a {
+ display: block;
+ padding: 5px 10px 7px;
+ line-height: 20px;
+ height: 20px;
+ text-decoration: none;
+ color: #eeeeee;
+ vertical-align: top;
+ text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
+ -webkit-transition: all 0.2s ease-out;
+ -moz-transition: all 0.2s ease-out;
+ -ms-transition: all 0.2s ease-out;
+ -o-transition: all 0.2s ease-out;
+ transition: all 0.2s ease-out;
+}
+.openerp .oe_menu > li > a:hover {
+ background: rgba(0, 0, 0, 0.2);
+ text-shadow: black 0px 0px 3px;
+ color: white;
+ -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4) inset;
+ -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4) inset;
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4) inset;
+}
+.openerp .oe_menu > li > .oe_active {
+ background: rgba(0, 0, 0, 0.2);
+ text-shadow: black 0px 0px 3px;
+ font-weight: bold;
+ -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4) inset;
+ -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4) inset;
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4) inset;
+}
+.openerp .oe_user_menu {
+ float: right;
+ padding: 0;
+ margin: 0;
+}
+.openerp .oe_user_menu li {
+ list-style-type: none;
+ float: left;
+}
+.openerp .oe_user_menu .oe_dropdown_menu {
+ right: -1px;
+}
+.openerp .oe_systray > div {
+ float: left;
+ padding: 0 4px 0 4px;
+}
+.openerp .oe_systray {
+ float: right;
+}
.openerp .oe_leftbar {
display: none;
width: 220px;
@@ -1078,102 +1167,6 @@
color: #c81010;
font-style: italic;
}
-.openerp .oe_user_menu {
- float: right;
- padding: 0;
- margin: 0;
-}
-.openerp .oe_user_menu li {
- list-style-type: none;
- float: left;
-}
-.openerp .oe_user_menu .oe_dropdown_menu {
- right: -1px;
-}
-.openerp .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 .oe_about a {
- color: #7c7bad;
-}
-.openerp .oe_about a:hover {
- text-decoration: underline;
-}
-.openerp .oe_about a:focus {
- outline: none;
-}
-.openerp .oe_about .oe_logo {
- margin-left: -6px;
-}
-.openerp .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 .oe_about .oe_bottom a {
- color: #eeeeee;
-}
-.openerp .oe_systray > div {
- float: left;
- padding: 0 4px 0 4px;
-}
-.openerp .oe_systray {
- float: right;
-}
-.openerp .oe_menu {
- float: left;
- padding: 0;
- margin: 0;
-}
-.openerp .oe_menu > li {
- list-style-type: none;
- padding: 0;
- margin: 0;
- float: left;
-}
-.openerp .oe_menu > li > a {
- display: block;
- padding: 5px 10px 7px;
- line-height: 20px;
- height: 20px;
- color: #eeeeee;
- vertical-align: top;
- text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
-}
-.openerp .oe_menu > li > a:hover {
- background: #303030;
- color: white;
- -moz-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset;
- -webkit-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset;
- box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset;
-}
-.openerp .oe_menu > li > .oe_active {
- background: #303030;
- font-weight: bold;
- color: white;
- -moz-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset;
- -webkit-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset;
- box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset;
-}
.openerp .oe_secondary_menu_section {
font-weight: bold;
margin-left: 8px;
@@ -1205,7 +1198,7 @@
color: white;
padding: 2px 4px;
margin: 1px 6px 0 0;
- border: 1px solid lightgrey;
+ border: 1px solid lightGray;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
-moz-border-radius: 4px;
-webkit-border-radius: 4px;
@@ -1230,7 +1223,7 @@
transform: scale(1.1);
}
.openerp .oe_secondary_submenu .oe_active {
- border-top: 1px solid lightgrey;
+ border-top: 1px solid lightGray;
border-bottom: 1px solid #dedede;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
-moz-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.2), inset 0 -1px 3px rgba(40, 40, 40, 0.2);
@@ -1280,6 +1273,48 @@
border-right: 4px solid transparent;
border-top: 4px solid #4c4c4c;
}
+.openerp .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 .oe_about a {
+ color: #7c7bad;
+}
+.openerp .oe_about a:hover {
+ text-decoration: underline;
+}
+.openerp .oe_about a:focus {
+ outline: none;
+}
+.openerp .oe_about .oe_logo {
+ margin-left: -6px;
+}
+.openerp .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 .oe_about .oe_bottom a {
+ color: #eeeeee;
+}
.openerp .oe_application {
width: 100%;
}
@@ -1476,7 +1511,6 @@
height: 100%;
}
.openerp .oe_view_manager_current > .oe_view_manager_header {
- border-top: 1px solid #cacaca;
border-bottom: 1px solid #cacaca;
background-color: #fcfcfc;
background-image: -webkit-gradient(linear, left top, left bottom, from(#fcfcfc), to(#dedede));
@@ -1997,63 +2031,46 @@
.openerp .oe_application .oe_form_sheet .oe_notebook_page {
padding: 0 16px;
}
-.openerp .oe_form header {
- position: relative;
- border-bottom: 1px solid #cacaca;
- padding-left: 2px;
- background-color: #fcfcfc;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#fcfcfc), to(#dedede));
- background-image: -webkit-linear-gradient(top, #fcfcfc, #dedede);
- background-image: -moz-linear-gradient(top, #fcfcfc, #dedede);
- background-image: -ms-linear-gradient(top, #fcfcfc, #dedede);
- background-image: -o-linear-gradient(top, #fcfcfc, #dedede);
- background-image: linear-gradient(to bottom, #fcfcfc, #dedede);
-}
-.openerp .oe_form header > span {
- margin-left: 4px;
-}
-.openerp .oe_form header ul {
- display: inline-block;
- float: right;
-}
-.openerp .oe_form header .oe_form_button {
- margin: 3px 2px 1px;
+.openerp .oe_form header .oe_tags {
+ margin: 5px 0 0 5px;
+ width: 400px;
+ padding-bottom: 0;
}
-.openerp .oe_form div.oe_chatter {
+.openerp .oe_form header .oe_tags div.oe_chatter {
min-width: 650px;
max-width: 860px;
margin: 0 auto;
padding: 16px 0 48px;
}
-.openerp .oe_form .oe_grey {
+.openerp .oe_form header .oe_tags .oe_grey {
color: #aaaaaa;
max-width: 650px;
margin: 0 0 10px 0;
}
-.openerp .oe_form div.oe_form_configuration p, .openerp .oe_form div.oe_form_configuration ul, .openerp .oe_form div.oe_form_configuration ol {
+.openerp .oe_form header .oe_tags div.oe_form_configuration p, .openerp .oe_form header .oe_tags div.oe_form_configuration ul, .openerp .oe_form header .oe_tags div.oe_form_configuration ol {
color: #aaaaaa;
max-width: 650px;
}
-.openerp .oe_form div.oe_form_configuration label {
+.openerp .oe_form header .oe_tags div.oe_form_configuration label {
min-width: 150px;
}
-.openerp .oe_form div.oe_form_configuration .oe_form_group_cell_label {
+.openerp .oe_form header .oe_tags div.oe_form_configuration .oe_form_group_cell_label {
padding: 1px 0;
}
-.openerp .oe_form div.oe_form_configuration .oe_form_group_cell div div {
+.openerp .oe_form header .oe_tags div.oe_form_configuration .oe_form_group_cell div div {
padding: 1px 0;
}
-.openerp .oe_form .oe_subtotal_footer {
+.openerp .oe_form header .oe_tags .oe_subtotal_footer {
width: 1% !important;
}
-.openerp .oe_form .oe_subtotal_footer td.oe_form_group_cell {
+.openerp .oe_form header .oe_tags .oe_subtotal_footer td.oe_form_group_cell {
text-align: right;
padding: 0 !important;
}
-.openerp .oe_form .oe_subtotal_footer td.oe_form_group_cell_label {
+.openerp .oe_form header .oe_tags .oe_subtotal_footer td.oe_form_group_cell_label {
border-right: none;
}
-.openerp .oe_form .oe_subtotal_footer .oe_subtotal_footer_separator {
+.openerp .oe_form header .oe_tags .oe_subtotal_footer .oe_subtotal_footer_separator {
width: 108px;
border-top: 1px solid #cacaca;
margin-top: 4px;
@@ -2061,14 +2078,14 @@
font-weight: bold;
font-size: 18px;
}
-.openerp .oe_form .oe_subtotal_footer label:after {
+.openerp .oe_form header .oe_tags .oe_subtotal_footer label:after {
content: ":";
}
-.openerp .oe_form .oe_subtotal_footer label.oe_subtotal_footer_separator {
+.openerp .oe_form header .oe_tags .oe_subtotal_footer label.oe_subtotal_footer_separator {
font-weight: bold !important;
padding: 2px 11px 2px 0px !important;
}
-.openerp .oe_form .oe_subtotal_footer label.oe_form_label_help {
+.openerp .oe_form header .oe_tags .oe_subtotal_footer label.oe_form_label_help {
font-weight: normal;
}
.openerp .oe_form .oe_form_button {
@@ -2100,7 +2117,7 @@
}
.openerp .oe_form .oe_form_label_help[for] span, .openerp .oe_form .oe_form_label[for] span {
font-size: 80%;
- color: darkgreen;
+ color: darkGreen;
vertical-align: top;
position: relative;
top: -4px;
@@ -2189,6 +2206,9 @@
min-width: 60px;
color: #1f1f1f;
}
+.openerp .oe_form textarea {
+ height: 32px;
+}
.openerp .oe_form input[readonly], .openerp .oe_form select[readonly], .openerp .oe_form textarea[readonly], .openerp .oe_form input[disabled], .openerp .oe_form select[disabled] {
background: #e5e5e5 !important;
color: #666666;
@@ -2745,27 +2765,33 @@
width: 100%;
background-color: white;
border-spacing: 0;
+ color: #4c4c4c;
}
.openerp .oe-treeview-table th {
padding: 10px;
- color: #4c4c4c;
font-weight: bold;
background-color: #f0f0f0;
border-bottom: 2px solid #cacaca;
}
-.openerp .oe-treeview-table .treeview-tr, .openerp .oe-treeview-table .treeview-td {
+.openerp .oe-treeview-table td {
cursor: pointer;
- border-right: 1px dotted #afafb6;
- vertical-align: top;
+ vertical-align: middle;
text-align: left;
- border-bottom: 1px solid #cfcccc;
-}
-.openerp .oe-treeview-table tr:hover {
- background-color: #e0e0f8;
+ vertical-align: middle;
+ height: 20px;
+ padding-left: 4px;
+ padding-right: 4px;
+ border-right: 1px solid #e7e7e7;
}
-.openerp .oe-treeview-table .oe-number {
+.openerp .oe-treeview-table td.oe_number {
text-align: right !important;
}
+.openerp .oe-treeview-table tr {
+ border-bottom: 1px solid #d6d6d6;
+}
+.openerp .oe-treeview-table tr:hover {
+ background-color: #e7e7e7;
+}
.openerp .oe-treeview-table span {
font-size: 90%;
font-weight: normal;
diff --git a/addons/web/static/src/css/base.sass b/addons/web/static/src/css/base.sass
index fc65f260db9..c56c4d6a3e9 100644
--- a/addons/web/static/src/css/base.sass
+++ b/addons/web/static/src/css/base.sass
@@ -451,6 +451,22 @@ $sheet-max-width: 860px
.oe_star_on
color: gold
// }}}
+
+ // Generic blocks {{{
+ header
+ position: relative
+ border-bottom: 1px solid #cacaca
+ padding-left: 2px
+ @include vertical-gradient(#fcfcfc, #dedede)
+ > span
+ margin-left: 4px
+ ul
+ display: inline-block
+ float: right
+ .oe_button
+ margin: 3px 2px 1px
+ // }}}
+
// Tags (for many2many tags, among others) {{{
.oe_tag
border: 1px solid $tag-border
@@ -476,12 +492,6 @@ $sheet-max-width: 860px
text-decoration: none
margin-bottom: 1px
// }}}
- .oe_form
- header
- .oe_tags
- margin: 5px 0 0 5px
- width: 400px
- padding-bottom: 0
// Tooltips {{{
&.oe_tooltip
font-size: 12px
@@ -605,7 +615,8 @@ $sheet-max-width: 860px
text-indent: -99999px
vertical-align: top
margin-top: 8px
- margin-left: 4px
+ //margin-left set at 3px to avoid a strange overflow
+ margin-left: 3px
border-left: 4px solid transparent
border-right: 4px solid transparent
border-top: 4px solid#404040
@@ -797,23 +808,28 @@ $sheet-max-width: 860px
.oe_topbar, .oe_leftbar
display: none
// }}}
- // WebClient.topbar {{{
+ // Top Menu {{{
+
.oe_topbar
width: 100%
- height: 31px
+ height: 32px
+ background-color: #414141
@include vertical-gradient(#646060, #262626)
.oe_topbar_item
display: block
padding: 5px 10px 7px
line-height: 20px
height: 20px
+ text-decoration: none
color: #eee
vertical-align: top
text-shadow: 0 1px 1px rgba(0,0,0,0.2)
- &:hover
- background: #303030
+ @include transition(all 0.2s ease-out)
+ &:hover,
+ background: rgba(0,0,0,0.2)
+ text-shadow: black 0px 0px 3px
color: white
- @include box-shadow(0 1px 2px rgba(255,255,255,0.3) inset)
+ @include box-shadow(0 1px 2px rgba(0,0,0,0.4) inset)
.oe_topbar_avatar
width: 24px
@@ -843,6 +859,59 @@ $sheet-max-width: 860px
@include vertical-gradient(#292929, #191919)
@include box-shadow(none)
+ // oe menu is the list of the buttons on the left side of the bar.
+ // So why aren't the buttons oe_topbar_items ? This sad state of affairs
+ // is a leftover from an uncomplete refactoring when the left and top menu
+ // were merged. You are welcome to refactor and clean this up
+ .oe_menu
+ float: left
+ padding: 0
+ margin: 0
+ font-size: 13px
+ > li
+ list-style-type: none
+ padding: 0
+ margin: 0
+ float: left
+ display: block
+ color: #eeeeee
+ > a
+ display: block
+ padding: 5px 10px 7px
+ line-height: 20px
+ height: 20px
+ text-decoration: none
+ color: #eee
+ vertical-align: top
+ text-shadow: 0 1px 1px rgba(0,0,0,0.2)
+ @include transition(all 0.2s ease-out)
+ &:hover,
+ background: rgba(0,0,0,0.2)
+ text-shadow: black 0px 0px 3px
+ color: white
+ @include box-shadow(0 1px 2px rgba(0,0,0,0.4) inset)
+ > .oe_active
+ background: rgba(0,0,0,0.2)
+ text-shadow: black 0px 0px 3px
+ font-weight: bold
+ @include box-shadow(0 1px 2px rgba(0,0,0,0.4) inset)
+
+ .oe_user_menu
+ float: right
+ padding: 0
+ margin: 0
+ li
+ list-style-type: none
+ float: left
+ .oe_dropdown_menu
+ right: -1px
+
+ .oe_systray > div
+ float: left
+ padding: 0 4px 0 4px
+ .oe_systray
+ float: right
+
// }}}
// Webclient.leftbar {{{
.oe_leftbar
@@ -878,78 +947,8 @@ $sheet-max-width: 860px
color: #c81010
font-style: italic
// }}}
- // UserMenu {{{
- .oe_user_menu
- float: right
- padding: 0
- margin: 0
- li
- list-style-type: none
- float: left
- .oe_dropdown_menu
- right: -1px
- // }}}
- // About openerp {{{
- .oe_about
- background-color: white
- background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAGCAYAAADgzO9IAAAAKUlEQVQIHWO8e/fufwYsgAUkJigoiCIF5DMyoYggcUiXgNnBiGQKmAkARpcEQeriln4AAAAASUVORK5CYII=)
- @include radius(0 0 2px 2px)
- a
- color: $link-color
- &:hover
- text-decoration: underline
- &:focus
- outline: none
- .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
- // }}}
- // Systray {{{
- .oe_systray > div
- float: left
- padding: 0 4px 0 4px
- .oe_systray
- float: right
- // }}}
- // Menu {{{
- .oe_menu
- float: left
- padding: 0
- margin: 0
- > li
- list-style-type: none
- padding: 0
- margin: 0
- float: left
- > a
- display: block
- padding: 5px 10px 7px
- line-height: 20px
- height: 20px
- color: #eee
- vertical-align: top
- text-shadow: 0 1px 1px rgba(0,0,0,0.2)
- &:hover
- background: #303030
- color: white
- @include box-shadow(0 1px 2px rgba(255,255,255,0.3) inset)
- > .oe_active
- background: #303030
- font-weight: bold
- color: white
- @include box-shadow(0 1px 2px rgba(255,255,255,0.3) inset)
+ // Webclient.leftbar items {{{
+
.oe_secondary_menu_section
font-weight: bold
margin-left: 8px
@@ -1028,6 +1027,34 @@ $sheet-max-width: 860px
border-left: 4px solid transparent
border-right: 4px solid transparent
border-top: 4px solid #4c4c4c
+
+ // }}}
+ // About openerp {{{
+ .oe_about
+ background-color: white
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAGCAYAAADgzO9IAAAAKUlEQVQIHWO8e/fufwYsgAUkJigoiCIF5DMyoYggcUiXgNnBiGQKmAkARpcEQeriln4AAAAASUVORK5CYII=)
+ @include radius(0 0 2px 2px)
+ a
+ color: $link-color
+ &:hover
+ text-decoration: underline
+ &:focus
+ outline: none
+ .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
// }}}
// ActionManager {{{
.oe_application
@@ -1048,7 +1075,7 @@ $sheet-max-width: 860px
// ViewManager common {{{
.oe_view_manager
.oe_view_manager_body
- height: inherit
+ height: inherit
.oe_view_manager_view_kanban
height: inherit
@@ -1186,7 +1213,6 @@ $sheet-max-width: 860px
.oe_view_manager_current
height: 100%
> .oe_view_manager_header
- border-top: 1px solid #cacaca
border-bottom: 1px solid #cacaca
@include vertical-gradient(#fcfcfc, #dedede)
@include box-shadow((0 1px 0 rgba(255,255,255,0.4), 0 0 9px rgba(0,0,0,0.1)))
@@ -1584,19 +1610,10 @@ $sheet-max-width: 860px
padding: 0 16px
// }}}
// FormView.custom tags and classes {{{
- .oe_form
- header
- position: relative
- border-bottom: 1px solid #cacaca
- padding-left: 2px
- @include vertical-gradient(#fcfcfc, #dedede)
- > span
- margin-left: 4px
- ul
- display: inline-block
- float: right
- .oe_form_button
- margin: 3px 2px 1px
+ .oe_form header .oe_tags
+ margin: 5px 0 0 5px
+ width: 400px
+ padding-bottom: 0
div.oe_chatter
min-width: 650px
max-width: $sheet-max-width
@@ -1736,6 +1753,8 @@ $sheet-max-width: 860px
background: white
min-width: 60px
color: #1f1f1f
+ textarea
+ height: 32px
input[readonly], select[readonly], textarea[readonly], input[disabled], select[disabled]
background: #E5E5E5 !important
color: #666
@@ -2165,22 +2184,29 @@ $sheet-max-width: 860px
width: 100%
background-color: white
border-spacing: 0
+ color: #4c4c4c
th
padding: 10px
- color: #4c4c4c
font-weight: bold
background-color: #f0f0f0
border-bottom: 2px solid #cacaca
- .treeview-tr, .treeview-td
+
+ td
cursor: pointer
- border-right: 1px dotted $tag-border
- vertical-align: top
+ vertical-align: middle
text-align: left
- border-bottom: 1px solid #cfcccc
- tr:hover
- background-color: #e0e0f8
- .oe-number
+ vertical-align: middle
+ height: 20px
+ padding-left: 4px
+ padding-right: 4px
+ border-right: 1px solid #e7e7e7
+
+ td.oe_number
text-align: right !important
+ tr
+ border-bottom: 1px solid #d6d6d6
+ &:hover
+ background-color: #e7e7e7
span
font-size: 90%
font-weight: normal
@@ -2192,6 +2218,7 @@ $sheet-max-width: 860px
background-image: url(/web/static/src/img/collapse.gif)
.treeview-tr.oe-treeview-first span, .treeview-td.oe-treeview-first span
margin-left: 16px
+
// }}}
// Debugging stuff {{{
.oe_layout_debugging
diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js
index e9185429993..014313d5428 100644
--- a/addons/web/static/src/js/chrome.js
+++ b/addons/web/static/src/js/chrome.js
@@ -234,22 +234,20 @@ instance.web.Loading = instance.web.Widget.extend({
this._super(parent);
this.count = 0;
this.blocked_ui = false;
- var self = this;
- this.request_call = function() {
- self.on_rpc_event(1);
- };
- this.response_call = function() {
- self.on_rpc_event(-1);
- };
- this.session.on_rpc_request.add_first(this.request_call);
- this.session.on_rpc_response.add_last(this.response_call);
+ this.session.on("request", this, this.request_call);
+ this.session.on("response", this, this.response_call);
+ this.session.on("error", this, this.response_call);
},
destroy: function() {
- this.session.on_rpc_request.remove(this.request_call);
- this.session.on_rpc_response.remove(this.response_call);
this.on_rpc_event(-this.count);
this._super();
},
+ request_call: function() {
+ this.on_rpc_event(1);
+ },
+ response_call: function() {
+ this.on_rpc_event(-1);
+ },
on_rpc_event : function(increment) {
var self = this;
if (!this.count && increment === 1) {
diff --git a/addons/web/static/src/js/corelib.js b/addons/web/static/src/js/corelib.js
index c74c2d5dc03..62ac48f0c61 100644
--- a/addons/web/static/src/js/corelib.js
+++ b/addons/web/static/src/js/corelib.js
@@ -972,6 +972,11 @@ instance.web.Registry = instance.web.Class.extend({
});
instance.web.JsonRPC = instance.web.CallbackEnabled.extend({
+ triggers: {
+ 'request': 'Request sent',
+ 'response': 'Response received',
+ 'error': 'HTTP Error response or timeout received',
+ },
/**
* @constructs instance.web.JsonRPC
* @extends instance.web.CallbackEnabled
@@ -1310,12 +1315,12 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({
id: _.uniqueId('r')
};
var deferred = $.Deferred();
- this.on_rpc_request();
+ this.trigger('request', url, payload);
var aborter = params.aborter;
delete params.aborter;
var request = this.rpc_function(url, payload).then(
function (response, textStatus, jqXHR) {
- self.on_rpc_response();
+ self.trigger('response', response);
if (!response.error) {
if (url.url === '/web/session/eval_domain_and_context') {
self.test_eval(params, response.result);
@@ -1328,7 +1333,7 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({
}
},
function(jqXHR, textStatus, errorThrown) {
- self.on_rpc_response();
+ self.trigger('error');
var error = {
code: -32098,
message: "XmlHttpRequestError " + errorThrown,
@@ -1436,10 +1441,6 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({
return deferred;
}
},
- on_rpc_request: function() {
- },
- on_rpc_response: function() {
- },
on_rpc_error: function(error) {
},
get_url: function (file) {
diff --git a/addons/web/static/src/js/data.js b/addons/web/static/src/js/data.js
index 29e799e54c3..2b82e618203 100644
--- a/addons/web/static/src/js/data.js
+++ b/addons/web/static/src/js/data.js
@@ -107,7 +107,7 @@ instance.web.Query = instance.web.Class.extend({
* Performs a groups read according to the provided grouping criterion
*
* @param {String|Array<String>} grouping
- * @returns {jQuery.Deferred<Array<openerp.web.data.Group>> | null}
+ * @returns {jQuery.Deferred<Array<openerp.web.QueryGroup>> | null}
*/
group_by: function (grouping) {
if (grouping === undefined) {
@@ -130,7 +130,7 @@ instance.web.Query = instance.web.Class.extend({
orderby: instance.web.serialize_sort(this._order_by) || false
}).pipe(function (results) {
return _(results).map(function (result) {
- return new instance.web.data.Group(
+ return new instance.web.QueryGroup(
self._model.name, grouping[0], result);
});
});
@@ -194,7 +194,54 @@ instance.web.Query = instance.web.Class.extend({
}
});
-instance.web.Model = instance.web.Class.extend(/** @lends openerp.web.Model# */{
+instance.web.QueryGroup = instance.web.Class.extend({
+ init: function (model, grouping_field, read_group_group) {
+ // In cases where group_by_no_leaf and no group_by, the result of
+ // read_group has aggregate fields but no __context or __domain.
+ // Create default (empty) values for those so that things don't break
+ var fixed_group = _.extend(
+ {__context: {group_by: []}, __domain: []},
+ read_group_group);
+
+ var aggregates = {};
+ _(fixed_group).each(function (value, key) {
+ if (key.indexOf('__') === 0
+ || key === grouping_field
+ || key === grouping_field + '_count') {
+ return;
+ }
+ aggregates[key] = value || 0;
+ });
+
+ this.model = new instance.web.Model(
+ model, fixed_group.__context, fixed_group.__domain);
+
+ var group_size = fixed_group[grouping_field + '_count'] || fixed_group.__count || 0;
+ var leaf_group = fixed_group.__context.group_by.length === 0;
+ this.attributes = {
+ folded: !!(fixed_group.__fold),
+ grouped_on: grouping_field,
+ // if terminal group (or no group) and group_by_no_leaf => use group.__count
+ length: group_size,
+ value: fixed_group[grouping_field],
+ // A group is open-able if it's not a leaf in group_by_no_leaf mode
+ has_children: !(leaf_group && fixed_group.__context['group_by_no_leaf']),
+
+ aggregates: aggregates
+ };
+ },
+ get: function (key) {
+ return this.attributes[key];
+ },
+ subgroups: function () {
+ return this.model.query().group_by(this.model.context().group_by);
+ },
+ query: function () {
+ return this.model.query.apply(this.model, arguments);
+ }
+});
+
+instance.web.Model = instance.web.Class.extend({
/**
* @constructs instance.web.Model
* @extends instance.web.Class
@@ -302,145 +349,7 @@ instance.web.Model = instance.web.Class.extend(/** @lends openerp.web.Model# */{
},
});
-instance.web.Traverser = instance.web.Class.extend(/** @lends openerp.web.Traverser# */{
- /**
- * @constructs instance.web.Traverser
- * @extends instance.web.Class
- *
- * @param {instance.web.Model} model instance this traverser is bound to
- */
- init: function (model) {
- this._model = model;
- this._index = 0;
- },
-
- /**
- * Gets and sets the current index
- *
- * @param {Number} [idx]
- * @returns {Number} current index
- */
- index: function (idx) {
- if (idx) { this._index = idx; }
- return this._index;
- },
- /**
- * Returns the model this traverser is currently bound to
- *
- * @returns {openerp.web.Model}
- */
- model: function () {
- return this._model;
- },
- /**
- * Fetches the size of the backing model's match
- *
- * @returns {Deferred<Number>} deferred count
- */
- size: function () {
- return this._model.query().count();
- },
-
- /**
- * Record at the current index for the collection, fails if there is no
- * record at the current index.
- *
- * @returns {Deferred<>}
- */
- current: function (fields) {
- return this._model.query(fields).first().pipe(function (record) {
- if (record == null) {
- return $.Deferred()
- .reject('No record at index' + this._index)
- .promise();
- }
- return record;
- });
- },
- next: function (fields) {
- var self = this;
- this._index++;
- return this.size().pipe(function (s) {
- if (self._index >= s) {
- self._index = 0;
- }
- return self.current(fields);
- });
- },
- previous: function (fields) {
- var self = this;
- this._index--;
- if (this._index < 0) {
- return this.size().pipe(function (s) {
- self._index = s-1;
- return self.current(fields);
- });
- }
- return this.current(fields);
- }
-
-});
-
-/**
- * Utility objects, should never need to be instantiated from outside of this
- * module
- *
- * @namespace
- */
-instance.web.data = {
- Group: instance.web.Class.extend(/** @lends openerp.web.data.Group# */{
- /**
- * @constructs instance.web.data.Group
- * @extends instance.web.Class
- */
- init: function (model, grouping_field, read_group_group) {
- // In cases where group_by_no_leaf and no group_by, the result of
- // read_group has aggregate fields but no __context or __domain.
- // Create default (empty) values for those so that things don't break
- var fixed_group = _.extend(
- {__context: {group_by: []}, __domain: []},
- read_group_group);
-
- var aggregates = {};
- _(fixed_group).each(function (value, key) {
- if (key.indexOf('__') === 0
- || key === grouping_field
- || key === grouping_field + '_count') {
- return;
- }
- aggregates[key] = value || 0;
- });
-
- this.model = new instance.web.Model(
- model, fixed_group.__context, fixed_group.__domain);
-
- var group_size = fixed_group[grouping_field + '_count'] || fixed_group.__count || 0;
- var leaf_group = fixed_group.__context.group_by.length === 0;
- this.attributes = {
- folded: !!(fixed_group.__fold),
- grouped_on: grouping_field,
- // if terminal group (or no group) and group_by_no_leaf => use group.__count
- length: group_size,
- value: fixed_group[grouping_field],
- // A group is open-able if it's not a leaf in group_by_no_leaf mode
- has_children: !(leaf_group && fixed_group.__context['group_by_no_leaf']),
-
- aggregates: aggregates
- };
- },
- get: function (key) {
- return this.attributes[key];
- },
- subgroups: function () {
- return this.model.query().group_by(this.model.context().group_by);
- },
- query: function () {
- return this.model.query.apply(this.model, arguments);
- }
- })
-};
-
-instance.web.DataGroup = instance.web.CallbackEnabled.extend( /** @lends openerp.web.DataGroup# */{
+instance.web.DataGroup = instance.web.CallbackEnabled.extend({
/**
* Management interface between views and grouped collections of OpenERP
* records.
@@ -472,50 +381,47 @@ instance.web.DataGroup = instance.web.CallbackEnabled.extend( /** @lends opener
},
list: function (fields, ifGroups, ifRecords) {
var self = this;
- $.when(this.model.query(fields)
- .order_by(this.sort)
- .group_by(this.group_by)).then(function (groups) {
- if (!groups) {
- ifRecords(_.extend(
- new instance.web.DataSetSearch(
- self, self.model.name,
- self.model.context(),
- self.model.domain()),
- {_sort: self.sort}));
+ var query = this.model.query(fields).order_by(this.sort).group_by(this.group_by);
+ $.when(query).then(function (querygroups) {
+ // leaf node
+ if (!querygroups) {
+ var ds = new instance.web.DataSetSearch(self, self.model.name, self.model.context(), self.model.domain());
+ ds._sort = self.sort;
+ ifRecords(ds);
return;
}
- ifGroups(_(groups).map(function (group) {
+ // internal node
+ var child_datagroups = _(querygroups).map(function (group) {
var child_context = _.extend(
{}, self.model.context(), group.model.context());
- return _.extend(
- new instance.web.DataGroup(
- self, self.model.name, group.model.domain(),
- child_context, group.model._context.group_by,
- self.level + 1),
- {
- __context: child_context,
- __domain: group.model.domain(),
- folded: group.get('folded'),
- grouped_on: group.get('grouped_on'),
- length: group.get('length'),
- value: group.get('value'),
- openable: group.get('has_children'),
- aggregates: group.get('aggregates')
- }, {sort: self.sort});
- }));
+ var child_dg = new instance.web.DataGroup(
+ self, self.model.name, group.model.domain(),
+ child_context, group.model._context.group_by,
+ self.level + 1);
+ child_dg.sort = self.sort;
+ // copy querygroup properties
+ child_dg.__context = child_context;
+ child_dg.__domain = group.model.domain();
+ child_dg.folded = group.get('folded');
+ child_dg.grouped_on = group.get('grouped_on');
+ child_dg.length = group.get('length');
+ child_dg.value = group.get('value');
+ child_dg.openable = group.get('has_children');
+ child_dg.aggregates = group.get('aggregates');
+ return child_dg;
+ });
+ ifGroups(child_datagroups);
});
}
});
-instance.web.ContainerDataGroup = instance.web.DataGroup.extend({ });
-instance.web.GrouplessDataGroup = instance.web.DataGroup.extend({ });
-instance.web.StaticDataGroup = instance.web.GrouplessDataGroup.extend( /** @lends openerp.web.StaticDataGroup# */ {
+instance.web.StaticDataGroup = instance.web.DataGroup.extend({
/**
- * A specialization of groupless data groups, relying on a single static
+ * A specialization of data groups, relying on a single static
* dataset as its records provider.
*
* @constructs instance.web.StaticDataGroup
- * @extends instance.web.GrouplessDataGroup
+ * @extends instance.web.DataGroup
* @param {openep.web.DataSetStatic} dataset a static dataset backing the groups
*/
init: function (dataset) {
@@ -526,10 +432,9 @@ instance.web.StaticDataGroup = instance.web.GrouplessDataGroup.extend( /** @lend
}
});
-instance.web.DataSet = instance.web.CallbackEnabled.extend( /** @lends openerp.web.DataSet# */{
+instance.web.DataSet = instance.web.CallbackEnabled.extend({
/**
- * DateaManagement interface between views and the collection of selected
- * OpenERP records (represents the view's state?)
+ * Collection of OpenERP records, used to share records and the current selection between views.
*
* @constructs instance.web.DataSet
* @extends instance.web.CallbackEnabled
@@ -646,15 +551,10 @@ instance.web.DataSet = instance.web.CallbackEnabled.extend( /** @lends openerp.
* Creates a new record in db
*
* @param {Object} data field values to set on the new record
- * @param {Function} callback function called with operation result
- * @param {Function} error_callback function called in case of creation error
* @returns {$.Deferred}
*/
- create: function(data, callback, error_callback) {
- return this._model.call('create',
- [data], {context: this._model.context()})
- .pipe(function (r) { return {result: r}; })
- .then(callback, error_callback);
+ create: function(data) {
+ return this._model.call('create', [data], {context: this._model.context()});
},
/**
* Saves the provided data in an existing db record
@@ -665,24 +565,17 @@ instance.web.DataSet = instance.web.CallbackEnabled.extend( /** @lends openerp.
* @param {Function} error_callback function called in case of write error
* @returns {$.Deferred}
*/
- write: function (id, data, options, callback, error_callback) {
+ write: function (id, data, options) {
options = options || {};
- return this._model.call('write',
- [[id], data], {context: this._model.context(options.context)})
- .pipe(function (r) { return {result: r}})
- .then(callback, error_callback);
+ return this._model.call('write', [[id], data], {context: this._model.context(options.context)});
},
/**
* Deletes an existing record from the database
*
* @param {Number|String} ids identifier of the record to delete
- * @param {Function} callback function called with operation result
- * @param {Function} error_callback function called in case of deletion error
*/
- unlink: function(ids, callback, error_callback) {
- return this._model.call('unlink',
- [ids], {context: this._model.context()})
- .then(callback, error_callback);
+ unlink: function(ids) {
+ return this._model.call('unlink', [ids], {context: this._model.context()});
},
/**
* Calls an arbitrary RPC method
@@ -703,43 +596,35 @@ instance.web.DataSet = instance.web.CallbackEnabled.extend( /** @lends openerp.
* @param {Array} [args]
* @param {Number} [domain_index] index of a domain to evaluate in the args array
* @param {Number} [context_index] index of a context to evaluate in the args array
- * @param {Function} callback
- * @param {Function} error_callback
* @returns {$.Deferred}
*/
- call_and_eval: function (method, args, domain_index, context_index, callback, error_callback) {
+ call_and_eval: function (method, args, domain_index, context_index) {
return instance.session.rpc('/web/dataset/call', {
model: this.model,
method: method,
domain_id: domain_index == undefined ? null : domain_index,
context_id: context_index == undefined ? null : context_index,
args: args || []
- }, callback, error_callback);
+ });
},
/**
* Calls a button method, usually returning some sort of action
*
* @param {String} method
* @param {Array} [args]
- * @param {Function} callback
- * @param {Function} error_callback
* @returns {$.Deferred}
*/
- call_button: function (method, args, callback, error_callback) {
- return this._model.call_button(method, args)
- .then(callback, error_callback);
+ call_button: function (method, args) {
+ return this._model.call_button(method, args);
},
/**
* Fetches the "readable name" for records, based on intrinsic rules
*
* @param {Array} ids
- * @param {Function} callback
* @returns {$.Deferred}
*/
- name_get: function(ids, callback) {
- return this._model.call('name_get',
- [ids], {context: this._model.context()})
- .then(callback);
+ name_get: function(ids) {
+ return this._model.call('name_get', [ids], {context: this._model.context()});
},
/**
*
@@ -750,28 +635,24 @@ instance.web.DataSet = instance.web.CallbackEnabled.extend( /** @lends openerp.
* @param {Function} callback function to call with name_search result
* @returns {$.Deferred}
*/
- name_search: function (name, domain, operator, limit, callback) {
+ name_search: function (name, domain, operator, limit) {
return this._model.call('name_search', {
name: name || '',
args: domain || false,
operator: operator || 'ilike',
context: this._model.context(),
limit: limit || 0
- }).then(callback);
+ });
},
/**
* @param name
- * @param callback
*/
- name_create: function(name, callback) {
- return this._model.call('name_create',
- [name], {context: this._model.context()})
- .then(callback);
+ name_create: function(name) {
+ return this._model.call('name_create', [name], {context: this._model.context()});
},
- exec_workflow: function (id, signal, callback) {
+ exec_workflow: function (id, signal) {
return this._model.exec_workflow(id, signal)
- .pipe(function (result) { return { result: result }; })
- .then(callback);
+ .pipe(function (result) { return { result: result }; });
},
get_context: function(request_context) {
return this._model.context(request_context);
@@ -824,6 +705,7 @@ instance.web.DataSet = instance.web.CallbackEnabled.extend( /** @lends openerp.
});
},
});
+
instance.web.DataSetStatic = instance.web.DataSet.extend({
init: function(parent, model, context, ids) {
this._super(parent, model, context);
@@ -854,7 +736,8 @@ instance.web.DataSetStatic = instance.web.DataSet.extend({
this.set_ids(_.without.apply(null, [this.ids].concat(ids)));
}
});
-instance.web.DataSetSearch = instance.web.DataSet.extend(/** @lends openerp.web.DataSetSearch */{
+
+instance.web.DataSetSearch = instance.web.DataSet.extend({
/**
* @constructs instance.web.DataSetSearch
* @extends instance.web.DataSet
@@ -904,7 +787,7 @@ instance.web.DataSetSearch = instance.web.DataSet.extend(/** @lends openerp.web
},
unlink: function(ids, callback, error_callback) {
var self = this;
- return this._super(ids, function(result) {
+ return this._super(ids).then(function(result) {
self.ids = _(self.ids).difference(ids);
if (self._length) {
self._length -= 1;
@@ -913,9 +796,7 @@ instance.web.DataSetSearch = instance.web.DataSet.extend(/** @lends openerp.web
self.index = self.index <= self.ids.length - 1 ?
self.index : (self.ids.length > 0 ? self.ids.length -1 : 0);
}
- if (callback)
- callback(result);
- }, error_callback);
+ });
},
size: function () {
if (this._length !== undefined) {
@@ -924,6 +805,7 @@ instance.web.DataSetSearch = instance.web.DataSet.extend(/** @lends openerp.web
return this._super();
}
});
+
instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
virtual_id_prefix: "one2many_v_id_",
debug_mode: true,
@@ -938,16 +820,14 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
on_default_get: function(res) {
this.last_default_get = res;
},
- create: function(data, callback, error_callback) {
+ create: function(data) {
var cached = {id:_.uniqueId(this.virtual_id_prefix), values: data,
defaults: this.last_default_get};
this.to_create.push(_.extend(_.clone(cached), {values: _.clone(cached.values)}));
this.cache.push(cached);
- var prom = $.Deferred().then(callback);
- prom.resolve({result: cached.id});
- return prom.promise();
+ return $.Deferred().resolve(cached.id).promise();
},
- write: function (id, data, options, callback) {
+ write: function (id, data, options) {
var self = this;
var record = _.detect(this.to_create, function(x) {return x.id === id;});
record = record || _.detect(this.to_write, function(x) {return x.id === id;});
@@ -973,9 +853,7 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
$.extend(cached.values, record.values);
if (dirty)
this.on_change();
- var to_return = $.Deferred().then(callback);
- to_return.resolve({result: true});
- return to_return.promise();
+ return $.Deferred().resolve(true).promise();
},
unlink: function(ids, callback, error_callback) {
var self = this;
@@ -999,7 +877,8 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
this.cache = [];
this.delete_all = false;
},
- on_change: function() {},
+ on_change: function() {
+ },
read_ids: function (ids, fields, options) {
var self = this;
var to_get = [];
@@ -1071,7 +950,7 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
}
return completion.promise();
},
- call_button: function (method, args, callback, error_callback) {
+ call_button: function (method, args) {
var id = args[0][0], index;
for(var i=0, len=this.cache.length; i<len; ++i) {
var record = this.cache[i];
@@ -1082,7 +961,7 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
break;
}
}
- return this._super(method, args, callback, error_callback);
+ return this._super(method, args);
},
alter_ids: function(n_ids) {
this._super(n_ids);
@@ -1114,23 +993,23 @@ instance.web.ProxyDataSet = instance.web.DataSetSearch.extend({
return this._super.apply(this, arguments);
}
},
- create: function(data, callback, error_callback) {
+ create: function(data) {
if (this.create_function) {
- return this.create_function(data, this._super).then(callback, error_callback);
+ return this.create_function(data, this._super);
} else {
return this._super.apply(this, arguments);
}
},
- write: function (id, data, options, callback, error_callback) {
+ write: function (id, data, options) {
if (this.write_function) {
- return this.write_function(id, data, options, this._super).then(callback, error_callback);
+ return this.write_function(id, data, options, this._super);
} else {
return this._super.apply(this, arguments);
}
},
- unlink: function(ids, callback, error_callback) {
+ unlink: function(ids) {
if (this.unlink_function) {
- return this.unlink_function(ids, this._super).then(callback, error_callback);
+ return this.unlink_function(ids, this._super);
} else {
return this._super.apply(this, arguments);
}
@@ -1183,7 +1062,7 @@ instance.web.CompoundDomain = instance.web.Class.extend({
}
});
-instance.web.DropMisordered = instance.web.Class.extend(/** @lends openerp.web.DropMisordered# */{
+instance.web.DropMisordered = instance.web.Class.extend({
/**
* @constructs instance.web.DropMisordered
* @extends instance.web.Class
diff --git a/addons/web/static/src/js/formats.js b/addons/web/static/src/js/formats.js
index 797e1a2cbdf..3e5e6fecd5e 100644
--- a/addons/web/static/src/js/formats.js
+++ b/addons/web/static/src/js/formats.js
@@ -138,6 +138,9 @@ instance.web.format_value = function (value, descriptor, value_if_empty) {
return value[1];
case 'one2many':
case 'many2many':
+ if (typeof value === 'string') {
+ return value;
+ }
return _.str.sprintf(_t("(%d records)"), value.length);
case 'datetime':
if (typeof(value) == "string")
diff --git a/addons/web/static/src/js/search.js b/addons/web/static/src/js/search.js
index cd3611366e4..71e955e80a6 100644
--- a/addons/web/static/src/js/search.js
+++ b/addons/web/static/src/js/search.js
@@ -1493,16 +1493,13 @@ instance.web.search.ManyToOneField = instance.web.search.CharField.extend({
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(
- _t("M2O search fields do not currently handle multiple default values"));
- }
+ assert(value.length <= 1,
+ _t("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) {
+ return this.model.call('name_get', [value]).pipe(function (names) {
if (_(names).isEmpty()) { return null; }
return facet_from(self, names[0]);
})
diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js
index f62b186f662..bf6b869482b 100644
--- a/addons/web/static/src/js/view_form.js
+++ b/addons/web/static/src/js/view_form.js
@@ -13,6 +13,7 @@ instance.web.form = {};
* Properties:
* - display_invalid_fields : if true, all fields where is_valid() return true should
* be displayed as invalid.
+ * - actual_mode : the current mode of the field manager. Can be "view", "edit" or "create".
* Events:
* - view_content_has_changed : when the values of the fields have changed. When
* this event is triggered all fields should reprocess their modifiers.
@@ -31,14 +32,18 @@ instance.web.form.FieldManagerMixin = {
*/
get_field: function(field_name) {},
/**
- * Returns true when the view is in create mode.
- */
- is_create_mode: function() {},
- /**
* Returns the current value of a field present in the view. See the get_value() method
* method in FieldInterface for further information.
*/
get_field_value: function(field_name) {},
+ /**
+ Gives new values for the fields contained in the view. The new values could not be setted
+ right after the call to this method. Setting new values can trigger on_changes.
+
+ @param (dict) values A dictonnary with key = field name and value = new value.
+ @return (Deferred) Is resolved after all the values are setted.
+ */
+ set_values: function(values) {},
};
instance.web.views.add('form', 'instance.web.FormView');
@@ -83,6 +88,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
this.default_focus_button = null;
this.fields_registry = instance.web.form.widgets;
this.tags_registry = instance.web.form.tags;
+ this.widgets_registry = instance.web.form.custom_widgets;
this.has_been_loaded = $.Deferred();
this.translatable_fields = [];
_.defaults(this.options, {
@@ -134,6 +140,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
this.rendering_engine.set_fields_registry(this.fields_registry);
this.rendering_engine.set_tags_registry(this.tags_registry);
+ this.rendering_engine.set_widgets_registry(this.widgets_registry);
if (!this.extract_qweb_template(data)) {
this.rendering_engine.set_fields_view(data);
var $dest = this.$el.hasClass("oe_form_container") ? this.$el : this.$el.find('.oe_form_container');
@@ -180,6 +187,14 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
instance.web.bus.trigger('click', e);
}
});
+ //bounce effect on red button when click on statusbar.
+ this.$el.find(".oe_form_field_status:not(.oe_form_status_clickable)").on('click', function (e) {
+ if((self.get("actual_mode") == "view")) {
+ var $button = self.$el.find(".oe_highlight:not(.oe_form_invisible)").css({'float':'left','clear':'none'});
+ $button.effect('bounce', {distance:18, times: 5}, 150);
+ e.stopPropagation();
+ }
+ });
return $.when();
},
@@ -588,24 +603,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
on_processed_onchange: function(result, processed) {
try {
if (result.value) {
- for (var f in result.value) {
- if (!result.value.hasOwnProperty(f)) { continue; }
- var field = this.fields[f];
- // If field is not defined in the view, just ignore it
- if (field) {
- var value_ = result.value[f];
- if (field.get_value() != value_) {
- field._inhibit_on_change_flag = true;
- field.set_value(value_);
- field._inhibit_on_change_flag = false;
- field._dirty_flag = true;
- if (!_.contains(processed, field.name)) {
- this.do_onchange(field, processed);
- }
- }
- }
- }
- this.on_form_changed();
+ this._internal_set_values(result.value, processed);
}
if (!_.isEmpty(result.warning)) {
instance.web.dialog($(QWeb.render("CrashManager.warning", result.warning)), {
@@ -636,6 +634,33 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
return $.Deferred().reject();
}
},
+ _internal_set_values: function(values, exclude) {
+ exclude = exclude || [];
+ for (var f in values) {
+ if (!values.hasOwnProperty(f)) { continue; }
+ var field = this.fields[f];
+ // If field is not defined in the view, just ignore it
+ if (field) {
+ var value_ = values[f];
+ if (field.get_value() != value_) {
+ field._inhibit_on_change_flag = true;
+ field.set_value(value_);
+ field._inhibit_on_change_flag = false;
+ field._dirty_flag = true;
+ if (!_.contains(exclude, field.name)) {
+ this.do_onchange(field, exclude);
+ }
+ }
+ }
+ }
+ this.on_form_changed();
+ },
+ set_values: function(values) {
+ var self = this;
+ return this.on_change_mutex.exec(function() {
+ self._internal_set_values(values);
+ });
+ },
/**
* Ask the view to switch to view mode if possible. The view may not do it
* if the current record is not yet saved. It will then stay in create mode.
@@ -676,17 +701,11 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
self.$buttons.find('.oe_form_buttons_edit').hide();
self.$buttons.find('.oe_form_buttons_view').show();
self.$sidebar.show();
- _.each(this.fields,function(field){
- field.set({"force_readonly": true});
- });
} else {
self.$el.removeClass('oe_form_readonly').addClass('oe_form_editable');
self.$buttons.find('.oe_form_buttons_edit').show();
self.$buttons.find('.oe_form_buttons_view').hide();
self.$sidebar.hide();
- _.each(this.fields,function(field){
- field.set({"force_readonly": false});
- });
this.autofocus();
}
},
@@ -862,7 +881,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
* @param {Object} r result of the write function.
*/
on_saved: function(r) {
- if (!r.result) {
+ if (!r) {
// should not happen in the server, but may happen for internal purpose
return $.Deferred().reject();
} else {
@@ -885,11 +904,11 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
* at the beginning of the dataset instead of the end
*/
on_created: function(r, prepend_on_create) {
- if (!r.result) {
+ if (!r) {
// should not happen in the server, but may happen for internal purpose
return $.Deferred().reject();
} else {
- this.datarecord.id = r.result;
+ this.datarecord.id = r;
if (!prepend_on_create) {
this.dataset.alter_ids(this.dataset.ids.concat([this.datarecord.id]));
this.dataset.index = this.dataset.ids.length - 1;
@@ -996,7 +1015,8 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
|| field.get('invisible')
|| field.get("readonly")
|| field.field.type === 'one2many'
- || field.field.type === 'many2many') {
+ || field.field.type === 'many2many'
+ || field.field.type === 'binary') {
return false;
}
var displayed = value;
@@ -1086,9 +1106,6 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
get_field: function(field_name) {
return this.fields_view.fields[field_name];
},
- is_create_mode: function() {
- return this.get("actual_mode") === "create";
- },
get_field_value: function(field_name) {
return this.fields[field_name].get_value();
},
@@ -1125,6 +1142,9 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
set_fields_registry: function(fields_registry) {
this.fields_registry = fields_registry;
},
+ set_widgets_registry: function(widgets_registry) {
+ this.widgets_registry = widgets_registry;
+ },
// Backward compatibility tools, current default version: v6.1
process_version: function() {
if (this.version < 7.0) {
@@ -1149,6 +1169,7 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
this.fields_to_init = [];
this.tags_to_init = [];
+ this.widgets_to_init = [];
this.labels = {};
this.process(this.$form);
@@ -1178,6 +1199,12 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
var w = new (obj)(self.view, instance.web.xml_to_json($elem[0]));
w.replace($elem);
});
+ _.each(this.widgets_to_init, function($elem) {
+ var widget_type = $elem.attr("type");
+ var obj = self.widgets_registry.get_object(widget_type);
+ var w = new (obj)(self.view, instance.web.xml_to_json($elem[0]));
+ w.replace($elem);
+ });
// TODO: return a deferred
},
render_element: function(template /* dictionaries */) {
@@ -1220,6 +1247,10 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
return $tag;
}
},
+ process_widget: function($widget) {
+ this.widgets_to_init.push($widget);
+ return $widget;
+ },
process_sheet: function($sheet) {
var $new_sheet = this.render_element('FormRenderingSheet', $sheet.getAttributes());
this.handle_common_properties($new_sheet, $sheet);
@@ -1646,22 +1677,42 @@ instance.web.form.InvisibilityChanger = instance.web.Class.extend(instance.web.P
},
});
+/**
+ Base class for all fields, custom widgets and buttons to be displayed in the form view.
+
+ Properties:
+ - effective_readonly: when it is true, the widget is displayed as readonly. Vary depending
+ the values of the "readonly" property and the "mode" property on the field manager.
+*/
instance.web.form.FormWidget = instance.web.Widget.extend(instance.web.form.InvisibilityChangerMixin, {
/**
* @constructs instance.web.form.FormWidget
* @extends instance.web.Widget
*
- * @param view
+ * @param field_manager
* @param node
*/
- init: function(view, node) {
- this._super(view);
- this.view = view;
+ init: function(field_manager, node) {
+ this._super(field_manager);
+ this.view = field_manager;
+ this.field_manager = field_manager;
this.node = node;
this.modifiers = JSON.parse(this.node.attrs.modifiers || '{}');
- instance.web.form.InvisibilityChangerMixin.init.call(this, view, this.modifiers.invisible);
+ instance.web.form.InvisibilityChangerMixin.init.call(this, this.field_manager, this.modifiers.invisible);
+
+ this.field_manager.on("view_content_has_changed", this, this.process_modifiers);
- this.view.on("view_content_has_changed", this, this.process_modifiers);
+ this.set({required: this.modifiers['required'] === true});
+ // some events to make the property "effective_readonly" sync automatically with "readonly" and
+ // "mode" on field_manager
+ var self = this;
+ this.set({"readonly": this.modifiers['readonly'] === true});
+ var test_effective_readonly = function() {
+ self.set({"effective_readonly": self.get("readonly") || self.field_manager.get("actual_mode") === "view"});
+ };
+ this.on("change:readonly", this, test_effective_readonly);
+ this.field_manager.on("change:actual_mode", this, test_effective_readonly);
+ test_effective_readonly.call(this);
},
renderElement: function() {
this._super();
@@ -1771,8 +1822,8 @@ instance.web.form.FormWidget = instance.web.Widget.extend(instance.web.form.Invi
instance.web.form.WidgetButton = instance.web.form.FormWidget.extend({
template: 'WidgetButton',
- init: function(view, node) {
- this._super(view, node);
+ init: function(field_manager, node) {
+ this._super(field_manager, node);
this.force_disabled = false;
this.string = (this.node.attrs.string || '').replace(/_/g, '');
if (JSON.parse(this.node.attrs.default_focus || "0")) {
@@ -1857,10 +1908,6 @@ instance.web.form.WidgetButton = instance.web.form.FormWidget.extend({
/**
* Interface to be implemented by fields.
*
- * Properties:
- * - readonly: boolean. If set to true the field should appear in readonly mode.
- * - force_readonly: boolean, When it is true, the field should always appear
- * in read only mode, no matter what the value of the "readonly" property can be.
* Events:
* - changed_value: triggered when the value of the field has changed. This can be due
* to a user interaction or a call to set_value().
@@ -1937,8 +1984,6 @@ instance.web.form.FieldInterface = {
* Abstract class for classes implementing FieldInterface.
*
* Properties:
- * - effective_readonly: when it is true, the widget is displayed as readonly. Vary depending
- * the values of the "readonly" property and the "force_readonly" property on the field manager.
* - value: useful property to hold the value of the field. By default, set_value() and get_value()
* set and retrieve the value property. Changing the value property also triggers automatically
* a 'changed_value' event that inform the view to trigger on_changes.
@@ -1955,25 +2000,13 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.w
init: function(field_manager, node) {
var self = this
this._super(field_manager, node);
- this.field_manager = field_manager;
this.name = this.node.attrs.name;
this.field = this.field_manager.get_field(this.name);
this.widget = this.node.attrs.widget;
this.string = this.node.attrs.string || this.field.string || this.name;
this.options = JSON.parse(this.node.attrs.options || '{}');
this.set({'value': false});
- this.set({required: this.modifiers['required'] === true});
- // some events to make the property "effective_readonly" sync automatically with "readonly" and
- // "force_readonly"
- this.set({"readonly": this.modifiers['readonly'] === true});
- this.set({"force_readonly": false});
- var test_effective_readonly = function() {
- 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);
- test_effective_readonly.call(this);
this.on("change:value", this, function() {
this.trigger('changed_value');
this._check_css_flags();
@@ -2030,7 +2063,7 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.w
},
_check_css_flags: function() {
if (this.field.translate) {
- this.$el.find('.oe_field_translate').toggle(!this.field_manager.is_create_mode());
+ this.$el.find('.oe_field_translate').toggle(this.field_manager.get('actual_mode') !== "create");
}
if (!this.disable_utility_classes) {
if (this.field_manager.get('display_invalid_fields')) {
@@ -2054,10 +2087,10 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.w
});
/**
- * A mixin to apply on any field that has to completely re-render when its readonly state
+ * A mixin to apply on any FormWidget that has to completely re-render when its readonly state
* switch.
*/
-instance.web.form.ReinitializeFieldMixin = {
+instance.web.form.ReinitializeWidgetMixin = {
/**
* Default implementation of start(), use it or call explicitly initialize_field().
*/
@@ -2070,10 +2103,8 @@ instance.web.form.ReinitializeFieldMixin = {
this.destroy_content();
this.renderElement();
this.initialize_content();
- this.render_value();
});
this.initialize_content();
- this.render_value();
},
/**
* Called to destroy anything that could have been created previously, called before a
@@ -2084,11 +2115,25 @@ instance.web.form.ReinitializeFieldMixin = {
* Called to initialize the content.
*/
initialize_content: function() {},
+};
+
+/**
+ * A mixin to apply on any field that has to completely re-render when its readonly state
+ * switch.
+ */
+instance.web.form.ReinitializeFieldMixin = _.extend({}, instance.web.form.ReinitializeWidgetMixin, {
+ initialize_field: function() {
+ instance.web.form.ReinitializeWidgetMixin.initialize_field.call(this);
+ this.on("change:effective_readonly", this, function() {
+ this.render_value();
+ });
+ this.render_value();
+ },
/**
* Called to render the value. Should also be explicitly called at the end of a set_value().
*/
render_value: function() {},
-};
+});
instance.web.form.FieldChar = instance.web.form.AbstractField.extend(instance.web.form.ReinitializeFieldMixin, {
template: 'FieldChar',
@@ -2670,7 +2715,7 @@ instance.web.form.CompletionFieldMixin = {
values.push({
label: _t("Search More..."),
action: function() {
- dataset.name_search(search_val, self.build_domain(), 'ilike', false, function(data) {
+ dataset.name_search(search_val, self.build_domain(), 'ilike', false).then(function(data) {
self._search_create_popup("search", data);
});
},
@@ -2711,7 +2756,7 @@ instance.web.form.CompletionFieldMixin = {
};
if (self.options.quick_create === undefined || self.options.quick_create) {
new instance.web.DataSet(this, this.field.relation, self.build_context())
- .name_create(name, function(data) {
+ .name_create(name).then(function(data) {
self.add_id(data[0]);
}).fail(function(error, event) {
event.preventDefault();
@@ -2996,7 +3041,7 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
}
if (! no_recurse) {
var dataset = new instance.web.DataSetStatic(this, this.field.relation, self.build_context());
- dataset.name_get([self.get("value")], function(data) {
+ dataset.name_get([self.get("value")]).then(function(data) {
self.display_value["" + self.get("value")] = data[0][1];
self.render_value(true);
});
@@ -3007,7 +3052,9 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
if (!this.get("effective_readonly")) {
this.$input.val(str.split("\n")[0]);
this.current_display = this.$input.val();
- this.$('.oe_m2o_cm_button').css({'visibility': this.is_false() ? 'hidden' : 'visible'});
+ if(this.is_false()){
+ this.$('.oe_m2o_cm_button').css({'display':'none'});
+ }
} else {
var lines = _.escape(str).split("\n");
var link = "";
@@ -3437,7 +3484,7 @@ instance.web.form.One2ManyViewManager = instance.web.ViewManager.extend({
title: _t("Open: ") + self.o2m.string,
create_function: function(data) {
return self.o2m.dataset.create(data).then(function(r) {
- self.o2m.dataset.set_ids(self.o2m.dataset.ids.concat([r.result]));
+ self.o2m.dataset.set_ids(self.o2m.dataset.ids.concat([r]));
self.o2m.dataset.on_change();
});
},
@@ -3527,7 +3574,7 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({
alternative_form_view: self.o2m.field.views ? self.o2m.field.views["form"] : undefined,
create_function: function(data, callback, error_callback) {
return self.o2m.dataset.create(data).then(function(r) {
- self.o2m.dataset.set_ids(self.o2m.dataset.ids.concat([r.result]));
+ self.o2m.dataset.set_ids(self.o2m.dataset.ids.concat([r]));
self.o2m.dataset.on_change();
}).then(callback, error_callback);
},
@@ -3552,7 +3599,7 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({
pop.show_element(self.o2m.field.relation, id, self.o2m.build_context(), {
title: _t("Open: ") + self.o2m.string,
write_function: function(id, data) {
- return self.o2m.dataset.write(id, data, {}, function(r) {
+ return self.o2m.dataset.write(id, data, {}).then(function() {
self.o2m.reload_current_view();
});
},
@@ -4505,10 +4552,10 @@ instance.web.form.FieldReference = instance.web.form.AbstractField.extend(instan
initialize_content: function() {
var self = this;
this.selection = new instance.web.form.FieldSelection(this, { attrs: {
- name: 'selection'
+ name: 'selection',
+ modifiers: JSON.stringify({readonly: this.get('effective_readonly')}),
}});
this.selection.view = this.view;
- this.selection.set({force_readonly: this.get('effective_readonly')});
this.selection.on("change:value", this, this.on_selection_changed);
this.selection.setElement(this.$(".oe_form_view_reference_selection"));
this.selection.renderElement();
@@ -4518,10 +4565,10 @@ instance.web.form.FieldReference = instance.web.form.AbstractField.extend(instan
.on('blurred', null, function () {self.trigger('blurred')});
this.m2o = new instance.web.form.FieldMany2One(this, { attrs: {
- name: 'm2o'
+ name: 'm2o',
+ modifiers: JSON.stringify({readonly: this.get('effective_readonly')}),
}});
this.m2o.view = this.view;
- this.m2o.set({force_readonly: this.get("effective_readonly")});
this.m2o.on("change:value", this, this.data_changed);
this.m2o.setElement(this.$(".oe_form_view_reference_m2o"));
this.m2o.renderElement();
@@ -4826,7 +4873,10 @@ instance.web.form.FieldStatus = instance.web.form.AbstractField.extend({
get_selection: function() {
var self = this;
if (this.field.type == "many2one") {
- var domain = new instance.web.CompoundDomain(['|'], self.build_domain(), [['id', '=', self.selected_value]]);
+ var domain = [];
+ if(!_.isEmpty(this.field.domain) || !_.isEmpty(this.node.attrs.domain)) {
+ domain = new instance.web.CompoundDomain(['|'], self.build_domain(), [['id', '=', self.selected_value]]);
+ }
var ds = new instance.web.DataSetSearch(this, this.field.relation, self.build_context(), domain);
ds.read_slice(['name'], {}).done( function (records) {
for(var i = 0; i < records.length; i++) {
@@ -4923,6 +4973,9 @@ instance.web.form.tags = new instance.web.Registry({
'button' : 'instance.web.form.WidgetButton',
});
+instance.web.form.custom_widgets = new instance.web.Registry({
+});
+
};
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js
index 8fcda146cd4..fd34ffddb7b 100644
--- a/addons/web/static/src/js/view_list.js
+++ b/addons/web/static/src/js/view_list.js
@@ -989,7 +989,7 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.
// to get a correctly displayable value in the field
var model = ref_match[1],
id = parseInt(ref_match[2], 10);
- new instance.web.DataSet(this.view, model).name_get([id], function(names) {
+ new instance.web.DataSet(this.view, model).name_get([id]).then(function(names) {
if (!names.length) { return; }
record.set(column.id, names[0][1]);
});
@@ -1005,11 +1005,33 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.
// and let the various registered events handle refreshing the
// row
new instance.web.DataSet(this.view, column.relation)
- .name_get([value], function (names) {
+ .name_get([value]).then(function (names) {
if (!names.length) { return; }
record.set(column.id, names[0]);
});
}
+ } else if (column.type === 'many2many') {
+ value = record.get(column.id);
+ // non-resolved (string) m2m values are arrays
+ if (value instanceof Array && !_.isEmpty(value)) {
+ var ids;
+ // they come in two shapes:
+ if (value[0] instanceof Array) {
+ var command = value[0];
+ // 1. an array of m2m commands (usually (6, false, ids))
+ if (command[0] !== 6) {
+ throw new Error(_t("Unknown m2m command ") + command[0]);
+ }
+ ids = command[2];
+ } else {
+ // 2. an array of ids
+ ids = value;
+ }
+ new instance.web.Model(column.relation)
+ .call('name_get', [ids]).then(function (names) {
+ record.set(column.id, _(names).pluck(1).join(', '));
+ })
+ }
}
return column.format(record.toForm().data, {
model: this.dataset.model,
@@ -1052,11 +1074,7 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.
var row = cells.join('');
this.$current
.children('tr:not([data-id])').remove().end()
- .append(new Array(count - this.records.length + 1).join(row)).click(
- function() {
- $('button.oe_list_add').effect('bounce', {distance: 18, times: 5}, 150);
- }
- );
+ .append(new Array(count - this.records.length + 1).join(row));
},
/**
* Gets the ids of all currently selected records, if any
@@ -2056,16 +2074,20 @@ instance.web.list.Button = instance.web.list.Column.extend({
* Return an actual ``<button>`` tag
*/
format: function (row_data, options) {
- return _.template('<button type="button" title="<%-title%>" <%=additional_attributes%> >' +
- '<img src="<%-prefix%>/web/static/src/img/icons/<%-icon%>.png" alt="<%-alt%>"/>' +
- '</button>', {
- title: this.string || '',
- additional_attributes: isNaN(row_data["id"].value) && instance.web.BufferedDataSet.virtual_id_regex.test(row_data["id"].value) ?
- 'disabled="disabled" class="oe_list_button_disabled"' : '',
- prefix: instance.session.prefix,
- icon: this.icon,
- alt: this.string || ''
- });
+ options = options || {};
+ var attrs = {};
+ if (options.process_modifiers !== false) {
+ attrs = this.modifiers_for(row_data);
+ }
+ if (attrs.invisible) { return ''; }
+
+ return QWeb.render('ListView.row.button', {
+ widget: this,
+ prefix: instance.session.prefix,
+ disabled: attrs.readonly
+ || isNaN(row_data.id.value)
+ || instance.web.BufferedDataSet.virtual_id_regex.test(row_data.id.value)
+ });
}
});
instance.web.list.Boolean = instance.web.list.Column.extend({
diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js
index d170aacc89d..9e7db823bc0 100644
--- a/addons/web/static/src/js/views.js
+++ b/addons/web/static/src/js/views.js
@@ -930,12 +930,10 @@ instance.web.Sidebar = instance.web.Widget.extend({
var view = this.getParent();
this.sections = [
{ 'name' : 'print', 'label' : _t('Print'), },
- { 'name' : 'files', 'label' : _t('Attachment(s)'), },
{ 'name' : 'other', 'label' : _t('More'), }
];
this.items = {
'print' : [],
- 'files' : [],
'other' : []
};
this.fileupload_id = _.uniqueId('oe_fileupload');
@@ -1217,11 +1215,11 @@ instance.web.View = instance.web.Widget.extend({
}
}
args.push(context);
- return dataset.call_button(action_data.name, args, handler);
+ return dataset.call_button(action_data.name, args).then(handler);
} else if (action_data.type=="action") {
return this.rpc('/web/action/load', { action_id: action_data.name, context: context, do_not_eval: true}, handler);
} else {
- return dataset.exec_workflow(record_id, action_data.name, handler);
+ return dataset.exec_workflow(record_id, action_data.name).then(handler);
}
},
/**
diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml
index b035c2c8c14..dfe86036c45 100644
--- a/addons/web/static/src/xml/base.xml
+++ b/addons/web/static/src/xml/base.xml
@@ -2,6 +2,12 @@
<!-- vim:fdl=1:
-->
<templates id="template" xml:space="preserve">
+<t t-name="ui.Header">
+ <header>
+ <t t-raw="__content__"/>
+ </header>
+</t>
+
<t t-name="EmptyComponent">
<div></div>
</t>
@@ -314,7 +320,7 @@
<t t-name="Menu">
<ul class="oe_menu" t-if="widget.data">
<li t-foreach="widget.data.data.children" t-as="menu">
- <t t-call="Menu.secondary.link"/>
+ <t t-call="Menu.link"/>
</li>
</ul>
</t>
@@ -333,7 +339,7 @@
<t t-esc="menu.name"/>
<!--
Shall the section be still clickable ?
- <t t-call="Menu.secondary.link"/>
+ <t t-call="Menu.link"/>
-->
</div>
<t t-call="Menu.secondary.submenu"/>
@@ -343,13 +349,13 @@
<t t-name="Menu.secondary.submenu">
<ul t-if="menu.children.length" class="oe_secondary_submenu">
<li t-foreach="menu.children" t-as="menu">
- <t t-call="Menu.secondary.link"/>
+ <t t-call="Menu.link"/>
<!--<span class="oe_menu_label">8</span>-->
<t t-call="Menu.secondary.submenu"/>
</li>
</ul>
</t>
-<t t-name="Menu.secondary.link">
+<t t-name="Menu.link">
<a t-attf-href="#menu_id=#{menu.id}&amp;action=#{menu.action ? menu.action.split(',')[1] : ''}"
t-att-class="menu.children.length ? 'oe_menu_toggler' : 'oe_menu_leaf'"
t-att-data-menu="menu.id"
@@ -577,7 +583,7 @@
<tr>
<th t-foreach="fields_view" t-as="field"
t-if="!field.attrs.modifiers.tree_invisible">
- <t t-esc="fields[field.attrs.name].string" />
+ <t t-esc="field_value.attrs.string || fields[field.attrs.name].string" />
</th>
</tr>
</thead>
@@ -592,7 +598,7 @@
<t t-set="children" t-value="record[children_field]"/>
<t t-set="class" t-value="children and children.length ? 'treeview-tr' : 'treeview-td'"/>
<t t-set="rank" t-value="'oe-treeview-first'"/>
- <t t-set="style" t-value="'background-position: ' + 19*(level-1) + 'px; padding-left: ' + 19*(level-1) + 'px;'"/>
+ <t t-set="style" t-value="'background-position: ' + 19*(level-1) + 'px; padding-left: ' + (4 + 19*(level-1)) + 'px;'"/>
<td t-foreach="fields_view" t-as="field"
t-if="!field.attrs.modifiers.tree_invisible"
@@ -650,11 +656,9 @@
</table>
<div t-name="ListView.buttons" class="oe_list_buttons">
<t t-if="!widget.no_leaf and widget.options.action_buttons !== false and widget.options.addable and widget.is_action_enabled('create')">
- <div name="oe_list_add_div">
- <button type="button" class="oe_button oe_list_add oe_highlight">
- <t t-esc="widget.options.addable"/>
- </button>
- </div>
+ <button type="button" class="oe_button oe_list_add oe_highlight">
+ <t t-esc="widget.options.addable"/>
+ </button>
</t>
</div>
<t t-name="ListView.pager">
@@ -695,7 +699,7 @@
</td>
</tr>
<t t-extend="ListView.buttons">
- <t t-jquery="div.oe_list_add_div" t-operation="after">
+ <t t-jquery="button.oe_list_add" t-operation="after">
<button class="oe_button oe_list_save oe_highlight"
type="button">Save</button>
<span class="oe_alternative">
@@ -704,6 +708,11 @@
</span>
</t>
</t>
+<button t-name="ListView.row.button" type="button"
+ t-att-title="widget.string" t-att-disabled="disabled || undefined"
+ t-att-class="disabled ? 'oe_list_button_disabled' : undefined"
+ ><img t-attf-src="#{prefix}/web/static/src/img/icons/#{widget.icon}.png"
+ t-att-alt="widget.string" /></button>
<t t-extend="ListView.row">
<!-- adds back padding to row being rendered after edition, if necessary
(if not deletable add back padding), otherwise the row being added is