From 2b2ec0743599260d1d3215dd273a6665ab5871d3 Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Wed, 29 Oct 2014 19:49:26 +0100 Subject: [PATCH 01/12] [IMP] .gitignore maintenance migration scripts --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 7828a605ef0eb..68382f297f616 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,9 @@ _build/ status # odoo filestore openerp/filestore +# maintenance migration scripts +openerp/addons/base/maintenance + # generated for windows installer? install/win32/*.bat install/win32/meta.py From 1661cf53e9b30dabaa5658906b68b3a1d25599b7 Mon Sep 17 00:00:00 2001 From: Sandy Carter Date: Thu, 23 Oct 2014 17:45:55 -0400 Subject: [PATCH 02/12] [FIX] base_action_rule: Make sure model exists before trying to wrap hooks In certain cases, before running an update unregistered models will try to register hooks. Trying to wrap create and write on these will cause AttributeError on model_obj which would be None Signed-off-by: Sandy Carter --- addons/base_action_rule/base_action_rule.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/base_action_rule/base_action_rule.py b/addons/base_action_rule/base_action_rule.py index 98d533b3b6dd0..398bf00286a50 100644 --- a/addons/base_action_rule/base_action_rule.py +++ b/addons/base_action_rule/base_action_rule.py @@ -199,7 +199,7 @@ def _register_hook(self, cr, ids=None): for action_rule in self.browse(cr, SUPERUSER_ID, ids): model = action_rule.model_id.model model_obj = self.pool.get(model) - if not hasattr(model_obj, 'base_action_ruled'): + if model_obj and not hasattr(model_obj, 'base_action_ruled'): model_obj.create = self._wrap_create(model_obj.create, model) model_obj.write = self._wrap_write(model_obj.write, model) model_obj.base_action_ruled = True From 6a95c9cf3e3f7acc534e9ef7440a30730db2033f Mon Sep 17 00:00:00 2001 From: Julien Legros Date: Thu, 30 Oct 2014 17:58:29 +0100 Subject: [PATCH 03/12] [FIX] project_issue: explicit store attribute for the progress function field This is a workaround for an ORM limitation. A stored function field is not updated when it should if the "source" field is also a stored function field --- addons/project_issue/project_issue.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/project_issue/project_issue.py b/addons/project_issue/project_issue.py index 20a61657e2090..efb4e4eff2b41 100644 --- a/addons/project_issue/project_issue.py +++ b/addons/project_issue/project_issue.py @@ -306,7 +306,7 @@ def _get_issue_work(self, cr, uid, ids, context=None): 'progress': fields.function(_hours_get, string='Progress (%)', multi='hours', group_operator="avg", help="Computed as: Time Spent / Total Time.", store = { 'project.issue': (lambda self, cr, uid, ids, c={}: ids, ['task_id'], 10), - 'project.task': (_get_issue_task, ['progress'], 10), + 'project.task': (_get_issue_task, ['work_ids', 'remaining_hours', 'planned_hours', 'state', 'stage_id'], 10), 'project.task.work': (_get_issue_work, ['hours'], 10), }), } From aed71f396284e8e5e948baa28be8d5d706b1e030 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Fri, 31 Oct 2014 12:20:56 +0100 Subject: [PATCH 04/12] [IMP] stock: avoid errors with picking without quantity When a line is not present in the partial delivery wizard, computation variables are initialized with generic values (zero quantity, zero price,...). Instead of setting the uom to False, keep the quantity of the move. This makes a difference only when the quantity of the move is 0. That means that the move will be marked as complete and can be processed. This avoids trying to update the stock.move with a uom at False. opw 616844 --- addons/stock/stock.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/stock/stock.py b/addons/stock/stock.py index 644dae4ddf933..3b6bb70e45755 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -1248,7 +1248,7 @@ def do_partial(self, cr, uid, ids, partial_datas, context=None): partial_data = partial_datas.get('move%s'%(move.id), {}) product_qty = partial_data.get('product_qty',0.0) move_product_qty[move.id] = product_qty - product_uom = partial_data.get('product_uom',False) + product_uom = partial_data.get('product_uom', move.product_uom.id) product_price = partial_data.get('product_price',0.0) product_currency = partial_data.get('product_currency',False) prodlot_id = partial_data.get('prodlot_id') From 8af9fdfa0635f6a9f78320d59d5cfb7ff1b89df2 Mon Sep 17 00:00:00 2001 From: Mohammad Alhashash Date: Fri, 31 Oct 2014 14:47:18 +0200 Subject: [PATCH 05/12] [FIX] stock: more tolerant copy method Allow setting origin/backorder when copying new stock.picking Fixes #379, lp:1098557 --- addons/stock/stock.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/stock/stock.py b/addons/stock/stock.py index 3b6bb70e45755..16565871de318 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -721,8 +721,8 @@ def copy(self, cr, uid, id, default=None, context=None): if ('name' not in default) or (picking_obj.name == '/'): seq_obj_name = 'stock.picking.' + picking_obj.type default['name'] = self.pool.get('ir.sequence').get(cr, uid, seq_obj_name) - default['origin'] = '' - default['backorder_id'] = False + default.setdefault('origin', False) + default.setdefault('backorder_id', False) if 'invoice_state' not in default and picking_obj.invoice_state == 'invoiced': default['invoice_state'] = '2binvoiced' res = super(stock_picking, self).copy(cr, uid, id, default, context) From df845940ed52040ef92b1b5759306c556fa38e66 Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Fri, 31 Oct 2014 17:40:56 +0100 Subject: [PATCH 06/12] [FIX] web: on editable list save, valid all records opw-617036: In my current timesheet, if you add a a required field on the timesheet ids lines, and add data in the summary tab, it was possible to validate the timesheet lines while requried fields were missing. --- addons/web/static/src/js/view_form.js | 33 ++++++++++++--------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index d140a9adb6288..40a895f6c90d6 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -3881,28 +3881,23 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({ this.o2m.trigger_on_change(); }, is_valid: function () { - var editor = this.editor; - var form = editor.form; - // If no edition is pending, the listview can not be invalid (?) - if (!editor.record) { - return true - } - // If the form has not been modified, the view can only be valid - // NB: is_dirty will also be set on defaults/onchanges/whatever? - // oe_form_dirty seems to only be set on actual user actions - if (!form.$el.is('.oe_form_dirty')) { + var self = this; + if (!this.editable()){ return true; } this.o2m._dirty_flag = true; - - // Otherwise validate internal form - return _(form.fields).chain() - .invoke(function () { - this._check_css_flags(); - return this.is_valid(); - }) - .all(_.identity) - .value(); + var r; + return _.every(this.records.records, function(record){ + r = record; + _.each(self.editor.form.fields, function(field){ + field.set_value(r.attributes[field.name]); + }); + return _.every(self.editor.form.fields, function(field){ + field.process_modifiers(); + field._check_css_flags(); + return field.is_valid(); + }); + }); }, do_add_record: function () { if (this.editable()) { From 37fe61412655e68cd64e6fe8d472c4e01170280d Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Mon, 3 Nov 2014 11:41:03 +0100 Subject: [PATCH 07/12] [IMP] account: remove duplicated field in view Partial backport of 5f06129. Fixes #2837 --- addons/account/account_view.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/addons/account/account_view.xml b/addons/account/account_view.xml index de51be0ddad0d..2716e39cd806c 100644 --- a/addons/account/account_view.xml +++ b/addons/account/account_view.xml @@ -2268,7 +2268,6 @@ - From aea9c968cd6d1e5e26d6a10c408989695006a05c Mon Sep 17 00:00:00 2001 From: Paramjit Singh Sahota Date: Tue, 4 Mar 2014 18:55:34 +0530 Subject: [PATCH 08/12] [FIX] web: backport of 2331b14 Update the cleditor lib from v1.3.0 to v1.4.4 fixing IE11 issue, opw 614826 --- .../static/lib/cleditor/jquery.cleditor.css | 2 +- .../static/lib/cleditor/jquery.cleditor.js | 232 ++++++++++-------- .../lib/cleditor/jquery.cleditor.min.js | 31 +-- 3 files changed, 138 insertions(+), 127 deletions(-) diff --git a/addons/web/static/lib/cleditor/jquery.cleditor.css b/addons/web/static/lib/cleditor/jquery.cleditor.css index 6ac490bcfc491..196694069478f 100644 --- a/addons/web/static/lib/cleditor/jquery.cleditor.css +++ b/addons/web/static/lib/cleditor/jquery.cleditor.css @@ -6,7 +6,7 @@ .cleditorButton {float:left; width:24px; height:24px; margin:1px 0 1px 0; background: url('images/buttons.gif')} .cleditorDisabled {opacity:0.3; filter:alpha(opacity=30)} .cleditorDivider {float:left; width:1px; height:23px; margin:1px 0 1px 0; background:#CCC} -.cleditorPopup {border:solid 1px #999; background-color:white; position:absolute; font:10pt Arial,Verdana; cursor:default; z-index:10000} +.cleditorPopup {border:solid 1px #999; background-color:white; color:#333333; position:absolute; font:10pt Arial,Verdana; cursor:default; z-index:10000} .cleditorList div {padding:2px 4px 2px 4px} .cleditorList p, .cleditorList h1, diff --git a/addons/web/static/lib/cleditor/jquery.cleditor.js b/addons/web/static/lib/cleditor/jquery.cleditor.js index 3de9089443d8b..7852037de58c7 100644 --- a/addons/web/static/lib/cleditor/jquery.cleditor.js +++ b/addons/web/static/lib/cleditor/jquery.cleditor.js @@ -1,18 +1,13 @@ -/** - @preserve CLEditor WYSIWYG HTML Editor v1.3.0 - http://premiumsoftware.net/cleditor +/*! + CLEditor WYSIWYG HTML Editor v1.4.4 + http://premiumsoftware.net/CLEditor requires jQuery v1.4.2 or later Copyright 2010, Chris Landowski, Premium Software, LLC Dual licensed under the MIT or GPL Version 2 licenses. */ -// ==ClosureCompiler== -// @compilation_level SIMPLE_OPTIMIZATIONS -// @output_file_name jquery.cleditor.min.js -// ==/ClosureCompiler== - -(function($) { +(function ($) { //============== // jQuery Plugin @@ -22,7 +17,7 @@ // Define the defaults used for all new cleditor instances defaultOptions: { - width: 500, // width not including margins, borders or padding + width: 'auto', // width not including margins, borders or padding height: 250, // height not including margins, borders or padding controls: // controls to add to the toolbar "bold italic underline strikethrough subscript superscript | font size " + @@ -46,18 +41,18 @@ [["Paragraph", "

