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/webrelease.py b/addons/web/common/release.py
similarity index 82%
rename from webrelease.py
rename to addons/web/common/release.py
index 77eb46dd2c3..47570c032bb 100644
--- a/webrelease.py
+++ b/addons/web/common/release.py
@@ -1,11 +1,10 @@
-
-name = 'openerp-web-proto'
+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 S.A."
+author = "OpenERP SA"
author_email = "info@openerp.com"
support_email = 'support@openerp.com'
url = "http://www.openerp.com/"
download_url = ''
-license = "OEPL"
+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
new file mode 100644
index 00000000000..765294c6bdd
Binary files /dev/null and b/addons/web/static/src/img/icons/color-picker.png differ
diff --git a/addons/web/static/src/img/icons/gtk-about.png b/addons/web/static/src/img/icons/gtk-about.png
new file mode 100644
index 00000000000..1b430a35758
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-about.png differ
diff --git a/addons/web/static/src/img/icons/gtk-bold.png b/addons/web/static/src/img/icons/gtk-bold.png
new file mode 100644
index 00000000000..408f25aa20b
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-bold.png differ
diff --git a/addons/web/static/src/img/icons/gtk-cdrom.png b/addons/web/static/src/img/icons/gtk-cdrom.png
new file mode 100644
index 00000000000..27e8edd8a0b
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-cdrom.png differ
diff --git a/addons/web/static/src/img/icons/gtk-clear.png b/addons/web/static/src/img/icons/gtk-clear.png
new file mode 100644
index 00000000000..b2874fb84fa
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-clear.png differ
diff --git a/addons/web/static/src/img/icons/gtk-color-picker.png b/addons/web/static/src/img/icons/gtk-color-picker.png
new file mode 100644
index 00000000000..232d54f8f3b
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-color-picker.png differ
diff --git a/addons/web/static/src/img/icons/gtk-connect.png b/addons/web/static/src/img/icons/gtk-connect.png
new file mode 100644
index 00000000000..c820b54abcc
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-connect.png differ
diff --git a/addons/web/static/src/img/icons/gtk-copy.png b/addons/web/static/src/img/icons/gtk-copy.png
new file mode 100644
index 00000000000..3d68da606d2
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-copy.png differ
diff --git a/addons/web/static/src/img/icons/gtk-cut.png b/addons/web/static/src/img/icons/gtk-cut.png
new file mode 100644
index 00000000000..96681d26968
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-cut.png differ
diff --git a/addons/web/static/src/img/icons/gtk-dialog-authentication.png b/addons/web/static/src/img/icons/gtk-dialog-authentication.png
new file mode 100644
index 00000000000..eb411112f83
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-dialog-authentication.png differ
diff --git a/addons/web/static/src/img/icons/gtk-dialog-error.png b/addons/web/static/src/img/icons/gtk-dialog-error.png
new file mode 100644
index 00000000000..e97bc43504b
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-dialog-error.png differ
diff --git a/addons/web/static/src/img/icons/gtk-dialog-info.png b/addons/web/static/src/img/icons/gtk-dialog-info.png
new file mode 100644
index 00000000000..ed46c3ecdb3
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-dialog-info.png differ
diff --git a/addons/web/static/src/img/icons/gtk-dialog-question.png b/addons/web/static/src/img/icons/gtk-dialog-question.png
new file mode 100644
index 00000000000..ef68f43f43f
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-dialog-question.png differ
diff --git a/addons/web/static/src/img/icons/gtk-dialog-warning.png b/addons/web/static/src/img/icons/gtk-dialog-warning.png
new file mode 100644
index 00000000000..65a4d2c7220
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-dialog-warning.png differ
diff --git a/addons/web/static/src/img/icons/gtk-directory-remote.png b/addons/web/static/src/img/icons/gtk-directory-remote.png
new file mode 100644
index 00000000000..34e737d7c07
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-directory-remote.png differ
diff --git a/addons/web/static/src/img/icons/gtk-directory.png b/addons/web/static/src/img/icons/gtk-directory.png
new file mode 100644
index 00000000000..8e41b7878a9
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-directory.png differ
diff --git a/addons/web/static/src/img/icons/gtk-disconnect.png b/addons/web/static/src/img/icons/gtk-disconnect.png
new file mode 100644
index 00000000000..fd188701fc2
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-disconnect.png differ
diff --git a/addons/web/static/src/img/icons/gtk-dnd-multiple.png b/addons/web/static/src/img/icons/gtk-dnd-multiple.png
new file mode 100644
index 00000000000..aa4c65ad5b1
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-dnd-multiple.png differ
diff --git a/addons/web/static/src/img/icons/gtk-file.png b/addons/web/static/src/img/icons/gtk-file.png
new file mode 100644
index 00000000000..fa157156bcc
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-file.png differ
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
new file mode 100644
index 00000000000..b8d8eeec1b2
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-find-and-replace.png differ
diff --git a/addons/web/static/src/img/icons/gtk-floppy.png b/addons/web/static/src/img/icons/gtk-floppy.png
new file mode 100644
index 00000000000..7d8209cfb6f
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-floppy.png differ
diff --git a/addons/web/static/src/img/icons/gtk-fullscreen.png b/addons/web/static/src/img/icons/gtk-fullscreen.png
new file mode 100644
index 00000000000..8ddc31e7481
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-fullscreen.png differ
diff --git a/addons/web/static/src/img/icons/gtk-goto-bottom.png b/addons/web/static/src/img/icons/gtk-goto-bottom.png
new file mode 100644
index 00000000000..be52840dee1
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-goto-bottom.png differ
diff --git a/addons/web/static/src/img/icons/gtk-goto-first.png b/addons/web/static/src/img/icons/gtk-goto-first.png
new file mode 100644
index 00000000000..3d00e36d696
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-goto-first.png differ
diff --git a/addons/web/static/src/img/icons/gtk-goto-top.png b/addons/web/static/src/img/icons/gtk-goto-top.png
new file mode 100644
index 00000000000..227a91e5157
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-goto-top.png differ
diff --git a/addons/web/static/src/img/icons/gtk-harddisk.png b/addons/web/static/src/img/icons/gtk-harddisk.png
new file mode 100644
index 00000000000..73e5ef0a82e
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-harddisk.png differ
diff --git a/addons/web/static/src/img/icons/gtk-help.png b/addons/web/static/src/img/icons/gtk-help.png
new file mode 100644
index 00000000000..f8e17009dd6
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-help.png differ
diff --git a/addons/web/static/src/img/icons/gtk-home.png b/addons/web/static/src/img/icons/gtk-home.png
new file mode 100644
index 00000000000..9d3d50d0fcb
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-home.png differ
diff --git a/addons/web/static/src/img/icons/gtk-info.png b/addons/web/static/src/img/icons/gtk-info.png
new file mode 100644
index 00000000000..272019f5c99
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-info.png differ
diff --git a/addons/web/static/src/img/icons/gtk-italic.png b/addons/web/static/src/img/icons/gtk-italic.png
new file mode 100644
index 00000000000..26c57226a31
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-italic.png differ
diff --git a/addons/web/static/src/img/icons/gtk-justify-center.png b/addons/web/static/src/img/icons/gtk-justify-center.png
new file mode 100644
index 00000000000..d5f514298eb
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-justify-center.png differ
diff --git a/addons/web/static/src/img/icons/gtk-justify-left.png b/addons/web/static/src/img/icons/gtk-justify-left.png
new file mode 100644
index 00000000000..ecb76b117eb
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-justify-left.png differ
diff --git a/addons/web/static/src/img/icons/gtk-justify-right.png b/addons/web/static/src/img/icons/gtk-justify-right.png
new file mode 100644
index 00000000000..3cd4d161420
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-justify-right.png differ
diff --git a/addons/web/static/src/img/icons/gtk-leave-fullscreen.png b/addons/web/static/src/img/icons/gtk-leave-fullscreen.png
new file mode 100644
index 00000000000..15bc8ab7d7b
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-leave-fullscreen.png differ
diff --git a/addons/web/static/src/img/icons/gtk-media-forward.png b/addons/web/static/src/img/icons/gtk-media-forward.png
new file mode 100644
index 00000000000..e5e198fbb79
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-media-forward.png differ
diff --git a/addons/web/static/src/img/icons/gtk-media-next.png b/addons/web/static/src/img/icons/gtk-media-next.png
new file mode 100644
index 00000000000..e0aa2431cf0
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-media-next.png differ
diff --git a/addons/web/static/src/img/icons/gtk-media-previous.png b/addons/web/static/src/img/icons/gtk-media-previous.png
new file mode 100644
index 00000000000..756899e0bc1
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-media-previous.png differ
diff --git a/addons/web/static/src/img/icons/gtk-media-record.png b/addons/web/static/src/img/icons/gtk-media-record.png
new file mode 100644
index 00000000000..904974c81e5
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-media-record.png differ
diff --git a/addons/web/static/src/img/icons/gtk-media-rewind.png b/addons/web/static/src/img/icons/gtk-media-rewind.png
new file mode 100644
index 00000000000..9c22d9e2778
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-media-rewind.png differ
diff --git a/addons/web/static/src/img/icons/gtk-media-stop.png b/addons/web/static/src/img/icons/gtk-media-stop.png
new file mode 100644
index 00000000000..128cc2313ec
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-media-stop.png differ
diff --git a/addons/web/static/src/img/icons/gtk-missing-image.png b/addons/web/static/src/img/icons/gtk-missing-image.png
new file mode 100644
index 00000000000..cc69ed33e4b
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-missing-image.png differ
diff --git a/addons/web/static/src/img/icons/gtk-paste-v.png b/addons/web/static/src/img/icons/gtk-paste-v.png
new file mode 100644
index 00000000000..76ec896f70e
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-paste-v.png differ
diff --git a/addons/web/static/src/img/icons/gtk-paste.png b/addons/web/static/src/img/icons/gtk-paste.png
new file mode 100644
index 00000000000..840f26c8b38
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-paste.png differ
diff --git a/addons/web/static/src/img/icons/gtk-preferences.png b/addons/web/static/src/img/icons/gtk-preferences.png
new file mode 100644
index 00000000000..9ba17a0ab20
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-preferences.png differ
diff --git a/addons/web/static/src/img/icons/gtk-print-preview.png b/addons/web/static/src/img/icons/gtk-print-preview.png
new file mode 100644
index 00000000000..ab15fcbd307
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-print-preview.png differ
diff --git a/addons/web/static/src/img/icons/gtk-properties.png b/addons/web/static/src/img/icons/gtk-properties.png
new file mode 100644
index 00000000000..894415ec1ce
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-properties.png differ
diff --git a/addons/web/static/src/img/icons/gtk-quit.png b/addons/web/static/src/img/icons/gtk-quit.png
new file mode 100644
index 00000000000..50b5144660b
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-quit.png differ
diff --git a/addons/web/static/src/img/icons/gtk-refresh.png b/addons/web/static/src/img/icons/gtk-refresh.png
new file mode 100644
index 00000000000..7474728b4c0
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-refresh.png differ
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
new file mode 100644
index 00000000000..eaf54584edf
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-revert-to-saved.png differ
diff --git a/addons/web/static/src/img/icons/gtk-save-as.png b/addons/web/static/src/img/icons/gtk-save-as.png
new file mode 100644
index 00000000000..1831e4e0a4b
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-save-as.png differ
diff --git a/addons/web/static/src/img/icons/gtk-select-color.png b/addons/web/static/src/img/icons/gtk-select-color.png
new file mode 100644
index 00000000000..12b015df107
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-select-color.png differ
diff --git a/addons/web/static/src/img/icons/gtk-select-font.png b/addons/web/static/src/img/icons/gtk-select-font.png
new file mode 100644
index 00000000000..66e606313a5
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-select-font.png differ
diff --git a/addons/web/static/src/img/icons/gtk-sort-ascending.png b/addons/web/static/src/img/icons/gtk-sort-ascending.png
new file mode 100644
index 00000000000..a648fa0d359
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-sort-ascending.png differ
diff --git a/addons/web/static/src/img/icons/gtk-spell-check.png b/addons/web/static/src/img/icons/gtk-spell-check.png
new file mode 100644
index 00000000000..698f7c24a43
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-spell-check.png differ
diff --git a/addons/web/static/src/img/icons/gtk-strikethrough.png b/addons/web/static/src/img/icons/gtk-strikethrough.png
new file mode 100644
index 00000000000..1c11f214622
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-strikethrough.png differ
diff --git a/addons/web/static/src/img/icons/gtk-undelete.png b/addons/web/static/src/img/icons/gtk-undelete.png
new file mode 100644
index 00000000000..c44b940c03b
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-undelete.png differ
diff --git a/addons/web/static/src/img/icons/gtk-underline.png b/addons/web/static/src/img/icons/gtk-underline.png
new file mode 100644
index 00000000000..7dbb5d7e60a
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-underline.png differ
diff --git a/addons/web/static/src/img/icons/gtk-unindent.png b/addons/web/static/src/img/icons/gtk-unindent.png
new file mode 100644
index 00000000000..8ace914c1bd
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-unindent.png differ
diff --git a/addons/web/static/src/img/icons/gtk-zoom-100.png b/addons/web/static/src/img/icons/gtk-zoom-100.png
new file mode 100644
index 00000000000..5193406b787
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-zoom-100.png differ
diff --git a/addons/web/static/src/img/icons/gtk-zoom-out.png b/addons/web/static/src/img/icons/gtk-zoom-out.png
new file mode 100644
index 00000000000..0da5ee7ec13
Binary files /dev/null and b/addons/web/static/src/img/icons/gtk-zoom-out.png differ
diff --git a/addons/web/static/src/img/icons/lead-stage-lost.png b/addons/web/static/src/img/icons/lead-stage-lost.png
new file mode 100644
index 00000000000..e1f98a26b59
Binary files /dev/null and b/addons/web/static/src/img/icons/lead-stage-lost.png differ
diff --git a/addons/web/static/src/img/icons/lead-stage-pending.png b/addons/web/static/src/img/icons/lead-stage-pending.png
new file mode 100644
index 00000000000..9e9544ee178
Binary files /dev/null and b/addons/web/static/src/img/icons/lead-stage-pending.png differ
diff --git a/addons/web/static/src/img/icons/lead-stage-won.png b/addons/web/static/src/img/icons/lead-stage-won.png
new file mode 100644
index 00000000000..02effd5f14c
Binary files /dev/null and b/addons/web/static/src/img/icons/lead-stage-won.png differ
diff --git a/addons/web/static/src/img/icons/star-off.png b/addons/web/static/src/img/icons/star-off.png
new file mode 100644
index 00000000000..7cd0afd4f03
Binary files /dev/null and b/addons/web/static/src/img/icons/star-off.png differ
diff --git a/addons/web/static/src/img/icons/star-on.png b/addons/web/static/src/img/icons/star-on.png
new file mode 100644
index 00000000000..4f2295e1395
Binary files /dev/null and b/addons/web/static/src/img/icons/star-on.png differ
diff --git a/addons/web/static/src/img/icons/stock.png b/addons/web/static/src/img/icons/stock.png
new file mode 100644
index 00000000000..ca38181db90
Binary files /dev/null and b/addons/web/static/src/img/icons/stock.png differ
diff --git a/addons/web/static/src/img/icons/stock_calendar.png b/addons/web/static/src/img/icons/stock_calendar.png
new file mode 100644
index 00000000000..99a1f3f9ef1
Binary files /dev/null and b/addons/web/static/src/img/icons/stock_calendar.png differ
diff --git a/addons/web/static/src/img/icons/stock_gantt.png b/addons/web/static/src/img/icons/stock_gantt.png
new file mode 100644
index 00000000000..585761b9220
Binary files /dev/null and b/addons/web/static/src/img/icons/stock_gantt.png differ
diff --git a/addons/web/static/src/img/icons/stock_graph.png b/addons/web/static/src/img/icons/stock_graph.png
new file mode 100644
index 00000000000..4d8310621ea
Binary files /dev/null and b/addons/web/static/src/img/icons/stock_graph.png differ
diff --git a/addons/web/static/src/img/icons/stock_log.png b/addons/web/static/src/img/icons/stock_log.png
new file mode 100644
index 00000000000..32af4dd6901
Binary files /dev/null and b/addons/web/static/src/img/icons/stock_log.png differ
diff --git a/addons/web/static/src/img/icons/stock_person.png b/addons/web/static/src/img/icons/stock_person.png
new file mode 100644
index 00000000000..202cf0ee6c5
Binary files /dev/null and b/addons/web/static/src/img/icons/stock_person.png differ
diff --git a/addons/web/static/src/img/icons/stock_translate.png b/addons/web/static/src/img/icons/stock_translate.png
new file mode 100644
index 00000000000..5ab41d49343
Binary files /dev/null and b/addons/web/static/src/img/icons/stock_translate.png differ
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($('', {
- '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) {
$('
').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('');
}
});
+ // 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(' | | ');
+ 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} 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);
-
- // schedule auto_search
- if (searchview_loaded != null && this.action['auto_search']) {
- $.when(searchview_loaded, inital_view_loaded)
- .then(this.searchview.do_search);
- }
}
+
+ 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
+ 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?
+ $("").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 @@
- |
+ |
|
@@ -420,8 +420,6 @@
+
+
+
+
+
+
+
-
+
|
@@ -799,10 +805,8 @@
t-att-id="widget.element_id + '_field'"
t-att-class="'field_' + widget.type"
style="width: 100%">
-
-
+
+
@@ -1113,7 +1117,8 @@
+
+
+
+
+
+
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;
+/* 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,
-.openerp .oe_kanban_color_1.oe_kanban_color_bgdark {
- background: #EEF093;
+.openerp .oe_kanban_color_1 .oe_kanban_color_bgdark {
+ background: #FF8F8F;
}
-.openerp .oe_kanban_color_1 .oe_kanban_color_border,
-.openerp .oe_kanban_color_1.oe_kanban_color_border {
- border-color: #DFE32D;
+.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><tr>
+
+
+
+
diff --git a/openerp-web.py b/openerp-web.py
index 29a958b7834..5617a0b8bdd 100755
--- a/openerp-web.py
+++ b/openerp-web.py
@@ -3,6 +3,8 @@ import optparse
import os
import sys
import tempfile
+import logging
+import logging.config
import werkzeug.serving
@@ -31,6 +33,10 @@ optparser.add_option('--no-serve-static', dest='serve_static',
optparser.add_option('--reloader', dest='reloader',
default=False, action='store_true',
help="Reload application when python files change")
+optparser.add_option("--log-level", dest="log_level",
+ default='debug', help="Log level", metavar="LOG_LEVEL")
+optparser.add_option("--log-config", dest="log_config",
+ default='', help="Log config file", metavar="LOG_CONFIG")
import web.common.dispatch
@@ -38,6 +44,12 @@ if __name__ == "__main__":
(options, args) = optparser.parse_args(sys.argv[1:])
os.environ["TZ"] = "UTC"
+
+ if not options.log_config:
+ logging.basicConfig(level=getattr(logging, options.log_level.upper()))
+ else:
+ logging.config.fileConfig(options.log_config)
+
app = web.common.dispatch.Root(options)
werkzeug.serving.run_simple(
diff --git a/setup.py b/setup.py
index cbbd4697687..36db48c45da 100644
--- a/setup.py
+++ b/setup.py
@@ -4,7 +4,7 @@ import re
import sys
from setuptools import setup
-execfile('webrelease.py')
+execfile('addons/web/common/release.py')
version_dash_incompatible = False
if 'bdist_rpm' in sys.argv:
@@ -49,7 +49,7 @@ setup(
"simplejson >= 2.0.9",
"python-dateutil >= 1.4.1",
"pytz",
- "werkzeug = 0.7",
+ "werkzeug == 0.7",
],
tests_require=[
'unittest2',