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