summaryrefslogtreecommitdiff
path: root/addons/web/static/src
diff options
context:
space:
mode:
Diffstat (limited to 'addons/web/static/src')
-rw-r--r--addons/web/static/src/css/Makefile8
-rw-r--r--addons/web/static/src/css/base.css620
-rw-r--r--addons/web/static/src/css/base.sass496
-rw-r--r--addons/web/static/src/css/data_export.css5
-rw-r--r--addons/web/static/src/img/throbber-large.gifbin0 -> 28519 bytes
-rw-r--r--addons/web/static/src/js/chrome.js295
-rw-r--r--addons/web/static/src/js/core.js6
-rw-r--r--addons/web/static/src/js/data.js204
-rw-r--r--addons/web/static/src/js/formats.js7
-rw-r--r--addons/web/static/src/js/openerpframework.js69
-rw-r--r--addons/web/static/src/js/pyeval.js60
-rw-r--r--addons/web/static/src/js/search.js32
-rw-r--r--addons/web/static/src/js/testing.js114
-rw-r--r--addons/web/static/src/js/view_form.js310
-rw-r--r--addons/web/static/src/js/view_list.js47
-rw-r--r--addons/web/static/src/js/view_list_editable.js29
-rw-r--r--addons/web/static/src/js/views.js144
-rw-r--r--addons/web/static/src/xml/base.xml133
18 files changed, 1407 insertions, 1172 deletions
diff --git a/addons/web/static/src/css/Makefile b/addons/web/static/src/css/Makefile
index d6b4f4b2fc8..ba88946baac 100644
--- a/addons/web/static/src/css/Makefile
+++ b/addons/web/static/src/css/Makefile
@@ -1,3 +1,5 @@
-base.css: base.sass
- sass --trace -t expanded base.sass base.css
-
+all: *.css
+%.css: %.sass
+ sass -t expanded --compass --unix-newlines $< $@
+watch:
+ sass -t expanded --compass --unix-newlines --watch .:.
diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css
index 87e129bb211..e43a2fb654e 100644
--- a/addons/web/static/src/css/base.css
+++ b/addons/web/static/src/css/base.css
@@ -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");
@@ -27,7 +27,6 @@
color: #4c4c4c;
font-size: 13px;
background: white;
- text-shadow: 0 1px 1px rgba(255, 255, 255, 0.5);
/* http://www.quirksmode.org/dom/inputfile.html
* http://stackoverflow.com/questions/2855589/replace-input-type-file-by-an-image
*/
@@ -79,6 +78,20 @@
.openerp td {
vertical-align: top;
}
+.openerp .oe_title {
+ width: 50%;
+ float: left;
+}
+.openerp .oe_title:after {
+ content: ".";
+ display: block;
+ height: 0;
+ clear: both;
+ visibility: hidden;
+}
+.openerp .oe_form_group {
+ clear: both;
+}
.openerp .zebra tbody tr:nth-child(odd) td {
background-color: #f0f0fa;
background-color: #efeff8;
@@ -111,8 +124,10 @@
}
.openerp h4 {
margin: 4px 0;
+ font-weight: bold;
+ font-size: inherit;
}
-.openerp a.button:link, .openerp a.button:visited, .openerp button, .openerp input[type='submit'], .openerp .ui-dialog-buttonpane .ui-dialog-buttonset .ui-button {
+.openerp a.button:link, .openerp a.button:visited, .openerp button, .openerp .oe_button, .openerp input[type='submit'], .openerp .ui-dialog-buttonpane .ui-dialog-buttonset .ui-button {
display: inline-block;
border: 1px solid rgba(0, 0, 0, 0.4);
color: #4c4c4c;
@@ -137,7 +152,7 @@
-webkit-font-smoothing: antialiased;
outline: none;
}
-.openerp a.button:hover, .openerp button:hover, .openerp input[type='submit']:hover, .openerp .ui-dialog-buttonpane .ui-dialog-buttonset .ui-button.ui-state-hover {
+.openerp a.button:hover, .openerp button:hover, .openerp .oe_button:hover, .openerp input[type='submit']:hover, .openerp .ui-dialog-buttonpane .ui-dialog-buttonset .ui-button.ui-state-hover {
background-color: #ececec;
background-image: -webkit-gradient(linear, left top, left bottom, from(#f6f6f6), to(#e3e3e3));
background-image: -webkit-linear-gradient(top, #f6f6f6, #e3e3e3);
@@ -148,7 +163,7 @@
cursor: pointer;
background-position: 0;
}
-.openerp a.button:focus, .openerp button:focus, .openerp input[type='submit']:focus, .openerp .ui-dialog-buttonpane .ui-dialog-buttonset .ui-button.ui-state-focus {
+.openerp a.button:focus, .openerp button:focus, .openerp .oe_button:focus, .openerp input[type='submit']:focus, .openerp .ui-dialog-buttonpane .ui-dialog-buttonset .ui-button.ui-state-focus {
border: 1px solid #80bfff;
background-position: 0;
background-color: #ececec;
@@ -162,7 +177,7 @@
-webkit-box-shadow: 0 0 3px #80bfff, 0 1px 1px rgba(255, 255, 255, 0.8) inset;
box-shadow: 0 0 3px #80bfff, 0 1px 1px rgba(255, 255, 255, 0.8) inset;
}
-.openerp a.button:active, .openerp a.button.active, .openerp button:active, .openerp button.active, .openerp input[type='submit']:active, .openerp input[type='submit'].active, .openerp .ui-dialog-buttonpane .ui-dialog-buttonset .ui-button.ui-state-active {
+.openerp a.button:active, .openerp a.button.active, .openerp button:active, .openerp .oe_button:active, .openerp .oe_button.active, .openerp input[type='submit']:active, .openerp input[type='submit'].active, .openerp .ui-dialog-buttonpane .ui-dialog-buttonset .ui-button.ui-state-active {
background-color: #ececec;
background-image: -webkit-gradient(linear, left top, left bottom, from(#e3e3e3), to(#f6f6f6));
background-image: -webkit-linear-gradient(top, #e3e3e3, #f6f6f6);
@@ -174,7 +189,7 @@
-webkit-box-shadow: none;
box-shadow: none;
}
-.openerp a.button.disabled, .openerp button:disabled, .openerp input[type='submit']:disabled {
+.openerp a.button.disabled, .openerp button:disabled, .openerp .oe_button:disabled, .openerp input[type='submit']:disabled {
background: #efefef !important;
border: 1px solid #d1d1d1 !important;
-moz-box-shadow: none !important;
@@ -199,6 +214,7 @@
}
.openerp.ui-dialog {
display: none;
+ height: auto !important;
padding: 6px;
background-color: rgba(60, 60, 60, 0.7);
border: 1px solid;
@@ -220,6 +236,9 @@
padding: 16px;
}
.openerp.ui-dialog .ui-dialog-titlebar {
+ border-top: none;
+ border-left: none;
+ border-right: none;
border-bottom: 1px solid #cacaca;
-moz-border-radius: 2px 2px 0 0;
-webkit-border-radius: 2px 2px 0 0;
@@ -236,11 +255,9 @@
margin: 0;
padding: 0;
}
-.openerp.ui-dialog .ui-widget-header {
- border: none;
-}
.openerp.ui-dialog .ui-dialog-content {
background: white;
+ width: auto !important;
}
.openerp.ui-dialog .ui-dialog-buttonpane {
border-top: 1px solid #e0e0e0;
@@ -346,6 +363,7 @@
font-family: "entypoRegular" !important;
font-size: 34px;
font-weight: 300 !important;
+ line-height: 100%;
}
.openerp .oe_left {
float: left;
@@ -437,17 +455,6 @@
.openerp .oe_form_dirty button.oe_highlight_on_dirty:hover {
background: #ed6f6a;
}
-.openerp .oe_title {
- width: 50%;
- float: left;
-}
-.openerp .oe_title:after {
- content: ".";
- display: block;
- height: 0;
- clear: both;
- visibility: hidden;
-}
.openerp .oe_button_box {
width: 270px;
text-align: right;
@@ -529,6 +536,15 @@
.openerp .oe_grey {
color: #aaaaaa;
}
+.openerp .oe_error_detail hr {
+ display: block;
+ -webkit-margin-before: 0.5em;
+ -webkit-margin-after: 0.5em;
+ -webkit-margin-start: auto;
+ -webkit-margin-end: auto;
+ border-style: inset;
+ border-width: 1px;
+}
.openerp .oe_tag {
border: 1px solid #afafb6;
font-size: 11px;
@@ -671,6 +687,10 @@
background-color: #eeeeee;
border-color: #eeeeee #eeeeee #dddddd;
}
+.openerp .ui-tabs .oe_notebook.ui-tabs-nav li.ui-tabs-active {
+ border-bottom: none;
+ padding-bottom: 1px;
+}
.openerp .oe_notebook > li.ui-state-active > a, .openerp .oe_notebook > li.ui-state-active > a:hover {
background-color: white;
border: 1px solid #dddddd;
@@ -680,6 +700,9 @@
.openerp .oe_notebook_page {
padding: 0;
}
+.openerp .oe_notebook_page > label:not([for]) {
+ font-weight: normal !important;
+}
.openerp div.ui-tabs {
padding: 3px 0px 3px 0px;
}
@@ -739,9 +762,6 @@
display: block;
color: #4c4c4c;
text-decoration: none;
- width: 200px;
- text-overflow: ellipsis;
- overflow: hidden;
}
.openerp .oe_dropdown_menu > li > a:hover {
text-decoration: none;
@@ -858,153 +878,6 @@
padding: 5px;
width: 32px;
}
-.openerp .oe_login {
- background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAGCAYAAADgzO9IAAAAKUlEQVQIHWO8e/fufwYsgAUkJigoiCIF5DMyoYggcUiXgNnBiGQKmAkARpcEQeriln4AAAAASUVORK5CYII=);
- text-align: center;
- font-size: 14px;
- height: 100%;
-}
-.openerp .oe_login ul, .openerp .oe_login li {
- margin: 0;
- list-style-type: none;
- padding: 0 0 4px 0;
-}
-.openerp .oe_login button {
- cursor: pointer;
- padding: 6px 16px;
- border: 1px solid #222222;
- color: white;
- margin: 0;
- background-color: #8c1313;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#b92020), to(#600606));
- background-image: -webkit-linear-gradient(top, #b92020, #600606);
- background-image: -moz-linear-gradient(top, #b92020, #600606);
- background-image: -ms-linear-gradient(top, #b92020, #600606);
- background-image: -o-linear-gradient(top, #b92020, #600606);
- background-image: linear-gradient(to bottom, #b92020, #600606);
- -moz-border-radius: 4px;
- -webkit-border-radius: 4px;
- border-radius: 4px;
- -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(155, 155, 155, 0.4) inset;
- -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(155, 155, 155, 0.4) inset;
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(155, 155, 155, 0.4) inset;
-}
-.openerp .oe_login input, .openerp .oe_login select {
- width: 252px;
- font-family: "Lucida Grande", Helvetica, Verdana, Arial;
- border: 1px solid #999999;
- background: whitesmoke;
- -moz-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.4);
- -webkit-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.4);
- box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.4);
- -moz-border-radius: 3px;
- -webkit-border-radius: 3px;
- border-radius: 3px;
-}
-.openerp .oe_login input {
- margin-bottom: 9px;
- padding: 5px 6px;
-}
-.openerp .oe_login select {
- padding: 1px;
-}
-.openerp .oe_login .oe_login_dbpane {
- position: fixed;
- top: 0;
- right: 8px;
- padding: 5px 10px;
- color: #eeeeee;
- border: solid 1px #333333;
- background: #1e1e1e;
- background: rgba(30, 30, 30, 0.94);
- -moz-border-radius: 0 0 8px 8px;
- -webkit-border-radius: 0 0 8px 8px;
- border-radius: 0 0 8px 8px;
-}
-.openerp .oe_login .oe_login_dbpane input {
- padding: 2px 4px;
- margin: 4px 0;
-}
-.openerp .oe_login .oe_login_bottom {
- position: absolute;
- top: 50%;
- left: 0;
- right: 0;
- bottom: 0;
- text-shadow: 0 1px 1px #999999;
- background-color: #8a0e0e;
- 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);
-}
-.openerp .oe_login .oe_login_pane {
- position: absolute;
- top: 50%;
- left: 50%;
- margin: -160px -166px;
- border: solid 1px #333333;
- background: #1e1e1e;
- background: rgba(30, 30, 30, 0.94);
- padding: 22px 32px;
- color: #eeeeee;
- text-align: left;
- -moz-border-radius: 8px;
- -webkit-border-radius: 8px;
- border-radius: 8px;
- -moz-box-shadow: 0 0 18px rgba(0, 0, 0, 0.9);
- -webkit-box-shadow: 0 0 18px rgba(0, 0, 0, 0.9);
- box-shadow: 0 0 18px rgba(0, 0, 0, 0.9);
-}
-.openerp .oe_login .oe_login_pane h2 {
- margin-top: 0;
- font-size: 18px;
-}
-.openerp .oe_login .oe_login_logo {
- position: absolute;
- top: -70px;
- left: 0;
- width: 100%;
- margin: 0 auto;
- text-align: center;
-}
-.openerp .oe_login .oe_login_footer {
- position: absolute;
- bottom: -40px;
- left: 0;
- width: 100%;
- text-align: center;
-}
-.openerp .oe_login .oe_login_footer a {
- color: #eeeeee;
- margin: 0 8px;
-}
-.openerp .oe_login .oe_login_footer a:hover {
- text-decoration: underline;
-}
-.openerp .oe_login .oe_login_footer span {
- font-weight: bold;
- font-size: 16px;
-}
-.openerp .oe_login .oe_login_error_message {
- display: none;
- background-color: #b41616;
- color: #eeeeee;
- padding: 14px 18px;
- margin-top: 15px;
- text-align: center;
- -moz-border-radius: 4px;
- -webkit-border-radius: 4px;
- border-radius: 4px;
- -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.8);
- -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.8);
- box-shadow: 0 1px 4px rgba(0, 0, 0, 0.8);
-}
-.openerp .oe_login_invalid .oe_login_error_message {
- display: inline-block;
-}
.openerp .oe_database_manager {
background: white;
color: black;
@@ -1025,19 +898,7 @@
.openerp .oe_content_full_screen .oe_topbar, .openerp .oe_content_full_screen .oe_leftbar {
display: none;
}
-.openerp .oe_topbar {
- width: 100%;
- height: 32px;
- background-color: #414141;
- background-color: #454343;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#646060), to(#262626));
- background-image: -webkit-linear-gradient(top, #646060, #262626);
- background-image: -moz-linear-gradient(top, #646060, #262626);
- background-image: -ms-linear-gradient(top, #646060, #262626);
- background-image: -o-linear-gradient(top, #646060, #262626);
- background-image: linear-gradient(to bottom, #646060, #262626);
-}
-.openerp .oe_topbar .oe_topbar_anonymous_login {
+.openerp .navbar .oe_topbar_anonymous_login {
background-color: #dc5f59;
color: #eeeeee;
background-color: #be4343;
@@ -1046,13 +907,13 @@
background-image: -moz-linear-gradient(top, #fc8787, maroon);
background-image: -ms-linear-gradient(top, #fc8787, maroon);
background-image: -o-linear-gradient(top, #fc8787, maroon);
- background-image: linear-gradient(to bottom, #fc8787, maroon);
+ background-image: linear-gradient(to bottom, #fc8787, #800000);
}
-.openerp .oe_topbar .oe_topbar_anonymous_login a {
+.openerp .navbar .oe_topbar_anonymous_login a {
display: block;
padding: 5px 10px 7px;
line-height: 20px;
- height: 20px;
+ height: 30px;
text-decoration: none;
color: white;
background: transparent;
@@ -1062,7 +923,7 @@
-o-transition: all 0.2s ease-out;
transition: all 0.2s ease-out;
}
-.openerp .oe_topbar .oe_topbar_anonymous_login a:hover {
+.openerp .navbar .oe_topbar_anonymous_login a:hover {
background: rgba(0, 0, 0, 0.1);
color: white;
text-shadow: 0px 0px 3px rgba(0, 0, 0, 0.2);
@@ -1070,9 +931,9 @@
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2) inset;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2) inset;
}
-.openerp .oe_topbar .oe_topbar_item {
+.openerp .navbar .oe_topbar_item {
display: block;
- padding: 5px 10px 7px;
+ padding: 5px 10px 26px;
line-height: 20px;
height: 20px;
text-decoration: none;
@@ -1085,7 +946,7 @@
-o-transition: all 0.2s ease-out;
transition: all 0.2s ease-out;
}
-.openerp .oe_topbar .oe_topbar_item:hover {
+.openerp .navbar .oe_topbar_item:hover {
background: rgba(0, 0, 0, 0.2);
text-shadow: black 0px 0px 3px;
color: white;
@@ -1093,7 +954,7 @@
-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 {
+.openerp .navbar .oe_topbar_avatar {
width: 24px;
height: 24px;
margin: -2px 2px 0 0;
@@ -1101,13 +962,13 @@
-webkit-border-radius: 3px;
border-radius: 3px;
}
-.openerp .oe_topbar .oe_topbar_avatar {
+.openerp .navbar .oe_topbar_avatar {
vertical-align: top;
}
-.openerp .oe_topbar .oe_dropdown_arrow:after {
+.openerp .navbar .oe_dropdown_arrow:after {
border-top: 4px solid white;
}
-.openerp .oe_topbar .oe_dropdown_menu {
+.openerp .navbar .dropdown_menu {
top: 32px;
background: #333333;
background: rgba(37, 37, 37, 0.9);
@@ -1122,14 +983,14 @@
-moz-background-clip: padding-box;
background-clip: padding-box;
}
-.openerp .oe_topbar .oe_dropdown_menu li {
+.openerp .navbar .dropdown_menu li {
float: none;
padding: 3px 12px;
}
-.openerp .oe_topbar .oe_dropdown_menu li a {
+.openerp .navbar .dropdown_menu li a {
color: #eeeeee;
}
-.openerp .oe_topbar .oe_dropdown_menu li:hover {
+.openerp .navbar .dropdown_menu li:hover {
background-color: #212121;
background-image: -webkit-gradient(linear, left top, left bottom, from(#292929), to(#191919));
background-image: -webkit-linear-gradient(top, #292929, #191919);
@@ -1141,21 +1002,21 @@
-webkit-box-shadow: none;
box-shadow: none;
}
-.openerp .oe_topbar .oe_topbar_name {
+.openerp .navbar .oe_topbar_name {
max-width: 150px;
- overflow: hidden;
display: inline-block;
- max-height: 100%;
+ height: 15px;
text-overflow: ellipsis;
white-space: nowrap;
+ overflow: hidden;
}
-.openerp .oe_menu {
+.openerp .navbar-nav {
float: left;
padding: 0;
margin: 0;
font-size: 13px;
}
-.openerp .oe_menu > li {
+.openerp .navbar-nav > li {
list-style-type: none;
padding: 0;
margin: 0;
@@ -1163,13 +1024,13 @@
display: block;
color: #eeeeee;
}
-.openerp .oe_menu > li > a {
+.openerp .navbar-nav > li > a {
display: block;
- padding: 5px 10px 7px;
+ padding: 5px 10px 26px !important;
line-height: 20px;
height: 20px;
text-decoration: none;
- color: #eeeeee;
+ color: #eeeeee !important;
vertical-align: top;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
-webkit-transition: all 0.2s ease-out;
@@ -1178,7 +1039,7 @@
-o-transition: all 0.2s ease-out;
transition: all 0.2s ease-out;
}
-.openerp .oe_menu > li > a:hover {
+.openerp .navbar-nav > li > a:hover {
background: rgba(0, 0, 0, 0.2);
text-shadow: black 0px 0px 3px;
color: white;
@@ -1186,7 +1047,7 @@
-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 {
+.openerp .navbar-nav > li > .active {
background: rgba(0, 0, 0, 0.3);
text-shadow: black 0px 0px 3px;
-moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4) inset;
@@ -1217,7 +1078,7 @@
width: 220px;
background: #f0eeee;
border-right: 1px solid #afafb6;
- text-shadow: 0 1px 1px white;
+ text-shadow: none;
padding-bottom: 16px;
}
.openerp a.oe_logo {
@@ -1269,37 +1130,9 @@
color: #7c7bad;
}
.openerp .oe_secondary_submenu {
- padding: 2px 0 8px 0;
- margin: 0;
-}
-.openerp .oe_secondary_submenu li {
- position: relative;
- margin: 0;
- padding: 1px 0 1px 20px !important;
- list-style-type: none;
-}
-.openerp .oe_secondary_submenu li a {
- display: block;
- color: #4c4c4c;
- padding: 2px 4px 2px 0;
-}
-.openerp .oe_secondary_submenu li .oe_menu_label {
- position: absolute;
- top: 1px;
- right: 1px;
- font-size: 10px;
- background: #7c7bad;
- color: white;
- padding: 2px 4px;
- margin: 1px 6px 0 0;
- border: 1px solid lightgrey;
- text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
- -moz-border-radius: 4px;
- -webkit-border-radius: 4px;
- border-radius: 4px;
- -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.2);
- -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.2);
- box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.2);
+ margin-bottom: 10px !important;
+ line-height: 1.1em;
+ margin-top: 3px;
}
.openerp .oe_secondary_submenu .oe_menu_text {
white-space: nowrap;
@@ -1307,15 +1140,9 @@
display: inline-block;
text-overflow: ellipsis;
max-width: 85%;
+ margin-top: 1px;
}
-.openerp .oe_secondary_submenu .oe_menu_counter {
- float: right;
- text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
- margin: 1px;
- padding: 1px 4px;
- border: none;
-}
-.openerp .oe_secondary_submenu .oe_menu_counter:hover {
+.openerp .oe_secondary_submenu .badge:hover {
cursor: pointer;
-webkit-transform: scale(1.1);
-moz-transform: scale(1.1);
@@ -1323,33 +1150,6 @@
-o-transform: scale(1.1);
transform: scale(1.1);
}
-.openerp .oe_secondary_submenu .oe_active {
- border-top: 1px solid lightgrey;
- 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);
- -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.2), inset 0 -1px 3px rgba(40, 40, 40, 0.2);
- box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.2), inset 0 -1px 3px rgba(40, 40, 40, 0.2);
- background: #7c7bad;
-}
-.openerp .oe_secondary_submenu .oe_active a {
- color: white;
-}
-.openerp .oe_secondary_submenu .oe_active .oe_menu_label {
- background: #eeeeee;
- color: #7c7bad;
- text-shadow: 0 1px 1px white;
- -moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
- -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
- box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
-}
-.openerp .oe_secondary_submenu .oe_active .oe_menu_counter {
- background: #eeeeee;
- color: #7c7bad;
- -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.2);
- -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.2);
- box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.2);
-}
.openerp .oe_secondary_submenu .oe_menu_toggler:before {
width: 0;
height: 0;
@@ -1374,9 +1174,6 @@
border-right: 4px solid transparent;
border-top: 4px solid #4c4c4c;
}
-.openerp .oe_secondary_submenu .oe_secondary_submenu {
- margin-left: -20px;
-}
.openerp .oe_secondary_submenu .oe_secondary_submenu li {
margin-left: 20px;
}
@@ -1422,6 +1219,9 @@
.openerp .oe_about .oe_bottom a {
color: #eeeeee;
}
+.openerp a.oe_form_uri:hover {
+ text-decoration: underline;
+}
.openerp .oe_application {
width: 100%;
height: 100%;
@@ -1429,9 +1229,6 @@
.openerp .oe_application a {
color: #7c7bad;
}
-.openerp .oe_application a:hover {
- text-decoration: underline;
-}
.openerp .oe_application > div {
height: 100%;
}
@@ -1443,6 +1240,7 @@
.openerp .oe_application .oe_breadcrumb_title > * {
display: inline-block;
overflow: hidden;
+ font-weight: bold;
}
.openerp .oe_view_manager {
display: table;
@@ -1485,6 +1283,7 @@
font-size: 18px;
margin: 0;
float: left;
+ line-height: 30px;
}
.openerp .oe_view_manager table.oe_view_manager_header h2 a {
color: #7c7bad;
@@ -1655,9 +1454,12 @@
.openerp .oe_view_manager_inline > .oe_view_manager_header, .openerp .oe_view_manager_inlineview > .oe_view_manager_header {
display: none;
}
-.openerp .oe_popup_form > .oe_formview > .oe_form_pager {
+.openerp .oe_popup_form .oe_formview .oe_form_pager {
display: none !important;
}
+.openerp .oe_popup_form table label {
+ font-weight: normal;
+}
.openerp .oe_popup_list_pager {
float: right;
}
@@ -1683,6 +1485,16 @@
font-size: 12px;
line-height: 18px;
}
+.openerp .oe_searchview input:not([type]), .openerp .oe_searchview input[type="text"], .openerp .oe_searchview input[type="number"] {
+ width: 156px;
+ height: 22px;
+}
+.openerp .oe_searchview input[type="checkbox"] {
+ margin: 3px 3px 3px 4px;
+}
+.openerp .oe_searchview select {
+ margin: 2px 4px 2px 0;
+}
.openerp .oe_searchview.oe_focused {
border-color: #a6a6fe;
-moz-box-shadow: 0 1px 2px #a6a6fe inset;
@@ -1793,7 +1605,6 @@
border-radius: 0 3px 3px 0;
}
.openerp .oe_searchview .oe_searchview_facets .oe_searchview_facet .oe_facet_category, .openerp .oe_searchview .oe_searchview_facets .oe_searchview_facet .oe_facet_value {
- height: 18px;
padding: 0 4px;
}
.openerp .oe_searchview .oe_searchview_facets .oe_searchview_facet .oe_facet_category {
@@ -1907,6 +1718,7 @@
box-sizing: border-box;
display: table-cell;
width: 50%;
+ padding-left: 2px;
}
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_section ul {
margin: 0 8px 8px;
@@ -1942,7 +1754,7 @@
line-height: 18px;
}
.openerp .oe_searchview .oe_searchview_drawer form button {
- margin: 0 0 8px 0;
+ margin: 0 0 8px -3px;
}
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_custom {
padding: 0 8px 8px 8px;
@@ -1970,6 +1782,9 @@
top: 0;
right: 5px;
}
+.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_custom label {
+ font-weight: normal;
+}
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_dashboard form {
display: none;
margin-top: 2px;
@@ -1997,6 +1812,7 @@
}
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_advanced li:first-child .searchview_extended_prop_or {
visibility: hidden;
+ margin-left: -14px;
}
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_advanced .searchview_extended_prop_or {
opacity: 0.5;
@@ -2188,6 +2004,12 @@
background: url(/web/static/src/img/form_sheetbg.png);
border-bottom: 1px solid #dddddd;
}
+.openerp .oe_application .oe_form_sheetbg .oe_subtotal_footer label {
+ font-weight: bold;
+}
+.openerp .oe_application .oe_form_sheetbg table label {
+ font-weight: normal;
+}
.openerp .oe_application .oe_form_sheet {
border: 1px solid #c8c8d3;
-moz-box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
@@ -2200,8 +2022,20 @@
.openerp .oe_application .oe_form_sheet .oe_notebook_page {
padding: 0 16px;
}
+.openerp .oe_form > :not(.oe_form_nosheet) header {
+ padding-left: 2px;
+}
+.openerp .oe_form > :not(.oe_form_nosheet) header ul {
+ display: inline-block;
+ float: right;
+}
+.openerp .oe_form > :not(.oe_form_nosheet) header .oe_button {
+ margin: 3px 2px 1px;
+}
+.openerp .oe_form > :not(.oe_form_nosheet) header .oe_button:first-child {
+ margin-left: 6px;
+}
.openerp .oe_form header {
- position: relative;
border-bottom: 1px solid #cacaca;
padding-left: 2px;
background-color: #ededed;
@@ -2215,16 +2049,6 @@
.openerp .oe_form header > span {
margin-left: 4px;
}
-.openerp .oe_form header ul {
- display: inline-block;
- float: right;
-}
-.openerp .oe_form header .oe_button {
- margin: 3px 2px 1px;
-}
-.openerp .oe_form header .oe_button:first-child {
- margin-left: 6px;
-}
.openerp .oe_form header .oe_tags {
margin: 5px 0 0 5px;
width: 400px;
@@ -2276,7 +2100,7 @@
padding: 2px 11px 2px 0px !important;
}
.openerp .oe_form .oe_subtotal_footer label.oe_form_label_help {
- font-weight: normal;
+ font-weight: normal !important;
}
.openerp .oe_form .oe_form_box_info {
background: #ffee99;
@@ -2308,7 +2132,8 @@
.openerp .oe_form td.oe_form_group_cell_label label {
line-height: 18px;
display: block;
- min-width: 140px;
+ min-width: 150px;
+ font-weight: bold !important;
}
.openerp .oe_form td.oe_form_group_cell + .oe_form_group_cell {
padding: 2px 0 2px 8px;
@@ -2321,7 +2146,6 @@
padding-left: 20px;
}
.openerp .oe_form .oe_form_label_help[for], .openerp .oe_form .oe_form_label[for] {
- font-weight: bold;
white-space: nowrap;
padding-right: 8px;
}
@@ -2405,6 +2229,18 @@
height: inherit !important;
font-size: inherit;
}
+.openerp .oe_form .oe_title h1, .openerp .oe_form h1 {
+ font-weight: bold;
+ font-size: 2em;
+}
+.openerp .oe_form h2 {
+ font-size: 1.5em;
+}
+.openerp .oe_form label {
+ font-weight: bold;
+ margin-bottom: 0px;
+ display: inline;
+}
.openerp .oe_form .oe_form_field {
width: 100%;
display: inline-block;
@@ -2542,6 +2378,9 @@
margin: 0;
padding: 0;
}
+.openerp .oe_form .oe_form_field_binary {
+ display: inline-block;
+}
.openerp .oe_form .oe_form_field_image {
padding: 0;
position: relative;
@@ -2573,13 +2412,14 @@
display: inline-block;
clear: both;
width: 100%;
+ margin-bottom: -15px;
}
.openerp .oe_fileupload .oe_add {
float: left;
position: relative;
width: 100%;
left: 2px;
- top: 7px;
+ margin: 9px 0;
overflow: hidden;
}
.openerp .oe_fileupload .oe_add button {
@@ -2591,9 +2431,6 @@
}
.openerp .oe_fileupload .oe_add button.oe_attach {
width: 24px;
- overflow: hidden;
- width: 24px;
- overflow: hidden;
background: transparent;
color: #7c7bad;
box-shadow: none;
@@ -2642,6 +2479,9 @@
.openerp .oe_fileupload .oe_attachments .oe_attachment:nth-child(even) {
background: #f4f5fa;
}
+.openerp .oe_form_field_many2one {
+ display: inline-block;
+}
.openerp .oe_form_field_many2one td:first-child {
position: relative;
}
@@ -2704,9 +2544,13 @@
padding-left: 24px;
margin: 0;
position: relative;
+ line-height: normal;
+ font-size: 100%;
+ font-weight: normal;
}
.openerp ul.oe_form_status li > .arrow, .openerp ul.oe_form_status_clickable li > .arrow {
width: 17px;
+ height: 30px;
display: inline-block;
vertical-align: top;
overflow: hidden;
@@ -2907,6 +2751,9 @@
.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_datetime input.oe_datepicker_master, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_date input.oe_datepicker_master {
width: 100% !important;
}
+.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_reference {
+ display: table;
+}
.openerp .oe_list_group_name {
white-space: nowrap;
}
@@ -3181,20 +3028,127 @@
}
.openerp .oe_debug_view_log {
font-size: 95%;
+ line-height: 1.2em;
}
-.openerp .oe_debug_view_log label {
- display: block;
- width: 49%;
- text-align: right;
- float: left;
+.openerp .navbar {
+ min-height: 32px;
+ margin-bottom: 0px;
+ border: none;
+ z-index: 1;
+ position: static;
+ background-color: #414141;
+ background-color: #454343;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#646060), to(#262626));
+ background-image: -webkit-linear-gradient(top, #646060, #262626);
+ background-image: -moz-linear-gradient(top, #646060, #262626);
+ background-image: -ms-linear-gradient(top, #646060, #262626);
+ background-image: -o-linear-gradient(top, #646060, #262626);
+ background-image: linear-gradient(to bottom, #646060, #262626);
+}
+.openerp .navbar-default .navbar-nav li a:hover, .openerp .navbar-default .navbar-nav li a:focus {
+ background: rgba(0, 0, 0, 0.3);
+}
+.openerp .navbar-default .navbar-nav .open > a, .openerp .navbar-default .navbar-nav a:hover, .openerp .navbar-default .navbar-nav a:focus {
+ background: rgba(0, 0, 0, 0.3) !important;
+}
+.openerp .navbar-default .navbar-nav .dropdown > a .caret {
+ border-top-color: #777777 !important;
+ border-bottom-color: #777777 !important;
+}
+.openerp .navbar-nav li a {
+ padding: 4px 32px 4px 12px;
+}
+.openerp .oe_navbar .dropdown-menu {
+ font-size: 13px;
+ padding: 4px 0;
+ background: #333333 !important;
+ background: rgba(37, 37, 37, 0.9) !important;
+ border-color: #999999;
+ border-color: rgba(0, 0, 0, 0.2);
+ background-color: #414141;
+ text-shadow: none;
+ background-color: #454343;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#646060), to(#262626));
+ background-image: -webkit-linear-gradient(top, #646060, #262626);
+ background-image: -moz-linear-gradient(top, #646060, #262626);
+ background-image: -ms-linear-gradient(top, #646060, #262626);
+ background-image: -o-linear-gradient(top, #646060, #262626);
+ background-image: linear-gradient(to bottom, #646060, #262626);
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ border-radius: 3px;
+}
+.openerp .oe_navbar .dropdown-menu li a, .openerp .oe_navbar .dropdown-menu li a:hover, .openerp .oe_navbar .dropdown-menu li a:focus {
+ color: #eeeeee;
+}
+.openerp .oe_view_manager_new .oe_form_nosheet {
+ margin-top: 8px;
+}
+.openerp .oe_view_manager_new .oe_form_nosheet .oe_form_label {
+ font-weight: normal;
+}
+.openerp .nav li > a {
+ padding: 3px 4px 2px 18px;
+ color: #4c4c4c;
+}
+.openerp .nav nav-pills.nav-stacked > li > ul {
+ padding-left: 16px;
+}
+.openerp .nav-pills > li.active > a, .openerp a.list-group-item.active > a {
+ background-color: #7c7bad;
+ color: white;
+ border-radius: 0;
+}
+.openerp .nav-pills > li.active a:hover, .openerp .nav-pills > li.active a:focus, .openerp a.list-group-item.active a:hover, .openerp a.list-group-item.active a:focus {
+ background-color: #7c7bad;
+}
+.openerp .nav-pills > li.active .badge, .openerp a.list-group-item.active .badge {
+ background-color: white;
+ color: #7c7bad;
+ text-shadow: none;
+}
+.openerp .badge {
+ font-weight: normal;
+ font-size: 11px;
+ background-color: #7c7bad;
+}
+.openerp button, .openerp body {
+ line-height: normal;
+}
+.openerp h1, .openerp h2 {
font-weight: bold;
- color: #000099;
}
-.openerp .oe_debug_view_log span {
+.openerp h3 {
+ font-size: 1.17em;
+ font-weight: bold;
+}
+.openerp p {
display: block;
- width: 49%;
- float: right;
- color: #333333;
+ -webkit-margin-before: 1em;
+ -webkit-margin-after: 1em;
+ -webkit-margin-start: 0px;
+ -webkit-margin-end: 0px;
+}
+.openerp pre {
+ background-color: white;
+ border: none;
+ padding: 10px 0 3px 0;
+}
+.openerp h5 {
+ font-weight: bold;
+ font-size: smaller;
+}
+.openerp .oe_form .oe_subtype label, .openerp .oe_subtype label {
+ font-weight: normal;
+}
+.openerp .oe_msg_subtype_check {
+ margin: 3px 3px 0 !important;
+}
+
+.jqstooltip {
+ height: auto !important;
+ width: auto !important;
+ padding: 0;
}
@-moz-document url-prefix() {
@@ -3204,9 +3158,6 @@
.openerp .oe_form_field_many2one .oe_m2o_cm_button {
line-height: 18px;
}
- .openerp .oe_secondary_submenu {
- line-height: 14px;
- }
.openerp .oe_webclient .oe_star_on, .openerp .oe_webclient .oe_star_off {
top: 0px;
}
@@ -3278,6 +3229,33 @@ div.ui-widget-overlay {
padding-bottom: 10px !important;
}
+body.oe_single_form {
+ background: #eeeeee url(/web/static/src/img/form_sheetbg.png);
+ height: 100%;
+}
+body.oe_single_form .oe_single_form_logo {
+ padding: 10px;
+ text-align: center;
+ margin-bottom: 10px;
+}
+body.oe_single_form .oe_single_form_footer {
+ position: absolute;
+ bottom: -30px;
+ right: 0px;
+ width: 100%;
+ text-align: center;
+}
+body.oe_single_form .oe_single_form_container {
+ padding: 10px;
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ width: 400px;
+ /* Set margins to offset 50% of the w/h */
+ margin-top: -200px;
+ margin-left: -200px;
+}
+
.openerp_ie .placeholder {
color: #afafb6 !important;
font-style: italic !important;
@@ -3402,7 +3380,7 @@ div.ui-widget-overlay {
background: transparent;
}
.openerp_ie ul.oe_form_status li > .arrow span, .openerp_ie ul.oe_form_status_clickable li > .arrow span {
- background-color: #fefefe !important;
+ background-color: #eeeeee !important;
}
.openerp_ie ul.oe_form_status li.oe_active > .label, .openerp_ie ul.oe_form_status_clickable li.oe_active > .label {
border-bottom: 1px solid #729fcf;
@@ -3454,6 +3432,16 @@ div.ui-widget-overlay {
overflow: hidden !important;
}
}
+.ui-icon {
+ width: 18px;
+ height: 18px;
+}
+
+input[type="radio"], input[type="checkbox"] {
+ margin-right: 4px;
+ margin-left: 4px;
+}
+
.blockUI.blockOverlay {
background-color: black;
opacity: 0.6;
diff --git a/addons/web/static/src/css/base.sass b/addons/web/static/src/css/base.sass
index e8eab4544a6..b19db9e4ebd 100644
--- a/addons/web/static/src/css/base.sass
+++ b/addons/web/static/src/css/base.sass
@@ -151,7 +151,6 @@ $sheet-padding: 16px
color: #4c4c4c
font-size: 13px
background: white
- text-shadow: 0 1px 1px rgba(255, 255, 255, 0.5)
&.openerp_webclient_container
height: 100%
// }}}
@@ -190,6 +189,17 @@ $sheet-padding: 16px
vertical-align: middle
td
vertical-align: top
+ .oe_title
+ width: 50%
+ float: left
+ .oe_title:after
+ content: "."
+ display: block
+ height: 0
+ clear: both
+ visibility: hidden
+ .oe_form_group
+ clear: both
.zebra tbody tr:nth-child(odd) td
background-color: #f0f0fa
@include vertical-gradient(#f0f0fa, #eeeef6)
@@ -204,9 +214,11 @@ $sheet-padding: 16px
vertical-align: middle
h4
margin: 4px 0
+ font-weight: bold
+ font-size: inherit
// }}}
// Button style {{{
- a.button:link, a.button:visited, button, input[type='submit'], .ui-dialog-buttonpane .ui-dialog-buttonset .ui-button
+ a.button:link, a.button:visited, button, .oe_button, input[type='submit'], .ui-dialog-buttonpane .ui-dialog-buttonset .ui-button
display: inline-block
border: 1px solid rgba(0,0,0,0.4)
color: #4c4c4c
@@ -221,22 +233,22 @@ $sheet-padding: 16px
-webkit-font-smoothing: antialiased
outline: none
- a.button:hover, button:hover, input[type='submit']:hover, .ui-dialog-buttonpane .ui-dialog-buttonset .ui-button.ui-state-hover
+ a.button:hover, button:hover,.oe_button:hover, input[type='submit']:hover, .ui-dialog-buttonpane .ui-dialog-buttonset .ui-button.ui-state-hover
@include vertical-gradient(#f6f6f6, #e3e3e3)
cursor: pointer
background-position: 0
- a.button:focus, button:focus, input[type='submit']:focus, .ui-dialog-buttonpane .ui-dialog-buttonset .ui-button.ui-state-focus
+ a.button:focus, button:focus, .oe_button:focus, input[type='submit']:focus, .ui-dialog-buttonpane .ui-dialog-buttonset .ui-button.ui-state-focus
border: 1px solid #80bfff
background-position: 0
@include vertical-gradient(#f6f6f6, #e3e3e3)
@include box-shadow((0 0 3px #80bfff, 0 1px 1px rgba(255, 255, 255, .8) inset))
- a.button:active, a.button.active, button:active, button.active, input[type='submit']:active, input[type='submit'].active, .ui-dialog-buttonpane .ui-dialog-buttonset .ui-button.ui-state-active
+ a.button:active, a.button.active, button:active, .oe_button:active, .oe_button.active, input[type='submit']:active, input[type='submit'].active, .ui-dialog-buttonpane .ui-dialog-buttonset .ui-button.ui-state-active
@include vertical-gradient(#e3e3e3, #f6f6f6)
@include box-shadow(none)
- a.button.disabled, button:disabled, input[type='submit']:disabled
+ a.button.disabled, button:disabled, .oe_button:disabled, input[type='submit']:disabled
background: #efefef !important
border: 1px solid #d1d1d1 !important
@include box-shadow(none !important)
@@ -263,6 +275,7 @@ $sheet-padding: 16px
// Modal box
&.ui-dialog
display: none
+ height: auto !important
padding: 6px
//overflow: hidden
background-color: rgba(60,60,60,0.7)
@@ -277,16 +290,18 @@ $sheet-padding: 16px
.ui-dialog-titlebar, .ui-dialog-content, .ui-dialog-buttonpane
padding: 16px
.ui-dialog-titlebar
+ border-top: none
+ border-left: none
+ border-right: none
border-bottom: 1px solid #cacaca
@include radius(2px 2px 0 0)
@include vertical-gradient(#FCFCFC, #DEDEDE)
.ui-dialog-title
margin: 0
padding: 0
- .ui-widget-header
- border: none
.ui-dialog-content
background: white
+ width: auto !important
.ui-dialog-buttonpane
border-top: 1px solid #e0e0e0
background: #f5f7f9
@@ -366,6 +381,7 @@ $sheet-padding: 16px
font-family: "entypoRegular" !important
font-size: 34px
font-weight: 300 !important
+ line-height: 100%
.oe_left
float: left
margin-right: 8px
@@ -412,15 +428,6 @@ $sheet-padding: 16px
@include box-shadow(none)
&:hover
background: #ED6F6A
- .oe_title
- width: 50%
- float: left
- .oe_title:after
- content: "."
- display: block
- height: 0
- clear: both
- visibility: hidden
.oe_button_box
width: 270px
text-align: right
@@ -467,7 +474,16 @@ $sheet-padding: 16px
max-width: 650px
.oe_grey
color: #aaa
-
+ // Added for generic error message and customize bootstrap3 <pre>,<hr>
+ .oe_error_detail
+ hr
+ display: block
+ -webkit-margin-before: 0.5em
+ -webkit-margin-after: 0.5em
+ -webkit-margin-start: auto
+ -webkit-margin-end: auto
+ border-style: inset
+ border-width: 1px
// }}}
// Tags (for many2many tags, among others) {{{
@@ -582,6 +598,9 @@ $sheet-padding: 16px
text-decoration: none
background-color: #eee
border-color: #eee #eee #ddd
+ .ui-tabs .oe_notebook.ui-tabs-nav li.ui-tabs-active
+ border-bottom: none
+ padding-bottom: 1px
.oe_notebook > li.ui-state-active > a, .oe_notebook > li.ui-state-active > a:hover
background-color: #ffffff
border: 1px solid #ddd
@@ -589,6 +608,10 @@ $sheet-padding: 16px
cursor: default
.oe_notebook_page
padding: 0
+ //Customize label weight according bootstrap3
+ > label:not([for])
+ font-weight: normal !important
+ // End of customize
div.ui-tabs
padding: 3px 0px 3px 0px
.ui-tabs-hide
@@ -630,9 +653,6 @@ $sheet-padding: 16px
display: block
color: #4c4c4c
text-decoration: none
- width: 200px
- text-overflow: ellipsis
- overflow: hidden
&:hover
text-decoration: none
.oe_dropdown_arrow:after
@@ -731,108 +751,6 @@ $sheet-padding: 16px
padding: 5px
width: 32px
// }}}
- // Login {{{
- .oe_login
- background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAGCAYAAADgzO9IAAAAKUlEQVQIHWO8e/fufwYsgAUkJigoiCIF5DMyoYggcUiXgNnBiGQKmAkARpcEQeriln4AAAAASUVORK5CYII=)
- text-align: center
- font-size: 14px
- height: 100%
- ul, li
- margin: 0
- list-style-type: none
- padding: 0 0 4px 0
- button
- cursor: pointer
- padding: 6px 16px
- border: 1px solid #222
- color: white
- margin: 0
- @include vertical-gradient(#b92020, #600606)
- @include radius(4px)
- @include box-shadow((0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(155, 155, 155, 0.4) inset))
- input, select
- width: 252px
- font-family: "Lucida Grande", Helvetica, Verdana, Arial
- border: 1px solid #999999
- background: whitesmoke
- @include box-shadow(inset 0 1px 4px rgba(0, 0, 0, 0.4))
- @include radius(3px)
- input
- margin-bottom: 9px
- padding: 5px 6px
- select
- padding: 1px
- .oe_login_dbpane
- position: fixed
- top: 0
- right: 8px
- padding: 5px 10px
- color: #eee
- border: solid 1px #333
- background: #1e1e1e
- background: rgba(30,30,30,0.94)
- @include radius(0 0 8px 8px)
- input
- padding: 2px 4px
- margin: 4px 0
- .oe_login_bottom
- position: absolute
- top: 50%
- left: 0
- right: 0
- bottom: 0
- text-shadow: 0 1px 1px #999999
- @include vertical-gradient(#b41616, #600606)
- .oe_login_pane
- position: absolute
- top: 50%
- left: 50%
- margin: -160px -166px
- border: solid 1px #333333
- background: #1e1e1e
- background: rgba(30,30,30,0.94)
- padding: 22px 32px
- color: #eee
- text-align: left
- @include radius(8px)
- @include box-shadow(0 0 18px rgba(0, 0, 0, 0.9))
- h2
- margin-top: 0
- font-size: 18px
- .oe_login_logo
- position: absolute
- top: -70px
- left: 0
- width: 100%
- margin: 0 auto
- text-align: center
- .oe_login_footer
- position: absolute
- bottom: -40px
- left: 0
- width: 100%
- text-align: center
- a
- color: #eee
- margin: 0 8px
- &:hover
- text-decoration: underline
- span
- font-weight: bold
- font-size: 16px
- .oe_login_error_message
- display: none
- background-color: #b41616
- color: #eee
- padding: 14px 18px
- margin-top: 15px
- text-align: center
- @include radius(4px)
- @include box-shadow(0 1px 4px rgba(0, 0, 0, 0.8))
- .oe_login_invalid
- .oe_login_error_message
- display: inline-block
- // }}}
// DatabaseManager {{{
.oe_database_manager
background: #fff
@@ -857,11 +775,7 @@ $sheet-padding: 16px
// }}}
// Top Menu {{{
- .oe_topbar
- width: 100%
- height: 32px
- background-color: #414141
- @include vertical-gradient(#646060, #262626)
+ .navbar
.oe_topbar_anonymous_login
background-color: #dc5f59
color: #eeeeee
@@ -870,7 +784,7 @@ $sheet-padding: 16px
display: block
padding: 5px 10px 7px
line-height: 20px
- height: 20px
+ height: 30px
text-decoration: none
color: white
background: transparent
@@ -882,7 +796,7 @@ $sheet-padding: 16px
@include box-shadow(0 1px 2px rgba(0,0,0,0.2) inset)
.oe_topbar_item
display: block
- padding: 5px 10px 7px
+ padding: 5px 10px 26px
line-height: 20px
height: 20px
text-decoration: none
@@ -905,7 +819,7 @@ $sheet-padding: 16px
vertical-align: top
.oe_dropdown_arrow:after
border-top: 4px solid white
- .oe_dropdown_menu
+ .dropdown_menu
top: 32px
background: #333
background: rgba(37,37,37,0.9)
@@ -925,17 +839,17 @@ $sheet-padding: 16px
@include box-shadow(none)
.oe_topbar_name
max-width: 150px
- overflow: hidden
display: inline-block
- max-height: 100%
+ height: 15px // Customize to manage caret when topbar_name is over sized.
text-overflow: ellipsis
white-space: nowrap
+ overflow: hidden // Customize to bring ellipsis when topbar_name is over sized.
// 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
+ .navbar-nav
float: left
padding: 0
margin: 0
@@ -949,11 +863,11 @@ $sheet-padding: 16px
color: #eeeeee
> a
display: block
- padding: 5px 10px 7px
+ padding: 5px 10px 26px !important
line-height: 20px
height: 20px
text-decoration: none
- color: #eee
+ color: #eee !important
vertical-align: top
text-shadow: 0 1px 1px rgba(0,0,0,0.2)
@include transition(all 0.2s ease-out)
@@ -962,7 +876,7 @@ $sheet-padding: 16px
text-shadow: black 0px 0px 3px
color: white
@include box-shadow(0 1px 2px rgba(0,0,0,0.4) inset)
- > .oe_active
+ > .active
background: rgba(0,0,0,0.30)
text-shadow: black 0px 0px 3px
@include box-shadow(0 1px 2px rgba(0,0,0,0.4) inset)
@@ -990,7 +904,7 @@ $sheet-padding: 16px
width: 220px
background: #f0eeee
border-right: 1px solid $tag-border
- text-shadow: 0 1px 1px white
+ text-shadow: none
padding-bottom: 16px
a.oe_logo
position: relative
@@ -1033,62 +947,20 @@ $sheet-padding: 16px
margin-left: 8px
color: $link-color
.oe_secondary_submenu
- padding: 2px 0 8px 0
- margin: 0
- li
- position: relative
- margin: 0
- padding: 1px 0 1px 20px !important
- list-style-type: none
- a
- display: block
- color: #4c4c4c
- padding: 2px 4px 2px 0
- .oe_menu_label
- position: absolute
- top: 1px
- right: 1px
- font-size: 10px
- background: $link-color
- color: white
- padding: 2px 4px
- margin: 1px 6px 0 0
- border: 1px solid lightGray
- text-shadow: 0 1px 1px rgba(0,0,0,0.2)
- @include radius(4px)
- @include box-shadow(inset 0 1px 1px rgba(0, 0, 0, 0.2))
+ margin-bottom: 10px !important
+ line-height: 1.1em
+ margin-top: 3px
.oe_menu_text
white-space: nowrap
overflow: hidden
display: inline-block
text-overflow: ellipsis
max-width: 85%
- .oe_menu_counter
- float: right
- text-shadow: 0 1px 1px rgba(0,0,0,0.2)
- margin: 1px
- padding: 1px 4px
- border: none
+ margin-top: 1px
+ .badge
&:hover
cursor: pointer
@include transform(scale(1.1))
- .oe_active
- border-top: 1px solid lightGray
- border-bottom: 1px solid #dedede
- text-shadow: 0 1px 1px rgba(0,0,0,0.2)
- @include box-shadow((inset 0 1px 3px rgba(0, 0, 0, 0.2), inset 0 -1px 3px rgba(40, 40, 40, 0.2)))
- background: $link-color
- a
- color: white
- .oe_menu_label
- background: #eee
- color: $link-color
- text-shadow: 0 1px 1px white
- @include box-shadow(0 1px 1px rgba(0, 0, 0, 0.2))
- .oe_menu_counter
- background: #eee
- color: $link-color
- @include box-shadow(inset 0 1px 2px rgba(0, 0, 0, 0.2))
.oe_menu_toggler:before
width: 0
height: 0
@@ -1110,11 +982,8 @@ $sheet-padding: 16px
border-left: 4px solid transparent
border-right: 4px solid transparent
border-top: 4px solid #4c4c4c
- .oe_secondary_submenu
- margin-left: -20px
.oe_secondary_submenu li
margin-left: 20px
-
// }}}
// About openerp {{{
.oe_about
@@ -1144,13 +1013,13 @@ $sheet-padding: 16px
color: #eee
// }}}
// ActionManager {{{
+ a.oe_form_uri:hover
+ text-decoration: underline
.oe_application
width: 100%
height: 100%
a
color: $link-color
- &:hover
- text-decoration: underline
> div
height: 100%
.oe_breadcrumb_item:not(:last-child)
@@ -1160,6 +1029,7 @@ $sheet-padding: 16px
.oe_breadcrumb_title > *
display: inline-block
overflow: hidden
+ font-weight: bold
// }}}
// ViewManager common {{{
.oe_view_manager
@@ -1198,6 +1068,7 @@ $sheet-padding: 16px
font-size: 18px
margin: 0
float: left
+ line-height: 30px
a
color: $link-color
.oe_dropdown_menu
@@ -1326,8 +1197,13 @@ $sheet-padding: 16px
display: none
// }}}
// FormPopup {{{
- .oe_popup_form > .oe_formview > .oe_form_pager
- display: none !important
+ .oe_popup_form
+ .oe_formview .oe_form_pager
+ display: none !important
+ // Customize label weight for popup wizard appear from another wizard according bootstrap3
+ table label
+ font-weight: normal
+ // End of customize
.oe_popup_list_pager
float: right
// }}}
@@ -1348,6 +1224,15 @@ $sheet-padding: 16px
height: 14px
font-size: 12px
line-height: 18px
+ //Customize searchview input, select
+ input:not([type]), input[type="text"], input[type="number"]
+ width: 156px
+ height: 22px
+ input[type="checkbox"]
+ margin: 3px 3px 3px 4px
+ select
+ margin: 2px 4px 2px 0
+ //End of customize
&.oe_focused
border-color: $tag-border-selected
@include box-shadow(0 1px 2px $tag-border-selected inset)
@@ -1426,7 +1311,6 @@ $sheet-padding: 16px
background: $tag-bg-light
@include radius(0 3px 3px 0)
.oe_facet_category, .oe_facet_value
- height: 18px
padding: 0 4px
.oe_facet_category
color: white
@@ -1512,6 +1396,7 @@ $sheet-padding: 16px
@include box-sizing(border)
display: table-cell
width: 50%
+ padding-left: 2px // Managed padding-left according bootstrap3
ul
margin: 0 8px 8px
padding: 0
@@ -1541,7 +1426,7 @@ $sheet-padding: 16px
margin: 4px 0
line-height: 18px
button
- margin: 0 0 8px 0
+ margin: 0 0 8px -3px // Managed margin-left according bootstrap3
.oe_searchview_custom
padding: 0 8px 8px 8px
div
@@ -1561,6 +1446,10 @@ $sheet-padding: 16px
position: absolute
top: 0
right: 5px
+ //Customize for searchview label
+ label
+ font-weight: normal
+ //End of Customize
.oe_searchview_dashboard
form
display: none
@@ -1586,9 +1475,10 @@ $sheet-padding: 16px
white-space: nowrap
&:first-child .searchview_extended_prop_or
visibility: hidden
+ margin-left: -14px
.searchview_extended_prop_or
opacity: 0.5
- margin-left: -14px
+ margin-left: -14px //Customize 'or' in searchview
.oe_opened
h4:before
content: "â–¾ "
@@ -1735,6 +1625,12 @@ $sheet-padding: 16px
.oe_form_sheetbg
background: url(/web/static/src/img/form_sheetbg.png)
border-bottom: 1px solid #ddd
+ // Customize font weight according bootstrap3
+ .oe_subtotal_footer label
+ font-weight: bold
+ table label
+ font-weight: normal
+ // End of customize
.oe_form_sheet
border: 1px solid #c8c8d3 //$tag-border
@include box-shadow(0 4px 20px rgba(0,0,0,0.15))
@@ -1744,13 +1640,8 @@ $sheet-padding: 16px
padding: 0 16px
// }}}
// FormView.header {{{
- .oe_form header
- position: relative
- border-bottom: 1px solid #cacaca
+ .oe_form > :not(.oe_form_nosheet) header
padding-left: 2px
- @include vertical-gradient(#fcfcfc, #dedede)
- > span
- margin-left: 4px
ul
display: inline-block
float: right
@@ -1758,6 +1649,14 @@ $sheet-padding: 16px
margin: 3px 2px 1px
&:first-child
margin-left: 6px
+
+ .oe_form header
+ border-bottom: 1px solid #cacaca
+ padding-left: 2px
+ @include vertical-gradient(#fcfcfc, #dedede)
+ > span
+ margin-left: 4px
+
// }}}
// FormView.custom tags and classes {{{
.oe_form
@@ -1802,7 +1701,7 @@ $sheet-padding: 16px
font-weight: bold !important
padding: 2px 11px 2px 0px !important
label.oe_form_label_help
- font-weight: normal
+ font-weight: normal !important
.oe_form_box_info
background: #fe9
border-bottom: 1px solid #cb6
@@ -1829,7 +1728,8 @@ $sheet-padding: 16px
label
line-height: 18px
display: block
- min-width: 140px
+ min-width: 150px
+ font-weight: bold !important
td.oe_form_group_cell + .oe_form_group_cell
padding: 2px 0 2px 8px
.oe_form_group
@@ -1841,7 +1741,6 @@ $sheet-padding: 16px
// FormView.label {{{
.oe_form
.oe_form_label_help[for], .oe_form_label[for]
- font-weight: bold
white-space: nowrap
padding-right: 8px
span
@@ -1910,6 +1809,17 @@ $sheet-padding: 16px
input
height: inherit !important
font-size: inherit
+ // Customize for formview
+ .oe_title h1, h1
+ font-weight: bold
+ font-size: 2em
+ h2
+ font-size: 1.5em
+ label
+ font-weight: bold
+ margin-bottom: 0px
+ display: inline
+ // End of customize
.oe_form_field
width: 100%
display: inline-block
@@ -2025,6 +1935,8 @@ $sheet-padding: 16px
margin: 0
padding: 0
.oe_form
+ .oe_form_field_binary
+ display: inline-block
.oe_form_field_image
padding: 0
position: relative
@@ -2048,12 +1960,13 @@ $sheet-padding: 16px
display: inline-block
clear: both
width: 100%
+ margin-bottom: -15px
.oe_add
float: left
position: relative
width: 100%
left: +2px
- top: +7px
+ margin: 9px 0 /* Customize margin according bootstrap3 */
overflow: hidden
button
display: inline
@@ -2063,9 +1976,6 @@ $sheet-padding: 16px
vertical-align: middle
button.oe_attach
width: 24px
- overflow: hidden
- width: 24px
- overflow: hidden
background: transparent
color: #7C7BAD
box-shadow: none
@@ -2074,7 +1984,7 @@ $sheet-padding: 16px
.oe_e
position: relative
top: -1px
- left: -9px
+ left: -9px
input.oe_form_binary_file
display: inline-block
margin-left: -85px
@@ -2107,6 +2017,7 @@ $sheet-padding: 16px
// }}}
// FormView.many2one {{{
.oe_form_field_many2one
+ display: inline-block
td:first-child
position: relative
span.oe_m2o_drop_down_button
@@ -2155,8 +2066,14 @@ $sheet-padding: 16px
padding-left: 24px
margin: 0
position: relative
+ // Customize for statusbar label
+ line-height: normal
+ font-size: 100%
+ font-weight: normal
+ // End of customize
> .arrow
width: 17px
+ height: 30px
display: inline-block
vertical-align: top
overflow: hidden
@@ -2293,6 +2210,8 @@ $sheet-padding: 16px
&.oe_form_field_datetime,&.oe_form_field_date
input.oe_datepicker_master
width: 100% !important
+ &.oe_form_field_reference
+ display: table
.oe_list_group_name
white-space: nowrap
// }}}
@@ -2506,27 +2425,117 @@ $sheet-padding: 16px
float: left
.oe_debug_view_log
font-size: 95%
- .oe_debug_view_log label
- display: block
- width: 49%
- text-align: right
- float: left
+ line-height: 1.2em
+ // }}}
+ // Bootstrap HACKS {{{
+ .navbar
+ min-height: 32px
+ margin-bottom: 0px
+ border: none
+ z-index: 1
+ position: static
+ background-color: #414141
+ @include vertical-gradient(#646060, #262626)
+ .navbar-default
+ .navbar-nav
+ li
+ a:hover,a:focus
+ background: rgba(0, 0, 0, 0.3)
+ .open > a,a:hover,a:focus
+ background: rgba(0, 0, 0, 0.3) !important
+ .dropdown > a .caret
+ border-top-color: #777 !important
+ border-bottom-color: #777 !important
+ .navbar-nav
+ li
+ a
+ padding: 4px 32px 4px 12px
+ .oe_navbar .dropdown-menu
+ font-size: 13px
+ padding: 4px 0
+ background: #333333 !important
+ background: rgba(37, 37, 37, 0.9) !important
+ border-color: #999999
+ border-color: rgba(0, 0, 0, 0.2)
+ background-color: #414141
+ text-shadow: none
+ @include vertical-gradient(#646060, #262626)
+ @include radius(3px)
+ li
+ a,a:hover,a:focus
+ color: #eeeeee
+
+ // Minimized unused white space and label weight for wizard popup
+ .oe_view_manager_new .oe_form_nosheet
+ margin-top: 8px
+ .oe_form_label
+ font-weight: normal
+
+ // Customize bootstrap3 for leftbar
+ .nav
+ li > a
+ padding: 3px 4px 2px 18px
+ color: #4c4c4c
+ nav-pills.nav-stacked > li > ul
+ padding-left: 16px
+ .nav-pills > li.active,a.list-group-item.active
+ > a
+ background-color: #7c7bad
+ color: white
+ border-radius: 0
+ a:hover,a:focus
+ background-color: #7c7bad
+ .badge
+ background-color: white
+ color: #7c7bad
+ text-shadow: none
+ .badge
+ font-weight: normal
+ font-size: 11px
+ background-color: #7c7bad
+
+ // Customize for global tags
+ button, body
+ line-height: normal
+ h1,h2
+ font-weight: bold
+ h3
+ font-size: 1.17em
font-weight: bold
- color: #009
- .oe_debug_view_log span
+ p
display: block
- width: 49%
- float: right
- color: #333
+ -webkit-margin-before: 1em
+ -webkit-margin-after: 1em
+ -webkit-margin-start: 0px
+ -webkit-margin-end: 0px
+ pre
+ background-color: #FFFFFF
+ border: none
+ padding: 10px 0 3px 0
+
+
+
+ // Customize for chatter
+ h5
+ font-weight: bold
+ font-size: smaller
+ .oe_form .oe_subtype label, .oe_subtype label
+ font-weight: normal
+ .oe_msg_subtype_check
+ margin: 3px 3px 0 !important
// }}}
+// Customize for kanban tooltip
+.jqstooltip
+ height: auto !important
+ width: auto !important
+ padding: 0
+
@-moz-document url-prefix()
.openerp
.oe_searchview .oe_searchview_search
top: -1px
.oe_form_field_many2one .oe_m2o_cm_button
line-height: 18px
- .oe_secondary_submenu
- line-height: 14px
.oe_webclient
.oe_star_on, .oe_star_off
top: 0px
@@ -2582,6 +2591,38 @@ div.ui-widget-overlay
td
padding-bottom: 10px !important
+// Server side single forms {{{
+@mixin single-form($width: 400px)
+ padding: 10px
+ position: absolute
+ left: 50%
+ top: 50%
+ width: $width
+ /* Set margins to offset 50% of the w/h */
+ margin-top: -($width / 2)
+ margin-left: -($width / 2)
+
+body.oe_single_form
+ background: #eee url(/web/static/src/img/form_sheetbg.png)
+ height: 100%
+
+ .oe_single_form_logo
+ padding: 10px
+ text-align: center
+ margin-bottom: 10px
+
+ .oe_single_form_footer
+ position: absolute
+ bottom: -30px
+ right: 0px
+ width: 100%
+ text-align: center
+
+ .oe_single_form_container
+ @include single-form(400px)
+
+// }}}
+
// Internet Explorer 9+ specifics {{{
.openerp_ie
.placeholder
@@ -2684,7 +2725,7 @@ div.ui-widget-overlay
background: transparent
> .arrow
span
- background-color: #fefefe !important
+ background-color: #eeeeee !important
li.oe_active
> .label
border-bottom: 1px solid #729fcf
@@ -2731,6 +2772,17 @@ div.ui-widget-overlay
overflow: hidden !important
// }}}
+
+// Hack for ui icon {{{
+.ui-icon
+ width: 18px
+ height: 18px
+// End hack}}}
+
+input[type="radio"], input[type="checkbox"]
+ margin-right: 4px
+ margin-left: 4px
+
.blockUI.blockOverlay
background-color: black
opacity: 0.6000000238418579
diff --git a/addons/web/static/src/css/data_export.css b/addons/web/static/src/css/data_export.css
index 3fa24265b45..b9b885cd8e9 100644
--- a/addons/web/static/src/css/data_export.css
+++ b/addons/web/static/src/css/data_export.css
@@ -1,6 +1,11 @@
.openerp .oe_export {
width: 100%;
}
+/* Customize according bootstrap3 */
+.openerp .oe_export label {
+ font-weight: normal;
+}
+/* End of customize */
.openerp .oe_export_row tr{
background-color: #FFFFFF;
font-size: 0.8em;
diff --git a/addons/web/static/src/img/throbber-large.gif b/addons/web/static/src/img/throbber-large.gif
new file mode 100644
index 00000000000..453b272979d
--- /dev/null
+++ b/addons/web/static/src/img/throbber-large.gif
Binary files differ
diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js
index f019972fd14..9b87d416206 100644
--- a/addons/web/static/src/js/chrome.js
+++ b/addons/web/static/src/js/chrome.js
@@ -256,7 +256,7 @@ instance.web.CrashManager = instance.web.Class.extend({
new (handler)(this, error).display();
return;
}
- if (error.data.name === "openerp.addons.web.session SessionExpiredException") {
+ if (error.data.name === "openerp.http.SessionExpiredException") {
this.show_warning({type: "Session Expired", data: { message: _t("Your OpenERP session expired. Please refresh the current web page.") }});
return;
}
@@ -442,10 +442,10 @@ instance.web.DatabaseManager = instance.web.Widget.extend({
self.$el.html(QWeb.render("DatabaseManager", { widget : self }));
$('.oe_user_menu_placeholder').append(QWeb.render("DatabaseManager.user_menu",{ widget : self }));
$('.oe_secondary_menus_container').append(QWeb.render("DatabaseManager.menu",{ widget : self }));
- $('ul.oe_secondary_submenu > li:first').addClass('oe_active');
+ $('ul.oe_secondary_submenu > li:first').addClass('active');
$('ul.oe_secondary_submenu > li').bind('click', function (event) {
var menuitem = $(this);
- menuitem.addClass('oe_active').siblings().removeClass('oe_active');
+ menuitem.addClass('active').siblings().removeClass('active');
var form_id =menuitem.find('a').attr('href');
$(form_id).show().siblings().hide();
event.preventDefault();
@@ -476,19 +476,6 @@ instance.web.DatabaseManager = instance.web.Widget.extend({
this._super();
},
/**
- * Converts a .serializeArray() result into a dict. Does not bother folding
- * multiple identical keys into an array, last key wins.
- *
- * @param {Array} array
- */
- to_object: function (array) {
- var result = {};
- _(array).each(function (record) {
- result[record.name] = record.value;
- });
- return result;
- },
- /**
* Blocks UI and replaces $.unblockUI by a noop to prevent third parties
* from unblocking the UI
*/
@@ -525,25 +512,11 @@ instance.web.DatabaseManager = instance.web.Widget.extend({
var self = this;
var fields = $(form).serializeArray();
self.rpc("/web/database/create", {'fields': fields}).done(function(result) {
- var form_obj = self.to_object(fields);
- var client_action = {
- type: 'ir.actions.client',
- tag: 'login',
- params: {
- 'db': form_obj['db_name'],
- 'login': 'admin',
- 'password': form_obj['create_admin_pwd'],
- 'login_successful': function() {
- var url = '/web?db=' + form_obj['db_name'];
- if (self.session.debug) {
- url += '&debug';
- }
- instance.web.redirect(url);
- },
- },
- _push_me: false,
- };
- self.do_action(client_action);
+ if (result) {
+ instance.web.redirect('/web');
+ } else {
+ alert("Failed to create database");
+ }
});
},
do_duplicate: function(form) {
@@ -641,156 +614,20 @@ instance.web.DatabaseManager = instance.web.Widget.extend({
},
do_exit: function () {
this.$el.remove();
- instance.webclient.show_login();
+ instance.web.redirect('/web');
}
});
instance.web.client_actions.add("database_manager", "instance.web.DatabaseManager");
-instance.web.Login = instance.web.Widget.extend({
- template: "Login",
- remember_credentials: true,
- events: {
- 'change input[name=db],select[name=db]': function(ev) {
- this.set('database_selector', $(ev.currentTarget).val());
- },
- },
-
- init: function(parent, action) {
- this._super(parent);
- this.has_local_storage = typeof(localStorage) != 'undefined';
- this.db_list = null;
- this.selected_db = null;
- this.selected_login = null;
- this.params = action.params || {};
- if (_.isEmpty(this.params)) {
- this.params = $.bbq.getState(true);
- }
- if (action && action.params && action.params.db) {
- this.params.db = action.params.db;
- } else if ($.deparam.querystring().db) {
- this.params.db = $.deparam.querystring().db;
- }
- if (this.params.db) {
- this.selected_db = this.params.db;
- }
-
- if (this.params.login_successful) {
- this.on('login_successful', this, this.params.login_successful);
- }
- // some cleanup to remove any trace of that last login feature
- if (typeof(localStorage) != 'undefined') {
- var toRemove = [];
- _.each(_.range(localStorage.length), function(i) {
- var key = localStorage.key(i);
- if (key.match(/^.*?\|last_password$/)) {
- toRemove.push(key);
- }
- });
- _.each(toRemove, function(k) {
- localStorage.removeItem(k);
- });
- }
- },
- start: function() {
- var self = this;
- self.$el.find("form").submit(self.on_submit);
- self.$el.find('.oe_login_manage_db').click(function() {
- self.do_action("database_manager");
- });
- self.on('change:database_selector', this, function() {
- this.database_selected(this.get('database_selector'));
- });
- var d = $.when();
- if ($.param.fragment().token) {
- self.params.token = $.param.fragment().token;
- }
- // used by dbmanager.do_create via internal client action
- if (self.params.db && self.params.login && self.params.password) {
- d = self.do_login(self.params.db, self.params.login, self.params.password);
- } else {
- d = self.rpc("/web/database/get_list", {})
- .done(self.on_db_loaded)
- .fail(self.on_db_failed)
- .always(function() {
- if (self.selected_db && self.has_local_storage && self.remember_credentials) {
- self.$("[name=login]").val(localStorage.getItem(self.selected_db + '|last_login') || '');
- }
- });
- }
- return d;
- },
- database_selected: function(db) {
- var params = $.deparam.querystring();
- params.db = db;
- this.$('.oe_login_dbpane').empty().text(_t('Loading...'));
- this.$('[name=login], [name=password]').prop('readonly', true);
- instance.web.redirect('/web?' + $.param(params));
- },
- on_db_loaded: function (result) {
- var self = this;
- this.db_list = result;
- if (!this.selected_db) {
- this.selected_db = result[0];
- }
- this.$("[name=db]").replaceWith(QWeb.render('Login.dblist', { db_list: this.db_list, selected_db: this.selected_db}));
- if(this.db_list.length === 0) {
- this.do_action("database_manager");
- } else if(this.db_list.length === 1) {
- this.$('div.oe_login_dbpane').hide();
- } else {
- this.$('div.oe_login_dbpane').show();
- }
- },
- on_db_failed: function (error, event) {
- if (error.data.name === 'openerp.exceptions.AccessDenied') {
- event.preventDefault();
- }
- },
- on_submit: function(ev) {
- if(ev) {
- ev.preventDefault();
- }
- var db = this.$("form [name=db]").val();
- if (!db) {
- this.do_warn(_t("Login"), _t("No database selected !"));
- return false;
- }
- var login = this.$("form input[name=login]").val();
- var password = this.$("form input[name=password]").val();
+instance.web.login = function() {
+ instance.web.redirect('/web/login');
+};
+instance.web.client_actions.add("login", "instance.web.login");
- this.do_login(db, login, password);
- },
- /**
- * Performs actual login operation, and UI-related stuff
- *
- * @param {String} db database to log in
- * @param {String} login user login
- * @param {String} password user password
- */
- do_login: function (db, login, password) {
- var self = this;
- self.hide_error();
- self.$(".oe_login_pane").fadeOut("slow");
- return this.session.session_authenticate(db, login, password).then(function() {
- if (self.has_local_storage && self.remember_credentials) {
- localStorage.setItem(db + '|last_login', login);
- }
- self.trigger('login_successful');
- }, function () {
- self.$(".oe_login_pane").fadeIn("fast", function() {
- self.show_error(_t("Invalid username or password"));
- });
- });
- },
- show_error: function(message) {
- this.$el.addClass("oe_login_invalid");
- this.$(".oe_login_error_message").text(message);
- },
- hide_error: function() {
- this.$el.removeClass('oe_login_invalid');
- },
-});
-instance.web.client_actions.add("login", "instance.web.Login");
+instance.web.logout = function() {
+ instance.web.redirect('/web/session/logout');
+};
+instance.web.client_actions.add("logout", "instance.web.logout");
/**
@@ -868,7 +705,7 @@ instance.web.client_actions.add("history_back", "instance.web.HistoryBack");
*/
instance.web.Home = function(parent, action) {
var url = '/' + (window.location.search || '');
- instance.web.redirect(url, action.params && action.params.wait);
+ instance.web.redirect(url, action && action.params && action.params.wait);
};
instance.web.client_actions.add("home", "instance.web.Home");
@@ -969,7 +806,7 @@ instance.web.Menu = instance.web.Widget.extend({
this.needaction_data = data;
_.each(this.needaction_data, function (item, menu_id) {
var $item = self.$secondary_menus.find('a[data-menu="' + menu_id + '"]');
- $item.find('.oe_menu_counter').remove();
+ $item.find('.badge').remove();
if (item.needaction_counter && item.needaction_counter > 0) {
$item.append(QWeb.render("Menu.needaction_counter", { widget : item }));
}
@@ -982,8 +819,8 @@ instance.web.Menu = instance.web.Widget.extend({
reflow: function() {
var self = this;
this.$el.height('auto').show();
- var $more_container = this.$('.oe_menu_more_container').hide();
- var $more = this.$('.oe_menu_more');
+ var $more_container = this.$('#menu_more_container').hide();
+ var $more = this.$('#menu_more');
$more.children('li').insertBefore($more_container);
var $toplevel_items = this.$el.children('li').not($more_container).hide();
$toplevel_items.each(function() {
@@ -1026,8 +863,8 @@ instance.web.Menu = instance.web.Widget.extend({
}
// Activate current main menu
- this.$el.find('.oe_active').removeClass('oe_active');
- $main_menu.addClass('oe_active');
+ this.$el.find('.active').removeClass('active');
+ $main_menu.addClass('active');
// Show current sub menu
this.$secondary_menus.find('.oe_secondary_menu').hide();
@@ -1037,13 +874,13 @@ instance.web.Menu = instance.web.Widget.extend({
this.$secondary_menus.parent('.oe_leftbar').toggle(!!$sub_menu.children().length);
// Activate current menu item and show parents
- this.$secondary_menus.find('.oe_active').removeClass('oe_active');
+ this.$secondary_menus.find('.active').removeClass('active');
if ($main_menu !== $clicked_menu) {
$clicked_menu.parents().show();
if ($clicked_menu.is('.oe_menu_toggler')) {
$clicked_menu.toggleClass('oe_menu_opened').siblings('.oe_secondary_submenu:first').toggle();
} else {
- $clicked_menu.parent().addClass('oe_active');
+ $clicked_menu.parent().addClass('active');
}
}
},
@@ -1129,7 +966,7 @@ instance.web.Menu = instance.web.Widget.extend({
},
on_menu_click: function(ev) {
ev.preventDefault();
- var needaction = $(ev.target).is('div.oe_menu_counter');
+ var needaction = $(ev.target).is('div#menu_counter');
this.menu_click($(ev.currentTarget).data('menu'), needaction);
},
});
@@ -1143,7 +980,7 @@ instance.web.UserMenu = instance.web.Widget.extend({
start: function() {
var self = this;
this._super.apply(this, arguments);
- this.$el.on('click', '.oe_dropdown_menu li a[data-menu]', function(ev) {
+ this.$el.on('click', '.dropdown-menu li a[data-menu]', function(ev) {
ev.preventDefault();
var f = self['on_menu_' + $(this).data('menu')];
if (f) {
@@ -1223,9 +1060,30 @@ instance.web.UserMenu = instance.web.Widget.extend({
},
});
+instance.web.FullscreenWidget = instance.web.Widget.extend({
+ /**
+ * Widgets extending the FullscreenWidget will be displayed fullscreen,
+ * and will have a fixed 1:1 zoom level on mobile devices.
+ */
+ start: function(){
+ if(!$('#oe-fullscreenwidget-viewport').length){
+ $('head').append('<meta id="oe-fullscreenwidget-viewport" name="viewport" content="initial-scale=1.0; maximum-scale=1.0; user-scalable=0;">');
+ }
+ instance.webclient.set_content_full_screen(true);
+ return this._super();
+ },
+ destroy: function(){
+ instance.webclient.set_content_full_screen(false);
+ $('#oe-fullscreenwidget-viewport').remove();
+ return this._super();
+ },
+
+});
+
instance.web.Client = instance.web.Widget.extend({
init: function(parent, origin) {
instance.client = instance.webclient = this;
+ this.client_options = {};
this._super(parent);
this.origin = origin;
},
@@ -1284,7 +1142,7 @@ instance.web.Client = instance.web.Widget.extend({
self.action_manager.appendTo(self.$('.oe_application'));
},
toggle_bars: function(value) {
- this.$('tr:has(td.oe_topbar),.oe_leftbar').toggle(value);
+ this.$('tr:has(td.navbar),.oe_leftbar').toggle(value);
},
has_uncommitted_changes: function() {
return false;
@@ -1296,8 +1154,11 @@ instance.web.WebClient = instance.web.Client.extend({
events: {
'click .oe_logo_edit_admin': 'logo_edit'
},
- init: function(parent) {
+ init: function(parent, client_options) {
this._super(parent);
+ if (client_options) {
+ _.extend(this.client_options, client_options);
+ }
this._current_state = null;
this.menu_dm = new instance.web.DropMisordered();
this.action_mutex = new $.Mutex();
@@ -1311,11 +1172,13 @@ instance.web.WebClient = instance.web.Client.extend({
if (jQuery.deparam !== undefined && jQuery.deparam(jQuery.param.querystring()).kitten !== undefined) {
self.to_kitten();
}
- if (!self.session.session_is_valid()) {
- self.show_login();
- } else {
+ if (self.session.session_is_valid()) {
self.show_application();
}
+ if (self.client_options.action) {
+ self.action_manager.do_action(self.client_options.action);
+ delete(self.client_options.action);
+ }
});
},
to_kitten: function() {
@@ -1365,25 +1228,6 @@ instance.web.WebClient = instance.web.Client.extend({
});
};
},
- show_login: function() {
- this.toggle_bars(false);
-
- var state = $.bbq.getState(true);
- var action = {
- type: 'ir.actions.client',
- tag: 'login',
- _push_me: false,
- };
-
- this.action_manager.do_action(action);
- this.action_manager.inner_widget.on('login_successful', this, function() {
- if ('redirect' in state) {
- openerp.web.redirect(state.redirect);
- } else {
- this.show_application(); // will load the state we just pushed
- }
- });
- },
show_application: function() {
var self = this;
self.toggle_bars(true);
@@ -1398,6 +1242,10 @@ instance.web.WebClient = instance.web.Client.extend({
self.bind_hashchange();
self.set_title();
self.check_timezone();
+ if (self.client_options.action_post_login) {
+ self.action_manager.do_action(self.client_options.action_post_login);
+ delete(self.client_options.action_post_login);
+ }
},
update_logo: function() {
var img = this.session.url('/web/binary/company_logo');
@@ -1478,11 +1326,7 @@ instance.web.WebClient = instance.web.Client.extend({
on_logout: function() {
var self = this;
if (!this.has_uncommitted_changes()) {
- this.session.session_logout().done(function () {
- $(window).unbind('hashchange', self.on_hashchange);
- self.do_push_state({});
- window.location.reload();
- });
+ self.action_manager.do_action('logout');
}
},
bind_hashchange: function() {
@@ -1492,10 +1336,15 @@ instance.web.WebClient = instance.web.Client.extend({
var state = $.bbq.getState(true);
if (_.isEmpty(state) || state.action == "login") {
self.menu.has_been_loaded.done(function() {
- var first_menu_id = self.menu.$el.find("a:first").data("menu");
- if(first_menu_id) {
- self.menu.menu_click(first_menu_id);
- }
+ new instance.web.Model("res.users").call("read", [self.session.uid, ["action_id"]]).done(function(data) {
+ var first_menu_id = self.menu.$el.find("a:first").data("menu");
+ if(first_menu_id)
+ self.menu.menu_click(first_menu_id);
+
+ if(data.action_id) {
+ self.action_manager.do_action(data.action_id[0]);
+ }
+ });
});
} else {
$(window).trigger('hashchange');
diff --git a/addons/web/static/src/js/core.js b/addons/web/static/src/js/core.js
index 37a217a8664..b92ca1d3ba5 100644
--- a/addons/web/static/src/js/core.js
+++ b/addons/web/static/src/js/core.js
@@ -252,7 +252,7 @@ instance.web.Session.include( /** @lends instance.web.Session# */{
return !!this.uid;
},
/**
- * The session is validated either by login or by restoration of a previous session
+ * The session is validated by restoration of a previous session
*/
session_authenticate: function() {
var self = this;
@@ -561,6 +561,10 @@ instance.web.TranslationDataBase.include({
});
/** Custom jQuery plugins */
+$.browser = $.browser || {};
+if(navigator.appVersion.indexOf("MSIE") !== -1) {
+ $.browser.msie = 1;
+}
$.fn.getAttributes = function() {
var o = {};
if (this.length) {
diff --git a/addons/web/static/src/js/data.js b/addons/web/static/src/js/data.js
index 04f657d91f7..cc595c30688 100644
--- a/addons/web/static/src/js/data.js
+++ b/addons/web/static/src/js/data.js
@@ -129,11 +129,14 @@ instance.web.Query = instance.web.Class.extend({
if (_.isEmpty(grouping) && !ctx['group_by_no_leaf']) {
return null;
}
+ var raw_fields = _.map(grouping.concat(this._fields || []), function (field) {
+ return field.split(':')[0];
+ });
var self = this;
return this._model.call('read_group', {
groupby: grouping,
- fields: _.uniq(grouping.concat(this._fields || [])),
+ fields: _.uniq(raw_fields),
domain: this._model.domain(this._filter),
context: ctx,
offset: this._offset,
@@ -231,14 +234,16 @@ instance.web.QueryGroup = instance.web.Class.extend({
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 raw_field = grouping_field && grouping_field.split(':')[0];
+ var group_size = fixed_group[raw_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],
+ value: fixed_group[raw_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']),
@@ -447,6 +452,9 @@ instance.web.DataSet = instance.web.Class.extend(instance.web.PropertiesMixin,
* @returns {$.Deferred}
*/
read_ids: function (ids, fields, options) {
+ if (_.isEmpty(ids))
+ return $.Deferred().resolve([]);
+
options = options || {};
// TODO: reorder results to match ids list
return this._model.call('read',
@@ -743,7 +751,7 @@ instance.web.DataSetSearch = instance.web.DataSet.extend({
});
},
get_domain: function (other_domain) {
- this._model.domain(other_domain);
+ return this._model.domain(other_domain);
},
alter_ids: function (ids) {
this._super(ids);
@@ -780,6 +788,7 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
this._super.apply(this, arguments);
this.reset_ids([]);
this.last_default_get = {};
+ this.running_reads = [];
},
default_get: function(fields, options) {
var self = this;
@@ -846,6 +855,9 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
this.to_write = [];
this.cache = [];
this.delete_all = false;
+ _.each(_.clone(this.running_reads), function(el) {
+ el.reject();
+ });
},
read_ids: function (ids, fields, options) {
var self = this;
@@ -861,10 +873,10 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
to_get.push(id);
}
});
- var completion = $.Deferred();
var return_records = function() {
var records = _.map(ids, function(id) {
- return _.extend({}, _.detect(self.cache, function(c) {return c.id === id;}).values, {"id": id});
+ var c = _.find(self.cache, function(c) {return c.id === id;});
+ return _.isUndefined(c) ? c : _.extend({}, c.values, {"id": id});
});
if (self.debug_mode) {
if (_.include(records, undefined)) {
@@ -896,10 +908,20 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
}, 0);
});
}
- completion.resolve(records);
+ return $.when(records);
};
if(to_get.length > 0) {
- var rpc_promise = this._super(to_get, fields, options).done(function(records) {
+ var def = $.Deferred();
+ self.running_reads.push(def);
+ def.always(function() {
+ self.running_reads = _.without(self.running_reads, def);
+ });
+ this._super(to_get, fields, options).then(function() {
+ def.resolve.apply(def, arguments);
+ }, function() {
+ def.reject.apply(def, arguments);
+ });
+ return def.then(function(records) {
_.each(records, function(record, index) {
var id = to_get[index];
var cached = _.detect(self.cache, function(x) {return x.id === id;});
@@ -910,13 +932,11 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
cached.values = _.defaults(_.clone(cached.values), record);
}
});
- return_records();
+ return return_records();
});
- $.when(rpc_promise).fail(function() {completion.reject();});
} else {
- return_records();
+ return return_records();
}
- return completion.promise();
},
/**
* Invalidates caching of a record in the dataset to ensure the next read
@@ -928,6 +948,15 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
* @param {Object} id record to remove from the BDS's cache
*/
evict_record: function (id) {
+ // Don't evict records which haven't yet been saved: there is no more
+ // recent data on the server (and there potentially isn't any data),
+ // and this breaks the assumptions of other methods (that the data
+ // for new and altered records is both in the cache and in the to_write
+ // or to_create collection)
+ if (_(this.to_create.concat(this.to_write)).find(function (record) {
+ return record.id === id; })) {
+ return;
+ }
for(var i=0, len=this.cache.length; i<len; ++i) {
var record = this.cache[i];
// if record we call the button upon is in the cache
@@ -1090,6 +1119,157 @@ instance.web.DropMisordered = instance.web.Class.extend({
}
});
+instance.web.SimpleIndexedDB = instance.web.Class.extend({
+ /**
+ * A simple wrapper around IndexedDB that provides an asynchronous
+ * localStorage-like Key-Value api that persists between browser
+ * restarts.
+ *
+ * It will not work if the browser doesn't support a recent version
+ * of IndexedDB, and it may fail if the user refuses db access.
+ *
+ * All instances of SimpleIndexedDB will by default refer to the same
+ * IndexedDB database, if you want to pick another one, use the 'name'
+ * option on instanciation.
+ */
+ init: function(opts){
+ var self = this;
+ var opts = opts || {};
+
+ this.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
+ this.db = undefined;
+ this._ready = new $.Deferred();
+
+ if(this.indexedDB && this.indexedDB.open){
+ var request = this.indexedDB.open( opts.name || "SimpleIndexedDB" ,1);
+
+ request.onerror = function(event){
+ self.db = null;
+ self._ready.reject(event.target.error);
+ };
+
+ request.onsuccess = function(event){
+ self.db = request.result;
+ self._ready.resolve();
+ };
+
+ request.onupgradeneeded = function(event){
+ self.db = event.target.result;
+
+ var objectStore = self.db.createObjectStore("keyvals", { keyPath:"key" });
+ self._ready.resolve();
+ };
+ }else{
+ this.db = null;
+ this._ready.reject({type:'UnknownError', message:'IndexedDB is not supported by your Browser'});
+ }
+ },
+
+ /**
+ * returns true if the browser supports the necessary IndexedDB API
+ * (but doesn't tell if the db can be created, check ready() for that)
+ */
+ isSupportedByBrowser: function(){
+ return this.indexedDB && this.indexedDB.open;
+ },
+
+ /**
+ * returns a deferred that resolves when the db has been successfully
+ * initialized. done/failed callbacks are optional.
+ */
+ ready: function(done,failed){
+ this._ready.then(done,failed);
+ return this._ready.promise();
+ },
+
+ /**
+ * fetches the value associated to 'key' in the db. if the key doesn't
+ * exists, it returns undefined. The returned value is provided as a
+ * deferred, or as the first parameter of the optional 'done' callback.
+ */
+ getItem: function(key,done,failed){
+ var self = this;
+ var def = new $.Deferred();
+ def.then(done,failed);
+
+ this._ready.then(function(){
+ var request = self.db.transaction(["keyvals"],"readwrite")
+ .objectStore("keyvals")
+ .get(key);
+
+ request.onsuccess = function(){
+ def.resolve(request.result ? request.result.value : undefined);
+ };
+
+ request.onerror = function(event){
+ def.reject(event.target.error);
+ };
+ },function(){
+ def.reject({type:'UnknownError', message:'Could not initialize the IndexedDB database'});
+ });
+
+ return def.promise();
+ },
+
+ /**
+ * Associates a value to 'key' in the db, overwriting previous value if
+ * necessary. Contrary to localStorage, the value is not limited to strings and
+ * can be any javascript object, even with cyclic references !
+ *
+ * Be sure to check for failure as the user may refuse to have data localy stored.
+ */
+ setItem: function(key,value,done,failed){
+ var self = this;
+ var def = new $.Deferred();
+ def.then(done,failed);
+
+ this._ready.then(function(){
+ var request = self.db.transaction(["keyvals"],"readwrite")
+ .objectStore("keyvals")
+ .put( {key:key, value:value} );
+
+ request.onsuccess = function(){
+ def.resolve();
+ };
+
+ request.onerror = function(event){
+ def.reject(event.target.error);
+ };
+ },function(){
+ def.reject({type:'UnknownError', message:'Could not initialize the IndexedDB database'});
+ });
+
+ return def.promise();
+ },
+
+ /**
+ * Removes the value associated with 'key' from the db.
+ */
+ removeItem: function(key,done,failed){
+ var self = this;
+ var def = new $.Deferred();
+ def.then(done,failed);
+
+ this._ready.then(function(){
+ var request = self.db.transaction(["keyvals"],"readwrite")
+ .objectStore("keyvals")
+ .delete(key);
+
+ request.onsuccess = function(){
+ def.resolve();
+ };
+
+ request.onerror = function(event){
+ def.reject(event.target.error);
+ };
+ },function(){
+ def.reject({type:'UnknownError', message:'Could not initialize the IndexedDB database'});
+ });
+
+ return def.promise();
+ },
+});
+
})();
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
diff --git a/addons/web/static/src/js/formats.js b/addons/web/static/src/js/formats.js
index f8394068c7c..3c32dbe0426 100644
--- a/addons/web/static/src/js/formats.js
+++ b/addons/web/static/src/js/formats.js
@@ -141,12 +141,13 @@ instance.web.format_value = function (value, descriptor, value_if_empty) {
//noinspection FallthroughInSwitchStatementJS
switch (value) {
case '':
- if (descriptor.type === 'char') {
+ if (descriptor.type === 'char' || descriptor.type === 'text') {
return '';
}
console.warn('Field', descriptor, 'had an empty string as value, treating as false...');
return value_if_empty === undefined ? '' : value_if_empty;
case false:
+ case undefined:
case Infinity:
case -Infinity:
return value_if_empty === undefined ? '' : value_if_empty;
@@ -195,7 +196,7 @@ instance.web.format_value = function (value, descriptor, value_if_empty) {
+ ' ' + normalize_format(l10n.time_format));
case 'date':
if (typeof(value) == "string")
- value = instance.web.auto_str_to_date(value);
+ value = instance.web.str_to_date(value.substring(0,10));
return value.toString(normalize_format(l10n.date_format));
case 'time':
if (typeof(value) == "string")
@@ -204,7 +205,7 @@ instance.web.format_value = function (value, descriptor, value_if_empty) {
case 'selection': case 'statusbar':
// Each choice is [value, label]
if(_.isArray(value)) {
- value = value[0];
+ return value[1];
}
var result = _(descriptor.selection).detect(function (choice) {
return choice[0] === value;
diff --git a/addons/web/static/src/js/openerpframework.js b/addons/web/static/src/js/openerpframework.js
index e986a2f4112..5dcc2197944 100644
--- a/addons/web/static/src/js/openerpframework.js
+++ b/addons/web/static/src/js/openerpframework.js
@@ -809,7 +809,8 @@ var genericJsonRpc = function(fct_name, params, fct) {
params: params,
id: Math.floor(Math.random() * 1000 * 1000 * 1000)
};
- return fct(data).pipe(function(result) {
+ var xhr = fct(data);
+ var result = xhr.pipe(function(result) {
if (result.error !== undefined) {
console.error("Server application error", result.error);
return $.Deferred().reject("server", result.error);
@@ -821,15 +822,38 @@ var genericJsonRpc = function(fct_name, params, fct) {
var def = $.Deferred();
return def.reject.apply(def, ["communication"].concat(_.toArray(arguments)));
});
+ // FIXME: jsonp?
+ result.abort = function () { xhr.abort && xhr.abort(); };
+ return result;
};
+/**
+ * Replacer function for JSON.stringify, serializes Date objects to UTC
+ * datetime in the OpenERP Server format.
+ *
+ * However, if a serialized value has a toJSON method that method is called
+ * *before* the replacer is invoked. Date#toJSON exists, and thus the value
+ * passed to the replacer is a string, the original Date has to be fetched
+ * on the parent object (which is provided as the replacer's context).
+ *
+ * @param {String} k
+ * @param {Object} v
+ * @returns {Object}
+ */
+function date_to_utc(k, v) {
+ var value = this[k];
+ if (!(value instanceof Date)) { return v; }
+
+ return openerp.datetime_to_str(value);
+}
+
openerp.jsonRpc = function(url, fct_name, params, settings) {
return genericJsonRpc(fct_name, params, function(data) {
return $.ajax(url, _.extend({}, settings, {
url: url,
dataType: 'json',
type: 'POST',
- data: JSON.stringify(data),
+ data: JSON.stringify(data, date_to_utc),
contentType: 'application/json'
}));
});
@@ -838,7 +862,7 @@ openerp.jsonRpc = function(url, fct_name, params, settings) {
openerp.jsonpRpc = function(url, fct_name, params, settings) {
settings = settings || {};
return genericJsonRpc(fct_name, params, function(data) {
- var payload_str = JSON.stringify(data);
+ var payload_str = JSON.stringify(data, date_to_utc);
var payload_url = $.param({r:payload_str});
var force2step = settings.force2step || false;
delete settings.force2step;
@@ -950,6 +974,7 @@ openerp.Session = openerp.Class.extend(openerp.PropertiesMixin, {
this.session_id = options.session_id || null;
this.override_session = options.override_session || !!options.session_id || false;
this.avoid_recursion = false;
+ this.use_cors = options.use_cors || false;
this.setup(origin);
},
setup: function(origin) {
@@ -993,7 +1018,7 @@ openerp.Session = openerp.Class.extend(openerp.PropertiesMixin, {
},
check_session_id: function() {
var self = this;
- if (this.avoid_recursion)
+ if (this.avoid_recursion || self.use_cors)
return $.when();
if (this.session_id)
return $.when(); // we already have the session id
@@ -1050,6 +1075,15 @@ openerp.Session = openerp.Class.extend(openerp.PropertiesMixin, {
"X-Openerp-Session-Id": self.override_session ? self.session_id || '' : ''
});
}
+ } else if (self.use_cors) {
+ fct = openerp.jsonRpc;
+ url = self.url(url, null);
+ options.session_id = self.session_id || '';
+ if (self.override_session) {
+ options.headers = _.extend({}, options.headers, {
+ "X-Openerp-Session-Id": self.override_session ? self.session_id || '' : ''
+ });
+ }
} else {
fct = openerp.jsonpRpc;
url = self.url(url, null);
@@ -1155,7 +1189,8 @@ openerp.Model = openerp.Class.extend({
kwargs = args;
args = [];
}
- return this.session().rpc('/web/dataset/call_kw', {
+ var call_kw = '/web/dataset/call_kw/' + this.name + '/' + method;
+ return this.session().rpc(call_kw, {
model: this.name,
method: method,
args: args,
@@ -1236,15 +1271,14 @@ openerp.TranslationDataBase = openerp.Class.extend(/** @lends instance.Translati
openerp._t = new openerp.TranslationDataBase().build_translation_function();
openerp.get_cookie = function(c_name) {
- if (document.cookie.length > 0) {
- var c_start = document.cookie.indexOf(c_name + "=");
- if (c_start != -1) {
- c_start = c_start + c_name.length + 1;
- var c_end = document.cookie.indexOf(";", c_start);
- if (c_end == -1) {
- c_end = document.cookie.length;
- }
- return unescape(document.cookie.substring(c_start, c_end));
+ var cookies = document.cookie ? document.cookie.split('; ') : [];
+ for (var i = 0, l = cookies.length; i < l; i++) {
+ var parts = cookies[i].split('=');
+ var name = parts.shift();
+ var cookie = parts.join('=');
+
+ if (c_name && c_name === name) {
+ return cookie;
}
}
return "";
@@ -1292,7 +1326,10 @@ openerp.str_to_datetime = function(str) {
if ( !res ) {
throw new Error("'" + str + "' is not a valid datetime");
}
- var tmp = new Date(0);
+ var tmp = new Date(2000,0,1);
+ tmp.setUTCMonth(1970);
+ tmp.setUTCMonth(0);
+ tmp.setUTCDate(1);
tmp.setUTCFullYear(parseFloat(res[1]));
tmp.setUTCMonth(parseFloat(res[2]) - 1);
tmp.setUTCDate(parseFloat(res[3]));
@@ -1324,7 +1361,7 @@ openerp.str_to_date = function(str) {
if ( !res ) {
throw new Error("'" + str + "' is not a valid date");
}
- var tmp = new Date(0);
+ var tmp = new Date(2000,0,1);
tmp.setFullYear(parseFloat(res[1]));
tmp.setMonth(parseFloat(res[2]) - 1);
tmp.setDate(parseFloat(res[3]));
diff --git a/addons/web/static/src/js/pyeval.js b/addons/web/static/src/js/pyeval.js
index 0b0e9725af9..d5499a028b7 100644
--- a/addons/web/static/src/js/pyeval.js
+++ b/addons/web/static/src/js/pyeval.js
@@ -382,13 +382,28 @@
this[key] = asJS(args[key]);
}
},
+ replace: function () {
+ var args = py.PY_parseArgs(arguments, [
+ ['year', py.None], ['month', py.None], ['day', py.None],
+ ['hour', py.None], ['minute', py.None], ['second', py.None],
+ ['microsecond', py.None] // FIXME: tzinfo, can't use None as valid input
+ ]);
+ var params = {};
+ for(var key in args) {
+ if (!args.hasOwnProperty(key)) { continue; }
+
+ var arg = args[key];
+ params[key] = (arg === py.None ? this[key] : asJS(arg));
+ }
+ return py.PY_call(datetime.datetime, params);
+ },
strftime: function () {
var self = this;
var args = py.PY_parseArgs(arguments, 'format');
return py.str.fromJSON(args.format.toJSON()
.replace(/%([A-Za-z])/g, function (m, c) {
switch (c) {
- case 'Y': return self.year;
+ case 'Y': return _.str.sprintf('%04d', self.year);
case 'm': return _.str.sprintf('%02d', self.month);
case 'd': return _.str.sprintf('%02d', self.day);
case 'H': return _.str.sprintf('%02d', self.hour);
@@ -399,6 +414,17 @@
}));
},
now: py.classmethod.fromJSON(function () {
+ var d = new Date;
+ return py.PY_call(datetime.datetime, [
+ d.getFullYear(), d.getMonth() + 1, d.getDate(),
+ d.getHours(), d.getMinutes(), d.getSeconds(),
+ d.getMilliseconds() * 1000]);
+ }),
+ today: py.classmethod.fromJSON(function () {
+ var dt_class = py.PY_getAttr(datetime, 'datetime');
+ return py.PY_call(py.PY_getAttr(dt_class, 'now'));
+ }),
+ utcnow: py.classmethod.fromJSON(function () {
var d = new Date();
return py.PY_call(datetime.datetime,
[d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate(),
@@ -415,7 +441,17 @@
py.PY_getAttr(args.time, 'minute'),
py.PY_getAttr(args.time, 'second')
]);
- })
+ }),
+ toJSON: function () {
+ return new Date(
+ this.year,
+ this.month - 1,
+ this.day,
+ this.hour,
+ this.minute,
+ this.second,
+ this.microsecond / 1000);
+ },
});
datetime.date = py.type('date', null, {
__init__: function () {
@@ -470,7 +506,12 @@
},
fromJSON: function (year, month, day) {
return py.PY_call(datetime.date, [year, month, day]);
- }
+ },
+ today: py.classmethod.fromJSON(function () {
+ var d = new Date;
+ return py.PY_call(datetime.date, [
+ d.getFullYear(), d.getMonth() + 1, d.getDate()]);
+ }),
});
/**
Returns the current local date, which means the date on the client (which can be different
@@ -501,7 +542,7 @@
time.strftime = py.PY_def.fromJSON(function () {
var args = py.PY_parseArgs(arguments, 'format');
var dt_class = py.PY_getAttr(datetime, 'datetime');
- var d = py.PY_call(py.PY_getAttr(dt_class, 'now'));
+ var d = py.PY_call(py.PY_getAttr(dt_class, 'utcnow'));
return py.PY_call(py.PY_getAttr(d, 'strftime'), [args.format]);
});
@@ -845,16 +886,18 @@
};
instance.web.pyeval.eval_domains_and_contexts = function (source) {
return new $.Deferred(function (d) {setTimeout(function () {
+ var result;
try {
var contexts = ([instance.session.user_context] || []).concat(source.contexts);
// see Session.eval_context in Python
- d.resolve({
+ result = {
context: instance.web.pyeval.eval('contexts', contexts),
domain: instance.web.pyeval.eval('domains', source.domains),
group_by: instance.web.pyeval.eval('groupbys', source.group_by_seq || [])
- });
+ };
+
} catch (e) {
- d.resolve({ error: {
+ result = { error: {
code: 400,
message: instance.web._t("Evaluation Error"),
data: {
@@ -863,8 +906,9 @@
instance.web._t("Local evaluation failure\n%s\n\n%s"),
e.message, JSON.stringify(source))
}
- }});
+ }};
}
+ d.resolve(result);
}, 0); });
};
})();
diff --git a/addons/web/static/src/js/search.js b/addons/web/static/src/js/search.js
index fcfcfb08f8e..b667d135bdd 100644
--- a/addons/web/static/src/js/search.js
+++ b/addons/web/static/src/js/search.js
@@ -71,7 +71,10 @@ my.SearchQuery = B.Collection.extend({
},
add: function (values, options) {
options = options || {};
- if (!(values instanceof Array)) {
+
+ if (!values) {
+ values = [];
+ } else if (!(values instanceof Array)) {
values = [values];
}
@@ -82,11 +85,15 @@ my.SearchQuery = B.Collection.extend({
&& facet.get('field') === model.get('field');
});
if (previous) {
- previous.values.add(model.get('values'));
+ previous.values.add(model.get('values'), _.omit(options, 'at', 'merge'));
return;
}
B.Collection.prototype.add.call(this, model, options);
}, this);
+ // warning: in backbone 1.0+ add is supposed to return the added models,
+ // but here toggle may delegate to add and return its value directly.
+ // return value of neither seems actually used but should be tested
+ // before change, probably
return this;
},
toggle: function (value, options) {
@@ -407,7 +414,7 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
context: this.dataset.get_context(),
});
- $.when(load_view).then(function (r) {
+ this.alive($.when(load_view)).then(function (r) {
return self.search_view_loaded(r);
}).fail(function () {
self.ready.reject.apply(null, arguments);
@@ -466,19 +473,20 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
* Sets up search view's view-wide auto-completion widget
*/
setup_global_completion: function () {
- var self = this;
-
var autocomplete = this.$el.autocomplete({
source: this.proxy('complete_global_search'),
select: this.proxy('select_completion'),
- search: function () { self.$el.autocomplete('close'); },
focus: function (e) { e.preventDefault(); },
html: true,
autoFocus: true,
minLength: 1,
- delay: 0,
+ delay: 250,
}).data('autocomplete');
+ this.$el.on('input', function () {
+ this.$el.autocomplete('close');
+ }.bind(this));
+
// MonkeyPatch autocomplete instance
_.extend(autocomplete, {
_renderItem: function (ul, item) {
@@ -528,7 +536,6 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
resp(_(arguments).chain()
.compact()
.map(function (completion) {
- console.log(completion);
if (completion.length && completion[0].facet !== undefined) {
completion[0].first = true;
}
@@ -1864,9 +1871,14 @@ instance.web.search.Advanced = instance.web.search.Input.extend({
new instance.web.Model(this.view.model).call('fields_get', {
context: this.view.dataset.context
}).done(function(data) {
- self.fields = _.extend({
+ self.fields = {
id: { string: 'ID', type: 'id' }
- }, data);
+ };
+ _.each(data, function(field_def, field_name) {
+ if (field_def.selectable !== false && field_name != 'id') {
+ self.fields[field_name] = field_def;
+ }
+ });
})).done(function () {
self.append_proposition();
});
diff --git a/addons/web/static/src/js/testing.js b/addons/web/static/src/js/testing.js
index a7768f0ca24..1f1c718ad5f 100644
--- a/addons/web/static/src/js/testing.js
+++ b/addons/web/static/src/js/testing.js
@@ -15,6 +15,37 @@ openerp.testing = {};
list_editable: ['list', 'form', 'data'],
};
+ var initialized = [];
+ /**
+ * openerp.init is a broken-ass piece of shit, makes it impossible to
+ * progressively add modules, using it, retarded openerp_inited flag
+ * means the first tests being run get their dependencies loaded
+ * (basically just base and web), and for *every other module* the tests
+ * run without the dependencies being correctly loaded
+ */
+ function init(modules) {
+ if (!initialized.length) {
+ openerp.init(modules);
+ initialized = openerp._modules.slice();
+ return;
+ }
+ var to_initialize = _.difference(modules, initialized);
+ for (var i = 0; i < to_initialize.length; i++) {
+ var modname = to_initialize[i];
+ var fct = openerp[modname];
+ if (typeof fct === 'function') {
+ var module = openerp[modname] = {};
+ for(var k in fct) {
+ if (!fct.hasOwnProperty(k)) { continue; }
+ module[k] = fct[k];
+ }
+ fct(openerp, module)
+ }
+ initialized.push(modname);
+ openerp._modules.push(modname);
+ }
+ }
+
testing.dependencies = window['oe_all_dependencies'] || [];
testing.current_module = null;
testing.templates = { };
@@ -58,8 +89,7 @@ openerp.testing = {};
}
var fn, params;
var needle = rparams.model + ':' + rparams.method;
- if (url.url === '/web/dataset/call_kw'
- && needle in this.responses) {
+ if (url.url.substr(0, 20) === '/web/dataset/call_kw' && needle in this.responses) {
fn = this.responses[needle];
params = [
rparams.args || [],
@@ -166,9 +196,6 @@ openerp.testing = {};
});
};
- var openerp_inited = false;
-
- var db = window['oe_db_info'];
testing.section = function (name, options, body) {
if (_.isFunction(options)) {
body = options;
@@ -207,47 +234,10 @@ openerp.testing = {};
.push(env._oe.setup, env._oe.teardown)
.push(options.setup, options.teardown);
var opts = _.defaults({}, options, env._oe);
- // FIXME: if this test is ignored, will still query
- if (opts.rpc === 'rpc' && !db) {
- QUnit.config.autostart = false;
- db = {
- source: null,
- supadmin: null,
- password: null
- };
- var $msg = $('<form style="margin: 0 1em 1em;">')
- .append('<h3>A test needs to clone a database</h3>')
- .append('<h4>Please provide the source clone information</h4>')
- .append(' Source DB: ').append('<input name="source">').append('<br>')
- .append(' DB Password: ').append('<input name="supadmin">').append('<br>')
- .append('Admin Password: ').append('<input name="password">').append('<br>')
- .append('<input type="submit" value="OK"/>')
- .submit(function (e) {
- e.preventDefault();
- e.stopPropagation();
- db.source = $msg.find('input[name=source]').val();
- db.supadmin = $msg.find('input[name=supadmin]').val();
- db.password = $msg.find('input[name=password]').val();
- QUnit.start();
- $.unblockUI();
- });
- $.blockUI({
- message: $msg,
- css: {
- fontFamily: 'monospace',
- textAlign: 'left',
- whiteSpace: 'pre-wrap',
- cursor: 'default'
- }
- });
- }
QUnit.test(name, function () {
var instance = openerp;
- if (!openerp_inited) {
- openerp.init(module_deps);
- openerp_inited = true;
- }
+ init(module_deps);
instance.session = new instance.web.Session();
instance.session.uid = 42;
if (_.isNumber(opts.asserts)) {
@@ -277,44 +267,6 @@ openerp.testing = {};
instance.session.responses[spec] = handler;
};
break;
- case 'rpc':
- async = true;
- (function () {
- // Bunch of random base36 characters
- var dbname = 'test_' + Math.random().toString(36).slice(2);
- // Add db setup/teardown at the start of the stack
- case_stack = case_stack.unshift(function (instance) {
- // FIXME hack: don't want the session to go through shitty loading process of everything
- instance.session.session_init = testing.noop;
- instance.session.load_modules = testing.noop;
- instance.session.session_bind();
- return instance.session.rpc('/web/database/duplicate', {
- fields: [
- {name: 'super_admin_pwd', value: db.supadmin},
- {name: 'db_original_name', value: db.source},
- {name: 'db_name', value: dbname}
- ]
- }).then(function (result) {
- if (result.error) {
- return $.Deferred().reject(result.error).promise();
- }
- return instance.session.session_authenticate(
- dbname, 'admin', db.password, true);
- });
- }, function (instance) {
- return instance.session.rpc('/web/database/drop', {
- fields: [
- {name: 'drop_pwd', value: db.supadmin},
- {name: 'drop_db', value: dbname}
- ]
- }).then(function (result) {
- if (result.error) {
- return $.Deferred().reject(result.error).promise();
- }
- return result;
- });
- });
- })();
}
// Always execute tests asynchronously
diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js
index 2cb318dcb17..f1fc377fa94 100644
--- a/addons/web/static/src/js/view_form.js
+++ b/addons/web/static/src/js/view_form.js
@@ -399,9 +399,11 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
this.dataset.index = this.dataset.ids.length - 1;
break;
}
- this.reload();
+ var def = this.reload();
this.trigger('pager_action_executed');
+ return def;
}
+ return $.when();
},
init_pager: function() {
var self = this;
@@ -416,8 +418,15 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
this.$el.find('.oe_form_pager').replaceWith(this.$pager);
}
this.$pager.on('click','a[data-pager-action]',function() {
- var action = $(this).data('pager-action');
- self.execute_pager_action(action);
+ var $el = $(this);
+ if ($el.attr("disabled"))
+ return;
+ var action = $el.data('pager-action');
+ var def = $.when(self.execute_pager_action(action));
+ $el.attr("disabled");
+ def.always(function() {
+ $el.removeAttr("disabled");
+ });
});
this.do_update_pager();
},
@@ -722,12 +731,13 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
var self = this;
return this.save().done(function(result) {
self.trigger("save", result);
- self.to_view_mode();
- }).then(function(result) {
- var parent = self.ViewManager.ActionManager.getParent();
- if(parent){
- parent.menu.do_reload_needaction();
- }
+ self.reload().then(function() {
+ self.to_view_mode();
+ var parent = self.ViewManager.ActionManager.getParent();
+ if(parent){
+ parent.menu.do_reload_needaction();
+ }
+ });
});
},
on_button_cancel: function(event) {
@@ -891,17 +901,12 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
* @param {Object} r result of the write function.
*/
record_saved: function(r) {
- var self = this;
+ this.trigger('record_saved', r);
if (!r) {
// should not happen in the server, but may happen for internal purpose
- this.trigger('record_saved', r);
return $.Deferred().reject();
- } else {
- return $.when(this.reload()).then(function () {
- self.trigger('record_saved', r);
- return r;
- });
}
+ return r;
},
/**
* Updates the form' dataset to contain the new record:
@@ -1025,8 +1030,9 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
open_defaults_dialog: function () {
var self = this;
var display = function (field, value) {
+ if (!value) { return value; }
if (field instanceof instance.web.form.FieldSelection) {
- return _(field.values).find(function (option) {
+ return _(field.get('values')).find(function (option) {
return option[0] === value;
})[1];
} else if (field instanceof instance.web.form.FieldMany2One) {
@@ -2619,6 +2625,11 @@ instance.web.form.FieldText = instance.web.form.AbstractField.extend(instance.we
e.stopPropagation();
}
},
+ 'keypress': function (e) {
+ if (e.which === $.ui.keyCode.ENTER) {
+ e.stopPropagation();
+ }
+ },
'change textarea': 'store_dom_value',
},
initialize_content: function() {
@@ -3071,7 +3082,7 @@ instance.web.form.CompletionFieldMixin = {
values.push({
label: _t("Search More..."),
action: function() {
- dataset.name_search(search_val, self.build_domain(), 'ilike', false).done(function(data) {
+ dataset.name_search(search_val, self.build_domain(), 'ilike', 160).done(function(data) {
self._search_create_popup("search", data);
});
},
@@ -3081,7 +3092,7 @@ instance.web.form.CompletionFieldMixin = {
// quick create
var raw_result = _(data.result).map(function(x) {return x[1];});
if (search_val.length > 0 && !_.include(raw_result, search_val) &&
- ! (self.options && self.options.no_quick_create)) {
+ ! (self.options && (self.options.no_create || self.options.no_quick_create))) {
values.push({
label: _.str.sprintf(_t('Create "<strong>%s</strong>"'),
$('<span />').text(search_val).html()),
@@ -3092,13 +3103,15 @@ instance.web.form.CompletionFieldMixin = {
});
}
// create...
- values.push({
- label: _t("Create and Edit..."),
- action: function() {
- self._search_create_popup("form", undefined, self._create_context(search_val));
- },
- classname: 'oe_m2o_dropdown_option'
- });
+ if (!(self.options && self.options.no_create)){
+ values.push({
+ label: _t("Create and Edit..."),
+ action: function() {
+ self._search_create_popup("form", undefined, self._create_context(search_val));
+ },
+ classname: 'oe_m2o_dropdown_option'
+ });
+ }
return values;
});
@@ -3418,7 +3431,7 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
// disabled to solve a bug, but may cause others
//close: anyoneLoosesFocus,
minLength: 0,
- delay: 0
+ delay: 250
});
this.$input.autocomplete("widget").openerpClass();
// used to correct a bug when selecting an element by pushing 'enter' in an editable list
@@ -3445,6 +3458,10 @@ 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());
this.alive(dataset.name_get([self.get("value")])).done(function(data) {
+ if (!data[0]) {
+ self.do_warn(_t("Render"), _t("No value found for the field "+self.field.string+" for value "+self.get("value")));
+ return;
+ }
self.display_value["" + self.get("value")] = data[0][1];
self.render_value(true);
}).fail( function (data, event) {
@@ -3583,6 +3600,67 @@ instance.web.form.Many2OneButton = instance.web.form.AbstractField.extend({
},
});
+/**
+ * Abstract-ish ListView.List subclass adding an "Add an item" row to replace
+ * the big ugly button in the header.
+ *
+ * Requires the implementation of a ``is_readonly`` method (usually a proxy to
+ * the corresponding field's readonly or effective_readonly property) to
+ * decide whether the special row should or should not be inserted.
+ *
+ * Optionally an ``_add_row_class`` attribute can be set for the class(es) to
+ * set on the insertion row.
+ */
+instance.web.form.AddAnItemList = instance.web.ListView.List.extend({
+ pad_table_to: function (count) {
+ if (!this.view.is_action_enabled('create') || this.is_readonly()) {
+ this._super(count);
+ return;
+ }
+
+ this._super(count > 0 ? count - 1 : 0);
+
+ var self = this;
+ var columns = _(this.columns).filter(function (column) {
+ return column.invisible !== '1';
+ }).length;
+ if (this.options.selectable) { columns++; }
+ if (this.options.deletable) { columns++; }
+
+ var $cell = $('<td>', {
+ colspan: columns,
+ 'class': this._add_row_class || ''
+ }).append(
+ $('<a>', {href: '#'}).text(_t("Add an item"))
+ .mousedown(function () {
+ // FIXME: needs to be an official API somehow
+ if (self.view.editor.is_editing()) {
+ self.view.__ignore_blur = true;
+ }
+ })
+ .click(function (e) {
+ e.preventDefault();
+ e.stopPropagation();
+ // FIXME: there should also be an API for that one
+ if (self.view.editor.form.__blur_timeout) {
+ clearTimeout(self.view.editor.form.__blur_timeout);
+ self.view.editor.form.__blur_timeout = false;
+ }
+ self.view.ensure_saved().done(function () {
+ self.view.do_add_record();
+ });
+ }));
+
+ var $padding = this.$current.find('tr:not([data-id]):first');
+ var $newrow = $('<tr>').append($cell);
+ if ($padding.length) {
+ $padding.before($newrow);
+ } else {
+ this.$current.append($newrow)
+ }
+ }
+});
+
/*
# Values: (0, 0, { fields }) create
# (1, ID, { fields }) update
@@ -3979,7 +4057,6 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({
GroupsType: instance.web.form.One2ManyGroups,
ListType: instance.web.form.One2ManyList
}));
- this.on('edit:before', this, this.proxy('_before_edit'));
this.on('edit:after', this, this.proxy('_after_edit'));
this.on('save:before cancel:before', this, this.proxy('_before_unedit'));
@@ -3992,7 +4069,7 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({
var ret = this._super();
this.$el
.off('mousedown.handleButtons')
- .on('mousedown.handleButtons', 'table button', this.proxy('_button_down'));
+ .on('mousedown.handleButtons', 'table button, div a.oe_m2o_cm_button', this.proxy('_button_down'));
return ret;
},
changed_records: function () {
@@ -4100,11 +4177,10 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({
});
},
- _before_edit: function () {
+ _after_edit: function () {
this.__ignore_blur = false;
this.editor.form.on('blurred', this, this._on_form_blur);
- },
- _after_edit: function () {
+
// The form's blur thing may be jiggered during the edition setup,
// potentially leading to the o2m instasaving the row. Cancel any
// blurring triggered the edition startup here
@@ -4137,7 +4213,7 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({
}
this.cancel_edition();
},
- keyup_ENTER: function () {
+ keypress_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)
@@ -4152,6 +4228,12 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({
} finally {
window.confirm = confirm;
}
+ },
+ reload_record: function (record) {
+ // Evict record.id from cache to ensure it will be reloaded correctly
+ this.dataset.evict_record(record.get('id'));
+
+ return this._super(record);
}
});
instance.web.form.One2ManyGroups = instance.web.ListView.Groups.extend({
@@ -4161,62 +4243,11 @@ instance.web.form.One2ManyGroups = instance.web.ListView.Groups.extend({
}
}
});
-instance.web.form.One2ManyList = instance.web.ListView.List.extend({
- pad_table_to: function (count) {
- if (!this.view.is_action_enabled('create')) {
- this._super(count);
- } else {
- this._super(count > 0 ? count - 1 : 0);
- }
-
- // magical invocation of wtf does that do
- if (this.view.o2m.get('effective_readonly')) {
- return;
- }
-
- var self = this;
- var columns = _(this.columns).filter(function (column) {
- return column.invisible !== '1';
- }).length;
- if (this.options.selectable) { columns++; }
- if (this.options.deletable) { columns++; }
-
- if (!this.view.is_action_enabled('create')) {
- return;
- }
-
- var $cell = $('<td>', {
- colspan: columns,
- 'class': 'oe_form_field_one2many_list_row_add'
- }).append(
- $('<a>', {href: '#'}).text(_t("Add an item"))
- .mousedown(function () {
- // FIXME: needs to be an official API somehow
- if (self.view.editor.is_editing()) {
- self.view.__ignore_blur = true;
- }
- })
- .click(function (e) {
- e.preventDefault();
- e.stopPropagation();
- // FIXME: there should also be an API for that one
- if (self.view.editor.form.__blur_timeout) {
- clearTimeout(self.view.editor.form.__blur_timeout);
- self.view.editor.form.__blur_timeout = false;
- }
- self.view.ensure_saved().done(function () {
- self.view.do_add_record();
- });
- }));
-
- var $padding = this.$current.find('tr:not([data-id]):first');
- var $newrow = $('<tr>').append($cell);
- if ($padding.length) {
- $padding.before($newrow);
- } else {
- this.$current.append($newrow);
- }
- }
+instance.web.form.One2ManyList = instance.web.form.AddAnItemList.extend({
+ _add_row_class: 'oe_form_field_one2many_list_row_add',
+ is_readonly: function () {
+ return this.view.o2m.get('effective_readonly');
+ },
});
instance.web.form.One2ManyFormView = instance.web.FormView.extend({
@@ -4246,6 +4277,7 @@ var lazy_build_o2m_kanban_view = function() {
instance.web.form.FieldMany2ManyTags = instance.web.form.AbstractField.extend(instance.web.form.CompletionFieldMixin, instance.web.form.ReinitializeFieldMixin, {
template: "FieldMany2ManyTags",
+ tag_template: "FieldMany2ManyTag",
init: function() {
this._super.apply(this, arguments);
instance.web.form.CompletionFieldMixin.init.call(this);
@@ -4253,13 +4285,9 @@ instance.web.form.FieldMany2ManyTags = instance.web.form.AbstractField.extend(in
this._display_orderer = new instance.web.DropMisordered();
this._drop_shown = false;
},
- initialize_content: function() {
- if (this.get("effective_readonly"))
- return;
+ initialize_texttext: function(){
var self = this;
- var ignore_blur = false;
- self.$text = this.$("textarea");
- self.$text.textext({
+ return {
plugins : 'tags arrow autocomplete',
autocomplete: {
render: function(suggestion) {
@@ -4310,7 +4338,15 @@ instance.web.form.FieldMany2ManyTags = instance.web.form.AbstractField.extend(in
},
},
},
- }).bind('getSuggestions', function(e, data) {
+ }
+ },
+ initialize_content: function() {
+ if (this.get("effective_readonly"))
+ return;
+ var self = this;
+ var ignore_blur = false;
+ self.$text = this.$("textarea");
+ self.$text.textext(self.initialize_texttext()).bind('getSuggestions', function(e, data) {
var _this = this;
var str = !!data ? data.query || '' : '';
self.get_search_result(str).done(function(result) {
@@ -4358,6 +4394,24 @@ instance.web.form.FieldMany2ManyTags = instance.web.form.AbstractField.extend(in
get_search_blacklist: function() {
return this.get("value");
},
+ map_tag: function(data){
+ return _.map(data, function(el) {return {name: el[1], id:el[0]};})
+ },
+ get_render_data: function(ids){
+ var self = this;
+ var dataset = new instance.web.DataSetStatic(this, this.field.relation, self.build_context());
+ return dataset.name_get(ids);
+ },
+ render_tag: function(data) {
+ var self = this;
+ if (! self.get("effective_readonly")) {
+ self.tags.containerElement().children().remove();
+ self.$('textarea').css("padding-left", "3px");
+ self.tags.addTags(self.map_tag(data));
+ } else {
+ self.$el.html(QWeb.render(self.tag_template, {elements: data}));
+ }
+ },
render_value: function() {
var self = this;
var dataset = new instance.web.DataSetStatic(this, this.field.relation, self.build_context());
@@ -4370,17 +4424,12 @@ instance.web.form.FieldMany2ManyTags = instance.web.form.AbstractField.extend(in
indexed[el[0]] = el;
});
data = _.map(values, function(el) { return indexed[el]; });
- if (! self.get("effective_readonly")) {
- self.tags.containerElement().children().remove();
- self.$('textarea').css("padding-left", "3px");
- self.tags.addTags(_.map(data, function(el) {return {name: el[1], id:el[0]};}));
- } else {
- self.$el.html(QWeb.render("FieldMany2ManyTag", {elements: data}));
- }
- };
+ self.render_tag(data);
+ }
if (! values || values.length > 0) {
- this._display_orderer.add(dataset.name_get(values)).done(handle_names);
- } else {
+ this._display_orderer.add(self.get_render_data(values)).done(handle_names);
+ }
+ else{
handle_names([]);
}
},
@@ -4428,7 +4477,7 @@ instance.web.form.FieldMany2Many = instance.web.form.AbstractField.extend(instan
this.$el.addClass('oe_form_field oe_form_field_many2many');
this.list_view = new instance.web.form.Many2ManyListView(this, this.dataset, false, {
- 'addable': this.get("effective_readonly") ? null : _t("Add"),
+ 'addable': null,
'deletable': this.get("effective_readonly") ? false : true,
'selectable': this.multi_selection,
'sortable': false,
@@ -4495,6 +4544,11 @@ instance.web.form.Many2ManyDataSet = instance.web.DataSetStatic.extend({
* @extends instance.web.ListView
*/
instance.web.form.Many2ManyListView = instance.web.ListView.extend(/** @lends instance.web.form.Many2ManyListView# */{
+ init: function (parent, dataset, view_id, options) {
+ this._super(parent, dataset, view_id, _.extend(options || {}, {
+ ListType: instance.web.form.Many2ManyList,
+ }));
+ },
do_add_record: function () {
var pop = new instance.web.form.SelectCreatePopup(this);
pop.select_element(
@@ -4544,6 +4598,12 @@ instance.web.form.Many2ManyListView = instance.web.ListView.extend(/** @lends in
},
is_action_enabled: function () { return true; },
});
+instance.web.form.Many2ManyList = instance.web.form.AddAnItemList.extend({
+ _add_row_class: 'oe_form_field_many2many_list_row_add',
+ is_readonly: function () {
+ return this.view.m2m_field.get('effective_readonly');
+ }
+});
instance.web.form.FieldMany2ManyKanban = instance.web.form.AbstractField.extend(instance.web.form.CompletionFieldMixin, {
disable_utility_classes: true,
@@ -5072,7 +5132,7 @@ instance.web.form.FieldReference = instance.web.form.AbstractField.extend(instan
.on('blurred', null, function () {self.trigger('blurred');});
this.m2o = new instance.web.form.FieldMany2One(fm, { attrs: {
- name: 'm2o',
+ name: 'Referenced Document',
modifiers: JSON.stringify({readonly: this.get('effective_readonly')}),
}});
this.m2o.on("change:value", this, this.data_changed);
@@ -5370,25 +5430,26 @@ instance.web.form.FieldMany2ManyBinaryMultiFiles = instance.web.form.AbstractFie
read_name_values : function () {
var self = this;
// don't reset know values
- var _value = _.filter(this.get('value'), function (id) { return typeof self.data[id] == 'undefined'; } );
+ var ids = this.get('value');
+ var _value = _.filter(ids, function (id) { return typeof self.data[id] == 'undefined'; } );
// send request for get_name
if (_value.length) {
- return this.ds_file.call('read', [_value, ['id', 'name', 'datas_fname']]).done(function (datas) {
+ return this.ds_file.call('read', [_value, ['id', 'name', 'datas_fname']]).then(function (datas) {
_.each(datas, function (data) {
data.no_unlink = true;
data.url = self.session.url('/web/binary/saveas', {model: 'ir.attachment', field: 'datas', filename_field: 'datas_fname', id: data.id});
self.data[data.id] = data;
});
+ return ids;
});
} else {
- return $.when();
+ return $.when(ids);
}
},
render_value: function () {
var self = this;
- this.read_name_values().then(function () {
-
- var render = $(instance.web.qweb.render('FieldBinaryFileUploader.files', {'widget': self}));
+ this.read_name_values().then(function (ids) {
+ var render = $(instance.web.qweb.render('FieldBinaryFileUploader.files', {'widget': self, 'values': ids}));
render.on('click', '.oe_delete', _.bind(self.on_file_delete, self));
self.$('.oe_placeholder_files, .oe_attachments').replaceWith( render );
@@ -5498,6 +5559,7 @@ instance.web.form.FieldStatus = instance.web.form.AbstractField.extend({
start: function() {
this.field_manager.on("view_content_has_changed", this, this.calc_domain);
this.calc_domain();
+ this.on("change:value", this, this.get_selection);
this.on("change:evaluated_selection_domain", this, this.get_selection);
this.on("change:selection", this, function() {
this.selection = this.get("selection");
@@ -5547,18 +5609,18 @@ instance.web.form.FieldStatus = instance.web.form.AbstractField.extend({
var self = this;
var selection_unfolded = [];
var selection_folded = [];
+ var fold_field = this.options.fold_field;
var calculation = _.bind(function() {
if (this.field.type == "many2one") {
- return self.get_distant_fields().then(function(fields) {
+ return self.get_distant_fields().then(function (fields) {
return new instance.web.DataSetSearch(self, self.field.relation, self.build_context(), self.get("evaluated_selection_domain"))
- .read_slice(fields.fold ? ['fold'] : ['id'], {}).then(function (records) {
-
- var ids = _.map(records, function (val) {return val.id;});
+ .read_slice(_.union(_.keys(self.distant_fields), ['id']), {}).then(function (records) {
+ var ids = _.pluck(records, 'id');
return self.dataset.name_get(ids).then(function (records_name) {
_.each(records, function (record) {
var name = _.find(records_name, function (val) {return val[0] == record.id;})[1];
- if (record.fold && record.id != self.get('value')) {
+ if (fold_field && record[fold_field] && record.id != self.get('value')) {
selection_folded.push([record.id, name]);
} else {
selection_unfolded.push([record.id, name]);
@@ -5566,7 +5628,7 @@ instance.web.form.FieldStatus = instance.web.form.AbstractField.extend({
});
});
});
- });
+ });
} else {
// For field type selection filter values according to
// statusbar_visible attribute of the field. For example:
@@ -5588,12 +5650,18 @@ instance.web.form.FieldStatus = instance.web.form.AbstractField.extend({
}
});
},
+ /*
+ * :deprecated: this feature will probably be removed with OpenERP v8
+ */
get_distant_fields: function() {
var self = this;
+ if (! this.options.fold_field) {
+ this.distant_fields = {}
+ }
if (this.distant_fields) {
return $.when(this.distant_fields);
}
- return new instance.web.Model(self.field.relation).call("fields_get", [["fold"]]).then(function(fields) {
+ return new instance.web.Model(self.field.relation).call("fields_get", [[this.options.fold_field]]).then(function(fields) {
self.distant_fields = fields;
return fields;
});
diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js
index 99e87725dce..e01ab29f914 100644
--- a/addons/web/static/src/js/view_list.js
+++ b/addons/web/static/src/js/view_list.js
@@ -508,7 +508,6 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
var reloaded = $.Deferred();
this.$el.find('.oe_list_content').append(
this.groups.render(function () {
- // Keep '=='. This is not a mistake, this is a wanted behaviour to match null & undefined
if (self.dataset.index == null) {
if (self.records.length) {
self.dataset.index = 0;
@@ -1057,7 +1056,7 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.
id = parseInt(ref_match[2], 10);
new instance.web.DataSet(this.view, model).name_get([id]).done(function(names) {
if (!names.length) { return; }
- record.set(column.id, names[0][1]);
+ record.set(column.id + '__display', names[0][1]);
});
}
} else if (column.type === 'many2one') {
@@ -1131,11 +1130,7 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.
if (column.invisible === '1') {
return;
}
- if (column.tag === 'button') {
- cells.push('<td class="oe_button" title="' + column.string + '">&nbsp;</td>');
- } else {
- cells.push('<td title="' + column.string + '">&nbsp;</td>');
- }
+ cells.push('<td title="' + column.string + '">&nbsp;</td>');
});
if (this.options.deletable) {
cells.push('<td class="oe_list_record_delete"><button type="button" style="visibility: hidden"> </button></td>');
@@ -1319,7 +1314,8 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
}
},
close: function () {
- this.$row.children().last().empty();
+ this.$row.children().last().find('button').remove();
+ this.$row.children().last().find('span').remove();
this.records.reset();
},
/**
@@ -1474,14 +1470,13 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
var view = this.view,
limit = view.limit(),
- d = new $.Deferred(),
page = this.datagroup.openable ? this.page : view.page;
var fields = _.pluck(_.select(this.columns, function(x) {return x.tag == "field";}), 'name');
var options = { offset: page * limit, limit: limit, context: {bin_size: true} };
//TODO xmo: investigate why we need to put the setTimeout
- $.async_when().done(function() {
- dataset.read_slice(fields, options).done(function (records) {
+ return $.async_when().then(function() {
+ return dataset.read_slice(fields, options).then(function (records) {
// FIXME: ignominious hacks, parents (aka form view) should not send two ListView#reload_content concurrently
if (self.records.length) {
self.records.reset(null, {silent: true});
@@ -1491,7 +1486,8 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
} else {
if (dataset.size() == records.length) {
// only one page
- self.$row.find('td.oe_list_group_pagination').empty();
+ self.$row.find('td.oe_list_group_pagination').find('button').remove();
+ self.$row.find('td.oe_list_group_pagination').find('span').remove();
} else {
var pages = Math.ceil(dataset.size() / limit);
self.$row
@@ -1513,13 +1509,12 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
self.records.add(records, {silent: true});
list.render();
- d.resolve(list);
if (_.isEmpty(records)) {
view.no_result();
}
+ return list;
});
});
- return d.promise();
},
setup_resequence_rows: function (list, dataset) {
// drag and drop enabled if list is not sorted and there is a
@@ -1600,11 +1595,12 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
self.render_groups(groups));
if (post_render) { post_render(); }
}, function (dataset) {
- self.render_dataset(dataset).done(function (list) {
+ self.render_dataset(dataset).then(function (list) {
self.children[null] = list;
self.elements =
[list.$current.replaceAll($el)[0]];
self.setup_resequence_rows(list, dataset);
+ }).always(function() {
if (post_render) { post_render(); }
});
});
@@ -1658,9 +1654,12 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
function synchronized(fn) {
var fn_mutex = new $.Mutex();
return function () {
+ var obj = this;
var args = _.toArray(arguments);
- args.unshift(this);
- return fn_mutex.exec(fn.bind.apply(fn, args));
+ return fn_mutex.exec(function () {
+ if (obj.isDestroyed()) { return $.when(); }
+ return fn.apply(obj, args)
+ });
};
}
var DataGroup = instance.web.Class.extend({
@@ -2133,6 +2132,7 @@ instance.web.list.columns = new instance.web.Registry({
'field.handle': 'instance.web.list.Handle',
'button': 'instance.web.list.Button',
'field.many2onebutton': 'instance.web.list.Many2OneButton',
+ 'field.reference': 'instance.web.list.Reference',
'field.many2many': 'instance.web.list.Many2Many'
});
instance.web.list.columns.for_ = function (id, field, node) {
@@ -2359,5 +2359,18 @@ instance.web.list.Many2Many = instance.web.list.Column.extend({
return this._super(row_data, options);
}
});
+instance.web.list.Reference = instance.web.list.Column.extend({
+ _format: function (row_data, options) {
+ if (!_.isEmpty(row_data[this.id].value)) {
+ // If value, use __display version for printing
+ if (!!row_data[this.id + '__display']) {
+ row_data[this.id] = row_data[this.id + '__display'];
+ } else {
+ row_data[this.id] = {'value': ''};
+ }
+ }
+ return this._super(row_data, options);
+ }
+});
})();
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
diff --git a/addons/web/static/src/js/view_list_editable.js b/addons/web/static/src/js/view_list_editable.js
index 0f529b7554a..fc9af47dfe6 100644
--- a/addons/web/static/src/js/view_list_editable.js
+++ b/addons/web/static/src/js/view_list_editable.js
@@ -53,8 +53,10 @@
});
this.on('edit:after', this, function () {
self.$el.add(self.$buttons).addClass('oe_editing');
+ self.$('.ui-sortable').sortable('disable');
});
this.on('save:after cancel:after', this, function () {
+ self.$('.ui-sortable').sortable('enable');
self.$el.add(self.$buttons).removeClass('oe_editing');
});
},
@@ -275,7 +277,7 @@
if (!this.editor.is_editing()) { return; }
for(var i=0, len=this.fields_for_resize.length; i<len; ++i) {
var item = this.fields_for_resize[i];
- if (!item.field.get('invisible')) {
+ if (!item.field.get('effective_invisible')) {
this.resize_field(item.field, item.cell);
}
}
@@ -441,20 +443,26 @@
setup_events: function () {
var self = this;
_.each(this.editor.form.fields, function(field, field_name) {
- var setting = false;
var set_invisible = function() {
- if (!setting && field.get("effective_readonly")) {
- setting = true;
- field.set({invisible: true});
- setting = false;
- }
+ field.set({'force_invisible': field.get('effective_readonly')});
};
field.on("change:effective_readonly", self, set_invisible);
- field.on("change:invisible", self, set_invisible);
set_invisible();
+ field.on('change:effective_invisible', self, function () {
+ if (field.get('effective_invisible')) { return; }
+ var item = _(self.fields_for_resize).find(function (item) {
+ return item.field === field;
+ });
+ if (item) {
+ setTimeout(function() {
+ self.resize_field(item.field, item.cell);
+ }, 0);
+ }
+
+ });
});
- this.editor.$el.on('keyup keydown', function (e) {
+ this.editor.$el.on('keyup keypress keydown', function (e) {
if (!self.editor.is_editing()) { return true; }
var key = _($.ui.keyCode).chain()
.map(function (v, k) { return {name: k, code: v}; })
@@ -480,6 +488,7 @@
next_record = next_record || 'succ';
var self = this;
return this.save_edition().then(function (saveInfo) {
+ if (!saveInfo) { return null; }
if (saveInfo.created) {
return self.start_edition();
}
@@ -488,7 +497,7 @@
return self.start_edition(record, options);
});
},
- keyup_ENTER: function () {
+ keypress_ENTER: function () {
return this._next();
},
keydown_ESCAPE: function (e) {
diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js
index ecdbaf8b465..c9229c3c70a 100644
--- a/addons/web/static/src/js/views.js
+++ b/addons/web/static/src/js/views.js
@@ -147,6 +147,19 @@ instance.web.ActionManager = instance.web.Widget.extend({
this.inner_action = last_widget.action;
}
},
+ add_breadcrumb_url: function (url, label) {
+ // Add a pseudo breadcrumb that will redirect to an url
+ this.push_breadcrumb({
+ show: function() {
+ instance.web.redirect(url);
+ },
+ hide: function() {},
+ destroy: function() {},
+ get_title: function() {
+ return label;
+ }
+ });
+ },
get_title: function() {
var titles = [];
for (var i = 0; i < this.breadcrumbs.length; i += 1) {
@@ -217,6 +230,12 @@ instance.web.ActionManager = instance.web.Widget.extend({
do_load_state: function(state, warm) {
var self = this,
action_loaded;
+ if (!warm && 'return_label' in state) {
+ var return_url = state.return_url || document.referrer;
+ if (return_url) {
+ this.add_breadcrumb_url(return_url, state.return_label);
+ }
+ }
if (state.action) {
if (_.isString(state.action) && instance.web.client_actions.contains(state.action)) {
var action_client = {
@@ -225,7 +244,9 @@ instance.web.ActionManager = instance.web.Widget.extend({
params: state,
_push_me: state._push_me,
};
- this.null_action();
+ if (warm) {
+ this.null_action();
+ }
action_loaded = this.do_action(action_client);
} else {
var run_action = (!this.inner_widget || !this.inner_widget.action) || this.inner_widget.action.id !== state.action;
@@ -245,7 +266,9 @@ instance.web.ActionManager = instance.web.Widget.extend({
add_context.active_ids = [state.active_id];
}
add_context.params = state;
- this.null_action();
+ if (warm) {
+ this.null_action();
+ }
action_loaded = this.do_action(state.action, { additional_context: add_context });
$.when(action_loaded || null).done(function() {
instance.webclient.menu.has_been_loaded.done(function() {
@@ -258,7 +281,9 @@ instance.web.ActionManager = instance.web.Widget.extend({
}
} else if (state.model && state.id) {
// TODO handle context & domain ?
- this.null_action();
+ if (warm) {
+ this.null_action();
+ }
var action = {
res_model: state.model,
res_id: state.id,
@@ -268,7 +293,9 @@ instance.web.ActionManager = instance.web.Widget.extend({
action_loaded = this.do_action(action);
} else if (state.sa) {
// load session action
- this.null_action();
+ if (warm) {
+ this.null_action();
+ }
action_loaded = this.rpc('/web/session/get_session_action', {key: state.sa}).then(function(action) {
if (action) {
return self.do_action(action);
@@ -304,6 +331,7 @@ instance.web.ActionManager = instance.web.Widget.extend({
action_menu_id: null,
additional_context: {},
});
+
if (action === false) {
action = { type: 'ir.actions.act_window_close' };
} else if (_.isString(action) && instance.web.client_actions.contains(action)) {
@@ -468,49 +496,48 @@ instance.web.ActionManager = instance.web.Widget.extend({
ir_actions_report_xml: function(action, options) {
var self = this;
instance.web.blockUI();
- return instance.web.pyeval.eval_domains_and_contexts({
- contexts: [action.context],
- domains: []
- }).then(function(res) {
- action = _.clone(action);
- action.context = res.context;
-
- // iOS devices doesn't allow iframe use the way we do it,
- // opening a new window seems the best way to workaround
- if (navigator.userAgent.match(/(iPod|iPhone|iPad)/)) {
- var params = {
- action: JSON.stringify(action),
- token: new Date().getTime()
- };
- var url = self.session.url('/web/report', params);
- instance.web.unblockUI();
- $('<a href="'+url+'" target="_blank"></a>')[0].click();
- return;
- }
+ action = _.clone(action);
+ var eval_contexts = ([instance.session.user_context] || []).concat([action.context]);
+ action.context = instance.web.pyeval.eval('contexts',eval_contexts);
- var c = instance.webclient.crashmanager;
- return $.Deferred(function (d) {
- self.session.get_file({
- url: '/web/report',
- data: {action: JSON.stringify(action)},
- complete: instance.web.unblockUI,
- success: function(){
- if (!self.dialog) {
- options.on_close();
- }
- self.dialog_stop();
- d.resolve();
- },
- error: function () {
- c.rpc_error.apply(c, arguments);
- d.reject();
+ // iOS devices doesn't allow iframe use the way we do it,
+ // opening a new window seems the best way to workaround
+ if (navigator.userAgent.match(/(iPod|iPhone|iPad)/)) {
+ var params = {
+ action: JSON.stringify(action),
+ token: new Date().getTime()
+ };
+ var url = self.session.url('/web/report', params);
+ instance.web.unblockUI();
+ $('<a href="'+url+'" target="_blank"></a>')[0].click();
+ return;
+ }
+ var c = instance.webclient.crashmanager;
+ return $.Deferred(function (d) {
+ self.session.get_file({
+ url: '/web/report',
+ data: {action: JSON.stringify(action)},
+ complete: instance.web.unblockUI,
+ success: function(){
+ if (!self.dialog) {
+ options.on_close();
}
- });
+ self.dialog_stop();
+ d.resolve();
+ },
+ error: function () {
+ c.rpc_error.apply(c, arguments);
+ d.reject();
+ }
});
});
},
ir_actions_act_url: function (action) {
- window.open(action.url, action.target === 'self' ? '_self' : '_blank');
+ if (action.target === 'self') {
+ instance.web.redirect(action.url);
+ } else {
+ window.open(action.url, '_blank');
+ }
return $.when();
},
});
@@ -568,6 +595,7 @@ instance.web.ViewManager = instance.web.Widget.extend({
action_views_ids : views_ids
}, self.flags, self.flags[view.view_type] || {}, view.options || {})
});
+
views_ids[view.view_type] = view.view_id;
});
if (this.flags.views_switcher === false) {
@@ -575,7 +603,11 @@ instance.web.ViewManager = instance.web.Widget.extend({
}
// If no default view defined, switch to the first one in sequence
var default_view = this.flags.default_view || this.views_src[0].view_type;
- return this.switch_mode(default_view);
+
+
+ return this.switch_mode(default_view, null, this.flags[default_view] && this.flags[default_view].options);
+
+
},
switch_mode: function(view_type, no_store, view_options) {
var self = this;
@@ -783,6 +815,11 @@ instance.web.ViewManager = instance.web.Widget.extend({
contexts: [action_context].concat(contexts || []),
group_by_seq: groupbys || []
}).done(function (results) {
+ if (results.error) {
+ throw new Error(
+ _.str.sprintf(_t("Failed to evaluate search criterions")+": \n%s",
+ JSON.stringify(results.error)));
+ }
self.dataset._model = new instance.web.Model(
self.dataset.model, results.context, results.domain);
var groupby = results.group_by.length
@@ -1178,11 +1215,12 @@ instance.web.Sidebar = instance.web.Widget.extend({
var self = this;
self.getParent().sidebar_eval_context().done(function (sidebar_eval_context) {
var ids = self.getParent().get_selected_ids();
+ var domain;
if (self.getParent().get_active_domain) {
- var domain = self.getParent().get_active_domain();
+ domain = self.getParent().get_active_domain();
}
else {
- var domain = $.Deferred().resolve(undefined);
+ domain = $.Deferred().resolve(undefined);
}
if (ids.length === 0) {
instance.web.dialog($("<div />").text(_t("You must choose at least one record.")), { title: _t("Warning"), modal: true });
@@ -1226,7 +1264,7 @@ instance.web.Sidebar = instance.web.Widget.extend({
this.dataset = dataset;
this.model_id = model_id;
if (args && args[0].error) {
- this.do_warn( instance.web.qweb.render('message_error_uploading'), args[0].error);
+ this.do_warn(_t('Uploading Error'), args[0].error);
}
if (!model_id) {
this.on_attachments_loaded([]);
@@ -1310,7 +1348,7 @@ instance.web.View = instance.web.Widget.extend({
"context": this.dataset.get_context(),
});
}
- return view_loaded_def.then(function(r) {
+ return this.alive(view_loaded_def).then(function(r) {
self.fields_view = r;
// add css classes that reflect the (absence of) access rights
self.$el.addClass('oe_view')
@@ -1355,14 +1393,24 @@ instance.web.View = instance.web.Widget.extend({
}
};
var context = new instance.web.CompoundContext(dataset.get_context(), action_data.context || {});
+
+ // response handler
var handler = function (action) {
if (action && action.constructor == Object) {
- var ncontext = new instance.web.CompoundContext(context);
+ // filter out context keys that are specific to the current action.
+ // Wrong default_* and search_default_* values will no give the expected result
+ // Wrong group_by values will simply fail and forbid rendering of the destination view
+ var ncontext = new instance.web.CompoundContext(
+ _.object(_.reject(_.pairs(dataset.get_context().eval()), function(pair) {
+ return pair[0].match('^(?:(?:default_|search_default_).+|group_by|group_by_no_leaf|active_id|active_ids)$') !== null;
+ }))
+ );
+ ncontext.add(action_data.context || {});
+ ncontext.add({active_model: dataset.model});
if (record_id) {
ncontext.add({
active_id: record_id,
active_ids: [record_id],
- active_model: dataset.model
});
}
ncontext.add(action.context || {});
diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml
index 60a344467c0..80b82c1f40b 100644
--- a/addons/web/static/src/xml/base.xml
+++ b/addons/web/static/src/xml/base.xml
@@ -58,46 +58,6 @@
</div>
</t>
-<t t-name="Login">
- <div class="oe_login">
- <div class="oe_login_bottom"> </div>
- <div class="oe_login_error_message"/>
- <div class="oe_login_pane">
- <div class="oe_login_logo"><img src='/web/static/src/img/logo2.png'/></div>
- <form action="" method="post">
- <div class="oe_login_dbpane" >
- Database:
- <input name="db" t-att-value="widget.selected_db || ''"/>
- </div>
- <ul>
- <li>Username</li>
- <li><input name="login" type="text" value="" autofocus="autofocus"/></li>
- <li>Password</li>
- <li><input name="password" type="password" value=""/></li>
- <li><button name="submit">Log in</button></li>
- </ul>
- </form>
- <div class="oe_login_footer">
- <a href="#" class="oe_login_manage_db">Manage Databases</a> |
- <a href="http://www.openerp.com" target="_blank">Powered by <span>OpenERP</span></a>
- </div>
- </div>
- </div>
-</t>
-<t t-name="Login.dblist">
- <select name="db">
- <t t-foreach="db_list" t-as="db">
- <t t-if="selected_db === db">
- <option t-att-value="db" selected="true">
- <t t-esc="db"/></option>
- </t>
- <t t-if="selected_db !== db">
- <option t-att-value="db"><t t-esc="db"/></option>
- </t>
- </t>
- </select>
-</t>
-
<t t-name="DatabaseManager">
<div class="oe_view_manager_view_form">
<div class="oe_form">
@@ -320,7 +280,7 @@
</t>
<t t-name="DatabaseManager.menu">
<div class="oe_secondary_menu_section">Database Management</div>
- <ul class="oe_secondary_submenu">
+ <ul class="oe_secondary_submenu nav nav-pills nav-stacked">
<li><a href="#db_create">Create</a></li>
<li><a href="#db_duplicate">Duplicate</a></li>
<li><a href="#db_drop">Drop</a></li>
@@ -362,15 +322,13 @@
</t>
<t t-name="Menu">
- <ul class="oe_menu" t-if="widget.data">
+ <ul class="nav navbar-nav navbar-left" t-if="widget.data">
<li t-foreach="widget.data.data.children" t-as="menu">
<t t-call="Menu.link"/>
</li>
- <li class="oe_menu_more_container" style="display: none;">
- <span class="oe_topbar_item oe_menu_more_link oe_dropdown_toggle oe_dropdown_arrow">
- More
- <ul class="oe_menu_more oe_dropdown_menu"/>
- </span>
+ <li id="menu_more_container" class="dropdown" style="display: none;">
+ <a href="#" class="dropdown-toggle" data-toggle="dropdown">More <b class="caret"></b></a>
+ <ul id="menu_more" class="dropdown-menu"></ul>
</li>
</ul>
</t>
@@ -389,7 +347,7 @@
</div>
</t>
<t t-name="Menu.secondary.submenu">
- <ul t-if="menu.children.length" class="oe_secondary_submenu">
+ <ul t-if="menu.children.length" class="oe_secondary_submenu nav nav-pills nav-stacked">
<li t-foreach="menu.children" t-as="menu">
<t t-call="Menu.link"/>
<!--<span class="oe_menu_label">8</span>-->
@@ -410,24 +368,28 @@
</t>
<t t-name="Menu.needaction_counter">
- <div class="oe_tag oe_tag_dark oe_menu_counter">
+ <div id="menu_counter" class="badge pull-right">
<t t-if="widget.needaction_counter &gt; 99"> 99+ </t>
<t t-if="widget.needaction_counter &lt;= 99"> <t t-esc="widget.needaction_counter"/> </t>
</div>
</t>
<t t-name="UserMenu">
- <span class="oe_user_menu oe_topbar_item oe_dropdown_toggle oe_dropdown_arrow">
- <img class="oe_topbar_avatar" t-att-data-default-src="_s + '/web/static/src/img/user_menu_avatar.png'"/>
- <span class="oe_topbar_name"/>
- <ul class="oe_dropdown_menu">
- <li><a href="#" data-menu="settings">Preferences</a></li>
- <li><a href="#" data-menu="account">My OpenERP.com account</a></li>
- <li><a href="#" data-menu="about">About OpenERP</a></li>
- <li><a href="#" data-menu="help">Help</a></li>
- <li><a href="#" data-menu="logout">Log out</a></li>
- </ul>
- </span>
+ <ul class="nav navbar-nav navbar-right">
+ <li class="dropdown">
+ <a href="#" class="dropdown-toggle" data-toggle="dropdown">
+ <img class="oe_topbar_avatar" t-att-data-default-src="_s + '/web/static/src/img/user_menu_avatar.png'"/>
+ <span class="oe_topbar_name"/> <b class="caret"></b>
+ </a>
+ <ul class="dropdown-menu">
+ <li><a href="#" data-menu="settings">Preferences</a></li>
+ <li><a href="#" data-menu="account">My OpenERP.com account</a></li>
+ <li><a href="#" data-menu="about">About OpenERP</a></li>
+ <li><a href="#" data-menu="help">Help</a></li>
+ <li><a href="#" data-menu="logout">Log out</a></li>
+ </ul>
+ </li>
+ </ul>
</t>
<t t-name="UserMenu.about">
@@ -450,7 +412,7 @@
<div class="openerp openerp_webclient_container">
<table class="oe_webclient">
<tr>
- <td colspan="2" class="oe_topbar">
+ <td colspan="2" class="navbar navbar-default navbar-static-top oe_navbar" role="navigation">
<div class="oe_menu_placeholder"/>
<div class="oe_user_menu_placeholder"/>
<div class="oe_systray"/>
@@ -574,23 +536,32 @@
</t>
<t t-name="ViewManagerDebugViewLog">
<div class="oe_debug_view_log">
- <label>ID:</label>
- <span><t t-esc="perm.id"/></span>
-
- <label>XML ID:</label>
- <span><t t-esc="perm.xmlid"/></span>
-
- <label>Creation User:</label>
- <span><t t-esc="format(perm.create_uid, { 'type' : 'many2one' }, '/')"/></span>
-
- <label>Creation Date:</label>
- <span><t t-esc="format(perm.create_date, { 'type' : 'datetime' }, '/')"/></span>
-
- <label>Latest Modification by:</label>
- <span><t t-esc="format(perm.write_uid, { 'type' : 'many2one' }, '/')"/></span>
-
- <label>Latest Modification Date:</label>
- <span><t t-esc="format(perm.write_date, { 'type' : 'datetime' }, '/')"/></span>
+ <table class="table table-condensed table-striped">
+ <tr>
+ <th>ID:</th>
+ <td><t t-esc="perm.id"/></td>
+ </tr>
+ <tr>
+ <th>XML ID:</th>
+ <td><t t-esc="perm.xmlid or '/'"/></td>
+ </tr>
+ <tr>
+ <th>Creation User:</th>
+ <td><t t-esc="format(perm.create_uid, { 'type' : 'many2one' }, '/')"/></td>
+ </tr>
+ <tr>
+ <th>Creation Date:</th>
+ <td><t t-esc="format(perm.create_date, { 'type' : 'datetime' }, '/')"/></td>
+ </tr>
+ <tr>
+ <th>Latest Modification by:</th>
+ <td><t t-esc="format(perm.write_uid, { 'type' : 'many2one' }, '/')"/></td>
+ </tr>
+ <tr>
+ <th>Latest Modification Date:</th>
+ <td><t t-esc="format(perm.write_date, { 'type' : 'datetime' }, '/')"/></td>
+ </tr>
+ </table>
</div>
</t>
<t t-name="ViewPager">
@@ -1334,7 +1305,7 @@
<t t-name="FieldBinaryFileUploader.files">
<div class="oe_attachments">
<t t-if="!widget.get('effective_readonly')">
- <t t-foreach="widget.get('value')" t-as="id">
+ <t t-foreach="values" t-as="id">
<t t-set="file" t-value="widget.data[id]"/>
<div class="oe_attachment">
<span t-if="(file.upload or file.percent_loaded&lt;100)" t-attf-title="{(file.name || file.filename) + (file.date?' \n('+file.date+')':'' )}" t-attf-name="{file.name || file.filename}">
@@ -1448,7 +1419,7 @@
<button type="button" class="oe_button oe_selectcreatepopup-search-select oe_highlight" disabled="disabled">Select</button>
</t>
<button type="button" class="oe_button oe_selectcreatepopup-search-create">Create</button>
- or <a class="oe_button oe_selectcreatepopup-search-close oe_bold oe_form_button_cancel" href="javascript:void(0)">Cancel</a>
+ or <a class="oe_selectcreatepopup-search-close oe_bold oe_form_button_cancel" href="javascript:void(0)">Cancel</a>
</t>
<t t-name="AbstractFormPopup.buttons">
<t t-if="! readonly">
@@ -1461,7 +1432,7 @@
</t>
or
</t>
- <a class="oe_button oe_abstractformpopup-form-close oe_bold oe_form_button_cancel" href="javascript:void(0)">
+ <a class="oe_abstractformpopup-form-close oe_bold oe_form_button_cancel" href="javascript:void(0)">
<t t-if="! readonly">
Discard
</t>