diff --git a/addons/web/__init__.py b/addons/web/__init__.py index 681fd6b42b3..dba47c9f45e 100644 --- a/addons/web/__init__.py +++ b/addons/web/__init__.py @@ -10,15 +10,14 @@ class Options(object): pass def wsgi_postload(): - import openerp.wsgi - import openerp.tools + import openerp import os import tempfile _logger.info("embedded mode") o = Options() o.dbfilter = openerp.tools.config['dbfilter'] o.session_storage = os.path.join(tempfile.gettempdir(), "oe-sessions") - o.addons_path = os.path.dirname(os.path.dirname(__file__)) + o.addons_path = openerp.modules.module.ad_paths o.serve_static = True o.backend = 'local' diff --git a/addons/web/__openerp__.py b/addons/web/__openerp__.py index 1f83634eadf..567642d3429 100644 --- a/addons/web/__openerp__.py +++ b/addons/web/__openerp__.py @@ -2,7 +2,8 @@ "name" : "web", "depends" : [], 'active': True, - 'web_auto_load': True, + 'post_load' : 'wsgi_postload', + 'web_preload' : True, 'js' : [ "static/lib/datejs/globalization/en-US.js", "static/lib/datejs/core.js", @@ -49,5 +50,4 @@ "static/src/css/data_export.css", "static/src/css/data_import.css", ], - 'post_load' : 'wsgi_postload', } diff --git a/addons/web/common/dispatch.py b/addons/web/common/dispatch.py index b0ab780cff0..7a293c8228b 100644 --- a/addons/web/common/dispatch.py +++ b/addons/web/common/dispatch.py @@ -33,7 +33,6 @@ _logger = logging.getLogger(__name__) # Globals (wont move into a pool) #----------------------------------------------------------- -applicationsession = {} addons_module = {} addons_manifest = {} controllers_class = {} @@ -52,10 +51,6 @@ class WebRequest(object): :type request: :class:`werkzeug.wrappers.BaseRequest` :param config: configuration object - .. attribute:: applicationsession - - an application-wide :class:`~collections.Mapping` - .. attribute:: httprequest the original :class:`werkzeug.wrappers.Request` object provided to the @@ -94,7 +89,6 @@ class WebRequest(object): ``bool``, indicates whether the debug mode is active on the client """ def __init__(self, request, config): - self.applicationsession = applicationsession self.httprequest = request self.httpresponse = None self.httpsession = request.session @@ -381,20 +375,21 @@ class Root(object): static URLs to the corresponding directories """ statics = {} - addons_path = self.config.addons_path - if addons_path not in sys.path: - sys.path.insert(0, addons_path) - for module in os.listdir(addons_path): - if module not in addons_module: - manifest_path = os.path.join(addons_path, module, '__openerp__.py') - path_static = os.path.join(addons_path, module, 'static') - if os.path.isfile(manifest_path) and os.path.isdir(path_static): - manifest = ast.literal_eval(open(manifest_path).read()) - _logger.info("Loading %s", module) - m = __import__(module) - addons_module[module] = m - addons_manifest[module] = manifest - statics['/%s/static' % module] = path_static + for addons_path in self.config.addons_path: + if addons_path not in sys.path: + sys.path.insert(0, addons_path) + for module in os.listdir(addons_path): + if module not in addons_module: + manifest_path = os.path.join(addons_path, module, '__openerp__.py') + path_static = os.path.join(addons_path, module, 'static') + if os.path.isfile(manifest_path) and os.path.isdir(path_static): + manifest = ast.literal_eval(open(manifest_path).read()) + manifest['addons_path'] = addons_path + _logger.info("Loading %s", module) + m = __import__(module) + addons_module[module] = m + addons_manifest[module] = manifest + statics['/%s/static' % module] = path_static for k, v in controllers_class.items(): if k not in controllers_object: o = v() diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index 4e71de59806..b9970ca8f72 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -68,15 +68,30 @@ class Xml2Json: # OpenERP Web web Controllers #---------------------------------------------------------- -def manifest_glob(addons_path, addons, key): +def manifest_preload(): + modules = [k for k,v in openerpweb.addons_manifest.items() if v.get('web_preload')] + return modules + +def manifest_addons(addons): + if addons is None: + addons = manifest_preload() + else: + addons = addons.split(',') + return addons + +def manifest_glob(addons, key): + addons = manifest_addons(addons) files = [] for addon in addons: - globlist = openerpweb.addons_manifest.get(addon, {}).get(key, []) + manifest = openerpweb.addons_manifest.get(addon, {}) + addons_path = manifest['addons_path'] + globlist = manifest.get(key, []) for pattern in globlist: for path in glob.glob(os.path.join(addons_path, addon, pattern)): files.append(path[len(addons_path):]) return files +# TODO change into concat_file(addons,key) taking care of addons_path def concat_files(addons_path, file_list): """ Concatenate file content return (concat,timestamp) @@ -117,49 +132,45 @@ class WebClient(openerpweb.Controller): _cp_path = "/web/webclient" @openerpweb.jsonrequest - def csslist(self, req, mods='web'): - return manifest_glob(req.config.addons_path, mods.split(','), 'css') + def csslist(self, req, mods=None): + return manifest_glob(mods, 'css') @openerpweb.jsonrequest - def jslist(self, req, mods='web'): - return manifest_glob(req.config.addons_path, mods.split(','), 'js') + def jslist(self, req, mods=None): + return manifest_glob(mods, 'js') @openerpweb.httprequest - def css(self, req, mods='web'): - files = manifest_glob(req.config.addons_path, mods.split(','), 'css') + def css(self, req, mods=None): + files = manifest_glob(mods, 'css') content,timestamp = concat_files(req.config.addons_path, files) # TODO request set the Date of last modif and Etag return req.make_response(content, [('Content-Type', 'text/css')]) @openerpweb.httprequest - def js(self, req, mods='web'): - files = manifest_glob(req.config.addons_path, mods.split(','), 'js') + def js(self, req, mods=None): + files = manifest_glob(mods, 'js') content,timestamp = concat_files(req.config.addons_path, files) # TODO request set the Date of last modif and Etag return req.make_response(content, [('Content-Type', 'application/javascript')]) @openerpweb.httprequest def home(self, req, s_action=None, **kw): - modules = [a for a, m in openerpweb.addons_manifest.iteritems() if m.get('web_auto_load')] - assert 'web' in modules - cs_mods = ','.join(modules) - # script tags - jslist = ['/web/webclient/js?mods='+cs_mods] + jslist = ['/web/webclient/js'] if req.debug: - jslist = [i + '?debug=' + str(time.time()) for i in manifest_glob(req.config.addons_path, modules, 'js')] + jslist = [i + '?debug=' + str(time.time()) for i in manifest_glob(None, 'js')] js = "\n ".join([''%i for i in jslist]) # css tags - csslist = ['/web/webclient/css?mods='+cs_mods] + csslist = ['/web/webclient/css'] if req.debug: - csslist = [i + '?debug=' + str(time.time()) for i in manifest_glob(req.config.addons_path, modules, 'css')] + csslist = [i + '?debug=' + str(time.time()) for i in manifest_glob(None, 'css')] css = "\n ".join([''%i for i in csslist]) r = home_template % { 'javascript': js, 'css': css, - 'modules': repr(modules), # XXX good js-ification ? + 'modules': repr(manifest_preload()), # XXX good js-ification ? } return r @@ -185,7 +196,8 @@ class WebClient(openerpweb.Controller): transl = {"messages":[]} transs[addon_name] = transl for l in langs: - f_name = os.path.join(req.config.addons_path, addon_name, "po", l + ".po") + addons_path = openerpweb.addons_manifest[addon_name]['addons_path'] + f_name = os.path.join(addons_path, addon_name, "po", l + ".po") if not os.path.exists(f_name): continue try: @@ -305,6 +317,7 @@ class Session(openerpweb.Controller): "context": ctx, "db": req.session._db } + @openerpweb.jsonrequest def get_session_info(self, req): req.session.assert_valid(force=True) @@ -313,6 +326,7 @@ class Session(openerpweb.Controller): "context": req.session.get_context() if req.session._uid else False, "db": req.session._db } + @openerpweb.jsonrequest def change_password (self,req,fields): old_password, new_password,confirm_password = operator.itemgetter('old_pwd', 'new_password','confirm_pwd')( @@ -328,6 +342,7 @@ class Session(openerpweb.Controller): except: return {'error': 'Original password incorrect, your password was not changed.', 'title': 'Change Password'} return {'error': 'Error, password not changed !', 'title': 'Change Password'} + @openerpweb.jsonrequest def sc_list(self, req): return req.session.model('ir.ui.view_sc').get_sc( @@ -348,7 +363,8 @@ class Session(openerpweb.Controller): # TODO query server for installed web modules mods = [] for name, manifest in openerpweb.addons_manifest.items(): - if not manifest.get('web_auto_load') and manifest.get('active', True): + # TODO replace by ir.module.module installed web + if not manifest.get('web_preload') and manifest.get('active', True): mods.append(name) return mods diff --git a/addons/web/static/src/js/data.js b/addons/web/static/src/js/data.js index e5d323c8a96..00281628e75 100644 --- a/addons/web/static/src/js/data.js +++ b/addons/web/static/src/js/data.js @@ -608,15 +608,23 @@ openerp.web.BufferedDataSet = openerp.web.DataSetStatic.extend({ init: function() { this._super.apply(this, arguments); this.reset_ids([]); + this.last_default_get = {}; + }, + default_get: function(fields, callback) { + return this._super(fields).then(this.on_default_get).then(callback); + }, + on_default_get: function(res) { + this.last_default_get = res; }, create: function(data, callback, error_callback) { - var cached = {id:_.uniqueId(this.virtual_id_prefix), values: data}; + var cached = {id:_.uniqueId(this.virtual_id_prefix), values: data, + defaults: this.last_default_get}; this.to_create.push(cached); this.cache.push(cached); this.on_change(); - var to_return = $.Deferred().then(callback); - to_return.resolve({result: cached.id}); - return to_return.promise(); + var prom = $.Deferred().then(callback); + setTimeout(function() {prom.resolve({result: cached.id});}, 0); + return prom.promise(); }, write: function (id, data, options, callback) { var self = this; @@ -676,7 +684,8 @@ openerp.web.BufferedDataSet = openerp.web.DataSetStatic.extend({ var cached = _.detect(self.cache, function(x) {return x.id === id;}); var created = _.detect(self.to_create, function(x) {return x.id === id;}); if (created) { - _.each(fields, function(x) {if (cached.values[x] === undefined) cached.values[x] = false;}); + _.each(fields, function(x) {if (cached.values[x] === undefined) + cached.values[x] = created.defaults[x] || false;}); } else { if (!cached || !_.all(fields, function(x) {return cached.values[x] !== undefined})) to_get.push(id); @@ -716,6 +725,10 @@ openerp.web.BufferedDataSet = openerp.web.DataSetStatic.extend({ } }); openerp.web.ReadOnlyDataSetSearch = openerp.web.DataSetSearch.extend({ + default_get: function(fields, callback) { + return this._super(fields, callback).then(this.on_default_get); + }, + on_default_get: function(result) {}, create: function(data, callback, error_callback) { this.on_create(data); var to_return = $.Deferred().then(callback); diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 4b9432ca41a..c7ffb896186 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -345,7 +345,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# do_save: function(success, prepend_on_create) { var self = this; if (!this.ready) { - return false; + return $.Deferred().reject(); } var form_dirty = false, form_invalid = false, @@ -367,17 +367,17 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# if (form_invalid) { first_invalid_field.focus(); this.on_invalid(); - return false; + return $.Deferred().reject(); } else { console.log("About to save", values); if (!this.datarecord.id) { - return this.dataset.create(values, function(r) { - self.on_created(r, success, prepend_on_create); - }); + return this.dataset.create(values).pipe(function(r) { + return self.on_created(r, undefined, prepend_on_create); + }).then(success); } else { - return this.dataset.write(this.datarecord.id, values, {}, function(r) { - self.on_saved(r, success); - }); + return this.dataset.write(this.datarecord.id, values, {}).pipe(function(r) { + return self.on_saved(r); + }).then(success); } } }, @@ -402,11 +402,10 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# on_saved: function(r, success) { if (!r.result) { // should not happen in the server, but may happen for internal purpose + return $.Deferred().reject(); } else { - if (success) { - success(r); - } this.reload(); + return $.Deferred().then(success).resolve(r); } }, /** @@ -425,6 +424,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# on_created: function(r, success, prepend_on_create) { if (!r.result) { // should not happen in the server, but may happen for internal purpose + return $.Deferred().reject(); } else { this.datarecord.id = r.result; if (!prepend_on_create) { @@ -439,10 +439,8 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# this.sidebar.attachments.do_update(); } console.debug("The record has been created with id #" + this.datarecord.id); - if (success) { - success(_.extend(r, {created: true})); - } this.reload(); + return $.Deferred().then(success).resolve(_.extend(r, {created: true})); } }, do_search: function (domains, contexts, groupbys) { @@ -472,6 +470,13 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# get_selected_ids: function() { var id = this.dataset.ids[this.dataset.index]; return id ? [id] : []; + }, + recursive_save: function() { + var self = this; + return $.when(this.do_save()).pipe(function(res) { + if (self.dataset.parent_view) + return self.dataset.parent_view.recursive_save(); + }); } }); openerp.web.FormDialog = openerp.web.Dialog.extend({ @@ -830,30 +835,31 @@ openerp.web.form.WidgetButton = openerp.web.form.Widget.extend({ this._super.apply(this, arguments); this.$element.click(this.on_click); }, - on_click: function(saved) { + on_click: function() { var self = this; - if ((!this.node.attrs.special && this.view.dirty_for_user && saved !== true) || !this.view.datarecord.id) { - this.view.do_save(function() { - self.on_click(true); - }); - } else { - if (this.node.attrs.confirm) { - var dialog = $('
' + this.node.attrs.confirm + '
').dialog({ + var exec_action = function() { + if (self.node.attrs.confirm) { + var dialog = $('
' + self.node.attrs.confirm + '
').dialog({ title: 'Confirm', modal: true, buttons: { Ok: function() { self.on_confirmed(); - $(this).dialog("close"); + $(self).dialog("close"); }, Cancel: function() { - $(this).dialog("close"); + $(self).dialog("close"); } } }); } else { - this.on_confirmed(); + self.on_confirmed(); } + }; + if ((!this.node.attrs.special && this.view.dirty_for_user) || !this.view.datarecord.id) { + this.view.recursive_save().then(exec_action); + } else { + exec_action(); } }, on_confirmed: function() { @@ -1945,11 +1951,7 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({ var view = this.viewmanager.views[this.viewmanager.active_view].controller; if (this.viewmanager.active_view === "form") { var res = $.when(view.do_save()); - if (res === false) { - // ignore - } else if (res.isRejected()) { - throw "Save or create on one2many dataset is not supposed to fail."; - } else if (!res.isResolved()) { + if (!res.isResolved() && !res.isRejected()) { throw "Asynchronous get_value() is not supported in form view."; } return res; @@ -2002,6 +2004,7 @@ openerp.web.form.One2ManyListView = openerp.web.ListView.extend({ } else { var self = this; var pop = new openerp.web.form.SelectCreatePopup(this); + pop.on_default_get.add(self.dataset.on_default_get); pop.select_element(self.o2m.field.relation,{ initial_view: "form", alternative_form_view: self.o2m.field.views ? self.o2m.field.views["form"] : undefined, @@ -2163,6 +2166,7 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope this.dataset = new openerp.web.ReadOnlyDataSetSearch(this, this.model, this.context); 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(); } else { // "form" @@ -2285,7 +2289,8 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope this.on_select_elements(this.created_elements); } this.stop(); - } + }, + on_default_get: function(res) {} }); openerp.web.form.SelectCreateListView = openerp.web.ListView.extend({ diff --git a/addons/web_graph/__init__.py b/addons/web_graph/__init__.py index 53e20f29429..e69de29bb2d 100644 --- a/addons/web_graph/__init__.py +++ b/addons/web_graph/__init__.py @@ -1,2 +0,0 @@ -#!/usr/bin/python -import controllers \ No newline at end of file diff --git a/addons/web_graph/controllers/__init__.py b/addons/web_graph/controllers/__init__.py deleted file mode 100644 index 039d9715fab..00000000000 --- a/addons/web_graph/controllers/__init__.py +++ /dev/null @@ -1 +0,0 @@ -import main \ No newline at end of file diff --git a/addons/web_graph/controllers/main.py b/addons/web_graph/controllers/main.py deleted file mode 100644 index cda868c0809..00000000000 --- a/addons/web_graph/controllers/main.py +++ /dev/null @@ -1,11 +0,0 @@ -from web.controllers.main import View -import web.common as openerpweb - -class GraphView(View): - _cp_path = "/web_graph/graphview" - - @openerpweb.jsonrequest - def load(self, req, model, view_id): - fields_view = self.fields_view_get(req, model, view_id, 'graph') - all_fields = req.session.model(model).fields_get() - return {'fields_view': fields_view, 'all_fields':all_fields} diff --git a/addons/web_graph/static/src/js/graph.js b/addons/web_graph/static/src/js/graph.js index 2a493b89166..59bc2c29dcf 100644 --- a/addons/web_graph/static/src/js/graph.js +++ b/addons/web_graph/static/src/js/graph.js @@ -33,8 +33,20 @@ openerp.web_graph.GraphView = openerp.web.View.extend({ this.$element.hide(); }, start: function() { + var self = this; this._super(); - return this.rpc("/web_graph/graphview/load", {"model": this.model, "view_id": this.view_id}, this.on_loaded); + return $.when( + new openerp.web.DataSet(this, this.model).call('fields_get', []), + this.rpc('/web/view/load', { + model: this.model, + view_id: this.view_id, + view_type: 'graph' + })).then(function (fields_result, view_result) { + self.on_loaded({ + all_fields: fields_result[0], + fields_view: view_result[0] + }); + }); }, on_loaded: function(data) { this.all_fields = data.all_fields; @@ -49,8 +61,6 @@ openerp.web_graph.GraphView = openerp.web.View.extend({ this.operator = []; this.group_field = []; this.orientation = this.fields_view.arch.attrs.orientation || ''; - this.elem_id = this.$element[0]['id']; - _.each(this.fields_view.arch.children, function (field) { if (field.attrs.operator) { this.operator.push(field.attrs.name); @@ -98,7 +108,7 @@ openerp.web_graph.GraphView = openerp.web.View.extend({ }, schedule_chart: function(results) { - this.$element.html(QWeb.render("GraphView", {"fields_view": this.fields_view, "chart": this.chart,'elem_id': this.elem_id})); + this.$element.html(QWeb.render("GraphView", {"fields_view": this.fields_view, "chart": this.chart,'element_id': this.element_id})); _.each(results, function (result) { _.each(result, function (field_value, field_name) { @@ -304,7 +314,7 @@ openerp.web_graph.GraphView = openerp.web.View.extend({ var bar_chart = new dhtmlXChart({ view: view_chart, - container: self.elem_id+"-barchart", + container: self.element_id+"-barchart", value:"#"+group_list[0]+"#", gradient: "3d", border: false, @@ -374,7 +384,7 @@ openerp.web_graph.GraphView = openerp.web.View.extend({ }); } bar_chart.parse(_.values(abscissa_data), "json"); - jQuery("#"+self.elem_id+"-barchart").height(jQuery("#"+self.elem_id+"-barchart").height()+50); + jQuery("#"+self.element_id+"-barchart").height(jQuery("#"+self.element_id+"-barchart").height()+50); bar_chart.attachEvent("onItemClick", function(id) { self.open_list_view(bar_chart.get(id)); }); @@ -383,7 +393,7 @@ openerp.web_graph.GraphView = openerp.web.View.extend({ var self = this; var chart = new dhtmlXChart({ view:"pie3D", - container:self.elem_id+"-piechart", + container:self.element_id+"-piechart", value:"#"+self.operator_field+"#", pieInnerText:function(obj) { var sum = chart.sum("#"+self.operator_field+"#"); diff --git a/addons/web_graph/static/src/xml/web_graph.xml b/addons/web_graph/static/src/xml/web_graph.xml index 4320b9f156e..90349623946 100644 --- a/addons/web_graph/static/src/xml/web_graph.xml +++ b/addons/web_graph/static/src/xml/web_graph.xml @@ -1,4 +1,4 @@ \ No newline at end of file diff --git a/addons/web_hello/__openerp__.py b/addons/web_hello/__openerp__.py index f67ff4b23d5..40e84db8cd5 100644 --- a/addons/web_hello/__openerp__.py +++ b/addons/web_hello/__openerp__.py @@ -5,5 +5,5 @@ "js": ["static/*/*.js", "static/*/js/*.js"], "css": [], 'active': False, - 'web_auto_load': True, + 'web_preload': True, } diff --git a/addons/web_kanban/static/src/css/kanban.css b/addons/web_kanban/static/src/css/kanban.css index afac6cdef47..f92203589b2 100644 --- a/addons/web_kanban/static/src/css/kanban.css +++ b/addons/web_kanban/static/src/css/kanban.css @@ -1,8 +1,21 @@ .openerp .oe_kanban_view .oe_column { - float: left; width: 100%; } +.openerp .oe_vertical_text { + writing-mode:tb-rl; + -webkit-transform:rotate(90deg); + -moz-transform:rotate(90deg); + -o-transform: rotate(90deg); + -ms-transform: rotate(90deg); + transform: rotate(90deg); + display:block; + width:30px; + height:20px; + align:center; + font-size:24px; +} + .openerp .oe_kanban_view .ui-sortable-placeholder { border: 1px dotted black; visibility: visible !important; @@ -14,6 +27,10 @@ font-size: 1.5em; font-weight: bold; } +.openerp .oe_kanban_view .fold-columns-icon { + float: left; + padding: 2px 2px 0 0; +} .openerp .oe_kanban_action_button { height: 22px; margin: 0; @@ -200,4 +217,3 @@ .openerp .oe_kanban_color_alert .oe_kanban_color_border { border-color: #c00 !important; } - diff --git a/addons/web_kanban/static/src/img/minus-icon.png b/addons/web_kanban/static/src/img/minus-icon.png new file mode 100644 index 00000000000..cb655d1e503 Binary files /dev/null and b/addons/web_kanban/static/src/img/minus-icon.png differ diff --git a/addons/web_kanban/static/src/img/plus-icon.png b/addons/web_kanban/static/src/img/plus-icon.png new file mode 100644 index 00000000000..7bb9b1be856 Binary files /dev/null and b/addons/web_kanban/static/src/img/plus-icon.png differ diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js index 8d6727facaa..df194e9f844 100644 --- a/addons/web_kanban/static/src/js/kanban.js +++ b/addons/web_kanban/static/src/js/kanban.js @@ -18,6 +18,8 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ this.all_display_data = false; this.groups = []; this.qweb = new QWeb2.Engine(); + this.aggregates = {}; + this.NO_OF_COLUMNS = 3; if (this.options.action_views_ids.form) { this.form_dialog = new openerp.web.FormDialog(this, {}, this.options.action_views_ids.form, dataset).start(); this.form_dialog.on_form_dialog_saved.add_last(this.on_record_saved); @@ -36,8 +38,17 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ } }, add_qweb_template: function() { + var group_operator = ["avg", "max", "min", "sum", "count"] for (var i=0, ii=this.fields_view.arch.children.length; i < ii; i++) { var child = this.fields_view.arch.children[i]; + if (child.tag === "field") { + for(j=0, jj=group_operator.length; j < jj; j++) { + if (child.attrs[group_operator[j]]) { + this.aggregates[child.attrs.name] = child.attrs[group_operator[j]]; + break; + } + } + } if (child.tag === "templates") { this.transform_qweb_template(child); this.qweb.add_template(openerp.web.json_node_to_xml(child)); @@ -118,14 +129,33 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ } } }, - on_show_data: function(data) { + sort_group: function (first, second) { + if (first.header && second.header) + { + first = first.header.toLowerCase(); + second = second.header.toLowerCase(); + if (first > second) return 1; + else if (first < second) return -1; + else return 0; + } + else return 0; + }, + on_show_data: function() { var self = this; - this.$element.html(QWeb.render("KanbanView", {"data": data})); + if (!this.group_by.length) { + this.do_record_group(); + } + self.all_display_data.sort(this.sort_group); + this.$element.html(QWeb.render("KanbanView", {"data": self.all_display_data})); this.on_reload_kanban(); + this.$element.find(".oe_vertical_text").hide(); var drag_handel = false; if (this.$element.find(".oe_kanban_draghandle").length > 0) { drag_handel = ".oe_kanban_draghandle"; } + if (!this.group_by.length) { + drag_handel = true; + } this.$element.find(".oe_column").sortable({ connectWith: ".oe_column", handle : drag_handel, @@ -134,36 +164,44 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ self.source_index['column'] = ui.item.parent().attr('id'); }, stop: self.on_receive_record, + scroll: false }); this.$element.find(".oe_column").disableSelection() this.$element.find('button.oe_kanban_button_new').click(this.do_add_record); + this.$element.find(".fold-columns-icon").click(function(event) { + self.do_fold_unfold_columns(event, this.id); + }); }, - on_button_click: function (button_attrs, record_id) { - var self = this; - if (this.groups.length) { - _.each(this.groups, function (group) { - group.list([], - function (groups) {}, - function (dataset) { - dataset.read_slice([], {}, function(records) { - var index = parseInt(_.indexOf(dataset.ids, record_id)); - if(index >= 0) { - self.on_confirm_click(dataset, button_attrs, index, record_id); - } - }); - } - ); - }); - } else { - var index = parseInt(_.indexOf(self.dataset.ids, record_id)); - if (index >= 0) { - _.extend(self.dataset, {domain: self.domain, context: self.context}); - self.on_confirm_click(self.dataset, button_attrs, index, record_id); + do_fold_unfold_columns: function(event, element_id) { + var column_id = "column_" + element_id; + var column_element = this.$element.find("#" + column_id + " .oe_fold_column"); + if (column_element.is(":hidden")) { + this.$element.find("#" + column_id).find("img.fold-columns-icon").attr('src', '/web_kanban/static/src/img/minus-icon.png'); + column_element.show(); + this.$element.find("#" + column_id + ".oe_table_column").css("width",Math.round(99 / this.all_display_data.length) + "%"); + this.$element.find("#" + column_id + ".oe_vertical_text").hide(); + } + else{ + this.$element.find("#" + column_id).find("img.fold-columns-icon").attr('src', '/web_kanban/static/src/img/plus-icon.png'); + column_element.hide(); + this.$element.find("#" + column_id + ".oe_table_column").css("width","0.5%"); + this.$element.find("#" + column_id + ".oe_vertical_text").show(); + } + + }, + do_record_group: function() { + if (this.NO_OF_COLUMNS && this.all_display_data.length > 0) { + var records = this.all_display_data[0].records; + var record_per_group = Math.round((records).length / this.NO_OF_COLUMNS); + this.all_display_data = []; + for (var i=0, ii=this.NO_OF_COLUMNS; i < ii; i++) { + this.all_display_data.push({'records': records.slice(0,record_per_group), 'value':i, 'header' : false, 'ids':[]}); + records.splice(0,record_per_group); } } }, - on_confirm_click: function (dataset, button_attrs, index, record_id) { - this.on_execute_button_click(dataset, button_attrs, record_id); + on_button_click: function (button_attrs, record_id) { + this.on_execute_button_click(this.dataset, button_attrs, record_id); }, do_add_record: function() { this.dataset.index = null; @@ -199,11 +237,37 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ data[$e.data('name')] = $(this).data('color'); self.dataset.write(id, data, {}, function() { // TODO fme: reload record instead of all. need refactoring - self.do_actual_search(); + self.on_reload_record(id, data); }); $cpicker.remove(); }); }, + /** + Reload one record in view. + record_id : reload record id. + data : change value in particular record. + */ + on_reload_record: function (record_id, data){ + var self = this; + for (var i=0, ii=this.all_display_data.length; i < ii; i++) { + for(j=0, jj=this.all_display_data[i].records.length; j < jj; j++) { + if (this.all_display_data[i].records[j].id == record_id) { + _.extend(this.all_display_data[i].records[j], data); + this.$element.find("#main_" + record_id).children().remove(); + this.$element.find("#main_" + record_id).append(this.qweb.render('kanban-box', { + record: this.do_transform_record(this.all_display_data[i].records[j]), + kanban_color: this.kanban_color, + kanban_gravatar: this.kanban_gravatar + })); + break; + } + } + } + this.$element.find("#main_" + record_id + " .oe_kanban_action").click(this.on_action_clicked); + this.$element.find("#main_" + record_id + " .oe_kanban_box_show_onclick_trigger").click(function() { + $(this).parent("#main_" + record_id + " .oe_kanban_box").find(".oe_kanban_box_show_onclick").toggle(); + }); + }, do_delete: function (id) { var self = this; return $.when(this.dataset.unlink([id])).then(function () { @@ -228,16 +292,7 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ this.do_execute_action( button_attrs, dataset, record_id, function () { - var count = 1; - _.each(self.all_display_data, function(data, index) { - self.dataset.read_ids( data.ids, [], function(records){ - self.all_display_data[index].records = records; - if(self.all_display_data.length == count) { - self.do_actual_search(); - } - count++; - }); - }); + self.do_actual_search(); } ); }, @@ -250,8 +305,8 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ return false; } // TODO fme: check what was this sequence - if (self.fields_view.fields.sequence && (self.source_index.index >= 0 && self.source_index.index != from) || - (self.source_index.column && self.source_index.column != ui.item.parent().attr('id'))) { + if (self.fields_view.fields.sequence != undefined && ((self.source_index.index >= 0 && self.source_index.index != from) || + (self.source_index.column && self.source_index.column != ui.item.parent().attr('id')))) { var child_record = ui.item.parent().children(); var data, sequence = 1, index = to; child_record.splice(0, to); @@ -338,9 +393,6 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ kanban_gravatar: self.kanban_gravatar })); }); - } else { - self.$element.find("#column_" + data.value).remove(); - self.all_display_data.splice(index, 1); } }); this.$element.find('.oe_kanban_action').click(this.on_action_clicked); @@ -377,8 +429,8 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ do_search: function (domains, contexts, group_by) { var self = this; this.rpc('/web/session/eval_domain_and_context', { - domains: domains, - contexts: contexts, + domains: [this.dataset.get_domain()].concat(domains), + contexts: [this.dataset.get_context()].concat(contexts), group_by_seq: group_by }, function (results) { self.domain = results.domain; @@ -392,23 +444,27 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ group_by = self.group_by; if (!group_by.length && this.fields_view.arch.attrs.default_group_by) { group_by = [this.fields_view.arch.attrs.default_group_by]; + self.group_by = group_by; } self.datagroup = new openerp.web.DataGroup(self, self.model, self.domain, self.context, group_by); - self.dataset.context = self.context; - self.dataset.domain = self.domain; - self.datagroup.list([], + self.datagroup.list(_.keys(self.fields_view.fields), function (groups) { self.groups = groups; - self.do_render_group(groups); + if (groups.length) { + self.do_render_group(groups); + } + else { + self.all_display_data = []; + self.on_show_data(); + } }, function (dataset) { - self.domain = dataset.domain; - self.context = dataset.context; self.groups = []; - self.dataset.read_slice([], {}, function(records) { - self.all_display_data = [{'records': records, 'value':false, 'header' : false, 'ids': self.dataset.ids}]; + self.dataset.read_slice([], {'domain': self.domain, 'context': self.context}, function(records) { + if (records.length) self.all_display_data = [{'records': records, 'value':false, 'header' : false, 'ids': self.dataset.ids}]; + else self.all_display_data = []; self.$element.find(".oe_kanban_view").remove(); - self.on_show_data(self.all_display_data); + self.on_show_data(); }); } ); @@ -417,8 +473,6 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ this.all_display_data = []; var self = this; _.each(datagroups, function (group) { - self.dataset.context = group.context; - self.dataset.domain = group.domain; var group_name = group.value; var group_value = group.value; if (!group.value) { @@ -428,11 +482,15 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ group_name = group.value[1]; group_value = group.value[0]; } - self.dataset.read_slice([], {}, function(records) { - self.all_display_data.push({"value" : group_value, "records": records, 'header':group_name, 'ids': self.dataset.ids}); + var group_aggregates = {}; + _.each(self.aggregates, function(value, key) { + group_aggregates[value] = group.aggregates[key]; + }); + self.dataset.read_slice([], {'domain': group.domain, 'context': group.context}, function(records) { + self.all_display_data.push({"value" : group_value, "records": records, 'header':group_name, 'ids': self.dataset.ids, 'aggregates': group_aggregates}); if (datagroups.length == self.all_display_data.length) { self.$element.find(".oe_kanban_view").remove(); - self.on_show_data(self.all_display_data); + self.on_show_data(); } }); }); diff --git a/addons/web_kanban/static/src/xml/web_kanban.xml b/addons/web_kanban/static/src/xml/web_kanban.xml index aa7bc73190d..4c98f42f8fe 100644 --- a/addons/web_kanban/static/src/xml/web_kanban.xml +++ b/addons/web_kanban/static/src/xml/web_kanban.xml @@ -9,15 +9,20 @@ - - + + +
+ +
+
+ : +
- -
-
-
+ +

+
diff --git a/openerp-web.py b/openerp-web.py index e5b28c83e0a..928a62e4528 100755 --- a/openerp-web.py +++ b/openerp-web.py @@ -24,7 +24,7 @@ optparser.add_option("--server-port", dest="server_port", default=8069, help="OpenERP server port", type="int", metavar="NUMBER") optparser.add_option("--db-filter", dest="dbfilter", default='.*', help="Filter listed database", metavar="REGEXP") -optparser.add_option('--addons-path', dest='addons_path', default=path_addons, +optparser.add_option('--addons-path', dest='addons_path', default=[path_addons], action='append', help="Path do addons directory", metavar="PATH") server_options = optparse.OptionGroup(optparser, "Server configuration")