← Back to team overview

openerp-dev-web team mailing list archive

[Merge] lp:~openerp-dev/openobject-client-web/o2m-dialogs into lp:openobject-client-web

 

Xavier (Open ERP) has proposed merging lp:~openerp-dev/openobject-client-web/o2m-dialogs into lp:openobject-client-web.

Requested reviews:
  Aline (OpenERP) (apr-tinyerp)


Fourth (and final?) installment of our summer blockbuster "let's replace popups with dialogs" series: migration of o2ms to dialogs.

Know "issue": in sales orders when adding a line, a warning pops up with "You have to select a customer in the sale form ! Please set one customer before choosing a product.". This warning also happens on trunk, so it doesn't seem to originate in the branch.
-- 
https://code.launchpad.net/~openerp-dev/openobject-client-web/o2m-dialogs/+merge/43630
Your team OpenERP R&D Team is subscribed to branch lp:~openerp-dev/openobject-client-web/o2m-dialogs.
=== modified file 'addons/openerp/controllers/openo2m.py'
--- addons/openerp/controllers/openo2m.py	2010-12-10 10:48:09 +0000
+++ addons/openerp/controllers/openo2m.py	2010-12-14 11:21:32 +0000
@@ -65,6 +65,7 @@
         params.prefix = params.o2m
         params.views = wid.view
 
+<<<<<<< TREE
         # IE hack, get context from cookies (see o2m.js)
         o2m_context = {}
         parent_context = {}
@@ -80,6 +81,8 @@
 
         params.o2m_context = params.o2m_context or o2m_context
         params.parent_context = params.parent_context or parent_context
+=======
+>>>>>>> MERGE-SOURCE
         ctx = params.context or {}
         ctx.update(params.parent_context or {})
         ctx.update(params.o2m_context or {})
@@ -136,8 +139,6 @@
         current = params.chain_get(prefix)
 
         params.load_counter = 1
-        if current and current.id and not params.button:
-            params.load_counter = 2
 
         ids = current.ids
         fld = params.o2m.split('/')[-1]

=== modified file 'addons/openerp/controllers/templates/openo2m.mako'
--- addons/openerp/controllers/templates/openo2m.mako	2010-11-18 08:47:41 +0000
+++ addons/openerp/controllers/templates/openo2m.mako	2010-12-14 11:21:32 +0000
@@ -5,64 +5,9 @@
 
     <script type="text/javascript">
         var form_controller = '/openerp/openo2m';
-    </script>
-
-    <script type="text/javascript">
-
         function do_select(id, src) {
             viewRecord(id, src);
         }
-        function save_o2m(el) {
-            jQuery(el).attr('disabled', 'disabled');
-            submit_form('save');
-            setTimeout(function(){
-                  jQuery(el).attr('disabled', '');
-            }, 3000);
-        }
-
-        function fetchParentData() {
-
-            var pwin = window.opener;
-            var pform = pwin.document.forms['view_form'];
-
-            var form = document.forms['view_form'];
-            var fields = [];
-
-            var required_attrs = ['id', 'name', 'value', 'kind', 'class', 'domain', 'context', 'relation'];
-
-            MochiKit.Iter.forEach(pform.elements, function(e){
-
-                if (e.name && e.type != 'button' && e.name.indexOf('${params.o2m}') != 0){
-
-                    var attrs = {};
-                    MochiKit.Iter.forEach(required_attrs, function(n){
-                        if (e.attributes[n]) attrs[n] = e.attributes[n].value;
-                    });
-                    attrs['type'] = 'hidden';
-                    attrs['disabled'] = 'disabled';
-                    attrs['value'] = e.value;
-
-                    var fld = MochiKit.DOM.INPUT(attrs);
-                    fields = fields.concat(fld);
-                }
-            });
-
-            MochiKit.DOM.appendChildNodes(form, fields);
-
-            var lc = openobject.dom.get('_terp_load_counter').value;
-
-            lc = parseInt(lc) || 0;
-
-            if (lc > 0) {
-                window.opener.setTimeout("new ListView('${params.o2m}').reload(null, 1)", 0.5);
-            }
-
-            if (lc > 1) {
-                window.close();
-            }
-
-        }
-
     </script>
 </%def>
 
@@ -86,11 +31,11 @@
                         <tr>
                             % if form.screen.editable:
 	                            <td class="save_close">
-                                    <button onclick="save_o2m(this); return false;" style="height: 20px;" class="button-a">${_("Save")}</button>
+                                    <button onclick="submit_form('save'); return false;" style="height: 20px;" class="button-a">${_("Save")}</button>
 	                            </td>
                             % endif
                             <td class="save_close">
