Merge lp:~xmo-deactivatedaccount/openobject-client-web/m2o-dialogs into lp:openobject-client-web/trunk

Proposed by Xavier (Open ERP)
Status: Merged
Merged at revision: 4045
Proposed branch: lp:~xmo-deactivatedaccount/openobject-client-web/m2o-dialogs
Merge into: lp:openobject-client-web/trunk
Diff against target: 543 lines (+187/-116)
7 files modified
addons/openerp/controllers/search.py (+5/-6)
addons/openerp/controllers/templates/openm2o.mako (+8/-13)
addons/openerp/controllers/templates/search.mako (+32/-46)
addons/openerp/static/css/style.css (+2/-2)
addons/openerp/static/javascript/form.js (+19/-3)
addons/openerp/static/javascript/m2o.js (+114/-29)
addons/openerp/static/javascript/openerp/openerp.base.js (+7/-17)
To merge this branch: bzr merge lp:~xmo-deactivatedaccount/openobject-client-web/m2o-dialogs
Reviewer Review Type Date Requested Status
Aline (OpenERP) Approve
Review via email: mp+42354@code.launchpad.net

Description of the change

Branch replacing the existing dialogs for selecting values in m2o (when no filter or initial filter matches more than a single value) by jquery-ui's in-page dialogs (tab-modal)

To post a comment you must log in.
Revision history for this message
Aline (OpenERP) (apr-tinyerp) wrote :

did you try to click "new" ?

4002. By Xavier (Open ERP)

[FIX] URL for creation in m2o and m2m popups

4003. By Xavier (Open ERP)

[FIX] issue with openLink in popup context

4004. By Xavier (Open ERP)

[FIX] make m2o dialog [new] aware of the $.m2o interface and use it, so that it's possible to create new objects via the m2o selector interface

Revision history for this message
Xavier (Open ERP) (xmo-deactivatedaccount) wrote :

