summaryrefslogtreecommitdiff
path: root/addons/web/static/src
diff options
context:
space:
mode:
authorOlivier Dony <odo@openerp.com>2012-10-01 16:53:08 +0200
committerOlivier Dony <odo@openerp.com>2012-10-01 16:53:08 +0200
commit9e6920415e41a052cffa7b898bf044dbf82b5ccb (patch)
tree05e8d64788da1ff9f65e7406e3ff3d96d58a5762 /addons/web/static/src
parent4ed79f3457a0252dcee4780385dc474ff2af356e (diff)
parent1370ae133d4477d0a7a2239732f529a1eef39824 (diff)
[MERGE] Sync with trunk
bzr revid: odo@openerp.com-20121001145308-ri5rp2cazxqxcxme
Diffstat (limited to 'addons/web/static/src')
-rw-r--r--addons/web/static/src/css/Makefile2
-rw-r--r--addons/web/static/src/css/base.css2693
-rw-r--r--addons/web/static/src/css/base.sass2446
-rw-r--r--addons/web/static/src/css/base_old.css999
-rw-r--r--addons/web/static/src/css/data_export.css2
-rw-r--r--addons/web/static/src/css/data_import.css46
-rw-r--r--addons/web/static/src/img/graph_background.pngbin0 -> 3920 bytes
-rw-r--r--addons/web/static/src/img/pattern.pngbin98 -> 0 bytes
-rw-r--r--addons/web/static/src/img/throbber2.gifbin7195 -> 0 bytes
-rw-r--r--addons/web/static/src/img/view_empty_arrow.pngbin2308 -> 4157 bytes
-rw-r--r--addons/web/static/src/js/boot.js14
-rw-r--r--addons/web/static/src/js/chrome.js848
-rw-r--r--addons/web/static/src/js/corelib.js221
-rw-r--r--addons/web/static/src/js/coresetup.js395
-rw-r--r--addons/web/static/src/js/data.js289
-rw-r--r--addons/web/static/src/js/data_export.js118
-rw-r--r--addons/web/static/src/js/data_import.js366
-rw-r--r--addons/web/static/src/js/formats.js85
-rw-r--r--addons/web/static/src/js/search.js291
-rw-r--r--addons/web/static/src/js/test_support.js13
-rw-r--r--addons/web/static/src/js/view_editor.js1207
-rw-r--r--addons/web/static/src/js/view_form.js2100
-rw-r--r--addons/web/static/src/js/view_list.js724
-rw-r--r--addons/web/static/src/js/view_list_editable.js965
-rw-r--r--addons/web/static/src/js/view_tree.js124
-rw-r--r--addons/web/static/src/js/views.js794
-rw-r--r--addons/web/static/src/xml/base.xml924
27 files changed, 7836 insertions, 7830 deletions
diff --git a/addons/web/static/src/css/Makefile b/addons/web/static/src/css/Makefile
index eca044497ef..d6b4f4b2fc8 100644
--- a/addons/web/static/src/css/Makefile
+++ b/addons/web/static/src/css/Makefile
@@ -1,3 +1,3 @@
base.css: base.sass
- sass -t expanded base.sass base.css
+ sass --trace -t expanded base.sass base.css
diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css
index 5ef56c69797..18db06c44a1 100644
--- a/addons/web/static/src/css/base.css
+++ b/addons/web/static/src/css/base.css
@@ -20,11 +20,21 @@
font-style: normal;
}
-.openerp.openerp-web-client-container {
+@media print {
+ .oe_topbar, .oe_leftbar, .oe_loading {
+ display: none !important;
+ }
+}
+.openerp.openerp_webclient_container {
height: 100%;
position: relative;
}
+.text-tag .text-button {
+ height: auto !important;
+ min-height: 16px;
+}
+
.openerp {
padding: 0;
margin: 0;
@@ -32,16 +42,25 @@
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
*/
}
+.openerp :-moz-placeholder {
+ color: #afafb6 !important;
+ font-style: italic !important;
+}
+.openerp ::-webkit-input-placeholder {
+ color: #afafb6 !important;
+ font-style: italic !important;
+}
.openerp a {
text-decoration: none;
+ cursor: pointer !important;
}
.openerp table {
padding: 0;
- font-size: 13px;
border-collapse: collapse;
}
.openerp thead {
@@ -84,63 +103,24 @@
background-image: -o-linear-gradient(top, #eeeeee, #dedede);
background-image: linear-gradient(to bottom, #eeeeee, #dedede);
}
-.openerp .numeric {
- text-align: right;
- width: 82px;
-}
-.openerp .numeric input {
- text-align: right;
-}
-.openerp ul, .openerp li, .openerp ol {
- margin: 0;
- padding: 0;
-}
-.openerp li {
- list-style-type: none;
-}
-.openerp input, .openerp textarea {
+.openerp input, .openerp textarea, .openerp select {
+ padding: 2px 4px;
border: 1px solid #cccccc;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
border-radius: 3px;
+ background: white;
}
-.openerp .oe_i {
- font-family: "mnmliconsRegular" !important;
- font-size: 21px;
- font-weight: 300 !important;
-}
-.openerp .oe_e {
- font-family: "entypoRegular" !important;
- font-size: 34px;
- font-weight: 300 !important;
-}
-.openerp .oe_left {
- float: left;
- margin-right: 8px;
-}
-.openerp .oe_right {
- float: right;
- margin-left: 8px;
-}
-.openerp .oe_text_right {
- text-align: right;
-}
-.openerp .oe_clear {
- clear: both;
-}
-.openerp .oe_wait {
- cursor: wait;
-}
-.openerp .oe_fade {
- color: #888888;
+.openerp img {
+ vertical-align: middle;
}
-.openerp .oe_bold {
- font-weight: bold;
+.openerp h4 {
+ margin: 4px 0;
}
-.openerp a.button:link, .openerp a.button:visited, .openerp button, .openerp input[type='submit'] {
+.openerp a.button:link, .openerp a.button:visited, .openerp button, .openerp input[type='submit'], .openerp .ui-dialog-buttonpane .ui-dialog-buttonset .ui-button {
display: inline-block;
- border: 1px solid #ababab;
- color: #404040;
+ border: 1px solid rgba(0, 0, 0, 0.4);
+ color: #4c4c4c;
margin: 0;
padding: 3px 12px;
font-size: 13px;
@@ -157,12 +137,12 @@
border-radius: 3px;
-moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(255, 255, 255, 0.8) inset;
-webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(255, 255, 255, 0.8) inset;
- -box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(255, 255, 255, 0.8) inset;
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(255, 255, 255, 0.8) inset;
text-shadow: 0 1px 1px rgba(255, 255, 255, 0.5);
-webkit-font-smoothing: antialiased;
outline: none;
}
-.openerp a.button:hover, .openerp button:hover, .openerp input[type='submit']:hover {
+.openerp a.button:hover, .openerp button:hover, .openerp input[type='submit']:hover, .openerp .ui-dialog-buttonpane .ui-dialog-buttonset .ui-button.ui-state-hover {
background-color: #f6f6f6;
background-image: -webkit-gradient(linear, left top, left bottom, from(#f6f6f6), to(#e3e3e3));
background-image: -webkit-linear-gradient(top, #f6f6f6, #e3e3e3);
@@ -171,9 +151,11 @@
background-image: -o-linear-gradient(top, #f6f6f6, #e3e3e3);
background-image: linear-gradient(to bottom, #f6f6f6, #e3e3e3);
cursor: pointer;
+ background-position: 0;
}
-.openerp a.button:focus, .openerp button:focus, .openerp input[type='submit']:focus {
+.openerp a.button:focus, .openerp 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: #f6f6f6;
background-image: -webkit-gradient(linear, left top, left bottom, from(#f6f6f6), to(#e3e3e3));
background-image: -webkit-linear-gradient(top, #f6f6f6, #e3e3e3);
@@ -183,29 +165,256 @@
background-image: linear-gradient(to bottom, #f6f6f6, #e3e3e3);
-moz-box-shadow: 0 0 3px #80bfff, 0 1px 1px rgba(255, 255, 255, 0.8) inset;
-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 {
- background: #e3e3e3;
- background: -moz-linear-gradient(top, #e3e3e3, #f6f6f6) #1b468f;
- background: -webkit-gradient(linear, left top, left bottom, from(#e3e3e3), to(#f6f6f6)) #1b468f;
- background: linear-gradient(top, #e3e3e3, #f6f6f6) #1b468f;
+ 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 {
+ background-color: #e3e3e3;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#e3e3e3), to(#f6f6f6));
+ background-image: -webkit-linear-gradient(top, #e3e3e3, #f6f6f6);
+ background-image: -moz-linear-gradient(top, #e3e3e3, #f6f6f6);
+ background-image: -ms-linear-gradient(top, #e3e3e3, #f6f6f6);
+ background-image: -o-linear-gradient(top, #e3e3e3, #f6f6f6);
+ background-image: linear-gradient(to bottom, #e3e3e3, #f6f6f6);
-moz-box-shadow: none;
-webkit-box-shadow: none;
- -box-shadow: none;
+ box-shadow: none;
}
.openerp a.button.disabled, .openerp button:disabled, .openerp input[type='submit']:disabled {
background: #efefef !important;
border: 1px solid #d1d1d1 !important;
-moz-box-shadow: none !important;
-webkit-box-shadow: none !important;
- -box-shadow: none !important;
+ box-shadow: none !important;
color: #aaaaaa !important;
cursor: default;
text-shadow: 0 1px 1px white !important;
}
-.openerp button.primary {
+.openerp .ui-widget-content a {
+ color: #7c7bad;
+}
+.openerp .oe_bounce_container {
+ display: inline-block;
+}
+.openerp.ui-dialog {
+ display: none;
+ padding: 6px;
+ background-color: rgba(60, 60, 60, 0.7);
+ border: 1px solid;
+ border-color: #888888 #555555 #444444;
+ -moz-border-radius: 8px;
+ -webkit-border-radius: 8px;
+ border-radius: 8px;
+ -moz-box-shadow: 0 1px 12px rgba(0, 0, 0, 0.6);
+ -webkit-box-shadow: 0 1px 12px rgba(0, 0, 0, 0.6);
+ box-shadow: 0 1px 12px rgba(0, 0, 0, 0.6);
+ -webkit-background-clip: padding-box;
+ -moz-background-clip: padding-box;
+ background-clip: padding-box;
+}
+.openerp.ui-dialog .ui-dialog-content {
+ padding: 0px;
+}
+.openerp.ui-dialog .ui-dialog-titlebar, .openerp.ui-dialog .ui-dialog-content, .openerp.ui-dialog .ui-dialog-buttonpane {
+ padding: 16px;
+}
+.openerp.ui-dialog .ui-dialog-titlebar {
+ border-bottom: 1px solid #cacaca;
+ -moz-border-radius: 2px 2px 0 0;
+ -webkit-border-radius: 2px 2px 0 0;
+ border-radius: 2px 2px 0 0;
+ background-color: #fcfcfc;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#fcfcfc), to(#dedede));
+ background-image: -webkit-linear-gradient(top, #fcfcfc, #dedede);
+ background-image: -moz-linear-gradient(top, #fcfcfc, #dedede);
+ background-image: -ms-linear-gradient(top, #fcfcfc, #dedede);
+ background-image: -o-linear-gradient(top, #fcfcfc, #dedede);
+ background-image: linear-gradient(to bottom, #fcfcfc, #dedede);
+}
+.openerp.ui-dialog .ui-dialog-titlebar .ui-dialog-title {
+ margin: 0;
+ padding: 0;
+}
+.openerp.ui-dialog .ui-widget-header {
+ border: none;
+}
+.openerp.ui-dialog .ui-dialog-content {
+ background: white;
+}
+.openerp.ui-dialog .ui-dialog-buttonpane {
+ border-top: 1px solid #e0e0e0;
+ background: #f5f7f9;
+ margin: 0;
+ -moz-border-radius: 0 0 2px 2px;
+ -webkit-border-radius: 0 0 2px 2px;
+ border-radius: 0 0 2px 2px;
+}
+.openerp.ui-dialog .ui-dialog-buttonpane button {
+ margin: 0 4px 0 0;
+}
+.openerp.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset {
+ float: left;
+}
+.openerp.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset .ui-button {
+ margin-right: 4px;
+}
+.openerp.ui-dialog .ui-dialog-titlebar-close {
+ padding: 0;
+}
+.openerp.ui-dialog .ui-dialog-titlebar-close .ui-icon-closethick {
+ display: none;
+}
+.openerp.ui-dialog .ui-dialog-titlebar-close:before {
+ content: "×";
+ font-size: 18px;
+ font-weight: bold;
+ line-height: 16px;
+ color: black;
+ text-shadow: 0 1px 0 white;
+ padding: 0;
+ cursor: pointer;
+ background: transparent;
+ border: 0;
+}
+.openerp.ui-dialog .ui-dialog-titlebar-close:before:hover {
+ color: black;
+ text-decoration: none;
+}
+.openerp.ui-dialog .oe_about {
+ background-color: white;
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAGCAYAAADgzO9IAAAAKUlEQVQIHWO8e/fufwYsgAUkJigoiCIF5DMyoYggcUiXgNnBiGQKmAkARpcEQeriln4AAAAASUVORK5CYII=);
+ -moz-border-radius: 0 0 2px 2px;
+ -webkit-border-radius: 0 0 2px 2px;
+ border-radius: 0 0 2px 2px;
+}
+.openerp.ui-dialog .oe_about a {
+ color: #7c7bad;
+}
+.openerp.ui-dialog .oe_about a:hover {
+ text-decoration: underline;
+}
+.openerp.ui-dialog .oe_about a:focus {
+ outline: none;
+}
+.openerp.ui-dialog .oe_about .oe_logo {
+ margin-left: -6px;
+}
+.openerp.ui-dialog .oe_about .oe_bottom {
+ position: absolute;
+ top: 50%;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ text-shadow: 0 1px 1px #999999;
+ background-color: #b41616;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#b41616), to(#600606));
+ background-image: -webkit-linear-gradient(top, #b41616, #600606);
+ background-image: -moz-linear-gradient(top, #b41616, #600606);
+ background-image: -ms-linear-gradient(top, #b41616, #600606);
+ background-image: -o-linear-gradient(top, #b41616, #600606);
+ background-image: linear-gradient(to bottom, #b41616, #600606);
+ color: #eeeeee;
+ padding: 0 16px;
+ -moz-border-radius: 0 0 2px 2px;
+ -webkit-border-radius: 0 0 2px 2px;
+ border-radius: 0 0 2px 2px;
+}
+.openerp.ui-dialog .oe_about .oe_bottom a {
+ color: #eeeeee;
+}
+.openerp.ui-dialog.oe_act_window .ui-dialog-content {
+ padding: 0px;
+}
+.openerp .modal-backdrop {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 1040;
+ background-color: black;
+ filter: alpha(opacity=30);
+ opacity: 0.3;
+}
+.openerp .oe_i {
+ font-family: "mnmliconsRegular" !important;
+ font-size: 21px;
+ font-weight: 300 !important;
+}
+.openerp .oe_e {
+ font-family: "entypoRegular" !important;
+ font-size: 34px;
+ font-weight: 300 !important;
+}
+.openerp .oe_left {
+ float: left;
+ margin-right: 8px;
+}
+.openerp .oe_right {
+ float: right;
+ margin-left: 8px;
+}
+.openerp .oe_text_right {
+ text-align: right;
+}
+.openerp .oe_clear {
+ clear: both;
+}
+.openerp .oe_wait {
+ cursor: wait;
+}
+.openerp .oe_fade {
+ color: #888888;
+ font-weight: normal;
+}
+.openerp .oe_bold {
+ font-weight: bold;
+}
+.openerp .oe_inline {
+ width: auto !important;
+}
+.openerp .oe_highlight {
color: white;
+ background: #dc5f59;
+}
+.openerp button.oe_highlight {
+ background-color: #df3f3f;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#df3f3f), to(#a21a1a));
+ background-image: -webkit-linear-gradient(top, #df3f3f, #a21a1a);
+ background-image: -moz-linear-gradient(top, #df3f3f, #a21a1a);
+ background-image: -ms-linear-gradient(top, #df3f3f, #a21a1a);
+ background-image: -o-linear-gradient(top, #df3f3f, #a21a1a);
+ background-image: linear-gradient(to bottom, #df3f3f, #a21a1a);
+ -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 button.oe_highlight:hover {
+ background-color: #e25858;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#e25858), to(#ab1b1b));
+ background-image: -webkit-linear-gradient(top, #e25858, #ab1b1b);
+ background-image: -moz-linear-gradient(top, #e25858, #ab1b1b);
+ background-image: -ms-linear-gradient(top, #e25858, #ab1b1b);
+ background-image: -o-linear-gradient(top, #e25858, #ab1b1b);
+ background-image: linear-gradient(to bottom, #e25858, #ab1b1b);
+}
+.openerp button.oe_highlight:active {
+ background-color: #c52020;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#c52020), to(#d22323));
+ background-image: -webkit-linear-gradient(top, #c52020, #d22323);
+ background-image: -moz-linear-gradient(top, #c52020, #d22323);
+ background-image: -ms-linear-gradient(top, #c52020, #d22323);
+ background-image: -o-linear-gradient(top, #c52020, #d22323);
+ background-image: linear-gradient(to bottom, #c52020, #d22323);
+}
+.openerp .oe_background_grey {
+ background: #eeeeee !important;
+}
+.openerp .oe_form_dirty .oe_highlight_on_dirty {
+ color: white;
+ background: #dc5f59;
+ font-weight: bold;
+}
+.openerp .oe_form_dirty button.oe_highlight_on_dirty {
background-color: #dc5f59;
background-image: -webkit-gradient(linear, left top, left bottom, from(#dc5f59), to(#b33630));
background-image: -webkit-linear-gradient(top, #dc5f59, #b33630);
@@ -215,54 +424,352 @@
background-image: linear-gradient(to bottom, #dc5f59, #b33630);
-moz-box-shadow: none;
-webkit-box-shadow: none;
- -box-shadow: none;
-}
-.openerp button.primary:hover {
- background-color: #df6b66;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#df6b66), to(#bf3a33));
- background-image: -webkit-linear-gradient(top, #df6b66, #bf3a33);
- background-image: -moz-linear-gradient(top, #df6b66, #bf3a33);
- background-image: -ms-linear-gradient(top, #df6b66, #bf3a33);
- background-image: -o-linear-gradient(top, #df6b66, #bf3a33);
- background-image: linear-gradient(to bottom, #df6b66, #bf3a33);
- -moz-box-shadow: 0 0 1px rgba(0, 0, 0, 0.2);
- -webkit-box-shadow: 0 0 1px rgba(0, 0, 0, 0.2);
- -box-shadow: 0 0 1px rgba(0, 0, 0, 0.2);
-}
-.openerp button.primary:active {
- background-color: #b33630;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#b33630), to(#dc5f59));
- background-image: -webkit-linear-gradient(top, #b33630, #dc5f59);
- background-image: -moz-linear-gradient(top, #b33630, #dc5f59);
- background-image: -ms-linear-gradient(top, #b33630, #dc5f59);
- background-image: -o-linear-gradient(top, #b33630, #dc5f59);
- background-image: linear-gradient(to bottom, #b33630, #dc5f59);
+ box-shadow: none;
+}
+.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;
+}
+.openerp .oe_button_box button {
+ margin: 4px;
+}
+.openerp .oe_avatar > img {
+ max-height: 90px;
+ max-width: 90px;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ border-radius: 3px;
+ -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
+ -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
+ border: none;
+ margin-bottom: 10px;
+}
+.openerp .oe_avatar + div {
+ margin-left: 5px;
+}
+.openerp .oe_image_small > img {
+ max-width: 50px;
+ max-height: 50px;
+}
+.openerp .oe_image_medium > img {
+ max-width: 180px;
+ max-height: 180px;
+}
+.openerp .oe_button.oe_link {
+ border: none;
+ padding: 0;
+ margin: 0;
+ background: none;
+ -moz-border-radius: 0;
+ -webkit-border-radius: 0;
+ border-radius: 0;
-moz-box-shadow: none;
-webkit-box-shadow: none;
- -box-shadow: none;
+ box-shadow: none;
+}
+.openerp .oe_button.oe_link img {
+ display: none;
+}
+.openerp .oe_button.oe_link span {
+ border: none;
+ padding: 0;
+ margin: 0;
+ background: none;
+ -moz-border-radius: 0;
+ -webkit-border-radius: 0;
+ border-radius: 0;
+ -moz-box-shadow: none;
+ -webkit-box-shadow: none;
+ box-shadow: none;
+ color: #7c7bad;
+ font-weight: bold;
+}
+.openerp .oe_button.oe_link span:hover {
+ text-decoration: underline;
+}
+.openerp .oe_webclient .oe_star_on, .openerp .oe_webclient .oe_star_off {
+ color: #cccccc;
+ text-shadow: 0 0 2px black;
+ vertical-align: top;
+ position: relative;
+ top: -5px;
+}
+.openerp .oe_webclient .oe_star_on:hover, .openerp .oe_webclient .oe_star_off:hover {
+ text-decoration: none;
+}
+.openerp .oe_webclient .oe_star_on {
+ color: gold;
+}
+.openerp .oe_tag {
+ border: 1px solid #afafb6;
+ font-size: 11px;
+ padding: 2px 4px;
+ margin: 0 2px 2px 0;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ border-radius: 3px;
+ background: #f0f0fa;
+ color: #4c4c4c;
+}
+.openerp .oe_tag_dark {
+ background: #7c7bad;
+ color: #eeeeee;
+}
+.openerp .oe_tags {
+ margin-bottom: 1px;
+}
+.openerp .oe_tags.oe_inline {
+ min-width: 250px;
+}
+.openerp .oe_tags .text-wrap {
+ width: 100% !important;
+}
+.openerp .oe_tags .text-wrap textarea {
+ width: 100% !important;
+}
+.openerp .oe_tags .text-core .text-wrap .text-dropdown .text-list .text-suggestion em {
+ font-style: italic;
+ text-decoration: none;
+}
+.openerp.oe_tooltip {
+ font-size: 12px;
+}
+.openerp.oe_tooltip .oe_tooltip_string {
+ color: #ffdd55;
+ font-weight: bold;
+ font-size: 13px;
+}
+.openerp.oe_tooltip .oe_tooltip_help {
+ white-space: pre-wrap;
+}
+.openerp.oe_tooltip .oe_tooltip_technical {
+ padding: 0 0 4px 0;
+ margin: 5px 0 0 15px;
+}
+.openerp.oe_tooltip .oe_tooltip_technical li {
+ list-style: circle;
+}
+.openerp.oe_tooltip .oe_tooltip_technical_title {
+ font-weight: bold;
}
-.openerp button.following {
+.openerp.oe_tooltip .oe_tooltip_close {
+ margin: -5px 0 0 2px;
+ cursor: default;
+ float: right;
color: white;
- width: 120px;
- background-color: #8a89ba;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#8a89ba), to(#807fb4));
- background-image: -webkit-linear-gradient(top, #8a89ba, #807fb4);
- background-image: -moz-linear-gradient(top, #8a89ba, #807fb4);
- background-image: -ms-linear-gradient(top, #8a89ba, #807fb4);
- background-image: -o-linear-gradient(top, #8a89ba, #807fb4);
- background-image: linear-gradient(to bottom, #8a89ba, #807fb4);
-}
-.openerp button.unfollow {
+}
+.openerp.oe_tooltip .oe_tooltip_close:hover {
+ color: #999999;
+ cursor: pointer;
+}
+.openerp.oe_tooltip .oe_tooltip_message {
+ max-width: 310px;
+}
+.openerp .oe_notebook {
+ margin: 8px 0;
+ padding: 0 16px;
+ list-style: none;
+ zoom: 1;
+}
+.openerp .oe_notebook.ui-corner-all {
+ -moz-border-radius: 0;
+ -webkit-border-radius: 0;
+ border-radius: 0;
+}
+.openerp .oe_notebook:before, .openerp .oe_notebook:after {
+ display: table;
+ content: "";
+ zoom: 1;
+}
+.openerp .oe_notebook:after {
+ clear: both;
+}
+.openerp .oe_notebook > li {
+ float: left;
+}
+.openerp .oe_notebook > li > a {
+ display: block;
+ color: #4c4c4c;
+}
+.openerp .oe_notebook {
+ border-color: #dddddd;
+ border-style: solid;
+ border-width: 0 0 1px;
+}
+.openerp .oe_notebook > li {
+ position: relative;
+}
+.openerp .oe_notebook > li > a {
+ padding: 0 12px;
+ margin-right: 2px;
+ line-height: 30px;
+ border: 1px solid transparent;
+ -moz-border-radius: 4px 4px 0 0;
+ -webkit-border-radius: 4px 4px 0 0;
+ border-radius: 4px 4px 0 0;
+}
+.openerp .oe_notebook > li > a:hover {
+ text-decoration: none;
+ background-color: #eeeeee;
+ border-color: #eeeeee #eeeeee #dddddd;
+}
+.openerp .oe_notebook > li.ui-state-active > a, .openerp .oe_notebook > li.ui-state-active > a:hover {
+ background-color: white;
+ border: 1px solid #dddddd;
+ border-bottom-color: transparent;
+ cursor: default;
+}
+.openerp .oe_notebook_page {
+ padding: 0;
+}
+.openerp div.ui-tabs {
+ padding: 3px 0px 3px 0px;
+}
+.openerp .ui-tabs-hide {
+ display: none;
+}
+.openerp .oe_dropdown, .openerp .oe_dropdown_hover, .openerp .oe_dropdown_toggle {
+ position: relative;
+ cursor: pointer;
+}
+.openerp .oe_dropdown_toggle {
+ color: rgba(0, 0, 0, 0.5);
+ font-weight: normal;
+}
+.openerp .oe_dropdown_hover:hover .oe_dropdown_menu, .openerp .oe_dropdown_menu.oe_opened {
+ display: block;
+}
+.openerp .oe_dropdown_menu {
+ display: none;
+ position: absolute;
+ top: 26px;
+ left: 0;
+ z-index: 3;
+ margin: 0;
+ padding: 0;
+ border: 1px solid #afafb6;
+ background: white;
+ padding: 4px 0;
+ min-width: 140px;
+ text-align: left;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ border-radius: 3px;
+ -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
+ -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
+}
+.openerp .oe_dropdown_menu > li {
+ list-style-type: none;
+ float: none;
+ display: block;
+ position: relative;
+ margin: 0;
+ padding: 2px 8px;
+}
+.openerp .oe_dropdown_menu > li:hover {
+ background-color: #f0f0fa;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#f0f0fa), to(#eeeef6));
+ background-image: -webkit-linear-gradient(top, #f0f0fa, #eeeef6);
+ background-image: -moz-linear-gradient(top, #f0f0fa, #eeeef6);
+ background-image: -ms-linear-gradient(top, #f0f0fa, #eeeef6);
+ background-image: -o-linear-gradient(top, #f0f0fa, #eeeef6);
+ background-image: linear-gradient(to bottom, #f0f0fa, #eeeef6);
+ -moz-box-shadow: none;
+ -webkit-box-shadow: none;
+ box-shadow: none;
+}
+.openerp .oe_dropdown_menu > li > a {
+ white-space: nowrap;
+ display: block;
+ color: #4c4c4c;
+ text-decoration: none;
+}
+.openerp .oe_dropdown_menu > li > a:hover {
+ text-decoration: none;
+}
+.openerp .oe_dropdown_arrow:after {
+ width: 0;
+ height: 0;
+ display: inline-block;
+ content: "&darr";
+ text-indent: -99999px;
+ vertical-align: top;
+ margin-top: 8px;
+ margin-left: 3px;
+ border-left: 4px solid transparent;
+ border-right: 4px solid transparent;
+ border-top: 4px solid #404040;
+ filter: alpha(opacity=50);
+ opacity: 0.5;
+}
+.openerp .oe_sidebar {
+ white-space: nowrap;
+}
+.openerp .oe_sidebar .oe_dropdown_menu .oe_sidebar_add_attachment {
+ height: 20px;
+ cursor: pointer;
+ padding-left: 6px;
+ margin-top: 6px;
+}
+.openerp .oe_sidebar .oe_dropdown_menu .oe_sidebar_add_attachment span {
+ font-weight: bold;
+}
+.openerp .oe_sidebar .oe_dropdown_menu .oe_sidebar_add_attachment .oe_hidden_input_file {
+ width: 200px;
+}
+.openerp .oe_sidebar .oe_dropdown_menu .oe_sidebar_add_attachment:hover {
+ background-color: #f0f0fa;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#f0f0fa), to(#eeeef6));
+ background-image: -webkit-linear-gradient(top, #f0f0fa, #eeeef6);
+ background-image: -moz-linear-gradient(top, #f0f0fa, #eeeef6);
+ background-image: -ms-linear-gradient(top, #f0f0fa, #eeeef6);
+ background-image: -o-linear-gradient(top, #f0f0fa, #eeeef6);
+ background-image: linear-gradient(to bottom, #f0f0fa, #eeeef6);
+ -moz-box-shadow: none;
+ -webkit-box-shadow: none;
+ box-shadow: none;
+}
+.openerp .oe_sidebar .oe_dropdown_menu li .oe_sidebar_delete_item {
+ position: absolute;
+ top: 4px;
+ right: 4px;
display: none;
+ width: 12px;
+ height: 12px;
+ padding: 1px;
+ color: #8786b7;
+ line-height: 8px;
+ text-align: center;
+ font-weight: bold;
+ text-shadow: 0 1px 1px white;
+}
+.openerp .oe_sidebar .oe_dropdown_menu li .oe_sidebar_delete_item:hover {
+ text-decoration: none;
color: white;
- width: 120px;
- background-color: #dc5f59;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#dc5f59), to(#b33630));
- background-image: -webkit-linear-gradient(top, #dc5f59, #b33630);
- background-image: -moz-linear-gradient(top, #dc5f59, #b33630);
- background-image: -ms-linear-gradient(top, #dc5f59, #b33630);
- background-image: -o-linear-gradient(top, #dc5f59, #b33630);
- background-image: linear-gradient(to bottom, #dc5f59, #b33630);
+ background: #8786b7;
+ text-shadow: 0 1px 1px rgba(0, 0, 0, 0.4);
+ -moz-border-radius: 2px;
+ -webkit-border-radius: 2px;
+ border-radius: 2px;
+}
+.openerp .oe_sidebar .oe_dropdown_menu li:hover .oe_sidebar_delete_item {
+ display: inline-block;
}
.openerp .oe_loading {
display: none;
@@ -285,14 +792,15 @@
z-index: 1050;
}
.openerp .oe_login {
- background: url("/web/static/src/img/pattern.png") repeat;
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAGCAYAAADgzO9IAAAAKUlEQVQIHWO8e/fufwYsgAUkJigoiCIF5DMyoYggcUiXgNnBiGQKmAkARpcEQeriln4AAAAASUVORK5CYII=);
text-align: center;
font-size: 14px;
height: 100%;
}
-.openerp .oe_login li {
+.openerp .oe_login ul, .openerp .oe_login li {
+ margin: 0;
list-style-type: none;
- padding-bottom: 4px;
+ padding: 0 0 4px 0;
}
.openerp .oe_login button {
float: right;
@@ -314,16 +822,16 @@
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;
+ 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.3);
- -webkit-box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.3);
- -box-shadow: inset 0 1px 4px rgba(0, 0, 0, 0.3);
+ -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;
@@ -383,7 +891,7 @@
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);
+ box-shadow: 0 0 18px rgba(0, 0, 0, 0.9);
}
.openerp .oe_login .oe_login_pane h2 {
margin-top: 0;
@@ -427,7 +935,7 @@
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);
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.8);
}
.openerp .oe_login_invalid .oe_login_error_message {
display: inline-block;
@@ -454,8 +962,8 @@
}
.openerp .oe_topbar {
width: 100%;
- height: 31px;
- border-top: solid 1px #d3d3d3;
+ height: 32px;
+ background-color: #414141;
background-color: #646060;
background-image: -webkit-gradient(linear, left top, left bottom, from(#646060), to(#262626));
background-image: -webkit-linear-gradient(top, #646060, #262626);
@@ -464,191 +972,47 @@
background-image: -o-linear-gradient(top, #646060, #262626);
background-image: linear-gradient(to bottom, #646060, #262626);
}
-.openerp .oe_topbar .oe_topbar_item li {
- float: left;
-}
-.openerp .oe_topbar .oe_topbar_item li a {
+.openerp .oe_topbar .oe_topbar_item {
display: block;
padding: 5px 10px 7px;
line-height: 20px;
height: 20px;
+ text-decoration: none;
color: #eeeeee;
vertical-align: top;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
-}
-.openerp .oe_topbar .oe_topbar_item li a:hover {
- background: #303030;
- color: white;
- -moz-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset;
- -webkit-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset;
- -box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset;
-}
-.openerp .oe_topbar .oe_topbar_item .oe_active {
- background: #303030;
- font-weight: bold;
+ -webkit-transition: all 0.2s ease-out;
+ -moz-transition: all 0.2s ease-out;
+ -ms-transition: all 0.2s ease-out;
+ -o-transition: all 0.2s ease-out;
+ transition: all 0.2s ease-out;
+}
+.openerp .oe_topbar .oe_topbar_item:hover {
+ background: rgba(0, 0, 0, 0.2);
+ text-shadow: black 0px 0px 3px;
color: white;
- -moz-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset;
- -webkit-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset;
- -box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset;
+ -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4) inset;
+ -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4) inset;
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4) inset;
}
.openerp .oe_topbar .oe_topbar_avatar {
width: 24px;
height: 24px;
margin: -2px 2px 0 0;
- -moz-border-radius: 4px;
- -webkit-border-radius: 4px;
- border-radius: 4px;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ border-radius: 3px;
}
.openerp .oe_topbar .oe_topbar_avatar {
vertical-align: top;
}
-.openerp .oe_topbar .oe_dropdown_toggle:after {
- width: 0;
- height: 0;
- display: inline-block;
- content: "&darr";
- text-indent: -99999px;
- vertical-align: top;
- margin-top: 8px;
- margin-left: 4px;
- border-left: 4px solid transparent;
- border-right: 4px solid transparent;
+.openerp .oe_topbar .oe_dropdown_arrow:after {
border-top: 4px solid white;
- filter: alpha(opacity=50);
- opacity: 0.5;
-}
-.openerp .oe_leftbar {
- display: none;
- width: 220px;
- background: #f0eeee;
- border-right: 1px solid #afafb6;
- text-shadow: 0 1px 1px white;
- padding-bottom: 16px;
- line-height: 18px;
-}
-.openerp a.oe_logo {
- width: 220px;
- display: block;
- text-align: center;
- height: 70px;
- line-height: 70px;
-}
-.openerp a.oe_logo img {
- height: 40px;
- width: 157px;
- margin: 14px 0;
-}
-.openerp .oe_footer {
- position: fixed;
- bottom: 0;
- padding: 4px 0;
- background: #f0eeee;
- width: 220px;
- text-align: center;
-}
-.openerp .oe_footer a {
- font-weight: 800;
- font-family: serif;
- font-size: 16px;
- color: black;
-}
-.openerp .oe_footer a span {
- color: #c81010;
- font-style: italic;
-}
-.openerp .oe_user_menu {
- float: right;
- padding: 0;
- margin: 0;
-}
-.openerp .oe_user_menu li {
- list-style-type: none;
- float: left;
-}
-.openerp .oe_user_menu .oe_dropdown {
- position: relative;
}
-.openerp .oe_user_menu .oe_dropdown_options {
- float: left;
- background: #333333;
- background: rgba(37, 37, 37, 0.9);
- display: none;
- position: absolute;
+.openerp .oe_topbar .oe_dropdown_menu {
top: 32px;
- right: -1px;
- border: 0;
- z-index: 900;
- margin-left: 0;
- margin-right: 0;
- padding: 6px 0;
- zoom: 1;
- border-color: #999999;
- border-color: rgba(0, 0, 0, 0.2);
- border-style: solid;
- border-width: 0 1px 1px;
- -moz-border-radius: 0 0 6px 6px;
- -webkit-border-radius: 0 0 6px 6px;
- border-radius: 0 0 6px 6px;
- -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
- -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
- -box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
- -webkit-background-clip: padding-box;
- -moz-background-clip: padding-box;
- background-clip: padding-box;
-}
-.openerp .oe_user_menu .oe_dropdown_options li {
- float: none;
- display: block;
- background-color: none;
-}
-.openerp .oe_user_menu .oe_dropdown_options li a {
- white-space: nowrap;
- display: block;
- padding: 4px 15px;
- clear: both;
- font-weight: normal;
- line-height: 18px;
- color: #eeeeee;
-}
-.openerp .oe_user_menu .oe_dropdown_options li a:hover {
- background-color: #292929;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#292929), to(#191919));
- background-image: -webkit-linear-gradient(top, #292929, #191919);
- background-image: -moz-linear-gradient(top, #292929, #191919);
- background-image: -ms-linear-gradient(top, #292929, #191919);
- background-image: -o-linear-gradient(top, #292929, #191919);
- background-image: linear-gradient(to bottom, #292929, #191919);
- -moz-box-shadow: none;
- -webkit-box-shadow: none;
- -box-shadow: none;
-}
-.openerp .oe_user_menu .oe_dropdown_options li hr {
- border-top: 1px solid #999999;
- border-bottom: 0;
-}
-.openerp .oe_systray > div {
- float: left;
- padding: 0 4px 0 4px;
-}
-.openerp .oe_systray {
- float: right;
-}
-.openerp .oe_systray .oe_systray_dropdown {
- position: relative;
-}
-.openerp .oe_systray .oe_systray_dropdown:hover .oe_systray_dropdown_options {
- display: block;
-}
-.openerp .oe_systray .oe_systray_dropdown_options {
background: #333333;
background: rgba(37, 37, 37, 0.9);
- display: none;
- position: absolute;
- z-index: 900;
- border: 0;
- margin-left: 0;
- margin-right: 0;
- padding: 6px 0;
border-color: #999999;
border-color: rgba(0, 0, 0, 0.2);
border-style: solid;
@@ -656,27 +1020,18 @@
-moz-border-radius: 0 0 6px 6px;
-webkit-border-radius: 0 0 6px 6px;
border-radius: 0 0 6px 6px;
- -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
- -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
- -box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
-webkit-background-clip: padding-box;
-moz-background-clip: padding-box;
background-clip: padding-box;
}
-.openerp .oe_systray .oe_systray_dropdown_options li {
+.openerp .oe_topbar .oe_dropdown_menu li {
float: none;
- display: block;
- background-color: none;
+ padding: 3px 12px;
}
-.openerp .oe_systray .oe_systray_dropdown_options li a {
- display: block;
- padding: 4px 15px;
- clear: both;
- font-weight: normal;
- line-height: 18px;
+.openerp .oe_topbar .oe_dropdown_menu li a {
color: #eeeeee;
}
-.openerp .oe_systray .oe_systray_dropdown_options li a:hover {
+.openerp .oe_topbar .oe_dropdown_menu li:hover {
background-color: #292929;
background-image: -webkit-gradient(linear, left top, left bottom, from(#292929), to(#191919));
background-image: -webkit-linear-gradient(top, #292929, #191919);
@@ -686,62 +1041,114 @@
background-image: linear-gradient(to bottom, #292929, #191919);
-moz-box-shadow: none;
-webkit-box-shadow: none;
- -box-shadow: none;
+ box-shadow: none;
}
.openerp .oe_menu {
float: left;
padding: 0;
margin: 0;
+ font-size: 13px;
}
-.openerp .oe_menu li {
+.openerp .oe_menu > li {
+ list-style-type: none;
+ padding: 0;
+ margin: 0;
float: left;
+ display: block;
+ color: #eeeeee;
}
-.openerp .oe_menu a {
+.openerp .oe_menu > li > a {
display: block;
padding: 5px 10px 7px;
line-height: 20px;
height: 20px;
+ text-decoration: none;
color: #eeeeee;
vertical-align: top;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
-}
-.openerp .oe_menu a:hover {
- background: #303030;
+ -webkit-transition: all 0.2s ease-out;
+ -moz-transition: all 0.2s ease-out;
+ -ms-transition: all 0.2s ease-out;
+ -o-transition: all 0.2s ease-out;
+ transition: all 0.2s ease-out;
+}
+.openerp .oe_menu > li > a:hover {
+ background: rgba(0, 0, 0, 0.2);
+ text-shadow: black 0px 0px 3px;
color: white;
- -moz-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset;
- -webkit-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset;
- -box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset;
+ -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4) inset;
+ -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4) inset;
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4) inset;
}
-.openerp .oe_menu .oe_active {
- background: #303030;
+.openerp .oe_menu > li > .oe_active {
+ background: rgba(0, 0, 0, 0.2);
+ text-shadow: black 0px 0px 3px;
font-weight: bold;
- color: white;
- -moz-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset;
- -webkit-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset;
- -box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset;
-}
-.openerp .oe_menu_more_container {
- position: relative;
+ -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4) inset;
+ -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4) inset;
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4) inset;
}
-.openerp .oe_menu_more_container .oe_menu_more {
- position: absolute;
+.openerp .oe_user_menu {
+ float: right;
padding: 0;
- background-color: #646060;
- z-index: 1;
- border: 1px solid black;
- border-bottom-left-radius: 5px;
- border-bottom-right-radius: 5px;
+ margin: 0;
}
-.openerp .oe_menu_more_container .oe_menu_more li {
- float: none;
+.openerp .oe_user_menu li {
+ list-style-type: none;
+ float: left;
}
-.openerp .oe_menu_more_container .oe_menu_more li a {
- white-space: nowrap;
+.openerp .oe_user_menu .oe_dropdown_menu {
+ right: -1px;
+}
+.openerp .oe_systray > div {
+ float: left;
+ padding: 0 4px 0 4px;
+}
+.openerp .oe_systray {
+ float: right;
+}
+.openerp .oe_leftbar {
+ display: none;
+ width: 220px;
+ background: #f0eeee;
+ border-right: 1px solid #afafb6;
+ text-shadow: 0 1px 1px white;
+ padding-bottom: 16px;
+}
+.openerp a.oe_logo {
+ width: 220px;
+ display: block;
+ text-align: center;
+ height: 70px;
+ line-height: 70px;
+}
+.openerp a.oe_logo img {
+ height: 40px;
+ width: 157px;
+ margin: 14px 0;
+}
+.openerp .oe_footer {
+ position: fixed;
+ bottom: 0;
+ padding: 4px 0;
+ background: #f0eeee;
+ width: 220px;
+ text-align: center;
+}
+.openerp .oe_footer a {
+ font-weight: 800;
+ font-family: serif;
+ font-size: 16px;
+ color: black;
+}
+.openerp .oe_footer a span {
+ color: #c81010;
+ font-style: italic;
}
.openerp .oe_secondary_menu_section {
font-weight: bold;
margin-left: 8px;
- color: #8a89ba;
+ color: #7c7bad;
}
.openerp .oe_secondary_submenu {
padding: 2px 0 8px 0;
@@ -749,69 +1156,76 @@
width: 100%;
display: inline-block;
}
-.openerp .oe_secondary_submenu > li {
+.openerp .oe_secondary_submenu li {
position: relative;
- padding: 1px 0 1px 20px;
+ margin: 0;
+ padding: 1px 0 1px 20px !important;
+ list-style-type: none;
}
-.openerp .oe_secondary_submenu > li a {
+.openerp .oe_secondary_submenu li a {
display: block;
color: #4c4c4c;
padding: 2px 4px 2px 0;
}
-.openerp .oe_secondary_submenu > li .oe_menu_label {
+.openerp .oe_secondary_submenu li .oe_menu_label {
position: absolute;
top: 1px;
right: 1px;
font-size: 10px;
- background: #8a89ba;
+ background: #7c7bad;
color: white;
padding: 2px 4px;
margin: 1px 6px 0 0;
- border: 1px solid lightGray;
+ 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);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.2);
}
.openerp .oe_secondary_submenu .oe_menu_counter {
float: right;
- background: #8a89ba;
- color: #eeeeee;
- font-size: 12px;
- border: 1px solid lightgray;
- padding: 0px 4px 0px 4px;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
- margin: 0px;
- -moz-border-radius: 4px;
- -webkit-border-radius: 4px;
- border-radius: 4px;
+ margin: 1px;
+ padding: 1px 4px;
+ border: none;
+}
+.openerp .oe_secondary_submenu .oe_menu_counter:hover {
+ cursor: pointer;
+ -webkit-transform: scale(1.1);
+ -moz-transform: scale(1.1);
+ -ms-transform: scale(1.1);
+ -o-transform: scale(1.1);
+ transform: scale(1.1);
}
.openerp .oe_secondary_submenu .oe_active {
- background: #8a89ba;
- border-top: 1px solid lightGray;
- border-bottom: 1px solid lightGray;
+ 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 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);
+ -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: #8a89ba;
+ 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);
+ box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
}
.openerp .oe_secondary_submenu .oe_active .oe_menu_counter {
background: #eeeeee;
- color: #8a89ba;
+ 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;
@@ -837,190 +1251,153 @@
border-right: 4px solid transparent;
border-top: 4px solid #4c4c4c;
}
-.openerp .oe_application {
- width: 100%;
+.openerp .oe_about {
+ background-color: white;
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAGCAYAAADgzO9IAAAAKUlEQVQIHWO8e/fufwYsgAUkJigoiCIF5DMyoYggcUiXgNnBiGQKmAkARpcEQeriln4AAAAASUVORK5CYII=);
+ -moz-border-radius: 0 0 2px 2px;
+ -webkit-border-radius: 0 0 2px 2px;
+ border-radius: 0 0 2px 2px;
}
-.openerp .oe_application a {
- color: #8a89ba;
+.openerp .oe_about a {
+ color: #7c7bad;
}
-.openerp .oe_application a:hover {
+.openerp .oe_about a:hover {
text-decoration: underline;
}
-.openerp .oe-view-manager-header {
- border-top: 1px solid #cacaca;
- border-bottom: 1px solid #cacaca;
- background-color: #fcfcfc;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#fcfcfc), to(#dedede));
- background-image: -webkit-linear-gradient(top, #fcfcfc, #dedede);
- background-image: -moz-linear-gradient(top, #fcfcfc, #dedede);
- background-image: -ms-linear-gradient(top, #fcfcfc, #dedede);
- background-image: -o-linear-gradient(top, #fcfcfc, #dedede);
- background-image: linear-gradient(to bottom, #fcfcfc, #dedede);
- -moz-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.4), 0 0 9px rgba(0, 0, 0, 0.1);
- -webkit-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.4), 0 0 9px rgba(0, 0, 0, 0.1);
- -box-shadow: 0 1px 0 rgba(255, 255, 255, 0.4), 0 0 9px rgba(0, 0, 0, 0.1);
-}
-.openerp .oe-view-manager-header h2 {
- float: left;
- font-size: 18px;
- margin: 1px 0;
-}
-.openerp .oe-view-manager-header h2 a {
- color: #8a89ba;
-}
-.openerp .oe-view-manager-header .buttons {
- padding-right: 8px;
+.openerp .oe_about a:focus {
+ outline: none;
}
-.openerp .oe-view-manager-header .buttons li {
- float: left;
- margin-right: 12px;
+.openerp .oe_about .oe_logo {
+ margin-left: -6px;
}
-.openerp .oe-view-manager-header .filter {
- vertical-align: top;
+.openerp .oe_about .oe_bottom {
+ position: absolute;
+ top: 50%;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ text-shadow: 0 1px 1px #999999;
+ background-color: #b41616;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#b41616), to(#600606));
+ background-image: -webkit-linear-gradient(top, #b41616, #600606);
+ background-image: -moz-linear-gradient(top, #b41616, #600606);
+ background-image: -ms-linear-gradient(top, #b41616, #600606);
+ background-image: -o-linear-gradient(top, #b41616, #600606);
+ background-image: linear-gradient(to bottom, #b41616, #600606);
+ color: #eeeeee;
+ padding: 0 16px;
+ -moz-border-radius: 0 0 2px 2px;
+ -webkit-border-radius: 0 0 2px 2px;
+ border-radius: 0 0 2px 2px;
}
-.openerp .oe-view-manager-header .filter li {
- height: 24px;
- line-height: 24px;
- padding: 0 8px;
+.openerp .oe_about .oe_bottom a {
+ color: #eeeeee;
}
-.openerp .oe-view-manager-header .header-row {
- height: 26px;
- line-height: 26px;
- margin: 8px;
- clear: both;
- text-shadow: 0 1px 1px white;
+.openerp .oe_application {
+ width: 100%;
}
-.openerp .oe-view-manager-header .header-row .dropdown-toggle:after {
- margin-top: 7px;
- margin-left: 6px;
- border-top-color: #404040;
+.openerp .oe_application a {
+ color: #7c7bad;
}
-.openerp .oe-view-manager-header .oe_vm_switch li {
- padding-left: 1px;
- text-align: center;
- width: 24px;
- height: 24px;
- line-height: 24px;
+.openerp .oe_application a:hover {
+ text-decoration: underline;
}
-.openerp .oe-view-manager-header .pagination li {
- height: 24px;
- line-height: 24px;
- padding: 0 8px;
+.openerp .oe_application > div {
+ height: 100%;
}
-.openerp .oe-view-manager-header .button-group {
+.openerp .oe_application .oe_breadcrumb_item:not(:last-child) {
display: inline-block;
- border: 1px solid #ababab;
- -moz-border-radius: 5px;
- -webkit-border-radius: 5px;
- border-radius: 5px;
-}
-.openerp .oe-view-manager-header .button-group li {
- float: left;
- border-right: 1px solid #ababab;
-}
-.openerp .oe-view-manager-header .button-group li:last-child {
- border: none;
+ max-width: 7em;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
}
-.openerp .oe-view-manager-header .button-group a {
- color: #4c4c4c;
+.openerp .oe_view_manager .oe_view_manager_body {
+ height: inherit;
}
-.openerp .oe-view-manager-header .button-group a:hover {
- text-decoration: none;
+.openerp .oe_view_manager .oe_view_manager_view_kanban {
+ height: inherit;
}
-.openerp .oe-view-manager-header .button-group .active {
+.openerp .oe_view_manager table.oe_view_manager_header {
width: 100%;
- background: #999999;
- -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3) inset;
- -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3) inset;
- -box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3) inset;
- color: white;
- text-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
+ table-layout: fixed;
}
-.openerp .oe_view_manager_header {
- width: 100%;
- border-top: 1px solid #cacaca;
- border-bottom: 1px solid #cacaca;
- background-color: #fcfcfc;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#fcfcfc), to(#dedede));
- background-image: -webkit-linear-gradient(top, #fcfcfc, #dedede);
- background-image: -moz-linear-gradient(top, #fcfcfc, #dedede);
- background-image: -ms-linear-gradient(top, #fcfcfc, #dedede);
- background-image: -o-linear-gradient(top, #fcfcfc, #dedede);
- background-image: linear-gradient(to bottom, #fcfcfc, #dedede);
- -moz-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.4), 0 0 9px rgba(0, 0, 0, 0.1);
- -webkit-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.4), 0 0 9px rgba(0, 0, 0, 0.1);
- -box-shadow: 0 1px 0 rgba(255, 255, 255, 0.4), 0 0 9px rgba(0, 0, 0, 0.1);
-}
-.openerp .oe_view_manager_header .oe_header_row {
+.openerp .oe_view_manager table.oe_view_manager_header .oe_header_row {
clear: both;
text-shadow: 0 1px 1px white;
}
-.openerp .oe_view_manager_header .oe_header_row td {
- padding: 8px;
-}
-.openerp .oe_view_manager_header .oe_header_row:last-child td {
+.openerp .oe_view_manager table.oe_view_manager_header .oe_header_row:last-child td {
padding-top: 0;
}
-.openerp .oe_view_manager_header .oe_header_row:first-child td {
+.openerp .oe_view_manager table.oe_view_manager_header .oe_header_row:first-child td {
padding-top: 8px;
}
-.openerp .oe_view_manager_header .oe_view_manager_sidebar {
+.openerp .oe_view_manager table.oe_view_manager_header .oe_view_manager_sidebar {
margin: 0px auto;
- width: 400px;
text-align: center;
}
-.openerp .oe_view_manager_header td {
+.openerp .oe_view_manager table.oe_view_manager_header td {
line-height: 26px;
}
-.openerp .oe_view_manager_header h2 {
+.openerp .oe_view_manager table.oe_view_manager_header h2 {
font-size: 18px;
margin: 0;
float: left;
}
-.openerp .oe_view_manager_header h2 a {
- color: #8a89ba;
+.openerp .oe_view_manager table.oe_view_manager_header h2 a {
+ color: #7c7bad;
+}
+.openerp .oe_view_manager table.oe_view_manager_header .oe_dropdown_menu {
+ line-height: normal;
}
-.openerp .oe_view_manager_header .oe_button_group {
+.openerp .oe_view_manager table.oe_view_manager_header .oe_button_group {
display: inline-block;
border: 1px solid #ababab;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
border-radius: 5px;
}
-.openerp .oe_view_manager_header .oe_button_group li {
+.openerp .oe_view_manager table.oe_view_manager_header .oe_button_group li {
float: left;
border-right: 1px solid #ababab;
}
-.openerp .oe_view_manager_header .oe_button_group li:last-child {
+.openerp .oe_view_manager table.oe_view_manager_header .oe_button_group li:last-child {
border: none;
}
-.openerp .oe_view_manager_header .oe_button_group a {
+.openerp .oe_view_manager table.oe_view_manager_header .oe_button_group a {
color: #4c4c4c;
}
-.openerp .oe_view_manager_header .oe_button_group a:hover {
+.openerp .oe_view_manager table.oe_view_manager_header .oe_button_group a:hover {
text-decoration: none;
}
-.openerp .oe_view_manager_header .oe_button_group .active {
+.openerp .oe_view_manager table.oe_view_manager_header .oe_button_group .active {
background: #999999;
-moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3) inset;
-webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3) inset;
- -box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3) inset;
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3) inset;
}
-.openerp .oe_view_manager_header .oe_button_group .active a {
+.openerp .oe_view_manager table.oe_view_manager_header .oe_button_group .active a {
color: white;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
}
-.openerp .oe_view_manager_header .oe_view_manager_buttons {
+.openerp .oe_view_manager table.oe_view_manager_header .oe_view_manager_buttons {
white-space: nowrap;
}
-.openerp .oe_view_manager_pager {
+.openerp .oe_view_manager .oe_view_manager_pager {
line-height: 26px;
}
-.openerp .oe_pager_value {
+.openerp .oe_view_manager .oe_view_manager_pager .oe_list_pager_single_page .oe_pager_group {
+ display: none;
+}
+.openerp .oe_view_manager .oe_pager_value {
float: left;
margin-right: 8px;
}
-.openerp .oe_pager_group {
+.openerp .oe_view_manager ul.oe_pager_group {
+ padding: 0;
+ margin: 0;
+}
+.openerp .oe_view_manager .oe_pager_group {
float: left;
height: 24px;
line-height: 24px;
@@ -1031,47 +1408,59 @@
-webkit-border-radius: 5px;
border-radius: 5px;
}
-.openerp .oe_pager_group li {
+.openerp .oe_view_manager .oe_pager_group li {
height: 24px;
line-height: 24px;
padding: 0;
+ margin: 0;
+ list-style-type: none;
float: left;
border-right: 1px solid #ababab;
}
-.openerp .oe_pager_group li:last-child {
+.openerp .oe_view_manager .oe_pager_group li:last-child {
border: none;
}
-.openerp .oe_pager_group a {
+.openerp .oe_view_manager .oe_pager_group a {
color: #4c4c4c;
padding: 0 8px;
}
-.openerp .oe_pager_group a:hover {
+.openerp .oe_view_manager .oe_pager_group a:hover {
text-decoration: none;
}
-.openerp .oe_pager_group .active {
+.openerp .oe_view_manager .oe_pager_group .active {
background: #999999;
-moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3) inset;
-webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3) inset;
- -box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3) inset;
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3) inset;
}
-.openerp .oe_pager_group .active a {
+.openerp .oe_view_manager .oe_pager_group .active a {
color: white;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
}
-.openerp .oe_view_manager_switch li {
- text-align: center;
+.openerp .oe_view_manager .oe_view_manager_switch {
+ padding: 0;
+ margin: 0 0 0 8px;
+}
+.openerp .oe_view_manager .oe_view_manager_switch li {
+ margin: 0;
width: 24px;
height: 24px;
line-height: 16px;
+ padding: 0;
+ text-align: center;
+ list-style-type: none;
}
-.openerp .oe_view_manager_switch li a {
+.openerp .oe_view_manager .oe_view_manager_switch li a {
position: relative;
}
-.openerp .oe_view_manager_switch .oe_vm_switch_list:after, .openerp .oe_view_manager_switch .oe_vm_switch_tree:after {
+.openerp .oe_view_manager .oe_view_manager_switch .oe_vm_switch_list:after, .openerp .oe_view_manager .oe_view_manager_switch .oe_vm_switch_tree:after {
padding: 2px;
content: "i";
}
-.openerp .oe_view_manager_switch .oe_vm_switch_graph:after {
+.openerp .oe_view_manager .oe_view_manager_switch .oe_vm_switch_form:after {
+ content: "m";
+}
+.openerp .oe_view_manager .oe_view_manager_switch .oe_vm_switch_graph:after {
font-family: "mnmliconsRegular" !important;
font-size: 21px;
font-weight: 300 !important;
@@ -1079,7 +1468,7 @@
top: -2px;
position: relative;
}
-.openerp .oe_view_manager_switch .oe_vm_switch_gantt:after {
+.openerp .oe_view_manager .oe_view_manager_switch .oe_vm_switch_gantt:after {
font-family: "mnmliconsRegular" !important;
font-size: 21px;
font-weight: 300 !important;
@@ -1087,85 +1476,45 @@
top: -2px;
position: relative;
}
-.openerp .oe_view_manager_switch .oe_vm_switch_calendar:after {
+.openerp .oe_view_manager .oe_view_manager_switch .oe_vm_switch_calendar:after {
content: "P";
}
-.openerp .oe_view_manager_switch .oe_vm_switch_kanban:after {
+.openerp .oe_view_manager .oe_view_manager_switch .oe_vm_switch_kanban:after {
content: "k";
}
-.openerp .oe_view_manager_switch .oe_vm_switch_diagram:after {
+.openerp .oe_view_manager .oe_view_manager_switch .oe_vm_switch_diagram:after {
content: "f";
}
-.openerp .oe_form_dropdown_section {
- position: relative;
- display: inline-block;
+.openerp .oe_view_manager_current {
+ height: 100%;
}
-.openerp .oe_dropdown_toggle:after {
- width: 0;
- height: 0;
- display: inline-block;
- content: "&darr";
- text-indent: -99999px;
- vertical-align: top;
- border-left: 4px solid transparent;
- border-right: 4px solid transparent;
- border-top: 4px solid white;
- filter: alpha(opacity=50);
- opacity: 0.5;
- margin-top: 7px;
- margin-left: 6px;
- border-top-color: #404040;
+.openerp .oe_view_manager_current > .oe_view_manager_header {
+ border-bottom: 1px solid #cacaca;
+ background-color: #fcfcfc;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#fcfcfc), to(#dedede));
+ background-image: -webkit-linear-gradient(top, #fcfcfc, #dedede);
+ background-image: -moz-linear-gradient(top, #fcfcfc, #dedede);
+ background-image: -ms-linear-gradient(top, #fcfcfc, #dedede);
+ background-image: -o-linear-gradient(top, #fcfcfc, #dedede);
+ background-image: linear-gradient(to bottom, #fcfcfc, #dedede);
+ -moz-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.4), 0 0 9px rgba(0, 0, 0, 0.1);
+ -webkit-box-shadow: 0 1px 0 rgba(255, 255, 255, 0.4), 0 0 9px rgba(0, 0, 0, 0.1);
+ box-shadow: 0 1px 0 rgba(255, 255, 255, 0.4), 0 0 9px rgba(0, 0, 0, 0.1);
}
-.openerp .oe_dropdown_menu {
- display: none;
- position: absolute;
- top: 28px;
- left: 0px;
+.openerp .oe_view_manager_current > .oe_view_manager_header .oe_header_row td {
padding: 8px;
- border: 1px solid #afafb6;
- min-width: 120px;
- overflow-x: hidden;
- z-index: 900;
- text-align: left;
- background: white;
- -moz-border-radius: 3px;
- -webkit-border-radius: 3px;
- border-radius: 3px;
- -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
- -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
- -box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
}
-.openerp .oe_dropdown_menu li {
- list-style-type: none;
- float: none;
- display: block;
- background-color: none;
+.openerp .oe_view_manager_current > .oe_view_manager_header .oe_header_row:first-child td {
+ padding-top: 8px;
}
-.openerp .oe_dropdown_menu li a {
- display: block;
- padding: 3px 6px;
- clear: both;
- font-weight: normal;
- line-height: 14px;
- color: #4c4c4c;
- text-decoration: none;
- cursor: pointer;
+.openerp .oe_view_manager_inline, .openerp .oe_view_manager_inlineview {
+ height: 100%;
}
-.openerp .oe_dropdown_menu li a:hover {
- text-decoration: none;
- background-color: #f0f0fa;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#f0f0fa), to(#eeeef6));
- background-image: -webkit-linear-gradient(top, #f0f0fa, #eeeef6);
- background-image: -moz-linear-gradient(top, #f0f0fa, #eeeef6);
- background-image: -ms-linear-gradient(top, #f0f0fa, #eeeef6);
- background-image: -o-linear-gradient(top, #f0f0fa, #eeeef6);
- background-image: linear-gradient(to bottom, #f0f0fa, #eeeef6);
- -moz-box-shadow: none;
- -webkit-box-shadow: none;
- -box-shadow: none;
+.openerp .oe_view_manager_inline > .oe_view_manager_header, .openerp .oe_view_manager_inlineview > .oe_view_manager_header {
+ display: none;
}
-.openerp .oe_sidebar {
- white-space: nowrap;
+.openerp .oe_popup_form > .oe_formview > .oe_form_pager {
+ display: none !important;
}
.openerp .oe_searchview {
cursor: text;
@@ -1173,7 +1522,7 @@
float: right;
padding: 1px 0;
line-height: 18px;
- width: 480px;
+ width: 400px;
border: 1px solid #ababab;
background: white;
-moz-border-radius: 13px;
@@ -1181,23 +1530,19 @@
border-radius: 13px;
-moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2) inset;
-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;
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2) inset;
}
.openerp .oe_searchview input, .openerp .oe_searchview textarea {
padding: 3px;
height: 14px;
- font-size: 11px;
+ font-size: 12px;
line-height: 18px;
- border: 1px solid #cccccc;
- -moz-border-radius: 3px;
- -webkit-border-radius: 3px;
- border-radius: 3px;
}
.openerp .oe_searchview.oe_focused {
border-color: #a6a6fe;
-moz-box-shadow: 0 1px 2px #a6a6fe inset;
-webkit-box-shadow: 0 1px 2px #a6a6fe inset;
- -box-shadow: 0 1px 2px #a6a6fe inset;
+ box-shadow: 0 1px 2px #a6a6fe inset;
}
.openerp .oe_searchview .oe_searchview_clear {
cursor: pointer;
@@ -1235,52 +1580,66 @@
filter: alpha(opacity=50);
opacity: 0.5;
}
-.openerp .oe_searchview .oe_searchview_facets {
- min-height: 22px;
+.openerp .oe_searchview .oe_searchview_search {
+ font-size: 1px;
+ letter-spacing: -1px;
+ color: transparent;
+ font-weight: normal;
+ -moz-box-shadow: none;
+ -webkit-box-shadow: none;
+ box-shadow: none;
+ -moz-border-radius: 0;
+ -webkit-border-radius: 0;
+ border-radius: 0;
+ position: absolute;
+ left: 3px;
+ top: 1px;
+ padding: 0;
+ border: none;
+ background: transparent;
}
-.openerp .oe_searchview .oe_searchview_facets:before {
- color: #cccccc;
- font-family: "mnmliconsRegular";
+.openerp .oe_searchview .oe_searchview_search:before {
+ font: 21px "mnmliconsRegular";
content: "r";
- font-size: 130%;
- display: inline;
- position: relative;
- left: 6px;
- top: 2px;
color: #a3a3a3;
- padding-right: 4px;
+}
+.openerp .oe_searchview .oe_searchview_facets {
+ min-height: 22px;
+ margin-left: 15px;
}
.openerp .oe_searchview .oe_searchview_facets * {
vertical-align: top;
display: inline-block;
line-height: 17px;
}
-.openerp .oe_searchview .oe_searchview_facets .oe_searchview_input, .openerp .oe_searchview .oe_searchview_facets .oe_searchview_facet {
+.openerp .oe_searchview .oe_searchview_facets .oe_searchview_facet {
height: 18px;
margin: 1px 0;
font-size: 11px;
}
-.openerp .oe_searchview .oe_searchview_facets .oe_searchview_input:focus, .openerp .oe_searchview .oe_searchview_facets .oe_searchview_facet:focus {
+.openerp .oe_searchview .oe_searchview_facets .oe_searchview_facet:focus {
outline: none;
}
.openerp .oe_searchview .oe_searchview_facets .oe_searchview_input {
- padding: 0 3px;
+ padding: 0 0 0 6px;
+ font-size: 12px;
+ height: 16px;
+ margin-top: 3px;
+}
+.openerp .oe_searchview .oe_searchview_facets .oe_searchview_input:focus {
+ outline: none;
}
.openerp .oe_searchview .oe_searchview_facets .oe_searchview_facet {
position: relative;
cursor: pointer;
- border: 1px solid #afafb6;
- -moz-border-radius: 3px;
- -webkit-border-radius: 3px;
- border-radius: 3px;
- background: #8786b7;
+ padding: 0;
-webkit-font-smoothing: auto;
}
.openerp .oe_searchview .oe_searchview_facets .oe_searchview_facet:focus {
border-color: #a6a6fe;
-moz-box-shadow: 0 0 3px 1px #a6a6fe;
-webkit-box-shadow: 0 0 3px 1px #a6a6fe;
- -box-shadow: 0 0 3px 1px #a6a6fe;
+ box-shadow: 0 0 3px 1px #a6a6fe;
}
.openerp .oe_searchview .oe_searchview_facets .oe_searchview_facet .oe_facet_values {
background: #f0f0fa;
@@ -1302,6 +1661,7 @@
.openerp .oe_searchview .oe_searchview_facets .oe_searchview_facet .oe_facet_value {
border-left: 1px solid #afafb6;
text-shadow: 0 1px 1px white;
+ color: #4c4c4c;
}
.openerp .oe_searchview .oe_searchview_facets .oe_searchview_facet .oe_facet_value:last-child {
padding-right: 16px;
@@ -1314,6 +1674,7 @@
line-height: 8px;
width: 12px;
height: 12px;
+ padding-top: 1px;
text-align: center;
font-weight: bold;
cursor: pointer;
@@ -1332,7 +1693,7 @@
}
.openerp .oe_searchview .oe_searchview_drawer {
position: absolute;
- z-index: 1;
+ z-index: 100;
margin-top: 4px;
top: 100%;
right: -1px;
@@ -1346,7 +1707,7 @@
border-radius: 4px;
-moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
-webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
- -box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
}
.openerp .oe_searchview .oe_searchview_drawer > div {
border-top: 1px solid #cccccc;
@@ -1359,7 +1720,7 @@
}
.openerp .oe_searchview .oe_searchview_drawer h3 {
margin: 8px 4px 4px 12px;
- color: #8786b7;
+ color: #7c7bad;
font-size: 13px;
}
.openerp .oe_searchview .oe_searchview_drawer h4, .openerp .oe_searchview .oe_searchview_drawer h4 * {
@@ -1384,7 +1745,7 @@
padding: 0 2px;
-moz-box-shadow: none;
-webkit-box-shadow: none;
- -box-shadow: none;
+ box-shadow: none;
-moz-border-radius: 0;
-webkit-border-radius: 0;
border-radius: 0;
@@ -1394,8 +1755,9 @@
width: 100%;
}
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_section > div {
- -moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ -ms-box-sizing: border-box;
box-sizing: border-box;
display: table-cell;
width: 50%;
@@ -1426,14 +1788,14 @@
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_section li:hover {
background-color: #f0f0fa;
}
-.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_section form {
+.openerp .oe_searchview .oe_searchview_drawer form {
margin-left: 12px;
}
-.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_section form p {
+.openerp .oe_searchview .oe_searchview_drawer form p {
margin: 4px 0;
line-height: 18px;
}
-.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_section form button {
+.openerp .oe_searchview .oe_searchview_drawer form button {
margin: 0 0 8px 0;
}
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_custom {
@@ -1456,6 +1818,10 @@
top: 0;
right: 5px;
}
+.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_dashboard form {
+ display: none;
+ margin-top: 2px;
+}
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_advanced form {
display: none;
margin-top: 8px;
@@ -1485,14 +1851,12 @@
.openerp .oe_searchview .oe_searchview_drawer .oe_opened form {
display: block;
}
-.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_custom_delete {
- position: absolute;
- left: 4px;
- top: 3px;
-}
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_custom_delete, .openerp .oe_searchview .oe_searchview_drawer .searchview_extended_delete_prop {
+ display: inline-block;
width: 12px;
height: 12px;
+ line-height: 12px;
+ padding: 1px;
color: #8786b7;
line-height: 8px;
text-align: center;
@@ -1508,55 +1872,147 @@
-webkit-border-radius: 2px;
border-radius: 2px;
}
-.openerp .oe_view_nocontent > img {
- float: left;
- margin: 1.5em;
+.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_custom_delete {
+ display: none;
+ position: absolute;
+ bottom: 1px;
+ right: 4px;
}
-.openerp .oe_view_nocontent > div {
- overflow: hidden;
- padding: 35px 0px 0px 0px;
- max-width: 700px;
+.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_custom_private:hover .oe_searchview_custom_delete, .openerp .oe_searchview .oe_searchview_drawer .oe_searchview_custom_public:hover .oe_searchview_custom_delete {
+ display: inline-block;
+}
+.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_custom_public:after {
+ content: ",";
+ font-family: "entypoRegular" !important;
+ font-size: 22px;
+ font-weight: 300 !important;
+ margin: 0 0 0 4px;
+ padding: 0;
+}
+.openerp .oe_view_nocontent {
+ padding: 15px;
+ margin-top: 0;
+ color: #777777;
font-size: 125%;
+ max-width: 700px;
}
-.openerp .oe_view_topbar {
- border-bottom: 1px solid #cacaca;
- background-color: #fcfcfc;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#fcfcfc), to(#dedede));
- background-image: -webkit-linear-gradient(top, #fcfcfc, #dedede);
- background-image: -moz-linear-gradient(top, #fcfcfc, #dedede);
- background-image: -ms-linear-gradient(top, #fcfcfc, #dedede);
- background-image: -o-linear-gradient(top, #fcfcfc, #dedede);
- background-image: linear-gradient(to bottom, #fcfcfc, #dedede);
- padding: 0 8px;
- line-height: 30px;
+.openerp .oe_view_nocontent .oe_view_nocontent_create {
+ background: transparent url(/web/static/src/img/view_empty_arrow.png) no-repeat 7px 0;
+ margin-top: 0;
+ padding-top: 35px;
+ min-height: 28px;
+ color: #4c4c4c;
+}
+.openerp .oe_view_nocontent > p {
+ padding-left: 95px;
+}
+.openerp .oe_view_nocontent .oe_empty_custom_dashboard {
+ background: transparent url(/web/static/src/img/graph_background.png) no-repeat 0 0;
+ margin-top: -15px;
+ padding: 100px 0 0 137px;
+ min-height: 327px;
+ margin-left: -15px;
}
.openerp .oe_formview {
background: white;
}
-.openerp .oe_form_header {
- padding: 8px;
+.openerp .oe_form_dropdown_section {
+ position: relative;
+ display: inline-block;
}
.openerp .oe_form_invalid input, .openerp .oe_form_invalid select, .openerp .oe_form_invalid textarea {
background-color: #ff6666 !important;
border: 1px solid #dd0000 !important;
}
-.openerp .oe_form_button_save_dirty {
- color: white;
- background: #dc5f59;
- background: -moz-linear-gradient(#dc5f59, #b33630);
- background: -webkit-gradient(linear, left top, left bottom, from(#dc5f59), to(#b33630));
- background: -webkit-linear-gradient(#dc5f59, #b33630);
+.openerp .oe_view_manager_current .oe_form_editable .oe_highlight {
+ color: #404040;
+ background: none;
+}
+.openerp .oe_view_manager_current .oe_form_editable button.oe_highlight {
+ background-color: #efefef;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#efefef), to(#d8d8d8));
+ background-image: -webkit-linear-gradient(top, #efefef, #d8d8d8);
+ background-image: -moz-linear-gradient(top, #efefef, #d8d8d8);
+ background-image: -ms-linear-gradient(top, #efefef, #d8d8d8);
+ background-image: -o-linear-gradient(top, #efefef, #d8d8d8);
+ background-image: linear-gradient(to bottom, #efefef, #d8d8d8);
+ -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(255, 255, 255, 0.8) inset;
+ -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(255, 255, 255, 0.8) inset;
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(255, 255, 255, 0.8) inset;
+}
+.openerp .oe_view_manager_current .oe_form_editable button.oe_highlight:active {
+ background-color: #e3e3e3;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#e3e3e3), to(#f6f6f6));
+ background-image: -webkit-linear-gradient(top, #e3e3e3, #f6f6f6);
+ background-image: -moz-linear-gradient(top, #e3e3e3, #f6f6f6);
+ background-image: -ms-linear-gradient(top, #e3e3e3, #f6f6f6);
+ background-image: -o-linear-gradient(top, #e3e3e3, #f6f6f6);
+ background-image: linear-gradient(to bottom, #e3e3e3, #f6f6f6);
-moz-box-shadow: none;
-webkit-box-shadow: none;
- -box-shadow: none;
- font-weight: bold;
+ box-shadow: none;
}
-.openerp .oe_form_button_save_dirty:hover {
- background: #ed6f6a;
+.openerp .oe_view_manager_current .oe_form_editable button.oe_highlight:hover {
+ background-color: #f6f6f6;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#f6f6f6), to(#e3e3e3));
+ background-image: -webkit-linear-gradient(top, #f6f6f6, #e3e3e3);
+ background-image: -moz-linear-gradient(top, #f6f6f6, #e3e3e3);
+ background-image: -ms-linear-gradient(top, #f6f6f6, #e3e3e3);
+ background-image: -o-linear-gradient(top, #f6f6f6, #e3e3e3);
+ background-image: linear-gradient(to bottom, #f6f6f6, #e3e3e3);
+ -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(255, 255, 255, 0.8) inset;
+ -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(255, 255, 255, 0.8) inset;
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(255, 255, 255, 0.8) inset;
+}
+.openerp .oe_form_invisible {
+ display: none !important;
+}
+.openerp .oe_form_readonly .oe_edit_only, .openerp .oe_form_readonly .oe_form_field:empty {
+ display: none !important;
+}
+.openerp .oe_form_readonly .oe_form .oe_form_field_date {
+ width: auto;
+}
+.openerp .oe_form_nosheet {
+ margin: 16px;
+}
+.openerp .oe_form_nosheet > header {
+ margin: -16px -16px 0 -16px;
+ padding: 0;
+}
+.openerp .oe_form_sheetbg {
+ padding: 16px 0;
+}
+.openerp .oe_form_sheet_width {
+ min-width: 650px;
+ max-width: 860px;
+ margin: 0 auto;
+}
+.openerp .oe_form_sheet {
+ background: white;
+ min-height: 330px;
+ padding: 16px;
+}
+.openerp .oe_application .oe_form_sheetbg {
+ background: url(/web/static/src/img/form_sheetbg.png);
+ border-bottom: 1px solid #dddddd;
+}
+.openerp .oe_application .oe_form_sheet {
+ border: 1px solid #c8c8d3;
+ -moz-box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
+ -webkit-box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
+}
+.openerp .oe_application .oe_form_sheet .ui-tabs {
+ margin: 0 -16px;
+}
+.openerp .oe_application .oe_form_sheet .oe_notebook_page {
+ padding: 0 16px;
}
.openerp .oe_form header {
position: relative;
border-bottom: 1px solid #cacaca;
+ padding-left: 2px;
background-color: #fcfcfc;
background-image: -webkit-gradient(linear, left top, left bottom, from(#fcfcfc), to(#dedede));
background-image: -webkit-linear-gradient(top, #fcfcfc, #dedede);
@@ -1564,146 +2020,95 @@
background-image: -ms-linear-gradient(top, #fcfcfc, #dedede);
background-image: -o-linear-gradient(top, #fcfcfc, #dedede);
background-image: linear-gradient(to bottom, #fcfcfc, #dedede);
- padding: 0 8px;
- line-height: 30px;
-}
-.openerp .oe_form header button {
- font-size: 12px;
- float: left;
- height: 24px !important;
- line-height: 24px;
- vertical-align: top;
- padding: 0 10px;
- margin: 3px 4px 3px 0;
}
-.openerp .oe_form header button span.i {
- line-height: 20px;
- height: 24px;
+.openerp .oe_form header > span {
+ margin-left: 4px;
}
.openerp .oe_form header ul {
- height: 30px;
- padding: 0;
- margin: 0;
- text-shadow: 0 1px 1px white;
- border-left: 1px solid #cacaca;
- border-right: 1px solid #cacaca;
-}
-.openerp .oe_form header ul li {
- padding: 0;
- margin: 0;
- float: left;
- vertical-align: top;
- border-right: 1px solid #cacaca;
- height: 30px;
- padding: 0 12px;
-}
-.openerp .oe_form header ul li:first-child {
- border-left: 1px solid #cacaca;
-}
-.openerp .oe_form header ul li a {
- color: #4c4c4c;
-}
-.openerp .oe_form header ul li a:hover {
- color: black;
-}
-.openerp .oe_form header .oe_form_steps {
display: inline-block;
float: right;
}
-.openerp .oe_form header .oe_form_steps img {
- margin: 0 8px;
- vertical-align: top;
+.openerp .oe_form header .oe_form_button {
+ margin: 3px 2px 1px;
}
-.openerp .oe_form header .oe_form_steps li {
- border-right: none;
- padding: 0;
+.openerp .oe_form header .oe_tags {
+ margin: 5px 0 0 5px;
+ width: 400px;
+ padding-bottom: 0;
}
-.openerp .oe_form header .oe_form_steps li:first-child {
- margin-left: 12px;
- border-left: none;
+.openerp .oe_form div.oe_chatter {
+ min-width: 650px;
+ max-width: 860px;
+ margin: 0 auto;
+ padding: 16px 0 48px;
}
-.openerp .oe_form header .oe_form_steps li:last-child {
- margin-right: 12px;
+.openerp .oe_form .oe_grey {
+ color: #aaaaaa;
+ max-width: 650px;
+ margin: 0 0 10px 0;
}
-.openerp .oe_form header .oe_form_steps_active {
- font-weight: bold;
- color: #b33630;
+.openerp .oe_form div.oe_form_configuration p, .openerp .oe_form div.oe_form_configuration ul, .openerp .oe_form div.oe_form_configuration ol {
+ color: #aaaaaa;
+ max-width: 650px;
}
-.openerp .oe_form_readonly button.oe_form_button_hi {
- color: white;
- background: #dc5f59;
- background: -moz-linear-gradient(#dc5f59, #b33630);
- background: -webkit-gradient(linear, left top, left bottom, from(#dc5f59), to(#b33630));
- background: -webkit-linear-gradient(#dc5f59, #b33630);
- -moz-box-shadow: none;
- -webkit-box-shadow: none;
- -box-shadow: none;
+.openerp .oe_form div.oe_form_configuration label {
+ min-width: 150px;
}
-.openerp .oe_form_readonly button.oe_form_button_hi:hover {
- background: #df6b66;
- background: -moz-linear-gradient(#df6b66, #bf3a33);
- background: -webkit-gradient(linear, left top, left bottom, from(#df6b66), to(#bf3a33));
- background: -webkit-linear-gradient(#df6b66, #bf3a33);
- -moz-box-shadow: 0 0 1px rgba(0, 0, 0, 0.2);
- -webkit-box-shadow: 0 0 1px rgba(0, 0, 0, 0.2);
- -box-shadow: 0 0 1px rgba(0, 0, 0, 0.2);
+.openerp .oe_form div.oe_form_configuration .oe_form_group_cell_label {
+ padding: 1px 0;
}
-.openerp .oe_application .oe_form_sheetbg {
- background: url(/web/static/src/img/form_sheetbg.png);
- padding: 8px 0;
- border-bottom: 1px solid #dddddd;
+.openerp .oe_form div.oe_form_configuration .oe_form_group_cell div div {
+ padding: 1px 0;
}
-.openerp .oe_application .oe_form_sheet_width, .openerp .oe_application footer {
- min-width: 650px;
- max-width: 960px;
- margin: 0 auto;
+.openerp .oe_form .oe_subtotal_footer {
+ width: 1% !important;
}
-.openerp .oe_application .oe_form_sheet {
- padding: 18px;
- background: white;
- min-height: 330px;
- border: 1px solid #afafb6;
- -moz-box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
- -webkit-box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
- -box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
+.openerp .oe_form .oe_subtotal_footer td.oe_form_group_cell {
+ text-align: right;
+ padding: 0 !important;
}
-.openerp .oe_form_invisible {
- display: none;
+.openerp .oe_form .oe_subtotal_footer td.oe_form_group_cell_label {
+ border-right: none;
}
-.openerp .oe_form_editable .oe_form_editable_hidden {
- display: none;
+.openerp .oe_form .oe_subtotal_footer .oe_subtotal_footer_separator {
+ width: 108px;
+ border-top: 1px solid #cacaca;
+ margin-top: 4px;
+ padding-top: 4px;
+ font-weight: bold;
+ font-size: 18px;
}
-.openerp .oe_form_readonly .oe_form_readonly_hidden, .openerp .oe_form_readonly .oe_form_field:empty {
- display: none;
+.openerp .oe_form .oe_subtotal_footer label:after {
+ content: ":";
}
-.openerp .oe_form .oe_form_field_text {
- width: 100%;
+.openerp .oe_form .oe_subtotal_footer label.oe_subtotal_footer_separator {
+ font-weight: bold !important;
+ padding: 2px 11px 2px 0px !important;
}
-.openerp .oe_form .oe_form_field_char input,
-.openerp .oe_form .oe_form_field_text textarea,
-.openerp .oe_form .oe_form_field_url input,
-.openerp .oe_form .oe_form_field_selection select {
- width: inherit;
+.openerp .oe_form .oe_subtotal_footer label.oe_form_label_help {
+ font-weight: normal;
}
-.openerp .oe_form .oe_form_field_float input {
- width: 100px;
+.openerp .oe_form .oe_form_button {
+ margin: 2px;
}
-.openerp .oe_form h1 .oe_form_field_float input {
- width: 140px;
+.openerp .oe_form td.oe_form_group_cell_label {
+ border-right: 1px solid #dddddd;
+ padding: 2px 0px;
}
-.openerp .oe_form h2 .oe_form_field_float input {
- width: 120px;
+.openerp .oe_form td.oe_form_group_cell_label label {
+ line-height: 18px;
+ display: block;
+ min-width: 140px;
}
-.openerp .oe_form h1, .openerp .oe_form h2, .openerp .oe_form h3, .openerp .oe_form h4, .openerp .oe_form h5, .openerp .oe_form h6 {
- margin: 0 0 4px 0;
+.openerp .oe_form td.oe_form_group_cell + .oe_form_group_cell {
+ padding: 2px 0 2px 8px;
}
-.openerp .oe_form h1 input, .openerp .oe_form h2 input, .openerp .oe_form h3 input, .openerp .oe_form h4 input, .openerp .oe_form h5 input, .openerp .oe_form h6 input {
- height: inherit !important;
- font-size: inherit;
+.openerp .oe_form .oe_form_group {
+ width: 100%;
+ margin: 9px 0 9px 0;
}
-.openerp .oe_form_nosheet > *:not(.oe_form_topbar) {
- margin-left: 10px !important;
- margin-right: 10px !important;
+.openerp .oe_form .oe_form_group .oe_form_group_cell.oe_group_right {
+ padding-left: 20px;
}
.openerp .oe_form .oe_form_label_help[for], .openerp .oe_form .oe_form_label[for] {
font-weight: bold;
@@ -1712,69 +2117,99 @@
}
.openerp .oe_form .oe_form_label_help[for] span, .openerp .oe_form .oe_form_label[for] span {
font-size: 80%;
- color: darkGreen;
+ color: darkgreen;
vertical-align: top;
position: relative;
top: -4px;
padding: 0 2px;
}
-.openerp .oe_form .oe_form_field_text textarea {
- resize: vertical;
+.openerp .oe_horizontal_border {
+ border-bottom: 1px solid black;
}
-.openerp .oe_form .oe_form_field {
- width: 100%;
+.openerp .oe_horizontal_separator {
+ font-weight: bold;
+ font-size: 20px;
+ margin: 15px 0px 10px 0px;
+ color: #7c7bad;
+}
+.openerp .oe_horizontal_separator:empty {
+ height: 5px;
+}
+.openerp .oe_vertical_separator {
+ border-left: 1px solid #666666;
+ padding: 0 4px 0 4px;
+}
+.openerp .oe_form_field_progressbar {
display: inline-block;
- padding: 2px 2px 2px 0px;
- line-height: 18px;
+ min-width: 70px;
}
-.openerp .oe_form .oe_form_field input {
- margin: 0px;
+.openerp .oe_form_field_progressbar.ui-progressbar {
+ height: 22px;
+ font-size: 10px;
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ -ms-box-sizing: border-box;
+ box-sizing: border-box;
+ border: 1px solid #999999;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ border-radius: 3px;
+ background: white;
+ min-width: 50px;
}
-.openerp .oe_form .oe_form_field_integer {
- width: 180px;
+.openerp .oe_form_field_progressbar.ui-progressbar span {
+ position: absolute;
+ margin-left: 10px;
+ font-weight: bold;
}
-.openerp .oe_form .oe_form_field_float {
- width: 180px;
+.openerp .oe_form_field_progressbar.ui-progressbar .ui-widget-header {
+ background: #cccccc url(/web/static/lib/jquery.ui/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png) 50% 50% repeat-x;
+}
+.openerp .oe_form .oe_form_field_text {
+ width: 100%;
}
-.openerp .oe_form .oe_form_field_date {
- width: 7em;
+.openerp .oe_form .oe_form_field_char input,
+.openerp .oe_form .oe_form_field_url input,
+.openerp .oe_form .oe_form_field_email input,
+.openerp .oe_form .oe_form_field_text textarea,
+.openerp .oe_form .oe_form_field_selection select {
+ width: 100%;
}
-.openerp .oe_form .oe_form_field_datetime {
- width: 11em;
+.openerp .oe_form .oe_form_field_text.oe_inline, .openerp .oe_form .oe_form_field_text.oe_inline > textarea {
+ width: 500px;
}
-.openerp .oe_form .oe_form_inline {
- width: auto;
+.openerp .oe_form h1, .openerp .oe_form h2, .openerp .oe_form h3, .openerp .oe_form h4, .openerp .oe_form h5, .openerp .oe_form h6 {
+ margin: 0 0 4px 0;
}
-.openerp .oe_form input[type="text"],
-.openerp .oe_form input[type="password"],
-.openerp .oe_form input[type="file"],
-.openerp .oe_form select {
+.openerp .oe_form h1 input, .openerp .oe_form h2 input, .openerp .oe_form h3 input, .openerp .oe_form h4 input, .openerp .oe_form h5 input, .openerp .oe_form h6 input {
+ height: inherit !important;
+ font-size: inherit;
+}
+.openerp .oe_form .oe_form_field {
+ width: 100%;
+ display: inline-block;
+ padding: 2px 2px 2px 0px;
+}
+.openerp .oe_form .oe_form_field input {
+ margin: 0px;
+}
+.openerp .oe_form input[type="text"], .openerp .oe_form input[type="password"], .openerp .oe_form input[type="file"], .openerp .oe_form select {
height: 22px;
padding-top: 2px;
}
-.openerp .oe_form input[type="text"],
-.openerp .oe_form input[type="password"],
-.openerp .oe_form input[type="file"],
-.openerp .oe_form select,
-.openerp .oe_form textarea {
- -moz-box-sizing: border-box;
+.openerp .oe_form input[type="text"], .openerp .oe_form input[type="password"], .openerp .oe_form input[type="file"], .openerp .oe_form select, .openerp .oe_form textarea {
-webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
-ms-box-sizing: border-box;
box-sizing: border-box;
- padding: 0 2px;
- border: 1px solid #999999;
- -moz-border-radius: 3px;
- -webkit-border-radius: 3px;
- border-radius: 3px;
background: white;
- min-width: 70px;
+ min-width: 60px;
color: #1f1f1f;
}
-.openerp .oe_form input[readonly],
-.openerp .oe_form select[readonly],
-.openerp .oe_form textarea[readonly],
-.openerp .oe_form input[disabled],
-.openerp .oe_form select[disabled] {
+.openerp .oe_form textarea {
+ height: 32px;
+}
+.openerp .oe_form input[readonly], .openerp .oe_form select[readonly], .openerp .oe_form textarea[readonly], .openerp .oe_form input[disabled], .openerp .oe_form select[disabled] {
background: #e5e5e5 !important;
color: #666666;
}
@@ -1784,25 +2219,11 @@
padding-left: 8px;
-moz-box-shadow: none;
-webkit-box-shadow: none;
- -box-shadow: none;
+ box-shadow: none;
-moz-border-radius: 0px;
-webkit-border-radius: 0px;
border-radius: 0px;
}
-.openerp .oe_form .oe_form_field_many2one input,
-.openerp .oe_form .oe_form_field_binary input,
-.openerp .oe_form .oe_form_field_binary input,
-.openerp .oe_form .oe_form_field_email input,
-.openerp .oe_form .oe_form_field_url input {
- border-right: none;
- -webkit-border-top-right-radius: 0px;
- -webkit-border-bottom-right-radius: 0px;
- -moz-border-radius-topright: 0px;
- -moz-border-radius-bottomright: 0px;
- border-top-right-radius: 0px;
- border-bottom-right-radius: 0px;
-}
-.openerp .oe_form .oe_form_field_email button img,
.openerp .oe_form .oe_form_field_url button img {
vertical-align: top;
}
@@ -1812,29 +2233,7 @@
}
.openerp .oe_form .oe_form_field_boolean {
padding-top: 4px;
-}
-.openerp .oe_form .oe_form_field_many2manytags .text-wrap {
- width: 100% !important;
-}
-.openerp .oe_form .oe_form_field_many2manytags .text-wrap textarea {
- width: 100% !important;
-}
-.openerp .oe_form .oe_form_field_many2manytags .oe_form_field_many2manytags_box {
- border-radius: 2px;
- box-sizing: border-box;
- position: relative;
- float: left;
- border: 1px solid #9daccc;
- background: #e2e6f0;
- color: black;
- padding: 0px 3px 0px 3px;
- margin: 0 2px 2px 0;
- height: 16px;
- font: 11px "lucida grande", tahoma, verdana, arial, sans-serif;
-}
-.openerp .oe_form .oe_form_field_many2manytags .text-core .text-wrap .text-dropdown .text-list .text-suggestion em {
- font-style: italic;
- text-decoration: none;
+ width: auto;
}
.openerp .oe_form .oe_datepicker_container {
display: none;
@@ -1862,72 +2261,55 @@
right: 5px;
top: 3px;
}
-.openerp .oe_form .oe_form_group_cell .oe_kanban_view {
- position: static;
-}
-.openerp .oe_form .oe_form_field_with_button input {
- width: 100%;
+.openerp .oe_form .oe_form_field_with_button.oe_no_button > .oe_button {
+ display: none;
}
-.openerp .oe_form .oe_form_field_with_button > .oe_button {
+.openerp .oe_form .oe_form_field_with_button:not(.oe_no_button) > .oe_button {
float: right;
- -webkit-border-top-left-radius: 0px;
- -webkit-border-bottom-left-radius: 0px;
- -moz-border-radius-topleft: 0px;
- -moz-border-radius-bottomleft: 0px;
- border-top-left-radius: 0px;
+ -moz-border-radius: 0;
+ -webkit-border-radius: 0;
+ border-radius: 0;
border-bottom-left-radius: 0px;
height: 22px;
}
+.openerp .oe_form .oe_form_field_with_button input {
+ width: 100%;
+}
.openerp .oe_form .oe_form_field_with_button > div {
position: relative;
overflow: hidden;
}
-.openerp .oe_form .oe_form_title {
- width: 55%;
-}
-.openerp .oe_form .oe_form_title:after {
- content: ".";
- display: block;
- height: 0;
- clear: both;
- visibility: hidden;
-}
-.openerp .oe_form .oe_form_button_box {
- width: 200px;
- text-align: center;
+.openerp .oe_form .oe_form_field_html {
+ position: relative;
+ width: 600px;
+ margin-left: 130px;
+ margin-top: 32px;
+ margin-bottom: 32px;
+ text-align: justify;
}
-.openerp .oe_form .oe_form_group {
- margin: 5px 0px 5px 0px;
+.openerp .oe_form_editable .oe_form .oe_form_field_integer {
+ width: 6em !important;
}
-.openerp .oe_form .oe_form_group_cell .oe_kanban_view {
- position: static;
+.openerp .oe_form_editable .oe_form .oe_form_field_float {
+ width: 7em !important;
}
-.openerp .oe_form .oe_form_button.oe_button {
- height: 22px;
- white-space: nowrap;
+.openerp .oe_form_editable .oe_form .oe_form_field_date {
+ width: 7.5em !important;
}
-.openerp .oe_form .oe_form_button.oe_button span {
- position: relative;
- vertical-align: top;
+.openerp .oe_form_editable .oe_form .oe_form_field_datetime {
+ width: 11.5em !important;
}
-.openerp .oe_form .oe_form_button > img {
- vertical-align: -3px;
- padding: 0 2px;
-}
-.openerp .oe_form .oe-binary-file-set {
- overflow: hidden;
+.openerp .oe_hidden_input_file {
position: relative;
- display: inline-block;
- width: 45px;
- height: 30px;
}
-.openerp .oe_form input.oe-binary-file {
+.openerp .oe_hidden_input_file input.oe_form_binary_file {
z-index: 0;
line-height: 0;
font-size: 12px;
position: absolute;
top: 1px;
- right: 10px;
+ left: 0;
+ right: 0;
opacity: 0;
filter: alpha(opacity=0);
-ms-filter: "alpha(opacity=0)";
@@ -1941,210 +2323,344 @@
width: auto;
vertical-align: top;
}
-.openerp .oe_form .oe_form_field_image > img {
- min-width: 100px;
-}
.openerp .oe_form .oe_form_field_image .oe_form_field_image_controls {
position: absolute;
top: 1px;
- padding: 3px 0 0 0;
- margin: 0 1px;
- filter: alpha(opacity=70);
- opacity: 0.7;
- width: 99%;
+ padding: 4px;
+ width: 100%;
+ display: none;
text-align: center;
- background: url(/web/static/src/img/form_sheetbg.png);
- border-bottom: 1px dotted black;
- -moz-box-sizing: border-box;
+ color: #eeeeee;
+ background: rgba(37, 37, 37, 0.9);
+ -moz-border-radius: 3px 3px 0 0;
+ -webkit-border-radius: 3px 3px 0 0;
+ border-radius: 3px 3px 0 0;
-webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
-ms-box-sizing: border-box;
box-sizing: border-box;
}
.openerp .oe_form .oe_form_field_image:hover .oe_form_field_image_controls {
- filter: alpha(opacity=100);
- opacity: 1;
+ display: block;
}
-.openerp .oe_horizontal_border {
- border-bottom: 1px solid black;
+.openerp .oe_form_field_many2one td:first-child {
+ position: relative;
}
-.openerp .oe_horizontal_separator {
- font-weight: bold;
- font-size: 20px;
- margin: 20px 0px 10px 0px;
- color: #aaaabb;
+.openerp .oe_form_field_many2one span.oe_m2o_drop_down_button {
+ position: absolute;
+ top: 2px;
+ right: 0px;
}
-.openerp .oe_horizontal_separator:empty {
- height: 5px;
+.openerp .oe_form_field_many2one .oe_m2o_cm_button {
+ line-height: 14px;
+ float: right;
+ padding-left: 2px;
}
-.openerp .oe_vertical_separator {
- border-left: 1px solid #666666;
- padding: 0 4px 0 4px;
+.openerp.ui-autocomplete li.oe_m2o_dropdown_option a {
+ font-style: italic;
+ padding-left: 2em;
}
-.openerp .oe_form_notebook {
- margin: 8px 0;
- padding: 0 8px;
- list-style: none;
- zoom: 1;
+.openerp.ui-autocomplete li:not(.oe_m2o_dropdown_option) + li.oe_m2o_dropdown_option {
+ margin-top: 10px;
}
-.openerp .oe_form_notebook:before, .openerp .oe_form_notebook:after {
- display: table;
- content: "";
- zoom: 1;
+.openerp ul.oe_form_status, .openerp ul.oe_form_status_clickable {
+ display: inline-block;
+ margin: 0;
+ padding: 0 18px 0 0;
}
-.openerp .oe_form_notebook:after {
- clear: both;
+.openerp ul.oe_form_status li, .openerp ul.oe_form_status_clickable li {
+ display: inline-block;
+ list-style-type: none;
+ margin: 0 -18px 0 0;
+ padding: 0;
+ background-color: #fcfcfc;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#fcfcfc), to(#dedede));
+ background-image: -webkit-linear-gradient(top, #fcfcfc, #dedede);
+ background-image: -moz-linear-gradient(top, #fcfcfc, #dedede);
+ background-image: -ms-linear-gradient(top, #fcfcfc, #dedede);
+ background-image: -o-linear-gradient(top, #fcfcfc, #dedede);
+ background-image: linear-gradient(to bottom, #fcfcfc, #dedede);
}
-.openerp .oe_form_notebook > li {
- float: left;
+.openerp ul.oe_form_status li:first-child .label, .openerp ul.oe_form_status_clickable li:first-child .label {
+ border-left: 1px solid #cacaca;
+ padding-left: 14px;
}
-.openerp .oe_form_notebook > li > a {
- display: block;
- color: #4c4c4c;
+.openerp ul.oe_form_status li:last-child, .openerp ul.oe_form_status_clickable li:last-child {
+ border-right: 1px solid #cacaca;
}
-.openerp .oe_form_notebook {
- border-color: #dddddd;
- border-style: solid;
- border-width: 0 0 1px;
+.openerp ul.oe_form_status li:last-child .label, .openerp ul.oe_form_status_clickable li:last-child .label {
+ padding-right: 14px;
}
-.openerp .oe_form_notebook > li {
+.openerp ul.oe_form_status li:last-child .arrow, .openerp ul.oe_form_status_clickable li:last-child .arrow {
+ display: none;
+}
+.openerp ul.oe_form_status li .label, .openerp ul.oe_form_status_clickable li .label {
+ color: #4c4c4c;
+ text-shadow: 0 1px 1px #fcfcfc, 0 -1px 1px #dedede;
+ padding: 7px;
+ display: inline-block;
+ padding-left: 24px;
+ margin: 0;
position: relative;
- margin-bottom: -1px;
}
-.openerp .oe_form_notebook > li > a {
- padding: 0 12px;
- margin-right: 2px;
- line-height: 30px;
- border: 1px solid transparent;
- -moz-border-radius: 4px 4px 0 0;
- -webkit-border-radius: 4px 4px 0 0;
- border-radius: 4px 4px 0 0;
+.openerp ul.oe_form_status li .arrow, .openerp ul.oe_form_status_clickable li .arrow {
+ width: 17px;
+ display: inline-block;
+ vertical-align: top;
+ overflow: hidden;
+ margin-left: -5px;
}
-.openerp .oe_form_notebook > li > a:hover {
- text-decoration: none;
- background-color: #eeeeee;
- border-color: #eeeeee #eeeeee #dddddd;
+.openerp ul.oe_form_status li .arrow span, .openerp ul.oe_form_status_clickable li .arrow span {
+ position: relative;
+ width: 24px;
+ height: 24px;
+ display: inline-block;
+ margin-left: -12px;
+ margin-top: 3px;
+ box-shadow: -1px 1px 2px rgba(255, 255, 255, 0.2), inset -1px 1px 1px rgba(0, 0, 0, 0.2);
+ background-color: #dedede;
+ background: -moz-linear-gradient(135deg, #dedede, #fcfcfc);
+ background: -o-linear-gradient(135deg, #fcfcfc, #dedede);
+ background: -webkit-gradient(linear, left top, right bottom, from(#fcfcfc), to(#dedede));
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ border-radius: 3px;
+ -webkit-transform: rotate(45deg);
+ -moz-transform: rotate(45deg);
+ -ms-transform: rotate(45deg);
+ -o-transform: rotate(45deg);
+ transform: rotate(45deg);
+}
+.openerp ul.oe_form_status li.oe_active, .openerp ul.oe_form_status_clickable li.oe_active {
+ background-color: #729fcf;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#729fcf), to(#3465a4));
+ background-image: -webkit-linear-gradient(top, #729fcf, #3465a4);
+ background-image: -moz-linear-gradient(top, #729fcf, #3465a4);
+ background-image: -ms-linear-gradient(top, #729fcf, #3465a4);
+ background-image: -o-linear-gradient(top, #729fcf, #3465a4);
+ background-image: linear-gradient(to bottom, #729fcf, #3465a4);
+}
+.openerp ul.oe_form_status li.oe_active .arrow span, .openerp ul.oe_form_status_clickable li.oe_active .arrow span {
+ background-color: #3465a4;
+ background: -moz-linear-gradient(135deg, #3465a4, #729fcf);
+ background: -o-linear-gradient(135deg, #729fcf, #3465a4);
+ background: -webkit-gradient(linear, left top, right bottom, from(#729fcf), to(#3465a4));
+}
+.openerp ul.oe_form_status li.oe_active .label, .openerp ul.oe_form_status_clickable li.oe_active .label {
+ color: white;
+ text-shadow: 0 1px 1px #729fcf, 0 -1px 1px #3465a4;
}
-.openerp .oe_form_notebook > li.ui-state-active > a, .openerp .oe_form_notebook > li.ui-state-active > a:hover {
- background-color: white;
- border: 1px solid #dddddd;
- border-bottom-color: transparent;
- cursor: default;
+.openerp ul.oe_form_status_clickable li {
+ cursor: pointer;
}
-.openerp .oe_form_notebook_page {
- padding: 0;
+.openerp ul.oe_form_status_clickable li:hover {
+ background-color: #e8e8e8;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#cacaca));
+ background-image: -webkit-linear-gradient(top, #e8e8e8, #cacaca);
+ background-image: -moz-linear-gradient(top, #e8e8e8, #cacaca);
+ background-image: -ms-linear-gradient(top, #e8e8e8, #cacaca);
+ background-image: -o-linear-gradient(top, #e8e8e8, #cacaca);
+ background-image: linear-gradient(to bottom, #e8e8e8, #cacaca);
+}
+.openerp ul.oe_form_status_clickable li:hover .label {
+ text-shadow: 0 -1px 1px #fcfcfc, 0 1px 1px #dedede;
+}
+.openerp ul.oe_form_status_clickable li:hover .arrow span {
+ background-color: #e8e8e8;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#cacaca));
+ background-image: -webkit-linear-gradient(top, #e8e8e8, #cacaca);
+ background-image: -moz-linear-gradient(top, #e8e8e8, #cacaca);
+ background-image: -ms-linear-gradient(top, #e8e8e8, #cacaca);
+ background-image: -o-linear-gradient(top, #e8e8e8, #cacaca);
+ background-image: linear-gradient(to bottom, #e8e8e8, #cacaca);
+}
+.openerp ul.oe_form_status_clickable li .label {
+ color: #7c7bad;
+}
+.openerp ul.oe_form_status_clickable li.oe_active:hover {
+ background-color: #4c85c2;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#4c85c2), to(#284d7d));
+ background-image: -webkit-linear-gradient(top, #4c85c2, #284d7d);
+ background-image: -moz-linear-gradient(top, #4c85c2, #284d7d);
+ background-image: -ms-linear-gradient(top, #4c85c2, #284d7d);
+ background-image: -o-linear-gradient(top, #4c85c2, #284d7d);
+ background-image: linear-gradient(to bottom, #4c85c2, #284d7d);
+}
+.openerp ul.oe_form_status_clickable li.oe_active:hover .label {
+ text-shadow: 0 -1px 1px #729fcf, 0 1px 1px #3465a4;
+}
+.openerp ul.oe_form_status_clickable li.oe_active:hover .arrow span {
+ background-color: #284d7d;
+ background: -moz-linear-gradient(135deg, #284d7d, #4c85c2);
+ background: -o-linear-gradient(135deg, #4c85c2, #284d7d);
+ background: -webkit-gradient(linear, left top, right bottom, from(#4c85c2), to(#284d7d));
+}
+.openerp .oe_form .oe_form_field_one2many > .oe_view_manager .oe_list_pager_single_page {
+ display: none;
}
-.openerp div.ui-tabs {
- padding: 3px 0px 3px 0px;
+.openerp .oe_form_field_one2many > .oe_view_manager .oe_list_pager_single_page, .openerp .oe_form_field_many2many > .oe_view_manager .oe_list_pager_single_page {
+ display: none !important;
}
-.openerp .ui-tabs-hide {
- display: none;
+.openerp .oe_form_field_one2many .oe_form_field_one2many_list_row_add, .openerp .oe_form_field_many2many .oe_form_field_one2many_list_row_add {
+ font-weight: bold;
}
-.openerp .oe_form .oe_form_field_progressbar {
- display: inline-block;
- min-width: 70px;
+.openerp .oe_form_field_one2many .oe_list_content > thead, .openerp .oe_form_field_many2many .oe_list_content > thead {
+ border-bottom: 1px;
}
-.openerp .oe_form .oe_form_field_progressbar.ui-progressbar {
- height: 22px;
- font-size: 10px;
- -moz-box-sizing: border-box;
- -webkit-box-sizing: border-box;
- -ms-box-sizing: border-box;
- box-sizing: border-box;
- border: 1px solid #999999;
- -moz-border-radius: 3px;
- -webkit-border-radius: 3px;
- border-radius: 3px;
- background: white;
- min-width: 50px;
+.openerp .oe_form_field_one2many .oe_list_content > tbody tr:nth-child(odd), .openerp .oe_form_field_many2many .oe_list_content > tbody tr:nth-child(odd) {
+ background: transparent;
}
-.openerp .oe_form .oe_form_field_progressbar.ui-progressbar span {
- position: absolute;
- margin-left: 10px;
- font-weight: bold;
+.openerp .oe_form_field_one2many .oe_list .oe_list_edit_row_save, .openerp .oe_form_field_many2many .oe_list .oe_list_edit_row_save {
+ background: url(/web/static/src/img/iconset-b-remove.png) 50% 50% no-repeat;
}
-.openerp .oe_form .oe_form_field_progressbar.ui-progressbar .ui-widget-header {
- background: #cccccc url(/web/static/lib/jquery.ui/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png) 50% 50% repeat-x;
+.openerp .oe_form_field_one2many .oe_list .oe_list_edit_row_save:before, .openerp .oe_form_field_many2many .oe_list .oe_list_edit_row_save:before {
+ visibility: hidden;
+}
+.openerp .oe_form_field_one2many > .oe_view_manager .oe_header_row_top, .openerp .oe_form_field_many2many > .oe_view_manager .oe_header_row_top {
+ display: none;
}
-.openerp .oe_form .oe_view_manager_header2 td {
+.openerp .oe_form_field_one2many > .oe_view_manager .oe_view_manager_header2 td, .openerp .oe_form_field_many2many > .oe_view_manager .oe_view_manager_header2 td {
padding: 0px 8px;
line-height: 16px;
}
-.openerp .oe_form .oe_view_manager_header2 td .oe_i {
+.openerp .oe_form_field_one2many > .oe_view_manager .oe_view_manager_header2 td .oe_i, .openerp .oe_form_field_many2many > .oe_view_manager .oe_view_manager_header2 td .oe_i {
font-size: 13px;
}
-.openerp .oe_form .oe_view_manager_header2 td .oe_pager_group {
+.openerp .oe_form_field_one2many > .oe_view_manager .oe_view_manager_header2 td .oe_pager_group, .openerp .oe_form_field_many2many > .oe_view_manager .oe_view_manager_header2 td .oe_pager_group {
height: auto;
line-height: 16px;
}
-.openerp .oe_form .oe_view_manager_header2 td .oe_pager_group li {
+.openerp .oe_form_field_one2many > .oe_view_manager .oe_view_manager_header2 td .oe_pager_group li, .openerp .oe_form_field_many2many > .oe_view_manager .oe_view_manager_header2 td .oe_pager_group li {
height: auto;
line-height: 16px;
}
-.openerp .oe-select-create-popup-view-form > .oe_formview > .oe_form_pager {
- display: none;
+.openerp .oe_form_field_one2many .oe_list_buttons.oe_editing .oe_list_save, .openerp .oe_form_field_many2many .oe_list_buttons.oe_editing .oe_list_save {
+ visibility: hidden;
}
-.openerp .oe_form td.oe_form_group_cell_label {
- border-right: 1px solid #dddddd;
- padding: 2px 0px 2px 0px;
+.openerp .oe_form_editable .oe_list_editable .oe_list_content td.oe_required {
+ background-color: #d2d2ff;
}
-.openerp .oe_form td.oe_form_group_cell_label label {
- line-height: 18px;
- display: block;
- min-width: 120px;
+.openerp .oe_form_editable .oe_list_editable .oe_list_content td.oe_readonly {
+ background-color: #eeeeee;
}
-.openerp .oe_form td.oe_form_group_cell + .oe_form_group_cell {
- padding-left: 8px;
+.openerp .oe_list_editable .oe_list_content td.oe_list_field_cell {
+ padding: 4px 6px 3px 6px;
}
-.openerp .oe_form .oe_form_subtotal_footer {
- width: auto;
- float: right;
+.openerp .oe_list.oe_list_editable td.oe_list_record_delete {
+ position: absolute;
+}
+.openerp .oe_list.oe_list_editable.oe_editing .oe_m2o_drop_down_button {
+ top: 5px;
+}
+.openerp .oe_list.oe_list_editable.oe_editing .oe_m2o_cm_button {
+ display: none;
+}
+.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field input, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field textarea {
+ height: 27px;
}
-.openerp .oe_form .oe_form_subtotal_footer td.oe_form_group_cell {
+.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field input, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field textarea {
+ -moz-border-radius: 0;
+ -webkit-border-radius: 0;
+ border-radius: 0;
+ border: 1px solid #aaaaff;
+ margin: 0;
+}
+.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_float input, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_view_integer input {
text-align: right;
- padding: 0;
+ width: 100%;
}
-.openerp .oe_form .oe_form_subtotal_footer td.oe_form_group_cell_label {
- border: none;
+.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_datetime > span, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_date > span {
+ width: 100%;
}
-.openerp .oe_form .oe_form_subtotal_footer .oe_form_field {
- width: auto !important;
+.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_form .oe_form_subtotal_footer .oe_form_subtotal_footer_separator {
- border-top: 1px solid #cacaca;
- font-size: 120%;
- font-weight: bold;
+.openerp .oe_list.oe_list_editable.oe_editing .oe_form_field .oe_form_field_float, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field .oe_form_view_integer, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_datetime, .openerp .oe_list.oe_list_editable.oe_editing .oe_form_field.oe_form_field_date {
+ min-width: 0 !important;
+ max-width: none !important;
}
-.openerp .oe_form .oe_form_subtotal_footer label.oe_form_subtotal_footer_separator {
- font-weight: bold !important;
- padding: 2px 8px 2px 0px !important;
+.openerp .oe_form .oe_form_field_many2many > .oe_list .oe_list_pager_single_page {
+ display: none;
+}
+.openerp .oe_list_buttons .oe_alternative {
+ visibility: hidden;
+}
+.openerp .oe_list_buttons .oe_list_save, .openerp .oe_list_buttons .oe_list_discard {
+ display: none;
+}
+.openerp .oe_list_buttons.oe_editing .oe_list_add {
+ display: none;
+}
+.openerp .oe_list_buttons.oe_editing .oe_list_save {
+ display: inline-block;
}
-.openerp .oe-listview-content {
+.openerp .oe_list_buttons.oe_editing .oe_list_discard {
+ display: inline;
+}
+.openerp .oe_list_buttons.oe_editing .oe_alternative {
+ visibility: visible;
+}
+.openerp .oe_list {
+ position: relative;
+}
+.openerp .oe_list .oe_form .oe_form_nosheet {
+ margin: 0;
+ padding: 0;
+ border: none;
+}
+.openerp .oe_list .oe_form .oe_form_field {
+ width: auto;
+ position: absolute;
+ margin: 0 !important;
+ padding: 0;
+}
+.openerp .oe_list .oe_list_content .oe_group_header {
+ background-color: #fcfcfc;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#fcfcfc), to(#dedede));
+ background-image: -webkit-linear-gradient(top, #fcfcfc, #dedede);
+ background-image: -moz-linear-gradient(top, #fcfcfc, #dedede);
+ background-image: -ms-linear-gradient(top, #fcfcfc, #dedede);
+ background-image: -o-linear-gradient(top, #fcfcfc, #dedede);
+ background-image: linear-gradient(to bottom, #fcfcfc, #dedede);
+}
+.openerp .oe_list_content {
width: 100%;
}
-.openerp .oe-listview-content > thead {
+.openerp .oe_list_content td:first-child:after, .openerp .oe_list_content th:first-child:after {
+ border-width: 0;
+}
+.openerp .oe_list_content td.oe_number {
+ text-align: right !important;
+ max-width: 100px;
+}
+.openerp .oe_list_content > thead {
border-bottom: 2px solid #cacaca;
background: #eeeeee;
vertical-align: top;
}
-.openerp .oe-listview-content > thead td, .openerp .oe-listview-content > thead th {
+.openerp .oe_list_content > thead th {
+ position: relative;
+}
+.openerp .oe_list_content td, .openerp .oe_list_content th {
padding: 3px 6px;
line-height: 18px;
}
-.openerp .oe-listview-content > thead th:after {
+.openerp .oe_list_content th:after {
+ position: absolute;
+ right: 6px;
content: "";
- float: right;
margin-top: 7px;
border-width: 0 4px 4px;
border-style: solid;
border-color: black transparent;
visibility: hidden;
}
-.openerp .oe-listview-content > thead th.sortup:after {
+.openerp .oe_list_content th.sortup:after {
visibility: visible;
filter: alpha(opacity=60);
opacity: 0.6;
}
-.openerp .oe-listview-content > thead th.sortdown:after {
+.openerp .oe_list_content th.sortdown:after {
border-bottom: none;
border-left: 4px solid transparent;
border-right: 4px solid transparent;
@@ -2152,41 +2668,36 @@
visibility: visible;
-moz-box-shadow: none;
-webkit-box-shadow: none;
- -box-shadow: none;
+ box-shadow: none;
filter: alpha(opacity=60);
opacity: 0.6;
}
-.openerp .oe-listview-content > tbody {
+.openerp .oe_list_content > tbody {
cursor: pointer;
- background: white;
}
-.openerp .oe-listview-content > tbody > tr {
+.openerp .oe_list_content > tbody > tr {
+ height: 27px;
border-top: 1px solid #dddddd;
}
-.openerp .oe-listview-content > tbody > tr > td.oe-field-cell {
+.openerp .oe_list_content > tbody > tr > td.oe_list_field_cell {
padding: 3px 6px;
+ white-space: pre-line;
}
-.openerp .oe-listview-content > tbody > tr > td, .openerp .oe-listview-content > tbody > tr > th {
- line-height: 18px;
-}
-.openerp .oe-listview-content > tbody > tr > td > button, .openerp .oe-listview-content > tbody > tr > th > button {
+.openerp .oe_list_content > tbody > tr > td > button, .openerp .oe_list_content > tbody > tr > th > button {
border: none;
background: transparent;
padding: 0;
-moz-box-shadow: none;
-webkit-box-shadow: none;
- -box-shadow: none;
+ box-shadow: none;
}
-.openerp .oe-listview-content > tbody > tr > td.oe_list_checkbox:first-child, .openerp .oe-listview-content > tbody > tr th.oe_list_checkbox:first-child {
+.openerp .oe_list_content > tbody > tr > td.oe_list_checkbox:first-child, .openerp .oe_list_content > tbody > tr th.oe_list_checkbox:first-child {
width: 17px;
}
-.openerp .oe-listview-content > tbody > tr > td.oe_list_checkbox:first-child:after, .openerp .oe-listview-content > tbody > tr th.oe_list_checkbox:first-child:after {
+.openerp .oe_list_content > tbody > tr > td.oe_list_checkbox:first-child:after, .openerp .oe_list_content > tbody > tr th.oe_list_checkbox:first-child:after {
border-width: 0;
}
-.openerp .oe-listview-content > tbody > tr > td.oe_number {
- text-align: right !important;
-}
-.openerp .oe-listview-content > tbody > tr:nth-child(odd) {
+.openerp .oe_list_content > tbody > tr:nth-child(odd) {
background-color: #f0f0fa;
background-color: #f0f0fa;
background-image: -webkit-gradient(linear, left top, left bottom, from(#f0f0fa), to(#eeeef6));
@@ -2196,51 +2707,105 @@
background-image: -o-linear-gradient(top, #f0f0fa, #eeeef6);
background-image: linear-gradient(to bottom, #f0f0fa, #eeeef6);
}
-.openerp .oe-listview-content > tfoot {
+.openerp .oe_list_content > tfoot {
border-top: 2px solid #cacaca;
border-bottom: 1px solid #cacaca;
background: #eeeeee;
font-weight: bold;
}
-.openerp .oe-listview-content > tbody tr:hover td, .openerp .oe-listview-content tbody tr:hover th {
- background-color: #eeeeee;
- background-image: -webkit-gradient(linear, left top, left bottom, from(#eeeeee), to(#dedede));
- background-image: -webkit-linear-gradient(top, #eeeeee, #dedede);
- background-image: -moz-linear-gradient(top, #eeeeee, #dedede);
- background-image: -ms-linear-gradient(top, #eeeeee, #dedede);
- background-image: -o-linear-gradient(top, #eeeeee, #dedede);
- background-image: linear-gradient(to bottom, #eeeeee, #dedede);
-}
-.openerp .oe-listview-content .numeric {
+.openerp .oe_list_content .numeric {
text-align: right;
width: 82px;
}
-.openerp .oe-listview-content .numeric input {
+.openerp .oe_list_content .numeric input {
text-align: right;
}
-.openerp .oe_trad_field.touched {
- border: 1px solid green !important;
+.openerp .oe_list_content th.oe_list_header_handle {
+ font-size: 1px;
+ overflow: hidden;
+ text-indent: -9001px;
}
-.openerp.oe_tooltip {
- font-size: 12px;
+.openerp .oe_list_content td.oe_list_field_handle {
+ width: 1em;
+ padding: 0 !important;
+ cursor: ns-resize;
}
-.openerp.oe_tooltip .oe_tooltip_string {
- color: #ffdd55;
- font-weight: bold;
- font-size: 13px;
+.openerp .oe_list_content td.oe_list_field_handle .oe_list_handle {
+ font-size: 1px;
+ letter-spacing: -1px;
+ color: transparent;
+ font-weight: normal;
+ margin-right: 7px;
}
-.openerp.oe_tooltip .oe_tooltip_help {
- white-space: pre-wrap;
+.openerp .oe_list_content td.oe_list_field_handle .oe_list_handle:before {
+ font: 18px "entypoRegular";
+ content: "}";
+ color: #e0e0e0;
}
-.openerp.oe_tooltip .oe_tooltip_technical {
- padding: 0 0 4px 0;
- margin: 5px 0 0 15px;
+.openerp .oe_list_cannot_edit .oe_list_header_handle, .openerp .oe_list_cannot_edit .oe_list_field_handle {
+ display: none !important;
+ padding: 0 !important;
}
-.openerp.oe_tooltip .oe_tooltip_technical li {
- list-style: circle;
+.openerp .oe_list_cannot_delete .oe_list_record_delete {
+ display: none !important;
}
-.openerp.oe_tooltip .oe_tooltip_technical_title {
+.openerp .tree_header {
+ background-color: #f0f0f0;
+ border-bottom: 1px solid #cacaca;
+ color: #4c4c4c;
+ padding: 5px;
+ height: 25px;
+}
+.openerp .tree_header button {
+ float: right;
+ height: 27px;
+ margin-right: 5px;
+}
+.openerp .oe-treeview-table {
+ width: 100%;
+ background-color: white;
+ border-spacing: 0;
+ color: #4c4c4c;
+}
+.openerp .oe-treeview-table th {
+ padding: 10px;
font-weight: bold;
+ background-color: #f0f0f0;
+ border-bottom: 2px solid #cacaca;
+}
+.openerp .oe-treeview-table td {
+ cursor: pointer;
+ vertical-align: middle;
+ text-align: left;
+ vertical-align: middle;
+ height: 20px;
+ padding-left: 4px;
+ padding-right: 4px;
+ border-right: 1px solid #e7e7e7;
+}
+.openerp .oe-treeview-table td.oe_number {
+ text-align: right !important;
+}
+.openerp .oe-treeview-table tr {
+ border-bottom: 1px solid #d6d6d6;
+}
+.openerp .oe-treeview-table tr:hover {
+ background-color: #e7e7e7;
+}
+.openerp .oe-treeview-table span {
+ font-size: 90%;
+ font-weight: normal;
+ white-space: nowrap;
+ display: block;
+}
+.openerp .oe-treeview-table .treeview-tr.oe-treeview-first {
+ background: transparent url(/web/static/src/img/expand.gif) 0 50% no-repeat;
+}
+.openerp .oe-treeview-table .oe_open .treeview-tr.oe-treeview-first {
+ background-image: url(/web/static/src/img/collapse.gif);
+}
+.openerp .oe-treeview-table .treeview-tr.oe-treeview-first span, .openerp .oe-treeview-table .treeview-td.oe-treeview-first span {
+ margin-left: 16px;
}
.openerp .oe_layout_debugging .oe_form_group {
outline: 2px dashed green;
@@ -2283,28 +2848,6 @@
color: #333333;
}
-.openerp .oe_view_editor {
- width: 100%;
- border-collapse: collapse;
- margin-left: -12px;
- width: 100%;
- background-color: white;
- border-spacing: 0;
-}
-.openerp .oe_view_editor td {
- text-align: center;
- white-space: nowrap;
- border: 1px solid #d8d8d8;
- cursor: pointer;
- font-size: 90%;
-}
-.openerp .oe_view_editor_field td {
- border: 0px !important;
-}
-.openerp .oe_view_editor tr:hover {
- background-color: #ecebf2;
-}
-
.kitten-mode-activated {
background-image: url(http://placekitten.com/g/1365/769);
background-size: cover;
@@ -2314,11 +2857,47 @@
opacity: 0.7;
}
-.openerp .oe_form_field_many2one td:first-child {
- position: relative;
+div.ui-widget-overlay {
+ background: black;
+ filter: alpha(opacity=30);
+ opacity: 0.3;
}
-.openerp .oe_form_field_many2one span.oe-m2o-drop-down-button {
- position: absolute;
- top: 3px;
- right: 2px;
+
+.ui-widget {
+ font-family: "Lucida Grande", Helvetica, Verdana, Arial, sans-serif;
+ color: #4c4c4c;
+ font-size: 13px;
+}
+
+.ui-menu {
+ padding: 2px 0;
+ -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
+ -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
+ margin-top: 4px;
+ border: 1px solid #afafb6;
+}
+.ui-menu .ui-menu-item {
+ width: 100%;
+ padding: 0;
+}
+.ui-menu .ui-menu-item a {
+ padding: 1px 16px;
+}
+.ui-menu .ui-menu-item a.ui-corner-all {
+ -moz-border-radius: 0;
+ -webkit-border-radius: 0;
+ border-radius: 0;
+}
+.ui-menu .ui-menu-item a.ui-state-active {
+ background: #f0f0fa;
+}
+.ui-menu .ui-menu-item a.ui-state-hover, .ui-menu .ui-menu-item a.ui-state-active {
+ background: #7c7bad;
+}
+
+.ui-corner-all {
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ border-radius: 3px;
}
diff --git a/addons/web/static/src/css/base.sass b/addons/web/static/src/css/base.sass
index c4d9d565b8b..851d0c3f9c9 100644
--- a/addons/web/static/src/css/base.sass
+++ b/addons/web/static/src/css/base.sass
@@ -1,11 +1,15 @@
@charset "utf-8"
// Variables {{{
-$section-title-color: #8786b7
$facets-border: #afafb6
-$facets-border-selected: #a6a6fe
+$section-title-color: #7C7BAD
+$tag-bg-light: #f0f0fa
+$tag-bg-dark: #7C7BAD
+$tag-border: #afafb6
+$tag-border-selected: #a6a6fe
$hover-background: #f0f0fa
-$colour4: #8a89ba
+$link-color: #7C7BAD
+$sheet-max-width: 860px
// }}}
// Mixins {{{
@font-face
@@ -27,6 +31,14 @@ $colour4: #8a89ba
font-weight: normal
font-style: normal
+@mixin reset()
+ border: none
+ padding: 0
+ margin: 0
+ background: none
+ @include radius(0)
+ @include box-shadow(none)
+
@mixin vertical-gradient($startColor: #555, $endColor: #333)
background-color: $startColor
background-image: -webkit-gradient(linear, left top, left bottom, from($startColor), to($endColor)) /* Saf4+, Chrome */
@@ -36,7 +48,6 @@ $colour4: #8a89ba
background-image: -o-linear-gradient(top, $startColor, $endColor) /* Opera 11.10+ */
background-image: linear-gradient(to bottom, $startColor, $endColor)
-
@mixin radial-gradient($gradient)
background-position: center center
background-image: -webkit-radial-gradient(circle, $gradient)
@@ -52,7 +63,7 @@ $colour4: #8a89ba
@mixin box-shadow($bsval: 0px 1px 4px #777)
-moz-box-shadow: $bsval
-webkit-box-shadow: $bsval
- -box-shadow: $bsval
+ box-shadow: $bsval
@mixin transition($transval: (border linear 0.2s, box-shadow linear 0.2s))
-webkit-transition: $transval
@@ -70,10 +81,38 @@ $colour4: #8a89ba
-moz-background-clip: $clip
background-clip: $clip
-@mixin unscrew-box
- -moz-box-sizing: border-box
- -webkit-box-sizing: border-box
- box-sizing: border-box
+@mixin box-sizing($type: content)
+ // type = border || content || padding
+ -webkit-box-sizing: #{$type}-box
+ -moz-box-sizing: #{$type}-box
+ -ms-box-sizing: #{$type}-box
+ box-sizing: #{$type}-box
+
+@mixin skew-gradient($startColor: #555, $endColor: #333)
+ background-color: $endColor
+ background: -moz-linear-gradient(135deg, $endColor, $startColor)
+ background: -o-linear-gradient(135deg, $startColor, $endColor)
+ background: -webkit-gradient(linear, left top, right bottom, from($startColor), to($endColor))
+
+@mixin transform($transform)
+ -webkit-transform: $transform
+ -moz-transform: $transform
+ -ms-transform: $transform
+ -o-transform: $transform
+ transform: $transform
+
+@mixin keyframes($name)
+ @-webkit-keyframes #{$name}
+ @content
+ @-moz-keyframes #{$name}
+ @content
+ @-ms-keyframes #{$name}
+ @content
+ @-o-keyframes #{$name}
+ @content
+ @keyframes #{$name}
+ @content
+
// Transforms the (readable) text of an inline element into an mmlicons icon,
// allows for actual readable text in-code (and in readers?) with iconic looks
@@ -81,18 +120,37 @@ $colour4: #8a89ba
font-size: 1px
letter-spacing: -1px
color: transparent
+ font-weight: normal
&:before
- font-family: "mnmliconsRegular"
+ font: 21px "mnmliconsRegular"
+ content: $icon-name
+ color: $color
+
+@mixin text-to-entypo-icon($icon-name, $color: #404040, $size: 21px)
+ font-size: 1px
+ letter-spacing: -1px
+ color: transparent
+ font-weight: normal
+ &:before
+ font: $size "entypoRegular"
content: $icon-name
- font-size: 20px
color: $color
// }}}
-.openerp.openerp-web-client-container
+@media print
+ .oe_topbar, .oe_leftbar, .oe_loading
+ display: none !important
+
+.openerp.openerp_webclient_container
height: 100%
position: relative
+// jQueryUI css bug fixing
+.text-tag .text-button
+ height: auto !important
+ min-height: 16px
+
.openerp
// Global style {{{
padding: 0
@@ -101,19 +159,28 @@ $colour4: #8a89ba
color: #4c4c4c
font-size: 13px
background: white
+ text-shadow: 0 1px 1px rgba(255, 255, 255, 0.5)
// }}}
+ //Placeholder style{{{
+ \:-moz-placeholder
+ color: $tag-border !important
+ font-style: italic !important
+ \::-webkit-input-placeholder
+ color: $tag-border !important
+ font-style: italic !important
+ //}}}
// Tag reset {{{
a
text-decoration: none
+ cursor: pointer !important
table
padding: 0
- font-size: 13px
border-collapse: collapse
thead
font-weight: bold
background-color: #f0f0f0
th
- border-right: 1px dotted #afafb6
+ border-right: 1px dotted $tag-border
&:last-child
border-right: none
th, td
@@ -129,53 +196,21 @@ $colour4: #8a89ba
@include vertical-gradient(#f0f0fa, #eeeef6)
.zebra tbody tr:hover td
@include vertical-gradient(#eee, #dedede)
- .numeric
- text-align: right
- width: 82px
- input
- text-align: right
- ul, li, ol
- margin: 0
- padding: 0
- li
- list-style-type: none
- input, textarea
+ input, textarea, select
+ padding: 2px 4px
border: 1px solid #ccc
@include radius(3px)
-
- // }}}
- // Generic classes {{{
- .oe_i
- font-family: "mnmliconsRegular" !important
- font-size: 21px
- font-weight: 300 !important
- .oe_e
- font-family: "entypoRegular" !important
- font-size: 34px
- font-weight: 300 !important
- .oe_left
- float: left
- margin-right: 8px
- .oe_right
- float: right
- margin-left: 8px
- .oe_text_right
- text-align: right
- .oe_clear
- clear: both
- .oe_wait
- cursor: wait
- .oe_fade
- color: #888
- //font-size: 12px
- .oe_bold
- font-weight: bold
+ background: white
+ img
+ vertical-align: middle
+ h4
+ margin: 4px 0
// }}}
// Button style {{{
- a.button:link, a.button:visited, button, input[type='submit']
+ a.button:link, a.button:visited, button, input[type='submit'], .ui-dialog-buttonpane .ui-dialog-buttonset .ui-button
display: inline-block
- border: 1px solid #ababab
- color: #404040
+ border: 1px solid rgba(0,0,0,0.4)
+ color: #4c4c4c
margin: 0
padding: 3px 12px
font-size: 13px
@@ -187,20 +222,19 @@ $colour4: #8a89ba
-webkit-font-smoothing: antialiased
outline: none
- a.button:hover, button:hover, input[type='submit']:hover
+ a.button:hover, 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
+ a.button:focus, 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
- background: #e3e3e3
- background: -moz-linear-gradient(top, #e3e3e3, #f6f6f6) #1b468f
- background: -webkit-gradient(linear, left top, left bottom, from(#e3e3e3), to(#f6f6f6)) #1b468f
- background: linear-gradient(top, #e3e3e3, #f6f6f6) #1b468f
+ 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
+ @include vertical-gradient(#e3e3e3, #f6f6f6)
@include box-shadow(none)
a.button.disabled, button:disabled, input[type='submit']:disabled
@@ -210,31 +244,405 @@ $colour4: #8a89ba
color: #aaa !important
cursor: default
text-shadow: 0 1px 1px #fff !important
+ // }}}
+ // Jquery ui Overrides {{{
+ .ui-widget-content a
+ color: $link-color
+ .oe_bounce_container
+ display: inline-block
- button.primary
- color: white
- @include vertical-gradient(#dc5f59, #b33630)
- @include box-shadow(none)
+ // Modal box
+ &.ui-dialog
+ display: none
+ padding: 6px
+ //overflow: hidden
+ background-color: rgba(60,60,60,0.7)
+ border: 1px solid
+ border-color: #888 #555 #444
+ //overflow: hidden
+ @include radius(8px)
+ @include box-shadow(0 1px 12px rgba(0, 0, 0, 0.6))
+ @include background-clip()
+ .ui-dialog-content
+ padding: 0px
+ .ui-dialog-titlebar, .ui-dialog-content, .ui-dialog-buttonpane
+ padding: 16px
+ .ui-dialog-titlebar
+ 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
+ .ui-dialog-buttonpane
+ border-top: 1px solid #e0e0e0
+ background: #f5f7f9
+ margin: 0
+ @include radius(0 0 2px 2px)
+ button
+ margin: 0 4px 0 0
+ .ui-dialog-buttonset
+ float: left
+ .ui-button
+ margin-right: 4px
+ .ui-dialog-titlebar-close
+ padding: 0
+ .ui-icon-closethick
+ display: none
+ &:before
+ content: "×"
+ font-size: 18px
+ font-weight: bold
+ line-height: 16px
+ color: black
+ text-shadow: 0 1px 0 white
+ padding: 0
+ cursor: pointer
+ background: transparent
+ border: 0
+ &:hover
+ color: black
+ text-decoration: none
+ .oe_about
+ background-color: white
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAGCAYAAADgzO9IAAAAKUlEQVQIHWO8e/fufwYsgAUkJigoiCIF5DMyoYggcUiXgNnBiGQKmAkARpcEQeriln4AAAAASUVORK5CYII=)
+ @include radius(0 0 2px 2px)
+ a
+ color: $link-color
+ &:hover
+ text-decoration: underline
+ &:focus
+ outline: none
+ .oe_logo
+ margin-left: -6px
+ .oe_bottom
+ position: absolute
+ top: 50%
+ left: 0
+ right: 0
+ bottom: 0
+ text-shadow: 0 1px 1px #999999
+ @include vertical-gradient(#b41616, #600606)
+ color: #eee
+ padding: 0 16px
+ @include radius(0 0 2px 2px)
+ a
+ color: #eee
- button.primary:hover
- @include vertical-gradient(lighten(#dc5f59, 3%), lighten(#b33630, 3%))
- @include box-shadow(0 0 1px rgba(0,0,0,0.2))
+ &.ui-dialog.oe_act_window
+ .ui-dialog-content
+ padding: 0px
- button.primary:active
- @include vertical-gradient(#b33630, #dc5f59)
- @include box-shadow(none)
+ .modal-backdrop
+ position: fixed
+ top: 0
+ right: 0
+ bottom: 0
+ left: 0
+ z-index: 1040
+ background-color: black
+ @include opacity(.3)
- button.following
+ // }}}
+ // Generic classes {{{
+ .oe_i
+ font-family: "mnmliconsRegular" !important
+ font-size: 21px
+ font-weight: 300 !important
+ .oe_e
+ font-family: "entypoRegular" !important
+ font-size: 34px
+ font-weight: 300 !important
+ .oe_left
+ float: left
+ margin-right: 8px
+ .oe_right
+ float: right
+ margin-left: 8px
+ .oe_text_right
+ text-align: right
+ .oe_clear
+ clear: both
+ .oe_wait
+ cursor: wait
+ .oe_fade
+ color: #888
+ font-weight: normal
+ .oe_bold
+ font-weight: bold
+ .oe_inline
+ width: auto !important
+ .oe_highlight
color: white
- width: 120px
- @include vertical-gradient($colour4, darken($colour4, 3%))
+ background: #DC5F59
+ button.oe_highlight
+ @include vertical-gradient(#df3f3f, #a21a1a)
+ @include box-shadow((0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 1px rgba(155, 155, 155, 0.4) inset))
+ button.oe_highlight:hover
+ @include vertical-gradient(lighten(#e04f4f, 2%), lighten(#a21a1a, 2%))
+ button.oe_highlight:active
+ @include vertical-gradient(lighten(#a21a1a, 8%), darken(#df3f3f, 8%))
+ .oe_background_grey
+ background: #eee !important
- button.unfollow
- display: none
- color: white
- width: 120px
- @include vertical-gradient(#dc5f59, #b33630)
+ .oe_form_dirty
+ .oe_highlight_on_dirty
+ color: white
+ background: #dc5f59
+ font-weight: bold
+ button.oe_highlight_on_dirty
+ @include vertical-gradient(#dc5f59, #b33630)
+ @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
+ button
+ margin: 4px
+ .oe_avatar
+ > img
+ max-height: 90px
+ max-width: 90px
+ @include radius(3px)
+ @include box-shadow(0 1px 4px rgba(0, 0, 0, 0.4))
+ border: none
+ margin-bottom: 10px
+ .oe_avatar + div
+ margin-left: 5px
+ .oe_image_small > img
+ max-width: 50px
+ max-height: 50px
+ .oe_image_medium > img
+ max-width: 180px
+ max-height: 180px
+ .oe_button.oe_link
+ @include reset()
+ img
+ display: none
+ span
+ @include reset()
+ color: $link-color
+ font-weight: bold
+ &:hover
+ text-decoration: underline
+ .oe_webclient
+ .oe_star_on, .oe_star_off
+ color: #cccccc
+ text-shadow: 0 0 2px black
+ vertical-align: top
+ position: relative
+ top: -5px
+ .oe_star_on:hover, .oe_star_off:hover
+ text-decoration: none
+ .oe_star_on
+ color: gold
+ // }}}
+ // Tags (for many2many tags, among others) {{{
+ .oe_tag
+ border: 1px solid $tag-border
+ font-size: 11px
+ padding: 2px 4px
+ margin: 0 2px 2px 0
+ @include radius(3px)
+ background: $tag-bg-light
+ color: #4C4C4C
+ .oe_tag_dark
+ background: $tag-bg-dark
+ color: #eee
+ .oe_tags
+ &.oe_inline
+ min-width: 250px
+ .text-wrap
+ width: 100% !important
+ textarea
+ width: 100% !important
+ .text-core .text-wrap .text-dropdown .text-list .text-suggestion em
+ font-style: italic
+ text-decoration: none
+ margin-bottom: 1px
+ // }}}
+ // Tooltips {{{
+ &.oe_tooltip
+ font-size: 12px
+ .oe_tooltip_string
+ color: #FD5
+ font-weight: bold
+ font-size: 13px
+ .oe_tooltip_help
+ white-space: pre-wrap
+ .oe_tooltip_technical
+ padding: 0 0 4px 0
+ margin: 5px 0 0 15px
+ li
+ list-style: circle
+ .oe_tooltip_technical_title
+ font-weight: bold
+ .oe_tooltip_close
+ margin: -5px 0 0 2px
+ cursor: default
+ float: right
+ color: white
+ &:hover
+ color: #999
+ cursor: pointer
+ .oe_tooltip_message
+ max-width: 310px
+ // }}}
+ // Notebook {{{
+ .oe_notebook
+ margin: 8px 0
+ padding: 0 16px
+ list-style: none
+ zoom: 1
+ .oe_notebook.ui-corner-all
+ @include radius(0)
+ .oe_notebook:before, .oe_notebook:after
+ display: table
+ content: ""
+ zoom: 1
+ .oe_notebook:after
+ clear: both
+ .oe_notebook > li
+ float: left
+ .oe_notebook > li > a
+ display: block
+ color: #4c4c4c
+ .oe_notebook
+ border-color: #ddd
+ border-style: solid
+ border-width: 0 0 1px
+ .oe_notebook > li
+ position: relative
+ .oe_notebook > li > a
+ padding: 0 12px
+ margin-right: 2px
+ line-height: 30px
+ border: 1px solid transparent
+ @include radius(4px 4px 0 0)
+ .oe_notebook > li > a:hover
+ text-decoration: none
+ background-color: #eee
+ border-color: #eee #eee #ddd
+ .oe_notebook > li.ui-state-active > a, .oe_notebook > li.ui-state-active > a:hover
+ background-color: #ffffff
+ border: 1px solid #ddd
+ border-bottom-color: transparent
+ cursor: default
+ .oe_notebook_page
+ padding: 0
+ div.ui-tabs
+ padding: 3px 0px 3px 0px
+ .ui-tabs-hide
+ display: none
+ // }}}
+ // Dropdown {{{
+ .oe_dropdown, .oe_dropdown_hover, .oe_dropdown_toggle
+ position: relative
+ cursor: pointer
+ .oe_dropdown_toggle
+ color: rgba(0,0,0,0.5)
+ font-weight: normal
+ .oe_dropdown_hover:hover .oe_dropdown_menu, .oe_dropdown_menu.oe_opened
+ display: block
+ .oe_dropdown_menu
+ display: none
+ position: absolute
+ top: 26px
+ left: 0
+ z-index: 3
+ margin: 0
+ padding: 0
+ border: 1px solid $tag-border
+ background: white
+ padding: 4px 0
+ min-width: 140px
+ text-align: left
+ @include radius(3px)
+ @include box-shadow(0 1px 4px rgba(0,0,0,0.3))
+ > li
+ &:hover
+ @include vertical-gradient(#f0f0fa, #eeeef6)
+ @include box-shadow(none)
+ list-style-type: none
+ float: none
+ display: block
+ position: relative
+ margin: 0
+ padding: 2px 8px
+ > a
+ white-space: nowrap
+ display: block
+ color: #4c4c4c
+ text-decoration: none
+ &:hover
+ text-decoration: none
+ .oe_dropdown_arrow:after
+ width: 0
+ height: 0
+ display: inline-block
+ content: "&darr"
+ text-indent: -99999px
+ vertical-align: top
+ margin-top: 8px
+ //margin-left set at 3px to avoid a strange overflow
+ margin-left: 3px
+ border-left: 4px solid transparent
+ border-right: 4px solid transparent
+ border-top: 4px solid#404040
+ @include opacity(0.5)
+ .oe_sidebar
+ white-space: nowrap
+ .oe_dropdown_menu
+ .oe_sidebar_add_attachment
+ height: 20px
+ cursor: pointer
+ padding-left: 6px
+ margin-top: 6px
+ span
+ font-weight: bold
+ .oe_hidden_input_file
+ width: 200px
+ &:hover
+ @include vertical-gradient(#f0f0fa, #eeeef6)
+ @include box-shadow(none)
+ li
+ .oe_sidebar_delete_item
+ position: absolute
+ top: 4px
+ right: 4px
+ display: none
+ width: 12px
+ height: 12px
+ padding: 1px
+ color: #8786b7
+ line-height: 8px
+ text-align: center
+ font-weight: bold
+ text-shadow: 0 1px 1px white
+ &:hover
+ text-decoration: none
+ color: white
+ background: #8786b7
+ text-shadow: 0 1px 1px rgba(0, 0, 0, 0.4)
+ @include radius(2px)
+ &:hover
+ .oe_sidebar_delete_item
+ display: inline-block
// }}}
// Loading {{{
.oe_loading
@@ -260,13 +668,14 @@ $colour4: #8a89ba
// }}}
// Login {{{
.oe_login
- background: url("/web/static/src/img/pattern.png") repeat
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAGCAYAAADgzO9IAAAAKUlEQVQIHWO8e/fufwYsgAUkJigoiCIF5DMyoYggcUiXgNnBiGQKmAkARpcEQeriln4AAAAASUVORK5CYII=)
text-align: center
font-size: 14px
height: 100%
- li
+ ul, li
+ margin: 0
list-style-type: none
- padding-bottom: 4px
+ padding: 0 0 4px 0
button
float: right
display: inline-block
@@ -283,7 +692,7 @@ $colour4: #8a89ba
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.3))
+ @include box-shadow(inset 0 1px 4px rgba(0, 0, 0, 0.4))
@include radius(3px)
input
margin-bottom: 9px
@@ -383,64 +792,119 @@ $colour4: #8a89ba
.oe_topbar, .oe_leftbar
display: none
// }}}
- // WebClient.topbar {{{
+ // Top Menu {{{
+
.oe_topbar
width: 100%
- height: 31px
- border-top: solid 1px #d3d3d3
+ height: 32px
+ background-color: #414141
@include vertical-gradient(#646060, #262626)
-
.oe_topbar_item
- li
- float: left
- a
- display: block
- padding: 5px 10px 7px
- line-height: 20px
- height: 20px
- color: #eee
- vertical-align: top
- text-shadow: 0 1px 1px rgba(0,0,0,0.2)
- &:hover
- background: #303030
- color: white
- @include box-shadow(0 1px 2px rgba(255,255,255,0.3) inset)
- .oe_active
- background: #303030
- font-weight: bold
+ display: block
+ padding: 5px 10px 7px
+ line-height: 20px
+ height: 20px
+ text-decoration: none
+ color: #eee
+ vertical-align: top
+ text-shadow: 0 1px 1px rgba(0,0,0,0.2)
+ @include transition(all 0.2s ease-out)
+ &:hover,
+ background: rgba(0,0,0,0.2)
+ text-shadow: black 0px 0px 3px
color: white
- @include box-shadow(0 1px 2px rgba(255,255,255,0.3) inset)
+ @include box-shadow(0 1px 2px rgba(0,0,0,0.4) inset)
.oe_topbar_avatar
width: 24px
height: 24px
margin: -2px 2px 0 0
- @include radius(4px)
+ @include radius(3px)
.oe_topbar_avatar
vertical-align: top
- .oe_dropdown_toggle:after
- width: 0
- height: 0
- display: inline-block
- content: "&darr"
- text-indent: -99999px
- vertical-align: top
- margin-top: 8px
- margin-left: 4px
- border-left: 4px solid transparent
- border-right: 4px solid transparent
+ .oe_dropdown_arrow:after
border-top: 4px solid white
- @include opacity(0.5)
+ .oe_dropdown_menu
+ top: 32px
+ background: #333
+ background: rgba(37,37,37,0.9)
+ border-color: #999
+ border-color: rgba(0, 0, 0, 0.2)
+ border-style: solid
+ border-width: 0 1px 1px
+ @include radius(0 0 6px 6px)
+ @include background-clip()
+ li
+ float: none
+ padding: 3px 12px
+ a
+ color: #eee
+ &:hover
+ @include vertical-gradient(#292929, #191919)
+ @include box-shadow(none)
+
+ // oe menu is the list of the buttons on the left side of the bar.
+ // So why aren't the buttons oe_topbar_items ? This sad state of affairs
+ // is a leftover from an uncomplete refactoring when the left and top menu
+ // were merged. You are welcome to refactor and clean this up
+ .oe_menu
+ float: left
+ padding: 0
+ margin: 0
+ font-size: 13px
+ > li
+ list-style-type: none
+ padding: 0
+ margin: 0
+ float: left
+ display: block
+ color: #eeeeee
+ > a
+ display: block
+ padding: 5px 10px 7px
+ line-height: 20px
+ height: 20px
+ text-decoration: none
+ color: #eee
+ vertical-align: top
+ text-shadow: 0 1px 1px rgba(0,0,0,0.2)
+ @include transition(all 0.2s ease-out)
+ &:hover,
+ background: rgba(0,0,0,0.2)
+ text-shadow: black 0px 0px 3px
+ color: white
+ @include box-shadow(0 1px 2px rgba(0,0,0,0.4) inset)
+ > .oe_active
+ background: rgba(0,0,0,0.2)
+ text-shadow: black 0px 0px 3px
+ font-weight: bold
+ @include box-shadow(0 1px 2px rgba(0,0,0,0.4) inset)
+
+ .oe_user_menu
+ float: right
+ padding: 0
+ margin: 0
+ li
+ list-style-type: none
+ float: left
+ .oe_dropdown_menu
+ right: -1px
+
+ .oe_systray > div
+ float: left
+ padding: 0 4px 0 4px
+ .oe_systray
+ float: right
+
// }}}
// Webclient.leftbar {{{
.oe_leftbar
display: none
width: 220px
background: #f0eeee
- border-right: 1px solid #afafb6
+ border-right: 1px solid $tag-border
text-shadow: 0 1px 1px white
padding-bottom: 16px
- line-height: 18px
a.oe_logo
width: 220px
display: block
@@ -467,150 +931,22 @@ $colour4: #8a89ba
color: #c81010
font-style: italic
// }}}
- // UserMenu {{{
- .oe_user_menu
- float: right
- padding: 0
- margin: 0
- li
- list-style-type: none
- float: left
- .oe_dropdown
- position: relative
-
- .oe_dropdown_options
- float: left
- background: #333
- background: rgba(37,37,37,0.9)
- display: none
- position: absolute
- top: 32px
- right: -1px
- border: 0
- z-index: 900
- margin-left: 0
- margin-right: 0
- padding: 6px 0
- zoom: 1
- border-color: #999
- border-color: rgba(0, 0, 0, 0.2)
- border-style: solid
- border-width: 0 1px 1px
- @include radius(0 0 6px 6px)
- @include box-shadow(0 1px 4px rgba(0,0,0,0.3))
- @include background-clip()
- li
- float: none
- display: block
- background-color: none
- a
- white-space: nowrap
- display: block
- padding: 4px 15px
- clear: both
- font-weight: normal
- line-height: 18px
- color: #eee
- &:hover
- @include vertical-gradient(#292929, #191919)
- @include box-shadow(none)
- hr
- border-top: 1px solid #999
- border-bottom: 0
- // }}}
- // Systray {{{
- .oe_systray > div
- float: left
- padding: 0 4px 0 4px
- .oe_systray
- float: right
+ // Webclient.leftbar items {{{
- .oe_systray_dropdown
- position: relative
- .oe_systray_dropdown:hover .oe_systray_dropdown_options
- display: block
- .oe_systray_dropdown_options
- background: #333
- background: rgba(37,37,37,0.9)
- display: none
- position: absolute
- z-index: 900
- border: 0
- margin-left: 0
- margin-right: 0
- padding: 6px 0
- border-color: #999
- border-color: rgba(0, 0, 0, 0.2)
- border-style: solid
- border-width: 0 1px 1px
- @include radius(0 0 6px 6px)
- @include box-shadow(0 1px 4px rgba(0,0,0,0.3))
- @include background-clip()
- li
- float: none
- display: block
- background-color: none
- a
- display: block
- padding: 4px 15px
- clear: both
- font-weight: normal
- line-height: 18px
- color: #eee
- &:hover
- @include vertical-gradient(#292929, #191919)
- @include box-shadow(none)
- // }}}
- // Menu {{{
- .oe_menu
- float: left
- padding: 0
- margin: 0
- li
- float: left
- a
- display: block
- padding: 5px 10px 7px
- line-height: 20px
- height: 20px
- color: #eee
- vertical-align: top
- text-shadow: 0 1px 1px rgba(0,0,0,0.2)
- &:hover
- background: #303030
- color: white
- @include box-shadow(0 1px 2px rgba(255,255,255,0.3) inset)
- .oe_active
- background: #303030
- font-weight: bold
- color: white
- @include box-shadow(0 1px 2px rgba(255,255,255,0.3) inset)
- .oe_menu_more_container
- position: relative
- .oe_menu_more
- position: absolute
- padding: 0
- background-color: #646060
- z-index: 1
- border: 1px solid black
- border-bottom-left-radius: 5px
- border-bottom-right-radius: 5px
- li
- float: none
- a
- white-space: nowrap
.oe_secondary_menu_section
font-weight: bold
margin-left: 8px
- color: $colour4
+ color: $link-color
.oe_secondary_submenu
padding: 2px 0 8px 0
margin: 0
width: 100%
display: inline-block
- > li
+ li
position: relative
- padding: 1px 0 1px 20px
+ margin: 0
+ padding: 1px 0 1px 20px !important
+ list-style-type: none
a
display: block
color: #4c4c4c
@@ -620,7 +956,7 @@ $colour4: #8a89ba
top: 1px
right: 1px
font-size: 10px
- background: $colour4
+ background: $link-color
color: white
padding: 2px 4px
margin: 1px 6px 0 0
@@ -628,32 +964,32 @@ $colour4: #8a89ba
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))
- .oe_menu_counter
+ .oe_menu_counter
float: right
- background: #8a89ba
- color: #eee
- font-size: 12px
- border: 1px solid lightgray
- padding: 0px 4px 0px 4px
text-shadow: 0 1px 1px rgba(0,0,0,0.2)
- margin: 0px
- @include radius(4px)
+ margin: 1px
+ padding: 1px 4px
+ border: none
+ &:hover
+ cursor: pointer
+ @include transform(scale(1.1))
.oe_active
- background: $colour4
border-top: 1px solid lightGray
- border-bottom: 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 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: $colour4
+ color: $link-color
text-shadow: 0 1px 1px white
@include box-shadow(0 1px 1px rgba(0, 0, 0, 0.2))
- .oe_menu_counter
+ .oe_menu_counter
background: #eee
- color: #8a89ba
+ color: $link-color
+ @include box-shadow(inset 0 1px 2px rgba(0, 0, 0, 0.2))
.oe_menu_toggler:before
width: 0
height: 0
@@ -675,123 +1011,139 @@ $colour4: #8a89ba
border-left: 4px solid transparent
border-right: 4px solid transparent
border-top: 4px solid #4c4c4c
+
+ // }}}
+ // About openerp {{{
+ .oe_about
+ background-color: white
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAGCAYAAADgzO9IAAAAKUlEQVQIHWO8e/fufwYsgAUkJigoiCIF5DMyoYggcUiXgNnBiGQKmAkARpcEQeriln4AAAAASUVORK5CYII=)
+ @include radius(0 0 2px 2px)
+ a
+ color: $link-color
+ &:hover
+ text-decoration: underline
+ &:focus
+ outline: none
+ .oe_logo
+ margin-left: -6px
+ .oe_bottom
+ position: absolute
+ top: 50%
+ left: 0
+ right: 0
+ bottom: 0
+ text-shadow: 0 1px 1px #999999
+ @include vertical-gradient(#b41616, #600606)
+ color: #eee
+ padding: 0 16px
+ @include radius(0 0 2px 2px)
+ a
+ color: #eee
// }}}
// ActionManager {{{
.oe_application
width: 100%
a
- color: $colour4
+ color: $link-color
&:hover
text-decoration: underline
- // }}}
- // Content Header MIT {{{
- .oe-view-manager-header
- border-top: 1px solid #cacaca
- border-bottom: 1px solid #cacaca
- @include vertical-gradient(#fcfcfc, #dedede)
- @include box-shadow((0 1px 0 rgba(255,255,255,0.4), 0 0 9px rgba(0,0,0,0.1)))
- h2
- float: left
- font-size: 18px
- margin: 1px 0
- a
- color: $colour4
- .buttons
- padding-right: 8px
- li
- float: left
- margin-right: 12px
- .filter
- vertical-align: top
- li
- height: 24px
- line-height: 24px
- padding: 0 8px
- .header-row
- height: 26px
- line-height: 26px
- margin: 8px
- clear: both
- text-shadow: 0 1px 1px white
- .dropdown-toggle:after
- margin-top: 7px
- margin-left: 6px
- border-top-color: #404040
- .oe_vm_switch
- li
- padding-left: 1px
- text-align: center
- width: 24px
- height: 24px
- line-height: 24px
- .pagination
- li
- height: 24px
- line-height: 24px
- padding: 0 8px
- .button-group
+ > div
+ height: 100%
+ .oe_breadcrumb_item:not(:last-child)
display: inline-block
- border: 1px solid #ababab
- @include radius(5px)
- li
- float: left
- border-right: 1px solid #ababab
- &:last-child
- border: none
- a
- color: #4c4c4c
- &:hover
- text-decoration: none
- .active
- width: 100%
- background: #999
- @include box-shadow(0 1px 4px rgba(0,0,0,0.3) inset)
- color: #fff
- text-shadow: 0 1px 2px rgba(0,0,0,0.4)
+ max-width: 7em
+ white-space: nowrap
+ overflow: hidden
+ text-overflow: ellipsis
// }}}
- // ViewManager.header {{{
- .oe_view_manager_header
- width: 100%
- border-top: 1px solid #cacaca
- border-bottom: 1px solid #cacaca
- @include vertical-gradient(#fcfcfc, #dedede)
- @include box-shadow((0 1px 0 rgba(255,255,255,0.4), 0 0 9px rgba(0,0,0,0.1)))
- .oe_header_row
- //min-height: 26px
- //line-height: 26px
- clear: both
- text-shadow: 0 1px 1px white
- td
- padding: 8px
- .oe_header_row:last-child
- td
- padding-top: 0
- .oe_header_row:first-child
+ // ViewManager common {{{
+ .oe_view_manager
+ .oe_view_manager_body
+ height: inherit
+ .oe_view_manager_view_kanban
+ height: inherit
+
+ table.oe_view_manager_header
+ width: 100%
+ table-layout: fixed
+ .oe_header_row
+ //min-height: 26px
+ //line-height: 26px
+ clear: both
+ text-shadow: 0 1px 1px white
+ .oe_header_row:last-child
+ td
+ padding-top: 0
+ .oe_header_row:first-child
+ td
+ padding-top: 8px
+ .oe_view_manager_sidebar
+ margin: 0px auto
+ text-align: center
td
- padding-top: 8px
- .oe_view_manager_sidebar
- margin: 0px auto
- width: 400px
- text-align: center
- td
+ line-height: 26px
+ h2
+ font-size: 18px
+ margin: 0
+ float: left
+ a
+ color: $link-color
+ .oe_dropdown_menu
+ line-height: normal
+ .oe_button_group
+ display: inline-block
+ border: 1px solid #ababab
+ @include radius(5px)
+ li
+ float: left
+ border-right: 1px solid #ababab
+ &:last-child
+ border: none
+ a
+ color: #4c4c4c
+ &:hover
+ text-decoration: none
+ .active
+ background: #999
+ @include box-shadow(0 1px 4px rgba(0,0,0,0.3) inset)
+ a
+ color: #fff
+ text-shadow: 0 1px 2px rgba(0,0,0,0.4)
+ .oe_view_manager_buttons
+ white-space: nowrap
+ // }}}
+ // ViewManager.pager {{{
+ .oe_view_manager_pager
line-height: 26px
- h2
- font-size: 18px
+ .oe_list_pager_single_page .oe_pager_group
+ display: none
+ .oe_pager_value
+ float: left
+ margin-right: 8px
+ ul.oe_pager_group
+ padding: 0
margin: 0
+ .oe_pager_group
float: left
- a
- color: $colour4
- .oe_button_group
+ height: 24px
+ line-height: 24px
display: inline-block
border: 1px solid #ababab
+ cursor: pointer
@include radius(5px)
li
+ height: 24px
+ line-height: 24px
+ padding: 0
+ margin: 0
+ list-style-type: none
float: left
border-right: 1px solid #ababab
&:last-child
border: none
a
color: #4c4c4c
+ padding: 0 8px
&:hover
text-decoration: none
.active
@@ -800,128 +1152,70 @@ $colour4: #8a89ba
a
color: #fff
text-shadow: 0 1px 2px rgba(0,0,0,0.4)
- .oe_view_manager_buttons
- white-space: nowrap
- // }}}
- // ViewManager.pager {{{
- .oe_view_manager_pager
- line-height: 26px
- .oe_pager_value
- float: left
- margin-right: 8px
- .oe_pager_group
- float: left
- height: 24px
- line-height: 24px
- display: inline-block
- border: 1px solid #ababab
- cursor: pointer
- @include radius(5px)
- li
- height: 24px
- line-height: 24px
+ // }}}
+ // ViewManager.switches {{{
+ .oe_view_manager_switch
padding: 0
- float: left
- border-right: 1px solid #ababab
- &:last-child
- border: none
- a
- color: #4c4c4c
- padding: 0 8px
- &:hover
- text-decoration: none
- .active
- background: #999
- @include box-shadow(0 1px 4px rgba(0,0,0,0.3) inset)
- a
- color: #fff
- text-shadow: 0 1px 2px rgba(0,0,0,0.4)
- // }}}
- // ViewManager.switches {{{
- .oe_view_manager_switch
- li
- text-align: center
- width: 24px
- height: 24px
- line-height: 16px
- a
+ margin: 0 0 0 8px
+ li
+ margin: 0
+ width: 24px
+ height: 24px
+ line-height: 16px
+ padding: 0
+ text-align: center
+ list-style-type: none
+ a
+ position: relative
+ .oe_vm_switch_list:after, .oe_vm_switch_tree:after
+ padding: 2px
+ content: "i"
+ .oe_vm_switch_form:after
+ content: "m"
+ .oe_vm_switch_graph:after
+ font-family: "mnmliconsRegular" !important
+ font-size: 21px
+ font-weight: 300 !important
+ content: "}"
+ top: -2px
position: relative
- .oe_vm_switch_list:after, .oe_vm_switch_tree:after
- padding: 2px
- content: "i"
- .oe_vm_switch_graph:after
- font-family: "mnmliconsRegular" !important
- font-size: 21px
- font-weight: 300 !important
- content: "}"
- top: -2px
- position: relative
- .oe_vm_switch_gantt:after
- font-family: "mnmliconsRegular" !important
- font-size: 21px
- font-weight: 300 !important
- content: "y"
- top: -2px
- position: relative
- .oe_vm_switch_calendar:after
- content: "P"
- .oe_vm_switch_kanban:after
- content: "k"
- .oe_vm_switch_diagram:after
- content: "f"
+ .oe_vm_switch_gantt:after
+ font-family: "mnmliconsRegular" !important
+ font-size: 21px
+ font-weight: 300 !important
+ content: "y"
+ top: -2px
+ position: relative
+ .oe_vm_switch_calendar:after
+ content: "P"
+ .oe_vm_switch_kanban:after
+ content: "k"
+ .oe_vm_switch_diagram:after
+ content: "f"
+ // }}}
+ // ViewManager application {{{
+ .oe_view_manager_current
+ height: 100%
+ > .oe_view_manager_header
+ border-bottom: 1px solid #cacaca
+ @include vertical-gradient(#fcfcfc, #dedede)
+ @include box-shadow((0 1px 0 rgba(255,255,255,0.4), 0 0 9px rgba(0,0,0,0.1)))
+ .oe_header_row
+ td
+ padding: 8px
+ .oe_header_row:first-child
+ td
+ padding-top: 8px
// }}}
- // ViewManager.sidebar {{{
- .oe_form_dropdown_section
- position: relative
- display: inline-block
- .oe_dropdown_toggle:after
- width: 0
- height: 0
- display: inline-block
- content: "&darr"
- text-indent: -99999px
- vertical-align: top
- border-left: 4px solid transparent
- border-right: 4px solid transparent
- border-top: 4px solid white
- @include opacity(0.5)
- margin-top: 7px
- margin-left: 6px
- border-top-color: #404040
- .oe_dropdown_menu
- display: none
- position: absolute
- top: 28px
- left: 0px
- padding: 8px
- border: 1px solid #afafb6
- min-width: 120px
- overflow-x: hidden
- z-index: 900
- text-align: left
- background: white
- @include radius(3px)
- @include box-shadow(0 1px 4px rgba(0,0,0,0.3))
- li
- list-style-type: none
- float: none
- display: block
- background-color: none
- a
- display: block
- padding: 3px 6px
- clear: both
- font-weight: normal
- line-height: 14px
- color: #4c4c4c
- text-decoration: none
- cursor: pointer
- &:hover
- text-decoration: none
- @include vertical-gradient(#f0f0fa, #eeeef6)
- @include box-shadow(none)
- .oe_sidebar
- white-space: nowrap
+ // ViewManager inline {{{
+ .oe_view_manager_inline, .oe_view_manager_inlineview
+ height: 100%
+ > .oe_view_manager_header
+ display: none
+ // }}}
+ // Viewmanager popup {{{
+ .oe_popup_form > .oe_formview > .oe_form_pager
+ display: none !important
// }}}
// SearchView {{{
.oe_searchview
@@ -930,7 +1224,7 @@ $colour4: #8a89ba
float: right
padding: 1px 0
line-height: 18px
- width: 480px
+ width: 400px
border: 1px solid #ababab
background: white
@include radius(13px)
@@ -938,13 +1232,11 @@ $colour4: #8a89ba
input, textarea
padding: 3px
height: 14px
- font-size: 11px
+ font-size: 12px
line-height: 18px
- border: 1px solid #ccc
- @include radius(3px)
&.oe_focused
- border-color: $facets-border-selected
- @include box-shadow(0 1px 2px $facets-border-selected inset)
+ border-color: $tag-border-selected
+ @include box-shadow(0 1px 2px $tag-border-selected inset)
.oe_searchview_clear
cursor: pointer
position: absolute
@@ -977,42 +1269,46 @@ $colour4: #8a89ba
border-right: 5px solid transparent
@include opacity()
+ .oe_searchview_search
+ @include text-to-icon("r", #a3a3a3)
+ @include box-shadow(none)
+ @include radius(0)
+ position: absolute
+ left: 3px
+ top: 1px
+ padding: 0
+ border: none
+ background: transparent
+
.oe_searchview_facets
min-height: 22px
- &:before
- color: #ccc
- font-family: "mnmliconsRegular"
- content: "r"
- font-size: 130%
- display: inline
- position: relative
- left: 6px
- top: 2px
- color: #a3a3a3
- padding-right: 4px
+ margin-left: 15px
*
vertical-align: top
display: inline-block
line-height: 17px
- .oe_searchview_input, .oe_searchview_facet
+ .oe_searchview_facet
height: 18px
margin: 1px 0
font-size: 11px
&:focus
outline: none
.oe_searchview_input
- padding: 0 3px
+ padding: 0 0 0 6px
+ font-size: 12px
+ height: 16px
+ margin-top: 3px
+ &:focus
+ outline: none
.oe_searchview_facet
position: relative
cursor: pointer
- border: 1px solid $facets-border
- @include radius(3px)
- background: #8786b7
+ padding: 0
-webkit-font-smoothing: auto
// spacing for opera, FF
&:focus
- border-color: $facets-border-selected
- @include box-shadow(0 0 3px 1px $facets-border-selected)
+ border-color: $tag-border-selected
+ @include box-shadow(0 0 3px 1px $tag-border-selected)
.oe_facet_values
background: #f0f0fa
@include radius(0 3px 3px 0)
@@ -1025,8 +1321,9 @@ $colour4: #8a89ba
.oe_facet_category.oe_i
font-size: 16px
.oe_facet_value
- border-left: 1px solid $facets-border
+ border-left: 1px solid $tag-border
text-shadow: 0 1px 1px white
+ color: #4C4C4C
&:last-child
padding-right: 16px
.oe_facet_remove
@@ -1037,6 +1334,7 @@ $colour4: #8a89ba
line-height: 8px
width: 12px
height: 12px
+ padding-top: 1px
text-align: center
font-weight: bold
cursor: pointer
@@ -1052,7 +1350,7 @@ $colour4: #8a89ba
.oe_searchview_drawer
position: absolute
- z-index: 1
+ z-index: 100
// detach drawer from field slightly
margin-top: 4px
top: 100%
@@ -1060,7 +1358,7 @@ $colour4: #8a89ba
background-color: white
min-width: 100%
display: none
- border: 1px solid #afafb6
+ border: 1px solid $tag-border
text-align: left
@include radius(4px)
@include box-shadow(0 1px 4px rgba(0,0,0,0.3))
@@ -1097,7 +1395,7 @@ $colour4: #8a89ba
display: table
width: 100%
> div
- @include unscrew-box
+ @include box-sizing(border)
display: table-cell
width: 50%
ul
@@ -1123,13 +1421,13 @@ $colour4: #8a89ba
// after oe_selected so background color is not overridden
&:hover
background-color: $hover-background
- form
- margin-left: 12px
- p
- margin: 4px 0
- line-height: 18px
- button
- margin: 0 0 8px 0
+ form
+ margin-left: 12px
+ p
+ margin: 4px 0
+ line-height: 18px
+ button
+ margin: 0 0 8px 0
.oe_searchview_custom
padding: 0 8px 8px 8px
form
@@ -1145,6 +1443,11 @@ $colour4: #8a89ba
position: absolute
top: 0
right: 5px
+ .oe_searchview_dashboard
+ form
+ display: none
+ margin-top: 2px
+
.oe_searchview_advanced
form
display: none
@@ -1172,13 +1475,12 @@ $colour4: #8a89ba
display: block
// delete buttons
- .oe_searchview_custom_delete
- position: absolute
- left: 4px
- top: 3px
.oe_searchview_custom_delete, .searchview_extended_delete_prop
+ display: inline-block
width: 12px
height: 12px
+ line-height: 12px
+ padding: 1px
color: #8786b7
line-height: 8px
text-align: center
@@ -1190,172 +1492,185 @@ $colour4: #8a89ba
background: #8786b7
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.4)
@include radius(2px)
+ .oe_searchview_custom_delete
+ display: none
+ position: absolute
+ bottom: 1px
+ right: 4px
+ .oe_searchview_custom_private, .oe_searchview_custom_public
+ &:hover
+ .oe_searchview_custom_delete
+ display: inline-block
+ .oe_searchview_custom_public:after
+ content: ","
+ font-family: "entypoRegular" !important
+ font-size: 22px
+ font-weight: 300 !important
+ margin: 0 0 0 4px
+ padding: 0
+
+
// }}}
// Views Common {{{
.oe_view_nocontent
- > img
- float: left
- margin: 1.5em
- > div
- // don't encroach on my arrow
- overflow: hidden
- padding: 35px 0px 0px 0px
- max-width: 700px
- font-size: 125%
- .oe_view_topbar
- border-bottom: 1px solid #cacaca
- @include vertical-gradient(#fcfcfc, #dedede)
- padding: 0 8px
- line-height: 30px
+ padding: 15px
+ margin-top: 0
+ color: #777777
+ font-size: 125%
+ max-width: 700px
+ .oe_view_nocontent_create
+ background: transparent url(/web/static/src/img/view_empty_arrow.png) no-repeat 7px 0
+ margin-top: 0
+ padding-top: 35px
+ min-height: 28px
+ color: #4c4c4c
+ > p
+ padding-left: 95px
+ .oe_empty_custom_dashboard
+ background: transparent url(/web/static/src/img/graph_background.png) no-repeat 0 0
+ margin-top: -15px
+ padding: 100px 0 0 137px
+ min-height: 327px
+ margin-left: -15px
+
// }}}
- // FormView.body {{{
+ // FormView.base and dynamic tags {{{
.oe_formview
background: white
- .oe_form_header
- padding: 8px
+ .oe_form_dropdown_section
+ position: relative
+ display: inline-block
.oe_form_invalid
input, select, textarea
background-color: #F66 !important
border: 1px solid #D00 !important
- .oe_form_button_save_dirty
- color: white
- background: #dc5f59
- background: -moz-linear-gradient(#dc5f59, #b33630)
- background: -webkit-gradient(linear, left top, left bottom, from(#dc5f59), to(#b33630))
- background: -webkit-linear-gradient(#dc5f59, #b33630)
- -moz-box-shadow: none
- -webkit-box-shadow: none
- -box-shadow: none
- font-weight: bold
- &:hover
- background: #ED6F6A
- // }}}
- // FormView.customdivs {{{
- .oe_form header
- position: relative
- border-bottom: 1px solid #cacaca
- @include vertical-gradient(#fcfcfc, #dedede)
- padding: 0 8px
- line-height: 30px
- button
- font-size: 12px
- float: left
- height: 24px !important
- line-height: 24px
- vertical-align: top
- padding: 0 10px
- margin: 3px 4px 3px 0
- span.i
- line-height: 20px
- height: 24px
- ul
- height: 30px
- padding: 0
- margin: 0
- text-shadow: 0 1px 1px white
- border-left: 1px solid #cacaca
- border-right: 1px solid #cacaca
- li
- padding: 0
- margin: 0
- float: left
- vertical-align: top
- border-right: 1px solid #cacaca
- height: 30px
- padding: 0 12px
- &:first-child
- border-left: 1px solid #cacaca
- a
- color: #4c4c4c
- &:hover
- color: black
- .oe_form_steps
- display: inline-block
- float: right
- img
- margin: 0 8px
- vertical-align: top
- li
- border-right: none
- padding: 0
- &:first-child
- margin-left: 12px
- border-left: none
- &:last-child
- margin-right: 12px
- .oe_form_steps_active
- font-weight: bold
- color: #b33630
+ .oe_view_manager_current .oe_form_editable
+ .oe_highlight
+ color: #404040
+ background: none
+ button.oe_highlight
+ @include vertical-gradient(#efefef, #d8d8d8)
+ @include box-shadow((0 1px 2px rgba(0, 0, 0, .1), 0 1px 1px rgba(255, 255, 255, .8) inset))
+ button.oe_highlight:active
+ @include vertical-gradient(#e3e3e3, #f6f6f6)
+ @include box-shadow(none)
+ button.oe_highlight:hover
+ @include vertical-gradient(#f6f6f6, #e3e3e3)
+ @include box-shadow((0 1px 2px rgba(0, 0, 0, .1), 0 1px 1px rgba(255, 255, 255, .8) inset))
+ .oe_form_invisible
+ display: none !important
.oe_form_readonly
- button.oe_form_button_hi
- color: white
- background: #DC5F59
- background: -moz-linear-gradient(#DC5F59, #B33630)
- background: -webkit-gradient(linear, left top, left bottom, from(#DC5F59), to(#B33630))
- background: -webkit-linear-gradient(#DC5F59, #B33630)
- -moz-box-shadow: none
- -webkit-box-shadow: none
- -box-shadow: none
- button.oe_form_button_hi:hover
- background: #DF6B66
- background: -moz-linear-gradient( #DF6B66, #BF3A33)
- background: -webkit-gradient(linear, left top, left bottom, from( #DF6B66), to( #BF3A33))
- background: -webkit-linear-gradient( #DF6B66, #BF3A33)
- -moz-box-shadow: 0 0 1px rgba(0, 0, 0, 0.2)
- -webkit-box-shadow: 0 0 1px rgba(0, 0, 0, 0.2)
- -box-shadow: 0 0 1px rgba(0, 0, 0, 0.2)
+ .oe_edit_only, .oe_form_field:empty
+ display: none !important
+ .oe_form .oe_form_field_date
+ width: auto
+ // Sheet and padding
+ .oe_form_nosheet
+ margin: 16px
+ > header
+ margin: -16px -16px 0 -16px
+ padding: 0
+ .oe_form_sheetbg
+ padding: 16px 0
+ .oe_form_sheet_width
+ min-width: 650px
+ max-width: $sheet-max-width
+ margin: 0 auto
+ .oe_form_sheet
+ background: white
+ min-height: 330px
+ padding: 16px
+ // Sheet inline mode
.oe_application
.oe_form_sheetbg
background: url(/web/static/src/img/form_sheetbg.png)
- padding: 8px 0
- border-bottom: 1px solid #ddd
- .oe_form_sheet_width, footer
+ border-bottom: 1px solid #ddd
+ .oe_form_sheet
+ border: 1px solid #c8c8d3 //$tag-border
+ @include box-shadow(0 4px 20px rgba(0,0,0,0.15))
+ .ui-tabs
+ margin: 0 -16px
+ .oe_notebook_page
+ padding: 0 16px
+ // }}}
+ // FormView.custom tags and classes {{{
+ .oe_form
+ header
+ position: relative
+ border-bottom: 1px solid #cacaca
+ padding-left: 2px
+ @include vertical-gradient(#fcfcfc, #dedede)
+ > span
+ margin-left: 4px
+ ul
+ display: inline-block
+ float: right
+ .oe_form_button
+ margin: 3px 2px 1px
+ .oe_tags
+ margin: 5px 0 0 5px
+ width: 400px
+ padding-bottom: 0
+ div.oe_chatter
min-width: 650px
- max-width: 960px
+ max-width: $sheet-max-width
margin: 0 auto
- .oe_form_sheet
- padding: 18px
- background: white
- min-height: 330px
- border: 1px solid #afafb6
- @include box-shadow(0 0 10px rgba(0,0,0,0.3))
+ padding: 16px 0 48px
+ .oe_grey
+ color: #aaa
+ max-width: 650px
+ margin: 0 0 10px 0
+ div.oe_form_configuration
+ p, ul, ol
+ color: #aaa
+ max-width: 650px
+ label
+ min-width: 150px
+ .oe_form_group_cell_label
+ padding: 1px 0
+ .oe_form_group_cell div div
+ padding: 1px 0
+ .oe_subtotal_footer
+ width: 1% !important
+ td.oe_form_group_cell
+ text-align: right
+ padding: 0 !important
+ td.oe_form_group_cell_label
+ border-right: none
+ .oe_subtotal_footer_separator
+ width: 108px
+ border-top: 1px solid #cacaca
+ margin-top: 4px
+ padding-top: 4px
+ font-weight: bold
+ font-size: 18px
+ label:after
+ content: ":"
+ label.oe_subtotal_footer_separator
+ font-weight: bold !important
+ padding: 2px 11px 2px 0px !important
+ label.oe_form_label_help
+ font-weight: normal
+
// }}}
- // FormView.layout {{{
- .oe_form_invisible
- display: none
- .oe_form_editable .oe_form_editable_hidden
- display: none
- .oe_form_readonly
- .oe_form_readonly_hidden, .oe_form_field:empty
- display: none
+ // FormView.group {{{
.oe_form
- .oe_form_field_text
+ .oe_form_button
+ margin: 2px
+ td.oe_form_group_cell_label
+ border-right: 1px solid #ddd
+ padding: 2px 0px
+ label
+ line-height: 18px
+ display: block
+ min-width: 140px
+ td.oe_form_group_cell + .oe_form_group_cell
+ padding: 2px 0 2px 8px
+ .oe_form_group
width: 100%
-
- .oe_form_field_char input,
- .oe_form_field_text textarea,
- .oe_form_field_url input,
- .oe_form_field_selection select
- width: inherit
-
- .oe_form_field_float input
- width: 100px
-
- h1 .oe_form_field_float input
- width: 140px
-
- h2 .oe_form_field_float input
- width: 120px
-
- h1, h2, h3, h4, h5, h6
- margin: 0 0 4px 0
- input
- height: inherit !important
- font-size: inherit
- .oe_form_nosheet > *:not(.oe_form_topbar)
- margin-left: 10px !important
- margin-right: 10px !important
-
+ margin: 9px 0 9px 0
+ .oe_form_group_cell.oe_group_right
+ padding-left: 20px
// }}}
// FormView.label {{{
.oe_form
@@ -1371,57 +1686,73 @@ $colour4: #8a89ba
top: -4px
padding: 0 2px
// }}}
+ // FormView.separator {{{
+ .oe_horizontal_border
+ border-bottom: 1px solid black
+ .oe_horizontal_separator
+ font-weight: bold
+ font-size: 20px
+ margin: 15px 0px 10px 0px
+ color: $section-title-color
+ .oe_horizontal_separator:empty
+ height: 5px
+ .oe_vertical_separator
+ border-left: 1px solid #666
+ padding: 0 4px 0 4px
+ // }}}
+ // FormView.progressbar {{{
+ .oe_form_field_progressbar
+ display: inline-block
+ min-width: 70px
+ .oe_form_field_progressbar.ui-progressbar
+ height: 22px
+ font-size: 10px
+ @include box-sizing(border)
+ border: 1px solid #999
+ @include radius(3px)
+ background: white
+ min-width: 50px
+ span
+ position: absolute
+ margin-left: 10px
+ font-weight: bold
+ .ui-widget-header
+ background: #cccccc url(/web/static/lib/jquery.ui/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png) 50% 50% repeat-x
+ // }}}
// FormView.fields {{{
.oe_form
- .oe_form_field_text textarea
- resize: vertical
+ .oe_form_field_text
+ width: 100%
+ .oe_form_field_char input,
+ .oe_form_field_url input,
+ .oe_form_field_email input,
+ .oe_form_field_text textarea,
+ .oe_form_field_selection select
+ width: 100%
+ .oe_form_field_text.oe_inline, .oe_form_field_text.oe_inline > textarea
+ width: 500px
+ h1, h2, h3, h4, h5, h6
+ margin: 0 0 4px 0
+ input
+ height: inherit !important
+ font-size: inherit
.oe_form_field
width: 100%
display: inline-block
padding: 2px 2px 2px 0px
- line-height: 18px
input
margin: 0px
-
- .oe_form_field_integer
- width: 180px
- .oe_form_field_float
- width: 180px
- .oe_form_field_date
- width: 7em
- .oe_form_field_datetime
- width: 11em
- .oe_form_inline
- width: auto
-
- input[type="text"],
- input[type="password"],
- input[type="file"],
- select
+ input[type="text"], input[type="password"], input[type="file"], select
height: 22px
padding-top: 2px
- input[type="text"],
- input[type="password"],
- input[type="file"],
- select,
- textarea
- -moz-box-sizing: border-box
- -webkit-box-sizing: border-box
- -ms-box-sizing: border-box
- box-sizing: border-box
- padding: 0 2px
- border: 1px solid #999
- -moz-border-radius: 3px
- -webkit-border-radius: 3px
- border-radius: 3px
+ input[type="text"], input[type="password"], input[type="file"], select, textarea
+ @include box-sizing(border)
background: white
- min-width: 70px
+ min-width: 60px
color: #1f1f1f
- input[readonly],
- select[readonly],
- textarea[readonly],
- input[disabled],
- select[disabled]
+ textarea
+ height: 32px
+ input[readonly], select[readonly], textarea[readonly], input[disabled], select[disabled]
background: #E5E5E5 !important
color: #666
textarea[disabled]
@@ -1430,19 +1761,6 @@ $colour4: #8a89ba
padding-left: 8px
@include box-shadow(none)
@include radius(0px)
- .oe_form_field_many2one input,
- .oe_form_field_binary input,
- .oe_form_field_binary input,
- .oe_form_field_email input,
- .oe_form_field_url input
- border-right: none
- -webkit-border-top-right-radius: 0px
- -webkit-border-bottom-right-radius: 0px
- -moz-border-radius-topright: 0px
- -moz-border-radius-bottomright: 0px
- border-top-right-radius: 0px
- border-bottom-right-radius: 0px
- .oe_form_field_email button img,
.oe_form_field_url button img
vertical-align: top
.oe_form_field_date,
@@ -1450,26 +1768,7 @@ $colour4: #8a89ba
white-space: nowrap
.oe_form_field_boolean
padding-top: 4px
- .oe_form_field_many2manytags
- .text-wrap
- width: 100% !important
- textarea
- width: 100% !important
- .oe_form_field_many2manytags_box
- border-radius: 2px
- box-sizing: border-box
- position: relative
- float: left
- border: 1px solid #9DACCC
- background: #E2E6F0
- color: black
- padding: 0px 3px 0px 3px
- margin: 0 2px 2px 0
- height: 16px
- font: 11px "lucida grande", tahoma, verdana, arial, sans-serif
- .text-core .text-wrap .text-dropdown .text-list .text-suggestion em
- font-style: italic
- text-decoration: none
+ width: auto
.oe_datepicker_container
display: none
.oe_datepicker_root
@@ -1492,90 +1791,62 @@ $colour4: #8a89ba
filter: alpha(opacity=50)
right: 5px
top: 3px
- .oe_form_group_cell .oe_kanban_view
- // Hack due to absolute positioning of kanban view messing with o2m
- position: static
- .oe_form_field_with_button
- input
- width: 100%
+ .oe_form_field_with_button.oe_no_button > .oe_button
+ display: none
+ .oe_form_field_with_button:not(.oe_no_button)
> .oe_button
float: right
- -webkit-border-top-left-radius: 0px
- -webkit-border-bottom-left-radius: 0px
- -moz-border-radius-topleft: 0px
- -moz-border-radius-bottomleft: 0px
- border-top-left-radius: 0px
+ @include radius(0)
border-bottom-left-radius: 0px
height: 22px
+ .oe_form_field_with_button
+ input
+ width: 100%
> div
position: relative
overflow: hidden
-
- .oe_form_title
- width: 55%
- .oe_form_title:after
- content: "."
- display: block
- height: 0
- clear: both
- visibility: hidden
- .oe_form_button_box
- width: 200px
- text-align: center
-
- .oe_form_group
- margin: 5px 0px 5px 0px
-
- .oe_form_group_cell .oe_kanban_view
- // Hack due to absolute positioning of kanban view messing with o2m
- position: static
-
- // }}}
- // FormView.buttons {{{
- .oe_form
- .oe_form_button.oe_button
- height: 22px
- white-space: nowrap
- span
- position: relative
- vertical-align: top
- .oe_form_button > img
- vertical-align: -3px
- padding: 0 2px
- // From FP: .oe_form_group_cell > .oe_form_button.oe_button
- // From FP: display: block
- // From FP: white-space: nowrap
- // From FP: min-width: 100%
- // From FP: width: 100%
-
+ .oe_form_field_html
+ position: relative
+ width: 600px
+ margin-left: 130px
+ margin-top: 32px
+ margin-bottom: 32px
+ text-align: justify
+
+ .oe_form_editable
+ .oe_form
+ .oe_form_field_integer
+ width: 6em !important
+ .oe_form_field_float
+ width: 7em !important
+ .oe_form_field_date
+ width: 7.5em !important
+ .oe_form_field_datetime
+ width: 11.5em !important
// }}}
- // FormView.binaryfile {{{
+ // FormView.fields_binary {{{
/* http://www.quirksmode.org/dom/inputfile.html
* http://stackoverflow.com/questions/2855589/replace-input-type-file-by-an-image
*/
- .oe_form
- .oe-binary-file-set
- overflow: hidden
- position: relative
- display: inline-block
- width: 45px
- height: 30px
- input.oe-binary-file
+ .oe_hidden_input_file
+ // Position: relative is used for the hidden input[type=file]
+ // Do not remove it anymore !
+ position: relative
+ input.oe_form_binary_file
z-index: 0
line-height: 0
font-size: 12px
position: absolute
- // Should be adjusted for all browsers
top: 1px
- right: 10px
+ left: 0
+ right: 0
opacity: 0
filter: alpha(opacity = 0)
-ms-filter: "alpha(opacity=0)"
margin: 0
padding: 0
+ .oe_form
.oe_form_field_image
- > img
- min-width: 100px
padding: 0
position: relative
display: inline-block
@@ -1584,202 +1855,271 @@ $colour4: #8a89ba
.oe_form_field_image_controls
position: absolute
top: 1px
- padding: 3px 0 0 0
- margin: 0 1px
- @include opacity(0.7)
- width: 99%
+ padding: 4px
+ width: 100%
+ display: none
text-align: center
- background: url(/web/static/src/img/form_sheetbg.png)
- border-bottom: 1px dotted black
- -moz-box-sizing: border-box
- -webkit-box-sizing: border-box
- -ms-box-sizing: border-box
- box-sizing: border-box
+ color: #eee
+ background: rgba(37,37,37,0.9)
+ @include radius(3px 3px 0 0)
+ @include box-sizing(border)
&:hover .oe_form_field_image_controls
- @include opacity(1)
- // }}}
- // FormView.separator {{{
- .oe_horizontal_border
- border-bottom: 1px solid black
- .oe_horizontal_separator
- font-weight: bold
- font-size: 20px
- margin: 20px 0px 10px 0px
- color: #aab
- .oe_horizontal_separator:empty
- height: 5px
- .oe_vertical_separator
- border-left: 1px solid #666
- padding: 0 4px 0 4px
+ display: block
// }}}
- // FormView.notebook {{{
- .oe_form_notebook
- margin: 8px 0
- padding: 0 8px
- list-style: none
- zoom: 1
- .oe_form_notebook:before, .oe_form_notebook:after
- display: table
- content: ""
- zoom: 1
- .oe_form_notebook:after
- clear: both
- .oe_form_notebook > li
- float: left
- .oe_form_notebook > li > a
- display: block
- color: #4c4c4c
- .oe_form_notebook
- border-color: #ddd
- border-style: solid
- border-width: 0 0 1px
- .oe_form_notebook > li
- position: relative
- margin-bottom: -1px
- .oe_form_notebook > li > a
- padding: 0 12px
- margin-right: 2px
- line-height: 30px
- border: 1px solid transparent
- @include radius(4px 4px 0 0)
- .oe_form_notebook > li > a:hover
- text-decoration: none
- background-color: #eee
- border-color: #eee #eee #ddd
- .oe_form_notebook > li.ui-state-active > a, .oe_form_notebook > li.ui-state-active > a:hover
- background-color: #ffffff
- border: 1px solid #ddd
- border-bottom-color: transparent
- cursor: default
- .oe_form_notebook_page
- padding: 0
- div.ui-tabs
- padding: 3px 0px 3px 0px
- .ui-tabs-hide
- display: none
-
+ // FormView.many2one {{{
+ .oe_form_field_many2one
+ td:first-child
+ position: relative
+ span.oe_m2o_drop_down_button
+ position: absolute
+ top: 2px
+ right: 0px
+ .oe_m2o_cm_button
+ line-height: 14px
+ float: right
+ padding-left: 2px
+ &.ui-autocomplete
+ li.oe_m2o_dropdown_option a
+ font-style: italic
+ padding-left: 2em
+ li:not(.oe_m2o_dropdown_option) + li.oe_m2o_dropdown_option
+ margin-top: 10px
// }}}
- // FormView.progressbar {{{
- .oe_form
- .oe_form_field_progressbar
+ // FormView.fieldstatus {{{
+ ul.oe_form_status, ul.oe_form_status_clickable
+ display: inline-block
+ margin: 0
+ padding: 0 18px 0 0
+ li
display: inline-block
- min-width: 70px
- .oe_form_field_progressbar.ui-progressbar
- height: 22px
- font-size: 10px
- -moz-box-sizing: border-box
- -webkit-box-sizing: border-box
- -ms-box-sizing: border-box
- box-sizing: border-box
- border: 1px solid #999
- -moz-border-radius: 3px
- -webkit-border-radius: 3px
- border-radius: 3px
- background: white
- min-width: 50px
-
- span
- position: absolute
- margin-left: 10px
- font-weight: bold
-
- .ui-widget-header
- background: #cccccc url(/web/static/lib/jquery.ui/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png) 50% 50% repeat-x
+ list-style-type: none
+ // It is probably a bad idea to modify this margin
+ margin: 0 -18px 0 0
+ padding: 0
+ @include vertical-gradient(#fcfcfc, #dedede)
+ &:first-child .label
+ border-left: 1px solid #cacaca
+ padding-left: 14px
+ &:last-child
+ border-right: 1px solid #cacaca
+ .label
+ padding-right: 14px
+ .arrow
+ display: none
+ .label
+ color: #4c4c4c
+ text-shadow: 0 1px 1px #fcfcfc, 0 -1px 1px #dedede
+ padding: 7px
+ display: inline-block
+ padding-left: 24px
+ margin: 0
+ position: relative
+ .arrow
+ width: 17px
+ display: inline-block
+ vertical-align: top
+ overflow: hidden
+ margin-left: -5px
+ span
+ position: relative
+ width: 24px
+ height: 24px
+ display: inline-block
+ margin-left: -12px
+ margin-top: 3px
+ box-shadow: -1px 1px 2px rgba(255,255,255,0.2), inset -1px 1px 1px rgba(0,0,0,0.2)
+ @include skew-gradient(#fcfcfc, #dedede)
+ @include radius(3px)
+ @include transform(rotate(45deg))
+ li.oe_active
+ @include vertical-gradient(#729fcf, #3465a4)
+ .arrow span
+ @include skew-gradient(#729fcf, #3465a4)
+ .label
+ color: white
+ text-shadow: 0 1px 1px #729fcf, 0 -1px 1px #3465a4
+ ul.oe_form_status_clickable
+ li
+ cursor: pointer
+ &:hover
+ @include vertical-gradient(darken(#fcfcfc, 8%), darken(#dedede, 8%))
+ .label
+ text-shadow: 0 -1px 1px #fcfcfc, 0 1px 1px #dedede
+ .arrow span
+ @include vertical-gradient(darken(#fcfcfc, 8%), darken(#dedede, 8%))
+ .label
+ color: $link-color
+ li.oe_active
+ &:hover
+ @include vertical-gradient(darken(#729fcf, 10%), darken(#3465a4, 10%))
+ .label
+ text-shadow: 0 -1px 1px #729fcf, 0 1px 1px #3465a4
+ .arrow span
+ @include skew-gradient(darken(#729fcf, 10%), darken(#3465a4, 10%))
// }}}
- // FormView.viewmanager {{{
- .oe_form
- .oe_view_manager_header2
- td
- padding: 0px 8px
- line-height: 16px
- .oe_i
- font-size: 13px
- .oe_pager_group
- height: auto
+ // FormView.one2many {{{
+ .oe_form .oe_form_field_one2many > .oe_view_manager
+ .oe_list_pager_single_page
+ display: none
+ .oe_form_field_one2many,.oe_form_field_many2many
+ // TODO: oe_form_field_one2many_list?
+ > .oe_view_manager
+ .oe_list_pager_single_page
+ display: none !important
+ .oe_form_field_one2many_list_row_add
+ font-weight: bold
+ .oe_list_content
+ > thead
+ border-bottom: 1px
+ > tbody
+ tr:nth-child(odd)
+ background: transparent
+ .oe_list .oe_list_edit_row_save
+ background: url(/web/static/src/img/iconset-b-remove.png) 50% 50% no-repeat
+ &:before
+ visibility: hidden
+
+ &> .oe_view_manager
+ .oe_header_row_top
+ display: none
+ .oe_view_manager_header2
+ td
+ padding: 0px 8px
line-height: 16px
- li
+ .oe_i
+ font-size: 13px
+ .oe_pager_group
height: auto
line-height: 16px
- .oe-select-create-popup-view-form > .oe_formview > .oe_form_pager
- display: none
+ li
+ height: auto
+ line-height: 16px
+ .oe_list_buttons.oe_editing .oe_list_save
+ // keep "save row" button hidden in o2m
+ visibility: hidden
+ .oe_form_editable
+ .oe_list_editable
+ .oe_list_content
+ td.oe_required
+ background-color: #d2d2ff
+ td.oe_readonly
+ background-color: #eee
+ $row-height: 27px
+ .oe_list_editable
+ .oe_list_content
+ td.oe_list_field_cell
+ padding: 4px 6px 3px 6px
+ .oe_list.oe_list_editable
+ td.oe_list_record_delete
+ position: absolute
+ .oe_list.oe_list_editable.oe_editing
+ .oe_m2o_drop_down_button
+ top: 5px
+ .oe_m2o_cm_button
+ display: none
+ .oe_form_field
+ input, textarea
+ height: $row-height
+ input, textarea
+ @include radius(0)
+ border: 1px solid #aaf
+ margin: 0
+ &.oe_form_field_float,&.oe_form_view_integer
+ input
+ text-align: right
+ width: 100%
+ &.oe_form_field_datetime,&.oe_form_field_date
+ > span
+ width: 100%
+ input.oe_datepicker_master
+ width: 100% !important
+ .oe_form_field_float,.oe_form_view_integer,&.oe_form_field_datetime,&.oe_form_field_date
+ min-width: 0 !important
+ max-width: none !important
// }}}
- // FormView.classes for openerp views {{{
- .oe_form
- td.oe_form_group_cell_label
- border-right: 1px solid #ddd
- padding: 2px 0px 2px 0px
- label
- line-height: 18px
- display: block
- min-width: 120px
- td.oe_form_group_cell + .oe_form_group_cell
- padding-left: 8px
- .oe_form_subtotal_footer
- width: auto
- float: right
- td.oe_form_group_cell
- text-align: right
+ // FormView.many2many {{{
+ .oe_form .oe_form_field_many2many > .oe_list
+ .oe_list_pager_single_page
+ display: none
+ // }}}
+ // ListView {{{
+ .oe_list_buttons
+ .oe_alternative
+ visibility: hidden
+ .oe_list_save, .oe_list_discard
+ display: none
+ &.oe_editing
+ .oe_list_add
+ display: none
+ .oe_list_save
+ display: inline-block
+ .oe_list_discard
+ display: inline
+ .oe_alternative
+ visibility: visible
+
+ .oe_list
+ position: relative
+ .oe_form
+ .oe_form_nosheet
+ margin: 0 // FIXME: either class or border should not be by default
padding: 0
- td.oe_form_group_cell_label
border: none
.oe_form_field
- width: auto !important
- .oe_form_subtotal_footer_separator
- border-top: 1px solid #cacaca
- font-size: 120%
- font-weight: bold
- label.oe_form_subtotal_footer_separator
- font-weight: bold !important
- padding: 2px 8px 2px 0px !important
- // }}}
- // ListView {{{
- .oe-listview-content
+ width: auto
+ position: absolute
+ margin: 0 !important // dammit
+ padding: 0
+ .oe_list_content .oe_group_header
+ @include vertical-gradient(#fcfcfc, #dedede)
+
+ .oe_list_content
width: 100%
+ td:first-child, th:first-child
+ &:after
+ border-width: 0
+ td.oe_number
+ text-align: right !important
+ max-width: 100px
> thead
border-bottom: 2px solid #cacaca
background: #eee
vertical-align: top
- td, th
- padding: 3px 6px
- line-height: 18px
- th:after
- content: ""
- float: right
- margin-top: 7px
- border-width: 0 4px 4px
- border-style: solid
- border-color: #000 transparent
- visibility: hidden
- th.sortup:after
- visibility: visible
- @include opacity(0.6)
- th.sortdown:after
- border-bottom: none
- border-left: 4px solid transparent
- border-right: 4px solid transparent
- border-top: 4px solid #000
- visibility: visible
- @include box-shadow(none)
- @include opacity(0.6)
- // TODO mit
- //th:hover:after
- // border-bottom: none
- // border-left: 4px solid transparent
- // border-right: 4px solid transparent
- // border-top: 4px solid #000
- // visibility: visible
- // @include box-shadow(none)
- // @include opacity(0.6)
+ th
+ position: relative
+ td, th
+ padding: 3px 6px
+ line-height: 18px
+ th:after
+ position: absolute
+ right: 6px
+ content: ""
+ margin-top: 7px
+ border-width: 0 4px 4px
+ border-style: solid
+ border-color: #000 transparent
+ visibility: hidden
+ th.sortup:after
+ visibility: visible
+ @include opacity(0.6)
+ th.sortdown:after
+ border-bottom: none
+ border-left: 4px solid transparent
+ border-right: 4px solid transparent
+ border-top: 4px solid #000
+ visibility: visible
+ @include box-shadow(none)
+ @include opacity(0.6)
> tbody
cursor: pointer
- background: white
> tr
+ height: $row-height
border-top: 1px solid #ddd
- > td.oe-field-cell
+ > td.oe_list_field_cell
padding: 3px 6px
+ white-space: pre-line
> td, > th
- line-height: 18px
> button
border: none
background: transparent
@@ -1789,46 +2129,93 @@ $colour4: #8a89ba
width: 17px
&:after
border-width: 0
- > td.oe_number
- text-align: right !important
> tr:nth-child(odd)
background-color: #f0f0fa
@include vertical-gradient(#f0f0fa, #eeeef6)
+
> tfoot
border-top: 2px solid #cacaca
border-bottom: 1px solid #cacaca
background: #eee
font-weight: bold
- > tbody tr:hover td, tbody tr:hover th
- @include vertical-gradient(#eee, #dedede)
.numeric
text-align: right
width: 82px
input
text-align: right
+ th.oe_list_header_handle
+ font-size: 1px
+ overflow: hidden
+ text-indent: -9001px
+ td.oe_list_field_handle
+ width: 1em
+ padding: 0 !important
+ cursor: ns-resize
+ .oe_list_handle
+ @include text-to-entypo-icon("}",#E0E0E0,18px)
+ margin-right: 7px
+
+ .oe_list_cannot_edit
+ .oe_list_header_handle, .oe_list_field_handle
+ display: none !important
+ padding: 0 !important
+ .oe_list_cannot_delete
+ .oe_list_record_delete
+ display: none !important
+
// }}}
- // Translation {{{
- .oe_trad_field.touched
- border: 1px solid green !important
- // }}}
- // Tooltips {{{
- &.oe_tooltip
- font-size: 12px
- .oe_tooltip_string
- color: #FD5
- font-weight: bold
- font-size: 13px
- .oe_tooltip_help
- white-space: pre-wrap
- .oe_tooltip_technical
- padding: 0 0 4px 0
- margin: 5px 0 0 15px
- li
- list-style: circle
- .oe_tooltip_technical_title
+ // Tree view {{{
+ .tree_header
+ background-color: #f0f0f0
+ border-bottom: 1px solid #cacaca
+ color: #4c4c4c
+ padding: 5px
+ height: 25px
+ button
+ float: right
+ height: 27px
+ margin-right: 5px
+ .oe-treeview-table
+ width: 100%
+ background-color: white
+ border-spacing: 0
+ color: #4c4c4c
+ th
+ padding: 10px
font-weight: bold
+ background-color: #f0f0f0
+ border-bottom: 2px solid #cacaca
+
+ td
+ cursor: pointer
+ vertical-align: middle
+ text-align: left
+ vertical-align: middle
+ height: 20px
+ padding-left: 4px
+ padding-right: 4px
+ border-right: 1px solid #e7e7e7
+
+ td.oe_number
+ text-align: right !important
+ tr
+ border-bottom: 1px solid #d6d6d6
+ &:hover
+ background-color: #e7e7e7
+ span
+ font-size: 90%
+ font-weight: normal
+ white-space: nowrap
+ display: block
+ .treeview-tr.oe-treeview-first
+ background: transparent url(/web/static/src/img/expand.gif) 0 50% no-repeat
+ .oe_open .treeview-tr.oe-treeview-first
+ background-image: url(/web/static/src/img/collapse.gif)
+ .treeview-tr.oe-treeview-first span, .treeview-td.oe-treeview-first span
+ margin-left: 16px
+
// }}}
// Debugging stuff {{{
.oe_layout_debugging
@@ -1847,7 +2234,6 @@ $colour4: #8a89ba
float: right
.oe_form_group_row_incomplete.oe_form_group_row_newline > td:last-child:after
content: "[newline]"
-
.oe_debug_view
float: left
.oe_debug_view_log
@@ -1865,26 +2251,7 @@ $colour4: #8a89ba
float: right
color: #333
// }}}
-//view editor
-.openerp
- .oe_view_editor
- width: 100%
- border-collapse: collapse
- margin-left: -12px
- width: 100%
- background-color: white
- border-spacing: 0
- td
- text-align: center
- white-space: nowrap
- border: 1px solid #D8D8D8
- cursor: pointer
- font-size: 90%
- .oe_view_editor_field td
- border: 0px !important
- .oe_view_editor tr:hover
- background-color: #ecebf2
-
+// Kitten Mode {{{
.kitten-mode-activated
background-image: url(http://placekitten.com/g/1365/769)
background-size: cover
@@ -1892,16 +2259,39 @@ $colour4: #8a89ba
>*
opacity: 0.70
-// Transitional overrides for old styles {{{
-.openerp
- .oe_form_field_many2one
- td:first-child
- position: relative
- span.oe-m2o-drop-down-button
- position: absolute
- top: 3px
- right: 2px
// }}}
+// The jQuery-ui overlay and Autocomplete are outside the .openerp div, please don't add indentation !!!
+div.ui-widget-overlay
+ background: black
+ @include opacity(0.3)
+
+.ui-widget
+ font-family: "Lucida Grande", Helvetica, Verdana, Arial, sans-serif
+ color: #4c4c4c
+ font-size: 13px
+
+.ui-menu
+ padding: 2px 0
+ @include box-shadow(0 1px 4px rgba(0,0,0,0.3))
+ margin-top: 4px
+ border: 1px solid $tag-border
+ .ui-menu-item
+ width: 100%
+ padding: 0
+ a
+ padding: 1px 16px
+ a.ui-corner-all
+ @include radius(0)
+ a.ui-state-active
+ background: #f0f0fa
+ .ui-menu-item a.ui-state-hover, .ui-menu-item a.ui-state-active
+ background: $tag-bg-dark
+ //color: #4c4c4c
+ //@include vertical-gradient(#f0f0fa, #eeeef6)
+
+.ui-corner-all
+ @include radius(3px)
+
// au BufWritePost,FileWritePost *.sass :!sass --style expanded --line-numbers <afile> > "%:p:r.css"
// vim:tabstop=4:shiftwidth=4:softtabstop=4:fdm=marker:
diff --git a/addons/web/static/src/css/base_old.css b/addons/web/static/src/css/base_old.css
deleted file mode 100644
index 74b660f7613..00000000000
--- a/addons/web/static/src/css/base_old.css
+++ /dev/null
@@ -1,999 +0,0 @@
-.openerp {
- padding: 0;
- margin: 0;
- font-size: 80%;
- font-family: Ubuntu, Helvetica, sans-serif;
-}
-
-.openerp, .openerp textarea, .openerp input, .openerp select, .openerp option,
-.openerp button, .openerp .ui-widget {
- font-family: Ubuntu, Helvetica, sans-serif;
- font-size:85%;
-}
-
-.openerp .view-manager-main-content {
- width: 100%;
-}
-
-.openerp .oe_form_group_cell .view-manager-main-content {
- padding: 0;
-}
-
-.oe_box {
- border: 1px solid #aaf;
- padding: 2px;
- margin: 2px;
-}
-
-
-#oe_errors pre {
- margin: 0;
-}
-
-.openerp .oe-listview .oe-number {
- text-align: right !important;
-}
-.oe-listview-header-columns {
- background: #d1d1d1; /* Old browsers */
- background: -moz-linear-gradient(top, #ffffff 0%, #d1d1d1 100%); /* FF3.6+ */
- background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ffffff), color-stop(100%,#d1d1d1)); /* Chrome,Safari4+ */
- background: -webkit-linear-gradient(top, #ffffff 0%,#d1d1d1 100%); /* Chrome10+,Safari5.1+ */
- background: -o-linear-gradient(top, #ffffff 0%,#d1d1d1 100%); /* Opera11.10+ */
- background: -ms-linear-gradient(top, #ffffff 0%,#d1d1d1 100%); /* IE10+ */
- filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#FFFFFF', endColorstr='#d1d1d1',GradientType=0 ); /* IE6-9 */
- background: linear-gradient(top, #ffffff 0%,#d1d1d1 100%); /* W3C */
-}
-
-.openerp .oe_hide {
- display: none !important;
-}
-
-/* STATES */
-.openerp .on_logged,
-.openerp .db_options_row {
- display: none;
-}
-
-
-
-/* Database */
-.login .oe-database-manager {
- display: none;
- height: 100%;
- width: 100%;
- background-color: white;
-}
-.login.database_block .bottom,
-.login.database_block .login_error_message,
-.login.database_block .pane {
- display: none;
-}
-.login.database_block .oe-database-manager {
- display: block;
-}
-
-.login .database {
- float: left;
- width: 202px;
- height: 100%;
- background: #666666;
-}
-.login .oe_db_options {
- margin-left: 202px;
- color: black;
- padding-top: 20px;
-}
-
-.login .database ul {
- margin-top: 65px;
-}
-
-ul.db_options li {
- padding: 5px 0 10px 5px;
- background: #949292; /* Old browsers */
- background: -moz-linear-gradient(top, #949292 30%, #6d6b6b 95%, #282828 100%); /* FF3.6+ */
- background: -webkit-gradient(linear, left top, left bottom, color-stop(30%,#949292), color-stop(95%,#6d6b6b), color-stop(100%,#282828)); /* Chrome,Safari4+ */
- background: -webkit-linear-gradient(top, #949292 30%,#6d6b6b 95%,#282828 100%); /* Chrome10+,Safari5.1+ */
- background: -o-linear-gradient(top, #949292 30%,#6d6b6b 95%,#282828 100%); /* Opera11.10+ */
- background: -ms-linear-gradient(top, #949292 30%,#6d6b6b 95%,#282828 100%); /* IE10+ */
- filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#949292', endColorstr='#282828',GradientType=0 ); /* IE6-9 */
- background: linear-gradient(top, #949292 30%,#6d6b6b 95%,#282828 100%); /* W3C */
- /* for ie9 */
- filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#949292', endColorstr='#5B5A5A',GradientType=0 ); /* IE6-9 */
- border: none;
- /* overriding jquery ui */
- -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
- display: block;
- font-weight: bold;
- text-transform: uppercase;
- margin: 1px;
- color: #EEEEEE;
- cursor: pointer;
- width: 195px;
- font-size: 12px;
-}
-
-.db_option_table {
- border: 1px solid #5A5858;
- padding: 5px;
- -moz-border-radius: 10px;
-}
-
-table.db_option_table input.required {
- background-color: #D2D2FF !important;
-}
-
-.db_option_table label {
- display: block;
- text-align: right;
-}
-
-.db_option_table input[type="text"],
-.db_option_table input[type="password"],
-.db_option_table input[type="file"],
-.db_option_table select {
- width: 300px;
-}
-
-.option_string {
- font-weight: bold;
- color: #555;
- width: 100%;
- text-align: center;
- padding: 10px 0;
- font-size: large;
-}
-
-label.error {
- float: none;
- color: red;
- padding-left: .5em;
- vertical-align: top;
-}
-
-/* Main*/
-.openerp .main_table {
- width: 100%;
- height: 100%;
- background: #f0eeee;
-}
-.openerp .oe-application {
- height: 100%;
-}
-.openerp .oe-application-container {
- width: 100%;
- height: 100%;
-}
-
-/* IE Hack - for IE < 9
- * Avoids footer to be placed statically at 100% cutting the middle of the views
- * */
-.openerp .oe-application-container {
- height: auto\9;
- min-height: 100%\9;
-}
-
-
-/* Main Application */
-.openerp .oe-main-content {
- padding: 0;
- height: 100%;
-}
-
-.openerp h2.oe_view_title {
- font-size: 110%;
- font-weight: normal;
- margin: 2px 0;
- color: #252424;
- text-shadow: white 0 1px 0;
-}
-.openerp div[id^="notebook"] .oe_view_title {
- font-size:85%;
- padding-bottom:4px;
-}
-
-/* List */
-.openerp table.oe-listview-content {
- clear: right;
- width: 100%;
- border-spacing: 0;
- border: 1px solid silver;
-}
-
-.openerp .oe-listview thead table {
- width: 100%;
- border: none;
-}
-.openerp .oe-listview tr.odd {
- background-color: #f3f3f3;
-}
-.openerp .oe-listview tbody tr:hover {
- background-color: #ecebf2;
-}
-.openerp .oe-listview tbody tr:hover {
- background-color: #eae9f0;
-}
-.openerp .oe-listview thead table tr,
-.openerp .oe-listview thead table tr:hover {
- background: none;
-}
-
-.openerp .oe-listview > table > tbody > tr > td,
-.openerp .oe-listview th {
- vertical-align: middle;
- text-align: left;
- padding: 1px 2px;
-}
-
-.openerp .oe-record-delete button,
-.openerp button.oe-edit-row-save {
- border: none;
- height: 12px;
- width: 12px;
- background: url("/web/static/src/img/iconset-b-remove.png") no-repeat scroll center center transparent;
- cursor: pointer;
-}
-.openerp button.oe-edit-row-save {
- background-image: url('/web/static/src/img/icons/save-document.png');
-}
-
-/* Could use :not selectors if they were supported by MSIE8... */
-.openerp .oe-listview > table > tbody > tr > td {
- border-left: 1px solid #dadada; /*currently commenting to test with no vertical lines in list view*/
-}
-.openerp .oe-listview tbody td:first-child,
-.openerp .oe-listview tbody td.oe-button,
-.openerp .oe-listview tbody td.oe-button,
-.openerp .oe-listview tbody th.oe-record-selector,
-.openerp .oe-listview tbody td.oe-record-delete {
- border-left: none;
-}
-
-.openerp .oe-listview td.oe-record-delete {
- text-align: right;
-}
-.openerp .oe-listview th.oe-sortable {
- cursor: pointer;
- font-size: 75%;
- text-transform: uppercase;
- padding: 0;
- margin: 0;
- padding-left: 3px;
- color: #333;
-}
-.openerp .oe-listview th.oe-sortable .ui-icon {
- height: 60%;
- margin: -6px 0 0;
- display: inline;
- display: inline-block;
- vertical-align: middle;
-}
-
-.openerp .oe-listview > table > tbody > tr > td {
- border-bottom: 1px solid #E3E3E3;
-}
-
-
-.openerp .oe-listview td.oe-actions {
- border-bottom:none;
-}
-
-.openerp .oe-listview .oe-record-selector, .openerp .oe-listview .oe-record-edit-link {
- border-bottom: 1px solid #E3E3E3;
-}
-.openerp .oe-listview .oe-record-edit-link {
- cursor: pointer;
-}
-
-.openerp .oe-listview .oe-field-cell {
- cursor: pointer;
- margin-top: 0;
- margin-bottom: 0;
- padding-top: 3px;
- padding-bottom: 3px;
- font-size: 80%;
-}
-.openerp .oe-listview .oe-field-cell progress {
- width: 100%;
-}
-.openerp .oe-listview .oe-field-cell.oe-button button,
-.openerp .oe-listview .oe_form_button button {
- margin: 0;
- padding: 0;
- border: none;
- background: none;
- width: 16px;
- box-shadow: none;
- -moz-box-shadow: none;
- -webkit-box-shadow: none;
-}
-.openerp .oe-listview .oe-field-cell button:active {
- opacity: 0.5;
-}
-.openerp .oe-listview .oe-field-cell button img {
- cursor: pointer;
-}
-.openerp .oe-listview .oe-field-cell button img:hover {
- opacity: 0.75;
-}
-
-.openerp .oe-listview .oe-field-cell .oe-listview-button-disabled img {
- opacity: 0.5;
-}
-
-.openerp .oe-listview th.oe-actions {
- text-align: left;
- white-space: nowrap;
-}
-.openerp .oe-listview th.oe-list-pager {
- text-align: right;
- white-space: nowrap;
-}
-.openerp .oe-list-pager .oe-pager-state {
- cursor: pointer;
- font-size: 90%;
- color: #555;
-}
-
-.openerp .oe_button.oe_button_pager,
-.openerp .oe-list-pager > span,
-.openerp .oe_form_pager > span {
- line-height: 17px;
- height: 17px;
- cursor: pointer;
- color: gray;
- font-weight: bold;
- vertical-align: middle;
-}
-.openerp .oe_button.oe_button_pager,
-.openerp .oe_button.oe_button_pager:disabled {
- padding: 0 3px 0 3px;
- margin: 0;
- height: 17px;
-}
-.openerp .oe-listview .oe-group-name {
- padding-right: 1em;
-}
-.openerp .oe-listview .oe-group-name,
-.openerp .oe-listview .oe-group-pagination {
- white-space: nowrap;
-}
-
-.openerp .oe-listview tfoot td {
- padding: 3px 3px 0;
-}
-.openerp .oe-listview .oe-list-footer {
- text-align: center;
- white-space: nowrap;
- color: #444;
- font-size: 85%;
-}
-.openerp .oe-listview .oe-list-footer span {
- margin: 0 1em;
-}
-.openerp .oe-listview .oe-list-footer progress {
- vertical-align:-10% !important;
- width: 100%;
-}
-
-/** list rounded corners
-
- rounded corners are a pain on tables: need to round not only table, but
- also on the first and last children of the first and last row
- */
-.openerp .oe-listview table.oe-listview-content {
- -webkit-border-radius: 4px;
- -moz-border-radius: 4px;
- border-radius: 4px;
-}
-.openerp .oe-listview table.oe-listview-content thead tr:first-child th:first-child {
- -webkit-border-top-left-radius: 4px;
- -moz-border-radius-topleft: 4px;
- border-top-left-radius: 4px;
-}
-.openerp .oe-listview table.oe-listview-content thead tr:first-child th:last-child {
- -webkit-border-top-right-radius: 4px;
- -moz-border-radius-topright: 4px;
- border-top-right-radius: 4px;
-}
-.openerp .oe-listview table.oe-listview-content tfoot tr:last-child th:first-child,
-.openerp .oe-listview table.oe-listview-content tfoot tr:last-child td:first-child,
-.openerp .oe-listview table.oe-listview-content tbody:last-child tr:last-child th:first-child {
- -webkit-border-bottom-left-radius: 4px;
- -moz-border-radius-bottomleft: 4px;
- border-bottom-left-radius: 4px;
-}
-.openerp .oe-listview table.oe-listview-content tfoot tr:last-child th:last-child,
-.openerp .oe-listview table.oe-listview-content tfoot tr:last-child td:last-child,
-.openerp .oe-listview table.oe-listview-content tbody:last-child tr:last-child td:last-child {
- -webkit-border-bottom-right-radius: 4px;
- -moz-border-radius-bottomright: 4px;
- border-bottom-right-radius: 4px;
-}
-
-/* Form */
-.openerp .oe_form_group_cell input[type="checkbox"] {
- margin-top: 3px;
- vertical-align: middle;
-}
-.openerp .oe_form_group_cell .input[type="text"] {
- padding-bottom: 1px;
-}
-
-.openerp table.oe_form_group td {
- color: #4c4c4c;
-}
-.openerp table.oe_form_group {
- color: #4c4c4c;
-}
-.openerp fieldset.oe_group_box {
- border: 1px solid #AAAAAA;
- moz-border-radius: 4px;
- -webkit-border-radius: 4px;
- border-radius: 4px;
- background: #F9F9F9;
- padding: 4px;
-}
-.openerp fieldset.oe_group_box legend {
- font-weight: bold;
-}
-.openerp .required.error {
- border: 1px solid #900;
-}
-.openerp .oe_form_buttons, .openerp .oe_list_buttons {
- float: left;
-}
-.openerp .oe_form_pager, .openerp .oe_list_pager {
- float: right;
- font-size: 80%;
- color: gray;
- font-weight: bold;
-}
-
-.openerp .oe_form_pager {
- margin-right: 3px;
-}
-
-.openerp .oe_align_left {
- text-align: left;
-}
-.openerp .oe_align_right {
- text-align: right;
-}
-.openerp .oe_align_center {
- text-align: center;
-}
-.openerp .oe_form .oe_form_paragraph {
- margin: 3px 0 0 0;
- white-space: normal;
-}
-
-.openerp .oe_form .oe_form_paragraph.oe_multilines {
- white-space: pre;
-}
-
-.openerp .oe_form_field_one2many .oe-actions h3.oe_view_title,
-.openerp .oe_form_field_one2many_list .oe-actions h3.oe_view_title{
- display: inline;
- margin: 0 0.5em 0 0;
-}
-
-.openerp .oe_form .oe-listview th.oe-sortable .ui-icon,
-.openerp .oe_form .oe-listview th.oe-sortable .ui-icon {
- height: 100%;
- margin-top: -9px;
-}
-
-.openerp table.oe_form_group .oe-listview-content td {
- color: inherit;
-}
-
-/* Uneditable Form View */
-.openerp .oe_form_readonly {
-
-}
-.openerp .oe_form_readonly .oe_form_group_cell .field_text,
-.openerp .oe_form_readonly .field_char,
-.openerp .oe_form_readonly .field_int,
-.openerp .oe_form_readonly .field_float,
-.openerp .oe_form_readonly .field_email,
-.openerp .oe_form_readonly .field_date,
-.openerp .oe_form_readonly .field_selection,
-.openerp .oe_form_readonly .oe_form_field_many2one {
- padding: 3px 2px 2px 2px;
- background-color: white;
- height: 17px;
-}
-.openerp .oe_form_readonly .oe_form_group_cell .field_text {
- height: auto;
-}
-.openerp .oe_form_readonly .field_datetime {
- padding: 1px 2px 2px 2px;
- background-color: white;
- height:19px;
-}
-.openerp .oe_form_readonly .oe_form_field_many2one div {
- background-color:white;
- height:18px;
- margin-bottom:1px;
- padding: 0px 2px 5px 2px;
-}
-
-.openerp .oe_form_readonly .oe_form_field_email div {
- background-color: white;
- padding: 1px 2px 3px 2px;
-}
-
-
-.openerp .oe_form_readonly .oe_form_field_text div.field_text,
-.openerp .oe_form_readonly .oe_form_field_text_html div.field_text_html {
- white-space: pre-wrap;
-}
-.openerp .oe_form_readonly .oe_form_group_cell .field_text {
- min-height:100px;
-}
-/* Inputs */
-/* vertically recentering filter management select tag */
-.openerp select.oe_search-view-filters-management {
- margin-top:2px;
-}
-
-
-@-moz-document url-prefix() {
- /* Strange firefox behaviour on width: 100% + white-space: nowrap */
- .openerp .oe_form .oe_form_button .oe_button {
- width: auto;
- }
-}
-/* IE Hack - for IE < 9
- * Avoids buttons overflow
- * */
-.openerp .oe_form .oe_form_button .oe_button {
- min-width: auto\9;
-}
-
-
-/* jQuery UI override */
-.openerp .ui-widget {
- font-size: 1em;
-}
-.openerp tbody.ui-widget-content {
- margin-bottom: 10px;
- border-spacing: 4px;
-}
-.openerp .ui-widget-header {
- background: white none;
-}
-
-/* Sidebar */
-.openerp .view-manager-main-table {
- margin: 0;
- width:100%;
- border-collapse:collapse;
- height:100%;
-}
-
-.openerp .view-manager-main-table tbody {
- vertical-align: top;
-}
-
-.openerp .oe-view-manager-header {
- overflow: auto;
- background: url("/web/static/src/img/sep-a.gif") 0 100% repeat-x;
- margin:6px 0 6px 2px;
-}
-.openerp .oe_form_group_cell .oe-view-manager-header { /* Trick: remove the background when element is in a formular */
- background: none;
-}
-
-.openerp .oe-view-manager-header h2 {
- float: left;
-}
-
-.openerp .oe_view_manager_menu_tips blockquote {
- display: none;
- font-size: 85%;
- margin: 0;
- background: #fff;
- border-bottom: 1px solid #CECBCB;
- padding: 1px 10px;
- color: #4C4C4C;
-}
-.openerp .oe_view_manager_menu_tips blockquote p {
- margin: 0;
- padding: 6px 1px 4px;
-}
-
-.openerp .oe_view_manager_menu_tips blockquote div {
- text-align: right;
- margin-right:10px;
-}
-
-.openerp .oe_view_manager_menu_tips blockquote div button {
- border: none;
- background: none;
- padding: 0 4px;
- margin: 0;
- display: inline;
- text-decoration: underline;
- color: inherit;
-}
-
-.openerp .view-manager-main-sidebar {
- width: 180px;
- padding: 0;
- margin: 0;
-}
-
-.openerp .sidebar-main-div {
- height: 100%;
- border-left: 1px solid #D2CFCF;
-}
-
-.openerp .sidebar-content {
- padding: 0;
- margin: 0;
- width: 180px;
- height: 100%;
- font-size: 0.9em;
-}
-
-.openerp .closed-sidebar .sidebar-content {
- width: 22px;
-}
-
-.openerp .closed-sidebar .sidebar-content {
- display: none;
-}
-
-.openerp .sidebar-main-div a {
- color: #555;
- text-decoration: none;
-}
-
-.openerp .sidebar-main-div a:hover {
- color: black;
-}
-
-.openerp .oe-sidebar-attachments-toolbar {
- margin: 4px 0 0 4px;
-}
-.openerp .oe-sidebar-attachments-items {
- clear: both;
- padding-top: 5px !important;
-}
-.openerp .oe-sidebar-attachments-items li {
- position: relative;
- padding: 0 0 3px 10px !important;
-}
-.openerp .oe-sidebar-attachments-items li:hover {
- background: #ddd;
-}
-.openerp .oe-sidebar-attachments-link {
- display: block;
- margin-right: 15px;
- overflow: hidden;
-}
-.openerp .oe-sidebar-attachment-delete {
- position: absolute;
- right: 2px;
- top: 1px;
- overflow: hidden;
- width: 15px;
- height: 15px;
- padding: 1px;
- border-radius: 7px;
- -moz-border-radius: 7px;
- -webkit-border-radius: 7px;
-}
-.openerp .oe-sidebar-attachment-delete:hover {
- background-color: white;
-}
-
-.openerp .view-manager-main-sidebar h2 {
- margin:0;
- font-size: 1.15em;
- color: #8E8E8E;
- text-shadow: white 0 1px 0;
- padding-left: 10px;
- padding-right: 21px;
- height: 21px;
-
- background: #ffffff; /* Old browsers */
- background: -moz-linear-gradient(top, #ffffff 0%, #ebe9e9 100%); /* FF3.6+ */
- background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ffffff), color-stop(100%,#ebe9e9)); /* Chrome,Safari4+ */
- background: -webkit-linear-gradient(top, #ffffff 0%,#ebe9e9 100%); /* Chrome10+,Safari5.1+ */
- background: -o-linear-gradient(top, #ffffff 0%,#ebe9e9 100%); /* Opera11.10+ */
- background: -ms-linear-gradient(top, #ffffff 0%,#ebe9e9 100%); /* IE10+ */
- filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#FFFFFF', endColorstr='#EBE9E9',GradientType=0 ); /* IE6-9 */
- background: linear-gradient(top, #ffffff 0%,#ebe9e9 100%); /* W3C */
-
- border: 1px solid #D2CFCF;
- border-right-width: 0;
- border-left-width: 0;
-}
-.openerp .view-manager-main-sidebar h2 {
- border-top-width: 0;
-}
-
-.openerp .view-manager-main-sidebar ul {
- list-style-type: none;
- margin: 0;
- padding: 0;
- display: block;
-}
-
-.openerp .view-manager-main-sidebar li {
- display: block;
- padding: 3px 3px 3px 10px;
-}
-
-.openerp .toggle-sidebar {
- cursor: pointer;
- border: 1px solid #D2CFCF;
- border-top-width: 0;
- display: block;
- background: url(/web/static/src/img/toggle-a-bg.png);
- width: 21px;
- height: 21px;
- z-index: 10;
-}
-.openerp .open-sidebar .toggle-sidebar {
- margin-left: 158px;
- background-position: 21px 0;
- position: absolute;
-}
-.openerp .closed-sidebar .toggle-sidebar {
- border-left: none;
-}
-.openerp li.oe_sidebar_print {
- padding-left: 20px;
- background: 1px 3px url(/web/static/src/img/icons/gtk-print.png) no-repeat;
-}
-
-.openerp .oe_sidebar_print ul {
- padding-left:8px;
-}
-
-.openerp.kitten-mode-activated .main_table {
- background: url(http://placekitten.com/g/1500/800) repeat;
-}
-.openerp.kitten-mode-activated.clark-gable .main_table {
- background: url(http://amigrave.com/ClarkGable.jpg);
- background-size: 100%;
-}
-
-.openerp.kitten-mode-activated .header {
- background: url(http://placekitten.com/g/211/65) repeat;
-}
-
-.openerp.kitten-mode-activated .menu {
- background: #828282;
- background: -moz-linear-gradient(top, #828282 0%, #4D4D4D 100%);
- background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#828282), color-stop(100%,#4D4D4D));
- filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#828282', endColorstr='#4D4D4D',GradientType=0 );
-}
-.openerp.kitten-mode-activated .menu a {
- background: none;
-}
-.openerp.kitten-mode-activated .menu span {
- background: none;
-}
-.openerp.kitten-mode-activated .sidebar-content li a,
-.openerp.kitten-mode-activated .oe-application .view-manager-main-content h2.oe_view_title,
-.openerp.kitten-mode-activated .oe-application .view-manager-main-content a.searchview_group_string,
-.openerp.kitten-mode-activated .oe-application .view-manager-main-content label {
- color: white;
-}
-.openerp.kitten-mode-activated .menu,
-.openerp.kitten-mode-activated .header_corner,
-.openerp.kitten-mode-activated .header_title,
-.openerp.kitten-mode-activated .oe-application,
-.openerp.kitten-mode-activated .oe_footer,
-.openerp.kitten-mode-activated .loading,
-.openerp.kitten-mode-activated .ui-dialog {
- opacity:0.8;
- filter:alpha(opacity=80);
-}
-.openerp.kitten-mode-activated .header .company_logo {
- background: url(http://placekitten.com/g/180/46);
-}
-.openerp.kitten-mode-activated .loading {
- background: #828282;
- border-color: #828282;
-}
-
-.openerp .oe-m2o-drop-down-button {
- margin-left: -24px;
-}
-.openerp .oe-m2o-drop-down-button img {
- margin-bottom: -4px;
- cursor: pointer;
-}
-.openerp .oe-m2o input {
- border-right: none;
- margin-right: 0px !important;
- padding-bottom: 2px !important;
-}
-.openerp .oe-m2o-disabled-cm {
- color: grey;
-}
-.openerp ul[role="listbox"] li a {
- font-size:80%;
-}
-.parent_top {
- vertical-align: text-top;
-}
-
-.openerp .oe-dialog-warning p {
- padding-left: 1em;
- font-size: 1.2em;
- font-weight: bold;
-}
-
-.openerp .dhx_mini_calendar {
- -moz-box-shadow: none;
- -khtml-box-shadow: none;
- -webkit-box-shadow: none;
- box-shadow: none;
-}
-.openerp .oe-treeview-table {
- width: 100%;
- background-color : #FFFFFF;
- border-spacing: 0;
-
-}
-.openerp .oe-treeview-table tr:hover{
- color: blue;
- background-color : #D8D8D8;
-}
-.treeview-tr, .treeview-td {
- cursor: pointer;
- vertical-align: top;
- text-align: left;
- border-bottom: 1px solid #CFCCCC;
-}
-.openerp .oe-treeview-table .oe-number {
- text-align: right !important;
-}
-.treeview-tr span, .treeview-td span {
- font-size: 90%;
- font-weight: normal;
- white-space: nowrap;
- display: block;
- }
-.treeview-tr.oe-treeview-first {
- background: transparent url(/web/static/src/img/expand.gif) 0 50% no-repeat;
-}
-.oe-open .treeview-tr.oe-treeview-first {
- background-image: url(/web/static/src/img/collapse.gif);
-}
-.treeview-tr.oe-treeview-first span,
-.treeview-td.oe-treeview-first span {
- margin-left: 16px;
-}
-
-.treeview-header {
- vertical-align: top;
- background-color : #D8D8D8;
- white-space: nowrap;
- text-align: left;
- padding: 4px 5px;
-}
-ul.oe-arrow-list {
- padding-left: 1.1em;
- margin: 0;
- white-space: nowrap;
-}
-ul.oe-arrow-list li {
- display: inline-block;
- margin-left: -1em;
-}
-ul.oe-arrow-list li span {
- vertical-align: top;
- display: inline-block;
- border: 1em solid #DEDEDE;
- line-height:0em;
-}
-ul.oe-arrow-list .oe-arrow-list-before {
- border-left-color: rgba(0,0,0,0);
- border-right-width:0;
-}
-ul.oe-arrow-list .oe-arrow-list-after {
- border-color: rgba(0,0,0,0);
- border-left-color: #DEDEDE;
- border-right-width:0;
-}
-ul.oe-arrow-list li.oe-arrow-list-selected span {
- border-color: #B5B9FF;
-}
-ul.oe-arrow-list li.oe-arrow-list-selected .oe-arrow-list-before {
- border-left-color: rgba(0,0,0,0);
-}
-ul.oe-arrow-list li.oe-arrow-list-selected .oe-arrow-list-after {
- border-color: rgba(0,0,0,0);
- border-left-color: #B5B9FF;
-}
-.openerp ul.oe-arrow-list li:first-child span:first-child{
- -webkit-border-top-left-radius: 3px;
- -moz-border-radius-topleft: 3px;
- border-top-left-radius: 3px;
- -webkit-border-bottom-left-radius: 3px;
- -moz-border-radius-bottomleft: 3px;
- border-bottom-left-radius: 3px;
-}
-.openerp ul.oe-arrow-list li:last-child span:last-child{
- -webkit-border-top-right-radius: 3px;
- -moz-border-radius-topright: 3px;
- border-top-right-radius: 3px;
- -webkit-border-bottom-right-radius: 3px;
- -moz-border-radius-bottomright: 3px;
- border-bottom-right-radius: 3px;
-}
-.openerp .oe_view_editor {
- width:100%;
- border-collapse : collapse;
- margin-left: -12px;
-
- width: 100%;
- background-color : white;
- border-spacing: 0;
-}
-.openerp .oe_view_editor td{
- text-align: center;
- white-space: nowrap;
- border: 1px solid #D8D8D8;
-
- cursor: pointer;
- font-size: 90%;
-}
-.openerp .oe_view_editor_field td{
- border: 0px !important;
-}
-
-.openerp .oe_view_editor tr:hover {
- background-color: #ecebf2;
-}
-
-
-/* Dialog traceback cases */
-.openerp .oe_error_detail{
- display: block;
-}
-.openerp .oe_error_send{
- display:block;
-}
-.openerp .oe_fielddiv{
- display:inline-block;
- width:100%;
-}
-.openerp .oe_fielddiv input[type=text],textarea{
- width:100%;
-}
-/* for Alignment center */
-.openerp .oe_centeralign{
- text-align:center;
-}
-
-.openerp .oe_applications_tiles {
- color: #4C4C4C;
- text-shadow: #EEE 0 1px 0;
- margin: 0 20px;
-}
-
-.openerp select.oe_search-view-filters-management {
- font-style: oblique;
- color: #999999;
-}
-
-.openerp .oe_search-view-filters-management option,
-.openerp .oe_search-view-filters-management optgroup {
- font-style: normal;
- color: black;
-}
-
-
-/* Internet Explorer Fix */
-a img {
- border: none;
-}
diff --git a/addons/web/static/src/css/data_export.css b/addons/web/static/src/css/data_export.css
index 149fcea81e6..3fa24265b45 100644
--- a/addons/web/static/src/css/data_export.css
+++ b/addons/web/static/src/css/data_export.css
@@ -1,4 +1,4 @@
-.openerp .oe-export {
+.openerp .oe_export {
width: 100%;
}
.openerp .oe_export_row tr{
diff --git a/addons/web/static/src/css/data_import.css b/addons/web/static/src/css/data_import.css
deleted file mode 100644
index a63a52802f0..00000000000
--- a/addons/web/static/src/css/data_import.css
+++ /dev/null
@@ -1,46 +0,0 @@
-.openerp .oe_import_grid {
- border: none;
- border-collapse: collapse;
-}
-.openerp .oe_import_grid-header .oe_import_grid-cell {
- background: url(../img/gradientlinebg.gif) repeat-x #CCCCCC;
- border-bottom: 1px solid #E3E3E3;
- font-weight: bold;
- text-align: left;
-}
-.openerp .oe_import_grid-row .oe_import_grid-cell {
- border-bottom: 1px solid #E3E3E3;
-}
-
-.openerp .oe-import-no-result .oe-import-result {
- display: none;
-}
-.openerp .oe-import fieldset {
- cursor: pointer;
-}
-.openerp .oe-import fieldset legend:before {
- content: '\25BC ';
-}
-.openerp .oe-import fieldset.oe-closed legend:before {
- content: '\25B6 ';
-}
-.openerp .oe-import fieldset.oe-closed table {
- display: none;
-}
-.openerp .oe-import .separator.horizontal {
- font-weight: bold;
- border-bottom-width: 1px;
- margin: 6px 4px 6px 1px;
- height: 20px;
-}
-.openerp .duplicate_fld{
- background-color:#FF6666;
-}
-.openerp .select_fld{
- background: none repeat scroll 0 0 white;
-}
-.openerp .ui-autocomplete {
- max-height: 300px;
- overflow-y: auto;
- padding-right: 20px;
-}
diff --git a/addons/web/static/src/img/graph_background.png b/addons/web/static/src/img/graph_background.png
new file mode 100644
index 00000000000..530312b20bb
--- /dev/null
+++ b/addons/web/static/src/img/graph_background.png
Binary files differ
diff --git a/addons/web/static/src/img/pattern.png b/addons/web/static/src/img/pattern.png
deleted file mode 100644
index 7e3a2b37834..00000000000
--- a/addons/web/static/src/img/pattern.png
+++ /dev/null
Binary files differ
diff --git a/addons/web/static/src/img/throbber2.gif b/addons/web/static/src/img/throbber2.gif
deleted file mode 100644
index 5d2057ae0e7..00000000000
--- a/addons/web/static/src/img/throbber2.gif
+++ /dev/null
Binary files differ
diff --git a/addons/web/static/src/img/view_empty_arrow.png b/addons/web/static/src/img/view_empty_arrow.png
index b0b1372adfd..ef8f3c820d7 100644
--- a/addons/web/static/src/img/view_empty_arrow.png
+++ b/addons/web/static/src/img/view_empty_arrow.png
Binary files differ
diff --git a/addons/web/static/src/js/boot.js b/addons/web/static/src/js/boot.js
index f83dbc97095..05ce709e80a 100644
--- a/addons/web/static/src/js/boot.js
+++ b/addons/web/static/src/js/boot.js
@@ -24,7 +24,7 @@
init: function(modules) {
// By default only web will be loaded, the rest will be by loaded
// by openerp.web.Session on the first session_authenticate
- modules = modules || ["web"];
+ modules = _.union(['web'], modules || []);
var new_instance = {
// links to the global openerp
_openerp: openerp,
@@ -37,7 +37,10 @@
};
openerp.instances[new_instance._session_id] = new_instance;
for(var i=0; i < modules.length; i++) {
- openerp[modules[i]](new_instance,new_instance[modules[i]]);
+ new_instance[modules[i]] = {};
+ if (openerp[modules[i]]) {
+ openerp[modules[i]](new_instance,new_instance[modules[i]]);
+ }
}
return new_instance;
}
@@ -48,17 +51,12 @@
* OpenERP Web web module split
*---------------------------------------------------------*/
openerp.web = function(session) {
- var files = ["corelib","coresetup","dates","formats","chrome","data","views","search","list","form","list_editable","web_mobile","view_tree","data_export","data_import","view_editor"];
+ var files = ["corelib","coresetup","dates","formats","chrome","data","views","search","list","form","list_editable","web_mobile","view_tree","data_export","data_import"];
for(var i=0; i<files.length; i++) {
if(openerp.web[files[i]]) {
openerp.web[files[i]](session);
}
}
- session.log = function() {
- if (session.connection.debug && window.console) {
- console.log.apply(console, arguments);
- }
- }
};
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js
index a36421f2b2b..014313d5428 100644
--- a/addons/web/static/src/js/chrome.js
+++ b/addons/web/static/src/js/chrome.js
@@ -9,12 +9,11 @@ instance.web.Notification = instance.web.Widget.extend({
template: 'Notification',
init: function() {
this._super.apply(this, arguments);
- // move to instance.web.notification
- instance.notification = this;
+ instance.web.notification = this;
},
start: function() {
this._super.apply(this, arguments);
- this.$element.notify({
+ this.$el.notify({
speed: 500,
expires: 2500
});
@@ -25,7 +24,7 @@ instance.web.Notification = instance.web.Widget.extend({
if (sticky) {
opts.expires = false;
}
- this.$element.notify('create', {
+ this.$el.notify('create', {
title: title,
text: text
}, opts);
@@ -36,7 +35,7 @@ instance.web.Notification = instance.web.Widget.extend({
if (sticky) {
opts.expires = false;
}
- this.$element.notify('create', 'oe_notification_alert', {
+ this.$el.notify('create', 'oe_notification_alert', {
title: title,
text: text
}, opts);
@@ -58,20 +57,18 @@ instance.web.Dialog = instance.web.Widget.extend({
init: function (parent, options, content) {
var self = this;
this._super(parent);
- if (content) {
- this.$element = content instanceof $ ? content : $(content);
- }
+ this.content_to_set = content;
this.dialog_options = {
modal: true,
destroy_on_close: true,
- width: $(window).width() * (($(window).width() > 1024) ? 0.5 : 0.75),
+ width: 900,
min_width: 0,
max_width: '95%',
height: 'auto',
min_height: 0,
- max_height: this.get_height('100%') - 140,
+ max_height: this.get_height('100%') - 200,
autoOpen: false,
- position: [false, 50],
+ position: [false, 40],
buttons: {},
beforeClose: function () { self.on_close(); },
resizeStop: this.on_resized
@@ -82,13 +79,11 @@ instance.web.Dialog = instance.web.Widget.extend({
}
}
if (options) {
+ if (options.buttons) {
+ this.params_buttons = true;
+ }
_.extend(this.dialog_options, options);
}
- if (this.dialog_options.autoOpen) {
- this.open();
- } else {
- instance.web.dialog(this.$element, this.get_options());
- }
},
get_options: function(options) {
var self = this,
@@ -120,32 +115,67 @@ instance.web.Dialog = instance.web.Widget.extend({
return parseInt(val, 10);
}
},
- open: function(options) {
- // TODO fme: bind window on resize
- if (this.template) {
- this.$element.html(this.renderElement());
+ renderElement: function() {
+ if (this.content_to_set) {
+ this.setElement(this.content_to_set);
+ } else if (this.template) {
+ this._super();
}
+ },
+ open: function(options) {
+ if (! this.dialog_inited)
+ this.init_dialog();
var o = this.get_options(options);
- instance.web.dialog(this.$element, o).dialog('open');
+ if (! this.params_buttons) {
+ this.$buttons.appendTo($("body"));
+ }
+ instance.web.dialog(this.$el, o).dialog('open');
+ if (! this.params_buttons) {
+ this.$buttons.appendTo(this.$el.dialog("widget"));
+ }
if (o.height === 'auto' && o.max_height) {
- this.$element.css({ 'max-height': o.max_height, 'overflow-y': 'auto' });
+ this.$el.css({ 'max-height': o.max_height, 'overflow-y': 'auto' });
}
return this;
},
+ init_dialog: function(options) {
+ this.renderElement();
+ var o = this.get_options(options);
+ instance.web.dialog(this.$el, o);
+ if (! this.params_buttons) {
+ this.$buttons = $('<div class="ui-dialog-buttonpane ui-widget-content ui-helper-clearfix" />');
+ this.$el.dialog("widget").append(this.$buttons);
+ }
+ var res = this.start();
+ this.dialog_inited = true;
+ return res;
+ },
close: function() {
- this.$element.dialog('close');
+ this.$el.dialog('close');
},
on_close: function() {
+ if (this.__tmp_dialog_destroying)
+ return;
if (this.dialog_options.destroy_on_close) {
- this.$element.dialog('destroy');
+ this.__tmp_dialog_closing = true;
+ this.destroy();
+ this.__tmp_dialog_closing = undefined;
}
},
on_resized: function() {
- //openerp.log("Dialog resized to %d x %d", this.$element.width(), this.$element.height());
},
destroy: function () {
- this.close();
- this.$element.dialog('destroy');
+ _.each(this.getChildren(), function(el) {
+ el.destroy();
+ });
+ if (! this.__tmp_dialog_closing) {
+ this.__tmp_dialog_destroying = true;
+ this.close();
+ this.__tmp_dialog_destroying = undefined;
+ }
+ if (! this.isDestroyed()) {
+ this.$el.dialog('destroy');
+ }
this._super();
}
});
@@ -176,29 +206,9 @@ instance.web.CrashManager = instance.web.CallbackEnabled.extend({
on_traceback: function(error) {
var self = this;
var buttons = {};
- if (instance.connection.openerp_entreprise) {
- buttons[_t("Send OpenERP Enterprise Report")] = function() {
- var $this = $(this);
- var issuename = $('#issuename').val();
- var explanation = $('#explanation').val();
- var remark = $('#remark').val();
- // Call the send method from server to send mail with details
- new instance.web.DataSet(self, 'publisher_warranty.contract').call_and_eval('send', [error.data,explanation,remark,issuename]).then(function(result){
- if (result === false) {
- alert('There was a communication error.');
- } else {
- $this.dialog('close');
- }
- });
- };
- buttons[_t("Dont send")] = function() {
- $(this).dialog("close");
- };
- } else {
- buttons[_t("Ok")] = function() {
- $(this).dialog("close");
- };
- }
+ buttons[_t("Ok")] = function() {
+ $(this).dialog("close");
+ };
var dialog = new instance.web.Dialog(this, {
title: "OpenERP " + _.str.capitalize(error.type),
width: '80%',
@@ -207,8 +217,15 @@ instance.web.CrashManager = instance.web.CallbackEnabled.extend({
min_height: '600px',
buttons: buttons
}).open();
- dialog.$element.html(QWeb.render('CrashManager.error', {session: instance.connection, error: error}));
- }
+ dialog.$el.html(QWeb.render('CrashManager.error', {session: instance.session, error: error}));
+ },
+ on_javascript_exception: function(exception) {
+ this.on_traceback({
+ type: _t("Client Error"),
+ message: exception,
+ data: {debug: ""}
+ });
+ },
});
instance.web.Loading = instance.web.Widget.extend({
@@ -217,47 +234,49 @@ instance.web.Loading = instance.web.Widget.extend({
this._super(parent);
this.count = 0;
this.blocked_ui = false;
- var self = this;
- this.request_call = function() {
- self.on_rpc_event(1);
- };
- this.response_call = function() {
- self.on_rpc_event(-1);
- };
- this.session.on_rpc_request.add_first(this.request_call);
- this.session.on_rpc_response.add_last(this.response_call);
+ this.session.on("request", this, this.request_call);
+ this.session.on("response", this, this.response_call);
+ this.session.on("error", this, this.response_call);
},
destroy: function() {
- this.session.on_rpc_request.remove(this.request_call);
- this.session.on_rpc_response.remove(this.response_call);
this.on_rpc_event(-this.count);
this._super();
},
+ request_call: function() {
+ this.on_rpc_event(1);
+ },
+ response_call: function() {
+ this.on_rpc_event(-1);
+ },
on_rpc_event : function(increment) {
var self = this;
if (!this.count && increment === 1) {
// Block UI after 3s
this.long_running_timer = setTimeout(function () {
self.blocked_ui = true;
- $.blockUI();
+ instance.web.blockUI();
}, 3000);
}
this.count += increment;
if (this.count > 0) {
- this.$element.text(_.str.sprintf( _t("Loading (%d)"), this.count));
- this.$element.show();
- this.getParent().$element.addClass('oe_wait');
+ if (instance.session.debug) {
+ this.$el.text(_.str.sprintf( _t("Loading (%d)"), this.count));
+ } else {
+ this.$el.text(_t("Loading"));
+ }
+ this.$el.show();
+ this.getParent().$el.addClass('oe_wait');
} else {
this.count = 0;
clearTimeout(this.long_running_timer);
// Don't unblock if blocked by somebody else
if (self.blocked_ui) {
this.blocked_ui = false;
- $.unblockUI();
+ instance.web.unblockUI();
}
- this.$element.fadeOut();
- this.getParent().$element.removeClass('oe_wait');
+ this.$el.fadeOut();
+ this.getParent().$el.removeClass('oe_wait');
}
}
});
@@ -265,13 +284,14 @@ instance.web.Loading = instance.web.Widget.extend({
instance.web.DatabaseManager = instance.web.Widget.extend({
init: function(parent) {
this._super(parent);
- this.unblockUIFunction = $.unblockUI;
+ this.unblockUIFunction = instance.web.unblockUI;
$.validator.addMethod('matches', function (s, _, re) {
return new RegExp(re).test(s);
}, _t("Invalid database name"));
},
start: function() {
var self = this;
+ $('.oe_secondary_menus_container,.oe_user_menu_placeholder').empty();
var fetch_db = this.rpc("/web/database/get_list", {}).pipe(
function(result) {
self.db_list = result.db_list;
@@ -287,17 +307,27 @@ instance.web.DatabaseManager = instance.web.Widget.extend({
},
do_render: function() {
var self = this;
- self.$element.html(QWeb.render("DatabaseManager", { widget : self }));
- self.$element.find(".oe_database_manager_menu").tabs({
- show: function(event, ui) {
- $('*[autofocus]:first', ui.panel).focus();
- }
+ instance.webclient.toggle_bars(true);
+ 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').bind('click', function (event) {
+ var menuitem = $(this);
+ menuitem.addClass('oe_active').siblings().removeClass('oe_active');
+ var form_id =menuitem.find('a').attr('href');
+ $(form_id).show().siblings().hide();
+ event.preventDefault();
});
- self.$element.find("form[name=create_db_form]").validate({ submitHandler: self.do_create });
- self.$element.find("form[name=drop_db_form]").validate({ submitHandler: self.do_drop });
- self.$element.find("form[name=backup_db_form]").validate({ submitHandler: self.do_backup });
- self.$element.find("form[name=restore_db_form]").validate({ submitHandler: self.do_restore });
- self.$element.find("form[name=change_pwd_form]").validate({
+ $('#back-to-login').click(self.do_exit);
+ self.$el.find("td").addClass("oe_form_group_cell");
+ self.$el.find("tr td:first-child").addClass("oe_form_group_cell_label");
+ self.$el.find("label").addClass("oe_form_label");
+ self.$el.find("form[name=create_db_form]").validate({ submitHandler: self.do_create });
+ self.$el.find("form[name=drop_db_form]").validate({ submitHandler: self.do_drop });
+ self.$el.find("form[name=backup_db_form]").validate({ submitHandler: self.do_backup });
+ self.$el.find("form[name=restore_db_form]").validate({ submitHandler: self.do_restore });
+ self.$el.find("form[name=change_pwd_form]").validate({
messages: {
old_pwd: "Please enter your previous password",
new_pwd: "Please enter your new password",
@@ -308,10 +338,9 @@ instance.web.DatabaseManager = instance.web.Widget.extend({
},
submitHandler: self.do_change_password
});
- self.$element.find("#back_to_login").click(self.do_exit);
},
destroy: function () {
- this.$element.find('#db-create, #db-drop, #db-backup, #db-restore, #db-change-password, #back-to-login').unbind('click').end().empty();
+ this.$el.find('#db-create, #db-drop, #db-backup, #db-restore, #db-change-password, #back-to-login').unbind('click').end().empty();
this._super();
},
/**
@@ -332,16 +361,16 @@ instance.web.DatabaseManager = instance.web.Widget.extend({
* from unblocking the UI
*/
blockUI: function () {
- $.blockUI();
- $.unblockUI = function () {};
+ instance.web.blockUI();
+ instance.web.unblockUI = function () {};
},
/**
* Reinstates $.unblockUI so third parties can play with blockUI, and
* unblocks the UI
*/
unblockUI: function () {
- $.unblockUI = this.unblockUIFunction;
- $.unblockUI();
+ instance.web.unblockUI = this.unblockUIFunction;
+ instance.web.unblockUI();
},
/**
* Displays an error dialog resulting from the various RPC communications
@@ -365,10 +394,20 @@ instance.web.DatabaseManager = instance.web.Widget.extend({
var fields = $(form).serializeArray();
self.rpc("/web/database/create", {'fields': fields}, function(result) {
var form_obj = self.to_object(fields);
- self.getParent().do_login( form_obj['db_name'], 'admin', form_obj['create_admin_pwd']);
- self.destroy();
+ 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() {
+ self.do_action("reload");
+ },
+ },
+ };
+ self.do_action(client_action);
});
-
},
do_drop: function(form) {
var self = this;
@@ -396,7 +435,14 @@ instance.web.DatabaseManager = instance.web.Widget.extend({
success: function () {
self.do_notify(_t("Backed"), _t("Database backed up successfully"));
},
- error: instance.webclient.crashmanager.on_rpc_error,
+ error: function(error){
+ if(error){
+ self.display_error({
+ title: 'Backup Database',
+ error: 'AccessDenied'
+ });
+ }
+ },
complete: function() {
self.unblockUI();
}
@@ -440,23 +486,33 @@ instance.web.DatabaseManager = instance.web.Widget.extend({
self.display_error(result);
return;
}
+ self.unblockUI();
self.do_notify("Changed Password", "Password has been changed successfully");
});
},
do_exit: function () {
+ this.$el.remove();
+ instance.webclient.toggle_bars(false);
+ this.do_action('login');
}
});
+instance.web.client_actions.add("database_manager", "instance.web.DatabaseManager");
instance.web.Login = instance.web.Widget.extend({
template: "Login",
remember_credentials: true,
- _db_list: null,
- init: function(parent) {
+ init: function(parent, params) {
this._super(parent);
this.has_local_storage = typeof(localStorage) != 'undefined';
+ this.db_list = null;
this.selected_db = null;
this.selected_login = null;
+ this.params = params || {};
+
+ if (this.params.login_successful) {
+ this.on('login_successful', this, this.params.login_successful);
+ }
if (this.has_local_storage && this.remember_credentials) {
this.selected_db = localStorage.getItem('last_db_login_success');
@@ -468,58 +524,47 @@ instance.web.Login = instance.web.Widget.extend({
},
start: function() {
var self = this;
-
- self.$element.find("form").submit(self.on_submit);
-
- self.$element.find('.oe_login_manage_db').click(function() {
- self.$element.find('.oe_login_bottom').hide();
- self.$element.find('.oe_login_pane').hide();
- self.databasemanager = new instance.web.DatabaseManager(self);
- self.databasemanager.appendTo(self.$element);
- self.databasemanager.do_exit.add_last(function() {
- self.databasemanager.destroy();
- self.$element.find('.oe_login_bottom').show();
- self.$element.find('.oe_login_pane').show();
- self.load_db_list(true).then(self.proxy('_db_list_loaded'));
- });
+ self.$el.find("form").submit(self.on_submit);
+ self.$el.find('.oe_login_manage_db').click(function() {
+ self.do_action("database_manager");
});
- return self.load_db_list().then(self.proxy('_db_list_loaded'));
- },
- load_db_list: function (force) {
- var d = $.when(), self = this;
- if (_.isNull(this._db_list) || force) {
- d = self.rpc("/web/database/get_list", {}, function(result) {
- self._db_list = _.clone(result.db_list);
- }, function(error, event) {
- if (error.data.fault_code === 'AccessDenied') {
- event.preventDefault();
- }
- });
+ var d;
+ if (self.params.db) {
+ if (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);
}
return d;
},
- _db_list_loaded: function () {
- var list = this._db_list,
- dbdiv = this.$element.find('div.oe_login_dbpane');
- this.$element.find("[name=db]").replaceWith(instance.web.qweb.render('Login.dblist', { db_list: list, selected_db: this.selected_db}));
- if(list && list.length === 1) {
- dbdiv.hide();
+ on_db_loaded: function (result) {
+ this.db_list = result.db_list;
+ 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 {
- dbdiv.show();
+ this.$('div.oe_login_dbpane').show();
+ }
+ },
+ on_db_failed: function (error, event) {
+ if (error.data.fault_code === 'AccessDenied') {
+ event.preventDefault();
}
},
on_submit: function(ev) {
if(ev) {
ev.preventDefault();
}
- var $e = this.$element;
- var db = $e.find("form [name=db]").val();
+ var db = this.$("form [name=db]").val();
if (!db) {
this.do_warn("Login", "No database selected !");
return false;
}
- var login = $e.find("form input[name=login]").val();
- var password = $e.find("form input[name=password]").val();
+ var login = this.$("form input[name=login]").val();
+ var password = this.$("form input[name=password]").val();
this.do_login(db, login, password);
},
@@ -532,7 +577,8 @@ instance.web.Login = instance.web.Widget.extend({
*/
do_login: function (db, login, password) {
var self = this;
- this.$element.removeClass('oe_login_invalid');
+ self.hide_error();
+ self.$(".oe_login_pane").fadeOut("slow");
return this.session.session_authenticate(db, login, password).pipe(function() {
if (self.has_local_storage) {
if(self.remember_credentials) {
@@ -547,13 +593,102 @@ instance.web.Login = instance.web.Widget.extend({
localStorage.setItem('last_password_login_success', '');
}
}
- self.$(".oe_login_pane").fadeOut("slow");
- self.trigger("login");
- },function () {
- self.$element.addClass("oe_login_invalid");
+ self.trigger('login_successful');
+ }, function () {
+ self.$(".oe_login_pane").fadeIn("fast", function() {
+ self.show_error("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");
+
+/**
+ * Client action to reload the whole interface.
+ * If params has an entry 'menu_id', it opens the given menu entry.
+ */
+instance.web.Reload = instance.web.Widget.extend({
+ init: function(parent, params) {
+ this._super(parent);
+ this.menu_id = (params && params.menu_id) || false;
+ },
+ start: function() {
+ var l = window.location;
+
+ var sobj = $.deparam(l.search.substr(1));
+ sobj.ts = new Date().getTime();
+ var search = '?' + $.param(sobj);
+
+ var hash = l.hash;
+ if (this.menu_id) {
+ hash = "#menu_id=" + this.menu_id;
+ }
+ var url = l.protocol + "//" + l.host + l.pathname + search + hash;
+ window.location = url;
}
});
+instance.web.client_actions.add("reload", "instance.web.Reload");
+
+/**
+ * Client action to go back in breadcrumb history.
+ * If can't go back in history stack, will go back to home.
+ */
+instance.web.HistoryBack = instance.web.Widget.extend({
+ init: function(parent, params) {
+ if (!parent.history_back()) {
+ window.location = '/' + (window.location.search || '');
+ }
+ }
+});
+instance.web.client_actions.add("history_back", "instance.web.HistoryBack");
+
+/**
+ * Client action to go back home.
+ */
+instance.web.Home = instance.web.Widget.extend({
+ init: function(parent, params) {
+ window.location = '/' + (window.location.search || '');
+ }
+});
+instance.web.client_actions.add("home", "instance.web.Home");
+
+instance.web.ChangePassword = instance.web.Widget.extend({
+ template: "ChangePassword",
+ start: function() {
+ var self = this;
+ self.$el.validate({
+ submitHandler: function (form) {
+ self.rpc("/web/session/change_password",{
+ 'fields': $(form).serializeArray()
+ }, function(result) {
+ if (result.error) {
+ self.display_error(result);
+ return;
+ } else {
+ instance.webclient.on_logout();
+ }
+ });
+ }
+ });
+ },
+ display_error: function (error) {
+ return instance.web.dialog($('<div>'), {
+ modal: true,
+ title: error.title,
+ buttons: [
+ {text: _t("Ok"), click: function() { $(this).dialog("close"); }}
+ ]
+ }).html(error.error);
+ },
+})
+instance.web.client_actions.add("change_password", "instance.web.ChangePassword");
instance.web.Menu = instance.web.Widget.extend({
template: 'Menu',
@@ -565,7 +700,8 @@ instance.web.Menu = instance.web.Widget.extend({
},
start: function() {
this._super.apply(this, arguments);
- this.$secondary_menus = this.getParent().$element.find('.oe_secondary_menus_container');
+ this.$secondary_menus = this.getParent().$el.find('.oe_secondary_menus_container');
+ this.$secondary_menus.on('click', 'a[data-menu]', this.on_menu_click);
return this.do_reload();
},
do_reload: function() {
@@ -576,13 +712,13 @@ instance.web.Menu = instance.web.Widget.extend({
this.data = data;
this.renderElement();
this.limit_entries();
+ // Hide toplevel item if there is only one
+ var $toplevel = this.$("li")
+ if($toplevel.length == 1) {
+ $toplevel.hide();
+ }
this.$secondary_menus.html(QWeb.render("Menu.secondary", { widget : this }));
- this.$element.on('click', 'a.oe_menu_more_link', function() {
- self.$element.find('.oe_menu_more').toggle();
- return false;
- });
- this.$element.on('click', 'a[data-menu]', this.on_menu_click);
- this.$secondary_menus.on('click', 'a[data-menu]', this.on_menu_click);
+ this.$el.on('click', 'a[data-menu]', this.on_menu_click);
// Hide second level submenus
this.$secondary_menus.find('.oe_menu_toggler').siblings('.oe_secondary_submenu').hide();
if (self.current_menu) {
@@ -597,20 +733,17 @@ instance.web.Menu = instance.web.Widget.extend({
}
if (maximum_visible_links < this.data.data.children.length) {
var $more = $(QWeb.render('Menu.more')),
- $index = this.$element.find('li').eq(maximum_visible_links - 1);
+ $index = this.$el.find('li').eq(maximum_visible_links - 1);
$index.after($more);
+ //$('.oe_topbar').append($more);
$more.find('.oe_menu_more').append($index.next().nextAll());
}
- this.do_hide_more();
},
auto_limit_entries: function() {
// TODO: auto detect overflow and bind window on resize
var width = $(window).width();
return Math.floor(width / 125);
},
- do_hide_more: function() {
- this.$element.find('.oe_menu_more').hide();
- },
/**
* Opens a given menu by id, as if a user had browsed to that menu by hand
* except does not trigger any event on the way
@@ -618,20 +751,22 @@ instance.web.Menu = instance.web.Widget.extend({
* @param {Number} id database id of the terminal menu to select
*/
open_menu: function (id) {
+ this.current_menu = id;
+ this.session.active_id = id;
var $clicked_menu, $sub_menu, $main_menu;
- $clicked_menu = this.$element.add(this.$secondary_menus).find('a[data-menu=' + id + ']');
+ $clicked_menu = this.$el.add(this.$secondary_menus).find('a[data-menu=' + id + ']');
this.trigger('open_menu', id, $clicked_menu);
if (this.$secondary_menus.has($clicked_menu).length) {
$sub_menu = $clicked_menu.parents('.oe_secondary_menu');
- $main_menu = this.$element.find('a[data-menu=' + $sub_menu.data('menu-parent') + ']');
+ $main_menu = this.$el.find('a[data-menu=' + $sub_menu.data('menu-parent') + ']');
} else {
$sub_menu = this.$secondary_menus.find('.oe_secondary_menu[data-menu-parent=' + $clicked_menu.attr('data-menu') + ']');
$main_menu = $clicked_menu;
}
// Activate current main menu
- this.$element.find('.oe_active').removeClass('oe_active');
+ this.$el.find('.oe_active').removeClass('oe_active');
$main_menu.addClass('oe_active');
// Show current sub menu
@@ -658,7 +793,7 @@ instance.web.Menu = instance.web.Widget.extend({
* @param {Number} id the action_id to match
*/
open_action: function (id) {
- var $menu = this.$element.add(this.$secondary_menus).find('a[data-action-id=' + id + ']');
+ var $menu = this.$el.add(this.$secondary_menus).find('a[data-action-id="' + id + '"]');
var menu_id = $menu.data('menu');
if (menu_id) {
this.open_menu(menu_id);
@@ -668,34 +803,37 @@ instance.web.Menu = instance.web.Widget.extend({
* Process a click on a menu item
*
* @param {Number} id the menu_id
+ * @param {Boolean} [needaction=false] whether the triggered action should execute in a `needs action` context
*/
- menu_click: function(id) {
- if (id) {
- this.do_hide_more();
- // find back the menuitem in dom to get the action
- var $item = this.$element.find('a[data-menu=' + id + ']');
- if (!$item.length) {
- $item = this.$secondary_menus.find('a[data-menu=' + id + ']');
- }
- var action_id = $item.data('action-id');
- // If first level menu doesnt have action trigger first leaf
- if (!action_id) {
- if(this.$element.has($item).length) {
- $sub_menu = this.$secondary_menus.find('.oe_secondary_menu[data-menu-parent=' + id + ']');
- $items = $sub_menu.find('a[data-action-id]').filter('[data-action-id!=""]');
- if($items.length) {
- action_id = $items.data('action-id');
- id = $items.data('menu');
- }
+ menu_click: function(id, needaction) {
+ if (!id) { return; }
+
+ // find back the menuitem in dom to get the action
+ var $item = this.$el.find('a[data-menu=' + id + ']');
+ if (!$item.length) {
+ $item = this.$secondary_menus.find('a[data-menu=' + id + ']');
+ }
+ var action_id = $item.data('action-id');
+ // If first level menu doesnt have action trigger first leaf
+ if (!action_id) {
+ if(this.$el.has($item).length) {
+ var $sub_menu = this.$secondary_menus.find('.oe_secondary_menu[data-menu-parent=' + id + ']');
+ var $items = $sub_menu.find('a[data-action-id]').filter('[data-action-id!=""]');
+ if($items.length) {
+ action_id = $items.data('action-id');
+ id = $items.data('menu');
}
}
- this.open_menu(id);
- this.current_menu = id;
- this.session.active_id = id;
- if (action_id) {
- this.trigger('menu_click', action_id, id, $item);
- }
}
+ if (action_id) {
+ this.trigger('menu_click', {
+ action_id: action_id,
+ needaction: needaction,
+ id: id,
+ previous_menu_id: this.current_menu // Here we don't know if action will fail (in which case we have to revert menu)
+ }, $item);
+ }
+ this.open_menu(id);
},
/**
* Jquery event handler for menu click
@@ -703,9 +841,9 @@ instance.web.Menu = instance.web.Widget.extend({
* @param {Event} ev the jquery event
*/
on_menu_click: function(ev) {
- this.menu_click($(ev.currentTarget).data('menu'));
- ev.stopPropagation();
- return false;
+ ev.preventDefault();
+ var needaction = $(ev.target).is('div.oe_menu_counter');
+ this.menu_click($(ev.currentTarget).data('menu'), needaction);
},
});
@@ -718,69 +856,30 @@ instance.web.UserMenu = instance.web.Widget.extend({
start: function() {
var self = this;
this._super.apply(this, arguments);
- $('html').bind('click', function() {
- self.$element.find('.oe_dropdown_options').hide();
- });
- this.$element.find('.oe_dropdown_toggle').click(function() {
- self.$element.find('.oe_dropdown_options').toggle();
- return false;
- });
- this.$element.on('click', '.oe_dropdown_options li a[data-menu]', function() {
+ this.$el.on('click', '.oe_dropdown_menu li a[data-menu]', function(ev) {
+ ev.preventDefault();
var f = self['on_menu_' + $(this).data('menu')];
if (f) {
f($(this));
}
- self.$element.find('.oe_dropdown_options').hide();
- return false;
});
},
- change_password :function() {
- var self = this;
- this.dialog = new instance.web.Dialog(this, {
- title: _t("Change Password"),
- width : 'auto'
- }).open();
- this.dialog.$element.html(QWeb.render("UserMenu.password", self));
- this.dialog.$element.find("form[name=change_password_form]").validate({
- submitHandler: function (form) {
- self.rpc("/web/session/change_password",{
- 'fields': $(form).serializeArray()
- }, function(result) {
- if (result.error) {
- self.display_error(result);
- return;
- } else {
- instance.webclient.on_logout();
- }
- });
- }
- });
- },
- display_error: function (error) {
- return instance.web.dialog($('<div>'), {
- modal: true,
- title: error.title,
- buttons: [
- {text: _("Ok"), click: function() { $(this).dialog("close"); }}
- ]
- }).html(error.error);
- },
do_update: function () {
var self = this;
var fct = function() {
- var $avatar = self.$element.find('.oe_topbar_avatar');
+ var $avatar = self.$el.find('.oe_topbar_avatar');
$avatar.attr('src', $avatar.data('default-src'));
if (!self.session.uid)
return;
var func = new instance.web.Model("res.users").get_func("read");
return func(self.session.uid, ["name", "company_id"]).pipe(function(res) {
var topbar_name = res.name;
- if(instance.connection.debug)
- topbar_name = _.str.sprintf("%s (%s)", topbar_name, instance.connection.db);
+ if(instance.session.debug)
+ topbar_name = _.str.sprintf("%s (%s)", topbar_name, instance.session.db);
if(res.company_id[0] > 1)
topbar_name = _.str.sprintf("%s (%s)", topbar_name, res.company_id[1]);
- self.$element.find('.oe_topbar_name').text(topbar_name);
- var avatar_src = _.str.sprintf('%s/web/binary/image?session_id=%s&model=res.users&field=avatar&id=%s', self.session.prefix, self.session.session_id, self.session.uid);
+ self.$el.find('.oe_topbar_name').text(topbar_name);
+ var avatar_src = _.str.sprintf('%s/web/binary/image?session_id=%s&model=res.users&field=image_small&id=%s', self.session.prefix, self.session.session_id, self.session.uid);
$avatar.attr('src', avatar_src);
});
};
@@ -792,44 +891,12 @@ instance.web.UserMenu = instance.web.Widget.extend({
},
on_menu_settings: function() {
var self = this;
- var action_manager = new instance.web.ActionManager(this);
- var dataset = new instance.web.DataSet (this,'res.users',this.context);
- dataset.call ('action_get','',function (result){
- self.rpc('/web/action/load', {action_id:result}, function(result){
- action_manager.do_action(_.extend(result['result'], {
- target: 'inline',
- res_id: self.session.uid,
- res_model: 'res.users',
- flags: {
- action_buttons: false,
- search_view: false,
- sidebar: false,
- views_switcher: false,
- pager: false
- }
- }));
+ if (!this.getParent().has_uncommitted_changes()) {
+ self.rpc("/web/action/load", { action_id: "base.action_res_users_my" }, function(result) {
+ result.result.res_id = instance.session.uid;
+ self.getParent().action_manager.do_action(result.result);
});
- });
- this.dialog = new instance.web.Dialog(this,{
- title: _t("Preferences"),
- width: '700px',
- buttons: [
- {text: _t("Change password"), click: function(){ self.change_password(); }},
- {text: _t("Cancel"), click: function(){ $(this).dialog('destroy'); }},
- {text: _t("Save"), click: function(){
- var inner_viewmanager = action_manager.inner_viewmanager;
- inner_viewmanager.views[inner_viewmanager.active_view].controller.do_save()
- .then(function() {
- self.dialog.destroy();
- // needs to refresh interface in case language changed
- window.location.reload();
- });
- }
- }
- ]
- }).open();
- action_manager.appendTo(this.dialog.$element);
- action_manager.renderElement(this.dialog);
+ }
},
on_menu_about: function() {
var self = this;
@@ -841,45 +908,111 @@ instance.web.UserMenu = instance.web.Widget.extend({
window.location.href, 'debug');
});
instance.web.dialog($help, {autoOpen: true,
- modal: true, width: 960, title: _t("About")});
+ modal: true, width: 507, height: 290, resizable: false, title: _t("About")});
});
},
});
-instance.web.WebClient = instance.web.Widget.extend({
- init: function(parent) {
+instance.web.Client = instance.web.Widget.extend({
+ init: function(parent, origin) {
+ instance.client = instance.webclient = this;
+ this._super(parent);
+ this.origin = origin;
+ },
+ start: function() {
+ var self = this;
+ return instance.session.session_bind(this.origin).pipe(function() {
+ var $e = $(QWeb.render(self._template, {}));
+ self.replaceElement($e);
+ self.bind_events();
+ return self.show_common();
+ });
+ },
+ bind_events: function() {
+ var self = this;
+ this.$el.on('mouseenter', '.oe_systray > div:not([data-tipsy=true])', function() {
+ $(this).attr('data-tipsy', 'true').tipsy().trigger('mouseenter');
+ });
+ this.$el.on('click', '.oe_dropdown_toggle', function(ev) {
+ ev.preventDefault();
+ var $toggle = $(this);
+ var $menu = $toggle.siblings('.oe_dropdown_menu');
+ $menu = $menu.size() >= 1 ? $menu : $toggle.find('.oe_dropdown_menu');
+ var state = $menu.is('.oe_opened');
+ setTimeout(function() {
+ // Do not alter propagation
+ $toggle.add($menu).toggleClass('oe_opened', !state);
+ if (!state) {
+ // Move $menu if outside window's edge
+ var doc_width = $(document).width();
+ var offset = $menu.offset();
+ var menu_width = $menu.width();
+ var x = doc_width - offset.left - menu_width - 2;
+ if (x < 0) {
+ $menu.offset({ left: offset.left + x }).width(menu_width);
+ }
+ }
+ }, 0);
+ });
+ instance.web.bus.on('click', this, function(ev) {
+ $.fn.tipsy.clear();
+ if (!$(ev.target).is('input[type=file]')) {
+ self.$el.find('.oe_dropdown_menu.oe_opened, .oe_dropdown_toggle.oe_opened').removeClass('oe_opened');
+ }
+ });
+ },
+ show_common: function() {
var self = this;
+ this.crashmanager = new instance.web.CrashManager();
+ instance.session.on_rpc_error.add(this.crashmanager.on_rpc_error);
+ self.notification = new instance.web.Notification(this);
+ self.notification.appendTo(self.$el);
+ self.loading = new instance.web.Loading(self);
+ self.loading.appendTo(self.$el);
+ self.action_manager = new instance.web.ActionManager(self);
+ self.action_manager.appendTo(self.$('.oe_application'));
+ },
+ toggle_bars: function(value) {
+ this.$('tr:has(td.oe_topbar),.oe_leftbar').toggle(value);
+ },
+ has_uncommitted_changes: function() {
+ return false;
+ },
+});
+
+instance.web.WebClient = instance.web.Client.extend({
+ _template: 'WebClient',
+ init: function(parent) {
this._super(parent);
- instance.webclient = this;
- this.querystring = '?' + jQuery.param.querystring();
this._current_state = null;
},
start: function() {
var self = this;
- this.$element.addClass("openerp openerp-web-client-container");
- if (jQuery.param !== undefined && jQuery.deparam(jQuery.param.querystring()).kitten !== undefined) {
- $("body").addClass("kitten-mode-activated");
- self.$element.delegate('img.oe-record-edit-link-img', 'hover', function(e) {
- self.$element.toggleClass('clark-gable');
+ return $.when(this._super()).pipe(function() {
+ self.$el.on('click', '.oe_logo', function() {
+ self.action_manager.do_action('home');
});
- }
- this.session.session_bind().then(function() {
- self.destroy_content();
- self.show_common();
+ if (jQuery.param !== undefined && jQuery.deparam(jQuery.param.querystring()).kitten !== undefined) {
+ $("body").addClass("kitten-mode-activated");
+ if ($.blockUI) {
+ $.blockUI.defaults.message = '<img src="http://www.amigrave.com/kitten.gif">';
+ }
+ }
if (!self.session.session_is_valid()) {
self.show_login();
} else {
self.show_application();
}
});
- this.$element.on('mouseenter', '.oe_systray > div:not([data-tipsy=true])', function() {
- $(this).attr('data-tipsy', 'true').tipsy().trigger('mouseenter');
- });
+ },
+ set_title: function(title) {
+ title = _.str.clean(title);
+ var sep = _.isEmpty(title) ? '' : ' - ';
+ document.title = title + sep + 'OpenERP';
},
show_common: function() {
var self = this;
- this.crashmanager = new instance.web.CrashManager();
- instance.connection.on_rpc_error.add(this.crashmanager.on_rpc_error);
+ this._super();
window.onerror = function (message, file, line) {
self.crashmanager.on_traceback({
type: _t("Client Error"),
@@ -887,49 +1020,50 @@ instance.web.WebClient = instance.web.Widget.extend({
data: {debug: file + ':' + line}
});
};
- self.notification = new instance.web.Notification(this);
- self.notification.appendTo(self.$element);
- self.loading = new instance.web.Loading(self);
- self.loading.appendTo(self.$element);
- self.login = new instance.web.Login(self);
- self.login.on("login",self,self.show_application);
- self.$table = $(QWeb.render("WebClient", {}));
- self.action_manager = new instance.web.ActionManager(self);
- self.action_manager.appendTo(self.$table.find('.oe_application'));
},
show_login: function() {
- var self = this;
- self.login.appendTo(self.$element);
+ this.toggle_bars(false);
+
+ var action = {
+ 'type': 'ir.actions.client',
+ 'tag': 'login'
+ };
+ var state = $.bbq.getState(true);
+ if (state.action === "login") {
+ action.params = state;
+ }
+
+ this.action_manager.do_action(action);
+ this.action_manager.inner_widget.on('login_successful', this, function() {
+ this.do_push_state(state);
+ this._current_state = null; // ensure the state will be loaded
+ this.show_application(); // will load the state we just pushed
+ });
},
show_application: function() {
var self = this;
- self.$element.append(self.$table);
- self.login.$element.hide();
+ self.toggle_bars(true);
self.menu = new instance.web.Menu(self);
- self.menu.replace(this.$element.find('.oe_menu_placeholder'));
+ self.menu.replace(this.$el.find('.oe_menu_placeholder'));
self.menu.on('menu_click', this, this.on_menu_action);
self.user_menu = new instance.web.UserMenu(self);
- self.user_menu.replace(this.$element.find('.oe_user_menu_placeholder'));
+ self.user_menu.replace(this.$el.find('.oe_user_menu_placeholder'));
self.user_menu.on_menu_logout.add(this.proxy('on_logout'));
self.user_menu.on_action.add(this.proxy('on_menu_action'));
self.user_menu.do_update();
self.bind_hashchange();
- var version_label = _t("OpenERP - Unsupported/Community Version");
- if (!self.session.openerp_entreprise) {
- self.$element.find('.oe_footer_powered').append(_.str.sprintf('<span> - <a href="http://www.openerp.com/support-or-publisher-warranty-contract" target="_blank">%s</a></span>', version_label));
- document.title = version_label;
- }
+ self.set_title();
},
destroy_content: function() {
_.each(_.clone(this.getChildren()), function(el) {
el.destroy();
});
- this.$element.children().remove();
+ this.$el.children().remove();
},
do_reload: function() {
var self = this;
return this.session.session_reload().pipe(function () {
- instance.connection.load_modules(true).pipe(
+ instance.session.load_modules(true).pipe(
self.menu.proxy('do_reload')); });
},
@@ -943,28 +1077,28 @@ instance.web.WebClient = instance.web.Widget.extend({
},
on_logout: function() {
var self = this;
- this.session.session_logout().then(function () {
- $(window).unbind('hashchange', self.on_hashchange);
- self.do_push_state({});
- //would be cool to be able to do this, but I think it will make addons do strange things
- //this.show_login();
- window.location.reload();
- });
+ if (!this.has_uncommitted_changes()) {
+ this.session.session_logout().then(function () {
+ $(window).unbind('hashchange', self.on_hashchange);
+ self.do_push_state({});
+ window.location.reload();
+ });
+ }
},
bind_hashchange: function() {
var self = this;
$(window).bind('hashchange', this.on_hashchange);
var state = $.bbq.getState(true);
- if (! _.isEmpty(state)) {
- $(window).trigger('hashchange');
- } else {
+ if (_.isEmpty(state) || state.action == "login") {
self.menu.has_been_loaded.then(function() {
- var first_menu_id = self.menu.$element.find("a:first").data("menu");
+ var first_menu_id = self.menu.$el.find("a:first").data("menu");
if(first_menu_id) {
self.menu.menu_click(first_menu_id);
}
});
+ } else {
+ $(window).trigger('hashchange');
}
},
on_hashchange: function(event) {
@@ -984,12 +1118,24 @@ instance.web.WebClient = instance.web.Widget.extend({
this._current_state = state;
},
do_push_state: function(state) {
+ this.set_title(state.title);
+ delete state.title;
var url = '#' + $.param(state);
this._current_state = _.clone(state);
$.bbq.pushState(url);
},
- on_menu_action: function(action) {
- this.action_manager.do_action(action);
+ on_menu_action: function(options) {
+ var self = this;
+ return this.rpc("/web/action/load", { action_id: options.action_id })
+ .pipe(function (result) {
+ var action = result.result;
+ if (options.needaction) {
+ action.context.search_default_needaction_pending = true;
+ }
+ return $.when(self.action_manager.do_action(action, null, true)).fail(function() {
+ self.menu.open_menu(options.previous_menu_id);
+ });
+ });
},
do_action: function(action) {
var self = this;
@@ -1001,38 +1147,55 @@ instance.web.WebClient = instance.web.Widget.extend({
}
},
set_content_full_screen: function(fullscreen) {
- if (fullscreen)
- $(".oe_webclient", this.$element).addClass("oe_content_full_screen");
- else
- $(".oe_webclient", this.$element).removeClass("oe_content_full_screen");
- }
+ if (fullscreen) {
+ $(".oe_webclient", this.$el).addClass("oe_content_full_screen");
+ $("body").css({'overflow-y':'hidden'});
+ } else {
+ $(".oe_webclient", this.$el).removeClass("oe_content_full_screen");
+ $("body").css({'overflow-y':'scroll'});
+ }
+ },
+ has_uncommitted_changes: function() {
+ var $e = $.Event('clear_uncommitted_changes');
+ instance.web.bus.trigger('clear_uncommitted_changes', $e);
+ if ($e.isDefaultPrevented()) {
+ return true;
+ } else {
+ return this._super.apply(this, arguments);
+ }
+ },
});
-instance.web.EmbeddedClient = instance.web.Widget.extend({
- template: 'EmptyComponent',
- init: function(parent, action_id, options) {
- this._super(parent);
- // TODO take the xmlid of a action instead of its id
+instance.web.EmbeddedClient = instance.web.Client.extend({
+ _template: 'EmbedClient',
+ init: function(parent, origin, dbname, login, key, action_id, options) {
+ this._super(parent, origin);
+
+ this.dbname = dbname;
+ this.login = login;
+ this.key = key;
this.action_id = action_id;
this.options = options || {};
- this.am = new instance.web.ActionManager(this);
},
start: function() {
var self = this;
- this.am.appendTo(this.$element.addClass('openerp'));
- return this.rpc("/web/action/load", { action_id: this.action_id }, function(result) {
- var action = result.result;
- action.flags = _.extend({
- //views_switcher : false,
- search_view : false,
- action_buttons : false,
- sidebar : false
- //pager : false
- }, self.options, action.flags || {});
+ return $.when(this._super()).pipe(function() {
+ return instance.session.session_authenticate(self.dbname, self.login, self.key, true).pipe(function() {
+ return self.rpc("/web/action/load", { action_id: self.action_id }, function(result) {
+ var action = result.result;
+ action.flags = _.extend({
+ //views_switcher : false,
+ search_view : false,
+ action_buttons : false,
+ sidebar : false
+ //pager : false
+ }, self.options, action.flags || {});
- self.am.do_action(action);
+ self.action_manager.do_action(action);
+ });
+ });
});
- }
+ },
});
instance.web.embed = function (origin, dbname, login, key, action, options) {
@@ -1046,13 +1209,8 @@ instance.web.embed = function (origin, dbname, login, key, action, options) {
var sc = document.getElementsByTagName('script');
currentScript = sc[sc.length-1];
}
- instance.connection.session_bind(origin).then(function () {
- instance.connection.session_authenticate(dbname, login, key, true).then(function () {
- var client = new instance.web.EmbeddedClient(null, action, options);
- client.insertAfter(currentScript);
- });
- });
-
+ var client = new instance.web.EmbeddedClient(null, origin, dbname, login, key, action, options);
+ client.insertAfter(currentScript);
};
};
diff --git a/addons/web/static/src/js/corelib.js b/addons/web/static/src/js/corelib.js
index 94c748f839c..62ac48f0c61 100644
--- a/addons/web/static/src/js/corelib.js
+++ b/addons/web/static/src/js/corelib.js
@@ -99,7 +99,6 @@ openerp.web.corelib = function(instance) {
for (var name in prop) {
// Check if we're overwriting an existing function
prototype[name] = typeof prop[name] == "function" &&
- typeof _super[name] == "function" &&
fnTest.test(prop[name]) ?
(function(name, fn) {
return function() {
@@ -379,7 +378,8 @@ instance.web.PropertiesMixin = _.extend({}, instance.web.EventDispatcherMixin, {
instance.web.EventDispatcherMixin.init.call(this);
this.__getterSetterInternalMap = {};
},
- set: function(map) {
+ set: function(map, options) {
+ options = options || {};
var self = this;
var changed = false;
_.each(map, function(val, key) {
@@ -388,10 +388,11 @@ instance.web.PropertiesMixin = _.extend({}, instance.web.EventDispatcherMixin, {
return;
changed = true;
self.__getterSetterInternalMap[key] = val;
- self.trigger("change:" + key, self, {
- oldValue: tmp,
- newValue: val
- });
+ if (! options.silent)
+ self.trigger("change:" + key, self, {
+ oldValue: tmp,
+ newValue: val
+ });
});
if (changed)
self.trigger("change", self);
@@ -490,24 +491,20 @@ instance.web.CallbackEnabledMixin = _.extend({}, instance.web.PropertiesMixin, {
*
* The semantics of this precisely replace closing over the method call.
*
- * @param {String} method_name name of the method to invoke
+ * @param {String|Function} method function or name of the method to invoke
* @returns {Function} proxied method
*/
- proxy: function (method_name) {
+ proxy: function (method) {
var self = this;
return function () {
- return self[method_name].apply(self, arguments);
+ var fn = (typeof method === 'string') ? self[method] : method;
+ return fn.apply(self, arguments);
}
}
});
instance.web.WidgetMixin = _.extend({},instance.web.CallbackEnabledMixin, {
/**
- * Tag name when creating a default $element.
- * @type string
- */
- tagName: 'div',
- /**
* Constructs the widget and sets its parent if a parent is given.
*
* @constructs instance.web.Widget
@@ -516,14 +513,9 @@ instance.web.WidgetMixin = _.extend({},instance.web.CallbackEnabledMixin, {
* @param {instance.web.Widget} parent Binds the current instance to the given Widget instance.
* When that widget is destroyed by calling destroy(), the current instance will be
* destroyed too. Can be null.
- * @param {String} element_id Deprecated. Sets the element_id. Only useful when you want
- * to bind the current Widget to an already existing part of the DOM, which is not compatible
- * with the DOM insertion methods provided by the current implementation of Widget. So
- * for new components this argument should not be provided any more.
*/
init: function(parent) {
instance.web.CallbackEnabledMixin.init.call(this);
- this.$element = $(document.createElement(this.tagName));
this.setParent(parent);
},
/**
@@ -533,8 +525,8 @@ instance.web.WidgetMixin = _.extend({},instance.web.CallbackEnabledMixin, {
_.each(this.getChildren(), function(el) {
el.destroy();
});
- if(this.$element != null) {
- this.$element.remove();
+ if(this.$el) {
+ this.$el.remove();
}
instance.web.PropertiesMixin.destroy.call(this);
},
@@ -546,7 +538,7 @@ instance.web.WidgetMixin = _.extend({},instance.web.CallbackEnabledMixin, {
appendTo: function(target) {
var self = this;
return this.__widgetRenderAndInsert(function(t) {
- self.$element.appendTo(t);
+ self.$el.appendTo(t);
}, target);
},
/**
@@ -557,7 +549,7 @@ instance.web.WidgetMixin = _.extend({},instance.web.CallbackEnabledMixin, {
prependTo: function(target) {
var self = this;
return this.__widgetRenderAndInsert(function(t) {
- self.$element.prependTo(t);
+ self.$el.prependTo(t);
}, target);
},
/**
@@ -568,7 +560,7 @@ instance.web.WidgetMixin = _.extend({},instance.web.CallbackEnabledMixin, {
insertAfter: function(target) {
var self = this;
return this.__widgetRenderAndInsert(function(t) {
- self.$element.insertAfter(t);
+ self.$el.insertAfter(t);
}, target);
},
/**
@@ -579,7 +571,7 @@ instance.web.WidgetMixin = _.extend({},instance.web.CallbackEnabledMixin, {
insertBefore: function(target) {
var self = this;
return this.__widgetRenderAndInsert(function(t) {
- self.$element.insertBefore(t);
+ self.$el.insertBefore(t);
}, target);
},
/**
@@ -589,7 +581,7 @@ instance.web.WidgetMixin = _.extend({},instance.web.CallbackEnabledMixin, {
*/
replace: function(target) {
return this.__widgetRenderAndInsert(_.bind(function(t) {
- this.$element.replaceAll(t);
+ this.$el.replaceAll(t);
}, this), target);
},
__widgetRenderAndInsert: function(insertion, target) {
@@ -612,6 +604,7 @@ instance.web.WidgetMixin = _.extend({},instance.web.CallbackEnabledMixin, {
* @returns {jQuery.Deferred}
*/
start: function() {
+ return $.when();
}
});
@@ -647,8 +640,8 @@ instance.web.CallbackEnabled = instance.web.Class.extend(instance.web.CallbackEn
* // stuff that you want to init before the rendering
* },
* start: function() {
- * // stuff you want to make after the rendering, `this.$element` holds a correct value
- * this.$element.find(".my_button").click(/* an example of event binding * /);
+ * // stuff you want to make after the rendering, `this.$el` holds a correct value
+ * this.$el.find(".my_button").click(/* an example of event binding * /);
*
* // if you have some asynchronous operations, it's a good idea to return
* // a promise in start()
@@ -672,6 +665,12 @@ instance.web.CallbackEnabled = instance.web.Class.extend(instance.web.CallbackEn
* That will kill the widget in a clean way and erase its content from the dom.
*/
instance.web.Widget = instance.web.Class.extend(instance.web.WidgetMixin, {
+ // Backbone-ish API
+ tagName: 'div',
+ id: null,
+ className: null,
+ attributes: {},
+ events: {},
/**
* The name of the QWeb template that will be used for rendering. Must be
* redefined in subclasses or the default render() method can not be used.
@@ -688,14 +687,12 @@ instance.web.Widget = instance.web.Class.extend(instance.web.WidgetMixin, {
* @param {instance.web.Widget} parent Binds the current instance to the given Widget instance.
* When that widget is destroyed by calling destroy(), the current instance will be
* destroyed too. Can be null.
- * @param {String} element_id Deprecated. Sets the element_id. Only useful when you want
- * to bind the current Widget to an already existing part of the DOM, which is not compatible
- * with the DOM insertion methods provided by the current implementation of Widget. So
- * for new components this argument should not be provided any more.
*/
init: function(parent) {
instance.web.WidgetMixin.init.call(this,parent);
- this.session = instance.connection;
+ // FIXME: this should not be
+ this.setElement(this._make_descriptive());
+ this.session = instance.session;
},
/**
* Renders the element. The default implementation renders the widget using QWeb,
@@ -703,20 +700,120 @@ instance.web.Widget = instance.web.Class.extend(instance.web.WidgetMixin, {
* key that references `this`.
*/
renderElement: function() {
- var rendered = null;
- if (this.template)
- rendered = instance.web.qweb.render(this.template, {widget: this});
- if (_.str.trim(rendered)) {
- var elem = $(rendered);
- this.$element.replaceWith(elem);
- this.$element = elem;
+ var $el;
+ if (this.template) {
+ $el = $(_.str.trim(instance.web.qweb.render(
+ this.template, {widget: this})));
+ } else {
+ $el = this._make_descriptive();
}
+ this.replaceElement($el);
},
+
/**
- * Shortcut for $element.find() like backbone
+ * Re-sets the widget's root element and replaces the old root element
+ * (if any) by the new one in the DOM.
+ *
+ * @param {HTMLElement | jQuery} $el
+ * @returns {*} this
*/
- "$": function() {
- return this.$element.find.apply(this.$element,arguments);
+ replaceElement: function ($el) {
+ var $oldel = this.$el;
+ this.setElement($el);
+ if ($oldel && !$oldel.is(this.$el)) {
+ $oldel.replaceWith(this.$el);
+ }
+ return this;
+ },
+ /**
+ * Re-sets the widget's root element (el/$el/$el).
+ *
+ * Includes:
+ * * re-delegating events
+ * * re-binding sub-elements
+ * * if the widget already had a root element, replacing the pre-existing
+ * element in the DOM
+ *
+ * @param {HTMLElement | jQuery} element new root element for the widget
+ * @return {*} this
+ */
+ setElement: function (element) {
+ // NB: completely useless, as WidgetMixin#init creates a $el
+ // always
+ if (this.$el) {
+ this.undelegateEvents();
+ }
+
+ this.$el = (element instanceof $) ? element : $(element);
+ this.el = this.$el[0];
+
+ this.delegateEvents();
+
+ return this;
+ },
+ /**
+ * Utility function to build small DOM elements.
+ *
+ * @param {String} tagName name of the DOM element to create
+ * @param {Object} [attributes] map of DOM attributes to set on the element
+ * @param {String} [content] HTML content to set on the element
+ * @return {Element}
+ */
+ make: function (tagName, attributes, content) {
+ var el = document.createElement(tagName);
+ if (!_.isEmpty(attributes)) {
+ $(el).attr(attributes);
+ }
+ if (content) {
+ $(el).html(content);
+ }
+ return el;
+ },
+ /**
+ * Makes a potential root element from the declarative builder of the
+ * widget
+ *
+ * @return {jQuery}
+ * @private
+ */
+ _make_descriptive: function () {
+ var attrs = _.extend({}, this.attributes || {});
+ if (this.id) { attrs.id = this.id; }
+ if (this.className) { attrs['class'] = this.className; }
+ return $(this.make(this.tagName, attrs));
+ },
+ delegateEvents: function () {
+ var events = this.events;
+ if (_.isEmpty(events)) { return; }
+
+ for(var key in events) {
+ if (!events.hasOwnProperty(key)) { continue; }
+
+ var method = this.proxy(events[key]);
+
+ var match = /^(\S+)(\s+(.*))?$/.exec(key);
+ var event = match[1];
+ var selector = match[3];
+
+ event += '.widget_events';
+ if (!selector) {
+ this.$el.on(event, method);
+ } else {
+ this.$el.on(event, selector, method);
+ }
+ }
+ },
+ undelegateEvents: function () {
+ this.$el.off('.widget_events');
+ },
+ /**
+ * Shortcut for ``this.$el.find(selector)``
+ *
+ * @param {String} selector CSS selector, rooted in $el
+ * @returns {jQuery} selector match
+ */
+ $: function(selector) {
+ return this.$el.find(selector);
},
/**
* Informs the action manager to do an action. This supposes that
@@ -744,7 +841,7 @@ instance.web.Widget = instance.web.Class.extend(instance.web.WidgetMixin, {
rpc: function(url, data, success, error) {
var def = $.Deferred().then(success, error);
var self = this;
- instance.connection.rpc(url, data). then(function() {
+ instance.session.rpc(url, data). then(function() {
if (!self.isDestroyed())
def.resolve.apply(def, arguments);
}, function() {
@@ -766,7 +863,7 @@ instance.web.Registry = instance.web.Class.extend({
*
* An object path is simply a dotted name from the instance root to the
* object pointed to (e.g. ``"instance.web.Session"`` for an OpenERP
- * connection object).
+ * session object).
*
* @constructs instance.web.Registry
* @param {Object} mapping a mapping of keys to object-paths
@@ -875,6 +972,11 @@ instance.web.Registry = instance.web.Class.extend({
});
instance.web.JsonRPC = instance.web.CallbackEnabled.extend({
+ triggers: {
+ 'request': 'Request sent',
+ 'response': 'Response received',
+ 'error': 'HTTP Error response or timeout received',
+ },
/**
* @constructs instance.web.JsonRPC
* @extends instance.web.CallbackEnabled
@@ -1038,7 +1140,8 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({
uid: new py.float(this.uid),
datetime: datetime,
time: time,
- relativedelta: relativedelta
+ relativedelta: relativedelta,
+ current_date: date.today.__call__().strftime(['%Y-%m-%d'])
};
},
/**
@@ -1212,12 +1315,12 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({
id: _.uniqueId('r')
};
var deferred = $.Deferred();
- this.on_rpc_request();
+ this.trigger('request', url, payload);
var aborter = params.aborter;
delete params.aborter;
var request = this.rpc_function(url, payload).then(
function (response, textStatus, jqXHR) {
- self.on_rpc_response();
+ self.trigger('response', response);
if (!response.error) {
if (url.url === '/web/session/eval_domain_and_context') {
self.test_eval(params, response.result);
@@ -1225,18 +1328,12 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({
deferred.resolve(response["result"], textStatus, jqXHR);
} else if (response.error.data.type === "session_invalid") {
self.uid = false;
- // TODO deprecate or use a deferred on login.do_ask_login()
- self.on_session_invalid(function() {
- self.rpc(url, payload.params,
- function() { deferred.resolve.apply(deferred, arguments); },
- function() { deferred.reject.apply(deferred, arguments); });
- });
} else {
deferred.reject(response.error, $.Event());
}
},
function(jqXHR, textStatus, errorThrown) {
- self.on_rpc_response();
+ self.trigger('error');
var error = {
code: -32098,
message: "XmlHttpRequestError " + errorThrown,
@@ -1279,7 +1376,7 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({
processData: false
}, url);
if (this.synch)
- ajax.async = false;
+ ajax.async = false;
return $.ajax(ajax);
},
rpc_jsonp: function(url, payload) {
@@ -1298,7 +1395,7 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({
data: data
}, url);
if (this.synch)
- ajax.async = false;
+ ajax.async = false;
var payload_str = JSON.stringify(payload);
var payload_url = $.param({r:payload_str});
if(payload_url.length < 2000) {
@@ -1308,19 +1405,19 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({
} else {
// Indirect jsonp request
var ifid = _.uniqueId('oe_rpc_iframe');
- var display = options.openerp.debug ? 'block' : 'none';
+ var display = self.debug ? 'block' : 'none';
var $iframe = $(_.str.sprintf("<iframe src='javascript:false;' name='%s' id='%s' style='display:%s'></iframe>", ifid, ifid, display));
var $form = $('<form>')
.attr('method', 'POST')
.attr('target', ifid)
.attr('enctype', "multipart/form-data")
- .attr('action', ajax.url + '?' + $.param(data))
+ .attr('action', ajax.url + '?jsonp=1&' + $.param(data))
.append($('<input type="hidden" name="r" />').attr('value', payload_str))
.hide()
.appendTo($('body'));
var cleanUp = function() {
if ($iframe) {
- $iframe.unbind("load").attr("src", "javascript:false;").remove();
+ $iframe.unbind("load").remove();
}
$form.remove();
};
@@ -1344,10 +1441,6 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({
return deferred;
}
},
- on_rpc_request: function() {
- },
- on_rpc_response: function() {
- },
on_rpc_error: function(error) {
},
get_url: function (file) {
diff --git a/addons/web/static/src/js/coresetup.js b/addons/web/static/src/js/coresetup.js
index 4defde54a3c..ea160d6faee 100644
--- a/addons/web/static/src/js/coresetup.js
+++ b/addons/web/static/src/js/coresetup.js
@@ -11,33 +11,6 @@ if (!console.debug) {
openerp.web.coresetup = function(instance) {
-/**
- * @deprecated use :class:`instance.web.Widget`
- */
-instance.web.OldWidget = instance.web.Widget.extend({
- init: function(parent, element_id) {
- this._super(parent);
- this.element_id = element_id;
- this.element_id = this.element_id || _.uniqueId('widget-');
- var tmp = document.getElementById(this.element_id);
- this.$element = tmp ? $(tmp) : $(document.createElement(this.tagName));
- },
- renderElement: function() {
- var rendered = this.render();
- if (rendered) {
- var elem = $(rendered);
- this.$element.replaceWith(elem);
- this.$element = elem;
- }
- return this;
- },
- render: function (additional) {
- if (this.template)
- return instance.web.qweb.render(this.template, _.extend({widget: this}, additional || {}));
- return null;
- }
-});
-
/** Session openerp specific RPC class */
instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Session# */{
init: function() {
@@ -62,7 +35,6 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess
this.username = false;
this.user_context= {};
this.db = false;
- this.openerp_entreprise = false;
this.module_list = instance._modules.slice();
this.module_loaded = {};
_(this.module_list).each(function (mod) {
@@ -105,8 +77,7 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess
db: result.db,
username: result.login,
uid: result.uid,
- user_context: result.context,
- openerp_entreprise: result.openerp_entreprise
+ user_context: result.context
});
});
},
@@ -130,8 +101,7 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess
db: result.db,
username: result.login,
uid: result.uid,
- user_context: result.context,
- openerp_entreprise: result.openerp_entreprise
+ user_context: result.context
});
if (!_volatile) {
self.set_cookie('session_id', self.session_id);
@@ -143,20 +113,6 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess
this.set_cookie('session_id', '');
return this.rpc("/web/session/destroy", {});
},
- on_session_valid: function() {
- },
- /**
- * Called when a rpc call fail due to an invalid session.
- * By default, it's a noop
- */
- on_session_invalid: function(retry_callback) {
- },
- /**
- * Fetches a cookie stored by an openerp session
- *
- * @private
- * @param name the cookie's name
- */
get_cookie: function (name) {
if (!this.name) { return null; }
var nameEQ = this.name + '|' + name + '=';
@@ -190,9 +146,8 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess
/**
* Load additional web addons of that instance and init them
*
- * @param {Boolean} [no_session_valid_signal=false] prevents load_module from triggering ``on_session_valid``.
*/
- load_modules: function(no_session_valid_signal) {
+ load_modules: function() {
var self = this;
return this.rpc('/web/session/modules', {}).pipe(function(result) {
var lang = self.user_context.lang,
@@ -201,31 +156,31 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess
var to_load = _.difference(result, self.module_list).join(',');
self.module_list = all_modules;
- var loaded = $.Deferred().resolve().promise();
- if (to_load.length) {
+ var loaded = self.rpc('/web/webclient/translations', params).then(function(trans) {
+ instance.web._t.database.set_bundle(trans);
+ });
+ var file_list = ["/web/static/lib/datejs/globalization/" + lang.replace("_", "-") + ".js"];
+ if(to_load.length) {
loaded = $.when(
- self.rpc('/web/webclient/csslist', {mods: to_load}, self.do_load_css),
+ loaded,
+ self.rpc('/web/webclient/csslist', {mods: to_load}).then(self.do_load_css),
self.rpc('/web/webclient/qweblist', {mods: to_load}).pipe(self.do_load_qweb),
- self.rpc('/web/webclient/translations', params).pipe(function(trans) {
- instance.web._t.database.set_bundle(trans);
- var file_list = ["/web/static/lib/datejs/globalization/" + lang.replace("_", "-") + ".js"];
- return self.rpc('/web/webclient/jslist', {mods: to_load}).pipe(function(files) {
- return self.do_load_js(file_list.concat(files));
- }).then(function () {
- if (!Date.CultureInfo.pmDesignator) {
- // If no am/pm designator is specified but the openerp
- // datetime format uses %i, date.js won't be able to
- // correctly format a date. See bug#938497.
- Date.CultureInfo.amDesignator = 'AM';
- Date.CultureInfo.pmDesignator = 'PM';
- }
- });
- }))
+ self.rpc('/web/webclient/jslist', {mods: to_load}).then(function(files) {
+ file_list = file_list.concat(files);
+ })
+ );
}
- return loaded.then(function() {
+ return loaded.pipe(function () {
+ return self.do_load_js(file_list);
+ }).then(function() {
self.on_modules_loaded();
- if (!no_session_valid_signal) {
- self.on_session_valid();
+ self.trigger('module_loaded');
+ if (!Date.CultureInfo.pmDesignator) {
+ // If no am/pm designator is specified but the openerp
+ // datetime format uses %i, date.js won't be able to
+ // correctly format a date. See bug#938497.
+ Date.CultureInfo.amDesignator = 'AM';
+ Date.CultureInfo.pmDesignator = 'PM';
}
});
});
@@ -333,11 +288,14 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess
.appendTo(document.body)
.load(function () {
try {
- if (options.error) {
- options.error(JSON.parse(
- this.contentDocument.body.childNodes[1].textContent
- ));
- }
+ if (options.error) {
+ if (!this.contentDocument.body.childNodes[1]) {
+ options.error(this.contentDocument.body.childNodes);
+ }
+ else {
+ options.error(JSON.parse(this.contentDocument.body.childNodes[1].textContent));
+ }
+ }
} finally {
complete();
}
@@ -390,16 +348,41 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess
timer = setTimeout(waitLoop, CHECK_INTERVAL);
},
synchronized_mode: function(to_execute) {
- var synch = this.synch;
- this.synch = true;
- try {
- return to_execute();
- } finally {
- this.synch = synch;
- }
+ var synch = this.synch;
+ this.synch = true;
+ try {
+ return to_execute();
+ } finally {
+ this.synch = synch;
+ }
}
});
+
+/**
+ * Event Bus used to bind events scoped in the current instance
+ */
+instance.web.Bus = instance.web.Class.extend(instance.web.EventDispatcherMixin, {
+ init: function() {
+ instance.web.EventDispatcherMixin.init.call(this, parent);
+ var self = this;
+ // TODO fme: allow user to bind keys for some global actions.
+ // check gtk bindings
+ // http://unixpapa.com/js/key.html
+ _.each('click,dblclick,keydown,keypress,keyup'.split(','), function(evtype) {
+ $('html').on(evtype, function(ev) {
+ self.trigger(evtype, ev);
+ });
+ });
+ _.each('resize,scroll'.split(','), function(evtype) {
+ $(window).on(evtype, function(ev) {
+ self.trigger(evtype, ev);
+ });
+ });
+ }
+})
+instance.web.bus = new instance.web.Bus();
+
/** OpenERP Translations */
instance.web.TranslationDataBase = instance.web.Class.extend(/** @lends instance.web.TranslationDataBase# */{
/**
@@ -454,64 +437,6 @@ instance.web.TranslationDataBase = instance.web.Class.extend(/** @lends instance
}
});
-/** Configure default qweb */
-instance.web._t = new instance.web.TranslationDataBase().build_translation_function();
-/**
- * Lazy translation function, only performs the translation when actually
- * printed (e.g. inserted into a template)
- *
- * Useful when defining translatable strings in code evaluated before the
- * translation database is loaded, as class attributes or at the top-level of
- * an OpenERP Web module
- *
- * @param {String} s string to translate
- * @returns {Object} lazy translation object
- */
-instance.web._lt = function (s) {
- return {toString: function () { return instance.web._t(s); }}
-};
-instance.web.qweb = new QWeb2.Engine();
-instance.web.qweb.debug = ($.deparam($.param.querystring()).debug != undefined);
-instance.web.qweb.default_dict = {
- '_' : _,
- '_t' : instance.web._t
-};
-instance.web.qweb.preprocess_node = function() {
- // Note that 'this' is the Qweb Node
- switch (this.node.nodeType) {
- case 3:
- case 4:
- // Text and CDATAs
- var translation = this.node.parentNode.attributes['t-translation'];
- if (translation && translation.value === 'off') {
- return;
- }
- var ts = _.str.trim(this.node.data);
- if (ts.length === 0) {
- return;
- }
- var tr = instance.web._t(ts);
- if (tr !== ts) {
- this.node.data = tr;
- }
- break;
- case 1:
- // Element
- var attr, attrs = ['label', 'title', 'alt'];
- while (attr = attrs.pop()) {
- if (this.attributes[attr]) {
- this.attributes[attr] = instance.web._t(this.attributes[attr]);
- }
- }
- }
-};
-
-/** Configure blockui */
-if ($.blockUI) {
- $.blockUI.defaults.baseZ = 1100;
- $.blockUI.defaults.message = '<img src="/web/static/src/img/throbber2.gif">';
-}
-
/** Custom jQuery plugins */
$.fn.getAttributes = function() {
var o = {};
@@ -541,10 +466,6 @@ $.Mutex = (function() {
return Mutex;
})();
-/** Setup default session */
-instance.connection = new instance.web.Session();
-instance.web.qweb.default_dict['__debug__'] = instance.connection.debug;
-
$.async_when = function() {
var async = false;
var def = $.Deferred();
@@ -574,38 +495,180 @@ $.async_when = function() {
// special tweak for the web client
var old_async_when = $.async_when;
$.async_when = function() {
- if (instance.connection.synch)
- return $.when.apply(this, arguments);
- else
- return old_async_when.apply(this, arguments);
+ if (instance.session.synch)
+ return $.when.apply(this, arguments);
+ else
+ return old_async_when.apply(this, arguments);
};
+/** Setup default session */
+instance.session = new instance.web.Session();
+
+/** Configure default qweb */
+instance.web._t = new instance.web.TranslationDataBase().build_translation_function();
/**
- * Registry for all the client actions key: tag value: widget
+ * Lazy translation function, only performs the translation when actually
+ * printed (e.g. inserted into a template)
+ *
+ * Useful when defining translatable strings in code evaluated before the
+ * translation database is loaded, as class attributes or at the top-level of
+ * an OpenERP Web module
+ *
+ * @param {String} s string to translate
+ * @returns {Object} lazy translation object
*/
-instance.web.client_actions = new instance.web.Registry();
+instance.web._lt = function (s) {
+ return {toString: function () { return instance.web._t(s); }}
+};
+instance.web.qweb = new QWeb2.Engine();
+instance.web.qweb.default_dict['__debug__'] = instance.session.debug; // Which one ?
+instance.web.qweb.debug = instance.session.debug;
+instance.web.qweb.default_dict = {
+ '_' : _,
+ '_t' : instance.web._t
+};
+instance.web.qweb.preprocess_node = function() {
+ // Note that 'this' is the Qweb Node
+ switch (this.node.nodeType) {
+ case 3:
+ case 4:
+ // Text and CDATAs
+ var translation = this.node.parentNode.attributes['t-translation'];
+ if (translation && translation.value === 'off') {
+ return;
+ }
+ var ts = _.str.trim(this.node.data);
+ if (ts.length === 0) {
+ return;
+ }
+ var tr = instance.web._t(ts);
+ if (tr !== ts) {
+ this.node.data = tr;
+ }
+ break;
+ case 1:
+ // Element
+ var attr, attrs = ['label', 'title', 'alt', 'placeholder'];
+ while (attr = attrs.pop()) {
+ if (this.attributes[attr]) {
+ this.attributes[attr] = instance.web._t(this.attributes[attr]);
+ }
+ }
+ }
+};
-/**
- * Client action to reload the whole interface.
- * If params has an entry 'menu_id', it opens the given menu entry.
+/** Setup jQuery timeago */
+var _t = instance.web._t;
+/*
+ * Strings in timeago are "composed" with prefixes, words and suffixes. This
+ * makes their detection by our translating system impossible. Use all literal
+ * strings we're using with a translation mark here so the extractor can do its
+ * job.
*/
-instance.web.client_actions.add("reload", "instance.web.Reload");
+{
+ _t('less than a minute ago');
+ _t('about a minute ago');
+ _t('%d minutes ago');
+ _t('about an hour ago');
+ _t('%d hours ago');
+ _t('a day ago');
+ _t('%d days ago');
+ _t('about a month ago');
+ _t('%d months ago');
+ _t('about a year ago');
+ _t('%d years ago');
+}
-instance.web.Reload = instance.web.Widget.extend({
- init: function(parent, params) {
- this._super(parent);
- this.menu_id = (params && params.menu_id) || false;
- },
+instance.session.on('module_loaded', this, function () {
+ // provide timeago.js with our own translator method
+ $.timeago.settings.translator = instance.web._t;
+});
+
+/** Setup blockui */
+if ($.blockUI) {
+ $.blockUI.defaults.baseZ = 1100;
+ $.blockUI.defaults.message = '<div class="openerp oe_blockui_spin_container" style="background-color: transparent;">';
+ $.blockUI.defaults.css.border = '0';
+ $.blockUI.defaults.css["background-color"] = '';
+}
+
+var messages_by_seconds = function() {
+ return [
+ [0, _t("Loading...")],
+ [20, _t("Still loading...")],
+ [60, _t("Still loading...<br />Please be patient.")],
+ [120, _t("Don't leave yet,<br />it's still loading...")],
+ [300, _t("You may not believe it,<br />but the application is actually loading...")],
+ [420, _t("Take a minute to get a coffee,<br />because it's loading...")],
+ [600, _t("It's loading...<br />By the way, did you tried the kitten mode?")],
+ [3600, _t("Maybe you should consider pressing F5...")],
+ ];
+};
+
+instance.web.Throbber = instance.web.Widget.extend({
+ template: "Throbber",
start: function() {
- if (this.menu_id) {
- // open the given menu id
- var url_without_fragment = window.location.toString().split("#", 1)[0];
- window.location = url_without_fragment + "#menu_id=" + this.menu_id;
- } else {
- window.location.reload();
- }
- }
+ var opts = {
+ lines: 13, // The number of lines to draw
+ length: 7, // The length of each line
+ width: 4, // The line thickness
+ radius: 10, // The radius of the inner circle
+ rotate: 0, // The rotation offset
+ color: '#FFF', // #rgb or #rrggbb
+ speed: 1, // Rounds per second
+ trail: 60, // Afterglow percentage
+ shadow: false, // Whether to render a shadow
+ hwaccel: false, // Whether to use hardware acceleration
+ className: 'spinner', // The CSS class to assign to the spinner
+ zIndex: 2e9, // The z-index (defaults to 2000000000)
+ top: 'auto', // Top position relative to parent in px
+ left: 'auto' // Left position relative to parent in px
+ };
+ this.spin = new Spinner(opts).spin(this.$el[0]);
+ this.start_time = new Date().getTime();
+ this.act_message();
+ },
+ act_message: function() {
+ var self = this;
+ setTimeout(function() {
+ if (self.isDestroyed())
+ return;
+ var seconds = (new Date().getTime() - self.start_time) / 1000;
+ var mes;
+ _.each(messages_by_seconds(), function(el) {
+ if (seconds >= el[0])
+ mes = el[1];
+ });
+ self.$(".oe_throbber_message").html(mes);
+ self.act_message();
+ }, 1000);
+ },
+ destroy: function() {
+ if (this.spin)
+ this.spin.stop();
+ this._super();
+ },
});
+instance.web.Throbber.throbbers = [];
+
+instance.web.blockUI = function() {
+ var tmp = $.blockUI.apply($, arguments);
+ var throbber = new instance.web.Throbber();
+ instance.web.Throbber.throbbers.push(throbber);
+ throbber.appendTo($(".oe_blockui_spin_container"));
+ return tmp;
+}
+instance.web.unblockUI = function() {
+ _.each(instance.web.Throbber.throbbers, function(el) {
+ el.destroy();
+ });
+ return $.unblockUI.apply($, arguments);
+}
+
+/**
+ * Registry for all the client actions key: tag value: widget
+ */
+instance.web.client_actions = new instance.web.Registry();
};
diff --git a/addons/web/static/src/js/data.js b/addons/web/static/src/js/data.js
index fdb15d598af..f1f8532821f 100644
--- a/addons/web/static/src/js/data.js
+++ b/addons/web/static/src/js/data.js
@@ -58,7 +58,7 @@ instance.web.Query = instance.web.Class.extend({
},
_execute: function () {
var self = this;
- return instance.connection.rpc('/web/dataset/search_read', {
+ return instance.session.rpc('/web/dataset/search_read', {
model: this._model.name,
fields: this._fields || false,
domain: this._model.domain(this._filter),
@@ -107,7 +107,7 @@ instance.web.Query = instance.web.Class.extend({
* Performs a groups read according to the provided grouping criterion
*
* @param {String|Array<String>} grouping
- * @returns {jQuery.Deferred<Array<openerp.web.data.Group>> | null}
+ * @returns {jQuery.Deferred<Array<openerp.web.QueryGroup>> | null}
*/
group_by: function (grouping) {
if (grouping === undefined) {
@@ -130,7 +130,7 @@ instance.web.Query = instance.web.Class.extend({
orderby: instance.web.serialize_sort(this._order_by) || false
}).pipe(function (results) {
return _(results).map(function (result) {
- return new instance.web.data.Group(
+ return new instance.web.QueryGroup(
self._model.name, grouping[0], result);
});
});
@@ -194,7 +194,54 @@ instance.web.Query = instance.web.Class.extend({
}
});
-instance.web.Model = instance.web.Class.extend(/** @lends openerp.web.Model# */{
+instance.web.QueryGroup = instance.web.Class.extend({
+ init: function (model, grouping_field, read_group_group) {
+ // In cases where group_by_no_leaf and no group_by, the result of
+ // read_group has aggregate fields but no __context or __domain.
+ // Create default (empty) values for those so that things don't break
+ var fixed_group = _.extend(
+ {__context: {group_by: []}, __domain: []},
+ read_group_group);
+
+ var aggregates = {};
+ _(fixed_group).each(function (value, key) {
+ if (key.indexOf('__') === 0
+ || key === grouping_field
+ || key === grouping_field + '_count') {
+ return;
+ }
+ aggregates[key] = value || 0;
+ });
+
+ this.model = new instance.web.Model(
+ model, fixed_group.__context, fixed_group.__domain);
+
+ var group_size = fixed_group[grouping_field + '_count'] || fixed_group.__count || 0;
+ var leaf_group = fixed_group.__context.group_by.length === 0;
+ this.attributes = {
+ folded: !!(fixed_group.__fold),
+ grouped_on: grouping_field,
+ // if terminal group (or no group) and group_by_no_leaf => use group.__count
+ length: group_size,
+ value: fixed_group[grouping_field],
+ // A group is open-able if it's not a leaf in group_by_no_leaf mode
+ has_children: !(leaf_group && fixed_group.__context['group_by_no_leaf']),
+
+ aggregates: aggregates
+ };
+ },
+ get: function (key) {
+ return this.attributes[key];
+ },
+ subgroups: function () {
+ return this.model.query().group_by(this.model.context().group_by);
+ },
+ query: function () {
+ return this.model.query.apply(this.model, arguments);
+ }
+});
+
+instance.web.Model = instance.web.Class.extend({
/**
* @constructs instance.web.Model
* @extends instance.web.Class
@@ -233,7 +280,7 @@ instance.web.Model = instance.web.Class.extend(/** @lends openerp.web.Model# */{
kwargs = args;
args = [];
}
- return instance.connection.rpc('/web/dataset/call_kw', {
+ return instance.session.rpc('/web/dataset/call_kw', {
model: this.name,
method: method,
args: args,
@@ -256,7 +303,7 @@ instance.web.Model = instance.web.Class.extend(/** @lends openerp.web.Model# */{
* @param {String} signal signal to trigger on the workflow
*/
exec_workflow: function (id, signal) {
- return instance.connection.rpc('/web/dataset/exec_workflow', {
+ return instance.session.rpc('/web/dataset/exec_workflow', {
model: this.name,
id: id,
signal: signal
@@ -282,7 +329,7 @@ instance.web.Model = instance.web.Class.extend(/** @lends openerp.web.Model# */{
*/
context: function (context) {
return new instance.web.CompoundContext(
- instance.connection.user_context, this._context, context || {});
+ instance.session.user_context, this._context, context || {});
},
/**
* Button action caller, needs to perform cleanup if an action is returned
@@ -292,7 +339,7 @@ instance.web.Model = instance.web.Class.extend(/** @lends openerp.web.Model# */{
* FIXME: remove when evaluator integrated
*/
call_button: function (method, args) {
- return instance.connection.rpc('/web/dataset/call_button', {
+ return instance.session.rpc('/web/dataset/call_button', {
model: this.name,
method: method,
domain_id: null,
@@ -302,7 +349,7 @@ instance.web.Model = instance.web.Class.extend(/** @lends openerp.web.Model# */{
},
});
-instance.web.Traverser = instance.web.Class.extend(/** @lends openerp.web.Traverser# */{
+instance.web.Traverser = instance.web.Class.extend({
/**
* @constructs instance.web.Traverser
* @extends instance.web.Class
@@ -381,65 +428,7 @@ instance.web.Traverser = instance.web.Class.extend(/** @lends openerp.web.Traver
});
-/**
- * Utility objects, should never need to be instantiated from outside of this
- * module
- *
- * @namespace
- */
-instance.web.data = {
- Group: instance.web.Class.extend(/** @lends openerp.web.data.Group# */{
- /**
- * @constructs instance.web.data.Group
- * @extends instance.web.Class
- */
- init: function (model, grouping_field, read_group_group) {
- // In cases where group_by_no_leaf and no group_by, the result of
- // read_group has aggregate fields but no __context or __domain.
- // Create default (empty) values for those so that things don't break
- var fixed_group = _.extend(
- {__context: {group_by: []}, __domain: []},
- read_group_group);
-
- var aggregates = {};
- _(fixed_group).each(function (value, key) {
- if (key.indexOf('__') === 0
- || key === grouping_field
- || key === grouping_field + '_count') {
- return;
- }
- aggregates[key] = value || 0;
- });
-
- this.model = new instance.web.Model(
- model, fixed_group.__context, fixed_group.__domain);
-
- var group_size = fixed_group[grouping_field + '_count'] || fixed_group.__count || 0;
- var leaf_group = fixed_group.__context.group_by.length === 0;
- this.attributes = {
- grouped_on: grouping_field,
- // if terminal group (or no group) and group_by_no_leaf => use group.__count
- length: group_size,
- value: fixed_group[grouping_field],
- // A group is open-able if it's not a leaf in group_by_no_leaf mode
- has_children: !(leaf_group && fixed_group.__context['group_by_no_leaf']),
-
- aggregates: aggregates
- };
- },
- get: function (key) {
- return this.attributes[key];
- },
- subgroups: function () {
- return this.model.query().group_by(this.model.context().group_by);
- },
- query: function () {
- return this.model.query.apply(this.model, arguments);
- }
- })
-};
-
-instance.web.DataGroup = instance.web.OldWidget.extend( /** @lends openerp.web.DataGroup# */{
+instance.web.DataGroup = instance.web.CallbackEnabled.extend({
/**
* Management interface between views and grouped collections of OpenERP
* records.
@@ -451,9 +440,9 @@ instance.web.DataGroup = instance.web.OldWidget.extend( /** @lends openerp.web.
* content of the current grouping level.
*
* @constructs instance.web.DataGroup
- * @extends instance.web.OldWidget
+ * @extends instance.web.CallbackEnabled
*
- * @param {instance.web.OldWidget} parent widget
+ * @param {instance.web.CallbackEnabled} parent widget
* @param {String} model name of the model managed by this DataGroup
* @param {Array} domain search domain for this DataGroup
* @param {Object} context context of the DataGroup's searches
@@ -471,49 +460,47 @@ instance.web.DataGroup = instance.web.OldWidget.extend( /** @lends openerp.web.
},
list: function (fields, ifGroups, ifRecords) {
var self = this;
- $.when(this.model.query(fields)
- .order_by(this.sort)
- .group_by(this.group_by)).then(function (groups) {
- if (!groups) {
- ifRecords(_.extend(
- new instance.web.DataSetSearch(
- self, self.model.name,
- self.model.context(),
- self.model.domain()),
- {_sort: self.sort}));
+ var query = this.model.query(fields).order_by(this.sort).group_by(this.group_by);
+ $.when(query).then(function (querygroups) {
+ // leaf node
+ if (!querygroups) {
+ var ds = new instance.web.DataSetSearch(self, self.model.name, self.model.context(), self.model.domain());
+ ds._sort = self.sort;
+ ifRecords(ds);
return;
}
- ifGroups(_(groups).map(function (group) {
+ // internal node
+ var child_datagroups = _(querygroups).map(function (group) {
var child_context = _.extend(
{}, self.model.context(), group.model.context());
- return _.extend(
- new instance.web.DataGroup(
- self, self.model.name, group.model.domain(),
- child_context, group.model._context.group_by,
- self.level + 1),
- {
- __context: child_context,
- __domain: group.model.domain(),
- grouped_on: group.get('grouped_on'),
- length: group.get('length'),
- value: group.get('value'),
- openable: group.get('has_children'),
- aggregates: group.get('aggregates')
- }, {sort: self.sort});
- }));
+ var child_dg = new instance.web.DataGroup(
+ self, self.model.name, group.model.domain(),
+ child_context, group.model._context.group_by,
+ self.level + 1);
+ child_dg.sort = self.sort;
+ // copy querygroup properties
+ child_dg.__context = child_context;
+ child_dg.__domain = group.model.domain();
+ child_dg.folded = group.get('folded');
+ child_dg.grouped_on = group.get('grouped_on');
+ child_dg.length = group.get('length');
+ child_dg.value = group.get('value');
+ child_dg.openable = group.get('has_children');
+ child_dg.aggregates = group.get('aggregates');
+ return child_dg;
+ });
+ ifGroups(child_datagroups);
});
}
});
-instance.web.ContainerDataGroup = instance.web.DataGroup.extend({ });
-instance.web.GrouplessDataGroup = instance.web.DataGroup.extend({ });
-instance.web.StaticDataGroup = instance.web.GrouplessDataGroup.extend( /** @lends openerp.web.StaticDataGroup# */ {
+instance.web.StaticDataGroup = instance.web.DataGroup.extend({
/**
- * A specialization of groupless data groups, relying on a single static
+ * A specialization of data groups, relying on a single static
* dataset as its records provider.
*
* @constructs instance.web.StaticDataGroup
- * @extends instance.web.GrouplessDataGroup
+ * @extends instance.web.DataGroup
* @param {openep.web.DataSetStatic} dataset a static dataset backing the groups
*/
init: function (dataset) {
@@ -524,13 +511,13 @@ instance.web.StaticDataGroup = instance.web.GrouplessDataGroup.extend( /** @lend
}
});
-instance.web.DataSet = instance.web.OldWidget.extend( /** @lends openerp.web.DataSet# */{
+instance.web.DataSet = instance.web.CallbackEnabled.extend({
/**
* DateaManagement interface between views and the collection of selected
* OpenERP records (represents the view's state?)
*
* @constructs instance.web.DataSet
- * @extends instance.web.OldWidget
+ * @extends instance.web.CallbackEnabled
*
* @param {String} model the OpenERP model this dataset will manage
*/
@@ -648,11 +635,9 @@ instance.web.DataSet = instance.web.OldWidget.extend( /** @lends openerp.web.Da
* @param {Function} error_callback function called in case of creation error
* @returns {$.Deferred}
*/
- create: function(data, callback, error_callback) {
- return this._model.call('create',
- [data], {context: this._model.context()})
- .pipe(function (r) { return {result: r}; })
- .then(callback, error_callback);
+ create: function(data) {
+ return this._model.call('create', [data], {context: this._model.context()})
+ .pipe(function (r) { return {result: r}; });
},
/**
* Saves the provided data in an existing db record
@@ -677,10 +662,8 @@ instance.web.DataSet = instance.web.OldWidget.extend( /** @lends openerp.web.Da
* @param {Function} callback function called with operation result
* @param {Function} error_callback function called in case of deletion error
*/
- unlink: function(ids, callback, error_callback) {
- return this._model.call('unlink',
- [ids], {context: this._model.context()})
- .then(callback, error_callback);
+ unlink: function(ids) {
+ return this._model.call('unlink', [ids], {context: this._model.context()});
},
/**
* Calls an arbitrary RPC method
@@ -705,14 +688,14 @@ instance.web.DataSet = instance.web.OldWidget.extend( /** @lends openerp.web.Da
* @param {Function} error_callback
* @returns {$.Deferred}
*/
- call_and_eval: function (method, args, domain_index, context_index, callback, error_callback) {
- return this.rpc('/web/dataset/call', {
+ call_and_eval: function (method, args, domain_index, context_index) {
+ return instance.session.rpc('/web/dataset/call', {
model: this.model,
method: method,
domain_id: domain_index == undefined ? null : domain_index,
context_id: context_index == undefined ? null : context_index,
args: args || []
- }, callback, error_callback);
+ });
},
/**
* Calls a button method, usually returning some sort of action
@@ -723,9 +706,8 @@ instance.web.DataSet = instance.web.OldWidget.extend( /** @lends openerp.web.Da
* @param {Function} error_callback
* @returns {$.Deferred}
*/
- call_button: function (method, args, callback, error_callback) {
- return this._model.call_button(method, args)
- .then(callback, error_callback);
+ call_button: function (method, args) {
+ return this._model.call_button(method, args);
},
/**
* Fetches the "readable name" for records, based on intrinsic rules
@@ -734,10 +716,8 @@ instance.web.DataSet = instance.web.OldWidget.extend( /** @lends openerp.web.Da
* @param {Function} callback
* @returns {$.Deferred}
*/
- name_get: function(ids, callback) {
- return this._model.call('name_get',
- [ids], {context: this._model.context()})
- .then(callback);
+ name_get: function(ids) {
+ return this._model.call('name_get', [ids], {context: this._model.context()});
},
/**
*
@@ -748,28 +728,25 @@ instance.web.DataSet = instance.web.OldWidget.extend( /** @lends openerp.web.Da
* @param {Function} callback function to call with name_search result
* @returns {$.Deferred}
*/
- name_search: function (name, domain, operator, limit, callback) {
+ name_search: function (name, domain, operator, limit) {
return this._model.call('name_search', {
name: name || '',
args: domain || false,
operator: operator || 'ilike',
context: this._model.context(),
limit: limit || 0
- }).then(callback);
+ });
},
/**
* @param name
* @param callback
*/
- name_create: function(name, callback) {
- return this._model.call('name_create',
- [name], {context: this._model.context()})
- .then(callback);
+ name_create: function(name) {
+ return this._model.call('name_create', [name], {context: this._model.context()});
},
- exec_workflow: function (id, signal, callback) {
+ exec_workflow: function (id, signal) {
return this._model.exec_workflow(id, signal)
- .pipe(function (result) { return { result: result }; })
- .then(callback);
+ .pipe(function (result) { return { result: result }; });
},
get_context: function(request_context) {
return this._model.context(request_context);
@@ -803,7 +780,23 @@ instance.web.DataSet = instance.web.OldWidget.extend( /** @lends openerp.web.Da
return this.ids.length;
},
alter_ids: function(n_ids) {
- this.ids = n_ids;
+ this.ids = n_ids;
+ },
+ /**
+ * Resequence records.
+ *
+ * @param {Array} ids identifiers of the records to resequence
+ * @returns {$.Deferred}
+ */
+ resequence: function (ids, options) {
+ options = options || {};
+ return instance.session.rpc('/web/dataset/resequence', {
+ model: this.model,
+ ids: ids,
+ context: this._model.context(options.context),
+ }).pipe(function (results) {
+ return results;
+ });
},
});
instance.web.DataSetStatic = instance.web.DataSet.extend({
@@ -836,7 +829,7 @@ instance.web.DataSetStatic = instance.web.DataSet.extend({
this.set_ids(_.without.apply(null, [this.ids].concat(ids)));
}
});
-instance.web.DataSetSearch = instance.web.DataSet.extend(/** @lends openerp.web.DataSetSearch */{
+instance.web.DataSetSearch = instance.web.DataSet.extend({
/**
* @constructs instance.web.DataSetSearch
* @extends instance.web.DataSet
@@ -886,7 +879,7 @@ instance.web.DataSetSearch = instance.web.DataSet.extend(/** @lends openerp.web
},
unlink: function(ids, callback, error_callback) {
var self = this;
- return this._super(ids, function(result) {
+ return this._super(ids).then(function(result) {
self.ids = _(self.ids).difference(ids);
if (self._length) {
self._length -= 1;
@@ -895,9 +888,7 @@ instance.web.DataSetSearch = instance.web.DataSet.extend(/** @lends openerp.web
self.index = self.index <= self.ids.length - 1 ?
self.index : (self.ids.length > 0 ? self.ids.length -1 : 0);
}
- if (callback)
- callback(result);
- }, error_callback);
+ });
},
size: function () {
if (this._length !== undefined) {
@@ -920,14 +911,12 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
on_default_get: function(res) {
this.last_default_get = res;
},
- create: function(data, callback, error_callback) {
+ create: function(data) {
var cached = {id:_.uniqueId(this.virtual_id_prefix), values: data,
defaults: this.last_default_get};
this.to_create.push(_.extend(_.clone(cached), {values: _.clone(cached.values)}));
this.cache.push(cached);
- var prom = $.Deferred().then(callback);
- prom.resolve({result: cached.id});
- return prom.promise();
+ return $.Deferred().resolve({result: cached.id}).promise();
},
write: function (id, data, options, callback) {
var self = this;
@@ -1042,7 +1031,7 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
self.cache.push({id: id, values: record});
} else {
// I assume cache value is prioritary
- cached.values = _.defaults(_.clone(cached.values), record);
+ cached.values = _.defaults(_.clone(cached.values), record);
}
});
return_records();
@@ -1053,7 +1042,7 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
}
return completion.promise();
},
- call_button: function (method, args, callback, error_callback) {
+ call_button: function (method, args) {
var id = args[0][0], index;
for(var i=0, len=this.cache.length; i<len; ++i) {
var record = this.cache[i];
@@ -1064,10 +1053,10 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
break;
}
}
- return this._super(method, args, callback, error_callback);
+ return this._super(method, args);
},
alter_ids: function(n_ids) {
- this._super(n_ids);
+ this._super(n_ids);
this.on_change();
},
});
@@ -1096,9 +1085,9 @@ instance.web.ProxyDataSet = instance.web.DataSetSearch.extend({
return this._super.apply(this, arguments);
}
},
- create: function(data, callback, error_callback) {
+ create: function(data) {
if (this.create_function) {
- return this.create_function(data, this._super).then(callback, error_callback);
+ return this.create_function(data, this._super);
} else {
return this._super.apply(this, arguments);
}
@@ -1110,9 +1099,9 @@ instance.web.ProxyDataSet = instance.web.DataSetSearch.extend({
return this._super.apply(this, arguments);
}
},
- unlink: function(ids, callback, error_callback) {
+ unlink: function(ids) {
if (this.unlink_function) {
- return this.unlink_function(ids, this._super).then(callback, error_callback);
+ return this.unlink_function(ids, this._super);
} else {
return this._super.apply(this, arguments);
}
@@ -1165,7 +1154,7 @@ instance.web.CompoundDomain = instance.web.Class.extend({
}
});
-instance.web.DropMisordered = instance.web.Class.extend(/** @lends openerp.web.DropMisordered# */{
+instance.web.DropMisordered = instance.web.Class.extend({
/**
* @constructs instance.web.DropMisordered
* @extends instance.web.Class
diff --git a/addons/web/static/src/js/data_export.js b/addons/web/static/src/js/data_export.js
index ebe82ddb4b4..29aea6362f7 100644
--- a/addons/web/static/src/js/data_export.js
+++ b/addons/web/static/src/js/data_export.js
@@ -21,31 +21,31 @@ instance.web.DataExport = instance.web.Dialog.extend({
],
close: function(event, ui){ self.close();}
});
- self.$element.removeClass('ui-dialog-content ui-widget-content');
- self.$element.find('#add_field').click(function() {
+ self.$el.removeClass('ui-dialog-content ui-widget-content');
+ self.$el.find('#add_field').click(function() {
if ($('#field-tree-structure tr.ui-selected')) {
- var fld = self.$element.find('#field-tree-structure tr.ui-selected').find('a');
+ var fld = self.$el.find('#field-tree-structure tr.ui-selected').find('a');
for (var i = 0; i < fld.length; i++) {
var id = $(fld[i]).attr('id').split('-')[1];
var string = $(fld[i]).attr('string');
self.add_field(id, string);
}
- self.$element.find('#field-tree-structure tr').removeClass('ui-selected');
+ self.$el.find('#field-tree-structure tr').removeClass('ui-selected');
}
});
- self.$element.find('#remove_field').click(function() {
- self.$element.find('#fields_list option:selected').remove();
+ self.$el.find('#remove_field').click(function() {
+ self.$el.find('#fields_list option:selected').remove();
});
- self.$element.find('#remove_all_field').click(function() {
- self.$element.find('#fields_list').empty();
+ self.$el.find('#remove_all_field').click(function() {
+ self.$el.find('#fields_list').empty();
});
- this.$element.find('#export_new_list').click(this.on_show_save_list);
+ this.$el.find('#export_new_list').click(this.on_show_save_list);
var got_fields = new $.Deferred();
- this.$element.find('#import_compat').change(function() {
- self.$element.find('#fields_list').empty();
- self.$element.find('#field-tree-structure').remove();
- var import_comp = self.$element.find("#import_compat").val();
+ this.$el.find('#import_compat').change(function() {
+ self.$el.find('#fields_list').empty();
+ self.$el.find('#field-tree-structure').remove();
+ var import_comp = self.$el.find("#import_compat").val();
self.rpc("/web/export/get_fields", {
model: self.dataset.model,
import_compat: Boolean(import_comp)
@@ -61,7 +61,7 @@ instance.web.DataExport = instance.web.Dialog.extend({
this.show_exports_list());
},
do_setup_export_formats: function (formats) {
- var $fmts = this.$element.find('#export_format');
+ var $fmts = this.$el.find('#export_format');
_(formats).each(function (format) {
var opt = new Option(format.label, format.tag);
if (format.error) {
@@ -76,8 +76,8 @@ instance.web.DataExport = instance.web.Dialog.extend({
},
show_exports_list: function() {
var self = this;
- if (self.$element.find('#saved_export_list').is(':hidden')) {
- self.$element.find('#ExistsExportList').show();
+ if (self.$el.find('#saved_export_list').is(':hidden')) {
+ self.$el.find('#ExistsExportList').show();
return;
}
return this.exports.read_slice(['name'], {
@@ -86,35 +86,35 @@ instance.web.DataExport = instance.web.Dialog.extend({
if (!export_list.length) {
return;
}
- self.$element.find('#ExistsExportList').append(QWeb.render('Exists.ExportList', {'existing_exports': export_list}));
- self.$element.find('#saved_export_list').change(function() {
- self.$element.find('#fields_list option').remove();
- var export_id = self.$element.find('#saved_export_list option:selected').val();
+ self.$el.find('#ExistsExportList').append(QWeb.render('Exists.ExportList', {'existing_exports': export_list}));
+ self.$el.find('#saved_export_list').change(function() {
+ self.$el.find('#fields_list option').remove();
+ var export_id = self.$el.find('#saved_export_list option:selected').val();
if (export_id) {
self.rpc('/web/export/namelist', {'model': self.dataset.model, export_id: parseInt(export_id)}, self.do_load_export_field);
}
});
- self.$element.find('#delete_export_list').click(function() {
- var select_exp = self.$element.find('#saved_export_list option:selected');
+ self.$el.find('#delete_export_list').click(function() {
+ var select_exp = self.$el.find('#saved_export_list option:selected');
if (select_exp.val()) {
self.exports.unlink([parseInt(select_exp.val(), 10)]);
select_exp.remove();
- if (self.$element.find('#saved_export_list option').length <= 1) {
- self.$element.find('#ExistsExportList').hide();
+ if (self.$el.find('#saved_export_list option').length <= 1) {
+ self.$el.find('#ExistsExportList').hide();
}
}
});
});
},
do_load_export_field: function(field_list) {
- var export_node = this.$element.find("#fields_list");
+ var export_node = this.$el.find("#fields_list");
_(field_list).each(function (field) {
export_node.append(new Option(field.label, field.name));
});
},
on_show_save_list: function() {
var self = this;
- var current_node = self.$element.find("#savenewlist");
+ var current_node = self.$el.find("#savenewlist");
if (!(current_node.find("label")).length) {
current_node.append(QWeb.render('ExportNewList'));
current_node.find("#add_export_list").click(function() {
@@ -150,14 +150,14 @@ instance.web.DataExport = instance.web.Dialog.extend({
if (!export_list_id.result) {
return;
}
- self.$element.find("#saved_export_list").append(
+ self.$el.find("#saved_export_list").append(
new Option(value, export_list_id.result));
- if (self.$element.find("#saved_export_list").is(":hidden")) {
+ if (self.$el.find("#saved_export_list").is(":hidden")) {
self.show_exports_list();
}
});
this.on_show_save_list();
- this.$element.find("#fields_list option").remove();
+ this.$el.find("#fields_list option").remove();
},
on_click: function(id, record) {
var self = this;
@@ -173,7 +173,7 @@ instance.web.DataExport = instance.web.Dialog.extend({
}
if (!record.loaded) {
- var import_comp = self.$element.find("#import_compat").val();
+ var import_comp = self.$el.find("#import_compat").val();
self.rpc("/web/export/get_fields", {
model: model,
prefix: prefix,
@@ -191,44 +191,44 @@ instance.web.DataExport = instance.web.Dialog.extend({
},
on_show_data: function(result, after) {
var self = this;
- var imp_cmpt = Boolean(self.$element.find("#import_compat").val());
+ var imp_cmpt = Boolean(self.$el.find("#import_compat").val());
if (after) {
- var current_tr = self.$element.find("tr[id='treerow-" + after + "']");
+ var current_tr = self.$el.find("tr[id='treerow-" + after + "']");
current_tr.addClass('open');
current_tr.find('img').attr('src','/web/static/src/img/collapse.gif');
current_tr.after(QWeb.render('ExportTreeView-Secondary.children', {'fields': result}));
} else {
- self.$element.find('#left_field_panel').append(QWeb.render('ExportTreeView-Secondary', {'fields': result}));
+ self.$el.find('#left_field_panel').append(QWeb.render('ExportTreeView-Secondary', {'fields': result}));
}
_.each(result, function(record) {
self.records[record.id] = record.value;
if (record.required) {
- var required_fld = self.$element.find("tr[id='treerow-" + record.id + "']").find('#tree-column');
+ var required_fld = self.$el.find("tr[id='treerow-" + record.id + "']").find('#tree-column');
required_fld.addClass("oe_export_requiredfield");
}
- self.$element.find("img[id='parentimg-" + record.id +"']").click(function() {
+ self.$el.find("img[id='parentimg-" + record.id +"']").click(function() {
self.on_click(this.id, record);
});
- self.$element.find("tr[id='treerow-" + record.id + "']").click(function(e) {
+ self.$el.find("tr[id='treerow-" + record.id + "']").click(function(e) {
if (e.shiftKey) {
var frst_click, scnd_click = '';
if (self.row_index == 0) {
self.row_index = this.rowIndex;
- frst_click = self.$element.find("tr[id^='treerow-']")[self.row_index-1];
+ frst_click = self.$el.find("tr[id^='treerow-']")[self.row_index-1];
$(frst_click).addClass("ui-selected");
} else {
if (this.rowIndex >=self.row_index) {
for (var i = (self.row_index-1); i < this.rowIndex; i++) {
- scnd_click = self.$element.find("tr[id^='treerow-']")[i];
+ scnd_click = self.$el.find("tr[id^='treerow-']")[i];
if (!$(scnd_click).find('#tree-column').hasClass("oe_export_readonlyfield")) {
$(scnd_click).addClass("ui-selected");
}
}
} else {
for (var i = (self.row_index-1); i >= (this.rowIndex-1); i--) {
- scnd_click = self.$element.find("tr[id^='treerow-']")[i];
+ scnd_click = self.$el.find("tr[id^='treerow-']")[i];
if (!$(scnd_click).find('#tree-column').hasClass("oe_export_readonlyfield")) {
$(scnd_click).addClass("ui-selected");
}
@@ -238,10 +238,10 @@ instance.web.DataExport = instance.web.Dialog.extend({
}
self.row_index = this.rowIndex;
- self.$element.find("tr[id='treerow-" + record.id + "']").keyup(function() {
+ self.$el.find("tr[id='treerow-" + record.id + "']").keyup(function() {
self.row_index = 0;
});
- var o2m_selection = self.$element.find("tr[id='treerow-" + record.id + "']").find('#tree-column');
+ var o2m_selection = self.$el.find("tr[id='treerow-" + record.id + "']").find('#tree-column');
if ($(o2m_selection).hasClass("oe_export_readonlyfield")) {
return false;
}
@@ -252,14 +252,14 @@ instance.web.DataExport = instance.web.Dialog.extend({
$(this).addClass('ui-selected').find('a').focus();
}
} else if (!e.shiftKey) {
- self.$element.find("tr.ui-selected")
+ self.$el.find("tr.ui-selected")
.removeClass("ui-selected").find('a').blur();
$(this).addClass("ui-selected").find('a').focus();
}
return false;
});
- self.$element.find("tr[id='treerow-" + record.id + "']").keydown(function(e) {
+ self.$el.find("tr[id='treerow-" + record.id + "']").keydown(function(e) {
var keyCode = e.keyCode || e.which;
var arrow = {left: 37, up: 38, right: 39, down: 40 };
switch (keyCode) {
@@ -297,19 +297,19 @@ instance.web.DataExport = instance.web.Dialog.extend({
break;
}
});
- self.$element.find("tr[id='treerow-" + record.id + "']").dblclick(function() {
- var $o2m_selection = self.$element.find("tr[id^='treerow-" + record.id + "']").find('#tree-column');
+ self.$el.find("tr[id='treerow-" + record.id + "']").dblclick(function() {
+ var $o2m_selection = self.$el.find("tr[id^='treerow-" + record.id + "']").find('#tree-column');
if (!$o2m_selection.hasClass("oe_export_readonlyfield")) {
self.add_field(record.id, $(this).find("a").attr("string"));
}
});
});
- self.$element.find('#fields_list').mouseover(function(event) {
+ self.$el.find('#fields_list').mouseover(function(event) {
if (event.relatedTarget) {
if (event.relatedTarget.attributes['id'] && event.relatedTarget.attributes['string']) {
var field_id = event.relatedTarget.attributes["id"]["value"];
if (field_id && field_id.split("-")[0] === 'export') {
- if (!self.$element.find("tr[id='treerow-" + field_id.split("-")[1] + "']").find('#tree-column').hasClass("oe_export_readonlyfield")) {
+ if (!self.$el.find("tr[id='treerow-" + field_id.split("-")[1] + "']").find('#tree-column').hasClass("oe_export_readonlyfield")) {
self.add_field(field_id.split("-")[1], event.relatedTarget.attributes["string"]["value"]);
}
}
@@ -319,7 +319,7 @@ instance.web.DataExport = instance.web.Dialog.extend({
},
showcontent: function(id) {
// show & hide the contents
- var $this = this.$element.find("tr[id='treerow-" + id + "']");
+ var $this = this.$el.find("tr[id='treerow-" + id + "']");
var is_open = $this.hasClass('open');
$this.toggleClass('open');
@@ -329,7 +329,7 @@ instance.web.DataExport = instance.web.Dialog.extend({
} else {
first_child.attr('src', '/web/static/src/img/collapse.gif');
}
- var child_field = this.$element.find("tr[id^='treerow-" + id +"/']");
+ var child_field = this.$el.find("tr[id^='treerow-" + id +"/']");
var child_len = (id.split("/")).length + 1;
for (var i = 0; i < child_field.length; i++) {
var $child = $(child_field[i]);
@@ -345,15 +345,15 @@ instance.web.DataExport = instance.web.Dialog.extend({
}
},
add_field: function(field_id, string) {
- var field_list = this.$element.find('#fields_list');
- if (this.$element.find("#fields_list option[value='" + field_id + "']")
- && !this.$element.find("#fields_list option[value='" + field_id + "']").length) {
+ var field_list = this.$el.find('#fields_list');
+ if (this.$el.find("#fields_list option[value='" + field_id + "']")
+ && !this.$el.find("#fields_list option[value='" + field_id + "']").length) {
field_list.append(new Option(string, field_id));
}
},
get_fields: function() {
var export_field = [];
- this.$element.find("#fields_list option").each(function() {
+ this.$el.find("#fields_list option").each(function() {
export_field.push($(this).val());
});
if (!export_field.length) {
@@ -363,7 +363,7 @@ instance.web.DataExport = instance.web.Dialog.extend({
},
on_click_export_data: function() {
var self = this;
- var exported_fields = this.$element.find('#fields_list option').map(function () {
+ var exported_fields = this.$el.find('#fields_list option').map(function () {
// DOM property is textContent, but IE8 only knows innerText
return {name: self.records[this.value] || this.value,
label: this.textContent || this.innerText};
@@ -375,8 +375,8 @@ instance.web.DataExport = instance.web.Dialog.extend({
}
exported_fields.unshift({name: 'id', label: 'External ID'});
- var export_format = this.$element.find("#export_format").val();
- $.blockUI();
+ var export_format = this.$el.find("#export_format").val();
+ instance.web.blockUI();
this.session.get_file({
url: '/web/export/' + export_format,
data: {data: JSON.stringify({
@@ -385,13 +385,13 @@ instance.web.DataExport = instance.web.Dialog.extend({
ids: this.dataset.ids,
domain: this.dataset.domain,
import_compat: Boolean(
- this.$element.find("#import_compat").val())
+ this.$el.find("#import_compat").val())
})},
- complete: $.unblockUI
+ complete: instance.web.unblockUI
});
},
close: function() {
- this.$element.remove();
+ this.$el.remove();
this._super();
}
});
diff --git a/addons/web/static/src/js/data_import.js b/addons/web/static/src/js/data_import.js
deleted file mode 100644
index 30f9323f723..00000000000
--- a/addons/web/static/src/js/data_import.js
+++ /dev/null
@@ -1,366 +0,0 @@
-openerp.web.data_import = function(instance) {
-var QWeb = instance.web.qweb,
- _t = instance.web._t;
-/**
- * Safari does not deal well at all with raw JSON data being returned. As a
- * result, we're going to cheat by using a pseudo-jsonp: instead of getting
- * JSON data in the iframe, we're getting a ``script`` tag which consists of a
- * function call and the returned data (the json dump).
- *
- * The function is an auto-generated name bound to ``window``, which calls
- * back into the callback provided here.
- *
- * @param {Object} form the form element (DOM or jQuery) to use in the call
- * @param {Object} attributes jquery.form attributes object
- * @param {Function} callback function to call with the returned data
- */
-function jsonp(form, attributes, callback) {
- attributes = attributes || {};
- var options = {jsonp: _.uniqueId('import_callback_')};
- window[options.jsonp] = function () {
- delete window[options.jsonp];
- callback.apply(null, arguments);
- };
- if ('data' in attributes) {
- _.extend(attributes.data, options);
- } else {
- _.extend(attributes, {data: options});
- }
- $(form).ajaxSubmit(attributes);
-}
-
-instance.web.DataImport = instance.web.Dialog.extend({
- template: 'ImportDataView',
- dialog_title: {toString: function () { return _t("Import Data"); }},
- init: function(parent, dataset){
- var self = this;
- this._super(parent, {});
- this.model = parent.model;
- this.fields = [];
- this.all_fields = [];
- this.fields_with_defaults = [];
- this.required_fields = null;
-
- var convert_fields = function (root, prefix) {
- prefix = prefix || '';
- _(root.fields).each(function (f) {
- self.all_fields.push(prefix + f.name);
- if (f.fields) {
- convert_fields(f, prefix + f.id + '/');
- }
- });
- };
- this.ready = $.Deferred.queue().then(function () {
- self.required_fields = _(self.fields).chain()
- .filter(function (field) {
- return field.required &&
- !_.include(self.fields_with_defaults, field.id); })
- .pluck('name')
- .value();
- convert_fields(self);
- self.all_fields.sort();
- });
- },
- start: function() {
- var self = this;
- this._super();
- this.open({
- buttons: [
- {text: _t("Close"), click: function() { self.destroy(); }},
- {text: _t("Import File"), click: function() { self.do_import(); }, 'class': 'oe-dialog-import-button'}
- ],
- close: function(event, ui) {
- self.destroy();
- }
- });
- this.toggle_import_button(false);
- this.$element.find('#csvfile').change(this.on_autodetect_data);
- this.$element.find('fieldset').change(this.on_autodetect_data);
- this.$element.delegate('fieldset legend', 'click', function() {
- $(this).parent().toggleClass('oe-closed');
- });
- this.ready.push(new instance.web.DataSet(this, this.model).call(
- 'fields_get', [], function (fields) {
- self.graft_fields(fields);
- self.ready.push(new instance.web.DataSet(self, self.model)
- .default_get(_.pluck(self.fields, 'id')).then(function (fields) {
- _.each(fields, function(val, key) {
- if (val) {
- self.fields_with_defaults.push(key);
- }
- });
- })
- )
- }));
- },
- graft_fields: function (fields, parent, level) {
- parent = parent || this;
- level = level || 0;
-
- var self = this;
- if (level === 0) {
- parent.fields.push({
- id: 'id',
- name: 'id',
- string: _t('External ID'),
- required: false
- });
- }
- _(fields).each(function (field, field_name) {
- // Ignore spec for id field
- // Don't import function fields (function and related)
- if (field_name === 'id') {
- return;
- }
- // Skip if there's no state which could disable @readonly,
- // if a field is ever always readonly we can't import it
- if (field.readonly) {
- // no states at all
- if (_.isEmpty(field.states)) { return; }
- // no state altering @readonly
- if (!_.any(field.states, function (modifiers) {
- return _(modifiers).chain().pluck(0).contains('readonly').value();
- })) { return; }
- }
-
- var f = {
- id: field_name,
- name: field_name,
- string: field.string,
- required: field.required
- };
-
- switch (field.type) {
- case 'many2many':
- case 'many2one':
- f.name += '/id';
- break;
- case 'one2many':
- f.name += '/id';
- f.fields = [];
- // only fetch sub-fields to a depth of 2 levels
- if (level < 2) {
- self.ready.push(new instance.web.DataSet(self, field.relation).call(
- 'fields_get', [], function (fields) {
- self.graft_fields(fields, f, level+1);
- }));
- }
- break;
- }
- parent.fields.push(f);
- });
- },
- toggle_import_button: function (newstate) {
- instance.web.dialog(this.$element, 'widget')
- .find('.oe-dialog-import-button')
- .button('option', 'disabled', !newstate);
- },
- do_import: function() {
- if(!this.$element.find('#csvfile').val()) { return; }
- var lines_to_skip = parseInt(this.$element.find('#csv_skip').val(), 10);
- var with_headers = this.$element.find('#file_has_headers').prop('checked');
- if (!lines_to_skip && with_headers) {
- lines_to_skip = 1;
- }
- var indices = [], fields = [];
- this.$element.find(".sel_fields").each(function(index, element) {
- var val = element.value;
- if (!val) {
- return;
- }
- indices.push(index);
- fields.push(val);
- });
-
- jsonp(this.$element.find('#import_data'), {
- url: '/web/import/import_data',
- data: {
- model: this.model,
- meta: JSON.stringify({
- skip: lines_to_skip,
- indices: indices,
- fields: fields
- })
- }
- }, this.on_import_results);
- },
- on_autodetect_data: function() {
- if(!this.$element.find('#csvfile').val()) { return; }
- jsonp(this.$element.find('#import_data'), {
- url: '/web/import/detect_data'
- }, this.on_import_results);
- },
- on_import_results: function(results) {
- this.$element.find('#result').empty();
- var headers, result_node = this.$element.find("#result");
-
- if (results['error']) {
- result_node.append(QWeb.render('ImportView.error', {
- 'error': results['error']}));
- this.$element.find('fieldset').removeClass('oe-closed');
- return;
- }
- if (results['success']) {
- if (this.getParent().getParent().active_view == "list") {
- this.getParent().reload_content();
- }
- this.destroy();
- return;
- }
-
- if (results['records']) {
- var lines_to_skip = parseInt(this.$element.find('#csv_skip').val(), 10),
- with_headers = this.$element.find('#file_has_headers').prop('checked');
- headers = with_headers ? results.records[0] : null;
-
- result_node.append(QWeb.render('ImportView.result', {
- 'headers': headers,
- 'records': lines_to_skip ? results.records.slice(lines_to_skip)
- : with_headers ? results.records.slice(1)
- : results.records
- }));
- this.$element.find('fieldset').addClass('oe-closed');
- }
- this.$element.find('form').removeClass('oe-import-no-result');
-
- this.$element.delegate('.oe-m2o-drop-down-button', 'click', function () {
- $(this).prev('input').focus();
- });
-
- var self = this;
- this.ready.then(function () {
- var $fields = self.$element.find('.sel_fields').bind('blur', function () {
- if (this.value && !_(self.all_fields).contains(this.value)) {
- this.value = '';
- }
- }).autocomplete({
- minLength: 0,
- source: self.all_fields,
- change: self.on_check_field_values
- }).focus(function () {
- $(this).autocomplete('search');
- });
- // Column auto-detection
- _(headers).each(function (header, index) {
- var field_name = self.match_column_to_field(header);
- if (field_name) {
- $fields.eq(index).val(field_name);
- }
- });
- self.on_check_field_values();
- });
- },
- /**
- * Returns the name of the field (nested) matching the provided column name
- *
- * @param {String} name column name to look for
- * @param {Array} [fields] fields to look into for the provided name
- * @returns {String|undefined}
- */
- match_column_to_field: function (name, fields) {
- fields = fields || this.fields;
- var f;
- f = _(fields).detect(function (field) {
- // TODO: levenshtein between header and field.string
- return field.name === name
- || field.string.toLowerCase() === name.toLowerCase();
- });
- if (f) { return f.name; }
-
- // if ``name`` is a path (o2m), we need to recurse through its .fields
- var index = name.indexOf('/');
- if (index === -1) { return undefined; }
- // Get the first path section, try to find the matching field
- var column_name = name.substring(0, index);
- f = _(fields).detect(function (field) {
- // field.name for o2m is $foo/id, so we want to match on id
- return field.id === column_name
- || field.string.toLowerCase() === column_name.toLowerCase()
- });
- if (!f) { return undefined; }
-
- // if we found a matching field for the first path section, recurse in
- // its own .fields to try and get the rest of the path matched
- var rest = this.match_column_to_field(
- name.substring(index+1), f.fields);
- if (!rest) { return undefined; }
- return f.id + '/' + rest;
- },
- /**
- * Looks through all the field selections, and tries to find if two
- * (or more) columns were matched to the same model field.
- *
- * Returns a map of the multiply-mapped fields to an array of offending
- * columns (not actually columns, but the inputs containing the same field
- * names).
- *
- * Also has the side-effect of marking the discovered inputs with the class
- * ``duplicate_fld``.
- *
- * @returns {Object<String, Array<String>>} map of duplicate field matches to same-valued inputs
- */
- find_duplicate_fields: function() {
- // Maps values to DOM nodes, in order to discover duplicates
- var values = {}, duplicates = {};
- this.$element.find(".sel_fields").each(function(index, element) {
- var value = element.value;
- var $element = $(element).removeClass('duplicate_fld');
- if (!value) { return; }
-
- if (!(value in values)) {
- values[value] = element;
- } else {
- var same_valued_field = values[value];
- if (value in duplicates) {
- duplicates[value].push(element);
- } else {
- duplicates[value] = [same_valued_field, element];
- }
- $element.add(same_valued_field).addClass('duplicate_fld');
- }
- });
- return duplicates;
- },
- on_check_field_values: function () {
- this.$element.find("#message, #msg").remove();
-
- var required_valid = this.check_required();
-
- var duplicates = this.find_duplicate_fields();
- if (_.isEmpty(duplicates)) {
- this.toggle_import_button(required_valid);
- } else {
- var $err = $('<div id="msg" style="color: red;">Destination fields should only be selected once, some fields are selected more than once:</div>').insertBefore(this.$element.find('#result'));
- var $dupes = $('<dl>').appendTo($err);
- _(duplicates).each(function(elements, value) {
- $('<dt>').text(value).appendTo($dupes);
- _(elements).each(function(element) {
- var cell = $(element).closest('td');
- $('<dd>').text(cell.parent().children().index(cell)).appendTo($dupes);
- });
- });
- this.toggle_import_button(false);
- }
-
- },
- check_required: function() {
- if (!this.required_fields.length) { return true; }
-
- var selected_fields = _(this.$element.find('.sel_fields').get()).chain()
- .pluck('value')
- .compact()
- .value();
-
- var missing_fields = _.difference(this.required_fields, selected_fields);
- if (missing_fields.length) {
- this.$element.find("#result").before('<div id="message" style="color:red">*Required Fields are not selected : ' + missing_fields + '.</div>');
- return false;
- }
- return true;
- },
- destroy: function() {
- this.$element.remove();
- this._super();
- }
-});
-};
diff --git a/addons/web/static/src/js/formats.js b/addons/web/static/src/js/formats.js
index 0f1c71643be..3e5e6fecd5e 100644
--- a/addons/web/static/src/js/formats.js
+++ b/addons/web/static/src/js/formats.js
@@ -118,7 +118,9 @@ instance.web.format_value = function (value, descriptor, value_if_empty) {
return instance.web.insert_thousand_seps(
_.str.sprintf('%d', value));
case 'float':
- var precision = descriptor.digits ? descriptor.digits[1] : 2;
+ var digits = descriptor.digits ? descriptor.digits : [69,2];
+ digits = typeof digits === "string" ? py.eval(digits) : digits;
+ var precision = digits[1];
var formatted = _.str.sprintf('%.' + precision + 'f', value).split('.');
formatted[0] = instance.web.insert_thousand_seps(formatted[0]);
return formatted.join(l10n.decimal_point);
@@ -136,6 +138,9 @@ instance.web.format_value = function (value, descriptor, value_if_empty) {
return value[1];
case 'one2many':
case 'many2many':
+ if (typeof value === 'string') {
+ return value;
+ }
return _.str.sprintf(_t("(%d records)"), value.length);
case 'datetime':
if (typeof(value) == "string")
@@ -269,82 +274,4 @@ instance.web.auto_date_to_str = function(value, type) {
}
};
-/**
- * Formats a provided cell based on its field type. Most of the field types
- * return a correctly formatted value, but some tags and fields are
- * special-cased in their handling:
- *
- * * buttons will return an actual ``<button>`` tag with a bunch of error handling
- *
- * * boolean fields will return a checkbox input, potentially disabled
- *
- * * binary fields will return a link to download the binary data as a file
- *
- * @param {Object} row_data record whose values should be displayed in the cell
- * @param {Object} column column descriptor
- * @param {"button"|"field"} column.tag base control type
- * @param {String} column.type widget type for a field control
- * @param {String} [column.string] button label
- * @param {String} [column.icon] button icon
- * @param {Object} [options]
- * @param {String} [options.value_if_empty=''] what to display if the field's value is ``false``
- * @param {Boolean} [options.process_modifiers=true] should the modifiers be computed ?
- * @param {String} [options.model] current record's model
- * @param {Number} [options.id] current record's id
- *
- */
-instance.web.format_cell = function (row_data, column, options) {
- options = options || {};
- var attrs = {};
- if (options.process_modifiers !== false) {
- attrs = column.modifiers_for(row_data);
- }
- if (attrs.invisible) { return ''; }
-
- if (column.tag === 'button') {
- return _.template('<button type="button" title="<%-title%>" <%=additional_attributes%> >' +
- '<img src="<%-prefix%>/web/static/src/img/icons/<%-icon%>.png" alt="<%-alt%>"/>' +
- '</button>', {
- title: column.string || '',
- additional_attributes: isNaN(row_data["id"].value) && instance.web.BufferedDataSet.virtual_id_regex.test(row_data["id"].value) ?
- 'disabled="disabled" class="oe-listview-button-disabled"' : '',
- prefix: instance.connection.prefix,
- icon: column.icon,
- alt: column.string || ''
- });
- }
- if (!row_data[column.id]) {
- return options.value_if_empty === undefined ? '' : options.value_if_empty;
- }
-
- switch (column.widget || column.type) {
- case "boolean":
- return _.str.sprintf('<input type="checkbox" %s disabled="disabled"/>',
- row_data[column.id].value ? 'checked="checked"' : '');
- case "binary":
- var text = _t("Download"),
- download_url = _.str.sprintf('/web/binary/saveas?session_id=%s&model=%s&field=%s&id=%d', instance.connection.session_id, options.model, column.id, options.id);
- if (column.filename) {
- download_url += '&filename_field=' + column.filename;
- if (row_data[column.filename]) {
- text = _.str.sprintf(_t("Download \"%s\""), instance.web.format_value(
- row_data[column.filename].value, {type: 'char'}));
- }
- }
- return _.template('<a href="<%-href%>"><%-text%></a> (%<-size%>)', {
- text: text,
- href: download_url,
- size: row_data[column.id].value
- });
- case 'progressbar':
- return _.template(
- '<progress value="<%-value%>" max="100"><%-value%>%</progress>', {
- value: _.str.sprintf("%.0f", row_data[column.id].value || 0)
- });
- }
-
- return _.escape(instance.web.format_value(
- row_data[column.id].value, column, options.value_if_empty));
-}
-
};
diff --git a/addons/web/static/src/js/search.js b/addons/web/static/src/js/search.js
index f9e737471ae..01bc0c76c87 100644
--- a/addons/web/static/src/js/search.js
+++ b/addons/web/static/src/js/search.js
@@ -126,21 +126,21 @@ my.InputView = instance.web.Widget.extend({
template: 'SearchView.InputView',
start: function () {
var p = this._super.apply(this, arguments);
- this.$element.on('focus', this.proxy('onFocus'));
- this.$element.on('blur', this.proxy('onBlur'));
- this.$element.on('keydown', this.proxy('onKeydown'));
+ this.$el.on('focus', this.proxy('onFocus'));
+ this.$el.on('blur', this.proxy('onBlur'));
+ this.$el.on('keydown', this.proxy('onKeydown'));
return p;
},
onFocus: function () {
this.trigger('focused', this);
},
onBlur: function () {
- this.$element.text('');
+ this.$el.text('');
this.trigger('blurred', this);
},
getSelection: function () {
// get Text node
- var root = this.$element[0].childNodes[0];
+ var root = this.$el[0].childNodes[0];
if (!root || !root.textContent) {
// if input does not have a child node, or the child node is an
// empty string, then the selection can only be (0, 0)
@@ -202,7 +202,7 @@ my.InputView = instance.web.Widget.extend({
break;
case $.ui.keyCode.RIGHT:
sel = this.getSelection();
- var len = this.$element.text().length;
+ var len = this.$el.text().length;
if (sel.start !== len || sel.start !== sel.end) {
e.stopPropagation();
}
@@ -223,17 +223,17 @@ my.FacetView = instance.web.Widget.extend({
},
start: function () {
var self = this;
- this.$element.on('focus', function () { self.trigger('focused', self); });
- this.$element.on('blur', function () { self.trigger('blurred', self); });
- this.$element.on('click', function (e) {
+ this.$el.on('focus', function () { self.trigger('focused', self); });
+ this.$el.on('blur', function () { self.trigger('blurred', self); });
+ this.$el.on('click', function (e) {
if ($(e.target).is('.oe_facet_remove')) {
self.model.destroy();
return false;
}
- self.$element.focus();
+ self.$el.focus();
e.stopPropagation();
});
- this.$element.on('keydown', function (e) {
+ this.$el.on('keydown', function (e) {
var keys = $.ui.keyCode;
switch (e.which) {
case keys.BACKSPACE:
@@ -242,7 +242,7 @@ my.FacetView = instance.web.Widget.extend({
return false;
}
});
- var $e = self.$element.find('> span:last-child');
+ var $e = self.$el.find('> span:last-child');
var q = $.when(this._super());
return q.pipe(function () {
var values = self.model.values.map(function (value) {
@@ -253,7 +253,7 @@ my.FacetView = instance.web.Widget.extend({
});
},
model_changed: function () {
- this.$element.text(this.$element.text() + '*');
+ this.$el.text(this.$el.text() + '*');
}
});
my.FacetValueView = instance.web.Widget.extend({
@@ -268,7 +268,7 @@ my.FacetValueView = instance.web.Widget.extend({
this._super();
},
model_changed: function () {
- this.$element.text(this.$element.text() + '*');
+ this.$el.text(this.$el.text() + '*');
}
});
@@ -313,7 +313,7 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
.on('add change reset remove', this.proxy('renderFacets'));
if (this.hidden) {
- this.$element.hide();
+ this.$el.hide();
}
if (this.headless) {
this.ready.resolve();
@@ -330,7 +330,13 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
});
}
- this.$element.on('keydown',
+ // Launch a search on clicking the oe_searchview_search button
+ this.$el.on('click', 'button.oe_searchview_search', function (e) {
+ e.stopImmediatePropagation();
+ self.do_search();
+ });
+
+ this.$el.on('keydown',
'.oe_searchview_input, .oe_searchview_facet', function (e) {
switch(e.which) {
case $.ui.keyCode.LEFT:
@@ -344,26 +350,31 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
}
});
- this.$element.on('click', '.oe_searchview_clear', function (e) {
+ this.$el.on('click', '.oe_searchview_clear', function (e) {
e.stopImmediatePropagation();
self.query.reset();
});
- this.$element.on('click', '.oe_searchview_unfold_drawer', function (e) {
+ this.$el.on('click', '.oe_searchview_unfold_drawer', function (e) {
e.stopImmediatePropagation();
- self.$element.toggleClass('oe_searchview_open_drawer');
+ self.$el.toggleClass('oe_searchview_open_drawer');
+ });
+ instance.web.bus.on('click', this, function(ev) {
+ if ($(ev.target).parents('.oe_searchview').length === 0) {
+ self.$el.removeClass('oe_searchview_open_drawer');
+ }
});
// Focus last input if the view itself is clicked (empty section of
// facets element)
- this.$element.on('click', function (e) {
- if (e.target === self.$element.find('.oe_searchview_facets')[0]) {
- self.$element.find('.oe_searchview_input:last').focus();
+ this.$el.on('click', function (e) {
+ if (e.target === self.$el.find('.oe_searchview_facets')[0]) {
+ self.$el.find('.oe_searchview_input:last').focus();
}
});
// when the completion list opens/refreshes, automatically select the
// first completion item so if the user just hits [RETURN] or [TAB] it
// automatically selects it
- this.$element.on('autocompleteopen', function () {
- var menu = self.$element.data('autocomplete').menu;
+ this.$el.on('autocompleteopen', function () {
+ var menu = self.$el.data('autocomplete').menu;
menu.activate(
$.Event({ type: "mouseenter" }),
menu.element.children().first());
@@ -372,15 +383,15 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
return $.when(p, this.ready);
},
show: function () {
- this.$element.show();
+ this.$el.show();
},
hide: function () {
- this.$element.hide();
+ this.$el.hide();
},
subviewForRoot: function (subview_root) {
return _(this.input_subviews).detect(function (subview) {
- return subview.$element[0] === subview_root;
+ return subview.$el[0] === subview_root;
});
},
siblingSubview: function (subview, direction, wrap_around) {
@@ -395,12 +406,12 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
focusPreceding: function (subview_root) {
return this.siblingSubview(
this.subviewForRoot(subview_root), -1, true)
- .$element.focus();
+ .$el.focus();
},
focusFollowing: function (subview_root) {
return this.siblingSubview(
this.subviewForRoot(subview_root), +1, true)
- .$element.focus();
+ .$el.focus();
},
/**
@@ -419,19 +430,19 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
// autocomplete only correctly handles being initialized on the actual
// editable element (and only an element with a @value in 1.8 e.g.
- // input or textarea), cheat by setting val() on $element
- this.$element.on('keydown', function () {
+ // input or textarea), cheat by setting val() on $el
+ this.$el.on('keydown', function () {
// keydown is triggered *before* the element's value is set, so
// delay this. Pray that setTimeout are executed in FIFO (if they
// have the same delay) as autocomplete uses the exact same trick.
// FIXME: brittle as fuck
setTimeout(function () {
- self.$element.val(self.currentInputValue());
+ self.$el.val(self.currentInputValue());
}, 0);
});
- this.$element.autocomplete({
+ this.$el.autocomplete({
source: this.proxy('complete_global_search'),
select: this.proxy('select_completion'),
focus: function (e) { e.preventDefault(); },
@@ -468,7 +479,7 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
* div[contenteditable].oe_searchview_input)
*/
currentInputValue: function () {
- return this.$element.find('div.oe_searchview_input:focus').text();
+ return this.$el.find('div.oe_searchview_input:focus').text();
},
/**
* Provide auto-completion result for req.term (an array to `resp`)
@@ -498,14 +509,23 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
var input_index = _(this.input_subviews).indexOf(
this.subviewForRoot(
- this.$element.find('div.oe_searchview_input:focus')[0]));
+ this.$el.find('div.oe_searchview_input:focus')[0]));
this.query.add(ui.item.facet, {at: input_index / 2});
},
childFocused: function () {
- this.$element.addClass('oe_focused');
+ this.$el.addClass('oe_focused');
},
childBlurred: function () {
- this.$element.removeClass('oe_focused');
+ var val = this.$el.val();
+ this.$el.val('');
+ var complete = this.$el.data('autocomplete');
+ if ((val && complete.term === undefined) || complete.previous !== undefined) {
+ throw new Error("new jquery.ui version altering implementation" +
+ " details relied on");
+ }
+ delete complete.term;
+ this.$el.removeClass('oe_focused')
+ .trigger('blur');
},
/**
*
@@ -518,7 +538,7 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
// _2: undefined if event=change, otherwise model
var self = this;
var started = [];
- var $e = this.$element.find('div.oe_searchview_facets');
+ var $e = this.$el.find('div.oe_searchview_facets');
_.invoke(this.input_subviews, 'destroy');
this.input_subviews = [];
@@ -549,7 +569,7 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
input_to_focus = self.input_subviews[(options.at + 1) * 2];
}
- input_to_focus.$element.focus();
+ input_to_focus.$el.focus();
});
},
@@ -615,6 +635,16 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
return null;
}
},
+
+ add_common_inputs: function() {
+ // add Filters to this.inputs, need view.controls filled
+ (new instance.web.search.Filters(this));
+ // add custom filters to this.inputs
+ (new instance.web.search.CustomFilters(this));
+ // add Advanced to this.inputs
+ (new instance.web.search.Advanced(this));
+ },
+
on_loaded: function(data) {
var self = this;
this.fields_view = data.fields_view;
@@ -629,18 +659,13 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
data.fields_view['arch'].children,
data.fields_view.fields);
- // add Filters to this.inputs, need view.controls filled
- (new instance.web.search.Filters(this));
- // add custom filters to this.inputs
- (new instance.web.search.CustomFilters(this));
- // add Advanced to this.inputs
- (new instance.web.search.Advanced(this));
+ this.add_common_inputs();
// build drawer
var drawer_started = $.when.apply(
null, _(this.select_for_drawer()).invoke(
- 'appendTo', this.$element.find('.oe_searchview_drawer')));
-
+ 'appendTo', this.$el.find('.oe_searchview_drawer')));
+
// load defaults
var defaults_fetched = $.when.apply(null, _(this.inputs).invoke(
'facet_for_defaults', this.defaults)).then(function () {
@@ -655,15 +680,12 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
*/
on_filters_management: function(e) {
var self = this;
- var select = this.$element.find(".oe_search-view-filters-management");
+ var select = this.$el.find(".oe_search-view-filters-management");
var val = select.val();
switch(val) {
case 'advanced_filter':
this.extended_search.on_activate();
break;
- case 'add_to_dashboard':
- this.on_add_to_dashboard();
- break;
case '':
this.do_clear();
}
@@ -692,53 +714,6 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
select.val('');
}
},
- on_add_to_dashboard: function() {
- this.$element.find(".oe_search-view-filters-management")[0].selectedIndex = 0;
- var self = this,
- menu = instance.webclient.menu,
- $dialog = $(QWeb.render("SearchView.add_to_dashboard", {
- dashboards : menu.data.data.children,
- selected_menu_id : menu.$element.find('a.active').data('menu')
- }));
- $dialog.find('input').val(this.fields_view.name);
- instance.web.dialog($dialog, {
- modal: true,
- title: _t("Add to Dashboard"),
- buttons: [
- {text: _t("Cancel"), click: function() {
- $(this).dialog("close");
- }},
- {text: _t("OK"), click: function() {
- $(this).dialog("close");
- var menu_id = $(this).find("select").val(),
- title = $(this).find("input").val(),
- data = self.build_search_data(),
- context = new instance.web.CompoundContext(),
- domain = new instance.web.CompoundDomain();
- _.each(data.contexts, function(x) {
- context.add(x);
- });
- _.each(data.domains, function(x) {
- domain.add(x);
- });
- self.rpc('/web/searchview/add_to_dashboard', {
- menu_id: menu_id,
- action_id: self.getParent().action.id,
- context_to_save: context,
- domain: domain,
- view_mode: self.getParent().active_view,
- name: title
- }, function(r) {
- if (r === false) {
- self.do_warn("Could not add filter to dashboard");
- } else {
- self.do_notify("Filter added to dashboard", '');
- }
- });
- }}
- ]
- });
- },
/**
* Extract search data from the view's facets.
*
@@ -886,13 +861,13 @@ instance.web.search.Invalid = instance.web.Class.extend( /** @lends instance.web
);
}
});
-instance.web.search.Widget = instance.web.OldWidget.extend( /** @lends instance.web.search.Widget# */{
+instance.web.search.Widget = instance.web.Widget.extend( /** @lends instance.web.search.Widget# */{
template: null,
/**
* Root class of all search widgets
*
* @constructs instance.web.search.Widget
- * @extends instance.web.OldWidget
+ * @extends instance.web.Widget
*
* @param view the ancestor view of this widget
*/
@@ -1014,7 +989,7 @@ instance.web.search.FilterGroup = instance.web.search.Input.extend(/** @lends in
this.view.query.on('add remove change reset', this.proxy('search_change'));
},
start: function () {
- this.$element.on('click', 'li', this.proxy('toggle_filter'));
+ this.$el.on('click', 'li', this.proxy('toggle_filter'));
return $.when(null);
},
/**
@@ -1023,7 +998,7 @@ instance.web.search.FilterGroup = instance.web.search.Input.extend(/** @lends in
*/
search_change: function () {
var self = this;
- var $filters = this.$element.find('> li').removeClass('oe_selected');
+ var $filters = this.$el.find('> li').removeClass('oe_selected');
var facet = this.view.query.find(_.bind(this.match_facet, this));
if (!facet) { return; }
facet.values.each(function (v) {
@@ -1234,7 +1209,7 @@ instance.web.search.Field = instance.web.search.Input.extend( /** @lends instanc
var self = this;
// A field needs a context to send when active
var context = this.attrs.context;
- if (!context || !facet.values.length) {
+ if (_.isEmpty(context) || !facet.values.length) {
return;
}
var contexts = facet.values.map(function (facetValue) {
@@ -1324,17 +1299,17 @@ instance.web.search.CharField = instance.web.search.Field.extend( /** @lends ins
});
instance.web.search.NumberField = instance.web.search.Field.extend(/** @lends instance.web.search.NumberField# */{
value_from: function () {
- if (!this.$element.val()) {
+ if (!this.$el.val()) {
return null;
}
- var val = this.parse(this.$element.val()),
- check = Number(this.$element.val());
+ var val = this.parse(this.$el.val()),
+ check = Number(this.$el.val());
if (isNaN(val) || val !== check) {
- this.$element.addClass('error');
+ this.$el.addClass('error');
throw new instance.web.search.Invalid(
- this.attrs.name, this.$element.val(), this.error_message);
+ this.attrs.name, this.$el.val(), this.error_message);
}
- this.$element.removeClass('error');
+ this.$el.removeClass('error');
return val;
}
});
@@ -1515,9 +1490,16 @@ instance.web.search.ManyToOneField = instance.web.search.CharField.extend({
facet_for: function (value) {
var self = this;
if (value instanceof Array) {
- return $.when(facet_from(this, value));
+ if (value.length === 2 && _.isString(value[1])) {
+ return $.when(facet_from(this, value));
+ }
+ assert(value.length <= 1,
+ _("M2O search fields do not currently handle multiple default values"));
+ // there are many cases of {search_default_$m2ofield: [id]}, need
+ // to handle this as if it were a single value.
+ value = value[0];
}
- return this.model.call('name_get', [value], {}).pipe(function (names) {
+ return this.model.call('name_get', [value]).pipe(function (names) {
if (_(names).isEmpty()) { return null; }
return facet_from(self, names[0]);
})
@@ -1530,6 +1512,15 @@ instance.web.search.ManyToOneField = instance.web.search.CharField.extend({
return [[name, '=', facetValue.get('value')]];
}
return this._super(name, operator, facetValue);
+ },
+ get_context: function (facet) {
+ var values = facet.values;
+ if (_.isEmpty(this.attrs.context) && values.length === 1) {
+ var c = {};
+ c['default_' + this.attrs.name] = values.at(0).get('value');
+ return c;
+ }
+ return this._super(facet);
}
});
@@ -1540,15 +1531,26 @@ instance.web.search.CustomFilters = instance.web.search.Input.extend({
var self = this;
this.model = new instance.web.Model('ir.filters');
this.filters = {};
- this.$element.on('submit', 'form', this.proxy('save_current'));
- this.$element.on('click', 'h4', function () {
- self.$element.toggleClass('oe_opened');
+ this.view.query
+ .on('remove', function (facet) {
+ if (!facet.get('is_custom_filter')) {
+ return;
+ }
+ self.clear_selection();
+ })
+ .on('reset', this.proxy('clear_selection'));
+ this.$el.on('submit', 'form', this.proxy('save_current'));
+ this.$el.on('click', 'h4', function () {
+ self.$el.toggleClass('oe_opened');
});
// FIXME: local eval of domain and context to get rid of special endpoint
return this.rpc('/web/searchview/get_filters', {
model: this.view.model
}).pipe(this.proxy('set_filters'));
},
+ clear_selection: function () {
+ this.$el.find('li.oe_selected').removeClass('oe_selected');
+ },
append_filter: function (filter) {
var self = this;
var key = _.str.sprintf('(%s)%s', filter.user_id, filter.name);
@@ -1559,7 +1561,7 @@ instance.web.search.CustomFilters = instance.web.search.Input.extend({
} else {
var id = filter.id;
$filter = this.filters[key] = $('<li></li>')
- .appendTo(this.$element.find('.oe_searchview_custom_list'))
+ .appendTo(this.$el.find('.oe_searchview_custom_list'))
.addClass(filter.user_id ? 'oe_searchview_custom_private'
: 'oe_searchview_custom_public')
.text(filter.name);
@@ -1571,7 +1573,7 @@ instance.web.search.CustomFilters = instance.web.search.Input.extend({
$filter.remove();
});
})
- .prependTo($filter);
+ .appendTo($filter);
}
$filter.unbind('click').click(function () {
@@ -1583,8 +1585,10 @@ instance.web.search.CustomFilters = instance.web.search.Input.extend({
get_groupby: function () { return [filter.context]; },
get_domain: function () { return filter.domain; }
},
+ is_custom_filter: true,
values: [{label: filter.name, value: null}]
}]);
+ $filter.addClass('oe_selected');
});
},
set_filters: function (filters) {
@@ -1592,8 +1596,8 @@ instance.web.search.CustomFilters = instance.web.search.Input.extend({
},
save_current: function () {
var self = this;
- var $name = this.$element.find('input:first');
- var private_filter = !this.$element.find('input:last').prop('checked');
+ var $name = this.$el.find('input:first');
+ var private_filter = !this.$el.find('input:last').prop('checked');
var search = this.view.build_search_data();
this.rpc('/web/session/eval_domain_and_context', {
@@ -1606,7 +1610,7 @@ instance.web.search.CustomFilters = instance.web.search.Input.extend({
}
var filter = {
name: $name.val(),
- user_id: private_filter ? instance.connection.uid : false,
+ user_id: private_filter ? instance.session.uid : false,
model_id: self.view.model,
context: results.context,
domain: results.domain
@@ -1615,7 +1619,7 @@ instance.web.search.CustomFilters = instance.web.search.Input.extend({
return self.model.call('create_or_replace', [filter]).then(function (id) {
filter.id = id;
self.append_filter(filter);
- self.$element
+ self.$el
.removeClass('oe_opened')
.find('form')[0].reset();
});
@@ -1660,8 +1664,8 @@ instance.web.search.Filters = instance.web.search.Input.extend({
}
return $.when(
- this.render_column(col1, $('<div>').appendTo(this.$element)),
- this.render_column(col2, $('<div>').appendTo(this.$element)));
+ this.render_column(col1, $('<div>').appendTo(this.$el)),
+ this.render_column(col2, $('<div>').appendTo(this.$el)));
},
render_column: function (column, $el) {
return $.when.apply(null, _(column).map(function (group) {
@@ -1671,15 +1675,16 @@ instance.web.search.Filters = instance.web.search.Input.extend({
}));
}
});
+
instance.web.search.Advanced = instance.web.search.Input.extend({
template: 'SearchView.advanced',
_in_drawer: true,
start: function () {
var self = this;
- this.$element
+ this.$el
.on('keypress keydown keyup', function (e) { e.stopPropagation(); })
.on('click', 'h4', function () {
- self.$element.toggleClass('oe_opened');
+ self.$el.toggleClass('oe_opened');
}).on('click', 'button.oe_add_condition', function () {
self.append_proposition();
}).on('submit', 'form', function (e) {
@@ -1698,7 +1703,7 @@ instance.web.search.Advanced = instance.web.search.Input.extend({
},
append_proposition: function () {
return (new instance.web.search.ExtendedSearchProposition(this, this.fields))
- .appendTo(this.$element.find('ul'));
+ .appendTo(this.$el.find('ul'));
},
commit_search: function () {
var self = this;
@@ -1725,15 +1730,15 @@ instance.web.search.Advanced = instance.web.search.Input.extend({
// add new empty proposition
this.append_proposition();
// TODO: API on searchview
- this.view.$element.removeClass('oe_searchview_open_drawer');
+ this.view.$el.removeClass('oe_searchview_open_drawer');
}
});
-instance.web.search.ExtendedSearchProposition = instance.web.OldWidget.extend(/** @lends instance.web.search.ExtendedSearchProposition# */{
+instance.web.search.ExtendedSearchProposition = instance.web.Widget.extend(/** @lends instance.web.search.ExtendedSearchProposition# */{
template: 'SearchView.extended_search.proposition',
/**
* @constructs instance.web.search.ExtendedSearchProposition
- * @extends instance.web.OldWidget
+ * @extends instance.web.Widget
*
* @param parent
* @param fields
@@ -1749,16 +1754,16 @@ instance.web.search.ExtendedSearchProposition = instance.web.OldWidget.extend(/*
},
start: function () {
var _this = this;
- this.$element.find(".searchview_extended_prop_field").change(function() {
+ this.$el.find(".searchview_extended_prop_field").change(function() {
_this.changed();
});
- this.$element.find('.searchview_extended_delete_prop').click(function () {
+ this.$el.find('.searchview_extended_delete_prop').click(function () {
_this.destroy();
});
this.changed();
},
changed: function() {
- var nval = this.$element.find(".searchview_extended_prop_field").val();
+ var nval = this.$el.find(".searchview_extended_prop_field").val();
if(this.attrs.selected == null || nval != this.attrs.selected.name) {
this.select_field(_.detect(this.fields, function(x) {return x.name == nval;}));
}
@@ -1773,7 +1778,7 @@ instance.web.search.ExtendedSearchProposition = instance.web.OldWidget.extend(/*
if(this.attrs.selected != null) {
this.value.destroy();
this.value = null;
- this.$element.find('.searchview_extended_prop_op').html('');
+ this.$el.find('.searchview_extended_prop_op').html('');
}
this.attrs.selected = field;
if(field == null) {
@@ -1789,9 +1794,9 @@ instance.web.search.ExtendedSearchProposition = instance.web.OldWidget.extend(/*
_.each(this.value.operators, function(operator) {
$('<option>', {value: operator.value})
.text(String(operator.text))
- .appendTo(self.$element.find('.searchview_extended_prop_op'));
+ .appendTo(self.$el.find('.searchview_extended_prop_op'));
});
- var $value_loc = this.$element.find('.searchview_extended_prop_value').empty();
+ var $value_loc = this.$el.find('.searchview_extended_prop_value').empty();
this.value.appendTo($value_loc);
},
@@ -1799,7 +1804,7 @@ instance.web.search.ExtendedSearchProposition = instance.web.OldWidget.extend(/*
if ( this.attrs.selected == null)
return null;
var field = this.attrs.selected;
- var op = this.$element.find('.searchview_extended_prop_op')[0];
+ var op = this.$el.find('.searchview_extended_prop_op')[0];
var operator = op.options[op.selectedIndex];
return {
label: _.str.sprintf(_t('%(field)s %(operator)s "%(value)s"'), {
@@ -1843,7 +1848,7 @@ instance.web.search.ExtendedSearchProposition.Char = instance.web.search.Extende
{value: "!=", text: _lt("is not equal to")}
],
get_value: function() {
- return this.$element.val();
+ return this.$el.val();
}
});
instance.web.search.ExtendedSearchProposition.DateTime = instance.web.search.ExtendedSearchProposition.Field.extend({
@@ -1867,7 +1872,7 @@ instance.web.search.ExtendedSearchProposition.DateTime = instance.web.search.Ext
start: function() {
var ready = this._super();
this.datewidget = new (this.widget())(this);
- this.datewidget.appendTo(this.$element);
+ this.datewidget.appendTo(this.$el);
return ready;
}
});
@@ -1885,11 +1890,11 @@ instance.web.search.ExtendedSearchProposition.Integer = instance.web.search.Exte
{value: "<=", text: _lt("less or equal than")}
],
toString: function () {
- return this.$element.val();
+ return this.$el.val();
},
get_value: function() {
try {
- return instance.web.parse_value(this.$element.val(), {'widget': 'integer'});
+ return instance.web.parse_value(this.$el.val(), {'widget': 'integer'});
} catch (e) {
return "";
}
@@ -1909,11 +1914,11 @@ instance.web.search.ExtendedSearchProposition.Float = instance.web.search.Extend
{value: "<=", text: _lt("less or equal than")}
],
toString: function () {
- return this.$element.val();
+ return this.$el.val();
},
get_value: function() {
try {
- return instance.web.parse_value(this.$element.val(), {'widget': 'float'});
+ return instance.web.parse_value(this.$el.val(), {'widget': 'float'});
} catch (e) {
return "";
}
@@ -1926,12 +1931,12 @@ instance.web.search.ExtendedSearchProposition.Selection = instance.web.search.Ex
{value: "!=", text: _lt("is not")}
],
toString: function () {
- var select = this.$element[0];
+ var select = this.$el[0];
var option = select.options[select.selectedIndex];
return option.label || option.text;
},
get_value: function() {
- return this.$element.val();
+ return this.$el.val();
}
});
instance.web.search.ExtendedSearchProposition.Boolean = instance.web.search.ExtendedSearchProposition.Field.extend({
diff --git a/addons/web/static/src/js/test_support.js b/addons/web/static/src/js/test_support.js
index d46366326b7..8e1317ba2b0 100644
--- a/addons/web/static/src/js/test_support.js
+++ b/addons/web/static/src/js/test_support.js
@@ -1,18 +1,17 @@
openerp.test_support = {
- setup_connection: function (connection) {
+ setup_session: function (session) {
var origin = location.protocol+"//"+location.host;
- _.extend(connection, {
+ _.extend(session, {
origin: origin,
prefix: origin,
server: origin, // keep chs happy
//openerp.web.qweb.default_dict['_s'] = this.origin;
- rpc_function: connection.rpc_json,
+ rpc_function: session.rpc_json,
session_id: false,
uid: false,
username: false,
user_context: {},
db: false,
- openerp_entreprise: false,
// this.module_list = openerp._modules.slice();
// this.module_loaded = {};
// _(this.module_list).each(function (mod) {
@@ -22,7 +21,7 @@ openerp.test_support = {
shortcuts: [],
active_id: null
});
- return connection.session_reload();
+ return session.session_reload();
},
module: function (title, tested_core, nonliterals) {
var conf = QUnit.config.openerp = {};
@@ -31,10 +30,10 @@ openerp.test_support = {
QUnit.stop();
var oe = conf.openerp = window.openerp.init();
window.openerp.web[tested_core](oe);
- var done = openerp.test_support.setup_connection(oe.connection);
+ var done = openerp.test_support.setup_session(oe.session);
if (nonliterals) {
done = done.pipe(function () {
- return oe.connection.rpc('/tests/add_nonliterals', {
+ return oe.session.rpc('/tests/add_nonliterals', {
domains: nonliterals.domains || [],
contexts: nonliterals.contexts || []
}).then(function (r) {
diff --git a/addons/web/static/src/js/view_editor.js b/addons/web/static/src/js/view_editor.js
deleted file mode 100644
index 9fdd4f0bca1..00000000000
--- a/addons/web/static/src/js/view_editor.js
+++ /dev/null
@@ -1,1207 +0,0 @@
-openerp.web.view_editor = function(instance) {
-var _t = instance.web._t;
-var QWeb = instance.web.qweb;
-instance.web.ViewEditor = instance.web.OldWidget.extend({
- init: function(parent, element_id, dataset, view, options) {
- this._super(parent);
- this.element_id = element_id;
- this.parent = parent;
- this.dataset = new instance.web.DataSetSearch(this, 'ir.ui.view', null, null),
- this.model = dataset.model;
- this.xml_element_id = 0;
- this.property = instance.web.ViewEditor.property_widget;
- this.one_object = false;
- },
- start: function() {
- this.init_view_editor();
- },
- init_view_editor: function() {
- var self = this,
- action_title = _.str.sprintf(_t("Manage Views (%s)"), this.model);
- var action = {
- name: action_title,
- context: this.session.user_context,
- domain: [["model", "=", this.model]],
- res_model: 'ir.ui.view',
- views: [[false, 'list']],
- type: 'ir.actions.act_window',
- target: "current",
- limit: this.dataset.limit || 80,
- auto_search : true,
- flags: {
- sidebar: false,
- deletable: false,
- views_switcher: false,
- action_buttons: false,
- search_view: false,
- pager: false,
- radio: true,
- select_view_id: self.parent.fields_view.view_id
- }
- };
- this.view_edit_dialog = new instance.web.Dialog(this, {
- title: action_title,
- width: 850,
- buttons: [
- {text: _t("Create"), click: function() { self.on_create_view(); }},
- {text: _t("Edit"), click: function() { self.xml_element_id = 0; self.get_arch(); }},
- {text: _t("Remove"), click: function() { self.do_delete_view(); }},
- {text: _t("Close"), click: function() { self.view_edit_dialog.close(); window.location.reload(); }}
- ]
- }).open();
- this.view_edit_dialog.on_close.add_last(function(){window.location.reload();});
- this.main_view_id = this.parent.fields_view.view_id;
- this.action_manager = new instance.web.ActionManager(this);
- $.when(this.action_manager.do_action(action)).then(function() {
- var viewmanager = self.action_manager.inner_viewmanager,
- controller = viewmanager.views[viewmanager.active_view].controller;
- self.action_manager.appendTo(self.view_edit_dialog.$element);
- self.action_manager.renderElement(self.view_edit_dialog);
- controller.on_loaded.add_last(function(){
- $(controller.groups).bind({
- 'selected': function(e, ids, records) {
- self.main_view_id = ids[0];
- }
- })
- });
- });
- },
- on_create_view: function() {
- var self = this;
- this.create_view_dialog = new instance.web.Dialog(this, {
- title: _.str.sprintf(_t("Create a view (%s)"), self.model),
- buttons: [
- {text: _t("Save"), click: function () {
- var view_values = {};
- var warn = false;
- _.each(self.create_view_widget, function(widget) {
- if (widget.is_invalid) {
- warn = true;
- return false;
- }
- if (widget.dirty && !widget.is_invalid) {
- view_values[widget.name] = widget.get_value();
- }
- });
- if (warn) {
- self.on_valid_create_view(self.create_view_widget);
- } else {
- $.when(self.do_save_view(view_values)).then(function() {
- self.create_view_dialog.close();
- var controller = self.action_manager.inner_viewmanager.views[self.action_manager.inner_viewmanager.active_view].controller;
- controller.reload_content();
- });
- }
- }},
- {text: _t("Cancel"), click: function () { self.create_view_dialog.close(); }}
- ]
- }).open();
- var view_widget = [{'name': 'view_name', 'string':'View Name', 'type': 'char', 'required': true, 'value' : this.model + '.custom_' + Math.round(Math.random() * 1000)},
- {'name': 'view_type', 'string': 'View Type', 'type': 'selection', 'required': true, 'value': 'Form', 'selection': [['',''],['tree', 'Tree'],['form', 'Form'],['graph', 'Graph'],['calendar', 'Calender']]},
- {'name': 'proirity', 'string': 'Priority', 'type': 'float', 'required': true, 'value':'16'}];
- this.create_view_dialog.$element.append('<table id="create_view" style="width:400px" class="oe_form"></table>');
- this.create_view_widget = [];
- _.each(view_widget, function(widget) {
- var type_widget = new (self.property.get_any([widget.type])) (self.create_view_dialog, widget);
- self.create_view_dialog.$element.find('table[id=create_view]').append('<tr><td width="100px" align="right">' + widget.string + ':</td>' + type_widget.render()+'</tr>');
- var value = null;
- if (widget.value) {
- value = widget.value;
- type_widget.dirty = true;
- }
- type_widget.start();
- type_widget.set_value(value);
- self.create_view_widget.push(type_widget);
- });
- },
- do_save_view: function(values) {
- def = $.Deferred();
- var field_dataset = new instance.web.DataSetSearch(this, this.model, null, null);
- var model_dataset = new instance.web.DataSetSearch(this, 'ir.model', null, null);
- var view_string = "", field_name = false, self = this;
- field_dataset.call( 'fields_get', [], function(fields) {
- _.each(['name', 'x_name'], function(value) {
- if (_.include(_.keys(fields), value)) {
- field_name = value;
- return false;
- }
- });
- if (field_name) {
- model_dataset.read_slice(['name','field_id'], {"domain": [['model','=',self.model]]}).then(function(records) {
- if (records) {view_string = records[0].name;}
- var arch = _.str.sprintf("<?xml version='1.0'?>\n<%s string='%s'>\n\t<field name='%s'/>\n</%s>", values.view_type, view_string, field_name, values.view_type);
- var vals = {'model': self.model, 'name': values.view_name, 'priority': values.priority, 'type': values.view_type, 'arch': arch};
- self.dataset.create(vals, function(suc) {
- def.resolve();
- });
- });
- }
- });
- return def.promise();
- },
- on_valid_create_view: function(widgets) {
- var msg = "<ul>";
- _.each(widgets, function(widget) {
- if (widget.is_invalid) {
- msg += "<li>" + widget.name + "</li>";
- }
- });
- msg += "</ul>";
- this.do_warn("The following fields are invalid :", msg);
- },
- add_node_name : function(node) {
- if(node.tagName.toLowerCase() == "button" || node.tagName.toLowerCase() == "field"){
- return (node.getAttribute('name'))?
- _.str.sprintf( "<%s name='%s'>",node.tagName.toLowerCase(), node.getAttribute('name')):
- _.str.sprintf( "<%s>",node.tagName.toLowerCase());
- }else if(node.tagName.toLowerCase() == "group"){
- return (node.getAttribute('string'))?
- _.str.sprintf( "<%s>",node.getAttribute('string')):
- _.str.sprintf( "<%s>",node.tagName.toLowerCase());
- }else{
- return (node.getAttribute('string'))?
- _.str.sprintf( "<%s string='%s'>",node.tagName.toLowerCase(), node.getAttribute('string')):
- _.str.sprintf( "<%s>",node.tagName.toLowerCase());
- }
- },
- do_delete_view: function() {
- var self = this;
- if (confirm(_t("Do you really want to remove this view?"))) {
- var controller = this.action_manager.inner_viewmanager.views[this.action_manager.inner_viewmanager.active_view].controller;
- this.dataset.unlink([this.main_view_id]).then(function() {
- controller.reload_content();
- self.main_view_id = self.parent.fields_view.view_id;
- });
- }
- },
- create_View_Node: function(node){
- ViewNode = {
- 'level': ($(node).parents()).length + 1,
- 'id': this.xml_element_id += 1,
- 'att_list': [],
- 'name': this.add_node_name(node),
- 'child_id': []
- };
- ViewNode.att_list.push(node.tagName.toLowerCase());
- _.each(node.attributes, function(att) {
- ViewNode.att_list.push([att.nodeName, att.nodeValue]);
- });
- return ViewNode;
- },
- append_child_object: function(main_object, parent_id, child_obj_list) {
- var self = this;
- if (main_object.id == parent_id) {
- main_object.child_id = child_obj_list;
- return main_object;
- } else {
- _.each(main_object.child_id, function(child_object) {
- self.append_child_object(child_object, parent_id, child_obj_list);
- });
- }
- },
- convert_arch_to_obj: function(xml_Node, main_object, parent_id) {
- var self = this;
- var child_obj_list = [];
- _.each(xml_Node, function(element) {
- child_obj_list.push(self.create_View_Node(element));
- });
- this.append_child_object(main_object, parent_id, child_obj_list);
- var obj_xml_list = _.zip(xml_Node, child_obj_list);
- _.each(obj_xml_list, function(node) {
- var children = _.filter(node[0].childNodes, function(child) {
- return child.nodeType == 1;
- });
- if (children) {
- self.convert_arch_to_obj(children, main_object, node[1].id);
- }
- });
- return main_object;
- },
- parse_xml: function(arch, view_id) {
- main_object = {
- 'level': 0,
- 'id': this.xml_element_id +=1,
- 'att_list': [],
- 'name': _.str.sprintf("<view view_id = %s>", view_id),
- 'child_id': []
- };
- var xml_arch = QWeb.load_xml(arch);
- return [this.convert_arch_to_obj(xml_arch.childNodes, main_object, this.xml_element_id)];
- },
- get_arch: function() {
- var self = this;
- var view_arch_list = [];
- this.dataset.read_ids([parseInt(self.main_view_id)], ['arch', 'type','priority']).then(function(arch) {
- if (arch.length) {
- var arch_object = self.parse_xml(arch[0].arch, self.main_view_id);
- self.main_view_type = arch[0].type == 'tree'? 'list': arch[0].type;
- view_arch_list.push({"view_id": self.main_view_id, "arch": arch[0].arch,"priority":arch[0].priority});
- self.dataset.read_slice([], {domain: [['inherit_id','=', parseInt(self.main_view_id)]]}).then(function(result) {
- _.each(result, function(res) {
- view_arch_list.push({"view_id": res.id, "arch": res.arch,"priority":res.priority});
- self.inherit_view(arch_object, res);
- });
- return self.edit_view({"main_object": arch_object,
- "parent_child_id": self.parent_child_list(arch_object, []),
- "arch": view_arch_list});
- });
- } else {
- self.do_warn("Please select view in list :");
- }
- });
- },
- parent_child_list : function(one_object, parent_list) {
- var self = this;
- _.each(one_object , function(element) {
- if (element.child_id.length != 0) {
- parent_list.push({"key": element.id, "value": _.pluck(element.child_id, 'id')});
- self.parent_child_list(element.child_id, parent_list);
- }
- });
- return parent_list;
- },
- inherit_view : function(arch_object, result) {
- var self = this, xml_list = [], xml_arch = QWeb.load_xml(result.arch);
- if (xml_arch.childNodes[0].tagName == "data") {
- xml_list = _.filter(xml_arch.childNodes[0].childNodes, function(child) {
- return child.nodeType == 1;
- });
- } else {
- xml_list.push( xml_arch.childNodes[0]);
- }
- _.each(xml_list, function(xml) {
- var expr_to_list = [], xpath_arch_object = self.parse_xml(QWeb.tools.xml_node_to_string(xml), result.id);
- if (xml.tagName == "xpath") {
- var part_expr = _.without(xml.getAttribute('expr').split("/"), "");
- _.each(part_expr, function(part) {
- expr_to_list.push(_.without($.trim(part.replace(/[^a-zA-Z 0-9 _]+/g,'!')).split("!"), ""));
- });
- } else {
- var temp = _.reject(xpath_arch_object[0].child_id[0].att_list, function(list) {
- return list instanceof Array? _.include(list, "position"): false;
- });
- expr_to_list = [_.flatten(temp)];
- }
- self.inherit_apply(expr_to_list, arch_object ,xpath_arch_object);
- });
- },
- inherit_apply: function(expr_list ,arch_object ,xpath_arch_object) {
- var self = this;
- if (xpath_arch_object.length) {
- var check = expr_list[0], obj = false;
- switch (check.length) {
- case 2:
- if (parseInt(check[1])) {
- //for field[3]
- var temp_list = _.select(arch_object, function(element) {
- return _.include(_.flatten(element.att_list), check[0]);
- });
- obj = arch_object[_.indexOf(arch_object, temp_list[parseInt(check[1]) - 1])];
- } else {
- //for notebook[last()]
- obj = _.detect(arch_object, function(element) {
- return _.include(_.flatten(element.att_list), check[0]);
- });
- }
- break;
- case 3:
- //for field[@name='type']
- obj = _.detect(arch_object, function(element){
- if ((_.intersection(_.flatten(element.att_list), _.uniq(check))).length == _.uniq(check).length) {
- return element;
- }
- });
- break;
- case 1:
- //for /form/notebook
- var temp_list = _.select(arch_object, function(element) {
- return _.include(_.flatten(element.att_list), check[0]);
- });
- if (temp_list.length != 0) {
- expr_list.length == 1 ? obj = temp_list[0] : expr_list.shift();
- }
- break;
- }
- if (obj) {
- expr_list.shift();
- if (expr_list.length) {
- self.inherit_apply(expr_list, obj.child_id, xpath_arch_object);
- } else {
- self.increase_level(xpath_arch_object[0], obj.level + 1);
- obj.child_id.push(xpath_arch_object[0]);
- xpath_arch_object.pop();
- }
- } else {
- _.each(arch_object, function(element) {
- self.inherit_apply(expr_list, element.child_id, xpath_arch_object);
- });
- }
- }
- },
- increase_level: function(val, level) {
- var self = this;
- val.level = level;
- _.each(val.child_id, function(val, key) {
- self.increase_level(val, level + 1);
- });
- },
- do_select_row: function(row_id) {
- this.edit_xml_dialog.$element.find("tr[id^='viewedit-']").removeClass('ui-selected');
- this.edit_xml_dialog.$element.find("tr[id=viewedit-" + row_id + "]").addClass('ui-selected');
- },
- do_parent_img_hide_show: function(img) {
- if ($(img).attr('src') == '/web/static/src/img/collapse.gif') {
- $(img).attr('src', '/web/static/src/img/expand.gif');
- this.on_expand(img);
- } else {
- $(img).attr('src', '/web/static/src/img/collapse.gif');
- this.on_collapse(img);
- }
- },
- edit_view: function(one_object) {
- var self = this;
- this.one_object = one_object;
- this.edit_xml_dialog = new instance.web.Dialog(this, {
- title: _.str.sprintf(_t("View Editor %d - %s"), self.main_view_id, self.model),
- height: '90%',
- buttons: [
- {text: _t("Inherited View"), click: function(){
- var selected_row = self.edit_xml_dialog.$element.find('.ui-selected');
- if (selected_row.length) {
- if(selected_row.find('a').text().search("field") != -1){
- if (confirm(_t("Do you really wants to create an inherited view here?"))) {
- self.inherited_view(selected_row);
- }
- }else{
- alert("Can't Update View");
- }
- }else{
- alert("Select an element");
- }
- }},
- {text: _t("Preview"), click: function() {
- var action = {
- context: self.session.user_context,
- res_model: self.model,
- views: [[self.main_view_id, self.main_view_type]],
- type: 'ir.actions.act_window',
- target: "new",
- auto_search: true,
- flags: {
- sidebar: false,
- views_switcher: false,
- action_buttons: false
- }
- };
- var action_manager = new instance.web.ActionManager(self);
- action_manager.do_action(action);
- }},
- {text: _t("Close"), click: function(){
- self.action_manager.inner_viewmanager.views[self.action_manager.inner_viewmanager.active_view].controller.reload_content();
- self.edit_xml_dialog.close();
- }}
- ]
- }).open();
- var no_property_att = [];
- _.each(_PROPERTIES, function(val, key) {
- if (! val.length) no_property_att.push(key);
- });
- this.edit_xml_dialog.$element.html(QWeb.render('view_editor', {'data': one_object['main_object'], 'no_properties': no_property_att}));
- this.edit_xml_dialog.$element.find("tr[id^='viewedit-']").click(function() {
- self.do_select_row(this.id.split('-')[1]);
- });
- this.edit_xml_dialog.$element.find("img[id^='parentimg-']").click(function() {
- self.do_parent_img_hide_show(this);
- });
- this.edit_xml_dialog.$element.find("img[id^='side-']").click(function() {
- self.on_select_img(this);
- });
- },
- inherited_view: function(selected_row){
- var self = this;
- var row_id = parseInt((selected_row.attr('id')).split('-')[1]);
- var obj = self.get_object_by_id(row_id,self.one_object['main_object'], [])[0];
- var view_name = this.model + '.inherit_' + Math.round(Math.random() * 1000);
- var view_find = selected_row;
- var view_id;
- var min_level = parseInt(selected_row.attr('level'));
- while (1) {
- view_find = view_find.prev();
- if (view_find.length == 0 ||
- self.edit_xml_dialog.$element.find(view_find).find('a').text().search("view_id") != -1 &&
- parseInt(view_find.attr('level')) < min_level ) {
- view_id = parseInt($(view_find).find('a').text().replace(/[^0-9]+/g, ''));
- break;
- }
- if (view_find.attr('level') < min_level) {
- min_level = parseInt(view_find.attr('level'));
- }
- }
- var val = _.detect(obj.att_list, function(val) {return val[0] == "name";});
- var priority = _.detect(self.one_object['arch'], function(val) {return val.view_id == view_id;});
- var arch = _.str.sprintf("<?xml version='1.0'?>\n\t <field name='%s' position='after'> </field>", val[1]);
- var vals = {'model': self.model, 'name': view_name, 'priority': priority.priority + 1, 'type': "form", 'arch': arch,'inherit_id':self.main_view_id};
- this.dataset.create(vals, function(suc) {
- var arch_to_obj = self.parse_xml(arch,suc.result);
- obj.child_id.push(arch_to_obj[0]);
- self.one_object['parent_child_id'] = self.parent_child_list(self.one_object['main_object'],[]);
- self.one_object['arch'].push({'view_id':suc.result,"arch":arch,'priority': priority.priority + 1});
- self.increase_level(arch_to_obj[0],obj.level+1);
- self.render_inherited_view(selected_row,arch_to_obj[0]);
- });
- },
- render_inherited_view: function(selected_row,obj){
- var self = this,row_id = parseInt((selected_row.attr('id')).split('-')[1]);
- var clone = this.create_clone(selected_row.clone(),obj);
- if (selected_row.find("img[id^='parentimg-']").length == 0) {
- ($(selected_row.find('a').parent()).siblings('td'))
- .append($('<img width="16" height="16"></img>').attr('src', '/web/static/src/img/collapse.gif').
- attr('id','parentimg-'+ row_id).click(function(){
- self.do_parent_img_hide_show(this);
- }));
- }
- self.edit_xml_dialog.$element.
- find("tr[id='viewedit-"+row_id+"']").after(clone.removeClass('ui-selected'));
- _.each(obj.child_id,function(obj){self.render_inherited_view(clone,obj);});
- },
- on_select_img: function(element_img) {
- var self = this;
- var side = $(element_img).closest("tr[id^='viewedit-']");
- this.one_object.clicked_tr_id = parseInt((side.attr('id')).split('-')[1]);
- this.one_object.clicked_tr_level = parseInt(side.attr('level'));
- var img = side.find("img[id='parentimg-" + this.one_object.clicked_tr_id + "']").attr('src');
- var view_id = 0, view_xml_id = 0, view_find = side;
- //for view id finding
- var min_level = this.one_object.clicked_tr_id;
- if (($(side).find('a').text()).search("view_id") != -1) {
- view_id = parseInt(($(view_find).find('a').text()).replace(/[^0-9]+/g, ''));
- view_xml_id = (view_find.attr('id')).split('-')[1];
- this.one_object.clicked_tr_id += 1;
- this.one_object.clicked_tr_level += 1;
- }else{
- while (1) {
- view_find = view_find.prev();
- if (view_find.length == 0 ||
- (self.edit_xml_dialog.$element.find(view_find).find('a').text()).search("view_id") != -1
- && parseInt(view_find.attr('level')) < min_level ) {
- view_id = parseInt(($(view_find).find('a').text()).replace(/[^0-9]+/g, ''));
- view_xml_id = parseInt((view_find.attr('id')).split('-')[1]);
- break;
- }
- if (view_find.attr('level') < min_level) {
- min_level = parseInt(view_find.attr('level'));
- }
- }
- }
- this.one_object.clicked_tr_view = [view_id, view_xml_id];
- switch (element_img.id) {
- case "side-add":
- self.do_node_add(side);
- break;
- case "side-remove":
- if (confirm(_t("Do you really want to remove this node?"))) {
- self.do_save_update_arch("remove_node");
- }
- break;
- case "side-edit":
- self.do_node_edit(side);
- break;
- case "side-up":
- self.do_node_up(side, img);
- break;
- case "side-down":
- self.do_node_down(side, img);
- break;
- }
- },
- do_node_add: function(side){
- var self = this;
- var tr = self.get_object_by_id(this.one_object.clicked_tr_id, this.one_object['main_object'], [])[0].att_list[0];
- var parent_tr = ($(side).prevAll("tr[level=" + String(this.one_object.clicked_tr_level - 1) + "]"))[0];
- var field_dataset = new instance.web.DataSetSearch(this, this.model, null, null);
- parent_tr = $(parent_tr).find('a').text();
- field_dataset.call( 'fields_get', [], function(result) {
- var fields = _.keys(result);
- fields.push(" "),fields.sort();
- var property_to_check = [];
- _.each([tr, parent_tr],function(element) {
- property_to_check.push(
- _.detect(_.keys(_CHILDREN),function(res){
- return _.str.include(element, res);
- }));
- });
- self.on_add_node(property_to_check, fields);
- });
- },
- do_node_edit: function(side) {
- var self = this;
- var result = self.get_object_by_id(this.one_object.clicked_tr_id, this.one_object['main_object'], []);
- if (result.length && result[0] && result[0].att_list) {
- var properties = _PROPERTIES[result[0].att_list[0]];
- self.on_edit_node(properties);
- }
- },
- do_node_down: function(cur_tr, img) {
- var self = this, next_tr, last_tr, tr_to_move = [];
- tr_to_move.push(cur_tr);
- if (img) {
- while (1) {
- next_tr = cur_tr.next();
- if ( parseInt(next_tr.attr('level')) <= this.one_object.clicked_tr_level || next_tr.length == 0) {
- last_tr = next_tr;
- break;
- } else {
- tr_to_move.push(next_tr);
- cur_tr = next_tr;
- }
- }
- } else {
- last_tr = cur_tr.next();
- }
- if ((self.edit_xml_dialog.$element.find(last_tr).find('a').text()).search("view_id") != -1) {
- return false;
- }
- if (last_tr.length != 0 && parseInt(last_tr.attr('level')) == this.one_object.clicked_tr_level) {
- var last_tr_id = (last_tr.attr('id')).split('-')[1];
- img = last_tr.find("img[id='parentimg-" + last_tr_id + "']").attr('src');
- if (img) {
- self.edit_xml_dialog.$element.find("img[id='parentimg-" + last_tr_id + "']").
- attr('src', '/web/static/src/img/expand.gif');
- while (1) {
- var next_tr = last_tr.next();
- if (next_tr.attr('level') <= this.one_object.clicked_tr_level || next_tr.length == 0) break;
- next_tr.hide();
- last_tr = next_tr;
- }
- }
- tr_to_move.reverse();
- _.each(tr_to_move, function(rec) {
- $(last_tr).after(rec);
- });
- self.do_save_update_arch("down");
- }
- },
- do_node_up: function(cur_tr, img) {
- var self = this, side = cur_tr, tr_to_move = [];
- tr_to_move.push(side);
- while (1) {
- var prev_tr = cur_tr.prev();
- if (this.one_object.clicked_tr_level >= parseInt(prev_tr.attr('level')) || prev_tr.length == 0) {
- last_tr = prev_tr;
- break;
- }
- cur_tr = prev_tr;
- }
- if (img) {
- self.edit_xml_dialog.$element.find("img[id='parentimg-" + this.one_object.clicked_tr_id + "']").
- attr('src', '/web/static/src/img/expand.gif');
- while (1) {
- next_tr = side.next();
- if (parseInt(next_tr.attr('level')) <= this.one_object.clicked_tr_level || next_tr.length == 0) {
- break;
- } else {
- next_tr.hide();
- tr_to_move.push(next_tr);
- side = next_tr;
- }
- }
- }
- if (last_tr.length != 0 && parseInt(last_tr.attr('level')) == this.one_object.clicked_tr_level &&
- (self.edit_xml_dialog.$element.find(last_tr).find('a').text()).search("view_id") == -1) {
- _.each(tr_to_move, function(rec) {
- $(last_tr).before(rec);
- });
- self.do_save_update_arch("up");
- }
- },
- do_save_update_arch: function(move_direct, update_values) {
- var self = this;
- var arch = _.detect(self.one_object['arch'], function(element)
- {return element.view_id == self.one_object.clicked_tr_view[0]});
- var obj = self.get_object_by_id(this.one_object.clicked_tr_view[1],this.one_object['main_object'], []);
- //for finding xpath tag from inherit view
- var xml_arch = QWeb.load_xml(arch.arch);
- if (xml_arch.childNodes[0].tagName == "data") {
- var check_list = _.flatten(obj[0].child_id[0].att_list);
- var children = _.filter(xml_arch.childNodes[0].childNodes, function (child) {
- return child.nodeType == 1;
- });
- arch.arch = _.detect(children, function(xml_child) {
- var temp_obj = self.create_View_Node(xml_child),
- insert = _.intersection(_.flatten(temp_obj.att_list),_.uniq(check_list));
- if (insert.length == _.uniq(check_list).length ) {return xml_child;}
- });
- xml_arch = QWeb.load_xml(arch.arch);
- }
- return self.do_save_xml(xml_arch.documentElement, obj[0].child_id[0],obj[0].child_id, move_direct, update_values,arch);
- },
- get_object_by_id: function(id, one_object, result) {
- var self = this;
- if (result.length == 0 ) {
- var check = _.detect(one_object , function(obj) {
- return id == obj.id;
- });
- if (check) {result.push(check);}
- _.each(one_object, function(obj) {
- self.get_object_by_id(id,obj.child_id, result);
- });
- }
- return result;
- },
- create_clone: function(clone, new_node_obj){
- var self = this;
- clone.find('a').text(new_node_obj.name);
- ($(clone.find('a').parent()).siblings('td')).css( "padding-left", 20 * new_node_obj.level);
- clone.attr("id", "viewedit-" + new_node_obj.id);
- clone.attr("level", new_node_obj.level);
- clone.find("img[id^='parentimg-']").remove();
- clone.bind("click",function(){
- self.do_select_row(this.id.split('-')[1]);
- });
- clone.find("img[id^='side-']").click(function() {
- self.on_select_img(this);
- });
- return clone;
- },
- do_save_xml: function(arch1, obj, child_list, move_direct, update_values, arch){
- var self = this, children_list = $(arch1).children(),list_obj_xml;
- try{list_obj_xml = _.zip(children_list, obj.child_id);}catch(err){return;}
- if (this.one_object.clicked_tr_id) {
- if (obj.id == this.one_object.clicked_tr_id) {
- var parent = false, index = _.indexOf(child_list, obj);
- if (move_direct == "down") {
- var next = $(arch1).next();
- $(next).after(arch1);
- var re_insert_obj = child_list.splice(index, 1);
- child_list.splice(index+1, 0, re_insert_obj[0]);
- parent = $(arch1).parents();
- } else if (move_direct == "up") {
- var prev = $(arch1).prev();
- $(prev).before(arch1);
- var re_insert_obj = child_list.splice(index, 1);
- child_list.splice(index-1, 0, re_insert_obj[0]);
- parent = $(arch1).parents();
- } else if (move_direct == "update_node") {
- _.each(update_values, function(val){
- if (val[1]) $(arch1)[0].setAttribute(val[0], val[1]);
- else $(arch1)[0].removeAttribute(val[0]);
- });
- var new_obj = self.create_View_Node(arch1);
- new_obj.id = obj.id,new_obj.child_id = obj.child_id;
- self.edit_xml_dialog.$element.
- find("tr[id='viewedit-"+this.one_object.clicked_tr_id+"']").
- find('a').text(new_obj.name);
- child_list.splice(index, 1, new_obj);
- parent = $(arch1).parents();
- } else if(move_direct == "add_node") {
- var tr_click = self.edit_xml_dialog.$element.find("tr[id='viewedit-"+self.one_object.clicked_tr_id+"']"),
- temp_xml = QWeb.load_xml(update_values[0]),
- object_xml = self.create_View_Node(temp_xml.childNodes[0]);
- (update_values[1] == "Inside")? object_xml.level = obj.level + 1:object_xml.level = obj.level;
- var clone = self.create_clone(tr_click.clone(),object_xml),
- after_append = _.detect(self.one_object['parent_child_id'],function(ele){
- return self.one_object.clicked_tr_id == ele.key;
- });
- after_append = (after_append)?_.last(after_append.value):self.one_object.clicked_tr_id;
- switch (update_values[1]) {
- case "After":
- self.edit_xml_dialog.$element.
- find("tr[id='viewedit-"+after_append+"']").after(clone);
- $(arch1).after($(update_values[0]));
- child_list.splice(index + 1, 0, object_xml);
- break;
- case "Before":
- tr_click.before(clone);
- $(arch1).before($(update_values[0]));
- child_list.splice(index - 1, 0, object_xml);
- break;
- case "Inside":
- if (tr_click.find("img[id^='parentimg-']").length == 0) {
- ($(tr_click.find('a').parent()).siblings('td'))
- .append($('<img width="16" height="16"></img>').attr('src', '/web/static/src/img/collapse.gif').
- attr('id','parentimg-'+ self.one_object.clicked_tr_id).click(function(){
- self.do_parent_img_hide_show(this);
- }));
- }
- $(arch1).append($(update_values[0]));
- self.edit_xml_dialog.$element.
- find("tr[id='viewedit-"+after_append+"']").after(clone);
- obj.child_id.push(object_xml);
- break;
- }
- self.edit_xml_dialog.$element.
- find("tr[id='viewedit-" + object_xml.id + "']").removeClass('ui-selected');
- parent = $(arch1).parents();
- } else if (move_direct == "remove_node") {
- parent = $(arch1).parents();
- if (parent.length == 0 || (parent[0].tagName.toLowerCase() == "data")) {
- self.one_object.clicked_tr_id = self.one_object.clicked_tr_id -1;
- self.one_object.clicked_tr_level = self.one_object.clicked_tr_level - 1;
- (parent.length == 0)?parent.push("remove_view"):false;
- }
- $(arch1).remove();
- child_list.splice(index,1);
- var cur_tr = self.edit_xml_dialog.$element.
- find("tr[id='viewedit-" + self.one_object.clicked_tr_id + "']");
- _.each(self.get_list_tr(cur_tr,self.one_object.clicked_tr_level), function(tr_element){
- tr_element.remove();
- });
- cur_tr.remove();
- var parent_img = _.detect(self.one_object['parent_child_id'],function(element){
- return _.include(element.value, self.one_object.clicked_tr_id);
- });
- if(parent_img.value.length == 1){
- self.edit_xml_dialog.$element.
- find("tr[id='viewedit-"+parent_img.key+"']").
- find("img[id^='parentimg-']").remove();
- }
- self.one_object['parent_child_id'] = self.parent_child_list(self.one_object['main_object'],[]);
- }
- var convert_to_utf = (parent.length != 0)? parent[parent.length-1]: arch1;
- if (convert_to_utf != "remove_view") {
- convert_to_utf = QWeb.tools.xml_node_to_string(convert_to_utf);
- convert_to_utf = convert_to_utf.replace('xmlns="http://www.w3.org/1999/xhtml"', "");
- convert_to_utf = '<?xml version="1.0"?>' + convert_to_utf;
- arch.arch = convert_to_utf;
- this.dataset.write(this.one_object.clicked_tr_view[0] ,{"arch":convert_to_utf}, function(r) {});
- } else {
- this.dataset.unlink([this.one_object.clicked_tr_view[0]],function(res) {});
- }
- if(move_direct == "add_node"){
- self.add_node_dialog.close();
- self.on_select_img(clone.find("img[id='side-edit']")[0]);
- self.one_object['parent_child_id'] = self.parent_child_list(self.one_object['main_object'],[]);
- }
- }
- if (obj.level <= this.one_object.clicked_tr_level) {
- _.each(list_obj_xml, function(child_node) {
- self.do_save_xml(child_node[0], child_node[1], obj.child_id, move_direct, update_values, arch);
- });
- }
- }
- },
- on_expand: function(expand_img){
- var level = parseInt($(expand_img).closest("tr[id^='viewedit-']").attr('level'));
- var cur_tr = $(expand_img).closest("tr[id^='viewedit-']");
- _.each(this.get_list_tr(cur_tr,level), function(tr_element){
- tr_element.hide();
- });
- },
- get_list_tr: function(cur_tr,level){
- tr_list = [];
- while (1) {
- var nxt_tr = cur_tr.next();
- if (parseInt(nxt_tr.attr('level')) > level) {
- cur_tr = nxt_tr;
- tr_list.push(nxt_tr);
- } else return tr_list;
- }
- },
- on_collapse: function(collapse_img) {
- var self = this, id = collapse_img.id.split('-')[1];
- var datas = _.detect(self.one_object['parent_child_id'] , function(res) {
- return res.key == id;
- });
- _.each(datas.value, function (rec) {
- var tr = self.edit_xml_dialog.$element.find("tr[id='viewedit-" + rec + "']");
- tr.find("img[id='parentimg-" + rec + "']").attr('src', '/web/static/src/img/expand.gif');
- tr.show();
- });
- },
- on_edit_node: function(properties){
- var self = this;
- this.edit_node_dialog = new instance.web.Dialog(this,{
- title: _t("Properties"),
- width: 450,
- buttons: [
- {text: _t("Update"), click: function () {
- var warn = false, update_values = [];
- _.each(self.edit_widget, function(widget) {
- if (widget.is_invalid) {
- warn = true;
- return false;
- }
- if (widget.dirty && !widget.is_invalid) {
- update_values.push([widget.name, widget.get_value()]);
- }
- });
- if (warn) {
- self.on_valid_create_view(self.edit_widget);
- } else {
- self.do_save_update_arch("update_node", update_values);
- self.edit_node_dialog.close();
- }
- }},
- {text: _t("Cancel"), click: function () { self.edit_node_dialog.close(); }}
- ]
- }).open();
- var _PROPERTIES_ATTRIBUTES = {
- 'name' : {'name':'name', 'string': 'Name', 'type': 'char'},
- 'string' : {'name':'string', 'string': 'String', 'type': 'char'},
- 'required' : {'name':'required', 'string': 'Required', 'type': 'boolean'},
- 'readonly' : {'name':'readonly', 'string': 'Readonly', 'type': 'boolean'},
- 'invisible' : {'name':'invisible', 'string': 'Invisible', 'type': 'boolean'},
- 'domain' : {'name':'domain', 'string': 'Domain', 'type': 'char'},
- 'context' : {'name':'context', 'string': 'Context', 'type': 'char'},
- 'limit' : {'name':'limit', 'string': 'Limit', 'type': 'float'},
- 'min_rows' : {'name':'min_rows', 'string': 'Minimum rows', 'type': 'float'},
- 'date_start' : {'name':'date_start', 'string': 'Start date', 'type': 'char'},
- 'date_delay' : {'name':'date_delay', 'string': 'Delay date', 'type': 'char'},
- 'day_length' : {'name':'day_length', 'string': 'Day length', 'type': 'char'},
- 'mode' : {'name':'mode', 'string': 'Mode', 'type': 'char'},
- 'align' : {'name':'align', 'string': 'Alignment ', 'type': 'selection', 'selection': [['', ''], ['0.0', 'Left'], ['0.5', 'Center'], ['1.0', 'Right']]},
- 'icon' : {'name':'icon', 'string': 'Icon', 'type': 'selection', 'selection': _ICONS},
- 'type' : {'name':'type', 'string': 'Type', 'type': 'selection', 'selection': [['', ''], ['action', 'Action'], ['object', 'Object'], ['workflow', 'Workflow'], ['server_action', 'Server Action']]},
- 'special' : {'name':'special', 'string': 'Special', 'type': 'selection', 'selection': [['',''],['save', 'Save Button'], ['cancel', 'Cancel Button'], ['open', 'Open Button']]},
- 'target' : {'name':'target', 'string': 'Target', 'type': 'selection', 'selection': [['', ''], ['new', 'New Window']]},
- 'confirm' : {'name':'confirm', 'string': 'Confirm', 'type': 'char'},
- 'style' : {'name':'style', 'string': 'Style', 'type': 'selection', 'selection':[["",""],["1", "1"],["1-1", "1-1"],["1-2", "1-2"],["2-1", "2-1"],["1-1-1", "1-1-1"]]},
- 'filename' : {'name':'filename', 'string': 'File Name', 'type': 'char'},
- 'width' : {'name':'width', 'string': 'Width', 'type': 'float'},
- 'height' : {'name':'height', 'string': 'Height', 'type': 'float'},
- 'attrs' : {'name':'attrs', 'string': 'Attrs', 'type': 'char'},
- 'col' : {'name':'col', 'string': 'col', 'type': 'float'},
- 'link' : {'name':'link', 'string': 'Link', 'type': 'char'},
- 'position' : {'name':'position', 'string': 'Position', 'type': 'selection', 'selection': [['',''],['after', 'After'],['before', 'Before'],['inside', 'Inside'],['replace', 'Replace']]},
- 'states' : {'name':'states', 'string': 'states', 'type': 'char'},
- 'eval' : {'name':'eval', 'string': 'Eval', 'type': 'char'},
- 'ref' : {'name':'ref', 'string': 'Ref', 'type': 'char'},
- 'on_change' : {'name':'on_change', 'string': 'On change', 'type': 'char'},
- 'nolabel' : {'name':'nolabel', 'string': 'No label', 'type': 'boolean'},
- 'completion' : {'name':'completion', 'string': 'Completion', 'type': 'boolean'},
- 'colspan' : {'name':'colspan', 'string': 'Colspan', 'type': 'float'},
- 'widget' : {'name':'widget', 'string': 'widget', 'type': 'selection'},
- 'colors' : {'name':'colors', 'string': 'Colors', 'type': 'char'},
- 'editable' : {'name':'editable', 'string': 'Editable', 'type': 'selection', 'selection': [["",""],["top","Top"],["bottom", "Bottom"]]},
- 'groups' : {'name':'groups', 'string': 'Groups', 'type': 'selection_multi'},
- 'fonts' : {'name':'fonts', 'string': 'fonts', 'type': 'char'},
- };
- var arch_val = self.get_object_by_id(this.one_object.clicked_tr_id,this.one_object['main_object'], []);
- this.edit_node_dialog.$element.append('<table id="rec_table" style="width:400px" class="oe_form"></table>');
- this.edit_widget = [];
- self.ready = $.when(self.on_groups(properties)).then(function () {
- _PROPERTIES_ATTRIBUTES['groups']['selection'] = self.groups;
- var values = _.keys( instance.web.form.widgets.map);
- values.push('');
- values.sort();
- _PROPERTIES_ATTRIBUTES['widget']['selection'] = values;
- var widgets = _.filter(_PROPERTIES_ATTRIBUTES, function (property) { return _.include(properties, property.name)});
- _.each(widgets, function(widget) {
- var type_widget = new (self.property.get_any([widget.type])) (self.edit_node_dialog, widget);
- var value = _.detect(arch_val[0]['att_list'],function(res) {
- return res instanceof Array? _.include(res, widget.name): false;
- });
-
- value = value instanceof Array ? value[1] : value;
- self.edit_node_dialog.$element.find('table[id=rec_table]').append('<tr><td align="right">' + widget.string + ':</td>' + type_widget.render() + '</tr>');
- type_widget.start();
- type_widget.set_value(value);
- self.edit_widget.push(type_widget);
- });
- });
- },
- //for getting groups
- on_groups: function(properties){
- var self = this,
- def = $.Deferred();
- if (!_.include(properties, 'groups')) {
- self.groups = false;
- def.resolve();
- }
- var group_ids = [], group_names = {}, groups = [];
- var res_groups = new instance.web.DataSetSearch(this,'res.groups', null, null),
- model_data = new instance.web.DataSetSearch(self,'ir.model.data', null, null);
- res_groups.read_slice([], {}).done(function (res_grp) {
- _.each(res_grp, function (res) {
- var key = res.id;
- group_names[key]=res.full_name;
- group_ids.push(res.id);
- });
- model_data.read_slice([], {domain: [
- ['res_id', 'in', group_ids],
- ['model', '=', 'res.groups']
- ]}).done(function (model_grp) {
- _.each(model_grp, function (res_group) {
- groups.push([res_group.module + "." + res_group.name, group_names[res_group.res_id]]);
- });
- self.groups = groups;
- def.resolve();
- });
- });
- return def.promise();
- },
- on_add_node: function(properties, fields){
- var self = this;
- var render_list = [{'name': 'node_type','selection': _.keys(_CHILDREN).sort(), 'value': 'field', 'string': 'Node Type','type': 'selection'},
- {'name': 'field_value','selection': fields, 'value': false, 'string': '','type': 'selection'},
- {'name': 'position','selection': ['After','Before','Inside'], 'value': false, 'string': 'Position','type': 'selection'}];
- this.add_widget = [];
- this.add_node_dialog = new instance.web.Dialog(this,{
- title: _t("Properties"),
- width: 450,
- buttons: [
- {text: _t("Update"), click: function() {
- var check_add_node = true, values = {};
- _.each(self.add_widget, function(widget) {
- values[widget.name] = widget.get_value() || false;
- });
- (values.position == "Inside")?
- check_add_node =(_.include(_CHILDREN[properties[0]],values.node_type))?true:false:
- check_add_node =(_.include(_CHILDREN[properties[1]],values.node_type))?true:false;
- if(values.node_type == "field" && check_add_node )
- {check_add_node = (values.field_value != " ")?true:false;
- }
- if(check_add_node){
- var tag = (values.node_type == "field")?
- _.str.sprintf("<%s name='%s'> </%s>",values.node_type,values.field_value,values.node_type):
- _.str.sprintf("<%s> </%s>",values.node_type,values.node_type);
- self.do_save_update_arch("add_node", [tag, values.position]);
- } else {
- alert("Can't Update View");
- }
- }},
- {text: _t("Cancel"), click: function() { self.add_node_dialog.close(); }}
- ]
- }).open();
- this.add_node_dialog.$element.append('<table id="rec_table" style="width:420px" class="oe_form"><tbody><tr></tbody></table>');
- var table_selector = self.add_node_dialog.$element.find('table[id=rec_table] tbody');
- _.each(render_list, function(node) {
- type_widget = new (self.property.get_any([node.type])) (self.add_node_dialog, node);
- if (node.name == "position") {
- table_selector.append('</tr><tr><td align="right" width="100px">' + node.string + '</td>' + type_widget.render() + '</tr>');
- } else {
- table_selector.append('<td align="right">' + node.string + '</td>' + type_widget.render() );
- if (node.name == "field_value") {
- table_selector.append('<td id="new_field" align="right" width="100px"> <button>New Field</button></td>');
- }
- }
- type_widget.start();
- type_widget.set_value(node.value);
- self.add_widget.push(type_widget);
- });
- table_selector.find("td[id^=]").attr("width","100px");
- self.add_node_dialog.$element.find('#new_field').click(function() {
- model_data = new instance.web.DataSetSearch(self,'ir.model', null, null);
- model_data.read_slice([], {domain: [['model','=', self.model]]}).then(function(result) {
- self.render_new_field(result[0]);
- });
- });
- },
- render_new_field :function( result ) {
- var self = this;
- var action = {
- context: {'default_model_id': result.id, 'manual': true, 'module' : result.modules},
- res_model: "ir.model.fields",
- views: [[false, 'form']],
- type: 'ir.actions.act_window',
- target: "new",
- flags: {
- action_buttons: true
- }
- };
- var action_manager = new instance.web.ActionManager(self);
- $.when(action_manager.do_action(action)).then(function() {
- var controller = action_manager.dialog_viewmanager.views['form'].controller;
- controller.on_button_cancel.add_last(function(){
- action_manager.destroy()
- });
- controller.do_save.add_last(function(){
- action_manager.destroy();
- var value =controller.fields.name.get('value');
- self.add_node_dialog.$element.find('select[id=field_value]').append($("<option selected></option>").attr("value",value).text(value));
- _.detect(self.add_widget,function(widget){
- widget.name == "field_value"? widget.selection.push(value): false;
- });
- });
- });
- }
-});
-instance.web.ViewEditor.Field = instance.web.Class.extend({
- init: function(view, widget) {
- this.$element = view.$element;
- this.dirty = false;
- this.name = widget.name;
- this.selection = widget.selection || [];
- this.required = widget.required || false;
- this.string = widget.string || "";
- this.type = widget.type;
- this.is_invalid = false;
- },
- start: function () {
- this.update_dom();
- },
- update_dom: function() {
- this.$element.find("td[id=" + this.name + "]").toggleClass('invalid', this.is_invalid);
- this.$element.find("td[id=" + this.name + "]").toggleClass('required', this.required);
- },
- on_ui_change: function() {
- this.validate();
- this.dirty = true;
- this.update_dom();
- },
- validate: function() {
- this.is_invalid = false;
- try {
- var value = instance.web.parse_value(this.get_value(), this, '');
- this.is_invalid = this.required && value === '';
- } catch(e) {
- this.is_invalid = true;
- }
- },
- render: function() {
- return _.str.sprintf("<td id = %s>%s</td>", this.name, QWeb.render(this.template, {widget: this}))
- }
-});
-instance.web.ViewEditor.FieldBoolean = instance.web.ViewEditor.Field.extend({
- template : "vieweditor_boolean",
- start: function() {
- var self = this;
- this._super();
- this.$element.find("input[id="+ self.name+"]").change(function() {
- self.on_ui_change();
- });
- },
- set_value: function(value) {
- if (value) {
- this.$element.find("input[id=" + this.name+ "]").attr('checked', true);
- }
- },
- get_value: function() {
- return this.$element.find("input[id=" + this.name + "]").is(':checked')? "1" : null;
- }
-});
-instance.web.ViewEditor.FieldChar = instance.web.ViewEditor.Field.extend({
- template : "vieweditor_char",
- start: function () {
- var self = this;
- this._super();
- this.$element.find("input[id="+ this.name+"]").css('width','100%').change(function() {
- self.on_ui_change();
- });
- },
- set_value: function(value) {
- this.$element.find("input[id=" + this.name + "]").val(value);
- },
- get_value: function() {
- return this.$element.find("input[id=" + this.name + "]").val();
- }
-});
-instance.web.ViewEditor.FieldSelect = instance.web.ViewEditor.Field.extend({
- template : "vieweditor_selection",
- start: function () {
- var self = this;
- this._super();
- this.$element.find("select[id=" + this.name + "]").css('width', '100%').change(function() {
- self.on_ui_change();
- if (self.name == "node_type") {
- if (self.get_value() == "field") {
- self.$element.find('#new_field').show();
- self.$element.find("select[id=field_value]").show();
- } else {
- self.$element.find('#new_field').hide();
- self.$element.find("select[id=field_value]").hide();
- }
- }
- });
- },
- set_value: function(value) {
- var index = 0;
- value = value === null? false: value;
- for (var i = 0, ii = this.selection.length; i < ii; i++) {
- if ((this.selection[i] instanceof Array && this.selection[i][0] === value) || this.selection[i] === value) index = i;
- }
- this.$element.find("select[id=" + this.name + "]")[0].selectedIndex = index;
- },
- get_value: function() {
- return this.$element.find("select[id=" + this.name + "]").val();
- }
-});
-instance.web.ViewEditor.FieldSelectMulti = instance.web.ViewEditor.FieldSelect.extend({
- start: function () {
- this._super();
- this.$element.find("select[id=" + this.name + "]").css('height', '100px').attr("multiple", true);
- },
- set_value: function(value) {
- var self = this;
- self.$element.find("#groups option").attr("selected",false);
- if (!value) return false;
- _.each(this.selection, function(item) {
- if (_.include(value.split(','), item[0])) {
- self.$element.find("select[id="+self.name+"] option[value='" + item[0] +"']").attr("selected",1)
- }
- });
- }
-});
-instance.web.ViewEditor.FieldFloat = instance.web.ViewEditor.FieldChar.extend({
-});
-
-var _PROPERTIES = {
- 'field' : ['name', 'string', 'required', 'readonly','invisible', 'domain', 'context', 'nolabel', 'completion',
- 'colspan', 'widget', 'eval', 'ref', 'on_change', 'attrs', 'groups'],
- 'form' : ['string', 'col', 'link'],
- 'notebook' : ['colspan', 'position', 'groups'],
- 'page' : ['string', 'states', 'attrs', 'groups'],
- 'group' : ['string', 'col', 'colspan','invisible', 'states', 'attrs', 'groups'],
- 'image' : ['filename', 'width', 'height', 'groups'],
- 'separator' : ['string', 'colspan', 'groups'],
- 'label': ['string', 'align', 'colspan', 'groups'],
- 'button': ['name', 'string', 'icon', 'type', 'states', 'readonly', 'special', 'target', 'confirm', 'context', 'attrs', 'colspan', 'groups'],
- 'newline' : [],
- 'board': ['style'],
- 'column' : [],
- 'action' : ['name', 'string', 'colspan', 'groups'],
- 'tree' : ['string', 'colors', 'editable', 'link', 'limit', 'min_rows', 'fonts'],
- 'graph' : ['string', 'type'],
- 'calendar' : ['string', 'date_start', 'date_stop', 'date_delay', 'day_length', 'color', 'mode']
-};
-var _CHILDREN = {
- 'form': ['notebook', 'group', 'field', 'label', 'button','board', 'newline', 'separator'],
- 'tree': ['field'],
- 'graph': ['field'],
- 'calendar': ['field'],
- 'notebook': ['page'],
- 'page': ['notebook', 'group', 'field', 'label', 'button', 'newline', 'separator'],
- 'group': ['field', 'label', 'button', 'separator', 'newline','group'],
- 'board': ['column'],
- 'action': [],
- 'field': ['form', 'tree', 'graph','field'],
- 'label': [],
- 'button' : [],
- 'newline': [],
- 'separator': []
-};
-var _ICONS = ['','STOCK_ABOUT', 'STOCK_ADD', 'STOCK_APPLY', 'STOCK_BOLD',
- 'STOCK_CANCEL', 'STOCK_CDROM', 'STOCK_CLEAR', 'STOCK_CLOSE', 'STOCK_COLOR_PICKER',
- 'STOCK_CONNECT', 'STOCK_CONVERT', 'STOCK_COPY', 'STOCK_CUT', 'STOCK_DELETE',
- 'STOCK_DIALOG_AUTHENTICATION', 'STOCK_DIALOG_ERROR', 'STOCK_DIALOG_INFO',
- 'STOCK_DIALOG_QUESTION', 'STOCK_DIALOG_WARNING', 'STOCK_DIRECTORY', 'STOCK_DISCONNECT',
- 'STOCK_DND', 'STOCK_DND_MULTIPLE', 'STOCK_EDIT', 'STOCK_EXECUTE', 'STOCK_FILE',
- 'STOCK_FIND', 'STOCK_FIND_AND_REPLACE', 'STOCK_FLOPPY', 'STOCK_GOTO_BOTTOM',
- 'STOCK_GOTO_FIRST', 'STOCK_GOTO_LAST', 'STOCK_GOTO_TOP', 'STOCK_GO_BACK',
- 'STOCK_GO_DOWN', 'STOCK_GO_FORWARD', 'STOCK_GO_UP', 'STOCK_HARDDISK',
- 'STOCK_HELP', 'STOCK_HOME', 'STOCK_INDENT', 'STOCK_INDEX', 'STOCK_ITALIC',
- 'STOCK_JUMP_TO', 'STOCK_JUSTIFY_CENTER', 'STOCK_JUSTIFY_FILL',
- 'STOCK_JUSTIFY_LEFT', 'STOCK_JUSTIFY_RIGHT', 'STOCK_MEDIA_FORWARD',
- 'STOCK_MEDIA_NEXT', 'STOCK_MEDIA_PAUSE', 'STOCK_MEDIA_PLAY',
- 'STOCK_MEDIA_PREVIOUS', 'STOCK_MEDIA_RECORD', 'STOCK_MEDIA_REWIND',
- 'STOCK_MEDIA_STOP', 'STOCK_MISSING_IMAGE', 'STOCK_NETWORK', 'STOCK_NEW',
- 'STOCK_NO', 'STOCK_OK', 'STOCK_OPEN', 'STOCK_PASTE', 'STOCK_PREFERENCES',
- 'STOCK_PRINT', 'STOCK_PRINT_PREVIEW', 'STOCK_PROPERTIES', 'STOCK_QUIT',
- 'STOCK_REDO', 'STOCK_REFRESH', 'STOCK_REMOVE', 'STOCK_REVERT_TO_SAVED',
- 'STOCK_SAVE', 'STOCK_SAVE_AS', 'STOCK_SELECT_COLOR', 'STOCK_SELECT_FONT',
- 'STOCK_SORT_ASCENDING', 'STOCK_SORT_DESCENDING', 'STOCK_SPELL_CHECK',
- 'STOCK_STOP', 'STOCK_STRIKETHROUGH', 'STOCK_UNDELETE', 'STOCK_UNDERLINE',
- 'STOCK_UNDO', 'STOCK_UNINDENT', 'STOCK_YES', 'STOCK_ZOOM_100',
- 'STOCK_ZOOM_FIT', 'STOCK_ZOOM_IN', 'STOCK_ZOOM_OUT',
- 'terp-account', 'terp-crm', 'terp-mrp', 'terp-product', 'terp-purchase',
- 'terp-sale', 'terp-tools', 'terp-administration', 'terp-hr', 'terp-partner',
- 'terp-project', 'terp-report', 'terp-stock', 'terp-calendar', 'terp-graph'
-];
-instance.web.ViewEditor.property_widget = new instance.web.Registry({
- 'boolean' : 'instance.web.ViewEditor.FieldBoolean',
- 'selection_multi' : 'instance.web.ViewEditor.FieldSelectMulti',
- 'selection' : 'instance.web.ViewEditor.FieldSelect',
- 'char' : 'instance.web.ViewEditor.FieldChar',
- 'float' : 'instance.web.ViewEditor.FieldFloat'
-});
-};
diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js
index 64381a66218..69e8db19fd3 100644
--- a/addons/web/static/src/js/view_form.js
+++ b/addons/web/static/src/js/view_form.js
@@ -9,13 +9,22 @@ instance.web.form = {};
/**
* Interface implemented by the form view or any other object
* able to provide the features necessary for the fields to work.
- *
+ *
* Properties:
* - display_invalid_fields : if true, all fields where is_valid() return true should
* be displayed as invalid.
+ * - actual_mode : the current mode of the field manager. Can be "view", "edit" or "create".
* Events:
* - view_content_has_changed : when the values of the fields have changed. When
* this event is triggered all fields should reprocess their modifiers.
+ * - field_changed:<field_name> : when the value of a field change, an event is triggered
+ * named "field_changed:<field_name>" with <field_name> replaced by the name of the field.
+ * This event is not related to the on_change mechanism of OpenERP and is always called
+ * when the value of a field is setted or changed. This event is only triggered when the
+ * value of the field is syntactically valid, but it can be triggered when the value
+ * is sematically invalid (ie, when a required field is false). It is possible that an event
+ * about a precise field is never triggered even if that field exists in the view, in that
+ * case the value of the field is assumed to be false.
*/
instance.web.form.FieldManagerMixin = {
/**
@@ -23,17 +32,27 @@ instance.web.form.FieldManagerMixin = {
*/
get_field: function(field_name) {},
/**
- * Called by the field when the translate button is clicked.
+ * Returns the current value of a field present in the view. See the get_value() method
+ * method in FieldInterface for further information.
*/
- open_translate_dialog: function(field) {},
+ get_field_value: function(field_name) {},
/**
- * Returns true when the view is in create mode.
- */
- is_create_mode: function() {},
+ Gives new values for the fields contained in the view. The new values could not be setted
+ right after the call to this method. Setting new values can trigger on_changes.
+
+ @param (dict) values A dictonnary with key = field name and value = new value.
+ @return (Deferred) Is resolved after all the values are setted.
+ */
+ set_values: function(values) {},
};
instance.web.views.add('form', 'instance.web.FormView');
-instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form.FieldManagerMixin, {
+/**
+ * Properties:
+ * - actual_mode: always "view", "edit" or "create". Read-only property. Determines
+ * the mode used by the view.
+ */
+instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerMixin, {
/**
* Indicates that this view is not searchable, and thus that no search
* view should be displayed (if there is one active).
@@ -55,6 +74,7 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form.
* @property {instance.web.Registry} registry=instance.web.form.widgets widgets registry for this form view instance
*/
init: function(parent, dataset, view_id, options) {
+ var self = this;
this._super(parent);
this.set_default_options(options);
this.dataset = dataset;
@@ -68,23 +88,43 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form.
this.default_focus_button = null;
this.fields_registry = instance.web.form.widgets;
this.tags_registry = instance.web.form.tags;
+ this.widgets_registry = instance.web.form.custom_widgets;
this.has_been_loaded = $.Deferred();
this.translatable_fields = [];
_.defaults(this.options, {
"not_interactible_on_create": false,
"initial_mode": "view",
+ "disable_autofocus": false,
});
this.is_initialized = $.Deferred();
this.mutating_mutex = new $.Mutex();
this.on_change_mutex = new $.Mutex();
this.reload_mutex = new $.Mutex();
- this.rendering_engine = new instance.web.form.FormRenderingEngineReadonly(this);
+ this.__clicked_inside = false;
+ this.__blur_timeout = null;
+ this.rendering_engine = new instance.web.form.FormRenderingEngine(this);
this.qweb = null; // A QWeb instance will be created if the view is a QWeb template
+ self.set({actual_mode: self.options.initial_mode});
+ this.has_been_loaded.then(function() {
+ self.on("change:actual_mode", self, self.check_actual_mode);
+ self.check_actual_mode();
+ self.on("change:actual_mode", self, self.init_pager);
+ self.init_pager();
+ });
+ instance.web.bus.on('clear_uncommitted_changes', this, function(e) {
+ if (!this.can_be_discarded()) {
+ e.preventDefault();
+ }
+ });
},
destroy: function() {
_.each(this.get_widgets(), function(w) {
+ w.off('focused blurred');
w.destroy();
});
+ if (this.$el) {
+ this.$el.off('.formBlur');
+ }
this._super();
},
on_loaded: function(data) {
@@ -100,51 +140,62 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form.
this.rendering_engine.set_fields_registry(this.fields_registry);
this.rendering_engine.set_tags_registry(this.tags_registry);
+ this.rendering_engine.set_widgets_registry(this.widgets_registry);
if (!this.extract_qweb_template(data)) {
this.rendering_engine.set_fields_view(data);
- var $dest = this.$element.hasClass("oe_form_container") ? this.$element : this.$element.find('.oe_form_container');
+ var $dest = this.$el.hasClass("oe_form_container") ? this.$el : this.$el.find('.oe_form_container');
this.rendering_engine.render_to($dest);
}
+ this.$el.on('mousedown.formBlur', function () {
+ self.__clicked_inside = true;
+ });
this.$buttons = $(QWeb.render("FormView.buttons", {'widget':self}));
if (this.options.$buttons) {
this.$buttons.appendTo(this.options.$buttons);
} else {
- this.$element.find('.oe_form_buttons').replaceWith(this.$buttons);
+ this.$el.find('.oe_form_buttons').replaceWith(this.$buttons);
}
this.$buttons.on('click','.oe_form_button_create',this.on_button_create);
this.$buttons.on('click','.oe_form_button_edit',this.on_button_edit);
this.$buttons.on('click','.oe_form_button_save',this.on_button_save);
this.$buttons.on('click','.oe_form_button_cancel',this.on_button_cancel);
- this.$pager = $(QWeb.render("FormView.pager", {'widget':self}));
- if (this.options.$pager) {
- this.$pager.appendTo(this.options.$pager);
- } else {
- this.$element.find('.oe_form_pager').replaceWith(this.$pager);
- }
- this.$pager.on('click','a[data-pager-action]',function() {
- var action = $(this).data('pager-action');
- self.on_pager_action(action);
- });
-
- this.$sidebar = this.options.$sidebar || this.$element.find('.oe_form_sidebar');
+ this.$sidebar = this.options.$sidebar || this.$el.find('.oe_form_sidebar');
if (!this.sidebar && this.options.$sidebar) {
this.sidebar = new instance.web.Sidebar(this);
this.sidebar.appendTo(this.$sidebar);
- if(this.fields_view.toolbar) {
+ if (this.fields_view.toolbar) {
this.sidebar.add_toolbar(this.fields_view.toolbar);
}
- this.sidebar.add_items('other', [
- { label: _t('Delete'), callback: self.on_button_delete },
- { label: _t('Duplicate'), callback: self.on_button_duplicate },
- { label: _t('Set Default'), callback: function (item) { self.open_defaults_dialog(); } },
- ]);
- }
- this.on("change:mode", this, this.switch_mode);
- this.set({mode: this.options.initial_mode});
+ this.sidebar.add_items('other', _.compact([
+ self.is_action_enabled('delete') && { label: _t('Delete'), callback: self.on_button_delete },
+ self.is_action_enabled('create') && { label: _t('Duplicate'), callback: self.on_button_duplicate },
+ { label: _t('Set Default'), callback: function (item) { self.open_defaults_dialog(); } }
+ ]));
+ }
+
this.has_been_loaded.resolve();
+
+ // Add bounce effect on button 'Edit' when click on readonly page view.
+ this.$el.find(".oe_form_group_row,.oe_form_field,label").on('click', function (e) {
+ if(self.get("actual_mode") == "view") {
+ var $button = self.options.$buttons.find(".oe_form_button_edit");
+ $button.effect('bounce', {distance: 18, times: 5}, 150);
+ e.stopPropagation();
+ instance.web.bus.trigger('click', e);
+ }
+ });
+ //bounce effect on red button when click on statusbar.
+ this.$el.find(".oe_form_field_status:not(.oe_form_status_clickable)").on('click', function (e) {
+ if((self.get("actual_mode") == "view")) {
+ var $button = self.$el.find(".oe_highlight:not(.oe_form_invisible)").css({'float':'left','clear':'none'});
+ $button.effect('bounce', {distance:18, times: 5}, 150);
+ e.stopPropagation();
+ }
+ });
+
return $.when();
},
extract_qweb_template: function(fvg) {
@@ -195,8 +246,32 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form.
this.default_focus_field = null;
this.default_focus_button = null;
this.translatable_fields = [];
- this.$element.find('.oe_form_container').empty();
+ this.$el.find('.oe_form_container').empty();
},
+
+ widgetFocused: function() {
+ // Clear click flag if used to focus a widget
+ this.__clicked_inside = false;
+ if (this.__blur_timeout) {
+ clearTimeout(this.__blur_timeout);
+ this.__blur_timeout = null;
+ }
+ },
+ widgetBlurred: function() {
+ if (this.__clicked_inside) {
+ // clicked in an other section of the form (than the currently
+ // focused widget) => just ignore the blurring entirely?
+ this.__clicked_inside = false;
+ return;
+ }
+ var self = this;
+ // clear timeout, if any
+ this.widgetFocused();
+ this.__blur_timeout = setTimeout(function () {
+ self.trigger('blurred');
+ }, 0);
+ },
+
do_load_state: function(state, warm) {
if (state.id && this.datarecord.id != state.id) {
if (!this.dataset.get_id_index(state.id)) {
@@ -208,43 +283,53 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form.
}
}
},
+ /**
+ *
+ * @param {Object} [options]
+ * @param {Boolean} [mode=undefined] If specified, switch the form to specified mode. Can be "edit" or "view".
+ * @param {Boolean} [reload=true] whether the form should reload its content on show, or use the currently loaded record
+ * @return {$.Deferred}
+ */
do_show: function (options) {
var self = this;
options = options || {};
if (this.sidebar) {
- this.sidebar.$element.show();
+ this.sidebar.$el.show();
}
if (this.$buttons) {
this.$buttons.show();
- this.$buttons.find('.oe_form_button_save').removeClass('oe_form_button_save_dirty');
}
- if (this.$pager) {
- this.$pager.show();
- }
- this.$element.show().css('visibility', 'hidden');
- this.$element.removeClass('oe_form_dirty');
- return this.has_been_loaded.pipe(function() {
- var result;
- if (self.dataset.index === null) {
- // null index means we should start a new record
- result = self.on_button_new();
- } else {
- result = self.dataset.read_index(_.keys(self.fields_view.fields), {
- context : { 'bin_size' : true }
- }).pipe(self.on_record_loaded);
- }
- result.pipe(function() {
- if (options.editable) {
- self.set({mode: "edit"});
+ this.$el.show().css({
+ opacity: '0',
+ filter: 'alpha(opacity = 0)'
+ });
+ this.$el.add(this.$buttons).removeClass('oe_form_dirty');
+
+ var shown = this.has_been_loaded;
+ if (options.reload !== false) {
+ shown = shown.pipe(function() {
+ if (self.dataset.index === null) {
+ // null index means we should start a new record
+ return self.on_button_new();
}
- self.$element.css('visibility', 'visible');
+ var fields = _.keys(self.fields_view.fields);
+ fields.push('display_name');
+ return self.dataset.read_index(fields, {
+ context: { 'bin_size': true, 'future_display_name' : true }
+ }).pipe(self.on_record_loaded);
+ });
+ }
+ return shown.pipe(function() {
+ self._actualize_mode(options.mode || self.options.initial_mode);
+ self.$el.css({
+ opacity: '1',
+ filter: 'alpha(opacity = 100)'
});
- return result;
});
},
do_hide: function () {
if (this.sidebar) {
- this.sidebar.$element.hide();
+ this.sidebar.$el.hide();
}
if (this.$buttons) {
this.$buttons.hide();
@@ -257,21 +342,26 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form.
on_record_loaded: function(record) {
var self = this, set_values = [];
if (!record) {
+ this.set({ 'title' : undefined });
this.do_warn("Form", "The record could not be found in the database.", true);
return $.Deferred().reject();
}
this.datarecord = record;
+ this._actualize_mode();
+ this.set({ 'title' : record.id ? record.display_name : "New" });
if (this.qweb) {
this.kill_current_form();
this.rendering_engine.set_fields_view(this.get_fvg_from_qweb(record));
- var $dest = this.$element.hasClass("oe_form_container") ? this.$element : this.$element.find('.oe_form_container');
+ var $dest = this.$el.hasClass("oe_form_container") ? this.$el : this.$el.find('.oe_form_container');
this.rendering_engine.render_to($dest);
}
_(this.fields).each(function (field, f) {
field._dirty_flag = false;
+ field._inhibit_on_change_flag = true;
var result = field.set_value(self.datarecord[f] || false);
+ field._inhibit_on_change_flag = false;
set_values.push(result);
});
return $.when.apply(null, set_values).pipe(function() {
@@ -292,22 +382,34 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form.
if (self.sidebar) {
self.sidebar.do_attachement_update(self.dataset, self.datarecord.id);
}
- if (self.default_focus_field) {
- self.default_focus_field.focus();
- }
if (record.id) {
self.do_push_state({id:record.id});
+ } else {
+ self.do_push_state({});
}
- self.$element.removeClass('oe_form_dirty');
- self.$buttons.find('.oe_form_button_save').removeClass('oe_form_button_save_dirty');
+ self.$el.add(self.$buttons).removeClass('oe_form_dirty');
+ self.autofocus();
});
},
+ /**
+ * Loads and sets up the default values for the model as the current
+ * record
+ *
+ * @return {$.Deferred}
+ */
+ load_defaults: function () {
+ var keys = _.keys(this.fields_view.fields);
+ if (keys.length) {
+ return this.dataset.default_get(keys)
+ .pipe(this.on_record_loaded);
+ }
+ return this.on_record_loaded({});
+ },
on_form_changed: function() {
this.trigger("view_content_has_changed");
},
do_notify_change: function() {
- this.$element.addClass('oe_form_dirty');
- this.$buttons.find('.oe_form_button_save').addClass('oe_form_button_save_dirty');
+ this.$el.add(this.$buttons).addClass('oe_form_dirty');
},
on_pager_action: function(action) {
if (this.can_be_discarded()) {
@@ -328,11 +430,31 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form.
this.reload();
}
},
+ init_pager: function() {
+ var self = this;
+ if (this.$pager)
+ this.$pager.remove();
+ if (this.get("actual_mode") === "create")
+ return;
+ this.$pager = $(QWeb.render("FormView.pager", {'widget':self})).hide();
+ if (this.options.$pager) {
+ this.$pager.appendTo(this.options.$pager);
+ } else {
+ 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.on_pager_action(action);
+ });
+ this.do_update_pager();
+ },
do_update_pager: function(hide_index) {
- var index = hide_index ? '-' : this.dataset.index + 1;
- this.$pager.find('button').prop('disabled', this.dataset.ids.length < 2).end()
- .find('span.oe_pager_index').html(index).end()
- .find('span.oe_pager_count').html(this.dataset.ids.length);
+ this.$pager.toggle(this.dataset.ids.length > 1);
+ if (hide_index) {
+ $(".oe_form_pager_state", this.$pager).html("");
+ } else {
+ $(".oe_form_pager_state", this.$pager).html(_.str.sprintf(_t("%d / %d"), this.dataset.index + 1, this.dataset.ids.length));
+ }
},
parse_on_change: function (on_change, widget) {
var self = this;
@@ -473,33 +595,18 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form.
return self.on_processed_onchange(response, processed);
} catch(e) {
console.error(e);
+ instance.webclient.crashmanager.on_javascript_exception(e);
return $.Deferred().reject();
}
});
},
- on_processed_onchange: function(response, processed) {
+ on_processed_onchange: function(result, processed) {
try {
- var result = response;
if (result.value) {
- for (var f in result.value) {
- if (!result.value.hasOwnProperty(f)) { continue; }
- var field = this.fields[f];
- // If field is not defined in the view, just ignore it
- if (field) {
- var value_ = result.value[f];
- if (field.get_value() != value_) {
- field.set_value(value_);
- field._dirty_flag = true;
- if (!_.contains(processed, field.name)) {
- this.do_onchange(field, processed);
- }
- }
- }
- }
- this.on_form_changed();
+ this._internal_set_values(result.value, processed);
}
if (!_.isEmpty(result.warning)) {
- instance.web.dialog($(QWeb.render("CrashManager.warning", result.warning)), {
+ instance.web.dialog($(QWeb.render("CrashManager.warning", result.warning)), {
title:result.warning.title,
modal: true,
buttons: [
@@ -509,6 +616,9 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form.
}
if (result.domain) {
function edit_domain(node) {
+ if (typeof node !== "object") {
+ return;
+ }
var new_domain = result.domain[node.attrs.name];
if (new_domain) {
node.attrs.domain = new_domain;
@@ -520,63 +630,129 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form.
return $.Deferred().resolve();
} catch(e) {
console.error(e);
+ instance.webclient.crashmanager.on_javascript_exception(e);
return $.Deferred().reject();
}
},
- switch_mode: function() {
+ _internal_set_values: function(values, exclude) {
+ exclude = exclude || [];
+ for (var f in values) {
+ if (!values.hasOwnProperty(f)) { continue; }
+ var field = this.fields[f];
+ // If field is not defined in the view, just ignore it
+ if (field) {
+ var value_ = values[f];
+ if (field.get_value() != value_) {
+ field._inhibit_on_change_flag = true;
+ field.set_value(value_);
+ field._inhibit_on_change_flag = false;
+ field._dirty_flag = true;
+ if (!_.contains(exclude, field.name)) {
+ this.do_onchange(field, exclude);
+ }
+ }
+ }
+ }
+ this.on_form_changed();
+ },
+ set_values: function(values) {
+ var self = this;
+ return this.on_change_mutex.exec(function() {
+ self._internal_set_values(values);
+ });
+ },
+ /**
+ * Ask the view to switch to view mode if possible. The view may not do it
+ * if the current record is not yet saved. It will then stay in create mode.
+ */
+ to_view_mode: function() {
+ this._actualize_mode("view");
+ },
+ /**
+ * Ask the view to switch to edit mode if possible. The view may not do it
+ * if the current record is not yet saved. It will then stay in create mode.
+ */
+ to_edit_mode: function() {
+ this._actualize_mode("edit");
+ },
+ /**
+ * Ask the view to switch to a precise mode if possible. The view is free to
+ * not respect this command if the state of the dataset is not compatible with
+ * the new mode. For example, it is not possible to switch to edit mode if
+ * the current record is not yet saved in database.
+ *
+ * @param {string} [new_mode] Can be "edit", "view", "create" or undefined. If
+ * undefined the view will test the actual mode to check if it is still consistent
+ * with the dataset state.
+ */
+ _actualize_mode: function(switch_to) {
+ var mode = switch_to || this.get("actual_mode");
+ if (! this.datarecord.id) {
+ mode = "create";
+ } else if (mode === "create") {
+ mode = "edit";
+ }
+ this.set({actual_mode: mode});
+ },
+ check_actual_mode: function(source, options) {
var self = this;
- if(this.get("mode") == "view") {
- self.$element.removeClass('oe_form_editable').addClass('oe_form_readonly');
+ if(this.get("actual_mode") === "view") {
+ self.$el.removeClass('oe_form_editable').addClass('oe_form_readonly');
self.$buttons.find('.oe_form_buttons_edit').hide();
self.$buttons.find('.oe_form_buttons_view').show();
self.$sidebar.show();
- _.each(this.fields,function(field){
- field.set({"force_readonly": true});
- });
} else {
- self.$element.removeClass('oe_form_readonly').addClass('oe_form_editable');
+ self.$el.removeClass('oe_form_readonly').addClass('oe_form_editable');
self.$buttons.find('.oe_form_buttons_edit').show();
self.$buttons.find('.oe_form_buttons_view').hide();
self.$sidebar.hide();
- _.each(this.fields,function(field){
- field.set({"force_readonly": false});
- });
+ this.autofocus();
+ }
+ },
+ autofocus: function() {
+ if (this.get("actual_mode") !== "view" && !this.options.disable_autofocus) {
+ var fields_order = this.fields_order.slice(0);
+ if (this.default_focus_field) {
+ fields_order.unshift(this.default_focus_field.name);
+ }
+ for (var i = 0; i < fields_order.length; i += 1) {
+ var field = this.fields[fields_order[i]];
+ if (!field.get('effective_invisible') && !field.get('effective_readonly')) {
+ if (field.focus() !== false) {
+ break;
+ }
+ }
+ }
}
},
on_button_save: function() {
var self = this;
return this.do_save().then(function(result) {
- self.set({mode: "view"});
+ self.to_view_mode();
});
},
on_button_cancel: function(event) {
if (this.can_be_discarded()) {
- this.set({mode: "view"});
+ if (this.get('actual_mode') === 'create') {
+ this.trigger('history_back');
+ } else {
+ this.to_view_mode();
+ this.on_record_loaded(this.datarecord);
+ }
}
return false;
},
on_button_new: function() {
var self = this;
- this.set({mode: "edit"});
- var def = $.Deferred();
- $.when(this.has_been_loaded).then(function() {
+ this.to_edit_mode();
+ return $.when(this.has_been_loaded).pipe(function() {
if (self.can_be_discarded()) {
- var keys = _.keys(self.fields_view.fields);
- if (keys.length) {
- self.dataset.default_get(keys).pipe(self.on_record_loaded).then(function() {
- def.resolve();
- });
- } else {
- self.on_record_loaded({}).then(function() {
- def.resolve();
- });
- }
+ return self.load_defaults();
}
});
- return def.promise();
},
on_button_edit: function() {
- return this.set({mode: "edit"});
+ return this.to_edit_mode();
},
on_button_create: function() {
this.dataset.index = null;
@@ -589,7 +765,7 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form.
self.dataset.call('copy', [self.datarecord.id, {}, self.dataset.context]).then(function(new_id) {
return self.on_created({ result : new_id });
}).then(function() {
- return self.set({mode: "edit"});
+ return self.to_edit_mode();
}).then(function() {
def.resolve();
});
@@ -614,17 +790,24 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form.
return def.promise();
},
can_be_discarded: function() {
- return !this.$element.is('.oe_form_dirty') || confirm(_t("Warning, the record has been modified, your changes will be discarded."));
+ if (this.$el.is('.oe_form_dirty')) {
+ if (!confirm(_t("Warning, the record has been modified, your changes will be discarded.\n\nAre you sure you want to leave this page ?"))) {
+ return false;
+ }
+ this.$el.removeClass('oe_form_dirty');
+ }
+ return true;
},
/**
* Triggers saving the form's record. Chooses between creating a new
* record or saving an existing one depending on whether the record
* already has an id property.
*
- * @param {Function} success callback on save success
- * @param {Boolean} [prepend_on_create=false] if ``do_save`` creates a new record, should that record be inserted at the start of the dataset (by default, records are added at the end)
+ * @param {Boolean} [prepend_on_create=false] if ``do_save`` creates a new
+ * record, should that record be inserted at the start of the dataset (by
+ * default, records are added at the end)
*/
- do_save: function(success, prepend_on_create) {
+ do_save: function(prepend_on_create) {
var self = this;
return this.mutating_mutex.exec(function() { return self.is_initialized.pipe(function() {
try {
@@ -632,13 +815,14 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form.
values = {},
first_invalid_field = null;
for (var f in self.fields) {
+ if (!self.fields.hasOwnProperty(f)) { continue; }
f = self.fields[f];
if (!f.is_valid()) {
form_invalid = true;
if (!first_invalid_field) {
first_invalid_field = f;
}
- } else if (f.name !== 'id' && !f.get("readonly") && (!self.datarecord.id || f._dirty_flag)) {
+ } else if (f.name !== 'id' && (!self.datarecord.id || (!f.get("readonly") && f._dirty_flag))) {
// Special case 'id' field, do not save this field
// on 'create' : save all non readonly fields
// on 'edit' : save non readonly modified fields
@@ -647,8 +831,9 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form.
}
if (form_invalid) {
self.set({'display_invalid_fields': true});
- for (var f in self.fields) {
- self.fields[f]._check_css_flags();
+ for (var g in self.fields) {
+ if (!self.fields.hasOwnProperty(g)) { continue; }
+ self.fields[g]._check_css_flags();
}
first_invalid_field.focus();
self.on_invalid();
@@ -657,21 +842,21 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form.
self.set({'display_invalid_fields': false});
var save_deferral;
if (!self.datarecord.id) {
- //console.log("FormView(", self, ") : About to create", values);
+ // Creation save
save_deferral = self.dataset.create(values).pipe(function(r) {
- return self.on_created(r, undefined, prepend_on_create);
+ return self.on_created(r, prepend_on_create);
}, null);
} else if (_.isEmpty(values) && ! self.force_dirty) {
- //console.log("FormView(", self, ") : Nothing to save");
+ // Not dirty, noop save
save_deferral = $.Deferred().resolve({}).promise();
} else {
self.force_dirty = false;
- //console.log("FormView(", self, ") : About to save", values);
+ // Write save
save_deferral = self.dataset.write(self.datarecord.id, values, {}).pipe(function(r) {
return self.on_saved(r);
}, null);
}
- return save_deferral.then(success);
+ return save_deferral;
}
} catch (e) {
console.error(e);
@@ -680,22 +865,29 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form.
});});
},
on_invalid: function() {
- var msg = "<ul>";
- _.each(this.fields, function(f) {
- if (!f.is_valid()) {
- msg += "<li>" + (f.node.attrs.string || f.field.string) + "</li>";
- }
- });
- msg += "</ul>";
- this.do_warn("The following fields are invalid :", msg);
+ var warnings = _(this.fields).chain()
+ .filter(function (f) { return !f.is_valid(); })
+ .map(function (f) {
+ return _.str.sprintf('<li>%s</li>',
+ _.escape(f.string));
+ }).value();
+ warnings.unshift('<ul>');
+ warnings.push('</ul>');
+ this.do_warn("The following fields are invalid :", warnings.join(''));
},
- on_saved: function(r, success) {
+ /**
+ * Reload the form after saving
+ *
+ * @param {Object} r result of the write function.
+ */
+ on_saved: function(r) {
if (!r.result) {
// should not happen in the server, but may happen for internal purpose
return $.Deferred().reject();
} else {
return $.when(this.reload()).pipe(function () {
- return $.when(r).then(success); }, null);
+ return r;
+ });
}
},
/**
@@ -708,10 +900,10 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form.
* * Updates the pager and sidebar displays
*
* @param {Object} r
- * @param {Function} success callback to execute after having updated the dataset
- * @param {Boolean} [prepend_on_create=false] adds the newly created record at the beginning of the dataset instead of the end
+ * @param {Boolean} [prepend_on_create=false] adds the newly created record
+ * at the beginning of the dataset instead of the end
*/
- on_created: function(r, success, prepend_on_create) {
+ on_created: function(r, prepend_on_create) {
if (!r.result) {
// should not happen in the server, but may happen for internal purpose
return $.Deferred().reject();
@@ -728,9 +920,10 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form.
if (this.sidebar) {
this.sidebar.do_attachement_update(this.dataset, this.datarecord.id);
}
- //instance.log("The record has been created with id #" + this.datarecord.id);
- this.reload();
- return $.when(_.extend(r, {created: true})).then(success);
+ //openerp.log("The record has been created with id #" + this.datarecord.id);
+ return $.when(this.reload()).pipe(function () {
+ return _.extend(r, {created: true});
+ });
}
},
on_action: function (action) {
@@ -746,8 +939,10 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form.
if (self.dataset.index == null || self.dataset.index < 0) {
return $.when(self.on_button_new());
} else {
- return self.dataset.read_index(_.keys(self.fields_view.fields), {
- context : { 'bin_size' : true }
+ var fields = _.keys(self.fields_view.fields);
+ fields.push('display_name');
+ return self.dataset.read_index(fields, {
+ context : { 'bin_size' : true, 'future_display_name' : true }
}).pipe(self.on_record_loaded);
}
});
@@ -758,15 +953,15 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form.
});
},
get_fields_values: function(blacklist) {
- blacklist = blacklist || [];
+ blacklist = blacklist || [];
var values = {};
var ids = this.get_selected_ids();
values["id"] = ids.length > 0 ? ids[0] : false;
_.each(this.fields, function(value_, key) {
- if (_.include(blacklist, key))
- return;
- var val = value_.get_value();
- values[key] = val;
+ if (_.include(blacklist, key)) {
+ return;
+ }
+ values[key] = value_.get_value();
});
return values;
},
@@ -781,6 +976,15 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form.
return self.dataset.parent_view.recursive_save();
});
},
+ recursive_reload: function() {
+ var self = this;
+ var pre = $.when();
+ if (self.dataset.parent_view)
+ pre = self.dataset.parent_view.recursive_reload();
+ return pre.pipe(function() {
+ return self.reload();
+ });
+ },
is_dirty: function() {
return _.any(this.fields, function (value_) {
return value_._dirty_flag;
@@ -811,21 +1015,22 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form.
|| field.get('invisible')
|| field.get("readonly")
|| field.field.type === 'one2many'
- || field.field.type === 'many2many') {
+ || field.field.type === 'many2many'
+ || field.field.type === 'binary') {
return false;
}
var displayed = value;
- switch (field.field.type) {
- case 'selection':
+ if (field instanceof instance.web.form.FieldSelection) {
displayed = _(field.values).find(function (option) {
return option[0] === value;
})[1];
- break;
+ } else if (field instanceof instance.web.form.FieldMany2One) {
+ displayed = field.get_displayed();
}
return {
name: name,
- string: field.node.attrs.string || field.field.string,
+ string: field.string,
value: value,
displayed: displayed,
// convert undefined to false
@@ -848,14 +1053,14 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form.
buttons: [
{text: _t("Close"), click: function () { d.close(); }},
{text: _t("Save default"), click: function () {
- var $defaults = d.$element.find('#formview_default_fields');
+ var $defaults = d.$el.find('#formview_default_fields');
var field_to_set = $defaults.val();
if (!field_to_set) {
$defaults.parent().addClass('oe_form_invalid');
return;
}
- var condition = d.$element.find('#formview_default_conditions').val(),
- all_users = d.$element.find('#formview_default_all').is(':checked');
+ var condition = d.$el.find('#formview_default_conditions').val(),
+ all_users = d.$el.find('#formview_default_all').is(':checked');
new instance.web.DataSet(self, 'ir.values').call(
'set_default', [
self.dataset.model,
@@ -874,10 +1079,22 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form.
register_field: function(field, name) {
this.fields[name] = field;
this.fields_order.push(name);
+ if (JSON.parse(field.node.attrs.default_focus || "0")) {
+ this.default_focus_field = field;
+ }
+
+ field.on('focused', null, this.proxy('widgetFocused'))
+ .on('blurred', null, this.proxy('widgetBlurred'));
if (this.get_field(name).translate) {
this.translatable_fields.push(field);
}
field.on('changed_value', this, function() {
+ if (field.is_syntax_valid()) {
+ this.trigger('field_changed:' + name);
+ }
+ if (field._inhibit_on_change_flag) {
+ return;
+ }
field._dirty_flag = true;
if (field.is_syntax_valid()) {
this.do_onchange(field);
@@ -889,10 +1106,10 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form.
get_field: function(field_name) {
return this.fields_view.fields[field_name];
},
- is_create_mode: function() {
- return !this.datarecord.id;
+ get_field_value: function(field_name) {
+ return this.fields[field_name].get_value();
},
-}));
+});
/**
* Interface to be implemented by rendering engines for the form view.
@@ -900,12 +1117,12 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form.
instance.web.form.FormRenderingEngineInterface = instance.web.Class.extend({
set_fields_view: function(fields_view) {},
set_fields_registry: function(fields_registry) {},
- render_to: function($element) {},
+ render_to: function($el) {},
});
/**
* Default rendering engine for the form view.
- *
+ *
* It is necessary to set the view using set_view() before usage.
*/
instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInterface.extend({
@@ -914,6 +1131,10 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
},
set_fields_view: function(fvg) {
this.fvg = fvg;
+ this.version = parseFloat(this.fvg.arch.attrs.version);
+ if (isNaN(this.version)) {
+ this.version = 6.1;
+ }
},
set_tags_registry: function(tags_registry) {
this.tags_registry = tags_registry;
@@ -921,6 +1142,20 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
set_fields_registry: function(fields_registry) {
this.fields_registry = fields_registry;
},
+ set_widgets_registry: function(widgets_registry) {
+ this.widgets_registry = widgets_registry;
+ },
+ // Backward compatibility tools, current default version: v6.1
+ process_version: function() {
+ if (this.version < 7.0) {
+ this.$form.find('form:first').wrapInner('<group col="4"/>');
+ this.$form.find('page').each(function() {
+ if (!$(this).parents('field').length) {
+ $(this).wrapInner('<group col="4"/>');
+ }
+ });
+ }
+ },
render_to: function($target) {
var self = this;
this.$target = $target;
@@ -930,8 +1165,11 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
var xml = instance.web.json_node_to_xml(this.fvg.arch);
this.$form = $('<div class="oe_form">' + xml + '</div>');
+ this.process_version();
+
this.fields_to_init = [];
this.tags_to_init = [];
+ this.widgets_to_init = [];
this.labels = {};
this.process(this.$form);
@@ -961,6 +1199,12 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
var w = new (obj)(self.view, instance.web.xml_to_json($elem[0]));
w.replace($elem);
});
+ _.each(this.widgets_to_init, function($elem) {
+ var widget_type = $elem.attr("type");
+ var obj = self.widgets_registry.get_object(widget_type);
+ var w = new (obj)(self.view, instance.web.xml_to_json($elem[0]));
+ w.replace($elem);
+ });
// TODO: return a deferred
},
render_element: function(template /* dictionaries */) {
@@ -1003,6 +1247,10 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
return $tag;
}
},
+ process_widget: function($widget) {
+ this.widgets_to_init.push($widget);
+ return $widget;
+ },
process_sheet: function($sheet) {
var $new_sheet = this.render_element('FormRenderingSheet', $sheet.getAttributes());
this.handle_common_properties($new_sheet, $sheet);
@@ -1051,7 +1299,7 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
if (found)
return;
- $label = $('<label/>').attr({
+ var $label = $('<label/>').attr({
'for' : name,
"modifiers": JSON.stringify({invisible: field_modifiers.invisible}),
"string": $field.attr('string'),
@@ -1116,6 +1364,9 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
if (!$tr || row_cols < colspan) {
$tr = $('<tr/>').addClass('oe_form_group_row').appendTo($table);
row_cols = cols;
+ } else if (tagName==='group') {
+ // When <group> <group/><group/> </group>, we need a spacing between the two groups
+ $td.addClass('oe_group_right')
}
row_cols -= colspan;
@@ -1141,12 +1392,6 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
$child = $td.children(':first');
switch ($child[0].tagName.toLowerCase()) {
case 'separator':
- if ($child.attr('orientation') === 'vertical') {
- $td.addClass('oe_vertical_separator').attr('width', '1');
- $td.empty();
- row_cols-= $td.attr('colspan') || 1;
- total--;
- }
break;
case 'label':
if ($child.attr('for')) {
@@ -1206,7 +1451,7 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
page_attrs.__page = $new_page;
page_attrs.__ic = ic;
pages.push(page_attrs);
-
+
$new_page.children().each(function() {
self.process($(this));
});
@@ -1221,6 +1466,10 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
if (! page.__ic)
return;
page.__ic.on("change:effective_invisible", null, function() {
+ if (!page.__ic.get('effective_invisible')) {
+ $new_notebook.tabs('select', i);
+ return;
+ }
var current = $new_notebook.tabs("option", "selected");
if (! pages[current].__ic || ! pages[current].__ic.get("effective_invisible"))
return;
@@ -1232,7 +1481,7 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
}
});
});
-
+
this.handle_common_properties($new_notebook, $notebook);
return $new_notebook;
},
@@ -1268,7 +1517,7 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
return $new_label;
},
handle_common_properties: function($new_element, $node) {
- var str_modifiers = $node.attr("modifiers") || "{}"
+ var str_modifiers = $node.attr("modifiers") || "{}";
var modifiers = JSON.parse(str_modifiers);
var ic = null;
if (modifiers.invisible !== undefined)
@@ -1279,12 +1528,6 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
},
});
-instance.web.form.FormRenderingEngineReadonly = instance.web.form.FormRenderingEngine.extend({
- alter_field: function(field) {
- field.set({"force_readonly": true});
- },
-});
-
instance.web.form.FormDialog = instance.web.Dialog.extend({
init: function(parent, options, view_id, dataset) {
this._super(parent, options);
@@ -1297,7 +1540,7 @@ instance.web.form.FormDialog = instance.web.Dialog.extend({
this.form = new instance.web.FormView(this, this.dataset, this.view_id, {
pager: false
});
- this.form.appendTo(this.$element);
+ this.form.appendTo(this.$el);
this.form.on_created.add_last(this.on_form_dialog_saved);
this.form.on_saved.add_last(this.on_form_dialog_saved);
return this;
@@ -1389,77 +1632,115 @@ instance.web.form.compute_domain = function(expr, fields) {
/**
* Must be applied over an class already possessing the PropertiesMixin.
*
- * Apply the result of the "invisible" domain to this.$element.
+ * Apply the result of the "invisible" domain to this.$el.
*/
instance.web.form.InvisibilityChangerMixin = {
init: function(field_manager, invisible_domain) {
- this._ic_field_manager = field_manager
+ var self = this;
+ this._ic_field_manager = field_manager;
this._ic_invisible_modifier = invisible_domain;
this._ic_field_manager.on("view_content_has_changed", this, function() {
- var result = this._ic_invisible_modifier === undefined ? false :
- instance.web.form.compute_domain(this._ic_invisible_modifier, this._ic_field_manager.fields);
- this.set({"invisible": result});
+ var result = self._ic_invisible_modifier === undefined ? false :
+ instance.web.form.compute_domain(
+ self._ic_invisible_modifier,
+ self._ic_field_manager.fields);
+ self.set({"invisible": result});
});
this.set({invisible: this._ic_invisible_modifier === true, force_invisible: false});
var check = function() {
- if (this.get("invisible") || this.get('force_invisible')) {
- this.set({"effective_invisible": true});
+ if (self.get("invisible") || self.get('force_invisible')) {
+ self.set({"effective_invisible": true});
} else {
- this.set({"effective_invisible": false});
+ self.set({"effective_invisible": false});
}
};
this.on('change:invisible', this, check);
this.on('change:force_invisible', this, check);
- _.bind(check, this)();
+ check.call(this);
},
start: function() {
this.on("change:effective_invisible", this, this._check_visibility);
this._check_visibility();
},
_check_visibility: function() {
- this.$element.toggleClass('oe_form_invisible', this.get("effective_invisible"));
+ this.$el.toggleClass('oe_form_invisible', this.get("effective_invisible"));
},
};
-instance.web.form.InvisibilityChanger = instance.web.Class.extend(_.extend({}, instance.web.PropertiesMixin, instance.web.form.InvisibilityChangerMixin, {
- init: function(parent, field_manager, invisible_domain, $element) {
+instance.web.form.InvisibilityChanger = instance.web.Class.extend(instance.web.PropertiesMixin, instance.web.form.InvisibilityChangerMixin, {
+ init: function(parent, field_manager, invisible_domain, $el) {
this.setParent(parent);
instance.web.PropertiesMixin.init.call(this);
instance.web.form.InvisibilityChangerMixin.init.call(this, field_manager, invisible_domain);
- this.$element = $element;
+ this.$el = $el;
this.start();
},
-}));
+});
+
+/**
+ Base class for all fields, custom widgets and buttons to be displayed in the form view.
-instance.web.form.FormWidget = instance.web.Widget.extend(_.extend({}, instance.web.form.InvisibilityChangerMixin, {
+ Properties:
+ - effective_readonly: when it is true, the widget is displayed as readonly. Vary depending
+ the values of the "readonly" property and the "mode" property on the field manager.
+*/
+instance.web.form.FormWidget = instance.web.Widget.extend(instance.web.form.InvisibilityChangerMixin, {
/**
* @constructs instance.web.form.FormWidget
* @extends instance.web.Widget
*
- * @param view
+ * @param field_manager
* @param node
*/
- init: function(view, node) {
- this._super(view);
- this.view = view;
+ init: function(field_manager, node) {
+ this._super(field_manager);
+ this.view = field_manager;
+ this.field_manager = field_manager;
this.node = node;
this.modifiers = JSON.parse(this.node.attrs.modifiers || '{}');
- instance.web.form.InvisibilityChangerMixin.init.call(this, view, this.modifiers.invisible);
+ instance.web.form.InvisibilityChangerMixin.init.call(this, this.field_manager, this.modifiers.invisible);
+
+ this.field_manager.on("view_content_has_changed", this, this.process_modifiers);
- this.view.on("view_content_has_changed", this, this.process_modifiers);
+ this.set({required: this.modifiers['required'] === true});
+ // some events to make the property "effective_readonly" sync automatically with "readonly" and
+ // "mode" on field_manager
+ var self = this;
+ this.set({"readonly": this.modifiers['readonly'] === true});
+ var test_effective_readonly = function() {
+ self.set({"effective_readonly": self.get("readonly") || self.field_manager.get("actual_mode") === "view"});
+ };
+ this.on("change:readonly", this, test_effective_readonly);
+ this.field_manager.on("change:actual_mode", this, test_effective_readonly);
+ test_effective_readonly.call(this);
},
renderElement: function() {
this._super();
- this.$element.addClass(this.node.attrs["class"] || "");
+ this.$el.addClass(this.node.attrs["class"] || "");
},
destroy: function() {
$.fn.tipsy.clear();
this._super.apply(this, arguments);
},
+ /**
+ * Sets up blur/focus forwarding from DOM elements to a widget (`this`).
+ *
+ * This method is an utility method that is meant to be called by child classes.
+ *
+ * @param {jQuery} $e jQuery object of elements to bind focus/blur on
+ */
+ setupFocus: function ($e) {
+ var self = this;
+ $e.on({
+ focus: function () { self.trigger('focused'); },
+ blur: function () { self.trigger('blurred'); }
+ });
+ },
process_modifiers: function() {
var compute_domain = instance.web.form.compute_domain;
var to_set = {};
for (var a in this.modifiers) {
+ if (!this.modifiers.hasOwnProperty(a)) { continue; }
if (!_.include(["invisible"], a)) {
var val = compute_domain(this.modifiers[a], this.view.fields);
to_set[a] = val;
@@ -1469,7 +1750,7 @@ instance.web.form.FormWidget = instance.web.Widget.extend(_.extend({}, instance.
},
do_attach_tooltip: function(widget, trigger, options) {
widget = widget || this;
- trigger = trigger || this.$element;
+ trigger = trigger || this.$el;
options = _.extend({
delayIn: 500,
delayOut: 0,
@@ -1480,7 +1761,7 @@ instance.web.form.FormWidget = instance.web.Widget.extend(_.extend({}, instance.
template = 'WidgetLabel.tooltip';
}
return QWeb.render(template, {
- debug: instance.connection.debug,
+ debug: instance.session.debug,
widget: widget
})},
gravity: $.fn.tipsy.autoBounds(50, 'nw'),
@@ -1501,7 +1782,7 @@ instance.web.form.FormWidget = instance.web.Widget.extend(_.extend({}, instance.
parent: {}
});
if (a_dataset.parent_view) {
- fields_values.parent = a_dataset.parent_view.get_fields_values([a_dataset.child_name]);
+ fields_values.parent = a_dataset.parent_view.get_fields_values([a_dataset.child_name]);
}
return fields_values;
},
@@ -1519,7 +1800,7 @@ instance.web.form.FormWidget = instance.web.Widget.extend(_.extend({}, instance.
if (! v_context) {
v_context = (this.field || {}).context || {};
}
-
+
if (v_context.__ref || true) { //TODO: remove true
var fields_values = this._build_eval_context(blacklist);
v_context = new instance.web.CompoundContext(v_context).set_eval_context(fields_values);
@@ -1537,26 +1818,30 @@ instance.web.form.FormWidget = instance.web.Widget.extend(_.extend({}, instance.
}
return final_domain;
}
-}));
+});
instance.web.form.WidgetButton = instance.web.form.FormWidget.extend({
template: 'WidgetButton',
- init: function(view, node) {
- this._super(view, node);
+ init: function(field_manager, node) {
+ this._super(field_manager, node);
this.force_disabled = false;
this.string = (this.node.attrs.string || '').replace(/_/g, '');
- if (this.node.attrs.default_focus == '1') {
+ if (JSON.parse(this.node.attrs.default_focus || "0")) {
// TODO fme: provide enter key binding to widgets
this.view.default_focus_button = this;
}
+ if (this.node.attrs.icon && (! /\//.test(this.node.attrs.icon))) {
+ this.node.attrs.icon = '/web/static/src/img/icons/' + this.node.attrs.icon + '.png';
+ }
this.view.on('view_content_has_changed', this, this.check_disable);
},
start: function() {
this._super.apply(this, arguments);
- this.$element.click(this.on_click);
- if (this.node.attrs.help || instance.connection.debug) {
+ this.$el.click(this.on_click);
+ if (this.node.attrs.help || instance.session.debug) {
this.do_attach_tooltip();
}
+ this.setupFocus(this.$el);
},
on_click: function() {
var self = this;
@@ -1605,37 +1890,30 @@ instance.web.form.WidgetButton = instance.web.form.FormWidget.extend({
on_confirmed: function() {
var self = this;
- var context = this.node.attrs.context;
- if (context && context.__ref) {
- context = new instance.web.CompoundContext(context);
- context.set_eval_context(this._build_eval_context());
- }
+ var context = this.build_context();
return this.view.do_execute_action(
_.extend({}, this.node.attrs, {context: context}),
this.view.dataset, this.view.datarecord.id, function () {
- self.view.reload();
+ self.view.recursive_reload();
});
},
check_disable: function() {
var disabled = (this.force_disabled || !this.view.is_interactible_record());
- this.$element.prop('disabled', disabled);
- this.$element.css('color', disabled ? 'grey' : '');
+ this.$el.prop('disabled', disabled);
+ this.$el.css('color', disabled ? 'grey' : '');
}
});
/**
* Interface to be implemented by fields.
- *
- * Properties:
- * - readonly: boolean. If set to true the field should appear in readonly mode.
- * - force_readonly: boolean, When it is true, the field should always appear
- * in read only mode, no matter what the value of the "readonly" property can be.
+ *
* Events:
- * - changed_value: triggered to inform the view to check on_changes
- *
+ * - changed_value: triggered when the value of the field has changed. This can be due
+ * to a user interaction or a call to set_value().
+ *
*/
-instance.web.form.FieldMixin = {
+instance.web.form.FieldInterface = {
/**
* Constructor takes 2 arguments:
* - field_manager: Implements FieldManagerMixin
@@ -1644,14 +1922,14 @@ instance.web.form.FieldMixin = {
init: function(field_manager, node) {},
/**
* Called by the form view to indicate the value of the field.
- *
+ *
* set_value() may return an object that can be passed to $.when() that represents the moment when
* the field has finished all operations necessary before the user can effectively use the widget.
- *
+ *
* Multiple calls to set_value() can occur at any time and must be handled correctly by the implementation,
* regardless of any asynchronous operation currently running and the status of any promise that a
* previous call to set_value() could have returned.
- *
+ *
* set_value() must be able, at any moment, to handle the syntax returned by the "read" method of the
* osv class in the OpenERP server as well as the syntax used by the set_value() (see below). It must
* also be able to handle any other format commonly used in the _defaults key on the models in the addons
@@ -1661,19 +1939,19 @@ instance.web.form.FieldMixin = {
set_value: function(value_) {},
/**
* Get the current value of the widget.
- *
- * Must always return a syntaxically correct value to be passed to the "write" method of the osv class in
+ *
+ * Must always return a syntactically correct value to be passed to the "write" method of the osv class in
* the OpenERP server, although it is not assumed to respect the constraints applied to the field.
- * For example if the field is marqued as "required", a call to get_value() can return false.
- *
+ * For example if the field is marked as "required", a call to get_value() can return false.
+ *
* get_value() can also be called *before* a call to set_value() and, in that case, is supposed to
- * return a defaut value according to the type of field.
- *
+ * return a default value according to the type of field.
+ *
* This method is always assumed to perform synchronously, it can not return a promise.
- *
+ *
* If there was no user interaction to modify the value of the field, it is always assumed that
* get_value() return the same semantic value than the one passed in the last call to set_value(),
- * altough the syntax can be different. This can be the case for type of fields that have a different
+ * although the syntax can be different. This can be the case for type of fields that have a different
* syntax for "read" and "write" (example: m2o: set_value([0, "Administrator"]), get_value() => 0).
*/
get_value: function() {},
@@ -1688,28 +1966,30 @@ instance.web.form.FieldMixin = {
*/
is_valid: function() {},
/**
- * Returns true if the field holds a value which is syntaxically correct, ignoring
+ * Returns true if the field holds a value which is syntactically correct, ignoring
* the potential semantic restrictions applied to the field.
*/
is_syntax_valid: function() {},
/**
- * Must set the focus on the field.
+ * Must set the focus on the field. Return false if field is not focusable.
*/
focus: function() {},
+ /**
+ * Called when the translate button is clicked.
+ */
+ on_translate: function() {},
};
/**
- * Abstract class for classes implementing FieldMixin.
- *
+ * Abstract class for classes implementing FieldInterface.
+ *
* Properties:
- * - effective_readonly: when it is true, the widget is displayed as readonly. Vary depending
- * the values of the "readonly" property and the "force_readonly" property on the field manager.
* - value: useful property to hold the value of the field. By default, set_value() and get_value()
* set and retrieve the value property. Changing the value property also triggers automatically
* a 'changed_value' event that inform the view to trigger on_changes.
- *
+ *
*/
-instance.web.form.AbstractField = instance.web.form.FormWidget.extend(_.extend({}, instance.web.form.FieldMixin, {
+instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.web.form.FieldInterface, {
/**
* @constructs instance.web.form.AbstractField
* @extends instance.web.form.FormWidget
@@ -1718,26 +1998,17 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(_.extend({
* @param node
*/
init: function(field_manager, node) {
+ var self = this
this._super(field_manager, node);
- this.field_manager = field_manager;
this.name = this.node.attrs.name;
- this.set({'value': false});
this.field = this.field_manager.get_field(this.name);
- this.set({required: this.modifiers['required'] === true});
-
- // some events to make the property "effective_readonly" sync automatically with "readonly" and
- // "force_readonly"
- this.set({"readonly": this.modifiers['readonly'] === true});
- var test_effective_readonly = function() {
- this.set({"effective_readonly": this.get("readonly") || !!this.get("force_readonly")});
- };
- this.on("change:readonly", this, test_effective_readonly);
- this.on("change:force_readonly", this, test_effective_readonly);
- _.bind(test_effective_readonly, this)();
-
+ this.widget = this.node.attrs.widget;
+ this.string = this.node.attrs.string || this.field.string || this.name;
+ this.options = JSON.parse(this.node.attrs.options || '{}');
+ this.set({'value': false});
+
this.on("change:value", this, function() {
- if (! this._inhibit_on_change)
- this.trigger('changed_value');
+ this.trigger('changed_value');
this._check_css_flags();
});
},
@@ -1745,14 +2016,12 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(_.extend({
var self = this;
this._super();
if (this.field.translate) {
- this.$element.addClass('oe_form_field_translatable');
- this.$element.find('.oe_field_translate').click(_.bind(function() {
- this.field_manager.open_translate_dialog(this);
- }, this));
+ this.$el.addClass('oe_form_field_translatable');
+ this.$el.find('.oe_field_translate').click(this.on_translate);
}
- this.$label = this.view.$element.find('label[for=' + this.id_for_label + ']');
- if (instance.connection.debug) {
- this.do_attach_tooltip(this, this.$label[0] || this.$element);
+ this.$label = this.view.$el.find('label[for=' + this.id_for_label + ']');
+ if (instance.session.debug) {
+ this.do_attach_tooltip(this, this.$label[0] || this.$el);
this.$label.off('dblclick').on('dblclick', function() {
console.log("Field '%s' of type '%s' in View: %o", self.name, (self.node.attrs.widget || self.field.type), self.view);
window.w = self;
@@ -1771,18 +2040,16 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(_.extend({
* Private. Do not use.
*/
_set_required: function() {
- this.$element.toggleClass('oe_form_required', this.get("required"));
+ this.$el.toggleClass('oe_form_required', this.get("required"));
},
set_value: function(value_) {
- this._inhibit_on_change = true;
this.set({'value': value_});
- this._inhibit_on_change = false;
},
get_value: function() {
return this.get('value');
},
is_valid: function() {
- return this.is_syntax_valid() && (! this.get('required') || ! this.is_false());
+ return this.is_syntax_valid() && !(this.get('required') && this.is_false());
},
is_syntax_valid: function() {
return true;
@@ -1794,46 +2061,36 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(_.extend({
is_false: function() {
return this.get('value') === false;
},
- _check_css_flags: function(show_invalid) {
+ _check_css_flags: function() {
if (this.field.translate) {
- this.$element.find('.oe_field_translate').toggle(!this.field_manager.is_create_mode());
+ this.$el.find('.oe_field_translate').toggle(this.field_manager.get('actual_mode') !== "create");
}
if (!this.disable_utility_classes) {
if (this.field_manager.get('display_invalid_fields')) {
- this.$element.toggleClass('oe_form_invalid', !this.is_valid());
+ this.$el.toggleClass('oe_form_invalid', !this.is_valid());
}
}
},
focus: function() {
- },
- /**
- * Utility method to focus an element, but only after a small amount of time.
- */
- delay_focus: function($elem) {
- setTimeout(function() {
- $elem.focus();
- }, 50);
- },
- /**
- * Utility method to get the widget options defined in the field xml description.
- */
- get_definition_options: function() {
- if (!this.definition_options) {
- var str = this.node.attrs.options || '{}';
- this.definition_options = JSON.parse(str);
- }
- return this.definition_options;
+ return false;
},
set_input_id: function(id) {
this.id_for_label = id;
},
-}));
+ on_translate: function() {
+ var self = this;
+ var trans = new instance.web.DataSet(this, 'ir.translation');
+ return trans.call_button('translate_fields', [this.view.dataset.model, this.view.datarecord.id, this.name, this.view.dataset.get_context()]).then(function(r) {
+ self.do_action(r.result);
+ });
+ },
+});
/**
- * A mixin to apply on any field that has to completely re-render when its readonly state
+ * A mixin to apply on any FormWidget that has to completely re-render when its readonly state
* switch.
*/
-instance.web.form.ReinitializeFieldMixin = {
+instance.web.form.ReinitializeWidgetMixin = {
/**
* Default implementation of start(), use it or call explicitly initialize_field().
*/
@@ -1846,10 +2103,8 @@ instance.web.form.ReinitializeFieldMixin = {
this.destroy_content();
this.renderElement();
this.initialize_content();
- this.render_value();
});
this.initialize_content();
- this.render_value();
},
/**
* Called to destroy anything that could have been created previously, called before a
@@ -1860,13 +2115,27 @@ instance.web.form.ReinitializeFieldMixin = {
* Called to initialize the content.
*/
initialize_content: function() {},
+};
+
+/**
+ * A mixin to apply on any field that has to completely re-render when its readonly state
+ * switch.
+ */
+instance.web.form.ReinitializeFieldMixin = _.extend({}, instance.web.form.ReinitializeWidgetMixin, {
+ initialize_field: function() {
+ instance.web.form.ReinitializeWidgetMixin.initialize_field.call(this);
+ this.on("change:effective_readonly", this, function() {
+ this.render_value();
+ });
+ this.render_value();
+ },
/**
* Called to render the value. Should also be explicitly called at the end of a set_value().
*/
render_value: function() {},
-};
+});
-instance.web.form.FieldChar = instance.web.form.AbstractField.extend(_.extend({}, instance.web.form.ReinitializeFieldMixin, {
+instance.web.form.FieldChar = instance.web.form.AbstractField.extend(instance.web.form.ReinitializeFieldMixin, {
template: 'FieldChar',
widget_class: 'oe_form_field_char',
init: function (field_manager, node) {
@@ -1875,9 +2144,11 @@ instance.web.form.FieldChar = instance.web.form.AbstractField.extend(_.extend({}
},
initialize_content: function() {
var self = this;
- this.$element.find('input').change(function() {
- self.set({'value': instance.web.parse_value(self.$element.find('input').val(), self)});
+ var $input = this.$el.find('input');
+ $input.change(function() {
+ self.set({'value': instance.web.parse_value($input.val(), self)});
});
+ this.setupFocus($input);
},
set_value: function(value_) {
this._super(value_);
@@ -1886,18 +2157,18 @@ instance.web.form.FieldChar = instance.web.form.AbstractField.extend(_.extend({}
render_value: function() {
var show_value = instance.web.format_value(this.get('value'), this, '');
if (!this.get("effective_readonly")) {
- this.$element.find('input').val(show_value);
+ this.$el.find('input').val(show_value);
} else {
if (this.password) {
show_value = new Array(show_value.length + 1).join('*');
}
- this.$element.text(show_value);
+ this.$el.text(show_value);
}
},
is_syntax_valid: function() {
if (!this.get("effective_readonly")) {
try {
- var value_ = instance.web.parse_value(this.$element.find('input').val(), this, '');
+ var value_ = instance.web.parse_value(this.$el.find('input').val(), this, '');
return true;
} catch(e) {
return false;
@@ -1909,27 +2180,29 @@ instance.web.form.FieldChar = instance.web.form.AbstractField.extend(_.extend({}
return this.get('value') === '' || this._super();
},
focus: function() {
- this.delay_focus(this.$element.find('input:first'));
+ this.$('input:first').focus();
}
-}));
+});
instance.web.form.FieldID = instance.web.form.FieldChar.extend({
-
+
});
instance.web.form.FieldEmail = instance.web.form.FieldChar.extend({
template: 'FieldEmail',
initialize_content: function() {
this._super();
- this.$element.find('button').click(this.on_button_clicked);
+ var $button = this.$el.find('button');
+ $button.click(this.on_button_clicked);
+ this.setupFocus($button);
},
render_value: function() {
if (!this.get("effective_readonly")) {
this._super();
} else {
- this.$element.find('a')
+ this.$el.find('a')
.attr('href', 'mailto:' + this.get('value'))
- .text(this.get('value'));
+ .text(this.get('value') || '');
}
},
on_button_clicked: function() {
@@ -1945,7 +2218,9 @@ instance.web.form.FieldUrl = instance.web.form.FieldChar.extend({
template: 'FieldUrl',
initialize_content: function() {
this._super();
- this.$element.find('button').click(this.on_button_clicked);
+ var $button = this.$el.find('button');
+ $button.click(this.on_button_clicked);
+ this.setupFocus($button);
},
render_value: function() {
if (!this.get("effective_readonly")) {
@@ -1956,7 +2231,7 @@ instance.web.form.FieldUrl = instance.web.form.FieldChar.extend({
if (!s) {
tmp = "http://" + this.get('value');
}
- this.$element.find('a').attr('href', tmp).text(tmp);
+ this.$el.find('a').attr('href', tmp).text(this.get('value') ? tmp : '');
}
},
on_button_clicked: function() {
@@ -1978,7 +2253,7 @@ instance.web.form.FieldFloat = instance.web.form.FieldChar.extend({
this._super(field_manager, node);
this.set({'value': 0});
if (this.node.attrs.digits) {
- this.digits = py.eval(node.attrs.digits);
+ this.digits = this.node.attrs.digits;
} else {
this.digits = this.field.digits;
}
@@ -1989,10 +2264,13 @@ instance.web.form.FieldFloat = instance.web.form.FieldChar.extend({
value_ = 0;
}
this._super.apply(this, [value_]);
+ },
+ focus: function () {
+ this.$('input:first').select();
}
});
-instance.web.DateTimeWidget = instance.web.OldWidget.extend({
+instance.web.DateTimeWidget = instance.web.Widget.extend({
template: "web.datepicker",
jqueryui_object: 'datetimepicker',
type_of_date: "datetime",
@@ -2002,23 +2280,27 @@ instance.web.DateTimeWidget = instance.web.OldWidget.extend({
},
start: function() {
var self = this;
- this.$input = this.$element.find('input.oe_datepicker_master');
- this.$input_picker = this.$element.find('input.oe_datepicker_container');
+ this.$input = this.$el.find('input.oe_datepicker_master');
+ this.$input_picker = this.$el.find('input.oe_datepicker_container');
this.$input.change(this.on_change);
this.picker({
+ onClose: this.on_picker_select,
onSelect: this.on_picker_select,
changeMonth: true,
changeYear: true,
showWeek: true,
- showButtonPanel: true
+ showButtonPanel: true,
+ firstDay: Date.CultureInfo.firstDayOfWeek
});
- this.$element.find('img.oe_datepicker_trigger').click(function() {
- if (!self.get("effective_readonly") && !self.picker('widget').is(':visible')) {
- self.picker('setDate', self.get('value') ? instance.web.auto_str_to_date(self.get('value')) : new Date());
- self.$input_picker.show();
- self.picker('show');
- self.$input_picker.hide();
+ this.$el.find('img.oe_datepicker_trigger').click(function() {
+ if (self.get("effective_readonly") || self.picker('widget').is(':visible')) {
+ self.$input.focus();
+ return;
}
+ self.picker('setDate', self.value ? instance.web.auto_str_to_date(self.value) : new Date());
+ self.$input_picker.show();
+ self.picker('show');
+ self.$input_picker.hide();
});
this.set_readonly(false);
this.set({'value': false});
@@ -2028,7 +2310,10 @@ instance.web.DateTimeWidget = instance.web.OldWidget.extend({
},
on_picker_select: function(text, instance_) {
var date = this.picker('getDate');
- this.$input.val(date ? this.format_client(date) : '').change();
+ this.$input
+ .val(date ? this.format_client(date) : '')
+ .change()
+ .focus();
},
set_value: function(value_) {
this.set({'value': value_});
@@ -2044,7 +2329,7 @@ instance.web.DateTimeWidget = instance.web.OldWidget.extend({
set_readonly: function(readonly) {
this.readonly = readonly;
this.$input.prop('readonly', this.readonly);
- this.$element.find('img.oe_datepicker_trigger').toggleClass('oe_input_icon_disabled', readonly);
+ this.$el.find('img.oe_datepicker_trigger').toggleClass('oe_input_icon_disabled', readonly);
},
is_valid_: function() {
var value_ = this.$input.val();
@@ -2077,7 +2362,7 @@ instance.web.DateWidget = instance.web.DateTimeWidget.extend({
type_of_date: "date"
});
-instance.web.form.FieldDatetime = instance.web.form.AbstractField.extend(_.extend({}, instance.web.form.ReinitializeFieldMixin, {
+instance.web.form.FieldDatetime = instance.web.form.AbstractField.extend(instance.web.form.ReinitializeFieldMixin, {
template: "FieldDatetime",
build_widget: function() {
return new instance.web.DateTimeWidget(this);
@@ -2094,7 +2379,8 @@ instance.web.form.FieldDatetime = instance.web.form.AbstractField.extend(_.exten
this.datewidget.on_change.add_last(_.bind(function() {
this.set({'value': this.datewidget.get_value()});
}, this));
- this.datewidget.appendTo(this.$element);
+ this.datewidget.appendTo(this.$el);
+ this.setupFocus(this.datewidget.$input);
}
},
set_value: function(value_) {
@@ -2105,7 +2391,7 @@ instance.web.form.FieldDatetime = instance.web.form.AbstractField.extend(_.exten
if (!this.get("effective_readonly")) {
this.datewidget.set_value(this.get('value'));
} else {
- this.$element.text(instance.web.format_value(this.get('value'), this, ''));
+ this.$el.text(instance.web.format_value(this.get('value'), this, ''));
}
},
is_syntax_valid: function() {
@@ -2118,10 +2404,11 @@ instance.web.form.FieldDatetime = instance.web.form.AbstractField.extend(_.exten
return this.get('value') === '' || this._super();
},
focus: function() {
- if (this.datewidget && this.datewidget.$input)
- this.delay_focus(this.datewidget.$input);
+ if (this.datewidget && this.datewidget.$input) {
+ this.datewidget.$input.focus();
+ }
}
-}));
+});
instance.web.form.FieldDate = instance.web.form.FieldDatetime.extend({
template: "FieldDate",
@@ -2130,28 +2417,41 @@ instance.web.form.FieldDate = instance.web.form.FieldDatetime.extend({
}
});
-instance.web.form.FieldText = instance.web.form.AbstractField.extend(_.extend({}, instance.web.form.ReinitializeFieldMixin, {
+instance.web.form.FieldText = instance.web.form.AbstractField.extend(instance.web.form.ReinitializeFieldMixin, {
template: 'FieldText',
+ init: function (field_manager, node) {
+ this._super(field_manager, node);
+ },
initialize_content: function() {
- this.$textarea = this.$element.find('textarea');
+ var self = this;
+ this.$textarea = this.$el.find('textarea');
+ this.default_height = this.$textarea.css('height');
if (!this.get("effective_readonly")) {
this.$textarea.change(_.bind(function() {
- this.set({'value': instance.web.parse_value(this.$textarea.val(), this)});
+ self.set({'value': instance.web.parse_value(self.$textarea.val(), self)});
}, this));
} else {
this.$textarea.attr('disabled', 'disabled');
}
+ this.$el.keyup(function (e) {
+ if (e.which === $.ui.keyCode.ENTER) {
+ e.stopPropagation();
+ }
+ });
+ this.setupFocus(this.$textarea);
},
set_value: function(value_) {
- this._super.apply(this, arguments);
+ this._super(value_);
this.render_value();
+ $(window).resize();
},
render_value: function() {
var show_value = instance.web.format_value(this.get('value'), this, '');
- this.$textarea.val(show_value);
- if (show_value && this.view.options.resize_textareas) {
- this.do_resize(this.view.options.resize_textareas);
+ if (show_value === '') {
+ this.$textarea.css('height', parseInt(this.default_height)+"px");
}
+ this.$textarea.val(show_value);
+ this.$textarea.autosize();
},
is_syntax_valid: function() {
if (!this.get("effective_readonly")) {
@@ -2167,51 +2467,87 @@ instance.web.form.FieldText = instance.web.form.AbstractField.extend(_.extend({}
is_false: function() {
return this.get('value') === '' || this._super();
},
- focus: function($element) {
- this.delay_focus(this.$textarea);
- },
- do_resize: function(max_height) {
- max_height = parseInt(max_height, 10);
- var $input = this.$textarea,
- $div = $('<div style="position: absolute; z-index: 1000; top: 0"/>').width($input.width()),
- new_height;
- $div.text($input.val());
- _.each('font-family,font-size,white-space'.split(','), function(style) {
- $div.css(style, $input.css(style));
- });
- $div.appendTo($('body'));
- new_height = $div.height();
- if (new_height < 90) {
- new_height = 90;
+ focus: function($el) {
+ this.$textarea.focus();
+ },
+});
+
+/**
+ * FieldTextHtml Widget
+ * Intended for FieldText widgets meant to display HTML content. This
+ * widget will instantiate the CLEditor (see cleditor in static/src/lib)
+ * To find more information about CLEditor configutation: go to
+ * http://premiumsoftware.net/cleditor/docs/GettingStarted.html
+ */
+instance.web.form.FieldTextHtml = instance.web.form.AbstractField.extend(instance.web.form.ReinitializeFieldMixin, {
+ template: 'FieldTextHtml',
+ init: function() {
+ this._super.apply(this, arguments);
+ },
+ initialize_content: function() {
+ var self = this;
+ if (! this.get("effective_readonly")) {
+ self._updating_editor = false;
+ this.$textarea = this.$el.find('textarea');
+ var width = ((this.node.attrs || {}).editor_width || 468);
+ var height = ((this.node.attrs || {}).editor_height || 250);
+ this.$textarea.cleditor({
+ width: width, // width not including margins, borders or padding
+ height: height, // height not including margins, borders or padding
+ controls: // controls to add to the toolbar
+ "bold italic underline strikethrough " +
+ "| removeformat | bullets numbering | outdent " +
+ "indent | link unlink | source",
+ bodyStyle: // style to assign to document body contained within the editor
+ "margin:4px; font:12px monospace; cursor:text; color:#1F1F1F"
+ });
+ this.$cleditor = this.$textarea.cleditor()[0];
+ this.$cleditor.change(function() {
+ if (! self._updating_editor) {
+ self.$cleditor.updateTextArea();
+ self.set({'value': self.$textarea.val()});
+ }
+ });
}
- if (!isNaN(max_height) && new_height > max_height) {
- new_height = max_height;
+ },
+ set_value: function(value_) {
+ this._super.apply(this, arguments);
+ this.render_value();
+ },
+ render_value: function() {
+ if (! this.get("effective_readonly")) {
+ this.$textarea.val(this.get('value') || '');
+ this._updating_editor = true;
+ this.$cleditor.updateFrame();
+ this._updating_editor = false;
+ } else {
+ this.$el.html(this.get('value'));
}
- $div.remove();
- $input.height(new_height);
},
-}));
+});
instance.web.form.FieldBoolean = instance.web.form.AbstractField.extend({
template: 'FieldBoolean',
start: function() {
+ var self = this;
this._super.apply(this, arguments);
- this.$checkbox = $("input", this.$element);
- this.$element.click(_.bind(function() {
+ this.$checkbox = $("input", this.$el);
+ this.setupFocus(this.$checkbox);
+ this.$el.click(_.bind(function() {
this.set({'value': this.$checkbox.is(':checked')});
}, this));
var check_readonly = function() {
- this.$checkbox.prop('disabled', this.get("effective_readonly"));
+ self.$checkbox.prop('disabled', self.get("effective_readonly"));
};
this.on("change:effective_readonly", this, check_readonly);
- _.bind(check_readonly, this)();
+ check_readonly.call(this);
},
set_value: function(value_) {
this._super.apply(this, arguments);
this.$checkbox[0].checked = value_;
},
focus: function() {
- this.delay_focus(this.$checkbox);
+ this.$checkbox.focus();
}
});
@@ -2219,7 +2555,7 @@ instance.web.form.FieldProgressBar = instance.web.form.AbstractField.extend({
template: 'FieldProgressBar',
start: function() {
this._super.apply(this, arguments);
- this.$element.progressbar({
+ this.$el.progressbar({
value: this.get('value'),
disabled: this.get("effective_readonly")
});
@@ -2231,15 +2567,12 @@ instance.web.form.FieldProgressBar = instance.web.form.AbstractField.extend({
show_value = 0;
}
var formatted_value = instance.web.format_value(show_value, { type : 'float' }, '0');
- this.$element.progressbar('option', 'value', show_value).find('span').html(formatted_value + '%');
+ this.$el.progressbar('option', 'value', show_value).find('span').html(formatted_value + '%');
}
});
-instance.web.form.FieldTextXml = instance.web.form.AbstractField.extend({
-// to replace view editor
-});
-instance.web.form.FieldSelection = instance.web.form.AbstractField.extend(_.extend({}, instance.web.form.ReinitializeFieldMixin, {
+instance.web.form.FieldSelection = instance.web.form.AbstractField.extend(instance.web.form.ReinitializeFieldMixin, {
template: 'FieldSelection',
init: function(field_manager, node) {
var self = this;
@@ -2265,9 +2598,9 @@ instance.web.form.FieldSelection = instance.web.form.AbstractField.extend(_.exte
// changing the selected value), takes the action as validating the
// row
var ischanging = false;
- this.$element.find('select')
+ var $select = this.$el.find('select')
.change(_.bind(function() {
- this.set({'value': this.values[this.$element.find('select')[0].selectedIndex][0]});
+ this.set({'value': this.values[this.$el.find('select')[0].selectedIndex][0]});
}, this))
.change(function () { ischanging = true; })
.click(function () { ischanging = false; })
@@ -2276,6 +2609,7 @@ instance.web.form.FieldSelection = instance.web.form.AbstractField.extend(_.exte
e.stopPropagation();
ischanging = false;
});
+ this.setupFocus($select);
},
set_value: function(value_) {
value_ = value_ === null ? false : value_;
@@ -2289,27 +2623,27 @@ instance.web.form.FieldSelection = instance.web.form.AbstractField.extend(_.exte
for (var i = 0, ii = this.values.length; i < ii; i++) {
if (this.values[i][0] === this.get('value')) index = i;
}
- this.$element.find('select')[0].selectedIndex = index;
+ this.$el.find('select')[0].selectedIndex = index;
} else {
var self = this;
var option = _(this.values)
- .detect(function (record) { return record[0] === self.get('value'); });
- this.$element.text(option ? option[1] : this.values[0][1]);
+ .detect(function (record) { return record[0] === self.get('value'); });
+ this.$el.text(option ? option[1] : this.values[0][1]);
}
},
is_syntax_valid: function() {
if (this.get("effective_readonly")) {
return true;
}
- var value_ = this.values[this.$element.find('select')[0].selectedIndex];
+ var value_ = this.values[this.$el.find('select')[0].selectedIndex];
return !! value_;
},
focus: function() {
- this.delay_focus(this.$element.find('select:first'));
+ this.$el.find('select:first').focus();
}
-}));
+});
-// jquery autocomplete tweak to allow html
+// jquery autocomplete tweak to allow html and classnames
(function() {
var proto = $.ui.autocomplete.prototype,
initSource = proto._initSource;
@@ -2336,7 +2670,8 @@ instance.web.form.FieldSelection = instance.web.form.AbstractField.extend(_.exte
return $( "<li></li>" )
.data( "item.autocomplete", item )
.append( $( "<a></a>" )[ this.options.html ? "html" : "text" ]( item.label ) )
- .appendTo( ul );
+ .appendTo( ul )
+ .addClass(item.classname);
}
});
})();
@@ -2357,43 +2692,56 @@ instance.web.form.CompletionFieldMixin = {
var dataset = new instance.web.DataSet(this, this.field.relation, self.build_context());
var blacklist = this.get_search_blacklist();
+ this.last_query = search_val;
return this.orderer.add(dataset.name_search(
search_val, new instance.web.CompoundDomain(self.build_domain(), [["id", "not in", blacklist]]),
- 'ilike', this.limit + 1)).pipe(function(data) {
+ 'ilike', this.limit + 1, self.build_context())).pipe(function(data) {
self.last_search = data;
// possible selections for the m2o
var values = _.map(data, function(x) {
+ x[1] = x[1].split("\n")[0];
return {
label: _.str.escapeHTML(x[1]),
- value:x[1],
- name:x[1],
- id:x[0]
+ value: x[1],
+ name: x[1],
+ id: x[0],
};
});
// search more... if more results that max
if (values.length > self.limit) {
values = values.slice(0, self.limit);
- values.push({label: _t("<em>   Search More...</em>"), action: function() {
- dataset.name_search(search_val, self.build_domain(), 'ilike'
- , false, function(data) {
- self._search_create_popup("search", data);
- });
- }});
+ values.push({
+ label: _t("Search More..."),
+ action: function() {
+ dataset.name_search(search_val, self.build_domain(), 'ilike', false).then(function(data) {
+ self._search_create_popup("search", data);
+ });
+ },
+ classname: 'oe_m2o_dropdown_option'
+ });
}
// quick create
var raw_result = _(data.result).map(function(x) {return x[1];});
if (search_val.length > 0 && !_.include(raw_result, search_val)) {
- values.push({label: _.str.sprintf(_t('<em>   Create "<strong>%s</strong>"</em>'),
- $('<span />').text(search_val).html()), action: function() {
- self._quick_create(search_val);
- }});
+ values.push({
+ label: _.str.sprintf(_t('Create "<strong>%s</strong>"'),
+ $('<span />').text(search_val).html()),
+ action: function() {
+ self._quick_create(search_val);
+ },
+ classname: 'oe_m2o_dropdown_option'
+ });
}
// create...
- values.push({label: _t("<em>   Create and Edit...</em>"), action: function() {
- self._search_create_popup("form", undefined, {"default_name": search_val});
- }});
+ 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;
});
@@ -2404,11 +2752,11 @@ instance.web.form.CompletionFieldMixin = {
_quick_create: function(name) {
var self = this;
var slow_create = function () {
- self._search_create_popup("form", undefined, {"default_name": name});
+ self._search_create_popup("form", undefined, self._create_context(name));
};
- if (self.get_definition_options().quick_create === undefined || self.get_definition_options().quick_create) {
+ if (self.options.quick_create === undefined || self.options.quick_create) {
new instance.web.DataSet(this, this.field.relation, self.build_context())
- .name_create(name, function(data) {
+ .name_create(name).then(function(data) {
self.add_id(data[0]);
}).fail(function(error, event) {
event.preventDefault();
@@ -2424,7 +2772,7 @@ instance.web.form.CompletionFieldMixin = {
pop.select_element(
self.field.relation,
{
- title: (view === 'search' ? _t("Search: ") : _t("Create: ")) + (this.string || this.name),
+ title: (view === 'search' ? _t("Search: ") : _t("Create: ")) + this.string,
initial_ids: ids ? _.map(ids, function(x) {return x[0]}) : undefined,
initial_view: view,
disable_multiple_selection: true
@@ -2434,16 +2782,51 @@ instance.web.form.CompletionFieldMixin = {
);
pop.on_select_elements.add(function(element_ids) {
self.add_id(element_ids[0]);
+ self.focus();
});
},
/**
* To implement.
*/
add_id: function(id) {},
+ _create_context: function(name) {
+ var tmp = {};
+ var field = (this.options || {}).create_name_field;
+ if (field === undefined)
+ field = "name";
+ if (field !== false && name && (this.options || {}).quick_create !== false)
+ tmp["default_" + field] = name;
+ return tmp;
+ },
};
-instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(_.extend({}, instance.web.form.ReinitializeFieldMixin,
- instance.web.form.CompletionFieldMixin, {
+instance.web.form.M2ODialog = instance.web.Dialog.extend({
+ template: "M2ODialog",
+ init: function(parent) {
+ this._super(parent, {
+ title: _.str.sprintf(_t("Add %s"), parent.string),
+ width: 312,
+ });
+ },
+ start: function() {
+ var self = this;
+ this.$buttons.html(QWeb.render("M2ODialog.buttons"));
+ this.$("input").val(this.getParent().last_query);
+ this.$buttons.find(".oe_form_m2o_qc_button").click(function(){
+ self.getParent()._quick_create(self.$("input").val());
+ self.destroy();
+ });
+ this.$buttons.find(".oe_form_m2o_sc_button").click(function(){
+ self.getParent()._search_create_popup("form", undefined, self.getParent()._create_context(self.$("input").val()));
+ self.destroy();
+ });
+ this.$buttons.find(".oe_form_m2o_cancel_button").click(function(){
+ self.destroy();
+ });
+ },
+});
+
+instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instance.web.form.CompletionFieldMixin, instance.web.form.ReinitializeFieldMixin, {
template: "FieldMany2One",
init: function(field_manager, node) {
this._super(field_manager, node);
@@ -2453,6 +2836,7 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(_.exten
this.last_search = [];
this.floating = false;
this.inhibit_on_change = false;
+ this.current_display = null;
},
start: function() {
this._super();
@@ -2461,29 +2845,43 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(_.exten
this.floating = false;
this.render_value();
});
+ instance.web.bus.on('click', this, function() {
+ if (!this.get("effective_readonly") && this.$input && this.$input.autocomplete('widget').is(':visible')) {
+ this.$input.autocomplete("close");
+ }
+ });
},
initialize_content: function() {
if (!this.get("effective_readonly"))
this.render_editable();
this.render_value();
},
+ init_error_displayer: function() {
+ // nothing
+ },
+ hide_error_displayer: function() {
+ // doesn't work
+ },
+ show_error_displayer: function() {
+ new instance.web.form.M2ODialog(this).open();
+ },
render_editable: function() {
var self = this;
- this.$input = this.$element.find("input");
-
- self.$input.tipsy({
- title: function() {
- return "No element was selected, you should create or select one from the dropdown list.";
- },
- trigger:'manual',
- fade: true,
+ this.$input = this.$el.find("input");
+
+ this.init_error_displayer();
+
+ self.$input.on('focus', function() {
+ self.hide_error_displayer();
});
-
- this.$drop_down = this.$element.find(".oe-m2o-drop-down-button");
- this.$follow_button = $(".oe-m2o-cm-button", this.$element);
-
- this.$follow_button.click(function() {
+
+ this.$drop_down = this.$el.find(".oe_m2o_drop_down_button");
+ this.$follow_button = $(".oe_m2o_cm_button", this.$el);
+
+ this.$follow_button.click(function(ev) {
+ ev.preventDefault();
if (!self.get('value')) {
+ self.focus();
return;
}
var pop = new instance.web.form.FormOpenPopup(self.view);
@@ -2492,40 +2890,48 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(_.exten
self.get("value"),
self.build_context(),
{
- title: _t("Open: ") + (self.string || self.name)
+ title: _t("Open: ") + self.string
}
);
pop.on_write_completed.add_last(function() {
self.display_value = {};
self.render_value();
+ self.focus();
});
});
// some behavior for input
- this.$input.keyup(function() {
- if (self.$input.val() === "") {
- self.set({value: false});
- } else {
- self.floating = true;
+ var input_changed = function() {
+ if (self.current_display !== self.$input.val()) {
+ self.current_display = self.$input.val();
+ if (self.$input.val() === "") {
+ self.set({value: false});
+ self.floating = false;
+ } else {
+ self.floating = true;
+ }
}
- });
+ };
+ this.$input.keydown(input_changed);
+ this.$input.change(input_changed);
this.$drop_down.click(function() {
if (self.$input.autocomplete("widget").is(":visible")) {
self.$input.autocomplete("close");
+ self.$input.focus();
} else {
if (self.get("value") && ! self.floating) {
self.$input.autocomplete("search", "");
} else {
self.$input.autocomplete("search");
}
- self.$input.focus();
}
});
- var tip_def = $.Deferred();
- var untip_def = $.Deferred();
- var tip_delay = 200;
- var tip_duration = 3000;
+ self.ed_def = $.Deferred();
+ self.uned_def = $.Deferred();
+ var ed_delay = 200;
+ var ed_duration = 15000;
var anyoneLoosesFocus = function() {
+ var used = false;
if (self.floating) {
if (self.last_search.length > 0) {
if (self.last_search[0][0] != self.get("value")) {
@@ -2533,33 +2939,52 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(_.exten
self.display_value["" + self.last_search[0][0]] = self.last_search[0][1];
self.set({value: self.last_search[0][0]});
} else {
+ used = true;
self.render_value();
}
} else {
+ used = true;
self.set({value: false});
+ self.render_value();
}
+ self.floating = false;
}
- if (! self.get("value")) {
- tip_def.reject();
- untip_def.reject();
- tip_def = $.Deferred();
- tip_def.then(function() {
- self.$input.tipsy("show");
+ if (used && self.get("value") === false && ! self.no_ed) {
+ self.ed_def.reject();
+ self.uned_def.reject();
+ self.ed_def = $.Deferred();
+ self.ed_def.then(function() {
+ self.show_error_displayer();
});
setTimeout(function() {
- tip_def.resolve();
- untip_def.reject();
- untip_def = $.Deferred();
- untip_def.then(function() {
- self.$input.tipsy("hide");
+ self.ed_def.resolve();
+ self.uned_def.reject();
+ self.uned_def = $.Deferred();
+ self.uned_def.then(function() {
+ self.hide_error_displayer();
});
- setTimeout(function() {untip_def.resolve();}, tip_duration);
- }, tip_delay);
+ setTimeout(function() {self.uned_def.resolve();}, ed_duration);
+ }, ed_delay);
} else {
- tip_def.reject();
+ self.no_ed = false;
+ self.ed_def.reject();
}
};
- this.$input.focusout(anyoneLoosesFocus);
+ var ignore_blur = false;
+ this.$input.on({
+ focusout: anyoneLoosesFocus,
+ focus: function () { self.trigger('focused'); },
+ autocompleteopen: function () { ignore_blur = true; },
+ autocompleteclose: function () { ignore_blur = false; },
+ blur: function () {
+ // autocomplete open
+ if (ignore_blur) { return; }
+ if (_(self.getChildren()).any(function (child) {
+ return child instanceof instance.web.form.AbstractFormPopup;
+ })) { return; }
+ self.trigger('blurred');
+ }
+ });
var isSelecting = false;
// autocomplete
@@ -2577,8 +3002,9 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(_.exten
self.display_value["" + item.id] = item.name;
self.set({value: item.id});
} else if (item.action) {
- self.floating = true;
item.action();
+ // Cancel widget blurring, to avoid form blur event
+ self.trigger('focused');
return false;
}
},
@@ -2586,21 +3012,22 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(_.exten
e.preventDefault();
},
html: true,
- close: anyoneLoosesFocus,
+ // disabled to solve a bug, but may cause others
+ //close: anyoneLoosesFocus,
minLength: 0,
delay: 0
});
this.$input.autocomplete("widget").addClass("openerp");
// used to correct a bug when selecting an element by pushing 'enter' in an editable list
this.$input.keyup(function(e) {
- if (e.which === 13) {
+ if (e.which === 13) { // ENTER
if (isSelecting)
e.stopPropagation();
}
isSelecting = false;
});
+ this.setupFocus(this.$follow_button);
},
-
render_value: function(no_recurse) {
var self = this;
if (! this.get("value")) {
@@ -2613,8 +3040,8 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(_.exten
return;
}
if (! no_recurse) {
- var dataset = new instance.web.DataSetStatic(this, this.field.relation, self.view.dataset.get_context());
- dataset.name_get([self.get("value")], function(data) {
+ var dataset = new instance.web.DataSetStatic(this, this.field.relation, self.build_context());
+ dataset.name_get([self.get("value")]).then(function(data) {
self.display_value["" + self.get("value")] = data[0][1];
self.render_value(true);
});
@@ -2623,12 +3050,24 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(_.exten
display_string: function(str) {
var self = this;
if (!this.get("effective_readonly")) {
- this.$input.val(str);
+ this.$input.val(str.split("\n")[0]);
+ this.current_display = this.$input.val();
+ if(this.is_false()){
+ this.$('.oe_m2o_cm_button').css({'display':'none'});
+ }
} else {
- this.$element.find('a')
+ var lines = _.escape(str).split("\n");
+ var link = "";
+ var follow = "";
+ link = lines[0];
+ follow = _.rest(lines).join("<br />");
+ if (follow)
+ link += "<br />";
+ var $link = this.$el.find('.oe_form_uri')
.unbind('click')
- .text(str)
- .click(function () {
+ .html(link);
+ if (! this.options.no_open)
+ $link.click(function () {
self.do_action({
type: 'ir.actions.act_window',
res_model: self.field.relation,
@@ -2639,13 +3078,16 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(_.exten
});
return false;
});
+ $(".oe_form_m2o_follow", this.$el).html(follow);
}
},
set_value: function(value_) {
var self = this;
if (value_ instanceof Array) {
this.display_value = {};
- this.display_value["" + value_[0]] = value_[1];
+ if (! this.options.always_reload) {
+ this.display_value["" + value_[0]] = value_[1];
+ }
value_ = value_[0];
}
value_ = value_ || false;
@@ -2653,6 +3095,9 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(_.exten
this._super(value_);
this.inhibit_on_change = false;
},
+ get_displayed: function() {
+ return this.display_value["" + this.get("value")];
+ },
add_id: function(id) {
this.display_value = {};
this.set({value: id});
@@ -2661,9 +3106,19 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(_.exten
return ! this.get("value");
},
focus: function () {
- this.delay_focus(this.$input);
- }
-}));
+ this.$input.focus();
+ },
+ _quick_create: function() {
+ this.no_ed = true;
+ this.ed_def.reject();
+ return instance.web.form.CompletionFieldMixin._quick_create.apply(this, arguments);
+ },
+ _search_create_popup: function() {
+ this.no_ed = true;
+ this.ed_def.reject();
+ return instance.web.form.CompletionFieldMixin._search_create_popup.apply(this, arguments);
+ },
+});
/*
# Values: (0, 0, { fields }) create
@@ -2724,6 +3179,7 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
},
start: function() {
this._super.apply(this, arguments);
+ this.$el.addClass('oe_form_field oe_form_field_one2many');
var self = this;
@@ -2731,7 +3187,6 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
this.dataset.o2m = this;
this.dataset.parent_view = this.view;
this.dataset.child_name = this.name;
- //this.dataset.child_name =
this.dataset.on_change.add_last(function() {
self.trigger_on_change();
});
@@ -2758,11 +3213,18 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
},
load_views: function() {
var self = this;
-
+
var modes = this.node.attrs.mode;
modes = !!modes ? modes.split(",") : ["tree"];
var views = [];
_.each(modes, function(mode) {
+ if (! _.include(["list", "tree", "graph", "kanban"], mode)) {
+ try {
+ throw new Error(_.str.sprintf("View type '%s' is not supported in One2Many.", mode));
+ } catch(e) {
+ instance.webclient.crashmanager.on_javascript_exception(e)
+ }
+ }
var view = {
view_id: false,
view_type: mode == "tree" ? "list" : mode,
@@ -2772,23 +3234,37 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
view.embedded_view = self.field.views[mode];
}
if(view.view_type === "list") {
- view.options.selectable = self.multi_selection;
+ _.extend(view.options, {
+ addable: null,
+ selectable: self.multi_selection,
+ sortable: false,
+ import_enabled: false,
+ deletable: true
+ });
if (self.get("effective_readonly")) {
- view.options.addable = null;
- view.options.deletable = null;
+ _.extend(view.options, {
+ deletable: null,
+ reorderable: false,
+ });
}
} else if (view.view_type === "form") {
if (self.get("effective_readonly")) {
view.view_type = 'form';
}
- view.options.not_interactible_on_create = true;
+ _.extend(view.options, {
+ not_interactible_on_create: true,
+ });
} else if (view.view_type === "kanban") {
- view.options.confirm_on_delete = false;
+ _.extend(view.options, {
+ confirm_on_delete: false,
+ });
if (self.get("effective_readonly")) {
- view.options.action_buttons = false;
- view.options.quick_creatable = false;
- view.options.creatable = false;
- view.options.read_only_mode = true;
+ _.extend(view.options, {
+ action_buttons: false,
+ quick_creatable: false,
+ creatable: false,
+ read_only_mode: true,
+ });
}
}
views.push(view);
@@ -2806,11 +3282,14 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
this.viewmanager.on_controller_inited.add_last(function(view_type, controller) {
controller.o2m = self;
if (view_type == "list") {
- if (self.get("effective_readonly"))
- controller.set_editable(false);
+ if (self.get("effective_readonly")) {
+ controller.on('edit:before', self, function (e) {
+ e.cancel = true;
+ });
+ }
} else if (view_type === "form") {
if (self.get("effective_readonly")) {
- $(".oe_form_buttons", controller.$element).children().remove();
+ $(".oe_form_buttons", controller.$el).children().remove();
}
controller.on_record_loaded.add_last(function() {
once.resolve();
@@ -2831,7 +3310,7 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
});
this.is_setted.then(function() {
$.async_when().then(function () {
- self.viewmanager.appendTo(self.$element);
+ self.viewmanager.appendTo(self.$el);
});
});
return def;
@@ -2913,6 +3392,7 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
this.dataset.index = 0;
}
self.is_setted.resolve();
+ this.trigger_on_change();
return self.reload_current_view();
},
get_value: function() {
@@ -2939,42 +3419,44 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
save_any_view: function() {
if (this.doing_on_change)
return false;
- return this.session.synchronized_mode(_.bind(function() {
- if (this.viewmanager && this.viewmanager.views && this.viewmanager.active_view &&
- this.viewmanager.views[this.viewmanager.active_view] &&
- this.viewmanager.views[this.viewmanager.active_view].controller) {
- var view = this.viewmanager.views[this.viewmanager.active_view].controller;
- if (this.viewmanager.active_view === "form") {
- if (!view.is_initialized.isResolved()) {
- return false;
- }
- var res = $.when(view.do_save());
- if (!res.isResolved() && !res.isRejected()) {
- console.warn("Asynchronous get_value() is not supported in form view.");
- }
- return res;
- } else if (this.viewmanager.active_view === "list") {
- var res = $.when(view.ensure_saved());
- if (!res.isResolved() && !res.isRejected()) {
- console.warn("Asynchronous get_value() is not supported in list view.");
- }
- return res;
- }
- }
- return false;
- }, this));
+ return this.session.synchronized_mode(_.bind(function() {
+ if (this.viewmanager && this.viewmanager.views && this.viewmanager.active_view &&
+ this.viewmanager.views[this.viewmanager.active_view] &&
+ this.viewmanager.views[this.viewmanager.active_view].controller) {
+ var view = this.viewmanager.views[this.viewmanager.active_view].controller;
+ if (this.viewmanager.active_view === "form") {
+ if (!view.is_initialized.isResolved()) {
+ return false;
+ }
+ var res = $.when(view.do_save());
+ if (!res.isResolved() && !res.isRejected()) {
+ console.warn("Asynchronous get_value() is not supported in form view.");
+ }
+ return res;
+ } else if (this.viewmanager.active_view === "list") {
+ var res = $.when(view.ensure_saved());
+ if (!res.isResolved() && !res.isRejected()) {
+ console.warn("Asynchronous get_value() is not supported in list view.");
+ }
+ return res;
+ }
+ }
+ return false;
+ }, this));
},
is_syntax_valid: function() {
- if (!this.viewmanager.views[this.viewmanager.active_view])
+ if (! this.viewmanager || ! this.viewmanager.views[this.viewmanager.active_view])
return true;
var view = this.viewmanager.views[this.viewmanager.active_view].controller;
- if (this.viewmanager.active_view === "form") {
- for (var f in view.fields) {
- f = view.fields[f];
- if (!f.is_valid()) {
- return false;
- }
- }
+ switch (this.viewmanager.active_view) {
+ case 'form':
+ return _(view.fields).chain()
+ .invoke('is_valid')
+ .all(_.identity)
+ .value();
+ break;
+ case 'list':
+ return view.is_valid();
}
return true;
},
@@ -2989,6 +3471,7 @@ instance.web.form.One2ManyViewManager = instance.web.ViewManager.extend({
form: 'instance.web.form.One2ManyFormView',
kanban: 'instance.web.form.One2ManyKanbanView',
});
+ this.__ignore_blur = false;
},
switch_view: function(mode, unused) {
if (mode !== 'form') {
@@ -2998,7 +3481,7 @@ instance.web.form.One2ManyViewManager = instance.web.ViewManager.extend({
var id = self.o2m.dataset.index !== null ? self.o2m.dataset.ids[self.o2m.dataset.index] : null;
var pop = new instance.web.form.FormOpenPopup(self.o2m.view);
pop.show_element(self.o2m.field.relation, id, self.o2m.build_context(), {
- title: _t("Open: ") + self.name,
+ title: _t("Open: ") + self.o2m.string,
create_function: function(data) {
return self.o2m.dataset.create(data).then(function(r) {
self.o2m.dataset.set_ids(self.o2m.dataset.ids.concat([r.result]));
@@ -3034,8 +3517,51 @@ instance.web.form.One2ManyDataSet = instance.web.BufferedDataSet.extend({
instance.web.form.One2ManyListView = instance.web.ListView.extend({
_template: 'One2Many.listview',
+ init: function (parent, dataset, view_id, options) {
+ this._super(parent, dataset, view_id, _.extend(options || {}, {
+ 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'));
+
+ this.records
+ .bind('add', this.proxy("changed_records"))
+ .bind('edit', this.proxy("changed_records"))
+ .bind('remove', this.proxy("changed_records"));
+ },
+ start: function () {
+ var ret = this._super();
+ this.$el
+ .off('mousedown.handleButtons')
+ .on('mousedown.handleButtons', 'table button', this.proxy('_button_down'));
+ return ret;
+ },
+ changed_records: function () {
+ this.o2m.trigger_on_change();
+ },
+ is_valid: function () {
+ var form = this.editor.form;
+
+ // If the form has not been modified, the view can only be valid
+ // NB: is_dirty will also be set on defaults/onchanges/whatever?
+ // oe_form_dirty seems to only be set on actual user actions
+ if (!form.$el.is('.oe_form_dirty')) {
+ return true;
+ }
+ this.o2m._dirty_flag = true;
+
+ // Otherwise validate internal form
+ return _(form.fields).chain()
+ .invoke(function () {
+ this._check_css_flags();
+ return this.is_valid();
+ })
+ .all(_.identity)
+ .value();
+ },
do_add_record: function () {
- if (this.options.editable) {
+ if (this.editable()) {
this._super.apply(this, arguments);
} else {
var self = this;
@@ -3043,7 +3569,7 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({
pop.select_element(
self.o2m.field.relation,
{
- title: _t("Create: ") + self.name,
+ title: _t("Create: ") + self.o2m.string,
initial_view: "form",
alternative_form_view: self.o2m.field.views ? self.o2m.field.views["form"] : undefined,
create_function: function(data, callback, error_callback) {
@@ -3071,7 +3597,7 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({
var self = this;
var pop = new instance.web.form.FormOpenPopup(self.o2m.view);
pop.show_element(self.o2m.field.relation, id, self.o2m.build_context(), {
- title: _t("Open: ") + self.name,
+ title: _t("Open: ") + self.o2m.string,
write_function: function(id, data) {
return self.o2m.dataset.write(id, data, {}, function(r) {
self.o2m.reload_current_view();
@@ -3084,13 +3610,147 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({
return self.o2m.dataset.read_ids.apply(self.o2m.dataset, arguments);
},
form_view_options: {'not_interactible_on_create':true},
- readonly: self.o2m.get("effective_readonly")
+ readonly: !this.is_action_enabled('edit') || self.o2m.get("effective_readonly")
});
},
do_button_action: function (name, id, callback) {
+ if (!_.isNumber(id)) {
+ instance.webclient.notification.warn(
+ _t("Action Button"),
+ _t("The o2m record must be saved before an action can be used"));
+ return;
+ }
+ var parent_form = this.o2m.view;
+ var self = this;
+ this.ensure_saved().pipe(function () {
+ return parent_form.do_save();
+ }).then(function () {
+ self.handle_button(name, id, callback);
+ });
+ },
+
+ _before_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
+ this.editor.form.widgetFocused();
+ },
+ _before_unedit: function () {
+ this.editor.form.off('blurred', this, this._on_form_blur);
+ },
+ _button_down: function () {
+ // If a button is clicked (usually some sort of action button), it's
+ // the button's responsibility to ensure the editable list is in the
+ // correct state -> ignore form blurring
+ this.__ignore_blur = true;
+ },
+ /**
+ * Handles blurring of the nested form (saves the currently edited row),
+ * unless the flag to ignore the event is set to ``true``
+ *
+ * Makes the internal form go away
+ */
+ _on_form_blur: function () {
+ if (this.__ignore_blur) {
+ this.__ignore_blur = false;
+ return;
+ }
+ // FIXME: why isn't there an API for this?
+ if (this.editor.form.$el.hasClass('oe_form_dirty')) {
+ this.save_edition();
+ return;
+ }
+ this.cancel_edition();
+ },
+ keyup_ENTER: function () {
+ // blurring caused by hitting the [Return] key, should skip the
+ // autosave-on-blur and let the handler for [Return] do its thing (save
+ // the current row *anyway*, then create a new one/edit the next one)
+ this.__ignore_blur = true;
+ this._super.apply(this, arguments);
+ },
+ do_delete: function (ids) {
+ var self = this;
+ var next = $.when();
+ var _super = this._super;
+ // handle deletion of an item which does not exist
+ // TODO: better handle that in the editable list?
+ var false_id_index = _(ids).indexOf(false);
+ if (false_id_index !== -1) {
+ ids.splice(false_id_index, 1);
+ next = this.cancel_edition(true);
+ }
+ return next.pipe(function () {
+ // wheeee
+ var confirm = window.confirm;
+ window.confirm = function () { return true; };
+ try {
+ return _super.call(self, ids);
+ } finally {
+ window.confirm = confirm;
+ }
+ });
+ }
+});
+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 def = $.Deferred().then(callback).then(function() {self.o2m.view.reload();});
- return this._super(name, id, _.bind(def.resolve, def));
+ 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 a row"))
+ .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().then(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)
+ }
}
});
@@ -3119,8 +3779,7 @@ instance.web.form.One2ManyKanbanView = instance.web_kanban.KanbanView.extend({
});
}
-instance.web.form.FieldMany2ManyTags = instance.web.form.AbstractField.extend(_.extend({}, instance.web.form.CompletionFieldMixin,
- instance.web.form.ReinitializeFieldMixin, {
+instance.web.form.FieldMany2ManyTags = instance.web.form.AbstractField.extend(instance.web.form.CompletionFieldMixin, instance.web.form.ReinitializeFieldMixin, {
template: "FieldMany2ManyTags",
init: function() {
this._super.apply(this, arguments);
@@ -3138,7 +3797,7 @@ instance.web.form.FieldMany2ManyTags = instance.web.form.AbstractField.extend(_.
if (this.get("effective_readonly"))
return;
var self = this;
- self.$text = $("textarea", this.$element);
+ self.$text = $("textarea", this.$el);
self.$text.textext({
plugins : 'tags arrow autocomplete',
autocomplete: {
@@ -3196,10 +3855,10 @@ instance.web.form.FieldMany2ManyTags = instance.web.form.AbstractField.extend(_.
self._drop_shown = true;
});
self.tags = self.$text.textext()[0].tags();
- $("textarea", this.$element).focusout(function() {
+ $("textarea", this.$el).focusout(function() {
self.$text.trigger("setInputData", "");
}).keydown(function(e) {
- if (event.keyCode === 9 && self._drop_shown) {
+ if (e.which === $.ui.keyCode.TAB && self._drop_shown) {
self.$text.textext()[0].autocomplete().selectFromDropdown();
}
});
@@ -3221,22 +3880,23 @@ instance.web.form.FieldMany2ManyTags = instance.web.form.AbstractField.extend(_.
render_value: function() {
var self = this;
var dataset = new instance.web.DataSetStatic(this, this.field.relation, self.view.dataset.get_context());
+ var values = self.get("value")
var handle_names = function(data) {
var indexed = {};
_.each(data, function(el) {
indexed[el[0]] = el;
});
- data = _.map(self.get("value"), function(el) { return indexed[el]; });
+ data = _.map(values, function(el) { return indexed[el]; });
if (! self.get("effective_readonly")) {
self.tags.containerElement().children().remove();
- $("textarea", self.$element).css("padding-left", "3px");
+ $("textarea", self.$el).css("padding-left", "3px");
self.tags.addTags(_.map(data, function(el) {return {name: el[1], id:el[0]};}));
} else {
- self.$element.html(QWeb.render("FieldMany2ManyTags.box", {elements: data}));
+ self.$el.html(QWeb.render("FieldMany2ManyTag", {elements: data}));
}
};
- if (! self.get('values') || self.get('values').length > 0) {
- this._display_orderer.add(dataset.name_get(self.get("value"))).then(handle_names);
+ if (! values || values.length > 0) {
+ this._display_orderer.add(dataset.name_get(values)).then(handle_names);
} else {
handle_names([]);
}
@@ -3244,7 +3904,7 @@ instance.web.form.FieldMany2ManyTags = instance.web.form.AbstractField.extend(_.
add_id: function(id) {
this.set({'value': _.uniq(this.get('value').concat([id]))});
},
-}));
+});
/*
* TODO niv: clean those deferred stuff, it could be better
@@ -3260,6 +3920,7 @@ instance.web.form.FieldMany2Many = instance.web.form.AbstractField.extend({
},
start: function() {
this._super.apply(this, arguments);
+ this.$el.addClass('oe_form_field oe_form_field_many2many');
var self = this;
@@ -3268,7 +3929,7 @@ instance.web.form.FieldMany2Many = instance.web.form.AbstractField.extend({
this.dataset.on_unlink.add_last(function(ids) {
self.dataset_changed();
});
-
+
this.is_setted.then(function() {
self.load_view();
});
@@ -3281,7 +3942,7 @@ instance.web.form.FieldMany2Many = instance.web.form.AbstractField.extend({
});
});
});
- })
+ });
},
set_value: function(value_) {
value_ = value_ || [];
@@ -3297,12 +3958,19 @@ instance.web.form.FieldMany2Many = instance.web.form.AbstractField.extend({
get_value: function() {
return [commands.replace_with(this.get('value'))];
},
+
+ is_false: function () {
+ return _(this.dataset.ids).isEmpty();
+ },
load_view: function() {
var self = this;
this.list_view = new instance.web.form.Many2ManyListView(this, this.dataset, false, {
'addable': self.get("effective_readonly") ? null : _t("Add"),
'deletable': self.get("effective_readonly") ? false : true,
'selectable': self.multi_selection,
+ 'sortable': false,
+ 'reorderable': false,
+ 'import_enabled': false,
});
var embedded = (this.field.views || {}).tree;
if (embedded) {
@@ -3315,7 +3983,7 @@ instance.web.form.FieldMany2Many = instance.web.form.AbstractField.extend({
loaded.resolve();
});
$.async_when().then(function () {
- self.list_view.appendTo(self.$element);
+ self.list_view.appendTo(self.$el);
});
return loaded;
},
@@ -3347,7 +4015,7 @@ instance.web.form.Many2ManyListView = instance.web.ListView.extend(/** @lends in
pop.select_element(
this.model,
{
- title: _t("Add: ") + this.name
+ title: _t("Add: ") + this.m2m_field.string
},
new instance.web.CompoundDomain(this.m2m_field.build_domain(), ["!", ["id", "in", this.m2m_field.dataset.ids]]),
this.m2m_field.build_context()
@@ -3367,7 +4035,7 @@ instance.web.form.Many2ManyListView = instance.web.ListView.extend(/** @lends in
var self = this;
var pop = new instance.web.form.FormOpenPopup(this);
pop.show_element(this.dataset.model, id, this.m2m_field.build_context(), {
- title: _t("Open: ") + this.name,
+ title: _t("Open: ") + this.m2m_field.string,
readonly: this.getParent().get("effective_readonly")
});
pop.on_write_completed.add_last(function() {
@@ -3376,7 +4044,7 @@ instance.web.form.Many2ManyListView = instance.web.ListView.extend(/** @lends in
}
});
-instance.web.form.FieldMany2ManyKanban = instance.web.form.AbstractField.extend(_.extend({}, instance.web.form.CompletionFieldMixin, {
+instance.web.form.FieldMany2ManyKanban = instance.web.form.AbstractField.extend(instance.web.form.CompletionFieldMixin, {
disable_utility_classes: true,
init: function(field_manager, node) {
this._super(field_manager, node);
@@ -3396,7 +4064,7 @@ instance.web.form.FieldMany2ManyKanban = instance.web.form.AbstractField.extend(
this.dataset.on_unlink.add_last(function(ids) {
self.dataset_changed();
});
-
+
this.is_setted.then(function() {
self.load_view();
});
@@ -3443,7 +4111,7 @@ instance.web.form.FieldMany2ManyKanban = instance.web.form.AbstractField.extend(
});
this.kanban_view.do_switch_view.add_last(_.bind(this.open_popup, this));
$.async_when().then(function () {
- self.kanban_view.appendTo(self.$element);
+ self.kanban_view.appendTo(self.$el);
});
return loaded;
},
@@ -3465,7 +4133,7 @@ instance.web.form.FieldMany2ManyKanban = instance.web.form.AbstractField.extend(
pop.select_element(
this.field.relation,
{
- title: _t("Add: ") + this.name
+ title: _t("Add: ") + this.string
},
new instance.web.CompoundDomain(this.build_domain(), ["!", ["id", "in", this.dataset.ids]]),
this.build_context()
@@ -3483,7 +4151,7 @@ instance.web.form.FieldMany2ManyKanban = instance.web.form.AbstractField.extend(
var id = self.dataset.ids[self.dataset.index];
var pop = new instance.web.form.FormOpenPopup(self.view);
pop.show_element(self.field.relation, id, self.build_context(), {
- title: _t("Open: ") + self.name,
+ title: _t("Open: ") + self.string,
write_function: function(id, data, options) {
return self.dataset.write(id, data, {}).then(function() {
self.reload_content();
@@ -3499,7 +4167,7 @@ instance.web.form.FieldMany2ManyKanban = instance.web.form.AbstractField.extend(
add_id: function(id) {
this.quick_create.add_id(id);
},
-}));
+});
function m2m_kanban_lazy_init() {
if (instance.web.form.Many2ManyKanbanView)
@@ -3512,7 +4180,7 @@ instance.web.form.Many2ManyKanbanView = instance.web_kanban.KanbanView.extend({
});
instance.web.form.Many2ManyQuickCreate = instance.web.Widget.extend({
template: 'Many2ManyKanban.quick_create',
-
+
/**
* close_btn: If true, the widget will display a "Close" button able to trigger
* a "close" event.
@@ -3527,7 +4195,7 @@ instance.web.form.Many2ManyQuickCreate = instance.web.Widget.extend({
},
start: function () {
var self = this;
- self.$text = this.$element.find('input').css("width", "200px");
+ self.$text = this.$el.find('input').css("width", "200px");
self.$text.textext({
plugins : 'arrow autocomplete',
autocomplete: {
@@ -3584,7 +4252,7 @@ instance.web.form.Many2ManyQuickCreate = instance.web.Widget.extend({
/**
* Class with everything which is common between FormOpenPopup and SelectCreatePopup.
*/
-instance.web.form.AbstractFormPopup = instance.web.OldWidget.extend({
+instance.web.form.AbstractFormPopup = instance.web.Widget.extend({
template: "AbstractFormPopup.render",
/**
* options:
@@ -3627,14 +4295,16 @@ instance.web.form.AbstractFormPopup = instance.web.OldWidget.extend({
display_popup: function() {
var self = this;
this.renderElement();
- new instance.web.Dialog(this, {
- width: '90%',
+ var dialog = new instance.web.Dialog(this, {
min_width: '800px',
+ dialogClass: 'oe_act_window',
close: function() {
self.check_exit(true);
},
title: this.options.title || "",
- }, this.$element).open();
+ buttons: [{text:"tmp"}],
+ }, this.$el).open();
+ this.$buttonpane = dialog.$el.dialog("widget").find(".ui-dialog-buttonpane").html("");
this.start();
},
on_write_completed: function() {},
@@ -3650,16 +4320,21 @@ instance.web.form.AbstractFormPopup = instance.web.OldWidget.extend({
if (this.row_id !== null) {
options.initial_mode = this.options.readonly ? "view" : "edit";
}
+ _.extend(options, {
+ $buttons: this.$buttonpane,
+ });
this.view_form = new instance.web.FormView(this, this.dataset, false, options);
if (this.options.alternative_form_view) {
this.view_form.set_embedded_view(this.options.alternative_form_view);
}
- this.view_form.appendTo(this.$element.find(".oe-form-view-popup-form-placeholder"));
+ this.view_form.appendTo(this.$el.find(".oe_popup_form"));
this.view_form.on_loaded.add_last(function() {
- var $buttons = self.view_form.$element.find(".oe_form_buttons");
var multi_select = self.row_id === null && ! self.options.disable_multiple_selection;
- $buttons.html(QWeb.render("AbstractFormPopup.buttons", {multi_select: multi_select}));
- var $snbutton = $buttons.find(".oe_abstractformpopup-form-save-new");
+ self.$buttonpane.html(QWeb.render("AbstractFormPopup.buttons", {
+ multi_select: multi_select,
+ readonly: self.row_id !== null && self.options.readonly,
+ }));
+ var $snbutton = self.$buttonpane.find(".oe_abstractformpopup-form-save-new");
$snbutton.click(function() {
$.when(self.view_form.do_save()).then(function() {
self.view_form.reload_mutex.exec(function() {
@@ -3667,7 +4342,7 @@ instance.web.form.AbstractFormPopup = instance.web.OldWidget.extend({
});
});
});
- var $sbutton = $buttons.find(".oe_abstractformpopup-form-save");
+ var $sbutton = self.$buttonpane.find(".oe_abstractformpopup-form-save");
$sbutton.click(function() {
$.when(self.view_form.do_save()).then(function() {
self.view_form.reload_mutex.exec(function() {
@@ -3675,15 +4350,10 @@ instance.web.form.AbstractFormPopup = instance.web.OldWidget.extend({
});
});
});
- var $cbutton = $buttons.find(".oe_abstractformpopup-form-close");
+ var $cbutton = self.$buttonpane.find(".oe_abstractformpopup-form-close");
$cbutton.click(function() {
self.check_exit();
});
- if (self.row_id !== null && self.options.readonly) {
- $snbutton.hide();
- $sbutton.hide();
- $cbutton.text(_t("Close"));
- }
self.view_form.do_show();
});
},
@@ -3697,7 +4367,7 @@ instance.web.form.AbstractFormPopup = instance.web.OldWidget.extend({
this.destroy();
},
destroy: function () {
- this.$element.dialog('close');
+ this.$el.dialog('close');
this._super();
},
});
@@ -3782,32 +4452,32 @@ instance.web.form.SelectCreatePopup = instance.web.form.AbstractFormPopup.extend
self.dataset, false,
_.extend({'deletable': false,
'selectable': !self.options.disable_multiple_selection,
- 'read_only': true,
+ 'import_enabled': false,
+ '$buttons': self.$buttonpane,
}, self.options.list_view_options || {}));
+ self.view_list.on('edit:before', self, function (e) {
+ e.cancel = true;
+ });
self.view_list.popup = self;
- self.view_list.appendTo($(".oe-select-create-popup-view-list", self.$element)).pipe(function() {
+ self.view_list.appendTo($(".oe_popup_list", self.$el)).pipe(function() {
self.view_list.do_show();
}).pipe(function() {
self.searchview.do_search();
});
self.view_list.on_loaded.add_last(function() {
- var $buttons = self.view_list.$element.find(".oe-actions");
- $buttons.prepend(QWeb.render("SelectCreatePopup.search.buttons"));
- var $cbutton = $buttons.find(".oe_selectcreatepopup-search-close");
+ self.$buttonpane.html(QWeb.render("SelectCreatePopup.search.buttons", {widget:self}));
+ var $cbutton = self.$buttonpane.find(".oe_selectcreatepopup-search-close");
$cbutton.click(function() {
self.destroy();
});
- var $sbutton = $buttons.find(".oe_selectcreatepopup-search-select");
- if(self.options.disable_multiple_selection) {
- $sbutton.hide();
- }
+ var $sbutton = self.$buttonpane.find(".oe_selectcreatepopup-search-select");
$sbutton.click(function() {
self.on_select_elements(self.selected_ids);
self.destroy();
});
});
});
- this.searchview.appendTo($(".oe-select-create-popup-view-list", self.$element));
+ this.searchview.appendTo($(".oe_popup_list", self.$el));
},
do_search: function(domains, contexts, groupbys) {
var self = this;
@@ -3820,11 +4490,12 @@ instance.web.form.SelectCreatePopup = instance.web.form.AbstractFormPopup.extend
});
},
on_click_element: function(ids) {
+ var self = this;
this.selected_ids = ids || [];
if(this.selected_ids.length > 0) {
- this.$element.find(".oe_selectcreatepopup-search-select").removeAttr('disabled');
+ self.$buttonpane.find(".oe_selectcreatepopup-search-select").removeAttr('disabled');
} else {
- this.$element.find(".oe_selectcreatepopup-search-select").attr('disabled', "disabled");
+ self.$buttonpane.find(".oe_selectcreatepopup-search-select").attr('disabled', "disabled");
}
},
new_object: function() {
@@ -3832,7 +4503,7 @@ instance.web.form.SelectCreatePopup = instance.web.form.AbstractFormPopup.extend
this.searchview.hide();
}
if (this.view_list) {
- this.view_list.$element.hide();
+ this.view_list.$el.hide();
}
this.setup_form_view();
},
@@ -3852,7 +4523,7 @@ instance.web.form.SelectCreateListView = instance.web.ListView.extend({
}
});
-instance.web.form.FieldReference = instance.web.form.AbstractField.extend(_.extend({}, instance.web.form.ReinitializeFieldMixin, {
+instance.web.form.FieldReference = instance.web.form.AbstractField.extend(instance.web.form.ReinitializeFieldMixin, {
template: 'FieldReference',
init: function(field_manager, node) {
this._super(field_manager, node);
@@ -3865,7 +4536,7 @@ instance.web.form.FieldReference = instance.web.form.AbstractField.extend(_.exte
var sel = this.selection.get_value();
this.m2o.field.relation = sel;
this.m2o.set_value(false);
- this.m2o.$element.toggle(sel !== false);
+ this.m2o.$el.toggle(sel !== false);
}
},
destroy_content: function() {
@@ -3879,25 +4550,32 @@ instance.web.form.FieldReference = instance.web.form.AbstractField.extend(_.exte
}
},
initialize_content: function() {
+ var self = this;
this.selection = new instance.web.form.FieldSelection(this, { attrs: {
- name: 'selection'
+ name: 'selection',
+ modifiers: JSON.stringify({readonly: this.get('effective_readonly')}),
}});
this.selection.view = this.view;
- this.selection.set({force_readonly: this.get('effective_readonly')});
this.selection.on("change:value", this, this.on_selection_changed);
- this.selection.$element = $(".oe_form_view_reference_selection", this.$element);
+ this.selection.setElement(this.$(".oe_form_view_reference_selection"));
this.selection.renderElement();
this.selection.start();
+ this.selection
+ .on('focused', null, function () {self.trigger('focused')})
+ .on('blurred', null, function () {self.trigger('blurred')});
this.m2o = new instance.web.form.FieldMany2One(this, { attrs: {
- name: 'm2o'
+ name: 'm2o',
+ modifiers: JSON.stringify({readonly: this.get('effective_readonly')}),
}});
this.m2o.view = this.view;
- this.m2o.set({force_readonly: this.get("effective_readonly")});
this.m2o.on("change:value", this, this.data_changed);
- this.m2o.$element = $(".oe_form_view_reference_m2o", this.$element);
+ this.m2o.setElement(this.$(".oe_form_view_reference_m2o"));
this.m2o.renderElement();
this.m2o.start();
+ this.m2o
+ .on('focused', null, function () {self.trigger('focused')})
+ .on('blurred', null, function () {self.trigger('blurred')});
},
is_false: function() {
return typeof(this.get_value()) !== 'string';
@@ -3944,18 +4622,27 @@ instance.web.form.FieldReference = instance.web.form.AbstractField.extend(_.exte
}
throw Exception("Should not happen");
},
-}));
+});
-instance.web.form.FieldBinary = instance.web.form.AbstractField.extend(_.extend({}, instance.web.form.ReinitializeFieldMixin, {
+instance.web.form.FieldBinary = instance.web.form.AbstractField.extend(instance.web.form.ReinitializeFieldMixin, {
init: function(field_manager, node) {
+ var self = this;
this._super(field_manager, node);
- this.iframe = this.element_id + '_iframe';
this.binary_value = false;
+ this.fileupload_id = _.uniqueId('oe_fileupload');
+ $(window).on(this.fileupload_id, function() {
+ var args = [].slice.call(arguments).slice(1);
+ self.on_file_uploaded.apply(self, args);
+ });
+ },
+ stop: function() {
+ $(window).off(this.fileupload_id);
+ this._super.apply(this, arguments);
},
initialize_content: function() {
- this.$element.find('input.oe-binary-file').change(this.on_file_change);
- this.$element.find('button.oe-binary-file-save').click(this.on_save_as);
- this.$element.find('.oe-binary-file-clear').click(this.on_clear);
+ this.$el.find('input.oe_form_binary_file').change(this.on_file_change);
+ this.$el.find('button.oe_form_binary_file_save').click(this.on_save_as);
+ this.$el.find('.oe_form_binary_file_clear').click(this.on_clear);
},
human_filesize : function(size) {
var units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
@@ -3970,42 +4657,62 @@ instance.web.form.FieldBinary = instance.web.form.AbstractField.extend(_.extend(
// TODO: on modern browsers, we could directly read the file locally on client ready to be used on image cropper
// http://www.html5rocks.com/tutorials/file/dndfiles/
// http://deepliquid.com/projects/Jcrop/demos.php?demo=handler
- window[this.iframe] = this.on_file_uploaded;
- if ($(e.target).val() != '') {
- this.$element.find('form.oe-binary-form input[name=session_id]').val(this.session.session_id);
- this.$element.find('form.oe-binary-form').submit();
- this.$element.find('.oe-binary-progress').show();
- this.$element.find('.oe-binary').hide();
+
+ if ($(e.target).val() !== '') {
+ this.$el.find('form.oe_form_binary_form input[name=session_id]').val(this.session.session_id);
+ this.$el.find('form.oe_form_binary_form').submit();
+ this.$el.find('.oe_form_binary_progress').show();
+ this.$el.find('.oe_form_binary').hide();
}
},
on_file_uploaded: function(size, name, content_type, file_base64) {
- delete(window[this.iframe]);
if (size === false) {
this.do_warn("File Upload", "There was a problem while uploading your file");
// TODO: use openerp web crashmanager
console.warn("Error while uploading file : ", name);
} else {
+ this.filename = name;
this.on_file_uploaded_and_valid.apply(this, arguments);
}
- this.$element.find('.oe-binary-progress').hide();
- this.$element.find('.oe-binary').show();
+ this.$el.find('.oe_form_binary_progress').hide();
+ this.$el.find('.oe_form_binary').show();
},
on_file_uploaded_and_valid: function(size, name, content_type, file_base64) {
},
- on_save_as: function() {
- $.blockUI();
- this.session.get_file({
- url: '/web/binary/saveas_ajax',
- data: {data: JSON.stringify({
- model: this.view.dataset.model,
- id: (this.view.datarecord.id || ''),
- field: this.name,
- filename_field: (this.node.attrs.filename || ''),
- context: this.view.dataset.get_context()
- })},
- complete: $.unblockUI,
- error: instance.webclient.crashmanager.on_rpc_error
- });
+ on_save_as: function(ev) {
+ var value = this.get('value');
+ if (!value) {
+ this.do_warn(_t("Save As..."), _t("The field is empty, there's nothing to save !"));
+ ev.stopPropagation();
+ } else if (this._dirty_flag) {
+ var link = this.$('.oe_form_binary_file_save_data')[0];
+ link.download = this.filename || "download.bin"; // Works on only on Google Chrome
+ //link.target = '_blank';
+ link.href = "data:application/octet-stream;base64," + value;
+ } else {
+ instance.web.blockUI();
+ this.session.get_file({
+ url: '/web/binary/saveas_ajax',
+ data: {data: JSON.stringify({
+ model: this.view.dataset.model,
+ id: (this.view.datarecord.id || ''),
+ field: this.name,
+ filename_field: (this.node.attrs.filename || ''),
+ context: this.view.dataset.get_context()
+ })},
+ complete: instance.web.unblockUI,
+ error: instance.webclient.crashmanager.on_rpc_error
+ });
+ ev.stopPropagation();
+ return false;
+ }
+ },
+ set_filename: function(value) {
+ var filename = this.node.attrs.filename;
+ if (this.view.fields[filename]) {
+ this.view.fields[filename].set_value(value);
+ this.view.fields[filename].on_ui_change();
+ }
},
on_clear: function() {
if (this.get('value') !== false) {
@@ -4014,7 +4721,7 @@ instance.web.form.FieldBinary = instance.web.form.AbstractField.extend(_.extend(
}
return false;
}
-}));
+});
instance.web.form.FieldBinaryFile = instance.web.form.FieldBinary.extend({
template: 'FieldBinaryFile',
@@ -4022,7 +4729,7 @@ instance.web.form.FieldBinaryFile = instance.web.form.FieldBinary.extend({
this._super();
if (this.get("effective_readonly")) {
var self = this;
- this.$element.find('a').click(function() {
+ this.$el.find('a').click(function() {
if (self.get('value')) {
self.on_save_as();
}
@@ -4042,12 +4749,12 @@ instance.web.form.FieldBinaryFile = instance.web.form.FieldBinary.extend({
} else {
show_value = (this.get('value') != null && this.get('value') !== false) ? this.get('value') : '';
}
- this.$element.find('input').eq(0).val(show_value);
+ this.$el.find('input').eq(0).val(show_value);
} else {
- this.$element.find('a').show(!!this.get('value'));
+ this.$el.find('a').show(!!this.get('value'));
if (this.get('value')) {
var show_value = _t("Download") + " " + (this.view.datarecord[this.node.attrs.filename] || '');
- this.$element.find('a').text(show_value);
+ this.$el.find('a').text(show_value);
}
}
},
@@ -4055,7 +4762,7 @@ instance.web.form.FieldBinaryFile = instance.web.form.FieldBinary.extend({
this.binary_value = true;
this.set({'value': file_base64});
var show_value = name + " (" + this.human_filesize(size) + ")";
- this.$element.find('input').eq(0).val(show_value);
+ this.$el.find('input').eq(0).val(show_value);
this.set_filename(name);
},
set_filename: function(value_) {
@@ -4066,38 +4773,43 @@ instance.web.form.FieldBinaryFile = instance.web.form.FieldBinary.extend({
},
on_clear: function() {
this._super.apply(this, arguments);
- this.$element.find('input').eq(0).val('');
+ this.$el.find('input').eq(0).val('');
this.set_filename('');
}
});
instance.web.form.FieldBinaryImage = instance.web.form.FieldBinary.extend({
template: 'FieldBinaryImage',
- initialize_content: function() {
- this._super();
- if (!this.get("effective_readonly")) {
- this.$element.find('.oe_form_field_image_controls').show();
- } else {
- this.$element.find('.oe_form_field_image_controls').hide();
- }
- },
set_value: function(value_) {
this._super.apply(this, arguments);
this.render_value();
},
render_value: function() {
+ var self = this;
var url;
- if (this.get('value') && this.get('value').substr(0, 10).indexOf(' ') == -1) {
+ if (this.get('value') && ! /^\d+(\.\d*)? \w+$/.test(this.get('value'))) {
url = 'data:image/png;base64,' + this.get('value');
} else if (this.get('value')) {
+ var id = escape(JSON.stringify(this.view.datarecord.id || null));
+ var field = this.name;
+ if (this.options.preview_image)
+ field = this.options.preview_image;
url = '/web/binary/image?session_id=' + this.session.session_id + '&model=' +
- this.view.dataset.model +'&id=' + (this.view.datarecord.id || '') + '&field=' + this.name + '&t=' + (new Date().getTime());
+ this.view.dataset.model +'&id=' + id + '&field=' + field + '&t=' + (new Date().getTime());
} else {
url = "/web/static/src/img/placeholder.png";
}
- var img = QWeb.render("FieldBinaryImage-img", { widget: this, url: url });
- this.$element.find('> img').remove();
- this.$element.prepend(img);
+ var $img = $(QWeb.render("FieldBinaryImage-img", { widget: this, url: url }));
+ this.$el.find('> img').remove();
+ this.$el.prepend($img);
+ $img.load(function() {
+ if (! self.options.size)
+ return;
+ $img.css("max-width", "" + self.options.size[0] + "px");
+ $img.css("max-height", "" + self.options.size[1] + "px");
+ $img.css("margin-left", "" + (self.options.size[0] - $img.width()) / 2 + "px");
+ $img.css("margin-top", "" + (self.options.size[1] - $img.height()) / 2 + "px");
+ });
},
on_file_change: function() {
this.render_value();
@@ -4115,13 +4827,23 @@ instance.web.form.FieldBinaryImage = instance.web.form.FieldBinary.extend({
});
instance.web.form.FieldStatus = instance.web.form.AbstractField.extend({
- tagName: "span",
+ template: "FieldStatus",
+ init: function(field_manager, node) {
+ this._super(field_manager, node);
+ this.options.clickable = this.options.clickable || (this.node.attrs || {}).clickable || false;
+ this.options.visible = this.options.visible || (this.node.attrs || {}).statusbar_visible || false;
+ this.selected_value = null;
+ },
start: function() {
this._super();
- this.selected_value = null;
- // preview in start only for selection fields, because of the dynamic behavior of many2one fields.
- if (this.field.type in ['selection']) {
- this.render_list();
+ // backward compatibility
+ this.loaded = new $.Deferred();
+ if (this.options.clickable) {
+ this.$el.on('click','li',this.on_click_stage);
+ }
+ // TODO move the following into css :after
+ if (this.$el.parent().is('header')) {
+ this.$el.after('<div class="oe_clear"/>');
}
},
set_value: function(value_) {
@@ -4137,97 +4859,73 @@ instance.web.form.FieldStatus = instance.web.form.AbstractField.extend({
}
// trick to be sure all values are loaded in the form, therefore
// enabling the evaluation of dynamic domains
+ self.selection = [];
$.async_when().then(function() {
- return self.render_list();
+ self.get_selection();
});
+ return this.loaded;
},
-
- /** Get the status list and render them
- * to_show: [[identifier, value_to_display]] where
- * - identifier = key for a selection, id for a many2one
- * - display_val = label that will be displayed
- * - ex: [[0, "New"]] (many2one) or [["new", "In Progress"]] (selection)
- */
- render_list: function() {
- var self = this;
- // get selection values, filter them and render them
- var selection_done = this.get_selection().pipe(self.proxy('filter_selection')).pipe(self.proxy('render_elements'));
- },
-
- /** Get the selection list to be displayed in the statusbar widget.
+ /** Get the selection and render it
+ * selection: [[identifier, value_to_display], ...]
* For selection fields: this is directly given by this.field.selection
- * For many2one fields :
- * - perform a search on the relation of the many2one field (given by
- * field.relation )
- * - get the field domain for the search
- * - self.build_domain() gives the domain given by the view or by
- * the field
- * - if the optional statusbar_fold attribute is set to true, make
- * an AND with build_domain to hide all 'fold=true' columns
- * - make an OR with current value, to be sure it is displayed,
- * with the correct order, even if it is folded
+ * For many2one fields: perform a search on the relation of the many2one field
*/
get_selection: function() {
var self = this;
if (this.field.type == "many2one") {
- this.selection = [];
- // get fold information from widget
- var fold = ((this.node.attrs || {}).statusbar_fold || true);
- // build final domain: if fold option required, add the
- if (fold == true) {
- var domain = new instance.web.CompoundDomain(['|'], ['&'], self.build_domain(), [['fold', '=', false]], [['id', '=', self.selected_value]]);
- } else {
- var domain = new instance.web.CompoundDomain(['|'], self.build_domain(), [['id', '=', self.selected_value]]);
+ var domain = [];
+ if(!_.isEmpty(this.field.domain) || !_.isEmpty(this.node.attrs.domain)) {
+ domain = new instance.web.CompoundDomain(['|'], self.build_domain(), [['id', '=', self.selected_value]]);
}
- // get a DataSetSearch on the current field relation (ex: crm.lead.stage_id -> crm.case.stage)
- var model_ext = new instance.web.DataSetSearch(this, this.field.relation, self.build_context(), domain);
- // fetch selection
- var read_defer = model_ext.read_slice(['name'], {}).pipe( function (records) {
- _(records).each(function (record) {
- self.selection.push([record.id, record.name]);
- });
+ var ds = new instance.web.DataSetSearch(this, this.field.relation, self.build_context(), domain);
+ ds.read_slice(['name'], {}).done( function (records) {
+ for(var i = 0; i < records.length; i++) {
+ self.selection.push([records[i].id, records[i].name]);
+ }
+ self.render_elements();
+ self.loaded.resolve();
});
} else {
- this.selection = this.field.selection;
- var read_defer = new $.Deferred().resolve();
- }
- return read_defer;
- },
-
- /** Filters this.selection, according to values coming from the statusbar_visible
- * attribute of the field. For example: statusbar_visible="draft,open"
- * Currently, the key of (key, label) pairs has to be used in the
- * selection of visible items. This feature is not meant to be used
- * with many2one fields.
- */
- filter_selection: function() {
- var self = this;
- var shown = _.map(((this.node.attrs || {}).statusbar_visible || "").split(","),
- function(x) { return _.str.trim(x); });
- shown = _.select(shown, function(x) { return x.length > 0; });
-
- if (shown.length == 0) {
- this.to_show = this.selection;
- } else {
- this.to_show = _.select(this.selection, function(x) {
- return _.indexOf(shown, x[0]) !== -1 || x[0] === self.selected_value;
- });
+ this.loaded.resolve();
+ // For field type selection filter values according to
+ // statusbar_visible attribute of the field. For example:
+ // statusbar_visible="draft,open".
+ var selection = this.field.selection;
+ for(var i=0; i< selection.length; i++) {
+ var key = selection[i][0];
+ if(key == this.selected_value || !this.options.visible || this.options.visible.indexOf(key) != -1) {
+ this.selection.push(selection[i]);
+ }
+ }
+ this.render_elements();
}
},
-
/** Renders the widget. This function also checks for statusbar_colors='{"pending": "blue"}'
* attribute in the widget. This allows to set a given color to a given
* state (given by the key of (key, label)).
*/
render_elements: function () {
- var content = instance.web.qweb.render("FieldStatus.content", {widget: this, _:_});
- this.$element.html(content);
-
+ var self = this;
+ var content = instance.web.qweb.render("FieldStatus.content", {widget: this});
+ this.$el.html(content);
var colors = JSON.parse((this.node.attrs || {}).statusbar_colors || "{}");
var color = colors[this.selected_value];
if (color) {
- var elem = this.$element.find("li.oe_form_steps_active span");
- elem.css("color", color);
+ this.$("oe_active").css("color", color);
+ }
+ },
+ on_click_stage: function (ev) {
+ var self = this;
+ var $li = $(ev.currentTarget);
+ var val = parseInt($li.data("id"));
+ if (val != self.selected_value) {
+ this.view.recursive_save().then(function() {
+ var change = {};
+ change[self.name] = val;
+ self.view.dataset.write(self.view.datarecord.id, change).then(function() {
+ self.view.reload();
+ });
+ });
}
},
});
@@ -4235,7 +4933,7 @@ instance.web.form.FieldStatus = instance.web.form.AbstractField.extend({
/**
* Registry of form fields, called by :js:`instance.web.FormView`.
*
- * All referenced classes must implement FieldMixin. Those represent the classes whose instances
+ * All referenced classes must implement FieldInterface. Those represent the classes whose instances
* will substitute to the <field> tags as defined in OpenERP's views.
*/
instance.web.form.widgets = new instance.web.Registry({
@@ -4244,6 +4942,7 @@ instance.web.form.widgets = new instance.web.Registry({
'email' : 'instance.web.form.FieldEmail',
'url' : 'instance.web.form.FieldUrl',
'text' : 'instance.web.form.FieldText',
+ 'html' : 'instance.web.form.FieldTextHtml',
'date' : 'instance.web.form.FieldDate',
'datetime' : 'instance.web.form.FieldDatetime',
'selection' : 'instance.web.form.FieldSelection',
@@ -4274,6 +4973,9 @@ instance.web.form.tags = new instance.web.Registry({
'button' : 'instance.web.form.WidgetButton',
});
+instance.web.form.custom_widgets = new instance.web.Registry({
+});
+
};
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js
index de73ec21884..fd34ffddb7b 100644
--- a/addons/web/static/src/js/view_list.js
+++ b/addons/web/static/src/js/view_list.js
@@ -21,9 +21,6 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
// whether the view rows can be reordered (via vertical drag & drop)
'reorderable': true,
'action_buttons': true,
- // if true, the view can't be editable, ignoring the view's and the context's
- // instructions
- 'read_only': false,
},
/**
* Core class for list-type displays.
@@ -64,7 +61,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
this.records = new Collection();
- this.set_groups(new instance.web.ListView.Groups(this));
+ this.set_groups(new (this.options.GroupsType)(this));
if (this.dataset instanceof instance.web.DataSetStatic) {
this.groups.datagroup = new instance.web.StaticDataGroup(this.dataset);
@@ -87,6 +84,14 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
this.no_leaf = false;
},
+ set_default_options: function (options) {
+ this._super(options);
+ _.defaults(this.options, {
+ GroupsType: instance.web.ListView.Groups,
+ ListType: instance.web.ListView.List
+ });
+ },
+
/**
* Retrieves the view's number of records per page (|| section)
*
@@ -132,13 +137,13 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
});
},
/**
- * View startup method, the default behavior is to set the ``oe-listview``
+ * View startup method, the default behavior is to set the ``oe_list``
* class on its root element and to perform an RPC load call.
*
* @returns {$.Deferred} loading promise
*/
start: function() {
- this.$element.addClass('oe-listview');
+ this.$el.addClass('oe_list');
return this.reload_view(null, null, true);
},
/**
@@ -241,19 +246,26 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
this.setup_columns(this.fields_view.fields, grouped);
- this.$element.html(QWeb.render(this._template, this));
+ this.$el.html(QWeb.render(this._template, this));
+ this.$el.addClass(this.fields_view.arch.attrs['class']);
+
+ // add css classes that reflect the (absence of) access rights
+ this.$el.toggleClass('oe_list_cannot_create', !this.is_action_enabled('create'))
+ .toggleClass('oe_list_cannot_edit', !this.is_action_enabled('edit'))
+ .toggleClass('oe_list_cannot_delete', !this.is_action_enabled('delete'));
+
// Head hook
// Selecting records
- this.$element.find('.all-record-selector').click(function(){
- self.$element.find('.oe-record-selector input').prop('checked',
- self.$element.find('.all-record-selector').prop('checked') || false);
+ this.$el.find('.oe_list_record_selector').click(function(){
+ self.$el.find('.oe_list_record_selector input').prop('checked',
+ self.$el.find('.oe_list_record_selector').prop('checked') || false);
var selection = self.groups.get_selection();
$(self.groups).trigger(
'selected', [selection.ids, selection.records]);
});
// Sorting columns
- this.$element.find('thead').delegate('th.oe-sortable[data-id]', 'click', function (e) {
+ this.$el.find('thead').delegate('th.oe_sortable[data-id]', 'click', function (e) {
e.stopPropagation();
var $this = $(this);
self.dataset.sort($this.data('id'));
@@ -262,26 +274,22 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
} else {
$this.toggleClass("sortdown");
}
- $this.siblings('.oe-sortable').removeClass("sortup sortdown");
+ $this.siblings('.oe_sortable').removeClass("sortup sortdown");
self.reload_content();
});
- // Add button and Import link
+ // Add button
if (!this.$buttons) {
this.$buttons = $(QWeb.render("ListView.buttons", {'widget':self}));
if (this.options.$buttons) {
this.$buttons.appendTo(this.options.$buttons);
} else {
- this.$element.find('.oe_list_buttons').replaceWith(this.$buttons);
+ this.$el.find('.oe_list_buttons').replaceWith(this.$buttons);
}
this.$buttons.find('.oe_list_add')
.click(this.proxy('do_add_record'))
- .prop('disabled', grouped && this.options.editable);
- this.$buttons.on('click', '.oe_list_button_import', function() {
- self.on_sidebar_import();
- return false;
- });
+ .prop('disabled', grouped);
}
// Pager
@@ -290,7 +298,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
if (this.options.$buttons) {
this.$pager.appendTo(this.options.$pager);
} else {
- this.$element.find('.oe_list_pager').replaceWith(this.$pager);
+ this.$el.find('.oe_list_pager').replaceWith(this.$pager);
}
this.$pager
@@ -314,7 +322,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
self.page = 0;
}
self.reload_content();
- }).find('.oe-pager-state')
+ }).find('.oe_list_pager_state')
.click(function (e) {
e.stopPropagation();
var $this = $(this);
@@ -332,6 +340,8 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
self._limit = (isNaN(val) ? null : val);
self.page = 0;
self.reload_content();
+ }).blur(function() {
+ $(this).trigger('change');
})
.val(self._limit || 'NaN');
});
@@ -341,12 +351,12 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
if (!this.sidebar && this.options.$sidebar) {
this.sidebar = new instance.web.Sidebar(this);
this.sidebar.appendTo(this.options.$sidebar);
- this.sidebar.add_items('other', [
- { label: _t("Import"), callback: this.on_sidebar_import },
+ this.sidebar.add_items('other', _.compact([
{ label: _t("Export"), callback: this.on_sidebar_export },
- { label: _t('Delete'), callback: this.do_delete_selected },
- ]);
+ self.is_action_enabled('delete') && { label: _t('Delete'), callback: this.do_delete_selected }
+ ]));
this.sidebar.add_toolbar(this.fields_view.toolbar);
+ this.sidebar.$el.hide();
}
},
/**
@@ -364,17 +374,20 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
}
var total = dataset.size();
+ var limit = this.limit() || total;
+ this.$pager.toggle(total !== 0);
+ this.$pager.toggleClass('oe_list_pager_single_page', (total <= limit));
var spager = '-';
if (total) {
- var range_start = this.page * this.limit() + 1;
- var range_stop = range_start - 1 + this.limit();
+ var range_start = this.page * limit + 1;
+ var range_stop = range_start - 1 + limit;
if (range_stop > total) {
range_stop = total;
}
spager = _.str.sprintf('%d-%d of %d', range_start, range_stop, total);
}
- this.$pager.find('.oe-pager-state').text(spager);
+ this.$pager.find('.oe_list_pager_state').text(spager);
},
/**
* Sets up the listview's columns: merges view and fields data, move
@@ -385,68 +398,26 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
* @param {Boolean} [grouped] Should the grouping columns (group and count) be displayed
*/
setup_columns: function (fields, grouped) {
- var domain_computer = instance.web.form.compute_domain;
-
- var noop = function () { return {}; };
- var field_to_column = function (field) {
- var name = field.attrs.name;
- var column = _.extend({id: name, tag: field.tag},
- fields[name], field.attrs);
- // modifiers computer
- if (column.modifiers) {
- var modifiers = JSON.parse(column.modifiers);
- column.modifiers_for = function (fields) {
- if (!modifiers.invisible) {
- return {};
- }
- return {
- 'invisible': domain_computer(modifiers.invisible, fields)
- };
- };
- if (modifiers['tree_invisible']) {
- column.invisible = '1';
- } else {
- delete column.invisible;
- }
- } else {
- column.modifiers_for = noop;
- }
- return column;
- };
-
+ var registry = instance.web.list.columns;
+ var reorder = this.options.reorderable;
this.columns.splice(0, this.columns.length);
- this.columns.push.apply(
- this.columns,
- _(this.fields_view.arch.children).map(field_to_column));
+ this.columns.push.apply(this.columns,
+ _(this.fields_view.arch.children).map(function (field) {
+ var id = field.attrs.name;
+ if(field.attrs.widget == 'handle' && !reorder){
+ field.attrs.reorderable = reorder || true;
+ }
+ return registry.for_(id, fields[id], field);
+ }));
if (grouped) {
- this.columns.unshift({
- id: '_group', tag: '', string: _t("Group"), meta: true,
- modifiers_for: function () { return {}; }
- }, {
- id: '_count', tag: '', string: '#', meta: true,
- modifiers_for: function () { return {}; }
- });
+ this.columns.unshift(
+ new instance.web.list.MetaColumn('_group', _t("Group")));
}
this.visible_columns = _.filter(this.columns, function (column) {
- return column.invisible !== '1';
+ return column.invisible !== '1' && !column.reorderable;
});
-
- this.aggregate_columns = _(this.visible_columns)
- .map(function (column) {
- if (column.type !== 'integer' && column.type !== 'float') {
- return {};
- }
- var aggregation_func = column['group_operator'] || 'sum';
- if (!(aggregation_func in column)) {
- return {};
- }
-
- return _.extend({}, column, {
- 'function': aggregation_func,
- label: column[aggregation_func]
- });
- });
+ this.aggregate_columns = _(this.visible_columns).invoke('to_aggregate');
},
/**
* Used to handle a click on a table row, if no other handler caught the
@@ -472,9 +443,6 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
},
do_show: function () {
this._super();
- if (this.sidebar) {
- this.sidebar.$element.show();
- }
if (this.$buttons) {
this.$buttons.show();
}
@@ -484,7 +452,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
},
do_hide: function () {
if (this.sidebar) {
- this.sidebar.$element.hide();
+ this.sidebar.$el.hide();
}
if (this.$buttons) {
this.$buttons.hide();
@@ -508,7 +476,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
if (this.embedded_view) {
return $.Deferred().then(callback).resolve(this.embedded_view);
} else {
- return this.rpc('/web/listview/load', {
+ return this.rpc('/web/view/load', {
model: this.model,
view_id: this.view_id,
view_type: "tree",
@@ -524,10 +492,10 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
*/
reload_content: function () {
var self = this;
- self.$element.find('.all-record-selector').prop('checked', false);
+ self.$el.find('.oe_list_record_selector').prop('checked', false);
this.records.reset();
var reloaded = $.Deferred();
- this.$element.find('.oe-listview-content').append(
+ this.$el.find('.oe_list_content').append(
this.groups.render(function () {
if (self.dataset.index == null) {
var has_one = false;
@@ -548,6 +516,20 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
reload: function () {
return this.reload_content();
},
+ reload_record: function (record) {
+ return this.dataset.read_ids(
+ [record.get('id')],
+ _.pluck(_(this.columns).filter(function (r) {
+ return r.tag === 'field';
+ }), 'name')
+ ).then(function (records) {
+ _(records[0]).each(function (value, key) {
+ record.set(key, value, {silent: true});
+ });
+ record.trigger('change', record);
+ });
+ },
+
do_load_state: function(state, warm) {
var reload = false;
if (state.page && this.page !== state.page) {
@@ -615,7 +597,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
if (!ids.length) {
this.dataset.index = 0;
if (this.sidebar) {
- this.sidebar.$element.hide();
+ this.sidebar.$el.hide();
}
this.compute_aggregates();
return;
@@ -623,7 +605,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
this.dataset.index = _(this.dataset.ids).indexOf(ids[0]);
if (this.sidebar) {
- this.sidebar.$element.show();
+ this.sidebar.$el.show();
}
this.compute_aggregates(_(records).map(function (record) {
@@ -638,6 +620,23 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
* @param {Function} callback should be called after the action is executed, if non-null
*/
do_button_action: function (name, id, callback) {
+ this.handle_button(name, id, callback);
+ },
+ /**
+ * Base handling of buttons, can be called when overriding do_button_action
+ * in order to bypass parent overrides.
+ *
+ * The callback will be provided with the ``id`` as its parameter, in case
+ * handle_button's caller had to alter the ``id`` (or even create one)
+ * while not being ``callback``'s creator.
+ *
+ * This method should not be overridden.
+ *
+ * @param {String} name action name
+ * @param {Object} id id of the record the action should be called on
+ * @param {Function} callback should be called after the action is executed, if non-null
+ */
+ handle_button: function (name, id, callback) {
var action = _.detect(this.columns, function (field) {
return field.name === name;
});
@@ -656,7 +655,8 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
c.add(action.context);
}
action.context = c;
- this.do_execute_action(action, this.dataset, id, callback);
+ this.do_execute_action(
+ action, this.dataset, id, _.bind(callback, null, id));
},
/**
* Handles the activation of a record (clicking on it)
@@ -765,16 +765,14 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
},
display_aggregates: function (aggregation) {
var self = this;
- var $footer_cells = this.$element.find('.oe-list-footer');
+ var $footer_cells = this.$el.find('.oe_list_footer');
_(this.aggregate_columns).each(function (column) {
if (!column['function']) {
return;
}
$footer_cells.filter(_.str.sprintf('[data-field=%s]', column.id))
- .html(instance.web.format_cell(aggregation, column, {
- process_modifiers: false
- }));
+ .html(column.format(aggregation, { process_modifiers: false }));
});
},
get_selected_ids: function() {
@@ -793,7 +791,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
pad_columns: function (count, options) {
options = options || {};
// padding for action/pager header
- var $first_header = this.$element.find('thead tr:first th');
+ var $first_header = this.$el.find('thead tr:first th');
var colspan = $first_header.attr('colspan');
if (colspan) {
if (!this.previous_colspan) {
@@ -802,10 +800,10 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
$first_header.attr('colspan', parseInt(colspan, 10) + count);
}
// Padding for column titles, footer and data rows
- var $rows = this.$element
- .find('.oe-listview-header-columns, tr:not(thead tr)')
+ var $rows = this.$el
+ .find('.oe_list_header_columns, tr:not(thead tr)')
.not(options['except']);
- var newcols = new Array(count+1).join('<td class="oe-listview-padding"></td>');
+ var newcols = new Array(count+1).join('<td class="oe_list_padding"></td>');
if (options.position === 'before') {
$rows.prepend(newcols);
} else {
@@ -816,27 +814,29 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
* Removes all padding columns of the table
*/
unpad_columns: function () {
- this.$element.find('.oe-listview-padding').remove();
+ this.$el.find('.oe_list_padding').remove();
if (this.previous_colspan) {
- this.$element
+ this.$el
.find('thead tr:first th')
.attr('colspan', this.previous_colspan);
this.previous_colspan = null;
}
},
no_result: function () {
- this.$element.find('.oe_view_nocontent').remove();
+ this.$el.find('.oe_view_nocontent').remove();
if (this.groups.group_by
|| !this.options.action
|| !this.options.action.help) {
return;
}
- this.$element.find('table:first').hide();
- this.$element.prepend(
- $('<div class="oe_view_nocontent">')
- .append($('<img>', { src: '/web/static/src/img/view_empty_arrow.png' }))
- .append($('<div>').html(this.options.action.help))
+ this.$el.find('table:first').hide();
+ this.$el.prepend(
+ $('<div class="oe_view_nocontent">').html(this.options.action.help)
);
+ var create_nocontent = this.$buttons;
+ this.$el.find('.oe_view_nocontent').click(function() {
+ create_nocontent.effect('bounce', {distance: 18, times: 5}, 150);
+ });
}
});
instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.ListView.List# */{
@@ -882,52 +882,63 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.
this.record_callbacks = {
'remove': function (event, record) {
- var $row = self.$current.find(
- '[data-id=' + record.get('id') + ']');
+ var $row = self.$current.children(
+ '[data-id=' + record.get('id') + ']');
var index = $row.data('index');
$row.remove();
- self.refresh_zebra(index);
},
'reset': function () { return self.on_records_reset(); },
- 'change': function (event, record) {
- var $row = self.$current.find('[data-id=' + record.get('id') + ']');
+ 'change': function (event, record, attribute, value, old_value) {
+ var $row;
+ if (attribute === 'id') {
+ if (old_value) {
+ throw new Error("Setting 'id' attribute on existing record "
+ + JSON.stringify(record.attributes));
+ }
+ if (!_.contains(self.dataset.ids, value)) {
+ // add record to dataset if not already in (added by
+ // the form view?)
+ self.dataset.ids.splice(
+ self.records.indexOf(record), 0, value);
+ }
+ // Set id on new record
+ $row = self.$current.children('[data-id=false]');
+ } else {
+ $row = self.$current.children(
+ '[data-id=' + record.get('id') + ']');
+ }
$row.replaceWith(self.render_record(record));
},
'add': function (ev, records, record, index) {
- var $new_row = $('<tr>').attr({
- 'data-id': record.get('id')
- });
+ var $new_row = $(self.render_record(record));
if (index === 0) {
$new_row.prependTo(self.$current);
} else {
var previous_record = records.at(index-1),
- $previous_sibling = self.$current.find(
+ $previous_sibling = self.$current.children(
'[data-id=' + previous_record.get('id') + ']');
$new_row.insertAfter($previous_sibling);
}
-
- self.refresh_zebra(index, 1);
}
};
_(this.record_callbacks).each(function (callback, event) {
this.records.bind(event, callback);
}, this);
- this.$_element = $('<tbody>')
- .appendTo(document.body)
- .delegate('th.oe-record-selector', 'click', function (e) {
+ this.$current = $('<tbody>')
+ .delegate('th.oe_list_record_selector', 'click', function (e) {
e.stopPropagation();
var selection = self.get_selection();
$(self).trigger(
'selected', [selection.ids, selection.records]);
})
- .delegate('td.oe-record-delete button', 'click', function (e) {
+ .delegate('td.oe_list_record_delete button', 'click', function (e) {
e.stopPropagation();
var $row = $(e.target).closest('tr');
$(self).trigger('deleted', [[self.row_id($row)]]);
})
- .delegate('td.oe-field-cell button', 'click', function (e) {
+ .delegate('td.oe_list_field_cell button', 'click', function (e) {
e.stopPropagation();
var $target = $(e.currentTarget),
field = $target.closest('td').data('field'),
@@ -938,19 +949,19 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.
// of digits, nice when storing actual numbers, not nice when
// storing strings composed only of digits. Force the action
// name to be a string
- $(self).trigger('action', [field.toString(), record_id, function () {
- return self.reload_record(self.records.get(record_id));
+ $(self).trigger('action', [field.toString(), record_id, function (id) {
+ return self.reload_record(self.records.get(id));
}]);
})
.delegate('a', 'click', function (e) {
e.stopPropagation();
})
.delegate('tr', 'click', function (e) {
- e.stopPropagation();
var row_id = self.row_id(e.currentTarget);
- if (row_id !== undefined) {
+ if (row_id) {
+ e.stopPropagation();
if (!self.dataset.select_id(row_id)) {
- throw "Could not find id in dataset"
+ throw new Error("Could not find id in dataset");
}
self.row_clicked(e);
}
@@ -978,7 +989,7 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.
// to get a correctly displayable value in the field
var model = ref_match[1],
id = parseInt(ref_match[2], 10);
- new instance.web.DataSet(this.view, model).name_get([id], function(names) {
+ new instance.web.DataSet(this.view, model).name_get([id]).then(function(names) {
if (!names.length) { return; }
record.set(column.id, names[0][1]);
});
@@ -994,29 +1005,46 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.
// and let the various registered events handle refreshing the
// row
new instance.web.DataSet(this.view, column.relation)
- .name_get([value], function (names) {
+ .name_get([value]).then(function (names) {
if (!names.length) { return; }
record.set(column.id, names[0]);
});
}
+ } else if (column.type === 'many2many') {
+ value = record.get(column.id);
+ // non-resolved (string) m2m values are arrays
+ if (value instanceof Array && !_.isEmpty(value)) {
+ var ids;
+ // they come in two shapes:
+ if (value[0] instanceof Array) {
+ var command = value[0];
+ // 1. an array of m2m commands (usually (6, false, ids))
+ if (command[0] !== 6) {
+ throw new Error(_t("Unknown m2m command ") + command[0]);
+ }
+ ids = command[2];
+ } else {
+ // 2. an array of ids
+ ids = value;
+ }
+ new instance.web.Model(column.relation)
+ .call('name_get', [ids]).then(function (names) {
+ record.set(column.id, _(names).pluck(1).join(', '));
+ })
+ }
}
- return instance.web.format_cell(record.toForm().data, column, {
+ return column.format(record.toForm().data, {
model: this.dataset.model,
id: record.get('id')
});
},
render: function () {
- var self = this;
- if (this.$current) {
- this.$current.remove();
- }
- this.$current = this.$_element.clone(true);
this.$current.empty().append(
QWeb.render('ListView.rows', _.extend({
render_cell: function () {
return self.render_cell.apply(self, arguments); }
}, this)));
- this.pad_table_to(5);
+ this.pad_table_to(4);
},
pad_table_to: function (count) {
if (this.records.length >= count ||
@@ -1025,20 +1053,20 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.
}
var cells = [];
if (this.options.selectable) {
- cells.push('<th class="oe-record-selector"></td>');
+ cells.push('<th class="oe_list_record_selector"></td>');
}
_(this.columns).each(function(column) {
if (column.invisible === '1') {
return;
}
if (column.tag === 'button') {
- cells.push('<td class="oe-button" title="' + column.string + '">&nbsp;</td>');
+ cells.push('<td class="oe_button" title="' + column.string + '">&nbsp;</td>');
} else {
cells.push('<td title="' + column.string + '">&nbsp;</td>');
}
});
if (this.options.deletable) {
- cells.push('<td class="oe-record-delete"><button type="button" style="visibility: hidden"> </button></td>');
+ cells.push('<td class="oe_list_record_delete"><button type="button" style="visibility: hidden"> </button></td>');
}
cells.unshift('<tr>');
cells.push('</tr>');
@@ -1047,19 +1075,18 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.
this.$current
.children('tr:not([data-id])').remove().end()
.append(new Array(count - this.records.length + 1).join(row));
- this.refresh_zebra(this.records.length);
},
/**
* Gets the ids of all currently selected records, if any
* @returns {Object} object with the keys ``ids`` and ``records``, holding respectively the ids of all selected records and the records themselves.
*/
get_selection: function () {
+ var result = {ids: [], records: []};
if (!this.options.selectable) {
- return [];
+ return result;
}
var records = this.records;
- var result = {ids: [], records: []};
- this.$current.find('th.oe-record-selector input:checked')
+ this.$current.find('th.oe_list_record_selector input:checked')
.closest('tr').each(function () {
var record = records.get($(this).data('id'));
result.ids.push(record.get('id'));
@@ -1087,7 +1114,6 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.
if (!this.$current) { return; }
this.$current.remove();
this.$current = null;
- this.$_element.remove();
},
get_records: function () {
return this.records.map(function (record) {
@@ -1101,17 +1127,7 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.
* @returns {$.Deferred} promise to the finalization of the reloading
*/
reload_record: function (record) {
- return this.dataset.read_ids(
- [record.get('id')],
- _.pluck(_(this.columns).filter(function (r) {
- return r.tag === 'field';
- }), 'name')
- ).then(function (records) {
- _(records[0]).each(function (value, key) {
- record.set(key, value, {silent: true});
- });
- record.trigger('change', record);
- });
+ return this.view.reload_record(record);
},
/**
* Renders a list record to HTML
@@ -1120,6 +1136,7 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.
* @returns {String} QWeb rendering of the selected record
*/
render_record: function (record) {
+ var self = this;
var index = this.records.indexOf(record);
return QWeb.render('ListView.row', {
columns: this.columns,
@@ -1128,26 +1145,7 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.
row_parity: (index % 2 === 0) ? 'even' : 'odd',
view: this.view,
render_cell: function () {
- return this.render_cell.apply(this, arguments); }
- });
- },
- /**
- * Fixes fixes the even/odd classes
- *
- * @param {Number} [from_index] index from which to resequence
- * @param {Number} [offset = 0] selection offset for DOM, in case there are rows to ignore in the table
- */
- refresh_zebra: function (from_index, offset) {
- offset = offset || 0;
- from_index = from_index || 0;
- var dom_offset = offset + from_index;
- var sel = dom_offset ? ':gt(' + (dom_offset - 1) + ')' : null;
- this.$current.children(sel).each(function (i, e) {
- var index = from_index + i;
- // reset record-index accelerators on rows and even/odd
- var even = index%2 === 0;
- $(e).toggleClass('even', even)
- .toggleClass('odd', !even);
+ return self.render_cell.apply(self, arguments); }
});
}
});
@@ -1236,7 +1234,7 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
});
this.$row.children().last()
.append($prev)
- .append('<span class="oe-pager-state"></span>')
+ .append('<span class="oe_list_pager_state"></span>')
.append($next);
},
open: function (point_insertion) {
@@ -1274,7 +1272,7 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
self.records.proxy(group.value).reset();
delete self.children[group.value];
}
- var child = self.children[group.value] = new instance.web.ListView.Groups(self.view, {
+ var child = self.children[group.value] = new (self.view.options.GroupsType)(self.view, {
records: self.records.proxy(group.value),
options: self.options,
columns: self.columns
@@ -1282,7 +1280,7 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
self.bind_child_events(child);
child.datagroup = group;
- var $row = child.$row = $('<tr>');
+ var $row = child.$row = $('<tr class="oe_group_header">');
if (group.openable && group.length) {
$row.click(function (e) {
if (!$row.data('open')) {
@@ -1302,22 +1300,30 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
}
placeholder.appendChild($row[0]);
- var $group_column = $('<th class="oe-group-name">').appendTo($row);
+ var $group_column = $('<th class="oe_list_group_name">').appendTo($row);
// Don't fill this if group_by_no_leaf but no group_by
if (group.grouped_on) {
var row_data = {};
row_data[group.grouped_on] = group;
var group_column = _(self.columns).detect(function (column) {
return column.id === group.grouped_on; });
+ if (! group_column) {
+ throw new Error(_.str.sprintf(
+ _t("Grouping on field '%s' is not possible because that field does not appear in the list view."),
+ group.grouped_on));
+ }
+ var group_label;
try {
- $group_column.html(instance.web.format_cell(
- row_data, group_column, {
- value_if_empty: _t("Undefined"),
- process_modifiers: false
- }));
+ group_label = group_column.format(row_data, {
+ value_if_empty: _t("Undefined"),
+ process_modifiers: false
+ });
} catch (e) {
- $group_column.html(row_data[group_column.id].value);
+ group_label = row_data[group_column.id].value;
}
+ $group_column.text(_.str.sprintf("%s (%d)",
+ group_label, group.length));
+
if (group.length && group.openable) {
// Make openable if not terminal group & group_by_no_leaf
$group_column.prepend('<span class="ui-icon ui-icon-triangle-1-e" style="float: left;">');
@@ -1329,14 +1335,12 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
}
}
self.indent($group_column, group.level);
- // count column
- $('<td>').text(group.length).appendTo($row);
if (self.options.selectable) {
$row.append('<td>');
}
_(self.columns).chain()
- .filter(function (column) {return !column.invisible;})
+ .filter(function (column) { return column.invisible !== '1'; })
.each(function (column) {
if (column.meta) {
// do not do anything
@@ -1344,15 +1348,14 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
var r = {};
r[column.id] = {value: group.aggregates[column.id]};
$('<td class="oe_number">')
- .html(instance.web.format_cell(
- r, column, {process_modifiers: false}))
+ .html(column.format(r, {process_modifiers: false}))
.appendTo($row);
} else {
$row.append('<td>');
}
});
if (self.options.deletable) {
- $row.append('<td class="oe-group-pagination">');
+ $row.append('<td class="oe_list_group_pagination">');
}
});
return placeholder;
@@ -1377,7 +1380,7 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
},
render_dataset: function (dataset) {
var self = this,
- list = new instance.web.ListView.List(this, {
+ list = new (this.view.options.ListType)(this, {
options: this.options,
columns: this.columns,
dataset: dataset,
@@ -1403,11 +1406,11 @@ 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-group-pagination').empty();
+ self.$row.find('td.oe_list_group_pagination').empty();
} else {
var pages = Math.ceil(dataset.size() / limit);
self.$row
- .find('.oe-pager-state')
+ .find('.oe_list_pager_state')
.text(_.str.sprintf(_t("%(page)d/%(page_count)d"), {
page: page + 1,
page_count: pages
@@ -1432,18 +1435,31 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
},
setup_resequence_rows: function (list, dataset) {
// drag and drop enabled if list is not sorted and there is a
- // "sequence" column in the view.
+ // visible column with @widget=handle or "sequence" column in the view.
if ((dataset.sort && dataset.sort())
|| !_(this.columns).any(function (column) {
- return column.name === 'sequence'; })) {
+ return column.widget === 'handle'
+ || column.name === 'sequence'; })) {
return;
}
+ var sequence_field = _(this.columns).find(function (c) {
+ return c.widget === 'handle';
+ });
+ var seqname = sequence_field ? sequence_field.name : 'sequence';
+
// ondrop, move relevant record & fix sequences
list.$current.sortable({
axis: 'y',
items: '> tr[data-id]',
- containment: 'parent',
- helper: 'clone',
+ helper: 'clone'
+ });
+ if (sequence_field) {
+ list.$current.sortable('option', 'handle', '.oe_list_field_handle');
+ }
+ list.$current.sortable('option', {
+ start: function (e, ui) {
+ ui.placeholder.height(ui.item.height());
+ },
stop: function (event, ui) {
var to_move = list.records.get(ui.item.data('id')),
target_id = ui.item.prev().data('id'),
@@ -1461,7 +1477,7 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
var record, index = to,
// if drag to 1st row (to = 0), start sequencing from 0
// (exclusive lower bound)
- seq = to ? list.records.at(to - 1).get('sequence') : 0;
+ seq = to ? list.records.at(to - 1).get(seqname) : 0;
while (++seq, record = list.records.at(index++)) {
// write are independent from one another, so we can just
// launch them all at the same time and we don't really
@@ -1471,39 +1487,39 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
// when synchronous (without setTimeout)
(function (dataset, id, seq) {
$.async_when().then(function () {
- dataset.write(id, {sequence: seq});
+ var attrs = {};
+ attrs[seqname] = seq;
+ dataset.write(id, attrs);
});
}(dataset, record.get('id'), seq));
- record.set('sequence', seq);
+ record.set(seqname, seq);
}
-
- list.refresh_zebra();
}
});
},
render: function (post_render) {
var self = this;
- var $element = $('<tbody>');
- this.elements = [$element[0]];
+ var $el = $('<tbody>');
+ this.elements = [$el[0]];
this.datagroup.list(
_(this.view.visible_columns).chain()
.filter(function (column) { return column.tag === 'field' })
.pluck('name').value(),
function (groups) {
- $element[0].appendChild(
+ $el[0].appendChild(
self.render_groups(groups));
if (post_render) { post_render(); }
}, function (dataset) {
self.render_dataset(dataset).then(function (list) {
self.children[null] = list;
self.elements =
- [list.$current.replaceAll($element)[0]];
+ [list.$current.replaceAll($el)[0]];
self.setup_resequence_rows(list, dataset);
if (post_render) { post_render(); }
});
});
- return $element;
+ return $el;
},
/**
* Returns the ids of all selected records for this group, and the records
@@ -1807,7 +1823,7 @@ var Collection = instance.web.Class.extend(/** @lends Collection# */{
* @returns this
*/
remove: function (record) {
- var index = _(this.records).indexOf(record);
+ var index = this.indexOf(record);
if (index === -1) {
_(this._proxies).each(function (proxy) {
proxy.remove(record);
@@ -1823,13 +1839,42 @@ var Collection = instance.web.Class.extend(/** @lends Collection# */{
return this;
},
- _onRecordEvent: function (event, record, options) {
+ _onRecordEvent: function (event) {
+ switch(event) {
// don't propagate reset events
- if (event === 'reset') { return; }
+ case 'reset': return;
+ case 'change:id':
+ var record = arguments[1];
+ var new_value = arguments[2];
+ var old_value = arguments[3];
+ // [change:id, record, new_value, old_value]
+ if (this._byId[old_value] === record) {
+ delete this._byId[old_value];
+ this._byId[new_value] = record;
+ }
+ break;
+ }
this.trigger.apply(this, arguments);
},
// underscore-type methods
+ find: function (callback) {
+ var record;
+ for(var section in this._proxies) {
+ if (!this._proxies.hasOwnProperty(section)) {
+ continue
+ }
+ if ((record = this._proxies[section].find(callback))) {
+ return record;
+ }
+ }
+ for(var i=0; i<this.length; ++i) {
+ record = this.records[i];
+ if (callback(record)) {
+ return record;
+ }
+ }
+ },
each: function (callback) {
for(var section in this._proxies) {
if (this._proxies.hasOwnProperty(section)) {
@@ -1854,6 +1899,46 @@ var Collection = instance.web.Class.extend(/** @lends Collection# */{
},
indexOf: function (record) {
return _(this.records).indexOf(record);
+ },
+ succ: function (record, options) {
+ options = options || {wraparound: false};
+ var result;
+ for(var section in this._proxies) {
+ if (!this._proxies.hasOwnProperty(section)) {
+ continue;
+ }
+ if ((result = this._proxies[section].succ(record, options))) {
+ return result;
+ }
+ }
+ var index = this.indexOf(record);
+ if (index === -1) { return null; }
+ var next_index = index + 1;
+ if (options.wraparound && (next_index === this.length)) {
+ return this.at(0);
+ }
+ return this.at(next_index);
+ },
+ pred: function (record, options) {
+ options = options || {wraparound: false};
+
+ var result;
+ for (var section in this._proxies) {
+ if (!this._proxies.hasOwnProperty(section)) {
+ continue;
+ }
+ if ((result = this._proxies[section].pred(record, options))) {
+ return result;
+ }
+ }
+
+ var index = this.indexOf(record);
+ if (index === -1) { return null; }
+ var next_index = index - 1;
+ if (options.wraparound && (next_index === -1)) {
+ return this.at(this.length - 1);
+ }
+ return this.at(next_index);
}
});
Collection.include(Events);
@@ -1861,6 +1946,213 @@ instance.web.list = {
Events: Events,
Record: Record,
Collection: Collection
-}
+};
+/**
+ * Registry for column objects used to format table cells (and some other tasks
+ * e.g. aggregation computations).
+ *
+ * Maps a field or button to a Column type via its ``$tag.$widget``,
+ * ``$tag.$type`` or its ``$tag`` (alone).
+ *
+ * This specific registry has a dedicated utility method ``for_`` taking a
+ * field (from fields_get/fields_view_get.field) and a node (from a view) and
+ * returning the right object *already instantiated from the data provided*.
+ *
+ * @type {instance.web.Registry}
+ */
+instance.web.list.columns = new instance.web.Registry({
+ 'field': 'instance.web.list.Column',
+ 'field.boolean': 'instance.web.list.Boolean',
+ 'field.binary': 'instance.web.list.Binary',
+ 'field.progressbar': 'instance.web.list.ProgressBar',
+ 'field.handle': 'instance.web.list.Handle',
+ 'button': 'instance.web.list.Button',
+});
+instance.web.list.columns.for_ = function (id, field, node) {
+ var description = _.extend({tag: node.tag}, field, node.attrs);
+ var tag = description.tag;
+ var Type = this.get_any([
+ tag + '.' + description.widget,
+ tag + '.'+ description.type,
+ tag
+ ]);
+ return new Type(id, node.tag, description)
+};
+
+instance.web.list.Column = instance.web.Class.extend({
+ init: function (id, tag, attrs) {
+ _.extend(attrs, {
+ id: id,
+ tag: tag
+ });
+
+ this.modifiers = attrs.modifiers ? JSON.parse(attrs.modifiers) : {};
+ delete attrs.modifiers;
+ _.extend(this, attrs);
+
+ if (this.modifiers['tree_invisible']) {
+ this.invisible = '1';
+ } else { delete this.invisible; }
+ },
+ modifiers_for: function (fields) {
+ var out = {};
+ var domain_computer = instance.web.form.compute_domain;
+
+ for (var attr in this.modifiers) {
+ if (!this.modifiers.hasOwnProperty(attr)) { continue; }
+ var modifier = this.modifiers[attr];
+ out[attr] = _.isBoolean(modifier)
+ ? modifier
+ : domain_computer(modifier, fields);
+ }
+
+ return out;
+ },
+ to_aggregate: function () {
+ if (this.type !== 'integer' && this.type !== 'float') {
+ return {};
+ }
+ var aggregation_func = this['group_operator'] || 'sum';
+ if (!(aggregation_func in this)) {
+ return {};
+ }
+ var C = function (fn, label) {
+ this['function'] = fn;
+ this.label = label;
+ };
+ C.prototype = this;
+ return new C(aggregation_func, this[aggregation_func]);
+ },
+ /**
+ *
+ * @param row_data record whose values should be displayed in the cell
+ * @param {Object} [options]
+ * @param {String} [options.value_if_empty=''] what to display if the field's value is ``false``
+ * @param {Boolean} [options.process_modifiers=true] should the modifiers be computed ?
+ * @param {String} [options.model] current record's model
+ * @param {Number} [options.id] current record's id
+ * @return {String}
+ */
+ format: function (row_data, options) {
+ options = options || {};
+ var attrs = {};
+ if (options.process_modifiers !== false) {
+ attrs = this.modifiers_for(row_data);
+ }
+ if (attrs.invisible) { return ''; }
+
+ if (!row_data[this.id]) {
+ return options.value_if_empty === undefined
+ ? ''
+ : options.value_if_empty;
+ }
+ return this._format(row_data, options);
+ },
+ /**
+ * Method to override in order to provide alternative HTML content for the
+ * cell. Column._format will simply call ``instance.web.format_value`` and
+ * escape the output.
+ *
+ * The output of ``_format`` will *not* be escaped by ``format``, any
+ * escaping *must be done* by ``format``.
+ *
+ * @private
+ */
+ _format: function (row_data, options) {
+ return _.escape(instance.web.format_value(
+ row_data[this.id].value, this, options.value_if_empty));
+ }
+});
+instance.web.list.MetaColumn = instance.web.list.Column.extend({
+ meta: true,
+ init: function (id, string) {
+ this._super(id, '', {string: string});
+ }
+});
+instance.web.list.Button = instance.web.list.Column.extend({
+ /**
+ * Return an actual ``<button>`` tag
+ */
+ format: function (row_data, options) {
+ options = options || {};
+ var attrs = {};
+ if (options.process_modifiers !== false) {
+ attrs = this.modifiers_for(row_data);
+ }
+ if (attrs.invisible) { return ''; }
+
+ return QWeb.render('ListView.row.button', {
+ widget: this,
+ prefix: instance.session.prefix,
+ disabled: attrs.readonly
+ || isNaN(row_data.id.value)
+ || instance.web.BufferedDataSet.virtual_id_regex.test(row_data.id.value)
+ });
+ }
+});
+instance.web.list.Boolean = instance.web.list.Column.extend({
+ /**
+ * Return a potentially disabled checkbox input
+ *
+ * @private
+ */
+ _format: function (row_data, options) {
+ return _.str.sprintf('<input type="checkbox" %s disabled="disabled"/>',
+ row_data[this.id].value ? 'checked="checked"' : '');
+ }
+});
+instance.web.list.Binary = instance.web.list.Column.extend({
+ /**
+ * Return a link to the binary data as a file
+ *
+ * @private
+ */
+ _format: function (row_data, options) {
+ var text = _t("Download");
+ var download_url = _.str.sprintf(
+ '/web/binary/saveas?session_id=%s&model=%s&field=%s&id=%d',
+ instance.session.session_id, options.model, this.id, options.id);
+ if (this.filename) {
+ download_url += '&filename_field=' + this.filename;
+ if (row_data[this.filename]) {
+ text = _.str.sprintf(_t("Download \"%s\""), instance.web.format_value(
+ row_data[this.filename].value, {type: 'char'}));
+ }
+ }
+ return _.template('<a href="<%-href%>"><%-text%></a> (%<-size%>)', {
+ text: text,
+ href: download_url,
+ size: row_data[this.id].value
+ });
+ }
+});
+instance.web.list.ProgressBar = instance.web.list.Column.extend({
+ /**
+ * Return a formatted progress bar display
+ *
+ * @private
+ */
+ _format: function (row_data, options) {
+ return _.template(
+ '<progress value="<%-value%>" max="100"><%-value%>%</progress>', {
+ value: _.str.sprintf("%.0f", row_data[this.id].value || 0)
+ });
+ }
+});
+instance.web.list.Handle = instance.web.list.Column.extend({
+ init: function () {
+ this._super.apply(this, arguments);
+ // Handle overrides the field to not be form-editable.
+ this.modifiers.readonly = true;
+ },
+ /**
+ * Return styling hooks for a drag handle
+ *
+ * @private
+ */
+ _format: function (row_data, options) {
+ return '<div class="oe_list_handle">';
+ }
+});
};
// 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 c80509a7b2b..892221a347b 100644
--- a/addons/web/static/src/js/view_list_editable.js
+++ b/addons/web/static/src/js/view_list_editable.js
@@ -3,10 +3,6 @@
* @namespace
*/
openerp.web.list_editable = function (instance) {
- var KEY_RETURN = 13,
- KEY_ESCAPE = 27;
- var QWeb = instance.web.qweb;
-
// editability status of list rows
instance.web.ListView.prototype.defaults.editable = null;
@@ -15,6 +11,15 @@ openerp.web.list_editable = function (instance) {
init: function () {
var self = this;
this._super.apply(this, arguments);
+
+ this._force_editability = null;
+ this._context_editable = false;
+ this.editor = this.make_editor();
+ // Stores records of {field, cell}, allows for re-rendering fields
+ // depending on cell state during and after resize events
+ this.fields_for_resize = [];
+ instance.web.bus.on('resize', this, this.resize_fields);
+
$(this.groups).bind({
'edit': function (e, id, dataset) {
self.do_edit(dataset.index, id, dataset);
@@ -26,7 +31,35 @@ openerp.web.list_editable = function (instance) {
self.configure_pager(self.dataset);
self.compute_aggregates();
}
- })
+ });
+
+ this.records.bind('remove', function () {
+ if (self.editor.is_editing()) {
+ self.cancel_edition();
+ }
+ });
+
+ this.on('edit:before', this, function (event) {
+ if (!self.editable() || self.editor.is_editing()) {
+ event.cancel = true;
+ }
+ });
+ this.on('edit:after', this, function () {
+ self.$el.add(self.$buttons).addClass('oe_editing');
+ });
+ this.on('save:after cancel:after', this, function () {
+ self.$el.add(self.$buttons).removeClass('oe_editing');
+ });
+ },
+ destroy: function () {
+ instance.web.bus.off('resize', this, this.resize_fields);
+ this._super();
+ },
+ do_hide: function () {
+ if (this.editor.is_editing()) {
+ this.cancel_edition(true);
+ }
+ this._super();
},
/**
* Handles the activation of a record in editable mode (making a record
@@ -42,25 +75,16 @@ openerp.web.list_editable = function (instance) {
do_edit: function (index, id, dataset) {
_.extend(this.dataset, dataset);
},
- /**
- * Sets editability status for the list, based on defaults, view
- * architecture and the provided flag, if any.
- *
- * @param {Boolean} [force] forces the list to editability. Sets new row edition status to "bottom".
- */
- set_editable: function (force) {
- // If ``force``, set editability to bottom
- // otherwise rely on view default
- // view' @editable is handled separately as we have not yet
- // fetched and processed the view at this point.
- this.options.editable = (
- ! this.options.read_only && ((force && "bottom") || this.defaults.editable));
+ editable: function () {
+ return this.fields_view.arch.attrs.editable
+ || this._context_editable
+ || this.options.editable;
},
/**
* Replace do_search to handle editability process
*/
do_search: function(domain, context, group_by) {
- this.set_editable(context['set_editable']);
+ this._context_editable = !!context.set_editable;
this._super.apply(this, arguments);
},
/**
@@ -68,18 +92,66 @@ openerp.web.list_editable = function (instance) {
* as an editable row at the top or bottom of the list)
*/
do_add_record: function () {
- if (this.options.editable) {
- this.$element.find('table:first').show();
- this.$element.find('.oe_view_nocontent').remove();
- this.groups.new_record();
+ if (this.editable()) {
+ this.$el.find('table:first').show();
+ this.$el.find('.oe_view_nocontent').remove();
+ this.start_edition();
} else {
this._super();
}
},
on_loaded: function (data, grouped) {
+ var self = this;
// tree/@editable takes priority on everything else if present.
- this.options.editable = ! this.options.read_only && (data.arch.attrs.editable || this.options.editable);
- return this._super(data, grouped);
+ var result = this._super(data, grouped);
+ if (this.editable()) {
+ this.$el.addClass('oe_list_editable');
+ // FIXME: any hook available to ensure this is only done once?
+ this.$buttons
+ .off('click', '.oe_list_save')
+ .on('click', '.oe_list_save', this.proxy('save_edition'))
+ .off('click', '.oe_list_discard')
+ .on('click', '.oe_list_discard', function (e) {
+ e.preventDefault();
+ self.cancel_edition();
+ });
+ this.$el
+ .off('click', 'tbody td:not(.oe_list_field_cell)')
+ .on('click', 'tbody td:not(.oe_list_field_cell)', function () {
+ if (!self.editor.is_editing()) {
+ self.start_edition();
+ }
+ });
+ this.editor.destroy();
+ // Editor is not restartable due to formview not being
+ // restartable
+ this.editor = this.make_editor();
+ var editor_ready = this.editor.prependTo(this.$el)
+ .then(this.proxy('setup_events'));
+
+ return $.when(result, editor_ready);
+ } else {
+ this.$el.removeClass('oe_list_editable');
+ }
+
+ return result;
+ },
+ /**
+ * Builds a new editor object
+ *
+ * @return {instance.web.list.Editor}
+ */
+ make_editor: function () {
+ return new instance.web.list.Editor(this);
+ },
+ do_button_action: function (name, id, callback) {
+ var self = this, args = arguments;
+ this.ensure_saved().then(function (done) {
+ if (!id && done.created) {
+ id = done.record.get('id');
+ }
+ self.handle_button.call(self, name, id, callback);
+ });
},
/**
* Ensures the editable list is saved (saves any pending edition if
@@ -90,338 +162,627 @@ openerp.web.list_editable = function (instance) {
* @returns {$.Deferred}
*/
ensure_saved: function () {
- return this.groups.ensure_saved();
- }
- });
-
- instance.web.ListView.Groups.include(/** @lends instance.web.ListView.Groups# */{
- passtrough_events: instance.web.ListView.Groups.prototype.passtrough_events + " edit saved",
- new_record: function () {
- // TODO: handle multiple children
- this.children[null].new_record();
+ if (!this.editor.is_editing()) {
+ return $.when();
+ }
+ return this.save_edition();
},
/**
- * Ensures descendant editable List instances are all saved if they have
- * pending editions.
+ * Set up the edition of a record of the list view "inline"
*
- * @returns {$.Deferred}
+ * @param {instance.web.list.Record} [record] record to edit, leave empty to create a new record
+ * @param {Object} [options]
+ * @param {String} [options.focus_field] field to focus at start of edition
+ * @return {jQuery.Deferred}
*/
- ensure_saved: function () {
- return $.when.apply(null,
- _.invoke(
- _.values(this.children),
- 'ensure_saved'));
- }
- });
-
- instance.web.ListView.List.include(/** @lends instance.web.ListView.List# */{
- row_clicked: function (event) {
- if (!this.options.editable) {
- return this._super.apply(this, arguments);
+ start_edition: function (record, options) {
+ var self = this;
+ var item = false;
+ if (record) {
+ item = record.attributes;
+ } else {
+ var attrs = {id: false};
+ _(this.columns).chain()
+ .filter(function (x) { return x.tag === 'field'})
+ .pluck('name')
+ .each(function (field) { attrs[field] = false; });
+ record = new instance.web.list.Record(attrs);
+ this.records.add(record, {
+ at: this.prepends_on_create() ? 0 : null});
}
- this.edit_record($(event.currentTarget).data('id'));
+ var $recordRow = this.groups.get_row_for(record);
+ var cells = this.get_cells_for($recordRow);
+
+ return this.ensure_saved().pipe(function () {
+ self.fields_for_resize.splice(0, self.fields_for_resize.length);
+ return self.with_event('edit', {
+ record: record.attributes,
+ cancel: false
+ }, function () {
+ return self.editor.edit(item, function (field_name, field) {
+ var cell = cells[field_name];
+ if (!cell || field.get('effective_readonly')) {
+ // Readonly fields can just remain the list's,
+ // form's usually don't have backgrounds &al
+ field.set({invisible: true});
+ return;
+ }
+
+ // FIXME: need better way to get the field back from bubbling (delegated) DOM events somehow
+ field.$el.attr('data-fieldname', field_name);
+ self.fields_for_resize.push({field: field, cell: cell});
+ }, options).pipe(function () {
+ $recordRow.addClass('oe_edition');
+ self.resize_fields();
+ return record.attributes;
+ });
+ }).fail(function () {
+ // if the start_edition event is cancelled and it was a
+ // creation, remove the newly-created empty record
+ if (!record.get('id')) {
+ self.records.remove(record);
+ }
+ });
+ });
+ },
+ get_cells_for: function ($row) {
+ var cells = {};
+ $row.children('td').each(function (index, el) {
+ cells[el.getAttribute('data-field')] = el
+ });
+ return cells;
},
/**
- * Checks if a record is being edited, and if so cancels it
+ * If currently editing a row, resizes all registered form fields based
+ * on the corresponding row cell
*/
- cancel_pending_edition: function () {
- var self = this, cancelled = $.Deferred();
- if (!this.edition) {
- cancelled.resolve();
- return cancelled.promise();
- }
-
- if (this.edition_id != null) {
- this.reload_record(self.records.get(this.edition_id)).then(function () {
- cancelled.resolve();
- });
- } else {
- cancelled.resolve();
+ resize_fields: function () {
+ 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];
+ this.resize_field(item.field, item.cell);
}
- cancelled.then(function () {
- self.view.unpad_columns();
- self.edition_form.destroy();
- self.edition_form.$element.remove();
- delete self.edition_form;
- delete self.edition_id;
- delete self.edition;
- });
- this.pad_table_to(5);
- return cancelled.promise();
},
/**
- * Adapts this list's view description to be suitable to the inner form
- * view of a row being edited.
+ * Resizes a field's root element based on the corresponding cell of
+ * a listview row
*
- * @returns {Object} fields_view_get's view section suitable for putting into form view of editable rows.
+ * @param {instance.web.form.AbstractField} field
+ * @param {jQuery} cell
*/
- get_form_fields_view: function () {
- // deep copy of view
- var view = $.extend(true, {}, this.group.view.fields_view);
- _(view.arch.children).each(function (widget) {
- widget.attrs.nolabel = true;
- if (widget.tag === 'button') {
- delete widget.attrs.string;
- }
+ resize_field: function (field, cell) {
+ var $cell = $(cell);
+ var position = $cell.position();
+
+ // jquery does not understand !important
+ field.$el.attr('style', 'width: '+$cell.outerWidth()+'px !important');
+ field.$el.css({
+ top: position.top,
+ left: position.left,
+ minHeight: $cell.outerHeight()
});
- view.arch.attrs.col = 2 * view.arch.children.length;
- return view;
},
- on_row_keyup: function (e) {
+ /**
+ * @return {jQuery.Deferred}
+ */
+ save_edition: function () {
var self = this;
- switch (e.which) {
- case KEY_RETURN:
- this.save_row().then(function (result) {
- if (result.created) {
- self.new_record();
- return;
- }
-
- var next_record_id,
- next_record = self.records.at(
- self.records.indexOf(result.edited_record) + 1);
- if (next_record) {
- next_record_id = next_record.get('id');
- self.dataset.index = _(self.dataset.ids)
- .indexOf(next_record_id);
- } else {
- self.dataset.index = 0;
- next_record_id = self.records.at(0).get('id');
+ return this.with_event('save', {
+ editor: this.editor,
+ form: this.editor.form,
+ cancel: false
+ }, function () {
+ return this.editor.save().pipe(function (attrs) {
+ var created = false;
+ var record = self.records.get(attrs.id);
+ if (!record) {
+ // new record
+ created = true;
+ record = self.records.find(function (r) {
+ return !r.get('id');
+ }).set('id', attrs.id);
}
- self.edit_record(next_record_id);
+ // onwrite callback could be altering & reloading the
+ // record which has *just* been saved, so first perform all
+ // onwrites then do a final reload of the record
+ return self.handle_onwrite(record)
+ .pipe(function () {
+ return self.reload_record(record); })
+ .pipe(function () {
+ return { created: created, record: record }; });
});
- break;
- case KEY_ESCAPE:
- this.cancel_edition();
- break;
- }
+ });
},
- render_row_as_form: function (row) {
+ /**
+ * @param {Boolean} [force=false] discards the data even if the form has been edited
+ * @return {jQuery.Deferred}
+ */
+ cancel_edition: function (force) {
var self = this;
- this.cancel_pending_edition().then(function () {
- var record_id = $(row).data('id');
- var $new_row = $('<tr>', {
- id: _.uniqueId('oe-editable-row-'),
- 'data-id': record_id,
- 'class': (row ? $(row).attr('class') : '') + ' oe_form',
- click: function (e) {e.stopPropagation();}
- })
- .delegate('button.oe-edit-row-save', 'click', function () {
- self.save_row();
- })
- .delegate('button', 'keyup', function (e) {
- e.stopImmediatePropagation();
- })
- .keyup(function () {
- return self.on_row_keyup.apply(self, arguments); })
- .keydown(function (e) { e.stopPropagation(); });
- if (row) {
- $new_row.replaceAll(row);
- } else if (self.options.editable) {
- var $last_child = self.$current.children('tr:last');
- if (self.records.length) {
- if (self.options.editable === 'top') {
- $new_row.insertBefore(
- self.$current.children('[data-id]:first'));
- } else {
- $new_row.insertAfter(
- self.$current.children('[data-id]:last'));
+ return this.with_event('cancel', {
+ editor: this.editor,
+ form: this.editor.form,
+ cancel: false
+ }, function () {
+ return this.editor.cancel(force).pipe(function (attrs) {
+ if (attrs.id) {
+ var record = self.records.get(attrs.id);
+ if (!record) {
+ // Record removed by third party during edition
+ return
}
- } else {
- $new_row.prependTo(self.$current);
- }
- if ($last_child.is(':not([data-id])')) {
- $last_child.remove();
+ return self.reload_record(record);
}
- }
- self.edition = true;
- self.edition_id = record_id;
- $new_row.addClass("oe_form_container");
- self.edition_form = new instance.web.ListEditableFormView(self.view, self.dataset, false);
- self.edition_form.$element = $new_row;
- self.edition_form.editable_list = self;
- // HO HO
- // empty
- $.when(self.edition_form.on_loaded(self.get_form_fields_view())).then(function () {
- // put in $.when just in case FormView.on_loaded becomes asynchronous
- $new_row.find('> td')
- .end()
- .find('td:last').removeClass('oe-field-cell').end();
- // pad in case of groupby
- _(self.columns).each(function (column) {
- if (column.meta) {
- $new_row.prepend('<td>');
- }
+ var to_delete = self.records.find(function (r) {
+ return !r.get('id');
});
- // Add column for the save, if
- // there is none in the list
- if (!self.options.deletable) {
- self.view.pad_columns(
- 1, {except: $new_row});
+ if (to_delete) {
+ self.records.remove(to_delete);
}
-
- self.edition_form.do_show();
});
});
},
- handle_onwrite: function (source_record_id) {
+ /**
+ * Executes an action on the view's editor bracketed by a cancellable
+ * event of the name provided.
+ *
+ * The event name provided will be post-fixed with ``:before`` and
+ * ``:after``, the ``event`` parameter will be passed alongside the
+ * ``:before`` variant and if the parameter's ``cancel`` key is set to
+ * ``true`` the action *will not be called* and the method will return
+ * a rejection
+ *
+ * @param {String} event_name name of the event
+ * @param {Object} event event object, provided to ``:before`` sub-event
+ * @param {Function} action callable, called with the view's editor as its context
+ * @param {Array} [args] supplementary arguments provided to the action
+ * @param {Array} [trigger_params] supplementary arguments provided to the ``:after`` sub-event, before anything fetched by the ``action`` function
+ * @return {jQuery.Deferred}
+ */
+ with_event: function (event_name, event, action) {
var self = this;
- var on_write_callback = self.view.fields_view.arch.attrs.on_write;
- if (!on_write_callback) { return; }
- this.dataset.call(on_write_callback, [source_record_id], function (ids) {
- _(ids).each(function (id) {
- var record = self.records.get(id);
- if (!record) {
- // insert after the source record
- var index = self.records.indexOf(
- self.records.get(source_record_id)) + 1;
- record = new instance.web.list.Record({id: id});
- self.records.add(record, {at: index});
- self.dataset.ids.splice(index, 0, id);
+ event = event || {};
+ this.trigger(event_name + ':before', event);
+ if (event.cancel) {
+ return $.Deferred().reject({
+ message: _.str.sprintf("Event %s:before cancelled",
+ event_name)});
+ }
+ return $.when(action.call(this)).then(function () {
+ self.trigger.apply(self, [event_name + ':after']
+ .concat(_.toArray(arguments)));
+ });
+ },
+ edition_view: function (editor) {
+ var view = $.extend(true, {}, this.fields_view);
+ view.arch.tag = 'form';
+ _.extend(view.arch.attrs, {
+ 'class': 'oe_form_container',
+ version: '7.0'
+ });
+ _(view.arch.children).chain()
+ .zip(this.columns)
+ .each(function (ar) {
+ var widget = ar[0], column = ar[1];
+ var modifiers = _.extend({}, column.modifiers);
+ widget.attrs.nolabel = true;
+ if (modifiers['tree_invisible'] || widget.tag === 'button') {
+ modifiers.invisible = true;
}
- self.reload_record(record);
+ widget.attrs.modifiers = JSON.stringify(modifiers);
+ });
+ return view;
+ },
+ handle_onwrite: function (source_record) {
+ var self = this;
+ var on_write_callback = self.fields_view.arch.attrs.on_write;
+ if (!on_write_callback) { return $.when(); }
+ return this.dataset.call(on_write_callback, [source_record.get('id')])
+ .pipe(function (ids) {
+ return $.when.apply(
+ null, _(ids).map(
+ _.bind(self.handle_onwrite_record, self, source_record)));
});
+ },
+ handle_onwrite_record: function (source_record, id) {
+ var record = this.records.get(id);
+ if (!record) {
+ // insert after the source record
+ var index = this.records.indexOf(source_record) + 1;
+ record = new instance.web.list.Record({id: id});
+ this.records.add(record, {at: index});
+ this.dataset.ids.splice(index, 0, id);
+ }
+ return this.reload_record(record);
+ },
+ prepends_on_create: function () {
+ return this.editable() === 'top';
+ },
+ setup_events: function () {
+ var self = this;
+ this.editor.$el.on('keyup keydown', function (e) {
+ if (!self.editor.is_editing()) { return; }
+ var key = _($.ui.keyCode).chain()
+ .map(function (v, k) { return {name: k, code: v}; })
+ .find(function (o) { return o.code === e.which; })
+ .value();
+ if (!key) { return; }
+ var method = e.type + '_' + key.name;
+ if (!(method in self)) { return; }
+ self[method](e);
});
},
/**
- * Saves the current row, and returns a Deferred resolving to an object
- * with the following properties:
+ * Saves the current record, and goes to the next one (creation or
+ * edition)
*
- * ``created``
- * Boolean flag indicating whether the record saved was being created
- * (``true`` or edited (``false``)
- * ``edited_record``
- * The result of saving the record (either the newly created record,
- * or the post-edition record), after insertion in the Collection if
- * needs be.
- *
- * @returns {$.Deferred<{created: Boolean, edited_record: Record}>}
+ * @private
+ * @param {String} [next_record='succ'] method to call on the records collection to get the next record to edit
+ * @param {Object} [options]
+ * @param {String} [options.focus_field]
+ * @return {*}
*/
- save_row: function () {
- //noinspection JSPotentiallyInvalidConstructorUsage
- var self = this, done = $.Deferred();
- return this.edition_form
- .do_save(null, this.options.editable === 'top')
- .pipe(function (result) {
- if (result.created && !self.edition_id) {
- self.records.add({id: result.result},
- {at: self.options.editable === 'top' ? 0 : null});
- self.edition_id = result.result;
- }
- var edited_record = self.records.get(self.edition_id);
-
- return $.when(
- self.handle_onwrite(self.edition_id),
- self.cancel_pending_edition().then(function () {
- $(self).trigger('saved', [self.dataset]);
- })).pipe(function () {
- return {
- created: result.created || false,
- edited_record: edited_record
- };
- }, null);
- }, null);
+ _next: function (next_record, options) {
+ next_record = next_record || 'succ';
+ var self = this;
+ return this.save_edition().pipe(function (saveInfo) {
+ if (saveInfo.created) {
+ return self.start_edition();
+ }
+ var record = self.records[next_record](
+ saveInfo.record, {wraparound: true});
+ return self.start_edition(record, options);
+ });
+ },
+ keyup_ENTER: function () {
+ return this._next();
+ },
+ keyup_ESCAPE: function () {
+ return this.cancel_edition();
},
/**
- * If the current list is being edited, ensures it's saved
+ * Gets the selection range (start, end) for the provided element,
+ * returns ``null`` if it can't get a range.
+ *
+ * @private
*/
- ensure_saved: function () {
- if (this.edition) {
- return this.save_row();
+ _text_selection_range: function (el) {
+ var selectionStart;
+ try {
+ selectionStart = el.selectionStart;
+ } catch (e) {
+ // radio or checkbox throw on selectionStart access
+ return null;
+ }
+ if (selectionStart !== undefined) {
+ return {
+ start: selectionStart,
+ end: el.selectionEnd
+ };
+ } else if (document.body.createTextRange) {
+ throw new Error("Implement text range handling for MSIE");
+ var sel = document.body.createTextRange();
+ if (sel.parentElement() === el) {
+
+ }
}
- //noinspection JSPotentiallyInvalidConstructorUsage
- return $.Deferred().resolve().promise();
+ // Element without selection ranges (select, div/@contenteditable)
+ return null;
+ },
+ _text_cursor: function (el) {
+ var selection = this._text_selection_range(el);
+ if (!selection) {
+ return null;
+ }
+ if (selection.start !== selection.end) {
+ return {position: null, collapsed: false};
+ }
+ return {position: selection.start, collapsed: true};
+ },
+ /**
+ * Checks if the cursor is at the start of the provided el
+ *
+ * @param {HTMLInputElement | HTMLTextAreaElement}
+ * @returns {Boolean}
+ * @private
+ */
+ _at_start: function (cursor, el) {
+ return cursor.collapsed && (cursor.position === 0);
},
/**
- * Cancels the edition of the row for the current dataset index
+ * Checks if the cursor is at the end of the provided el
+ *
+ * @param {HTMLInputElement | HTMLTextAreaElement}
+ * @returns {Boolean}
+ * @private
*/
- cancel_edition: function () {
- this.cancel_pending_edition();
+ _at_end: function (cursor, el) {
+ return cursor.collapsed && (cursor.position === el.value.length);
},
/**
- * Edits record currently selected via dataset
+ * @param DOMEvent event
+ * @param {String} record_direction direction to move into to get the next record (pred | succ)
+ * @param {Function} is_valid_move whether the edition should be moved to the next record
+ * @private
*/
- edit_record: function (record_id) {
- this.render_row_as_form(
- this.$current.find('[data-id=' + record_id + ']'));
- $(this).trigger(
- 'edit',
- [record_id, this.dataset]);
- },
- new_record: function () {
- this.dataset.index = null;
- this.render_row_as_form();
- },
- render_record: function (record) {
- var index = this.records.indexOf(record),
- self = this;
- // FIXME: context dict should probably be extracted cleanly
- return QWeb.render('ListView.row', {
- columns: this.columns,
- options: this.options,
- record: record,
- row_parity: (index % 2 === 0) ? 'even' : 'odd',
- view: this.view,
- render_cell: function () {
- return self.render_cell.apply(self, arguments); },
- edited: !!this.edition_form
+ _key_move_record: function (event, record_direction, is_valid_move) {
+ if (!this.editor.is_editing('edit')) { return $.when(); }
+ var cursor = this._text_cursor(event.target);
+ // if text-based input (has a cursor)
+ // and selecting (not collapsed) or not at a field boundary
+ // don't move to the next record
+ if (cursor && !is_valid_move(event.target, cursor)) { return $.when(); }
+
+ event.preventDefault();
+ var source_field = $(event.target).closest('[data-fieldname]')
+ .attr('data-fieldname');
+ return this._next(record_direction, {focus_field: source_field});
+
+ },
+ keydown_UP: function (e) {
+ var self = this;
+ return this._key_move_record(e, 'pred', function (el, cursor) {
+ return self._at_start(cursor, el);
});
+ },
+ keydown_DOWN: function (e) {
+ var self = this;
+ return this._key_move_record(e, 'succ', function (el, cursor) {
+ return self._at_end(cursor, el);
+ });
+ },
+
+ keydown_LEFT: function (e) {
+ // If the cursor is at the beginning of the field
+ var source_field = $(e.target).closest('[data-fieldname]')
+ .attr('data-fieldname');
+ var cursor = this._text_cursor(e.target);
+ if (cursor && !this._at_start(cursor, e.target)) { return $.when(); }
+
+ var fields_order = this.editor.form.fields_order;
+ var field_index = _(fields_order).indexOf(source_field);
+
+ // Look for the closest visible form field to the left
+ var fields = this.editor.form.fields;
+ var field;
+ do {
+ if (--field_index < 0) { return $.when(); }
+
+ field = fields[fields_order[field_index]];
+ } while (!field.$el.is(':visible'));
+
+ // and focus it
+ field.focus();
+ return $.when();
+ },
+ keydown_RIGHT: function (e) {
+ // same as above, but with cursor at the end of the field and
+ // looking for new fields at the right
+ var source_field = $(e.target).closest('[data-fieldname]')
+ .attr('data-fieldname');
+ var cursor = this._text_cursor(e.target);
+ if (cursor && !this._at_end(cursor, e.target)) { return $.when(); }
+
+ var fields_order = this.editor.form.fields_order;
+ var field_index = _(fields_order).indexOf(source_field);
+
+ var fields = this.editor.form.fields;
+ var field;
+ do {
+ if (++field_index >= fields_order.length) { return $.when(); }
+
+ field = fields[fields_order[field_index]];
+ } while (!field.$el.is(':visible'));
+
+ field.focus();
+ return $.when();
+ },
+ keydown_TAB: function (e) {
+ var form = this.editor.form;
+ var last_field = _(form.fields_order).chain()
+ .map(function (name) { return form.fields[name]; })
+ .filter(function (field) { return field.$el.is(':visible'); })
+ .last()
+ .value();
+ // tabbed from last field in form
+ if (last_field && last_field.$el.has(e.target).length) {
+ e.preventDefault();
+ return this._next();
+ }
+ return $.when();
}
});
-
- instance.web.ListEditableFormView = instance.web.FormView.extend({
- init: function() {
- this._super.apply(this, arguments);
- this.rendering_engine = new instance.web.ListEditableRenderingEngine(this);
- this.options.initial_mode = "edit";
+
+ instance.web.list.Editor = instance.web.Widget.extend({
+ /**
+ * @constructs instance.web.list.Editor
+ * @extends instance.web.Widget
+ *
+ * Adapter between listview and formview for editable-listview purposes
+ *
+ * @param {instance.web.Widget} parent
+ * @param {Object} options
+ * @param {instance.web.FormView} [options.formView=instance.web.FormView]
+ * @param {Object} [options.delegate]
+ */
+ init: function (parent, options) {
+ this._super(parent);
+ this.options = options || {};
+ _.defaults(this.options, {
+ formView: instance.web.FormView,
+ delegate: this.getParent()
+ });
+ this.delegate = this.options.delegate;
+
+ this.record = null;
+
+ this.form = new (this.options.formView)(
+ this, this.delegate.dataset, false, {
+ initial_mode: 'edit',
+ disable_autofocus: true,
+ $buttons: $(),
+ $pager: $()
+ });
},
- renderElement: function() {}
- });
-
- instance.web.ListEditableRenderingEngine = instance.web.form.FormRenderingEngineInterface.extend({
- init: function(view) {
- this.view = view;
+ start: function () {
+ var self = this;
+ var _super = this._super();
+ this.form.embedded_view = this._validate_view(
+ this.delegate.edition_view(this));
+ var form_ready = this.form.appendTo(this.$el).then(
+ self.form.proxy('do_hide'));
+ return $.when(_super, form_ready);
},
- set_fields_view: function(fields_view) {
- this.fvg = fields_view;
+ _validate_view: function (edition_view) {
+ if (!edition_view) {
+ throw new Error("editor delegate's #edition_view must return "
+ + "a view descriptor");
+ }
+ var arch = edition_view.arch;
+ if (!(arch && arch.children instanceof Array)) {
+ throw new Error("Editor delegate's #edition_view must have a" +
+ " non-empty arch")
+ }
+ if (!(arch.tag === "form")) {
+ throw new Error("Editor delegate's #edition_view must have a" +
+ " 'form' root node");
+ }
+ if (!(arch.attrs && arch.attrs.version === "7.0")) {
+ throw new Error("Editor delegate's #edition_view must be a" +
+ " version 7 view");
+ }
+ if (!/\boe_form_container\b/.test(arch.attrs['class'])) {
+ throw new Error("Editor delegate's #edition_view must have the" +
+ " class 'oe_form_container' on its root" +
+ " element");
+ }
+
+ return edition_view;
},
- set_tags_registry: function(tags_registry) {
- this.tags_registry = tags_registry;
+
+ /**
+ *
+ * @param {String} [state] either ``new`` or ``edit``
+ * @return {Boolean}
+ */
+ is_editing: function (state) {
+ if (!this.record) {
+ return false;
+ }
+ switch(state) {
+ case null: case undefined:
+ return true;
+ case 'new': return !this.record.id;
+ case 'edit': return !!this.record.id;
+ }
+ throw new Error("is_editing's state filter must be either `new` or" +
+ " `edit` if provided");
},
- set_fields_registry: function(fields_registry) {
- this.fields_registry = fields_registry;
+ _focus_setup: function (focus_field) {
+ var form = this.form;
+
+ var field;
+ // If a field to focus was specified
+ if (focus_field
+ // Is actually in the form
+ && (field = form.fields[focus_field])
+ // And is visible
+ && field.$el.is(':visible')) {
+ // focus it
+ field.focus();
+ return;
+ }
+
+ _(form.fields_order).detect(function (name) {
+ // look for first visible field in fields_order, focus it
+ var field = form.fields[name];
+ if (!field.$el.is(':visible')) {
+ return false;
+ }
+ // Stop as soon as a field got focused
+ return field.focus() !== false;
+ });
},
- render_to: function($element) {
+ edit: function (record, configureField, options) {
+ // TODO: specify sequence of edit calls
var self = this;
-
- var xml = instance.web.json_node_to_xml(this.fvg.arch);
- var $xml = $(xml);
-
- if (this.view.editable_list.options.selectable)
- $("<td>").appendTo($element);
-
- $xml.children().each(function(i, el) {
- var modifiers = JSON.parse($(el).attr("modifiers") || "{}");
- var $td = $("<td>");
- if (modifiers.tree_invisible === true)
- $td.hide();
- var tag_name = el.tagName.toLowerCase();
- var w;
- if (tag_name === "field") {
- var name = $(el).attr("name");
- var key = $(el).attr('widget') || self.fvg.fields[name].type;
- var obj = self.view.fields_registry.get_object(key);
- w = new (obj)(self.view, instance.web.xml_to_json(el));
- self.view.register_field(w, $(el).attr("name"));
- } else {
- var obj = self.tags_registry.get_object(tag_name);
- w = new (obj)(self.view, instance.web.xml_to_json(el));
- }
- w.appendTo($td);
- $td.appendTo($element);
+ var form = self.form;
+ var loaded = record
+ ? form.on_record_loaded(_.extend({}, record))
+ : form.load_defaults();
+
+ return loaded.pipe(function () {
+ return form.do_show({reload: false});
+ }).pipe(function () {
+ self.record = form.datarecord;
+ _(form.fields).each(function (field, name) {
+ configureField(name, field);
+ });
+ self._focus_setup(options && options.focus_field);
+ return form;
});
- save = QWeb.render('ListView.row.save');
- $(save).appendTo($element);
},
+ save: function () {
+ var self = this;
+ return this.form
+ .do_save(this.delegate.prepends_on_create())
+ .pipe(function (result) {
+ var created = result.created && !self.record.id;
+ if (created) {
+ self.record.id = result.result;
+ }
+ return self.cancel();
+ });
+ },
+ cancel: function (force) {
+ if (!(force || this.form.can_be_discarded())) {
+ return $.Deferred().reject({
+ message: "The form's data can not be discarded"}).promise();
+ }
+ var record = this.record;
+ this.record = null;
+ this.form.do_hide();
+ return $.when(record);
+ }
+ });
+
+ instance.web.ListView.Groups.include(/** @lends instance.web.ListView.Groups# */{
+ passtrough_events: instance.web.ListView.Groups.prototype.passtrough_events + " edit saved",
+ get_row_for: function (record) {
+ return _(this.children).chain()
+ .invoke('get_row_for', record)
+ .compact()
+ .first()
+ .value();
+ }
+ });
+
+ instance.web.ListView.List.include(/** @lends instance.web.ListView.List# */{
+ row_clicked: function (event) {
+ if (!this.view.editable() || ! this.view.is_action_enabled('edit')) {
+ return this._super.apply(this, arguments);
+ }
+ var record_id = $(event.currentTarget).data('id');
+ this.view.start_edition(
+ record_id ? this.records.get(record_id) : null, {
+ focus_field: $(event.target).data('field')
+ });
+ },
+ /**
+ * If a row mapping to the record (@data-id matching the record's id or
+ * no @data-id if the record has no id), returns it. Otherwise returns
+ * ``null``.
+ *
+ * @param {Record} record the record to get a row for
+ * @return {jQuery|null}
+ */
+ get_row_for: function (record) {
+ var id;
+ var $row = this.$current.children('[data-id=' + record.get('id') + ']');
+ if ($row.length) {
+ return $row;
+ }
+ return null;
+ }
});
};
diff --git a/addons/web/static/src/js/view_tree.js b/addons/web/static/src/js/view_tree.js
index 9e938fe293a..e0493a446e2 100644
--- a/addons/web/static/src/js/view_tree.js
+++ b/addons/web/static/src/js/view_tree.js
@@ -63,6 +63,12 @@ instance.web.TreeView = instance.web.View.extend(/** @lends instance.web.TreeVie
}
return fields;
},
+ store_record:function(records){
+ var self = this;
+ _(records).each(function (record) {
+ self.records[record.id] = record;
+ });
+ },
on_loaded: function (fields_view) {
var self = this;
var has_toolbar = !!fields_view.arch.attrs.toolbar;
@@ -78,25 +84,28 @@ instance.web.TreeView = instance.web.View.extend(/** @lends instance.web.TreeVie
});
this.fields = fields_view.fields;
this.hook_row_click();
- this.$element.html(QWeb.render('TreeView', {
+ this.$el.html(QWeb.render('TreeView', {
'title': this.fields_view.arch.attrs.string,
'fields_view': this.fields_view.arch.children,
'fields': this.fields,
'toolbar': has_toolbar
}));
+ this.$el.addClass(this.fields_view.arch.attrs['class']);
this.dataset.read_slice(this.fields_list()).then(function(records) {
+ self.store_record(records);
if (!has_toolbar) {
// WARNING: will do a second read on the same ids, but only on
// first load so not very important
- self.getdata(null, _(records).pluck('id'));
+ self.render_data({'null':records})
+ self.getdata(_.pluck(records,"id"));
return;
}
- var $select = self.$element.find('select')
+ var $select = self.$el.find('select')
.change(function () {
var $option = $(this).find(':selected');
- self.getdata($option.val(), $option.data('children'));
+ self.getdata($option.val());
});
_(records).each(function (record) {
self.records[record.id] = record;
@@ -111,7 +120,12 @@ instance.web.TreeView = instance.web.View.extend(/** @lends instance.web.TreeVie
$select.change();
}
});
-
+ this.$el.find("#tree_view_expand").click(function(){
+ self.expand_all();
+ });
+ this.$el.find("#tree_view_collapse").click(function(){
+ self.collpase_all();
+ });
// TODO store open nodes in url ?...
this.do_push_state({});
@@ -127,6 +141,22 @@ instance.web.TreeView = instance.web.View.extend(/** @lends instance.web.TreeVie
return [color, py.parse(py.tokenize(expr)), expr];
}).value();
},
+ expand_all: function(){
+ var self = this;
+ var tr = this.$el.find(".oe-treeview-table tbody tr[id^='treerow_']");
+ _.each(tr,function(rec){
+ self.showcontent($(rec).attr('data-id'),true);
+ });
+ },
+ collpase_all: function(){
+ var self = this;
+ var root_tr = this.$el.find(".oe-treeview-table tbody tr[data-level='"+1+"']");
+ _.each(root_tr,function(rec){
+ if($(rec).hasClass('oe_open')){
+ self.showcontent($(rec).attr('data-id'),false);
+ }
+ });
+ },
/**
* Returns the color for the provided record in the current view (from the
* ``@colors`` attribute)
@@ -157,65 +187,61 @@ instance.web.TreeView = instance.web.View.extend(/** @lends instance.web.TreeVie
*/
hook_row_click: function () {
var self = this;
- this.$element.delegate('.treeview-td span, .treeview-tr span', 'click', function (e) {
+ this.$el.delegate('.treeview-td span, .treeview-tr span', 'click', function (e) {
e.stopImmediatePropagation();
self.activate($(this).closest('tr').data('id'));
});
- this.$element.delegate('.treeview-tr', 'click', function () {
- var is_loaded = 0,
- $this = $(this),
+ this.$el.delegate('.treeview-tr', 'click', function () {
+ var $this = $(this),
record_id = $this.data('id'),
- record = self.records[record_id],
- children_ids = record[self.children_field];
-
- _(children_ids).each(function(childid) {
- if (self.$element.find('#treerow_' + childid).length) {
- if (self.$element.find('#treerow_' + childid).is(':hidden')) {
- is_loaded = -1;
- } else {
- is_loaded++;
- }
- }
- });
- if (is_loaded === 0) {
- if (!$this.parent().hasClass('oe-open')) {
- self.getdata(record_id, children_ids);
- }
- } else {
- self.showcontent(record_id, is_loaded < 0);
- }
+ bool = $this.parent().hasClass('oe_open');
+ self.showcontent(record_id, !bool);
});
},
// get child data of selected value
- getdata: function (id, children_ids) {
+ getdata: function (id) {
var self = this;
-
- self.dataset.read_ids(children_ids, this.fields_list()).then(function(records) {
- _(records).each(function (record) {
- self.records[record.id] = record;
- });
-
- var $curr_node = self.$element.find('#treerow_' + id);
+ var parent_child ={};
+ id = _.isArray(id)?id:parseInt(id);
+ var ir_model_data = new instance.web.Model(this.model,self.dataset.get_context() || {},[['id','child_of',id]]).query();
+ ir_model_data._execute().then(function(records){
+ self.store_record(records);
+ _.each(records,function(rec){
+ if(rec[self.children_field].length === 0)return;
+ parent_child[rec.id] = [];
+ _.each(rec[self.children_field],function(key){
+ parent_child[rec.id].push(self.records[key]);
+ });
+ })
+ self.render_data(parent_child);
+ });
+ },
+ render_data: function(groupby){
+ var self = this;
+ _.each(_.keys(groupby),function(key){
+ var $curr_node = self.$el.find('#treerow_' + key);
+ var record = groupby[key];
var children_rows = QWeb.render('TreeView.rows', {
- 'records': records,
+ 'records': record,
'children_field': self.children_field,
'fields_view': self.fields_view.arch.children,
'fields': self.fields,
- 'level': $curr_node.data('level') || 0,
+ 'level': ($curr_node.data('level') || 0) + 1,
'render': instance.web.format_value,
'color_for': self.color_for
});
-
if ($curr_node.length) {
- $curr_node.addClass('oe-open');
+ $curr_node.addClass('oe_open');
$curr_node.after(children_rows);
} else {
- self.$element.find('tbody').html(children_rows);
+ self.$el.find('tbody').html(children_rows);
}
});
+ self.collpase_all();
},
+
// Get details in listview
activate: function(id) {
var self = this;
@@ -246,25 +272,17 @@ instance.web.TreeView = instance.web.View.extend(/** @lends instance.web.TreeVie
// show & hide the contents
showcontent: function (record_id, show) {
- this.$element.find('#treerow_' + record_id)
- .toggleClass('oe-open', show);
+ this.$el.find('#treerow_' + record_id)
+ .toggleClass('oe_open', show);
_(this.records[record_id][this.children_field]).each(function (child_id) {
- var $child_row = this.$element.find('#treerow_' + child_id);
- if ($child_row.hasClass('oe-open')) {
+ var $child_row = this.$el.find('#treerow_' + child_id);
+ if ($child_row.hasClass('oe_open')) {
this.showcontent(child_id, false);
}
$child_row.toggle(show);
}, this);
},
- do_show: function () {
- this.$element.show();
- },
-
- do_hide: function () {
- this.$element.hide();
- this.hidden = true;
- }
});
};
diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js
index 753e14fa251..9e7db823bc0 100644
--- a/addons/web/static/src/js/views.js
+++ b/addons/web/static/src/js/views.js
@@ -10,51 +10,189 @@ instance.web.ActionManager = instance.web.Widget.extend({
init: function(parent) {
this._super(parent);
this.inner_action = null;
- this.inner_viewmanager = null;
+ this.inner_widget = null;
this.dialog = null;
- this.dialog_viewmanager = null;
- this.client_widget = null;
+ this.dialog_widget = null;
+ this.breadcrumbs = [];
+ this.on('history_back', this, function() {
+ return this.history_back();
+ });
+ },
+ start: function() {
+ this._super.apply(this, arguments);
+ this.$el.on('click', 'a.oe_breadcrumb_item', this.on_breadcrumb_clicked);
},
dialog_stop: function () {
if (this.dialog) {
- this.dialog_viewmanager.destroy();
- this.dialog_viewmanager = null;
+ this.dialog_widget.destroy();
+ this.dialog_widget = null;
this.dialog.destroy();
this.dialog = null;
}
},
- content_stop: function () {
- if (this.inner_viewmanager) {
- this.inner_viewmanager.destroy();
- this.inner_viewmanager = null;
+ /**
+ * Add a new item to the breadcrumb
+ *
+ * If the title of an item is an array, the multiple title mode is in use.
+ * (eg: a widget with multiple views might need to display a title for each view)
+ * In multiple title mode, the show() callback can check the index it receives
+ * in order to detect which of its titles has been clicked on by the user.
+ *
+ * @param {Object} item breadcrumb item
+ * @param {Object} item.widget widget containing the view(s) to be added to the breadcrumb added
+ * @param {Function} [item.show] triggered whenever the widget should be shown back
+ * @param {Function} [item.hide] triggered whenever the widget should be shown hidden
+ * @param {Function} [item.destroy] triggered whenever the widget should be destroyed
+ * @param {String|Array} [item.title] title(s) of the view(s) to be displayed in the breadcrumb
+ * @param {Function} [item.get_title] should return the title(s) of the view(s) to be displayed in the breadcrumb
+ */
+ push_breadcrumb: function(item) {
+ var last = this.breadcrumbs.slice(-1)[0];
+ if (last) {
+ last.hide();
+ }
+ var item = _.extend({
+ show: function(index) {
+ this.widget.$el.show();
+ },
+ hide: function() {
+ this.widget.$el.hide();
+ },
+ destroy: function() {
+ this.widget.destroy();
+ },
+ get_title: function() {
+ return this.title || this.widget.get('title');
+ }
+ }, item);
+ item.id = _.uniqueId('breadcrumb_');
+ this.breadcrumbs.push(item);
+ },
+ history_back: function() {
+ var last = this.breadcrumbs.slice(-1)[0];
+ if (!last) {
+ return false;
+ }
+ var title = last.get_title();
+ if (_.isArray(title) && title.length > 1) {
+ return this.select_breadcrumb(this.breadcrumbs.length - 1, title.length - 2);
+ } else if (this.breadcrumbs.length === 1) {
+ // Only one single titled item in breadcrumb, most of the time you want to trigger back to home
+ return false;
+ } else {
+ var prev = this.breadcrumbs[this.breadcrumbs.length - 2];
+ title = prev.get_title();
+ return this.select_breadcrumb(this.breadcrumbs.length - 2, _.isArray(title) ? title.length - 1 : undefined);
+ }
+ },
+ on_breadcrumb_clicked: function(ev) {
+ var $e = $(ev.target);
+ var id = $e.data('id');
+ var index;
+ for (var i = this.breadcrumbs.length - 1; i >= 0; i--) {
+ if (this.breadcrumbs[i].id == id) {
+ index = i;
+ break;
+ }
+ }
+ var subindex = $e.parent().find('a.oe_breadcrumb_item[data-id=' + $e.data('id') + ']').index($e);
+ this.select_breadcrumb(index, subindex);
+ },
+ select_breadcrumb: function(index, subindex) {
+ for (var i = this.breadcrumbs.length - 1; i >= 0; i--) {
+ if (i > index) {
+ if (this.remove_breadcrumb(i) === false) {
+ return false;
+ }
+ }
+ }
+ var item = this.breadcrumbs[index];
+ item.show(subindex);
+ this.inner_widget = item.widget;
+ return true;
+ },
+ clear_breadcrumbs: function() {
+ for (var i = this.breadcrumbs.length - 1; i >= 0; i--) {
+ if (this.remove_breadcrumb(0) === false) {
+ break;
+ }
+ }
+ },
+ remove_breadcrumb: function(index) {
+ var item = this.breadcrumbs.splice(index, 1)[0];
+ if (item) {
+ var dups = _.filter(this.breadcrumbs, function(it) {
+ return item.widget === it.widget;
+ });
+ if (!dups.length) {
+ if (this.getParent().has_uncommitted_changes()) {
+ this.inner_widget = item.widget;
+ this.breadcrumbs.splice(index, 0, item);
+ return false;
+ } else {
+ item.destroy();
+ }
+ }
}
- if (this.client_widget) {
- this.client_widget.destroy();
- this.client_widget = null;
+ var last_widget = this.breadcrumbs.slice(-1)[0];
+ this.inner_widget = last_widget && last_widget.widget;
+ },
+ get_title: function() {
+ var titles = [];
+ for (var i = 0; i < this.breadcrumbs.length; i += 1) {
+ var item = this.breadcrumbs[i];
+ var tit = item.get_title();
+ if (!_.isArray(tit)) {
+ tit = [tit];
+ }
+ for (var j = 0; j < tit.length; j += 1) {
+ var label = _.escape(tit[j]);
+ if (i === this.breadcrumbs.length - 1 && j === tit.length - 1) {
+ titles.push(_.str.sprintf('<span class="oe_breadcrumb_item">%s</span>', label));
+ } else {
+ titles.push(_.str.sprintf('<a href="#" class="oe_breadcrumb_item" data-id="%s">%s</a>', item.id, label));
+ }
+ }
}
+ return titles.join(' <span class="oe_fade">/</span> ');
},
do_push_state: function(state) {
+ state = state || {};
if (this.getParent() && this.getParent().do_push_state) {
if (this.inner_action) {
- state['model'] = this.inner_action.res_model;
+ state['title'] = this.inner_action.name;
+ if(this.inner_action.type == 'ir.actions.act_window') {
+ state['model'] = this.inner_action.res_model;
+ }
if (this.inner_action.id) {
- state['action_id'] = this.inner_action.id;
+ state['action'] = this.inner_action.id;
+ } else if (this.inner_action.type == 'ir.actions.client') {
+ state['action'] = this.inner_action.tag;
+ //state = _.extend(this.inner_action.params || {}, state);
}
}
- this.getParent().do_push_state(state);
+ if(!this.dialog) {
+ this.getParent().do_push_state(state);
+ }
}
},
do_load_state: function(state, warm) {
var self = this,
action_loaded;
- if (state.action_id) {
- var run_action = (!this.inner_viewmanager) || this.inner_viewmanager.action.id !== state.action_id;
- if (run_action) {
+ if (state.action) {
+ if (_.isString(state.action) && instance.web.client_actions.contains(state.action)) {
+ var action_client = {type: "ir.actions.client", tag: state.action, params: state};
this.null_action();
- action_loaded = this.do_action(state.action_id);
- instance.webclient.menu.has_been_loaded.then(function() {
- instance.webclient.menu.open_action(state.action_id);
- });
+ 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;
+ if (run_action) {
+ this.null_action();
+ action_loaded = this.do_action(state.action);
+ instance.webclient.menu.has_been_loaded.then(function() {
+ instance.webclient.menu.open_action(state.action);
+ });
+ }
}
} else if (state.model && state.id) {
// TODO handle context & domain ?
@@ -68,38 +206,37 @@ instance.web.ActionManager = instance.web.Widget.extend({
action_loaded = this.do_action(action);
} else if (state.sa) {
// load session action
- var self = this;
this.null_action();
action_loaded = this.rpc('/web/session/get_session_action', {key: state.sa}).pipe(function(action) {
if (action) {
return self.do_action(action);
}
});
- } else if (state.client_action) {
- this.null_action();
- this.ir_actions_client(state.client_action);
}
$.when(action_loaded || null).then(function() {
- if (self.inner_viewmanager) {
- self.inner_viewmanager.do_load_state(state, warm);
+ if (self.inner_widget && self.inner_widget.do_load_state) {
+ self.inner_widget.do_load_state(state, warm);
}
});
},
- do_action: function(action, on_close) {
- if (_.isNumber(action)) {
+ do_action: function(action, on_close, clear_breadcrumbs) {
+ if (_.isString(action) && instance.web.client_actions.contains(action)) {
+ var action_client = { type: "ir.actions.client", tag: action };
+ return this.do_action(action_client, on_close, clear_breadcrumbs);
+ } else if (_.isNumber(action) || _.isString(action)) {
var self = this;
- return self.rpc("/web/action/load", { action_id: action }, function(result) {
- self.do_action(result.result, on_close);
+ return self.rpc("/web/action/load", { action_id: action }).pipe(function(result) {
+ return self.do_action(result.result, on_close, clear_breadcrumbs);
});
}
if (!action.type) {
console.error("No type for action", action);
- return;
+ return $.Deferred().reject();
}
var type = action.type.replace(/\./g,'_');
var popup = action.target === 'new';
- var inline = action.target === 'inline';
+ var inline = action.target === 'inline' || action.target === 'inlineview';
action.flags = _.extend({
views_switcher : !popup && !inline,
search_view : !popup && !inline,
@@ -110,47 +247,79 @@ instance.web.ActionManager = instance.web.Widget.extend({
}, action.flags || {});
if (!(type in this)) {
console.error("Action manager can't handle action of type " + action.type, action);
- return;
+ return $.Deferred().reject();
}
- return this[type](action, on_close);
+ return this[type](action, on_close, clear_breadcrumbs);
},
null_action: function() {
this.dialog_stop();
- this.content_stop();
- },
- ir_actions_act_window: function (action, on_close) {
- var self = this;
- if (_(['base.module.upgrade', 'base.setup.installer'])
- .contains(action.res_model)) {
- var old_close = on_close;
- on_close = function () {
- instance.webclient.do_reload().then(old_close);
+ this.clear_breadcrumbs();
+ },
+ ir_actions_common: function(action, on_close, clear_breadcrumbs) {
+ var self = this, klass, widget, post_process;
+ if (this.inner_widget && (action.type === 'ir.actions.client' || action.target !== 'new')) {
+ if (this.getParent().has_uncommitted_changes()) {
+ return $.Deferred().reject();
+ } else if (clear_breadcrumbs) {
+ this.clear_breadcrumbs();
+ }
+ }
+ if (action.type === 'ir.actions.client') {
+ var ClientWidget = instance.web.client_actions.get_object(action.tag);
+ widget = new ClientWidget(this, action.params);
+ klass = 'oe_act_client';
+ post_process = function() {
+ self.push_breadcrumb({
+ widget: widget,
+ title: action.name
+ });
+ if (action.tag !== 'reload') {
+ self.do_push_state({});
+ }
};
+ } else {
+ widget = new instance.web.ViewManagerAction(this, action);
+ klass = 'oe_act_window';
+ post_process = widget.proxy('add_breadcrumb');
}
if (action.target === 'new') {
- if (this.dialog == null) {
- this.dialog = new instance.web.Dialog(this, { width: '80%' });
+ if (this.dialog === null) {
+ // These buttons will be overwrited by <footer> if any
+ this.dialog = new instance.web.Dialog(this, {
+ buttons: { "Close": function() { $(this).dialog("close"); }},
+ dialogClass: klass
+ });
if(on_close)
this.dialog.on_close.add(on_close);
} else {
- this.dialog_viewmanager.destroy();
+ this.dialog_widget.destroy();
}
this.dialog.dialog_title = action.name;
- this.dialog_viewmanager = new instance.web.ViewManagerAction(this, action);
- this.dialog_viewmanager.appendTo(this.dialog.$element);
+ this.dialog_widget = widget;
+ this.dialog_widget.appendTo(this.dialog.$el);
this.dialog.open();
} else {
this.dialog_stop();
- this.content_stop();
+ this.inner_action = action;
+ this.inner_widget = widget;
+ post_process();
+ this.inner_widget.appendTo(this.$el);
+ }
+ },
+ ir_actions_act_window: function (action, on_close, clear_breadcrumbs) {
+ var self = this;
+ if (action.target !== 'new') {
if(action.menu_id) {
+ this.dialog_stop();
return this.getParent().do_action(action, function () {
instance.webclient.menu.open_menu(action.menu_id);
- });
+ }, clear_breadcrumbs);
}
- this.inner_action = action;
- this.inner_viewmanager = new instance.web.ViewManagerAction(this, action);
- this.inner_viewmanager.appendTo(this.$element);
}
+ return this.ir_actions_common(action, on_close, clear_breadcrumbs);
+ },
+ ir_actions_client: function (action, on_close, clear_breadcrumbs) {
+ return this.ir_actions_common(action, on_close, clear_breadcrumbs);
},
ir_actions_act_window_close: function (action, on_closed) {
if (!this.dialog && on_closed) {
@@ -158,24 +327,18 @@ instance.web.ActionManager = instance.web.Widget.extend({
}
this.dialog_stop();
},
- ir_actions_server: function (action, on_closed) {
+ ir_actions_server: function (action, on_closed, clear_breadcrumbs) {
var self = this;
this.rpc('/web/action/run', {
action_id: action.id,
context: action.context || {}
}).then(function (action) {
- self.do_action(action, on_closed)
+ self.do_action(action, on_closed, clear_breadcrumbs)
});
},
- ir_actions_client: function (action) {
- this.content_stop();
- this.dialog_stop();
- var ClientWidget = instance.web.client_actions.get_object(action.tag);
- (this.client_widget = new ClientWidget(this, action.params)).appendTo(this.$element);
- },
ir_actions_report_xml: function(action, on_closed) {
var self = this;
- $.blockUI();
+ instance.web.blockUI();
self.rpc("/web/session/eval_domain_and_context", {
contexts: [action.context],
domains: []
@@ -185,7 +348,7 @@ instance.web.ActionManager = instance.web.Widget.extend({
self.session.get_file({
url: '/web/report',
data: {action: JSON.stringify(action)},
- complete: $.unblockUI,
+ complete: instance.web.unblockUI,
success: function(){
if (!self.dialog && on_closed) {
on_closed();
@@ -199,9 +362,6 @@ instance.web.ActionManager = instance.web.Widget.extend({
ir_actions_act_url: function (action) {
window.open(action.url, action.target === 'self' ? '_self' : '_blank');
},
- ir_ui_menu: function (action) {
- this.getParent().do_action(action);
- }
});
instance.web.ViewManager = instance.web.Widget.extend({
@@ -235,7 +395,7 @@ instance.web.ViewManager = instance.web.Widget.extend({
start: function() {
this._super();
var self = this;
- this.$element.find('.oe_view_manager_switch a').click(function() {
+ this.$el.find('.oe_view_manager_switch a').click(function() {
self.on_mode_switch($(this).data('view-type'));
}).tipsy();
var views_ids = {};
@@ -244,9 +404,9 @@ instance.web.ViewManager = instance.web.Widget.extend({
deferred : $.Deferred(),
controller : null,
options : _.extend({
- $buttons : self.$element.find('.oe_view_manager_buttons'),
- $sidebar : self.flags.sidebar ? self.$element.find('.oe_view_manager_sidebar') : undefined,
- $pager : self.$element.find('.oe_view_manager_pager'),
+ $buttons : self.$el.find('.oe_view_manager_buttons'),
+ $sidebar : self.flags.sidebar ? self.$el.find('.oe_view_manager_sidebar') : undefined,
+ $pager : self.$el.find('.oe_view_manager_pager'),
action : self.action,
action_views_ids : views_ids
}, self.flags, self.flags[view.view_type] || {}, view.options || {})
@@ -254,7 +414,7 @@ instance.web.ViewManager = instance.web.Widget.extend({
views_ids[view.view_type] = view.view_id;
});
if (this.flags.views_switcher === false) {
- this.$element.find('.oe_view_manager_switch').hide();
+ this.$el.find('.oe_view_manager_switch').hide();
}
// 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;
@@ -271,8 +431,10 @@ instance.web.ViewManager = instance.web.Widget.extend({
var self = this;
var view = this.views[view_type];
var view_promise;
- if(!view)
+ var form = this.views['form'];
+ if (!view || (form && form.controller && !form.controller.can_be_discarded())) {
return $.Deferred().reject();
+ }
if (!no_store) {
this.views_history.push(view_type);
@@ -280,35 +442,7 @@ instance.web.ViewManager = instance.web.Widget.extend({
this.active_view = view_type;
if (!view.controller) {
- // Lazy loading of views
- var controllerclass = this.registry.get_object(view_type);
- var options = _.clone(view.options);
- if (view_type === "form" && this.action) {
- switch (this.action.target) {
- case 'new':
- case 'inline':
- options.initial_mode = 'edit';
- break;
- }
- }
- var controller = new controllerclass(this, this.dataset, view.view_id, options);
- if (view.embedded_view) {
- controller.set_embedded_view(view.embedded_view);
- }
- controller.do_switch_view.add_last(_.bind(this.switch_view, this));
- controller.do_prev_view.add_last(this.on_prev_view);
- var container = this.$element.find(".oe_view_manager_view_" + view_type);
- view_promise = controller.appendTo(container);
- this.views[view_type].controller = controller;
- this.views[view_type].deferred.resolve(view_type);
- $.when(view_promise).then(function() {
- self.on_controller_inited(view_type, controller);
- if (self.searchview
- && self.flags.auto_search
- && view.controller.searchable !== false) {
- self.searchview.ready.then(self.searchview.do_search);
- }
- });
+ view_promise = this.do_create_view(view_type);
} else if (this.searchview
&& self.flags.auto_search
&& view.controller.searchable !== false) {
@@ -319,28 +453,128 @@ instance.web.ViewManager = instance.web.Widget.extend({
this.searchview[(view.controller.searchable === false || this.searchview.hidden) ? 'hide' : 'show']();
}
- this.$element
- .find('.oe_view_manager_switch a').parent().removeClass('active')
- this.$element
+ this.$el
+ .find('.oe_view_manager_switch a').parent().removeClass('active');
+ this.$el
.find('.oe_view_manager_switch a').filter('[data-view-type="' + view_type + '"]')
.parent().addClass('active');
- $.when(view_promise).then(function () {
+ return $.when(view_promise).then(function () {
_.each(_.keys(self.views), function(view_name) {
var controller = self.views[view_name].controller;
if (controller) {
+ var container = self.$el.find(".oe_view_manager_view_" + view_name + ":first");
if (view_name === view_type) {
+ container.show();
controller.do_show(view_options || {});
} else {
+ container.hide();
controller.do_hide();
}
+ // put the <footer> in the dialog's buttonpane
+ if (self.$el.parent('.ui-dialog-content') && self.$el.find('footer')) {
+ self.$el.parent('.ui-dialog-content').parent().find('div.ui-dialog-buttonset').hide()
+ self.$el.find('footer').appendTo(
+ self.$el.parent('.ui-dialog-content').parent().find('div.ui-dialog-buttonpane')
+ );
+ }
}
});
+ });
+ },
+ do_create_view: function(view_type) {
+ // Lazy loading of views
+ var self = this;
+ var view = this.views[view_type];
+ var viewclass = this.registry.get_object(view_type);
+ var options = _.clone(view.options);
+ if (view_type === "form" && this.action && (this.action.target == 'new' || this.action.target == 'inline')) {
+ options.initial_mode = 'edit';
+ }
+ var controller = new viewclass(this, this.dataset, view.view_id, options);
+
+ controller.on('history_back', this, function() {
+ var am = self.getParent();
+ if (am && am.trigger) {
+ return am.trigger('history_back');
+ }
+ });
- self.$element.find('.oe_view_title_text:first').text(
- self.display_title());
+ controller.on("change:title", this, function() {
+ if (self.active_view === view_type) {
+ self.set_title(controller.get('title'));
+ }
+ });
+
+ if (view.embedded_view) {
+ controller.set_embedded_view(view.embedded_view);
+ }
+ controller.do_switch_view.add_last(_.bind(this.switch_view, this));
+
+ controller.do_prev_view.add_last(this.on_prev_view);
+ var container = this.$el.find(".oe_view_manager_view_" + view_type);
+ var view_promise = controller.appendTo(container);
+ this.views[view_type].controller = controller;
+ this.views[view_type].deferred.resolve(view_type);
+ return $.when(view_promise).then(function() {
+ self.on_controller_inited(view_type, controller);
+ if (self.searchview
+ && self.flags.auto_search
+ && view.controller.searchable !== false) {
+ self.searchview.ready.then(self.searchview.do_search);
+ }
+ });
+ },
+ set_title: function(title) {
+ this.$el.find('.oe_view_title_text:first').text(title);
+ },
+ add_breadcrumb: function() {
+ var self = this;
+ var views = [this.active_view || this.views_src[0].view_type];
+ this.on_mode_switch.add(function(mode) {
+ var last = views.slice(-1)[0];
+ if (mode !== last) {
+ if (mode !== 'form') {
+ views.length = 0;
+ }
+ views.push(mode);
+ }
+ });
+ this.getParent().push_breadcrumb({
+ widget: this,
+ action: this.action,
+ show: function(index) {
+ var view_to_select = views[index];
+ self.$el.show();
+ if (self.active_view !== view_to_select) {
+ self.on_mode_switch(view_to_select);
+ }
+ },
+ get_title: function() {
+ var id;
+ var currentIndex;
+ _.each(self.getParent().breadcrumbs, function(bc, i) {
+ if (bc.widget === self) {
+ currentIndex = i;
+ }
+ });
+ var next = self.getParent().breadcrumbs.slice(currentIndex + 1)[0];
+ var titles = _.map(views, function(v) {
+ var controller = self.views[v].controller;
+ if (v === 'form') {
+ id = controller.datarecord.id;
+ }
+ return controller.get('title');
+ });
+ if (next && next.action && next.action.res_id && self.dataset &&
+ self.active_view === 'form' && self.dataset.model === next.action.res_model && id === next.action.res_id) {
+ // If the current active view is a formview and the next item in the breadcrumbs
+ // is an action on same object (model / res_id), then we omit the current formview's title
+ titles.pop();
+ }
+ return titles;
+ }
});
- return view_promise;
},
/**
* Method used internally when a view asks to switch view. This method is meant
@@ -388,7 +622,7 @@ instance.web.ViewManager = instance.web.Widget.extend({
this.searchview = new instance.web.SearchView(this, this.dataset, view_id, search_defaults, this.flags.search_view === false);
this.searchview.on_search.add(this.do_searchview_search);
- return this.searchview.appendTo(this.$element.find(".oe_view_manager_view_search"));
+ return this.searchview.appendTo(this.$el.find(".oe_view_manager_view_search"));
},
do_searchview_search: function(domains, contexts, groupbys) {
var self = this,
@@ -434,14 +668,6 @@ instance.web.ViewManager = instance.web.Widget.extend({
*/
on_action_executed: function () {
},
- display_title: function () {
- var view = this.views[this.active_view];
- if (view) {
- // ick
- return view.controller.fields_view.arch.attrs.string;
- }
- return '';
- }
});
instance.web.ViewManagerAction = instance.web.ViewManager.extend({
@@ -486,7 +712,7 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({
if (this.session.hidden_menutips) {
return;
}
- this.session.hidden_menutips = {}
+ this.session.hidden_menutips = {};
},
/**
* Initializes the ViewManagerAction: sets up the searchview (if the
@@ -513,33 +739,8 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({
var manager_ready = $.when(searchview_loaded, main_view_loaded);
- this.$element.find('.oe_debug_view').change(this.on_debug_changed);
-
- if (this.action.help && !this.flags.low_profile) {
- var Users = new instance.web.DataSet(self, 'res.users'),
- $tips = this.$element.find('.oe_view_manager_menu_tips');
- $tips.delegate('blockquote button', 'click', function() {
- var $this = $(this);
- //noinspection FallthroughInSwitchStatementJS
- switch ($this.attr('name')) {
- case 'disable':
- Users.write(self.session.uid, {menu_tips:false});
- case 'hide':
- $this.closest('blockquote').hide();
- self.session.hidden_menutips[self.action.id] = true;
- }
- });
- if (!(self.action.id in self.session.hidden_menutips)) {
- Users.read_ids([this.session.uid], ['menu_tips']).then(function(users) {
- var user = users[0];
- if (!(user && user.id === self.session.uid)) {
- return;
- }
- $tips.find('blockquote').toggle(user.menu_tips);
- });
- }
- }
-
+ this.$el.find('.oe_debug_view').change(this.on_debug_changed);
+ this.$el.addClass("oe_view_manager_" + (this.action.target || 'current'));
return manager_ready;
},
on_debug_changed: function (evt) {
@@ -551,7 +752,15 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({
switch (val) {
case 'fvg':
var dialog = new instance.web.Dialog(this, { title: _t("Fields View Get"), width: '95%' }).open();
- $('<pre>').text(instance.web.json_node_to_xml(current_view.fields_view.arch, true)).appendTo(dialog.$element);
+ $('<pre>').text(instance.web.json_node_to_xml(current_view.fields_view.arch, true)).appendTo(dialog.$el);
+ break;
+ case 'tests':
+ this.do_action({
+ name: "JS Tests",
+ target: 'new',
+ type : 'ir.actions.act_url',
+ url: '/web/static/test/test.html'
+ })
break;
case 'perm_read':
var ids = current_view.get_selected_ids();
@@ -570,6 +779,17 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({
case 'toggle_layout_outline':
current_view.rendering_engine.toggle_layout_debugging();
break;
+ case 'translate':
+ this.do_action({
+ name: "Technical Translation",
+ res_model : 'ir.translation',
+ domain : [['type', '!=', 'object'], '|', ['name', '=', this.dataset.model], ['name', 'ilike', this.dataset.model + ',']],
+ views: [[false, 'list'], [false, 'form']],
+ type : 'ir.actions.act_window',
+ view_type : "list",
+ view_mode : "list"
+ });
+ break;
case 'fields':
this.dataset.call_and_eval(
'fields_get', [false, {}], null, 1).then(function (fields) {
@@ -593,15 +813,6 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({
width: '95%'}, $root).open();
});
break;
- case 'manage_views':
- if (current_view.fields_view && current_view.fields_view.arch) {
- var view_editor = new instance.web.ViewEditor(current_view, current_view.$element, this.dataset, current_view.fields_view.arch);
- view_editor.start();
- } else {
- this.do_warn(_t("Manage Views"),
- _t("Could not find current view declaration"));
- }
- break;
case 'edit_workflow':
return this.do_action({
res_model : 'workflow',
@@ -626,6 +837,21 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({
}
});
break;
+ case 'print_workflow':
+ if (current_view.get_selected_ids && current_view.get_selected_ids().length == 1) {
+ instance.web.blockUI();
+ var action = {
+ context: { active_ids: current_view.get_selected_ids() },
+ report_name: "workflow.instance.graph",
+ datas: {
+ model: this.dataset.model,
+ id: current_view.get_selected_ids()[0],
+ nested: true,
+ }
+ };
+ this.session.get_file({ url: '/web/report', data: {action: JSON.stringify(action)}, complete: instance.web.unblockUI });
+ }
+ break;
default:
if (val) {
console.log("No debug handler for ", val);
@@ -658,16 +884,22 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({
var controller = self.views[self.active_view].controller,
fvg = controller.fields_view,
view_id = (fvg && fvg.view_id) || '--';
- self.$element.find('.oe_debug_view').html(QWeb.render('ViewManagerDebug', {
+ self.$el.find('.oe_debug_view').html(QWeb.render('ViewManagerDebug', {
view: controller,
view_manager: self
}));
- if (!self.action.name && fvg) {
- self.$element.find('.oe_view_title_text').text(fvg.arch.attrs.string || fvg.name);
- }
-
+ self.set_title();
});
},
+ do_create_view: function(view_type) {
+ var r = this._super.apply(this, arguments);
+ var view = this.views[view_type].controller;
+ view.set({ 'title': this.action.name });
+ return r;
+ },
+ set_title: function(title) {
+ this.$el.find('.oe_breadcrumb_title:first').html(this.getParent().get_title());
+ },
do_push_state: function(state) {
if (this.getParent() && this.getParent().do_push_state) {
state["view_type"] = this.active_view;
@@ -689,42 +921,39 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({
self.views[self.active_view].controller.do_load_state(state, warm);
});
},
- display_title: function () {
- return this.action.name;
- }
});
instance.web.Sidebar = instance.web.Widget.extend({
init: function(parent) {
+ var self = this;
this._super(parent);
var view = this.getParent();
this.sections = [
{ 'name' : 'print', 'label' : _t('Print'), },
- { 'name' : 'files', 'label' : _t('Attachment'), },
{ 'name' : 'other', 'label' : _t('More'), }
];
this.items = {
'print' : [],
- 'files' : [],
'other' : []
};
- if (this.session.uid === 1) {
- var item = { label: _t("Translate"), callback: view.on_sidebar_translate, title: _t("Technical translation") };
- this.items.other.push(item);
- }
+ this.fileupload_id = _.uniqueId('oe_fileupload');
+ $(window).on(this.fileupload_id, function() {
+ var args = [].slice.call(arguments).slice(1);
+ if (args[0] && args[0].error) {
+ alert(args[0].error);
+ } else {
+ self.do_attachement_update(self.dataset, self.model_id);
+ }
+ instance.web.unblockUI();
+ });
},
start: function() {
var self = this;
this._super(this);
this.redraw();
- this.$element.on('click','.oe_dropdown_toggle',function(event) {
- $(this).parent().find('ul').toggle();
- return false;
- });
- this.$element.on('click','.oe_dropdown_menu li a', function(event) {
+ this.$el.on('click','.oe_dropdown_menu li a', function(event) {
var section = $(this).data('section');
var index = $(this).data('index');
- $(this).closest('ul').hide();
var item = self.items[section][index];
if (item.callback) {
item.callback.apply(self, [item]);
@@ -733,16 +962,12 @@ instance.web.Sidebar = instance.web.Widget.extend({
} else if (item.url) {
return true;
}
- return false;
+ event.preventDefault();
});
- //this.$div.html(QWeb.render('FormView.sidebar.attachments', this));
- //this.$element.find('.oe-binary-file').change(this.on_attachment_changed);
- //this.$element.find('.oe-sidebar-attachment-delete').click(this.on_attachment_delete);
},
redraw: function() {
var self = this;
- self.$element.html(QWeb.render('Sidebar', {widget: self}));
- this.$element.find('ul').hide();
+ self.$el.html(QWeb.render('Sidebar', {widget: self}));
// Hides Sidebar sections when item list is empty
this.$('.oe_form_dropdown_section').each(function() {
@@ -826,6 +1051,8 @@ instance.web.Sidebar = instance.web.Widget.extend({
});
},
do_attachement_update: function(dataset, model_id) {
+ this.dataset = dataset;
+ this.model_id = model_id;
if (!model_id) {
this.on_attachments_loaded([]);
} else {
@@ -837,165 +1064,43 @@ instance.web.Sidebar = instance.web.Widget.extend({
on_attachments_loaded: function(attachments) {
var self = this;
var items = [];
- // TODO: preprend: _s +
- var prefix = '/web/binary/saveas?session_id=' + self.session.session_id + '&model=ir.attachment&field=datas&filename_field=name&id=';
+ var prefix = this.session.origin + '/web/binary/saveas?session_id=' + self.session.session_id + '&model=ir.attachment&field=datas&filename_field=name&id=';
_.each(attachments,function(a) {
a.label = a.name;
if(a.type === "binary") {
a.url = prefix + a.id + '&t=' + (new Date().getTime());
}
});
- attachments.push( { label: _t("Add..."), callback: self.on_attachment_add } );
self.items['files'] = attachments;
self.redraw();
- },
- on_attachment_add: function(e) {
- this.$element.find('.oe_sidebar_add').show();
+ this.$('.oe_sidebar_add_attachment .oe_form_binary_file').change(this.on_attachment_changed);
+ this.$el.find('.oe_sidebar_delete_item').click(this.on_attachment_delete);
},
on_attachment_changed: function(e) {
- return;
- window[this.element_id + '_iframe'] = this.do_update;
var $e = $(e.target);
- if ($e.val() != '') {
- this.$element.find('form.oe-binary-form').submit();
+ if ($e.val() !== '') {
+ this.$el.find('form.oe_form_binary_form').submit();
$e.parent().find('input[type=file]').prop('disabled', true);
$e.parent().find('button').prop('disabled', true).find('img, span').toggle();
+ this.$('.oe_sidebar_add_attachment span').text(_t('Uploading...'));
+ instance.web.blockUI();
}
},
on_attachment_delete: function(e) {
- return;
- var self = this, $e = $(e.currentTarget);
- var name = _.str.trim($e.parent().find('a.oe-sidebar-attachments-link').text());
- if (confirm(_.str.sprintf(_t("Do you really want to delete the attachment %s?"), name))) {
- this.rpc('/web/dataset/unlink', {
- model: 'ir.attachment',
- ids: [parseInt($e.attr('data-id'))]
- }, function(r) {
- $e.parent().remove();
- self.do_notify("Delete an attachment", "The attachment '" + name + "' has been deleted");
- });
- }
- }
-});
-
-instance.web.TranslateDialog = instance.web.Dialog.extend({
- dialog_title: {toString: function () { return _t("Translations"); }},
- init: function(view) {
- // TODO fme: should add the language to fields_view_get because between the fields view get
- // and the moment the user opens the translation dialog, the user language could have been changed
- this.view_language = view.session.user_context.lang;
- this['on_button' + _t("Save")] = this.on_button_Save;
- this['on_button' + _t("Close")] = this.on_button_Close;
- this._super(view, {
- width: '80%',
- height: '80%'
- });
- this.view = view;
- this.view_type = view.fields_view.type || '';
- this.$fields_form = null;
- this.$view_form = null;
- this.$sidebar_form = null;
- this.translatable_fields_keys = _.map(this.view.translatable_fields || [], function(i) { return i.name });
- this.languages = null;
- this.languages_loaded = $.Deferred();
- (new instance.web.DataSetSearch(this, 'res.lang', this.view.dataset.get_context(),
- [['translatable', '=', '1']])).read_slice(['code', 'name'], { sort: 'id' }).then(this.on_languages_loaded);
- },
- start: function() {
var self = this;
- this._super();
- $.when(this.languages_loaded).then(function() {
- self.$element.html(instance.web.qweb.render('TranslateDialog', { widget: self }));
- self.$fields_form = self.$element.find('.oe_translation_form');
- self.$fields_form.find('.oe_trad_field').change(function() {
- $(this).toggleClass('touched', ($(this).val() != $(this).attr('data-value')));
- });
- });
- return this;
- },
- on_languages_loaded: function(langs) {
- this.languages = langs;
- this.languages_loaded.resolve();
- },
- do_load_fields_values: function(callback) {
- var self = this,
- deffered = [];
- this.$fields_form.find('.oe_trad_field').val('').removeClass('touched');
- _.each(self.languages, function(lg) {
- var deff = $.Deferred();
- deffered.push(deff);
- var callback = function(values) {
- _.each(self.translatable_fields_keys, function(f) {
- self.$fields_form.find('.oe_trad_field[name="' + lg.code + '-' + f + '"]').val(values[0][f] || '').attr('data-value', values[0][f] || '');
- });
- deff.resolve();
- };
- if (lg.code === self.view_language) {
- var values = {};
- _.each(self.translatable_fields_keys, function(field) {
- values[field] = self.view.fields[field].get_value();
- });
- callback([values]);
- } else {
- self.rpc('/web/dataset/get', {
- model: self.view.dataset.model,
- ids: [self.view.datarecord.id],
- fields: self.translatable_fields_keys,
- context: self.view.dataset.get_context({
- 'lang': lg.code
- })}, callback);
- }
- });
- $.when.apply(null, deffered).then(callback);
- },
- open: function(field) {
- var self = this,
- sup = this._super;
- $.when(this.languages_loaded).then(function() {
- if (self.view.translatable_fields && self.view.translatable_fields.length) {
- self.do_load_fields_values(function() {
- sup.call(self);
- if (field) {
- var $field_input = self.$element.find('tr[data-field="' + field.name + '"] td:nth-child(2) *:first-child');
- self.$element.scrollTo($field_input);
- $field_input.focus();
- }
- });
- } else {
- sup.call(self);
- }
- });
- },
- on_button_Save: function() {
- var trads = {},
- self = this,
- trads_mutex = new $.Mutex();
- self.$fields_form.find('.oe_trad_field.touched').each(function() {
- var field = $(this).attr('name').split('-');
- if (!trads[field[0]]) {
- trads[field[0]] = {};
- }
- trads[field[0]][field[1]] = $(this).val();
- });
- _.each(trads, function(data, code) {
- if (code === self.view_language) {
- _.each(data, function(value, field) {
- self.view.fields[field].set_value(value);
- });
- }
- trads_mutex.exec(function() {
- return self.view.dataset.write(self.view.datarecord.id, data, { context : { 'lang': code } });
+ e.preventDefault();
+ e.stopPropagation();
+ var self = this;
+ var $e = $(e.currentTarget);
+ if (confirm(_t("Do you really want to delete this attachment ?"))) {
+ (new instance.web.DataSet(this, 'ir.attachment')).unlink([parseInt($e.attr('data-id'), 10)]).then(function() {
+ self.do_attachement_update(self.dataset, self.model_id);
});
- });
- this.close();
- },
- on_button_Close: function() {
- this.close();
+ }
}
});
instance.web.View = instance.web.Widget.extend({
- template: "EmptyComponent",
// name displayed in view switchers
display_name: '',
/**
@@ -1008,7 +1113,7 @@ instance.web.View = instance.web.Widget.extend({
this.view_id = view_id;
this.set_default_options(options);
},
- start: function() {
+ start: function () {
return this.load_view();
},
load_view: function() {
@@ -1046,12 +1151,6 @@ instance.web.View = instance.web.Widget.extend({
action_views_ids: {}
});
},
- open_translate_dialog: function(field) {
- if (!this.translate_dialog) {
- this.translate_dialog = new instance.web.TranslateDialog(this).start();
- }
- this.translate_dialog.open(field);
- },
/**
* Fetches and executes the action identified by ``action_data``.
*
@@ -1116,11 +1215,11 @@ instance.web.View = instance.web.Widget.extend({
}
}
args.push(context);
- return dataset.call_button(action_data.name, args, handler);
+ return dataset.call_button(action_data.name, args).then(handler);
} else if (action_data.type=="action") {
- return this.rpc('/web/action/load', { action_id: parseInt(action_data.name, 10), context: context, do_not_eval: true}, handler);
+ return this.rpc('/web/action/load', { action_id: action_data.name, context: context, do_not_eval: true}, handler);
} else {
- return dataset.exec_workflow(record_id, action_data.name, handler);
+ return dataset.exec_workflow(record_id, action_data.name).then(handler);
}
},
/**
@@ -1134,10 +1233,10 @@ instance.web.View = instance.web.Widget.extend({
this.embedded_view = embedded_view;
},
do_show: function () {
- this.$element.show();
+ this.$el.show();
},
do_hide: function () {
- this.$element.hide();
+ this.$el.hide();
},
do_push_state: function(state) {
if (this.getParent() && this.getParent().do_push_state) {
@@ -1164,23 +1263,8 @@ instance.web.View = instance.web.Widget.extend({
},
do_search: function(view) {
},
- on_sidebar_import: function() {
- var import_view = new instance.web.DataImport(this, this.dataset);
- import_view.start();
- },
on_sidebar_export: function() {
- var export_view = new instance.web.DataExport(this, this.dataset);
- export_view.start();
- },
- on_sidebar_translate: function() {
- return this.do_action({
- res_model : 'ir.translation',
- domain : [['type', '!=', 'object'], '|', ['name', '=', this.dataset.model], ['name', 'ilike', this.dataset.model + ',']],
- views: [[false, 'list'], [false, 'form']],
- type : 'ir.actions.act_window',
- view_type : "list",
- view_mode : "list"
- });
+ new instance.web.DataExport(this, this.dataset).open();
},
sidebar_context: function () {
return $.when();
@@ -1191,6 +1275,15 @@ instance.web.View = instance.web.Widget.extend({
*/
reload: function () {
return $.when();
+ },
+ /**
+ * Return whether the user can perform the action ('create', 'edit', 'delete') in this view.
+ * An action is disabled by setting the corresponding attribute in the view's main element,
+ * like: <form string="" create="false" edit="false" delete="false">
+ */
+ is_action_enabled: function(action) {
+ var attrs = this.fields_view.arch.attrs;
+ return (action in attrs) ? JSON.parse(attrs[action]) : true;
}
});
@@ -1218,7 +1311,6 @@ instance.web.xml_to_json = function(node) {
}
instance.web.json_node_to_xml = function(node, human_readable, indent) {
// For debugging purpose, this function will convert a json node back to xml
- // Maybe useful for xml view editor
indent = indent || 0;
var sindent = (human_readable ? (new Array(indent + 1).join('\t')) : ''),
r = sindent + '<' + node.tag,
@@ -1227,7 +1319,9 @@ instance.web.json_node_to_xml = function(node, human_readable, indent) {
if (typeof(node) === 'string') {
return sindent + node;
} else if (typeof(node.tag) !== 'string' || !node.children instanceof Array || !node.attrs instanceof Object) {
- throw("Node a json node");
+ throw new Error(
+ _.str.sprintf("Node [%s] is not a JSONified XML node",
+ JSON.stringify(node)));
}
for (var attr in node.attrs) {
var vattr = node.attrs[attr];
diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml
index 3764395dc13..fdf39633186 100644
--- a/addons/web/static/src/xml/base.xml
+++ b/addons/web/static/src/xml/base.xml
@@ -25,11 +25,18 @@
</div>
</div>
</t>
+<t t-name="Tipsy.alert">
+ <a class="oe_tooltip_close oe_e">[</a>
+ <span style="float:left; margin:2px 5px 0 0;" class="ui-icon ui-icon-alert ui-state-error"></span>
+ <div class="oe_tooltip_message">
+ <t t-esc="message"/>
+ </div>
+</t>
<t t-name="CrashManager.warning">
- <table cellspacing="0" cellpadding="0" border="0" class="oe-dialog-warning">
+ <table cellspacing="0" cellpadding="0" border="0" class="oe_dialog_warning">
<tr>
- <td><img t-att-src='_s + "/web/static/src/img/warning.png"' class="oe-dialog-icon"/></td>
+ <td><img t-att-src='_s + "/web/static/src/img/warning.png"' class="oe_dialog_icon"/></td>
<td>
<p>
<t t-js="d">
@@ -44,29 +51,6 @@
</table>
</t>
<t t-name="CrashManager.error">
- <t t-if="!session.openerp_entreprise">
- <span>Your version of OpenERP is unsupported. Support &amp; maintenance services are available here: <a href="http://www.openerp.com/support-or-publisher-warranty-contract" target="_blank">OpenERP Entreprise</a>.</span>
- </t>
- <t t-if="session.openerp_entreprise">
- <div class="oe_error_send">
- <div>
- <div class="oe_centeralign"><b>OpenERP Enterprise Contract.</b></div>
- <div><br/>Your report will be sent to the OpenERP Enterprise team.<br/></div><br/>
- <div>
- <label>Summary:</label><br/>
- <input id="issuename" type="text" class="oe_fielddiv"/>
- </div><br/><br/>
- <div>
- <label>Description:</label><br/>
- <textarea id="explanation" rows="6"></textarea>
- </div><br/><br/>
- <div>
- <label>What you did:</label><br/>
- <textarea id="remark" rows="6" ></textarea>
- </div>
- </div>
- </div><br/>
- </t>
<div class="oe_error_detail">
<pre><t t-esc="error.message"/></pre>
<hr/>
@@ -77,7 +61,7 @@
<t t-name="Login">
<div class="oe_login">
<div class="oe_login_bottom"> </div>
- <div class="oe_login_error_message">Invalid username or password</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">
@@ -95,7 +79,7 @@
</form>
<div class="oe_login_footer">
<a href="#" class="oe_login_manage_db">Manage Databases</a> |
- <a href="http://www.openerp.com">Powered by <span>OpenERP</span></a>
+ <a href="http://www.openerp.com" target="_blank">Powered by <span>OpenERP</span></a>
</div>
</div>
</div>
@@ -114,68 +98,80 @@
</select>
</t>
+
<t t-name="DatabaseManager">
- <div class="oe_database_manager">
- <div class="oe_database_manager_menu">
- <ul class="oe_form_notebook">
- <li><a href="#db_create">Create</a></li>
- <li><a href="#db_drop">Drop</a></li>
- <li><a href="#db_backup">Backup</a></li>
- <li><a href="#db_restore">Restore</a></li>
- <li><a href="#db_change_password">Password</a></li>
- <li><a id="back_to_login" href="#">Back to Login</a></li>
- </ul>
- <form id="db_create" name="create_db_form" method="POST">
+ <div class="oe_view_manager_view_form">
+ <div class="oe_form">
+ <form id="db_create" name="create_db_form" style="display: block;">
+ <div class="oe_view_manager oe_view_manager_current">
+ <div class="oe_view_manager_header" style="padding: 8px;">
+ <div class="oe_header_row">
+ <h2 class="oe_view_title">
+ <span class="oe_view_title_text oe_breadcrumb_title">Create Database</span>
+ </h2>
+ <button type="submit" class="oe_button oe_highlight db_create">Create</button>
+ </div>
+ </div>
+ </div>
<table align="center" class="db_option_table">
<tr>
- <th colspan="2" class="option_string"> CREATE DATABASE </th>
- </tr>
- <tr>
<td><label for="super_admin_pwd">Master password:</label></td>
- <td><input type="password" name="super_admin_pwd" class="required" value="admin"/></td>
+ <td><input type="password" name="super_admin_pwd" class="required" value="admin" /></td>
</tr>
<tr>
<td><label for="db_name">New database name:</label></td>
- <td><input type="text" name="db_name" class="required" matches="^[a-zA-Z][a-zA-Z0-9_]+$" autofocus="true"/></td>
+ <td><input type="text" name="db_name" class="required" matches="^[a-zA-Z][a-zA-Z0-9_-]+$" autofocus="true"/></td>
</tr>
<tr>
<td><label for="demo_data">Load Demonstration data:</label></td>
- <td><input type="checkbox" name="demo_data"/></td>
+ <td class="oe_form_group_cell">
+ <span class="oe_form_field oe_form_field_boolean">
+ <input type="checkbox" name="demo_data" />
+ </span>
+ </td>
</tr>
<tr>
<td><label for="db_lang">Default language:</label></td>
- <td>
+ <td class="oe_form_field oe_form_field_selection ">
<select name="db_lang" t-if="widget.lang_list">
<t t-foreach="widget.lang_list" t-as="lang">
- <option t-att-value="lang[0]" t-att-selected="lang[0] === 'en_US' ? 'selected' : undefined"><t t-esc="lang[1]"/></option>
+ <option t-att-value="lang[0]" t-att-selected="lang[0] === 'en_US' ? 'selected' : undefined">
+ <t t-esc="lang[1]" />
+ </option>
</t>
</select>
</td>
</tr>
<tr>
<td><label for="create_admin_pwd">Admin password:</label></td>
- <td><input type="password" name="create_admin_pwd" class="required"/></td>
+ <td><input type="password" name="create_admin_pwd" class="required" /></td>
</tr>
<tr>
<td><label for="create_confirm_pwd">Confirm password:</label></td>
<td><input type="password" name="create_confirm_pwd" class="required" equalTo="input[name=create_admin_pwd]"/></td>
</tr>
- <tr>
- <td colspan="2" align="right"><button class="oe_button">Create</button></td>
- </tr>
</table>
</form>
- <form id="db_drop" name="drop_db_form" method="POST">
+ <form id="db_drop" name="drop_db_form" style="display: none; ">
+ <div class="oe_view_manager oe_view_manager_current">
+ <div class="oe_view_manager_header" style="padding: 8px;">
+ <div class="oe_header_row">
+ <h2 class="oe_view_title">
+ <span class="oe_view_title_text oe_breadcrumb_title">Drop Database</span>
+ </h2>
+ <button type="submit" class="oe_button oe_highlight db_drop">Drop</button>
+ </div>
+ </div>
+ </div>
<table align="center" class="db_option_table">
<tr>
- <th colspan="2" class="option_string"> DROP DATABASE </th>
- </tr>
- <tr>
<td><label for="drop_db">Database:</label></td>
- <td>
+ <td class="oe_form_field oe_form_field_selection">
<select t-if="widget.db_list" name="drop_db" autofocus="autofocus">
<t t-foreach="widget.db_list" t-as="db">
- <option t-att-value="db"><t t-esc="db"/></option>
+ <option t-att-value="db">
+ <t t-esc="db" />
+ </option>
</t>
</select>
<input t-if="!widget.db_list" name="drop_db" class="required" type="text" autofocus="autofocus"/>
@@ -183,25 +179,31 @@
</tr>
<tr>
<td><label for="drop_password">Master Password:</label></td>
- <td><input type="password" name="drop_pwd" class="required"/></td>
- </tr>
- <tr>
- <td colspan="2" align="right"><button class="oe_button">Drop</button></td>
+ <td><input type="password" name="drop_pwd" class="required" /></td>
</tr>
</table>
</form>
- <form id="db_backup" name="backup_db_form" method="POST" target="backup-target" action="/web/database/backup">
- <input type="hidden" name="token"/>
+ <form id="db_backup" name="backup_db_form" target="backup-target" action="/web/database/backup" style="display: none;">
+ <div class="oe_view_manager oe_view_manager_current">
+ <div class="oe_view_manager_header" style="padding: 8px;">
+ <div class="oe_header_row">
+ <h2 class="oe_view_title">
+ <span class="oe_view_title_text oe_breadcrumb_title">Backup Database</span>
+ </h2>
+ <button type="submit" class="oe_button oe_highlight db_backup">Backup</button>
+ </div>
+ </div>
+ </div>
+ <input type="hidden" name="token" />
<table align="center" class="db_option_table">
<tr>
- <th colspan="2" class="option_string"> BACKUP DATABASE </th>
- </tr>
- <tr>
<td><label for="backup_db">Database:</label></td>
- <td>
+ <td class="oe_form_field oe_form_field_selection ">
<select t-if="widget.db_list" name="backup_db" autofocus="autofocus">
<t t-foreach="widget.db_list" t-as="db">
- <option t-att-value="db"><t t-esc="db"/></option>
+ <option t-att-value="db">
+ <t t-esc="db" />
+ </option>
</t>
</select>
<input t-if="!widget.db_list" name="backup_db" class="required" type="text" autofocus="autofocus"/>
@@ -209,19 +211,23 @@
</tr>
<tr>
<td><label for="backup_pwd">Master Password:</label></td>
- <td><input type="password" name="backup_pwd" class="required"/></td>
- </tr>
- <tr>
- <td colspan="2" align="right"><button class="oe_button">Backup</button></td>
+ <td><input type="password" name="backup_pwd" class="required" /></td>
</tr>
</table>
</form>
- <form id="db_restore" name="restore_db_form" method="POST">
+ <form id="db_restore" name="restore_db_form" style="display: none; ">
+ <div class="oe_view_manager oe_view_manager_current">
+ <div class="oe_view_manager_header" style="padding: 8px;">
+ <div class="oe_header_row">
+ <h2 class="oe_view_title">
+ <span class="oe_view_title_text oe_breadcrumb_title">Restore Database</span>
+ </h2>
+ <button type="submit" class="oe_button oe_highlight db_restore">Restore</button>
+ </div>
+ </div>
+ </div>
<table align="center" class="db_option_table">
<tr>
- <th colspan="2" class="option_string"> RESTORE DATABASE </th>
- </tr>
- <tr>
<td><label for="restore_db">File:</label></td>
<td><input type="file" name="db_file" class="required" autofocus="autofocus"/></td>
</tr>
@@ -233,17 +239,21 @@
<td><label for="new_db">New database name:</label></td>
<td><input type="text" name="new_db" class="required"/></td>
</tr>
- <tr>
- <td colspan="2" align="right"><button class="oe_button">Restore</button></td>
- </tr>
</table>
</form>
- <form id="db_change_password" name="change_pwd_form" method="POST">
+ <form id="db_change_password" name="change_pwd_form" style="display: none;">
+ <div class="oe_view_manager oe_view_manager_current">
+ <div class="oe_view_manager_header" style="padding: 8px;">
+ <div class="oe_header_row">
+ <h2 class="oe_view_title">
+ <span class="oe_view_title_text oe_breadcrumb_title">Change Master Password</span>
+ </h2>
+ <button type="submit" class="oe_button oe_highlight db-change-password">Change Password</button>
+ </div>
+ </div>
+ </div>
<table align="center" class="db_option_table">
<tr>
- <th colspan="2" class="option_string"> CHANGE MASTER PASSWORD </th>
- </tr>
- <tr>
<td><label for="old_pwd">Master password:</label></td>
<td><input type="password" name="old_pwd" class="required" minlength="1" autofocus="autofocus"/></td>
</tr>
@@ -253,28 +263,67 @@
</tr>
<tr>
<td><label for="confirm_pwd">Confirm new master password:</label></td>
- <td><input type="password" name="confirm_pwd" class="required" equalTo="input[name=new_pwd]" minlength="1"/></td>
- </tr>
- <tr>
- <td colspan="2" align="right"><button class="oe_button">Change Password</button></td>
+ <td><input type="password" name="confirm_pwd" class="required" equalTo="input[name=new_pwd]" minlength="1"/> </td>
</tr>
</table>
</form>
</div>
</div>
</t>
+<t t-name="DatabaseManager.user_menu">
+ <span class="oe_right">
+ <a id="back-to-login" href="#"><span class="oe_topbar_item oe_topbar_name">Back to Login</span></a>
+ </span>
+</t>
+<t t-name="DatabaseManager.menu">
+ <div class="oe_secondary_menu_section">Database Management</div>
+ <ul class="oe_secondary_submenu">
+ <li><a href="#db_create">Create</a></li>
+ <li><a href="#db_drop">Drop</a></li>
+ <li><a href="#db_backup">Backup</a></li>
+ <li><a href="#db_restore">Restore</a></li>
+ <li><a href="#db_change_password">Password</a></li>
+ </ul>
+</t>
+
+<t t-name="ChangePassword">
+ <form name="change_password_form" method="POST">
+ <table align="center">
+ <tr>
+ <td><label for="old_pwd">Old Password:</label></td>
+ <td><input type="password" name="old_pwd"
+ minlength="1" autofocus="autofocus"/></td>
+ </tr>
+ <tr>
+ <td><label for="new_password">New Password:</label></td>
+ <td><input type="password" name="new_password"
+ minlength="1"/></td>
+ </tr>
+ <tr>
+ <td><label for="confirm_pwd">Confirm Password:</label></td>
+ <td><input type="password" name="confirm_pwd"
+ minlength="1"/></td>
+ </tr>
+ <tr>
+ <td colspan="2" align="right"><button class="oe_button">Change Password</button></td>
+ </tr>
+ </table>
+ </form>
+</t>
<t t-name="Menu">
<ul class="oe_menu" t-if="widget.data">
<li t-foreach="widget.data.data.children" t-as="menu">
- <t t-call="Menu.secondary.link"/>
+ <t t-call="Menu.link"/>
</li>
</ul>
</t>
<t t-name="Menu.more">
<li class="oe_menu_more_container">
- <a href="#" class="oe_menu_more_link oe_dropdown_toggle">More</a>
- <ul class="oe_menu_more" 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>
</t>
<t t-name="Menu.secondary">
@@ -284,7 +333,7 @@
<t t-esc="menu.name"/>
<!--
Shall the section be still clickable ?
- <t t-call="Menu.secondary.link"/>
+ <t t-call="Menu.link"/>
-->
</div>
<t t-call="Menu.secondary.submenu"/>
@@ -294,21 +343,21 @@
<t t-name="Menu.secondary.submenu">
<ul t-if="menu.children.length" class="oe_secondary_submenu">
<li t-foreach="menu.children" t-as="menu">
- <t t-call="Menu.secondary.link"/>
+ <t t-call="Menu.link"/>
<!--<span class="oe_menu_label">8</span>-->
<t t-call="Menu.secondary.submenu"/>
</li>
</ul>
</t>
-<t t-name="Menu.secondary.link">
- <a t-attf-href="#menu_id=#{menu.id}&amp;action_id=#{menu.action ? menu.action.split(',')[1] : ''}"
+<t t-name="Menu.link">
+ <a t-attf-href="#menu_id=#{menu.id}&amp;action=#{menu.action ? menu.action.split(',')[1] : ''}"
t-att-class="menu.children.length ? 'oe_menu_toggler' : 'oe_menu_leaf'"
t-att-data-menu="menu.id"
t-att-data-action-model="menu.action ? menu.action.split(',')[0] : ''"
t-att-data-action-id="menu.action ? menu.action.split(',')[1] : ''">
<t t-esc="menu.name"/>
<t t-if="menu.needaction_enabled and menu.needaction_counter">
- <div class="oe_menu_counter">
+ <div class="oe_tag oe_tag_dark oe_menu_counter">
<t t-esc="menu.needaction_counter"/>
</div>
</t>
@@ -316,63 +365,35 @@
</t>
<t t-name="UserMenu">
- <ul class="oe_user_menu oe_topbar_item">
- <li class="oe_dropdown">
- <a href="#" class="oe_dropdown_toggle">
- <img class="oe_topbar_avatar" t-att-data-default-src="_s + '/web/static/src/img/user_menu_avatar.png'"/>
- <span class="oe_topbar_name"/>
- </a>
- <ul class="oe_dropdown_options">
- <li><a href="#" data-menu="about">About OpenERP</a></li>
- <li><a href="#" data-menu="settings">Preferences</a></li>
- <li><a href="#" data-menu="logout">Log out</a></li>
- </ul>
- </li>
- </ul>
+ <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="about">About OpenERP</a></li>
+ <li><a href="#" data-menu="settings">Preferences</a></li>
+ <li><a href="#" data-menu="logout">Log out</a></li>
+ </ul>
+ </span>
</t>
<t t-name="UserMenu.about">
- <div>
- <a class="oe_activate_debug_mode" href="?debug" style="float:right; font-size: 80%;">Activate the developer mode</a>
- <h1 style="margin:0;">OpenERP</h1>
- <h3 style="margin:15px 0;padding:0;">Version <t t-esc="version_info.version"/></h3>
- <p>
- Copyright © 2004-TODAY OpenERP SA. All Rights Reserved.<br />
- OpenERP is a trademark of the <a target="_blank" href="http://openerp.com/" style="text-decoration: underline;">OpenERP SA Company</a>.
- </p>
- <p>
- Licenced under the terms of <a target="_blank" href="http://www.gnu.org/licenses/agpl.html" style="text-decoration: underline;">GNU Affero General Public License</a>
- </p>
- <p>
- For more information visit <a target="_blank" href="http://openerp.com/" style="text-decoration: underline;">OpenERP.com</a>
- </p>
+
+ <div class="oe_about">
+ <a class="oe_activate_debug_mode oe_right" href="?debug" style="background-color: white; padding:2px 6px; border-radius: 10px;">Activate the developer mode</a>
+ <img class="oe_logo" src="/web/static/src/img/logo2.png"/>
+ <h3>Version <t t-esc="version_info.version"/></h3>
+
+ <div class="oe_bottom">
+ <p>Copyright © 2004-TODAY OpenERP SA. All Rights Reserved.<br />
+ OpenERP is a trademark of the <a target="_blank" href="http://openerp.com/" style="text-decoration: underline;">OpenERP SA Company</a>.</p>
+ <p>Licenced under the terms of <a target="_blank" href="http://www.gnu.org/licenses/agpl.html" style="text-decoration: underline;">GNU Affero General Public License</a></p>
+ <p>For more information visit <a target="_blank" href="http://openerp.com/" style="text-decoration: underline;">OpenERP.com</a></p>
+ </div>
</div>
-</t>
-<t t-name="UserMenu.password">
- <form name="change_password_form" method="POST">
- <table align="center">
- <tr>
- <td><label for="old_pwd">Old Password:</label></td>
- <td><input type="password" name="old_pwd"
- minlength="1" autofocus="autofocus"/></td>
- </tr>
- <tr>
- <td><label for="new_password">New Password:</label></td>
- <td><input type="password" name="new_password"
- minlength="1" autofocus="autofocus"/></td>
- </tr>
- <tr>
- <td><label for="confirm_pwd">Confirm Password:</label></td>
- <td><input type="password" name="confirm_pwd"
- minlength="1"/></td>
- </tr>
- <tr>
- <td colspan="2" align="right"><button class="oe_button">Change Password</button></td>
- </tr>
- </table>
- </form>
+
</t>
<t t-name="WebClient">
+ <div class="openerp openerp_webclient_container">
<table class="oe_webclient">
<tr>
<td colspan="2" class="oe_topbar">
@@ -383,34 +404,39 @@
</tr>
<tr>
<td class="oe_leftbar" valign="top">
- <t t-js="d">
- d.url = '/' + (window.location.search || '');
- </t>
- <a t-att-href="url" class="oe_logo"><img t-att-src='_s + "/web/static/src/img/logo.png"'/></a>
+ <a class="oe_logo" href="#"><img t-att-src='_s + "/web/static/src/img/logo.png"'/></a>
<div class="oe_secondary_menus_container"/>
<div class="oe_footer">
- Powered by <a href="http://www.openerp.com"><span>Open</span>ERP</a>
+ Powered by <a href="http://www.openerp.com" target="_blank"><span>Open</span>ERP</a>
</div>
</td>
<td class="oe_application">
</td>
</tr>
</table>
+ </div>
+</t>
+
+<t t-name="EmbedClient">
+ <div class="openerp">
+ <div class="oe_application"></div>
+ </div>
</t>
<t t-name="ViewManager">
<div class="oe_view_manager">
<table class="oe_view_manager_header">
- <col width="33%"/>
- <col width="33%"/>
- <col width="34%"/>
- <tr class="oe_header_row">
+ <col width="20%"/>
+ <col width="35%"/>
+ <col width="15%"/>
+ <col width="30%"/>
+ <tr class="oe_header_row oe_header_row_top">
<td colspan="2">
<h2 class="oe_view_title" t-if="widget.flags.display_title !== false">
- <span class="oe_view_title_text"><t t-esc="widget.display_title()"/></span>
+ <span class="oe_view_title_text oe_breadcrumb_title"/>
</h2>
</td>
- <td>
+ <td colspan="2">
<div class="oe_view_manager_view_search" t-opentag="true"/>
</td>
</tr>
@@ -418,13 +444,13 @@
<td>
<div class="oe_view_manager_buttons"/>
</td>
- <td>
+ <td colspan="2">
<div class="oe_view_manager_sidebar"/>
</td>
<td>
<ul class="oe_view_manager_switch oe_button_group oe_right">
- <t t-if="widget.views_src.length > 2" t-foreach="widget.views_src" t-as="view">
- <li t-if="view.view_type != 'form'" class="oe_e"><a t-attf-class="oe_vm_switch_#{view.view_type}" t-att-data-view-type="view.view_type" t-att-title="_t('Switch to ') + (view.label || view.view_type)">
+ <t t-if="widget.views_src.length > 1" t-foreach="widget.views_src" t-as="view">
+ <li class="oe_e"><a t-attf-class="oe_vm_switch_#{view.view_type}" t-att-data-view-type="view.view_type" t-att-title="_t('Switch to ') + (view.label || view.view_type)">
</a></li>
</t>
</ul>
@@ -433,7 +459,6 @@
</tr>
</table>
-
<div class="oe_view_manager_body">
<t t-foreach="widget.views_src" t-as="view">
<div t-attf-class="oe_view_manager_view_#{view.view_type}"/>
@@ -451,15 +476,18 @@
<option value="">Debug View#<t t-esc="view.fields_view.view_id"/></option>
<option t-if="view_manager.active_view === 'form'" value="perm_read" data-views="form,page">View Log (perm_read)</option>
<option t-if="view_manager.active_view === 'form'" value="toggle_layout_outline">Toggle Form Layout Outline</option>
+ <option value="tests">JS Tests</option>
<option value="fields">View Fields</option>
<option value="fvg">Fields View Get</option>
<option value="manage_filters">Manage Filters</option>
<t t-if="view_manager.session.uid === 1">
+ <option value="translate">Technical translation</option>
<option value="manage_views">Manage Views</option>
<option value="edit" data-model="ir.ui.view" t-att-data-id="view.fields_view.view_id">Edit <t t-esc="_.str.capitalize(view.fields_view.type)"/>View</option>
<option t-if="view_manager.searchview" value="edit" data-model="ir.ui.view" t-att-data-id="view_manager.searchview.view_id">Edit SearchView</option>
<option t-if="view_manager.action" value="edit" t-att-data-model="view_manager.action.type" t-att-data-id="view_manager.action.id">Edit Action</option>
<option value="edit_workflow">Edit Workflow</option>
+ <option value="print_workflow">Print Workflow</option>
</t>
</t>
<t t-name="ViewManagerDebugViewLog">
@@ -511,66 +539,45 @@
<div class="oe_sidebar">
<t t-foreach="widget.sections" t-as="section">
<div class="oe_form_dropdown_section">
- <button class="oe_dropdown_toggle"><t t-esc="section.label"/></button>
+ <button class="oe_dropdown_toggle oe_dropdown_arrow">
+ <t t-if="section.name == 'files'" t-raw="widget.items[section.name].length || ''"/>
+ <t t-esc="section.label"/>
+ </button>
<ul class="oe_dropdown_menu">
<li t-foreach="widget.items[section.name]" t-as="item" t-att-class="item.classname">
<a class="oe_sidebar_action_a" t-att-title="item.title" t-att-data-section="section.name" t-att-data-index="item_index" t-att-href="item.url" target="_blank">
<t t-raw="item.label"/>
</a>
+ <a t-if="section.name == 'files'" class="oe_sidebar_delete_item" t-att-data-id="item.id" title="Delete this attachment">x</a>
</li>
- <li t-if="section.name == 'files'" class="oe_sidebar_add" style="display:none;">
- Input type file stuff
+ <li t-if="section.name == 'files'" class="oe_sidebar_add_attachment">
+ <t t-call="HiddenInputFile">
+ <t t-set="fileupload_id" t-value="widget.fileupload_id"/>
+ <t t-set="fileupload_action">/web/binary/upload_attachment</t>
+ <input type="hidden" name="model" t-att-value="widget.dataset and widget.dataset.model"/>
+ <input type="hidden" name="id" t-att-value="widget.model_id"/>
+ <input type="hidden" name="session_id" t-att-value="widget.session.session_id"/>
+ <span>Add...</span>
+ </t>
</li>
</ul>
</div>
</t>
</div>
</t>
-<t t-name="Sidebar.attachments">
- <div class="oe-sidebar-attachments-toolbar">
- <div class="oe-binary-file-set" style="float: right">
- <form class="oe-binary-form" t-attf-target="#{element_id}_iframe"
- method="post" enctype="multipart/form-data" action="/web/binary/upload_attachment">
- <input type="hidden" name="session_id" t-att-value="session.session_id"/>
- <input type="hidden" name="callback" t-attf-value="#{element_id}_iframe"/>
- <input type="hidden" name="model" t-att-value="view.dataset.model"/>
- <input type="hidden" name="id" t-att-value="view.datarecord.id"/>
- <button class="oe_button" type="button">
- <img t-att-src='_s + "/web/static/src/img/throbber.gif"' width="16" height="16" style="display: none"/>
- <span>Add</span>
- </button>
- <input type="file" class="oe-binary-file" name="ufile" title="Add attachment"
- t-att-onclick="view.datarecord.id ? null : 'alert(\'No record selected ! You can only attach to existing record.\'); return false;'"/>
- </form>
- <iframe t-attf-id="#{element_id}_iframe" t-attf-name="#{element_id}_iframe" style="display: none"/>
- </div>
- </div>
- <br style="clear: both"/>
- <ul class="oe-sidebar-attachments-items">
- <li t-foreach="attachments" t-as="attachment">
- <t t-if="attachment.type == 'binary'" t-set="attachment.url" t-value="_s + '/web/binary/saveas?session_id='
- + session.session_id + '&amp;model=ir.attachment&amp;id=' + attachment.id
- + '&amp;field=datas&amp;filename_field=name&amp;t=' + (new Date().getTime())"/>
- <a class="oe-sidebar-attachments-link" t-att-href="attachment.url" target="_blank">
- <t t-esc="attachment.name"/>
- </a>
- <a href="#" class="oe-sidebar-attachment-delete" t-att-data-id="attachment.id" t-attf-title="Delete the attachment #{attachment.name}">
- <img t-att-src='_s + "/web/static/src/img/attachments-close.png"' width="15" height="15" border="0"/>
- </a>
- </li>
- </ul>
-</t>
<t t-name="TreeView">
- <select t-if="toolbar" style="width: 30%">
- </select>
+ <div class = "tree_header">
+ <select t-if="toolbar" ></select>
+ <button id = "tree_view_collapse">Collapse All</button>
+ <button id = "tree_view_expand">Expand All</button>
+ </div>
<table class="oe-treeview-table">
<thead>
<tr>
<th t-foreach="fields_view" t-as="field"
- t-if="!field.attrs.modifiers.tree_invisible"
- class="treeview-header">
- <t t-esc="fields[field.attrs.name].string" />
+ t-if="!field.attrs.modifiers.tree_invisible">
+ <t t-esc="field_value.attrs.string || fields[field.attrs.name].string" />
</th>
</tr>
</thead>
@@ -581,11 +588,11 @@
<tr t-name="TreeView.rows"
t-foreach="records" t-as="record"
t-att-id="'treerow_' + record.id"
- t-att-data-id="record.id" t-att-data-level="level + 1">
+ t-att-data-id="record.id" t-att-data-level="level">
<t t-set="children" t-value="record[children_field]"/>
<t t-set="class" t-value="children and children.length ? 'treeview-tr' : 'treeview-td'"/>
<t t-set="rank" t-value="'oe-treeview-first'"/>
- <t t-set="style" t-value="'background-position: ' + 19*level + 'px; padding-left: ' + 19*level + 'px;'"/>
+ <t t-set="style" t-value="'background-position: ' + 19*(level-1) + 'px; padding-left: ' + (4 + 19*(level-1)) + 'px;'"/>
<td t-foreach="fields_view" t-as="field"
t-if="!field.attrs.modifiers.tree_invisible"
@@ -603,7 +610,7 @@
</td>
</tr>
-<table t-name="ListView" class="oe-listview-content">
+<table t-name="ListView" class="oe_list_content">
<t t-set="columns_count" t-value="visible_columns.length + (options.selectable ? 1 : 0) + (options.deletable ? 1 : 0)"/>
<thead>
<tr t-if="(!!options.action_buttons and !options.$buttons) or (!!options.pager and !options.$pager)">
@@ -613,28 +620,28 @@
<div class="oe_list_pager"/>
</th>
</tr>
- <tr t-if="options.header" class="oe-listview-header-columns">
+ <tr t-if="options.header" class="oe_list_header_columns">
<t t-foreach="columns" t-as="column">
<th t-if="column.meta">
<t t-esc="column.string"/>
</th>
</t>
<th t-if="options.selectable" width="1" >
- <input type="checkbox" class="all-record-selector"/>
+ <input type="checkbox" class="oe_list_record_selector"/>
</th>
<t t-foreach="columns" t-as="column">
- <th t-if="!column.meta and column.invisible !== '1'" t-att-data-id="column.id"
- t-att-class="((options.sortable and column.tag !== 'button') ? 'oe-sortable' : null)">
+ <th t-if="!column.meta and column.invisible !== '1' and !column.reorderable" t-att-data-id="column.id"
+ t-attf-class="oe_list_header_#{column.widget or column.type} #{((options.sortable and column.tag !== 'button') ? 'oe_sortable' : null)}">
<t t-if="column.tag !== 'button'"><t t-esc="column.string"/></t>
</th>
</t>
- <th t-if="options.deletable" width="1"/>
+ <th t-if="options.deletable" class="oe_list_record_delete" width="13px"/>
</tr>
</thead>
<tfoot>
<tr>
<td t-if="options.selectable"/>
- <td t-foreach="aggregate_columns" t-as="column" class="oe-list-footer oe_number"
+ <td t-foreach="aggregate_columns" t-as="column" class="oe_list_footer oe_number"
t-att-data-field="column.id" t-att-title="column.label">
</td>
<td t-if="options.deletable"/>
@@ -642,17 +649,16 @@
</tfoot>
</table>
<div t-name="ListView.buttons" class="oe_list_buttons">
- <t t-if="!widget.no_leaf and widget.options.action_buttons !== false">
- <button type="button" class="oe_button oe_list_add oe_form_button_hi" t-if="widget.options.addable">
+ <t t-if="!widget.no_leaf and widget.options.action_buttons !== false and widget.options.addable and widget.is_action_enabled('create')">
+ <button type="button" class="oe_button oe_list_add oe_highlight">
<t t-esc="widget.options.addable"/>
</button>
- <span class="oe_fade">or</span> <a href="#" class="oe_bold oe_list_button_import">Import</a>
</t>
</div>
<t t-name="ListView.pager">
<div class="oe_list_pager" t-att-colspan="widget.columns_count">
<t t-if="!widget.no_leaf and widget.options.pager !== false" t-call="ViewPager">
- <span class="oe-pager-state">
+ <span class="oe_list_pager_state">
</span>
</t>
</div>
@@ -660,40 +666,55 @@
<t t-name="ListView.rows" t-foreach="records.length" t-as="index">
<t t-call="ListView.row">
<t t-set="record" t-value="records.at(index)"/>
- <t t-set="row_parity" t-value="index_parity"/>
</t>
</t>
-<tr t-name="ListView.row" t-att-class="row_parity"
+<tr t-name="ListView.row"
t-att-data-id="record.get('id')"
t-att-style="view.style_for(record)">
+ <t t-set="asData" t-value="record.toForm().data"/>
<t t-foreach="columns" t-as="column">
- <td t-if="column.meta">
-
- </td>
+ <td t-if="column.meta"> </td>
</t>
- <th t-if="options.selectable" class="oe-record-selector" width="1">
+ <th t-if="options.selectable" class="oe_list_record_selector" width="1">
<t t-set="checked" t-value="options.select_view_id == record.get('id') ? 'checked' : null"/>
<input t-if="options.radio" type="radio" name="radiogroup" t-att-checked="checked"/>
<input t-if="!options.radio" type="checkbox" name="radiogroup" t-att-checked="checked"/>
</th>
<t t-foreach="columns" t-as="column">
- <t t-set="align" t-value="column.type === 'integer' or column.type == 'float'"/>
- <td t-if="!column.meta and column.invisible !== '1'" t-att-title="column.help"
- t-att-class="'oe-field-cell' + (align ? ' oe_number' : '')
- + (column.tag === 'button' ? ' oe-button' : '')"
- t-att-data-field="column.id">
- <t t-raw="render_cell(record, column)"/>
- </td>
+ <t t-set="number" t-value="column.type === 'integer' or column.type == 'float'"/>
+ <t t-set="modifiers" t-value="column.modifiers_for(asData)"/>
+ <td t-if="!column.meta and column.invisible !== '1' and !column.reorderable" t-att-title="column.help"
+ t-attf-class="oe_list_field_cell oe_list_field_#{column.widget or column.type} #{number ? 'oe_number' : ''} #{column.tag === 'button' ? 'oe-button' : ''} #{modifiers.readonly ? 'oe_readonly' : ''} #{modifiers.required ? 'oe_required' : ''}"
+ t-att-data-field="column.id"
+ ><t t-raw="render_cell(record, column)"/></td>
</t>
- <td t-if="options.deletable" class='oe-record-delete' width="1">
+ <td t-if="options.deletable" class='oe_list_record_delete' width="13px">
<button type="button" name="delete" class="oe_i">d</button>
</td>
</tr>
-
-<t t-name="ListView.row.save">
- <td>
- <button class='oe_i oe-edit-row-save' type='button' name='save'>S</button>
- </td>
+<t t-extend="ListView.buttons">
+ <t t-jquery="button.oe_list_add" t-operation="after">
+ <button class="oe_button oe_list_save oe_highlight"
+ type="button">Save</button>
+ <span class="oe_alternative">
+ <span class="oe_fade">or</span>
+ <a href="#" class="oe_bold oe_list_discard">Discard</a>
+ </span>
+ </t>
+</t>
+<button t-name="ListView.row.button" type="button"
+ t-att-title="widget.string" t-att-disabled="disabled || undefined"
+ t-att-class="disabled ? 'oe_list_button_disabled' : undefined"
+ ><img t-attf-src="#{prefix}/web/static/src/img/icons/#{widget.icon}.png"
+ t-att-alt="widget.string" /></button>
+<t t-extend="ListView.row">
+ <!-- adds back padding to row being rendered after edition, if necessary
+ (if not deletable add back padding), otherwise the row being added is
+ missing columns
+ -->
+ <t t-jquery="&gt; :last" t-operation="after">
+ <td t-if="edited and !options.deletable" class="oe-listview-padding"/>
+ </t>
</t>
<t t-name="FormView">
@@ -707,18 +728,24 @@
<div t-name="FormView.buttons" class="oe_form_buttons">
<t t-if="widget.options.action_buttons !== false">
<span class="oe_form_buttons_view">
- <button type="button" class="oe_button oe_form_button_edit">Edit</button>
- <button type="button" class="oe_button oe_form_button_create">Create</button>
+ <!-- required for the bounce effect on button -->
+ <div t-if="widget.is_action_enabled('edit')" style="display: inline-block;">
+ <button type="button" class="oe_button oe_form_button_edit" accesskey="E">Edit</button>
+ </div>
+ <button t-if="widget.is_action_enabled('create')"
+ type="button" class="oe_button oe_form_button_create" accesskey="C">Create</button>
</span>
<span class="oe_form_buttons_edit">
- <button type="button" class="oe_button oe_form_button_save">Save</button> <span class="oe_fade">or</span> <a href="#" class="oe_bold oe_form_button_cancel">Discard</a>
+ <button type="button" class="oe_button oe_form_button_save oe_highlight" accesskey="S">Save</button>
+ <span class="oe_fade">or</span>
+ <a href="#" class="oe_bold oe_form_button_cancel" accesskey="D">Discard</a>
</span>
</t>
</div>
<t t-name="FormView.pager">
<div class="oe_form_pager">
<t t-if="widget.options.pager !== false" t-call="ViewPager">
- <span class="oe_pager_index">0</span><span class="oe_pager_separator"> / </span><span class="oe_pager_count">0</span>
+ <span class="oe_form_pager_state"></span>
</t>
</div>
</t>
@@ -791,13 +818,13 @@
</t>
<t t-name="FormRenderingGroup">
<t t-if="string" t-call="FormRenderingSeparator"/>
- <table border="0" cellpadding="0" cellspacing="0" width="100%" t-attf-class="oe_form_group #{classnames}"/>
+ <table border="0" cellpadding="0" cellspacing="0" t-attf-class="oe_form_group #{classnames}"/>
</t>
<t t-name="FormRenderingNotebook">
- <div>
- <ul t-attf-class="oe_form_notebook #{classnames}">
+ <div class="oe_clear">
+ <ul t-attf-class="oe_notebook #{classnames}">
<li t-foreach="pages" t-as="page" t-att-modifiers="page.modifiers">
- <a t-attf-href="##{page.id}">
+ <a t-attf-href="##{page.id}" t-att-accesskey="page.accesskey">
<t t-esc="page.string"/>
</a>
</li>
@@ -805,11 +832,11 @@
</div>
</t>
<t t-name="FormRenderingNotebookPage">
- <div t-attf-class="oe_form_notebook_page #{classnames}" t-att-id="id">
+ <div t-attf-class="oe_notebook_page #{classnames}" t-att-id="id">
</div>
</t>
<t t-name="FormRenderingSeparator">
- <div t-attf-class="oe_horizontal_separator #{classnames}">
+ <div t-attf-class="oe_horizontal_separator oe_clear #{classnames}">
<t t-esc="string"/>
</div>
</t>
@@ -888,30 +915,29 @@
</ul>
</t>
<t t-name="FieldChar">
- <span t-att-class="'oe_form_field '+widget.widget_class">
+ <span t-att-class="'oe_form_field '+widget.widget_class" t-att-style="widget.node.attrs.style">
<t t-if="!widget.get('effective_readonly')">
<input t-att-type="widget.password ? 'password' : 'text'"
t-att-id="widget.id_for_label"
t-att-tabindex="widget.node.attrs.tabindex"
t-att-autofocus="widget.node.attrs.autofocus"
t-att-placeholder="widget.node.attrs.placeholder"
+ t-att-maxlength="widget.field.size"
/><img class="oe_field_translate oe_input_icon" t-if="widget.field.translate" t-att-src='_s + "/web/static/src/img/icons/terp-translate.png"' width="16" height="16" border="0"/>
</t>
</span>
</t>
<t t-name="FieldEmail">
- <span class="oe_form_field oe_form_field_email oe_form_field_with_button">
- <a t-if="widget.get('effective_readonly')" href="#" class="oe_form_uri"/>
+ <span class="oe_form_field oe_form_field_email" t-att-style="widget.node.attrs.style">
+ <a t-if="widget.get('effective_readonly')" href="#" class="oe_form_uri" target="_blank"/>
<t t-if="!widget.get('effective_readonly')">
- <button class="oe_button" tabindex="-1" type="button" title="Send an e-mail with your default e-mail client">
- <img t-att-src='_s + "/web/static/src/img/icons/terp-mail-message-new.png"'/>
- </button>
<div>
<input type="text"
t-att-id="widget.id_for_label"
t-att-tabindex="widget.node.attrs.tabindex"
t-att-autofocus="widget.node.attrs.autofocus"
t-att-placeholder="widget.node.attrs.placeholder"
+ t-att-maxlength="widget.field.size"
/>
</div>
</t>
@@ -921,23 +947,28 @@
<t t-jquery="span:first">
this.removeClass('oe_form_field_email').addClass('oe_form_field_url');
</t>
- <t t-jquery="button.oe_button" t-operation="replace">
- <button class="oe_button" tabindex="-1" type="button" title="Open this resource">
- <img t-att-src='_s + "/web/static/src/img/icons/gtk-ok.png"'/>
- </button>
- </t>
</t>
<t t-name="FieldText">
- <div class="oe_form_field oe_form_field_text">
+ <div class="oe_form_field oe_form_field_text" t-att-style="widget.node.attrs.style">
<textarea rows="6"
t-att-name="widget.name"
class="field_text"
t-att-tabindex="widget.node.attrs.tabindex"
t-att-autofocus="widget.node.attrs.autofocus"
- t-att-placeholder="widget.node.attrs.placeholder"
- ></textarea>
- <img class="oe_field_translate oe_input_icon" t-if="widget.field.translate"
- t-att-src='_s + "/web/static/src/img/icons/terp-translate.png"' width="16" height="16" border="0"/>
+ t-att-placeholder="! widget.get('effective_readonly') ? widget.node.attrs.placeholder : ''"
+ t-att-maxlength="widget.field.size"
+ ></textarea><img class="oe_field_translate oe_input_icon"
+ t-if="widget.field.translate and !widget.get('effective_readonly')"
+ t-att-src='_s + "/web/static/src/img/icons/terp-translate.png"' width="16" height="16" border="0"
+ />
+ </div>
+</t>
+<t t-name="FieldTextHtml">
+ <div t-att-class="'oe_form_field oe_form_field_html' + (widget.get('effective_readonly') ? ' oe_form_embedded_html' : '')"
+ t-att-style="widget.node.attrs.style">
+ <t t-if="! widget.get('effective_readonly')">
+ <textarea/>
+ </t>
</div>
</t>
<t t-name="web.datepicker">
@@ -953,13 +984,13 @@
</span>
</t>
<t t-name="FieldDate">
- <span class="oe_form_field oe_datepicker_root oe_form_field_date"/>
+ <span class="oe_form_field oe_datepicker_root oe_form_field_date" t-att-style="widget.node.attrs.style"/>
</t>
<t t-name="FieldDatetime">
- <span class="oe_form_field oe_datepicker_root oe_form_field_datetime"/>
+ <span class="oe_form_field oe_datepicker_root oe_form_field_datetime" t-att-style="widget.node.attrs.style"/>
</t>
<t t-name="FieldSelection">
- <span class="oe_form_field oe_form_field_selection">
+ <span class="oe_form_field oe_form_field_selection" t-att-style="widget.node.attrs.style">
<select t-if="!widget.get('effective_readonly')"
t-att-name="widget.name"
t-att-tabindex="widget.node.attrs.tabindex"
@@ -972,12 +1003,15 @@
</span>
</t>
<t t-name="FieldMany2One">
- <span class="oe_form_field oe_form_field_many2one oe_form_field_with_button">
- <a t-if="widget.get('effective_readonly')" href="#" class="oe_form_uri"/>
+ <span class="oe_form_field oe_form_field_many2one oe_form_field_with_button" t-att-style="widget.node.attrs.style">
+ <t t-if="widget.get('effective_readonly')">
+ <a t-if="! widget.options.no_open" href="#" class="oe_form_uri"/>
+ <span t-if="widget.options.no_open" href="#" class="oe_form_uri"/>
+ <span class="oe_form_m2o_follow"/>
+ </t>
<t t-if="!widget.get('effective_readonly')">
- <button class="oe_button oe-m2o-cm-button" title="Open Resource">
- <img t-att-src='_s + "/web/static/src/img/icons/terp-folder-yellow.png"'/>
- </button>
+ <a t-if="! widget.options.no_open" href="#" tabindex="-1"
+ class="oe_m2o_cm_button oe_e">/</a>
<div>
<input type="text"
t-att-id="widget.id_for_label"
@@ -985,7 +1019,7 @@
t-att-autofocus="widget.node.attrs.autofocus"
t-att-placeholder="widget.node.attrs.placeholder"
/>
- <span class="oe-m2o-drop-down-button">
+ <span class="oe_m2o_drop_down_button">
<img t-att-src='_s + "/web/static/src/img/down-arrow.png"'/>
</span>
</div>
@@ -993,23 +1027,24 @@
</span>
</t>
<t t-name="FieldMany2ManyTags">
- <div class="oe_form_field oe_form_field_many2manytags">
+ <div class="oe_form_field oe_tags" t-att-style="widget.node.attrs.style">
<t t-if="! widget.get('effective_readonly')">
- <textarea rows="1" style="width: 100%"></textarea>
+ <textarea rows="1" style="width: 100%"
+ t-att-placeholder="widget.node.attrs.placeholder"></textarea>
</t>
</div>
</t>
-<t t-name="FieldMany2ManyTags.box">
+<t t-name="FieldMany2ManyTag">
<t t-set="i" t-value="0"/>
<t t-foreach="elements" t-as="el">
- <span class="oe_form_field_many2manytags_box" t-att-data-index="i">
+ <span class="oe_tag" t-att-data-index="i">
<t t-esc="el[1]"/>
</span>
<t t-set="i" t-value="i + 1"/>
</t>
</t>
<t t-name="FieldReference">
- <table class="oe_form_field oe_form_field_reference oe_form" border="0" cellpadding="0" cellspacing="0">
+ <table class="oe_form_field oe_form_field_reference" border="0" cellpadding="0" cellspacing="0" t-att-style="widget.node.attrs.style">
<tr>
<td t-attf-class="oe_form_frame_cell oe_form_selection">
<span t-attf-class="oe_form_view_reference_selection"/>
@@ -1021,7 +1056,7 @@
</table>
</t>
<t t-name="FieldBoolean">
- <span class="oe_form_field oe_form_field_boolean">
+ <span class="oe_form_field oe_form_field_boolean" t-att-style="widget.node.attrs.style">
<input type="checkbox"
t-att-id="widget.id_for_label"
t-att-name="widget.name"
@@ -1031,47 +1066,33 @@
</span>
</t>
<t t-name="FieldProgressBar">
- <span class="oe_form_field oe_form_field_progressbar" t-opentag="true">
+ <span class="oe_form_field oe_form_field_progressbar" t-att-style="widget.node.attrs.style">
<span></span>
</span>
</t>
+<t t-name="FieldStatus">
+ <ul t-att-class="'oe_form_field_status ' + (widget.options.clickable ? 'oe_form_status_clickable' : 'oe_form_status')" t-att-style="widget.node.attrs.style"/>
+</t>
<t t-name="FieldStatus.content">
- <ul class="oe_form_steps">
- <t t-set="size" t-value="widget.to_show.length"/>
- <t t-foreach="_.range(size)" t-as="i">
- <li t-att-class="widget.to_show[i][0] === widget.selected_value ? 'oe_form_steps_active' : ''">
- <span><t t-esc="widget.to_show[i][1]"/></span>
- <img t-att-src='_s + "/web/static/src/img/form_steps.png"' class="oe_form_steps_arrow" t-if="i &lt; size - 1"/>
- </li>
- </t>
- </ul>
- <div class="oe_clear"/>
+ <t t-foreach="widget.selection" t-as="i">
+ <li t-att-class="i[0] === widget.selected_value ? 'oe_active' : ''" t-att-data-id="i[0]">
+ <span class="label"><t t-esc="i[1]"/></span>
+ <!-- are you mit ? -->
+ <span class="arrow"><span></span></span>
+ </li>
+ </t>
</t>
<t t-name="FieldBinaryImage">
- <span class="oe_form_field oe_form_field_image">
- <div class="oe_form_field_image_controls">
- <div class="oe-binary-file-set">
- <form class="oe-binary-form" t-att-target="widget.iframe"
- method="post" enctype="multipart/form-data" action="/web/binary/upload">
- <input type="hidden" name="session_id" value=""/>
- <input type="hidden" name="callback" t-att-value="widget.iframe"/>
- <button class="oe_button" type="button" title="Set Image">
- <img t-att-src='_s + "/web/static/src/img/icons/STOCK_DIRECTORY.png"'/>
- </button>
- <input type="file" class="oe-binary-file" name="ufile"
- t-att-tabindex="widget.node.attrs.tabindex"
- t-att-autofocus="widget.node.attrs.autofocus"
- />
- </form>
- </div>
- <button class="oe_button oe-binary-file-clear" type="button" title="Clear">
- <img t-att-src='_s + "/web/static/src/img/icons/STOCK_MISSING_IMAGE.png"'/>
- </button>
- <div class="oe-binary-progress" style="display: none">
+ <span class="oe_form_field oe_form_field_image" t-att-style="widget.node.attrs.style">
+ <div class="oe_form_field_image_controls oe_edit_only">
+ <t t-call="HiddenInputFile">
+ <t t-set="fileupload_id" t-value="widget.fileupload_id"/>
+ Edit
+ </t>
+ <div class="oe_form_binary_progress" style="display: none">
<img t-att-src='_s + "/web/static/src/img/throbber.gif"' width="16" height="16"/>
<b>Uploading ...</b>
</div>
- <iframe t-att-id="widget.iframe" t-att-name="widget.iframe" style="display: none"/>
</div>
</span>
</t>
@@ -1085,7 +1106,7 @@
</t>
<t t-name="FieldBinaryFile">
<t t-if="!widget.get('effective_readonly')">
- <table class="oe_form_field oe_form_field_binary" cellpadding="0" cellspacing="0" border="0">
+ <table class="oe_form_field oe_form_field_binary" cellpadding="0" cellspacing="0" border="0" t-att-style="widget.node.attrs.style">
<tr>
<td>
<input type="text" size="1" readonly="readonly"
@@ -1095,31 +1116,29 @@
class="field_binary"
/>
</td>
- <td class="oe-binary" nowrap="true">
+ <td class="oe_form_binary" nowrap="true">
<table cellspacing="0" cellpadding="0" border="0">
<tr>
<td>
- <div class="oe-binary-file-set" style="width: 80px; height:22px;">
- <form class="oe-binary-form" t-att-target="widget.iframe"
- method="post" enctype="multipart/form-data" action="/web/binary/upload">
- <input type="hidden" name="session_id" value=""/>
- <input type="hidden" name="callback" t-att-value="widget.iframe"/>
- <button class="oe_button oe_field_button" type="button" title="Set Image">
- <img t-att-src='_s + "/web/static/src/img/icons/STOCK_DIRECTORY.png"'/>
- <span>Select</span>
- </button>
- <input type="file" class="oe-binary-file" name="ufile"/>
- </form>
- </div>
+ <t t-call="HiddenInputFile">
+ <t t-set="fileupload_id" t-value="widget.fileupload_id"/>
+ <t t-set="fileupload_style">width: 83px;</t>
+ <button class="oe_button oe_field_button" type="button">
+ <img t-att-src='_s + "/web/static/src/img/icons/STOCK_DIRECTORY.png"'/>
+ <span>Select</span>
+ </button>
+ </t>
</td>
<td>
- <button class="oe_button oe-binary-file-save" type="button" title="Save As">
- <img t-att-src='_s + "/web/static/src/img/icons/gtk-save.png"'/>
- <span>Save As</span>
- </button>
+ <a class="oe_form_binary_file_save_data">
+ <button class="oe_button oe_form_binary_file_save" type="button" title="Save As">
+ <img t-att-src='_s + "/web/static/src/img/icons/gtk-save.png"'/>
+ <span>Save As</span>
+ </button>
+ </a>
</td>
<td>
- <button class="oe_button oe-binary-file-clear" type="button" title="Clear">
+ <button class="oe_button oe_form_binary_file_clear" type="button" title="Clear">
<img t-att-src='_s + "/web/static/src/img/icons/STOCK_MISSING_IMAGE.png"'/>
<span>Clear</span>
</button>
@@ -1127,25 +1146,38 @@
</tr>
</table>
</td>
- <td class="oe-binary-progress" style="display: none" nowrap="true">
+ <td class="oe_form_binary_progress" style="display: none" nowrap="true">
<img t-att-src='_s + "/web/static/src/img/throbber.gif"' width="16" height="16"/>
<b>Uploading ...</b>
- <iframe t-att-id="widget.iframe" t-att-name="widget.iframe" style="display: none"/>
</td>
</tr>
</table>
</t>
<t t-if="widget.get('effective_readonly')">
- <span>
+ <span t-att-style="widget.node.attrs.style">
<a href="javascript:void(0)" class="oe_form_uri"/>
</span>
</t>
</t>
+<t t-name="HiddenInputFile">
+ <div t-attf-class="oe_hidden_input_file #{fileupload_class or ''}" t-att-style="fileupload_style">
+ <form class="oe_form_binary_form" t-att-target="fileupload_id"
+ method="post" enctype="multipart/form-data" t-att-action="fileupload_action || '/web/binary/upload'">
+ <input type="hidden" name="session_id" value=""/>
+ <input type="hidden" name="callback" t-att-value="fileupload_id"/>
+ <t t-raw="__content__"/>
+ <input type="file" class="oe_form_binary_file" name="ufile"/>
+ </form>
+ <iframe t-att-id="fileupload_id" t-att-name="fileupload_id" style="display: none"/>
+ </div>
+</t>
<t t-name="WidgetButton">
<button type="button" class="oe_button oe_form_button"
+ t-att-style="widget.node.attrs.style"
t-att-tabindex="widget.node.attrs.tabindex"
- t-att-autofocus="widget.node.attrs.autofocus">
- <img t-if="widget.node.attrs.icon" t-att-src="_s + '/web/static/src/img/icons/' + widget.node.attrs.icon + '.png'" width="16" height="16"/>
+ t-att-autofocus="widget.node.attrs.autofocus"
+ t-att-accesskey="widget.node.attrs.accesskey">
+ <img t-if="widget.node.attrs.icon" t-att-src="_s + widget.node.attrs.icon" width="16" height="16"/>
<span t-if="widget.string"><t t-esc="widget.string"/></span>
</button>
</t>
@@ -1181,31 +1213,52 @@
</t>
</t>
-<t t-name="TranslateDialog">
- <table t-if="widget.view.translatable_fields" class="oe_form oe_translation_form" border="0" cellpadding="0" cellspacing="0" width="100%">
- <tr>
- <td class="oe_form_separator" width="1%" nowrap="nowrap">
- <div class="oe_horizontal_separator">Field</div>
- </td>
- <th t-foreach="widget.languages" align="left">
- <div class="oe_horizontal_separator"><t t-esc="name"/></div>
- </th>
- </tr>
- <tr t-foreach="widget.view.translatable_fields" t-as="field" t-att-data-field="field.name">
- <td class="oe_form_group_cell" width="1%" nowrap="nowrap">
- <label class="oe_label"><t t-esc="field.node.attrs.string"/>:</label>
- </td>
- <td t-foreach="widget.languages" t-as="lg" class="oe_form_group_cell">
- <input t-if="field.type == 'char'" type="text" t-attf-name="#{lg.code}-#{field.name}" value="" data-value="" class="oe_trad_field" style="width: 100%"/>
- <textarea t-if="field.type == 'text'" t-attf-name="#{lg.code}-#{field.name}" data-value="" class="oe_trad_field" style="width: 100%"></textarea>
- </td>
- </tr>
- </table>
+<t t-name="AbstractFormPopup.render">
+ <div>
+ <table style="width:100%">
+ <tr style="width:100%">
+ <td style="width:100%">
+ <div class="oe_popup_search" style="width:100%"></div>
+ </td>
+ </tr>
+ <tr style="width:100%">
+ <td style="width:100%">
+ <div class="oe_popup_list" style="width:100%"></div>
+ </td>
+ </tr>
+ </table>
+ <div class="oe_popup_form" style="width:100%"></div>
+ </div>
+</t>
+<t t-name="SelectCreatePopup.search.buttons">
+ <t t-if="! widget.options.disable_multiple_selection">
+ <button type="button" class="oe_button oe_selectcreatepopup-search-select" disabled="disabled">Select</button>
+ or
+ </t>
+ <a class="oe_button 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">
+ <t t-if="! multi_select">
+ <button type="button" class="oe_button oe_abstractformpopup-form-save oe_highlight">Save</button>
+ </t>
+ <t t-if="multi_select">
+ <button type="button" class="oe_button oe_abstractformpopup-form-save oe_highlight">Save &amp; Close</button>
+ <button type="button" class="oe_button oe_abstractformpopup-form-save-new oe_highlight">Save &amp; New</button>
+ </t>
+ or
+ </t>
+ <a class="oe_button oe_abstractformpopup-form-close oe_bold oe_form_button_cancel" href="javascript:void(0)">
+ <t t-if="! readonly">
+ Discard
+ </t>
+ <t t-if="readonly">
+ Close
+ </t>
+ </a>
</t>
<t t-name="One2Many.viewmanager" t-extend="ViewManager">
- <t t-jquery="span.oe_view_title_text" t-operation="replace"/>
-
<t t-jquery=".oe-view-manager-header">
this.attr('t-if', 'views.length != 1');
</t>
@@ -1233,8 +1286,10 @@
<div t-name="SearchView" class="oe_searchview">
<div class="oe_searchview_facets"/>
<div class="oe_searchview_clear"/>
- <div class="oe_searchview_unfold_drawer"/>
+ <div class="oe_searchview_unfold_drawer" title="Advanced Search..."/>
<div class="oe_searchview_drawer"/>
+ <button type="button" class="oe_searchview_search"
+ title="Search Again">Search</button>
</div>
<div t-name="SearchView.InputView"
@@ -1242,7 +1297,7 @@
contenteditable="true"/>
<!-- tabindex: makes div focusable -->
<div t-name="SearchView.FacetView"
- class="oe_searchview_facet"
+ class="oe_tag oe_tag_dark oe_searchview_facet"
tabindex="0"
><span class="oe_facet_remove">x</span
><span class="oe_facet_category oe_i" t-if="widget.model.has('icon')">
@@ -1255,9 +1310,8 @@
<span t-name="SearchView.FacetView.Value" class="oe_facet_value">
<t t-esc="widget.model.get('label')"/>
</span>
-
<t t-name="SearchView.managed-filters">
- <option class="oe-filters-title" value="">Filters</option>
+ <option class="oe_search_filters_title" value="">Filters</option>
<optgroup label="-- Filters --">
<t t-foreach="filters" t-as="filter">
<option t-attf-value="get:#{filter_index}"
@@ -1270,7 +1324,6 @@
<optgroup label="-- Actions --">
<option value="advanced_filter">Add Advanced Filter</option>
<option value="save_filter">Save Filter</option>
- <option value="add_to_dashboard">Add to Dashboard</option>
<option value="manage_filters">Manage Filters</option>
</optgroup>
</t>
@@ -1281,18 +1334,8 @@
<p>(Any existing filter with the same name will be replaced)</p>
</div>
</t>
-<t t-name="SearchView.add_to_dashboard">
- <div class="oe_form">
- <p><b>Select Dashboard to add this filter to:</b></p>
- <select style="width: 100%; margin-right: 1em;">
- <option t-foreach="dashboards" t-as="menu" t-att-value="menu.id" t-att-selected="(menu.id == selected_menu_id) || undefined"><t t-esc="menu.name"/></option>
- </select>
- <p><b>Title of new Dashboard item:</b></p>
- <input type="text" style="width: 100%; margin-right: 1em;"/>
- </div>
-</t>
<t t-name="SearchView.render_lines">
- <table class="oe-searchview-render-line" border="0" cellspacing="0" cellpadding="0"
+ <table class="oe_search_render_line" border="0" cellspacing="0" cellpadding="0"
t-foreach="lines" t-as="line">
<tr>
<td t-foreach="line" t-as="widget" class="oe_searchview_field">
@@ -1409,10 +1452,10 @@
</t>
</t>
</t>
-<div t-name="SearchView.Filters" class="oe_searchview_section">
+<div t-name="SearchView.Filters" class="oe_searchview_filters oe_searchview_section">
</div>
-<div t-name="SearchView.CustomFilters" class="oe_searchview_section">
+<div t-name="SearchView.CustomFilters" class="oe_searchview_custom oe_searchview_section">
<div>
<h3><span class="oe_i">M</span> Custom Filters</h3>
<ul class="oe_searchview_custom_list"/>
@@ -1429,6 +1472,7 @@
<div>
</div>
</div>
+
<div t-name="SearchView.advanced" class="oe_searchview_advanced">
<h4>Advanced Search</h4>
<form>
@@ -1443,8 +1487,7 @@
<li>
<select class="searchview_extended_prop_field">
<t t-foreach="widget.attrs.fields" t-as="field">
- <option t-if="typeof field.store === 'undefined' || field.store || field.fnct_search"
- t-att="{'selected': field === widget.attrs.selected ? 'selected' : null}"
+ <option t-att="{'selected': field === widget.attrs.selected ? 'selected' : null}"
t-att-value="field.name">
<t t-esc="field.string"/>
</option>
@@ -1474,115 +1517,11 @@
</t>
</select>
</t>
-<t t-name="AbstractFormPopup.render">
- <div>
- <table style="width:100%">
- <tr style="width:100%">
- <td style="width:100%">
- <div class="oe-select-create-popup-view-search" style="width:100%"></div>
- </td>
- </tr>
- <tr style="width:100%">
- <td style="width:100%">
- <div class="oe-select-create-popup-view-list" style="width:100%"></div>
- </td>
- </tr>
- </table>
- <div class="oe-form-view-popup-form-placeholder oe-select-create-popup-view-form" style="width:100%"></div>
- </div>
-</t>
-<t t-name="SelectCreatePopup.search.buttons">
- <button type="button" class="oe_button oe_selectcreatepopup-search-select" disabled="disabled">Select</button>
- <button type="button" class="oe_button oe_selectcreatepopup-search-close">Cancel</button>
-</t>
-<t t-name="AbstractFormPopup.buttons">
- <t t-if="! multi_select">
- <button type="button" class="oe_button oe_abstractformpopup-form-save">Save</button>
- </t>
- <t t-if="multi_select">
- <button type="button" class="oe_button oe_abstractformpopup-form-save-new">Save &amp; New</button>
- <button type="button" class="oe_button oe_abstractformpopup-form-save">Save &amp; Close</button>
- </t>
- <button type="button" class="oe_button oe_abstractformpopup-form-close">Cancel</button>
-</t>
-<t t-extend="ListView.row">
- <!-- adds back padding to row being rendered after edition, if necessary
- (if not deletable add back padding), otherwise the row being added is
- missing columns
- -->
- <t t-jquery="&gt; :last" t-operation="after">
- <td t-if="edited and !options.deletable" class="oe-listview-padding"/>
- </t>
-</t>
-
-<t t-name="view_editor">
- <table class="oe_view_editor">
- <t t-call="view_editor.row"/>
- </table>
-</t>
-<t t-name="view_editor.row">
- <tr t-att-id="'viewedit-' + rec.id" t-att-level="rec.level" t-foreach="data" t-as="rec">
- <td width="90%">
- <table class="oe_view_editor_field">
- <tr>
- <td width="16px" t-att-style="'background-position: ' + 20*rec.level + 'px; padding-left: ' + 20*rec.level + 'px'">
- <img t-if="rec.child_id.length" t-att-id="'parentimg-' + rec.id"
- src="/web/static/src/img/collapse.gif" width="16" height="16" border="0"/>
- </td>
- <td style="cursor: pointer;">
- <a style="text-decoration:none" href="javascript:void(0);">
- <t t-esc="rec.name"/>
- </a>
- </td>
- </tr>
- </table>
- </td>
- <td width="2%">
- <img t-if="rec.att_list.length"
- id="side-add" src="/web/static/src/img/icons/gtk-add.png" style="cursor: pointer;"/>
- </td>
- <td width="2%">
- <img id="side-remove" src="/web/static/src/img/icons/gtk-remove.png" style="cursor: pointer;"/>
- </td>
- <td width="2%">
- <img t-if="rec.att_list.length and !_.include(no_properties, rec.att_list[0])"
- id="side-edit" src="/web/static/src/img/icons/gtk-edit.png" style="cursor: pointer;"/>
- </td>
- <td width="2%">
- <img t-if="rec.att_list.length"
- id="side-up" src="/web/static/src/img/icons/gtk-go-up.png" style="cursor: pointer;"/>
- </td>
- <td width="2%">
- <img t-if="rec.att_list.length"
- id="side-down" src="/web/static/src/img/icons/gtk-go-down.png" style="cursor: pointer;"/>
- </td>
- <t t-if="rec.child_id.length">
- <t t-set="data" t-value="rec.child_id"/>
- <t t-call="view_editor.row"/>
- </t>
- </tr>
-</t>
-<t t-name="vieweditor_char">
- <input type="text" t-att-id="widget.name" class="field_char" size="50"/>
-</t>
-<t t-name="vieweditor_selection">
- <select t-att-id="widget.name" >
- <t t-if="widget.selection" t-foreach="widget.selection" t-as="option">
- <option
- t-att-value="typeof option === 'object' ? option[0] : option">
- <t t-esc="typeof option === 'object' ? option[1] : option"/>
- </option>
- </t>
- </select>
-</t>
-<t t-name="vieweditor_boolean">
- <input type="checkbox" t-att-id="widget.name"/>
-</t>
<t t-name="ExportView">
<a id="exportview" href="javascript: void(0)" style="text-decoration: none;color: #3D3D3D;">Export</a>
</t>
-<table t-name="ExportTreeView" class="oe-export"
+<table t-name="ExportTreeView" class="oe_export"
style="background-color: #F3F3F3;">
<tr>
<td colspan="3">
@@ -1687,7 +1626,7 @@
</t>
<t t-name="ImportDataView">
<form name="import_data" id="import_data" action="" method="post" enctype="multipart/form-data"
- class="oe-import oe-import-no-result">
+ class="oe_import oe_import_no_result">
<input type="hidden" name="session_id" t-att-value="widget.session.session_id"/>
<h2 class="separator horizontal">1. Import a .CSV file</h2>
<p>Select a .CSV file to import. If you need a sample of file to import,
@@ -1697,9 +1636,9 @@
<label for="csvfile">CSV File:</label>
<input type="file" id="csvfile" size="50" name="csvfile"/>
</p>
- <h2 class="separator horizontal oe-import-result">2. Check your file format</h2>
- <div id="result" class="oe-import-result"></div>
- <fieldset class="oe-closed oe-import-result">
+ <h2 class="separator horizontal oe_import_result">2. Check your file format</h2>
+ <div id="result" class="oe_import_result"></div>
+ <fieldset class="oe_closed oe_import_result">
<legend>Import Options</legend>
<table>
<tr>
@@ -1739,7 +1678,7 @@
</tr>
<tr>
<td t-foreach="records[0]" t-as="column">
- <input class="sel_fields" placeholder="--- Don't Import ---"/><span class="oe-m2o-drop-down-button">
+ <input class="sel_fields" placeholder="--- Don't Import ---"/><span class="oe_m2o_drop_down_button">
<img t-att-src='_s + "/web/static/src/img/down-arrow.png"' /></span>
</td>
</tr>
@@ -1759,5 +1698,22 @@
<div t-name="Many2ManyKanban.quick_create" class="oe_kanban_quick_create">
<input t-att-placeholder="_t('Type name to search')"/>
</div>
-
+<t t-name="Throbber">
+ <div>
+ <div class="oe_blockui_spin" style="height: 50px">
+ </div>
+ <br />
+ <div class="oe_throbber_message" style="color:white"></div>
+ </div>
+</t>
+<t t-name="M2ODialog">
+ <div>
+ Name: <input class="oe_form_m2o_input_name" type="text"/>
+ </div>
+</t>
+<t t-name="M2ODialog.buttons">
+ <button class="oe_form_m2o_qc_button oe_button oe_highlight">Quick Add</button>
+ <button class="oe_form_m2o_sc_button oe_button">Add All Info...</button>
+ <button class="oe_form_m2o_cancel_button oe_button">Cancel</button>
+</t>
</templates>