diff options
| author | Jiten (OpenERP) <jra@tinyerp.com> | 2011-09-13 16:47:54 +0530 |
|---|---|---|
| committer | Jiten (OpenERP) <jra@tinyerp.com> | 2011-09-13 16:47:54 +0530 |
| commit | 47cdc8d62d3627d3d03e5d0963a5104e7dff9d7e (patch) | |
| tree | 6b6a28e30a036024c18b35e25a75aaa11faf1fa5 /addons | |
| parent | 655d615f52fabf821d354f0399a792ac595de88e (diff) | |
| parent | a6b981eca52e04678b74acedeba21c2f4c2c5155 (diff) | |
[MERGE] Merge from openerp-web.
bzr revid: jra@tinyerp.com-20110913111754-zk57t5dqc22s98vm
Diffstat (limited to 'addons')
95 files changed, 901 insertions, 280 deletions
diff --git a/addons/web/__init__.py b/addons/web/__init__.py index 343b53d050d..ec702292ce8 100644 --- a/addons/web/__init__.py +++ b/addons/web/__init__.py @@ -1,5 +1,31 @@ import common import controllers +import common.dispatch +import logging + +_logger = logging.getLogger(__name__) + +try: + import openerp.wsgi + import os + import tempfile + _logger.info("embedded mode") + class Options(object): + pass + o = Options() + o.dbfilter = '.*' + o.session_storage = os.path.join(tempfile.gettempdir(), "oe-sessions") + o.addons_path = os.path.dirname(os.path.dirname(__file__)) + o.serve_static = True + o.server_host = '127.0.0.1' + o.server_port = 8069 + + app = common.dispatch.Root(o) + #import openerp.wsgi + openerp.wsgi.register_wsgi_handler(app) + +except ImportError: + _logger.info("standalone mode") # TODO # if we detect that we are imported from the openerp server register common.Root() as a wsgi entry point diff --git a/addons/web/__openerp__.py b/addons/web/__openerp__.py index 290bca17464..dd4d2071f00 100644 --- a/addons/web/__openerp__.py +++ b/addons/web/__openerp__.py @@ -1,6 +1,5 @@ { "name" : "web", - "version" : "2.0", "depends" : [], 'active': True, 'js' : [ @@ -46,4 +45,5 @@ "static/src/css/base.css", "static/src/css/data_export.css", ], + 'wsgi' : 'app', } diff --git a/addons/web/common/dispatch.py b/addons/web/common/dispatch.py index 0f2b7fd2293..2336ca2d426 100644 --- a/addons/web/common/dispatch.py +++ b/addons/web/common/dispatch.py @@ -4,6 +4,7 @@ from __future__ import with_statement import functools import logging import os +import pprint import sys import traceback import uuid @@ -26,6 +27,8 @@ import backendrpc as backend __all__ = ['Root', 'jsonrequest', 'httprequest', 'Controller', 'WebRequest', 'JsonRequest', 'HttpRequest'] +_logger = logging.getLogger(__name__) + #----------------------------------------------------------- # Globals (wont move into a pool) #----------------------------------------------------------- @@ -159,8 +162,8 @@ class JsonRequest(WebRequest): else: self.jsonrequest = simplejson.loads(request, object_hook=nonliterals.non_literal_decoder) self.init(self.jsonrequest.get("params", {})) - if self.debug or 1: - print "--> %s.%s %s" % (controller.__class__.__name__, method.__name__, self.jsonrequest) + if _logger.isEnabledFor(logging.DEBUG): + _logger.debug("--> %s.%s\n%s", controller.__class__.__name__, method.__name__, pprint.pformat(self.jsonrequest)) response['id'] = self.jsonrequest.get('id') response["result"] = method(controller, self, **self.params) except backend.OpenERPUnboundException: @@ -184,7 +187,7 @@ class JsonRequest(WebRequest): } } except Exception: - logging.getLogger('openerp.JSONRequest.dispatch').exception\ + logging.getLogger(__name__ + '.JSONRequest.dispatch').exception\ ("An error occured while handling a json request") error = { 'code': 300, @@ -197,10 +200,8 @@ class JsonRequest(WebRequest): if error: response["error"] = error - if self.debug or 1: - print "<--", response - print - + if _logger.isEnabledFor(logging.DEBUG): + _logger.debug("<--\n%s", pprint.pformat(response)) content = simplejson.dumps(response, cls=nonliterals.NonLiteralEncoder) return werkzeug.wrappers.Response( content, headers=[('Content-Type', 'application/json'), @@ -237,15 +238,13 @@ class HttpRequest(WebRequest): akw[key] = value else: akw[key] = type(value) - if self.debug or 1: - print "%s --> %s.%s %r" % (self.httprequest.method, controller.__class__.__name__, method.__name__, akw) + _logger.debug("%s --> %s.%s %r", self.httprequest.method, controller.__class__.__name__, method.__name__, akw) r = method(controller, self, **self.params) if self.debug or 1: if isinstance(r, werkzeug.wrappers.BaseResponse): - print '<--', r + _logger.debug('<-- %s', r) else: - print "<--", 'size:', len(r) - print + _logger.debug("<-- size: %s", len(r)) return r def make_response(self, data, headers=None, cookies=None): @@ -350,7 +349,7 @@ class Root(object): if request.path == '/': return werkzeug.utils.redirect( - self.root(request.args), 301)( + self.root(dict(request.args, debug='')), 301)( environ, start_response) elif request.path == '/mobile': return werkzeug.utils.redirect( @@ -391,7 +390,7 @@ class Root(object): manifest_path = os.path.join(addons_path, module, '__openerp__.py') if os.path.isfile(manifest_path): manifest = ast.literal_eval(open(manifest_path).read()) - print "Loading", module + _logger.info("Loading %s", module) m = __import__(module) addons_module[module] = m addons_manifest[module] = manifest @@ -424,6 +423,6 @@ class Root(object): meth = rest[0] m = getattr(c, meth) if getattr(m, 'exposed', False): - print "Dispatching to", ps, c, meth, m + _logger.debug("Dispatching to %s %s %s", ps, c, meth) return m return None diff --git a/addons/web/common/release.py b/addons/web/common/release.py new file mode 100644 index 00000000000..47570c032bb --- /dev/null +++ b/addons/web/common/release.py @@ -0,0 +1,10 @@ +name = 'openerp-web' +version = '6.1.0 alpha' +description = "Web Client of OpenERP, the Enterprise Management Software" +long_description = "OpenERP Web is the web client of the OpenERP, a free enterprise management software" +author = "OpenERP SA" +author_email = "info@openerp.com" +support_email = 'support@openerp.com' +url = "http://www.openerp.com/" +download_url = '' +license = "AGPL" diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index 3a67a44f710..c85072d625b 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -12,17 +12,18 @@ import textwrap import xmlrpclib import time import zlib -import webrelease from xml.etree import ElementTree from cStringIO import StringIO +from babel.messages.pofile import read_po + import web.common.dispatch as openerpweb import web.common.ast import web.common.nonliterals +import web.common.release openerpweb.ast = web.common.ast openerpweb.nonliterals = web.common.nonliterals -from babel.messages.pofile import read_po # Should move to openerpweb.Xml2Json class Xml2Json: @@ -195,7 +196,7 @@ class WebClient(openerpweb.Controller): @openerpweb.jsonrequest def version_info(self, req): return { - "version": webrelease.version + "version": web.common.release.version } class Database(openerpweb.Controller): @@ -451,8 +452,6 @@ def load_actions_from_ir_values(req, key, key2, models, meta): def clean_action(req, action): action.setdefault('flags', {}) - if action['type'] != 'ir.actions.act_window': - return action context = req.session.eval_context(req.context) eval_ctx = req.session.evaluation_context(context) @@ -464,7 +463,9 @@ def clean_action(req, action): if isinstance(action.get('domain'), basestring): action['domain'] = eval( action['domain'], eval_ctx ) or [] - return fix_view_modes(action) + if action['type'] == 'ir.actions.act_window': + return fix_view_modes(action) + return action # I think generate_views,fix_view_modes should go into js ActionManager def generate_views(action): @@ -762,6 +763,8 @@ class View(openerpweb.Controller): fvg = Model.fields_view_get(view_id, view_type, context, toolbar, submenu) # todo fme?: check that we should pass the evaluated context here self.process_view(req.session, fvg, context, transform) + if toolbar and transform: + self.process_toolbar(req, fvg['toolbar']) return fvg def process_view(self, session, fvg, context, transform): @@ -793,6 +796,22 @@ class View(openerpweb.Controller): if field.get('context'): field["context"] = self.parse_context(field["context"], session) + def process_toolbar(self, req, toolbar): + """ + The toolbar is a mapping of section_key: [action_descriptor] + + We need to clean all those actions in order to ensure correct + round-tripping + """ + for actions in toolbar.itervalues(): + for action in actions: + if 'context' in action: + action['context'] = self.parse_context( + action['context'], req.session) + if 'domain' in action: + action['domain'] = self.parse_domain( + action['domain'], req.session) + @openerpweb.jsonrequest def add_custom(self, req, view_id, arch): CustomView = req.session.model('ir.ui.view.custom') @@ -1060,8 +1079,11 @@ class Action(openerpweb.Controller): context = req.session.eval_context(req.context) action_type = Actions.read([action_id], ['type'], context) if action_type: - action = req.session.model(action_type[0]['type']).read([action_id], False, - context) + ctx = {} + if action_type[0]['type'] == 'ir.actions.report.xml': + ctx.update({'bin_size': True}) + ctx.update(context) + action = req.session.model(action_type[0]['type']).read([action_id], False, ctx) if action: value = clean_action(req, action[0]) return {'result': value} @@ -1341,13 +1363,22 @@ class Reports(View): context = req.session.eval_context( openerpweb.nonliterals.CompoundContext( req.context or {}, action[ "context"])) + + report_data = {} + report_ids = context["active_ids"] + if 'report_type' in action: + report_data['report_type'] = action['report_type'] + if 'datas' in action: + if 'form' in action['datas']: + report_data['form'] = action['datas']['form'] + if 'ids' in action['datas']: + report_ids = action['datas']['ids'] + report_id = report_srv.report( req.session._db, req.session._uid, req.session._password, - action["report_name"], context["active_ids"], - {"id": context["active_id"], - "model": context["active_model"], - "report_type": action["report_type"]}, - context) + action["report_name"], report_ids, + report_data, context) + report_struct = None while True: report_struct = report_srv.report_get( @@ -1363,7 +1394,7 @@ class Reports(View): report_struct['format'], 'octet-stream') return req.make_response(report, headers=[ - ('Content-Disposition', 'attachment; filename="%s.%s"' % (action['report_name'], action['report_type'])), + ('Content-Disposition', 'attachment; filename="%s.%s"' % (action['report_name'], report_struct['format'])), ('Content-Type', report_mimetype), ('Content-Length', len(report))], cookies={'fileToken': int(token)}) diff --git a/addons/web/static/lib/qweb/qweb2.js b/addons/web/static/lib/qweb/qweb2.js index 473e80d1229..621ea39c74f 100644 --- a/addons/web/static/lib/qweb/qweb2.js +++ b/addons/web/static/lib/qweb/qweb2.js @@ -259,6 +259,9 @@ QWeb2.Engine = (function() { if (req) { // TODO: third parameter is async : https://developer.mozilla.org/en/XMLHttpRequest#open() // do an on_ready in QWeb2{} that could be passed to add_template + if (this.debug) { + s += '?debug=' + (new Date()).getTime(); // TODO fme: do it properly in case there's already url parameters + } req.open('GET', s, false); req.send(null); if (req.responseXML) { @@ -325,13 +328,13 @@ QWeb2.Engine = (function() { render : function(template, dict) { var ndict = QWeb2.tools.extend({}, this.default_dict); QWeb2.tools.extend(ndict, dict); - if (this.debug && window['console'] !== undefined) { + /*if (this.debug && window['console'] !== undefined) { console.time("QWeb render template " + template); - } + }*/ var r = this._render(template, ndict); - if (this.debug && window['console'] !== undefined) { + /*if (this.debug && window['console'] !== undefined) { console.timeEnd("QWeb render template " + template); - } + }*/ return r; }, _render : function(template, dict) { diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css index 3003f9402ac..cc8b596e6f5 100644 --- a/addons/web/static/src/css/base.css +++ b/addons/web/static/src/css/base.css @@ -294,6 +294,9 @@ label.error { color: #fff; } +.openerp .oe-application-container { + height: 100%; +} /* Secondary Menu */ .openerp .secondary_menu { width: 200px; @@ -1276,3 +1279,39 @@ label.error { border-left: none; padding-left: 0; } + +ul.oe-arrow-list { + padding-left: 0.5em; + margin: 0; +} +ul.oe-arrow-list li { + display: inline-block; + margin-left: -0.5em; +} +ul.oe-arrow-list li span { + vertical-align: top; + display: inline-block; + border-width:1em; + border-style:solid; + border-color: white; + line-height:0em; +} +ul.oe-arrow-list .oe-arrow-list-before { + border-left-color: rgba(0,0,0,0); + border-right-width:0; +} +ul.oe-arrow-list .oe-arrow-list-after { + border-color: rgba(0,0,0,0); + border-left-color: white; + border-right-width:0; +} +ul.oe-arrow-list li.oe-arrow-list-selected span { + border-color: #CFCCCC; +} +ul.oe-arrow-list li.oe-arrow-list-selected .oe-arrow-list-before { + border-left-color: rgba(0,0,0,0); +} +ul.oe-arrow-list li.oe-arrow-list-selected .oe-arrow-list-after { + border-color: rgba(0,0,0,0); + border-left-color: #CFCCCC; +} diff --git a/addons/web/static/src/img/icons/color-picker.png b/addons/web/static/src/img/icons/color-picker.png Binary files differnew file mode 100644 index 00000000000..765294c6bdd --- /dev/null +++ b/addons/web/static/src/img/icons/color-picker.png diff --git a/addons/web/static/src/img/icons/gtk-about.png b/addons/web/static/src/img/icons/gtk-about.png Binary files differnew file mode 100644 index 00000000000..1b430a35758 --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-about.png diff --git a/addons/web/static/src/img/icons/gtk-bold.png b/addons/web/static/src/img/icons/gtk-bold.png Binary files differnew file mode 100644 index 00000000000..408f25aa20b --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-bold.png diff --git a/addons/web/static/src/img/icons/gtk-cdrom.png b/addons/web/static/src/img/icons/gtk-cdrom.png Binary files differnew file mode 100644 index 00000000000..27e8edd8a0b --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-cdrom.png diff --git a/addons/web/static/src/img/icons/gtk-clear.png b/addons/web/static/src/img/icons/gtk-clear.png Binary files differnew file mode 100644 index 00000000000..b2874fb84fa --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-clear.png diff --git a/addons/web/static/src/img/icons/gtk-color-picker.png b/addons/web/static/src/img/icons/gtk-color-picker.png Binary files differnew file mode 100644 index 00000000000..232d54f8f3b --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-color-picker.png diff --git a/addons/web/static/src/img/icons/gtk-connect.png b/addons/web/static/src/img/icons/gtk-connect.png Binary files differnew file mode 100644 index 00000000000..c820b54abcc --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-connect.png diff --git a/addons/web/static/src/img/icons/gtk-copy.png b/addons/web/static/src/img/icons/gtk-copy.png Binary files differnew file mode 100644 index 00000000000..3d68da606d2 --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-copy.png diff --git a/addons/web/static/src/img/icons/gtk-cut.png b/addons/web/static/src/img/icons/gtk-cut.png Binary files differnew file mode 100644 index 00000000000..96681d26968 --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-cut.png diff --git a/addons/web/static/src/img/icons/gtk-dialog-authentication.png b/addons/web/static/src/img/icons/gtk-dialog-authentication.png Binary files differnew file mode 100644 index 00000000000..eb411112f83 --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-dialog-authentication.png diff --git a/addons/web/static/src/img/icons/gtk-dialog-error.png b/addons/web/static/src/img/icons/gtk-dialog-error.png Binary files differnew file mode 100644 index 00000000000..e97bc43504b --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-dialog-error.png diff --git a/addons/web/static/src/img/icons/gtk-dialog-info.png b/addons/web/static/src/img/icons/gtk-dialog-info.png Binary files differnew file mode 100644 index 00000000000..ed46c3ecdb3 --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-dialog-info.png diff --git a/addons/web/static/src/img/icons/gtk-dialog-question.png b/addons/web/static/src/img/icons/gtk-dialog-question.png Binary files differnew file mode 100644 index 00000000000..ef68f43f43f --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-dialog-question.png diff --git a/addons/web/static/src/img/icons/gtk-dialog-warning.png b/addons/web/static/src/img/icons/gtk-dialog-warning.png Binary files differnew file mode 100644 index 00000000000..65a4d2c7220 --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-dialog-warning.png diff --git a/addons/web/static/src/img/icons/gtk-directory-remote.png b/addons/web/static/src/img/icons/gtk-directory-remote.png Binary files differnew file mode 100644 index 00000000000..34e737d7c07 --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-directory-remote.png diff --git a/addons/web/static/src/img/icons/gtk-directory.png b/addons/web/static/src/img/icons/gtk-directory.png Binary files differnew file mode 100644 index 00000000000..8e41b7878a9 --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-directory.png diff --git a/addons/web/static/src/img/icons/gtk-disconnect.png b/addons/web/static/src/img/icons/gtk-disconnect.png Binary files differnew file mode 100644 index 00000000000..fd188701fc2 --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-disconnect.png diff --git a/addons/web/static/src/img/icons/gtk-dnd-multiple.png b/addons/web/static/src/img/icons/gtk-dnd-multiple.png Binary files differnew file mode 100644 index 00000000000..aa4c65ad5b1 --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-dnd-multiple.png diff --git a/addons/web/static/src/img/icons/gtk-file.png b/addons/web/static/src/img/icons/gtk-file.png Binary files differnew file mode 100644 index 00000000000..fa157156bcc --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-file.png diff --git a/addons/web/static/src/img/icons/gtk-find-and-replace.png b/addons/web/static/src/img/icons/gtk-find-and-replace.png Binary files differnew file mode 100644 index 00000000000..b8d8eeec1b2 --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-find-and-replace.png diff --git a/addons/web/static/src/img/icons/gtk-floppy.png b/addons/web/static/src/img/icons/gtk-floppy.png Binary files differnew file mode 100644 index 00000000000..7d8209cfb6f --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-floppy.png diff --git a/addons/web/static/src/img/icons/gtk-fullscreen.png b/addons/web/static/src/img/icons/gtk-fullscreen.png Binary files differnew file mode 100644 index 00000000000..8ddc31e7481 --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-fullscreen.png diff --git a/addons/web/static/src/img/icons/gtk-goto-bottom.png b/addons/web/static/src/img/icons/gtk-goto-bottom.png Binary files differnew file mode 100644 index 00000000000..be52840dee1 --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-goto-bottom.png diff --git a/addons/web/static/src/img/icons/gtk-goto-first.png b/addons/web/static/src/img/icons/gtk-goto-first.png Binary files differnew file mode 100644 index 00000000000..3d00e36d696 --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-goto-first.png diff --git a/addons/web/static/src/img/icons/gtk-goto-top.png b/addons/web/static/src/img/icons/gtk-goto-top.png Binary files differnew file mode 100644 index 00000000000..227a91e5157 --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-goto-top.png diff --git a/addons/web/static/src/img/icons/gtk-harddisk.png b/addons/web/static/src/img/icons/gtk-harddisk.png Binary files differnew file mode 100644 index 00000000000..73e5ef0a82e --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-harddisk.png diff --git a/addons/web/static/src/img/icons/gtk-help.png b/addons/web/static/src/img/icons/gtk-help.png Binary files differnew file mode 100644 index 00000000000..f8e17009dd6 --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-help.png diff --git a/addons/web/static/src/img/icons/gtk-home.png b/addons/web/static/src/img/icons/gtk-home.png Binary files differnew file mode 100644 index 00000000000..9d3d50d0fcb --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-home.png diff --git a/addons/web/static/src/img/icons/gtk-info.png b/addons/web/static/src/img/icons/gtk-info.png Binary files differnew file mode 100644 index 00000000000..272019f5c99 --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-info.png diff --git a/addons/web/static/src/img/icons/gtk-italic.png b/addons/web/static/src/img/icons/gtk-italic.png Binary files differnew file mode 100644 index 00000000000..26c57226a31 --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-italic.png diff --git a/addons/web/static/src/img/icons/gtk-justify-center.png b/addons/web/static/src/img/icons/gtk-justify-center.png Binary files differnew file mode 100644 index 00000000000..d5f514298eb --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-justify-center.png diff --git a/addons/web/static/src/img/icons/gtk-justify-left.png b/addons/web/static/src/img/icons/gtk-justify-left.png Binary files differnew file mode 100644 index 00000000000..ecb76b117eb --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-justify-left.png diff --git a/addons/web/static/src/img/icons/gtk-justify-right.png b/addons/web/static/src/img/icons/gtk-justify-right.png Binary files differnew file mode 100644 index 00000000000..3cd4d161420 --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-justify-right.png diff --git a/addons/web/static/src/img/icons/gtk-leave-fullscreen.png b/addons/web/static/src/img/icons/gtk-leave-fullscreen.png Binary files differnew file mode 100644 index 00000000000..15bc8ab7d7b --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-leave-fullscreen.png diff --git a/addons/web/static/src/img/icons/gtk-media-forward.png b/addons/web/static/src/img/icons/gtk-media-forward.png Binary files differnew file mode 100644 index 00000000000..e5e198fbb79 --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-media-forward.png diff --git a/addons/web/static/src/img/icons/gtk-media-next.png b/addons/web/static/src/img/icons/gtk-media-next.png Binary files differnew file mode 100644 index 00000000000..e0aa2431cf0 --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-media-next.png diff --git a/addons/web/static/src/img/icons/gtk-media-previous.png b/addons/web/static/src/img/icons/gtk-media-previous.png Binary files differnew file mode 100644 index 00000000000..756899e0bc1 --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-media-previous.png diff --git a/addons/web/static/src/img/icons/gtk-media-record.png b/addons/web/static/src/img/icons/gtk-media-record.png Binary files differnew file mode 100644 index 00000000000..904974c81e5 --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-media-record.png diff --git a/addons/web/static/src/img/icons/gtk-media-rewind.png b/addons/web/static/src/img/icons/gtk-media-rewind.png Binary files differnew file mode 100644 index 00000000000..9c22d9e2778 --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-media-rewind.png diff --git a/addons/web/static/src/img/icons/gtk-media-stop.png b/addons/web/static/src/img/icons/gtk-media-stop.png Binary files differnew file mode 100644 index 00000000000..128cc2313ec --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-media-stop.png diff --git a/addons/web/static/src/img/icons/gtk-missing-image.png b/addons/web/static/src/img/icons/gtk-missing-image.png Binary files differnew file mode 100644 index 00000000000..cc69ed33e4b --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-missing-image.png diff --git a/addons/web/static/src/img/icons/gtk-paste-v.png b/addons/web/static/src/img/icons/gtk-paste-v.png Binary files differnew file mode 100644 index 00000000000..76ec896f70e --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-paste-v.png diff --git a/addons/web/static/src/img/icons/gtk-paste.png b/addons/web/static/src/img/icons/gtk-paste.png Binary files differnew file mode 100644 index 00000000000..840f26c8b38 --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-paste.png diff --git a/addons/web/static/src/img/icons/gtk-preferences.png b/addons/web/static/src/img/icons/gtk-preferences.png Binary files differnew file mode 100644 index 00000000000..9ba17a0ab20 --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-preferences.png diff --git a/addons/web/static/src/img/icons/gtk-print-preview.png b/addons/web/static/src/img/icons/gtk-print-preview.png Binary files differnew file mode 100644 index 00000000000..ab15fcbd307 --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-print-preview.png diff --git a/addons/web/static/src/img/icons/gtk-properties.png b/addons/web/static/src/img/icons/gtk-properties.png Binary files differnew file mode 100644 index 00000000000..894415ec1ce --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-properties.png diff --git a/addons/web/static/src/img/icons/gtk-quit.png b/addons/web/static/src/img/icons/gtk-quit.png Binary files differnew file mode 100644 index 00000000000..50b5144660b --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-quit.png diff --git a/addons/web/static/src/img/icons/gtk-refresh.png b/addons/web/static/src/img/icons/gtk-refresh.png Binary files differnew file mode 100644 index 00000000000..7474728b4c0 --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-refresh.png diff --git a/addons/web/static/src/img/icons/gtk-revert-to-saved.png b/addons/web/static/src/img/icons/gtk-revert-to-saved.png Binary files differnew file mode 100644 index 00000000000..eaf54584edf --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-revert-to-saved.png diff --git a/addons/web/static/src/img/icons/gtk-save-as.png b/addons/web/static/src/img/icons/gtk-save-as.png Binary files differnew file mode 100644 index 00000000000..1831e4e0a4b --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-save-as.png diff --git a/addons/web/static/src/img/icons/gtk-select-color.png b/addons/web/static/src/img/icons/gtk-select-color.png Binary files differnew file mode 100644 index 00000000000..12b015df107 --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-select-color.png diff --git a/addons/web/static/src/img/icons/gtk-select-font.png b/addons/web/static/src/img/icons/gtk-select-font.png Binary files differnew file mode 100644 index 00000000000..66e606313a5 --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-select-font.png diff --git a/addons/web/static/src/img/icons/gtk-sort-ascending.png b/addons/web/static/src/img/icons/gtk-sort-ascending.png Binary files differnew file mode 100644 index 00000000000..a648fa0d359 --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-sort-ascending.png diff --git a/addons/web/static/src/img/icons/gtk-spell-check.png b/addons/web/static/src/img/icons/gtk-spell-check.png Binary files differnew file mode 100644 index 00000000000..698f7c24a43 --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-spell-check.png diff --git a/addons/web/static/src/img/icons/gtk-strikethrough.png b/addons/web/static/src/img/icons/gtk-strikethrough.png Binary files differnew file mode 100644 index 00000000000..1c11f214622 --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-strikethrough.png diff --git a/addons/web/static/src/img/icons/gtk-undelete.png b/addons/web/static/src/img/icons/gtk-undelete.png Binary files differnew file mode 100644 index 00000000000..c44b940c03b --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-undelete.png diff --git a/addons/web/static/src/img/icons/gtk-underline.png b/addons/web/static/src/img/icons/gtk-underline.png Binary files differnew file mode 100644 index 00000000000..7dbb5d7e60a --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-underline.png diff --git a/addons/web/static/src/img/icons/gtk-unindent.png b/addons/web/static/src/img/icons/gtk-unindent.png Binary files differnew file mode 100644 index 00000000000..8ace914c1bd --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-unindent.png diff --git a/addons/web/static/src/img/icons/gtk-zoom-100.png b/addons/web/static/src/img/icons/gtk-zoom-100.png Binary files differnew file mode 100644 index 00000000000..5193406b787 --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-zoom-100.png diff --git a/addons/web/static/src/img/icons/gtk-zoom-out.png b/addons/web/static/src/img/icons/gtk-zoom-out.png Binary files differnew file mode 100644 index 00000000000..0da5ee7ec13 --- /dev/null +++ b/addons/web/static/src/img/icons/gtk-zoom-out.png diff --git a/addons/web/static/src/img/icons/lead-stage-lost.png b/addons/web/static/src/img/icons/lead-stage-lost.png Binary files differnew file mode 100644 index 00000000000..e1f98a26b59 --- /dev/null +++ b/addons/web/static/src/img/icons/lead-stage-lost.png diff --git a/addons/web/static/src/img/icons/lead-stage-pending.png b/addons/web/static/src/img/icons/lead-stage-pending.png Binary files differnew file mode 100644 index 00000000000..9e9544ee178 --- /dev/null +++ b/addons/web/static/src/img/icons/lead-stage-pending.png diff --git a/addons/web/static/src/img/icons/lead-stage-won.png b/addons/web/static/src/img/icons/lead-stage-won.png Binary files differnew file mode 100644 index 00000000000..02effd5f14c --- /dev/null +++ b/addons/web/static/src/img/icons/lead-stage-won.png diff --git a/addons/web/static/src/img/icons/star-off.png b/addons/web/static/src/img/icons/star-off.png Binary files differnew file mode 100644 index 00000000000..7cd0afd4f03 --- /dev/null +++ b/addons/web/static/src/img/icons/star-off.png diff --git a/addons/web/static/src/img/icons/star-on.png b/addons/web/static/src/img/icons/star-on.png Binary files differnew file mode 100644 index 00000000000..4f2295e1395 --- /dev/null +++ b/addons/web/static/src/img/icons/star-on.png diff --git a/addons/web/static/src/img/icons/stock.png b/addons/web/static/src/img/icons/stock.png Binary files differnew file mode 100644 index 00000000000..ca38181db90 --- /dev/null +++ b/addons/web/static/src/img/icons/stock.png diff --git a/addons/web/static/src/img/icons/stock_calendar.png b/addons/web/static/src/img/icons/stock_calendar.png Binary files differnew file mode 100644 index 00000000000..99a1f3f9ef1 --- /dev/null +++ b/addons/web/static/src/img/icons/stock_calendar.png diff --git a/addons/web/static/src/img/icons/stock_gantt.png b/addons/web/static/src/img/icons/stock_gantt.png Binary files differnew file mode 100644 index 00000000000..585761b9220 --- /dev/null +++ b/addons/web/static/src/img/icons/stock_gantt.png diff --git a/addons/web/static/src/img/icons/stock_graph.png b/addons/web/static/src/img/icons/stock_graph.png Binary files differnew file mode 100644 index 00000000000..4d8310621ea --- /dev/null +++ b/addons/web/static/src/img/icons/stock_graph.png diff --git a/addons/web/static/src/img/icons/stock_log.png b/addons/web/static/src/img/icons/stock_log.png Binary files differnew file mode 100644 index 00000000000..32af4dd6901 --- /dev/null +++ b/addons/web/static/src/img/icons/stock_log.png diff --git a/addons/web/static/src/img/icons/stock_person.png b/addons/web/static/src/img/icons/stock_person.png Binary files differnew file mode 100644 index 00000000000..202cf0ee6c5 --- /dev/null +++ b/addons/web/static/src/img/icons/stock_person.png diff --git a/addons/web/static/src/img/icons/stock_translate.png b/addons/web/static/src/img/icons/stock_translate.png Binary files differnew file mode 100644 index 00000000000..5ab41d49343 --- /dev/null +++ b/addons/web/static/src/img/icons/stock_translate.png diff --git a/addons/web/static/src/js/boot.js b/addons/web/static/src/js/boot.js index 2c00eb3d5a5..b9d5180b5c4 100644 --- a/addons/web/static/src/js/boot.js +++ b/addons/web/static/src/js/boot.js @@ -5,7 +5,6 @@ /** * @name openerp * @namespace openerp - * @namespace window.openerp */ (function() { if (this.openerp) @@ -48,6 +47,9 @@ * OpenERP Web web module split *---------------------------------------------------------*/ +/** + * @namespace + */ openerp.web = function(instance) { openerp.web.core(instance); if (openerp.web.dates) { diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js index d58dadac210..c12d157a668 100644 --- a/addons/web/static/src/js/chrome.js +++ b/addons/web/static/src/js/chrome.js @@ -5,7 +5,14 @@ openerp.web.chrome = function(openerp) { var QWeb = openerp.web.qweb; -openerp.web.Notification = openerp.web.Widget.extend({ +openerp.web.Notification = openerp.web.Widget.extend(/** @lends openerp.web.Notification# */{ + /** + * @constructs openerp.web.Notification + * @extends openerp.web.Widget + * + * @param parent + * @param element_id + */ init: function(parent, element_id) { this._super(parent, element_id); this.$element.notify({ @@ -27,9 +34,16 @@ openerp.web.Notification = openerp.web.Widget.extend({ } }); -openerp.web.Dialog = openerp.web.OldWidget.extend({ +openerp.web.Dialog = openerp.web.OldWidget.extend(/** @lends openerp.web.Dialog# */{ dialog_title: "", identifier_prefix: 'dialog', + /** + * @constructs openerp.web.Dialog + * @extends openerp.web.OldWidget + * + * @param parent + * @param dialog_options + */ init: function (parent, dialog_options) { var self = this; this._super(parent); @@ -118,8 +132,14 @@ openerp.web.Dialog = openerp.web.OldWidget.extend({ } }); -openerp.web.CrashManager = openerp.web.Dialog.extend({ +openerp.web.CrashManager = openerp.web.Dialog.extend(/** @lends openerp.web.CrashManager# */{ identifier_prefix: 'dialog_crash', + /** + * @constructs opener.web.CrashManager + * @extends openerp.web.Dialog + * + * @param parent + */ init: function(parent) { this._super(parent); this.session.on_rpc_error.add(this.on_rpc_error); @@ -130,7 +150,7 @@ openerp.web.CrashManager = openerp.web.Dialog.extend({ on_rpc_error: function(error) { this.error = error; if (error.data.fault_code) { - var split = error.data.fault_code.split('\n')[0].split(' -- '); + var split = ("" + error.data.fault_code).split('\n')[0].split(' -- '); if (split.length > 1) { error.type = split.shift(); error.data.fault_code = error.data.fault_code.substr(error.type.length + 4); @@ -154,7 +174,14 @@ openerp.web.CrashManager = openerp.web.Dialog.extend({ } }); -openerp.web.Loading = openerp.web.Widget.extend({ +openerp.web.Loading = openerp.web.Widget.extend(/** @lends openerp.web.Loading# */{ + /** + * @constructs openerp.web.Loading + * @extends openerp.web.Widget + * + * @param parent + * @param element_id + */ init: function(parent, element_id) { this._super(parent, element_id); this.count = 0; @@ -173,7 +200,15 @@ openerp.web.Loading = openerp.web.Widget.extend({ } }); -openerp.web.Database = openerp.web.Widget.extend({ +openerp.web.Database = openerp.web.Widget.extend(/** @lends openerp.web.Database# */{ + /** + * @constructs openerp.web.Database + * @extends openerp.web.Widget + * + * @param parent + * @param element_id + * @param option_id + */ init: function(parent, element_id, option_id) { this._super(parent, element_id); this.$option_id = $('#' + option_id); @@ -427,9 +462,15 @@ openerp.web.Database = openerp.web.Widget.extend({ } }); -openerp.web.Login = openerp.web.Widget.extend({ +openerp.web.Login = openerp.web.Widget.extend(/** @lends openerp.web.Login# */{ remember_creditentials: true, - + /** + * @constructs openerp.web.Login + * @extends openerp.web.Widget + * + * @param parent + * @param element_id + */ init: function(parent, element_id) { this._super(parent, element_id); this.has_local_storage = typeof(localStorage) != 'undefined'; @@ -527,9 +568,15 @@ openerp.web.Login = openerp.web.Widget.extend({ } }); -openerp.web.Header = openerp.web.Widget.extend({ +openerp.web.Header = openerp.web.Widget.extend(/** @lends openerp.web.Header# */{ template: "Header", identifier_prefix: 'oe-app-header-', + /** + * @constructs openerp.web.Header + * @extends openerp.web.Widget + * + * @param parent + */ init: function(parent) { this._super(parent); this.qs = "?" + jQuery.param.querystring(); @@ -540,6 +587,9 @@ openerp.web.Header = openerp.web.Widget.extend({ }, do_update: function () { var self = this; + this.$content.remove(); + if (! this.session.uid) + return; var func = new openerp.web.Model(self.session, "res.users").get_func("read"); func(self.session.uid, ["name", "company_id"]).then(function(res) { self.$content = $(QWeb.render("Header-content", {widget: self, user: res})); @@ -558,9 +608,6 @@ openerp.web.Header = openerp.web.Widget.extend({ modal: true, width: 960, title: "About"}); }); }, - do_reset: function() { - this.$content.remove(); - }, shortcut_load :function(){ var self = this, sc = self.session.shortcuts, @@ -707,11 +754,18 @@ openerp.web.Header = openerp.web.Widget.extend({ }).html(error.error); }, on_logout: function() { - this.$element.find('.oe-shortcuts ul').empty(); } }); -openerp.web.Menu = openerp.web.Widget.extend({ +openerp.web.Menu = openerp.web.Widget.extend(/** @lends openerp.web.Menu# */{ + /** + * @constructs openerp.web.Menu + * @extends openerp.web.Widget + * + * @param parent + * @param element_id + * @param secondary_menu_id + */ init: function(parent, element_id, secondary_menu_id) { this._super(parent, element_id); this.secondary_menu_id = secondary_menu_id; @@ -793,13 +847,13 @@ openerp.web.Menu = openerp.web.Widget.extend({ } }); -openerp.web.Homepage = openerp.web.Widget.extend({ -}); - -openerp.web.Preferences = openerp.web.Widget.extend({ -}); - -openerp.web.WebClient = openerp.web.Widget.extend({ +openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClient */{ + /** + * @constructs openerp.web.WebClient + * @extends openerp.web.Widget + * + * @param element_id + */ init: function(element_id) { this._super(null, element_id); openerp.webclient = this; @@ -827,7 +881,7 @@ openerp.web.WebClient = openerp.web.Widget.extend({ this.session.on_session_invalid.add(this.login.do_ask_login); this.session.on_session_valid.add_last(this.header.do_update); - this.session.on_session_invalid.add_last(this.header.do_reset); + this.session.on_session_invalid.add_last(this.header.do_update); this.session.on_session_valid.add_last(this.on_logged); this.menu = new openerp.web.Menu(this, "oe_menu", "oe_secondary_menu"); @@ -838,7 +892,7 @@ openerp.web.WebClient = openerp.web.Widget.extend({ this.session.start(); this.login.start(); this.menu.start(); - this.notification.notify("OpenERP Client", "The openerp client has been initialized."); + console.debug("The openerp client has been initialized."); }, on_logged: function() { if(this.action_manager) diff --git a/addons/web/static/src/js/core.js b/addons/web/static/src/js/core.js index ae2c4d0d657..77b48820e83 100644 --- a/addons/web/static/src/js/core.js +++ b/addons/web/static/src/js/core.js @@ -4,6 +4,7 @@ openerp.web.core = function(openerp) { openerp.web.qweb = new QWeb2.Engine(); +openerp.web.qweb.debug = (window.location.search.indexOf('?debug') !== -1); /** * John Resig Class with factory improvement */ @@ -12,11 +13,17 @@ openerp.web.qweb = new QWeb2.Engine(); fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/; // The web Class implementation (does nothing) /** + * Extended version of John Resig's Class pattern + * * @class */ openerp.web.Class = function(){}; - // Create a new Class that inherits from this class + /** + * Subclass an existing class + * + * @param {Object} prop class-level properties (class attributes and instance methods) to set on the new class + */ openerp.web.Class.extend = function(prop) { var _super = this.prototype; @@ -157,7 +164,7 @@ openerp.web.callback = function(obj, method) { * that does nothing and always return undefined). * * @param {Class} claz - * @param {dict} add Additional functions to override. + * @param {Object} add Additional functions to override. * @return {Class} */ openerp.web.generate_null_object_class = function(claz, add) { @@ -188,7 +195,7 @@ openerp.web.KeyNotFound = openerp.web.NotFound.extend( /** @lends openerp.web.Ke /** * Thrown when a key could not be found in a mapping * - * @constructs + * @constructs openerp.web.KeyNotFound * @extends openerp.web.NotFound * @param {String} key the key which could not be found */ @@ -204,7 +211,7 @@ openerp.web.ObjectNotFound = openerp.web.NotFound.extend( /** @lends openerp.web * Thrown when an object path does not designate a valid class or object * in the openerp hierarchy. * - * @constructs + * @constructs openerp.web.ObjectNotFound * @extends openerp.web.NotFound * @param {String} path the invalid object path */ @@ -228,7 +235,7 @@ openerp.web.Registry = openerp.web.Class.extend( /** @lends openerp.web.Registry * object pointed to (e.g. ``"openerp.web.Session"`` for an OpenERP * session object). * - * @constructs + * @constructs openerp.web.Registry * @param {Object} mapping a mapping of keys to object-paths */ init: function (mapping) { @@ -309,7 +316,11 @@ openerp.web.Registry = openerp.web.Class.extend( /** @lends openerp.web.Registry } }); -openerp.web.CallbackEnabled = openerp.web.Class.extend({ +openerp.web.CallbackEnabled = openerp.web.Class.extend(/** @lends openerp.web.CallbackEnabled# */{ + /** + * @constructs openerp.web.CallbackEnabled + * @extends openerp.web.Class + */ init: function() { // Transform on_* method into openerp.web.callbacks for (var name in this) { @@ -326,9 +337,11 @@ openerp.web.CallbackEnabled = openerp.web.Class.extend({ openerp.web.Session = openerp.web.CallbackEnabled.extend( /** @lends openerp.web.Session# */{ /** - * @constructs - * @param server - * @param port + * @constructs openerp.web.Session + * @extends openerp.web.CallbackEnabled + * + * @param {String} [server] JSON-RPC endpoint hostname + * @param {String} [port] JSON-RPC endpoint port */ init: function(server, port) { this._super(); @@ -375,7 +388,7 @@ openerp.web.Session = openerp.web.CallbackEnabled.extend( /** @lends openerp.web jsonrpc: "2.0", method: "call", params: params, - id:null + id: _.uniqueId('browser-client-') }).then(function () {deferred.resolve.apply(deferred, arguments);}, function(error) {deferred.reject(error, $.Event());}); return deferred.fail(function() { @@ -576,9 +589,10 @@ openerp.web.Session = openerp.web.CallbackEnabled.extend( /** @lends openerp.web }); }, do_load_css: function (files) { + var self = this; _.each(files, function (file) { $('head').append($('<link>', { - 'href': file, + 'href': file + (self.debug ? '?debug=' + (new Date().getTime()) : ''), 'rel': 'stylesheet', 'type': 'text/css' })); @@ -590,7 +604,7 @@ openerp.web.Session = openerp.web.CallbackEnabled.extend( /** @lends openerp.web var file = files.shift(); var tag = document.createElement('script'); tag.type = 'text/javascript'; - tag.src = file; + tag.src = file + (this.debug ? '?debug=' + (new Date().getTime()) : ''); tag.onload = tag.onreadystatechange = function() { if ( (tag.readyState && tag.readyState != "loaded" && tag.readyState != "complete") || tag.onload_done ) return; @@ -708,13 +722,18 @@ openerp.web.Session = openerp.web.CallbackEnabled.extend( /** @lends openerp.web } }); -/** - * Utility class that any class is allowed to extend to easy common manipulations. - * - * It provides rpc calls, callback on all methods preceded by "on_" or "do_" and a - * logging facility. - */ -openerp.web.SessionAware = openerp.web.CallbackEnabled.extend({ +openerp.web.SessionAware = openerp.web.CallbackEnabled.extend(/** @lends openerp.web.SessionAware# */{ + /** + * Utility class that any class is allowed to extend to easy common manipulations. + * + * It provides rpc calls, callback on all methods preceded by "on_" or "do_" and a + * logging facility. + * + * @constructs openerp.web.SessionAware + * @extends openerp.web.CallbackEnabled + * + * @param {openerp.web.Session} session + */ init: function(session) { this._super(); this.session = session; @@ -758,15 +777,12 @@ openerp.web.SessionAware = openerp.web.CallbackEnabled.extend({ if(body) { $('<pre></pre>').text(v).appendTo($('body')); } - if(notify && this.notification) { - this.notification.notify("Logging:",v); - } } } } }); -openerp.web.Widget = openerp.web.SessionAware.extend({ +openerp.web.Widget = openerp.web.SessionAware.extend(/** @lends openerp.web.Widget# */{ /** * The name of the QWeb template that will be used for rendering. Must be * redefined in subclasses or the default render() method can not be used. @@ -784,7 +800,9 @@ openerp.web.Widget = openerp.web.SessionAware.extend({ /** * Construct the widget and set its parent if a parent is given. * - * @constructs + * @constructs openerp.web.Widget + * @extends openerp.web.SessionAware + * * @param {openerp.web.Widget} parent Binds the current instance to the given Widget instance. * When that widget is destroyed by calling stop(), the current instance will be * destroyed too. Can be null. @@ -860,8 +878,10 @@ openerp.web.Widget = openerp.web.SessionAware.extend({ if (target instanceof openerp.web.Widget) target = target.$element; insertion(target); + this.on_inserted(this.$element, this); return this.start(); }, + on_inserted: function(element, widget) {}, /** * Renders the widget using QWeb, `this.template` must be defined. * The context given to QWeb contains the "widget" key that references `this`. @@ -941,17 +961,23 @@ openerp.web.Widget = openerp.web.SessionAware.extend({ }); /** + * @class + * @extends openerp.web.Widget * @deprecated * For retro compatibility only, the only difference with is that render() uses - * directly `this` instead of context with a "widget" key. + * directly ``this`` instead of context with a ``widget`` key. */ -openerp.web.OldWidget = openerp.web.Widget.extend({ +openerp.web.OldWidget = openerp.web.Widget.extend(/** @lends openerp.web.OldWidget# */{ render: function (additional) { return openerp.web.qweb.render(this.template, _.extend(_.extend({}, this), additional || {})); } }); -openerp.web.TranslationDataBase = openerp.web.Class.extend({ +openerp.web.TranslationDataBase = openerp.web.Class.extend(/** @lends openerp.web.TranslationDataBase# */{ + /** + * @constructs openerp.web.TranslationDataBase + * @extends openerp.web.Class + */ init: function() { this.db = {}; this.parameters = {"direction": 'ltr', diff --git a/addons/web/static/src/js/data.js b/addons/web/static/src/js/data.js index 4b4523a2723..1eb57241fa6 100644 --- a/addons/web/static/src/js/data.js +++ b/addons/web/static/src/js/data.js @@ -29,7 +29,7 @@ openerp.web.DataGroup = openerp.web.Widget.extend( /** @lends openerp.web.DataG * :js:func:`~openerp.web.DataGroup.list`, which is used to read the * content of the current grouping level. * - * @constructs + * @constructs openerp.web.DataGroup * @extends openerp.web.Widget * * @param {openerp.web.Session} session Current OpenERP session @@ -60,7 +60,7 @@ openerp.web.DataGroup = openerp.web.Widget.extend( /** @lends openerp.web.DataG openerp.web.ContainerDataGroup = openerp.web.DataGroup.extend( /** @lends openerp.web.ContainerDataGroup# */ { /** * - * @constructs + * @constructs openerp.web.ContainerDataGroup * @extends openerp.web.DataGroup * * @param session @@ -197,7 +197,7 @@ openerp.web.ContainerDataGroup = openerp.web.DataGroup.extend( /** @lends opener openerp.web.GrouplessDataGroup = openerp.web.DataGroup.extend( /** @lends openerp.web.GrouplessDataGroup# */ { /** * - * @constructs + * @constructs openerp.web.GrouplessDataGroup * @extends openerp.web.DataGroup * * @param session @@ -220,7 +220,7 @@ openerp.web.StaticDataGroup = openerp.web.GrouplessDataGroup.extend( /** @lends * A specialization of groupless data groups, relying on a single static * dataset as its records provider. * - * @constructs + * @constructs openerp.web.StaticDataGroup * @extends openerp.web.GrouplessDataGroup * @param {openep.web.DataSetStatic} dataset a static dataset backing the groups */ @@ -237,7 +237,7 @@ openerp.web.DataSet = openerp.web.Widget.extend( /** @lends openerp.web.DataSet * DateaManagement interface between views and the collection of selected * OpenERP records (represents the view's state?) * - * @constructs + * @constructs openerp.web.DataSet * @extends openerp.web.Widget * * @param {String} model the OpenERP model this dataset will manage @@ -505,9 +505,10 @@ openerp.web.DataSetStatic = openerp.web.DataSet.extend({ this.set_ids(_.without.apply(null, [this.ids].concat(ids))); } }); -openerp.web.DataSetSearch = openerp.web.DataSet.extend({ +openerp.web.DataSetSearch = openerp.web.DataSet.extend(/** @lends openerp.web.DataSetSearch */{ /** - * @constructs + * @constructs openerp.web.DataSetSearch + * @extends openerp.web.DataSet * * @param {Object} parent * @param {String} model @@ -612,11 +613,12 @@ openerp.web.BufferedDataSet = openerp.web.DataSetStatic.extend({ var cached = {id:_.uniqueId(this.virtual_id_prefix), values: data}; 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(); }, - write: function (id, data, callback) { + write: function (id, data, options, callback) { var self = this; var record = _.detect(this.to_create, function(x) {return x.id === id;}); record = record || _.detect(this.to_write, function(x) {return x.id === id;}); @@ -721,7 +723,7 @@ openerp.web.ReadOnlyDataSetSearch = openerp.web.DataSetSearch.extend({ return to_return.promise(); }, on_create: function(data) {}, - write: function (id, data, callback) { + write: function (id, data, options, callback) { this.on_write(id, data); var to_return = $.Deferred().then(callback); setTimeout(function () {to_return.resolve({"result": true});}, 0); diff --git a/addons/web/static/src/js/form.js b/addons/web/static/src/js/form.js index 73695e93ea3..d419516dc77 100644 --- a/addons/web/static/src/js/form.js +++ b/addons/web/static/src/js/form.js @@ -12,7 +12,9 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# searchable: false, template: "FormView", /** - * @constructs + * @constructs openerp.web.FormView + * @extends openerp.web.View + * * @param {openerp.web.Session} session the current openerp session * @param {String} element_id this view's root element id * @param {openerp.web.DataSet} dataset the dataset this view will work with @@ -382,9 +384,9 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# }, on_saved: function(r, success) { if (!r.result) { - this.notification.warn("Record not saved", "Problem while saving record."); + // should not happen in the server, but may happen for internal purpose } else { - this.notification.notify("Record saved", "The record #" + this.datarecord.id + " has been saved."); + console.debug(_.sprintf("The record #%s has been saved.", this.datarecord.id)); if (success) { success(r); } @@ -406,7 +408,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# */ on_created: function(r, success, prepend_on_create) { if (!r.result) { - this.notification.warn("Record not created", "Problem while creating record."); + // should not happen in the server, but may happen for internal purpose } else { this.datarecord.id = r.result; if (!prepend_on_create) { @@ -420,7 +422,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# if (this.sidebar) { this.sidebar.attachments.do_update(); } - this.notification.notify("Record created", "The record has been created with id #" + this.datarecord.id); + console.debug("The record has been created with id #" + this.datarecord.id); if (success) { success(_.extend(r, {created: true})); } @@ -428,13 +430,13 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# } }, do_search: function (domains, contexts, groupbys) { - this.notification.notify("Searching form"); + console.debug("Searching form"); }, on_action: function (action) { - this.notification.notify('Executing action ' + action); + console.debug('Executing action', action); }, do_cancel: function () { - this.notification.notify("Cancelling form"); + console.debug("Cancelling form"); }, reload: function() { if (this.dataset.index == null || this.dataset.index < 0) { @@ -456,6 +458,34 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# return id ? [id] : []; } }); +openerp.web.FormDialog = openerp.web.Dialog.extend({ + init: function(parent, options, view_id, dataset) { + this._super(parent, options); + this.dataset = dataset; + this.view_id = view_id; + return this; + }, + start: function() { + this._super(); + this.form = new openerp.web.FormView(this, this.element_id, this.dataset, this.view_id, { + sidebar: false, + pager: false + }); + this.form.start(); + this.form.on_created.add_last(this.on_form_dialog_saved); + this.form.on_saved.add_last(this.on_form_dialog_saved); + return this; + }, + load_id: function(id) { + var self = this; + return this.dataset.read_ids([id], _.keys(this.form.fields_view.fields), function(records) { + self.form.on_record_loaded(records[0]); + }); + }, + on_form_dialog_saved: function(r) { + this.close(); + } +}); /** @namespace */ openerp.web.form = {}; @@ -571,8 +601,15 @@ openerp.web.form.compute_domain = function(expr, fields) { return _.all(stack, _.identity); }; -openerp.web.form.Widget = openerp.web.Widget.extend({ +openerp.web.form.Widget = openerp.web.Widget.extend(/** @lends openerp.web.form.Widget# */{ template: 'Widget', + /** + * @constructs openerp.web.form.Widget + * @extends openerp.web.Widget + * + * @param view + * @param node + */ init: function(view, node) { this.view = view; this.node = node; @@ -849,7 +886,14 @@ openerp.web.form.WidgetLabel = openerp.web.form.Widget.extend({ } }); -openerp.web.form.Field = openerp.web.form.Widget.extend({ +openerp.web.form.Field = openerp.web.form.Widget.extend(/** @lends openerp.web.form.Field# */{ + /** + * @constructs openerp.web.form.Field + * @extends openerp.web.form.Widget + * + * @param view + * @param node + */ init: function(view, node) { this.name = node.attrs.name; this.value = undefined; @@ -1223,11 +1267,16 @@ openerp.web.form.FieldTextXml = openerp.web.form.Field.extend({ openerp.web.form.FieldSelection = openerp.web.form.Field.extend({ init: function(view, node) { + var self = this; this._super(view, node); this.template = "FieldSelection"; - this.field_index = _.map(this.field.selection, function(x, index) { - return {"ikey": "" + index, "ekey": x[0], "label": x[1]}; + this.values = this.field.selection; + _.each(this.values, function(v, i) { + if (v[0] === false && v[1] === '') { + self.values.splice(i, 1); + } }); + this.values.unshift([false, '']); }, start: function() { // Flag indicating whether we're in an event chain containing a change @@ -1257,13 +1306,14 @@ openerp.web.form.FieldSelection = openerp.web.form.Field.extend({ value = value === null ? false : value; value = value instanceof Array ? value[0] : value; this._super(value); - var option = _.detect(this.field_index, function(x) {return x.ekey === value;}); - this.$element.find('select').val(option === undefined ? '' : option.ikey); + var index = 0; + for (var i = 0, ii = this.values.length; i < ii; i++) { + if (this.values[i][0] === value) index = i; + } + this.$element.find('select')[0].selectedIndex = index; }, set_value_from_ui: function() { - var ikey = this.$element.find('select').val(); - var option = _.detect(this.field_index, function(x) {return x.ikey === ikey;}); - this.value = option === undefined ? false : option.ekey; + this.value = this.values[this.$element.find('select')[0].selectedIndex][0]; this._super(); }, update_dom: function() { @@ -1271,9 +1321,8 @@ openerp.web.form.FieldSelection = openerp.web.form.Field.extend({ this.$element.find('select').attr('disabled', this.readonly); }, validate: function() { - var ikey = this.$element.find('select').val(); - var option = _.detect(this.field_index, function(x) {return x.ikey === ikey;}); - this.invalid = this.required && (option === undefined || option.ekey === false); + var value = this.values[this.$element.find('select')[0].selectedIndex]; + this.invalid = !(value && !(this.required && value[0] === false)); }, focus: function() { this.$element.find('select').focus(); @@ -1819,7 +1868,7 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({ this.viewmanager.views[this.viewmanager.active_view].controller) { var view = this.viewmanager.views[this.viewmanager.active_view].controller; if (this.viewmanager.active_view === "form") { - var res = view.do_save(); + var res = $.when(view.do_save()); if (res === false) { // ignore } else if (res.isRejected()) { @@ -1971,7 +2020,11 @@ openerp.web.form.Many2ManyDataSet = openerp.web.DataSetStatic.extend({ } }); -openerp.web.form.Many2ManyListView = openerp.web.ListView.extend({ +/** + * @class + * @extends openerp.web.ListView + */ +openerp.web.form.Many2ManyListView = openerp.web.ListView.extend(/** @lends openerp.web.form.Many2ManyListView# */{ do_add_record: function () { var pop = new openerp.web.form.SelectCreatePopup(this); pop.select_element(this.model, {}, @@ -1998,7 +2051,11 @@ openerp.web.form.Many2ManyListView = openerp.web.ListView.extend({ } }); -openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend({ +/** + * @class + * @extends openerp.web.OldWidget + */ +openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends openerp.web.form.SelectCreatePopup# */{ identifier_prefix: "selectcreatepopup", template: "SelectCreatePopup", /** @@ -2167,7 +2224,11 @@ openerp.web.form.SelectCreateListView = openerp.web.ListView.extend({ } }); -openerp.web.form.FormOpenPopup = openerp.web.OldWidget.extend({ +/** + * @class + * @extends openerp.web.OldWidget + */ +openerp.web.form.FormOpenPopup = openerp.web.OldWidget.extend(/** @lends openerp.web.form.FormOpenPopup# */{ identifier_prefix: "formopenpopup", template: "FormOpenPopup", /** @@ -2351,7 +2412,7 @@ openerp.web.form.FieldBinary = openerp.web.form.Field.extend({ if (size === false) { this.notification.warn("File Upload", "There was a problem while uploading your file"); // TODO: use openerp web crashmanager - this.log("Error while uploading file : ", name); + console.warn("Error while uploading file : ", name); } else { this.on_file_uploaded_and_valid.apply(this, arguments); this.on_ui_change(); @@ -2445,6 +2506,50 @@ openerp.web.form.FieldBinaryImage = openerp.web.form.FieldBinary.extend({ } }); +openerp.web.form.FieldStatus = openerp.web.form.Field.extend({ + template: "FieldStatus", + start: function() { + this._super(); + this.selected_value = null; + + this.render_list(); + }, + set_value: function(value) { + this._super(value); + this.selected_value = value; + + this.render_list(); + }, + render_list: function() { + var self = this; + var shown = _.map(((this.node.attrs || {}).statusbar_visible || "").split(","), + function(x) { return x.trim(); }); + + if (shown.length == 0) { + this.to_show = this.field.selection; + } else { + this.to_show = _.select(this.field.selection, function(x) { + return _.indexOf(shown, x[0]) !== -1 || x[0] === self.selected_value; + }); + } + + var content = openerp.web.qweb.render("FieldStatus.content", {widget: this, _:_}); + this.$element.html(content); + + var colors = JSON.parse((this.node.attrs || {}).statusbar_colors || "{}"); + var color = colors[this.selected_value]; + if (color) { + var elem = this.$element.find("li.oe-arrow-list-selected span"); + elem.css("border-color", color); + elem = this.$element.find("li.oe-arrow-list-selected .oe-arrow-list-before"); + elem.css("border-left-color", "rgba(0,0,0,0)"); + elem = this.$element.find("li.oe-arrow-list-selected .oe-arrow-list-after"); + elem.css("border-color", "rgba(0,0,0,0)"); + elem.css("border-left-color", color); + } + } +}); + /** * Registry of form widgets, called by :js:`openerp.web.FormView` */ @@ -2474,7 +2579,8 @@ openerp.web.form.widgets = new openerp.web.Registry({ 'float_time': 'openerp.web.form.FieldFloat', 'progressbar': 'openerp.web.form.FieldProgressBar', 'image': 'openerp.web.form.FieldBinaryImage', - 'binary': 'openerp.web.form.FieldBinaryFile' + 'binary': 'openerp.web.form.FieldBinaryFile', + 'statusbar': 'openerp.web.form.FieldStatus' }); }; diff --git a/addons/web/static/src/js/list-editable.js b/addons/web/static/src/js/list-editable.js index 614c6fa42ef..c38c43a4c9c 100644 --- a/addons/web/static/src/js/list-editable.js +++ b/addons/web/static/src/js/list-editable.js @@ -1,5 +1,6 @@ /** - * @namespace handles editability case for lists, because it depends on form and forms already depends on lists it had to be split out + * handles editability case for lists, because it depends on form and forms already depends on lists it had to be split out + * @namespace */ openerp.web.list_editable = function (openerp) { var KEY_RETURN = 13, @@ -87,7 +88,7 @@ openerp.web.list_editable = function (openerp) { } }); - openerp.web.ListView.List.include(/** @lends openerp.web.ListView.List */{ + openerp.web.ListView.List.include(/** @lends openerp.web.ListView.List# */{ row_clicked: function (event) { if (!this.options.editable) { return this._super(event); @@ -112,6 +113,7 @@ openerp.web.list_editable = function (openerp) { cancelled.resolve(); } cancelled.then(function () { + self.view.unpad_columns(); self.edition_form.stop(); self.edition_form.$element.remove(); delete self.edition_form; @@ -183,7 +185,7 @@ openerp.web.list_editable = function (openerp) { template: 'ListView.row.form', registry: openerp.web.list.form.widgets }); - $.when(self.edition_form.on_loaded({fields_view: self.get_form_fields_view()})).then(function () { + $.when(self.edition_form.on_loaded(self.get_form_fields_view())).then(function () { // put in $.when just in case FormView.on_loaded becomes asynchronous $new_row.find('td') .addClass('oe-field-cell') @@ -197,6 +199,16 @@ openerp.web.list_editable = function (openerp) { $new_row.prepend('<td>'); } }); + // Add columns for the cancel and save buttons, if + // there are none in the list + if (!self.options.selectable) { + self.view.pad_columns( + 1, {except: $new_row, position: 'before'}); + } + if (!self.options.deletable) { + self.view.pad_columns( + 1, {except: $new_row}); + } self.edition_form.do_show(); }); diff --git a/addons/web/static/src/js/list.js b/addons/web/static/src/js/list.js index 6029b30f866..cca616d0055 100644 --- a/addons/web/static/src/js/list.js +++ b/addons/web/static/src/js/list.js @@ -27,7 +27,9 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# * See constructor parameters and method documentations for information on * the default behaviors and possible options for the list view. * - * @constructs + * @constructs openerp.web.ListView + * @extends openerp.web.View + * * @param parent parent object * @param element_id the id of the DOM elements this view should link itself to * @param {openerp.web.DataSet} dataset the dataset the view should work with @@ -49,6 +51,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# this.dataset = dataset; this.model = dataset.model; this.view_id = view_id; + this.previous_colspan = null; this.columns = []; @@ -58,6 +61,13 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# if (this.dataset instanceof openerp.web.DataSetStatic) { this.groups.datagroup = new openerp.web.StaticDataGroup(this.dataset); + } else { + this.groups.datagroup = new openerp.web.DataGroup( + this, this.model, + dataset.get_domain(), + dataset.get_context(), + {}); + this.groups.datagroup.sort = this.dataset._sort; } this.page = 0; @@ -346,7 +356,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# if (this.sidebar) { this.sidebar.$element.show(); } - if (this.hidden) { + if (!_(this.dataset.ids).isEmpty()) { this.reload_content(); } }, @@ -355,7 +365,6 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# if (this.sidebar) { this.sidebar.$element.hide(); } - this.hidden = true; }, /** * Reloads the list view based on the current settings (dataset & al) @@ -603,6 +612,49 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# get_selected_ids: function() { var ids = this.groups.get_selection().ids; return ids; + }, + /** + * Adds padding columns at the start or end of all table rows (including + * field names row) + * + * @param {Number} count number of columns to add + * @param {Object} options + * @param {"before"|"after"} [position="after"] insertion position for the new columns + * @param {Object} [except] content row to not pad + */ + pad_columns: function (count, options) { + options = options || {}; + // padding for action/pager header + var $first_header = this.$element.find('thead tr:first th'); + var colspan = $first_header.attr('colspan'); + if (colspan) { + if (!this.previous_colspan) { + this.previous_colspan = colspan; + } + $first_header.attr('colspan', parseInt(colspan, 10) + count); + } + // Padding for column titles, footer and data rows + var $rows = this.$element + .find('.oe-listview-header-columns, tr:not(thead tr)') + .not(options['except']); + var newcols = new Array(count+1).join('<td class="oe-listview-padding"></td>'); + if (options.position === 'before') { + $rows.prepend(newcols); + } else { + $rows.append(newcols); + } + }, + /** + * Removes all padding columns of the table + */ + unpad_columns: function () { + this.$element.find('.oe-listview-padding').remove(); + if (this.previous_colspan) { + this.$element + .find('thead tr:first th') + .attr('colspan', this.previous_colspan); + this.previous_colspan = null; + } } }); openerp.web.ListView.List = openerp.web.Class.extend( /** @lends openerp.web.ListView.List# */{ @@ -630,7 +682,9 @@ openerp.web.ListView.List = openerp.web.Class.extend( /** @lends openerp.web.Lis * Triggered when a row of the table is clicked, provides the index (in * the rows array) and id of the selected record to the handle function. * - * @constructs + * @constructs openerp.web.ListView.List + * @extends openerp.web.Class + * * @param {Object} opts display options, identical to those of :js:class:`openerp.web.ListView` */ init: function (group, opts) { @@ -835,7 +889,9 @@ openerp.web.ListView.Groups = openerp.web.Class.extend( /** @lends openerp.web.L * Provides events similar to those of * :js:class:`~openerp.web.ListView.List` * - * @constructs + * @constructs openerp.web.ListView.Groups + * @extends openerp.web.Class + * * @param {openerp.web.ListView} view * @param {Object} [options] * @param {Collection} [options.records] @@ -1168,10 +1224,9 @@ openerp.web.ListView.Groups = openerp.web.Class.extend( /** @lends openerp.web.L }); /** - * @class - * @extends openerp.web.Class + * @mixin Events */ -var Events = { +var Events = /** @lends Events# */{ /** * @param {String} event event to listen to on the current object, null for all events * @param {Function} handler event handler to bind to the relevant event @@ -1221,10 +1276,10 @@ var Events = { }; var Record = openerp.web.Class.extend(/** @lends Record# */{ /** - * @constructs + * @constructs Record * @extends openerp.web.Class - * @borrows Events#bind as this.bind - * @borrows Events#trigger as this.trigger + * + * @mixes Events * @param {Object} [data] */ init: function (data) { @@ -1288,10 +1343,10 @@ var Collection = openerp.web.Class.extend(/** @lends Collection# */{ * Using a "dumb" array of records makes synchronization between the * various serious * - * @constructs + * @constructs Collection * @extends openerp.web.Class - * @borrows Events#bind as this.bind - * @borrows Events#trigger as this.trigger + * + * @mixes Events * @param {Array} [records] records to initialize the collection with * @param {Object} [options] */ diff --git a/addons/web/static/src/js/search.js b/addons/web/static/src/js/search.js index 272e96b7d01..21f397c0867 100644 --- a/addons/web/static/src/js/search.js +++ b/addons/web/static/src/js/search.js @@ -1,7 +1,17 @@ openerp.web.search = function(openerp) { var QWeb = openerp.web.qweb; -openerp.web.SearchView = openerp.web.Widget.extend({ +openerp.web.SearchView = openerp.web.Widget.extend(/** @lends openerp.web.SearchView# */{ + /** + * @constructs openerp.web.SearchView + * @extends openerp.web.Widget + * + * @param parent + * @param element_id + * @param dataset + * @param view_id + * @param defaults + */ init: function(parent, element_id, dataset, view_id, defaults) { this._super(parent, element_id); this.dataset = dataset; @@ -380,7 +390,9 @@ openerp.web.search.Invalid = openerp.web.Class.extend( /** @lends openerp.web.se * Exception thrown by search widgets when they hold invalid values, * which they can not return when asked. * - * @constructs + * @constructs openerp.web.search.Invalid + * @extends openerp.web.Class + * * @param field the name of the field holding an invalid value * @param value the invalid value * @param message validation failure message @@ -400,7 +412,7 @@ openerp.web.search.Widget = openerp.web.Widget.extend( /** @lends openerp.web.se /** * Root class of all search widgets * - * @constructs + * @constructs openerp.web.search.Widget * @extends openerp.web.Widget * * @param view the ancestor view of this widget @@ -452,8 +464,18 @@ openerp.web.search.Widget = openerp.web.Widget.extend( /** @lends openerp.web.se })); } }); -openerp.web.search.FilterGroup = openerp.web.search.Widget.extend({ +openerp.web.search.FilterGroup = openerp.web.search.Widget.extend(/** @lends openerp.web.search.FilterGroup# */{ template: 'SearchView.filters', + /** + * Inclusive group of filters, creates a continuous "button" with clickable + * sections (the normal display for filters is to be a self-contained button) + * + * @constructs openerp.web.search.FilterGroup + * @extends openerp.web.search.Widget + * + * @param {Array<openerp.web.search.Filter>} filters elements of the group + * @param {openerp.web.SearchView} view view in which the filters are contained + */ init: function (filters, view) { this._super(view); this.filters = filters; @@ -494,7 +516,7 @@ openerp.web.search.Group = openerp.web.search.Widget.extend({ openerp.web.search.Input = openerp.web.search.Widget.extend( /** @lends openerp.web.search.Input# */{ /** - * @constructs + * @constructs openerp.web.search.Input * @extends openerp.web.search.Widget * * @param view @@ -512,8 +534,18 @@ openerp.web.search.Input = openerp.web.search.Widget.extend( /** @lends openerp. "get_domain not implemented for widget " + this.attrs.type); } }); -openerp.web.search.Filter = openerp.web.search.Input.extend({ +openerp.web.search.Filter = openerp.web.search.Input.extend(/** @lends openerp.web.search.Filter# */{ template: 'SearchView.filter', + /** + * Implementation of the OpenERP filters (button with a context and/or + * a domain sent as-is to the search view) + * + * @constructs openerp.web.search.Filter + * @extends openerp.web.search.Input + * + * @param node + * @param view + */ init: function (node, view) { this._super(view); this.attrs = node.attrs; @@ -566,7 +598,7 @@ openerp.web.search.Field = openerp.web.search.Input.extend( /** @lends openerp.w template: 'SearchView.field', default_operator: '=', /** - * @constructs + * @constructs openerp.web.search.Field * @extends openerp.web.search.Input * * @param view_section @@ -694,7 +726,7 @@ openerp.web.search.SelectionField = openerp.web.search.Field.extend(/** @lends o }); openerp.web.search.BooleanField = openerp.web.search.SelectionField.extend(/** @lends openerp.web.search.BooleanField# */{ /** - * @constructs + * @constructs openerp.web.search.BooleanField * @extends openerp.web.search.BooleanField */ init: function () { @@ -729,7 +761,11 @@ openerp.web.search.BooleanField = openerp.web.search.SelectionField.extend(/** @ } } }); -openerp.web.search.DateField = openerp.web.search.Field.extend( /** @lends openerp.web.search.DateField# */{ +/** + * @class + * @extends openerp.web.search.DateField + */ +openerp.web.search.DateField = openerp.web.search.Field.extend(/** @lends openerp.web.search.DateField# */{ /** * enables date picker on the HTML widgets */ @@ -944,9 +980,16 @@ openerp.web.search.ExtendedSearchGroup = openerp.web.OldWidget.extend({ } }); -openerp.web.search.ExtendedSearchProposition = openerp.web.OldWidget.extend({ +openerp.web.search.ExtendedSearchProposition = openerp.web.OldWidget.extend(/** @lends openerp.web.search.ExtendedSearchProposition# */{ template: 'SearchView.extended_search.proposition', identifier_prefix: 'extended-search-proposition', + /** + * @constructs openerp.web.search.ExtendedSearchProposition + * @extends openerp.web.OldWidget + * + * @param parent + * @param fields + */ init: function (parent, fields) { this._super(parent); this.fields = _(fields).chain() diff --git a/addons/web/static/src/js/view_tree.js b/addons/web/static/src/js/view_tree.js index a71e4010fcf..2a314f57fa8 100644 --- a/addons/web/static/src/js/view_tree.js +++ b/addons/web/static/src/js/view_tree.js @@ -6,16 +6,24 @@ openerp.web.view_tree = function(openerp) { var QWeb = openerp.web.qweb; openerp.web.views.add('tree', 'openerp.web.TreeView'); -/** - * Genuine tree view (the one displayed as a tree, not the list) - */ -openerp.web.TreeView = openerp.web.View.extend({ +openerp.web.TreeView = openerp.web.View.extend(/** @lends openerp.web.TreeView# */{ /** * Indicates that this view is not searchable, and thus that no search * view should be displayed (if there is one active). */ searchable : false, - + /** + * Genuine tree view (the one displayed as a tree, not the list) + * + * @constructs openerp.web.TreeView + * @extends openerp.web.View + * + * @param parent + * @param element_id + * @param dataset + * @param view_id + * @param options + */ init: function(parent, element_id, dataset, view_id, options) { this._super(parent, element_id); this.dataset = dataset; diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js index 2c616359f01..6c10174d109 100644 --- a/addons/web/static/src/js/views.js +++ b/addons/web/static/src/js/views.js @@ -115,7 +115,7 @@ openerp.web.ActionManager = openerp.web.Widget.extend({ var self = this; this.rpc('/web/action/run', { action_id: action.id, - context: {active_id: 66, active_ids: [66], active_model: 'ir.ui.menu'} + context: action.context || {} }).then(function (action) { self.do_action(action, on_closed) }); @@ -135,8 +135,17 @@ openerp.web.ActionManager = openerp.web.Widget.extend({ } }); -openerp.web.ViewManager = openerp.web.Widget.extend({ +openerp.web.ViewManager = openerp.web.Widget.extend(/** @lends openerp.web.ViewManager# */{ identifier_prefix: "viewmanager", + template: "ViewManager", + /** + * @constructs openerp.web.ViewManager + * @extends openerp.web.Widget + * + * @param parent + * @param dataset + * @param views + */ init: function(parent, dataset, views) { this._super(parent); this.model = dataset.model; @@ -149,7 +158,7 @@ openerp.web.ViewManager = openerp.web.Widget.extend({ this.registry = openerp.web.views; }, render: function() { - return QWeb.render("ViewManager", {"prefix": this.element_id, views: this.views_src}) + return QWeb.render(this.template, {"prefix": this.element_id, views: this.views_src}) }, /** * @returns {jQuery.Deferred} initial view loading promise @@ -257,7 +266,7 @@ openerp.web.ViewManager = openerp.web.Widget.extend({ /** * Sets up the current viewmanager's search view. * - * @param view_id the view to use or false for a default one + * @param {Number|false} view_id the view to use or false for a default one * @returns {jQuery.Deferred} search view startup deferred */ setup_search_view: function(view_id, search_defaults) { @@ -265,10 +274,10 @@ openerp.web.ViewManager = openerp.web.Widget.extend({ if (this.searchview) { this.searchview.stop(); } - this.searchview = new openerp.web.SearchView(this, this.element_id + "_search", this.dataset, view_id, search_defaults); - if (this.flags.search_view === false) { - this.searchview.hide(); - } + this.searchview = new openerp.web.SearchView( + this, this.element_id + "_search", this.dataset, + view_id, search_defaults); + this.searchview.on_search.add(function(domains, contexts, groupbys) { var controller = self.views[self.active_view].controller; controller.do_search.call(controller, domains, contexts, groupbys); @@ -288,50 +297,65 @@ openerp.web.ViewManager = openerp.web.Widget.extend({ } }); -openerp.web.ViewManagerAction = openerp.web.ViewManager.extend({ +openerp.web.ViewManagerAction = openerp.web.ViewManager.extend(/** @lends oepnerp.web.ViewManagerAction# */{ + template:"ViewManagerAction", + /** + * @constructs openerp.web.ViewManagerAction + * @extends openerp.web.ViewManager + * + * @param {openerp.web.ActionManager} parent parent object/widget + * @param {Object} action descriptor for the action this viewmanager needs to manage its views. + */ init: function(parent, action) { + // dataset initialization will take the session from ``this``, so if we + // do not have it yet (and we don't, because we've not called our own + // ``_super()``) rpc requests will blow up. this.session = parent.session; this.action = action; - var dataset; - if (!action.res_id) { - dataset = new openerp.web.DataSetSearch(this, action.res_model, action.context, action.domain); - } else { - this.action.flags.search_view = false; - dataset = new openerp.web.DataSetStatic(this, action.res_model, action.context, [action.res_id]); + var dataset = new openerp.web.DataSetSearch(this, action.res_model, action.context, action.domain); + if (action.res_id) { + dataset.ids.push(action.res_id); dataset.index = 0; } this._super(parent, dataset, action.views); - this.action = action; this.flags = this.action.flags || {}; if (action.res_model == 'board.board' && action.views.length == 1 && action.views) { - // Not elegant but allows to avoid flickering of SearchView#do_hide + // Not elegant but allows to avoid form chrome (pager, save/new + // buttons, sidebar, ...) displaying this.flags.search_view = this.flags.pager = this.flags.sidebar = this.flags.action_buttons = false; } }, + /** + * Initializes the ViewManagerAction: sets up the searchview (if the + * searchview is enabled in the manager's action flags), calls into the + * parent to initialize the primary view and (if the VMA has a searchview) + * launches an initial search after both views are done rendering. + */ start: function() { - var inital_view_loaded = this._super(); - - var search_defaults = {}; - _.each(this.action.context, function (value, key) { - var match = /^search_default_(.*)$/.exec(key); - if (match) { - search_defaults[match[1]] = value; - } - }); - + var searchview_loaded; if (this.flags.search_view !== false) { + var search_defaults = {}; + _.each(this.action.context, function (value, key) { + var match = /^search_default_(.*)$/.exec(key); + if (match) { + search_defaults[match[1]] = value; + } + }); // init search view - var searchview_id = this.action.search_view_id && this.action.search_view_id[0]; + var searchview_id = this.action['search_view_id'] && this.action['search_view_id'][0]; - var searchview_loaded = this.setup_search_view( + searchview_loaded = this.setup_search_view( searchview_id || false, search_defaults); + } + + var main_view_loaded = this._super(); + var manager_ready = $.when(searchview_loaded, main_view_loaded); + if (searchview_loaded && this.action['auto_search']) { // schedule auto_search - if (searchview_loaded != null && this.action['auto_search']) { - $.when(searchview_loaded, inital_view_loaded) - .then(this.searchview.do_search); - } + manager_ready.then(this.searchview.do_search); } + return manager_ready; }, on_mode_switch: function (view_type) { return $.when( @@ -444,7 +468,11 @@ openerp.web.Sidebar = openerp.web.Widget.extend({ if (item.action) { var ids = self.widget_parent.get_selected_ids(); if (ids.length == 0) { - //TODO niv: maybe show a warning? + //TODO: make prettier warning? + $("<div />").text(_t("You must choose at least one record.")).dialog({ + title: _t("Warning"), + modal: true + }); return false; } var additional_context = { @@ -454,7 +482,7 @@ openerp.web.Sidebar = openerp.web.Widget.extend({ }; self.rpc("/web/action/load", { action_id: item.action.id, - context: _.extend({"bin_size": true}, additional_context) + context: additional_context }, function(result) { result.result.context = _.extend(result.result.context || {}, additional_context); @@ -613,7 +641,11 @@ openerp.web.TranslateDialog = openerp.web.Dialog.extend({ } }); -openerp.web.View = openerp.web.Widget.extend({ +/** + * @class + * @extends openerp.web.Widget + */ +openerp.web.View = openerp.web.Widget.extend(/** @lends openerp.web.View# */{ set_default_options: function(options) { this.options = options || {}; _.defaults(this.options, { @@ -653,6 +685,7 @@ openerp.web.View = openerp.web.Widget.extend({ active_ids: [record_id || false], active_model: dataset.model }); + action.context = new openerp.web.CompoundContext(dataset.get_context(), action.context); self.do_action(action, on_closed); } else if (on_closed) { on_closed(action); @@ -675,7 +708,7 @@ openerp.web.View = openerp.web.Widget.extend({ * Directly set a view to use instead of calling fields_view_get. This method must * be called before start(). When an embedded view is set, underlying implementations * of openerp.web.View must use the provided view instead of any other one. - * + * * @param embedded_view A view. */ set_embedded_view: function(embedded_view) { diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 677488d9d7d..1b1745036c4 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -40,7 +40,7 @@ <tr> <td valign="top" id="oe_secondary_menu" class="secondary_menu"> </td> - <td valign="top"> + <td valign="top" class="oe-application-container"> <div id="oe_app" class="oe-application"> </div> </td> @@ -420,8 +420,6 @@ <table class="view-manager-main-table"> <tr> <td class="view-manager-main-content"> - <a class="oe-shortcut-toggle" title="Add / Remove Shortcut..." - href="javascript: void(0)"> </a> <div class="oe_vm_switch"> <t t-if="views.length != 1" t-foreach="views" t-as="view"> <button type="button" t-att-data-view-type="view.view_type"> @@ -442,6 +440,14 @@ </tr> </table> </t> + +<t t-extend="ViewManager" t-name="ViewManagerAction"> + <t t-jquery=".view-manager-main-content" t-operation="prepend"> + <a class="oe-shortcut-toggle" title="Add / Remove Shortcut..." + href="javascript: void(0)"> </a> + </t> +</t> + <t t-name="Sidebar"> <a class="toggle-sidebar"></a> <div class="sidebar-content"> @@ -566,7 +572,7 @@ </table> </th> </tr> - <tr t-if="options.header"> + <tr t-if="options.header" class="oe-listview-header-columns"> <t t-foreach="columns" t-as="column"> <th t-if="column.meta"> <t t-esc="column.string"/> @@ -799,10 +805,8 @@ t-att-id="widget.element_id + '_field'" t-att-class="'field_' + widget.type" style="width: 100%"> - <t t-foreach="widget.field_index" t-as="options"> - <option t-att-value="options.ikey"> - <t t-esc="options.label"/> - </option> + <t t-foreach="widget.values" t-as="option"> + <option><t t-esc="option[1]"/></option> </t> </select> </t> @@ -1113,7 +1117,8 @@ <div t-att-id="element_id"> <select class="searchview_extended_prop_field"> <t t-foreach="attrs.fields" t-as="field"> - <option t-att="{'selected': field === attrs.selected ? 'selected' : null}" + <option t-if="typeof field.store === 'undefined' || field.store || field.fnct_search" + t-att="{'selected': field === attrs.selected ? 'selected' : null}" t-att-value="field.name"> <t t-esc="field.string"/> </option> @@ -1388,4 +1393,17 @@ </p> </div> </t> +<t t-name="FieldStatus"> + <div t-att-id="widget.element_id"></div> +</t> +<t t-name="FieldStatus.content"> + <ul class="oe-arrow-list"> + <t t-set="size" t-value="widget.to_show.length"/> + <t t-foreach="_.range(size)" t-as="i"> + <li t-att-class="widget.to_show[i][0] === widget.selected_value ? 'oe-arrow-list-selected' : ''"> + <span class="oe-arrow-list-before" t-if="i > 0"></span><span><t t-esc="widget.to_show[i][1]"/></span><span class="oe-arrow-list-after" t-if="i < size - 1"></span> + </li> + </t> + </ul> +</t> </templates> diff --git a/addons/web_chat/controllers/main.py b/addons/web_chat/controllers/main.py index 1e0710163f8..e9a026759b1 100644 --- a/addons/web_chat/controllers/main.py +++ b/addons/web_chat/controllers/main.py @@ -3,6 +3,9 @@ import time import simplejson import web.common as openerpweb +import logging + +_logger = logging.getLogger(__name__) #---------------------------------------------------------- # OpenERP Web ajaxim Controllers @@ -195,7 +198,7 @@ class PollServer(openerpweb.Controller): """ mq = req.applicationsession.setdefault("web_chat", PollServerMessageQueue()) - print "======== chat status ========",kw + _logger.debug("======== chat status ======== %s", kw) # mq.write() return {"action": ""} diff --git a/addons/web_dashboard/static/src/js/dashboard.js b/addons/web_dashboard/static/src/js/dashboard.js index 5662a88fc5b..9fe904319f9 100644 --- a/addons/web_dashboard/static/src/js/dashboard.js +++ b/addons/web_dashboard/static/src/js/dashboard.js @@ -282,6 +282,7 @@ openerp.web.client_actions.add( 'board.config.overview', 'openerp.web_dashboard.ConfigOverview' ); if (!openerp.web_dashboard) { + /** @namespace */ openerp.web_dashboard = {}; } openerp.web_dashboard.ConfigOverview = openerp.web.View.extend({ @@ -379,8 +380,9 @@ openerp.web_dashboard.Widget = openerp.web.View.extend(/** @lends openerp.web_da * Initializes a "HomeWidget" client widget: handles the display of a given * res.widget objects in an OpenERP view (mainly a dashboard). * - * @constructs + * @constructs openerp.web_dashboard.Widget * @extends openerp.web.View + * * @param {Object} parent * @param {Object} options * @param {Number} options.widget_id diff --git a/addons/web_kanban/static/src/css/kanban.css b/addons/web_kanban/static/src/css/kanban.css index e3fac6dbafe..afac6cdef47 100644 --- a/addons/web_kanban/static/src/css/kanban.css +++ b/addons/web_kanban/static/src/css/kanban.css @@ -26,49 +26,70 @@ background: #FFF; border: 2px solid #CCC; border-radius: 4px; - margin-bottom: 1em; + margin-bottom: 5px; } .openerp .oe_kanban_box_header { background: #EEE; border-bottom: 1px solid #CCC; - padding: 2px; - height: 2em; - white-space: nowrap; } -.openerp .oe_kanban_box_header h4 { +.openerp .oe_kanban_title1 { font-size: 130%; font-weight: bold; - margin: 0; - padding: 0; + padding: 0 4px 0 4px; } -.openerp .oe_kanban_box_header h5 { +.openerp .oe_kanban_title2 { font-size: 120%; font-weight: bold; - margin: 0; - padding: 2px; + padding: 0 4px 0 4px; } -.openerp .oe_kanban_box_header h6 { +.openerp .oe_kanban_title3 { font-size: 110%; font-weight: normal; - margin: 0; - padding: 2px; + padding: 0 4px 0 4px; } .openerp .oe_kanban_small { font-size: 80%; + font-weight: normal; +} +.openerp .oe_kanban_table { + width: 100%; + border: none; + border-collapse: collapse; + margin: 0; + padding: 0; +} +.openerp .oe_kanban_gravatar { + display: block; + border-radius: 2px; } .openerp .oe_kanban_box_content { padding: 4px; } .openerp .oe_kanban_buttons_set { - border-top: 1px dotted #CCC; + border-top: 1px dotted; + white-space: nowrap; + padding-top: 2px; + position: relative; } -.openerp .oe_kanban_buttons_set .oe_kanban_left a { - border-right: 1px dotted #CCC; +.openerp .oe_kanban_buttons_set a { padding: 2px; } -.openerp .oe_kanban_buttons_set .oe_kanban_right a { - border-left: 1px dotted #CCC; - padding: 2px; +.oe_kanban_color_picker { + position: absolute; + top: -10px; + border: 1px solid black; + -moz-box-shadow: 2px 2px 5px #666; + -webkit-box-shadow: 2px 2px 5px #666; + box-shadow: 2px 2px 5px #666; +} +.oe_kanban_color_picker a { + display: block; + width: 30px; + height: 30px; + border: 1px solid black; +} +.oe_kanban_color_picker a:hover { + border-color: white; } .openerp .oe_kanban_left { float: left; @@ -86,17 +107,97 @@ cursor: move; } -/* Custom colors */ -.openerp .oe_kanban_color_1 .oe_kanban_color_bglight, -.openerp .oe_kanban_color_1.oe_kanban_color_bglight { - background: #F5F7C4; -} -.openerp .oe_kanban_color_1 .oe_kanban_color_bgdark, -.openerp .oe_kanban_color_1.oe_kanban_color_bgdark { - background: #EEF093; -} -.openerp .oe_kanban_color_1 .oe_kanban_color_border, -.openerp .oe_kanban_color_1.oe_kanban_color_border { - border-color: #DFE32D; +/* Custom colors are also present in kanban.js */ +/* Custom color#1 */ +.openerp .oe_kanban_color_1 .oe_kanban_color_bglight { + background: #FFC7C7; +} +.openerp .oe_kanban_color_1 .oe_kanban_color_bgdark { + background: #FF8F8F; +} +.openerp .oe_kanban_color_1 .oe_kanban_color_border { + border-color: #D97979; +} + +/* Custom color#2 */ +.openerp .oe_kanban_color_2 .oe_kanban_color_bglight { + background: #FFF1C7; +} +.openerp .oe_kanban_color_2 .oe_kanban_color_bgdark { + background: #FFE38F; +} +.openerp .oe_kanban_color_2 .oe_kanban_color_border { + border-color: #D9C179; +} + +/* Custom color#3 */ +.openerp .oe_kanban_color_3 .oe_kanban_color_bglight { + background: #E3FFC7; +} +.openerp .oe_kanban_color_3 .oe_kanban_color_bgdark { + background: #C7FF8F; +} +.openerp .oe_kanban_color_3 .oe_kanban_color_border { + border-color: #A9D979; +} + +/* Custom color#4 */ +.openerp .oe_kanban_color_4 .oe_kanban_color_bglight { + background: #C7FFD5; +} +.openerp .oe_kanban_color_4 .oe_kanban_color_bgdark { + background: #8FFFAB; +} +.openerp .oe_kanban_color_4 .oe_kanban_color_border { + border-color: #79D991; +} + +/* Custom color#5 */ +.openerp .oe_kanban_color_5 .oe_kanban_color_bglight { + background: #C7FFFF; +} +.openerp .oe_kanban_color_5 .oe_kanban_color_bgdark { + background: #8FFFFF; +} +.openerp .oe_kanban_color_5 .oe_kanban_color_border { + border-color: #79D9D9; +} + +/* Custom color#6 */ +.openerp .oe_kanban_color_6 .oe_kanban_color_bglight { + background: #C7D5FF; +} +.openerp .oe_kanban_color_6 .oe_kanban_color_bgdark { + background: #8FABFF; +} +.openerp .oe_kanban_color_6 .oe_kanban_color_border { + border-color: #8FABFF; +} + +/* Custom color#7 */ +.openerp .oe_kanban_color_7 .oe_kanban_color_bglight { + background: #E3C7FF; +} +.openerp .oe_kanban_color_7 .oe_kanban_color_bgdark { + background: #C78FFF; +} +.openerp .oe_kanban_color_7 .oe_kanban_color_border { + border-color: #A979D9; +} + +/* Custom color#8 */ +.openerp .oe_kanban_color_8 .oe_kanban_color_bglight { + background: #FFC7F1; +} +.openerp .oe_kanban_color_8 .oe_kanban_color_bgdark { + background: #FF8FE3; +} +.openerp .oe_kanban_color_8 .oe_kanban_color_border { + border-color: #D979C1; +} + +/* Alert color */ +.openerp .oe_kanban_color_alert .oe_kanban_color_border { + border-color: #c00 !important; } diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js index dd101f44346..dfaf7769df5 100644 --- a/addons/web_kanban/static/src/js/kanban.js +++ b/addons/web_kanban/static/src/js/kanban.js @@ -18,6 +18,10 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ this.all_display_data = false; this.groups = []; this.qweb = new QWeb2.Engine(); + 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); + } }, start: function() { return this.rpc("/web/view/load", {"model": this.model, "view_id": this.view_id, "view_type": "kanban"}, this.on_loaded); @@ -27,14 +31,7 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ this.fields_view = data; this.add_qweb_template(); if (this.qweb.has_template('kanban-box')) { - self.dataset.read_slice(_.keys(self.fields_view.fields), { - context: self.dataset.get_context(), - domain: self.dataset.get_domain() - }, function (records) { - self.all_display_data = [{'records': records, 'value': false, 'header': false, 'ids': self.dataset.ids}]; - self.on_show_data(self.all_display_data); - } - ); + this.do_actual_search(); } }, add_qweb_template: function() { @@ -47,8 +44,8 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ } } }, - do_get_kanban_color: function(variable) { - var number_of_color_schemes = 2, + kanban_color: function(variable) { + var number_of_color_schemes = 8, index = 0; switch (typeof(variable)) { case 'string': @@ -62,25 +59,33 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ default: return ''; } - return 'oe_kanban_color_' + ((index % number_of_color_schemes) + 1); + return 'oe_kanban_color_' + ((index % number_of_color_schemes) || number_of_color_schemes); + }, + kanban_gravatar: function(email, size) { + size = size || 22; + var email_md5 = '2eb60ad22dadcf4dc456b28390a80268'; + return 'http://www.gravatar.com/avatar/' + email_md5 + '.png?s=' + size; }, transform_qweb_template: function(node) { + var qweb_prefix = QWeb.prefix; switch (node.tag) { case 'field': node.tag = 't'; - node.attrs['t-esc'] = node.attrs['name'] + '.value'; + node.attrs['t-esc'] = 'record.' + node.attrs['name'] + '.value'; break case 'button': case 'a': var type = node.attrs.type || ''; - if (_.indexOf('action,object,edit,delete,'.split(','), type) !== -1) { + if (_.indexOf('action,object,edit,delete,color'.split(','), type) !== -1) { _.each(node.attrs, function(v, k) { - node.attrs['data-' + k] = v; - delete(node.attrs[k]); + if (_.indexOf('icon,type,name,string,context,states'.split(','), k) != -1) { + node.attrs['data-' + k] = v; + delete(node.attrs[k]); + } }); if (node.attrs['data-states']) { var states = _.map(node.attrs['data-states'].split(','), function(state) { - return "state.value == '" + _.trim(state) + "'"; + return "record.state.value == '" + _.trim(state) + "'"; }); node.attrs['t-if'] = states.join(' or '); } @@ -157,23 +162,45 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ } }, on_confirm_click: function (dataset, button_attrs, index, record_id) { - if (button_attrs.type == 'edit') { - this.do_edit_record(dataset, index); + this.on_execute_button_click(dataset, button_attrs, record_id); + }, + do_add_record: function() { + this.dataset.index = null; + this.do_switch_view('form'); + }, + do_edit_record: function(record_id) { + if (this.form_dialog) { + this.form_dialog.load_id(record_id); + this.form_dialog.open(); } else { - this.on_execute_button_click(dataset, button_attrs, record_id); + this.notification.warn("Kanban", "No form view defined for this object"); } }, - do_add_record: function () { - this.do_edit_record(this.dataset, null); + on_record_saved: function(r) { + var id = this.form_dialog.form.datarecord.id; + // TODO fme: reload record instead of all. need refactoring + this.do_actual_search(); }, - do_edit_record: function (dataset, index) { - var self = this; - _.extend(this.dataset, { - domain: dataset.domain, - context: dataset.get_context() - }).read_slice([], {}, function () { - self.dataset.index = index; - self.do_switch_view('form'); + do_change_color: function(record_id, $e) { + var self = this, + id = record_id, + colors = '#FFC7C7,#FFF1C7,#E3FFC7,#C7FFD5,#C7FFFF,#C7D5FF,#E3C7FF,#FFC7F1'.split(','), + $cpicker = $(QWeb.render('KanbanColorPicker', { colors : colors, columns: 2 })); + $e.after($cpicker); + $cpicker.mouseenter(function() { + clearTimeout($cpicker.data('timeoutId')); + }).mouseleave(function(evt) { + var timeoutId = setTimeout(function() { $cpicker.remove() }, 500); + $cpicker.data('timeoutId', timeoutId); + }); + $cpicker.find('a').click(function() { + var data = {}; + 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(); + }); + $cpicker.remove(); }); }, do_delete: function (id) { @@ -304,31 +331,37 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ if (data.records.length > 0){ _.each(data.records, function(record) { self.$element.find("#main_" + record.id).children().remove(); - self.$element.find("#main_" + record.id).append(self.qweb.render('kanban-box', self.do_transform_record(record))); + self.$element.find("#main_" + record.id).append(self.qweb.render('kanban-box', { + record: self.do_transform_record(record), + kanban_color: self.kanban_color, + 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(function() { - var record_id = $(this).closest(".oe_kanban_record").attr("id"); - if (record_id) { - record_id = parseInt(record_id.split("_")[1]) - if (record_id) { - if ($(this).data("type") == "delete") { - self.do_delete(record_id); - } else { - var button_attrs = $(this).data() - self.on_button_click(button_attrs, record_id); - } - } - } - }); - this.$element.find('.oe_kanban_record').click(function() { - $(this).find('.oe_kanban_box_show_onclick').removeClass('oe_kanban_box_show_onclick'); + this.$element.find('.oe_kanban_action').click(this.on_action_clicked); + this.$element.find('.oe_kanban_box_show_onclick_trigger').click(function() { + $(this).parent('.oe_kanban_box').find('.oe_kanban_box_show_onclick').toggle(); }); }, + on_action_clicked: function(evt) { + var $action = $(evt.currentTarget), + record_id = parseInt($action.closest(".oe_kanban_record").attr("id").split('_')[1]), + type = $action.data('type'); + if (type == 'delete') { + this.do_delete(record_id); + } else if (type == 'edit') { + this.do_edit_record(record_id); + } else if (type == 'color') { + this.do_change_color(record_id, $action); + } else { + var button_attrs = $action.data(); + this.on_button_click(button_attrs, record_id); + } + }, do_transform_record: function(record) { var self = this, new_record = {}; @@ -338,7 +371,6 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ r.value = openerp.web.format_value(value, r); new_record[name] = r; }); - new_record.__kanban_color = this.do_get_kanban_color; return new_record; }, do_search: function (domains, contexts, group_by) { @@ -355,8 +387,12 @@ openerp.web_kanban.KanbanView = openerp.web.View.extend({ }); }, do_actual_search : function () { - var self = this; - self.datagroup = new openerp.web.DataGroup(self, self.model, self.domain, self.context, self.group_by || []); + var self = this, + 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.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([], diff --git a/addons/web_kanban/static/src/xml/web_kanban.xml b/addons/web_kanban/static/src/xml/web_kanban.xml index 564761c955a..aa7bc73190d 100644 --- a/addons/web_kanban/static/src/xml/web_kanban.xml +++ b/addons/web_kanban/static/src/xml/web_kanban.xml @@ -22,4 +22,16 @@ </tr> </table> </t> +<t t-name="KanbanColorPicker"> + <table cellspacing="0" cellpadding="0" border="0" class="oe_kanban_color_picker"> + <tr> + <t t-foreach="colors" t-as="color"> + <td t-att-bgcolor="color"> + <a href="#" t-att-data-color="color_index + 1"/> + </td> + <t t-if="((color_index + 1) % Math.round(colors.length / columns)) == 0"></tr><tr></t> + </t> + </tr> + </table> +</t> </template> |