"], ["Header 1", "

"], ["Header 2", "

"], ["Header 3", "

"], ["Header 4","

"], ["Header 5","

"], ["Header 6","
"]], - useCSS: false, // use CSS to style HTML when possible (not supported in ie) + useCSS: true, // use CSS to style HTML when possible (not supported in ie) docType: // Document type contained within the editor '', docCSSFile: // CSS file used to style the document contained within the editor "", bodyStyle: // style to assign to document body contained within the editor - "margin:4px; color:#4c4c4c; font-size:13px; font-family:\"Lucida Grande\",Helvetica,Verdana,Arial,sans-serif; cursor:text" + "margin:4px; font:10pt Arial,Verdana; cursor:text" }, // Define all usable toolbar buttons - the init string property is // expanded during initialization back into the buttons object and - // seperate object properties are created for each button. + // separate object properties are created for each button. // e.g. buttons.size.title = "Font Size" buttons: { // name,title,command,popupName (""=use name) @@ -109,7 +104,7 @@ // Loop through all matching textareas and create the editors this.each(function(idx, elem) { - if (elem.tagName == "TEXTAREA") { + if (elem.tagName.toUpperCase() === "TEXTAREA") { var data = $.data(elem, CLEDITOR); if (!data) data = new cleditor(elem, options); $result = $result.add(data); @@ -129,6 +124,7 @@ // Misc constants BACKGROUND_COLOR = "backgroundColor", + BLURRED = "blurred", BUTTON = "button", BUTTON_NAME = "buttonName", CHANGE = "change", @@ -136,6 +132,7 @@ CLICK = "click", DISABLED = "disabled", DIV_TAG = "
", + FOCUSED = "focused", TRANSPARENT = "transparent", UNSELECTABLE = "unselectable", @@ -152,12 +149,15 @@ PROMPT_CLASS = "cleditorPrompt", // prompt popup divs inside body MSG_CLASS = "cleditorMsg", // message popup div inside body - // Test for ie - ie = $.browser.msie, - ie6 = /msie\s6/i.test(navigator.userAgent), + // Browser detection + ua = navigator.userAgent.toLowerCase(), + ie = /msie/.test(ua), + ie6 = /msie\s6/.test(ua), + iege11 = /(trident)(?:.*rv:([\w.]+))?/.test(ua), + webkit = /webkit/.test(ua), // Test for iPhone/iTouch/iPad - iOS = /iphone|ipad|ipod/i.test(navigator.userAgent), + iOS = /iphone|ipad|ipod/i.test(ua), // Popups are created once as needed and shared by all editor instances popups = {}, @@ -223,19 +223,26 @@ var $group = $(DIV_TAG) .addClass(GROUP_CLASS) .appendTo($toolbar); + + // Initialize the group width + var groupWidth = 0; // Add the buttons to the toolbar $.each(options.controls.split(" "), function(idx, buttonName) { if (buttonName === "") return true; // Divider - if (buttonName == "|") { + if (buttonName === "|") { // Add a new divider to the group var $div = $(DIV_TAG) .addClass(DIVIDER_CLASS) .appendTo($group); + // Update the group width + $group.width(groupWidth + 1); + groupWidth = 0; + // Create a new group $group = $(DIV_TAG) .addClass(GROUP_CLASS) @@ -258,6 +265,10 @@ .appendTo($group) .hover(hoverEnter, hoverLeave); + // Update the group width + groupWidth += 24; + $group.width(groupWidth + 1); + // Prepare the button image var map = {}; if (button.css) map = button.css; @@ -295,17 +306,7 @@ // Bind the window resize event when the width or height is auto or % if (/auto|%/.test("" + options.width + options.height)) - $(window).resize(function() { - //Forcefully blurred iframe contentWindow, chrome, IE, safari doesn't trigger blur on window resize and due to which text disappears - var contentWindow = editor.$frame[0].contentWindow; - if(!$.browser.mozilla && contentWindow){ - $(contentWindow).trigger('blur'); - } - // CHM Note MonkeyPatch: if the DOM is not remove, refresh the cleditor - if(editor.$main.parent().parent().size()) { - refresh(editor); - } - }); + $(window).bind('resize.cleditor', function () { refresh(editor); }); // Create the iframe and resize the controls refresh(editor); @@ -347,13 +348,26 @@ return editor; }; }); + + // blurred - shortcut for .bind("blurred", handler) or .trigger("blurred") + fn.blurred = function(handler) { + var $this = $(this); + return handler ? $this.bind(BLURRED, handler) : $this.trigger(BLURRED); + }; // change - shortcut for .bind("change", handler) or .trigger("change") - fn.change = function(handler) { + fn.change = function change(handler) { + console.log('change test'); var $this = $(this); return handler ? $this.bind(CHANGE, handler) : $this.trigger(CHANGE); }; + // focused - shortcut for .bind("focused", handler) or .trigger("focused") + fn.focused = function(handler) { + var $this = $(this); + return handler ? $this.bind(FOCUSED, handler) : $this.trigger(FOCUSED); + }; + //=============== // Event Handlers //=============== @@ -369,7 +383,7 @@ popup = popups[popupName]; // Check if disabled - if (editor.disabled || $(buttonDiv).attr(DISABLED) == DISABLED) + if (editor.disabled || $(buttonDiv).attr(DISABLED) === DISABLED) return; // Fire the buttonClick event @@ -387,7 +401,7 @@ return false; // Toggle source - if (buttonName == "source") { + if (buttonName === "source") { // Show the iframe if (sourceMode(editor)) { @@ -418,10 +432,10 @@ var $popup = $(popup); // URL - if (popupName == "url") { + if (popupName === "url") { // Check for selection before showing the link url popup - if (buttonName == "link" && selectedText(editor) === "") { + if (buttonName === "link" && selectedText(editor) === "") { showMessage(editor, "A selection is required when inserting a link.", buttonDiv); return false; } @@ -447,7 +461,7 @@ } // Paste as Text - else if (popupName == "pastetext") { + else if (popupName === "pastetext") { // Wire up the submit button click event handler $popup.children(":button") @@ -475,13 +489,13 @@ return false; // stop propagination to document click } - // propaginate to documnt click + // propaginate to document click return; } // Print - else if (buttonName == "print") + else if (buttonName === "print") editor.$frame[0].contentWindow.print(); // All other buttons @@ -526,19 +540,19 @@ useCSS = editor.options.useCSS; // Get the command value - if (buttonName == "font") + if (buttonName === "font") // Opera returns the fontfamily wrapped in quotes value = target.style.fontFamily.replace(/"/g, ""); - else if (buttonName == "size") { - if (target.tagName == "DIV") + else if (buttonName === "size") { + if (target.tagName.toUpperCase() === "DIV") target = target.children[0]; value = target.innerHTML; } - else if (buttonName == "style") + else if (buttonName === "style") value = "<" + target.tagName + ">"; - else if (buttonName == "color") + else if (buttonName === "color") value = hex(target.style.backgroundColor); - else if (buttonName == "highlight") { + else if (buttonName === "highlight") { value = hex(target.style.backgroundColor); if (ie) command = 'backcolor'; else useCSS = true; @@ -572,7 +586,7 @@ //================== // Private Functions //================== - + // checksum - returns a checksum using the Adler-32 method function checksum(text) { @@ -610,7 +624,7 @@ $popup.html(popupContent); // Color - else if (popupName == "color") { + else if (popupName === "color") { var colors = options.colors.split(" "); if (colors.length < 10) $popup.width("auto"); @@ -622,7 +636,7 @@ } // Font - else if (popupName == "font") + else if (popupName === "font") $.each(options.fonts.split(","), function(idx, font) { $(DIV_TAG).appendTo($popup) .css("fontFamily", font) @@ -630,28 +644,28 @@ }); // Size - else if (popupName == "size") + else if (popupName === "size") $.each(options.sizes.split(","), function(idx, size) { $(DIV_TAG).appendTo($popup) - .html("" + size + ""); + .html('' + size + ''); }); // Style - else if (popupName == "style") + else if (popupName === "style") $.each(options.styles, function(idx, style) { $(DIV_TAG).appendTo($popup) .html(style[1] + style[0] + style[1].replace("<", "
'); + else if (popupName === "url") { + $popup.html('Enter URL:

'); popupTypeClass = PROMPT_CLASS; } // Paste as Text - else if (popupName == "pastetext") { - $popup.html('Paste your content here and click submit.

'); + else if (popupName === "pastetext") { + $popup.html('Paste your content here and click submit.

'); popupTypeClass = PROMPT_CLASS; } @@ -720,12 +734,12 @@ } // Execute the command and check for error - var success = true, description; - if (ie && command.toLowerCase() == "inserthtml") + var success = true, message; + if (ie && command.toLowerCase() === "inserthtml") getRange(editor).pasteHTML(value); else { try { success = editor.doc.execCommand(command, 0, value || null); } - catch (err) { description = err.description; success = false; } + catch (err) { message = err.message; success = false; } if (!success) { if ("cutcopypaste".indexOf(command) > -1) showMessage(editor, "For security reasons, your browser does not support the " + @@ -733,13 +747,14 @@ button); else showMessage(editor, - (description ? description : "Error executing the " + command + " command."), + (message ? message : "Error executing the " + command + " command."), button); } } - // Enable the buttons + // Enable the buttons and update the textarea refreshButtons(editor); + updateTextArea(editor, true); return success; } @@ -765,19 +780,26 @@ return editor.$frame[0].contentWindow.getSelection(); } - // Returns the hex value for the passed in string. - // hex("rgb(255, 0, 0)"); // #FF0000 - // hex("#FF0000"); // #FF0000 - // hex("#F00"); // #FF0000 + // hex - returns the hex value for the passed in color string function hex(s) { - var m = /rgba?\((\d+), (\d+), (\d+)/.exec(s), - c = s.split(""); + + // hex("rgb(255, 0, 0)") returns #FF0000 + var m = /rgba?\((\d+), (\d+), (\d+)/.exec(s); if (m) { - s = ( m[1] << 16 | m[2] << 8 | m[3] ).toString(16); + s = (m[1] << 16 | m[2] << 8 | m[3]).toString(16); while (s.length < 6) s = "0" + s; + return "#" + s; } - return "#" + (s.length == 6 ? s : c[1] + c[1] + c[2] + c[2] + c[3] + c[3]); + + // hex("#F00") returns #FF0000 + var c = s.split(""); + if (s.length === 4) + return "#" + c[1] + c[1] + c[2] + c[2] + c[3] + c[3]; + + // hex("#FF0000") returns #FF0000 + return s; + } // hidePopups - hides all popups @@ -792,9 +814,8 @@ // imagesPath - returns the path to the images folder function imagesPath() { - var cssFile = "jquery.cleditor.css", - href = $("link[href$='" + cssFile +"']").attr("href"); - return href.substr(0, href.length - cssFile.length) + "images/"; + var href = $("link[href*=cleditor]").attr("href"); + return href.replace(/^(.*\/)[^\/]+$/, '$1') + "images/"; } // imageUrl - Returns the css url string for a filemane @@ -813,7 +834,7 @@ editor.$frame.remove(); // Create a new iframe - var $frame = editor.$frame = $('