-                            	<button class="button-a" style="height: 20px;" onclick="window.close()">${_("Close")}</button>
+                            	<button class="button-a" style="height: 20px;" onclick="jQuery.o2m('close'); return false;">${_("Close")}</button>
                             </td>
                             <td width="100%">
                             </td>
@@ -104,6 +49,9 @@
         </tr>
     </table>
     <script type="text/javascript">
-        fetchParentData();
+        jQuery('form').submit(function () {
+            jQuery('.save_close:eq(0) button').attr('disabled', true);
+        });
+        jQuery.o2m('init');
     </script>
 </%def>

=== modified file 'addons/openerp/static/javascript/o2m.js'
--- addons/openerp/static/javascript/o2m.js	2010-12-07 06:45:57 +0000
+++ addons/openerp/static/javascript/o2m.js	2010-12-14 11:21:32 +0000
@@ -28,23 +28,23 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 var One2Many = function(name, inline) {
-    
+
     this.name = name;
     this.inline = inline > 0;
-    
+
     this.model = openobject.dom.get(name + '/_terp_model').value;
     this.mode = openobject.dom.get(name + '/_terp_view_type').value;
-    
+
     if (openobject.dom.get(name + '/_terp_default_get_ctx'))
         this.default_get_ctx = openobject.dom.get(name + '/_terp_default_get_ctx').value;
-    
+
     var parent_prefix = name.indexOf('/') > -1 ? name.slice(0, name.lastIndexOf('/') + 1) : '';
-    
+
     this.parent_model = openobject.dom.get(parent_prefix + '_terp_model').value;
     this.parent_id = openobject.dom.get(parent_prefix + '_terp_id').value;
     this.parent_context = openobject.dom.get(parent_prefix + '_terp_context').value;
     this.parent_view_id = openobject.dom.get(parent_prefix + '_terp_view_id').value;
-    
+
     // hide new button when editors are visible
     if (this.mode == 'tree' && this.inline) {
         var self = this;
@@ -58,13 +58,13 @@
 One2Many.prototype = {
 
 	remove: function(id) {
-		
+
 	if (!confirm(_('Do you really want to delete record ?'))) {
             return false;
     }
-        
+
 	var req = openobject.http.postJSON('/openerp/openo2m/delete', {'model': this.model, 'id': id});
-	
+
 	req.addCallback(function(obj) {
             if (obj.error) {
                 jQuery.fancybox(obj.error, {scrolling: 'no'});
@@ -133,36 +133,28 @@
             _terp_parent_view_id: this.parent_view_id,
             _terp_o2m: this.name,
             _terp_o2m_model: this.model,
-            _terp_o2m_id: id,
             _terp_editable: readonly ? 0 : 1
         });
-                    
+
+        if(id != null) {
+            params['_terp_o2m_id'] = id;
+        }
         if (id && id != 'False' && !this.default_get_ctx) {
-            return openobject.tools.openWindow(openobject.http.getURL('/openerp/openo2m/edit', params));
+            jQuery.o2m(params);
+            return;
         }
 
         eval_domain_context_request({
             source: this.name,
             context : this.default_get_ctx
         }).addCallback(function(res) {
-            //XXX: IE hack, long context value generate long URI
-            if (!window.browser.isIE) {
-                params['_terp_o2m_context'] = res.context;
-                params['_terp_parent_context'] = this.parent_context;
-                return openobject.tools.openWindow(openobject.http.getURL('/openerp/openo2m/edit', params));
-            }
-            
-            openobject.http.setCookie('_terp_o2m_context', res.context || '{}');
-            openobject.http.setCookie('_terp_parent_context', this.parent_context || '{}');
-            try {
-                return openobject.tools.openWindow(openobject.http.getURL('/openerp/openo2m/edit', params));
-            } finally {
-                openobject.http.delCookie('_terp_o2m_context');
-                openobject.http.delCookie('_terp_parent_context');
-            }
+            jQuery.o2m(jQuery.extend(params, {
+                _terp_o2m_context: res.context,
+                _terp_parent_context: this.parent_context
+            }));
         });
     },
-    
+
     setReadonly: function(readonly) {
         var btn=MochiKit.DOM.getElement(this.name+'_btn_');
         var grid=MochiKit.DOM.getElement(this.name+'_grid');
@@ -189,3 +181,169 @@
         }
     }
 };
+
+(function ($) {
+    /**
+     * Frame counter, used to uniquify (monotonously increasing) frame id in
+     * order to ensure we avoid collisions between o2m frames (in case we
+     * have nested o2ms somehow), as we need to get a frame we can target
+     * with a form submission.
+     */
+    var frame_counter = 0;
+
+    function frame_data($this, data) {
+        return $($this.attr('frameElement')).data(data);
+    }
+
+    /**
+     * Opens an o2m dialog linked to the provided <code>$this</code> window,
+     * with the selected options.
+     *
+     * @param $this the parent window of the opened dialog, contains the
+     * input to fill with the selected o2m value if any
+     * @param options A map of options to provide to the newly opened iframe
+     * call.
+     */
+    function open($this, options) {
+        var frame_identifier = 'test-frame' + frame_counter++;
+        var $frame = $('<iframe>', {
+            src: 'about:blank',
+            // never sure whether the iframe is targetted by name or by id,
+            // so let's just set both
+            id: frame_identifier,
+            name: frame_identifier,
+            frameborder: 0
+        }).data('source-window', $this[0])
+          .data('list', options['_terp_o2m'])
+          .appendTo(document.documentElement)
+          .dialog({
+              modal: true,
+              width: 640,
+              height: 480,
+              close: function () {
+                  jQuery(this).dialog('destroy').remove();
+              }
+          });
+        var $form = jQuery('<form>', {
+            method: 'POST',
+            action: '/openerp/openo2m/edit',
+            target: frame_identifier
+        }).appendTo(document.documentElement);
+        jQuery.each(options, function (key, value) {
+            $form.append(jQuery('<input>', {
+                type: 'hidden',
+                name: key,
+                value: value
+            }));
+        });
+        setTimeout(function () {
+            $form.submit();
+        });
+        return $frame;
+    }
+
+
+    var fetched_attrs = ['id', 'name', 'value', 'kind', 'class', 'domain',
+                         'context', 'relation'];
+
+    
+    function initialize($frame_window) {
+        // Within initialize, <code>jQ</code> is the "toplevel" jQuery whereas
+        // <code>$</code> is the jQuery from within the iframe
+        var jQ = jQuery;
+        var $ = $frame_window[0].jQuery;
+
+        var $form = $('#view_form');
+
+        var list_id = frame_data($frame_window, 'list');
+        jQ('#view_form :input[name]:not(:button)').each(function () {
+            if (this.name.indexOf(list_id) == 0) {
+                return;
+            }
+            var $this = $(this);
+            var attrs = {};
+            $.each(fetched_attrs, function (index, attribute) {
+                var attr = $this.attr(attribute);
+                if(!attr) { return; }
+                attrs[attribute] = attr;
+            });
+            $form.append($('<input>', $.extend(attrs, {
+                type: 'hidden',
+                disabled: 'disabled',
+                value: $this.val()
+            })));
+        });
+
+        var lc = parseInt($('#_terp_load_counter').val(), 10) || 0;
+        if(lc) {
+            $.o2m('refresh');
+        }
+    }
+
+    /**
+     * Refreshes the backing ListView to make the newly added items appear.
+     *
+     * @param $this the window of the dialog to close
+     */
+    function refresh($this) {
+        setTimeout(function () {
+            frame_data($this, 'source-window')
+                .ListView(frame_data($this, 'list'))
+                    .reload(null, 1);
+        })
+    }
+
+    /**
+     * Closes the o2m dialog it was called from (represented by
+     * <code>$this</code>.
+     *
+     * @param $this the window of the dialog to close
+     */
+    function close($this) {
+        $($this.attr('frameElement')).dialog('close');
+        return null;
+    }
+
+    /**
+     * Manage o2m dialogs for this scope
+     * <ul>
+     *  <li><p>Called with only options, opens a new o2m dialog linking to the
+     *         current scope.</p></li>
+     *  <li><p>Called with the <code>"init"</code> command, performs the
+     *         initial setup for the o2m dialog within the opened iframe
+     *  </p></li>
+     *  <li><p>Called with the <code>"close"</code> command, closes the o2m
+     *         dialog it was invoked from and focuses its parent scope.
+     *  </p></li>
+     *  <li><p>Called with the <code>"refresh"</code> command, reloads
+     *         the backing ListView to make the new items in it appear.
+     *  </p></li>
+     * </ul>
+     *
+     * @returns the o2m container (iframe) if one was created
+     */
+    $.o2m = function () {
+        // $this should be the holder for the window from which $.o2m was
+        // originally called, even if $.o2m() was bubbled to the top of
+        // the window stack.
+        var $this;
+        if(this == $) $this = $(window);
+        else $this = $(this);
+        if(window != window.top) {
+            return window.top.jQuery.o2m.apply($this[0], arguments);
+        }
+        // We're at the top-level window, $this is the window from which the
+        // original $.o2m call was performed, window being the current window
+        // level.
+        switch(arguments[0]) {
+            case 'close':
+                return close($this);
+            case 'refresh':
+                return refresh($this);
+            case 'init':
+                return initialize($this);
+            default:
+                return open($this, arguments[0]);
+        }
+    };
+})(jQuery);


Follow ups