diff --git a/web_advanced_filters/wizard/ir_filters_combine_with_existing.py b/web_advanced_filters/wizard/ir_filters_combine_with_existing.py index 7cb48206..b6d6668c 100644 --- a/web_advanced_filters/wizard/ir_filters_combine_with_existing.py +++ b/web_advanced_filters/wizard/ir_filters_combine_with_existing.py @@ -19,6 +19,7 @@ # ############################################################################## import time +import json from openerp.osv.orm import TransientModel from openerp.osv import fields, expression from openerp.tools.safe_eval import safe_eval @@ -41,7 +42,7 @@ class IrFiltersCombineWithExisting(TransientModel): def button_save(self, cr, uid, ids, context=None): assert len(ids) == 1 this = self.browse(cr, uid, ids[0], context=context) - domain = safe_eval(this.domain) + domain = json.loads(this.domain) is_frozen = (len(domain) == 1 and expression.is_leaf(domain[0]) and domain[0][0] == 'id') diff --git a/web_m2x_options/README.rst b/web_m2x_options/README.rst index 2b1fafdb..ef7d2841 100644 --- a/web_m2x_options/README.rst +++ b/web_m2x_options/README.rst @@ -6,15 +6,17 @@ Add new options for many2one field Description ----------- -This modules modifies "many2one" form fields so as to add some new display -control options. +This module changes "many2one" and "many2manytags" form widgets, so as to +add some new display control options. ** New: support many2manytags widget ! ** -Options provided includes possibility to remove "Create..." and/or "Create and -Edit..." entries from many2one drop down. You can also change default number of -proposition appearing in the drop-down. Or prevent the dialog box poping in -case of validation error. +** New: support global option management with ir.config_parameter ! ** + +The options provided include the possibility to remove "Create..." and/or +"Create and Edit..." entries from many2one and many2many drop down lists. You +can also change the default number of entries appearing in the drop-down, or + prevent the dialog box poping in case of validation error occurring. If not specified, the module will avoid proposing any of the create options if the current user have no permission rights to create the related object. @@ -46,6 +48,31 @@ New option Number of displayed record in drop-down panel +ir.config_parameter options +--------------------------- + +Now you can disable "Create..." and "Create and Edit..." entry for all widgets in the odoo instance. +If you disable one option, you can enable it for particular field by setting "create: True" option directly on the field definition. + +``web_m2x_options.create`` *boolean* (Default: depends if user have create rights) + + Whether to display the "Create..." entry in dropdown panel for all fields in the odoo instance. + +``web_m2x_options.create_edit`` *boolean* (Default: depends if user have create rights) + + Whether to display "Create and Edit..." entry in dropdown panel for all fields in the odoo instance. + +``web_m2x_options.limit`` *int* (Default: openerp default value is ``7``) + + Number of displayed records in drop-down panel for all fields in the odoo instance + +To add these parameters go to Configuration -> Technical -> Parameters -> System Parameters and add new parameters like: + +- web_m2x_options.create: False +- web_m2x_options.create_edit: False +- web_m2x_options.limit: 10 + + Example ------- @@ -59,5 +86,5 @@ Note ---- Double check that you have no inherited view that remote ``options`` you set on a field ! -If nothing work, add a debugger in the first ligne of ``get_search_result method`` and enable debug mode in OpenERP. When you write something in a many2one field, javascript debugger should pause. If not verify your installation. +If nothing work, add a debugger in the first line of ``get_search_result`` method and enable debug mode in OpenERP. When you write something in a many2one field, javascript debugger should pause. If not verify your installation. diff --git a/web_m2x_options/static/src/js/form.js b/web_m2x_options/static/src/js/form.js index 00885a09..e4540567 100644 --- a/web_m2x_options/static/src/js/form.js +++ b/web_m2x_options/static/src/js/form.js @@ -8,8 +8,35 @@ openerp.web_m2x_options = function (instance) { _t = instance.web._t, _lt = instance.web._lt; + var OPTIONS = ['web_m2x_options.create', + 'web_m2x_options.create_edit', + 'web_m2x_options.limit',]; + instance.web.form.FieldMany2One.include({ + start: function() { + this._super.apply(this, arguments); + return this.get_options(); + }, + + get_options: function() { + var self = this; + if (!_.isUndefined(this.view) && _.isUndefined(this.view.ir_options_loaded)) { + this.view.ir_options_loaded = $.Deferred(); + this.view.ir_options = {}; + (new instance.web.Model("ir.config_parameter")) + .query(["key", "value"]).filter([['key', 'in', OPTIONS]]) + .all().then(function(records) { + _(records).each(function(record) { + self.view.ir_options[record.key] = record.value; + }); + self.view.ir_options_loaded.resolve(); + }); + return this.view.ir_options_loaded; + } + return $.when(); + }, + show_error_displayer: function () { if ((typeof this.options.m2o_dialog === 'undefined' && this.can_create) || this.options.m2o_dialog) { @@ -24,6 +51,12 @@ openerp.web_m2x_options = function (instance) { // add options limit used to change number of selections record // returned. + if (_.isUndefined(this.view)) + return this._super.apply(this, arguments); + if (!_.isUndefined(this.view.ir_options['web_m2x_options.limit'])) { + this.limit = parseInt(this.view.ir_options['web_m2x_options.limit']); + } + if (typeof this.options.limit === 'number') { this.limit = this.options.limit; } @@ -65,7 +98,7 @@ openerp.web_m2x_options = function (instance) { }; }); - // search more... if more results that max + // search more... if more results than max if (values.length > self.limit) { values = values.slice(0, self.limit); @@ -88,7 +121,8 @@ openerp.web_m2x_options = function (instance) { return x[1]; }); - if ((typeof self.options.create === 'undefined' && can_create) || + if ((_.isUndefined(self.options.create) && _.isUndefined(self.view.ir_options['web_m2x_options.create']) && can_create) || + (_.isUndefined(self.options.create) && self.view.ir_options['web_m2x_options.create'] == "True") || self.options.create) { if (search_val.length > 0 && @@ -106,9 +140,11 @@ openerp.web_m2x_options = function (instance) { } } + // create... - if ((typeof self.options.create_edit === 'undefined' && can_create) || + if ((_.isUndefined(self.options.create_edit) && _.isUndefined(self.view.ir_options['web_m2x_options.create_edit']) && can_create) || + (_.isUndefined(self.options.create) && self.view.ir_options['web_m2x_options.create_edit'] == "True") || self.options.create_edit) { values.push({ @@ -138,6 +174,28 @@ openerp.web_m2x_options = function (instance) { } }, + start: function() { + this._super.apply(this, arguments); + return this.get_options(); + }, + + get_options: function() { + var self = this; + if (_.isUndefined(this.view.ir_options_loaded)) { + this.view.ir_options_loaded = $.Deferred(); + this.view.ir_options = {}; + (new instance.web.Model("ir.config_parameter")) + .query(["key", "value"]).filter([['key', 'in', OPTIONS]]) + .all().then(function(records) { + _(records).each(function(record) { + self.view.ir_options[record.key] = record.value; + }); + self.view.ir_options_loaded.resolve(); + }); + } + return this.view.ir_options_loaded; + }, + /** * Call this method to search using a string. */ @@ -148,6 +206,10 @@ openerp.web_m2x_options = function (instance) { // add options limit used to change number of selections record // returned. + if (!_.isUndefined(this.view.ir_options['web_m2x_options.limit'])) { + this.limit = parseInt(this.view.ir_options['web_m2x_options.limit']); + } + if (typeof this.options.limit === 'number') { this.limit = this.options.limit; } @@ -186,7 +248,8 @@ openerp.web_m2x_options = function (instance) { } // quick create - if ((typeof self.options.create === 'undefined') || + if ((_.isUndefined(self.options.create) && _.isUndefined(self.view.ir_options['web_m2x_options.create'])) || + (_.isUndefined(self.options.create) && self.view.ir_options['web_m2x_options.create'] == 'True') || self.options.create) { var raw_result = _(data.result).map(function(x) {return x[1];}); @@ -201,10 +264,10 @@ openerp.web_m2x_options = function (instance) { }); } } - // create... - if ((typeof self.options.create_edit === 'undefined') || + if ((_.isUndefined(self.options.create_edit === 'undefined') && _.isUndefined(self.view.ir_options['web_m2x_options.create_edit'])) || + (_.isUndefined(self.options.create) && self.view.ir_options['web_m2x_options.create_edit'] == 'True') || self.options.create_edit) { values.push({ diff --git a/web_widget_color/__init__.py b/web_widget_color/__init__.py new file mode 100644 index 00000000..c18661e9 --- /dev/null +++ b/web_widget_color/__init__.py @@ -0,0 +1,25 @@ +# -*- encoding: utf-8 -*- +############################################################################ +# +# Odoo, Open Source Web Color +# Copyright (C) 2012 Savoir-faire Linux (). +# Copyright (C) 2014 Anybox +# Copyright (C) 2015 Taktik SA +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# @author Étienne Beaudry Auger +# @author Adil Houmadi +# +############################################################################## diff --git a/web_widget_color/__openerp__.py b/web_widget_color/__openerp__.py new file mode 100644 index 00000000..4d00eb83 --- /dev/null +++ b/web_widget_color/__openerp__.py @@ -0,0 +1,140 @@ +# -*- encoding: utf-8 -*- +############################################################################ +# +# Odoo, Open Source Web Widget Color +# Copyright (C) 2012 Savoir-faire Linux (). +# Copyright (C) 2014 Anybox +# Copyright (C) 2015 Taktik SA +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# @author Étienne Beaudry Auger +# @author Adil Houmadi +# +############################################################################## +{ + 'name': "Web Widget Color", + 'category': "web", + 'version': "1.0", + 'depends': ['base', 'web'], + 'description': ''' +Color widget for Odoo web client +================================ + +This module aims to add a color picker to Odoo. + +It's a `jsColor `_ lib integration. + + +Features +======== + +* The picker allow the user to quickly select a color on edit mode + + |picker| + + .. note:: + + Notice how html code and the background color is updating when selecting + a color. + + +* Display the color on form view when you are not editing it + + |formview| + +* Display the color on list view to quickly find what's wrong! + + |listview| + + +Requirements +============ + +This module has been ported to 8.0 + + +Usage +===== + +You need to declare a char field of at least size 7:: + + _columns = { + 'color': fields.char( + u"Couleur", + help=u"Toutes couleur valid css, exemple blue ou #f57900" + ), + } + + OR + + color = fields.Char( + string="Color", + help="Choose your color" + ) + + +In the view declaration, put widget='color' attribute in the field tag:: + + ... + + + ... + + + ... + + + ... + +.. |picker| image:: /web_widget_color/static/src/img/picker.png +.. |formview| image:: /web_widget_color/static/src/img/form_view.png +.. |listview| image:: /web_widget_color/static/src/img/list_view.png + +Credits +======= + +Contributors +------------ + +* Adil Houmadi + +Maintainer +---------- + +.. image:: http://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: http://odoo-community.org + +This module is maintained by the OCA. + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +To contribute to this module, please visit http://odoo-community.org.''', + 'qweb': [ + 'static/src/xml/widget.xml', + ], + 'css': [ + 'static/src/css/widget.css', + ], + 'js': [ + 'static/lib/jscolor/jscolor.js', + 'static/src/js/widget.js', + ], + 'auto_install': False, + 'installable': True, + 'web_preload': True, +} diff --git a/web_widget_color/static/lib/jscolor/arrow.gif b/web_widget_color/static/lib/jscolor/arrow.gif new file mode 100644 index 00000000..246478a8 Binary files /dev/null and b/web_widget_color/static/lib/jscolor/arrow.gif differ diff --git a/web_widget_color/static/lib/jscolor/cross.gif b/web_widget_color/static/lib/jscolor/cross.gif new file mode 100644 index 00000000..0ee9c7ac Binary files /dev/null and b/web_widget_color/static/lib/jscolor/cross.gif differ diff --git a/web_widget_color/static/lib/jscolor/demo.html b/web_widget_color/static/lib/jscolor/demo.html new file mode 100644 index 00000000..cb860664 --- /dev/null +++ b/web_widget_color/static/lib/jscolor/demo.html @@ -0,0 +1,12 @@ + + + jscolor demo + + + + + + Click here: + + + diff --git a/web_widget_color/static/lib/jscolor/hs.png b/web_widget_color/static/lib/jscolor/hs.png new file mode 100644 index 00000000..3d94486c Binary files /dev/null and b/web_widget_color/static/lib/jscolor/hs.png differ diff --git a/web_widget_color/static/lib/jscolor/hv.png b/web_widget_color/static/lib/jscolor/hv.png new file mode 100644 index 00000000..1c5e01f8 Binary files /dev/null and b/web_widget_color/static/lib/jscolor/hv.png differ diff --git a/web_widget_color/static/lib/jscolor/jscolor.js b/web_widget_color/static/lib/jscolor/jscolor.js new file mode 100644 index 00000000..47c89616 --- /dev/null +++ b/web_widget_color/static/lib/jscolor/jscolor.js @@ -0,0 +1,1010 @@ +/** + * jscolor, JavaScript Color Picker + * + * @version 1.4.4 + * @license GNU Lesser General Public License, http://www.gnu.org/copyleft/lesser.html + * @author Jan Odvarko, http://odvarko.cz + * @created 2008-06-15 + * @updated 2014-12-09 + * @link http://jscolor.com + */ + + +var jscolor = { + + + dir : '', // location of jscolor directory (leave empty to autodetect) + bindClass : 'color', // class name + binding : true, // automatic binding via + preloading : true, // use image preloading? + + + install : function() { + jscolor.addEvent(window, 'load', jscolor.init); + }, + + + init : function() { + if(jscolor.binding) { + jscolor.bind(); + } + if(jscolor.preloading) { + jscolor.preload(); + } + }, + + + getDir : function() { + if(!jscolor.dir) { + var detected = jscolor.detectDir(); + jscolor.dir = detected!==false ? detected : 'jscolor/'; + } + return jscolor.dir; + }, + + + detectDir : function() { + var base = location.href; + + var e = document.getElementsByTagName('base'); + for(var i=0; i vs[a] ? + (-vp[a]+tp[a]+ts[a]/2 > vs[a]/2 && tp[a]+ts[a]-ps[a] >= 0 ? tp[a]+ts[a]-ps[a] : tp[a]) : + tp[a], + -vp[b]+tp[b]+ts[b]+ps[b]-l+l*c > vs[b] ? + (-vp[b]+tp[b]+ts[b]/2 > vs[b]/2 && tp[b]+ts[b]-l-l*c >= 0 ? tp[b]+ts[b]-l-l*c : tp[b]+ts[b]-l+l*c) : + (tp[b]+ts[b]-l+l*c >= 0 ? tp[b]+ts[b]-l+l*c : tp[b]+ts[b]-l-l*c) + ]; + } + drawPicker(pp[a], pp[b]); + } + }; + + + this.importColor = function() { + if(!valueElement) { + this.exportColor(); + } else { + if(!this.adjust) { + if(!this.fromString(valueElement.value, leaveValue)) { + styleElement.style.backgroundImage = styleElement.jscStyle.backgroundImage; + styleElement.style.backgroundColor = styleElement.jscStyle.backgroundColor; + styleElement.style.color = styleElement.jscStyle.color; + this.exportColor(leaveValue | leaveStyle); + } + } else if(!this.required && /^\s*$/.test(valueElement.value)) { + valueElement.value = ''; + styleElement.style.backgroundImage = styleElement.jscStyle.backgroundImage; + styleElement.style.backgroundColor = styleElement.jscStyle.backgroundColor; + styleElement.style.color = styleElement.jscStyle.color; + this.exportColor(leaveValue | leaveStyle); + + } else if(this.fromString(valueElement.value)) { + // OK + } else { + this.exportColor(); + } + } + }; + + + this.exportColor = function(flags) { + if(!(flags & leaveValue) && valueElement) { + var value = this.toString(); + if(this.caps) { value = value.toUpperCase(); } + if(this.hash) { value = '#'+value; } + valueElement.value = value; + } + if(!(flags & leaveStyle) && styleElement) { + styleElement.style.backgroundImage = "none"; + styleElement.style.backgroundColor = + '#'+this.toString(); + styleElement.style.color = + 0.213 * this.rgb[0] + + 0.715 * this.rgb[1] + + 0.072 * this.rgb[2] + < 0.5 ? '#FFF' : '#000'; + } + if(!(flags & leavePad) && isPickerOwner()) { + redrawPad(); + } + if(!(flags & leaveSld) && isPickerOwner()) { + redrawSld(); + } + }; + + + this.fromHSV = function(h, s, v, flags) { // null = don't change + if(h !== null) { h = Math.max(0.0, this.minH, Math.min(6.0, this.maxH, h)); } + if(s !== null) { s = Math.max(0.0, this.minS, Math.min(1.0, this.maxS, s)); } + if(v !== null) { v = Math.max(0.0, this.minV, Math.min(1.0, this.maxV, v)); } + + this.rgb = HSV_RGB( + h===null ? this.hsv[0] : (this.hsv[0]=h), + s===null ? this.hsv[1] : (this.hsv[1]=s), + v===null ? this.hsv[2] : (this.hsv[2]=v) + ); + + this.exportColor(flags); + }; + + + this.fromRGB = function(r, g, b, flags) { // null = don't change + if(r !== null) { r = Math.max(0.0, Math.min(1.0, r)); } + if(g !== null) { g = Math.max(0.0, Math.min(1.0, g)); } + if(b !== null) { b = Math.max(0.0, Math.min(1.0, b)); } + + var hsv = RGB_HSV( + r===null ? this.rgb[0] : r, + g===null ? this.rgb[1] : g, + b===null ? this.rgb[2] : b + ); + if(hsv[0] !== null) { + this.hsv[0] = Math.max(0.0, this.minH, Math.min(6.0, this.maxH, hsv[0])); + } + if(hsv[2] !== 0) { + this.hsv[1] = hsv[1]===null ? null : Math.max(0.0, this.minS, Math.min(1.0, this.maxS, hsv[1])); + } + this.hsv[2] = hsv[2]===null ? null : Math.max(0.0, this.minV, Math.min(1.0, this.maxV, hsv[2])); + + // update RGB according to final HSV, as some values might be trimmed + var rgb = HSV_RGB(this.hsv[0], this.hsv[1], this.hsv[2]); + this.rgb[0] = rgb[0]; + this.rgb[1] = rgb[1]; + this.rgb[2] = rgb[2]; + + this.exportColor(flags); + }; + + + this.fromString = function(hex, flags) { + var m = hex.match(/^\W*([0-9A-F]{3}([0-9A-F]{3})?)\W*$/i); + if(!m) { + return false; + } else { + if(m[1].length === 6) { // 6-char notation + this.fromRGB( + parseInt(m[1].substr(0,2),16) / 255, + parseInt(m[1].substr(2,2),16) / 255, + parseInt(m[1].substr(4,2),16) / 255, + flags + ); + } else { // 3-char notation + this.fromRGB( + parseInt(m[1].charAt(0)+m[1].charAt(0),16) / 255, + parseInt(m[1].charAt(1)+m[1].charAt(1),16) / 255, + parseInt(m[1].charAt(2)+m[1].charAt(2),16) / 255, + flags + ); + } + return true; + } + }; + + + this.toString = function() { + return ( + (0x100 | Math.round(255*this.rgb[0])).toString(16).substr(1) + + (0x100 | Math.round(255*this.rgb[1])).toString(16).substr(1) + + (0x100 | Math.round(255*this.rgb[2])).toString(16).substr(1) + ); + }; + + + function RGB_HSV(r, g, b) { + var n = Math.min(Math.min(r,g),b); + var v = Math.max(Math.max(r,g),b); + var m = v - n; + if(m === 0) { return [ null, 0, v ]; } + var h = r===n ? 3+(b-g)/m : (g===n ? 5+(r-b)/m : 1+(g-r)/m); + return [ h===6?0:h, m/v, v ]; + } + + + function HSV_RGB(h, s, v) { + if(h === null) { return [ v, v, v ]; } + var i = Math.floor(h); + var f = i%2 ? h-i : 1-(h-i); + var m = v * (1 - s); + var n = v * (1 - s*f); + switch(i) { + case 6: + case 0: return [v,n,m]; + case 1: return [n,v,m]; + case 2: return [m,v,n]; + case 3: return [m,n,v]; + case 4: return [n,m,v]; + case 5: return [v,m,n]; + } + } + + + function removePicker() { + delete jscolor.picker.owner; + document.getElementsByTagName('body')[0].removeChild(jscolor.picker.boxB); + } + + + function drawPicker(x, y) { + if(!jscolor.picker) { + jscolor.picker = { + box : document.createElement('div'), + boxB : document.createElement('div'), + pad : document.createElement('div'), + padB : document.createElement('div'), + padM : document.createElement('div'), + sld : document.createElement('div'), + sldB : document.createElement('div'), + sldM : document.createElement('div'), + btn : document.createElement('div'), + btnS : document.createElement('span'), + btnT : document.createTextNode(THIS.pickerCloseText) + }; + for(var i=0,segSize=4; i 0) { + var val = $input.val(); + var isOk = /^#[0-9A-F]{6}$/i.test(val); + if (!isOk) { + return false; + } + try { + this.parse_value(this.$('input').val(), ''); + return true; + } catch (e) { + return false; + } + } + return true; + }, + render_value: function () { + var show_value = this.format_value(this.get('value'), ''); + if (!this.get("effective_readonly")) { + var $input = this.$el.find('input'); + $input.val(show_value); + $input.css("background-color", show_value) + jscolor.init(this.$el[0]); + } else { + this.$(".oe_form_char_content").text(show_value); + this.$('div').css("background-color", show_value) + } + } + }); + + /* + * Init jscolor for each editable mode on view form + */ + instance.web.FormView.include({ + to_edit_mode: function () { + this._super(); + jscolor.init(this.$el[0]); + } + }); +}; diff --git a/web_widget_color/static/src/xml/widget.xml b/web_widget_color/static/src/xml/widget.xml new file mode 100644 index 00000000..c62ae1cf --- /dev/null +++ b/web_widget_color/static/src/xml/widget.xml @@ -0,0 +1,24 @@ + + + + + + + + +
+ + + + + +
+ +