[new] should be fixed, there is a known issue with clicking on an m2o link in a readonly form view (popup opens and the [close] button doesn't work).

Revision history for this message
Xavier (Open ERP) (xmo-deactivatedaccount) wrote :

Voilà, j'avais un peu oublié les popups pour voir ou éditer un record m2o, normalement c'est bon maintenant.

4005. By Xavier (Open ERP)

[IMP] make m2o popups for single records (edition or readonly) use dialogs as well

Revision history for this message
Aline (OpenERP) (apr-tinyerp) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'addons/openerp/controllers/search.py'
2--- addons/openerp/controllers/search.py 2010-11-30 10:18:24 +0000
3+++ addons/openerp/controllers/search.py 2010-12-02 08:39:30 +0000
4@@ -199,19 +199,18 @@
5 data = {}
6 res = proxy.fields_get()
7
8- frm = {}
9 all_values = {}
10
11 for k, v in record.items():
12 values = {}
13 for key, val in v.items():
14 for field in val:
15- fld = {}
16- datas = {}
17- fld['value'] = val[field]
18- fld['type'] = res[field].get('type')
19+ fld = {
20+ 'value': val[field],
21+ 'type': res[field].get('type')
22+ }
23+ datas = {field: fld}
24
25- data[field] = fld
26 try:
27 frm = TinyForm(**data).to_python()
28 except TinyFormError, e:
29
30=== modified file 'addons/openerp/controllers/templates/openm2o.mako'
31--- addons/openerp/controllers/templates/openm2o.mako 2010-09-29 10:26:28 +0000
32+++ addons/openerp/controllers/templates/openm2o.mako 2010-12-02 08:39:30 +0000
33@@ -15,18 +15,13 @@
34
35 jQuery(document).ready(function() {
36
37- var id = parseInt(openobject.dom.get('_terp_id').value) || null;
38- var lc = parseInt(openobject.dom.get('_terp_load_counter').value) || 1;
39-
40- if (lc > 1 && id) {
41- window.opener.document.getElementById('${params.m2o}').value = id;
42- window.opener.document.getElementById('${params.m2o}_text').value = '';
43- window.opener.setTimeout("signal(openobject.dom.get('${params.m2o}'), 'onchange')", 0);
44- }
45-
46- if (lc > 1) {
47- window.close();
48- }
49+ var id = parseInt(openobject.dom.get('_terp_id').value, 10) || null;
50+ var lc = parseInt(openobject.dom.get('_terp_load_counter', 10).value) || 1;
51+
52+ if(lc <= 1) {
53+ return;
54+ }
55+ $.m2o('close', id);
56 });
57 </script>
58 </%def>
59@@ -54,7 +49,7 @@
60 </td>
61 % endif
62 <td class="save_close">
63- <a class="button-a" onclick="window.close()" href="javascript: void(0)">${_("Close")}</a>
64+ <a class="button-a" onclick="$.m2o('close');" href="javascript: void(0)">${_("Close")}</a>
65 </td>
66 <td width="100%">
67 </td>
68
69=== modified file 'addons/openerp/controllers/templates/search.mako'
70--- addons/openerp/controllers/templates/search.mako 2010-11-30 10:49:29 +0000
71+++ addons/openerp/controllers/templates/search.mako 2010-12-02 08:39:30 +0000
72@@ -1,22 +1,32 @@
73 <%inherit file="/openerp/controllers/templates/base_dispatch.mako"/>
74+<%!
75+ KINDS = {
76+ 'M2O': 1,
77+ 'M2M': 2
78+ }
79+%>
80
81 <%def name="header()">
82 <%
83- if params.selectable == 1:
84+ if params.selectable == KINDS['M2O']:
85 create_url = "/openm2o/edit"
86- elif params.selectable == 2:
87+ elif params.selectable == KINDS['M2M']:
88 create_url = "/openm2m/new"
89 %>
90 <title>Search ${form.screen.string}</title>
91
92 <script type="text/javascript">
93 var form_controller = '/openerp/search';
94+ function close_dialog() {
95+ window.close()
96+ }
97 </script>
98- % if params.selectable == 1:
99+ % if params.selectable == KINDS['M2O']:
100 <script type="text/javascript">
101- function do_select(res_id){
102- var selected_id = res_id
103-
104+ function close_dialog() {
105+ $.m2o('close');
106+ }
107+ function do_select(selected_id){
108 if (!selected_id) {
109 var ids = new ListView('_terp_list').getSelectedRecords();
110
111@@ -25,34 +35,10 @@
112
113 selected_id = ids[0];
114 }
115-
116- var $ = window.opener.jQuery;
117- var $value = $(idSelector('${params.source}'));
118- var $text = $(idSelector('${params.source}_text'));
119-
120- $value.val(selected_id);
121- $text.val('');
122-
123- if($value[0].onchange) {
124- $value[0].onchange();
125- } else {
126- $value.change();
127- window.opener.MochiKit.Signal.signal($value[0], 'onchange');
128- }
129-
130- window.close();
131- }
132-
133- function do_create(){
134- openLink(openobject.http.getURL('/openerp/openm2o/edit', {
135- _terp_model: '${params.model}',
136- _terp_source: '${params.source}',
137- _terp_m2o: '${params.source}',
138- _terp_domain: openobject.dom.get('_terp_domain').value,
139- _terp_context: openobject.dom.get('_terp_context').value}));
140+ $.m2o('close', selected_id);
141 }
142 </script>
143- % elif params.selectable == 2:
144+ % elif params.selectable == KINDS['M2M']:
145 % if params.get('return_to'):
146 <script type="text/javascript">
147 function do_select() {
148@@ -76,7 +62,7 @@
149 return;
150 }
151 }
152- window.close()
153+ close_dialog();
154 }
155 </script>
156 % else:
157@@ -109,21 +95,21 @@
158
159 m2m.setValue(ids);
160 }
161- window.close();
162+ close_dialog();
163 }
164 </script>
165 % endif
166- <script type="text/javascript">
167- function do_create(){
168- openLink(openobject.http.getURL('/openerp/openm2m/new', {
169- _terp_model: '${params.model}',
170- _terp_source: '${params.source}',
171- _terp_m2m: '${params.source}',
172- _terp_domain: openobject.dom.get('_terp_domain').value,
173- _terp_context: openobject.dom.get('_terp_context').value}));
174- }
175- </script>
176 % endif
177+ <script type="text/javascript">
178+ function do_create(){
179+ openLink(openobject.http.getURL('/openerp${create_url}', {
180+ _terp_model: '${params.model}',
181+ _terp_source: '${params.source}',
182+ _terp_m2m: '${params.source}',
183+ _terp_domain: openobject.dom.get('_terp_domain').value,
184+ _terp_context: openobject.dom.get('_terp_context').value}));
185+ }
186+ </script>
187 </%def>
188
189 <%def name="content()">
190@@ -160,7 +146,7 @@
191 % endif
192 <a class="button-a" href="javascript: void(0)" onclick="search_filter()">${_("Search")}</a>
193 <a class="button-a" href="javascript: void(0)" onclick="do_create()">${_("New")}</a>
194- <a class="button-a" href="javascript: void(0)" onclick="window.close()">${_("Close")}</a>
195+ <a class="button-a" href="javascript: void(0)" onclick="close_dialog();">${_("Close")}</a>
196
197 </td>
198 </tr>
199@@ -196,7 +182,7 @@
200 });
201 }
202 jQuery('table.search_table input:text').eq(0).focus();
203- % if params.selectable == 2:
204+ % if params.selectable == KINDS['M2M']:
205 var $select_link = jQuery('a.select-link').hide();
206 jQuery('form#search_form').click(function(event) {
207 if ($(event.target).is("input[type=checkbox]")) {
208
209=== modified file 'addons/openerp/static/css/style.css'
210--- addons/openerp/static/css/style.css 2010-11-30 17:30:39 +0000
211+++ addons/openerp/static/css/style.css 2010-12-02 08:39:30 +0000
212@@ -1022,7 +1022,7 @@
213 vertical-align: middle;
214 }
215
216-.action-dialog,
217-.action-dialog .ui-dialog-content {
218+iframe.ui-dialog-content {
219 padding: 0 !important;
220+ width: 100% !important;
221 }
222
223=== modified file 'addons/openerp/static/javascript/form.js'
224--- addons/openerp/static/javascript/form.js 2010-12-01 17:27:22 +0000
225+++ addons/openerp/static/javascript/form.js 2010-12-02 08:39:30 +0000
226@@ -720,8 +720,10 @@
227 });
228 }
229
230+var KIND_M2O = 1;
231+var KIND_M2M = 2;
232 function open_search_window(relation, domain, context, source, kind, text){
233- if (kind == 2 && source.indexOf('_terp_listfields/') == 0) {
234+ if (kind == KIND_M2M && source.indexOf('_terp_listfields/') == 0) {
235 text = "";
236 }
237
238@@ -730,14 +732,28 @@
239 'domain': domain,
240 'context': context
241 }).addCallback(function(obj){
242- openobject.tools.openWindow(openobject.http.getURL('/openerp/search/new', {
243+ var dialog_url = openobject.http.getURL('/openerp/search/new', {
244 'model': relation,
245 'domain': obj.domain,
246 'context': obj.context,
247 'source': source,
248 'kind': kind,
249 'text': text
250- }));
251+ });
252+ switch(kind) {
253+ case KIND_M2O:
254+ jQuery.m2o({
255+ 'model': relation,
256+ 'domain': obj.domain,
257+ 'context': obj.context,
258+ 'source': source,
259+ 'kind': kind,
260+ 'text': text
261+ });
262+ break;
263+ default:
264+ openobject.tools.openWindow(dialog_url);
265+ }
266 });
267 }
268
269
270=== modified file 'addons/openerp/static/javascript/m2o.js'
271--- addons/openerp/static/javascript/m2o.js 2010-11-29 15:28:52 +0000
272+++ addons/openerp/static/javascript/m2o.js 2010-12-02 08:39:30 +0000
273@@ -56,13 +56,8 @@
274 this.lastKey = null;
275 this.delayedRequest = null;
276 this.completeDelay = 1;
277- this.hasHiddenValue = false;
278 this.lastTextResult = null;
279 this.lastSearch = null;
280- this.onlySuggest = false;
281- this.minChars = 1;
282- this.processCount = 0;
283- this.takeFocus = false;
284 this.hasFocus = false;
285 this.suggestionBoxMouseOver = false;
286 this.selectedResult = false;
287@@ -103,17 +98,11 @@
288 this.field._m2o = this;
289
290 this.change_icon();
291-
292- if(this.takeFocus) {
293- this.text.focus();
294- this.gotFocus();
295- }
296 }
297 };
298
299 ManyToOne.prototype.gotFocus = function(evt) {
300 this.hasFocus = true;
301- if(!this.minChars) this.on_keyup(evt);
302 };
303
304 ManyToOne.prototype.lostFocus = function() {
305@@ -167,10 +156,15 @@
306 domain: domain,
307 context: context
308 }).addCallback(function(obj) {
309- openobject.tools.openWindow(openobject.http.getURL('/openerp/openm2o/edit', {
310- _terp_model: model, _terp_id: id,
311- _terp_domain: obj.domain, _terp_context: obj.context,
312- _terp_m2o: source, _terp_editable: editable ? 'True' : 'False'}));
313+ $.m2o({
314+ record: true,
315+ _terp_model: model,
316+ _terp_id: id,
317+ _terp_domain: obj.domain,
318+ _terp_context: obj.context,
319+ _terp_m2o: source,
320+ _terp_editable: editable ? 'True' : 'False'
321+ });
322 });
323 };
324
325@@ -235,7 +229,7 @@
326 // Stop processing if a special key has been pressed. Or if the last search requested the same string
327 if(this.specialKeyPressed || (this.text.value == this.lastSearch)) return false;
328
329- if(this.minChars && this.text.value.length < this.minChars) {
330+ if(!this.text.value.length) {
331 if(this.delayedRequest) {
332 this.delayedRequest.cancel();
333 this.clearResults();
334@@ -287,10 +281,6 @@
335 case 13:
336 case 1:
337 var $selectedRow = jQuery(idSelector("autoComplete" + this.name + "_" + this.selectedResultRow));
338- if(this.onlySuggest && $selectedRow.length) {
339- this.clearResults();
340- break;
341- }
342
343 this.setCompletionText($selectedRow);
344
345@@ -436,14 +426,12 @@
346 var val = s.lastIndexOf(',') >= 0 ? s.substring(s.lastIndexOf(',') + 1).replace(/^\s+|\s+$/g, "") : s.replace(/^\s+|\s+$/g, "");
347
348 // Check again if less than required chars, then we won't search.
349- if(this.minChars && val.length < this.minChars) {
350+ if(!val.length) {
351 this.clearResults();
352 return false;
353 }
354
355 // Get what we are searching for
356- this.processCount++;
357-
358 this.lastSearch = this.text.value;
359 jQuery.getJSON('/openerp/search/get_matched', {
360 text: val,
361@@ -456,7 +444,6 @@
362 try {
363 if(!this.hasFocus) {
364 this.updateSelectedResult();
365- this.processCount--;
366 return false;
367 }
368
369@@ -466,10 +453,7 @@
370 "id": "autoCompleteTable" + this.name});
371 this.numResultRows = result.values.length;
372
373- if(this.onlySuggest)
374- this.selectedResultRow = null;
375- else
376- this.selectedResultRow = 0;
377+ this.selectedResultRow = 0;
378
379 var mouseOver = jQuery.proxy(this, 'getMouseover');
380 var onClick = jQuery.proxy(this, 'getOnclick');
381@@ -501,7 +485,6 @@
382 $resultsHolder.hide();
383 }
384
385- this.processCount--;
386 return true;
387 }
388 catch(e) {
389@@ -538,3 +521,105 @@
390 evt.which = 13;
391 this.on_keydown(evt);
392 };
393+
394+(function ($) {
395+ /**
396+ * Opens an m2o dialog linked to the provided <code>$this</code> window,
397+ * with the selected options.
398+ *
399+ * @param $this the parent window of the opened dialog, contains the
400+ * input to fill with the selected m2o value if any
401+ * @param options A map of options to provide to the xhr call.
402+ * The <code>source</code> key is also used for the id of the element
403+ * (in <code>$this</code>) on which any selected m2o value should be set.
404+ * The <code>record</code> key indicates whether a record should be opened
405+ * instead of a search view
406+ */
407+ function open($this, options) {
408+ var url;
409+ if(options.record) {
410+ url = '/openerp/openm2o/edit'
411+ } else {
412+ url = '/openerp/search/new';
413+ }
414+ return $('<iframe>', {
415+ src: openobject.http.getURL(url, options),
416+ frameborder: 0
417+ }).data('source_window', $this[0])
418+ .data('source_id', options.source || null)
419+ .appendTo(document.documentElement)
420+ .dialog({
421+ modal: true,
422+ width: 640,
423+ height: 480,
424+ close: function () {
425+ jQuery(this).dialog('destroy').remove();
426+ }
427+ });
428+ }
429+
430+ /**
431+ * Closes the m2o dialog it was called from (represented by
432+ * <code>$this</code>, setting the related m2o input to the provided
433+ * <code>value</code>, if any.
434+ *
435+ * @param $this the window of the dialog to close
436+ * @param value optional, the value to set the m2o input to if it is
437+ * provided
438+ */
439+ function close($this, value) {
440+ var $frame = $($this.attr('frameElement'));
441+ if(value) {
442+ // the m2o input to set is in the source_window, which is set as
443+ // a `data` of the dialog iframe
444+ var jQ = $frame.data('source_window').jQuery;
445+ var source_id = $frame.data('source_id');
446+ jQ(idSelector(source_id + '_text')).val('');
447+ var $m2o_field = jQ(idSelector(source_id)).val(value);
448+
449+ if($m2o_field[0].onchange) {
450+ $m2o_field[0].onchange();
451+ } else {
452+ $m2o_field.change();
453+ }
454+ }
455+ $frame.dialog('close');
456+ return null;
457+ }
458+
459+ /**
460+ * Manage m2o dialogs for this scope
461+ * <ul>
462+ * <li><p>Called with only options, opens a new m2o dialog linking to the
463+ * current scope.</p></li>
464+ * <li><p>Called with the <code>"close"</code> command, closes the m2o
465+ * dialog it was invoked from and focuses its parent scope.
466+ * </p></li>
467+ * <li><p>Called with the <code>"close"</code> command and an argument,
468+ * sets that argument as the m2o value of the parent widget and
469+ * closes the m2o dialog it was invoked from as above.
470+ * </p></li>
471+ * </ul>
472+ *
473+ * @returns the m2o container (iframe) if one was created
474+ */
475+ $.m2o = function () {
476+ // $this should be the holder for the window from which $.m2o was
477+ // originally called, even if $.m2o() was bubbled to the top of
478+ // the window stack.
479+ var $this;
480+ if(this == $) $this = $(window);
481+ else $this = $(this);
482+ if(window != window.top) {
483+ return window.top.jQuery.m2o.apply($this[0], arguments);
484+ }
485+ // We're at the top-level window, $this is the window from which the
486+ // original $.m2o call was performed, window being the current window
487+ // level.
488+ if(arguments[0] === "close") {
489+ return close($this, arguments[1]);
490+ } else {
491+ return open($this, arguments[0]);
492+ }
493+ };
494+})(jQuery);
495
496=== modified file 'addons/openerp/static/javascript/openerp/openerp.base.js'
497--- addons/openerp/static/javascript/openerp/openerp.base.js 2010-11-29 11:13:59 +0000
498+++ addons/openerp/static/javascript/openerp/openerp.base.js 2010-12-02 08:39:30 +0000
499@@ -25,9 +25,7 @@
500 error: loadingError
501 });
502 } else {
503- window.location.assign(
504- '/?' + jQuery.param({next: url})
505- );
506+ window.location.assign(url);
507 }
508 }
509 /**
510@@ -100,27 +98,19 @@
511 var $dialogs = jQuery('.action-dialog');
512 switch(target) {
513 case 'new':
514- var $contentFrame = jQuery('<iframe>', {
515+ jQuery('<iframe>', {
516 src: action_url,
517- frameborder: 0,
518- width: '99%',
519- height: '99%'
520- });
521- jQuery('<div class="action-dialog">')
522- .appendTo(document.documentElement)
523+ 'class': 'action-dialog',
524+ frameborder: 0
525+ }).appendTo(document.documentElement)
526 .dialog({
527 modal: true,
528 width: 640,
529 height: 480,
530 close: function () {
531- var $this = jQuery(this);
532- $this.find('iframe').remove();
533- setTimeout(function () {
534- $this.dialog('destroy').remove();
535- });
536+ jQuery(this).dialog('destroy').remove();
537 }
538- })
539- .append($contentFrame);
540+ });
541 break;
542 case 'current':
543 default: