summaryrefslogtreecommitdiff
path: root/addons/web/static/src
diff options
context:
space:
mode:
authorXavier Morel <xmo@openerp.com>2012-11-07 12:25:16 +0100
committerXavier Morel <xmo@openerp.com>2012-11-07 12:25:16 +0100
commit36dad4cef994449755d7d01560cd84a4f92b629e (patch)
tree4b8aa0e2c8d0ca788856d8f542516b7619a18452 /addons/web/static/src
parent64dc8880532ab198147a03fe6c78b28bd1af794e (diff)
parent1c5e8da2765e51b2c8bb49c6d1bf3c6b55c95dbf (diff)
[MERGE] from trunk
bzr revid: xmo@openerp.com-20121107112516-64hqps4jjgmrs3a4
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.css3022
-rw-r--r--addons/web/static/src/css/base.sass2735
-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/search_reset.gifbin0 -> 195 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.js920
-rw-r--r--addons/web/static/src/js/corelib.js354
-rw-r--r--addons/web/static/src/js/coresetup.js409
-rw-r--r--addons/web/static/src/js/data.js462
-rw-r--r--addons/web/static/src/js/data_export.js144
-rw-r--r--addons/web/static/src/js/data_import.js366
-rw-r--r--addons/web/static/src/js/formats.js90
-rw-r--r--addons/web/static/src/js/search.js500
-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.js3577
-rw-r--r--addons/web/static/src/js/view_list.js816
-rw-r--r--addons/web/static/src/js/view_list_editable.js979
-rw-r--r--addons/web/static/src/js/view_tree.js126
-rw-r--r--addons/web/static/src/js/views.js1016
-rw-r--r--addons/web/static/src/xml/base.xml1191
28 files changed, 9934 insertions, 9056 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 f2fcc9abf33..30fe4d415cf 100644
--- a/addons/web/static/src/css/base.css
+++ b/addons/web/static/src/css/base.css
@@ -20,9 +20,19 @@
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 {
@@ -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 {
@@ -49,10 +68,13 @@
background-color: #f0f0f0;
}
.openerp thead th {
- border-right: 1px dotted #afafb6;
+ border-left: 1px solid #dfdfdf;
}
-.openerp thead th:last-child {
- border-right: none;
+.openerp thead th:first-child {
+ border-left: none;
+}
+.openerp thead th.null {
+ border-left: none;
}
.openerp th, .openerp td {
padding: 0;
@@ -84,59 +106,26 @@
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 .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 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_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: 4px 12px;
+ padding: 3px 12px;
font-size: 13px;
text-align: center;
background-color: #efefef;
@@ -151,12 +140,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);
@@ -165,9 +154,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);
@@ -177,29 +168,259 @@
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-tabs {
+ position: static;
+}
+.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: 0;
+}
+.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);
@@ -209,54 +430,358 @@
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;
+}
+.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;
+ 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 p.oe_grey {
+ max-width: 650px;
+}
+.openerp .oe_grey {
+ color: #aaaaaa;
+}
+.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 button.following {
+.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.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.3);
+ 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;
@@ -279,14 +804,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;
@@ -308,16 +834,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;
@@ -377,7 +903,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;
@@ -421,7 +947,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;
@@ -448,8 +974,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);
@@ -458,189 +984,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 {
- 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 {
- 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;
@@ -648,27 +1032,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);
@@ -678,62 +1053,113 @@
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;
- 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;
+.openerp .oe_menu > li > .oe_active {
+ background: rgba(0, 0, 0, 0.3);
+ text-shadow: black 0px 0px 3px;
+ -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4) inset;
+ -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;
@@ -741,21 +1167,23 @@
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;
@@ -766,44 +1194,49 @@
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-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;
@@ -829,190 +1262,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;
-}
-.openerp .oe-view-manager-header .button-group a {
- color: #4c4c4c;
+ max-width: 7em;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
}
-.openerp .oe-view-manager-header .button-group a:hover {
- text-decoration: none;
+.openerp .oe_view_manager .oe_view_manager_body {
+ height: inherit;
}
-.openerp .oe-view-manager-header .button-group .active {
- 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);
+.openerp .oe_view_manager .oe_view_manager_view_kanban {
+ height: inherit;
}
-.openerp .oe_view_manager_header {
+.openerp .oe_view_manager table.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);
+ table-layout: fixed;
}
-.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;
@@ -1023,47 +1419,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;
@@ -1071,7 +1479,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;
@@ -1079,122 +1487,88 @@
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;
- 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;
position: relative;
float: right;
- padding-right: 20px;
- width: 410px;
+ padding: 1px 0;
+ line-height: 18px;
+ width: 400px;
border: 1px solid #ababab;
background: white;
- -moz-border-radius: 1em;
- -webkit-border-radius: 1em;
- border-radius: 1em;
+ -moz-border-radius: 13px;
+ -webkit-border-radius: 13px;
+ 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: 12px;
+ line-height: 18px;
}
.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;
position: absolute;
top: 0;
- right: 22px;
+ right: 18px;
width: 15px;
height: 100%;
- background: url(../img/attachments-close.png) center center no-repeat;
+ background: url(../img/search_reset.gif) center center no-repeat;
}
.openerp .oe_searchview .oe_searchview_unfold_drawer {
position: absolute;
top: 0;
right: 0;
height: 100%;
- line-height: 2.5em;
padding: 0 7px 0 4px;
color: #cccccc;
cursor: pointer;
@@ -1203,47 +1577,80 @@
color: #999999;
}
.openerp .oe_searchview .oe_searchview_unfold_drawer:before {
- content: "◀";
+ position: absolute;
+ top: 10px;
+ right: 7px;
+ width: 0;
+ height: 0;
+ display: inline-block;
+ content: "";
+ vertical-align: top;
+ border-top: 5px solid #4c4c4c;
+ border-left: 5px solid transparent;
+ border-right: 5px solid transparent;
+ filter: alpha(opacity=50);
+ opacity: 0.5;
}
-.openerp .oe_searchview .oe_searchview_facets:before {
- color: #cccccc;
- font-family: "mnmliconsRegular";
+.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_search:before {
+ font: 21px "mnmliconsRegular";
content: "r";
- font-size: 150%;
- padding: 0 1px 0 3px;
- display: inline;
+ color: #a3a3a3;
+}
+.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: 26px;
+ line-height: 17px;
}
-.openerp .oe_searchview .oe_searchview_facets .oe_searchview_input, .openerp .oe_searchview .oe_searchview_facets .oe_searchview_facet {
- height: 26px;
+.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;
- padding-left: 1.1em;
- margin: 1px 0;
}
.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;
@@ -1252,128 +1659,167 @@
border-radius: 0 3px 3px 0;
}
.openerp .oe_searchview .oe_searchview_facets .oe_searchview_facet .oe_facet_category, .openerp .oe_searchview .oe_searchview_facets .oe_searchview_facet .oe_facet_value {
- height: 24px;
- padding: 1px 0.1em;
+ height: 18px;
+ padding: 0 4px;
}
.openerp .oe_searchview .oe_searchview_facets .oe_searchview_facet .oe_facet_category {
color: white;
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.4);
}
+.openerp .oe_searchview .oe_searchview_facets .oe_searchview_facet .oe_facet_category.oe_i {
+ font-size: 16px;
+}
.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;
}
.openerp .oe_searchview .oe_searchview_facets .oe_searchview_facet .oe_facet_remove {
position: absolute;
- top: 0;
- left: 2px;
- color: white;
+ top: 3px;
+ right: 3px;
+ color: #8786b7;
+ line-height: 8px;
+ width: 12px;
+ height: 12px;
+ padding-top: 1px;
+ text-align: center;
+ font-weight: bold;
+ cursor: pointer;
+ text-shadow: 0 1px 1px white;
}
.openerp .oe_searchview .oe_searchview_facets .oe_searchview_facet .oe_facet_remove:hover {
- color: #cccccc;
+ color: white;
+ 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_searchview.oe_searchview_open_drawer .oe_searchview_drawer {
display: block;
}
-.openerp .oe_searchview.oe_searchview_open_drawer .oe_searchview_unfold_drawer:before {
- content: "▼";
-}
.openerp .oe_searchview .oe_searchview_drawer {
position: absolute;
- z-index: 1;
- margin-top: 3px;
+ z-index: 100;
+ margin-top: 4px;
top: 100%;
- right: 0;
+ right: -1px;
background-color: white;
min-width: 100%;
display: none;
- border: 1px solid #cccccc;
+ border: 1px solid #afafb6;
text-align: left;
- padding-bottom: 0.5em;
- -moz-border-radius: 1em;
- -webkit-border-radius: 1em;
- border-radius: 1em;
+ -moz-border-radius: 4px;
+ -webkit-border-radius: 4px;
+ 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);
}
.openerp .oe_searchview .oe_searchview_drawer > div {
border-top: 1px solid #cccccc;
- margin: 3px 0;
+ margin: 0;
+ padding: 8px;
}
.openerp .oe_searchview .oe_searchview_drawer > div:first-child {
border-top: none;
margin: 0;
}
+.openerp .oe_searchview .oe_searchview_drawer h3 {
+ margin: 8px 4px 4px 12px;
+ color: #7c7bad;
+ font-size: 13px;
+}
.openerp .oe_searchview .oe_searchview_drawer h4, .openerp .oe_searchview .oe_searchview_drawer h4 * {
margin: 0;
cursor: pointer;
+ font-weight: normal;
+ display: inline-block;
+}
+.openerp .oe_searchview .oe_searchview_drawer h4:hover, .openerp .oe_searchview .oe_searchview_drawer h4 *:hover {
+ background-color: #f0f0fa;
}
.openerp .oe_searchview .oe_searchview_drawer h4:before {
content: "▸ ";
+ color: #a3a3a3;
+}
+.openerp .oe_searchview .oe_searchview_drawer button {
+ margin: 4px 0;
}
-.openerp .oe_searchview .oe_searchview_drawer button, .openerp .oe_searchview .oe_searchview_drawer .button {
+.openerp .oe_searchview .oe_searchview_drawer .button {
border: none;
background: transparent;
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;
}
-.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_filters {
+.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_section {
display: table;
width: 100%;
}
-.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_filters > div {
- -moz-box-sizing: border-box;
+.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_section > div {
-webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ -ms-box-sizing: border-box;
box-sizing: border-box;
display: table-cell;
width: 50%;
}
-.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_filters h3 {
- margin: 2px 4px 2px 8px;
- color: #8786b7;
-}
-.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_filters ul {
- margin: 0 12px 3px;
+.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_section ul {
+ margin: 0 8px 8px;
padding: 0;
list-style: none;
}
-.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_filters li {
+.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_section li {
list-style: none;
- padding: 3px 6px 3px 18px;
+ padding: 2px 4px 2px 20px;
line-height: 14px;
color: inherit;
cursor: pointer;
+ position: relative;
}
-.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_filters li.oe_selected {
- background: url(/web/static/src/img/icons/gtk-apply.png) left 2px no-repeat;
+.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_section li.oe_selected:before {
+ content: "W";
+ font-family: "entypoRegular" !important;
+ font-size: 24px;
+ font-weight: 300 !important;
+ color: #a3a3a3;
+ position: absolute;
+ left: 4px;
+ top: -2px;
}
-.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_filters li:hover {
+.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_section li:hover {
background-color: #f0f0fa;
}
-.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_custom form {
- display: none;
+.openerp .oe_searchview .oe_searchview_drawer form {
+ margin-left: 12px;
}
-.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_custom form button {
- font-size: 1px;
- letter-spacing: -1px;
- color: transparent;
+.openerp .oe_searchview .oe_searchview_drawer form p {
+ margin: 4px 0;
+ line-height: 18px;
}
-.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_custom form button:before {
- font-family: "mnmliconsRegular";
- content: "S";
- font-size: 20px;
- color: #404040;
+.openerp .oe_searchview .oe_searchview_drawer form button {
+ margin: 0 0 8px 0;
+}
+.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_custom {
+ padding: 0 8px 8px 8px;
+}
+.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_custom form {
+ display: none;
}
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_custom li {
cursor: pointer;
position: relative;
- line-height: 1.2em;
- padding: 2px 20px 2px 25px;
-}
-.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_custom li.oe_searchview_custom_private {
- background: url(/web/static/src/img/icons/terp-locked.png) 5px center no-repeat;
+ line-height: 14px;
+ padding: 2px 4px 2px 20px;
}
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_custom li:hover {
background-color: #f0f0fa;
@@ -1383,89 +1829,211 @@
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;
}
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_advanced button.oe_add_condition:before {
- content: "⊞ ";
-}
-.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_advanced button.oe_apply:before {
- content: "⚡ ";
+ content: "Z";
+ font-family: "entypoRegular" !important;
+ font-size: 24px;
+ font-weight: 300 !important;
+ margin-right: 4px;
}
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_advanced ul {
list-style: none;
padding: 0;
}
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_advanced li {
+ position: relative;
list-style: none;
margin: 0;
white-space: nowrap;
}
+.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_advanced li:first-child .searchview_extended_prop_or {
+ visibility: hidden;
+}
+.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_advanced .searchview_extended_prop_or {
+ opacity: 0.5;
+ margin-left: -14px;
+}
.openerp .oe_searchview .oe_searchview_drawer .oe_opened h4:before {
content: "▾ ";
+ position: relative;
+ top: -1px;
}
.openerp .oe_searchview .oe_searchview_drawer .oe_opened form {
display: block;
}
.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_custom_delete, .openerp .oe_searchview .oe_searchview_drawer .searchview_extended_delete_prop {
- font-size: 1px;
- letter-spacing: -1px;
- color: transparent;
+ display: inline-block;
+ width: 12px;
+ height: 12px;
+ line-height: 12px;
+ padding: 1px;
+ color: #8786b7;
+ line-height: 8px;
+ text-align: center;
+ font-weight: bold;
+ text-shadow: 0 1px 1px white;
}
-.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_custom_delete:before, .openerp .oe_searchview .oe_searchview_drawer .searchview_extended_delete_prop:before {
- font-family: "mnmliconsRegular";
- content: "d";
- font-size: 20px;
- color: #404040;
+.openerp .oe_searchview .oe_searchview_drawer .oe_searchview_custom_delete:hover, .openerp .oe_searchview .oe_searchview_drawer .searchview_extended_delete_prop:hover {
+ text-decoration: none;
+ color: white;
+ 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_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_view.oe_cannot_create .oe_view_nocontent_create {
+ display: none;
}
.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_topbar {
+.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);
@@ -1473,125 +2041,94 @@
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_topbar button {
- font-size: 12px;
- height: 24px !important;
- line-height: 24px;
- vertical-align: top;
- padding: 0 10px;
- margin: 3px 4px 3px 0;
+.openerp .oe_form header > span {
+ margin-left: 4px;
}
-.openerp .oe_form_topbar button span.i {
- line-height: 20px;
- height: 24px;
+.openerp .oe_form header ul {
+ display: inline-block;
+ float: right;
}
-.openerp .oe_form_topbar 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 .oe_button {
+ margin: 3px 2px 1px;
}
-.openerp .oe_form_topbar 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 .oe_button:first-child {
+ margin-left: 6px;
}
-.openerp .oe_form_topbar ul li:first-child {
- border-left: 1px solid #cacaca;
+.openerp .oe_form header .oe_tags {
+ margin: 5px 0 0 5px;
+ width: 400px;
+ padding-bottom: 0;
}
-.openerp .oe_form_topbar ul li a {
- color: #4c4c4c;
+.openerp .oe_form div.oe_chatter {
+ box-sizing: border-box;
+ min-width: 682px;
+ max-width: 892px;
+ margin: 0 auto;
+ padding: 16px 16px 48px;
}
-.openerp .oe_form_topbar ul li a:hover {
- color: black;
+.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_topbar .oe_form_steps img {
- margin: 0 8px;
- vertical-align: top;
+.openerp .oe_form div.oe_form_configuration label {
+ min-width: 150px;
}
-.openerp .oe_form_topbar .oe_form_steps li {
- border-right: none;
- padding: 0;
+.openerp .oe_form div.oe_form_configuration .oe_form_group_cell_label {
+ padding: 1px 0;
}
-.openerp .oe_form_topbar .oe_form_steps li:first-child {
- margin-left: 12px;
- border-left: none;
+.openerp .oe_form div.oe_form_configuration .oe_form_group_cell div div {
+ padding: 1px 0;
+}
+.openerp .oe_form .oe_subtotal_footer {
+ width: 1% !important;
}
-.openerp .oe_form_topbar .oe_form_steps li:last-child {
- margin-right: 12px;
+.openerp .oe_form .oe_subtotal_footer td.oe_form_group_cell {
+ text-align: right;
+ padding: 0 !important;
+}
+.openerp .oe_form .oe_subtotal_footer td.oe_form_group_cell_label {
+ border-right: none;
}
-.openerp .oe_form_topbar .oe_form_steps_active {
+.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;
- color: #b33630;
+ font-size: 18px;
}
-.openerp .oe_form_topbar.oe_form_topbar_hifirst button:first-child, .openerp 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 .oe_subtotal_footer label:after {
+ content: ":";
}
-.openerp .oe_form_topbar.oe_form_topbar_hifirst button:first-child:hover, .openerp 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 .oe_subtotal_footer label.oe_subtotal_footer_separator {
+ font-weight: bold !important;
+ padding: 2px 11px 2px 0px !important;
}
-.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 .oe_subtotal_footer label.oe_form_label_help {
+ font-weight: normal;
}
-.openerp .oe_application .oe_form_sheet_width, .openerp .oe_application .oe_form_bottom {
- min-width: 650px;
- max-width: 980px;
- margin: 0 auto;
+.openerp .oe_form .oe_form_button {
+ margin: 2px;
}
-.openerp .oe_application .oe_form_sheet {
- padding: 8px 8px 8px 8px;
- background: white;
- min-height: 420px;
- 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 td.oe_form_group_cell_label {
+ border-right: 1px solid #dddddd;
+ padding: 2px 0px;
}
-.openerp .oe_form .oe_form_group_cell {
+.openerp .oe_form td.oe_form_group_cell_label label {
line-height: 18px;
- padding: 2px;
- height: 20px;
+ display: block;
+ min-width: 140px;
+}
+.openerp .oe_form td.oe_form_group_cell + .oe_form_group_cell {
+ padding: 2px 0 2px 8px;
}
-.openerp .oe_form .oe_form_group_cell > .oe_form_field_many2one,
-.openerp .oe_form .oe_form_group_cell > .oe_form_field_many2one input,
-.openerp .oe_form .oe_form_group_cell > .oe_form_field_char input,
-.openerp .oe_form .oe_form_group_cell > .oe_form_field_text textarea,
-.openerp .oe_form .oe_form_group_cell > .oe_form_field_email,
-.openerp .oe_form .oe_form_group_cell > .oe_form_field_email input,
-.openerp .oe_form .oe_form_group_cell > .oe_form_field_url,
-.openerp .oe_form .oe_form_group_cell > .oe_form_field_url input,
-.openerp .oe_form .oe_form_group_cell > .oe_form_field_float input,
-.openerp .oe_form .oe_form_group_cell > .oe_form_field_selection select,
-.openerp .oe_form .oe_form_group_cell > div > .oe_datepicker_root,
-.openerp .oe_form .oe_form_group_cell > div > .oe_datepicker_root > input.oe_datepicker_master {
+.openerp .oe_form .oe_form_group {
width: 100%;
+ margin: 9px 0 9px 0;
}
-.openerp .oe_form .oe_form_group_cell.oe_form_group_nested {
- padding: 0;
+.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;
@@ -1606,106 +2143,116 @@
top: -4px;
padding: 0 2px;
}
-.openerp .oe_form textarea {
- resize: vertical;
+.openerp .oe_horizontal_border {
+ border-bottom: 1px solid black;
}
-.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_horizontal_separator {
+ font-weight: bold;
+ font-size: 20px;
+ margin: 15px 0px 10px 0px;
+ color: #7c7bad;
}
-.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_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;
+ min-width: 70px;
+}
+.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;
- padding: 0 2px;
- margin: 0 2px;
border: 1px solid #999999;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
border-radius: 3px;
background: white;
- min-width: 90px;
+ min-width: 50px;
+}
+.openerp .oe_form_field_progressbar.ui-progressbar span {
+ position: absolute;
+ margin-left: 10px;
+ font-weight: bold;
+}
+.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_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_text.oe_inline, .openerp .oe_form .oe_form_field_text.oe_inline > textarea {
+ width: 500px;
+}
+.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 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 {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ -ms-box-sizing: border-box;
+ box-sizing: border-box;
+ background: white;
+ 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 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;
}
.openerp .oe_form textarea[disabled] {
border: none;
- border-left: 8px solid #eeeeee;
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 textarea.oe_inline[disabled] {
+ border-left: 8px solid #eeeeee;
+}
.openerp .oe_form .oe_form_field_url button img {
vertical-align: top;
}
-.openerp .oe_form .oe_form_field_translatable,
-.openerp .oe_form .oe_form_field_many2one,
.openerp .oe_form .oe_form_field_date,
.openerp .oe_form .oe_form_field_datetime {
white-space: nowrap;
}
.openerp .oe_form .oe_form_field_boolean {
padding-top: 4px;
-}
-.openerp .oe_form .oe_form_field_datetime input {
- min-width: 11em;
-}
-.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;
- cursor: pointer;
- 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;
@@ -1713,24 +2260,13 @@
.openerp .oe_form .oe_datepicker_root {
display: inline-block;
}
-.openerp .oe_form .oe_form_required input, .openerp .oe_form .oe_form_required select, .openerp .oe_form .oe_form_required textarea {
+.openerp .oe_form .oe_form_required input:not([disabled]):not([readonly]), .openerp .oe_form .oe_form_required select:not([disabled]):not([readonly]), .openerp .oe_form .oe_form_required textarea:not([disabled]):not([readonly]) {
background-color: #d2d2ff !important;
}
.openerp .oe_form .oe_form_invalid input, .openerp .oe_form .oe_form_invalid select, .openerp .oe_form .oe_form_invalid textarea {
background-color: #ff6666 !important;
border: 1px solid #dd0000 !important;
}
-.openerp .oe_form .oe_button.oe_field_button {
- display: block;
- -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;
- border-bottom-left-radius: 0px;
- margin-right: -1px;
- height: 22px;
-}
.openerp .oe_form .oe_input_icon {
cursor: pointer;
margin: 3px 0 0 -21px;
@@ -1744,222 +2280,423 @@
right: 5px;
top: 3px;
}
-.openerp .oe_form .oe_form_button.oe_button {
+.openerp .oe_form .oe_form_field_with_button.oe_no_button > .oe_button {
+ display: none;
+}
+.openerp .oe_form .oe_form_field_with_button:not(.oe_no_button) > .oe_button {
+ float: right;
+ -moz-border-radius: 0;
+ -webkit-border-radius: 0;
+ border-radius: 0;
+ border-bottom-left-radius: 0px;
height: 22px;
- color: #4c4c4c;
}
-.openerp .oe_form .oe_form_button.oe_button span {
+.openerp .oe_form .oe_form_field_with_button input {
+ width: 100%;
+}
+.openerp .oe_form .oe_form_field_with_button > div {
position: relative;
- vertical-align: top;
+ overflow: hidden;
}
-.openerp .oe_form .oe_form_button > img {
- vertical-align: -3px;
- padding: 0 2px;
+.openerp .oe_form .oe_form_embedded_html {
+ position: relative;
+ width: 600px;
+ margin-left: 130px;
+ margin-top: 32px;
+ margin-bottom: 32px;
+ text-align: justify;
}
-.openerp .oe_form .oe_form_group_cell > .oe_form_button.oe_button {
- display: block;
- white-space: nowrap;
- min-width: 100%;
- width: 100%;
+.openerp .oe_form_editable .oe_form .oe_form_field_integer input {
+ width: 6em !important;
}
-.openerp .oe_form .oe-binary-file-set {
- overflow: hidden;
+.openerp .oe_form_editable .oe_form .oe_form_field_float input {
+ width: 7em !important;
+}
+.openerp .oe_form_editable .oe_form .oe_form_field_date input {
+ width: 7.5em !important;
+}
+.openerp .oe_form_editable .oe_form .oe_form_field_datetime input {
+ width: 11.5em !important;
+}
+.openerp .oe_hidden_input_file {
position: relative;
- 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)";
margin: 0;
padding: 0;
}
-.openerp .oe_horizontal_border {
- border-bottom: 1px solid black;
+.openerp .oe_form .oe_form_field_image {
+ padding: 0;
+ position: relative;
+ display: inline-block;
+ width: auto;
+ vertical-align: top;
}
-.openerp .oe_horizontal_separator {
- font-weight: bold;
- font-size: 16px;
- margin: 4px 0;
+.openerp .oe_form .oe_form_field_image .oe_form_field_image_controls {
+ position: absolute;
+ top: 1px;
+ padding: 4px;
+ width: 100%;
+ display: none;
+ text-align: center;
+ 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_horizontal_separator:empty {
- height: 5px;
+.openerp .oe_form .oe_form_field_image:hover .oe_form_field_image_controls {
+ display: block;
}
-.openerp .oe_vertical_separator {
- border-left: 1px solid #666666;
- padding: 0 4px 0 4px;
+.openerp .oe_form_field_many2one td:first-child {
+ position: relative;
}
-.openerp .oe_form_notebook {
- margin: 8px 0;
- padding: 0 8px;
- list-style: none;
- zoom: 1;
+.openerp .oe_form_field_many2one span.oe_m2o_drop_down_button {
+ position: absolute;
+ top: 2px;
+ right: 0px;
}
-.openerp .oe_form_notebook:before, .openerp .oe_form_notebook:after {
- display: table;
- content: "";
- zoom: 1;
+.openerp .oe_form_field_many2one .oe_m2o_cm_button {
+ line-height: 14px;
+ float: right;
+ padding-left: 2px;
}
-.openerp .oe_form_notebook:after {
- clear: both;
+.openerp.ui-autocomplete li.oe_m2o_dropdown_option a {
+ font-style: italic;
+ padding-left: 2em;
}
-.openerp .oe_form_notebook > li {
- float: left;
+.openerp.ui-autocomplete li:not(.oe_m2o_dropdown_option) + li.oe_m2o_dropdown_option {
+ margin-top: 10px;
}
-.openerp .oe_form_notebook > li > a {
- display: block;
- color: #4c4c4c;
+.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 {
- border-color: #dddddd;
- border-style: solid;
- border-width: 0 0 1px;
+.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 {
- position: relative;
- margin-bottom: -1px;
+.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 {
- 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:last-child, .openerp ul.oe_form_status_clickable li:last-child {
+ border-right: 1px solid #cacaca;
}
-.openerp .oe_form_notebook > li > a:hover {
- text-decoration: none;
- background-color: #eeeeee;
- border-color: #eeeeee #eeeeee #dddddd;
+.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.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 li:last-child .arrow, .openerp ul.oe_form_status_clickable li:last-child .arrow {
+ display: none;
}
-.openerp .oe_form_notebook_page {
- padding: 0;
+.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;
}
-.openerp .ui-tabs-hide {
- display: none;
+.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 .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;
+.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;
- background: white;
- min-width: 90px;
+ -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 .oe_form_field_progressbar.ui-progressbar span {
- position: absolute;
- margin-left: 10px;
+.openerp ul.oe_form_status_clickable li {
+ cursor: pointer;
+}
+.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 .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 .oe_form_field_one2many > .oe_view_manager .oe_view_manager_view_list, .openerp .oe_form_field_many2many > .oe_view_manager .oe_view_manager_view_list {
+ min-height: 132px;
+}
+.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.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_content > thead, .openerp .oe_form_field_many2many .oe_list_content > thead {
+ border-bottom: 1px;
+}
+.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_view_manager_header2 td {
+.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_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_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 .oe_form_title, .openerp .oe_form .oe_form_title input {
- font-size: 20px;
- font-weight: bold;
- height: 30px;
+.openerp .oe_form_editable .oe_list_editable .oe_list_content td.oe_required {
+ background-color: #d2d2ff;
}
-.openerp .oe_form .oe_form_group_odd_border > tbody > tr.oe_form_group_row > td.oe_form_group_cell:nth-child(odd),
-.openerp .oe_form .oe_form_group_label_border > tbody > tr.oe_form_group_row > td.oe_form_group_cell_label {
- border-right: 1px solid #dddddd;
+.openerp .oe_form_editable .oe_list_editable .oe_list_content td.oe_readonly {
+ background-color: #eeeeee;
}
-.openerp .oe_form .oe_form_group_odd_border > tbody > tr.oe_form_group_row > td.oe_form_group_cell:nth-child(odd) label,
-.openerp .oe_form .oe_form_group_label_border > tbody > tr.oe_form_group_row > td.oe_form_group_cell_label label {
- display: block;
- min-width: 120px;
+.openerp .oe_list_editable .oe_list_content td.oe_list_field_cell {
+ padding: 4px 6px 3px 6px;
}
-.openerp .oe_form .oe_form_group_odd_border > tbody > tr.oe_form_group_row > td.oe_form_group_cell,
-.openerp .oe_form .oe_form_group_label_border > tbody > tr.oe_form_group_row > td.oe_form_group_cell {
- padding-left: 8px;
+.openerp .oe_list.oe_list_editable.oe_editing .oe_edition .oe_list_field_cell:not(.oe_readonly) {
+ color: transparent;
+ text-shadow: none;
}
-.openerp .oe_form .oe_form_subtotal_footer {
- float: right;
+.openerp .oe_list.oe_list_editable.oe_editing .oe_edition .oe_list_field_cell:not(.oe_readonly) * {
+ visibility: hidden;
+}
+.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 > div {
+.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: 3px;
+ width: 100% !important;
+}
+.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% !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 label {
- font-weight: normal !important;
+.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_list_group_name {
+ white-space: nowrap;
+}
+.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_form .oe_form_subtotal_footer span {
+.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;
- min-width: 80px;
- text-align: right;
}
-.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_buttons.oe_editing .oe_list_discard {
+ display: inline;
}
-.openerp .oe_form .oe_form_subtotal_footer .oe_form_subtotal_footer_separator label {
- font-weight: bold !important;
+.openerp .oe_list_buttons.oe_editing .oe_alternative {
+ visibility: visible;
+}
+.openerp .oe_list.oe_cannot_edit .oe_list_header_handle, .openerp .oe_list.oe_cannot_edit .oe_list_field_handle {
+ display: none !important;
+ padding: 0 !important;
+}
+.openerp .oe_list.oe_cannot_delete .oe_list_record_delete {
+ display: none !important;
}
-.openerp .oe-listview-content {
+.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 td, .openerp .oe_list_content th {
padding: 3px 6px;
line-height: 18px;
}
-.openerp .oe-listview-content > thead th:after {
+.openerp .oe_list_content th.oe_sortable, .openerp .oe_list_content th.oe_sortable div {
+ cursor: pointer;
+}
+.openerp .oe_list_content th.oe_sortable div {
+ position: relative;
+}
+.openerp .oe_list_content th.oe_sortable div: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 div:after {
visibility: visible;
filter: alpha(opacity=60);
opacity: 0.6;
}
-.openerp .oe-listview-content > thead th.sortdown:after {
+.openerp .oe_list_content th.sortdown div:after {
border-bottom: none;
border-left: 4px solid transparent;
border-right: 4px solid transparent;
@@ -1967,39 +2704,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, .openerp .oe-listview-content > tbody > tr > th {
+.openerp .oe_list_content > tbody > tr > td.oe_list_field_cell {
padding: 3px 6px;
- line-height: 18px;
+ white-space: pre-line;
}
-.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));
@@ -2009,104 +2743,101 @@
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_kanban_group_title {
- margin: 1px 1px 4px;
- font-size: 15px;
- font-weight: bold;
- text-shadow: 0 1px 0 white;
+.openerp .oe_list_content th.oe_list_header_handle {
+ font-size: 1px;
+ overflow: hidden;
+ text-indent: -9001px;
}
-.openerp .oe_kanban_column, .openerp .oe_kanban_group_header {
- width: 240px;
- vertical-align: top;
- padding: 6px 7px 6px 6px;
- background: #f0eeee;
- border-left: 1px solid #f0f8f8;
- border-right: 1px solid #b9b9b9;
+.openerp .oe_list_content td.oe_list_field_handle {
+ width: 1em;
+ padding: 0 !important;
+ cursor: ns-resize;
}
-.openerp .oe_kanban_record {
- position: relative;
- display: block;
- min-height: 50px;
- margin: 6px 0;
- display: block;
- -moz-border-radius: 4px;
- -webkit-border-radius: 4px;
- border-radius: 4px;
+.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_list_content td.oe_list_field_handle .oe_list_handle:before {
+ font: 18px "entypoRegular";
+ content: "}";
+ color: #e0e0e0;
+}
+.openerp .oe_list_content .oe_list_field_progressbar progress {
+ width: 100%;
+}
+.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_kanban_record:last-child {
- margin-bottom: 0;
+.openerp .oe-treeview-table {
+ width: 100%;
+ background-color: white;
+ border-spacing: 0;
+ color: #4c4c4c;
}
-.openerp .oe_kanban_record .oe_kanban_title {
+.openerp .oe-treeview-table th {
+ padding: 10px;
font-weight: bold;
- margin: 2px 4px;
+ background-color: #f0f0f0;
+ border-bottom: 2px solid #cacaca;
}
-.openerp .oe_kanban_gravatar {
- width: 20px;
+.openerp .oe-treeview-table td {
+ cursor: pointer;
+ vertical-align: middle;
+ text-align: left;
+ vertical-align: middle;
height: 20px;
- -moz-border-radius: 3px;
- -webkit-border-radius: 3px;
- border-radius: 3px;
- -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
- -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
- -box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
+ padding-left: 4px;
+ padding-right: 4px;
+ border-right: 1px solid #e7e7e7;
}
-.openerp .oe_kanban_avatar_toto {
- height: 40px;
- width: 40px;
- border: 1px solid;
- border-color: #e5e5e5 #dbdbdb #d2d2d2;
- -moz-border-radius: 3px;
- -webkit-border-radius: 3px;
- border-radius: 3px;
- -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
- -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
- -box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
+.openerp .oe-treeview-table td.oe_number {
+ text-align: right !important;
}
-.openerp .oe_trad_field.touched {
- border: 1px solid green !important;
+.openerp .oe-treeview-table tr {
+ border-bottom: 1px solid #d6d6d6;
}
-.openerp.oe_tooltip {
- font-size: 12px;
+.openerp .oe-treeview-table tr:hover {
+ background-color: #e7e7e7;
}
-.openerp.oe_tooltip .oe_tooltip_string {
- color: #ffdd55;
- font-weight: bold;
- font-size: 13px;
+.openerp .oe-treeview-table span {
+ font-size: 90%;
+ font-weight: normal;
+ white-space: nowrap;
+ display: block;
}
-.openerp.oe_tooltip .oe_tooltip_help {
- white-space: pre-wrap;
+.openerp .oe-treeview-table .treeview-tr.oe-treeview-first {
+ background: transparent url(/web/static/src/img/expand.gif) 0 50% no-repeat;
}
-.openerp.oe_tooltip .oe_tooltip_technical {
- padding: 0 0 4px 0;
- margin: 5px 0 0 15px;
+.openerp .oe-treeview-table .oe_open .treeview-tr.oe-treeview-first {
+ background-image: url(/web/static/src/img/collapse.gif);
}
-.openerp.oe_tooltip .oe_tooltip_technical li {
- list-style: circle;
-}
-.openerp.oe_tooltip .oe_tooltip_technical_title {
- font-weight: bold;
+.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;
@@ -2149,11 +2880,166 @@
color: #333333;
}
-.openerp .oe_form_field_many2one td:first-child {
+.openerp .oe_fileupload {
+ display: inline-block;
+ clear: both;
+ width: 100%;
+}
+.openerp .oe_fileupload .oe_add {
+ float: left;
position: relative;
+ width: 100%;
+ left: 2px;
+ top: 7px;
}
-.openerp .oe_form_field_many2one span.oe-m2o-drop-down-button {
- position: absolute;
- top: 3px;
- right: 2px;
+.openerp .oe_fileupload .oe_add button {
+ display: inline;
+ height: 24px;
+ font-size: 12px;
+ line-height: 12px;
+ vertical-align: middle;
+}
+.openerp .oe_fileupload .oe_add button.oe_attach {
+ width: 24px;
+ overflow: hidden;
+ width: 24px;
+ overflow: hidden;
+ background: transparent;
+ color: #7c7bad;
+ box-shadow: none;
+ border: none;
+ text-shadow: none;
+}
+.openerp .oe_fileupload .oe_add button.oe_attach .oe_e {
+ position: relative;
+ top: -1px;
+ left: -9px;
+}
+.openerp .oe_fileupload .oe_add input.oe_form_binary_file {
+ display: inline-block;
+ margin-left: -5px;
+ height: 28px;
+ width: 52px;
+ margin-top: -26px;
+}
+.openerp .oe_fileupload .oe_add .oe_attach_label {
+ color: #7c7bad;
+ margin-left: -3px;
+}
+.openerp .oe_fileupload .oe_attachments {
+ margin-bottom: 4px;
+ margin-right: 0px;
+ font-size: 12px;
+ border-radius: 2px;
+ border: solid 1px rgba(124, 123, 173, 0.14);
+}
+.openerp .oe_fileupload .oe_attachments .oe_attachment {
+ padding: 2px;
+ padding-left: 4px;
+ padding-right: 4px;
+}
+.openerp .oe_fileupload .oe_attachments .oe_attachment .oe_e {
+ font-size: 23px;
+ margin-top: -5px;
+}
+.openerp .oe_fileupload .oe_attachments .oe_attachment .oe_e:hover {
+ text-decoration: none;
+}
+.openerp .oe_fileupload .oe_attachments .oe_attachment:nth-child(odd) {
+ background: white;
+}
+.openerp .oe_fileupload .oe_attachments .oe_attachment:nth-child(even) {
+ background: #f4f5fa;
+}
+
+.kitten-mode-activated {
+ background-image: url(http://placekitten.com/g/1365/769);
+ background-size: cover;
+ background-attachment: fixed;
+}
+.kitten-mode-activated > * {
+ opacity: 0.7;
+}
+
+div.ui-widget-overlay {
+ background: black;
+ filter: alpha(opacity=30);
+ opacity: 0.3;
+}
+
+.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;
+}
+
+@media print {
+ .openerp {
+ text-shadow: none;
+ }
+ .openerp .oe_header_row, .openerp ul.oe_header, .openerp div.oe_mail_thread_action, .openerp .oe_mail_recthread_actions, .openerp .oe_button_box, .openerp .oe_form button, .openerp button.oe_invite, .openerp .oe_form header, .openerp .openerp .oe_notebook > li.ui-state-default {
+ display: none;
+ }
+ .openerp .oe_list_content button, .openerp .oe_list_content input[type=checkbox] {
+ visibility: hidden;
+ }
+ .openerp .tree_header button, .openerp .oe_mail .oe_mail_thread_msg .oe_mail_unread, .openerp .oe_mail_fetch_more, .openerp .oe_m2o_drop_down_button img, .openerp .oe_form_field_one2many_list_row_add {
+ visibility: hidden;
+ }
+ .openerp a.oe_m2o_cm_button, .openerp a.oe_e {
+ visibility: hidden;
+ }
+ .openerp .oe_form .oe_form_field_date img, .openerp .oe_form .oe_form_field_datetime img {
+ visibility: hidden;
+ }
+ .openerp .oe_notebook > li.ui-tabs-selected {
+ display: block;
+ }
+ .openerp .oe_application .oe_form_sheet, .openerp .oe_application .oe_form_sheetbg {
+ border: 0px !important;
+ box-shadow: 0px 0px 0px;
+ }
+ .openerp .oe_view_manager_current > .oe_view_manager_header {
+ border: 0px !important;
+ box-shadow: 0px 0px 0px;
+ }
+ .openerp .text-core .text-wrap .text-arrow {
+ background: none;
+ }
+ .openerp .openerp div.oe_mail_wall {
+ overflow: hidden !important;
+ }
}
diff --git a/addons/web/static/src/css/base.sass b/addons/web/static/src/css/base.sass
index 3dcde9c9f70..e388ad16727 100644
--- a/addons/web/static/src/css/base.sass
+++ b/addons/web/static/src/css/base.sass
@@ -1,11 +1,17 @@
@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
+$sheet-min-width: 650px
+$sheet-padding: 16px
// }}}
// Mixins {{{
@font-face
@@ -27,6 +33,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 +50,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 +65,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 +83,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,17 +122,35 @@ $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 {{{
@@ -101,21 +160,32 @@ $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
- &:last-child
- border-right: none
+ border-left: 1px solid #dfdfdf
+ &:first-child
+ border-left: none
+ &.null
+ border-left: none
th, td
padding: 0
text-align: left
@@ -129,51 +199,23 @@ $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
- // }}}
- // 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
+ input, textarea, select
+ padding: 2px 4px
+ border: 1px solid #ccc
+ @include radius(3px)
+ 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: 4px 12px
+ padding: 3px 12px
font-size: 13px
text-align: center
@include vertical-gradient(#efefef, #d8d8d8)
@@ -183,20 +225,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
@@ -206,32 +247,417 @@ $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)
+ // bug noted in jquery ui CSS doesn't seem to occur in IE9,
+ // so remove position:relative
+ .ui-tabs
+ position: static
- button.primary:hover
- @include vertical-gradient(lighten(#dc5f59, 3%), lighten(#b33630, 3%))
- @include box-shadow(0 0 1px rgba(0,0,0,0.2))
+ // 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: 0
+ .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:active
- @include vertical-gradient(#b33630, #dc5f59)
- @include box-shadow(none)
+ &.ui-dialog.oe_act_window
+ .ui-dialog-content
+ padding: 0px
- button.following
- color: white
- width: 120px
- @include vertical-gradient($colour4, darken($colour4, 3%))
+ .modal-backdrop
+ position: fixed
+ top: 0
+ right: 0
+ bottom: 0
+ left: 0
+ z-index: 1040
+ background-color: black
+ @include opacity(.3)
- button.unfollow
- display: none
+ // }}}
+ // 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(#dc5f59, #b33630)
+ 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
+
+ .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
+ p.oe_grey
+ max-width: 650px
+ .oe_grey
+ color: #aaa
// }}}
+
+ // 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.3)
+ 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
display: none
@@ -256,13 +682,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
@@ -279,7 +706,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
@@ -379,63 +806,118 @@ $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.30)
+ text-shadow: black 0px 0px 3px
+ @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
@@ -462,149 +944,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
- 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
@@ -614,7 +969,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
@@ -622,32 +977,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
@@ -669,123 +1024,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
@@ -794,511 +1165,534 @@ $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_list:after, .oe_vm_switch_tree:after
- //content: "ö"
- //.oe_vm_switch_graph:after
- //content: "ó"
- //.oe_vm_switch_gantt:after
- //content: "y"
- //.oe_vm_switch_calendar:after
- //content: "b"
- //.oe_vm_switch_kanban:after
- //content: "ó"
- //.oe_vm_switch_diagram:after
- //content: "}"
+ .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
- 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 xmo {{{
+ // SearchView {{{
.oe_searchview
cursor: text
position: relative
float: right
- padding-right: 20px
- width: 410px
+ padding: 1px 0
+ line-height: 18px
+ width: 400px
border: 1px solid #ababab
background: white
- @include radius(1em)
+ @include radius(13px)
@include box-shadow(0 1px 2px rgba(0,0,0,0.2) inset)
-
+ input, textarea
+ padding: 3px
+ height: 14px
+ font-size: 12px
+ line-height: 18px
&.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
top: 0
- right: 22px
+ right: 18px
width: 15px
height: 100%
- background: url(../img/attachments-close.png) center center no-repeat
-
+ background: url(../img/search_reset.gif) center center no-repeat
.oe_searchview_unfold_drawer
position: absolute
top: 0
right: 0
height: 100%
- line-height: 2.5em
padding: 0 7px 0 4px
color: #ccc
cursor: pointer
&:hover
color: #999
&:before
- content: "◀"
+ position: absolute
+ top: 10px
+ right: 7px
+ width: 0
+ height: 0
+ display: inline-block
+ content: ""
+ vertical-align: top
+ border-top: 5px solid #4C4C4C
+ border-left: 5px solid transparent
+ border-right: 5px solid transparent
+ @include opacity()
- .oe_searchview_facets
- &:before
- color: #ccc
- font-family: "mnmliconsRegular"
- content: "r"
- font-size: 150%
- padding: 0 1px 0 3px
- display: inline
+ .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
+ margin-left: 15px
*
vertical-align: top
display: inline-block
- line-height: 26px
-
- .oe_searchview_input, .oe_searchview_facet
- height: 26px
+ line-height: 17px
+ .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
- padding-left: 1.1em
// spacing for opera, FF
- margin: 1px 0
-
&: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)
-
.oe_facet_category, .oe_facet_value
- height: 24px
- padding: 1px 0.1em
-
+ height: 18px
+ padding: 0 4px
.oe_facet_category
color: white
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.4)
-
+ .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
position: absolute
- top: 0
- left: 2px
- color: white
-
+ top: 3px
+ right: 3px
+ color: #8786b7
+ line-height: 8px
+ width: 12px
+ height: 12px
+ padding-top: 1px
+ text-align: center
+ font-weight: bold
+ cursor: pointer
+ text-shadow: 0 1px 1px white
&:hover
- color: #ccc
-
+ color: white
+ background: #8786b7
+ text-shadow: 0 1px 1px rgba(0, 0, 0, 0.4)
+ @include radius(2px)
&.oe_searchview_open_drawer
.oe_searchview_drawer
display: block
- .oe_searchview_unfold_drawer:before
- content: "▼"
.oe_searchview_drawer
position: absolute
- z-index: 1
+ z-index: 100
// detach drawer from field slightly
- margin-top: 3px
+ margin-top: 4px
top: 100%
- right: 0
+ right: -1px
background-color: white
min-width: 100%
display: none
- border: 1px solid #ccc
+ border: 1px solid $tag-border
text-align: left
- padding-bottom: 0.5em
- @include radius(1em)
-
+ @include radius(4px)
+ @include box-shadow(0 1px 4px rgba(0,0,0,0.3))
> div
border-top: 1px solid #ccc
- margin: 3px 0
+ margin: 0
+ padding: 8px
> div:first-child
border-top: none
margin: 0
-
+ h3
+ margin: 8px 4px 4px 12px
+ color: $section-title-color
+ font-size: 13px
h4, h4 *
margin: 0
cursor: pointer
-
+ font-weight: normal
+ display: inline-block
+ &:hover
+ background-color: $hover-background
h4:before
content: "▸ "
-
- button, .button
+ color: #a3a3a3
+ button
+ margin: 4px 0
+ .button
border: none
background: transparent
padding: 0 2px
@include box-shadow(none)
@include radius(0)
-
- .oe_searchview_filters
+ .oe_searchview_section
display: table
width: 100%
-
> div
- @include unscrew-box
+ @include box-sizing(border)
display: table-cell
width: 50%
-
- h3
- margin: 2px 4px 2px 8px
- color: $section-title-color
-
ul
- margin: 0 12px 3px
+ margin: 0 8px 8px
padding: 0
list-style: none
-
li
list-style: none
- padding: 3px 6px 3px 18px
+ padding: 2px 4px 2px 20px
line-height: 14px
color: inherit
cursor: pointer
-
- &.oe_selected
- background: url(/web/static/src/img/icons/gtk-apply.png) left 2px no-repeat
+ position: relative
+ &.oe_selected:before
+ content: "W"
+ font-family: "entypoRegular" !important
+ font-size: 24px
+ font-weight: 300 !important
+ color: #a3a3a3
+ position: absolute
+ left: 4px
+ top: -2px
// 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
.oe_searchview_custom
+ padding: 0 8px 8px 8px
form
display: none
-
- button
- @include text-to-icon("S")
li
cursor: pointer
position: relative
- line-height: 1.2em
- padding: 2px 20px 2px 25px
-
- &.oe_searchview_custom_private
- background: url(/web/static/src/img/icons/terp-locked.png) 5px center no-repeat
-
+ line-height: 14px
+ padding: 2px 4px 2px 20px
&:hover
background-color: $hover-background
-
button
position: absolute
top: 0
right: 5px
-
+ .oe_searchview_dashboard
+ form
+ display: none
+ margin-top: 2px
.oe_searchview_advanced
form
display: none
-
+ margin-top: 8px
button.oe_add_condition:before
- content: "⊞ "
- button.oe_apply:before
- content: "⚡ "
-
+ content: "Z"
+ font-family: "entypoRegular" !important
+ font-size: 24px
+ font-weight: 300 !important
+ margin-right: 4px
ul
list-style: none
padding: 0
li
+ position: relative
list-style: none
margin: 0
white-space: nowrap
-
+ &:first-child .searchview_extended_prop_or
+ visibility: hidden
+ .searchview_extended_prop_or
+ opacity: 0.5
+ margin-left: -14px
.oe_opened
h4:before
content: "▾ "
+ position: relative
+ top: -1px
form
display: block
// delete buttons
.oe_searchview_custom_delete, .searchview_extended_delete_prop
- @include text-to-icon("d")
+ display: inline-block
+ width: 12px
+ height: 12px
+ line-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)
+ .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
+ .oe_view.oe_cannot_create
+ .oe_view_nocontent_create
+ display: none
// }}}
- // 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_topbar
- border-bottom: 1px solid #cacaca
- @include vertical-gradient(#fcfcfc, #dedede)
- padding: 0 8px
- line-height: 30px
- button
- font-size: 12px
- 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
+ .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
+ .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
- 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
- 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_form_topbar.oe_form_topbar_hifirst button:first-child, 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
- .oe_form_topbar.oe_form_topbar_hifirst button:first-child:hover, 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_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, .oe_form_bottom
- min-width: 650px
- max-width: 980px
- margin: 0 auto
+ border-bottom: 1px solid #ddd
.oe_form_sheet
- padding: 8px 8px 8px 8px
- background: white
- min-height: 420px
- border: 1px solid #afafb6
- @include box-shadow(0 0 10px rgba(0,0,0,0.3))
+ 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.layout {{{
+ // FormView.header {{{
+ .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_button
+ margin: 3px 2px 1px
+ &:first-child
+ margin-left: 6px
+ // }}}
+ // FormView.custom tags and classes {{{
.oe_form
- .oe_form_group_cell
- line-height: 18px
- padding: 2px
- height: 20px
- .oe_form_group_cell > .oe_form_field_many2one,
- .oe_form_group_cell > .oe_form_field_many2one input,
- .oe_form_group_cell > .oe_form_field_char input,
- .oe_form_group_cell > .oe_form_field_text textarea,
- .oe_form_group_cell > .oe_form_field_email,
- .oe_form_group_cell > .oe_form_field_email input,
- .oe_form_group_cell > .oe_form_field_url,
- .oe_form_group_cell > .oe_form_field_url input,
- .oe_form_group_cell > .oe_form_field_float input,
- .oe_form_group_cell > .oe_form_field_selection select,
- .oe_form_group_cell > div > .oe_datepicker_root,
- .oe_form_group_cell > div > .oe_datepicker_root > input.oe_datepicker_master
+ header
+ .oe_tags
+ margin: 5px 0 0 5px
+ width: 400px
+ padding-bottom: 0
+ div.oe_chatter
+ box-sizing: border-box
+ min-width: $sheet-min-width + 2* $sheet-padding
+ max-width: $sheet-max-width + 2* $sheet-padding
+ margin: 0 auto
+ padding: 16px 16px 48px
+ 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.group {{{
+ .oe_form
+ .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_group_cell.oe_form_group_nested
- padding: 0
+ margin: 9px 0 9px 0
+ .oe_form_group_cell.oe_group_right
+ padding-left: 20px
// }}}
// FormView.label {{{
.oe_form
@@ -1314,113 +1708,99 @@ $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
- textarea
- resize: vertical
- input[type="text"],
- input[type="password"],
- input[type="file"],
- select
+ .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
+ input
+ margin: 0px
+ 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
- margin: 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: 90px
+ min-width: 60px
color: #1f1f1f
- input[readonly],
- select[readonly],
- textarea[readonly],
- input[disabled],
- select[disabled],
+ input[readonly], select[readonly], textarea[readonly], input[disabled], select[disabled]
background: #E5E5E5 !important
color: #666
textarea[disabled]
border: none
- border-left: 8px solid #eee
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,
+ textarea.oe_inline[disabled]
+ border-left: 8px solid #eee
.oe_form_field_url button img
vertical-align: top
- .oe_form_field_translatable,
- .oe_form_field_many2one,
.oe_form_field_date,
.oe_form_field_datetime
white-space: nowrap
.oe_form_field_boolean
padding-top: 4px
- .oe_form_field_datetime input
- min-width: 11em
- .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
- cursor: pointer
- 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
display: inline-block
.oe_form_required
- input, select, textarea
+ input:not([disabled]):not([readonly]), select:not([disabled]):not([readonly]), textarea:not([disabled]):not([readonly])
background-color: #D2D2FF !important
.oe_form_invalid
input, select, textarea
background-color: #F66 !important
border: 1px solid #D00 !important
- .oe_button.oe_field_button
- display: block
- -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
- border-bottom-left-radius: 0px
- margin-right: -1px
- height: 22px
.oe_input_icon
cursor: pointer
margin: 3px 0 0 -21px
@@ -1432,227 +1812,351 @@ $colour4: #8a89ba
filter: alpha(opacity=50)
right: 5px
top: 3px
- // }}}
- // FormView.buttons {{{
- .oe_form
- .oe_form_button.oe_button
- height: 22px
- color: #4c4c4c
- span
+ .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
+ @include radius(0)
+ border-bottom-left-radius: 0px
+ height: 22px
+ .oe_form_field_with_button
+ input
+ width: 100%
+ > div
position: relative
- vertical-align: top
- .oe_form_button > img
- vertical-align: -3px
- padding: 0 2px
- .oe_form_group_cell > .oe_form_button.oe_button
- display: block
- white-space: nowrap
- min-width: 100%
- width: 100%
+ overflow: hidden
+ .oe_form_embedded_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 input
+ width: 6em !important
+ .oe_form_field_float input
+ width: 7em !important
+ .oe_form_field_date input
+ width: 7.5em !important
+ .oe_form_field_datetime input
+ 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
- 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
+ padding: 0
+ position: relative
+ display: inline-block
+ width: auto
+ vertical-align: top
+ .oe_form_field_image_controls
+ position: absolute
+ top: 1px
+ padding: 4px
+ width: 100%
+ display: none
+ text-align: center
+ 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
+ display: block
// }}}
- // FormView.separator {{{
- .oe_horizontal_border
- border-bottom: 1px solid black
- .oe_horizontal_separator
- font-weight: bold
- font-size: 16px
- margin: 4px 0
- .oe_horizontal_separator:empty
- height: 5px
- .oe_vertical_separator
- border-left: 1px solid #666
- padding: 0 4px 0 4px
+ // 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.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
- .ui-tabs-hide
- display: none
-
+ // 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
+ 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.progressbar {{{
- .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 #999
- -moz-border-radius: 3px
- -webkit-border-radius: 3px
- border-radius: 3px
- background: white
- min-width: 90px
-
- span
- position: absolute
- margin-left: 10px
- font-weight: bold
+ // 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_view_manager_view_list
+ min-height: 132px
+
+ .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
- .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.viewmanager {{{
- .oe_form
- .oe_view_manager_header2
- td
- padding: 0px 8px
- line-height: 16px
- .oe_i
- font-size: 13px
- .oe_pager_group
- height: auto
+ &> .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.oe_editing
+ .oe_edition .oe_list_field_cell:not(.oe_readonly)
+ *
+ visibility: hidden
+ color: transparent
+ text-shadow: none
+ .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% !important
+ &.oe_form_field_datetime,&.oe_form_field_date
+ > span
+ width: 100% !important
+ 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
+ .oe_list_group_name
+ white-space: nowrap
// }}}
- // FormView.classes for openerp views {{{
- .oe_form
- .oe_form_title, .oe_form_title input
- font-size: 20px
- font-weight: bold
- height: 30px
- .oe_form_group_odd_border > tbody > tr.oe_form_group_row > td.oe_form_group_cell:nth-child(odd),
- .oe_form_group_label_border > tbody > tr.oe_form_group_row > td.oe_form_group_cell_label
- border-right: 1px solid #ddd
- label
- display: block
- min-width: 120px
- .oe_form_group_odd_border > tbody > tr.oe_form_group_row > td.oe_form_group_cell,
- .oe_form_group_label_border > tbody > tr.oe_form_group_row > td.oe_form_group_cell
- padding-left: 8px
- .oe_form_subtotal_footer
- float: right
- > div
- text-align: right
- padding: 3px
- label
- font-weight: normal !important
- span
- display: inline-block
- min-width: 80px
- text-align: right
- .oe_form_subtotal_footer_separator
- border-top: 1px solid #cacaca
- font-size: 120%
- font-weight: bold
- label
- font-weight: bold !important
+ // FormView.many2many {{{
+ .oe_form .oe_form_field_many2many > .oe_list
+ .oe_list_pager_single_page
+ display: none
// }}}
// ListView {{{
- .oe-listview-content
+ .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
+ &.oe_cannot_edit
+ .oe_list_header_handle, .oe_list_field_handle
+ display: none !important
+ padding: 0 !important
+ &.oe_cannot_delete
+ .oe_list_record_delete
+ display: none !important
+
+ .oe_form
+ .oe_form_nosheet
+ margin: 0 // FIXME: either class or border should not be by default
+ padding: 0
+ border: none
+ .oe_form_field
+ 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)
+ td, th
+ padding: 3px 6px
+ line-height: 18px
+ th.oe_sortable, th.oe_sortable div
+ cursor: pointer
+ th.oe_sortable div
+ position: relative
+ th.oe_sortable div: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 div:after
+ visibility: visible
+ @include opacity(0.6)
+ th.sortdown div: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, > th
+ > td.oe_list_field_cell
padding: 3px 6px
- line-height: 18px
+ white-space: pre-line
+ > td, > th
> button
border: none
background: transparent
@@ -1662,84 +2166,85 @@ $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_field_progressbar progress
+ width: 100%
// }}}
- // KanbanView {{{
- .oe_kanban_group_title
- margin: 1px 1px 4px
- font-size: 15px
- font-weight: bold
- text-shadow: 0 1px 0 white
- .oe_kanban_column, .oe_kanban_group_header
- width: 240px
- vertical-align: top
- padding: 6px 7px 6px 6px
- background: #f0eeee
- border-left: 1px solid #f0f8f8
- border-right: 1px solid #b9b9b9
- .oe_kanban_record
- position: relative
- display: block
- min-height: 50px
- margin: 6px 0
- display: block
- @include radius(4px)
- &:last-child
- margin-bottom: 0
- .oe_kanban_title
- font-weight: bold
- margin: 2px 4px
- .oe_kanban_gravatar
- width: 20px
- height: 20px
- @include radius(3px)
- @include box-shadow(0 1px 2px rgba(0,0,0,0.2))
- .oe_kanban_avatar_toto
- height: 40px
- width: 40px
- border: 1px solid
- border-color: #e5e5e5 #dbdbdb #d2d2d2
- @include radius(3px)
- @include box-shadow(0 1px 2px rgba(0,0,0,0.2))
- // }}}
- // 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
@@ -1758,7 +2263,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
@@ -1777,16 +2281,135 @@ $colour4: #8a89ba
color: #333
// }}}
-// Transitional overrides for old styles {{{
.openerp
- .oe_form_field_many2one
- td:first-child
+ .oe_fileupload
+ display: inline-block
+ clear: both
+ width: 100%
+ .oe_add
+ float: left
position: relative
- span.oe-m2o-drop-down-button
- position: absolute
- top: 3px
- right: 2px
+ width: 100%
+ left: +2px
+ top: +7px
+ button
+ display: inline
+ height: 24px
+ font-size: 12px
+ line-height: 12px
+ vertical-align: middle
+ button.oe_attach
+ width: 24px
+ overflow: hidden
+ width: 24px
+ overflow: hidden
+ background: transparent
+ color: #7C7BAD
+ box-shadow: none
+ border: none
+ text-shadow: none
+ .oe_e
+ position: relative
+ top: -1px
+ left: -9px
+ input.oe_form_binary_file
+ display: inline-block
+ margin-left: -5px
+ height: 28px
+ width: 52px
+ margin-top: -26px
+ .oe_attach_label
+ color: #7C7BAD
+ margin-left: -3px
+ .oe_attachments
+ margin-bottom: 4px
+ margin-right: 0px
+ font-size: 12px
+ border-radius: 2px
+ border: solid 1px rgba(124,123,173,0.14)
+ .oe_attachment
+ padding: 2px
+ padding-left: 4px
+ padding-right: 4px
+ .oe_e
+ font-size: 23px
+ margin-top: -5px
+ .oe_e:hover
+ text-decoration: none
+ .oe_attachment:nth-child(odd)
+ background: white
+ .oe_attachment:nth-child(even)
+ background: #F4F5FA
+
+// Kitten Mode {{{
+.kitten-mode-activated
+ background-image: url(http://placekitten.com/g/1365/769)
+ background-size: cover
+ background-attachment: fixed
+ >*
+ opacity: 0.70
// }}}
+// 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)
+
+@media print
+ .openerp
+ .oe_header_row, ul.oe_header, div.oe_mail_thread_action, .oe_mail_recthread_actions, .oe_button_box, .oe_form button, button.oe_invite, .oe_form header, .openerp .oe_notebook > li.ui-state-default
+ display: none
+ .oe_list_content
+ button, input[type=checkbox]
+ visibility: hidden
+ .tree_header button, .oe_mail .oe_mail_thread_msg .oe_mail_unread, .oe_mail_fetch_more, .oe_m2o_drop_down_button img, .oe_form_field_one2many_list_row_add
+ visibility: hidden
+ a
+ &.oe_m2o_cm_button, &.oe_e
+ visibility: hidden
+ .oe_form
+ .oe_form_field_date img, .oe_form_field_datetime img
+ visibility: hidden
+ .oe_notebook > li.ui-tabs-selected
+ display: block
+ .oe_application
+ .oe_form_sheet, .oe_form_sheetbg
+ border: 0px !important
+ box-shadow: 0px 0px 0px
+ .oe_view_manager_current > .oe_view_manager_header
+ border: 0px !important
+ box-shadow: 0px 0px 0px
+ text-shadow: none
+ .text-core .text-wrap .text-arrow
+ background: none
+ .openerp div.oe_mail_wall
+ overflow: hidden !important
// 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/search_reset.gif b/addons/web/static/src/img/search_reset.gif
new file mode 100644
index 00000000000..9d4253e018b
--- /dev/null
+++ b/addons/web/static/src/img/search_reset.gif
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 47983ed322d..08ef8c4f9bf 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,22 +57,22 @@ 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(); },
+ beforeClose: function () {
+ self.trigger("closing");
+ },
resizeStop: this.on_resized
};
for (var f in this) {
@@ -82,13 +81,12 @@ 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());
- }
+ this.on("closing", this, this._closing);
},
get_options: function(options) {
var self = this,
@@ -120,38 +118,72 @@ 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');
+ this.$buttons.appendTo($("body"));
+ instance.web.dialog(this.$el, o).dialog('open');
+ this.$el.dialog("widget").find(".ui-dialog-buttonpane").remove();
+ 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);
+ } else {
+ this.$buttons = this.$el.dialog("widget").find(".ui-dialog-buttonpane");
+ }
+ this.dialog_inited = true;
+ var res = this.start();
+ return res;
+ },
close: function() {
- this.$element.dialog('close');
+ this.$el.dialog('close');
},
- on_close: function() {
+ _closing: 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();
}
});
instance.web.CrashManager = instance.web.CallbackEnabled.extend({
- on_rpc_error: function(error) {
+ rpc_error: function(error) {
if (error.data.fault_code) {
var split = ("" + error.data.fault_code).split('\n')[0].split(' -- ');
if (split.length > 1) {
@@ -160,12 +192,12 @@ instance.web.CrashManager = instance.web.CallbackEnabled.extend({
}
}
if (error.code === 200 && error.type) {
- this.on_managed_error(error);
+ this.show_warning(error);
} else {
- this.on_traceback(error);
+ this.show_error(error);
}
},
- on_managed_error: function(error) {
+ show_warning: function(error) {
instance.web.dialog($('<div>' + QWeb.render('CrashManager.warning', {error: error}) + '</div>'), {
title: "OpenERP " + _.str.capitalize(error.type),
buttons: [
@@ -173,32 +205,12 @@ instance.web.CrashManager = instance.web.CallbackEnabled.extend({
]
});
},
- on_traceback: function(error) {
+ show_error: 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 +219,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}));
+ },
+ show_message: function(exception) {
+ this.show_error({
+ type: _t("Client Error"),
+ message: exception,
+ data: {debug: ""}
+ });
+ },
});
instance.web.Loading = instance.web.Widget.extend({
@@ -217,47 +236,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("response_failed", 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 +286,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 +309,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 +340,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 +363,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
@@ -363,12 +394,22 @@ instance.web.DatabaseManager = instance.web.Widget.extend({
do_create: function(form) {
var self = this;
var fields = $(form).serializeArray();
- self.rpc("/web/database/create", {'fields': fields}, function(result) {
+ self.rpc("/web/database/create", {'fields': fields}).then(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;
@@ -379,7 +420,7 @@ instance.web.DatabaseManager = instance.web.Widget.extend({
if (!db || !confirm("Do you really want to delete the database: " + db + " ?")) {
return;
}
- self.rpc("/web/database/drop", {'fields': fields}, function(result) {
+ self.rpc("/web/database/drop", {'fields': fields}).then(function(result) {
if (result.error) {
self.display_error(result);
return;
@@ -396,7 +437,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();
}
@@ -435,26 +483,38 @@ instance.web.DatabaseManager = instance.web.Widget.extend({
var self = this;
self.rpc("/web/database/change_password", {
'fields': $(form).serializeArray()
- }, function(result) {
+ }).then(function(result) {
if (result.error) {
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,
- init: function(parent) {
+
+ init: function(parent, action) {
this._super(parent);
this.has_local_storage = typeof(localStorage) != 'undefined';
+ this.db_list = null;
this.selected_db = null;
this.selected_login = null;
+ this.params = action.params || {};
+
+ if (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');
@@ -466,48 +526,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();
- });
+ self.$el.find("form").submit(self.on_submit);
+ self.$el.find('.oe_login_manage_db').click(function() {
+ self.do_action("database_manager");
});
- self.load_db_list();
- },
- load_db_list: function () {
- var self = this;
- self.rpc("/web/database/get_list", {}, function(result) {
- self.set_db_list(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;
+ },
+ 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 {
+ this.$('div.oe_login_dbpane').show();
+ }
},
- set_db_list: function (list) {
- this.$element.find("[name=db]").replaceWith(instance.web.qweb.render('Login.dblist', { db_list: list, selected_db: this.selected_db}));
+ 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);
},
@@ -520,15 +579,9 @@ instance.web.Login = instance.web.Widget.extend({
*/
do_login: function (db, login, password) {
var self = this;
- this.$element.removeClass('oe_login_invalid');
- this.session.on_session_invalid.add({
- callback: function () {
- self.$element.addClass("oe_login_invalid");
- },
- unique: true
- });
- this.session.session_authenticate(db, login, password).then(function() {
- self.$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) {
localStorage.setItem('last_db_login_success', db);
@@ -542,9 +595,97 @@ instance.web.Login = instance.web.Widget.extend({
localStorage.setItem('last_password_login_success', '');
}
}
+ 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 = function(parent, action) {
+ var params = action.params || {};
+ var menu_id = params.menu_id || false;
+ 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 (menu_id) {
+ hash = "#menu_id=" + menu_id;
+ }
+ var url = l.protocol + "//" + l.host + l.pathname + search + hash;
+ window.onerror = function() {};
+ 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 = function(parent) {
+ 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) {
+ 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()
+ }).then(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',
@@ -556,29 +697,34 @@ 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() {
- return this.rpc("/web/menu/load", {}).then(this.on_loaded);
+ var self = this;
+ return this.rpc("/web/menu/load", {}).then(function(r) {
+ self.menu_loaded(r);
+ });
},
- on_loaded: function(data) {
+ menu_loaded: function(data) {
var self = this;
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) {
self.open_menu(self.current_menu);
}
+ this.trigger('menu_loaded', data);
this.has_been_loaded.resolve();
},
limit_entries: function() {
@@ -588,20 +734,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
@@ -609,20 +752,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
@@ -649,7 +794,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);
@@ -659,34 +804,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
@@ -694,9 +842,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);
},
});
@@ -709,118 +857,46 @@ 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);
});
};
this.update_promise = this.update_promise.pipe(fct, fct);
},
- on_action: function() {
- },
on_menu_logout: function() {
+ this.trigger('user_logout');
},
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.res_id = instance.session.uid;
+ self.getParent().action_manager.do_action(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;
@@ -828,105 +904,160 @@ instance.web.UserMenu = instance.web.Widget.extend({
var $help = $(QWeb.render("UserMenu.about", {version_info: res}));
$help.find('a.oe_activate_debug_mode').click(function (e) {
e.preventDefault();
- window.location = $.param.querystring(
- window.location.href, 'debug');
+ window.location = $.param.querystring( 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('error', this.crashmanager, this.crashmanager.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) {
- this.$element.addClass("kitten-mode-activated");
- this.$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() {
+ 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.session.on_session_valid.add(function() {
- self.show_application();
-
- if(self.action_manager)
- self.action_manager.destroy();
- self.action_manager = new instance.web.ActionManager(self);
- self.action_manager.appendTo(self.$element.find('.oe_application'));
- 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;
- }
- });
- this.$element.on('mouseenter', '.oe_systray > div:not([data-tipsy=true])', function() {
- $(this).attr('data-tipsy', 'true').tipsy().trigger('mouseenter');
- });
},
- show_login: function() {
+ set_title: function(title) {
+ title = _.str.clean(title);
+ var sep = _.isEmpty(title) ? '' : ' - ';
+ document.title = title + sep + 'OpenERP';
+ },
+ show_common: function() {
var self = this;
- this.destroy_content();
- this.show_common();
- self.login = new instance.web.Login(self);
- self.login.appendTo(self.$element);
+ this._super();
+ window.onerror = function (message, file, line) {
+ self.crashmanager.show_error({
+ type: _t("Client Error"),
+ message: message,
+ data: {debug: file + ':' + line}
+ });
+ };
+ },
+ show_login: function() {
+ this.toggle_bars(false);
+
+ var state = $.bbq.getState(true);
+ var action = {
+ type: 'ir.actions.client',
+ tag: 'login',
+ _push_me: false,
+ };
+
+ this.action_manager.do_action(action);
+ this.action_manager.inner_widget.on('login_successful', this, function() {
+ this.show_application(); // will load the state we just pushed
+ });
},
show_application: function() {
var self = this;
- this.destroy_content();
- this.show_common();
- self.$table = $(QWeb.render("WebClient", {}));
- self.$element.append(self.$table);
+ 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.on_menu_logout.add(this.proxy('on_logout'));
- self.user_menu.on_action.add(this.proxy('on_menu_action'));
+ self.user_menu.replace(this.$el.find('.oe_user_menu_placeholder'));
+ self.user_menu.on('user_logout', self, self.on_logout);
self.user_menu.do_update();
- },
- show_common: function() {
- var self = this;
- if (!this.crashmanager) {
- this.crashmanager = new instance.web.CrashManager();
- instance.connection.on_rpc_error.add(this.crashmanager.on_rpc_error);
- window.onerror = function (message, file, line) {
- self.crashmanager.on_traceback({
- type: _t("Client Error"),
- message: message,
- data: {debug: file + ':' + line}
- });
- };
- }
- this.notification = new instance.web.Notification(this);
- this.notification.appendTo(this.$element);
- this.loading = new instance.web.Loading(this);
- this.loading.appendTo(this.$element);
+ self.bind_hashchange();
+ 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')); });
},
@@ -940,28 +1071,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) {
@@ -975,61 +1106,85 @@ instance.web.WebClient = instance.web.Widget.extend({
});
});
} else {
+ state._push_me = false; // no need to push state back...
this.action_manager.do_load_state(state, !!this._current_state);
}
}
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);
- },
- do_action: function(action) {
+ on_menu_action: function(options) {
var self = this;
- // TODO replace by client action menuclick
- if(action.menu_id) {
- this.do_reload().then(function () {
- self.menu.menu_click(action.menu_id);
+ return this.rpc("/web/action/load", { action_id: options.action_id })
+ .pipe(function (result) {
+ var action = result;
+ if (options.needaction) {
+ action.context.search_default_message_unread = true;
+ }
+ return $.when(self.action_manager.do_action(action, {
+ clear_breadcrumbs: true,
+ action_menu_id: self.menu.current_menu,
+ })).fail(function() {
+ self.menu.open_menu(options.previous_menu_id);
+ });
});
- }
},
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 }).then(function(result) {
+ var action = 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) {
@@ -1043,13 +1198,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 93796ab4282..6ecbfdd01ac 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() {
@@ -122,6 +121,10 @@ openerp.web.corelib = function(instance) {
// The dummy class constructor
function Class() {
+ if(this.constructor !== instance.web.Class){
+ throw new Error("You can only instanciate objects with the 'new' operator");
+ return null;
+ }
// All construction is actually done in the init method
if (!initializing && this.init) {
var ret = this.init.apply(this, arguments);
@@ -288,6 +291,17 @@ var Events = instance.web.Class.extend({
return this;
},
+ callbackList: function() {
+ var lst = [];
+ _.each(this._callbacks || {}, function(el, eventName) {
+ var node = el;
+ while ((node = node.next) && node.next) {
+ lst.push([eventName, node.callback, node.context]);
+ }
+ });
+ return lst;
+ },
+
trigger : function(events) {
var event, node, calls, tail, args, all, rest;
if (!(calls = this._callbacks))
@@ -331,6 +345,9 @@ instance.web.EventDispatcherMixin = _.extend({}, instance.web.ParentedMixin, {
},
on: function(events, dest, func) {
var self = this;
+ if (!(func instanceof Function)) {
+ throw new Error("Event handler must be a function.");
+ }
events = events.split(/\s+/);
_.each(events, function(eventName) {
self.__edispatcherEvents.on(eventName, func, dest);
@@ -363,9 +380,9 @@ instance.web.EventDispatcherMixin = _.extend({}, instance.web.ParentedMixin, {
event.source.__edispatcherEvents.off(event.name, event.func, self);
});
this.__edispatcherRegisteredEvents = [];
- if(!this.__edispatcherEvents) {
- debugger;
- }
+ _.each(this.__edispatcherEvents.callbackList(), function(cal) {
+ this.off(cal[0], cal[2], cal[1]);
+ }, this);
this.__edispatcherEvents.off();
instance.web.ParentedMixin.destroy.call(this);
}
@@ -376,7 +393,17 @@ instance.web.PropertiesMixin = _.extend({}, instance.web.EventDispatcherMixin, {
instance.web.EventDispatcherMixin.init.call(this);
this.__getterSetterInternalMap = {};
},
- set: function(map) {
+ set: function(arg1, arg2, arg3) {
+ var map;
+ var options;
+ if (typeof arg1 === "string") {
+ map = {};
+ map[arg1] = arg2;
+ options = arg3 || {};
+ } else {
+ map = arg1;
+ options = arg2 || {};
+ }
var self = this;
var changed = false;
_.each(map, function(val, key) {
@@ -385,10 +412,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);
@@ -402,71 +430,16 @@ instance.web.CallbackEnabledMixin = _.extend({}, instance.web.PropertiesMixin, {
init: function() {
instance.web.PropertiesMixin.init.call(this);
var self = this;
- var callback_maker = function(obj, name, method) {
- var callback = function() {
- var args = Array.prototype.slice.call(arguments);
- self.trigger.apply(self, [name].concat(args));
- var r;
- for(var i = 0; i < callback.callback_chain.length; i++) {
- var c = callback.callback_chain[i];
- if(c.unique) {
- callback.callback_chain.splice(i, 1);
- i -= 1;
- }
- var result = c.callback.apply(c.self, c.args.concat(args));
- if (c.callback === method) {
- // return the result of the original method
- r = result;
- }
- // TODO special value to stop the chain
- // instance.web.callback_stop
- }
- return r;
- };
- callback.callback_chain = [];
- callback.add = function(f) {
- if(typeof(f) == 'function') {
- f = { callback: f, args: Array.prototype.slice.call(arguments, 1) };
- }
- f.self = f.self || null;
- f.args = f.args || [];
- f.unique = !!f.unique;
- if(f.position == 'last') {
- callback.callback_chain.push(f);
- } else {
- callback.callback_chain.unshift(f);
- }
- return callback;
- };
- callback.add_first = function(f) {
- return callback.add.apply(null,arguments);
- };
- callback.add_last = function(f) {
- return callback.add({
- callback: f,
- args: Array.prototype.slice.call(arguments, 1),
- position: "last"
- });
- };
- callback.remove = function(f) {
- callback.callback_chain = _.difference(callback.callback_chain, _.filter(callback.callback_chain, function(el) {
- return el.callback === f;
- }));
- return callback;
- };
-
- return callback.add({
- callback: method,
- self:obj,
- args:Array.prototype.slice.call(arguments, 3)
- });
- };
// Transform on_/do_* methods into callbacks
+ var callback_maker = function(fn) {
+ return function() {
+ return fn.apply(self, arguments);
+ }
+ };
for (var name in this) {
if(typeof(this[name]) == "function") {
- this[name].debug_name = name;
if((/^on_|^do_/).test(name)) {
- this[name] = callback_maker(this, name, this[name]);
+ this[name] = callback_maker(this[name]);
}
}
}
@@ -487,24 +460,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
@@ -513,14 +482,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);
},
/**
@@ -530,8 +494,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);
},
@@ -543,7 +507,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);
},
/**
@@ -554,7 +518,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);
},
/**
@@ -565,7 +529,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);
},
/**
@@ -576,7 +540,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);
},
/**
@@ -586,7 +550,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) {
@@ -609,6 +573,7 @@ instance.web.WidgetMixin = _.extend({},instance.web.CallbackEnabledMixin, {
* @returns {jQuery.Deferred}
*/
start: function() {
+ return $.when();
}
});
@@ -644,8 +609,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()
@@ -669,6 +634,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.
@@ -685,14 +656,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,
@@ -700,29 +669,130 @@ 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);
+ },
+
+ /**
+ * 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
+ */
+ replaceElement: function ($el) {
+ var $oldel = this.$el;
+ this.setElement($el);
+ if ($oldel && !$oldel.is(this.$el)) {
+ $oldel.replaceWith(this.$el);
}
+ return this;
},
/**
- * Shortcut for $element.find() like backbone
+ * 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
*/
- "$": function() {
- return this.$element.find.apply(this.$element,arguments);
+ 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
* the action manager can be found amongst the ancestors of the current widget.
* If that's not the case this method will simply return `false`.
*/
- do_action: function(action, on_finished) {
- if (this.getParent()) {
- return this.getParent().do_action(action, on_finished);
+ do_action: function() {
+ var parent = this.getParent();
+ if (parent) {
+ return parent.do_action.apply(parent, arguments);
}
return false;
},
@@ -741,7 +811,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() {
@@ -763,7 +833,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
@@ -872,6 +942,12 @@ instance.web.Registry = instance.web.Class.extend({
});
instance.web.JsonRPC = instance.web.CallbackEnabled.extend({
+ triggers: {
+ 'request': 'Request sent',
+ 'response': 'Response received',
+ 'response_failed': 'HTTP Error response or timeout received',
+ 'error': 'The received response is an JSON-RPC error',
+ },
/**
* @constructs instance.web.JsonRPC
* @extends instance.web.CallbackEnabled
@@ -1035,7 +1111,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'])
};
},
/**
@@ -1193,7 +1270,7 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({
* @param {Function} error_callback function to execute on RPC call failure
* @returns {jQuery.Deferred} jquery-provided ajax deferred
*/
- rpc: function(url, params, success_callback, error_callback) {
+ rpc: function(url, params) {
var self = this;
// url can be an $.ajax option object
if (_.isString(url)) {
@@ -1209,12 +1286,10 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({
id: _.uniqueId('r')
};
var deferred = $.Deferred();
- this.on_rpc_request();
- var aborter = params.aborter;
- delete params.aborter;
+ this.trigger('request', url, payload);
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);
@@ -1222,18 +1297,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('response_failed', jqXHR);
var error = {
code: -32098,
message: "XmlHttpRequestError " + errorThrown,
@@ -1241,24 +1310,14 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({
};
deferred.reject(error, $.Event());
});
- if (aborter) {
- aborter.abort_last = function () {
- if (!(request.isResolved() || request.isRejected())) {
- deferred.fail(function (error, event) {
- event.preventDefault();
- });
- request.abort();
- }
- };
- }
// Allow deferred user to disable on_rpc_error in fail
deferred.fail(function() {
deferred.fail(function(error, event) {
if (!event.isDefaultPrevented()) {
- self.on_rpc_error(error, event);
+ self.trigger('error', error, event);
}
});
- }).then(success_callback, error_callback).promise();
+ });
return deferred;
},
/**
@@ -1276,7 +1335,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) {
@@ -1295,7 +1354,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) {
@@ -1305,19 +1364,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();
};
@@ -1341,14 +1400,15 @@ 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) {
+ return this.prefix + file;
},
});
+instance.web.py_eval = function(expr, context) {
+ return py.eval(expr, _.extend({}, context || {}, {"true": true, "false": false, "null": null}));
+};
+
}
// vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax:
diff --git a/addons/web/static/src/js/coresetup.js b/addons/web/static/src/js/coresetup.js
index 5a8a2b50722..220b9b63d5c 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() {
@@ -46,9 +19,9 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess
this.name = instance._session_id;
this.qweb_mutex = new $.Mutex();
},
- rpc: function(url, params, success_callback, error_callback) {
+ rpc: function(url, params) {
params.session_id = this.session_id;
- return this._super(url, params, success_callback, error_callback);
+ return this._super(url, params);
},
/**
* Setup a sessionm
@@ -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) {
@@ -85,7 +57,12 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess
if(self.session_is_valid()) {
return deferred.pipe(function() { return self.load_modules(); });
}
- return deferred;
+ return $.when(
+ deferred,
+ self.rpc('/web/webclient/bootstrap_translations', {mods: instance._modules}).pipe(function(trans) {
+ instance.web._t.database.set_bundle(trans);
+ })
+ );
});
},
/**
@@ -105,8 +82,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
});
});
},
@@ -121,13 +97,16 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess
var base_location = document.location.protocol + '//' + document.location.host;
var params = { db: db, login: login, password: password, base_location: base_location };
return this.rpc("/web/session/authenticate", params).pipe(function(result) {
+ if (!result.uid) {
+ return $.Deferred().reject();
+ }
+
_.extend(self, {
session_id: result.session_id,
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);
@@ -139,20 +118,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 + '=';
@@ -186,9 +151,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,
@@ -197,31 +161,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';
}
});
});
@@ -284,9 +248,6 @@ instance.web.Session = instance.web.JsonRPC.extend( /** @lends instance.web.Sess
}
}
},
- get_url: function (file) {
- return this.prefix + file;
- },
/**
* Cooperative file download implementation, for ajaxy APIs.
*
@@ -332,11 +293,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();
}
@@ -389,16 +353,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# */{
/**
@@ -453,64 +442,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 = {};
@@ -540,10 +471,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();
@@ -573,38 +500,176 @@ $.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,
+ 'JSON': JSON,
+};
+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 match = /^(\s*)(.+?)(\s*)$/.exec(this.node.data);
+ if (match) {
+ this.node.data = match[1] + instance.web._t(match[2]) + match[3];
+ }
+ 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...")],
+ [3600, _t("Maybe you should consider reloading the application by 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..63c90f53758 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) {
@@ -120,6 +120,10 @@ instance.web.Query = instance.web.Class.extend({
if (_.isEmpty(grouping)) { return null; }
var self = this;
+
+ // FIXME: when pyeval is merged
+ var ctx = instance.session.test_eval_contexts(
+ [this._model.context(this._context)]);
return this._model.call('read_group', {
groupby: grouping,
fields: _.uniq(grouping.concat(this._fields || [])),
@@ -130,7 +134,11 @@ 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(
+ // FIX: querygroup initialization
+ result.__context = result.__context || {};
+ result.__context.group_by = result.__context.group_by || [];
+ _.defaults(result.__context, ctx);
+ return new instance.web.QueryGroup(
self._model.name, grouping[0], result);
});
});
@@ -194,7 +202,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 +288,8 @@ instance.web.Model = instance.web.Class.extend(/** @lends openerp.web.Model# */{
kwargs = args;
args = [];
}
- return instance.connection.rpc('/web/dataset/call_kw', {
+ var debug = instance.session.debug ? '/'+this.name+':'+method : '';
+ return instance.session.rpc('/web/dataset/call_kw' + debug, {
model: this.name,
method: method,
args: args,
@@ -256,7 +312,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 +338,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 +348,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,235 +358,12 @@ instance.web.Model = instance.web.Class.extend(/** @lends openerp.web.Model# */{
},
});
-instance.web.Traverser = instance.web.Class.extend(/** @lends openerp.web.Traverser# */{
- /**
- * @constructs instance.web.Traverser
- * @extends instance.web.Class
- *
- * @param {instance.web.Model} model instance this traverser is bound to
- */
- init: function (model) {
- this._model = model;
- this._index = 0;
- },
-
- /**
- * Gets and sets the current index
- *
- * @param {Number} [idx]
- * @returns {Number} current index
- */
- index: function (idx) {
- if (idx) { this._index = idx; }
- return this._index;
- },
- /**
- * Returns the model this traverser is currently bound to
- *
- * @returns {openerp.web.Model}
- */
- model: function () {
- return this._model;
- },
- /**
- * Fetches the size of the backing model's match
- *
- * @returns {Deferred<Number>} deferred count
- */
- size: function () {
- return this._model.query().count();
- },
-
- /**
- * Record at the current index for the collection, fails if there is no
- * record at the current index.
- *
- * @returns {Deferred<>}
- */
- current: function (fields) {
- return this._model.query(fields).first().pipe(function (record) {
- if (record == null) {
- return $.Deferred()
- .reject('No record at index' + this._index)
- .promise();
- }
- return record;
- });
- },
- next: function (fields) {
- var self = this;
- this._index++;
- return this.size().pipe(function (s) {
- if (self._index >= s) {
- self._index = 0;
- }
- return self.current(fields);
- });
- },
- previous: function (fields) {
- var self = this;
- this._index--;
- if (this._index < 0) {
- return this.size().pipe(function (s) {
- self._index = s-1;
- return self.current(fields);
- });
- }
- return this.current(fields);
- }
-
-});
-
-/**
- * Utility objects, should never need to be instantiated from outside of this
- * module
- *
- * @namespace
- */
-instance.web.data = {
- Group: instance.web.Class.extend(/** @lends openerp.web.data.Group# */{
- /**
- * @constructs instance.web.data.Group
- * @extends instance.web.Class
- */
- init: function (model, grouping_field, read_group_group) {
- // In cases where group_by_no_leaf and no group_by, the result of
- // read_group has aggregate fields but no __context or __domain.
- // Create default (empty) values for those so that things don't break
- var fixed_group = _.extend(
- {__context: {group_by: []}, __domain: []},
- read_group_group);
-
- var aggregates = {};
- _(fixed_group).each(function (value, key) {
- if (key.indexOf('__') === 0
- || key === grouping_field
- || key === grouping_field + '_count') {
- return;
- }
- aggregates[key] = value || 0;
- });
-
- this.model = new instance.web.Model(
- model, fixed_group.__context, fixed_group.__domain);
-
- var group_size = fixed_group[grouping_field + '_count'] || fixed_group.__count || 0;
- var leaf_group = fixed_group.__context.group_by.length === 0;
- this.attributes = {
- 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# */{
- /**
- * Management interface between views and grouped collections of OpenERP
- * records.
- *
- * The root DataGroup is instantiated with the relevant information
- * (a session, a model, a domain, a context and a group_by sequence), the
- * domain and context may be empty. It is then interacted with via
- * :js:func:`~instance.web.DataGroup.list`, which is used to read the
- * content of the current grouping level.
- *
- * @constructs instance.web.DataGroup
- * @extends instance.web.OldWidget
- *
- * @param {instance.web.OldWidget} 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
- * @param {Array} group_by sequence of fields by which to group
- * @param {Number} [level=0] nesting level of the group
- */
- init: function(parent, model, domain, context, group_by, level) {
- this._super(parent, null);
- this.model = new instance.web.Model(model, context, domain);
- this.group_by = group_by;
- this.context = context;
- this.domain = domain;
-
- this.level = level || 0;
- },
- 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}));
- return;
- }
- ifGroups(_(groups).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});
- }));
- });
- }
-});
-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# */ {
- /**
- * A specialization of groupless data groups, relying on a single static
- * dataset as its records provider.
- *
- * @constructs instance.web.StaticDataGroup
- * @extends instance.web.GrouplessDataGroup
- * @param {openep.web.DataSetStatic} dataset a static dataset backing the groups
- */
- init: function (dataset) {
- this.dataset = dataset;
- },
- list: function (fields, ifGroups, ifRecords) {
- ifRecords(this.dataset);
- }
-});
-
-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?)
+ * Collection of OpenERP records, used to share records and the current selection between views.
*
* @constructs instance.web.DataSet
- * @extends instance.web.OldWidget
+ * @extends instance.web.CallbackEnabled
*
* @param {String} model the OpenERP model this dataset will manage
*/
@@ -638,21 +471,16 @@ instance.web.DataSet = instance.web.OldWidget.extend( /** @lends openerp.web.Da
default_get: function(fields, options) {
options = options || {};
return this._model.call('default_get',
- [fields], {context: this._model.context(options.context)});
+ [fields], {context: this.get_context(options.context)});
},
/**
* Creates a new record in db
*
* @param {Object} data field values to set on the new record
- * @param {Function} callback function called with operation result
- * @param {Function} error_callback function called in case of creation error
* @returns {$.Deferred}
*/
- create: function(data, callback, error_callback) {
- return this._model.call('create',
- [data], {context: this._model.context()})
- .pipe(function (r) { return {result: r}; })
- .then(callback, error_callback);
+ create: function(data) {
+ return this._model.call('create', [data], {context: this.get_context()});
},
/**
* Saves the provided data in an existing db record
@@ -663,24 +491,17 @@ instance.web.DataSet = instance.web.OldWidget.extend( /** @lends openerp.web.Da
* @param {Function} error_callback function called in case of write error
* @returns {$.Deferred}
*/
- write: function (id, data, options, callback, error_callback) {
+ write: function (id, data, options) {
options = options || {};
- return this._model.call('write',
- [[id], data], {context: this._model.context(options.context)})
- .pipe(function (r) { return {result: r}})
- .then(callback, error_callback);
+ return this._model.call('write', [[id], data], {context: this.get_context(options.context)}).then(this.trigger('dataset_changed', id, data, options));
},
/**
* Deletes an existing record from the database
*
* @param {Number|String} ids identifier of the record to delete
- * @param {Function} callback function called with operation result
- * @param {Function} error_callback function called in case of deletion error
*/
- unlink: function(ids, callback, error_callback) {
- return this._model.call('unlink',
- [ids], {context: this._model.context()})
- .then(callback, error_callback);
+ unlink: function(ids) {
+ return this._model.call('unlink', [ids], {context: this.get_context()}).then(this.trigger('dataset_changed', ids));
},
/**
* Calls an arbitrary RPC method
@@ -691,53 +512,27 @@ instance.web.DataSet = instance.web.OldWidget.extend( /** @lends openerp.web.Da
* @param {Function} error_callback
* @returns {$.Deferred}
*/
- call: function (method, args, callback, error_callback) {
- return this._model.call(method, args).then(callback, error_callback);
- },
- /**
- * Calls an arbitrary method, with more crazy
- *
- * @param {String} method
- * @param {Array} [args]
- * @param {Number} [domain_index] index of a domain to evaluate in the args array
- * @param {Number} [context_index] index of a context to evaluate in the args array
- * @param {Function} callback
- * @param {Function} error_callback
- * @returns {$.Deferred}
- */
- call_and_eval: function (method, args, domain_index, context_index, callback, error_callback) {
- return this.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);
+ call: function (method, args) {
+ return this._model.call(method, args);
},
/**
* Calls a button method, usually returning some sort of action
*
* @param {String} method
* @param {Array} [args]
- * @param {Function} callback
- * @param {Function} error_callback
* @returns {$.Deferred}
*/
- call_button: function (method, args, callback, error_callback) {
- return this._model.call_button(method, args)
- .then(callback, error_callback);
+ call_button: function (method, args) {
+ return this._model.call_button(method, args);
},
/**
* Fetches the "readable name" for records, based on intrinsic rules
*
* @param {Array} ids
- * @param {Function} callback
* @returns {$.Deferred}
*/
- name_get: function(ids, callback) {
- return this._model.call('name_get',
- [ids], {context: this._model.context()})
- .then(callback);
+ name_get: function(ids) {
+ return this._model.call('name_get', [ids], {context: this.get_context()});
},
/**
*
@@ -748,28 +543,23 @@ 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.get_context()});
},
- exec_workflow: function (id, signal, callback) {
- return this._model.exec_workflow(id, signal)
- .pipe(function (result) { return { result: result }; })
- .then(callback);
+ exec_workflow: function (id, signal) {
+ return this._model.exec_workflow(id, signal);
},
get_context: function(request_context) {
return this._model.context(request_context);
@@ -803,11 +593,29 @@ 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.get_context(options.context),
+ }).pipe(function (results) {
+ return results;
+ });
},
});
+
instance.web.DataSetStatic = instance.web.DataSet.extend({
init: function(parent, model, context, ids) {
+ var self = this;
this._super(parent, model, context);
// all local records
this.ids = ids || [];
@@ -829,14 +637,13 @@ instance.web.DataSetStatic = instance.web.DataSet.extend({
}
},
unlink: function(ids) {
- this.on_unlink(ids);
+ this.set_ids(_.without.apply(null, [this.ids].concat(ids)));
+ this.trigger('unlink', ids);
return $.Deferred().resolve({result: true});
},
- on_unlink: function(ids) {
- 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,18 +693,17 @@ 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;
}
- if (this.index !== null) {
+ if (self.index !== null) {
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);
+ self.trigger("dataset_changed", ids, callback, error_callback);
+ });
},
size: function () {
if (this._length !== undefined) {
@@ -906,6 +712,7 @@ instance.web.DataSetSearch = instance.web.DataSet.extend(/** @lends openerp.web
return this._super();
}
});
+
instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
virtual_id_prefix: "one2many_v_id_",
debug_mode: true,
@@ -920,16 +727,14 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
on_default_get: function(res) {
this.last_default_get = res;
},
- create: function(data, callback, error_callback) {
+ create: function(data) {
var cached = {id:_.uniqueId(this.virtual_id_prefix), values: data,
defaults: this.last_default_get};
this.to_create.push(_.extend(_.clone(cached), {values: _.clone(cached.values)}));
this.cache.push(cached);
- var prom = $.Deferred().then(callback);
- prom.resolve({result: cached.id});
- return prom.promise();
+ return $.Deferred().resolve(cached.id).promise();
},
- write: function (id, data, options, callback) {
+ write: function (id, data, options) {
var self = this;
var record = _.detect(this.to_create, function(x) {return x.id === id;});
record = record || _.detect(this.to_write, function(x) {return x.id === id;});
@@ -954,10 +759,8 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
}
$.extend(cached.values, record.values);
if (dirty)
- this.on_change();
- var to_return = $.Deferred().then(callback);
- to_return.resolve({result: true});
- return to_return.promise();
+ this.trigger("dataset_changed", id, data, options);
+ return $.Deferred().resolve(true).promise();
},
unlink: function(ids, callback, error_callback) {
var self = this;
@@ -970,7 +773,7 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
this.to_write = _.reject(this.to_write, function(x) { return _.include(ids, x.id);});
this.cache = _.reject(this.cache, function(x) { return _.include(ids, x.id);});
this.set_ids(_.without.apply(_, [this.ids].concat(ids)));
- this.on_change();
+ this.trigger("dataset_changed", ids, callback, error_callback);
return $.async_when({result: true}).then(callback);
},
reset_ids: function(ids) {
@@ -981,7 +784,6 @@ instance.web.BufferedDataSet = instance.web.DataSetStatic.extend({
this.cache = [];
this.delete_all = false;
},
- on_change: function() {},
read_ids: function (ids, fields, options) {
var self = this;
var to_get = [];
@@ -1042,7 +844,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 +855,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,11 +866,11 @@ 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.on_change();
+ this._super(n_ids);
+ this.trigger("dataset_changed", n_ids);
},
});
instance.web.BufferedDataSet.virtual_id_regex = /^one2many_v_id_.*$/;
@@ -1096,23 +898,23 @@ instance.web.ProxyDataSet = instance.web.DataSetSearch.extend({
return this._super.apply(this, arguments);
}
},
- create: function(data, callback, error_callback) {
+ create: function(data) {
if (this.create_function) {
- return this.create_function(data, this._super).then(callback, error_callback);
+ return this.create_function(data, this._super);
} else {
return this._super.apply(this, arguments);
}
},
- write: function (id, data, options, callback, error_callback) {
+ write: function (id, data, options) {
if (this.write_function) {
- return this.write_function(id, data, options, this._super).then(callback, error_callback);
+ return this.write_function(id, data, options, this._super);
} else {
return this._super.apply(this, arguments);
}
},
- unlink: function(ids, callback, error_callback) {
+ unlink: function(ids) {
if (this.unlink_function) {
- return this.unlink_function(ids, this._super).then(callback, error_callback);
+ return this.unlink_function(ids, this._super);
} else {
return this._super.apply(this, arguments);
}
@@ -1165,7 +967,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..b4beb4c3a58 100644
--- a/addons/web/static/src/js/data_export.js
+++ b/addons/web/static/src/js/data_export.js
@@ -5,7 +5,15 @@ instance.web.DataExport = instance.web.Dialog.extend({
template: 'ExportTreeView',
dialog_title: {toString: function () { return _t("Export Data"); }},
init: function(parent, dataset) {
- this._super(parent);
+ var self = this;
+ options = {
+ buttons : [
+ {text: _t("Close"), click: function() { self.close(); }},
+ {text: _t("Export To File"), click: function() { self.on_click_export_data(); }}
+ ],
+ close: function(event, ui){ self.close();}
+ }
+ this._super(parent, options);
this.records = {};
this.dataset = dataset;
this.exports = new instance.web.DataSetSearch(
@@ -14,42 +22,36 @@ instance.web.DataExport = instance.web.Dialog.extend({
start: function() {
var self = this;
this._super.apply(this, arguments);
- this.open({
- buttons : [
- {text: _t("Close"), click: function() { self.close(); }},
- {text: _t("Export To File"), click: function() { self.on_click_export_data(); }}
- ],
- close: function(event, ui){ self.close();}
- });
- self.$element.removeClass('ui-dialog-content ui-widget-content');
- self.$element.find('#add_field').click(function() {
+ this.open();
+ 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)
- }, function (records) {
+ }).then(function (records) {
got_fields.resolve();
self.on_show_data(records);
});
@@ -57,11 +59,11 @@ instance.web.DataExport = instance.web.Dialog.extend({
return $.when(
got_fields,
- this.rpc('/web/export/formats', {}, this.do_setup_export_formats),
+ this.rpc('/web/export/formats', {}).then(this.do_setup_export_formats),
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 +78,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 +88,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.rpc('/web/export/namelist', {'model': self.dataset.model, export_id: parseInt(export_id)}).then(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 +152,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 +175,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,
@@ -181,7 +183,7 @@ instance.web.DataExport = instance.web.Dialog.extend({
import_compat: Boolean(import_comp),
parent_field_type : record['field_type'],
exclude: exclude_fields
- }, function(results) {
+ }).then(function(results) {
record.loaded = true;
self.on_show_data(results, record.id);
});
@@ -191,44 +193,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 +240,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 +254,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 +299,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 +321,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 +331,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 +347,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 +365,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 +377,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 +387,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 2b527686ddd..03b2c5b775d 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);
@@ -133,7 +135,13 @@ instance.web.format_value = function (value, descriptor, value_if_empty) {
Math.round((value % 1) * 60));
case 'many2one':
// name_get value format
- return value[1];
+ return value[1] ? value[1].split("\n")[0] : 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")
value = instance.web.auto_str_to_date(value);
@@ -266,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 689f315fa63..8da5eb0ca55 100644
--- a/addons/web/static/src/js/search.js
+++ b/addons/web/static/src/js/search.js
@@ -61,7 +61,7 @@ my.SearchQuery = B.Collection.extend({
this.on('change', function (facet) {
if(!facet.values.isEmpty()) { return; }
- this.remove(facet);
+ this.remove(facet, {silent: true});
}, this);
},
add: function (values, options) {
@@ -124,23 +124,14 @@ function assert(condition, message) {
}
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'));
- return p;
- },
- onFocus: function () {
- this.trigger('focused', this);
- },
- onBlur: function () {
- this.$element.text('');
- this.trigger('blurred', this);
+ events: {
+ focus: function () { this.trigger('focused', this); },
+ blur: function () { this.$el.text(''); this.trigger('blurred', this); },
+ keydown: 'onKeydown'
},
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 +193,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();
}
@@ -212,6 +203,27 @@ my.InputView = instance.web.Widget.extend({
});
my.FacetView = instance.web.Widget.extend({
template: 'SearchView.FacetView',
+ events: {
+ 'focus': function () { this.trigger('focused', this); },
+ 'blur': function () { this.trigger('blurred', this); },
+ 'click': function (e) {
+ if ($(e.target).is('.oe_facet_remove')) {
+ this.model.destroy();
+ return false;
+ }
+ this.$el.focus();
+ e.stopPropagation();
+ },
+ 'keydown': function (e) {
+ var keys = $.ui.keyCode;
+ switch (e.which) {
+ case keys.BACKSPACE:
+ case keys.DELETE:
+ this.model.destroy();
+ return false;
+ }
+ }
+ },
init: function (parent, model) {
this._super(parent);
this.model = model;
@@ -223,37 +235,15 @@ 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) {
- if ($(e.target).is('.oe_facet_remove')) {
- self.model.destroy();
- return false;
- }
- self.$element.focus();
- e.stopPropagation();
- });
- this.$element.on('keydown', function (e) {
- var keys = $.ui.keyCode;
- switch (e.which) {
- case keys.BACKSPACE:
- case keys.DELETE:
- self.model.destroy();
- return false;
- }
- });
- var $e = self.$element.find('> span:last-child');
- var q = $.when(this._super());
- return q.pipe(function () {
- var values = self.model.values.map(function (value) {
+ var $e = this.$('> span:last-child');
+ return $.when(this._super()).pipe(function () {
+ return $.when.apply(null, self.model.values.map(function (value) {
return new my.FacetValueView(self, value).appendTo($e);
- });
-
- return $.when.apply(null, values);
+ }));
});
},
model_changed: function () {
- this.$element.text(this.$element.text() + '*');
+ this.$el.text(this.$el.text() + '*');
}
});
my.FacetValueView = instance.web.Widget.extend({
@@ -268,12 +258,54 @@ my.FacetValueView = instance.web.Widget.extend({
this._super();
},
model_changed: function () {
- this.$element.text(this.$element.text() + '*');
+ this.$el.text(this.$el.text() + '*');
}
});
instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.SearchView# */{
template: "SearchView",
+ events: {
+ // focus last input if view itself is clicked
+ 'click': function (e) {
+ if (e.target === this.$('.oe_searchview_facets')[0]) {
+ this.$('.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
+ 'autocompleteopen': function () {
+ var menu = this.$el.data('autocomplete').menu;
+ menu.activate(
+ $.Event({ type: "mouseenter" }),
+ menu.element.children().first());
+ },
+ // search button
+ 'click button.oe_searchview_search': function (e) {
+ e.stopImmediatePropagation();
+ this.do_search();
+ },
+ 'click .oe_searchview_clear': function (e) {
+ e.stopImmediatePropagation();
+ this.query.reset();
+ },
+ 'click .oe_searchview_unfold_drawer': function (e) {
+ e.stopImmediatePropagation();
+ this.$el.toggleClass('oe_searchview_open_drawer');
+ },
+ 'keydown .oe_searchview_input, .oe_searchview_facet': function (e) {
+ switch(e.which) {
+ case $.ui.keyCode.LEFT:
+ this.focusPreceding(this);
+ e.preventDefault();
+ break;
+ case $.ui.keyCode.RIGHT:
+ this.focusFollowing(this);
+ e.preventDefault();
+ break;
+ }
+ }
+ },
/**
* @constructs instance.web.SearchView
* @extends instance.web.Widget
@@ -313,7 +345,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();
@@ -324,63 +356,31 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
context: this.dataset.get_context() });
$.when(load_view)
- .pipe(this.on_loaded)
- .fail(function () {
+ .pipe(function(r) {
+ self.search_view_loaded(r)
+ }).fail(function () {
self.ready.reject.apply(null, arguments);
});
}
- this.$element.on('keydown',
- '.oe_searchview_input, .oe_searchview_facet', function (e) {
- switch(e.which) {
- case $.ui.keyCode.LEFT:
- self.focusPreceding(this);
- e.preventDefault();
- break;
- case $.ui.keyCode.RIGHT:
- self.focusFollowing(this);
- e.preventDefault();
- break;
- }
- });
-
- this.$element.on('click', '.oe_searchview_clear', function (e) {
- e.stopImmediatePropagation();
- self.query.reset();
- });
- this.$element.on('click', '.oe_searchview_unfold_drawer', function (e) {
- e.stopImmediatePropagation();
- self.$element.toggleClass('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();
+ instance.web.bus.on('click', this, function(ev) {
+ if ($(ev.target).parents('.oe_searchview').length === 0) {
+ self.$el.removeClass('oe_searchview_open_drawer');
}
});
- // 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;
- menu.activate(
- $.Event({ type: "mouseenter" }),
- menu.element.children().first());
- });
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 +395,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 +419,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 +468,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.$('div.oe_searchview_input:focus').text();
},
/**
* Provide auto-completion result for req.term (an array to `resp`)
@@ -498,14 +498,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.$('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 +527,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.$('div.oe_searchview_facets');
_.invoke(this.input_subviews, 'destroy');
this.input_subviews = [];
@@ -549,7 +558,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,7 +624,17 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
return null;
}
},
- on_loaded: function(data) {
+
+ 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
+ this.custom_filters = new instance.web.search.CustomFilters(this);
+ // add Advanced to this.inputs
+ (new instance.web.search.Advanced(this));
+ },
+
+ search_view_loaded: function(data) {
var self = this;
this.fields_view = data.fields_view;
if (data.fields_view.type !== 'search' ||
@@ -624,30 +643,28 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
"Got non-search view after asking for a search view: type %s, arch root %s",
data.fields_view.type, data.fields_view.arch.tag));
}
-
this.make_widgets(
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
- this.custom_filters = 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.$('.oe_searchview_drawer')));
+
// load defaults
var defaults_fetched = $.when.apply(null, _(this.inputs).invoke(
- 'facet_for_defaults', this.defaults))
- .pipe(this.proxy('setup_default_query'));
+ 'facet_for_defaults', this.defaults)).then(function () {
+ self.query.reset(_(arguments).compact(), {preventSearch: true});
+ });
return $.when(drawer_started, defaults_fetched)
- .then(function () { self.ready.resolve(); })
+ .then(function () {
+ self.trigger("search_view_loaded", data);
+ self.ready.resolve();
+ });
},
setup_default_query: function () {
// Hacky implementation of CustomFilters#facet_for_defaults ensure
@@ -678,95 +695,6 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
this.query.reset(_(arguments).compact(), {preventSearch: true});
},
/**
- * Handle event when the user make a selection in the filters management select box.
- */
- on_filters_management: function(e) {
- var self = this;
- var select = this.$element.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();
- }
- if (val.slice(0, 4) == "get:") {
- val = val.slice(4);
- val = parseInt(val, 10);
- var filter = this.managed_filters[val];
- this.do_clear(false).then(_.bind(function() {
- select.val('get:' + val);
-
- var groupbys = [];
- var group_by = filter.context.group_by;
- if (group_by) {
- groupbys = _.map(
- group_by instanceof Array ? group_by : group_by.split(','),
- function (el) { return { group_by: el }; });
- }
- this.filter_data = {
- domains: [filter.domain],
- contexts: [filter.context],
- groupbys: groupbys
- };
- this.do_search();
- }, this));
- } else {
- 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.
*
* Result is an object with 4 (own) properties:
@@ -815,7 +743,8 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
groupbys: groupbys,
errors: errors
};
- }, /**
+ },
+ /**
* Performs the search view collection of widget data.
*
* If the collection went well (all fields are valid), then triggers
@@ -824,7 +753,8 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
* If at least one field failed its validation, triggers
* :js:func:`instance.web.SearchView.on_invalid` instead.
*
- * @param e jQuery event object coming from the "Search" button
+ * @param [_query]
+ * @param {Object} [options]
*/
do_search: function (_query, options) {
if (options && options.preventSearch) {
@@ -835,7 +765,7 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
this.on_invalid(search.errors);
return;
}
- return this.on_search(search.domains, search.contexts, search.groupbys);
+ this.trigger('search_data', search.domains, search.contexts, search.groupbys);
},
/**
* Triggered after the SearchView has collected all relevant domains and
@@ -853,8 +783,6 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
* @param {Array} contexts an array of literal contexts or context refs
* @param {Array} groupbys ordered contexts which may or may not have group_by keys
*/
- on_search: function (domains, contexts, groupbys) {
- },
/**
* Triggered after a validation error in the SearchView fields.
*
@@ -868,6 +796,7 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
*/
on_invalid: function (errors) {
this.do_notify(_t("Invalid Search"), _t("triggered from search view"));
+ this.trigger('invalid_search', errors);
}
});
@@ -878,6 +807,7 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea
instance.web.search.fields = new instance.web.Registry({
'char': 'instance.web.search.CharField',
'text': 'instance.web.search.CharField',
+ 'html': 'instance.web.search.CharField',
'boolean': 'instance.web.search.BooleanField',
'integer': 'instance.web.search.IntegerField',
'id': 'instance.web.search.IntegerField',
@@ -913,13 +843,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
*/
@@ -1041,7 +971,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);
},
/**
@@ -1050,7 +980,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.$('> li').removeClass('oe_selected');
var facet = this.view.query.find(_.bind(this.match_facet, this));
if (!facet) { return; }
facet.values.each(function (v) {
@@ -1261,7 +1191,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) {
@@ -1351,17 +1281,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;
}
});
@@ -1542,9 +1472,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,
+ _t("M2O search fields do not currently handle multiple default values"));
+ // there are many cases of {search_default_$m2ofield: [id]}, need
+ // to handle this as if it were a single value.
+ value = value[0];
}
- return this.model.call('name_get', [value], {}).pipe(function (names) {
+ return this.model.call('name_get', [value]).pipe(function (names) {
if (_(names).isEmpty()) { return null; }
return facet_from(self, names[0]);
})
@@ -1557,6 +1494,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);
}
});
@@ -1572,9 +1518,17 @@ instance.web.search.CustomFilters = instance.web.search.Input.extend({
this.model = new instance.web.Model('ir.filters');
this.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', {
@@ -1621,18 +1575,20 @@ instance.web.search.CustomFilters = instance.web.search.Input.extend({
*/
facet_for: function (filter) {
return {
- category: _("Custom Filter"),
+ category: _t("Custom Filter"),
icon: 'M',
field: {
get_context: function () { return filter.context; },
get_groupby: function () { return [filter.context]; },
get_domain: function () { return filter.domain; }
},
- values: [
- {label: filter.name, value: null}
- ]
+ is_custom_filter: true,
+ values: [{label: filter.name, value: null}]
};
},
+ clear_selection: function () {
+ this.$('li.oe_selected').removeClass('oe_selected');
+ },
append_filter: function (filter) {
var self = this;
var key = this.key_for(filter);
@@ -1644,13 +1600,12 @@ instance.web.search.CustomFilters = instance.web.search.Input.extend({
var id = filter.id;
this.filters[key] = filter;
$filter = this.$filters[key] = $('<li></li>')
- .appendTo(this.$element.find('.oe_searchview_custom_list'))
+ .appendTo(this.$('.oe_searchview_custom_list'))
.addClass(filter.user_id ? 'oe_searchview_custom_private'
: 'oe_searchview_custom_public')
.text(filter.name);
- $('<button type="button" class="oe_searchview_custom_delete">')
- .text(_t("Delete"))
+ $('<a class="oe_searchview_custom_delete">x</a>')
.click(function (e) {
e.stopPropagation();
self.model.call('unlink', [id]).then(function () {
@@ -1664,6 +1619,7 @@ instance.web.search.CustomFilters = instance.web.search.Input.extend({
$filter.unbind('click').click(function () {
self.view.query.reset([self.facet_for(filter)]);
+ $filter.addClass('oe_selected');
});
},
set_filters: function (filters) {
@@ -1671,11 +1627,9 @@ 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#oe_searchview_custom_public').prop('checked');
- var set_as_default = this.$element.find(
- 'input#oe_searchview_custom_default').prop('checked');
+ var $name = this.$('input:first');
+ var private_filter = !this.$('#oe_searchview_custom_public').prop('checked');
+ var set_as_default = this.$('#oe_searchview_custom_default').prop('checked');
var search = this.view.build_search_data();
this.rpc('/web/session/eval_domain_and_context', {
@@ -1688,7 +1642,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,
@@ -1698,7 +1652,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();
});
@@ -1725,7 +1679,7 @@ instance.web.search.Filters = instance.web.search.Input.extend({
var col1 = [], col2 = _(this.view.controls).map(function (inputs, group) {
var filters = _(inputs).filter(is_group);
return {
- name: group === 'null' ? _t("Filters") : group,
+ name: group === 'null' ? "<span class='oe_i'>q</span> " + _t("Filters") : "<span class='oe_i'>w</span> " + group,
filters: filters,
length: _(filters).chain().map(function (i) {
return i.filters.length; }).sum().value()
@@ -1743,26 +1697,27 @@ 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) {
- $('<h3>').text(group.name).appendTo($el);
+ $('<h3>').html(group.name).appendTo($el);
return $.when.apply(null,
_(group.filters).invoke('appendTo', $el));
}));
}
});
+
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) {
@@ -1771,7 +1726,7 @@ instance.web.search.Advanced = instance.web.search.Input.extend({
});
return $.when(
this._super(),
- this.rpc("/web/searchview/fields_get", {model: this.view.model}, function(data) {
+ this.rpc("/web/searchview/fields_get", {model: this.view.model}).then(function(data) {
self.fields = _.extend({
id: { string: 'ID', type: 'id' }
}, data.fields);
@@ -1780,11 +1735,20 @@ instance.web.search.Advanced = instance.web.search.Input.extend({
});
},
append_proposition: function () {
+ var self = this;
return (new instance.web.search.ExtendedSearchProposition(this, this.fields))
- .appendTo(this.$element.find('ul'));
+ .appendTo(this.$('ul')).then(function () {
+ self.$('button.oe_apply').prop('disabled', false);
+ });
+ },
+ remove_proposition: function (prop) {
+ // removing last proposition, disable apply button
+ if (this.getChildren().length <= 1) {
+ this.$('button.oe_apply').prop('disabled', true);
+ }
+ prop.destroy();
},
commit_search: function () {
- var self = this;
// Get domain sections from all propositions
var children = this.getChildren();
var propositions = _.invoke(children, 'get_proposition');
@@ -1808,15 +1772,22 @@ 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',
+ events: {
+ 'change .searchview_extended_prop_field': 'changed',
+ 'click .searchview_extended_delete_prop': function (e) {
+ e.stopPropagation();
+ this.getParent().remove_proposition(this);
+ }
+ },
/**
* @constructs instance.web.search.ExtendedSearchProposition
- * @extends instance.web.OldWidget
+ * @extends instance.web.Widget
*
* @param parent
* @param fields
@@ -1831,17 +1802,10 @@ instance.web.search.ExtendedSearchProposition = instance.web.OldWidget.extend(/*
this.value = null;
},
start: function () {
- var _this = this;
- this.$element.find(".searchview_extended_prop_field").change(function() {
- _this.changed();
- });
- this.$element.find('.searchview_extended_delete_prop').click(function () {
- _this.destroy();
- });
- this.changed();
+ return this._super().then(this.proxy('changed'));
},
changed: function() {
- var nval = this.$element.find(".searchview_extended_prop_field").val();
+ var nval = this.$(".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;}));
}
@@ -1856,7 +1820,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.$('.searchview_extended_prop_op').html('');
}
this.attrs.selected = field;
if(field == null) {
@@ -1872,9 +1836,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.$('.searchview_extended_prop_op'));
});
- var $value_loc = this.$element.find('.searchview_extended_prop_value').empty();
+ var $value_loc = this.$('.searchview_extended_prop_value').empty();
this.value.appendTo($value_loc);
},
@@ -1882,7 +1846,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.$('.searchview_extended_prop_op')[0];
var operator = op.options[op.selectedIndex];
return {
label: _.str.sprintf(_t('%(field)s %(operator)s "%(value)s"'), {
@@ -1926,7 +1890,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({
@@ -1950,7 +1914,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;
}
});
@@ -1968,11 +1932,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 "";
}
@@ -1992,11 +1956,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 "";
}
@@ -2009,12 +1973,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 f169887cc54..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);
- this.action_manager.appendTo(this.view_edit_dialog);
- $.when(this.action_manager.do_action(action)).then(function() {
- var viewmanager = self.action_manager.inner_viewmanager,
- controller = viewmanager.views[viewmanager.active_view].controller;
- 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;}
- });
- }
- arch_to_pass = _.filter($(arch.arch), function (child) {
- return child.nodeType == 1;
- });
- return self.do_save_xml(arch_to_pass[0], 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'},
- '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].id);
- });
- });
- },
- render_new_field :function(id){
- var self = this;
- var action = {
- context: {'default_model_id': id, 'manual': true},
- 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.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', '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', '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 3430f1dd703..bbb2234e3bd 100644
--- a/addons/web/static/src/js/view_form.js
+++ b/addons/web/static/src/js/view_form.js
@@ -9,31 +9,65 @@ 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 = {
/**
* Must return the asked field as in fields_get.
*/
- get_field: function(field_name) {},
+ get_field_desc: 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) {},
+ /**
+ Computes an OpenERP domain.
+
+ @param (list) expression An OpenERP domain.
+ @return (boolean) The computed value of the domain.
+ */
+ compute_domain: function(expression) {},
+ /**
+ Builds an evaluation context for the resolution of the fields' contexts. Please note
+ the field are only supposed to use this context to evualuate their own, they should not
+ extend it.
+
+ @return (CompoundContext) An OpenERP context.
+ */
+ build_eval_context: function() {},
};
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 +89,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,135 +103,142 @@ 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,
+ "footer_to_buttons": 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.qweb = null; // A QWeb instance will be created if the view is a QWeb template
+ this.__clicked_inside = false;
+ this.__blur_timeout = null;
+ this.rendering_engine = new instance.web.form.FormRenderingEngine(this);
+ 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();
+ });
+ self.on("load_record", self, self.load_record);
+ this.on('view_loaded', self, self.load_form);
+ 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) {
+ load_form: function(data) {
var self = this;
if (!data) {
throw new Error("No data provided.");
}
if (this.arch) {
- throw "Form view does not support multiple calls to on_loaded";
+ throw "Form view does not support multiple calls to load_form";
}
this.fields_order = [];
this.fields_view = data;
this.rendering_engine.set_fields_registry(this.fields_registry);
this.rendering_engine.set_tags_registry(this.tags_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');
- this.rendering_engine.render_to($dest);
- }
+ this.rendering_engine.set_widgets_registry(this.widgets_registry);
+ this.rendering_engine.set_fields_view(data);
+ 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.$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);
+ if (this.options.footer_to_buttons) {
+ this.$el.find('footer').appendTo(this.$buttons);
}
- 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.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.on("change:mode", this, this.switch_mode);
- this.set({mode: this.options.initial_mode});
+
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();
+ }
+ });
+ this.trigger('form_view_loaded', data);
return $.when();
},
- extract_qweb_template: function(fvg) {
- for (var i=0, ii=fvg.arch.children.length; i < ii; i++) {
- var child = fvg.arch.children[i];
- if (child.tag === "templates") {
- this.qweb = new QWeb2.Engine();
- this.qweb.add_template(instance.web.json_node_to_xml(child));
- if (!this.qweb.has_template('form')) {
- throw new Error("No QWeb template found for form view");
- }
- return true;
- }
+ 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;
}
- this.qweb = null;
- return false;
- },
- get_fvg_from_qweb: function(record) {
- var view = this.qweb.render('form', this.get_qweb_context(record));
- var fvg = _.clone(this.fields_view);
- fvg.arch = instance.web.xml_to_json(instance.web.str_to_xml(view).firstChild);
- return fvg;
- },
- get_qweb_context: function(record) {
- var self = this,
- new_record = {};
- _.each(record, function(value_, name) {
- var r = _.clone(self.fields_view.fields[name] || {});
- if ((r.type === 'date' || r.type === 'datetime') && value_) {
- r.raw_value = instance.web.auto_str_to_date(value_);
- } else {
- r.raw_value = value_;
- }
- r.value = instance.web.format_value(value_, r);
- new_record[name] = r;
- });
- return {
- record : new_record,
- new_record : !record.id
- };
},
- kill_current_form: function() {
- _.each(this.getChildren(), function(el) {
- el.destroy();
- });
- this.fields = {};
- this.fields_order = [];
- this.default_focus_field = null;
- this.default_focus_button = null;
- this.translatable_fields = [];
- this.$element.find('.oe_form_container').empty();
+ 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,39 +250,55 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form.
}
}
},
- do_show: function () {
+ /**
+ *
+ * @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() {
- self.$element.css('visibility', 'visible');
+ 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();
+ }
+ 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(function(r) {
+ self.trigger('load_record', r);
+ });
+ });
+ }
+ 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();
@@ -250,24 +308,22 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form.
}
this._super();
},
- on_record_loaded: function(record) {
+ load_record: 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;
-
- 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');
- this.rendering_engine.render_to($dest);
- }
+ this._actualize_mode();
+ this.set({ 'title' : record.id ? record.display_name : "New" });
_(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() {
@@ -283,29 +339,44 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form.
});
}
self.on_form_changed();
+ self.rendering_engine.init_fields();
self.is_initialized.resolve();
self.do_update_pager(record.id == null);
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 self = this;
+ var keys = _.keys(this.fields_view.fields);
+ if (keys.length) {
+ return this.dataset.default_get(keys).pipe(function(r) {
+ self.trigger('load_record', r);
+ });
+ }
+ return self.trigger('load_record', {});
+ },
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) {
+ execute_pager_action: function(action) {
if (this.can_be_discarded()) {
switch (action) {
case 'first':
@@ -322,20 +393,41 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form.
break;
}
this.reload();
+ this.trigger('pager_action_executed');
+ }
+ },
+ 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.execute_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;
var onchange = _.str.trim(on_change);
var call = onchange.match(/^\s?(.*?)\((.*?)\)\s?$/);
if (!call) {
- return null;
+ throw new Error("Wrong on change format: " + onchange);
}
var method = call[1];
@@ -402,100 +494,74 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form.
var self = this;
return this.on_change_mutex.exec(function() {
try {
- var response = {}, can_process_onchange = $.Deferred();
+ var def;
processed = processed || [];
processed.push(widget.name);
var on_change = widget.node.attrs.on_change;
if (on_change) {
var change_spec = self.parse_on_change(on_change, widget);
- if (change_spec) {
- var ajax = {
- url: '/web/dataset/onchange',
- async: false
- };
- can_process_onchange = self.rpc(ajax, {
- model: self.dataset.model,
- method: change_spec.method,
- args: [(self.datarecord.id == null ? [] : [self.datarecord.id])].concat(change_spec.args),
- context_id: change_spec.context_index == undefined ? null : change_spec.context_index + 1
- }).then(function(r) {
- _.extend(response, r);
- });
- } else {
- console.warn("Wrong on_change format", on_change);
- }
- }
- // fail if onchange failed
- if (can_process_onchange.isRejected()) {
- return can_process_onchange;
+ def = self.rpc('/web/dataset/onchange', {
+ model: self.dataset.model,
+ method: change_spec.method,
+ args: [(self.datarecord.id == null ? [] : [self.datarecord.id])].concat(change_spec.args),
+ context_id: change_spec.context_index == undefined ? null : change_spec.context_index + 1
+ });
+ } else {
+ def = $.when({});
}
+ return def.pipe(function(response) {
+ if (widget.field['change_default']) {
+ var fieldname = widget.name
+ var value_;
+ if (response.value && (fieldname in response.value)) {
+ // Use value from onchange if onchange executed
+ value_ = response.value[fieldname];
+ } else {
+ // otherwise get form value for field
+ value_ = self.fields[fieldname].get_value();
+ }
+ var condition = fieldname + '=' + value_;
- if (widget.field['change_default']) {
- var fieldname = widget.name, value_;
- if (response.value && (fieldname in response.value)) {
- // Use value from onchange if onchange executed
- value_ = response.value[fieldname];
- } else {
- // otherwise get form value for field
- value_ = self.fields[fieldname].get_value();
- }
- var condition = fieldname + '=' + value_;
-
- if (value_) {
- can_process_onchange = self.rpc({
- url: '/web/dataset/call',
- async: false
- }, {
- model: 'ir.values',
- method: 'get_defaults',
- args: [self.model, condition]
- }).then(function (results) {
- if (!results.length) { return; }
- if (!response.value) {
- response.value = {};
- }
- for(var i=0; i<results.length; ++i) {
- // [whatever, key, value]
- var triplet = results[i];
- response.value[triplet[1]] = triplet[2];
- }
- });
+ if (value_) {
+ return self.rpc('/web/dataset/call', {
+ model: 'ir.values',
+ method: 'get_defaults',
+ args: [self.model, condition]
+ }).pipe(function (results) {
+ if (!results.length) {
+ return response;
+ }
+ if (!response.value) {
+ response.value = {};
+ }
+ for(var i=0; i<results.length; ++i) {
+ // [whatever, key, value]
+ var triplet = results[i];
+ response.value[triplet[1]] = triplet[2];
+ }
+ return response;
+ });
+ }
}
- }
- if (can_process_onchange.isRejected()) {
- return can_process_onchange;
- }
-
- return self.on_processed_onchange(response, processed);
+ return response;
+ }).pipe(function(response) {
+ return self.on_processed_onchange(response, processed);
+ });
} catch(e) {
console.error(e);
+ instance.webclient.crashmanager.show_message(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("CrashManagerWarning", result.warning)), {
+ instance.web.dialog($(QWeb.render("CrashManager.warning", result.warning)), {
+ title:result.warning.title,
modal: true,
buttons: [
{text: _t("Ok"), click: function() { $(this).dialog("close"); }}
@@ -504,6 +570,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;
@@ -515,61 +584,131 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form.
return $.Deferred().resolve();
} catch(e) {
console.error(e);
+ instance.webclient.crashmanager.show_message(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") {
+ 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.$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') && field.$label) {
+ if (field.focus() !== false) {
+ break;
+ }
+ }
+ }
}
},
on_button_save: function() {
var self = this;
- return this.do_save().then(function(result) {
- self.set({mode: "view"});
+ return this.save().then(function(result) {
+ self.trigger("save", result);
+ 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.trigger('load_record', this.datarecord);
+ }
}
+ this.trigger('on_button_cancel');
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;
@@ -580,9 +719,9 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form.
var def = $.Deferred();
$.when(this.has_been_loaded).then(function() {
self.dataset.call('copy', [self.datarecord.id, {}, self.dataset.context]).then(function(new_id) {
- return self.on_created({ result : new_id });
+ return self.record_created(new_id);
}).then(function() {
- return self.set({mode: "edit"});
+ return self.to_edit_mode();
}).then(function() {
def.resolve();
});
@@ -595,7 +734,7 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form.
$.when(this.has_been_loaded).then(function() {
if (self.datarecord.id && confirm(_t("Do you really want to delete this record?"))) {
self.dataset.unlink([self.datarecord.id]).then(function() {
- self.on_pager_action('next');
+ self.execute_pager_action('next');
def.resolve();
});
} else {
@@ -607,17 +746,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 ``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) {
+ save: function(prepend_on_create) {
var self = this;
return this.mutating_mutex.exec(function() { return self.is_initialized.pipe(function() {
try {
@@ -625,6 +771,7 @@ 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;
@@ -647,21 +794,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.record_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);
+ return self.record_saved(r);
}, null);
}
- return save_deferral.then(success);
+ return save_deferral;
}
} catch (e) {
console.error(e);
@@ -670,22 +817,32 @@ 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 + "</li>";
- }
- });
- msg += "</ul>";
- this.do_warn("The following fields are invalid :", msg);
+ var warnings = _(this.fields).chain()
+ .filter(function (f) { return !f.is_valid(); })
+ .map(function (f) {
+ return _.str.sprintf('<li>%s</li>',
+ _.escape(f.string));
+ }).value();
+ warnings.unshift('<ul>');
+ warnings.push('</ul>');
+ this.do_warn("The following fields are invalid :", warnings.join(''));
},
- on_saved: function(r, success) {
- if (!r.result) {
+ /**
+ * Reload the form after saving
+ *
+ * @param {Object} r result of the write function.
+ */
+ record_saved: function(r) {
+ var self = this;
+ if (!r) {
// should not happen in the server, but may happen for internal purpose
+ this.trigger('record_saved', r);
return $.Deferred().reject();
} else {
return $.when(this.reload()).pipe(function () {
- return $.when(r).then(success); }, null);
+ self.trigger('record_saved', r);
+ return r;
+ });
}
},
/**
@@ -698,15 +855,17 @@ 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) {
- if (!r.result) {
+ record_created: function(r, prepend_on_create) {
+ var self = this;
+ if (!r) {
// should not happen in the server, but may happen for internal purpose
+ this.trigger('record_created', r);
return $.Deferred().reject();
} else {
- this.datarecord.id = r.result;
+ this.datarecord.id = r;
if (!prepend_on_create) {
this.dataset.alter_ids(this.dataset.ids.concat([this.datarecord.id]));
this.dataset.index = this.dataset.ids.length - 1;
@@ -717,10 +876,12 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form.
this.do_update_pager();
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 () {
+ self.trigger('record_created', r);
+ return _.extend(r, {created: true});
+ });
}
},
on_action: function (action) {
@@ -730,15 +891,23 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form.
var self = this;
return this.reload_mutex.exec(function() {
if (self.dataset.index == null) {
- self.do_prev_view();
+ self.trigger("previous_view");
return $.Deferred().reject().promise();
}
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 }
- }).pipe(self.on_record_loaded);
+ 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(function(r) {
+ self.trigger('load_record', r);
+ });
}
});
},
@@ -748,15 +917,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;
},
@@ -766,11 +935,20 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form.
},
recursive_save: function() {
var self = this;
- return $.when(this.do_save()).pipe(function(res) {
+ return $.when(this.save()).pipe(function(res) {
if (self.dataset.parent_view)
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;
@@ -788,10 +966,20 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form.
return true;
},
sidebar_context: function () {
- return this.do_save().pipe(_.bind(function() {return this.get_fields_values();}, this));
+ return this.save().pipe(_.bind(function() {return this.get_fields_values();}, this));
},
open_defaults_dialog: function () {
var self = this;
+ var display = function (field, value) {
+ if (field instanceof instance.web.form.FieldSelection) {
+ return _(field.values).find(function (option) {
+ return option[0] === value;
+ })[1];
+ } else if (field instanceof instance.web.form.FieldMany2One) {
+ return field.get_displayed();
+ }
+ return value;
+ }
var fields = _.chain(this.fields)
.map(function (field, name) {
var value = field.get_value();
@@ -801,32 +989,32 @@ 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':
- displayed = _(field.values).find(function (option) {
- return option[0] === value;
- })[1];
- break;
- }
return {
name: name,
- string: field.node.attrs.string || field.field.string,
+ string: field.string,
value: value,
- displayed: displayed,
- // convert undefined to false
- change_default: !!field.field.change_default
+ displayed: display(field, value),
}
})
.compact()
.sortBy(function (field) { return field.string; })
.value();
- var conditions = _.chain(fields)
- .filter(function (field) { return field.change_default; })
+ var conditions = _.chain(self.fields)
+ .filter(function (field) { return field.field.change_default; })
+ .map(function (field, name) {
+ var value = field.get_value();
+ return {
+ name: name,
+ string: field.string,
+ value: value,
+ displayed: display(field, value),
+ }
+ })
.value();
var d = new instance.web.Dialog(this, {
@@ -838,21 +1026,21 @@ 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,
field_to_set,
self.fields[field_to_set].get_value(),
all_users,
- false,
+ true,
condition || false
]).then(function () { d.close(); });
}}
@@ -864,10 +1052,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 (this.get_field(name).translate) {
+ 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_desc(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);
@@ -876,13 +1076,35 @@ instance.web.FormView = instance.web.View.extend(_.extend({}, instance.web.form.
}
});
},
- get_field: function(field_name) {
+ get_field_desc: 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();
},
-}));
+ compute_domain: function(expression) {
+ return instance.web.form.compute_domain(expression, this.fields);
+ },
+ _build_view_fields_values: function(blacklist) {
+ var a_dataset = this.dataset;
+ var fields_values = this.get_fields_values(blacklist);
+ var active_id = a_dataset.ids[a_dataset.index];
+ _.extend(fields_values, {
+ active_id: active_id || false,
+ active_ids: active_id ? [active_id] : [],
+ active_model: a_dataset.model,
+ parent: {}
+ });
+ if (a_dataset.parent_view) {
+ fields_values.parent = a_dataset.parent_view.get_fields_values([a_dataset.child_name]);
+ }
+ return fields_values;
+ },
+ build_eval_context: function(blacklist) {
+ var a_dataset = this.dataset;
+ return new instance.web.CompoundContext(a_dataset.get_context(), this._build_view_fields_values(blacklist));
+ },
+});
/**
* Interface to be implemented by rendering engines for the form view.
@@ -890,12 +1112,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({
@@ -904,6 +1126,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;
@@ -911,6 +1137,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;
@@ -919,17 +1159,19 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
// but one day, we will have to get rid of xml2json
var xml = instance.web.json_node_to_xml(this.fvg.arch);
this.$form = $('<div class="oe_form">' + xml + '</div>');
- if (this.fvg.arch.attrs && this.fvg.arch.attrs['layout'] !== 'manual') {
- this.$form.attr('layout', 'auto');
- }
+
+ this.process_version();
this.fields_to_init = [];
this.tags_to_init = [];
+ this.widgets_to_init = [];
this.labels = {};
this.process(this.$form);
this.$form.appendTo(this.$target);
+ this.to_replace = [];
+
_.each(this.fields_to_init, function($elem) {
var name = $elem.attr("name");
if (!self.fvg.fields[name]) {
@@ -937,7 +1179,7 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
}
var obj = self.fields_registry.get_any([$elem.attr('widget'), self.fvg.fields[name].type]);
if (!obj) {
- throw new Error("Widget type '"+ key + "' is not implemented");
+ throw new Error("Widget type '"+ $elem.attr('widget') + "' is not implemented");
}
var w = new (obj)(self.view, instance.web.xml_to_json($elem[0]));
var $label = self.labels[$elem.attr("name")];
@@ -946,23 +1188,33 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
}
self.alter_field(w);
self.view.register_field(w, $elem.attr("name"));
- w.replace($elem);
+ self.to_replace.push([w, $elem]);
});
_.each(this.tags_to_init, function($elem) {
var tag_name = $elem[0].tagName.toLowerCase();
var obj = self.tags_registry.get_object(tag_name);
var w = new (obj)(self.view, instance.web.xml_to_json($elem[0]));
- w.replace($elem);
- })
- // TODO: return a deferred
+ self.to_replace.push([w, $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]));
+ self.to_replace.push([w, $elem]);
+ });
+ },
+ init_fields: function() {
+ var defs = [];
+ _.each(this.to_replace, function(el) {
+ defs.push(el[0].replace(el[1]));
+ });
+ this.to_replace = [];
+ return $.when.apply($, defs);
},
- render_element: function(template, layout/* dictionaries */) {
- var dicts = [].slice.call(arguments).slice(2);
- dicts.unshift({ 'layout' : layout });
+ render_element: function(template /* dictionaries */) {
+ var dicts = [].slice.call(arguments).slice(1);
var dict = _.extend.apply(_, dicts);
dict['classnames'] = dict['class'] || ''; // class is a reserved word and might caused problem to Safari when used from QWeb
- var alt_template = template + '.' + layout;
- template = QWeb.has_template(alt_template) ? alt_template : template;
return $(QWeb.render(template, dict));
},
alter_field: function(field) {
@@ -977,10 +1229,8 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
}
this.$target.toggleClass('oe_layout_debugging');
},
- process: function($tag, layout) {
+ process: function($tag) {
var self = this;
- layout = $tag.attr('layout') || layout || 'auto';
- $tag.removeAttr('layout');
var tagname = $tag[0].nodeName.toLowerCase();
if (this.tags_registry.contains(tagname)) {
this.tags_to_init.push($tag);
@@ -990,39 +1240,49 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
if (fn) {
var args = [].slice.call(arguments);
args[0] = $tag;
- args[1] = layout;
return fn.apply(self, args);
} else {
// generic tag handling, just process children
$tag.children().each(function() {
- self.process($(this), layout);
+ self.process($(this));
});
self.handle_common_properties($tag, $tag);
$tag.removeAttr("modifiers");
return $tag;
}
},
- process_sheet: function($sheet, layout) {
- var $new_sheet = this.render_element('FormRenderingSheet', layout, $sheet.getAttributes());
+ 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);
- var $dst = (layout === 'auto') ? $new_sheet.find('group:first') : $new_sheet.find('.oe_form_sheet');
- $sheet.children().appendTo($dst);
+ var $dst = $new_sheet.find('.oe_form_sheet');
+ $sheet.contents().appendTo($dst);
$sheet.before($new_sheet).remove();
- this.process($new_sheet, layout);
+ this.process($new_sheet);
},
- process_form: function($form, layout) {
- var $new_form = this.render_element('FormRenderingForm', layout, $form.getAttributes());
+ process_form: function($form) {
+ if ($form.find('> sheet').length === 0) {
+ $form.addClass('oe_form_nosheet');
+ }
+ var $new_form = this.render_element('FormRenderingForm', $form.getAttributes());
this.handle_common_properties($new_form, $form);
- var $dst = (layout === 'auto') ? $new_form.find('group:first') : $new_form;
- $form.children().appendTo($dst);
+ $form.contents().appendTo($new_form);
if ($form[0] === this.$form[0]) {
// If root element, replace it
this.$form = $new_form;
} else {
$form.before($new_form).remove();
}
- this.process($new_form, layout);
+ this.process($new_form);
},
+ /*
+ * Used by direct <field> children of a <group> tag only
+ * This method will add the implicit <label...> for every field
+ * in the <group>
+ */
preprocess_field: function($field) {
var self = this;
var name = $field.attr('name'),
@@ -1043,11 +1303,12 @@ 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'),
"help": $field.attr('help'),
+ "class": $field.attr('class'),
});
$label.insertBefore($field);
if (field_colspan > 1) {
@@ -1055,31 +1316,34 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
}
return $label;
},
- process_field: function($field, layout) {
- var $label = this.preprocess_field($field);
- if ($label)
- this.process($label, layout);
+ process_field: function($field) {
+ if ($field.parent().is('group')) {
+ // No implicit labels for normal fields, only for <group> direct children
+ var $label = this.preprocess_field($field);
+ if ($label) {
+ this.process($label);
+ }
+ }
this.fields_to_init.push($field);
return $field;
},
- process_group: function($group, layout) {
+ process_group: function($group) {
var self = this;
- if ($group.parent().is('.oe_form_group_cell')) {
- $group.parent().addClass('oe_form_group_nested');
- }
$group.children('field').each(function() {
self.preprocess_field($(this));
});
- var $new_group = this.render_element('FormRenderingGroup', layout, $group.getAttributes()),
- $table;
- if ($new_group.is('table')) {
+ var $new_group = this.render_element('FormRenderingGroup', $group.getAttributes());
+ var $table;
+ if ($new_group.first().is('table.oe_form_group')) {
$table = $new_group;
+ } else if ($new_group.filter('table.oe_form_group').length) {
+ $table = $new_group.filter('table.oe_form_group').first();
} else {
- $table = $new_group.find('table:first');
+ $table = $new_group.find('table.oe_form_group').first();
}
- $table.addClass('oe_form_group');
+
var $tr, $td,
- cols = parseInt($group.attr('col') || 4, 10),
+ cols = parseInt($group.attr('col') || 2, 10),
row_cols = cols;
var children = [];
@@ -1089,6 +1353,8 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
var tagName = $child[0].tagName.toLowerCase();
var $td = $('<td/>').addClass('oe_form_group_cell').attr('colspan', colspan);
var newline = tagName === 'newline';
+
+ // Note FME: those classes are used in layout debug mode
if ($tr && row_cols > 0 && (newline || row_cols < colspan)) {
$tr.addClass('oe_form_group_row_incomplete');
if (newline) {
@@ -1102,6 +1368,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;
@@ -1118,7 +1387,6 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
}
$group.before($new_group).remove();
- // Now compute width of cells
$table.find('> tbody > tr').each(function() {
var to_compute = [],
row_cols = cols,
@@ -1128,16 +1396,11 @@ 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--;
- }
break;
case 'label':
if ($child.attr('for')) {
$td.attr('width', '1%').addClass('oe_form_group_cell_label');
- row_cols--;
+ row_cols-= $td.attr('colspan') || 1;
total--;
}
break;
@@ -1154,20 +1417,22 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
}
$td.attr('width', width);
$child.removeAttr('width');
- row_cols--;
+ row_cols-= $td.attr('colspan') || 1;
} else {
to_compute.push($td);
}
}
});
- var unit = Math.floor(total / row_cols);
- if (!$(this).is('.oe_form_group_row_incomplete')) {
- _.each(to_compute, function($td, i) {
- var width = parseInt($td.attr('colspan'), 10) * unit;
- $td.attr('width', ((i == to_compute.length - 1) ? total : width) + '%');
- total -= width;
- });
+ if (row_cols) {
+ var unit = Math.floor(total / row_cols);
+ if (!$(this).is('.oe_form_group_row_incomplete')) {
+ _.each(to_compute, function($td, i) {
+ var width = parseInt($td.attr('colspan'), 10) * unit;
+ $td.attr('width', width + '%');
+ total -= width;
+ });
+ }
}
});
_.each(children, function(el) {
@@ -1176,37 +1441,61 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
this.handle_common_properties($new_group, $group);
return $new_group;
},
- process_notebook: function($notebook, layout) {
+ process_notebook: function($notebook) {
var self = this;
var pages = [];
$notebook.find('> page').each(function() {
var $page = $(this);
var page_attrs = $page.getAttributes();
page_attrs.id = _.uniqueId('notebook_page_');
- pages.push(page_attrs);
- var $new_page = self.render_element('FormRenderingNotebookPage', layout, page_attrs);
- var $dst = (layout === 'auto') ? $new_page.find('group:first') : $new_page;
- $page.children().appendTo($dst);
+ var $new_page = self.render_element('FormRenderingNotebookPage', page_attrs);
+ $page.contents().appendTo($new_page);
$page.before($new_page).remove();
- self.handle_common_properties($new_page, $page);
+ var ic = self.handle_common_properties($new_page, $page).invisibility_changer;
+ page_attrs.__page = $new_page;
+ page_attrs.__ic = ic;
+ pages.push(page_attrs);
+
+ $new_page.children().each(function() {
+ self.process($(this));
+ });
});
- var $new_notebook = this.render_element('FormRenderingNotebook', layout, { pages : pages });
- $notebook.children().appendTo($new_notebook);
+ var $new_notebook = this.render_element('FormRenderingNotebook', { pages : pages });
+ $notebook.contents().appendTo($new_notebook);
$notebook.before($new_notebook).remove();
- $new_notebook.children().each(function() {
- self.process($(this));
- });
+ self.process($($new_notebook.children()[0]));
+ //tabs and invisibility handling
$new_notebook.tabs();
+ _.each(pages, function(page, i) {
+ 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;
+ var first_visible = _.find(_.range(pages.length), function(i2) {
+ return (! pages[i2].__ic) || (! pages[i2].__ic.get("effective_invisible"));
+ });
+ if (first_visible !== undefined) {
+ $new_notebook.tabs('select', first_visible);
+ }
+ });
+ });
+
this.handle_common_properties($new_notebook, $notebook);
return $new_notebook;
},
- process_separator: function($separator, layout) {
- var $new_separator = this.render_element('FormRenderingSeparator', layout, $separator.getAttributes());
+ process_separator: function($separator) {
+ var $new_separator = this.render_element('FormRenderingSeparator', $separator.getAttributes());
$separator.before($new_separator).remove();
this.handle_common_properties($new_separator, $separator);
return $new_separator;
},
- process_label: function($label, layout) {
+ process_label: function($label) {
var name = $label.attr("for"),
field_orm = this.fvg.fields[name];
var dict = {
@@ -1223,7 +1512,7 @@ instance.web.form.FormRenderingEngine = instance.web.form.FormRenderingEngineInt
align = 'center';
}
dict.align = align;
- var $new_label = this.render_element('FormRenderingLabel', layout, dict);
+ var $new_label = this.render_element('FormRenderingLabel', dict);
$label.before($new_label).remove();
this.handle_common_properties($new_label, $label);
if (name) {
@@ -1232,52 +1521,66 @@ 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)
- new instance.web.form.InvisibilityChanger(this.view, this.view, modifiers.invisible, $new_element);
+ ic = new instance.web.form.InvisibilityChanger(this.view, this.view, modifiers.invisible, $new_element);
$new_element.addClass($node.attr("class") || "");
$new_element.attr('style', $node.attr('style'));
+ return {invisibility_changer: ic,};
},
});
-instance.web.form.FormRenderingEngineReadonly = instance.web.form.FormRenderingEngine.extend({
- alter_field: function(field) {
- field.set({"force_readonly": true});
- },
-});
+/**
+ Welcome.
-instance.web.form.FormDialog = instance.web.Dialog.extend({
- init: function(parent, options, view_id, dataset) {
- this._super(parent, options);
- this.dataset = dataset;
- this.view_id = view_id;
- return this;
- },
- start: function() {
- this._super();
- this.form = new instance.web.FormView(this, this.dataset, this.view_id, {
- pager: false
+ If you read this documentation, it probably means that you were asked to use a form view widget outside of
+ a form view. Before going further, you must understand that those fields were never really created for
+ that usage. Don't think that this class will hold the answer to all your problems, at best it will allow
+ you to hack the system with more style.
+*/
+instance.web.form.DefaultFieldManager = instance.web.Widget.extend({
+ init: function(parent, eval_context) {
+ this._super(parent);
+ this.field_descs = {};
+ this.eval_context = eval_context || {};
+ this.set({
+ display_invalid_fields: false,
+ actual_mode: 'create',
});
- this.form.appendTo(this.$element);
- this.form.on_created.add_last(this.on_form_dialog_saved);
- this.form.on_saved.add_last(this.on_form_dialog_saved);
- return this;
},
- select_id: function(id) {
- if (this.form.dataset.select_id(id)) {
- return this.form.do_show();
- } else {
- this.do_warn("Could not find id in dataset");
- return $.Deferred().reject();
+ get_field_desc: function(field_name) {
+ if (this.field_descs[field_name] === undefined) {
+ this.field_descs[field_name] = {
+ string: field_name,
+ };
}
+ return this.field_descs[field_name];
+ },
+ extend_field_desc: function(fields) {
+ var self = this;
+ _.each(fields, function(v, k) {
+ _.extend(self.get_field_desc(k), v);
+ });
+ },
+ get_field_value: function(field_name) {
+ return false;
+ },
+ set_values: function(values) {
+ // nothing
+ },
+ compute_domain: function(expression) {
+ return instance.web.form.compute_domain(expression, {});
+ },
+ build_eval_context: function() {
+ return new instance.web.CompoundContext(this.eval_context);
},
- on_form_dialog_saved: function(r) {
- this.close();
- }
});
instance.web.form.compute_domain = function(expr, fields) {
+ if (! (expr instanceof Array))
+ return !! expr;
var stack = [];
for (var i = expr.length - 1; i >= 0; i--) {
var ex = expr[i];
@@ -1351,83 +1654,118 @@ 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 :
+ self._ic_field_manager.compute_domain(self._ic_invisible_modifier);
+ 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() {
- var check_visibility = function() {
- if (this.get("effective_invisible")) {
- this.$element.hide();
- } else {
- this.$element.show();
- }
- };
- this.on("change:effective_invisible", this, check_visibility);
- _.bind(check_visibility, this)();
+ this.on("change:effective_invisible", this, this._check_visibility);
+ this._check_visibility();
+ },
+ _check_visibility: function() {
+ 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.field_manager = field_manager;
+ if (this.field_manager instanceof instance.web.FormView)
+ this.view = this.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.view.on("view_content_has_changed", this, this.process_modifiers);
+ this.field_manager.on("view_content_has_changed", this, this.process_modifiers);
+
+ this.set({
+ required: false,
+ readonly: false,
+ });
+ // some events to make the property "effective_readonly" sync automatically with "readonly" and
+ // "mode" on field_manager
+ var self = this;
+ 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.process_modifiers();
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);
+ var val = this.field_manager.compute_domain(this.modifiers[a]);
to_set[a] = val;
}
}
@@ -1435,7 +1773,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,
@@ -1446,7 +1784,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'),
@@ -1456,25 +1794,6 @@ instance.web.form.FormWidget = instance.web.Widget.extend(_.extend({}, instance.
}, options || {});
$(trigger).tipsy(options);
},
- _build_view_fields_values: function(blacklist) {
- var a_dataset = this.view.dataset;
- var fields_values = this.view.get_fields_values(blacklist);
- var active_id = a_dataset.ids[a_dataset.index];
- _.extend(fields_values, {
- active_id: active_id || false,
- active_ids: active_id ? [active_id] : [],
- active_model: a_dataset.model,
- parent: {}
- });
- if (a_dataset.parent_view) {
- fields_values.parent = a_dataset.parent_view.get_fields_values([a_dataset.child_name]);
- }
- return fields_values;
- },
- _build_eval_context: function(blacklist) {
- var a_dataset = this.view.dataset;
- return new instance.web.CompoundContext(a_dataset.get_context(), this._build_view_fields_values(blacklist));
- },
/**
* Builds a new context usable for operations related to fields by merging
* the fields'context with the action's context.
@@ -1485,9 +1804,9 @@ 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);
+ var fields_values = this.field_manager.build_eval_context(blacklist);
v_context = new instance.web.CompoundContext(v_context).set_eval_context(fields_values);
}
return v_context;
@@ -1498,31 +1817,35 @@ instance.web.form.FormWidget = instance.web.Widget.extend(_.extend({}, instance.
// if there is a domain on the node, overrides the model's domain
var final_domain = n_domain !== null ? n_domain : f_domain;
if (!(final_domain instanceof Array) || true) { //TODO: remove true
- var fields_values = this._build_eval_context();
+ var fields_values = this.field_manager.build_eval_context();
final_domain = new instance.web.CompoundDomain(final_domain).set_eval_context(fields_values);
}
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;
@@ -1571,37 +1894,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
@@ -1610,14 +1926,11 @@ 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.
- *
+ * regardless of any asynchronous operation currently running. Calls to set_value() can and will also occur
+ * before the widget is inserted into the DOM.
+ *
* 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
@@ -1627,19 +1940,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() {},
@@ -1654,28 +1967,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
@@ -1684,41 +1999,30 @@ 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.field = this.field_manager.get_field_desc(this.name);
+ this.widget = this.node.attrs.widget;
+ this.string = this.node.attrs.string || this.field.string || this.name;
+ this.options = instance.web.py_eval(this.node.attrs.options || '{}');
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.on("change:value", this, function() {
- if (! this._inhibit_on_change)
- this.trigger('changed_value');
+ this.trigger('changed_value');
this._check_css_flags();
});
},
renderElement: function() {
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));
+ if (this.field.translate && this.view) {
+ this.$el.addClass('oe_form_field_translatable');
+ this.$el.find('.oe_field_translate').click(this.on_translate);
}
- this.$label = this.view.$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 ? 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;
@@ -1730,23 +2034,47 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(_.extend({
this.on("change:required", this, this._set_required);
this._set_required();
}
+ this._check_visibility();
+ this.field_manager.off("change:display_invalid_fields", this, this._check_css_flags);
+ this.field_manager.on("change:display_invalid_fields", this, this._check_css_flags);
+ this._check_css_flags();
+ },
+ start: function() {
+ var tmp = this._super();
+ this.on("change:value", this, function() {
+ if (! this.no_rerender)
+ this.render_value();
+ });
+ this.render_value();
},
/**
* 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');
},
+ /**
+ Utility method that all implementations should use to change the
+ value without triggering a re-rendering.
+ */
+ internal_set_value: function(value_) {
+ var tmp = this.no_render;
+ this.no_rerender = true;
+ this.set({'value': value_});
+ this.no_rerender = tmp;
+ },
+ /**
+ This method is called each time the value is modified.
+ */
+ render_value: function() {},
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;
@@ -1758,62 +2086,65 @@ 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);
+ });
+ },
+
+ set_dimensions: function (height, width) {
+ // remove width css property
+ this.$el.css('width', '');
+ // extract style (without width)
+ var old_style = this.$el.attr('style');
+ // jQuery doesn't understand/use !important
+ var style = 'width:' + width + 'px !important;';
+ if (old_style) {
+ style += old_style
+ }
+ this.$el.attr('style', style);
+ this.$el.css('minHeight', height);
+ }
+});
/**
- * 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().
+ * Default implementation of, you should not override it, use initialize_field() instead.
*/
start: function() {
- this._super();
this.initialize_field();
+ this._super();
},
initialize_field: function() {
- this.on("change:effective_readonly", this, function() {
- this.destroy_content();
- this.renderElement();
- this.initialize_content();
- this.render_value();
- });
+ this.on("change:effective_readonly", this, this.reinitialize);
+ this.initialize_content();
+ },
+ reinitialize: function() {
+ this.destroy_content();
+ this.renderElement();
this.initialize_content();
- this.render_value();
},
/**
* Called to destroy anything that could have been created previously, called before a
@@ -1824,43 +2155,49 @@ instance.web.form.ReinitializeFieldMixin = {
* Called to initialize the content.
*/
initialize_content: function() {},
- /**
- * 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, {
+/**
+ * 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, {
+ reinitialize: function() {
+ instance.web.form.ReinitializeWidgetMixin.reinitialize.call(this);
+ this.render_value();
+ },
+});
+
+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) {
this._super(field_manager, node);
this.password = this.node.attrs.password === 'True' || this.node.attrs.password === '1';
},
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.internal_set_value(self.parse_value($input.val()));
});
- },
- set_value: function(value_) {
- this._super(value_);
- this.render_value();
+ this.setupFocus($input);
},
render_value: function() {
- var show_value = instance.web.format_value(this.get('value'), this, '');
+ var show_value = this.format_value(this.get('value'), '');
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.$(".oe_form_char_content").text(show_value);
}
},
is_syntax_valid: function() {
- if (!this.get("effective_readonly")) {
+ if (!this.get("effective_readonly") && this.$("input").size() > 0) {
try {
- var value_ = instance.web.parse_value(this.$element.find('input').val(), this, '');
+ var value_ = this.parse_value(this.$el.find('input').val(), '');
return true;
} catch(e) {
return false;
@@ -1868,31 +2205,39 @@ instance.web.form.FieldChar = instance.web.form.AbstractField.extend(_.extend({}
}
return true;
},
+ parse_value: function(val, def) {
+ return instance.web.parse_value(val, this, def);
+ },
+ format_value: function(val, def) {
+ return instance.web.format_value(val, this, def);
+ },
is_false: function() {
- return this.get('value') === '';
+ 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() {
@@ -1908,7 +2253,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")) {
@@ -1919,7 +2266,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() {
@@ -1936,11 +2283,12 @@ instance.web.form.FieldUrl = instance.web.form.FieldChar.extend({
instance.web.form.FieldFloat = instance.web.form.FieldChar.extend({
is_field_number: true,
+ widget_class: 'oe_form_field_float',
init: function (field_manager, node) {
this._super(field_manager, node);
- this.set({'value': 0});
+ this.internal_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;
}
@@ -1951,11 +2299,14 @@ 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({
- template: "web.datetimepicker",
+instance.web.DateTimeWidget = instance.web.Widget.extend({
+ template: "web.datepicker",
jqueryui_object: 'datetimepicker',
type_of_date: "datetime",
init: function(parent) {
@@ -1964,23 +2315,30 @@ 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.change(this.on_change);
+ this.$input = this.$el.find('input.oe_datepicker_master');
+ this.$input_picker = this.$el.find('input.oe_datepicker_container');
+ this.$input.change(function(){
+ self.change_datetime();
+ });
+
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});
@@ -1990,7 +2348,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_});
@@ -2006,7 +2367,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();
@@ -2027,9 +2388,10 @@ instance.web.DateTimeWidget = instance.web.OldWidget.extend({
format_client: function(v) {
return instance.web.format_value(v, {"widget": this.type_of_date});
},
- on_change: function() {
+ change_datetime: function() {
if (this.is_valid_()) {
this.set_value_from_ui_();
+ this.trigger("datetime_changed");
}
}
});
@@ -2039,8 +2401,8 @@ 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, {
- template: "EmptyComponent",
+instance.web.form.FieldDatetime = instance.web.form.AbstractField.extend(instance.web.form.ReinitializeFieldMixin, {
+ template: "FieldDatetime",
build_widget: function() {
return new instance.web.DateTimeWidget(this);
},
@@ -2053,69 +2415,77 @@ instance.web.form.FieldDatetime = instance.web.form.AbstractField.extend(_.exten
initialize_content: function() {
if (!this.get("effective_readonly")) {
this.datewidget = this.build_widget();
- this.datewidget.on_change.add_last(_.bind(function() {
- this.set({'value': this.datewidget.get_value()});
+ this.datewidget.on('datetime_changed', this, _.bind(function() {
+ this.internal_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_) {
- this._super(value_);
- this.render_value();
- },
render_value: function() {
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() {
- if (!this.get("effective_readonly")) {
+ if (!this.get("effective_readonly") && this.datewidget) {
return this.datewidget.is_valid_();
}
return true;
},
is_false: function() {
- return this.get('value') === '';
+ 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",
build_widget: function() {
return new instance.web.DateWidget(this);
}
});
-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.internal_set_value(instance.web.parse_value(self.$textarea.val(), self));
}, this));
} else {
this.$textarea.attr('disabled', 'disabled');
}
- },
- set_value: function(value_) {
- this._super.apply(this, arguments);
- this.render_value();
+ this.$el.keyup(function (e) {
+ if (e.which === $.ui.keyCode.ENTER) {
+ e.stopPropagation();
+ }
+ });
+ this.setupFocus(this.$textarea);
},
render_value: function() {
+ $(window).resize();
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")) {
+ if (!this.get("effective_readonly") && this.$textarea) {
try {
var value_ = instance.web.parse_value(this.$textarea.val(), this, '');
return true;
@@ -2126,53 +2496,91 @@ instance.web.form.FieldText = instance.web.form.AbstractField.extend(_.extend({}
return true;
},
is_false: function() {
- return this.get('value') === '';
+ return this.get('value') === '' || this._super();
},
- focus: function($element) {
- this.delay_focus(this.$textarea);
+ focus: function($el) {
+ this.$textarea.focus();
},
- 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));
+ set_dimensions: function (height, width) {
+ this._super();
+ this.$textarea.css({
+ width: width,
+ minHeight: height
});
- $div.appendTo($('body'));
- new_height = $div.height();
- if (new_height < 90) {
- new_height = 90;
+ },
+});
+
+/**
+ * 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 || '100%');
+ 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.internal_set_value(self.$textarea.val());
+ }
+ });
}
- if (!isNaN(max_height) && new_height > max_height) {
- new_height = max_height;
+ },
+ 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() {
- this._super.apply(this, arguments);
- this.$checkbox = $("input", this.$element);
- this.$element.click(_.bind(function() {
- this.set({'value': this.$checkbox.is(':checked')});
+ var self = this;
+ this.$checkbox = $("input", this.$el);
+ this.setupFocus(this.$checkbox);
+ this.$el.click(_.bind(function() {
+ this.internal_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)();
- },
- set_value: function(value_) {
+ check_readonly.call(this);
this._super.apply(this, arguments);
- this.$checkbox[0].checked = value_;
+ },
+ render_value: function() {
+ this.$checkbox[0].checked = this.get('value');
},
focus: function() {
- this.delay_focus(this.$checkbox);
+ this.$checkbox.focus();
}
});
@@ -2180,27 +2588,23 @@ 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")
});
},
- set_value: function(value_) {
- this._super.apply(this, arguments);
- var show_value = Number(value_);
+ render_value: function() {
+ var show_value = Number(this.get('value'));
if (isNaN(show_value)) {
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;
@@ -2226,9 +2630,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.internal_set_value(this.values[this.$el.find('select')[0].selectedIndex][0]);
}, this))
.change(function () { ischanging = true; })
.click(function () { ischanging = false; })
@@ -2237,12 +2641,12 @@ 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_;
value_ = value_ instanceof Array ? value_[0] : value_;
this._super(value_);
- this.render_value();
},
render_value: function() {
if (!this.get("effective_readonly")) {
@@ -2250,27 +2654,20 @@ 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];
- 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;
@@ -2297,7 +2694,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);
}
});
})();
@@ -2317,54 +2715,72 @@ instance.web.form.CompletionFieldMixin = {
var self = this;
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, self.build_domain(), 'ilike', this.limit + 1)).pipe(function(data) {
+ search_val, new instance.web.CompoundDomain(self.build_domain(), [["id", "not in", blacklist]]),
+ '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;
});
},
+ get_search_blacklist: function() {
+ return [];
+ },
_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();
@@ -2380,7 +2796,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
@@ -2388,18 +2804,53 @@ instance.web.form.CompletionFieldMixin = {
self.build_domain(),
new instance.web.CompoundContext(self.build_context(), context || {})
);
- pop.on_select_elements.add(function(element_ids) {
+ pop.on("elements_selected", self, 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);
@@ -2408,114 +2859,159 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(_.exten
this.display_value = {};
this.last_search = [];
this.floating = false;
- this.inhibit_on_change = false;
+ this.current_display = null;
+ this.is_started = false;
},
- start: function() {
- this._super();
- instance.web.form.ReinitializeFieldMixin.start.call(this);
- this.on("change:value", this, function() {
- this.floating = false;
+ reinit_value: function(val) {
+ this.internal_set_value(val);
+ this.floating = false;
+ if (this.is_started)
this.render_value();
+ },
+ initialize_field: function() {
+ this.is_started = true;
+ instance.web.bus.on('click', this, function() {
+ if (!this.get("effective_readonly") && this.$input && this.$input.autocomplete('widget').is(':visible')) {
+ this.$input.autocomplete("close");
+ }
});
+ instance.web.form.ReinitializeFieldMixin.initialize_field.call(this);
},
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);
+ var pop = new instance.web.form.FormOpenPopup(self);
pop.show_element(
self.field.relation,
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() {
+ pop.on('write_completed', self, 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.internal_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;
- var anyoneLoosesFocus = function() {
+ self.ed_def = $.Deferred();
+ self.uned_def = $.Deferred();
+ var ed_delay = 200;
+ var ed_duration = 15000;
+ var anyoneLoosesFocus = function (e) {
+ var used = false;
if (self.floating) {
if (self.last_search.length > 0) {
if (self.last_search[0][0] != self.get("value")) {
self.display_value = {};
self.display_value["" + self.last_search[0][0]] = self.last_search[0][1];
- self.set({value: self.last_search[0][0]});
+ self.reinit_value(self.last_search[0][0]);
} else {
+ used = true;
self.render_value();
}
} else {
- self.set({value: false});
+ used = true;
+ self.reinit_value(false);
}
+ 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();
+ ignore_blur = false;
+ self.trigger('focused');
});
+ ignore_blur = true;
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
@@ -2531,10 +3027,11 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(_.exten
if (item.id) {
self.display_value = {};
self.display_value["" + item.id] = item.name;
- self.set({value: item.id});
+ self.reinit_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;
}
},
@@ -2542,21 +3039,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")) {
@@ -2569,8 +3067,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);
});
@@ -2579,47 +3077,76 @@ 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.$('.oe_m2o_cm_button').css({'display':'inline'});
+ }
} 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,
res_id: self.get("value"),
- context: self.build_context(),
views: [[false, 'form']],
target: 'current'
});
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;
- this.inhibit_on_change = true;
- this._super(value_);
- this.inhibit_on_change = false;
+ this.reinit_value(value_);
+ },
+ get_displayed: function() {
+ return this.display_value["" + this.get("value")];
},
add_id: function(id) {
this.display_value = {};
- this.set({value: id});
+ this.reinit_value(id);
},
is_false: function() {
return ! this.get("value");
},
focus: function () {
- this.delay_focus(this.$input);
- }
-}));
+ if (!this.get('effective_readonly')) {
+ 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
@@ -2674,27 +3201,26 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
lazy_build_o2m_kanban_view();
this.is_loaded = $.Deferred();
this.initial_is_loaded = this.is_loaded;
- this.is_setted = $.Deferred();
this.form_last_update = $.Deferred();
this.init_form_last_update = this.form_last_update;
- },
- start: function() {
- this._super.apply(this, arguments);
-
- var self = this;
-
+ this.is_started = false;
this.dataset = new instance.web.form.One2ManyDataSet(this, this.field.relation);
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() {
+ var self = this;
+ this.dataset.on('dataset_changed', this, function() {
self.trigger_on_change();
});
+ this.set_value([]);
+ },
+ start: function() {
+ this._super.apply(this, arguments);
+ this.$el.addClass('oe_form_field oe_form_field_one2many');
- this.is_setted.then(function() {
- self.load_views();
- });
+ var self = this;
+
+ self.load_views();
this.is_loaded.then(function() {
self.on("change:effective_readonly", self, function() {
self.is_loaded = self.is_loaded.pipe(function() {
@@ -2705,6 +3231,8 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
});
});
});
+ this.is_started = true;
+ this.reload_current_view();
},
trigger_on_change: function() {
var tmp = this.doing_on_change;
@@ -2714,11 +3242,14 @@ 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)) {
+ throw new Error(_.str.sprintf("View type '%s' is not supported in One2Many.", mode));
+ }
var view = {
view_id: false,
view_type: mode == "tree" ? "list" : mode,
@@ -2728,20 +3259,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") {
+ _.extend(view.options, {
+ confirm_on_delete: false,
+ });
if (self.get("effective_readonly")) {
- view.options.action_buttons = false;
- view.options.quick_creatable = false;
+ _.extend(view.options, {
+ action_buttons: false,
+ quick_creatable: false,
+ creatable: false,
+ read_only_mode: true,
+ });
}
}
views.push(view);
@@ -2756,36 +3304,45 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
var def = $.Deferred().then(function() {
self.initial_is_loaded.resolve();
});
- this.viewmanager.on_controller_inited.add_last(function(view_type, controller) {
+ this.viewmanager.on("controller_inited", self, 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;
+ });
+ _(controller.columns).find(function (column) {
+ if (!(column instanceof instance.web.list.Handle)) {
+ return false;
+ }
+ column.modifiers.invisible = true;
+ return 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();
- });
- controller.on_pager_action.add_first(function() {
- self.save_any_view();
- });
+ controller.on("load_record", self, function(){
+ once.resolve();
+ });
+ controller.on('pager_action_executed',self,self.save_any_view);
} else if (view_type == "graph") {
self.reload_current_view()
}
def.resolve();
});
- this.viewmanager.on_mode_switch.add_first(function(n_mode, b, c, d, e) {
+ this.viewmanager.on("switch_mode", self, function(n_mode, b, c, d, e) {
$.when(self.save_any_view()).then(function() {
- if(n_mode === "list")
- $.async_when().then(function() {self.reload_current_view();});
+ if (n_mode === "list") {
+ $.async_when().then(function() {
+ self.reload_current_view();
+ });
+ }
});
});
- this.is_setted.then(function() {
- $.async_when().then(function () {
- self.viewmanager.appendTo(self.$element);
- });
+ $.async_when().then(function () {
+ self.viewmanager.appendTo(self.$el);
});
return def;
},
@@ -2865,8 +3422,12 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({
if (this.dataset.index === null && this.dataset.ids.length > 0) {
this.dataset.index = 0;
}
- self.is_setted.resolve();
- return self.reload_current_view();
+ this.trigger_on_change();
+ if (this.is_started) {
+ return self.reload_current_view();
+ } else {
+ return $.when();
+ }
},
get_value: function() {
var self = this;
@@ -2892,42 +3453,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.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;
},
@@ -2942,16 +3505,23 @@ 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) {
+ switch_mode: function(mode, unused) {
if (mode !== 'form') {
return this._super(mode, unused);
}
var self = this;
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);
+ var pop = new instance.web.form.FormOpenPopup(this);
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]));
+ self.o2m.dataset.trigger("dataset_changed", r);
+ });
+ },
write_function: function(id, data, options) {
return self.o2m.dataset.write(id, data, {}).then(function() {
self.o2m.reload_current_view();
@@ -2966,6 +3536,9 @@ instance.web.form.One2ManyViewManager = instance.web.ViewManager.extend({
form_view_options: {'not_interactible_on_create':true},
readonly: self.o2m.get("effective_readonly")
});
+ pop.on("elements_selected", self, function() {
+ self.o2m.reload_current_view();
+ });
},
});
@@ -2978,8 +3551,52 @@ 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 || {}, {
+ GroupsType: instance.web.form.One2ManyGroups,
+ ListType: instance.web.form.One2ManyList
+ }));
+ this.on('edit:before', this, this.proxy('_before_edit'));
+ this.on('edit:after', this, this.proxy('_after_edit'));
+ this.on('save:before cancel:before', this, this.proxy('_before_unedit'));
+
+ 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;
@@ -2987,13 +3604,13 @@ 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) {
return self.o2m.dataset.create(data).then(function(r) {
- self.o2m.dataset.set_ids(self.o2m.dataset.ids.concat([r.result]));
- self.o2m.dataset.on_change();
+ self.o2m.dataset.set_ids(self.o2m.dataset.ids.concat([r]));
+ self.o2m.dataset.trigger("dataset_changed", r);
}).then(callback, error_callback);
},
read_function: function() {
@@ -3006,18 +3623,18 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({
self.o2m.build_domain(),
self.o2m.build_context()
);
- pop.on_select_elements.add_last(function() {
+ pop.on("elements_selected", self, function() {
self.o2m.reload_current_view();
});
}
},
do_activate_record: function(index, id) {
var self = this;
- var pop = new instance.web.form.FormOpenPopup(self.o2m.view);
+ var pop = new instance.web.form.FormOpenPopup(self);
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) {
+ return self.o2m.dataset.write(id, data, {}).then(function() {
self.o2m.reload_current_view();
});
},
@@ -3028,23 +3645,167 @@ 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;
- var def = $.Deferred().then(callback).then(function() {self.o2m.view.reload();});
- return this._super(name, id, _.bind(def.resolve, def));
+ this.ensure_saved().pipe(function () {
+ if (parent_form)
+ return parent_form.save();
+ else
+ return $.when();
+ }).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.One2ManyGroups = instance.web.ListView.Groups.extend({
+ setup_resequence_rows: function () {
+ if (!this.view.o2m.get('effective_readonly')) {
+ this._super.apply(this, arguments);
+ }
+ }
+});
+instance.web.form.One2ManyList = instance.web.ListView.List.extend({
+ pad_table_to: function (count) {
+ if (!this.view.is_action_enabled('create')) {
+ this._super(count);
+ } else {
+ this._super(count > 0 ? count - 1 : 0);
+ }
+
+ // magical invocation of wtf does that do
+ if (this.view.o2m.get('effective_readonly')) {
+ return;
+ }
+
+ var self = this;
+ var columns = _(this.columns).filter(function (column) {
+ return column.invisible !== '1';
+ }).length;
+ if (this.options.selectable) { columns++; }
+ if (this.options.deletable) { columns++; }
+
+ if (!this.view.is_action_enabled('create')) {
+ return;
+ }
+
+ var $cell = $('<td>', {
+ colspan: columns,
+ 'class': 'oe_form_field_one2many_list_row_add'
+ }).append(
+ $('<a>', {href: '#'}).text(_t("Add an item"))
+ .mousedown(function () {
+ // FIXME: needs to be an official API somehow
+ if (self.view.editor.is_editing()) {
+ self.view.__ignore_blur = true;
+ }
+ })
+ .click(function (e) {
+ e.preventDefault();
+ e.stopPropagation();
+ // FIXME: there should also be an API for that one
+ if (self.view.editor.form.__blur_timeout) {
+ clearTimeout(self.view.editor.form.__blur_timeout);
+ self.view.editor.form.__blur_timeout = false;
+ }
+ self.view.ensure_saved().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)
+ }
}
});
instance.web.form.One2ManyFormView = instance.web.FormView.extend({
form_template: 'One2Many.formview',
- on_loaded: function(data) {
+ load_form: function(data) {
this._super(data);
var self = this;
this.$buttons.find('button.oe_form_button_create').click(function() {
- self.do_save().then(self.on_button_new);
+ self.save().then(self.on_button_new);
});
},
do_notify_change: function() {
@@ -3063,8 +3824,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);
@@ -3073,16 +3833,11 @@ instance.web.form.FieldMany2ManyTags = instance.web.form.AbstractField.extend(_.
this._display_orderer = new instance.web.DropMisordered();
this._drop_shown = false;
},
- start: function() {
- this._super();
- instance.web.form.ReinitializeFieldMixin.start.call(this);
- this.on("change:value", this, this.render_value);
- },
initialize_content: function() {
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: {
@@ -3134,29 +3889,16 @@ instance.web.form.FieldMany2ManyTags = instance.web.form.AbstractField.extend(_.
return _.extend(el, {index:i});
})});
});
- }).bind('tagClick', function(e, tag, value, callback) {
- var pop = new instance.web.form.FormOpenPopup(self.view);
- pop.show_element(
- self.field.relation,
- value.id,
- self.build_context(),
- {
- title: _t("Open: ") + (self.string || self.name)
- }
- );
- pop.on_write_completed.add_last(function() {
- self.render_value();
- });
}).bind('hideDropdown', function() {
self._drop_shown = false;
- }).bind('hideDropdown', function() {
+ }).bind('showDropdown', function() {
self._drop_shown = true;
});
self.tags = self.$text.textext()[0].tags();
- $("textarea", this.$element).focusout(function() {
- $("textarea", this.$element).val("");
+ $("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();
}
});
@@ -3172,36 +3914,31 @@ instance.web.form.FieldMany2ManyTags = instance.web.form.AbstractField.extend(_.
var tmp = [commands.replace_with(this.get("value"))];
return tmp;
},
+ get_search_blacklist: function() {
+ return this.get("value");
+ },
render_value: function() {
var self = this;
- var dataset = new instance.web.DataSetStatic(this, this.field.relation, self.view.dataset.get_context());
+ var dataset = new instance.web.DataSetStatic(this, this.field.relation, self.build_context());
+ var values = self.get("value")
var handle_names = function(data) {
+ if (self.isDestroyed())
+ return;
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}));
- $(".oe_form_field_many2manytags_box", self.$element).click(function() {
- var index = Number($(this).data("index"));
- self.do_action({
- type: 'ir.actions.act_window',
- res_model: self.field.relation,
- res_id: self.get("value")[index],
- context: self.build_context(),
- views: [[false, 'form']],
- target: 'current'
- });
- });
+ 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([]);
}
@@ -3209,11 +3946,14 @@ 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
- */
+/**
+ widget options:
+ - reload_on_button: Reload the whole form view if click on a button in a list view.
+ If you see this options, do not use it, it's basically a dirty hack to make one
+ precise o2m to behave the way we want.
+*/
instance.web.form.FieldMany2Many = instance.web.form.AbstractField.extend({
multi_selection: false,
disable_utility_classes: true,
@@ -3221,32 +3961,31 @@ instance.web.form.FieldMany2Many = instance.web.form.AbstractField.extend({
this._super(field_manager, node);
this.is_loaded = $.Deferred();
this.initial_is_loaded = this.is_loaded;
- this.is_setted = $.Deferred();
+ this.dataset = new instance.web.form.Many2ManyDataSet(this, this.field.relation);
+ this.dataset.m2m = this;
+ var self = this;
+ this.dataset.on('unlink', self, function(ids) {
+ self.dataset_changed();
+ });
+ this.set_value([]);
},
start: function() {
- this._super.apply(this, arguments);
+ this.$el.addClass('oe_form_field oe_form_field_many2many');
var self = this;
- this.dataset = new instance.web.form.Many2ManyDataSet(this, this.field.relation);
- this.dataset.m2m = this;
- this.dataset.on_unlink.add_last(function(ids) {
- self.dataset_changed();
- });
-
- this.is_setted.then(function() {
- self.load_view();
- });
+ self.load_view();
this.is_loaded.then(function() {
self.on("change:effective_readonly", self, function() {
self.is_loaded = self.is_loaded.pipe(function() {
self.list_view.destroy();
return $.when(self.load_view()).then(function() {
- self.reload_content();
+ self.render_value();
});
});
});
- })
+ });
+ this._super.apply(this, arguments);
},
set_value: function(value_) {
value_ = value_ || [];
@@ -3254,10 +3993,12 @@ instance.web.form.FieldMany2Many = instance.web.form.AbstractField.extend({
value_ = value_[0][2];
}
this._super(value_);
- this.dataset.set_ids(value_);
- var self = this;
- self.reload_content();
- this.is_setted.resolve();
+ },
+ get_value: function() {
+ return [commands.replace_with(this.get('value'))];
+ },
+ is_false: function () {
+ return _(this.get("value")).isEmpty();
},
load_view: function() {
var self = this;
@@ -3265,6 +4006,9 @@ instance.web.form.FieldMany2Many = instance.web.form.AbstractField.extend({
'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) {
@@ -3272,23 +4016,24 @@ instance.web.form.FieldMany2Many = instance.web.form.AbstractField.extend({
}
this.list_view.m2m_field = this;
var loaded = $.Deferred();
- this.list_view.on_loaded.add_last(function() {
+ this.list_view.on("list_view_loaded", self, function() {
self.initial_is_loaded.resolve();
loaded.resolve();
});
$.async_when().then(function () {
- self.list_view.appendTo(self.$element);
+ self.list_view.appendTo(self.$el);
});
return loaded;
},
- reload_content: function() {
+ render_value: function() {
var self = this;
+ this.dataset.set_ids(this.get("value"));
this.is_loaded = this.is_loaded.pipe(function() {
return self.list_view.reload_content();
});
},
dataset_changed: function() {
- this.set({'value': [commands.replace_with(this.dataset.ids)]});
+ this.internal_set_value(this.dataset.ids);
},
});
@@ -3309,39 +4054,257 @@ 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()
);
var self = this;
- pop.on_select_elements.add(function(element_ids) {
- _.each(element_ids, function(one_id) {
- if(! _.detect(self.dataset.ids, function(x) {return x == one_id;})) {
- self.dataset.set_ids([].concat(self.dataset.ids, [one_id]));
+ pop.on("elements_selected", self, function(element_ids) {
+ var reload = false;
+ _(element_ids).each(function (id) {
+ if(! _.detect(self.dataset.ids, function(x) {return x == id;})) {
+ self.dataset.set_ids(self.dataset.ids.concat([id]));
self.m2m_field.dataset_changed();
- self.reload_content();
+ reload = true;
}
});
+ if (reload) {
+ self.reload_content();
+ }
});
},
do_activate_record: function(index, id) {
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() {
- self.reload_content();
+ pop.on('write_completed', self, self.reload_content);
+ },
+ do_button_action: function(name, id, callback) {
+ var self = this;
+ var _sup = _.bind(this._super, this);
+ if (! this.m2m_field.options.reload_on_button) {
+ return _sup(name, id, callback);
+ } else {
+ return this.m2m_field.view.save().pipe(function() {
+ return _sup(name, id, function() {
+ self.m2m_field.view.reload();
+ });
+ });
+ }
+ },
+});
+
+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);
+ instance.web.form.CompletionFieldMixin.init.call(this);
+ m2m_kanban_lazy_init();
+ this.is_loaded = $.Deferred();
+ this.initial_is_loaded = this.is_loaded;
+
+ var self = this;
+ this.dataset = new instance.web.form.Many2ManyDataSet(this, this.field.relation);
+ this.dataset.m2m = this;
+ this.dataset.on('unlink', self, function(ids) {
+ self.dataset_changed();
});
- }
+ },
+ start: function() {
+ this._super.apply(this, arguments);
+
+ var self = this;
+
+ self.load_view();
+ this.is_loaded.then(function() {
+ self.on("change:effective_readonly", self, function() {
+ self.is_loaded = self.is_loaded.pipe(function() {
+ self.kanban_view.destroy();
+ return $.when(self.load_view()).then(function() {
+ self.render_value();
+ });
+ });
+ });
+ })
+ },
+ set_value: function(value_) {
+ value_ = value_ || [];
+ if (value_.length >= 1 && value_[0] instanceof Array) {
+ value_ = value_[0][2];
+ }
+ this._super(value_);
+ },
+ get_value: function() {
+ return [commands.replace_with(this.get('value'))];
+ },
+ load_view: function() {
+ var self = this;
+ this.kanban_view = new instance.web.form.Many2ManyKanbanView(this, this.dataset, false, {
+ 'create_text': _t("Add"),
+ 'creatable': self.get("effective_readonly") ? false : true,
+ 'quick_creatable': self.get("effective_readonly") ? false : true,
+ 'read_only_mode': self.get("effective_readonly") ? true : false,
+ 'confirm_on_delete': false,
+ });
+ var embedded = (this.field.views || {}).kanban;
+ if (embedded) {
+ this.kanban_view.set_embedded_view(embedded);
+ }
+ this.kanban_view.m2m = this;
+ var loaded = $.Deferred();
+ this.kanban_view.on("kanban_view_loaded",self,function() {
+ self.initial_is_loaded.resolve();
+ loaded.resolve();
+ });
+ this.kanban_view.on('switch_mode', this, this.open_popup);
+ $.async_when().then(function () {
+ self.kanban_view.appendTo(self.$el);
+ });
+ return loaded;
+ },
+ render_value: function() {
+ var self = this;
+ this.dataset.set_ids(this.get("value"));
+ this.is_loaded = this.is_loaded.pipe(function() {
+ return self.kanban_view.do_search(self.build_domain(), self.dataset.get_context(), []);
+ });
+ },
+ dataset_changed: function() {
+ this.set({'value': this.dataset.ids});
+ },
+ open_popup: function(type, unused) {
+ if (type !== "form")
+ return;
+ var self = this;
+ if (this.dataset.index === null) {
+ var pop = new instance.web.form.SelectCreatePopup(this);
+ pop.select_element(
+ this.field.relation,
+ {
+ title: _t("Add: ") + this.string
+ },
+ new instance.web.CompoundDomain(this.build_domain(), ["!", ["id", "in", this.dataset.ids]]),
+ this.build_context()
+ );
+ pop.on("elements_selected", self, function(element_ids) {
+ _.each(element_ids, function(one_id) {
+ if(! _.detect(self.dataset.ids, function(x) {return x == one_id;})) {
+ self.dataset.set_ids([].concat(self.dataset.ids, [one_id]));
+ self.dataset_changed();
+ self.render_value();
+ }
+ });
+ });
+ } else {
+ var id = self.dataset.ids[self.dataset.index];
+ var pop = new instance.web.form.FormOpenPopup(this);
+ pop.show_element(self.field.relation, id, self.build_context(), {
+ title: _t("Open: ") + self.string,
+ write_function: function(id, data, options) {
+ return self.dataset.write(id, data, {}).then(function() {
+ self.render_value();
+ });
+ },
+ alternative_form_view: self.field.views ? self.field.views["form"] : undefined,
+ parent_view: self.view,
+ child_name: self.name,
+ readonly: self.get("effective_readonly")
+ });
+ }
+ },
+ add_id: function(id) {
+ this.quick_create.add_id(id);
+ },
+});
+
+function m2m_kanban_lazy_init() {
+if (instance.web.form.Many2ManyKanbanView)
+ return;
+instance.web.form.Many2ManyKanbanView = instance.web_kanban.KanbanView.extend({
+ quick_create_class: 'instance.web.form.Many2ManyQuickCreate',
+ _is_quick_create_enabled: function() {
+ return this._super() && ! this.group_by;
+ },
+});
+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.
+ */
+ init: function(parent, dataset, context, buttons) {
+ this._super(parent);
+ this.m2m = this.getParent().view.m2m;
+ this.m2m.quick_create = this;
+ this._dataset = dataset;
+ this._buttons = buttons || false;
+ this._context = context || {};
+ },
+ start: function () {
+ var self = this;
+ self.$text = this.$el.find('input').css("width", "200px");
+ self.$text.textext({
+ plugins : 'arrow autocomplete',
+ autocomplete: {
+ render: function(suggestion) {
+ return $('<span class="text-label"/>').
+ data('index', suggestion['index']).html(suggestion['label']);
+ }
+ },
+ ext: {
+ autocomplete: {
+ selectFromDropdown: function() {
+ $(this).trigger('hideDropdown');
+ var index = Number(this.selectedSuggestionElement().children().children().data('index'));
+ var data = self.search_result[index];
+ if (data.id) {
+ self.add_id(data.id);
+ } else {
+ data.action();
+ }
+ },
+ },
+ itemManager: {
+ itemToString: function(item) {
+ return item.name;
+ },
+ },
+ },
+ }).bind('getSuggestions', function(e, data) {
+ var _this = this;
+ var str = !!data ? data.query || '' : '';
+ self.m2m.get_search_result(str).then(function(result) {
+ self.search_result = result;
+ $(_this).trigger('setSuggestions', {result : _.map(result, function(el, i) {
+ return _.extend(el, {index:i});
+ })});
+ });
+ });
+ self.$text.focusout(function() {
+ self.$text.val("");
+ });
+ },
+ focus: function() {
+ this.$text.focus();
+ },
+ add_id: function(id) {
+ var self = this;
+ self.$text.val("");
+ self.trigger('added', id);
+ this.m2m.dataset_changed();
+ },
});
+}
/**
* 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:
@@ -3371,12 +4334,15 @@ instance.web.form.AbstractFormPopup = instance.web.OldWidget.extend({
this.dataset.create_function = function(data, sup) {
var fct = self.options.create_function || sup;
return fct.call(this, data).then(function(r) {
- self.created_elements.push(r.result);
+ self.trigger('create_completed saved', r);
+ self.created_elements.push(r);
});
};
this.dataset.write_function = function(id, data, options, sup) {
var fct = self.options.write_function || sup;
- return fct.call(this, id, data, options).then(self.on_write_completed);
+ return fct.call(this, id, data, options).then(function() {
+ self.trigger('write_completed saved');
+ });
};
this.dataset.parent_view = this.options.parent_view;
this.dataset.child_name = this.options.child_name;
@@ -3384,17 +4350,17 @@ 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();
+ self.check_exit(true);
},
title: this.options.title || "",
- }, this.$element).open();
+ }, this.$el).open();
+ this.$buttonpane = dialog.$buttons;
this.start();
},
- on_write_completed: function() {},
setup_form_view: function() {
var self = this;
if (this.row_id) {
@@ -3407,53 +4373,57 @@ 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.on_loaded.add_last(function() {
- var $buttons = self.view_form.$element.find(".oe_form_buttons");
+ this.view_form.appendTo(this.$el.find(".oe_popup_form"));
+ this.view_form.on("form_view_loaded", self, function() {
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() {
+ $.when(self.view_form.save()).then(function() {
self.view_form.reload_mutex.exec(function() {
self.view_form.on_button_new();
});
});
});
- 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() {
+ $.when(self.view_form.save()).then(function() {
self.view_form.reload_mutex.exec(function() {
self.check_exit();
});
});
});
- 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();
});
},
- on_select_elements: function(element_ids) {
+ select_elements: function(element_ids) {
+ this.trigger("elements_selected", element_ids);
},
- check_exit: function() {
+ check_exit: function(no_destroy) {
if (this.created_elements.length > 0) {
- this.on_select_elements(this.created_elements);
+ this.select_elements(this.created_elements);
+ this.created_elements = [];
}
+ this.trigger('closed');
this.destroy();
},
destroy: function () {
- this.$element.dialog('close');
+ this.trigger('closed');
+ this.$el.dialog('close');
this._super();
},
});
@@ -3503,7 +4473,7 @@ instance.web.form.SelectCreatePopup = instance.web.form.AbstractFormPopup.extend
self.rpc('/web/session/eval_domain_and_context', {
domains: [],
contexts: [this.context]
- }, function (results) {
+ }).then(function (results) {
var search_defaults = {};
_.each(results.context, function (value_, key) {
var match = /^search_default_(.*)$/.exec(key);
@@ -3524,7 +4494,7 @@ instance.web.form.SelectCreatePopup = instance.web.form.AbstractFormPopup.extend
}
this.searchview = new instance.web.SearchView(this,
this.dataset, false, search_defaults);
- this.searchview.on_search.add(function(domains, contexts, groupbys) {
+ this.searchview.on('search_data', self, function(domains, contexts, groupbys) {
if (self.initial_ids) {
self.do_search(domains.concat([[["id", "in", self.initial_ids]], self.domain]),
contexts, groupbys);
@@ -3533,36 +4503,41 @@ instance.web.form.SelectCreatePopup = instance.web.form.AbstractFormPopup.extend
self.do_search(domains.concat([self.domain]), contexts.concat(self.context), groupbys);
}
});
- this.searchview.on_loaded.add_last(function () {
+ this.searchview.on("search_view_loaded", self, function() {
self.view_list = new instance.web.form.SelectCreateListView(self,
self.dataset, false,
_.extend({'deletable': false,
- 'selectable': !self.options.disable_multiple_selection
+ 'selectable': !self.options.disable_multiple_selection,
+ '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.view_list.on("list_view_loaded", self, function() {
+ 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.select_elements(self.selected_ids);
self.destroy();
});
+ var $cbutton = self.$buttonpane.find(".oe_selectcreatepopup-search-create");
+ $cbutton.click(function() {
+ self.new_object();
+ });
});
});
- this.searchview.appendTo($(".oe-select-create-popup-view-list", self.$element));
+ this.searchview.appendTo($(".oe_popup_search", self.$el));
},
do_search: function(domains, contexts, groupbys) {
var self = this;
@@ -3570,16 +4545,17 @@ instance.web.form.SelectCreatePopup = instance.web.form.AbstractFormPopup.extend
domains: domains || [],
contexts: contexts || [],
group_by_seq: groupbys || []
- }, function (results) {
+ }).then(function (results) {
self.view_list.do_search(results.domain, results.context, results.group_by);
});
},
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() {
@@ -3587,7 +4563,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();
},
@@ -3598,7 +4574,7 @@ instance.web.form.SelectCreateListView = instance.web.ListView.extend({
this.popup.new_object();
},
select_record: function(index) {
- this.popup.on_select_elements([this.dataset.ids[index]]);
+ this.popup.select_elements([this.dataset.ids[index]]);
this.popup.destroy();
},
do_select: function(ids, records) {
@@ -3607,110 +4583,110 @@ 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);
this.reference_ready = true;
},
- on_nop: function() {
- },
- on_selection_changed: function() {
- if (this.reference_ready) {
- var sel = this.selection.get_value();
- this.m2o.field.relation = sel;
- this.m2o.set_value(false);
- this.m2o.$element.toggle(sel !== false);
- }
- },
destroy_content: function() {
- if (this.selection) {
- this.selection.destroy();
- this.selection = undefined;
- }
- if (this.m2o) {
- this.m2o.destroy();
- this.m2o = undefined;
+ if (this.fm) {
+ this.fm.destroy();
+ this.fm = undefined;
}
},
initialize_content: function() {
- this.selection = new instance.web.form.FieldSelection(this, { attrs: {
- name: 'selection'
+ var self = this;
+ var fm = new instance.web.form.DefaultFieldManager(this);
+ this.fm = fm;
+ fm.extend_field_desc({
+ "selection": {
+ selection: this.field_manager.get_field_desc(this.name).selection,
+ type: "selection",
+ },
+ "m2o": {
+ relation: null,
+ type: "many2one",
+ },
+ });
+ this.selection = new instance.web.form.FieldSelection(fm, { attrs: {
+ 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.renderElement();
- this.selection.start();
+ this.selection.appendTo(this.$(".oe_form_view_reference_selection"));
+ 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'
+ this.m2o = new instance.web.form.FieldMany2One(fm, { attrs: {
+ 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.renderElement();
- this.m2o.start();
+ this.m2o.appendTo(this.$(".oe_form_view_reference_m2o"));
+ 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';
+ on_selection_changed: function() {
+ if (this.reference_ready) {
+ this.internal_set_value([this.selection.get_value(), false]);
+ this.render_value();
+ }
},
- set_value: function(value_) {
- this._super(value_);
- this.render_value();
+ data_changed: function() {
+ if (this.reference_ready) {
+ this.internal_set_value([this.selection.get_value(), this.m2o.get_value()]);
+ }
+ },
+ set_value: function(val) {
+ if (val) {
+ val = val.split(',');
+ val[0] = val[0] || false;
+ val[1] = val[0] ? (val[1] ? parseInt(val[1], 10) : val[1]) : false;
+ }
+ this._super(val || [false, false]);
+ },
+ get_value: function() {
+ return this.get('value')[0] && this.get('value')[1] ? (this.get('value')[0] + ',' + this.get('value')[1]) : false;
},
render_value: function() {
this.reference_ready = false;
- var vals = [], sel_val, m2o_val;
- if (typeof(this.get('value')) === 'string') {
- vals = this.get('value').split(',');
- }
- sel_val = vals[0] || false;
- m2o_val = vals[1] ? parseInt(vals[1], 10) : vals[1];
if (!this.get("effective_readonly")) {
- this.selection.set_value(sel_val);
+ this.selection.set_value(this.get('value')[0]);
}
- this.m2o.field.relation = sel_val;
- this.m2o.set_value(m2o_val);
+ this.m2o.field.relation = this.get('value')[0];
+ this.m2o.set_value(this.get('value')[1]);
+ this.m2o.$el.toggle(!!this.get('value')[0]);
this.reference_ready = true;
},
- data_changed: function() {
- var model = this.selection.get_value(),
- id = this.m2o.get_value();
- if (typeof(model) === 'string' && typeof(id) === 'number') {
- this.set({'value': model + ',' + id});
- } else {
- this.set({'value': false});
- }
- },
- get_field: function(name) {
- if (name === "selection") {
- return {
- selection: this.view.fields_view.fields[this.name].selection,
- type: "selection",
- };
- } else if (name === "m2o") {
- return {
- relation: null,
- type: "many2one",
- };
- }
- 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.useFileAPI = !!window.FileReader;
+ if (!this.useFileAPI) {
+ 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() {
+ if (!this.useFileAPI) {
+ $(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'];
@@ -3722,54 +4698,84 @@ instance.web.form.FieldBinary = instance.web.form.AbstractField.extend(_.extend(
return size.toFixed(2) + ' ' + units[i];
},
on_file_change: function(e) {
- // 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();
+ var self = this;
+ var file_node = e.target;
+ if ((this.useFileAPI && file_node.files.length) || (!this.useFileAPI && $(file_node).val() !== '')) {
+ if (this.useFileAPI) {
+ var file = file_node.files[0];
+ var filereader = new FileReader();
+ filereader.readAsDataURL(file);
+ filereader.onloadend = function(upload) {
+ var data = upload.target.result;
+ data = data.split(',')[1];
+ self.on_file_uploaded(file.size, file.name, file.type, data);
+ };
+ } else {
+ 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 (filename) {
+ var tmp = {};
+ tmp[filename] = value;
+ this.field_manager.set_values(tmp);
+ }
},
on_clear: function() {
if (this.get('value') !== false) {
this.binary_value = false;
- this.set({'value': false});
+ this.internal_set_value(false);
}
return false;
}
-}));
+});
instance.web.form.FieldBinaryFile = instance.web.form.FieldBinary.extend({
template: 'FieldBinaryFile',
@@ -3777,18 +4783,14 @@ 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(ev) {
if (self.get('value')) {
- self.on_save_as();
+ self.on_save_as(ev);
}
return false;
});
}
},
- set_value: function(value_) {
- this._super.apply(this, arguments);
- this.render_value();
- },
render_value: function() {
if (!this.get("effective_readonly")) {
var show_value;
@@ -3797,146 +4799,334 @@ 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);
+ var show_value = _t("Download")
+ if (this.view)
+ show_value += " " + (this.view.datarecord[this.node.attrs.filename] || '');
+ this.$el.find('a').text(show_value);
}
}
},
on_file_uploaded_and_valid: function(size, name, content_type, file_base64) {
this.binary_value = true;
- this.set({'value': file_base64});
+ this.internal_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_) {
- var filename = this.node.attrs.filename;
- if (this.view.fields[filename]) {
- this.view.fields[filename].set({value: value_});
- }
- },
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();
- this.$placeholder = $(".oe_form_field-binary-image-placeholder", this.$element);
- if (!this.get("effective_readonly"))
- this.$element.find('.oe-binary').show();
- else
- this.$element.find('.oe-binary').hide();
- },
- set_value: function(value_) {
- this._super.apply(this, arguments);
- this.render_value();
- },
+ placeholder: "/web/static/src/img/placeholder.png",
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";
+ url = this.placeholder;
}
- var rendered = QWeb.render("FieldBinaryImage-img", {widget: this, url: url});;
- this.$placeholder.html(rendered);
+ 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");
+ });
+ $img.on('error', function() {
+ $img.attr('src', self.placeholder);
+ instance.webclient.notification.warn(_t("Image"), _t("Could not display the selected image."));
+ });
},
on_file_change: function() {
this.render_value();
this._super.apply(this, arguments);
},
on_file_uploaded_and_valid: function(size, name, content_type, file_base64) {
- this.set({'value': file_base64});
+ this.internal_set_value(file_base64);
this.binary_value = true;
this.render_value();
+ this.set_filename(name);
},
on_clear: function() {
this._super.apply(this, arguments);
this.render_value();
+ this.set_filename('');
}
});
+/**
+ * Widget for (one2many field) to upload one or more file in same time and display in list.
+ * The user can delete his files.
+ * Options on attribute ; "blockui" {Boolean} block the UI or not
+ * during the file is uploading
+ */
+instance.web.form.FieldOne2ManyBinaryMultiFiles = instance.web.form.AbstractField.extend({
+ template: "FieldBinaryFileUploader",
+ init: function(field_manager, node) {
+ this._super(field_manager, node);
+ this.field_manager = field_manager;
+ this.node = node;
+ if(this.field.type != "one2many" || this.field.relation != 'ir.attachment') {
+ throw "The type of the field '"+this.field.string+"' must be a one2many field with a relation to 'ir.attachment' model.";
+ }
+ this.ds_file = new instance.web.DataSetSearch(this, 'ir.attachment');
+ this.fileupload_id = _.uniqueId('oe_fileupload_temp');
+ $(window).on(this.fileupload_id, _.bind(this.on_file_loaded, this));
+ },
+ start: function() {
+ this._super(this);
+ this.$el.on('change', 'input.oe_form_binary_file', this.on_file_change );
+ },
+ get_value: function() {
+ return _.map(this.get('value'), function (value) { return commands.link_to( value.id ); });
+ },
+ get_file_url: function (attachment) {
+ return instance.origin + '/web/binary/saveas?session_id=' + this.session.session_id + '&model=ir.attachment&field=datas&filename_field=datas_fname&id=' + attachment['id'];
+ },
+ render_value: function () {
+ var render = $(instance.web.qweb.render('FieldBinaryFileUploader.files', {'widget': this}));
+ render.on('click', '.oe_delete', _.bind(this.on_file_delete, this));
+ this.$('.oe_placeholder_files, .oe_attachments').replaceWith( render );
+
+ // reinit input type file
+ var $input = this.$('input.oe_form_binary_file');
+ $input.after($input.clone(true)).remove();
+ this.$(".oe_fileupload").show();
+ },
+ on_file_change: function (event) {
+ event.stopPropagation();
+ var self = this;
+ var $target = $(event.target);
+ if ($target.val() !== '') {
+
+ var filename = $target.val().replace(/.*[\\\/]/,'');
+
+ // if the files is currently uploded, don't send again
+ if( !isNaN(_.find(this.get('value'), function (file) { return (file.filename || file.name) == filename && file.upload; } )) ) {
+ return false;
+ }
+
+ // block UI or not
+ if(this.node.attrs.blockui) {
+ instance.web.blockUI();
+ }
+
+ // if the files exits for this answer, delete the file before upload
+ var files = _.filter(this.get('value'), function (file) {
+ if((file.filename || file.name) == filename) {
+ self.ds_file.unlink([file.id]);
+ return false;
+ } else {
+ return true;
+ }
+ });
+
+ // TODO : unactivate send on wizard and form
+
+ // submit file
+ this.$('form.oe_form_binary_form').submit();
+ this.$(".oe_fileupload").hide();
+
+ // add file on result
+ files.push({
+ 'id': 0,
+ 'name': filename,
+ 'filename': filename,
+ 'url': '',
+ 'upload': true
+ });
+
+ this.set({'value': files});
+ }
+ },
+ on_file_loaded: function (event, result) {
+ // unblock UI
+ if(this.node.attrs.blockui) {
+ instance.web.unblockUI();
+ }
+
+ // TODO : activate send on wizard and form
+
+ var files = this.get('value');
+ for(var i in files){
+ if(files[i].filename == result.filename && files[i].upload) {
+ files[i] = {
+ 'id': result.id,
+ 'name': result.name,
+ 'filename': result.filename,
+ 'url': this.get_file_url(result)
+ };
+ }
+ }
+
+ this.set({'value': files});
+ this.render_value()
+ },
+ on_file_delete: function (event) {
+ event.stopPropagation();
+ var file_id=$(event.target).data("id");
+ if (file_id) {
+ var files=[];
+ for(var i in this.get('value')){
+ if(file_id != this.get('value')[i].id){
+ files.push(this.get('value')[i]);
+ }
+ else {
+ this.ds_file.unlink([file_id]);
+ }
+ }
+ this.set({'value': files});
+ }
+ },
+});
+
instance.web.form.FieldStatus = instance.web.form.AbstractField.extend({
- template: "EmptyComponent",
+ 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.set({value: false});
+ },
start: function() {
+ if (this.options.clickable) {
+ this.$el.on('click','li',this.on_click_stage);
+ }
+ if (this.$el.parent().is('header')) {
+ this.$el.after('<div class="oe_clear"/>');
+ }
this._super();
- this.selected_value = null;
-
- this.render_list();
},
set_value: function(value_) {
+ if (value_ instanceof Array) {
+ value_ = value_[0];
+ }
this._super(value_);
- this.selected_value = value_;
-
- this.render_list();
},
- render_list: function() {
+ render_value: 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.field.selection;
+ self.get_selection().then(function() {
+ var content = QWeb.render("FieldStatus.content", {widget: self});
+ self.$el.html(content);
+ var colors = JSON.parse((self.node.attrs || {}).statusbar_colors || "{}");
+ var color = colors[self.get('value')];
+ if (color) {
+ self.$("oe_active").css("color", color);
+ }
+ });
+ },
+ /** 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
+ */
+ get_selection: function() {
+ var self = this;
+ self.selection = [];
+ if (this.field.type == "many2one") {
+ var domain = [];
+ if(!_.isEmpty(this.field.domain) || !_.isEmpty(this.node.attrs.domain)) {
+ domain = new instance.web.CompoundDomain(['|'], self.build_domain(), [['id', '=', self.get('value')]]);
+ }
+ var ds = new instance.web.DataSetSearch(this, this.field.relation, self.build_context(), domain);
+ return ds.read_slice(['name'], {}).pipe(function (records) {
+ for(var i = 0; i < records.length; i++) {
+ self.selection.push([records[i].id, records[i].name]);
+ }
+ });
} else {
- this.to_show = _.select(this.field.selection, function(x) {
- return _.indexOf(shown, x[0]) !== -1 || x[0] === self.selected_value;
+ // 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.get('value') || !this.options.visible || this.options.visible.indexOf(key) != -1) {
+ this.selection.push(selection[i]);
+ }
+ }
+ return $.when();
+ }
+ },
+ on_click_stage: function (ev) {
+ var self = this;
+ var $li = $(ev.currentTarget);
+ var val = parseInt($li.data("id"));
+ if (val != self.get('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();
+ });
});
}
+ },
+});
- var content = instance.web.qweb.render("FieldStatus.content", {widget: this, _:_});
- this.$element.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-arrow-list-selected span");
- elem.css("border-color", color);
- if (this.check_white(color))
- elem.css("color", "white");
- elem = this.$element.find("li.oe-arrow-list-selected .oe-arrow-list-before");
- elem.css("border-left-color", "rgba(0,0,0,0)");
- elem = this.$element.find("li.oe-arrow-list-selected .oe-arrow-list-after");
- elem.css("border-color", "rgba(0,0,0,0)");
- elem.css("border-left-color", color);
- }
- },
- check_white: function(color) {
- var div = $("<div></div>");
- div.css("display", "none");
- div.css("color", color);
- div.appendTo($("body"));
- var ncolor = div.css("color");
- div.remove();
- var res = /^\s*rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)\s*$/.exec(ncolor);
- if (!res) {
- return false;
+instance.web.form.FieldMonetary = instance.web.form.FieldFloat.extend({
+ template: "FieldMonetary",
+ init: function() {
+ this._super.apply(this, arguments);
+ this.set({"currency": false});
+ if (this.options.currency_field) {
+ this.field_manager.on("field_changed:" + this.options.currency_field, this, function() {
+ this.set({"currency": this.field_manager.get_field_value(this.options.currency_field)});
+ });
}
- var comps = [parseInt(res[1]), parseInt(res[2]), parseInt(res[3])];
- var lum = comps[0] * 0.3 + comps[1] * 0.59 + comps[1] * 0.11;
- if (lum < 128) {
- return true;
+ this.on("change:currency", this, this.get_currency_info);
+ this.get_currency_info();
+ this.ci_dm = new instance.web.DropMisordered();
+ },
+ start: function() {
+ var tmp = this._super();
+ this.on("change:currency_info", this, this.reinitialize);
+ return tmp;
+ },
+ get_currency_info: function() {
+ var self = this;
+ if (this.get("currency") === false) {
+ this.set({"currency_info": null});
+ return;
}
- return false;
- }
+ return this.ci_dm.add(new instance.web.Model("res.currency").query(["symbol", "position"])
+ .filter([["id", "=", self.get("currency")]]).first()).pipe(function(res) {
+ self.set({"currency_info": res});
+ });
+ },
+ parse_value: function(val, def) {
+ return instance.web.parse_value(val, {type: "float"}, def);
+ },
+ format_value: function(val, def) {
+ return instance.web.format_value(val, {type: "float"}, def);
+ },
});
/**
* 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({
@@ -3945,12 +5135,14 @@ 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',
'many2one' : 'instance.web.form.FieldMany2One',
'many2many' : 'instance.web.form.FieldMany2Many',
- 'many2manytags' : 'instance.web.form.FieldMany2ManyTags',
+ 'many2many_tags' : 'instance.web.form.FieldMany2ManyTags',
+ 'many2many_kanban' : 'instance.web.form.FieldMany2ManyKanban',
'one2many' : 'instance.web.form.FieldOne2Many',
'one2many_list' : 'instance.web.form.FieldOne2Many',
'reference' : 'instance.web.form.FieldReference',
@@ -3961,7 +5153,9 @@ instance.web.form.widgets = new instance.web.Registry({
'progressbar': 'instance.web.form.FieldProgressBar',
'image': 'instance.web.form.FieldBinaryImage',
'binary': 'instance.web.form.FieldBinaryFile',
- 'statusbar': 'instance.web.form.FieldStatus'
+ 'one2many_binary': 'instance.web.form.FieldOne2ManyBinaryMultiFiles',
+ 'statusbar': 'instance.web.form.FieldStatus',
+ 'monetary': 'instance.web.form.FieldMonetary',
});
/**
@@ -3974,6 +5168,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 67c8ce0239e..ab50ae933d9 100644
--- a/addons/web/static/src/js/view_list.js
+++ b/addons/web/static/src/js/view_list.js
@@ -22,6 +22,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
'reorderable': true,
'action_buttons': true,
},
+ view_type: 'tree',
/**
* Core class for list-type displays.
*
@@ -61,12 +62,12 @@ 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);
+ this.groups.datagroup = new StaticDataGroup(this.dataset);
} else {
- this.groups.datagroup = new instance.web.DataGroup(
+ this.groups.datagroup = new DataGroup(
this, this.model,
dataset.get_domain(),
dataset.get_context());
@@ -83,7 +84,17 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
});
this.no_leaf = false;
+ this.grouped = false;
+ this.on('view_loaded', self, self.load_list);
+ },
+ 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)
*
@@ -129,14 +140,14 @@ 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');
- return this.reload_view(null, null, true);
+ this.$el.addClass('oe_list');
+ return this._super();
},
/**
* Returns the style for the provided record in the current view (from the
@@ -210,7 +221,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
* @param {Object} data.fields_view.arch current list view descriptor
* @param {Boolean} grouped Is the list view grouped
*/
- on_loaded: function(data, grouped) {
+ load_list: function(data) {
var self = this;
this.fields_view = data;
this.name = "" + this.fields_view.arch.attrs.string;
@@ -236,21 +247,23 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
}).value();
}
- this.setup_columns(this.fields_view.fields, grouped);
+ this.setup_columns(this.fields_view.fields, this.grouped);
+
+ this.$el.html(QWeb.render(this._template, this));
+ this.$el.addClass(this.fields_view.arch.attrs['class']);
- this.$element.html(QWeb.render(this._template, this));
// 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'));
@@ -259,26 +272,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', this.grouped);
}
// Pager
@@ -287,7 +296,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
@@ -311,7 +320,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);
@@ -329,6 +338,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');
});
@@ -338,11 +349,14 @@ 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('Delete'), callback: this.do_delete_selected },
- ]);
+ this.sidebar.add_items('other', _.compact([
+ { label: _t("Export"), callback: this.on_sidebar_export },
+ 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();
}
+ this.trigger('list_view_loaded', data, this.grouped);
},
/**
* Configures the ListView pager based on the provided dataset's information
@@ -359,17 +373,23 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
}
var total = dataset.size();
+ var limit = this.limit() || total;
+ if (total == 0)
+ this.$pager.hide();
+ else
+ this.$pager.css("display", "");
+ 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
@@ -380,68 +400,23 @@ 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;
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;
+ 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';
});
- 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
@@ -467,9 +442,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();
}
@@ -479,7 +451,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();
@@ -492,25 +464,12 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
/**
* Reloads the list view based on the current settings (dataset & al)
*
+ * @deprecated
* @param {Boolean} [grouped] Should the list be displayed grouped
* @param {Object} [context] context to send the server while loading the view
*/
reload_view: function (grouped, context, initial) {
- var self = this;
- var callback = function (field_view_get) {
- self.on_loaded(field_view_get, grouped);
- };
- if (this.embedded_view) {
- return $.Deferred().then(callback).resolve(this.embedded_view);
- } else {
- return this.rpc('/web/listview/load', {
- model: this.model,
- view_id: this.view_id,
- view_type: "tree",
- context: this.dataset.get_context(context),
- toolbar: !!this.options.$sidebar
- }, callback);
- }
+ return this.load_view(context);
},
/**
* re-renders the content of the list view
@@ -519,10 +478,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;
@@ -543,6 +502,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) {
@@ -570,7 +543,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
*/
do_search: function (domain, context, group_by) {
this.page = 0;
- this.groups.datagroup = new instance.web.DataGroup(
+ this.groups.datagroup = new DataGroup(
this, this.model, domain, context, group_by);
this.groups.datagroup.sort = this.dataset._sort;
@@ -578,8 +551,9 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi
group_by = null;
}
this.no_leaf = !!context['group_by_no_leaf'];
+ this.grouped = !!group_by;
- this.reload_view(!!group_by, context).then(
+ return this.load_view(context).pipe(
this.proxy('reload_content'));
},
/**
@@ -610,7 +584,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;
@@ -618,7 +592,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) {
@@ -633,6 +607,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;
});
@@ -651,7 +642,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)
@@ -760,16 +752,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() {
@@ -788,7 +778,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) {
@@ -797,10 +787,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 {
@@ -811,27 +801,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# */{
@@ -877,52 +869,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'),
@@ -933,19 +936,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);
}
@@ -967,13 +970,13 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.
// set) a human-readable version. m2o does not have this issue
// because the non-human-readable is just a number, where the
// human-readable version is a pair
- if (value && (ref_match = /([\w\.]+),(\d+)/.exec(value))) {
+ if (value && (ref_match = /^([\w\.]+),(\d+)$/.exec(value))) {
// reference values are in the shape "$model,$id" (as a
// string), we need to split and name_get this pair in order
// 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]);
});
@@ -989,29 +992,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 ||
@@ -1020,20 +1040,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>');
@@ -1042,19 +1062,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'));
@@ -1081,8 +1100,6 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web.
}, this);
if (!this.$current) { return; }
this.$current.remove();
- this.$current = null;
- this.$_element.remove();
},
get_records: function () {
return this.records.map(function (record) {
@@ -1096,17 +1113,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
@@ -1115,6 +1122,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,
@@ -1123,26 +1131,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); }
});
}
});
@@ -1150,7 +1139,7 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
passtrough_events: 'action deleted row_link',
/**
* Grouped display for the ListView. Handles basic DOM events and interacts
- * with the :js:class:`~instance.web.DataGroup` bound to it.
+ * with the :js:class:`~DataGroup` bound to it.
*
* Provides events similar to those of
* :js:class:`~instance.web.ListView.List`
@@ -1230,8 +1219,9 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
.replaceWith(self.render());
});
this.$row.children().last()
+ .addClass('oe_list_group_pagination')
.append($prev)
- .append('<span class="oe-pager-state"></span>')
+ .append('<span class="oe_list_pager_state"></span>')
.append($next);
},
open: function (point_insertion) {
@@ -1269,7 +1259,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
@@ -1277,7 +1267,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')) {
@@ -1297,22 +1287,32 @@ 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 = _.str.escapeHTML(row_data[group_column.id].value);
}
+ // group_label is html-clean (through format or explicit
+ // escaping if format failed), can inject straight into HTML
+ $group_column.html(_.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;">');
@@ -1324,30 +1324,27 @@ 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
} else if (column.id in group.aggregates) {
var r = {};
r[column.id] = {value: group.aggregates[column.id]};
- $('<td class="oe-number">')
- .html(instance.web.format_cell(
- r, column, {process_modifiers: false}))
+ $('<td class="oe_number">')
+ .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;
@@ -1372,7 +1369,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,
@@ -1398,21 +1395,23 @@ 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
}))
.end()
.find('button[data-pager-action=previous]')
- .attr('disabled', page === 0)
+ .css('visibility',
+ page === 0 ? 'hidden' : '')
.end()
.find('button[data-pager-action=next]')
- .attr('disabled', page === pages - 1);
+ .css('visibility',
+ page === pages - 1 ? 'hidden' : '');
}
}
@@ -1427,18 +1426,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'),
@@ -1456,7 +1468,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
@@ -1466,39 +1478,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
@@ -1537,6 +1549,60 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we
}
});
+var DataGroup = instance.web.CallbackEnabled.extend({
+ init: function(parent, model, domain, context, group_by, level) {
+ this._super(parent, null);
+ this.model = new instance.web.Model(model, context, domain);
+ this.group_by = group_by;
+ this.context = context;
+ this.domain = domain;
+
+ this.level = level || 0;
+ },
+ list: function (fields, ifGroups, ifRecords) {
+ var self = this;
+ 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;
+ }
+ // internal node
+ var child_datagroups = _(querygroups).map(function (group) {
+ var child_context = _.extend(
+ {}, self.model.context(), group.model.context());
+ var child_dg = new 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);
+ });
+ }
+});
+var StaticDataGroup = DataGroup.extend({
+ init: function (dataset) {
+ this.dataset = dataset;
+ },
+ list: function (fields, ifGroups, ifRecords) {
+ ifRecords(this.dataset);
+ }
+});
+
/**
* @mixin Events
*/
@@ -1802,7 +1868,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);
@@ -1818,13 +1884,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)) {
@@ -1849,6 +1944,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);
@@ -1856,6 +1991,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 c918012e60d..021e5549c18 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,26 +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 = (
- (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);
},
/**
@@ -69,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) {
+ load_list: function (data, grouped) {
+ var self = this;
// tree/@editable takes priority on everything else if present.
- this.options.editable = 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(name, id, callback);
+ });
},
/**
* Ensures the editable list is saved (saves any pending edition if
@@ -91,337 +162,639 @@ 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'));
+
+ return this.ensure_saved().pipe(function () {
+ var $recordRow = self.groups.get_row_for(record);
+ var cells = self.get_cells_for($recordRow);
+ 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) {
+ 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();
+
+ field.set_dimensions($cell.outerHeight(), $cell.outerWidth());
+ field.$el.position({
+ my: 'left top',
+ at: 'left top',
+ of: $cell
});
- 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;
+ _.each(this.editor.form.fields, function(field, field_name) {
+ var setting = false;
+ var set_invisible = function() {
+ if (!setting && field.get("effective_readonly")) {
+ setting = true;
+ field.set({invisible: true});
+ setting = false;
+ }
+ };
+ field.on("change:effective_readonly", self, set_invisible);
+ field.on("change:invisible", self, set_invisible);
+ set_invisible();
+ });
+
+ this.editor.$el.on('keyup keydown', function (e) {
+ if (!self.editor.is_editing()) { return true; }
+ 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 true; }
+ var method = e.type + '_' + key.name;
+ if (!(method in self)) { return true; }
+ return self[method](e);
});
},
/**
- * Saves the current row, and returns a Deferred resolving to an object
- * with the following properties:
- *
- * ``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.
+ * Saves the current record, and goes to the next one (creation or
+ * edition)
*
- * @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();
+ },
+ keydown_ESCAPE: function (e) {
+ return false;
+ },
+ keyup_ESCAPE: function (e) {
+ 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;
}
- //noinspection JSPotentiallyInvalidConstructorUsage
- return $.Deferred().resolve().promise();
+ 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) {
+
+ }
+ }
+ // 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.trigger('load_record', _.extend({}, record))
+ : form.load_defaults();
+ return $.when(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;
});
- $("<td><button class='oe-edit-row-save' type='button'>Save</button></td>").appendTo($element);
},
+ save: function () {
+ var self = this;
+ return this.form
+ .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');
+ return 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..05760420ba8 100644
--- a/addons/web/static/src/js/view_tree.js
+++ b/addons/web/static/src/js/view_tree.js
@@ -45,7 +45,7 @@ instance.web.TreeView = instance.web.View.extend(/** @lends instance.web.TreeVie
view_type: "tree",
toolbar: this.view_manager ? !!this.view_manager.sidebar : false,
context: this.dataset.get_context()
- }, this.on_loaded);
+ }).then(this.on_loaded);
},
/**
* Returns the list of fields needed to correctly read objects.
@@ -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 6bf7e273eab..a4fe86c516d 100644
--- a/addons/web/static/src/js/views.js
+++ b/addons/web/static/src/js/views.js
@@ -10,51 +10,208 @@ 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.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();
}
- if (this.client_widget) {
- this.client_widget.destroy();
- this.client_widget = null;
+ 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) {
+ var next_item = this.breadcrumbs[index + 1];
+ if (next_item && next_item.on_reverse_breadcrumb) {
+ next_item.on_reverse_breadcrumb(this.breadcrumbs[index].widget);
+ }
+ 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;
+ this.inner_action = item.action;
+ 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.inner_action = item.action;
+ this.breadcrumbs.splice(index, 0, item);
+ return false;
+ } else {
+ item.destroy();
+ }
+ }
+ }
+ var last_widget = this.breadcrumbs.slice(-1)[0];
+ if (last_widget) {
+ this.inner_widget = last_widget.widget;
+ this.inner_action = last_widget.action;
+ }
+ },
+ 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;
+ if (this.inner_action._push_me === false) {
+ // this action has been explicitly marked as not pushable
+ return;
+ }
+ 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.menu_id) {
+ state['menu_id'] = this.inner_action.menu_id;
+ }
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;
+ var params = {};
+ _.each(this.inner_action.params, function(v, k) {
+ if(_.isString(v) || _.isNumber(v)) {
+ params[k] = v;
+ }
+ });
+ state = _.extend(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 +225,43 @@ 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, options) {
+ options = _.defaults(options || {}, {
+ clear_breadcrumbs: false,
+ on_reverse_breadcrumb: function() {},
+ on_close: function() {},
+ action_menu_id: null,
+ });
+ if (_.isString(action) && instance.web.client_actions.contains(action)) {
+ var action_client = { type: "ir.actions.client", tag: action, params: {} };
+ return this.do_action(action_client, options);
+ } 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, options);
});
}
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,
@@ -108,74 +270,122 @@ instance.web.ActionManager = instance.web.Widget.extend({
pager : !popup && !inline,
display_title : !popup
}, action.flags || {});
+ action.menu_id = options.action_menu_id;
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, options);
},
null_action: function() {
this.dialog_stop();
- this.content_stop();
+ this.clear_breadcrumbs();
},
- 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);
- };
+ /**
+ *
+ * @param {Object} executor
+ * @param {Object} executor.action original action
+ * @param {Function<instance.web.Widget>} executor.widget function used to fetch the widget instance
+ * @param {String} executor.klass CSS class to add on the dialog root, if action.target=new
+ * @param {Function<instance.web.Widget, undefined>} executor.post_process cleanup called after a widget has been added as inner_widget
+ * @param {Object} options
+ * @return {*}
+ */
+ ir_actions_common: function(executor, options) {
+ if (this.inner_widget && executor.action.target !== 'new') {
+ if (this.getParent().has_uncommitted_changes()) {
+ return $.Deferred().reject();
+ } else if (options.clear_breadcrumbs) {
+ this.clear_breadcrumbs();
+ }
}
- if (action.target === 'new') {
- if (this.dialog == null) {
- this.dialog = new instance.web.Dialog(this, { width: '80%' });
- if(on_close)
- this.dialog.on_close.add(on_close);
- } else {
- this.dialog_viewmanager.destroy();
+ var widget = executor.widget();
+ if (executor.action.target === 'new') {
+ if (this.dialog_widget && ! this.dialog_widget.isDestroyed())
+ this.dialog_widget.destroy();
+ if (this.dialog === null || this.dialog.isDestroyed()) {
+ this.dialog = new instance.web.Dialog(this, {
+ dialogClass: executor.klass,
+ });
+ this.dialog.on("closing", null, options.on_close);
+ this.dialog.init_dialog();
+ }
+ this.dialog.dialog_title = executor.action.name;
+ if (widget instanceof instance.web.ViewManager) {
+ _.extend(widget.flags, {
+ $buttons: this.dialog.$buttons,
+ footer_to_buttons: true,
+ });
}
- 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.setParent(this.dialog);
+ var initialized = this.dialog_widget.appendTo(this.dialog.$el);
this.dialog.open();
+ return initialized;
} else {
this.dialog_stop();
- this.content_stop();
- if(action.menu_id) {
- return this.getParent().do_action(action, function () {
- instance.webclient.menu.open_menu(action.menu_id);
- });
+ this.inner_action = executor.action;
+ this.inner_widget = widget;
+ executor.post_process(widget);
+ return this.inner_widget.appendTo(this.$el);
+ }
+ },
+ ir_actions_act_window: function (action, options) {
+ var self = this;
+
+ return this.ir_actions_common({
+ widget: function () { return new instance.web.ViewManagerAction(self, action); },
+ action: action,
+ klass: 'oe_act_window',
+ post_process: function (widget) { widget.add_breadcrumb(options.on_reverse_breadcrumb); }
+ }, options);
+ },
+ ir_actions_client: function (action, options) {
+ var self = this;
+ var ClientWidget = instance.web.client_actions.get_object(action.tag);
+
+ if (!(ClientWidget.prototype instanceof instance.web.Widget)) {
+ var next;
+ if (next = ClientWidget(this, action)) {
+ return this.do_action(next, options);
}
- this.inner_action = action;
- this.inner_viewmanager = new instance.web.ViewManagerAction(this, action);
- this.inner_viewmanager.appendTo(this.$element);
+ return $.when();
}
+
+ return this.ir_actions_common({
+ widget: function () { return new ClientWidget(self, action); },
+ action: action,
+ klass: 'oe_act_client',
+ post_process: function(widget) {
+ self.push_breadcrumb({
+ widget: widget,
+ title: action.name,
+ on_reverse_breadcrumb: options.on_reverse_breadcrumb,
+ });
+ if (action.tag !== 'reload') {
+ self.do_push_state({});
+ }
+ }
+ }, options);
},
- ir_actions_act_window_close: function (action, on_closed) {
- if (!this.dialog && on_closed) {
- on_closed();
+ ir_actions_act_window_close: function (action, options) {
+ if (!this.dialog) {
+ options.on_close();
}
this.dialog_stop();
},
- ir_actions_server: function (action, on_closed) {
+ ir_actions_server: function (action, options) {
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, options)
});
},
- 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) {
+ ir_actions_report_xml: function(action, options) {
var self = this;
- $.blockUI();
+ instance.web.blockUI();
self.rpc("/web/session/eval_domain_and_context", {
contexts: [action.context],
domains: []
@@ -185,10 +395,10 @@ 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();
+ if (!self.dialog) {
+ options.on_close();
}
self.dialog_stop();
},
@@ -199,15 +409,13 @@ 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({
template: "ViewManager",
init: function(parent, dataset, views, flags) {
this._super(parent);
+ this.url_states = {};
this.model = dataset ? dataset.model : undefined;
this.dataset = dataset;
this.searchview = null;
@@ -235,8 +443,8 @@ instance.web.ViewManager = instance.web.Widget.extend({
start: function() {
this._super();
var self = this;
- this.$element.find('.oe_view_manager_switch a').click(function() {
- self.on_mode_switch($(this).data('view-type'));
+ this.$el.find('.oe_view_manager_switch a').click(function() {
+ self.switch_mode($(this).data('view-type'));
}).tipsy();
var views_ids = {};
_.each(this.views_src, function(view) {
@@ -244,9 +452,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,61 +462,28 @@ 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;
- return this.on_mode_switch(default_view);
+ return this.switch_mode(default_view);
},
- /**
- * Asks the view manager to switch visualization mode.
- *
- * @param {String} view_type type of view to display
- * @param {Boolean} [no_store=false] don't store the view being switched to on the switch stack
- * @returns {jQuery.Deferred} new view loading promise
- */
- on_mode_switch: function(view_type, no_store) {
+ switch_mode: function(view_type, no_store, view_options) {
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())) {
+ self.trigger('switch_mode', view_type, no_store, view_options);
return $.Deferred().reject();
-
+ }
if (!no_store) {
this.views_history.push(view_type);
}
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,60 +494,148 @@ 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 () {
+ r = $.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) {
- controller.do_show();
+ container.show();
+ controller.do_show(view_options || {});
} else {
+ container.hide();
controller.do_hide();
}
}
});
+ self.trigger('switch_mode', view_type, no_store, view_options);
+ });
+ return r;
+ },
+ 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.on('switch_mode', self, this.switch_mode);
+ controller.on('previous_view', self, this.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() {
+ if (self.searchview
+ && self.flags.auto_search
+ && view.controller.searchable !== false) {
+ self.searchview.ready.then(self.searchview.do_search);
+ }
+ self.trigger("controller_inited",view_type,controller);
});
- return view_promise;
},
- /**
- * Method used internally when a view asks to switch view. This method is meant
- * to be extended by child classes to change the default behavior, which simply
- * consist to switch to the asked view.
- */
- switch_view: function(view_type, no_store) {
- return this.on_mode_switch(view_type, no_store);
+ set_title: function(title) {
+ this.$el.find('.oe_view_title_text:first').text(title);
+ },
+ add_breadcrumb: function(on_reverse_breadcrumb) {
+ var self = this;
+ var views = [this.active_view || this.views_src[0].view_type];
+ this.on('switch_mode', self, 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];
+ var state = self.url_states[view_to_select];
+ self.do_push_state(state || {});
+ $.when(self.switch_mode(view_to_select)).then(function() {
+ self.$el.show();
+ });
+ },
+ 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;
+ },
+ on_reverse_breadcrumb: on_reverse_breadcrumb,
+ });
},
/**
* Returns to the view preceding the caller view in this manager's
* navigation history (the navigation history is appended to via
- * on_mode_switch)
+ * switch_mode)
*
* @param {Object} [options]
* @param {Boolean} [options.created=false] resource was created
* @param {String} [options.default=null] view to switch to if no previous view
* @returns {$.Deferred} switching end signal
*/
- on_prev_view: function (options) {
+ prev_view: function (options) {
options = options || {};
var current_view = this.views_history.pop();
var previous_view = this.views_history[this.views_history.length - 1] || options['default'];
if (options.created && current_view === 'form' && previous_view === 'list') {
// APR special case: "If creation mode from list (and only from a list),
// after saving, go to page view (don't come back in list)"
- return this.on_mode_switch('form');
+ return this.switch_mode('form');
} else if (options.created && !previous_view && this.action && this.action.flags.default_view === 'form') {
// APR special case: "If creation from dashboard, we have no previous view
- return this.on_mode_switch('form');
+ return this.switch_mode('form');
}
- return this.on_mode_switch(previous_view, true);
+ return this.switch_mode(previous_view, true);
},
/**
* Sets up the current viewmanager's search view.
@@ -387,8 +650,8 @@ 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"));
+ this.searchview.on('search_data', self, this.do_searchview_search);
+ return this.searchview.appendTo(this.$el.find(".oe_view_manager_view_search"));
},
do_searchview_search: function(domains, contexts, groupbys) {
var self = this,
@@ -398,7 +661,7 @@ instance.web.ViewManager = instance.web.Widget.extend({
domains: [this.action.domain || []].concat(domains || []),
contexts: [action_context].concat(contexts || []),
group_by_seq: groupbys || []
- }, function (results) {
+ }).then(function (results) {
self.dataset._model = new instance.web.Model(
self.dataset.model, results.context, results.domain);
var groupby = results.group_by.length
@@ -411,14 +674,6 @@ instance.web.ViewManager = instance.web.Widget.extend({
});
},
/**
- * Event launched when a controller has been inited.
- *
- * @param {String} view_type type of view
- * @param {String} view the inited controller
- */
- on_controller_inited: function(view_type, view) {
- },
- /**
* Called when one of the view want to execute an action
*/
on_action: function(action) {
@@ -434,14 +689,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 +733,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 +760,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 +773,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,14 +800,23 @@ 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) {
+ this.dataset.call('fields_get', [false, {}]).then(function (fields) {
var $root = $('<dl>');
_(fields).each(function (attributes, name) {
$root.append($('<dt>').append($('<h4>').text(name)));
- var $attrs = $('<dl>').appendTo(
- $('<dd>').appendTo($root));
+ var $attrs = $('<dl>').appendTo($('<dd>').appendTo($root));
_(attributes).each(function (def, name) {
if (def instanceof Object) {
def = JSON.stringify(def);
@@ -593,15 +832,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 +856,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);
@@ -651,26 +896,40 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({
}, action || {});
this.do_action(action);
},
- on_mode_switch: function (view_type, no_store) {
+ switch_mode: function (view_type, no_store, options) {
var self = this;
- return $.when(this._super(view_type, no_store)).then(function () {
- 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', {
+ return $.when(this._super.apply(this, arguments)).then(function () {
+ var controller = self.views[self.active_view].controller;
+ 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;
+ },
+ get_action_manager: function() {
+ var cur = this;
+ while (cur = cur.getParent()) {
+ if (cur instanceof instance.web.ActionManager) {
+ return cur;
+ }
+ }
+ return undefined;
+ },
+ set_title: function(title) {
+ this.$el.find('.oe_breadcrumb_title:first').html(this.get_action_manager().get_title());
+ },
do_push_state: function(state) {
if (this.getParent() && this.getParent().do_push_state) {
state["view_type"] = this.active_view;
+ this.url_states[this.active_view] = state;
this.getParent().do_push_state(state);
}
},
@@ -680,7 +939,7 @@ instance.web.ViewManagerAction = instance.web.ViewManager.extend({
if (state.view_type && state.view_type !== this.active_view) {
defs.push(
this.views[this.active_view].deferred.pipe(function() {
- return self.on_mode_switch(state.view_type, true);
+ return self.switch_mode(state.view_type, true);
})
);
}
@@ -689,45 +948,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('Attachement'), },
{ 'name' : 'other', 'label' : _t('More'), }
];
this.items = {
'print' : [],
- 'files' : [],
- 'other' : [
- { label: _t("Import"), callback: view.on_sidebar_import },
- { label: _t("Export"), callback: view.on_sidebar_export }
- ]
- }
- if (this.session.uid === 1) {
- var item = { label: _t("Translate"), callback: view.on_sidebar_translate, title: _t("Technical translation") };
- this.items.other.push(item);
- }
+ 'other' : []
+ };
+ 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]);
@@ -736,16 +989,17 @@ 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() {
+ $(this).toggle(!!$(this).find('li').length);
+ });
},
/**
* For each item added to the section:
@@ -811,19 +1065,23 @@ instance.web.Sidebar = instance.web.Widget.extend({
self.rpc("/web/action/load", {
action_id: item.action.id,
context: additional_context
- }, function(result) {
- result.result.context = _.extend(result.result.context || {},
+ }).then(function(result) {
+ result.context = _.extend(result.context || {},
additional_context);
- result.result.flags = result.result.flags || {};
- result.result.flags.new_window = true;
- self.do_action(result.result, function () {
- // reload view
- self.getParent().reload();
+ result.flags = result.flags || {};
+ result.flags.new_window = true;
+ self.do_action(result, {
+ on_close: function() {
+ // reload view
+ self.getParent().reload();
+ },
});
});
});
},
do_attachement_update: function(dataset, model_id) {
+ this.dataset = dataset;
+ this.model_id = model_id;
if (!model_id) {
this.on_attachments_loaded([]);
} else {
@@ -835,165 +1093,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: '',
/**
@@ -1006,33 +1142,36 @@ 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() {
+ load_view: function(context) {
+ var self = this;
+ var view_loaded;
if (this.embedded_view) {
- var def = $.Deferred();
- var self = this;
- $.async_when().then(function() {def.resolve(self.embedded_view);});
- return def.pipe(this.on_loaded);
+ view_loaded = $.Deferred();
+ $.async_when().then(function() {
+ view_loaded.resolve(self.embedded_view);
+ });
} else {
- var context = new instance.web.CompoundContext(this.dataset.get_context());
if (! this.view_type)
console.warn("view_type is not defined", this);
- return this.rpc("/web/view/load", {
+ view_loaded = this.rpc("/web/view/load", {
"model": this.dataset.model,
"view_id": this.view_id,
"view_type": this.view_type,
toolbar: !!this.options.$sidebar,
- context: context
- }).pipe(this.on_loaded);
+ context: this.dataset.get_context(context)
+ });
}
- },
- /**
- * Called after a successful call to fields_view_get.
- * Must return a promise.
- */
- on_loaded: function(fields_view_get) {
+ return view_loaded.pipe(function(r) {
+ self.trigger('view_loaded', r);
+ // add css classes that reflect the (absence of) access rights
+ self.$el.addClass('oe_view')
+ .toggleClass('oe_cannot_create', !self.is_action_enabled('create'))
+ .toggleClass('oe_cannot_edit', !self.is_action_enabled('edit'))
+ .toggleClass('oe_cannot_delete', !self.is_action_enabled('delete'));
+ });
},
set_default_options: function(options) {
this.options = options || {};
@@ -1044,12 +1183,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``.
*
@@ -1073,7 +1206,7 @@ instance.web.View = instance.web.Widget.extend({
var context = new instance.web.CompoundContext(dataset.get_context(), action_data.context || {});
var handler = function (r) {
- var action = r.result;
+ var action = r;
if (action && action.constructor == Object) {
var ncontext = new instance.web.CompoundContext(context);
if (record_id) {
@@ -1092,7 +1225,9 @@ instance.web.View = instance.web.Widget.extend({
/* niv: previously we were overriding once more with action_data.context,
* I assumed this was not a correct behavior and removed it
*/
- return self.do_action(action, result_handler);
+ return self.do_action(action, {
+ on_close: result_handler,
+ });
}, null);
} else {
return result_handler();
@@ -1100,7 +1235,7 @@ instance.web.View = instance.web.Widget.extend({
};
if (action_data.special) {
- return handler({result: {"type":"ir.actions.act_window_close"}});
+ return handler({"type":"ir.actions.act_window_close"});
} else if (action_data.type=="object") {
var args = [[record_id]], additional_args = [];
if (action_data.args) {
@@ -1114,11 +1249,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}).then(handler);
} else {
- return dataset.exec_workflow(record_id, action_data.name, handler);
+ return dataset.exec_workflow(record_id, action_data.name).then(handler);
}
},
/**
@@ -1132,10 +1267,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) {
@@ -1146,10 +1281,9 @@ instance.web.View = instance.web.Widget.extend({
},
/**
* Switches to a specific view type
- *
- * @param {String} view view type to switch to
*/
- do_switch_view: function(view) {
+ do_switch_view: function() {
+ this.trigger.apply(this, ['switch_mode'].concat(_.toArray(arguments)));
},
/**
* Cancels the switch to the current view, switches to the previous one
@@ -1158,27 +1292,11 @@ instance.web.View = instance.web.Widget.extend({
* @param {Boolean} [options.created=false] resource was created
* @param {String} [options.default=null] view to switch to if no previous view
*/
- do_prev_view: function (options) {
- },
+
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();
@@ -1189,6 +1307,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;
}
});
@@ -1216,7 +1343,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,
@@ -1225,7 +1351,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 14f6f727daf..2ab51a32f03 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 developper 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>OpenERP</span></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="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' and !item.callback" 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,17 +588,17 @@
<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"
t-att-data-id="record.id"
t-att-style="color_for(record) + style "
- t-attf-class="#{class} #{rank} #{(fields[field.attrs.name].type === 'float') or (fields[field.attrs.name].type === 'integer') ? 'oe-number' : ''}">
+ t-attf-class="#{class} #{rank} #{(fields[field.attrs.name].type === 'float') or (fields[field.attrs.name].type === 'integer') ? 'oe_number' : ''}">
<span t-if="!field.attrs.modifiers.invisible" >
<t t-esc="render(record[field.attrs.name], fields[field.attrs.name])" />
@@ -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)">
+ t-attf-class="oe_list_header_#{column.widget or column.type} #{((options.sortable and column.tag !== 'button') ? 'oe_sortable' : null)}"><div>
<t t-if="column.tag !== 'button'"><t t-esc="column.string"/></t>
- </th>
+ </div></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,35 +666,56 @@
<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'"/>
+ <t t-set="number" t-value="column.type === 'integer' or column.type == 'float'"/>
+ <t t-set="modifiers" t-value="column.modifiers_for(asData)"/>
<td t-if="!column.meta and column.invisible !== '1'" t-att-title="column.help"
- t-att-class="'oe-field-cell' + (align ? ' oe-number' : '')
- + (column.tag === 'button' ? ' oe-button' : '')"
- t-att-data-field="column.id">
- <t t-raw="render_cell(record, column)"/>
- </td>
+ t-attf-class="oe_list_field_cell oe_list_field_#{column.widget or column.type} #{number ? 'oe_number' : ''} #{column.tag === 'button' ? 'oe-button' : ''} #{modifiers.readonly ? 'oe_readonly' : ''} #{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-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">
<div class="oe_formview">
@@ -701,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>
@@ -773,37 +806,25 @@
</table>
</form>
-<t t-name="FormRenderingForm.manual">
- <div class="oe_form_manual_layout"/>
-</t>
<t t-name="FormRenderingForm">
+ <div t-attf-class="#{classnames}">
+ </div>
+</t>
+<t t-name="FormRenderingSheet">
<div class="oe_form_sheetbg">
<div t-attf-class="oe_form_sheet oe_form_sheet_width #{classnames}">
- <group t-if="layout === 'auto'" col="4"/>
</div>
</div>
</t>
-<t t-name="FormRenderingSheet" t-extend="FormRenderingForm">
-</t>
<t t-name="FormRenderingGroup">
- <t t-set="table">
- <table border="0" cellpadding="0" cellspacing="0" width="100%" t-att-class="classnames"/>
- </t>
- <t t-if="string">
- <fieldset class="oe_group_box">
- <legend><t t-esc="string"/></legend>
- <t t-raw="table"/>
- </fieldset>
- </t>
- <t t-if="!string">
- <t t-raw="table"/>
- </t>
+ <t t-if="string" t-call="FormRenderingSeparator"/>
+ <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>
@@ -811,12 +832,11 @@
</div>
</t>
<t t-name="FormRenderingNotebookPage">
- <div t-attf-class="oe_form_notebook_page #{classnames}" t-att-id="id">
- <group t-if="layout === 'auto'" col="4"/>
+ <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>
@@ -895,172 +915,151 @@
</ul>
</t>
<t t-name="FieldChar">
- <t t-if="!widget.get('effective_readonly')">
- <div class="oe_form_field_char">
+ <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-attf-class="field_char #{widget.is_field_number ? 'oe-number' : ''}"
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"/>
- </div>
- </t>
- <t t-if="widget.get('effective_readonly')">
- <span t-attf-class="oe_form_field_char #{widget.is_field_number ? 'oe-number' : ''}"/>
- </t>
+ </t>
+ <t t-if="widget.get('effective_readonly')">
+ <span class="oe_form_char_content"></span>
+ </t>
+ </span>
</t>
<t t-name="FieldEmail">
- <t t-if="!widget.get('effective_readonly')">
- <table class="oe_form_field_email" cellpadding="0" cellspacing="0" border="0">
- <tr>
- <td>
+ <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')">
+ <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"
/>
- </td>
- <td width="16">
- <button class="oe_button oe_field_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>
- </td>
- </tr>
- </table>
- </t>
- <t t-if="widget.get('effective_readonly')">
- <span>
- <a href="#" class="oe_form_uri"/>
- </span>
- </t>
+ </div>
+ </t>
+ </span>
</t>
-<t t-name="FieldUrl">
- <t t-if="!widget.get('effective_readonly')">
- <table class="oe_form_field_url" cellpadding="0" cellspacing="0" border="0">
- <tr>
- <td>
- <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"
- />
- </td>
- <td width="16">
- <button class="oe_button oe_field_button" tabindex="-1" type="button" title="Open this resource">
- <img t-att-src='_s + "/web/static/src/img/icons/gtk-ok.png"'/>
- </button>
- </td>
- </tr>
- </table>
- </t>
- <t t-if="widget.get('effective_readonly')">
- <span>
- <a href="#" class="oe_form_uri"/>
- </span>
+<t t-name="FieldUrl" t-extend="FieldEmail">
+ <t t-jquery="span:first">
+ this.removeClass('oe_form_field_email').addClass('oe_form_field_url');
</t>
</t>
<t t-name="FieldText">
- <div class="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.datetimepicker">
- <t t-set="placeholder" t-value="widget.getParent().node and widget.getParent().node.attrs.placeholder"/>
- <div class="oe_datepicker_root oe_form_field_datetime">
+<t t-name="web.datepicker">
+ <span>
+ <t t-set="placeholder" t-value="widget.getParent().node and widget.getParent().node.attrs.placeholder"/>
<input type="text" class="oe_datepicker_container" disabled="disabled" style="display: none;"/>
<input type="text"
t-att-name="widget.name"
t-att-placeholder="placeholder"
- t-attf-class="oe_datepicker_master field_#{widget.type_of_date} oe_form_field_#{widget.type_of_date}"
+ class="oe_datepicker_master"
/><img class="oe_input_icon oe_datepicker_trigger" t-att-src='_s + "/web/static/src/img/ui/field_calendar.png"'
title="Select date" width="16" height="16" border="0"/>
- </div>
+ </span>
+</t>
+<t t-name="FieldDate">
+ <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" t-att-style="widget.node.attrs.style"/>
</t>
<t t-name="FieldSelection">
- <t t-if="!widget.get('effective_readonly')">
- <div class="oe_form_field_selection">
- <select
- t-att-name="widget.name"
- t-att-tabindex="widget.node.attrs.tabindex"
- t-att-autofocus="widget.node.attrs.autofocus"
- t-att-id="widget.id_for_label">
- <t t-foreach="widget.values" t-as="option">
- <option><t t-esc="option[1]"/></option>
- </t>
- </select>
- </div>
- </t>
- <t t-if="widget.get('effective_readonly')">
- <span class="field_selection"/>
- </t>
+ <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"
+ t-att-autofocus="widget.node.attrs.autofocus"
+ t-att-id="widget.id_for_label">
+ <t t-foreach="widget.values" t-as="option">
+ <option><t t-esc="option[1]"/></option>
+ </t>
+ </select>
+ </span>
</t>
<t t-name="FieldMany2One">
- <t t-if="!widget.get('effective_readonly')">
- <table class="oe_form_field_many2one" cellpadding="0" cellspacing="0" border="0">
- <tr>
- <td valign="top">
+ <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')">
+ <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"
t-att-tabindex="widget.node.attrs.tabindex"
t-att-autofocus="widget.node.attrs.autofocus"
t-att-placeholder="widget.node.attrs.placeholder"
/>
- <span class="oe-m2o-drop-down-button">
- <img t-att-src='_s + "/web/static/src/img/down-arrow.png"' /></span>
- </td>
- <td width="16">
- <button class="oe_button oe_field_button oe-m2o-cm-button" title="Open Resource">
- <img t-att-src='_s + "/web/static/src/img/icons/terp-folder-yellow.png"'/>
- </button>
- </td>
- </tr>
- </table>
- </t>
- <t t-if="widget.get('effective_readonly')">
- <span>
- <a href="#" class="oe_form_uri"/>
- </span>
- </t>
+ <span class="oe_m2o_drop_down_button">
+ <img t-att-src='_s + "/web/static/src/img/down-arrow.png"'/>
+ </span>
+ </div>
+ </t>
+ </span>
</t>
<t t-name="FieldMany2ManyTags">
- <div class="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_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 oe_form_view_reference_selection">
+ <td t-attf-class="oe_form_frame_cell oe_form_selection">
+ <span t-attf-class="oe_form_view_reference_selection"/>
</td>
- <td t-attf-class="oe_form_frame_cell oe_form_many2one oe_form_view_reference_m2o" nowrap="true" style="display: none">
+ <td t-attf-class="oe_form_frame_cell oe_form_many2one" nowrap="true">
+ <span t-attf-class="oe_form_view_reference_m2o"/>
</td>
</tr>
</table>
</t>
<t t-name="FieldBoolean">
- <span class="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"
@@ -1070,88 +1069,47 @@
</span>
</t>
<t t-name="FieldProgressBar">
- <div class="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>
- </div>
+ </span>
</t>
-<t t-name="FieldStatus.content">
- <ul class="oe-arrow-list">
- <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-arrow-list-selected' : ''">
- <span class="oe-arrow-list-before" t-if="i &gt; 0"></span><span><t t-esc="widget.to_show[i][1]"/></span><span class="oe-arrow-list-after" t-if="i &lt; size - 1"></span>
- </li>
- </t>
- </ul>
+<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' : ''">
- <t t-esc="widget.to_show[i][1]"/>
- <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>
+ <t t-foreach="widget.selection" t-as="i">
+ <li t-att-class="i[0] === widget.get('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">
- <table cellpadding="0" cellspacing="0" border="0" width="100%">
- <tr>
- <td class="oe_form_field-binary-image-placeholder" align="center">
-
- </td>
- </tr>
- <tr>
- <td align="center" valign="bottom" height="25">
- <div class="oe-binary">
- <table cellspacing="0" cellpadding="0" border="0">
- <tr>
- <td>
- <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>
- </td>
- <td>
- <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>
- </td>
- </tr>
- </table>
- </div>
- <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"/>
- </td>
- </tr>
- </table>
+ </div>
+ </span>
</t>
<t t-name="FieldBinaryImage-img">
- <img t-att-src='url' class="oe-binary-image field_image"
+ <img t-att-src='url'
t-att-border="widget.readonly ? 0 : 1"
t-att-name="widget.name"
t-att-width="widget.node.attrs.img_width || widget.node.attrs.width"
t-att-height="widget.node.attrs.img_height || widget.node.attrs.height"
- t-att-style="'max-width: ' + (widget.$element.width() > 60 ? widget.$element.width() : 60) + 'px'"
/>
</t>
<t t-name="FieldBinaryFile">
<t t-if="!widget.get('effective_readonly')">
- <table class="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"
@@ -1161,31 +1119,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" t-translation="off">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>
@@ -1193,25 +1149,81 @@
</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" t-if="widget.widget!='image'"/>
+ <input type="file" class="oe_form_binary_file" name="ufile" accept="image/*" t-if="widget.widget=='image'"/>
+ </form>
+ <iframe t-att-id="fileupload_id" t-att-name="fileupload_id" style="display: none"/>
+ </div>
+</t>
+<t t-name="FieldBinaryFileUploader.files">
+ <div class="oe_attachments">
+ <t t-if="!widget.get('effective_readonly')" t-foreach="widget.get('value')" t-as="file">
+ <div class="oe_attachment">
+ <span t-if="(file.upload or file.percent_loaded&lt;100)" t-attf-title="{(file.name || file.filename) + (file.date?' \n('+file.date+')':'' )}" t-attf-name="{file.name || file.filename}">
+ <span class="oe_fileuploader_in_process">...Upload in progress...</span>
+ <t t-raw="file.name || file.filename"/>
+ </span>
+ <a t-if="(!file.upload or file.percent_loaded&gt;=100)" t-att-href="file.url" t-attf-title="{(file.name || file.filename) + (file.date?' \n('+file.date+')':'' )}">
+ <t t-raw="file.name || file.filename"/>
+ </a>
+ <t t-if="(!file.upload or file.percent_loaded&gt;=100)">
+ <a class="oe_right oe_delete oe_e" title="Delete this file" t-attf-data-id="{file.id}">[</a>
+ </t>
+ </div>
+ </t>
+ <t t-if="widget.get('effective_readonly')" t-foreach="widget.get('value')" t-as="file">
+ <div>
+ <a t-att-href="file.url" t-attf-title="{(file.name || file.filename) + (file.date?' \n('+file.date+')':'' )}">
+ <t t-raw="file.name || file.filename"/>
+ </a>
+ </div>
+ </t>
+ </div>
+</t>
+<t t-name="FieldBinaryFileUploader">
+ <div t-att-style="widget.node.attrs.style" t-attf-class="oe_fileupload #{widget.node.attrs.class ? widget.node.attrs.class :''}">
+ <div class="oe_placeholder_files"/>
+ <div class="oe_add" t-if="!widget.get('effective_readonly')">
+ <!-- uploader of file -->
+ <button class="oe_attach"><span class="oe_e">'</span></button>
+ <span class='oe_attach_label'>File</span>
+ <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.view.model"/>
+ <input type="hidden" name="id" value="0"/>
+ <input type="hidden" name="session_id" t-att-value="widget.session.session_id"/>
+ </t>
+ </div>
+ </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>
@@ -1247,31 +1259,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>
+ </t>
+ <button type="button" class="oe_button oe_selectcreatepopup-search-create">Create</button>
+ or <a class="oe_button oe_selectcreatepopup-search-close oe_bold oe_form_button_cancel" href="javascript:void(0)">Cancel</a>
+</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>
@@ -1299,18 +1332,21 @@
<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"
class="oe_searchview_input"
- contenteditable="true"/>
+ contenteditable="true"
+ tabindex="0"/>
<!-- 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">✖</span
+ ><span class="oe_facet_remove">x</span
><span class="oe_facet_category oe_i" t-if="widget.model.has('icon')">
<t t-esc="widget.model.get('icon')"/>
</span
@@ -1321,9 +1357,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}"
@@ -1336,7 +1371,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>
@@ -1347,18 +1381,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">
@@ -1475,38 +1499,45 @@
</t>
</t>
</t>
-<div t-name="SearchView.Filters" class="oe_searchview_filters">
+<div t-name="SearchView.Filters" class="oe_searchview_filters oe_searchview_section">
</div>
-<div t-name="SearchView.CustomFilters" class="oe_searchview_custom">
- <ul class="oe_searchview_custom_list"/>
- <h4 class="oe_searchview_custom_title">
- <label for="oe_searchview_custom_input">Save search</label></h4>
- <form>
- <input id="oe_searchview_custom_input"/>
- <button>Save</button><br/>
- <label for="oe_searchview_custom_public">Share with all users:</label>
- <input id="oe_searchview_custom_public" type="checkbox"/>
- <label for="oe_searchview_custom_default">Use by default:</label>
- <input id="oe_searchview_custom_default" type="checkbox"/>
- </form>
+<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"/>
+ <div class="oe_searchview_custom">
+ <h4>Save current filter</h4>
+ <form>
+ <p><input id="oe_searchview_custom_input" placeholder="Filter name"/></p>
+ <p>
+ <input id="oe_searchview_custom_public" type="checkbox"/>
+ <label for="oe_searchview_custom_public">Share with all users</label>
+ <input id="oe_searchview_custom_default" type="checkbox"/>
+ <label for="oe_searchview_custom_default">Use by default</label>
+ </p>
+ <button>Save</button>
+ </form>
+ </div>
+ </div>
</div>
+
<div t-name="SearchView.advanced" class="oe_searchview_advanced">
<h4>Advanced Search</h4>
<form>
<ul>
</ul>
- <button class="oe_add_condition" type="button">Add a condition</button>
+ <button class="oe_add_condition button" type="button">Add a condition</button><br/>
<button class="oe_apply" type="submit">Apply</button>
</form>
</div>
<t t-name="SearchView.extended_search.proposition">
<li>
+ <span class="searchview_extended_prop_or">or</span>
<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>
@@ -1514,7 +1545,7 @@
</select>
<select class="searchview_extended_prop_op"/>
<span class="searchview_extended_prop_value"/>
- <button type="button" class="searchview_extended_delete_prop">Delete</button>
+ <a class="searchview_extended_delete_prop button">x</a>
</li>
</t>
<t t-name="SearchView.extended_search.proposition.char">
@@ -1536,115 +1567,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">
@@ -1656,7 +1583,7 @@
<td colspan="3">
<label for="import_compat">Export Type:</label>
<select id="import_compat" name="import_compat">
- <option value="yes">Import Compatible Export</option>
+ <option value="yes">Import-Compatible Export</option>
<option value="">Export all Data</option>
</select>
@@ -1749,7 +1676,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,
@@ -1759,9 +1686,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>
@@ -1801,7 +1728,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>
@@ -1818,5 +1745,37 @@
<pre><t t-esc="error.preview"/></pre>
</t>
</t>
-
+<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>
+<t t-name="FieldMonetary" t-extend="FieldChar">
+ <t t-jquery="t:first" t-operation="before">
+ <t t-if="widget.get('currency_info') and widget.get('currency_info').position === 'before'">
+ <t t-esc="widget.get('currency_info').symbol"/>
+ </t>
+ </t>
+ <t t-jquery="t:last" t-operation="after">
+ <t t-if="widget.get('currency_info') and widget.get('currency_info').position === 'after'">
+ <t t-esc="widget.get('currency_info').symbol"/>
+ </t>
+ </t>
+</t>
</templates>