diff options
| author | Antony Lesuisse <al@openerp.com> | 2011-12-08 14:39:52 +0100 |
|---|---|---|
| committer | Antony Lesuisse <al@openerp.com> | 2011-12-08 14:39:52 +0100 |
| commit | 78312ed1dd77aff07951cf5646bb4e38e51becb6 (patch) | |
| tree | 7c36ad833ac86d01f242995a722e5f609ce35312 /addons/web/static/src | |
| parent | 3225e9d5c42d7396ba9e3089e46c494bf919e283 (diff) | |
| parent | 54f3f38957fc36fbb705524b99cc2af0b5aceeca (diff) | |
[MERGE] trunk
bzr revid: al@openerp.com-20111208133952-183wp4ozrr0gvea8
Diffstat (limited to 'addons/web/static/src')
21 files changed, 2107 insertions, 945 deletions
diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css index d8f31af1d35..3a163beb832 100644 --- a/addons/web/static/src/css/base.css +++ b/addons/web/static/src/css/base.css @@ -1,4 +1,3 @@ -/* TODO: separate openerp web client page css from openerp views css */ body.openerp { padding: 0; margin: 0; @@ -6,10 +5,17 @@ body.openerp { min-width: 1000px; overflow-y: scroll; font-size: 80%; + font-family: Ubuntu, Helvetica, sans-serif; } body.openerp, .openerp textarea, .openerp input, .openerp select, .openerp option, .openerp button, .openerp .ui-widget { font-family: Ubuntu, Helvetica, sans-serif; + font-size:85%; +} + +.openerp .view-manager-main-content { + width: 100%; + padding: 0 8px 8px 8px; } .oe_box { @@ -29,6 +35,17 @@ body.openerp, .openerp textarea, .openerp input, .openerp select, .openerp optio .openerp .oe-listview .oe-number { text-align: right !important; } +.oe-listview-header-columns { + background: #444; /* Old browsers */ + background: -moz-linear-gradient(top, #ffffff 0%, #d1d1d1 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ffffff), color-stop(100%,#d1d1d1)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(top, #ffffff 0%,#d1d1d1 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(top, #ffffff 0%,#d1d1d1 100%); /* Opera11.10+ */ + background: -ms-linear-gradient(top, #ffffff 0%,#d1d1d1 100%); /* IE10+ */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#FFFFFF', endColorstr='#d1d1d1',GradientType=0 ); /* IE6-9 */ + background: linear-gradient(top, #ffffff 0%,#d1d1d1 100%); /* W3C */ +} + .openerp .oe_hide { display: none !important; } @@ -261,7 +278,7 @@ label.error { filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#BD5E54', endColorstr='#90322A',GradientType=0 ); /* IE6-9 */ background: linear-gradient(top, #bd5e54 0%,#90322a 60%); /* W3C */ - border: 1px solid #6E2A24; + border: 1px solid #5E1A14; border-radius: 4px; -moz-border-radius: 4px; -webkit-border-radius: 4px; @@ -272,7 +289,7 @@ label.error { text-transform: uppercase; line-height: 20px; font-weight: bold; - font-size: 85%; + font-size: 75%; white-space: nowrap; } @@ -331,6 +348,7 @@ label.error { height: 100%; display: block; position: relative; + font-size:85%; } .openerp .secondary_menu.oe_folded { width: 20px; @@ -371,6 +389,10 @@ label.error { white-space: nowrap; color: white; text-shadow: 0 1px 0 #333; + +} +.openerp a.oe_secondary_submenu_item { + padding: 0 5px 2px 10px; } .openerp a.oe_secondary_submenu_item:hover, .openerp a.oe_secondary_submenu_item.leaf.active { @@ -383,7 +405,7 @@ label.error { background: -ms-linear-gradient(top, #ffffff 0%,#d8d8d8 11%,#afafaf 86%,#333333 91%,#5a5858 96%); /* IE10+ */ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#FFFFFF', endColorstr='#5A5858',GradientType=0 ); /* IE6-9 */ background: linear-gradient(top, #ffffff 0%,#d8d8d8 11%,#afafaf 86%,#333333 91%,#5a5858 96%); /* W3C */ - padding: 0 5px 2px 5px; + padding: 0 5px 2px 10px; line-height: 20px; color: #3f3d3d; text-decoration: none; @@ -431,6 +453,8 @@ label.error { margin: 0; padding: 4px 10px; text-shadow: 0 1px 0 #111111; + font-weight:normal; + line-height:14px; } .openerp .header_title small { color: #ccc; @@ -464,15 +488,11 @@ label.error { filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#929292', endColorstr='#4D4D4D',GradientType=0 ); } .openerp .header_corner ul.block { - /*float: left;*/ list-style: none; height: 34px; margin: 0; padding: 0 0 0 2px; - /*background: url(../images/top-sep-a.png) no-repeat;*/ line-height: 33px; - /*font-size: 1em;*/ - /*text-transform: uppercase;*/ } .openerp .header_corner ul.block li { float: left; @@ -498,6 +518,10 @@ label.error { font-weight: bold; } +.openerp .logout { + font-size:80%; +} + /* Footer */ .openerp div.oe_footer { background: none repeat scroll 0 0 #CCCCCC; @@ -530,7 +554,7 @@ label.error { } .openerp h2.oe_view_title { - font-size: 175%; + font-size: 110%; font-weight: normal; margin: 2px 0; color: #252424; @@ -541,12 +565,15 @@ label.error { .openerp .oe_vm_switch { float: right; } +.openerp .oe-view-manager-header .oe_view_title { + font-size:150%; +} /* SearchView */ .openerp .filter_label, .openerp .filter_icon { - border: 1px solid #666; border-left-width: 0; background: #F0F0F0; + border: 1px solid #999; background: -moz-linear-gradient(top, #F0F0F0 0%, #C0C0C0 100%); background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#F0F0F0), color-stop(100%,#C0C0C0)); filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#F0F0F0', endColorstr='#C0C0C0',GradientType=0 ); @@ -558,13 +585,13 @@ label.error { filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#F0F0F0', endColorstr='#A1A7CE',GradientType=0 ); } .openerp .filter_label:active, .openerp .filter_icon:active { - background: #AAAAAA; + background: #aaa; background: -moz-linear-gradient(top, #999999 0%, #EEEEEE 100%); background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#999999), color-stop(100%,#EEEEEE)); filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#999999', endColorstr='#EEEEEE',GradientType=0 ); } .openerp .filter_label.enabled, .openerp .filter_icon.enabled { - background: #AAAAAA; + background: #aaa; } .openerp .filter_icon { height: 22px; @@ -588,13 +615,16 @@ label.error { white-space: nowrap; } .openerp .filter_label_group button:first-child { - border-left: 1px solid #666; -webkit-border-top-left-radius: 7px; -webkit-border-bottom-left-radius: 7px; -moz-border-radius-topleft: 7px; -moz-border-radius-bottomleft: 7px; border-top-left-radius: 7px; border-bottom-left-radius: 7px; + border-right: none; +} +.openerp .filter_label_group button { + border-right: none; } .openerp .filter_label_group button:last-child { -webkit-border-top-right-radius: 7px; @@ -603,6 +633,20 @@ label.error { -moz-border-radius-bottomright: 7px; border-top-right-radius: 7px; border-bottom-right-radius: 7px; + border-right: 1px solid #999; +} +.openerp .filter_label_group button.filter_icon img { + padding: 1px 8px 0 8px; +} +.openerp .filter_label_group button.filter_icon:first-child { + border-left: solid 1px #999; + margin-left: -7px; + -webkit-border-top-left-radius: 0px; + -webkit-border-bottom-left-radius: 0px; + -moz-border-radius-topleft: 0px; + -moz-border-radius-bottomleft: 0px; + border-top-left-radius: 0px; + border-bottom-left-radius: 0px; } .openerp .searchview_group_string { @@ -628,8 +672,16 @@ label.error { .openerp .searchview_group.expanded .searchview_group_content { display: block; } -.openerp .searchview_group_content { - padding-left: 10px; + +.openerp .searchview_group_content .oe_label, .openerp .searchview_group_content .oe_label_help { + font-weight: bold; + color: #4c4c4c; +} + +.openerp .oe-searchview-render-line .oe_label, .openerp .oe-searchview-render-line .oe_label_help { + font-weight: bold; + font-size: 80%; + white-space: nowrap; } .openerp .searchview_extended_group { @@ -715,20 +767,27 @@ label.error { text-align: left; padding: 1px 2px; } -.openerp .oe-listview th.oe-record-selector, -.openerp .oe-listview td.oe-button, -.openerp .oe-listview td.oe-record-delete { - padding: 0 1px; + +.openerp .oe-record-delete button, +.openerp button.oe-edit-row-save { + border: none; + height: 12px; + width: 12px; + background: url("/web/static/src/img/iconset-b-remove.png") no-repeat scroll center center transparent; + cursor: pointer; +} +.openerp button.oe-edit-row-save { + background-image: url('/web/static/src/img/icons/save-document.png'); } /* Could use :not selectors if they were supported by MSIE8... */ .openerp .oe-listview tbody td { - border-left: 1px solid #dadada; + border-left: 1px solid #dadada; /*currently commenting to test with no vertical lines in list view*/ } .openerp .oe-listview tbody td:first-child, .openerp .oe-listview tbody td.oe-button, -.openerp .oe-listview tbody td.oe-button+td, -.openerp .oe-listview tbody th.oe-record-selector+td, +.openerp .oe-listview tbody td.oe-button, +.openerp .oe-listview tbody th.oe-record-selector, .openerp .oe-listview tbody td.oe-record-delete { border-left: none; } @@ -738,15 +797,36 @@ label.error { } .openerp .oe-listview th.oe-sortable { cursor: pointer; + font-size: 75%; + text-transform: uppercase; + padding: 0; + margin: 0; + padding-left: 3px; + color: #333; } .openerp .oe-listview th.oe-sortable .ui-icon { - height: 1em; + height: 60%; + margin: -6px 0 0; display: inline; display: inline-block; + vertical-align: middle; +} + +.openerp .oe-listview table tbody td { + border-bottom: 1px solid #E3E3E3; +} + +.openerp .oe-listview .oe-record-selector { + border-bottom: 1px solid #E3E3E3; } .openerp .oe-listview .oe-field-cell { cursor: pointer; + margin-top: 0; + margin-bottom: 0; + padding-top: 3px; + padding-bottom: 3px; + font-size: 80%; } .openerp .oe-listview .oe-field-cell progress { width: 100%; @@ -778,6 +858,8 @@ label.error { } .openerp .oe-list-pager .oe-pager-state { cursor: pointer; + font-size: 90%; + color: #555; } .openerp .oe-listview .oe-group-name { @@ -794,6 +876,8 @@ label.error { .openerp .oe-listview .oe-list-footer { text-align: center; white-space: nowrap; + color: #444; + font-size: 85%; } .openerp .oe-listview .oe-list-footer span { margin: 0 1em; @@ -846,28 +930,68 @@ label.error { } .openerp .oe_form_notebook .ui-tabs-panel { padding: 4px; + -moz-border-radius-topright: 4px; + -webkit-border-top-right-radius: 4px; + border-top-right-radius: 4px; } .openerp .oe_form_notebook ul.ui-tabs-nav { padding-left: 0; background: transparent; - border-width: 0 0 1px 0; + border-width: 0; border-radius: 0; -moz-border-radius: 0; -webkit-border-radius: 0; - line-height: 0.5em; + line-height: 0.8em; + font-size: 95%; + color: #555; } .openerp .oe_form_notebook ul.ui-tabs-nav li { font-weight: bold; } .openerp .oe_form_notebook .ui-tabs-panel { background: #f9f9f9; - border-width: 0 1px 1px 1px; + border-width: 1px; } .openerp .oe_form_notebook .ui-tabs-selected { background: #f9f9f9; } +/* Unedit Form */ +.openerp .field_char, +.openerp .field_date, +.openerp .field_float, +.openerp .field_selection, +.openerp .oe_form_field_many2one a, +.openerp .oe_form_field_reference a{ + vertical-align: middle; + padding-top: 3px; + font-size: 90%; + color: #222; +} +.openerp .oe_form_field_many2one a, +.openerp .oe_form_field_reference a { + color: #9A0404; + line-height: 12px; +} + + /* Form */ + +.openerp .oe_form_frame_cell input[type="checkbox"] { + margin-top: 3px; + vertical-align: center; +} +.openerp .oe_form_frame_cell .input[type="text"] { + padding-bottom: 1px; +} + +.openerp table.oe_frame td { + color: #4c4c4c; +} +.openerp td.oe_form_frame_cell { + padding: 2px; + position: relative; +} .openerp .oe_frame.oe_forms { clear: both; } @@ -889,6 +1013,9 @@ label.error { padding: 2px; position: relative; } +.openerp td.oe_form_field_boolean { + padding-top: 4px; +} .openerp td.oe_form_frame_cell.oe_form_group { padding: 0; } @@ -900,14 +1027,31 @@ label.error { } .openerp .oe_form_pager, .openerp .oe_list_pager { float: right; + font-size: 80%; + color: gray; + font-weight: bold; +} + +.openerp .oe_form_pager { + margin-right: 3px; +} + + +.openerp label.oe_label_help, .openerp label.oe_label, .openerp .oe_forms input[type="text"], .openerp .oe_forms input[type="password"], .openerp .oe_forms select, .openerp .oe_forms .oe_button, .openerp .oe_forms textarea { + font-size: 85%; } .openerp label.oe_label_help, .openerp label.oe_label { display: block; + color: #4c4c4c; + font-weight: normal; } .openerp label.oe_label_help { cursor: help; } +.openerp .oe_form_frame_cell .oe_label, .openerp .oe_form_frame_cell .oe_label_help { + font-weight: normal; +} .openerp #tiptip_content { font-size: 12px; } @@ -926,8 +1070,13 @@ label.error { } .openerp .oe_forms label.oe_label, .openerp .oe_forms label.oe_label_help { - margin: 3px 0 0 10px; + margin: 4px 0 0 3px; + white-space: nowrap; } +.openerp .oe_forms .searchview_group_content label.oe_label, .openerp .searchview_group_content .oe_forms label.oe_label_help { /* making a distinction between labels in search view and other labels */ + margin: 3px 0 0 3px; +} + .openerp label.oe_label_help span { font-size: 80%; color: darkgreen; @@ -949,13 +1098,29 @@ label.error { margin: 3px 0 0 0; } +/* Uneditable Form View */ +.openerp .oe_form_readonly { + +} +.openerp .oe_form_readonly .field_char, .openerp .oe_form_readonly .oe_form_field_email { + padding-top: 4px; + background-color: white; +} + +.openerp .oe_form_readonly .field_selection { + padding-top: 2px; + background-color: white; +} + + /* Inputs */ .openerp .oe_forms input[type="text"], .openerp .oe_forms input[type="password"], .openerp .oe_forms select, .openerp .oe_forms textarea { -moz-box-sizing: border-box; -webkit-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box; - padding: 0 2px 0 2px; + padding: 0 2px; + margin: 0 2px; border: 1px solid #999; -moz-border-radius: 3px; -webkit-border-radius: 3px; @@ -964,6 +1129,10 @@ label.error { min-width: 90px; color: #1f1f1f; } +.openerp .oe_forms select{ + padding-top: 2px; +} + .openerp .oe_forms textarea { resize:vertical; } @@ -979,6 +1148,9 @@ label.error { min-width: 100%; width: 100%; } +.openerp .oe_forms .button { + height: 22px; +} @-moz-document url-prefix() { /* Strange firefox behaviour on width: 100% + white-space: nowrap */ .openerp .oe_forms .oe_button { @@ -992,9 +1164,9 @@ label.error { .openerp .oe_input_icon { position: absolute; cursor: pointer; - right: 5px; - top: 3px; - z-index: 2; + right: 4px; + top: 5px; + z-index: 1; } .openerp .oe_datepicker_container { position: absolute; @@ -1003,7 +1175,6 @@ label.error { display: none; } .openerp .oe_datepicker_root { - position: relative; display: inline-block; } .openerp .oe_forms.oe_frame .oe_datepicker_root { @@ -1017,13 +1188,6 @@ label.error { right: 5px; top: 3px; } -.openerp img.oe_field_translate { - margin-left: -21px; - vertical-align: top; - cursor: pointer; - position: relative; - top: 4px; -} .openerp .oe_trad_field.touched { border: 1px solid green !important; } @@ -1055,10 +1219,14 @@ label.error { border: 0 solid #666; } .openerp .separator.horizontal { - font-weight: bold; + font-weight: bold; border-bottom-width: 1px; margin: 3px 4px 3px 1px; - height: 15px; + height: 17px; + font-size: 95%; +} +.openerp .separator.horizontal:empty { + height: 5px; } .openerp .oe_form_frame_cell.oe_form_separator_vertical { border-left: 1px solid #666; @@ -1095,6 +1263,14 @@ label.error { background: white; min-width: 90px; } +.openerp tbody.ui-widget-content { + margin-bottom: 10px; + border-spacing: 4px; +} +.openerp .ui-widget-header { + background-image: none; + background-color: white; +} /* Sidebar */ .openerp .view-manager-main-table { @@ -1108,16 +1284,13 @@ label.error { vertical-align: top; } -.openerp .view-manager-main-content { - width: 100%; -} .openerp .oe-view-manager-header { overflow: auto; } .openerp .oe-view-manager-header h2 { float: left; } -.openerp .oe-view-manager-header blockquote { +.openerp .oe_view_manager_menu_tips blockquote { display: none; font-size: 85%; margin: 0; @@ -1126,14 +1299,14 @@ label.error { padding: 1px 10px; color: #4C4C4C; } -.openerp .oe-view-manager-header blockquote p { +.openerp .oe_view_manager_menu_tips blockquote p { margin: 0; padding: 6px 1px 4px; } -.openerp .oe-view-manager-header blockquote div { +.openerp .oe_view_manager_menu_tips blockquote div { text-align: right; } -.openerp .oe-view-manager-header blockquote div button { +.openerp .oe_view_manager_menu_tips blockquote div button { border: none; background: none; padding: 0 4px; @@ -1365,6 +1538,7 @@ label.error { } .openerp .oe_forms .oe-m2o input[type="text"] { padding-right: 20px; + padding-top: 2px; } .openerp .oe-m2o-drop-down-button { margin-left: -23px; @@ -1374,12 +1548,12 @@ label.error { margin-bottom: -4px; cursor: pointer; } -.openerp .oe-m2o-cm-button img { - margin-left: 4px; -} .openerp .oe-m2o-disabled-cm { color: grey; } +.openerp ul[role="listbox"] li a { + font-size:80%; +} .parent_top { vertical-align: text-top; } @@ -1425,20 +1599,19 @@ label.error { /* Shortcuts*/ .oe-shortcut-toggle { height: 20px; + margin-top: 3px; padding: 0; width: 24px; cursor: pointer; display: block; - background: url(/web/static/src/img/add-shortcut.png) no-repeat bottom; + background: url(/web/static/src/img/add-shortcut.png) no-repeat center center; float: left; } .oe-shortcut-remove{ - background: url(/web/static/src/img/remove-shortcut.png) no-repeat bottom; + background: url(/web/static/src/img/remove-shortcut.png) no-repeat center center; } -/* ================ */ .oe-shortcuts { position: absolute; - margin: 0; padding: 6px 15px; top: 37px; @@ -1463,9 +1636,14 @@ label.error { color: #fff; text-align: center; border-left: 1px solid #909090; - padding: 4px; - font-size: 90%; + padding: 0 4px; + font-size: 80%; font-weight: normal; + vertical-align: top; +} + +.oe-shortcuts li:hover { + background-color: #666; } .oe-shortcuts li:first-child { border-left: none; @@ -1535,4 +1713,3 @@ ul.oe-arrow-list li.oe-arrow-list-selected .oe-arrow-list-after { .openerp .oe_view_editor_tree_grid a { display: block; } - diff --git a/addons/web/static/src/css/data_import.css b/addons/web/static/src/css/data_import.css index e5d1c9a910c..a63a52802f0 100644 --- a/addons/web/static/src/css/data_import.css +++ b/addons/web/static/src/css/data_import.css @@ -27,7 +27,7 @@ .openerp .oe-import fieldset.oe-closed table { display: none; } -.openerp .separator.horizontal { +.openerp .oe-import .separator.horizontal { font-weight: bold; border-bottom-width: 1px; margin: 6px 4px 6px 1px; diff --git a/addons/web/static/src/img/icons/lead-stage-won.png b/addons/web/static/src/img/icons/kanban-apply.png Binary files differindex 02effd5f14c..02effd5f14c 100644 --- a/addons/web/static/src/img/icons/lead-stage-won.png +++ b/addons/web/static/src/img/icons/kanban-apply.png diff --git a/addons/web/static/src/img/icons/lead-stage-pending.png b/addons/web/static/src/img/icons/kanban-pause.png Binary files differindex 9e9544ee178..9e9544ee178 100644 --- a/addons/web/static/src/img/icons/lead-stage-pending.png +++ b/addons/web/static/src/img/icons/kanban-pause.png diff --git a/addons/web/static/src/img/icons/lead-stage-lost.png b/addons/web/static/src/img/icons/kanban-stop.png Binary files differindex e1f98a26b59..e1f98a26b59 100644 --- a/addons/web/static/src/img/icons/lead-stage-lost.png +++ b/addons/web/static/src/img/icons/kanban-stop.png diff --git a/addons/web/static/src/img/icons/save-document.png b/addons/web/static/src/img/icons/save-document.png Binary files differnew file mode 100644 index 00000000000..8933ca0bed9 --- /dev/null +++ b/addons/web/static/src/img/icons/save-document.png diff --git a/addons/web/static/src/img/iconset-b-remove.png b/addons/web/static/src/img/iconset-b-remove.png Binary files differnew file mode 100644 index 00000000000..497f03b4ceb --- /dev/null +++ b/addons/web/static/src/img/iconset-b-remove.png diff --git a/addons/web/static/src/js/boot.js b/addons/web/static/src/js/boot.js index 7440ae42dbc..14f50adb473 100644 --- a/addons/web/static/src/js/boot.js +++ b/addons/web/static/src/js/boot.js @@ -59,7 +59,7 @@ openerp.web = function(instance) { openerp.web.formats(instance); openerp.web.chrome(instance); openerp.web.data(instance); - var files = ["views","search","list","form","list_editable","web_mobile","view_tree","data_export","data_import","view_editor", 'embed']; + var files = ["views","search","list","form", "page","list_editable","web_mobile","view_tree","data_export","data_import","view_editor","embed"]; for(var i=0; i<files.length; i++) { if(openerp.web[files[i]]) { openerp.web[files[i]](instance); diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js index b69b6f40cc4..1eaab8b2dee 100644 --- a/addons/web/static/src/js/chrome.js +++ b/addons/web/static/src/js/chrome.js @@ -30,8 +30,6 @@ openerp.web.Notification = openerp.web.Widget.extend(/** @lends openerp.web.Not this.$element.notify('create', 'oe_notification_alert', { title: title, text: text - }, { - expires: false }); } @@ -242,6 +240,7 @@ openerp.web.Database = openerp.web.Widget.extend(/** @lends openerp.web.Database init: function(parent, element_id, option_id) { this._super(parent, element_id); this.$option_id = $('#' + option_id); + this.unblockUIFunction = $.unblockUI; }, start: function() { this._super(); @@ -337,11 +336,27 @@ openerp.web.Database = openerp.web.Widget.extend(/** @lends openerp.web.Database self.widget_parent.do_login( info.db, admin.login, admin.password); self.stop(); - $.unblockUI(); + self.unblockUI(); }); }); }, /** + * Blocks UI and replaces $.unblockUI by a noop to prevent third parties + * from unblocking the UI + */ + blockUI: function () { + $.blockUI(); + $.unblockUI = function () {}; + }, + /** + * Reinstates $.unblockUI so third parties can play with blockUI, and + * unblocks the UI + */ + unblockUI: function () { + $.unblockUI = this.unblockUIFunction; + $.unblockUI(); + }, + /** * Displays an error dialog resulting from the various RPC communications * failing over themselves * @@ -366,10 +381,10 @@ openerp.web.Database = openerp.web.Widget.extend(/** @lends openerp.web.Database self.$option_id.find("form[name=create_db_form]").validate({ submitHandler: function (form) { var fields = $(form).serializeArray(); - $.blockUI(); + self.blockUI(); self.rpc("/web/database/create", {'fields': fields}, function(result) { if (result.error) { - $.unblockUI(); + self.unblockUI(); self.display_error(result); return; } @@ -417,7 +432,7 @@ openerp.web.Database = openerp.web.Widget.extend(/** @lends openerp.web.Database .html(QWeb.render("BackupDB", self)) .find("form[name=backup_db_form]").validate({ submitHandler: function (form) { - $.blockUI(); + self.blockUI(); self.session.get_file({ form: form, error: function (body) { @@ -427,7 +442,7 @@ openerp.web.Database = openerp.web.Widget.extend(/** @lends openerp.web.Database error: error[1] }); }, - complete: $.unblockUI + complete: $.proxy(self, 'unblockUI') }); } }); @@ -438,7 +453,7 @@ openerp.web.Database = openerp.web.Widget.extend(/** @lends openerp.web.Database self.$option_id.find("form[name=restore_db_form]").validate({ submitHandler: function (form) { - $.blockUI(); + self.blockUI(); $(form).ajaxSubmit({ url: '/web/database/restore', type: 'POST', @@ -463,9 +478,7 @@ openerp.web.Database = openerp.web.Widget.extend(/** @lends openerp.web.Database }) } }, - complete: function () { - $.unblockUI(); - } + complete: $.proxy(self, 'unblockUI') }); } }); @@ -824,7 +837,8 @@ openerp.web.Menu = openerp.web.Widget.extend(/** @lends openerp.web.Menu# */{ init: function(parent, element_id, secondary_menu_id) { this._super(parent, element_id); this.secondary_menu_id = secondary_menu_id; - this.$secondary_menu = $("#" + secondary_menu_id).hide(); + this.$secondary_menu = $("#" + secondary_menu_id); + this.$secondary_menu.hide(); this.menu = false; this.folded = false; if (window.localStorage) { @@ -898,7 +912,10 @@ openerp.web.Menu = openerp.web.Widget.extend(/** @lends openerp.web.Menu# */{ sub_menu_visible = $sub_menu.is(':visible'); this.$secondary_menu.find('.oe_secondary_menu').hide(); - $('.active', this.$element.add(this.$secondary_menu.show())).removeClass('active'); + if (this.$secondary_menu.hasClass('oe_folded')) { + this.$secondary_menu.show(); + } + $('.active', this.$element.add(this.$secondary_menu)).removeClass('active'); $main_menu.add($clicked_menu).add($sub_menu).addClass('active'); if (!(this.folded && manual)) { @@ -960,6 +977,8 @@ openerp.web.Menu = openerp.web.Widget.extend(/** @lends openerp.web.Menu# */{ if (data.action.length) { var action = data.action[0][2]; self.on_action(action); + } else { + self.on_action({type: 'null_action'}); } }, on_action: function(action) { diff --git a/addons/web/static/src/js/core.js b/addons/web/static/src/js/core.js index b67a0c59147..ddfca9d4698 100644 --- a/addons/web/static/src/js/core.js +++ b/addons/web/static/src/js/core.js @@ -1109,7 +1109,8 @@ openerp.web.TranslationDataBase = openerp.web.Class.extend(/** @lends openerp.we set_bundle: function(translation_bundle) { var self = this; this.db = {}; - var modules = _.keys(translation_bundle.modules).sort(); + var modules = _.keys(translation_bundle.modules); + modules.sort(); if (_.include(modules, "web")) { modules = ["web"].concat(_.without(modules, "web")); } @@ -1156,7 +1157,8 @@ openerp.web._t = new openerp.web.TranslationDataBase().build_translation_functio openerp.web.qweb = new QWeb2.Engine(); openerp.web.qweb.debug = (window.location.search.indexOf('?debug') !== -1); openerp.web.qweb.default_dict = { - '_' : _ + '_' : _, + '_t' : openerp.web._t } openerp.web.qweb.format_text_node = function(s) { // Note that 'this' is the Qweb Node of the text @@ -1174,6 +1176,7 @@ openerp.web.qweb.format_text_node = function(s) { /** Setup default connection */ openerp.connection = new openerp.web.Connection(); +openerp.web.qweb.default_dict['__debug__'] = openerp.connection.debug; }; diff --git a/addons/web/static/src/js/data.js b/addons/web/static/src/js/data.js index 1f2520ee774..26147b4d4e0 100644 --- a/addons/web/static/src/js/data.js +++ b/addons/web/static/src/js/data.js @@ -265,14 +265,25 @@ openerp.web.DataSet = openerp.web.Widget.extend( /** @lends openerp.web.DataSet return this; }, select_id: function(id) { - var idx = _.indexOf(this.ids, id); - if (idx === -1) { + var idx = this.get_id_index(id); + if (idx === null) { return false; } else { this.index = idx; return true; } }, + get_id_index: function(id) { + for (var i=0, ii=this.ids.length; i<ii; i++) { + // Here we use type coercion because of the mess potentially caused by + // OpenERP ids fetched from the DOM as string. (eg: dhtmlxcalendar) + // OpenERP ids can be non-numeric too ! (eg: recursive events in calendar) + if (id == this.ids[i]) { + return i; + } + } + return null; + }, /** * Read records. * @@ -414,8 +425,8 @@ openerp.web.DataSet = openerp.web.Widget.extend( /** @lends openerp.web.DataSet return this.rpc('/web/dataset/call', { model: this.model, method: method, - domain_id: domain_index || null, - context_id: context_index || null, + domain_id: domain_index == undefined ? null : domain_index, + context_id: context_index == undefined ? null : context_index, args: args || [] }, callback, error_callback); }, @@ -433,7 +444,7 @@ openerp.web.DataSet = openerp.web.Widget.extend( /** @lends openerp.web.DataSet model: this.model, method: method, domain_id: null, - context_id: 1, + context_id: args.length - 1, args: args || [] }, callback, error_callback); }, diff --git a/addons/web/static/src/js/data_import.js b/addons/web/static/src/js/data_import.js index d5e789ce3e7..cd699c0481d 100644 --- a/addons/web/static/src/js/data_import.js +++ b/addons/web/static/src/js/data_import.js @@ -183,6 +183,20 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ this.$element.find('#result').empty(); var headers, result_node = this.$element.find("#result"); + if (results['error']) { + result_node.append(QWeb.render('ImportView.error', { + 'error': results['error']})); + this.$element.find('fieldset').removeClass('oe-closed'); + return; + } + if (results['success']) { + if (this.widget_parent.widget_parent.active_view == "list") { + this.widget_parent.reload_content(); + } + this.stop(); + return; + } + if (results['records']) { var lines_to_skip = parseInt(this.$element.find('#csv_skip').val(), 10), with_headers = this.$element.find('#file_has_headers').prop('checked'); @@ -195,16 +209,6 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ : results.records })); this.$element.find('fieldset').addClass('oe-closed'); - } else if (results['error']) { - result_node.append(QWeb.render('ImportView.error', { - 'error': results['error']})); - this.$element.find('fieldset').removeClass('oe-closed'); - } else if (results['success']) { - if (this.widget_parent.widget_parent.active_view == "list") { - this.widget_parent.reload_content(); - } - this.stop(); - return; } this.$element.find('form').removeClass('oe-import-no-result'); diff --git a/addons/web/static/src/js/formats.js b/addons/web/static/src/js/formats.js index 18b679bed3c..13cceaeba8d 100644 --- a/addons/web/static/src/js/formats.js +++ b/addons/web/static/src/js/formats.js @@ -158,16 +158,17 @@ openerp.web.parse_value = function (value, descriptor, value_if_empty) { var tmp = Number(value); if (!isNaN(tmp)) return tmp; - tmp = value.replace(openerp.web._t.database.parameters.decimal_point, "."); - var tmp2 = tmp; + + var tmp2 = value; do { tmp = tmp2; tmp2 = tmp.replace(openerp.web._t.database.parameters.thousands_sep, ""); } while(tmp !== tmp2); - tmp = Number(tmp); - if (isNaN(tmp)) + var reformatted_value = tmp.replace(openerp.web._t.database.parameters.decimal_point, "."); + var parsed = Number(reformatted_value); + if (isNaN(parsed)) throw new Error(value + " is not a correct float"); - return tmp; + return parsed; case 'float_time': var float_time_pair = value.split(":"); if (float_time_pair.length != 2) diff --git a/addons/web/static/src/js/search.js b/addons/web/static/src/js/search.js index f95801975b5..c3e27fc52be 100644 --- a/addons/web/static/src/js/search.js +++ b/addons/web/static/src/js/search.js @@ -139,6 +139,7 @@ openerp.web.SearchView = openerp.web.Widget.extend(/** @lends openerp.web.Search } }, on_loaded: function(data) { + this.fields_view = data.fields_view; if (data.fields_view.type !== 'search' || data.fields_view.arch.tag !== 'search') { throw new Error(_.str.sprintf( @@ -197,18 +198,22 @@ openerp.web.SearchView = openerp.web.Widget.extend(/** @lends openerp.web.Search var self = this; var select = this.$element.find(".oe_search-view-filters-management"); var val = select.val(); - - if (val.slice(0,1) == "_") { // useless action - select.val("_filters"); - return; - } - if (val.slice(0, "get:".length) == "get:") { - val = val.slice("get:".length); - val = parseInt(val); - var filter = this.managed_filters[val]; - this.on_search([filter.domain], [filter.context], []); - } else if (val == "save_filter") { - select.val("_filters"); + switch(val) { + case 'add_to_dashboard': + this.on_add_to_dashboard(); + break; + case 'manage_filters': + this.do_action({ + res_model: 'ir.filters', + views: [[false, 'list'], [false, 'form']], + type: 'ir.actions.act_window', + context: {"search_default_user_id": this.session.uid, + "search_default_model_id": this.dataset.model}, + target: "current", + limit : 80 + }); + break; + case 'save_filter': var data = this.build_search_data(); var context = new openerp.web.CompoundContext(); _.each(data.contexts, function(x) { @@ -241,19 +246,63 @@ openerp.web.SearchView = openerp.web.Widget.extend(/** @lends openerp.web.Search }} ] }); - } else { // manage_filters - select.val("_filters"); - this.do_action({ - res_model: 'ir.filters', - views: [[false, 'list'], [false, 'form']], - type: 'ir.actions.act_window', - context: {"search_default_user_id": this.session.uid, - "search_default_model_id": this.dataset.model}, - target: "current", - limit : 80, - auto_search : true - }); + break; } + if (val.slice(0, 4) == "get:") { + val = val.slice(4); + val = parseInt(val, 10); + var filter = this.managed_filters[val]; + this.on_search([filter.domain], [filter.context], []); + } else { + select.val(''); + } + }, + on_add_to_dashboard: function() { + this.$element.find(".oe_search-view-filters-management")[0].selectedIndex = 0; + var self = this, + menu = openerp.webclient.menu, + $dialog = $(QWeb.render("SearchView.add_to_dashboard", { + dashboards : menu.data.data.children, + selected_menu_id : menu.$element.find('a.active').data('menu') + })); + $dialog.find('input').val(this.fields_view.name); + $dialog.dialog({ + modal: true, + title: _t("Add to Dashboard"), + buttons: [ + {text: _t("Cancel"), click: function() { + $(this).dialog("close"); + }}, + {text: _t("OK"), click: function() { + $(this).dialog("close"); + var menu_id = $(this).find("select").val(), + title = $(this).find("input").val(), + data = self.build_search_data(), + context = new openerp.web.CompoundContext(), + domain = new openerp.web.CompoundDomain(); + _.each(data.contexts, function(x) { + context.add(x); + }); + _.each(data.domains, function(x) { + domain.add(x); + }); + self.rpc('/web/searchview/add_to_dashboard', { + menu_id: menu_id, + action_id: self.widget_parent.action.id, + context_to_save: context, + domain: domain, + view_mode: self.widget_parent.active_view, + name: title + }, function(r) { + if (r === false) { + self.do_warn("Could not add filter to dashboard"); + } else { + self.do_notify("Filter added to dashboard", ''); + } + }); + }} + ] + }); }, /** * Performs the search view collection of widget data. diff --git a/addons/web/static/src/js/view_editor.js b/addons/web/static/src/js/view_editor.js index c53a7f8df38..352856c2b6c 100644 --- a/addons/web/static/src/js/view_editor.js +++ b/addons/web/static/src/js/view_editor.js @@ -1,13 +1,16 @@ openerp.web.view_editor = function(openerp) { +var _t = openerp.web._t; var QWeb = openerp.web.qweb; openerp.web.ViewEditor = openerp.web.Widget.extend({ init: function(parent, element_id, dataset, view, options) { this._super(parent); this.element_id = element_id this.parent = parent - this.dataset = dataset; + this.dataset = new openerp.web.DataSetSearch(this, 'ir.ui.view', null, null), this.model = dataset.model; - this.xml_id = 0; + this.xml_element_id = 0; + this.property = openerp.web.ViewEditor.property_widget; + this.one_object = false; }, start: function() { this.init_view_editor(); @@ -15,24 +18,24 @@ openerp.web.ViewEditor = openerp.web.Widget.extend({ init_view_editor: function() { var self = this; var action = { - name:'ViewEditor', - context:this.session.user_context, - domain: [["model", "=", this.dataset.model]], - res_model : 'ir.ui.view', - views : [[false, 'list']], + name: _.str.sprintf("Manage Views (%s)", this.model), + context: this.session.user_context, + domain: [["model", "=", this.model]], + res_model: 'ir.ui.view', + views: [[false, 'list']], type: 'ir.actions.act_window', target: "current", - limit : 80, - auto_search : true, + limit: this.dataset.limit || 80, flags: { sidebar: false, deletable: false, views_switcher: false, - action_buttons:false, - search_view:false, - pager:false, - radio: true - } + action_buttons: false, + search_view: false, + pager: false, + radio: true, + select_view_id: self.parent.fields_view.view_id + }, }; this.view_edit_dialog = new openerp.web.Dialog(this, { modal: true, @@ -41,207 +44,308 @@ openerp.web.ViewEditor = openerp.web.Widget.extend({ height: 500, buttons: { "Create": function(){ - //to do + self.on_create_view(); }, "Edit": function(){ - self.xml_id=0; - self.get_data(); + self.xml_element_id = 0; + self.get_arch(); + }, + "Remove": function(){ + self.do_delete_view(); }, "Close": function(){ self.view_edit_dialog.close(); } - } - }); - this.view_edit_dialog.start().open(); - var action_manager = new openerp.web.ActionManager(this); - action_manager.appendTo(this.view_edit_dialog); - action_manager.do_action(action); - }, - check_attr: function(xml, tag, level) { - var obj = {'child_id': [], 'id': this.xml_id++, 'level': level+1, 'att_list': [], 'name': ""}; - var render_name = "<" + tag; - obj.att_list.push(tag); - $(xml).each(function() { - _.each(this.attributes, function(attrs){ - if (tag != 'button') { - if (attrs.nodeName == "string" || attrs.nodeName == "name" || attrs.nodeName == "index") { - render_name += ' ' + attrs.nodeName + '=' + '"' + attrs.nodeValue + '"' ; + }, + }).start().open(); + this.main_view_id = this.parent.fields_view.view_id; + this.action_manager = new openerp.web.ActionManager(this); + this.action_manager.appendTo(this.view_edit_dialog); + $.when(this.action_manager.do_action(action)).then(function() { + var viewmanager = self.action_manager.inner_viewmanager, + controller = viewmanager.views[viewmanager.active_view].controller; + controller.on_loaded.add_last(function(){ + $(controller.groups).bind({ + 'selected': function(e, ids, records) { + self.main_view_id = ids[0]; } - } else { - if (attrs.nodeName == "name") { - render_name += ' ' + attrs.nodeName + '=' + '"' + attrs.nodeValue + '"'; - } - } - obj.att_list.push( [attrs.nodeName,attrs.nodeValue] ); + }) }); - render_name+= ">"; }); - obj.name = render_name; - return obj; }, - save_object: function(val, parent_list, child_obj_list) { + on_create_view: function() { var self = this; - var check_id = parent_list[0]; - var p_list = parent_list.slice(1); - if (val.child_id.length != 0) { - _.each(val.child_id, function(val, key) { - if (val.id == check_id) { - if (p_list.length!=0) { - self.save_object(val, p_list, child_obj_list); - } else { - val.child_id = child_obj_list; - return false; + this.create_view_dialog = new openerp.web.Dialog(this, { + modal: true, + title: _.str.sprintf("Create a view (%s)", self.model), + width: 500, + height: 400, + buttons: { + "Save": function(){ + var view_values = {}; + var warn = false; + _.each(self.create_view_widget, function(widget) { + if (widget.is_invalid) { + warn = true; + return false; + }; + if (widget.dirty && !widget.is_invalid) { + view_values[widget.name] = widget.get_value(); + } + }); + if (warn) { + self.on_valid_create_view(self.create_view_widget); + } else { + $.when(self.do_save_view(view_values)).then(function() { + self.create_view_dialog.close(); + var controller = self.action_manager.inner_viewmanager.views[self.action_manager.inner_viewmanager.active_view].controller; + controller.reload_content(); + }); + } + }, + "Cancel": function(){ + self.create_view_dialog.close(); } } + }); + this.create_view_dialog.start().open(); + var view_widget = [{'name': 'view_name', 'string':'View Name', 'type': 'char', 'required': true, 'value' : this.model + '.custom_' + Math.round(Math.random() * 1000)}, + {'name': 'view_type', 'string': 'View Type', 'type': 'selection', 'required': true, 'value': 'Form', 'selection': [['',''],['tree', 'Tree'],['form', 'Form'],['graph', 'Graph'],['calendar', 'Calender']]}, + {'name': 'proirity', 'string': 'Priority', 'type': 'float', 'required': true, 'value':'16'}]; + this.create_view_dialog.$element.append('<table id="create_view" style="width:400px" class="oe_forms"></table>'); + this.create_view_widget = []; + _.each(view_widget, function(widget) { + var type_widget = new (self.property.get_any([widget.type])) (self.create_view_dialog, widget); + self.create_view_dialog.$element.find('table[id=create_view]').append('<tr><td width="100px" align="right">' + widget.string + ':</td>' + type_widget.render()+'</tr>'); + var value = null; + if (widget.value) { + value = widget.value; + type_widget.dirty = true; + } + type_widget.start(); + type_widget.set_value(value) + self.create_view_widget.push(type_widget); + }); + }, + do_save_view: function(values) { + def = $.Deferred(); + var field_dataset = new openerp.web.DataSetSearch(this, this.model, null, null); + var model_dataset = new openerp.web.DataSetSearch(this, 'ir.model', null, null); + var view_string = "", field_name = false, self = this; + field_dataset.call( 'fields_get', [], function(fields) { + _.each(['name', 'x_name'], function(value) { + if (_.include(_.keys(fields), value)) { + field_name = value; + return false; + } + }); + if (field_name) { + model_dataset.read_slice(['name','field_id'], {"domain": [['model','=',self.model]]}, function(records) { + if (records) {view_string = records[0].name;} + var arch = _.str.sprintf("<?xml version='1.0'?>\n<%s string='%s'>\n\t<field name='%s'/>\n</%s>", values.view_type, view_string, field_name, values.view_type); + var vals = {'model': self.model, 'name': values.view_name, 'priority': values.priority, 'type': values.view_type, 'arch': arch}; + self.dataset.create(vals, function(suc) { + def.resolve(); + }); + }); + } + }); + return def.promise(); + }, + on_valid_create_view: function(widgets) { + var msg = "<ul>"; + _.each(widgets, function(widget) { + if (widget.is_invalid) { + msg += "<li>" + widget.name + "</li>"; + } + }); + msg += "</ul>"; + this.do_warn("The following fields are invalid :", msg); + }, + add_node_name : function(node) { + if(node.tagName.toLowerCase() == "button" || node.tagName.toLowerCase() == "field"){ + return (node.getAttribute('name'))? + _.str.sprintf( "<%s name='%s'>",node.tagName.toLowerCase(), node.getAttribute('name')): + _.str.sprintf( "<%s>",node.tagName.toLowerCase()); + }else if(node.tagName.toLowerCase() == "group"){ + return (node.getAttribute('string'))? + _.str.sprintf( "<%s>",node.getAttribute('string')): + _.str.sprintf( "<%s>",node.tagName.toLowerCase()); + }else{ + return (node.getAttribute('string'))? + _.str.sprintf( "<%s string='%s'>",node.tagName.toLowerCase(), node.getAttribute('string')): + _.str.sprintf( "<%s>",node.tagName.toLowerCase()); + } + }, + do_delete_view: function() { + if (confirm(_t("Do you really want to remove this view?"))) { + var controller = this.action_manager.inner_viewmanager.views[this.action_manager.inner_viewmanager.active_view].controller; + this.dataset.unlink([this.main_view_id]).then(function() { + controller.reload_content(); + this.main_view_id = self.parent.fields_view.view_id; }); + } + }, + create_View_Node: function(node){ + ViewNode = { + 'level': ($(node).parents()).length + 1, + 'id': this.xml_element_id += 1, + 'att_list': [], + 'name': this.add_node_name(node), + 'child_id': [] + } + ViewNode.att_list.push(node.tagName.toLowerCase()); + _.each(node.attributes, function(att) { + ViewNode.att_list.push([att.nodeName, att.nodeValue]); + }); + return ViewNode; + }, + append_child_object: function(main_object, parent_id, child_obj_list) { + var self = this; + if (main_object.id == parent_id) { + main_object.child_id = child_obj_list; + return main_object; } else { - val.child_id = child_obj_list; + _.each(main_object.child_id, function(child_object) { + self.append_child_object(child_object, parent_id, child_obj_list); + }); } }, - xml_node_create: function(xml, root, parent_list, parent_id, main_object){ + convert_arch_to_obj: function(xml_Node, main_object, parent_id) { var self = this; var child_obj_list = []; - var children_list = $(xml).filter(root).children(); - var parents = $(children_list[0]).parents().get(); - _.each(children_list, function (child_node) { - child_obj_list.push(self.check_attr(child_node,child_node.tagName.toLowerCase(),parents.length)); + _.each(xml_Node, function(element) { + child_obj_list.push(self.create_View_Node(element)); }); - if (children_list.length != 0) { - if (parents.length <= parent_list.length) { - parent_list.splice(parents.length - 1); + this.append_child_object(main_object, parent_id, child_obj_list); + var obj_xml_list = _.zip(xml_Node, child_obj_list); + _.each(obj_xml_list, function(node) { + var children = _.filter(node[0].childNodes, function(child) { + return child.nodeType == 1; + }); + if (children) { + self.convert_arch_to_obj(children, main_object, node[1].id); } - parent_list.push(parent_id); - self.save_object(main_object[0], parent_list.slice(1), child_obj_list); - } - for (var i = 0; i < children_list.length; i++) { - self.xml_node_create - (children_list[i], children_list[i].tagName.toLowerCase(), - parent_list, child_obj_list[i].id, main_object); - } + }); return main_object; }, parse_xml: function(arch, view_id) { - var root = $(arch).filter(":first")[0]; - var tag = root.tagName.toLowerCase(); - var obj ={'child_id':[],'id':this.xml_id++,'level':0,'att_list':[],'name':"<view view_id='"+view_id+"'>"}; - var root_object = this.check_attr(root,tag,0); - obj.child_id = this.xml_node_create(arch, tag, [], this.xml_id-1, [root_object], []) - return [obj]; - }, - get_data: function() { + main_object = { + 'level': 0, + 'id': this.xml_element_id +=1, + 'att_list': [], + 'name': _.str.sprintf("<view view_id = %s>", view_id), + 'child_id': [] + }; + var xml_arch = QWeb.load_xml(arch); + return [this.convert_arch_to_obj(xml_arch.childNodes, main_object, this.xml_element_id)]; + }, + get_arch: function() { var self = this; var view_arch_list = []; - self.main_view_id =((this.view_edit_dialog.$element.find("input[name='radiogroup']:checked").parent()).parent()).attr('data-id'); - var ve_dataset = new openerp.web.DataSet(this, 'ir.ui.view'); - ve_dataset.read_ids([parseInt(self.main_view_id)], ['arch', 'type'], function (arch) { - one_object = self.parse_xml(arch[0].arch,self.main_view_id); - self.main_view_type = arch[0].type - view_arch_list.push({"view_id" : self.main_view_id, "arch" : arch[0].arch}); - dataset = new openerp.web.DataSetSearch(self, 'ir.ui.view', null, null); - dataset.read_slice([], {domain : [['inherit_id','=', parseInt(self.main_view_id)]]}, function (result) { - _.each(result, function(res) { - view_arch_list.push({"view_id":res.id,"arch":res.arch}); - self.inherit_view(one_object, res); + this.dataset.read_ids([parseInt(self.main_view_id)], ['arch', 'type'], function(arch) { + if (arch.length) { + var arch_object = self.parse_xml(arch[0].arch, self.main_view_id); + self.main_view_type = arch[0].type == 'tree'? 'list': arch[0].type; + view_arch_list.push({"view_id": self.main_view_id, "arch": arch[0].arch}); + self.dataset.read_slice([], {domain: [['inherit_id','=', parseInt(self.main_view_id)]]}, function(result) { + _.each(result, function(res) { + view_arch_list.push({"view_id": res.id, "arch": res.arch}); + self.inherit_view(arch_object, res); + }); + return self.edit_view({"main_object": arch_object, + "parent_child_id": self.parent_child_list(arch_object, []), + "arch": view_arch_list}); }); - return self.edit_view({"main_object": one_object, - "parent_child_id": self.parent_child_list(one_object, []), - "arch": view_arch_list}); - }); + } else { + self.do_warn("Please select view in list :"); + } }); }, - parent_child_list : function(one_object, p_list) { + parent_child_list : function(one_object, parent_list) { var self = this; - _.each(one_object , function(element){ - if(element.child_id.length != 0){ - p_list.push({"key":element.id,"value":_.pluck(element.child_id, 'id')}); - self.parent_child_list(element.child_id, p_list); + _.each(one_object , function(element) { + if (element.child_id.length != 0) { + parent_list.push({"key": element.id, "value": _.pluck(element.child_id, 'id')}); + self.parent_child_list(element.child_id, parent_list); } }); - return p_list; + return parent_list; }, - inherit_view : function(one_object, result) { - var self = this; - var root = $(result.arch).filter('*'); - var xml_list = []; - if (root[0].tagName.toLowerCase() == "data") { - xml_list = $(root[0]).children(); + inherit_view : function(arch_object, result) { + var self = this, xml_list = [], xml_arch = QWeb.load_xml(result.arch); + if (xml_arch.childNodes[0].tagName == "data") { + xml_list = _.filter(xml_arch.childNodes[0].childNodes, function(child) { + return child.nodeType == 1; + }); } else { - xml_list.push(root[0]); + xml_list.push( xml_arch.childNodes[0]); } - _.each(xml_list , function(xml){ - var parent_id; - var check_list = []; - var xpath_object = self.parse_xml(xml,result.id); - if (xml.tagName.toLowerCase() == "xpath" ) { - var part_expr = _.without($(xml).attr('expr').split("/"),""); - _.each(part_expr,function(part){ - check_list.push(_.without($.trim(part.replace(/[^a-zA-Z 0-9 _]+/g,'!')).split("!"),"")); + _.each(xml_list, function(xml) { + var expr_to_list = [], xpath_arch_object = self.parse_xml(QWeb.tools.xml_node_to_string(xml), result.id); + if (xml.tagName == "xpath") { + var part_expr = _.without(xml.getAttribute('expr').split("/"), ""); + _.each(part_expr, function(part) { + expr_to_list.push(_.without($.trim(part.replace(/[^a-zA-Z 0-9 _]+/g,'!')).split("!"), "")); }); } else { - var temp = []; - _.each(xpath_object[0].child_id[0].att_list, function(list){ - if(!_.include(list, "position")){ - temp.push(list); - } + var temp = _.reject(xpath_arch_object[0].child_id[0].att_list, function(list) { + return list instanceof Array? _.include(list, "position"): false; }); - check_list = [_.flatten(temp)]; + expr_to_list = [_.flatten(temp)]; } - self.full_path_search(check_list ,one_object ,xpath_object); + self.inherit_apply(expr_to_list, arch_object ,xpath_arch_object); }); }, - full_path_search: function(check_list ,val ,xpath_object) { + inherit_apply: function(expr_list ,arch_object ,xpath_arch_object) { var self = this; - if(xpath_object.length!=0){ - var check = check_list[0]; - var obj; + if (xpath_arch_object.length) { + var check = expr_list[0], obj = false; switch (check.length) { case 2: - if(parseInt(check[1])){ - var list_1 = _.select(val,function(element){ - var main_list = _.flatten(element.att_list); - return _.include(main_list, check[0]); + if (parseInt(check[1])) { + //for field[3] + var temp_list = _.select(arch_object, function(element) { + return _.include(_.flatten(element.att_list), check[0]); }); - obj = val[_.indexOf(val,list_1[parseInt(check[1])-1])]; + obj = arch_object[_.indexOf(arch_object, temp_list[parseInt(check[1]) - 1])]; } else { - obj = _.detect(val, function(element){ - var main_list = _.flatten(element.att_list); - return _.include(main_list, check[0]); + //for notebook[last()] + obj = _.detect(arch_object, function(element) { + return _.include(_.flatten(element.att_list), check[0]); }); } break; case 3: - obj = _.detect(val,function(element){ - var main_list = _.flatten(element.att_list); - check = _.uniq(check); - var insert = _.intersection(main_list,check); - if(insert.length == check.length ){return element;} + //for field[@name='type'] + obj = _.detect(arch_object, function(element){ + if ((_.intersection(_.flatten(element.att_list), _.uniq(check))).length == check.length) { + return element; + } }); break; case 1: - var list_1 = _.select(val,function(element){ - var main_list = _.flatten(element.att_list); - return _.include(main_list, check[0]); + //for /form/notebook + var temp_list = _.select(arch_object, function(element) { + return _.include(_.flatten(element.att_list), check[0]); }); - if(list_1.length != 0){ - (check_list.length == 1)? obj = list_1[0] : check_list.shift(); + if (temp_list.length != 0) { + expr_list.length == 1 ? obj = temp_list[0] : expr_list.shift(); } break; } - if(obj) { - check_list.shift(); - if (check_list.length !=0){ - self.full_path_search(check_list ,obj.child_id ,xpath_object); + if (obj) { + expr_list.shift(); + if (expr_list.length) { + self.inherit_apply(expr_list, obj.child_id, xpath_arch_object); } else { - var level = obj.level+1; - self.increase_level(xpath_object[0], level) - obj.child_id.push(xpath_object[0]); - xpath_object.pop(); - return; + self.increase_level(xpath_arch_object[0], obj.level + 1); + obj.child_id.push(xpath_arch_object[0]); + xpath_arch_object.pop(); } - } - else { - _.each(val,function(element){ - self.full_path_search(check_list ,element.child_id ,xpath_object); + } else { + _.each(arch_object, function(element) { + self.inherit_apply(expr_list, element.child_id, xpath_arch_object); }); } } @@ -253,31 +357,40 @@ openerp.web.ViewEditor = openerp.web.Widget.extend({ self.increase_level(val, level + 1); }); }, + do_select_row: function(row_id) { + this.edit_xml_dialog.$element.find("tr[id^='viewedit-']").removeClass('ui-selected'); + this.edit_xml_dialog.$element.find("tr[id=viewedit-" + row_id + "]").addClass('ui-selected'); + }, + do_parent_img_hide_show: function(img) { + if ($(img).attr('src') == '/web/static/src/img/collapse.gif') { + $(img).attr('src', '/web/static/src/img/expand.gif'); + this.on_expand(img); + } else { + $(img).attr('src', '/web/static/src/img/collapse.gif'); + this.on_collapse(img); + } + }, edit_view: function(one_object) { var self = this; - this.edit_xml_dialog = new openerp.web.Dialog(this,{ + this.one_object = one_object; + this.edit_xml_dialog = new openerp.web.Dialog(this, { modal: true, - title: 'View Editor', + title: _.str.sprintf("View Editor %d - %s", self.main_view_id, self.model), width: 750, height: 500, buttons: { - "Inherited View": function(){ - //todo - }, - "Preview": function(){ + "Preview": function() { var action = { - context:self.session.user_context, - res_model : self.model, - views : [[self.main_view_id, self.main_view_type]], + context: self.session.user_context, + res_model: self.model, + views: [[self.main_view_id, self.main_view_type]], type: 'ir.actions.act_window', target: "new", flags: { sidebar: false, views_switcher: false, - action_buttons:false, - search_view:false, - pager:false - } + action_buttons: false, + }, }; var action_manager = new openerp.web.ActionManager(self); action_manager.do_action(action); @@ -286,231 +399,766 @@ openerp.web.ViewEditor = openerp.web.Widget.extend({ self.edit_xml_dialog.close(); } } + }).start().open(); + var no_property_att = []; + _.each(_PROPERTIES, function(val, key) { + if (! val.length) no_property_att.push(key); }); - this.edit_xml_dialog.start().open(); - this.edit_xml_dialog.$element.html(QWeb.render('view_editor', { - 'data': one_object['main_object'] - })); + this.edit_xml_dialog.$element.html(QWeb.render('view_editor', {'data': one_object['main_object'], 'no_properties': no_property_att})); this.edit_xml_dialog.$element.find("tr[id^='viewedit-']").click(function() { - self.edit_xml_dialog.$element.find("tr[id^='viewedit-']").removeClass('ui-selected'); - $(this).addClass('ui-selected'); + self.do_select_row(this.id.split('-')[1]); }); this.edit_xml_dialog.$element.find("img[id^='parentimg-']").click(function() { - if ($(this).attr('src') == '/web/static/src/img/collapse.gif') { - $(this).attr('src', '/web/static/src/img/expand.gif'); - self.on_expand(this); - } else { - $(this).attr('src', '/web/static/src/img/collapse.gif'); - var id = this.id.split('-')[1]; - self.on_collapse(this,one_object['parent_child_id'], one_object['main_object']); - } + self.do_parent_img_hide_show(this); }); this.edit_xml_dialog.$element.find("img[id^='side-']").click(function() { - var side = $(this).closest("tr[id^='viewedit-']") - var id_tr = (side.attr('id')).split('-')[1]; - var img = side.find("img[id='parentimg-"+id_tr+"']").attr('src'); ; - var level = parseInt(side.attr('level')); - var list_shift =[]; - var last_tr; - var cur_tr = side; - list_shift.push(side); - var next_tr; - var ls = side; - var view_id; - var view_xml_id; - while(1){ - ls = ls.prev(); - if((self.edit_xml_dialog.$element.find(ls).find('a').text()).search("view_id") != -1 - && parseInt(ls.attr('level')) < level){ - view_id = parseInt(($(ls).find('a').text()).replace(/[^0-9]+/g,'')); - view_xml_id = (ls.attr('id')).split('-')[1]; + self.on_select_img(this); + }); + }, + on_select_img: function(element_img) { + var self = this; + var side = $(element_img).closest("tr[id^='viewedit-']"); + this.one_object.clicked_tr_id = parseInt((side.attr('id')).split('-')[1]); + this.one_object.clicked_tr_level = parseInt(side.attr('level')); + var img = side.find("img[id='parentimg-" + this.one_object.clicked_tr_id + "']").attr('src'); + var view_id = 0, view_xml_id = 0, view_find = side; + //for view id finding + var min_level = this.one_object.clicked_tr_id; + if (($(side).find('a').text()).search("view_id") != -1) { + view_id = parseInt(($(view_find).find('a').text()).replace(/[^0-9]+/g, '')); + view_xml_id = (view_find.attr('id')).split('-')[1]; + this.one_object.clicked_tr_id += 1; + this.one_object.clicked_tr_level += 1; + }else{ + while (1) { + view_find = view_find.prev(); + if (view_find.length == 0 || + (self.edit_xml_dialog.$element.find(view_find).find('a').text()).search("view_id") != -1 + && parseInt(view_find.attr('level')) < min_level ) { + view_id = parseInt(($(view_find).find('a').text()).replace(/[^0-9]+/g, '')); + view_xml_id = parseInt((view_find.attr('id')).split('-')[1]); break; } + if (view_find.attr('level') < min_level) { + min_level = parseInt(view_find.attr('level')); + } } - switch (this.id) { - case "side-add": - break; - case "side-remove": - break; - case "side-edit": - break; - case "side-up": - while (1) { - var prev_tr = cur_tr.prev(); - if(level >= parseInt(prev_tr.attr('level')) || prev_tr.length == 0) { - last_tr = prev_tr; - break; - } - cur_tr = prev_tr; - } - if (img) { - while (1) { - next_tr = side.next(); - if ( parseInt(next_tr.attr('level')) <= level || next_tr.length == 0) { - break; - } else { - list_shift.push(next_tr); - side = next_tr; - } - } - } - if (last_tr.length != 0 && parseInt(last_tr.attr('level')) == level && - (self.edit_xml_dialog.$element.find(last_tr).find('a').text()).search("view_id") == -1) { - _.each(list_shift, function(rec) { - $(last_tr).before(rec); - }); - self.save_move_arch(one_object, view_id, view_xml_id, id_tr, level, "up"); - } + } + this.one_object.clicked_tr_view = [view_id, view_xml_id]; + switch (element_img.id) { + case "side-add": + self.do_node_add(side); break; - case "side-down": - if (img) { - while (1) { - next_tr = cur_tr.next(); - if ( parseInt(next_tr.attr('level')) <= level || next_tr.length == 0) { - last_tr = next_tr; - break; - } else { - list_shift.push(next_tr); - cur_tr = next_tr; - } - } + case "side-remove": + if (confirm(_t("Do you really want to remove this node?"))) { + self.do_save_update_arch("remove_node"); } - else { - last_tr = cur_tr.next(); + break; + case "side-edit": + self.do_node_edit(side); + break; + case "side-up": + self.do_node_up(side, img); + break; + case "side-down": + self.do_node_down(side, img); + break; + } + }, + do_node_add: function(side){ + var self = this; + var tr = $(side).find('a').text(); + var parent_tr = ($(side).prevAll("tr[level=" + String(this.one_object.clicked_tr_level - 1) + "]"))[0]; + var field_dataset = new openerp.web.DataSetSearch(this, this.model, null, null); + parent_tr = $(parent_tr).find('a').text(); + field_dataset.call( 'fields_get', [], function(result) { + var fields = _.keys(result); + fields.push(" "),fields.sort(); + var property_to_check = []; + _.each([tr, parent_tr],function(element) { + property_to_check.push( + _.detect(_.keys(_CHILDREN),function(res){ + return _.str.include(element, res); + })); + }); + self.on_add_node(property_to_check, fields); + }); + }, + do_node_edit: function(side) { + var self = this; + var result = self.get_object_by_id(this.one_object.clicked_tr_id, this.one_object['main_object'], []); + if (result.length && result[0] && result[0].att_list) { + var properties = _PROPERTIES[result[0].att_list[0]]; + self.on_edit_node(properties); + } + }, + do_node_down: function(cur_tr, img) { + var self = this, next_tr, last_tr, tr_to_move = []; + tr_to_move.push(cur_tr); + if (img) { + while (1) { + next_tr = cur_tr.next(); + if ( parseInt(next_tr.attr('level')) <= this.one_object.clicked_tr_level || next_tr.length == 0) { + last_tr = next_tr; + break; + } else { + tr_to_move.push(next_tr); + cur_tr = next_tr; } - if((self.edit_xml_dialog.$element.find(last_tr).find('a').text()).search("view_id") != -1){ - return; + } + } else { + last_tr = cur_tr.next(); + } + if ((self.edit_xml_dialog.$element.find(last_tr).find('a').text()).search("view_id") != -1) { + return false; + } + if (last_tr.length != 0 && parseInt(last_tr.attr('level')) == this.one_object.clicked_tr_level) { + var last_tr_id = (last_tr.attr('id')).split('-')[1]; + img = last_tr.find("img[id='parentimg-" + last_tr_id + "']").attr('src'); + if (img) { + self.edit_xml_dialog.$element.find("img[id='parentimg-" + last_tr_id + "']"). + attr('src', '/web/static/src/img/expand.gif'); + while (1) { + var next_tr = last_tr.next(); + if (next_tr.attr('level') <= this.one_object.clicked_tr_level || next_tr.length == 0) break; + next_tr.hide(); + last_tr = next_tr; } - if (last_tr.length != 0 && parseInt(last_tr.attr('level')) == level) { - var last_tr_id = (last_tr.attr('id')).split('-')[1]; - img = last_tr.find("img[id='parentimg-" + last_tr_id + "']").attr('src'); - if (img) { - self.edit_xml_dialog.$element.find("img[id='parentimg-" + last_tr_id + "']"). - attr('src', '/web/static/src/img/expand.gif'); - while (1) { - var next_tr = last_tr.next(); - if (next_tr.attr('level') <= level || next_tr.length == 0) break; - next_tr.hide(); - last_tr = next_tr; - } - } - list_shift.reverse(); - _.each(list_shift, function(rec) { - $(last_tr).after(rec); - }); - self.save_move_arch(one_object, view_id, view_xml_id, id_tr, level, "down"); + } + tr_to_move.reverse(); + _.each(tr_to_move, function(rec) { + $(last_tr).after(rec); + }); + self.do_save_update_arch("down"); + } + }, + do_node_up: function(cur_tr, img) { + var self = this, side = cur_tr, tr_to_move = []; + tr_to_move.push(side); + while (1) { + var prev_tr = cur_tr.prev(); + if (this.one_object.clicked_tr_level >= parseInt(prev_tr.attr('level')) || prev_tr.length == 0) { + last_tr = prev_tr; + break; + } + cur_tr = prev_tr; + } + if (img) { + self.edit_xml_dialog.$element.find("img[id='parentimg-" + this.one_object.clicked_tr_id + "']"). + attr('src', '/web/static/src/img/expand.gif'); + while (1) { + next_tr = side.next(); + if (parseInt(next_tr.attr('level')) <= this.one_object.clicked_tr_level || next_tr.length == 0) { + break; + } else { + next_tr.hide(); + tr_to_move.push(next_tr); + side = next_tr; } - break; } - }); + } + if (last_tr.length != 0 && parseInt(last_tr.attr('level')) == this.one_object.clicked_tr_level && + (self.edit_xml_dialog.$element.find(last_tr).find('a').text()).search("view_id") == -1) { + _.each(tr_to_move, function(rec) { + $(last_tr).before(rec); + }); + self.do_save_update_arch("up"); + } }, - save_move_arch: function(one_object, view_id, view_xml_id, id_tr, level, move_direct){ + do_save_update_arch: function(move_direct, update_values) { var self = this; - var arch = _.detect(one_object['arch'],function(element){ - return element.view_id == view_id; - }); - var obj = self.get_view_object(view_xml_id, one_object['main_object'], []); - if(($(arch.arch).filter("data")).length != 0 && view_xml_id != 0){ + var arch = _.detect(self.one_object['arch'], function(element) + {return element.view_id == self.one_object.clicked_tr_view[0]}); + var obj = self.get_object_by_id(this.one_object.clicked_tr_view[1],this.one_object['main_object'], []); + //for finding xpath tag from inherit view + var xml_arch = QWeb.load_xml(arch.arch); + if (xml_arch.childNodes[0].tagName == "data") { var check_list = _.flatten(obj[0].child_id[0].att_list); - arch.arch = _.detect($(arch.arch).children(), function(xml_child){ - var temp_obj = self.check_attr(xml_child, xml_child.tagName.toLowerCase()); - var main_list = _.flatten(temp_obj.att_list); - check_list = _.uniq(check_list); - var insert = _.intersection(main_list,check_list); - if(insert.length == check_list.length ){return xml_child;} + var children = _.filter(xml_arch.childNodes[0].childNodes, function (child) { + return child.nodeType == 1; + }); + arch.arch = _.detect(children, function(xml_child) { + var temp_obj = self.create_View_Node(xml_child), + insert = _.intersection(_.flatten(temp_obj.att_list),_.uniq(check_list)); + if (insert.length == check_list.length ) {return xml_child;} }); } - return self.get_node(arch.arch, obj[0].child_id[0], parseInt(id_tr), [], parseInt(level), - parseInt(view_id), arch, move_direct); + arch_to_pass = _.filter($(arch.arch), function (child) { + return child.nodeType == 1; + }); + return self.do_save_xml(arch_to_pass[0], obj[0].child_id[0],[], move_direct, update_values,arch); }, - - get_view_object: function(view_xml_id, one_object,result){ + get_object_by_id: function(id, one_object, result) { var self = this; - if(result.length==0){ - var check = _.detect(one_object , function(obj){ - return view_xml_id==obj.id; + if (result.length == 0 ) { + var check = _.detect(one_object , function(obj) { + return id == obj.id; }); - if(check){result.push(check);}; - _.each(one_object, function(obj){ - self.get_view_object(view_xml_id, obj.child_id, result); + if (check) {result.push(check);}; + _.each(one_object, function(obj) { + self.get_object_by_id(id,obj.child_id, result); }); } return result; }, - get_node: function(arch1, obj, id, child_list, level, view_id, arch, move_direct){ + create_clone: function(clone, new_node_obj, position){ var self = this; - var children_list = $(arch1).children(); - var list_obj_xml = _.zip(children_list,obj.child_id); - if (id) { - if (obj.id == id) { - var id; - var parent = $(arch1).parents(); - var index = _.indexOf(child_list, obj) - var re_insert_obj = child_list.splice(index, 1); + clone.find('a').text(new_node_obj.name); + ($(clone.find('a').parent()).siblings('td')).css( "padding-left", 20 * new_node_obj.level); + clone.attr("id", "viewedit-" + new_node_obj.id); + clone.attr("level", new_node_obj.level); + clone.find("img[id^='parentimg-']").remove(); + clone.bind("click",function(){ + self.do_select_row(this.id.split('-')[1]); + }); + clone.find("img[id^='side-']").click(function() { + self.on_select_img(this); + }); + return clone; + }, + do_save_xml: function(arch1, obj, child_list, move_direct, update_values, arch){ + var self = this, children_list = $(arch1).children(), list_obj_xml = _.zip(children_list, obj.child_id); + if (this.one_object.clicked_tr_id) { + if (obj.id == this.one_object.clicked_tr_id) { + var parent = false, index = _.indexOf(child_list, obj); if (move_direct == "down") { var next = $(arch1).next(); $(next).after(arch1); + var re_insert_obj = child_list.splice(index, 1); child_list.splice(index+1, 0, re_insert_obj[0]); - } else { + parent = $(arch1).parents(); + } else if (move_direct == "up") { var prev = $(arch1).prev(); $(prev).before(arch1); + var re_insert_obj = child_list.splice(index, 1); child_list.splice(index-1, 0, re_insert_obj[0]); + parent = $(arch1).parents(); + } else if (move_direct == "update_node") { + _.each(update_values, function(val){ + if (val[1]) $(arch1)[0].setAttribute(val[0], val[1]); + else $(arch1)[0].removeAttribute(val[0]); + }); + var new_obj = self.create_View_Node(arch1); + new_obj.id = obj.id,new_obj.child_id = obj.child_id; + self.edit_xml_dialog.$element. + find("tr[id='viewedit-"+this.one_object.clicked_tr_id+"']"). + find('a').text(new_obj.name); + child_list.splice(index, 1, new_obj); + parent = $(arch1).parents(); + } else if(move_direct == "add_node") { + var tr_click = self.edit_xml_dialog.$element.find("tr[id='viewedit-"+self.one_object.clicked_tr_id+"']"), + temp_xml = QWeb.load_xml(update_values[0]), + object_xml = self.create_View_Node(temp_xml.childNodes[0]); + (update_values[1] == "Inside")? object_xml.level = obj.level + 1:object_xml.level = obj.level; + var clone = self.create_clone(tr_click.clone(),object_xml), + after_append = _.detect(self.one_object['parent_child_id'],function(ele){ + return self.one_object.clicked_tr_id == ele.key; + }); + after_append = (after_append)?_.last(after_append.value):self.one_object.clicked_tr_id; + switch (update_values[1]) { + case "After": + self.edit_xml_dialog.$element. + find("tr[id='viewedit-"+after_append+"']").after(clone); + $(arch1).after(update_values[0]); + child_list.splice(index + 1, 0, object_xml); + break; + case "Before": + tr_click.before(clone); + $(arch1).before(update_values[0]); + child_list.splice(index - 1, 0, object_xml); + break; + case "Inside": + if (tr_click.find("img[id^='parentimg-']").length == 0) { + ($(tr_click.find('a').parent()).siblings('td')) + .append($('<img width="16" height="16"></img>').attr('src', '/web/static/src/img/collapse.gif'). + attr('id','parentimg-'+ self.one_object.clicked_tr_id).click(function(){ + self.do_parent_img_hide_show(this); + })); + } + $(arch1).append(update_values[0]); + self.edit_xml_dialog.$element. + find("tr[id='viewedit-"+after_append+"']").after(clone); + obj.child_id.push(object_xml); + break; + } + self.edit_xml_dialog.$element. + find("tr[id='viewedit-" + object_xml.id + "']").removeClass('ui-selected'); + parent = $(arch1).parents(); + } else if (move_direct == "remove_node") { + parent = $(arch1).parents(); + if (parent.length == 0 || (parent[0].tagName.toLowerCase() == "data")) { + self.one_object.clicked_tr_id = self.one_object.clicked_tr_id -1; + self.one_object.clicked_tr_level = self.one_object.clicked_tr_level - 1; + (parent.length == 0)?parent.push("remove_view"):false; + } + $(arch1).remove(); + child_list.splice(index,1); + var cur_tr = self.edit_xml_dialog.$element. + find("tr[id='viewedit-" + self.one_object.clicked_tr_id + "']"); + _.each(self.get_list_tr(cur_tr,self.one_object.clicked_tr_level), function(tr_element){ + tr_element.remove(); + }); + cur_tr.remove(); + var parent_img = _.detect(self.one_object['parent_child_id'],function(element){ + return _.include(element.value, self.one_object.clicked_tr_id); + }); + if(parent_img.value.length == 1){ + self.edit_xml_dialog.$element. + find("tr[id='viewedit-"+parent_img.key+"']"). + find("img[id^='parentimg-']").remove(); + } + self.one_object['parent_child_id'] = self.parent_child_list(self.one_object['main_object'],[]); } - parent = parent[parent.length-1]; - var convert_to_utf = self.xml2Str(parent); - if (convert_to_utf) { + var convert_to_utf = (parent.length != 0)? parent[parent.length-1]: arch1; + if (convert_to_utf != "remove_view") { + convert_to_utf = QWeb.tools.xml_node_to_string(convert_to_utf); convert_to_utf = convert_to_utf.replace('xmlns="http://www.w3.org/1999/xhtml"', ""); convert_to_utf = '<?xml version="1.0"?>' + convert_to_utf; arch.arch = convert_to_utf; - dataset = new openerp.web.DataSet(this, 'ir.ui.view'); - dataset.write(parseInt(view_id),{"arch":convert_to_utf}, function(r) { - }); + this.dataset.write(this.one_object.clicked_tr_view[0] ,{"arch":convert_to_utf}, function(r) {}); + } else { + this.dataset.unlink([this.one_object.clicked_tr_view[0]],function(res) {}); + } + if(move_direct == "add_node"){ + self.add_node_dialog.close(); + self.on_select_img(clone.find("img[id='side-edit']")[0]); + self.one_object['parent_child_id'] = self.parent_child_list(self.one_object['main_object'],[]); } } - if (obj.level <= level) { + if (obj.level <= this.one_object.clicked_tr_level) { _.each(list_obj_xml, function(child_node) { - self.get_node(child_node[0], child_node[1], id, obj.child_id, level, view_id, arch, move_direct); + self.do_save_xml(child_node[0], child_node[1], obj.child_id, move_direct, update_values, arch); }); } } }, - xml2Str: function(xmlNode) { - try { - return (new XMLSerializer()).serializeToString(xmlNode); - } - catch (e) { - try { - return xmlNode.xml; - } - catch (e) { - return false; - } - } - - }, on_expand: function(expand_img){ var level = parseInt($(expand_img).closest("tr[id^='viewedit-']").attr('level')); var cur_tr = $(expand_img).closest("tr[id^='viewedit-']"); + _.each(this.get_list_tr(cur_tr,level), function(tr_element){ + tr_element.hide(); + }); + }, + get_list_tr: function(cur_tr,level){ + tr_list = []; while (1) { var nxt_tr = cur_tr.next(); - if (parseInt(nxt_tr.attr('level')) > level){ + if (parseInt(nxt_tr.attr('level')) > level) { cur_tr = nxt_tr; - nxt_tr.hide(); - } else return nxt_tr; + tr_list.push(nxt_tr); + } else return tr_list; } }, - on_collapse: function(collapse_img, parent_child_id, id, main_object) { - var self = this; - var id = collapse_img.id.split('-')[1]; - var datas = _.detect(parent_child_id,function(res) { + on_collapse: function(collapse_img) { + var self = this, id = collapse_img.id.split('-')[1]; + var datas = _.detect(self.one_object['parent_child_id'] , function(res) { return res.key == id; }); - _.each(datas.value, function(rec) { - var tr = self.edit_xml_dialog.$element.find("tr[id='viewedit-"+rec+"']"); - tr.find("img[id='parentimg-"+rec+"']").attr('src','/web/static/src/img/expand.gif'); + _.each(datas.value, function (rec) { + var tr = self.edit_xml_dialog.$element.find("tr[id='viewedit-" + rec + "']"); + tr.find("img[id='parentimg-" + rec + "']").attr('src', '/web/static/src/img/expand.gif'); tr.show(); }); + }, + on_edit_node: function(properties){ + var self = this; + this.edit_node_dialog = new openerp.web.Dialog(this,{ + modal: true, + title: 'Properties', + width: 500, + height: 400, + buttons: { + "Update": function(){ + var warn = false, update_values = []; + _.each(self.edit_widget, function(widget) { + if (widget.is_invalid) { + warn = true; + return false; + }; + if (widget.dirty && !widget.is_invalid) { + update_values.push([widget.name, widget.get_value()]); + } + }); + if (warn) { + self.on_valid_create_view(self.edit_widget); + } else { + self.do_save_update_arch("update_node", update_values); + self.edit_node_dialog.close(); + } + }, + "Cancel": function(){ + self.edit_node_dialog.close(); + } + } + }); + this.edit_node_dialog.start().open(); + var _PROPERTIES_ATTRIBUTES = { + 'name' : {'name':'name', 'string': 'Name', 'type': 'char'}, + 'string' : {'name':'string', 'string': 'String', 'type': 'char'}, + 'required' : {'name':'required', 'string': 'Required', 'type': 'boolean'}, + 'readonly' : {'name':'readonly', 'string': 'Readonly', 'type': 'boolean'}, + 'domain' : {'name':'domain', 'string': 'Domain', 'type': 'char'}, + 'context' : {'name':'context', 'string': 'Context', 'type': 'char'}, + 'limit' : {'name':'limit', 'string': 'Limit', 'type': 'float'}, + 'min_rows' : {'name':'min_rows', 'string': 'Minimum rows', 'type': 'float'}, + 'date_start' : {'name':'date_start', 'string': 'Start date', 'type': 'char'}, + 'date_delay' : {'name':'date_delay', 'string': 'Delay date', 'type': 'char'}, + 'day_length' : {'name':'day_length', 'string': 'Day length', 'type': 'char'}, + 'mode' : {'name':'mode', 'string': 'Mode', 'type': 'char'}, + 'align' : {'name':'align', 'string': 'Alignment ', 'type': 'selection', 'selection': [['', ''], ['0.0', 'Left'], ['0.5', 'Center'], ['1.0', 'Right']]}, + 'icon' : {'name':'icon', 'string': 'Icon', 'type': 'selection', 'selection': _ICONS}, + 'type' : {'name':'type', 'string': 'Type', 'type': 'selection', 'selection': [['', ''], ['action', 'Action'], ['object', 'Object'], ['workflow', 'Workflow'], ['server_action', 'Server Action']]}, + 'special' : {'name':'special', 'string': 'Special', 'type': 'selection', 'selection': [['',''],['save', 'Save Button'], ['cancel', 'Cancel Button'], ['open', 'Open Button']]}, + 'target' : {'name':'target', 'string': 'Target', 'type': 'selection', 'selection': [['', ''], ['new', 'New Window']]}, + 'confirm' : {'name':'confirm', 'string': 'Confirm', 'type': 'char'}, + 'style' : {'name':'style', 'string': 'Style', 'type': 'selection', 'selection':[["",""],["1", "1"],["1-1", "1-1"],["1-2", "1-2"],["2-1", "2-1"],["1-1-1", "1-1-1"]]}, + 'filename' : {'name':'filename', 'string': 'File Name', 'type': 'char'}, + 'width' : {'name':'width', 'string': 'Width', 'type': 'float'}, + 'height' : {'name':'height', 'string': 'Height', 'type': 'float'}, + 'attrs' : {'name':'attrs', 'string': 'Attrs', 'type': 'char'}, + 'col' : {'name':'col', 'string': 'col', 'type': 'float'}, + 'link' : {'name':'link', 'string': 'Link', 'type': 'char'}, + 'position' : {'name':'position', 'string': 'Position', 'type': 'selection', 'selection': [['',''],['after', 'After'],['before', 'Before'],['inside', 'Inside'],['replace', 'Replace']]}, + 'states' : {'name':'states', 'string': 'states', 'type': 'char'}, + 'eval' : {'name':'eval', 'string': 'Eval', 'type': 'char'}, + 'ref' : {'name':'ref', 'string': 'Ref', 'type': 'char'}, + 'on_change' : {'name':'on_change', 'string': 'On change', 'type': 'char'}, + 'nolabel' : {'name':'nolabel', 'string': 'No label', 'type': 'boolean'}, + 'completion' : {'name':'completion', 'string': 'Completion', 'type': 'boolean'}, + 'colspan' : {'name':'colspan', 'string': 'Colspan', 'type': 'float'}, + 'widget' : {'name':'widget', 'string': 'widget', 'type': 'selection'}, + 'colors' : {'name':'colors', 'string': 'Colors', 'type': 'char'}, + 'editable' : {'name':'editable', 'string': 'Editable', 'type': 'selection', 'selection': [["",""],["top","Top"],["bottom", "Bottom"]]}, + 'groups' : {'name':'groups', 'string': 'Groups', 'type': 'seleciton_multi'}, + }; + var arch_val = self.get_object_by_id(this.one_object.clicked_tr_id,this.one_object['main_object'], []); + this.edit_node_dialog.$element.append('<table id="rec_table" style="width:400px" class="oe_forms"></table>'); + this.edit_widget = []; + self.ready = $.when(self.on_groups(properties)).then(function () { + _PROPERTIES_ATTRIBUTES['groups']['selection'] = self.groups; + var values = _.keys( openerp.web.form.widgets.map); + values.push(''); + values.sort(); + _PROPERTIES_ATTRIBUTES['widget']['selection'] = values; + var widgets = _.filter(_PROPERTIES_ATTRIBUTES, function(property){ return _.include(properties, property.name)}) + _.each(widgets, function(widget) { + var type_widget = new (self.property.get_any([widget.type])) (self.edit_node_dialog, widget); + var value = _.detect(arch_val[0]['att_list'],function(res) { + return res instanceof Array? _.include(res, widget.name): false; + }); + value = value instanceof Array ? value[1] : value; + self.edit_node_dialog.$element.find('table[id=rec_table]').append('<tr><td align="right">' + widget.string + ':</td>' + type_widget.render() + '</tr>'); + type_widget.start(); + type_widget.set_value(value); + self.edit_widget.push(type_widget); + }); + }); + }, + //for getting groups + on_groups: function(properties){ + var self = this, + def = $.Deferred(); + if (!_.include(properties, 'groups')) { + self.groups = false; + def.resolve(); + } + var group_ids = [], group_names = {}, groups = []; + var res_groups = new openerp.web.DataSetSearch(this,'res.groups', null, null), + model_data = new openerp.web.DataSetSearch(self,'ir.model.data', null, null); + res_groups + .read_slice([], {}) + .done(function(res_grp) { + _.each(res_grp,function(res){ + var key = res.id; + group_names[key]=res.name; + group_ids.push(res.id); + }); + model_data + .read_slice([],{domain:[['res_id', 'in', group_ids],['model','=','res.groups']]}) + .done(function(model_grp) { + _.each(model_grp, function(res_group) { + groups.push([res_group.module + "." + res_group.name,group_names[res_group.res_id]]); + }); + self.groups = groups; + def.resolve(); + }); + }) + return def.promise(); + }, + on_add_node: function(properties, fields){ + var self = this; + var render_list = [{'name': 'node_type','selection': _.keys(_CHILDREN).sort(), 'value': 'field', 'string': 'Node Type','type': 'selection'}, + {'name': 'field_value','selection': fields, 'value': false, 'string': '','type': 'selection'}, + {'name': 'position','selection': ['After','Before','Inside'], 'value': false, 'string': 'Position','type': 'selection'}]; + this.add_widget = []; + this.add_node_dialog = new openerp.web.Dialog(this,{ + modal: true, + title: 'Properties', + width: 450, + height: 190, + buttons: { + "Update": function(){ + var check_add_node = true, values = {}; + _.each(self.add_widget, function(widget) { + values[widget.name] = widget.get_value() || false; + }); + (values.position == "Inside")? + check_add_node =(_.include(_CHILDREN[properties[0]],values.node_type))?true:false: + check_add_node =(_.include(_CHILDREN[properties[1]],values.node_type))?true:false; + if(values.node_type == "field" && check_add_node ) + {check_add_node = (values.field_value != " ")?true:false; + } + if(check_add_node){ + var tag = (values.node_type == "field")? + _.str.sprintf("<%s name='%s'> </%s>",values.node_type,values.field_value,values.node_type): + _.str.sprintf("<%s> </%s>",values.node_type,values.node_type); + self.do_save_update_arch("add_node", [tag, values.position]); + }else{alert("Can't Update View");} + }, + "Cancel": function(){ + self.add_node_dialog.close(); + } + } + }).start().open(); + this.add_node_dialog.$element.append('<table id="rec_table" style="width:420px" class="oe_forms"><tbody><tr></tbody></table>'); + var table_selector = self.add_node_dialog.$element.find('table[id=rec_table] tbody'); + _.each(render_list, function(node) { + type_widget = new (self.property.get_any([node.type])) (self.add_node_dialog, node); + if (node.name == "position") { + table_selector.append('</tr><tr><td align="right" width="100px">' + node.string + '</td>' + type_widget.render() + '</tr>'); + } else { + table_selector.append('<td align="right">' + node.string + '</td>' + type_widget.render() ); + if (node.name == "field_value") { + table_selector.append('<td id="new_field" align="right" width="100px"> <button>New Field</button></td>'); + } + } + type_widget.start(); + type_widget.set_value(node.value); + self.add_widget.push(type_widget); + }); + table_selector.find("td[id^=]").attr("width","100px"); + self.add_node_dialog.$element.find('#new_field').click(function() { + model_data = new openerp.web.DataSetSearch(self,'ir.model', null, null); + model_data.read_slice([], {domain: [['model','=', self.model]]}, function(result) { + self.render_new_field(result[0].id); + }); + }); + }, + render_new_field :function(id){ + var self = this; + var action = { + context: {'default_model_id': id, 'manual':true}, + res_model: "ir.model.fields", + views: [[false, 'form']], + type: 'ir.actions.act_window', + target: "new", + flags: { + action_buttons: true, + } + } + var action_manager = new openerp.web.ActionManager(self); + $.when(action_manager.do_action(action)).then(function() { + var controller = action_manager.dialog_viewmanager.views['form'].controller; + // TODO NIV: use page view + controller.do_set_readonly.add_last(function(){ + action_manager.stop(); + new_fields_name = new openerp.web.DataSetSearch(self,'ir.model.fields', null, null); + new_fields_name.read_ids([controller.datarecord.id], ['name'], function(result) { + self.add_node_dialog.$element.find('select[id=field_value]').append($("<option selected></option>").attr("value", result[0].name).text(result[0].name)); + _.detect(self.add_widget,function(widget){ + widget.name == "field_value"? widget.selection.push(result[0].name): false; + }); + }); + }); + }); + } +}); +openerp.web.ViewEditor.Field = openerp.web.Class.extend({ + init: function(view, widget) { + this.$element = view.$element; + this.dirty = false; + this.name = widget.name; + this.selection = widget.selection || []; + this.required = widget.required || false; + this.string = widget.string || ""; + this.type = widget.type; + this.is_invalid = false; + }, + start: function () { + this.update_dom(); + }, + update_dom: function() { + this.$element.find("td[id=" + this.name + "]").toggleClass('invalid', this.is_invalid); + this.$element.find("td[id=" + this.name + "]").toggleClass('required', this.required); + }, + on_ui_change: function() { + this.validate(); + this.dirty = true; + this.update_dom(); + }, + validate: function() { + this.is_invalid = false; + try { + var value = openerp.web.parse_value(this.get_value(), this, ''); + this.is_invalid = this.required && value === ''; + } catch(e) { + this.is_invalid = true; + } + }, + render: function() { + return _.str.sprintf("<td id = %s>%s</td>", this.name, QWeb.render(this.template, {widget: this})) + }, +}); +openerp.web.ViewEditor.FieldBoolean = openerp.web.ViewEditor.Field.extend({ + template : "vieweditor_boolean", + start: function() { + var self = this; + this._super(); + this.$element.find("input[id="+ self.name+"]").change(function() { + self.on_ui_change(); + }); + }, + set_value: function(value) { + if (value) { + this.$element.find("input[id=" + this.name+ "]").attr('checked', true); + } + }, + get_value: function() { + return this.$element.find("input[id=" + this.name + "]").is(':checked')? "1" : null; } +}); +openerp.web.ViewEditor.FieldChar = openerp.web.ViewEditor.Field.extend({ + template : "vieweditor_char", + start: function () { + var self = this; + this._super(); + this.$element.find("input[id="+ this.name+"]").css('width','100%').change(function() { + self.on_ui_change(); + }); + }, + set_value: function(value) { + this.$element.find("input[id=" + this.name + "]").val(value); + }, + get_value: function() { + return this.$element.find("input[id=" + this.name + "]").val(); + } +}); +openerp.web.ViewEditor.FieldSelect = openerp.web.ViewEditor.Field.extend({ + template : "vieweditor_selection", + start: function () { + var self = this; + this._super(); + this.$element.find("select[id=" + this.name + "]").css('width', '100%').change(function() { + self.on_ui_change(); + if (self.name == "node_type") { + if (self.get_value() == "field") { + self.$element.find('#new_field').show(); + self.$element.find("select[id=field_value]").show(); + } else { + self.$element.find('#new_field').hide(); + self.$element.find("select[id=field_value]").hide(); + } + } + }); + }, + set_value: function(value) { + var index = 0; + value = value === null? false: value; + for (var i = 0, ii = this.selection.length; i < ii; i++) { + if ((this.selection[i] instanceof Array && this.selection[i][1] === value) || this.selection[i] === value) index = i; + } + this.$element.find("select[id=" + this.name + "]")[0].selectedIndex = index; + }, + get_value: function() { + return this.$element.find("select[id=" + this.name + "]").val(); + } +}); +openerp.web.ViewEditor.FieldSelectMulti = openerp.web.ViewEditor.FieldSelect.extend({ + start: function () { + this._super(); + this.$element.find("select[id=" + this.name + "]").css('height', '100px').attr("multiple", true); + }, + set_value: function(value) { + var self = this; + self.$element.find("#groups option").attr("selected",false); + if (!value) return false; + _.each(this.selection, function(item) { + if (_.include(value.split(','), item[0])) { + self.$element.find("select[id="+self.name+"] option[value='" + item[0] +"']").attr("selected",1) + } + }); + } +}); +openerp.web.ViewEditor.FieldFloat = openerp.web.ViewEditor.FieldChar.extend({ +}); +var _PROPERTIES = { + 'field' : ['name', 'string', 'required', 'readonly', 'domain', 'context', 'nolabel', 'completion', + 'colspan', 'widget', 'eval', 'ref', 'on_change', 'attrs', 'groups'], + 'form' : ['string', 'col', 'link'], + 'notebook' : ['colspan', 'position', 'groups'], + 'page' : ['string', 'states', 'attrs', 'groups'], + 'group' : ['string', 'col', 'colspan', 'states', 'attrs', 'groups'], + 'image' : ['filename', 'width', 'height', 'groups'], + 'separator' : ['string', 'colspan', 'groups'], + 'label': ['string', 'align', 'colspan', 'groups'], + 'button': ['name', 'string', 'icon', 'type', 'states', 'readonly', 'special', 'target', 'confirm', 'context', 'attrs', 'colspan', 'groups'], + 'newline' : [], + 'board': ['style'], + 'column' : [], + 'action' : ['name', 'string', 'colspan', 'groups'], + 'tree' : ['string', 'colors', 'editable', 'link', 'limit', 'min_rows'], + 'graph' : ['string', 'type'], + 'calendar' : ['string', 'date_start', 'date_stop', 'date_delay', 'day_length', 'color', 'mode'], +}; +var _CHILDREN = { + 'form': ['notebook', 'group', 'field', 'label', 'button','board', 'newline', 'separator'], + 'tree': ['field'], + 'graph': ['field'], + 'calendar': ['field'], + 'notebook': ['page'], + 'page': ['notebook', 'group', 'field', 'label', 'button', 'newline', 'separator'], + 'group': ['field', 'label', 'button', 'separator', 'newline'], + 'board': ['column'], + 'action': [], + 'field': ['form', 'tree', 'graph'], + 'label': [], + 'button' : [], + 'newline': [], + 'separator': [], +}; +var _ICONS = ['','STOCK_ABOUT', 'STOCK_ADD', 'STOCK_APPLY', 'STOCK_BOLD', + 'STOCK_CANCEL', 'STOCK_CDROM', 'STOCK_CLEAR', 'STOCK_CLOSE', 'STOCK_COLOR_PICKER', + 'STOCK_CONNECT', 'STOCK_CONVERT', 'STOCK_COPY', 'STOCK_CUT', 'STOCK_DELETE', + 'STOCK_DIALOG_AUTHENTICATION', 'STOCK_DIALOG_ERROR', 'STOCK_DIALOG_INFO', + 'STOCK_DIALOG_QUESTION', 'STOCK_DIALOG_WARNING', 'STOCK_DIRECTORY', 'STOCK_DISCONNECT', + 'STOCK_DND', 'STOCK_DND_MULTIPLE', 'STOCK_EDIT', 'STOCK_EXECUTE', 'STOCK_FILE', + 'STOCK_FIND', 'STOCK_FIND_AND_REPLACE', 'STOCK_FLOPPY', 'STOCK_GOTO_BOTTOM', + 'STOCK_GOTO_FIRST', 'STOCK_GOTO_LAST', 'STOCK_GOTO_TOP', 'STOCK_GO_BACK', + 'STOCK_GO_DOWN', 'STOCK_GO_FORWARD', 'STOCK_GO_UP', 'STOCK_HARDDISK', + 'STOCK_HELP', 'STOCK_HOME', 'STOCK_INDENT', 'STOCK_INDEX', 'STOCK_ITALIC', + 'STOCK_JUMP_TO', 'STOCK_JUSTIFY_CENTER', 'STOCK_JUSTIFY_FILL', + 'STOCK_JUSTIFY_LEFT', 'STOCK_JUSTIFY_RIGHT', 'STOCK_MEDIA_FORWARD', + 'STOCK_MEDIA_NEXT', 'STOCK_MEDIA_PAUSE', 'STOCK_MEDIA_PLAY', + 'STOCK_MEDIA_PREVIOUS', 'STOCK_MEDIA_RECORD', 'STOCK_MEDIA_REWIND', + 'STOCK_MEDIA_STOP', 'STOCK_MISSING_IMAGE', 'STOCK_NETWORK', 'STOCK_NEW', + 'STOCK_NO', 'STOCK_OK', 'STOCK_OPEN', 'STOCK_PASTE', 'STOCK_PREFERENCES', + 'STOCK_PRINT', 'STOCK_PRINT_PREVIEW', 'STOCK_PROPERTIES', 'STOCK_QUIT', + 'STOCK_REDO', 'STOCK_REFRESH', 'STOCK_REMOVE', 'STOCK_REVERT_TO_SAVED', + 'STOCK_SAVE', 'STOCK_SAVE_AS', 'STOCK_SELECT_COLOR', 'STOCK_SELECT_FONT', + 'STOCK_SORT_ASCENDING', 'STOCK_SORT_DESCENDING', 'STOCK_SPELL_CHECK', + 'STOCK_STOP', 'STOCK_STRIKETHROUGH', 'STOCK_UNDELETE', 'STOCK_UNDERLINE', + 'STOCK_UNDO', 'STOCK_UNINDENT', 'STOCK_YES', 'STOCK_ZOOM_100', + 'STOCK_ZOOM_FIT', 'STOCK_ZOOM_IN', 'STOCK_ZOOM_OUT', + 'terp-account', 'terp-crm', 'terp-mrp', 'terp-product', 'terp-purchase', + 'terp-sale', 'terp-tools', 'terp-administration', 'terp-hr', 'terp-partner', + 'terp-project', 'terp-report', 'terp-stock', 'terp-calendar', 'terp-graph' +]; +openerp.web.ViewEditor.property_widget = new openerp.web.Registry({ + 'boolean' : 'openerp.web.ViewEditor.FieldBoolean', + 'seleciton_multi' : 'openerp.web.ViewEditor.FieldSelectMulti', + 'selection' : 'openerp.web.ViewEditor.FieldSelect', + 'char' : 'openerp.web.ViewEditor.FieldChar', + 'float' : 'openerp.web.ViewEditor.FieldFloat', }); }; diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index c5a2fbb4dc6..15a402efb47 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -33,16 +33,18 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# this.widgets = {}; this.widgets_counter = 0; this.fields = {}; + this.fields_order = []; this.datarecord = {}; this.show_invalid = true; this.default_focus_field = null; this.default_focus_button = null; - this.registry = this.readonly ? openerp.web.form.readonly : openerp.web.form.widgets; + this.registry = openerp.web.form.widgets; this.has_been_loaded = $.Deferred(); this.$form_header = null; this.translatable_fields = []; - _.defaults(this.options, {"always_show_new_button": true, - "not_interactible_on_create": false}); + _.defaults(this.options, { + "not_interactible_on_create": false + }); this.mutating_lock = $.Deferred(); this.initial_mutating_lock = this.mutating_lock; this.on_change_lock = $.Deferred().resolve(); @@ -86,6 +88,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# on_loaded: function(data) { var self = this; if (data) { + this.fields_order = []; this.fields_view = data; var frame = new (this.registry.get_object('frame'))(this, this.fields_view.arch); @@ -102,10 +105,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# }); this.$form_header.find('button.oe_form_button_save').click(this.on_button_save); - this.$form_header.find('button.oe_form_button_new').click(this.on_button_new); - this.$form_header.find('button.oe_form_button_duplicate').click(this.on_button_duplicate); - this.$form_header.find('button.oe_form_button_delete').click(this.on_button_delete); - this.$form_header.find('button.oe_form_button_toggle').click(this.on_toggle_readonly); + this.$form_header.find('button.oe_form_button_cancel').click(this.on_button_cancel); if (!this.sidebar && this.options.sidebar && this.options.sidebar_id) { this.sidebar = new openerp.web.Sidebar(this, this.options.sidebar_id); @@ -117,23 +117,6 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# } this.has_been_loaded.resolve(); }, - on_toggle_readonly: function() { - var self = this; - self.translatable_fields = []; - self.widgets = {}; - self.fields = {}; - self.$form_header.find('button').unbind('click'); - self.readonly = !self.readonly; - self.registry = self.readonly ? openerp.web.form.readonly : openerp.web.form.widgets; - self.on_loaded(self.fields_view); - return self.reload(); - }, - do_set_readonly: function() { - return this.readonly ? $.Deferred().resolve() : this.on_toggle_readonly(); - }, - do_set_editable: function() { - return !this.readonly ? $.Deferred().resolve() : this.on_toggle_readonly(); - }, do_show: function () { var promise; if (this.dataset.index === null) { @@ -160,19 +143,6 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# if (!record) { throw("Form: No record received"); } - if (!record.id) { - this.$form_header.find('.oe_form_on_create').show(); - this.$form_header.find('.oe_form_on_update').hide(); - if (!this.options["always_show_new_button"]) { - this.$form_header.find('button.oe_form_button_new').hide(); - } - } else { - this.$form_header.find('.oe_form_on_create').hide(); - this.$form_header.find('.oe_form_on_update').show(); - this.$form_header.find('button.oe_form_button_new').show(); - } - this.$form_header.find('.oe_form_on_readonly').toggle(this.readonly); - this.$form_header.find('.oe_form_on_editable').toggle(!this.readonly); this.datarecord = record; _(this.fields).each(function (field, f) { @@ -188,15 +158,16 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# deferred_stack.push('force resolution if no fields'); return deferred_stack.then(function() { if (!record.id) { - // New record: Second pass in order to trigger the onchanges self.show_invalid = false; - for (var f in record) { - var field = self.fields[f]; - if (field) { + // New record: Second pass in order to trigger the onchanges + // respecting the fields order defined in the view + _.each(self.fields_order, function(field_name) { + if (record[field_name] !== undefined) { + var field = self.fields[field_name]; field.dirty = true; self.do_onchange(field); } - } + }); } self.on_form_changed(); self.initial_mutating_lock.resolve(); @@ -273,6 +244,10 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# if (field in argument_replacement) { return argument_replacement[field](i); } + // literal number + if (/^-?\d+(\.\d+)?$/.test(field)) { + return Number(field); + } // form field if (self.fields[field]) { var value = self.fields[field].get_on_change_value(); @@ -380,63 +355,35 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# } }, on_button_save: function() { - return this.do_save().then(this.do_set_readonly); - }, - on_button_new: function() { var self = this; - var def = $.Deferred(); - $.when(this.has_been_loaded).then(function() { - if (self.can_be_discarded()) { - var keys = _.keys(self.fields_view.fields); - $.when(self.do_set_editable()).then(function() { - if (keys.length) { - self.dataset.default_get(keys).pipe(self.on_record_loaded).then(function() { - def.resolve(); - }); - } else { - self.on_record_loaded({}).then(function() { - def.resolve(); - }); - } - }); - } + return this.do_save().then(function(result) { + self.do_prev_view(result.created); }); - return def.promise(); }, - on_button_duplicate: function() { - var self = this; - var def = $.Deferred(); - $.when(this.has_been_loaded).then(function() { - if (self.can_be_discarded()) { - self.dataset.call('copy', [self.datarecord.id, {}, self.dataset.context]).then(function(new_id) { - return self.on_created({ result : new_id }); - }).then(self.do_set_editable).then(function() { - def.resolve(); - }); - } - }); - return def.promise(); + on_button_cancel: function() { + return this.do_prev_view(); }, - on_button_delete: function() { + on_button_new: function() { var self = this; var def = $.Deferred(); $.when(this.has_been_loaded).then(function() { - if (self.can_be_discarded() && self.datarecord.id) { - if (confirm(_t("Do you really want to delete this record?"))) { - self.dataset.unlink([self.datarecord.id]).then(function() { - self.on_pager_action('next'); + if (self.can_be_discarded()) { + var keys = _.keys(self.fields_view.fields); + if (keys.length) { + self.dataset.default_get(keys).pipe(self.on_record_loaded).then(function() { def.resolve(); }); } else { - setTimeout(function () { - def.reject(); - }, 0) + self.on_record_loaded({}).then(function() { + def.resolve(); + }); } } }); return def.promise(); }, can_be_discarded: function() { + return true; // FIXME: Disabled until the page view and button refactoring is done return !this.is_dirty() || confirm(_t("Warning, the record has been modified, your changes will be discarded.")); }, /** @@ -476,22 +423,22 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# self.on_invalid(); return $.Deferred().reject(); } else { + var save_deferral; if (!self.datarecord.id) { openerp.log("FormView(", self, ") : About to create", values); - return self.dataset.create(values).pipe(function(r) { + save_deferral = self.dataset.create(values).pipe(function(r) { return self.on_created(r, undefined, prepend_on_create); - }).then(success); + }, null); } else if (_.isEmpty(values)) { openerp.log("FormView(", self, ") : Nothing to save"); - if (success) { - success(); - } + save_deferral = $.Deferred().resolve({}).promise(); } else { openerp.log("FormView(", self, ") : About to save", values); - return self.dataset.write(self.datarecord.id, values, {}).pipe(function(r) { + save_deferral = self.dataset.write(self.datarecord.id, values, {}).pipe(function(r) { return self.on_saved(r); - }).then(success); + }, null); } + return save_deferral.then(success); } } catch (e) { console.error(e); @@ -951,8 +898,9 @@ openerp.web.form.WidgetFrame = openerp.web.form.Widget.extend({ var type = {}; if (node.tag == 'field') { type = this.view.fields_view.fields[node.attrs.name] || {}; - if (node.attrs.widget == 'statusbar') { + if (node.attrs.widget == 'statusbar' && node.attrs.nolabel !== '1') { // This way we can retain backward compatibility between addons and old clients + node.attrs.colspan = (parseInt(node.attrs.colspan, 10) || 1) + 1; node.attrs.nolabel = '1'; } } @@ -1095,6 +1043,7 @@ openerp.web.form.WidgetButton = openerp.web.form.Widget.extend({ this.execute_action().always(function() { self.force_disabled = false; self.check_disable(); + $.tipTipClear(); }); }, execute_action: function() { @@ -1169,6 +1118,11 @@ openerp.web.form.WidgetLabel = openerp.web.form.Widget.extend({ if (this.node.tag == 'label' && (this.align === 'left' || this.node.attrs.colspan || (this.string && this.string.length > 32))) { this.template = "WidgetParagraph"; this.colspan = parseInt(this.node.attrs.colspan || 1, 10); + // Widgets default to right-aligned, but paragraph defaults to + // left-aligned + if (isNaN(parseFloat(this.node.attrs.align))) { + this.align = 'left'; + } } else { this.colspan = 1; this.width = '1%'; @@ -1209,6 +1163,7 @@ openerp.web.form.Field = openerp.web.form.Widget.extend(/** @lends openerp.web.f this.name = node.attrs.name; this.value = undefined; view.fields[this.name] = this; + view.fields_order.push(this.name); this.type = node.attrs.widget || view.fields_view.fields[node.attrs.name].type; this.element_name = "field_" + this.name + "_" + this.type; @@ -1600,7 +1555,7 @@ openerp.web.form.FieldSelection = openerp.web.form.Field.extend({ init: function(view, node) { var self = this; this._super(view, node); - this.values = this.field.selection; + this.values = _.clone(this.field.selection); _.each(this.values, function(v, i) { if (v[0] === false && v[1] === '') { self.values.splice(i, 1); @@ -2099,16 +2054,20 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({ view.options.deletable = null; } } else if (view.view_type === "form") { + if (self.is_readonly()) { + view.view_type = 'page'; + } view.options.not_interactible_on_create = true; } views.push(view); }); this.views = views; - + this.viewmanager = new openerp.web.ViewManager(this, this.dataset, views); this.viewmanager.registry = openerp.web.views.clone({ list: 'openerp.web.form.One2ManyListView', - form: 'openerp.web.FormView' + form: 'openerp.web.FormView', + page: 'openerp.web.PageView' }); var once = $.Deferred().then(function() { self.init_form_last_update.resolve(); @@ -2121,10 +2080,9 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({ controller.o2m = self; if (self.is_readonly()) controller.set_editable(false); - } else if (view_type == "form") { - if (self.is_readonly()) { - controller.on_toggle_readonly(); - $(controller.$element.find(".oe_form_buttons")[0]).children().remove(); + } else if (view_type == "form" || view_type == 'page') { + if (view_type == 'page') { + controller.$element.find(".oe_form_buttons").hide(); } controller.on_record_loaded.add_last(function() { once.resolve(); @@ -2154,22 +2112,23 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({ reload_current_view: function() { var self = this; return self.is_loaded = self.is_loaded.pipe(function() { - var view = self.viewmanager.views[self.viewmanager.active_view].controller; - if(self.viewmanager.active_view === "list") { + var active_view = self.viewmanager.active_view; + var view = self.viewmanager.views[active_view].controller; + if(active_view === "list") { return view.reload_content(); - } else if (self.viewmanager.active_view === "form") { + } else if (active_view === "form" || active_view === 'page') { if (self.dataset.index === null && self.dataset.ids.length >= 1) { self.dataset.index = 0; } var act = function() { return view.do_show(); - } + }; self.form_last_update = self.form_last_update.pipe(act, act); return self.form_last_update; - } else if (self.viewmanager.active_view === "graph") { + } else if (active_view === "graph") { return view.do_search(self.build_domain(), self.dataset.get_context(), []); } - }); + }, undefined); }, set_value: function(value) { value = value || []; @@ -2233,6 +2192,7 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({ var self = this; if (!this.dataset) return []; + this.save_any_view(); var val = this.dataset.delete_all ? [commands.delete_all()] : []; val = val.concat(_.map(this.dataset.ids, function(id) { var alter_order = _.detect(self.dataset.to_create, function(x) {return x.id === id;}); @@ -2489,7 +2449,9 @@ openerp.web.form.Many2ManyListView = openerp.web.ListView.extend(/** @lends open do_activate_record: function(index, id) { var self = this; var pop = new openerp.web.form.FormOpenPopup(this); - pop.show_element(this.dataset.model, id, this.m2m_field.build_context(), {}); + pop.show_element(this.dataset.model, id, this.m2m_field.build_context(), { + readonly: this.widget_parent.is_readonly() + }); pop.on_write_completed.add_last(function() { self.reload_content(); }); @@ -2548,18 +2510,30 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope this.dataset.parent_view = this.options.parent_view; this.dataset.on_default_get.add(this.on_default_get); if (this.options.initial_view == "search") { - this.setup_search_view(); + self.rpc('/web/session/eval_domain_and_context', { + domains: [], + contexts: [this.context] + }, function (results) { + var search_defaults = {}; + _.each(results.context, function (value, key) { + var match = /^search_default_(.*)$/.exec(key); + if (match) { + search_defaults[match[1]] = value; + } + }); + self.setup_search_view(search_defaults); + }); } else { // "form" this.new_object(); } }, - setup_search_view: function() { + setup_search_view: function(search_defaults) { var self = this; if (this.searchview) { this.searchview.stop(); } this.searchview = new openerp.web.SearchView(this, - this.dataset, false, {}); + this.dataset, false, search_defaults); this.searchview.on_search.add(function(domains, contexts, groupbys) { if (self.initial_ids) { self.do_search(domains.concat([[["id", "in", self.initial_ids]], self.domain]), @@ -2743,18 +2717,15 @@ openerp.web.form.FormOpenPopup = openerp.web.OldWidget.extend(/** @lends openerp on_write_completed: function() {}, setup_form_view: function() { var self = this; - this.view_form = new openerp.web.FormView(this, this.dataset, false, self.options.form_view_options); + var FormClass = this.options.readonly + ? openerp.web.views.get_object('page') + : openerp.web.views.get_object('form'); + this.view_form = new FormClass(this, this.dataset, false, self.options.form_view_options); if (this.options.alternative_form_view) { this.view_form.set_embedded_view(this.options.alternative_form_view); } this.view_form.appendTo(this.$element.find("#" + this.element_id + "_view_form")); - var once = $.Deferred().then(function() { - if (self.options.readonly) { - self.view_form.on_toggle_readonly(); - } - }); this.view_form.on_loaded.add_last(function() { - once.resolve(); var $buttons = self.view_form.$element.find(".oe_form_buttons"); $buttons.html(QWeb.render("FormOpenPopup.form.buttons")); var $nbutton = $buttons.find(".oe_formopenpopup-form-save"); @@ -2808,10 +2779,12 @@ openerp.web.form.FieldReference = openerp.web.form.Field.extend({ this.view_id = 'reference_' + _.uniqueId(); this.widgets = {}; this.fields = {}; + this.fields_order = []; this.selection = new openerp.web.form.FieldSelection(this, { attrs: { name: 'selection', widget: 'selection' }}); + this.reference_ready = true; this.selection.on_value_changed.add_last(this.on_selection_changed); this.m2o = new openerp.web.form.FieldMany2One(this, { attrs: { name: 'm2o', @@ -2821,10 +2794,12 @@ openerp.web.form.FieldReference = openerp.web.form.Field.extend({ on_nop: function() { }, on_selection_changed: function() { - var sel = this.selection.get_value(); - this.m2o.field.relation = sel; - this.m2o.set_value(null); - this.m2o.$element.toggle(sel !== false); + if (this.reference_ready) { + var sel = this.selection.get_value(); + this.m2o.field.relation = sel; + this.m2o.set_value(null); + this.m2o.$element.toggle(sel !== false); + } }, start: function() { this._super(); @@ -2839,11 +2814,18 @@ openerp.web.form.FieldReference = openerp.web.form.Field.extend({ }, set_value: function(value) { this._super(value); + this.reference_ready = false; + var vals = [], sel_val, m2o_val; if (typeof(value) === 'string') { - var vals = value.split(','); - this.selection.set_value(vals[0]); - this.m2o.set_value(parseInt(vals[1], 10)); + vals = value.split(','); } + sel_val = vals[0] || false; + m2o_val = vals[1] ? parseInt(vals[1], 10) : false; + this.selection.set_value(sel_val); + this.m2o.field.relation = sel_val; + this.m2o.set_value(m2o_val); + this.m2o.$element.toggle(sel_val !== false); + this.reference_ready = true; }, get_value: function() { var model = this.selection.get_value(), @@ -3053,119 +3035,7 @@ openerp.web.form.FieldStatus = openerp.web.form.Field.extend({ } }); -openerp.web.form.FieldReadonly = openerp.web.form.Field.extend({ -}); -openerp.web.form.FieldCharReadonly = openerp.web.form.FieldReadonly.extend({ - template: 'FieldChar.readonly', - init: function(view, node) { - this._super(view, node); - this.password = this.node.attrs.password === 'True' || this.node.attrs.password === '1'; - }, - set_value: function (value) { - this._super.apply(this, arguments); - var show_value = openerp.web.format_value(value, this, ''); - if (this.password) { - show_value = new Array(show_value.length + 1).join('*'); - } - this.$element.find('div').text(show_value); - return show_value; - } -}); -openerp.web.form.FieldURIReadonly = openerp.web.form.FieldCharReadonly.extend({ - template: 'FieldURI.readonly', - scheme: null, - set_value: function (value) { - var displayed = this._super.apply(this, arguments); - this.$element.find('a') - .attr('href', this.scheme + ':' + displayed) - .text(displayed); - } -}); -openerp.web.form.FieldEmailReadonly = openerp.web.form.FieldURIReadonly.extend({ - scheme: 'mailto' -}); -openerp.web.form.FieldUrlReadonly = openerp.web.form.FieldURIReadonly.extend({ - set_value: function (value) { - var s = /(\w+):(.+)/.exec(value); - if (!s || !(s[1] === 'http' || s[1] === 'https')) { return; } - this.scheme = s[1]; - this._super(s[2]); - } -}); -openerp.web.form.FieldBooleanReadonly = openerp.web.form.FieldCharReadonly.extend({ - set_value: function (value) { - this._super(value ? '\u2611' : '\u2610'); - } -}); -openerp.web.form.FieldSelectionReadonly = openerp.web.form.FieldReadonly.extend({ - template: 'FieldChar.readonly', - init: function(view, node) { - // lifted straight from r/w version - var self = this; - this._super(view, node); - this.values = this.field.selection; - _.each(this.values, function(v, i) { - if (v[0] === false && v[1] === '') { - self.values.splice(i, 1); - } - }); - this.values.unshift([false, '']); - }, - set_value: function (value) { - value = value === null ? false : value; - value = value instanceof Array ? value[0] : value; - var option = _(this.values) - .detect(function (record) { return record[0] === value; }); - this._super(value); - this.$element.find('div').text(option ? option[1] : this.values[0][1]); - } -}); -openerp.web.form.FieldMany2OneReadonly = openerp.web.form.FieldURIReadonly.extend({ - set_value: function (value) { - value = value || null; - this.invalid = false; - var self = this; - this.value = value; - self.update_dom(); - self.on_value_changed(); - var real_set_value = function(rval) { - self.value = rval; - self.$element.find('a') - .unbind('click') - .text(rval ? rval[1] : '') - .click(function () { - self.do_action({ - type: 'ir.actions.act_window', - res_model: self.field.relation, - res_id: self.value[0], - context: self.build_context(), - views: [[false, 'form']], - target: 'current' - }); - return false; - }); - }; - if (value && !(value instanceof Array)) { - new openerp.web.DataSetStatic( - this, this.field.relation, self.build_context()) - .name_get([value], function(data) { - real_set_value(data[0]); - }); - } else { - setTimeout(function() {real_set_value(value);}, 0); - } - }, - get_value: function() { - if (!this.value) { - return false; - } else if (this.value instanceof Array) { - return this.value[0]; - } else { - return this.value; - } - } -}); /** * Registry of form widgets, called by :js:`openerp.web.FormView` @@ -3201,30 +3071,6 @@ openerp.web.form.widgets = new openerp.web.Registry({ 'statusbar': 'openerp.web.form.FieldStatus' }); -openerp.web.form.FieldMany2ManyReadonly = openerp.web.form.FieldMany2Many.extend({ - force_readonly: true -}); -openerp.web.form.FieldOne2ManyReadonly = openerp.web.form.FieldOne2Many.extend({ - force_readonly: true -}); -openerp.web.form.readonly = openerp.web.form.widgets.clone({ - 'char': 'openerp.web.form.FieldCharReadonly', - 'email': 'openerp.web.form.FieldEmailReadonly', - 'url': 'openerp.web.form.FieldUrlReadonly', - 'text': 'openerp.web.form.FieldCharReadonly', - 'text_wiki' : 'openerp.web.form.FieldCharReadonly', - 'date': 'openerp.web.form.FieldCharReadonly', - 'datetime': 'openerp.web.form.FieldCharReadonly', - 'selection' : 'openerp.web.form.FieldSelectionReadonly', - 'many2one': 'openerp.web.form.FieldMany2OneReadonly', - 'many2many' : 'openerp.web.form.FieldMany2ManyReadonly', - 'one2many' : 'openerp.web.form.FieldOne2ManyReadonly', - 'one2many_list' : 'openerp.web.form.FieldOne2ManyReadonly', - 'boolean': 'openerp.web.form.FieldBooleanReadonly', - 'float': 'openerp.web.form.FieldCharReadonly', - 'integer': 'openerp.web.form.FieldCharReadonly', - 'float_time': 'openerp.web.form.FieldCharReadonly' -}); }; diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js index 728b090d679..f76eec7d178 100644 --- a/addons/web/static/src/js/view_list.js +++ b/addons/web/static/src/js/view_list.js @@ -388,10 +388,10 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# * new record. * * @param {Number|void} index the record index (in the current dataset) to switch to - * @param {String} [view="form"] the view type to switch to + * @param {String} [view="page"] the view type to switch to */ select_record:function (index, view) { - view = view || 'form'; + view = view || index == null ? 'form' : 'page'; this.dataset.index = index; _.delay(_.bind(function () { this.do_switch_view(view); @@ -462,6 +462,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# * @param {Object} results results of evaluating domain and process for a search */ do_search: function (domain, context, group_by) { + this.page = 0; this.groups.datagroup = new openerp.web.DataGroup( this, this.model, domain, context, group_by); this.groups.datagroup.sort = this.dataset._sort; @@ -661,8 +662,8 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# * * @param {Number} count number of columns to add * @param {Object} options - * @param {"before"|"after"} [position="after"] insertion position for the new columns - * @param {Object} [except] content row to not pad + * @param {"before"|"after"} [options.position="after"] insertion position for the new columns + * @param {Object} [options.except] content row to not pad */ pad_columns: function (count, options) { options = options || {}; @@ -1191,7 +1192,7 @@ openerp.web.ListView.Groups = openerp.web.Class.extend( /** @lends openerp.web.L } else if (column.type === 'float') { format = "%.2f"; } - $('<td>') + $('<td class="oe-number">') .text(_.str.sprintf(format, value)) .appendTo($row); } else { @@ -1280,10 +1281,15 @@ openerp.web.ListView.Groups = openerp.web.Class.extend( /** @lends openerp.web.L items: '> tr[data-id]', stop: function (event, ui) { var to_move = list.records.get(ui.item.data('id')), - target_id = ui.item.prev().data('id'); + target_id = ui.item.prev().data('id'), + from_index = list.records.indexOf(to_move), + target = list.records.get(target_id); + if (list.records.at(from_index - 1) == target) { + return; + } list.records.remove(to_move); - var to = target_id ? list.records.indexOf(list.records.get(target_id)) + 1 : 0; + var to = target_id ? list.records.indexOf(target) + 1 : 0; list.records.add(to_move, { at: to }); // resequencing time! @@ -1295,7 +1301,14 @@ openerp.web.ListView.Groups = openerp.web.Class.extend( /** @lends openerp.web.L // write are independent from one another, so we can just // launch them all at the same time and we don't really // give a fig about when they're done - dataset.write(record.get('id'), {sequence: seq}); + // FIXME: breaks on o2ms (e.g. Accounting > Financial + // Accounting > Taxes > Taxes, child tax accounts) + // when synchronous (without setTimeout) + (function (dataset, id, seq) { + setTimeout(function () { + dataset.write(id, {sequence: seq}); + }, 0); + }(dataset, record.get('id'), seq)); record.set('sequence', seq); } diff --git a/addons/web/static/src/js/view_list_editable.js b/addons/web/static/src/js/view_list_editable.js index dba0a1abba4..b2edf55b314 100644 --- a/addons/web/static/src/js/view_list_editable.js +++ b/addons/web/static/src/js/view_list_editable.js @@ -5,6 +5,7 @@ openerp.web.list_editable = function (openerp) { var KEY_RETURN = 13, KEY_ESCAPE = 27; + var QWeb = openerp.web.qweb; // editability status of list rows openerp.web.ListView.prototype.defaults.editable = null; @@ -165,6 +166,35 @@ openerp.web.list_editable = function (openerp) { view.arch.attrs.col = 2 * view.arch.children.length; return view; }, + on_row_keyup: function (e) { + var self = this; + switch (e.which) { + case KEY_RETURN: + this.save_row().then(function (result) { + if (result.created) { + self.new_record(); + return; + } + + var next_record_id, + next_record = self.records.at( + self.records.indexOf(result.edited_record) + 1); + if (next_record) { + next_record_id = next_record.get('id'); + self.dataset.index = _(self.dataset.ids) + .indexOf(next_record_id); + } else { + self.dataset.index = 0; + next_record_id = self.records.at(0).get('id'); + } + self.edit_record(next_record_id); + }); + break; + case KEY_ESCAPE: + this.cancel_edition(); + break; + } + }, render_row_as_form: function (row) { var self = this; this.cancel_pending_edition().then(function () { @@ -178,24 +208,10 @@ openerp.web.list_editable = function (openerp) { .delegate('button.oe-edit-row-save', 'click', function () { self.save_row(); }) - .delegate('button.oe-edit-row-cancel', 'click', function () { - self.cancel_edition(); - }) .delegate('button', 'keyup', function (e) { e.stopImmediatePropagation(); }) - .keyup(function (e) { - switch (e.which) { - case KEY_RETURN: - self.save_row(true); - break; - case KEY_ESCAPE: - self.cancel_edition(); - break; - default: - return; - } - }); + .keyup($.proxy(self, 'on_row_keyup')); if (row) { $new_row.replaceAll(row); } else if (self.options.editable) { @@ -230,20 +246,18 @@ openerp.web.list_editable = function (openerp) { .addClass('oe-field-cell') .removeAttr('width') .end() - .find('td:first').removeClass('oe-field-cell').end() .find('td:last').removeClass('oe-field-cell').end(); + if (self.options.selectable) { + $new_row.prepend('<td>'); + } // pad in case of groupby _(self.columns).each(function (column) { if (column.meta) { $new_row.prepend('<td>'); } }); - // Add columns for the cancel and save buttons, if - // there are none in the list - if (!self.options.selectable) { - self.view.pad_columns( - 1, {except: $new_row, position: 'before'}); - } + // Add column for the save, if + // there is none in the list if (!self.options.deletable) { self.view.pad_columns( 1, {except: $new_row}); @@ -273,53 +287,43 @@ openerp.web.list_editable = function (openerp) { }); }, /** - * Saves the current row, and triggers the edition of its following - * sibling if asked. + * Saves the current row, and returns a Deferred resolving to an object + * with the following properties: * - * @param {Boolean} [edit_next=false] should the next row become editable - * @returns {$.Deferred} + * ``created`` + * Boolean flag indicating whether the record saved was being created + * (``true`` or edited (``false``) + * ``edited_record`` + * The result of saving the record (either the newly created record, + * or the post-edition record), after insertion in the Collection if + * needs be. + * + * @returns {$.Deferred<{created: Boolean, edited_record: Record}>} */ - save_row: function (edit_next) { + save_row: function () { //noinspection JSPotentiallyInvalidConstructorUsage var self = this, done = $.Deferred(); - this.edition_form.do_save(function (result) { - if (result.created && !self.edition_id) { - self.records.add({id: result.result}, - {at: self.options.editable === 'top' ? 0 : null}); - self.edition_id = result.result; - } - var edited_record = self.records.get(self.edition_id), - next_record = self.records.at( - self.records.indexOf(edited_record) + 1); + return this.edition_form + .do_save(null, this.options.editable === 'top') + .pipe(function (result) { + if (result.created && !self.edition_id) { + self.records.add({id: result.result}, + {at: self.options.editable === 'top' ? 0 : null}); + self.edition_id = result.result; + } + var edited_record = self.records.get(self.edition_id); - $.when( - self.handle_onwrite(self.edition_id), - self.cancel_pending_edition().then(function () { - $(self).trigger('saved', [self.dataset]); - if (!edit_next) { - return; - } - if (result.created) { - self.new_record(); - return; - } - var next_record_id; - if (next_record) { - next_record_id = next_record.get('id'); - self.dataset.index = _(self.dataset.ids) - .indexOf(next_record_id); - } else { - self.dataset.index = 0; - next_record_id = self.records.at(0).get('id'); - } - self.edit_record(next_record_id); - })).then(function () { - done.resolve(); - }); - }, this.options.editable === 'top').fail(function () { - done.reject(); - }); - return done.promise(); + return $.when( + self.handle_onwrite(self.edition_id), + self.cancel_pending_edition().then(function () { + $(self).trigger('saved', [self.dataset]); + })).pipe(function () { + return { + created: result.created || false, + edited_record: edited_record + }; + }, null); + }, null); }, /** * If the current list is being edited, ensures it's saved @@ -350,6 +354,19 @@ openerp.web.list_editable = function (openerp) { new_record: function () { this.dataset.index = null; this.render_row_as_form(); + }, + render_record: function (record) { + var index = this.records.indexOf(record); + // FIXME: context dict should probably be extracted cleanly + return QWeb.render('ListView.row', { + columns: this.columns, + options: this.options, + record: record, + row_parity: (index % 2 === 0) ? 'even' : 'odd', + view: this.view, + render_cell: $.proxy(this, 'render_cell'), + edited: !!this.edition_form + }); } }); if (!openerp.web.list) { diff --git a/addons/web/static/src/js/view_page.js b/addons/web/static/src/js/view_page.js new file mode 100644 index 00000000000..2ca07152a56 --- /dev/null +++ b/addons/web/static/src/js/view_page.js @@ -0,0 +1,218 @@ +openerp.web.page = function (openerp) { + var _t = openerp.web._t; + var QWeb = openerp.web.qweb; + + openerp.web.views.add('page', 'openerp.web.PageView'); + openerp.web.PageView = openerp.web.FormView.extend({ + form_template: "PageView", + init: function () { + this._super.apply(this, arguments); + this.registry = openerp.web.page.readonly; + }, + on_loaded: function(data) { + this._super(data); + this.$form_header.find('button.oe_form_button_edit').click(this.on_button_edit); + this.$form_header.find('button.oe_form_button_create').click(this.on_button_create); + this.$form_header.find('button.oe_form_button_duplicate').click(this.on_button_duplicate); + this.$form_header.find('button.oe_form_button_delete').click(this.on_button_delete); + }, + on_button_edit: function() { + return this.do_switch_view('form'); + }, + on_button_create: function() { + this.dataset.index = null; + return this.do_switch_view('form'); + }, + on_button_duplicate: function() { + var self = this; + var def = $.Deferred(); + $.when(this.has_been_loaded).then(function() { + self.dataset.call('copy', [self.datarecord.id, {}, self.dataset.context]).then(function(new_id) { + return self.on_created({ result : new_id }); + }).then(function() { + return self.do_switch_view('form'); + }).then(function() { + def.resolve(); + }); + }); + return def.promise(); + }, + on_button_delete: function() { + var self = this; + var def = $.Deferred(); + $.when(this.has_been_loaded).then(function() { + if (self.datarecord.id && confirm(_t("Do you really want to delete this record?"))) { + self.dataset.unlink([self.datarecord.id]).then(function() { + self.on_pager_action('next'); + def.resolve(); + }); + } else { + setTimeout(function () { + def.reject(); + }, 0) + } + }); + return def.promise(); + } + }); + + /** @namespace */ + openerp.web.page = {}; + + openerp.web.page.FieldReadonly = openerp.web.form.Field.extend({ + + }); + openerp.web.page.FieldCharReadonly = openerp.web.page.FieldReadonly.extend({ + template: 'FieldChar.readonly', + init: function(view, node) { + this._super(view, node); + this.password = this.node.attrs.password === 'True' || this.node.attrs.password === '1'; + }, + set_value: function (value) { + this._super.apply(this, arguments); + var show_value = openerp.web.format_value(value, this, ''); + if (this.password) { + show_value = new Array(show_value.length + 1).join('*'); + } + this.$element.find('div').text(show_value); + return show_value; + } + }); + openerp.web.page.FieldURIReadonly = openerp.web.page.FieldCharReadonly.extend({ + template: 'FieldURI.readonly', + scheme: null, + set_value: function (value) { + var displayed = this._super.apply(this, arguments); + this.$element.find('a') + .attr('href', this.scheme + ':' + displayed) + .text(displayed); + } + }); + openerp.web.page.FieldEmailReadonly = openerp.web.page.FieldURIReadonly.extend({ + scheme: 'mailto' + }); + openerp.web.page.FieldUrlReadonly = openerp.web.page.FieldURIReadonly.extend({ + set_value: function (value) { + var s = /(\w+):(.+)/.exec(value); + if (!s || !(s[1] === 'http' || s[1] === 'https')) { return; } + this.scheme = s[1]; + this._super(s[2]); + } + }); + openerp.web.page.FieldBooleanReadonly = openerp.web.page.FieldCharReadonly.extend({ + set_value: function (value) { + this._super(value ? '\u2611' : '\u2610'); + } + }); + openerp.web.page.FieldSelectionReadonly = openerp.web.page.FieldReadonly.extend({ + template: 'FieldChar.readonly', + init: function(view, node) { + // lifted straight from r/w version + var self = this; + this._super(view, node); + this.values = _.clone(this.field.selection); + _.each(this.values, function(v, i) { + if (v[0] === false && v[1] === '') { + self.values.splice(i, 1); + } + }); + this.values.unshift([false, '']); + }, + set_value: function (value) { + value = value === null ? false : value; + value = value instanceof Array ? value[0] : value; + var option = _(this.values) + .detect(function (record) { return record[0] === value; }); + this._super(value); + this.$element.find('div').text(option ? option[1] : this.values[0][1]); + } + }); + openerp.web.page.FieldMany2OneReadonly = openerp.web.page.FieldURIReadonly.extend({ + set_value: function (value) { + value = value || null; + this.invalid = false; + var self = this; + this.value = value; + self.update_dom(); + self.on_value_changed(); + var real_set_value = function(rval) { + self.value = rval; + self.$element.find('a') + .unbind('click') + .text(rval ? rval[1] : '') + .click(function () { + self.do_action({ + type: 'ir.actions.act_window', + res_model: self.field.relation, + res_id: self.value[0], + context: self.build_context(), + views: [[false, 'page'], [false, 'form']], + target: 'current' + }); + return false; + }); + }; + if (value && !(value instanceof Array)) { + new openerp.web.DataSetStatic( + this, this.field.relation, self.build_context()) + .name_get([value], function(data) { + real_set_value(data[0]); + }); + } else { + setTimeout(function() {real_set_value(value);}, 0); + } + }, + get_value: function() { + if (!this.value) { + return false; + } else if (this.value instanceof Array) { + return this.value[0]; + } else { + return this.value; + } + } + }); + openerp.web.page.FieldReferenceReadonly = openerp.web.page.FieldMany2OneReadonly.extend({ + set_value: function (value) { + if (!value) { + return this._super(null); + } + var reference = value.split(','); + this.field.relation = reference[0]; + var id = parseInt(reference[1], 10); + return this._super(id); + }, + get_value: function () { + if (!this.value) { + return null; + } + return _.str.sprintf('%s,%d', this.field.relation, this.value[0]); + } + }); + + openerp.web.page.FieldMany2ManyReadonly = openerp.web.form.FieldMany2Many.extend({ + force_readonly: true + }); + openerp.web.page.FieldOne2ManyReadonly = openerp.web.form.FieldOne2Many.extend({ + force_readonly: true + }); + openerp.web.page.readonly = openerp.web.form.widgets.clone({ + 'char': 'openerp.web.page.FieldCharReadonly', + 'email': 'openerp.web.page.FieldEmailReadonly', + 'url': 'openerp.web.page.FieldUrlReadonly', + 'text': 'openerp.web.page.FieldCharReadonly', + 'text_wiki' : 'openerp.web.page.FieldCharReadonly', + 'date': 'openerp.web.page.FieldCharReadonly', + 'datetime': 'openerp.web.page.FieldCharReadonly', + 'selection' : 'openerp.web.page.FieldSelectionReadonly', + 'many2one': 'openerp.web.page.FieldMany2OneReadonly', + 'many2many' : 'openerp.web.page.FieldMany2ManyReadonly', + 'one2many' : 'openerp.web.page.FieldOne2ManyReadonly', + 'one2many_list' : 'openerp.web.page.FieldOne2ManyReadonly', + 'reference': 'openerp.web.page.FieldReferenceReadonly', + 'boolean': 'openerp.web.page.FieldBooleanReadonly', + 'float': 'openerp.web.page.FieldCharReadonly', + 'integer': 'openerp.web.page.FieldCharReadonly', + 'float_time': 'openerp.web.page.FieldCharReadonly' + }); +}; diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js index 5a26ef58b03..f583dd259e7 100644 --- a/addons/web/static/src/js/views.js +++ b/addons/web/static/src/js/views.js @@ -75,6 +75,10 @@ session.web.ActionManager = session.web.Widget.extend({ } }, do_action: function(action, on_close) { + if (!action.type) { + console.error("No type for action", action); + return; + } var type = action.type.replace(/\./g,'_'); var popup = action.target === 'new'; action.flags = _.extend({ @@ -85,12 +89,24 @@ session.web.ActionManager = session.web.Widget.extend({ pager : !popup }, action.flags || {}); if (!(type in this)) { - console.log("Action manager can't handle action of type " + action.type, action); + console.error("Action manager can't handle action of type " + action.type, action); return; } return this[type](action, on_close); }, + null_action: function() { + this.dialog_stop(); + this.content_stop(); + }, ir_actions_act_window: function (action, on_close) { + if (_(['base.module.upgrade', 'base.setup.installer']) + .contains(action.res_model)) { + var old_close = on_close; + on_close = function () { + session.webclient.do_reload(); + if (old_close) { old_close(); } + }; + } if (action.target === 'new') { if (this.dialog == null) { this.dialog = new session.web.Dialog(this, { title: action.name, width: '80%' }); @@ -121,12 +137,6 @@ session.web.ActionManager = session.web.Widget.extend({ if (!this.dialog && on_closed) { on_closed(); } - if (this.dialog && action.context) { - var model = action.context.active_model; - if (model === 'base.module.upgrade' || model === 'base.setup.installer' || model === 'base.module.upgrade') { - session.webclient.do_reload(); - } - } this.dialog_stop(); }, ir_actions_server: function (action, on_closed) { @@ -186,12 +196,12 @@ session.web.ViewManager = session.web.Widget.extend(/** @lends session.web.View this.model = dataset ? dataset.model : undefined; this.dataset = dataset; this.searchview = null; - this.last_search = false; this.active_view = null; this.views_src = _.map(views, function(x) {return x instanceof Array? {view_id: x[0], view_type: x[1]} : x;}); this.views = {}; this.flags = this.flags || {}; this.registry = session.web.views; + this.views_history = []; }, render: function() { return session.web.qweb.render(this.template, { @@ -211,6 +221,7 @@ session.web.ViewManager = session.web.Widget.extend(/** @lends session.web.View var views_ids = {}; _.each(this.views_src, function(view) { self.views[view.view_type] = $.extend({}, view, { + deferred : $.Deferred(), controller : null, options : _.extend({ sidebar_id : self.element_id + '_sidebar_' + view.view_type, @@ -230,11 +241,15 @@ session.web.ViewManager = session.web.Widget.extend(/** @lends session.web.View * Asks the view manager to switch visualization mode. * * @param {String} view_type type of view to display + * @param {Boolean} [no_store=false] don't store the view being switched to on the switch stack * @returns {jQuery.Deferred} new view loading promise */ - on_mode_switch: function(view_type) { + on_mode_switch: function(view_type, no_store) { var self = this, view_promise; + if (!no_store) { + this.views_history.push(view_type); + } this.active_view = view_type; var view = this.views[view_type]; if (!view.controller) { @@ -245,17 +260,19 @@ session.web.ViewManager = session.web.Widget.extend(/** @lends session.web.View controller.set_embedded_view(view.embedded_view); } controller.do_switch_view.add_last(this.on_mode_switch); + controller.do_prev_view.add_last(this.on_prev_view); var container = $("#" + this.element_id + '_view_' + view_type); view_promise = controller.appendTo(container); this.views[view_type].controller = controller; + this.views[view_type].deferred.resolve(); $.when(view_promise).then(function() { self.on_controller_inited(view_type, controller); if (self.searchview && view.controller.searchable !== false) { - self.do_searchview_search(); + self.searchview.ready.then(self.searchview.do_search); } }); } else if (this.searchview && view.controller.searchable !== false) { - self.do_searchview_search(); + this.searchview.ready.then(this.searchview.do_search); } if (this.searchview) { @@ -263,7 +280,7 @@ session.web.ViewManager = session.web.Widget.extend(/** @lends session.web.View } this.$element - .find('.views-switchers button').removeAttr('disabled') + .find('.oe_vm_switch button').removeAttr('disabled') .filter('[data-view-type="' + view_type + '"]') .attr('disabled', true); @@ -278,12 +295,30 @@ session.web.ViewManager = session.web.Widget.extend(/** @lends session.web.View } } $.when(view_promise).then(function () { - self.$element.find('.oe_view_title:first').text( + self.$element.find('.oe_view_title_text:first').text( self.display_title()); }); return view_promise; }, /** + * Returns to the view preceding the caller view in this manager's + * navigation history (the navigation history is appended to via + * on_mode_switch) + * + * @param {Boolean} [created=false] returning from a creation + * @returns {$.Deferred} switching end signal + */ + on_prev_view: function (created) { + var current_view = this.views_history.pop(); + var previous_view = this.views_history[this.views_history.length - 1]; + // APR special case: "If creation mode from list (and only from a list), + // after saving, go to page view (don't come back in list)" + if (created && current_view === 'form' && previous_view === 'list') { + return this.on_mode_switch('page'); + } + return this.on_mode_switch(previous_view, true); + }, + /** * Sets up the current viewmanager's search view. * * @param {Number|false} view_id the view to use or false for a default one @@ -304,20 +339,15 @@ session.web.ViewManager = session.web.Widget.extend(/** @lends session.web.View do_searchview_search: function(domains, contexts, groupbys) { var self = this, controller = this.views[this.active_view].controller; - if (domains || contexts) { - this.rpc('/web/session/eval_domain_and_context', { - domains: [this.action.domain || []].concat(domains || []), - contexts: [this.action.context || {}].concat(contexts || []), - group_by_seq: groupbys || [] - }, function (results) { - self.dataset.context = results.context; - self.dataset.domain = results.domain; - self.last_search = [results.domain, results.context, results.group_by]; - controller.do_search(results.domain, results.context, results.group_by); - }); - } else if (this.last_search) { - controller.do_search.apply(controller, this.last_search); - } + this.rpc('/web/session/eval_domain_and_context', { + domains: [this.action.domain || []].concat(domains || []), + contexts: [this.action.context || {}].concat(contexts || []), + group_by_seq: groupbys || [] + }, function (results) { + self.dataset.context = results.context; + self.dataset.domain = results.domain; + controller.do_search(results.domain, results.context, results.group_by); + }); }, /** * Event launched when a controller has been inited. @@ -375,10 +405,16 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner } this.dataset = dataset; this.flags = this.action.flags || {}; - if (action.res_model == 'board.board' && action.views.length == 1 && action.views) { - // Not elegant but allows to avoid form chrome (pager, save/new - // buttons, sidebar, ...) displaying - this.flags.search_view = this.flags.pager = this.flags.sidebar = this.flags.action_buttons = false; + if (action.res_model == 'board.board' && action.view_mode === 'form') { + // Special case for Dashboards + _.extend(this.flags, { + views_switcher : false, + display_title : false, + search_view : false, + pager : false, + sidebar : false, + action_buttons : false + }); } // setup storage for session-wise menu hiding @@ -411,21 +447,18 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner var main_view_loaded = this._super(); var manager_ready = $.when(searchview_loaded, main_view_loaded); - if (searchview_loaded && this.action['auto_search'] !== false) { - // schedule auto_search - manager_ready.then(this.searchview.do_search); - } this.$element.find('.oe_get_xml_view').click(function () { - // TODO: add search view? - $('<pre>').text(session.web.json_node_to_xml( - self.views[self.active_view].controller.fields_view.arch, true)) - .dialog({ width: '95%'}); + var view = self.views[self.active_view].controller, + view_id = view.fields_view.view_id; + if (view_id) { + view.on_sidebar_edit_resource('ir.ui.view', view_id); + } }); if (this.action.help && !this.flags.low_profile) { var Users = new session.web.DataSet(self, 'res.users'), - header = this.$element.find('.oe-view-manager-header'); - header.delegate('blockquote button', 'click', function() { + $tips = this.$element.find('.oe_view_manager_menu_tips'); + $tips.delegate('blockquote button', 'click', function() { var $this = $(this); //noinspection FallthroughInSwitchStatementJS switch ($this.attr('name')) { @@ -442,7 +475,7 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner if (!(user && user.id === self.session.uid)) { return; } - header.find('blockquote').toggle(user.menu_tips); + $tips.find('blockquote').toggle(user.menu_tips); }); } } @@ -459,10 +492,15 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner return manager_ready; }, - on_mode_switch: function (view_type) { + on_mode_switch: function (view_type, no_store) { var self = this; + var switched = $.when(this._super(view_type, no_store)).then(function () { + self.$element.find('.oe-view-manager-logs:first') + .addClass('oe-folded').removeClass('oe-has-more') + .find('ul').empty(); + }); return $.when( - this._super(view_type), + switched, this.shortcut_check(this.views[view_type]) ).then(function() { var controller = self.views[self.active_view].controller, @@ -470,10 +508,10 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner view_id = (fvg && fvg.view_id) || '--'; self.$element.find('.oe_get_xml_view span').text(view_id); if (!self.action.name && fvg) { - self.$element.find('.oe_view_title').text(fvg.arch.attrs.string || fvg.name); + self.$element.find('.oe_view_title_text').text(fvg.arch.attrs.string || fvg.name); } - var $title = self.$element.find('.oe_view_title'), + var $title = self.$element.find('.oe_view_title_text'), $search_prefix = $title.find('span.oe_searchable_view'); if (controller.searchable !== false) { if (!$search_prefix.length) { @@ -575,25 +613,29 @@ session.web.Sidebar = session.web.Widget.extend({ }); }, add_default_sections: function() { + var self = this, + view = this.widget_parent, + view_manager = view.widget_parent, + action = view_manager.action; if (this.session.uid === 1) { this.add_section(_t('Customize'), 'customize'); this.add_items('customize', [ { label: _t("Manage Views"), - callback: this.widget_parent.on_sidebar_manage_views, + callback: view.on_sidebar_manage_views, title: _t("Manage views of the current object") }, { label: _t("Edit Workflow"), - callback: this.widget_parent.on_sidebar_edit_workflow, + callback: view.on_sidebar_edit_workflow, title: _t("Manage views of the current object"), classname: 'oe_hide oe_sidebar_edit_workflow' }, { label: _t("Customize Object"), - callback: this.widget_parent.on_sidebar_customize_object, + callback: view.on_sidebar_customize_object, title: _t("Manage views of the current object") }, { label: _t("Translate"), - callback: this.widget_parent.on_sidebar_translate, + callback: view.on_sidebar_translate, title: _t("Technical translation") } ]); @@ -603,16 +645,36 @@ session.web.Sidebar = session.web.Widget.extend({ this.add_items('other', [ { label: _t("Import"), - callback: this.widget_parent.on_sidebar_import + callback: view.on_sidebar_import }, { label: _t("Export"), - callback: this.widget_parent.on_sidebar_export + callback: view.on_sidebar_export }, { label: _t("View Log"), - callback: this.widget_parent.on_sidebar_view_log, + callback: view.on_sidebar_view_log, classname: 'oe_hide oe_sidebar_view_log' } ]); + + if (session.connection.debug) { + this.add_section("Debug", 'debug'); + if (action && action.id) { + this.add_items('debug', [{ + label: "Edit Action", + callback: function() { + view.on_sidebar_edit_resource(action.type, action.id); + } + }]); + } + if (view_manager.searchview && view_manager.searchview.view_id) { + this.add_items('debug', [{ + label: "Edit SearchView", + callback: function() { + view.on_sidebar_edit_resource('ir.ui.view', view_manager.searchview.view_id); + } + }]); + } + } }, add_toolbar: function(toolbar) { @@ -915,7 +977,19 @@ session.web.View = session.web.Widget.extend(/** @lends session.web.View# */{ if (action_data.special) { return handler({result: {"type":"ir.actions.act_window_close"}}); } else if (action_data.type=="object") { - return dataset.call_button(action_data.name, [[record_id], context], handler); + var args = [[record_id]], additional_args = []; + if (action_data.args) { + try { + // Warning: quotes and double quotes problem due to json and xml clash + // Maybe we should force escaping in xml or do a better parse of the args array + additional_args = JSON.parse(action_data.args.replace(/'/g, '"')); + args = args.concat(additional_args); + } catch(e) { + console.error("Could not JSON.parse arguments", action_data.args); + } + } + args.push(context); + return dataset.call_button(action_data.name, args, handler); } else if (action_data.type=="action") { return this.rpc('/web/action/load', { action_id: parseInt(action_data.name, 10), context: context, do_not_eval: true}, handler); } else { @@ -933,8 +1007,16 @@ session.web.View = session.web.Widget.extend(/** @lends session.web.View# */{ this.embedded_view = embedded_view; this.options.sidebar = false; }, - do_switch_view: function(view) { - }, + /** + * Switches to a specific view type + * + * @param {String} view view type to switch to + */ + do_switch_view: function(view) { }, + /** + * Cancels the switch to the current view, switches to the previous one + */ + do_prev_view: function () { }, do_search: function(view) { }, @@ -953,7 +1035,14 @@ session.web.View = session.web.Widget.extend(/** @lends session.web.View# */{ console.log('Todo'); }, on_sidebar_customize_object: function() { - console.log('Todo'); + var self = this; + this.rpc('/web/dataset/search_read', { + model: 'ir.model', + fields: ['id'], + domain: [['model', '=', self.dataset.model]] + }, function (result) { + self.on_sidebar_edit_resource('ir.model', result.ids[0]); + }); }, on_sidebar_import: function() { var import_view = new session.web.DataImport(this, this.dataset); @@ -969,11 +1058,29 @@ session.web.View = session.web.Widget.extend(/** @lends session.web.View# */{ domain : [['type', '!=', 'object'], '|', ['name', '=', this.dataset.model], ['name', 'ilike', this.dataset.model + ',']], views: [[false, 'list'], [false, 'form']], type : 'ir.actions.act_window', - auto_search : true, view_type : "list", view_mode : "list" }); }, + on_sidebar_edit_resource: function(model, id, domain) { + var action = { + res_model : model, + type : 'ir.actions.act_window', + view_type : 'form', + view_mode : 'form', + target : 'new', + flags : {} + } + if (id) { + action.res_id = id, + action.views = [[false, 'form']]; + } else if (domain) { + action.views = [[false, 'list'], [false, 'form']]; + action.domain = domain; + action.flags.views_switcher = true; + } + this.do_action(action); + }, on_sidebar_view_log: function() { }, sidebar_context: function () { diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 24defe23e2e..393cb46143c 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -414,11 +414,12 @@ </t> <t t-name="ViewManager"> <table class="view-manager-main-table"> + <tbody> <tr> <td class="view-manager-main-content"> <div class="oe-view-manager-header"> <h2 class="oe_view_title" t-if="self.flags.display_title !== false"> - <t t-esc="self.display_title()"/> + <span class="oe_view_title_text"><t t-esc="self.display_title()"/></span> </h2> <div class="oe_vm_switch"> <t t-if="views.length != 1" t-foreach="views" t-as="view"> @@ -439,22 +440,31 @@ </t> </td> </tr> + </tbody> </table> </t> <t t-extend="ViewManager" t-name="ViewManagerAction"> - <t t-jquery=".oe-view-manager-header" t-operation="prepend"> - <blockquote t-if="self.action.help and !self.flags.low_profile - and !(self.action.id in self.session.hidden_menutips)"> - <p><t t-esc="self.action.help"/></p> - <div> - <button type="button" name="hide">Hide this tip</button> - <button type="button" name="disable">Disable all tips</button> - </div> - </blockquote> - <a class="oe-shortcut-toggle" title="Add / Remove Shortcut..." - href="javascript: void(0)"> </a> - <button t-if="self.session.debug" class="oe_get_xml_view"> + <t t-jquery=".view-manager-main-table tbody" t-operation="prepend"> + <tr> + <td class="oe_view_manager_menu_tips" colspan="2"> + <blockquote t-if="self.action.help and !self.flags.low_profile + and !(self.action.id in self.session.hidden_menutips)"> + <p><t t-esc="self.action.help"/></p> + <div> + <button type="button" name="hide">Hide this tip</button> + <button type="button" name="disable">Disable all tips</button> + </div> + </blockquote> + </td> + </tr> + </t> + <t t-jquery="h2.oe_view_title" t-operation="prepend"> + <a t-if="self.flags.display_title !== false" class="oe-shortcut-toggle" title="Add / Remove Shortcut..." + href="javascript: void(0)"> </a> + </t> + <t t-jquery="h2.oe_view_title" t-operation="after"> + <button t-if="self.session.debug and self.flags.display_title !== false" class="oe_get_xml_view"> View#<span></span> </button> </t> @@ -530,16 +540,17 @@ t-att-id="'treerow_' + record.id" t-att-data-id="record.id" t-att-data-level="level + 1"> <t t-set="children" t-value="record[children_field]"/> - <t t-set="has_children" t-value="children and children.length"/> + <t t-set="class" t-value="children and children.length ? 'treeview-tr' : 'treeview-td'"/> <td t-foreach="fields_view" t-as="field" t-if="!field.attrs.modifiers.tree_invisible" t-att-data-id="record.id" t-att-style="color_for(record) + (!field_index ? 'background-position: ' + 19*level + 'px; padding-left: ' + 19*level + 'px;' : '')" - t-att-class="!field_index and has_children ? 'treeview-tr' : 'treeview-td'"> + t-att-class="class"> <span t-if="!field.attrs.modifiers.invisible"> <t t-esc="render(record[field.attrs.name], fields[field.attrs.name])" /> </span> + <t t-set="class" t-value="'treeview-td'"/> </td> </tr> @@ -625,18 +636,9 @@ </td> </t> - <t t-if="options.radio"> - <th t-if="options.selectable" class="oe-record-selector" width="1"> - <input type="radio" name ="radiogroup"/> - </th> - </t> - <t t-if="!options.radio"> - <th t-if="options.selectable" class="oe-record-selector" width="1"> - <input type="checkbox"/> - </th> - </t> - - + <th t-if="options.selectable" class="oe-record-selector" width="1"> + <input t-att-type="options.radio? 'radio': 'checkbox'" name ="radiogroup" t-att-checked="options.select_view_id == record.get('id')? true: null"/> + </th> <t t-foreach="columns" t-as="column"> <t t-set="align" t-value="column.type === 'integer' or column.type == 'float'"/> <td t-if="!column.meta and column.invisible !== '1'" t-att-title="column.help" @@ -647,7 +649,7 @@ </td> </t> <td t-if="options.deletable" class='oe-record-delete' width="1"> - <button type="button" name="delete">♻</button> + <button type="button" name="delete"></button> </td> </tr> <t t-name="ListView.row.form"> @@ -657,14 +659,8 @@ <t t-name="FormView"> <div class="oe_form_header"> <div class="oe_form_buttons" t-if="widget.options.action_buttons !== false"> - <button type="button" class="oe_form_button_save oe_form_on_editable">Save</button> - <button type="button" class="oe_form_button_toggle"> - <span class="oe_form_on_editable">Cancel</span> - <span class="oe_form_on_readonly">Edit</span> - </button> - <button type="button" class="oe_form_button_new oe_form_on_readonly">Create</button> - <button type="button" class="oe_form_button_duplicate oe_form_on_readonly">Duplicate</button> - <button type="button" class="oe_form_button_delete oe_form_on_readonly">Delete</button> + <button type="button" class="oe_form_button_save">Save</button> + <button type="button" class="oe_form_button_cancel">Cancel</button> </div> <div class="oe_form_pager" t-if="widget.options.pager !== false"> <button type="button" data-pager-action="first">First</button> @@ -678,6 +674,14 @@ </div> <t t-raw="frame.render()"/> </t> +<t t-name="PageView" t-extend="FormView"> + <t t-jquery=".oe_form_buttons" t-operation="inner"> + <button type="button" class="oe_form_button_edit">Edit</button> + <button type="button" class="oe_form_button_create">Create</button> + <button type="button" class="oe_form_button_duplicate">Duplicate</button> + <button type="button" class="oe_form_button_delete">Delete</button> + </t> +</t> <t t-name="FormView.sidebar.attachments"> <div class="oe-sidebar-attachments-toolbar"> <div class="oe-binary-file-set" style="float: right"> @@ -722,7 +726,6 @@ <t t-foreach="row" t-as="td"> <td t-att-colspan="td.colspan gt 1 ? td.colspan : undefined" t-att-width="td.width" - t-att-nowrap="td.nowrap or td.is_field_m2o? 'true' : undefined" t-att-valign="td.table ? 'top' : undefined" t-attf-class="oe_form_frame_cell #{td.classname} #{td.element_class}" > @@ -732,6 +735,11 @@ </tr> </table> </t> +<t t-name="WidgetFrame.readonly" t-extend="WidgetFrame"> + <t t-jquery=">table"> + this.attr('class', this.attr('class')+' oe_form_readonly'); + </t> +</t> <t t-name="WidgetGroup"> <t t-if="widget.string"> <fieldset class="oe_group_box"> @@ -854,15 +862,15 @@ t-att-name="widget.name" t-att-id="widget.element_id" t-attf-class="field_#{widget.type} #{_(['integer', 'float', 'float_time']).contains(widget.type) ? 'oe-number' : ''}" - t-attf-style="width: #{widget.field.translate ? '99' : '100'}%" + style="width: 100%" /> - <img class="oe_field_translate" t-if="widget.field.translate" t-att-src='widget.session.server + "/web/static/src/img/icons/terp-translate.png"' width="16" height="16" border="0"/> + <img class="oe_field_translate oe_input_icon" t-if="widget.field.translate" t-att-src='widget.session.server + "/web/static/src/img/icons/terp-translate.png"' width="16" height="16" border="0"/> </t> <t t-name="FieldChar.readonly"> <div t-att-id="widget.element_id" t-attf-class="field_#{widget.type} #{_(['integer', 'float', 'float_time']).contains(widget.type) ? 'oe-number' : ''}" - t-attf-style="width: #{widget.field.translate ? '99' : '100'}%"> + style="width: 100%"> </div> </t> <t t-name="FieldURI.readonly"> @@ -871,7 +879,7 @@ <t t-name="FieldEmail"> <table cellpadding="0" cellspacing="0" border="0" width="100%"> <tr> - <td width="100%"> + <td width="100%" style="position: relative"> <t t-call="FieldChar"/> </td> <td width="16"> @@ -885,7 +893,7 @@ <t t-name="FieldUrl"> <table cellpadding="0" cellspacing="0" border="0" width="100%"> <tr> - <td width="100%"> + <td width="100%" style="position: relative"> <t t-call="FieldChar"/> </td> <td width="16"> @@ -901,9 +909,9 @@ t-att-name="widget.name" t-att-id="widget.element_id" t-attf-class="field_#{widget.type}" - t-attf-style="width: #{widget.field.translate ? '99' : '100'}%" + style="width: 100%" ></textarea> - <img class="oe_field_translate" t-if="widget.field.translate" t-att-src='widget.session.server + "/web/static/src/img/icons/terp-translate.png"' width="16" height="16" border="0"/> + <img class="oe_field_translate oe_input_icon" t-if="widget.field.translate" t-att-src='widget.session.server + "/web/static/src/img/icons/terp-translate.png"' width="16" height="16" border="0"/> </t> <t t-name="web.datetimepicker"> <div class="oe_datepicker_root"> @@ -1131,15 +1139,17 @@ </form> </t> <t t-name="SearchView.managed-filters"> - <option value="_filters">-- Filters --</option> - <t t-set="i" t-value="0"/> - <t t-foreach="filters" t-as="filter"> - <option t-att-value="'get:' + i"><t t-esc="filter.name"/></option> - <t t-set="i" t-value="i+1"/> - </t> - <option value="_actions">-- Actions --</option> - <option value="save_filter">Save Filter</option> - <option value="manage_filters">Manage Filters</option> + <option/> + <optgroup label="-- Filters --"> + <t t-foreach="filters" t-as="filter"> + <option t-attf-value="get:#{filter_index}"><t t-esc="filter.name"/></option> + </t> + </optgroup> + <optgroup label="-- Actions --"> + <option value="save_filter">Save Filter</option> + <option value="add_to_dashboard">Add to Dashboard</option> + <option value="manage_filters">Manage Filters</option> + </optgroup> </t> <t t-name="SearchView.managed-filters.add"> <div> @@ -1148,6 +1158,16 @@ <p>(Any existing filter with the same name will be replaced)</p> </div> </t> +<t t-name="SearchView.add_to_dashboard"> + <div class="oe_forms"> + <p><b>Select Dashboard to add this filter to:</b></p> + <select style="width: 100%; margin-right: 1em;"> + <option t-foreach="dashboards" t-as="menu" t-att-value="menu.id" t-att-selected="(menu.id == selected_menu_id) || undefined"><t t-esc="menu.name"/></option> + </select> + <p><b>Title of new Dashboard item:</b></p> + <input type="text" style="width: 100%; margin-right: 1em;"/> + </div> +</t> <t t-name="SearchView.render_lines"> <table class="oe-searchview-render-line" border="0" cellspacing="0" cellpadding="0" t-foreach="lines" t-as="line"> @@ -1369,6 +1389,18 @@ <button type="button" class="oe_formopenpopup-form-save">Save</button> <button type="button" class="oe_formopenpopup-form-close">Cancel</button> </t> +<t t-extend="ListView.row"> + <!-- adds front & back padding to row being rendered after edition, if + necessary (if not selectable add front padding and if not deletable + add back padding), otherwise the row being added is missing columns + --> + <t t-jquery="> :first" t-operation="before"> + <td t-if="edited and !options.selectable" class="oe-listview-padding"/> + </t> + <t t-jquery="> :last" t-operation="after"> + <td t-if="edited and !options.deletable" class="oe-listview-padding"/> + </t> +</t> <t t-name="ListView.row.frame" t-extend="WidgetFrame"> <t t-jquery="tr"> $(document.createElement('t')) @@ -1380,10 +1412,8 @@ .replaceAll(this) .after($(document.createElement('td')).append( $(document.createElement('button')).attr({ - 'class': 'oe-edit-row-save', 'type': 'button'}).text('Save'))) - .before($(document.createElement('td')).append( - $(document.createElement('button')).attr({ - 'class': 'oe-edit-row-cancel', 'type': 'button'}).text('Cancel'))) + 'class': 'oe-edit-row-save', 'type': 'button'}) + .html(' '))) .unwrap(); </t> </t> @@ -1392,51 +1422,70 @@ <t t-call="view_editor.row"/> </table> </t> -<tr t-name="view_editor.row" class="oe_view_editor_row" t-att-id="'viewedit-' + rec.id" t-att-level="rec.level" t-foreach="data" t-as="rec"> - <td class="oe_view_editor_colum" width="85%"> - <table class="oe_view_editor_tree_grid"> - <tr> - <td width="16px" t-att-style="'background-position: ' + 20*rec.level + 'px; padding-left: ' + 20*rec.level + 'px'"> - <img t-if="rec.child_id.length" t-att-id="'parentimg-' + rec.id" - t-att-src='widget.session.server + "/web/static/src/img/collapse.gif"' width="16" height="16" border="0"/> - </td> - <td style="cursor: pointer;"> - <a style="text-decoration:none" href="javascript:void(0);"> <t t-esc="rec.name"/> </a> - </td> - </tr> - </table> - </td> - <td align="left" class="oe_view_editor_colum" width="15%"> - <table width="100%"> - <tr> - <td width="20%"> - <img t-if="rec.att_list.length" - id="side-add" t-att-src='widget.session.server + "/web/static/src/img/icons/gtk-add.png"' style="cursor: pointer;"/> - </td> - <td width="20%"> - <img id="side-remove" t-att-src='widget.session.server + "/web/static/src/img/icons/gtk-remove.png"' style="cursor: pointer;"/> - </td> - <td width="20%"> - <img t-if="rec.att_list.length" - id="side-edit" t-att-src='widget.session.server + "/web/static/src/img/icons/gtk-edit.png"' style="cursor: pointer;"/> - </td> - <td width="20%"> - <img t-if="rec.att_list.length" - id="side-up" t-att-src='widget.session.server + "/web/static/src/img/icons/gtk-go-up.png"' style="cursor: pointer;"/> - </td> - <td width="20%"> - <img t-if="rec.att_list.length" - id="side-down" t-att-src='widget.session.server + "/web/static/src/img/icons/gtk-go-down.png"' style="cursor: pointer;"/> - </td> - </tr> - </table> - </td> - <t t-if="rec.child_id.length"> - <t t-set="data" t-value="rec.child_id"/> - <t t-call="view_editor.row"/> - </t> -</tr> - +<t t-name="view_editor.row"> + <tr class="oe_view_editor_row" t-att-id="'viewedit-' + rec.id" t-att-level="rec.level" t-foreach="data" t-as="rec"> + <td class="oe_view_editor_colum" width="85%"> + <table class="oe_view_editor_tree_grid"> + <tr> + <td width="16px" t-att-style="'background-position: ' + 20*rec.level + 'px; padding-left: ' + 20*rec.level + 'px'"> + <img t-if="rec.child_id.length" t-att-id="'parentimg-' + rec.id" + src="/web/static/src/img/collapse.gif" width="16" height="16" border="0"/> + </td> + <td style="cursor: pointer;"> + <a style="text-decoration:none" href="javascript:void(0);"> + <t t-esc="rec.name"/> + </a> + </td> + </tr> + </table> + </td> + <td align="left" class="oe_view_editor_colum" width="15%"> + <table width="100%"> + <tr> + <td width="20%"> + <img t-if="rec.att_list.length" + id="side-add" src="/web/static/src/img/icons/gtk-add.png" style="cursor: pointer;"/> + </td> + <td width="20%"> + <img id="side-remove" src="/web/static/src/img/icons/gtk-remove.png" style="cursor: pointer;"/> + </td> + <td width="20%"> + <img t-if="rec.att_list.length and !_.include(no_properties, rec.att_list[0])" + id="side-edit" src="/web/static/src/img/icons/gtk-edit.png" style="cursor: pointer;"/> + </td> + <td width="20%"> + <img t-if="rec.att_list.length" + id="side-up" src="/web/static/src/img/icons/gtk-go-up.png" style="cursor: pointer;"/> + </td> + <td width="20%"> + <img t-if="rec.att_list.length" + id="side-down" src="/web/static/src/img/icons/gtk-go-down.png" style="cursor: pointer;"/> + </td> + </tr> + </table> + </td> + <t t-if="rec.child_id.length"> + <t t-set="data" t-value="rec.child_id"/> + <t t-call="view_editor.row"/> + </t> + </tr> +</t> +<t t-name="vieweditor_char"> + <input type="text" t-att-id="widget.name" class="field_char" size="50"/> +</t> +<t t-name="vieweditor_selection"> + <select t-att-id="widget.name" > + <t t-if="widget.selection" t-foreach="widget.selection" t-as="option"> + <option + t-att-value="typeof option === 'object' ? option[0] : option"> + <t t-esc="typeof option === 'object' ? option[1] : option"/> + </option> + </t> + </select> +</t> +<t t-name="vieweditor_boolean"> + <input type="checkbox" t-att-id="widget.name"/> +</t> <t t-name="ExportView"> <a id="exportview" href="javascript: void(0)" style="text-decoration: none;color: #3D3D3D;">Export</a> </t> @@ -1634,7 +1683,7 @@ </table> </t> <t t-name="ImportView.error"> - <p style="white-space:pre-line;">The import failed due to:<t t-esc="error.message"/></p> + <p style="white-space:pre;">The import failed due to:<t t-esc="error.message"/></p> <t t-if="error.preview"> <p>Here is a preview of the file we could not import:</p> <pre><t t-esc="error.preview"/></pre> |
