diff options
| author | Vidhin Mehta (OpenERP) <vme@tinyerp.com> | 2014-05-05 10:02:33 +0530 |
|---|---|---|
| committer | Vidhin Mehta (OpenERP) <vme@tinyerp.com> | 2014-05-05 10:02:33 +0530 |
| commit | e41508cb8d7fd2a2c06e3fab9ce1503e8efa30fa (patch) | |
| tree | 8e0fbb849cebc9b3ac0f87355a5248f51826a491 /addons/web | |
| parent | 4312b921f3db41529cbd4f501a6b5dcba2bebd08 (diff) | |
| parent | 8fbebfd752fa8933584711522675d354b3217839 (diff) | |
[MERGE]Trunk.
bzr revid: vme@tinyerp.com-20140505043233-2t2e2vb1yv86hyxp
Diffstat (limited to 'addons/web')
87 files changed, 484 insertions, 1150 deletions
diff --git a/addons/web/__openerp__.py b/addons/web/__openerp__.py index 5941a6aa5b0..a4000856afe 100644 --- a/addons/web/__openerp__.py +++ b/addons/web/__openerp__.py @@ -14,81 +14,7 @@ This module provides the core of the OpenERP Web Client. 'data': [ 'views/webclient_templates.xml', ], - 'js': [ - "static/lib/es5-shim/es5-shim.min.js", - "static/lib/datejs/globalization/en-US.js", - "static/lib/datejs/core.js", - "static/lib/datejs/parser.js", - "static/lib/datejs/sugarpak.js", - "static/lib/datejs/extras.js", - "static/lib/jquery/jquery.js", - "static/lib/jquery.form/jquery.form.js", - "static/lib/jquery.validate/jquery.validate.js", - "static/lib/jquery.ba-bbq/jquery.ba-bbq.js", - "static/lib/spinjs/spin.js", - "static/lib/jquery.autosize/jquery.autosize.js", - "static/lib/jquery.blockUI/jquery.blockUI.js", - "static/lib/jquery.hotkeys/jquery.hotkeys.js", - "static/lib/jquery.placeholder/jquery.placeholder.js", - "static/lib/jquery.ui/js/jquery-ui-1.9.1.custom.js", - "static/lib/jquery.ui.timepicker/js/jquery-ui-timepicker-addon.js", - "static/lib/jquery.ui.notify/js/jquery.notify.js", - "static/lib/jquery.deferred-queue/jquery.deferred-queue.js", - "static/lib/jquery.scrollTo/jquery.scrollTo-min.js", - "static/lib/jquery.textext/jquery.textext.js", - "static/lib/jquery.timeago/jquery.timeago.js", - "static/lib/bootstrap/js/bootstrap.js", - "static/lib/qweb/qweb2.js", - "static/lib/underscore/underscore.js", - "static/lib/underscore.string/lib/underscore.string.js", - "static/lib/backbone/backbone.js", - "static/lib/cleditor/jquery.cleditor.js", - "static/lib/py.js/lib/py.js", - "static/lib/select2/select2.js", - "static/src/js/openerpframework.js", - "static/src/js/boot.js", - "static/src/js/testing.js", - "static/src/js/pyeval.js", - "static/src/js/core.js", - "static/src/js/formats.js", - "static/src/js/chrome.js", - "static/src/js/views.js", - "static/src/js/data.js", - "static/src/js/data_export.js", - "static/src/js/search.js", - "static/src/js/view_list.js", - "static/src/js/view_form.js", - "static/src/js/view_list_editable.js", - "static/src/js/view_tree.js", - ], - 'css' : [ - "static/lib/jquery.ui.bootstrap/css/custom-theme/jquery-ui-1.9.0.custom.css", - "static/lib/jquery.ui.timepicker/css/jquery-ui-timepicker-addon.css", - "static/lib/jquery.ui.notify/css/ui.notify.css", - "static/lib/jquery.textext/jquery.textext.css", - "static/lib/fontawesome/css/font-awesome.css", - "static/lib/bootstrap/css/bootstrap.css", - "static/lib/select2/select2.css", - "static/src/css/base.css", - "static/src/css/data_export.css", - "static/lib/cleditor/jquery.cleditor.css", - ], 'qweb' : [ "static/src/xml/*.xml", ], - 'test': [ - "static/test/testing.js", - "static/test/framework.js", - "static/test/registry.js", - "static/test/form.js", - "static/test/data.js", - "static/test/list-utils.js", - "static/test/formats.js", - "static/test/rpc-misordered.js", - "static/test/evals.js", - "static/test/search.js", - "static/test/list.js", - "static/test/list-editable.js", - "static/test/mutex.js" - ], } diff --git a/addons/web/controllers/__init__.py b/addons/web/controllers/__init__.py index 74c27518ece..12a7e529b67 100644 --- a/addons/web/controllers/__init__.py +++ b/addons/web/controllers/__init__.py @@ -1,2 +1 @@ from . import main -from . import testing diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index f2101b27fd3..6dcc069fcc2 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -31,6 +31,7 @@ except ImportError: import openerp import openerp.modules.registry +from openerp.addons.base.ir.ir_qweb import AssetsBundle, QWebTemplateNotFound from openerp.tools.translate import _ from openerp import http @@ -52,50 +53,6 @@ env.filters["json"] = simplejson.dumps # OpenERP Web helpers #---------------------------------------------------------- -def rjsmin(script): - """ Minify js with a clever regex. - Taken from http://opensource.perlig.de/rjsmin - Apache License, Version 2.0 """ - def subber(match): - """ Substitution callback """ - groups = match.groups() - return ( - groups[0] or - groups[1] or - groups[2] or - groups[3] or - (groups[4] and '\n') or - (groups[5] and ' ') or - (groups[6] and ' ') or - (groups[7] and ' ') or - '' - ) - - result = re.sub( - r'([^\047"/\000-\040]+)|((?:(?:\047[^\047\\\r\n]*(?:\\(?:[^\r\n]|\r?' - r'\n|\r)[^\047\\\r\n]*)*\047)|(?:"[^"\\\r\n]*(?:\\(?:[^\r\n]|\r?\n|' - r'\r)[^"\\\r\n]*)*"))[^\047"/\000-\040]*)|(?:(?<=[(,=:\[!&|?{};\r\n]' - r')(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/' - r'))*((?:/(?![\r\n/*])[^/\\\[\r\n]*(?:(?:\\[^\r\n]|(?:\[[^\\\]\r\n]*' - r'(?:\\[^\r\n][^\\\]\r\n]*)*\]))[^/\\\[\r\n]*)*/)[^\047"/\000-\040]*' - r'))|(?:(?<=[\000-#%-,./:-@\[-^`{-~-]return)(?:[\000-\011\013\014\01' - r'6-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*((?:/(?![\r\n/*])[^/' - r'\\\[\r\n]*(?:(?:\\[^\r\n]|(?:\[[^\\\]\r\n]*(?:\\[^\r\n][^\\\]\r\n]' - r'*)*\]))[^/\\\[\r\n]*)*/)[^\047"/\000-\040]*))|(?<=[^\000-!#%&(*,./' - r':-@\[\\^`{|~])(?:[\000-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/' - r'*][^*]*\*+)*/))*(?:((?:(?://[^\r\n]*)?[\r\n]))(?:[\000-\011\013\01' - r'4\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))*)+(?=[^\000-\040"#' - r'%-\047)*,./:-@\\-^`|-~])|(?<=[^\000-#%-,./:-@\[-^`{-~-])((?:[\000-' - r'\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)))+(?=[^' - r'\000-#%-,./:-@\[-^`{-~-])|(?<=\+)((?:[\000-\011\013\014\016-\040]|' - r'(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)))+(?=\+)|(?<=-)((?:[\000-\011\0' - r'13\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/)))+(?=-)|(?:[\0' - r'00-\011\013\014\016-\040]|(?:/\*[^*]*\*+(?:[^/*][^*]*\*+)*/))+|(?:' - r'(?:(?://[^\r\n]*)?[\r\n])(?:[\000-\011\013\014\016-\040]|(?:/\*[^*' - r']*\*+(?:[^/*][^*]*\*+)*/))*)+', subber, '\n%s\n' % script - ).strip() - return result - db_list = http.db_list db_monodb = http.db_monodb @@ -309,45 +266,6 @@ def concat_xml(file_list): root.append(child) return ElementTree.tostring(root, 'utf-8'), checksum.hexdigest() -def concat_files(file_list, reader=None, intersperse=""): - """ Concatenates contents of all provided files - - :param list(str) file_list: list of files to check - :param function reader: reading procedure for each file - :param str intersperse: string to intersperse between file contents - :returns: (concatenation_result, checksum) - :rtype: (str, str) - """ - checksum = hashlib.new('sha1') - if not file_list: - return '', checksum.hexdigest() - - if reader is None: - def reader(f): - import codecs - with codecs.open(f, 'rb', "utf-8-sig") as fp: - return fp.read().encode("utf-8") - - files_content = [] - for fname in file_list: - contents = reader(fname) - checksum.update(contents) - files_content.append(contents) - - files_concat = intersperse.join(files_content) - return files_concat, checksum.hexdigest() - -concat_js_cache = {} - -def concat_js(file_list): - content, checksum = concat_files(file_list, intersperse=';') - if checksum in concat_js_cache: - content = concat_js_cache[checksum] - else: - content = rjsmin(content) - concat_js_cache[checksum] = content - return content, checksum - def fs2web(path): """convert FS path into web path""" return '/'.join(path.split(os.path.sep)) @@ -371,30 +289,17 @@ def manifest_glob(extension, addons=None, db=None, include_remotes=False): r.append((None, pattern)) else: for path in glob.glob(os.path.normpath(os.path.join(addons_path, addon, pattern))): - # Hack for IE, who limit 288Ko, 4095 rules, 31 sheets - # http://support.microsoft.com/kb/262161/en - if pattern == "static/lib/bootstrap/css/bootstrap.css": - if include_remotes: - r.insert(0, (None, fs2web(path[len(addons_path):]))) - else: - r.append((path, fs2web(path[len(addons_path):]))) + r.append((path, fs2web(path[len(addons_path):]))) return r -def manifest_list(extension, mods=None, db=None, debug=False): +def manifest_list(extension, mods=None, db=None, debug=None): """ list ressources to load specifying either: mods: a comma separated string listing modules db: a database name (return all installed modules in that database) """ + if debug is not None: + _logger.warning("openerp.addons.web.main.manifest_list(): debug parameter is deprecated") files = manifest_glob(extension, addons=mods, db=db, include_remotes=True) - if not debug: - path = '/web/webclient/' + extension - if mods is not None: - path += '?' + werkzeug.url_encode({'mods': mods}) - elif db: - path += '?' + werkzeug.url_encode({'db': db}) - - remotes = [wp for fp, wp in files if fp is None] - return [path] + remotes return [wp for _fp, wp in files] def get_last_modified(files): @@ -411,7 +316,7 @@ def get_last_modified(files): for f in files) return datetime.datetime(1970, 1, 1) -def make_conditional(response, last_modified=None, etag=None): +def make_conditional(response, last_modified=None, etag=None, max_age=0): """ Makes the provided response conditional based upon the request, and mandates revalidation from clients @@ -426,7 +331,7 @@ def make_conditional(response, last_modified=None, etag=None): :rtype: werkzeug.wrappers.Response """ response.cache_control.must_revalidate = True - response.cache_control.max_age = 0 + response.cache_control.max_age = max_age if last_modified: response.last_modified = last_modified if etag: @@ -603,6 +508,7 @@ html_template = """<!DOCTYPE html> <title>OpenERP</title> <link rel="shortcut icon" href="/web/static/src/img/favicon.ico" type="image/x-icon"/> <link rel="stylesheet" href="/web/static/src/css/full.css" /> + %(css)s %(js)s <script type="text/javascript"> @@ -621,26 +527,16 @@ html_template = """<!DOCTYPE html> </html> """ -def render_bootstrap_template(template, values=None, debug=False, db=None, **kw): - if not db: - db = request.db - if request.debug: - debug = True +def render_bootstrap_template(template, values=None, **kw): if values is None: - values = {} - values['debug'] = debug - values['current_db'] = db + values = dict() try: values['databases'] = http.db_list() except openerp.exceptions.AccessDenied: values['databases'] = None - for res in ['js', 'css']: - if res not in values: - values[res] = manifest_list(res, db=db, debug=debug) - if 'modules' not in values: - values['modules'] = module_boot(db=db) + values['modules'] = module_boot() values['modules'] = simplejson.dumps(values['modules']) return request.render(template, values, **kw) @@ -682,9 +578,11 @@ class Home(http.Controller): redirect = '/web?' + request.httprequest.query_string values['redirect'] = redirect if request.httprequest.method == 'POST': + old_uid = request.uid uid = request.session.authenticate(request.session.db, request.params['login'], request.params['password']) if uid is not False: return http.redirect_with_hash(redirect) + request.uid = old_uid values['error'] = "Wrong login/password" return render_bootstrap_template('web.login', values) @@ -692,83 +590,47 @@ class Home(http.Controller): def login(self, db, login, key, redirect="/web", **kw): return login_and_redirect(db, login, key, redirect_url=redirect) -class WebClient(http.Controller): - - @http.route('/web/webclient/csslist', type='json', auth="none") - def csslist(self, mods=None): - return manifest_list('css', mods=mods) - - @http.route('/web/webclient/jslist', type='json', auth="none") - def jslist(self, mods=None): - return manifest_list('js', mods=mods) - - @http.route('/web/webclient/qweblist', type='json', auth="none") - def qweblist(self, mods=None): - return manifest_list('qweb', mods=mods) - - @http.route('/web/webclient/css', type='http', auth="none") - def css(self, mods=None, db=None): - files = list(manifest_glob('css', addons=mods, db=db)) - last_modified = get_last_modified(f[0] for f in files) - if request.httprequest.if_modified_since and request.httprequest.if_modified_since >= last_modified: - return werkzeug.wrappers.Response(status=304) - - file_map = dict(files) - - rx_import = re.compile(r"""@import\s+('|")(?!'|"|/|https?://)""", re.U) - rx_url = re.compile(r"""url\s*\(\s*('|"|)(?!'|"|/|https?://|data:)""", re.U) - - def reader(f): - """read the a css file and absolutify all relative uris""" - with open(f, 'rb') as fp: - data = fp.read().decode('utf-8') - - path = file_map[f] - web_dir = os.path.dirname(path) - - data = re.sub( - rx_import, - r"""@import \1%s/""" % (web_dir,), - data, - ) - - data = re.sub( - rx_url, - r"url(\1%s/" % (web_dir,), - data, - ) - return data.encode('utf-8') + @http.route('/web/js/<xmlid>', type='http', auth="public") + def js_bundle(self, xmlid, **kw): + # manifest backward compatible mode, to be removed + values = {'manifest_list': manifest_list} + try: + assets_html = request.render(xmlid, lazy=False, qcontext=values) + except QWebTemplateNotFound: + return request.not_found() + bundle = AssetsBundle(xmlid, assets_html, debug=request.debug) - content, checksum = concat_files((f[0] for f in files), reader) + response = request.make_response( + bundle.js(), [('Content-Type', 'application/javascript')]) - # move up all @import and @charset rules to the top - matches = [] - def push(matchobj): - matches.append(matchobj.group(0)) - return '' + # TODO: check that we don't do weird lazy overriding of __call__ which break body-removal + return make_conditional( + response, bundle.last_modified, bundle.checksum, max_age=60*60*24) - content = re.sub(re.compile("(@charset.+;$)", re.M), push, content) - content = re.sub(re.compile("(@import.+;$)", re.M), push, content) + @http.route('/web/css/<xmlid>', type='http', auth='public') + def css_bundle(self, xmlid, **kw): + values = {'manifest_list': manifest_list} # manifest backward compatible mode, to be removed + try: + assets_html = request.render(xmlid, lazy=False, qcontext=values) + except QWebTemplateNotFound: + return request.not_found() + bundle = AssetsBundle(xmlid, assets_html, debug=request.debug) - matches.append(content) - content = '\n'.join(matches) + response = request.make_response( + bundle.css(), [('Content-Type', 'text/css')]) return make_conditional( - request.make_response(content, [('Content-Type', 'text/css')]), - last_modified, checksum) + response, bundle.last_modified, bundle.checksum, max_age=60*60*24) - @http.route('/web/webclient/js', type='http', auth="none") - def js(self, mods=None, db=None): - files = [f[0] for f in manifest_glob('js', addons=mods, db=db)] - last_modified = get_last_modified(files) - if request.httprequest.if_modified_since and request.httprequest.if_modified_since >= last_modified: - return werkzeug.wrappers.Response(status=304) +class WebClient(http.Controller): - content, checksum = concat_js(files) + @http.route('/web/webclient/csslist', type='json', auth="none") + def csslist(self, mods=None): + return manifest_list('css', mods=mods) - return make_conditional( - request.make_response(content, [('Content-Type', 'application/javascript')]), - last_modified, checksum) + @http.route('/web/webclient/jslist', type='json', auth="none") + def jslist(self, mods=None): + return manifest_list('js', mods=mods) @http.route('/web/webclient/qweb', type='http', auth="none") def qweb(self, mods=None, db=None): @@ -843,6 +705,10 @@ class WebClient(http.Controller): def version_info(self): return openerp.service.common.exp_version() + @http.route('/web/tests', type='http', auth="none") + def index(self, mod=None, **kwargs): + return request.render('web.qunit_suite') + class Proxy(http.Controller): @http.route('/web/proxy/load', type='json', auth="none") @@ -879,8 +745,64 @@ class Database(http.Controller): def manager(self, **kw): # TODO: migrate the webclient's database manager to server side views request.session.logout() - js = "\n ".join('<script type="text/javascript" src="%s"></script>' % i for i in manifest_list('js', debug=request.debug)) - css = "\n ".join('<link rel="stylesheet" href="%s">' % i for i in manifest_list('css', debug=request.debug)) + css = """ + <link href="/web/static/lib/fontawesome/css/font-awesome.css" rel="stylesheet"/> + <link href="/web/static/lib/cleditor/jquery.cleditor.css" rel="stylesheet"/> + <link href="/web/static/lib/jquery.textext/jquery.textext.css" rel="stylesheet"/> + <link href="/web/static/lib/select2/select2.css" rel="stylesheet"/> + <link href="/web/static/lib/jquery.ui.bootstrap/css/custom-theme/jquery-ui-1.9.0.custom.css" rel="stylesheet"/> + <link href="/web/static/lib/jquery.ui.timepicker/css/jquery-ui-timepicker-addon.css" rel="stylesheet"/> + <link href="/web/static/lib/jquery.ui.notify/css/ui.notify.css" rel="stylesheet"/> + <link href="/web/static/lib/bootstrap/css/bootstrap.css" rel="stylesheet"/> + <link href="/web/static/src/css/base.css" rel="stylesheet"/> + <link href="/web/static/src/css/data_export.css" rel="stylesheet"/> + <link href="/base/static/src/css/modules.css" rel="stylesheet"/>""" + js = """ + <script src="/web/static/lib/es5-shim/es5-shim.min.js" type="text/javascript"></script> + <script src="/web/static/lib/underscore/underscore.js" type="text/javascript"></script> + <script src="/web/static/lib/underscore.string/lib/underscore.string.js" type="text/javascript"></script> + <script src="/web/static/lib/datejs/globalization/en-US.js" type="text/javascript"></script> + <script src="/web/static/lib/spinjs/spin.js" type="text/javascript"></script> + <script src="/web/static/lib/jquery/jquery.js" type="text/javascript"></script> + <script src="/web/static/lib/jquery.blockUI/jquery.blockUI.js" type="text/javascript"></script> + <script src="/web/static/lib/jquery.hotkeys/jquery.hotkeys.js" type="text/javascript"></script> + <script src="/web/static/lib/jquery.placeholder/jquery.placeholder.js" type="text/javascript"></script> + <script src="/web/static/lib/jquery.timeago/jquery.timeago.js" type="text/javascript"></script> + <script src="/web/static/lib/jquery.form/jquery.form.js" type="text/javascript"></script> + <script src="/web/static/lib/jquery.ba-bbq/jquery.ba-bbq.js" type="text/javascript"></script> + <script src="/web/static/lib/datejs/core.js" type="text/javascript"></script> + <script src="/web/static/lib/datejs/parser.js" type="text/javascript"></script> + <script src="/web/static/lib/datejs/sugarpak.js" type="text/javascript"></script> + <script src="/web/static/lib/datejs/extras.js" type="text/javascript"></script> + <script src="/web/static/lib/jquery.validate/jquery.validate.js" type="text/javascript"></script> + <script src="/web/static/lib/jquery.autosize/jquery.autosize.js" type="text/javascript"></script> + <script src="/web/static/lib/jquery.scrollTo/jquery.scrollTo-min.js" type="text/javascript"></script> + <script src="/web/static/lib/cleditor/jquery.cleditor.js" type="text/javascript"></script> + <script src="/web/static/lib/jquery.textext/jquery.textext.js" type="text/javascript"></script> + <script src="/web/static/lib/select2/select2.js" type="text/javascript"></script> + <script src="/web/static/lib/jquery.ui/js/jquery-ui-1.9.1.custom.js" type="text/javascript"></script> + <script src="/web/static/lib/jquery.ui.timepicker/js/jquery-ui-timepicker-addon.js" type="text/javascript"></script> + <script src="/web/static/lib/jquery.ui.notify/js/jquery.notify.js" type="text/javascript"></script> + <script src="/web/static/lib/bootstrap/js/bootstrap.js" type="text/javascript"></script> + <script src="/web/static/lib/backbone/backbone.js" type="text/javascript"></script> + <script src="/web/static/lib/qweb/qweb2.js" type="text/javascript"></script> + <script src="/web/static/src/js/openerpframework.js" type="text/javascript"></script> + <script src="/web/static/lib/py.js/lib/py.js" type="text/javascript"></script> + <script src="/web/static/src/js/boot.js" type="text/javascript"></script> + <script src="/web/static/src/js/testing.js" type="text/javascript"></script> + <script src="/web/static/src/js/pyeval.js" type="text/javascript"></script> + <script src="/web/static/src/js/core.js" type="text/javascript"></script> + <script src="/web/static/src/js/formats.js" type="text/javascript"></script> + <script src="/web/static/src/js/chrome.js" type="text/javascript"></script> + <script src="/web/static/src/js/views.js" type="text/javascript"></script> + <script src="/web/static/src/js/data.js" type="text/javascript"></script> + <script src="/web/static/src/js/data_export.js" type="text/javascript"></script> + <script src="/web/static/src/js/search.js" type="text/javascript"></script> + <script src="/web/static/src/js/view_list.js" type="text/javascript"></script> + <script src="/web/static/src/js/view_form.js" type="text/javascript"></script> + <script src="/web/static/src/js/view_list_editable.js" type="text/javascript"></script> + <script src="/web/static/src/js/view_tree.js" type="text/javascript"></script> + <script src="/base/static/src/js/apps.js" type="text/javascript"></script>""" r = html_template % { 'js': js, @@ -1088,16 +1010,7 @@ class Menu(http.Controller): """ s = request.session Menus = s.model('ir.ui.menu') - # If a menu action is defined use its domain to get the root menu items - user_menu_id = s.model('res.users').read([s.uid], ['menu_id'], - request.context)[0]['menu_id'] - menu_domain = [('parent_id', '=', False)] - if user_menu_id: - domain_string = s.model('ir.actions.act_window').read( - [user_menu_id[0]], ['domain'],request.context)[0]['domain'] - if domain_string: - menu_domain = ast.literal_eval(domain_string) return Menus.search(menu_domain, 0, False, False, request.context) diff --git a/addons/web/controllers/testing.py b/addons/web/controllers/testing.py deleted file mode 100644 index a51b619d49f..00000000000 --- a/addons/web/controllers/testing.py +++ /dev/null @@ -1,159 +0,0 @@ -# coding=utf-8 -# -*- encoding: utf-8 -*- - -import glob -import itertools -import json -import operator -import os - -from mako.template import Template -from openerp.modules import module -from openerp import http -from openerp.http import request - -from .main import module_topological_sort - -NOMODULE_TEMPLATE = Template(u"""<!DOCTYPE html> -<html> - <head> - <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/> - <meta http-equiv="content-type" content="text/html; charset=utf-8" /> - <title>OpenERP Testing</title> - </head> - <body> - <form action="/web/tests" method="GET"> - <button name="mod" value="*">Run all tests</button> - <ul> - % for name, module in modules: - <li>${name} <button name="mod" value="${module}"> - Run Tests</button></li> - % endfor - </ul> - </form> - </body> -</html> -""", default_filters=['h']) -NOTFOUND = Template(u""" -<p>Unable to find the module [${module}], please check that the module - name is correct and the module is on OpenERP's path.</p> -<a href="/web/tests"><< Back to tests</a> -""", default_filters=['h']) -TESTING = Template(u"""<!DOCTYPE html> -<html style="height: 100%"> -<%def name="to_path(module, p)">/${module}/${p}</%def> -<head> - <meta http-equiv="X-UA-Compatible" content="IE=edge"/> - <meta http-equiv="content-type" content="text/html; charset=utf-8" /> - <title>OpenERP Web Tests</title> - <link rel="shortcut icon" href="/web/static/src/img/favicon.ico" type="image/x-icon"/> - - <link rel="stylesheet" href="/web/static/lib/qunit/qunit.css"> - <script src="/web/static/lib/qunit/qunit.js"></script> - - <script type="text/javascript"> - // List of modules, each module is preceded by its dependencies - var oe_all_dependencies = ${dependencies | n}; - QUnit.config.testTimeout = 5 * 60 * 1000; - </script> -</head> -<body id="oe" class="openerp"> - <div id="qunit"></div> - <div id="qunit-fixture"></div> -</body> -<!-- TODO xmo please use the regular template even for testing --> -% for module, jss, tests, templates in files: - % for js in jss: - % if not js.endswith('/apps.js'): - <script src="${to_path(module, js)}"></script> - % endif - % endfor - % if tests or templates: - <script> - openerp.testing.current_module = "${module}"; - % for template in templates: - openerp.testing.add_template("${to_path(module, template)}"); - % endfor - </script> - % endif - % if tests: - % for test in tests: - <script type="text/javascript" src="${to_path(module, test)}"></script> - % endfor - % endif -% endfor -</html> -""", default_filters=['h']) - -class TestRunnerController(http.Controller): - - @http.route('/web/tests', type='http', auth="none") - def index(self, mod=None, **kwargs): - ms = module.get_modules() - manifests = dict( - (name, desc) - for name, desc in zip(ms, map(self.load_manifest, ms)) - if desc # remove not-actually-openerp-modules - ) - - if not mod: - return NOMODULE_TEMPLATE.render(modules=( - (manifest['name'], name) - for name, manifest in manifests.iteritems() - if any(testfile.endswith('.js') - for testfile in manifest['test']) - )) - sorted_mods = module_topological_sort(dict( - (name, manifest.get('depends', [])) - for name, manifest in manifests.iteritems() - )) - # to_load and to_test should be zippable lists of the same length. - # A falsy value in to_test indicate nothing to test at that index (just - # load the corresponding part of to_load) - to_test = sorted_mods - if mod != '*': - if mod not in manifests: - return request.not_found(NOTFOUND.render(module=mod)) - idx = sorted_mods.index(mod) - to_test = [None] * len(sorted_mods) - to_test[idx] = mod - - tests_candicates = [ - filter(lambda path: path.endswith('.js'), - manifests[mod]['test'] if mod else []) - for mod in to_test] - # remove trailing test-less modules - tests = reversed(list( - itertools.dropwhile( - operator.not_, - reversed(tests_candicates)))) - - files = [ - (mod, manifests[mod]['js'], tests, manifests[mod]['qweb']) - for mod, tests in itertools.izip(sorted_mods, tests) - ] - - return TESTING.render(files=files, dependencies=json.dumps( - [name for name in sorted_mods - if module.get_module_resource(name, 'static') - if manifests[name]['js']])) - - def load_manifest(self, name): - manifest = module.load_information_from_description_file(name) - if manifest: - path = module.get_module_path(name) - manifest['js'] = list( - self.expand_patterns(path, manifest.get('js', []))) - manifest['test'] = list( - self.expand_patterns(path, manifest.get('test', []))) - manifest['qweb'] = list( - self.expand_patterns(path, manifest.get('qweb', []))) - return manifest - - def expand_patterns(self, root, patterns): - for pattern in patterns: - normalized_pattern = os.path.normpath(os.path.join(root, pattern)) - for path in glob.glob(normalized_pattern): - # replace OS path separators (from join & normpath) by URI ones - yield path[len(root):].replace(os.path.sep, '/') - diff --git a/addons/web/i18n/ar.po b/addons/web/i18n/ar.po index 4b2083866bc..5500e9103f1 100644 --- a/addons/web/i18n/ar.po +++ b/addons/web/i18n/ar.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:38+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:52+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/bg.po b/addons/web/i18n/bg.po index 4877f186483..8cfebbfe61f 100644 --- a/addons/web/i18n/bg.po +++ b/addons/web/i18n/bg.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:38+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:52+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/bn.po b/addons/web/i18n/bn.po index a2e1317df9f..b2aa1b04340 100644 --- a/addons/web/i18n/bn.po +++ b/addons/web/i18n/bn.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:38+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:52+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/bs.po b/addons/web/i18n/bs.po index 3cfb2e85bdf..b35ac75513d 100644 --- a/addons/web/i18n/bs.po +++ b/addons/web/i18n/bs.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:38+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:52+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/ca.po b/addons/web/i18n/ca.po index 0e1fe377fa3..5a772e2ca2f 100644 --- a/addons/web/i18n/ca.po +++ b/addons/web/i18n/ca.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:38+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:52+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/cs.po b/addons/web/i18n/cs.po index 4dac86f7964..acd387fa39a 100644 --- a/addons/web/i18n/cs.po +++ b/addons/web/i18n/cs.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:38+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:52+0000\n" "X-Generator: Launchpad (build 16985)\n" "X-Poedit-Language: Czech\n" diff --git a/addons/web/i18n/da.po b/addons/web/i18n/da.po index 758400611c0..8bf7a9f5086 100644 --- a/addons/web/i18n/da.po +++ b/addons/web/i18n/da.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:38+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:52+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/de.po b/addons/web/i18n/de.po index b3aa01c56fd..cb5f6fa9e6e 100644 --- a/addons/web/i18n/de.po +++ b/addons/web/i18n/de.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:39+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:52+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/en_AU.po b/addons/web/i18n/en_AU.po index 01509d52bbf..f92d5c282b3 100644 --- a/addons/web/i18n/en_AU.po +++ b/addons/web/i18n/en_AU.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:40+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:53+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/en_GB.po b/addons/web/i18n/en_GB.po index bd0a20a5f7a..88f6a33907f 100644 --- a/addons/web/i18n/en_GB.po +++ b/addons/web/i18n/en_GB.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:40+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:53+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/es.po b/addons/web/i18n/es.po index 2b9303e87a4..52e1f5dbadf 100644 --- a/addons/web/i18n/es.po +++ b/addons/web/i18n/es.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:40+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:53+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/es_CL.po b/addons/web/i18n/es_CL.po index bc342c8bcd0..d54195b2ffe 100644 --- a/addons/web/i18n/es_CL.po +++ b/addons/web/i18n/es_CL.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:40+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:53+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/es_CR.po b/addons/web/i18n/es_CR.po index 0ef3e7a0975..862531a7b68 100644 --- a/addons/web/i18n/es_CR.po +++ b/addons/web/i18n/es_CR.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:40+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:53+0000\n" "X-Generator: Launchpad (build 16985)\n" "Language: es\n" diff --git a/addons/web/i18n/es_DO.po b/addons/web/i18n/es_DO.po index 86d6f43f20d..96e28c1ae9d 100644 --- a/addons/web/i18n/es_DO.po +++ b/addons/web/i18n/es_DO.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:40+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:53+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/es_EC.po b/addons/web/i18n/es_EC.po index c4fa890182f..e634ab5c076 100644 --- a/addons/web/i18n/es_EC.po +++ b/addons/web/i18n/es_EC.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:40+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:54+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/es_MX.po b/addons/web/i18n/es_MX.po index 84298e0bce1..ec8bf391953 100644 --- a/addons/web/i18n/es_MX.po +++ b/addons/web/i18n/es_MX.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:40+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:54+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/es_PE.po b/addons/web/i18n/es_PE.po index 3ec2d60e5bb..71d37a83395 100644 --- a/addons/web/i18n/es_PE.po +++ b/addons/web/i18n/es_PE.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:40+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:54+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/et.po b/addons/web/i18n/et.po index b55354266a8..6efd5e392d3 100644 --- a/addons/web/i18n/et.po +++ b/addons/web/i18n/et.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:38+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:52+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/eu.po b/addons/web/i18n/eu.po index 4e059ff6b09..b29f74e3b76 100644 --- a/addons/web/i18n/eu.po +++ b/addons/web/i18n/eu.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:38+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:52+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/fa.po b/addons/web/i18n/fa.po index 309ac285423..c190534f18c 100644 --- a/addons/web/i18n/fa.po +++ b/addons/web/i18n/fa.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:39+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:53+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/fi.po b/addons/web/i18n/fi.po index 821429ebae1..827eb9edac0 100644 --- a/addons/web/i18n/fi.po +++ b/addons/web/i18n/fi.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:38+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:52+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/fr.po b/addons/web/i18n/fr.po index d4efe82bbcf..5cc8a8785a1 100644 --- a/addons/web/i18n/fr.po +++ b/addons/web/i18n/fr.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:38+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:52+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/fr_CA.po b/addons/web/i18n/fr_CA.po index 840dfb3b07e..5860eea177e 100644 --- a/addons/web/i18n/fr_CA.po +++ b/addons/web/i18n/fr_CA.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:40+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:53+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/gl.po b/addons/web/i18n/gl.po index 07f34b44994..108125b647c 100644 --- a/addons/web/i18n/gl.po +++ b/addons/web/i18n/gl.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:39+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:52+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/gu.po b/addons/web/i18n/gu.po index dbcbf6bab93..5e064c2f121 100644 --- a/addons/web/i18n/gu.po +++ b/addons/web/i18n/gu.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:39+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:52+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/he.po b/addons/web/i18n/he.po index b9f64d0585c..6982efa679a 100644 --- a/addons/web/i18n/he.po +++ b/addons/web/i18n/he.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:39+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:52+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/hi.po b/addons/web/i18n/hi.po index 0db5502ce67..b8d43b0547c 100644 --- a/addons/web/i18n/hi.po +++ b/addons/web/i18n/hi.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:39+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:52+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/hr.po b/addons/web/i18n/hr.po index 375bd8d6a12..d47cefc95d3 100644 --- a/addons/web/i18n/hr.po +++ b/addons/web/i18n/hr.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:40+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:53+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/hu.po b/addons/web/i18n/hu.po index e710ce1d9d9..f1a92ab0186 100644 --- a/addons/web/i18n/hu.po +++ b/addons/web/i18n/hu.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:39+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:52+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/id.po b/addons/web/i18n/id.po index 86a783057cf..ecc7f580c42 100644 --- a/addons/web/i18n/id.po +++ b/addons/web/i18n/id.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:39+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:52+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/it.po b/addons/web/i18n/it.po index 124054cea6b..e37592a6b89 100644 --- a/addons/web/i18n/it.po +++ b/addons/web/i18n/it.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:39+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:52+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/ja.po b/addons/web/i18n/ja.po index 7c80f9b942a..81fa526f501 100644 --- a/addons/web/i18n/ja.po +++ b/addons/web/i18n/ja.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:39+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:52+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/ka.po b/addons/web/i18n/ka.po index b13eefea7f6..bbfbdad689e 100644 --- a/addons/web/i18n/ka.po +++ b/addons/web/i18n/ka.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:39+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:52+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/ko.po b/addons/web/i18n/ko.po index 7788c990963..c2445599c55 100644 --- a/addons/web/i18n/ko.po +++ b/addons/web/i18n/ko.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:39+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:52+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/lo.po b/addons/web/i18n/lo.po index 4559a6b28d5..8f4bf33bcd1 100644 --- a/addons/web/i18n/lo.po +++ b/addons/web/i18n/lo.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:39+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:52+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/lt.po b/addons/web/i18n/lt.po index afbd9a9e1d6..384a22b5d7a 100644 --- a/addons/web/i18n/lt.po +++ b/addons/web/i18n/lt.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:39+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:53+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/lv.po b/addons/web/i18n/lv.po index e32d8f57708..a1358f54205 100644 --- a/addons/web/i18n/lv.po +++ b/addons/web/i18n/lv.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:39+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:52+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/mk.po b/addons/web/i18n/mk.po index 1faa1f5a07c..70d9144d92f 100644 --- a/addons/web/i18n/mk.po +++ b/addons/web/i18n/mk.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:39+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:53+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/mn.po b/addons/web/i18n/mn.po index f9853570da9..09694dae1c4 100644 --- a/addons/web/i18n/mn.po +++ b/addons/web/i18n/mn.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:39+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:53+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/nb.po b/addons/web/i18n/nb.po index 9a2545585b2..7ff320014f4 100644 --- a/addons/web/i18n/nb.po +++ b/addons/web/i18n/nb.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:39+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:53+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/nl.po b/addons/web/i18n/nl.po index c55799ac9cb..3f1e2d7c5ac 100644 --- a/addons/web/i18n/nl.po +++ b/addons/web/i18n/nl.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:38+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:52+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/nl_BE.po b/addons/web/i18n/nl_BE.po index dc52a1a7d67..f6dc9d6f34f 100644 --- a/addons/web/i18n/nl_BE.po +++ b/addons/web/i18n/nl_BE.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:40+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:53+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/pl.po b/addons/web/i18n/pl.po index 8ee646f2dc7..df2dd4def17 100644 --- a/addons/web/i18n/pl.po +++ b/addons/web/i18n/pl.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:39+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:53+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/pt.po b/addons/web/i18n/pt.po index 6d781d7454e..a1716f3b7b0 100644 --- a/addons/web/i18n/pt.po +++ b/addons/web/i18n/pt.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:39+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:53+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/pt_BR.po b/addons/web/i18n/pt_BR.po index 1e0d6547806..c9e6cfab751 100644 --- a/addons/web/i18n/pt_BR.po +++ b/addons/web/i18n/pt_BR.po @@ -15,7 +15,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:40+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:53+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/ro.po b/addons/web/i18n/ro.po index 9b5aa45f1ca..2285aa1ec28 100644 --- a/addons/web/i18n/ro.po +++ b/addons/web/i18n/ro.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:39+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:53+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/ru.po b/addons/web/i18n/ru.po index 7b6ad85b2d3..7ab615d6312 100644 --- a/addons/web/i18n/ru.po +++ b/addons/web/i18n/ru.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:39+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:53+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/sk.po b/addons/web/i18n/sk.po index e191a83103e..e1ad376ba14 100644 --- a/addons/web/i18n/sk.po +++ b/addons/web/i18n/sk.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:40+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:53+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/sl.po b/addons/web/i18n/sl.po index e0d7ae00ef5..6c7dc176481 100644 --- a/addons/web/i18n/sl.po +++ b/addons/web/i18n/sl.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:40+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:53+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/sq.po b/addons/web/i18n/sq.po index 3309d32e981..1e338e967a5 100644 --- a/addons/web/i18n/sq.po +++ b/addons/web/i18n/sq.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:38+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:52+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/sr@latin.po b/addons/web/i18n/sr@latin.po index 733c2defc8f..88d941461c7 100644 --- a/addons/web/i18n/sr@latin.po +++ b/addons/web/i18n/sr@latin.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:40+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:54+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/sv.po b/addons/web/i18n/sv.po index 561fb58b765..9847bdf1ffe 100644 --- a/addons/web/i18n/sv.po +++ b/addons/web/i18n/sv.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:40+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:53+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/th.po b/addons/web/i18n/th.po index 9ea50242c2b..d61e828b1a7 100644 --- a/addons/web/i18n/th.po +++ b/addons/web/i18n/th.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:40+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:53+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/tr.po b/addons/web/i18n/tr.po index dff19630d9e..4decaa7f356 100644 --- a/addons/web/i18n/tr.po +++ b/addons/web/i18n/tr.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:40+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:53+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/uk.po b/addons/web/i18n/uk.po index 547d9eb3b72..92826b245bc 100644 --- a/addons/web/i18n/uk.po +++ b/addons/web/i18n/uk.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:40+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:53+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/vi.po b/addons/web/i18n/vi.po index a6fadf0ae53..6eef5cbad3c 100644 --- a/addons/web/i18n/vi.po +++ b/addons/web/i18n/vi.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:40+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:53+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/zh_CN.po b/addons/web/i18n/zh_CN.po index 2f6ded3b9b0..9bfc9ed283a 100644 --- a/addons/web/i18n/zh_CN.po +++ b/addons/web/i18n/zh_CN.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:40+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:54+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/i18n/zh_TW.po b/addons/web/i18n/zh_TW.po index 06ab836e14f..80de08053cb 100644 --- a/addons/web/i18n/zh_TW.po +++ b/addons/web/i18n/zh_TW.po @@ -14,7 +14,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2014-04-18 06:40+0000\n" +"X-Launchpad-Export-Date: 2014-04-26 06:53+0000\n" "X-Generator: Launchpad (build 16985)\n" #. module: web diff --git a/addons/web/static/lib/fontawesome/css/font-awesome.css b/addons/web/static/lib/fontawesome/css/font-awesome.css index 7fa0e6f467b..35da66e7484 100644 --- a/addons/web/static/lib/fontawesome/css/font-awesome.css +++ b/addons/web/static/lib/fontawesome/css/font-awesome.css @@ -207,6 +207,9 @@ .fa-star:before { content: "\f005"; } +. fa-tasks:before { + content: "\f0ae"; +} .fa-star-o:before { content: "\f006"; } @@ -237,6 +240,12 @@ .fa-search-minus:before { content: "\f010"; } +.fa-adn:before { + content: "\f170"; +} +. fa-times:before { + content: "\f00d"; +} .fa-power-off:before { content: "\f011"; } @@ -308,6 +317,9 @@ .fa-qrcode:before { content: "\f029"; } +.fa-calendar:before { + content: "\f073"; +} .fa-barcode:before { content: "\f02a"; } @@ -759,6 +771,9 @@ .fa-pinterest:before { content: "\f0d2"; } +.fa-pencil-square-o:before { + content: "\f044"; +} .fa-pinterest-square:before { content: "\f0d3"; } diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css index 916e4ffe9ba..8534927def1 100644 --- a/addons/web/static/src/css/base.css +++ b/addons/web/static/src/css/base.css @@ -1,4 +1,4 @@ -@charset "utf-8"; +@charset "UTF-8"; @font-face { font-family: "mnmliconsRegular"; src: url("/web/static/src/font/mnmliconsv21-webfont.eot") format("eot"); @@ -79,7 +79,7 @@ vertical-align: top; } .openerp .oe_title { - width: 50%; + width: 38%; float: left; } .openerp .oe_title:after { @@ -314,56 +314,58 @@ .openerp .oe_form_dirty button.oe_highlight_on_dirty:hover { background: #ed6f6a; } -.openerp .oe_button_box { - width: 400px; - text-align: left; -} -.openerp .oe_button_box .oe_stat_button:hover { - background: #7c7bad; - color: white; -} -.openerp .oe_button_box .oe_stat_button:hover .fa { - color: white; -} -.openerp .oe_button_box .oe_stat_button { +.openerp .oe_stat_button { font-weight: normal; - display: inline-table; - width: 33% !important; - height: 42px; + width: 132px !important; + height: 40px; + color: #666666; margin: 0px -1px -1px 0px; padding: 0; - color: #666666; border: 1px solid #dddddd; border-radius: 0; box-shadow: none; background: white; } -.openerp .oe_button_box .oe_stat_button > div { +.openerp .oe_stat_button > div { display: table-cell; vertical-align: middle; text-align: left; padding: 0; line-height: 120%; } -.openerp .oe_button_box .oe_stat_button .stat_button_icon { +.openerp .oe_stat_button .stat_button_icon { color: #7c7bad; font-size: 24px; padding: 0px 3px; width: 37px; text-align: center; } -.openerp .oe_button_box .oe_stat_button .oe_form_field_percent_pie { +.openerp .oe_stat_button .oe_form_field_percent_pie { width: 42px; } -.openerp .oe_button_box .oe_stat_button .oe_form_field_bar_chart { +.openerp .oe_stat_button .oe_form_field_bar_chart { width: 42px; } -.openerp .oe_button_box .oe_stat_button svg { +.openerp .oe_stat_button svg { width: 38px; height: 38px; display: inline; vertical-align: middle; } +.openerp .oe_stat_button:hover { + background: #7c7bad; + color: white; +} +.openerp .oe_stat_button:hover .fa { + color: white; +} +.openerp .oe_button_box { + width: 400px; + text-align: right; +} +.openerp .oe_button_box .oe_stat_button { + display: inline-table; +} .openerp .oe_avatar > img { max-height: 90px; max-width: 90px; @@ -720,7 +722,7 @@ border-bottom-left-radius: 8px; } .openerp .oe_notification { - z-index: 1050; + z-index: 1500; } .openerp .oe_webclient_timezone_notification a { color: white; @@ -2742,13 +2744,15 @@ padding: 3px 6px; white-space: pre-line; } -.openerp .oe_list_content > tbody > tr > td > button.btn_img, .openerp .oe_list_content > tbody > tr > th > button.btn_img { +.openerp .oe_list_content > tbody > tr > td > button, .openerp .oe_list_content > tbody > tr > th > button { border: none; background: transparent; padding: 0; - -moz-box-shadow: none; - -webkit-box-shadow: none; - box-shadow: none; +} +.openerp .oe_list_content > tbody > tr > td > button.btn_txt, .openerp .oe_list_content > tbody > tr > th > button.btn_txt { + border: 1px solid rgba(0, 0, 0, 0.4); + background: #e3e3e3; + padding: 3px 12px; } .openerp .oe_list_content > tbody > tr > td.oe_list_checkbox:first-child, .openerp .oe_list_content > tbody > tr th.oe_list_checkbox:first-child { width: 17px; @@ -3293,11 +3297,6 @@ body.oe_single_form .oe_single_form_container { overflow: hidden !important; } } -.ui-icon { - width: 18px; - height: 18px; -} - .tooltip { padding: 0; margin: 0; @@ -3307,8 +3306,6 @@ body.oe_single_form .oe_single_form_container { background: white; text-shadow: 0 1px 1px rgba(255, 255, 255, 0.5); background-color: transparent; - /*We need a greater z-index in order for tooltip to go over bootstrap modal z-index*/ - z-index: 1500; } .tooltip .tooltip-inner { text-align: left !important; @@ -3345,6 +3342,12 @@ body.oe_single_form .oe_single_form_container { .tooltip .tooltip-inner .oe_tooltip_message { max-width: 310px; } + +.ui-icon { + width: 18px; + height: 18px; +} + .modal .modal-header button.close { border: none; background: none; @@ -3355,12 +3358,17 @@ body.oe_single_form .oe_single_form_container { .modal .modal-footer { text-align: left; } -.modal .oe_act_window.modal-body{ +.modal .oe_button { + margin: 0 4px 0 0; +} +.modal .oe_act_window.modal-body { padding: 0; } -.modal .oe_button{ - margin: 0 4px 0 0; + +.ui-datepicker { + z-index: 1500 !important; } + input[type="radio"], input[type="checkbox"] { margin-right: 4px; margin-left: 4px; diff --git a/addons/web/static/src/css/base.sass b/addons/web/static/src/css/base.sass index d83b09bc897..f1a92f44288 100644 --- a/addons/web/static/src/css/base.sass +++ b/addons/web/static/src/css/base.sass @@ -190,7 +190,7 @@ $sheet-padding: 16px td vertical-align: top .oe_title - width: 50% + width: 38% float: left .oe_title:after content: "." @@ -330,47 +330,48 @@ $sheet-padding: 16px @include box-shadow(none) &:hover background: #ED6F6A + .oe_stat_button + font-weight: normal + width: 132px !important + height: 40px + color: #666 + margin: 0px -1px -1px 0px + padding: 0 + border: 1px solid #dddddd + border-radius: 0 + box-shadow: none + background: white + > div + display: table-cell + vertical-align: middle + text-align: left + padding: 0 + line-height: 120% + .stat_button_icon + color: #7C7BAD + font-size: 24px + padding: 0px 3px + width: 37px + text-align: center + .oe_form_field_percent_pie + width: 42px + .oe_form_field_bar_chart + width: 42px + svg + width: 38px + height: 38px + display: inline + vertical-align: middle + .oe_stat_button:hover + background: #7c7bad + color: white + .fa + color: white .oe_button_box width: 400px - text-align: left - .oe_stat_button:hover - background: #7c7bad - color: white - .fa - color: white + text-align: right .oe_stat_button - font-weight: normal display: inline-table - width: 33% !important - height: 42px - margin: 0px -1px -1px 0px - padding: 0 - color: #666 - border: 1px solid #dddddd - border-radius: 0 - box-shadow: none - background: white - > div - display: table-cell - vertical-align: middle - text-align: left - padding: 0 - line-height: 120% - .stat_button_icon - color: #7C7BAD - font-size: 24px - padding: 0px 3px - width: 37px - text-align: center - .oe_form_field_percent_pie - width: 42px - .oe_form_field_bar_chart - width: 42px - svg - width: 38px - height: 38px - display: inline - vertical-align: middle .oe_avatar > img max-height: 90px @@ -639,7 +640,8 @@ $sheet-padding: 16px // }}} // Notifications {{{ .oe_notification - z-index: 1050 + z-index: 1500 + .oe_webclient_timezone_notification a color: white @@ -2220,11 +2222,14 @@ $sheet-padding: 16px padding: 3px 6px white-space: pre-line > td, > th - > button.btn_img + > button border: none background: transparent padding: 0 - @include box-shadow(none) + > button.btn_txt + border: 1px solid rgba(0,0,0,0.4) + background: #e3e3e3 + padding: 3px 12px > td.oe_list_checkbox:first-child, th.oe_list_checkbox:first-child width: 17px &:after @@ -2736,6 +2741,9 @@ body.oe_single_form .oe_act_window.modal-body padding: 0 +.ui-datepicker + z-index: 1500 !important + input[type="radio"], input[type="checkbox"] margin-right: 4px margin-left: 4px diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js index e1b654aa3f7..0aa46ebb104 100644 --- a/addons/web/static/src/js/chrome.js +++ b/addons/web/static/src/js/chrome.js @@ -1460,7 +1460,7 @@ instance.web.embed = function (origin, dbname, login, key, action, options) { $('head').append($('<link>', { 'rel': 'stylesheet', 'type': 'text/css', - 'href': origin +'/web/webclient/css' + 'href': origin +'/web/css/web.assets_webclient' })); var currentScript = document.currentScript; if (!currentScript) { diff --git a/addons/web/static/src/js/core.js b/addons/web/static/src/js/core.js index a4bcd1922de..c64da7e5810 100644 --- a/addons/web/static/src/js/core.js +++ b/addons/web/static/src/js/core.js @@ -232,7 +232,7 @@ instance.web.Session.include( /** @lends instance.web.Session# */{ var self = this; return this.session_reload().then(function(result) { var modules = instance._modules.join(','); - var deferred = self.rpc('/web/webclient/qweblist', {mods: modules}).then(self.load_qweb.bind(self)); + var deferred = self.load_qweb(modules); if(self.session_is_valid()) { return deferred.then(function() { return self.load_modules(); }); } @@ -318,7 +318,7 @@ instance.web.Session.include( /** @lends instance.web.Session# */{ loaded = $.when( loaded, self.rpc('/web/webclient/csslist', {mods: to_load}).done(self.load_css.bind(self)), - self.rpc('/web/webclient/qweblist', {mods: to_load}).then(self.load_qweb.bind(self)), + self.load_qweb(to_load), self.rpc('/web/webclient/jslist', {mods: to_load}).done(function(files) { file_list = file_list.concat(files); }) @@ -375,14 +375,12 @@ instance.web.Session.include( /** @lends instance.web.Session# */{ } return d; }, - load_qweb: function(files) { + load_qweb: function(mods) { var self = this; - _.each(files, function(file) { - self.qweb_mutex.exec(function() { - return self.rpc('/web/proxy/load', {path: file}).then(function(xml) { - if (!xml) { return; } - instance.web.qweb.add_template(_.str.trim(xml)); - }); + self.qweb_mutex.exec(function() { + return self.rpc('/web/proxy/load', {path: '/web/webclient/qweb?mods=' + mods}).then(function(xml) { + if (!xml) { return; } + instance.web.qweb.add_template(_.str.trim(xml)); }); }); return self.qweb_mutex.def; @@ -460,14 +458,11 @@ instance.web.Session.include( /** @lends instance.web.Session# */{ .appendTo(document.body) .load(function () { try { - if (options.error) { - if (!this.contentDocument.body.childNodes[1]) { - options.error(this.contentDocument.body.childNodes); - } - else { - options.error(JSON.parse(this.contentDocument.body.childNodes[1].textContent)); - } - } + if (options.error) { + var body = this.contentDocument.body; + var node = body.childNodes[1] || body.childNodes[0]; + options.error(JSON.parse(node.textContent)); + } } finally { complete(); } diff --git a/addons/web/static/src/js/data_export.js b/addons/web/static/src/js/data_export.js index 2de933f45e8..2f7ff81d089 100644 --- a/addons/web/static/src/js/data_export.js +++ b/addons/web/static/src/js/data_export.js @@ -32,7 +32,7 @@ instance.web.DataExport = instance.web.Dialog.extend({ var self = this; var options = { buttons: [ - {text: _t("Close"), click: function () { self.close(); }}, + {text: _t("Close"), click: function () { self.$el.parents('.modal').modal('hide'); }}, {text: _t("Export To File"), click: function () { self.on_click_export_data(); }} ], close: function () { self.close();} diff --git a/addons/web/static/src/js/search.js b/addons/web/static/src/js/search.js index 04bb1a61d99..832faa4ca62 100644 --- a/addons/web/static/src/js/search.js +++ b/addons/web/static/src/js/search.js @@ -29,8 +29,8 @@ my.Facet = B.Model.extend({ B.Model.prototype.initialize.apply(this, arguments); this.values = new my.FacetValues(values || []); - this.values.on('add remove change reset', function () { - this.trigger('change', this); + this.values.on('add remove change reset', function (_, options) { + this.trigger('change', this, options); }, this); }, get: function (key) { @@ -399,7 +399,8 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea this.setup_global_completion(); this.query = new my.SearchQuery() .on('add change reset remove', this.proxy('do_search')) - .on('add change reset remove', this.proxy('renderFacets')); + .on('change', this.proxy('renderChangedFacets')) + .on('add reset remove', this.proxy('renderFacets')); if (this.options.hidden) { this.$el.hide(); @@ -578,14 +579,20 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea .trigger('blur'); }, /** - * - * @param {openerp.web.search.SearchQuery | openerp.web.search.Facet} _1 - * @param {openerp.web.search.Facet} [_2] + * Call the renderFacets method with the correct arguments. + * This is due to the fact that change events are called with two arguments + * (model, options) while add, reset and remove events are called with + * (collection, model, options) as arguments + */ + renderChangedFacets: function (model, options) { + this.renderFacets(undefined, model, options); + }, + /** + * @param {openerp.web.search.SearchQuery | undefined} Undefined if event is change + * @param {openerp.web.search.Facet} * @param {Object} [options] */ - renderFacets: function (_1, _2, options) { - // _1: model if event=change, otherwise collection - // _2: undefined if event=change, otherwise model + renderFacets: function (collection, model, options) { var self = this; var started = []; var $e = this.$('div.oe_searchview_facets'); @@ -610,6 +617,7 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea }); $.when.apply(null, started).then(function () { + if (options && options.focus_input === false) return; var input_to_focus; // options.at: facet inserted at given index, focus next input // otherwise just focus last input @@ -618,7 +626,6 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea } else { input_to_focus = self.input_subviews[(options.at + 1) * 2]; } - input_to_focus.$el.focus(); }); }, @@ -1602,8 +1609,11 @@ instance.web.search.ManyToOneField = instance.web.search.CharField.extend({ return facetValue.get('label'); }, make_domain: function (name, operator, facetValue) { - if (operator === this.default_operator) { + switch(operator){ + case this.default_operator: return [[name, '=', facetValue.get('value')]]; + case 'child_of': + return [[name, 'child_of', facetValue.get('value')]]; } return this._super(name, operator, facetValue); }, diff --git a/addons/web/static/src/js/testing.js b/addons/web/static/src/js/testing.js index 1f1c718ad5f..5e9483addc1 100644 --- a/addons/web/static/src/js/testing.js +++ b/addons/web/static/src/js/testing.js @@ -48,15 +48,6 @@ openerp.testing = {}; testing.dependencies = window['oe_all_dependencies'] || []; testing.current_module = null; - testing.templates = { }; - testing.add_template = function (name) { - var xhr = QWeb2.Engine.prototype.get_xhr(); - xhr.open('GET', name, false); - xhr.send(null); - (testing.templates[testing.current_module] = - testing.templates[testing.current_module] || []) - .push(xhr.responseXML); - }; /** * Function which does not do anything */ @@ -206,7 +197,7 @@ openerp.testing = {}; teardown: testing.noop }); - QUnit.module(testing.current_module + '.' + name, {_oe: options}); + QUnit.module(name, {_oe: options}); body(testing['case']); }; testing['case'] = function (name, options, callback) { @@ -244,18 +235,6 @@ openerp.testing = {}; expect(opts.asserts); } - if (opts.templates) { - for(var i=0; i<module_deps.length; ++i) { - var dep = module_deps[i]; - var templates = testing.templates[dep]; - if (_.isEmpty(templates)) { continue; } - - for (var j=0; j < templates.length; ++j) { - instance.web.qweb.add_template(templates[j]); - } - } - } - var $fixture = $('#qunit-fixture'); var mock, async = false; diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 247fd2d2a1a..2c68a23dbf9 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -1940,7 +1940,7 @@ instance.web.form.WidgetButton = instance.web.form.FormWidget.extend({ init: function(field_manager, node) { node.attrs.type = node.attrs['data-button-type']; this.is_stat_button = /\boe_stat_button\b/.test(node.attrs['class']); - this.icon = node.attrs.icon && "<span class=\"fa " + node.attrs.icon + " fa-fw\"></span>"; + this.icon_class = node.attrs.icon && "stat_button_icon fa " + node.attrs.icon + " fa-fw"; this._super(field_manager, node); this.force_disabled = false; this.string = (this.node.attrs.string || '').replace(/_/g, ''); @@ -3356,13 +3356,16 @@ instance.web.form.CompletionFieldMixin = { instance.web.form.M2ODialog = instance.web.Dialog.extend({ template: "M2ODialog", init: function(parent) { + this.name = parent.string; this._super(parent, { - title: _.str.sprintf(_t("Add %s"), parent.string), + title: _.str.sprintf(_t("Create a %s"), parent.string), size: 'medium', }); }, start: function() { var self = this; + var text = _.str.sprintf(_t("You are creating a new %s, are you sure it does not exist yet?"), self.name); + this.$("p").text( text ); this.$buttons.html(QWeb.render("M2ODialog.buttons")); this.$("input").val(this.getParent().last_query); this.$buttons.find(".oe_form_m2o_qc_button").click(function(){ @@ -3486,7 +3489,7 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc self.display_value_backup = {}; self.render_value(); self.focus(); - self.view.do_onchange(self); + self.trigger('changed_value'); }); }); }); @@ -3621,6 +3624,8 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc minLength: 0, delay: 250 }); + // set position for list of suggestions box + this.$input.autocomplete( "option", "position", { my : "left top", at: "left bottom" } ); this.$input.autocomplete("widget").openerpClass(); // used to correct a bug when selecting an element by pushing 'enter' in an editable list this.$input.keyup(function(e) { diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 24c5ba625b0..18a55656df2 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -40,7 +40,7 @@ <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> <h3 class="modal-title"><t t-raw="title"/></h3> </div> - <div class="modal-body"> + <div class="modal-body" style="overflow-y: auto;"> </div> </div> </div> @@ -800,12 +800,11 @@ </t> <button t-name="ListView.row.text_button" type="button" t-att-title="widget.string" t-att-disabled="disabled || undefined" - t-att-class="disabled ? 'oe_list_button_disabled btn' : 'btn'"> - <t t-esc="widget.string"/> -</button> + t-att-class="disabled ? 'oe_list_button_disabled btn_txt oe_link' : 'btn_txt oe_link'" + ><t t-esc="widget.string"/></button> <button t-name="ListView.row.button" type="button" t-att-title="widget.string" t-att-disabled="disabled || undefined" - t-att-class="disabled ? 'oe_list_button_disabled btn_img' : 'btn_img'" + t-att-class="disabled ? 'oe_list_button_disabled' : ''" ><img t-attf-src="#{prefix}/web/static/src/img/icons/#{widget.icon}.png" t-att-alt="widget.string"/></button> <t t-extend="ListView.row"> @@ -1409,7 +1408,7 @@ t-att-autofocus="widget.node.attrs.autofocus" t-att-accesskey="widget.node.attrs.accesskey"> <img t-if="!widget.is_stat_button and widget.node.attrs.icon " t-att-src="_s + widget.node.attrs.icon" width="16" height="16"/> - <div t-if="widget.is_stat_button and widget.icon" class="stat_button_icon"><t t-raw="widget.icon"/></div> + <div t-if="widget.is_stat_button and widget.icon_class" t-att-class="widget.icon_class"></div> <span t-if="widget.string and !widget.is_stat_button"><t t-esc="widget.string"/></span> <div t-if="widget.string and widget.is_stat_button"><t t-esc="widget.string"/></div> </button> @@ -1948,13 +1947,14 @@ </t> <t t-name="M2ODialog"> <div> + <p class="oe_grey"/> Name: <input class="oe_form_m2o_input_name" type="text"/> </div> </t> <t t-name="M2ODialog.buttons"> - <button class="oe_form_m2o_qc_button oe_button oe_highlight">Quick Add</button> - <button class="oe_form_m2o_sc_button oe_button">Add All Info...</button> - <button class="oe_form_m2o_cancel_button oe_button">Cancel</button> + <button class="oe_form_m2o_qc_button oe_button oe_highlight">Create</button> + <button class="oe_form_m2o_sc_button oe_button">Create and edit</button> or + <button class="oe_form_m2o_cancel_button oe_button oe_link"><span>Cancel</span></button> </t> <t t-name="FieldMonetary" t-extend="FieldChar"> <t t-jquery="t:first" t-operation="before"> @@ -1985,6 +1985,5 @@ <a href="javascript:void(0)"><t t-esc="text"/></a> </t> <t t-name="StatInfo"> - <strong><t t-esc="value"/></strong> - <t t-esc="text"/></t> + <strong><t t-esc="value"/></strong><br/><t t-esc="text"/></t> </templates> diff --git a/addons/web/static/test/list-editable.js b/addons/web/static/test/list-editable.js index e73249549b8..ef2d608bc5d 100644 --- a/addons/web/static/test/list-editable.js +++ b/addons/web/static/test/list-editable.js @@ -361,8 +361,8 @@ openerp.testing.section('list.edition.onwrite', { strictEqual( $fix.find('tbody tr:eq(1)').css('color'), 'rgb(255, 0, 0)', 'shoud have color applied'); - strictEqual( - $fix.find('tbody tr:eq(2)').css('color'), 'rgb(0, 0, 0)', + notStrictEqual( + $fix.find('tbody tr:eq(2)').css('color'), 'rgb(255, 0, 0)', 'should have default color applied'); }); }); diff --git a/addons/web/tests/__init__.py b/addons/web/tests/__init__.py index 8c20aed1ce5..beb9e90eb76 100644 --- a/addons/web/tests/__init__.py +++ b/addons/web/tests/__init__.py @@ -2,4 +2,3 @@ import test_js import test_menu import test_serving_base -import test_ui diff --git a/addons/web/tests/qunitsuite/README.rst b/addons/web/tests/qunitsuite/README.rst deleted file mode 100644 index f1cfb96a508..00000000000 --- a/addons/web/tests/qunitsuite/README.rst +++ /dev/null @@ -1,116 +0,0 @@ -QUnitSuite is a ``unittest.TestSuite`` able to run QUnit_ test suites -within the normal unittest process, through PhantomJS_. - -QUnitSuite is built upon `Ben Alman`_'s work of for the interfacing -between PhantomJS_ and the host/reporting code: the shims and the -PhantomJS_ configuration files are those of grunt_'s ``qunit`` task. - -Why ---- - -You're a Python shop or developer, you have tools and tests built -around unittest (or compatible with unittests) and your testing -pipeline is predicated upon that, you're doing web development of some -sort these days (as so many are) and you'd like to do some testing of -your web stuff. - -But you don't really want to redo your whole testing stack just for -that. - -QUnitSuite simply grafts QUnit_-based tests, run in PhantomJS_, in -your existing ``unittest``-based architecture. - -What ----- - -QUnitSuite currently provides a single object as part of its API: -``qunitsuite.QUnitSuite(testfile[, timeout])``. - -This produces a ``unittest.TestSuite`` suitable for all the usual -stuff (running it, and giving it to an other test suite which will run -it, that is). - -``testfile`` is the HTML file bootstrapping your qunit tests, as would -usually be accessed via a browser. It can be either a local -(``file:``) url, or an HTTP one. As long as a regular browser can open -and execute it, PhantomJS_ will manage. - -``timeout`` is a check passed to the PhantomJS_ runner: if the runner -produces no information for longer than ``timeout`` milliseconds, the -run will be cancelled and a test error will be generated. This -situation usually means either your ``testfile`` is not a qunit test -file, qunit is not running or qunit's runner was stopped (for an async -test) and never restarted. - -The default value is very conservative, most tests should run -correctly with lower timeouts (especially if all tests are -synchronous). - -How ---- - -``unittest``'s autodiscovery protocol does not directly work with test -suites (it looks for test cases). If you want autodiscovery to work -correctly, you will have to use the ``load_tests`` protocol:: - - # in a testing module - def load_tests(loader, tests, pattern): - tests.addTest(QUnitSuite(qunit_test_path.html)) - return tests - -outside of that specific case, you can use a ``QUnitSuite`` as a -standard ``TestSuite`` instance, running it, adding it to an other -suite or passing it to a ``TestRunner`` - -Complaints and Grievances -------------------------- - -Speed -~~~~~ - -Starting up a phantomjs instance and running a suite turns out to have -a rather high overhead, on the order of a second on this machine -(2.4GHz, 8GB RAM and an SSD). - -As each ``QUnitSuite`` currently creates its own phantomjs instance, -it's probably a good idea to create bigger suites (put many modules & -tests in the same QUnit html file, which doesn't preclude splitting -them across multiple js files). - -Hacks -~~~~~ - -QUnitSuite contains a pretty big hack which may or may not cause -problem depending on your exact setup: in case of case failure or -error, ``unittest.TestResult`` formats the error traceback provided -alongside the test object. This goes through Python's -traceback-formatting code and there are no hooks there. - -One could expect to use a custom ``TestResult``, but for test suites -the ``TestResult`` instance must be provided by the caller, so there -is no direct hook onto it. - -This leaves three options: - -* Create a custom ``TestResult`` class and require that it be the one - provided to the test suite. This requires altered work flows, - customization of the test runner and (as far as I know) isn't - available through Python 2.7's autodiscovery. It's the cleanest - option but completely fails on practicality. - -* Create a custom ``TestResult`` which directly alters the original - result's ``errors`` and ``failures`` attributes as they're part of - the testrunner API. This would work but may put custom results in a - strange state and break e.g. unittest2's ``@failfast``. - -* Lastly, monkeypatch the undocumented and implementation detail - ``_exc_info_to_string`` on the provided ``result``. This is the - route taken, at least for now. - -.. _QUnit: http://qunitjs.com/ - -.. _PhantomJS: http://phantomjs.org/ - -.. _Ben Alman: http://benalman.com/ - -.. _grunt: http://gruntjs.com/ diff --git a/addons/web/tests/qunitsuite/__init__.py b/addons/web/tests/qunitsuite/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 --- a/addons/web/tests/qunitsuite/__init__.py +++ /dev/null diff --git a/addons/web/tests/qunitsuite/grunt/bootstrap.js b/addons/web/tests/qunitsuite/grunt/bootstrap.js deleted file mode 100644 index 3d5e38912ca..00000000000 --- a/addons/web/tests/qunitsuite/grunt/bootstrap.js +++ /dev/null @@ -1,95 +0,0 @@ -/* - * grunt - * http://gruntjs.com/ - * - * Copyright (c) 2012 "Cowboy" Ben Alman - * Licensed under the MIT license. - * http://benalman.com/about/license/ - */ - -/*global phantom:true*/ - -'use strict'; - -var fs = require('fs'); - -// The page .html file to load. -var url = phantom.args[0]; -// Extra, optionally overridable stuff. -var options = JSON.parse(phantom.args[1] || {}); - -// Default options. -if (!options.timeout) { options.timeout = 5000; } - -// Keep track of the last time a client message was sent. -var last = new Date(); - -// Messages are sent to the parent by appending them to the tempfile. -var sendMessage = function(arg) { - var args = Array.isArray(arg) ? arg : [].slice.call(arguments); - last = new Date(); - console.log(JSON.stringify(args)); -}; - -// This allows grunt to abort if the PhantomJS version isn't adequate. -sendMessage('private', 'version', phantom.version); - -// Abort if the page doesn't send any messages for a while. -setInterval(function() { - if (new Date() - last > options.timeout) { - sendMessage('fail.timeout'); - phantom.exit(); - } -}, 100); - -// Create a new page. -var page = require('webpage').create(); - -// The client page must send its messages via alert(jsonstring). -page.onAlert = function(args) { - sendMessage(JSON.parse(args)); -}; - -// Keep track if the client-side helper script already has been injected. -var injected; -page.onUrlChanged = function(newUrl) { - injected = false; - sendMessage('onUrlChanged', newUrl); -}; - -// Relay console logging messages. -page.onConsoleMessage = function(message) { - sendMessage('console', message); -}; - -// For debugging. -page.onResourceRequested = function(request) { - sendMessage('onResourceRequested', request.url); -}; - -page.onResourceReceived = function(request) { - if (request.stage === 'end') { - sendMessage('onResourceReceived', request.url); - } -}; - -// Run when the page has finished loading. -page.onLoadFinished = function(status) { - // The window has loaded. - sendMessage('onLoadFinished', status); - if (status === 'success') { - if (options.inject && !injected) { - // Inject client-side helper script, but only if it has not yet been - // injected. - sendMessage('inject', options.inject); - page.injectJs(options.inject); - } - } else { - // File loading failure. - sendMessage('fail.load', url); - phantom.exit(); - } -}; - -// Actually load url. -page.open(url); diff --git a/addons/web/tests/qunitsuite/grunt/license b/addons/web/tests/qunitsuite/grunt/license deleted file mode 100644 index 90c336c39d3..00000000000 --- a/addons/web/tests/qunitsuite/grunt/license +++ /dev/null @@ -1,22 +0,0 @@ -Copyright (c) 2012 "Cowboy" Ben Alman - -Permission is hereby granted, free of charge, to any person -obtaining a copy of this software and associated documentation -files (the "Software"), to deal in the Software without -restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the -Software is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -OTHER DEALINGS IN THE SOFTWARE. diff --git a/addons/web/tests/qunitsuite/grunt/phantomjs.json b/addons/web/tests/qunitsuite/grunt/phantomjs.json deleted file mode 100644 index 9e26dfeeb6e..00000000000 --- a/addons/web/tests/qunitsuite/grunt/phantomjs.json +++ /dev/null @@ -1 +0,0 @@ -{}
\ No newline at end of file diff --git a/addons/web/tests/qunitsuite/grunt/qunit-phantomjs-bridge.js b/addons/web/tests/qunitsuite/grunt/qunit-phantomjs-bridge.js deleted file mode 100644 index 032fe8a3aa0..00000000000 --- a/addons/web/tests/qunitsuite/grunt/qunit-phantomjs-bridge.js +++ /dev/null @@ -1,88 +0,0 @@ -/* - * grunt - * http://gruntjs.com/ - * - * Copyright (c) 2012 "Cowboy" Ben Alman - * Licensed under the MIT license. - * http://benalman.com/about/license/ - */ - -/*global QUnit:true, alert:true*/ - -'use strict'; - -// Don't re-order tests. -QUnit.config.reorder = false; -// Run tests serially, not in parallel. -QUnit.config.autorun = false; - -// Send messages to the parent PhantomJS process via alert! Good times!! -function sendMessage() { - var args = [].slice.call(arguments); - alert(JSON.stringify(args)); -} - -// These methods connect QUnit to PhantomJS. -QUnit.log(function(obj) { - // What is this I don’t even - if (obj.message === '[object Object], undefined:undefined') { return; } - // Parse some stuff before sending it. - var actual = QUnit.jsDump.parse(obj.actual); - var expected = QUnit.jsDump.parse(obj.expected); - // Send it. - sendMessage('qunit.log', obj.result, actual, expected, obj.message, obj.source); -}); - -QUnit.testStart(function(obj) { - sendMessage('qunit.testStart', obj.name); -}); - -QUnit.testDone(function(obj) { - sendMessage('qunit.testDone', obj.name, obj.failed, obj.passed, obj.total); -}); - -QUnit.moduleStart(function(obj) { - sendMessage('qunit.moduleStart', obj.name); -}); - -QUnit.moduleDone(function(obj) { - sendMessage('qunit.moduleDone', obj.name, obj.failed, obj.passed, obj.total); -}); - -QUnit.begin(function() { - sendMessage('qunit.begin'); -}); - -QUnit.done(function(obj) { - sendMessage('qunit.done', obj.failed, obj.passed, obj.total, obj.runtime); -}); - -// PhantomJS (up to and including 1.7) uses a version of webkit so old -// it does not have Function.prototype.bind: -// http://code.google.com/p/phantomjs/issues/detail?id=522 - -// Use moz polyfill: -// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind#Compatibility -if (!Function.prototype.bind) { - Function.prototype.bind = function (oThis) { - if (typeof this !== "function") { - // closest thing possible to the ECMAScript 5 internal IsCallable function - throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable"); - } - - var aArgs = Array.prototype.slice.call(arguments, 1), - fToBind = this, - fNOP = function () {}, - fBound = function () { - return fToBind.apply(this instanceof fNOP && oThis - ? this - : oThis, - aArgs.concat(Array.prototype.slice.call(arguments))); - }; - - fNOP.prototype = this.prototype; - fBound.prototype = new fNOP(); - - return fBound; - }; -} diff --git a/addons/web/tests/qunitsuite/suite.py b/addons/web/tests/qunitsuite/suite.py deleted file mode 100644 index 54d2e18e36e..00000000000 --- a/addons/web/tests/qunitsuite/suite.py +++ /dev/null @@ -1,136 +0,0 @@ -import json -import subprocess -import unittest -import os -import time - -ROOT = os.path.join(os.path.dirname(__file__), 'grunt') - -__all__ = ['QUnitSuite'] - -def _exc_info_to_string(err, test): - return err - -class QUnitTest(unittest.TestCase): - def __init__(self, module, name): - self.module = module - self.name = name - self.failed = False - def shortDescription(self): - return None - def __repr__(self): - return '<QUnitTest %s:%s>' % (self.module, self.name) - def __str__(self): - return '%s: %s' % (self.module, self.name) - -class QUnitSuite(unittest.TestSuite): - def __init__(self, qunitfile, timeout=5000): - super(QUnitSuite, self).__init__() - self.testfile = qunitfile - self.timeout = timeout - self._module = None - self._test = None - - def run(self, result): - try: - subprocess.call(['phantomjs', '-v'], - stdout=open(os.devnull, 'w'), - stderr=subprocess.STDOUT) - except OSError: - test = QUnitTest('phantomjs', 'javascript tests') - result.startTest(test) - result.startTest(test) - result.addSkip(test , "phantomjs command not found") - result.stopTest(test) - return - - result._exc_info_to_string = _exc_info_to_string - try: - self._run(result) - finally: - del result._exc_info_to_string - - def _run(self, result): - phantom = subprocess.Popen([ - 'phantomjs', - '--config=%s' % os.path.join(ROOT, 'phantomjs.json'), - os.path.join(ROOT, 'bootstrap.js'), self.testfile, - json.dumps({ - 'timeout': self.timeout, - 'inject': os.path.join(ROOT, 'qunit-phantomjs-bridge.js') - }) - ], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - - try: - while True: - line = phantom.stdout.readline() - if line: - if self.process(line, result): - break - else: - time.sleep(0.1) - finally: - # If the phantomjs process hasn't quit, kill it - if phantom.poll() is None: - phantom.terminate() - - def process(self, line, result): - try: - args = json.loads(line) - except ValueError: # phantomjs stderr - if 'CoreText' not in line: - print line - return False - event_name = args[0] - - if event_name == 'qunit.done': - return True - elif event_name == 'fail.load': - self.add_error(result, "PhantomJS unable to load %s" % args[1]) - return True - elif event_name == 'fail.timeout': - self.add_error(result, "PhantomJS timed out, possibly due to a" - " missing QUnit start() call") - return True - - elif event_name == 'qunit.moduleStart': - self._module = args[1].encode('utf-8') if args[1] else '' - elif event_name == 'qunit.moduleStop': - self._test = None - self._module = None - elif event_name == 'qunit.testStart': - self._test = QUnitTest(self._module, args[1].encode('utf-8')) - result.startTest(self._test) - elif event_name == 'qunit.testDone': - if not self._test.failed: - result.addSuccess(self._test) - result.stopTest(self._test) - self._test = None - elif event_name == 'qunit.log': - if args[1]: - return False - - self._test.failed = True - result.addFailure( - self._test, self.failure_to_str(*args[2:])) - elif event_name == 'console': - print args[1] - - return False - - def add_error(self, result, s): - test = QUnitTest('phantomjs', 'startup') - result.startTest(test) - result.addError(test, s) - result.stopTest(test) - - def failure_to_str(self, actual, expected, message, source): - if message or actual == expected: - formatted = str(message or '') - else: - formatted = "%s != %s" % (actual, expected) - - if source: - formatted += '\n\n' + source - - return formatted diff --git a/addons/web/tests/test_js.py b/addons/web/tests/test_js.py index 0bab1de96a5..496d7e283bc 100644 --- a/addons/web/tests/test_js.py +++ b/addons/web/tests/test_js.py @@ -1,24 +1,6 @@ -import urllib -import urlparse -from openerp import sql_db, tools -from qunitsuite.suite import QUnitSuite +import openerp -class WebSuite(QUnitSuite): - def __init__(self, module): - url = urlparse.urlunsplit([ - 'http', - 'localhost:{port}'.format(port=tools.config['xmlrpc_port']), - '/web/tests', - urllib.urlencode({ - 'mod': module, - 'source': tools.config['db_name'], - 'supadmin': tools.config['admin_passwd'], - 'password': 'admin', - }), - '' - ]) - super(WebSuite, self).__init__(url, 50000) +class WebSuite(openerp.tests.HttpCase): + def test_01_js(self): + self.phantom_js('/web/tests?mod=web',"","", login='admin') -def load_tests(loader, standard_tests, _): - standard_tests.addTest(WebSuite('web')) - return standard_tests diff --git a/addons/web/tests/test_menu.py b/addons/web/tests/test_menu.py index fb9337a589b..0978a7093ad 100644 --- a/addons/web/tests/test_menu.py +++ b/addons/web/tests/test_menu.py @@ -28,9 +28,7 @@ class LoadTest(common.MockRequestCase): self.MockMenus = model('ir.ui.menu') # Mock the absence of custom menu - model('res.users').read.return_value = [{ - 'menu_id': False - }] + model('res.users').read.return_value = [] def tearDown(self): del self.MockMenus diff --git a/addons/web/tests/test_ui.py b/addons/web/tests/test_ui.py deleted file mode 100644 index 4bbc4a3954c..00000000000 --- a/addons/web/tests/test_ui.py +++ /dev/null @@ -1,16 +0,0 @@ -# -*- coding: utf-8 -*- -import os - -import openerp.tests - -class TestUi(openerp.tests.HttpCase): - def test_01_jsfile_ui_hello(self): - self.phantom_jsfile(os.path.join(os.path.dirname(__file__), 'test_ui_hello.js')) - def test_02_jsfile_ui_load(self): - self.phantom_jsfile(os.path.join(os.path.dirname(__file__), 'test_ui_load.js')) - def test_03_js_public(self): - self.phantom_js('/',"console.log('ok')","console") - def test_04_js_admin(self): - self.phantom_js('/',"console.log('ok')","openerp.client.action_manager.inner_widget.views.form", login='admin') - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/web/tests/test_ui_hello.js b/addons/web/tests/test_ui_hello.js deleted file mode 100644 index 6a453e947aa..00000000000 --- a/addons/web/tests/test_ui_hello.js +++ /dev/null @@ -1,5 +0,0 @@ -console.log("hello from test_phantom_hello.js"); -console.log("ok"); -// For a failure use: -//console.log("error"); -phantom.exit(); diff --git a/addons/web/tests/test_ui_load.js b/addons/web/tests/test_ui_load.js deleted file mode 100644 index 9d76250d239..00000000000 --- a/addons/web/tests/test_ui_load.js +++ /dev/null @@ -1,4 +0,0 @@ -// Load helper -phantom.injectJs(phantom.args[0]); -pt = new PhantomTest(); -pt.run("/", "console.log('ok')", "console"); diff --git a/addons/web/views/webclient_templates.xml b/addons/web/views/webclient_templates.xml index 46bae5f83ca..2c3a16ac13f 100644 --- a/addons/web/views/webclient_templates.xml +++ b/addons/web/views/webclient_templates.xml @@ -3,6 +3,101 @@ --> <openerp> <data> + <template id="web.assets_common"> + <script type="text/javascript" src="/web/static/lib/es5-shim/es5-shim.min.js"></script> + <script type="text/javascript" src="/web/static/lib/underscore/underscore.js"></script> + <script type="text/javascript" src="/web/static/lib/underscore.string/lib/underscore.string.js"></script> + <script type="text/javascript" src="/web/static/lib/datejs/globalization/en-US.js"></script> + <script type="text/javascript" src="/web/static/lib/spinjs/spin.js"></script> + + <!-- jQuery stuff --> + <script type="text/javascript" src="/web/static/lib/jquery/jquery.js"></script> + <script type="text/javascript" src="/web/static/lib/jquery.blockUI/jquery.blockUI.js"></script> + <script type="text/javascript" src="/web/static/lib/jquery.hotkeys/jquery.hotkeys.js"></script> + <script type="text/javascript" src="/web/static/lib/jquery.placeholder/jquery.placeholder.js"></script> + <script type="text/javascript" src="/web/static/lib/jquery.timeago/jquery.timeago.js"></script> + <script type="text/javascript" src="/web/static/lib/jquery.form/jquery.form.js"></script> + + <script type="text/javascript" src="/web/static/lib/jquery.ba-bbq/jquery.ba-bbq.js"></script> + + <link rel="stylesheet" href="/web/static/lib/fontawesome/css/font-awesome.css"/> + </template> + + <template id="web.assets_backend"> + <t t-call="web.assets_common"/> + <!-- Datejs --> + <script type="text/javascript" src="/web/static/lib/datejs/core.js"></script> + <script type="text/javascript" src="/web/static/lib/datejs/parser.js"></script> + <script type="text/javascript" src="/web/static/lib/datejs/sugarpak.js"></script> + <script type="text/javascript" src="/web/static/lib/datejs/extras.js"></script> + + <!-- jQuery addons --> + <script type="text/javascript" src="/web/static/lib/jquery.validate/jquery.validate.js"></script> + <script type="text/javascript" src="/web/static/lib/jquery.autosize/jquery.autosize.js"></script> + <script type="text/javascript" src="/web/static/lib/jquery.scrollTo/jquery.scrollTo-min.js"></script> + + <link rel="stylesheet" href="/web/static/lib/cleditor/jquery.cleditor.css"/> + <script type="text/javascript" src="/web/static/lib/cleditor/jquery.cleditor.js"></script> + + <link rel="stylesheet" href="/web/static/lib/jquery.textext/jquery.textext.css"/> + <script type="text/javascript" src="/web/static/lib/jquery.textext/jquery.textext.js"></script> + + <link rel="stylesheet" href="/web/static/lib/select2/select2.css"/> + <script type="text/javascript" src="/web/static/lib/select2/select2.js"></script> + + + <!-- jQuery ui --> + <link rel="stylesheet" href="/web/static/lib/jquery.ui.bootstrap/css/custom-theme/jquery-ui-1.9.0.custom.css"/> + <script type="text/javascript" src="/web/static/lib/jquery.ui/js/jquery-ui-1.9.1.custom.js"></script> + + <link rel="stylesheet" href="/web/static/lib/jquery.ui.timepicker/css/jquery-ui-timepicker-addon.css"/> + <script type="text/javascript" src="/web/static/lib/jquery.ui.timepicker/js/jquery-ui-timepicker-addon.js"></script> + + <link rel="stylesheet" href="/web/static/lib/jquery.ui.notify/css/ui.notify.css"/> + <script type="text/javascript" src="/web/static/lib/jquery.ui.notify/js/jquery.notify.js"></script> + + <!-- Bootstrap must be loaded after jquery.ui until jquery.ui is removed --> + <link rel="stylesheet" href="/web/static/lib/bootstrap/css/bootstrap.css"/> + <script type="text/javascript" src="/web/static/lib/bootstrap/js/bootstrap.js"></script> + + <!-- Backbone --> + <script type="text/javascript" src="/web/static/lib/backbone/backbone.js"></script> + + <!-- Internals --> + <link rel="stylesheet" href="/web/static/src/css/base.css"/> + <link rel="stylesheet" href="/web/static/src/css/data_export.css"/> + <link rel="stylesheet" href="/base/static/src/css/modules.css"/> + + <script type="text/javascript" src="/web/static/lib/qweb/qweb2.js"></script> + <script type="text/javascript" src="/web/static/src/js/openerpframework.js"></script> + <script type="text/javascript" src="/web/static/lib/py.js/lib/py.js"></script> + <script type="text/javascript" src="/web/static/src/js/boot.js"></script> + <script type="text/javascript" src="/web/static/src/js/testing.js"></script> + <script type="text/javascript" src="/web/static/src/js/pyeval.js"></script> + <script type="text/javascript" src="/web/static/src/js/core.js"></script> + <script type="text/javascript" src="/web/static/src/js/formats.js"></script> + <script type="text/javascript" src="/web/static/src/js/chrome.js"></script> + <script type="text/javascript" src="/web/static/src/js/views.js"></script> + <script type="text/javascript" src="/web/static/src/js/data.js"></script> + <script type="text/javascript" src="/web/static/src/js/data_export.js"></script> + <script type="text/javascript" src="/web/static/src/js/search.js"></script> + <script type="text/javascript" src="/web/static/src/js/view_list.js"></script> + <script type="text/javascript" src="/web/static/src/js/view_form.js"></script> + <script type="text/javascript" src="/web/static/src/js/view_list_editable.js"></script> + <script type="text/javascript" src="/web/static/src/js/view_tree.js"></script> + <script type="text/javascript" src="/base/static/src/js/apps.js"></script> + + </template> + + <template id="web.assets_webclient_manifest"> + <!-- This bundle can be used for module manifest asset declaration backward compatibility --> + <t t-foreach="manifest_list('css')" t-as="css_file"> + <link rel="stylesheet" t-att-href="css_file"/> + </t> + <t t-foreach="manifest_list('js')" t-as="js_file"> + <script type="text/javascript" t-att-src="js_file"></script> + </t> + </template> <template id="web.layout" name="Web layout"><!DOCTYPE html> <html style="height: 100%"> @@ -23,12 +118,7 @@ <template id="web.webclient_bootstrap" name="Webclient Bootstrap"> <t t-call="web.layout"> <t t-set="head"> - <t t-foreach="css" t-as="css_file"> - <link rel="stylesheet" t-att-href="css_file"/> - </t> - <t t-foreach="js" t-as="js_file"> - <script type="text/javascript" t-att-src="js_file"></script> - </t> + <t t-call-assets="web.assets_backend"/> <script type="text/javascript"> $(function() { var s = new openerp.init(<t t-raw="modules"/>); @@ -78,10 +168,10 @@ </script> <div class="form-group field-db" t-if="databases and len(databases) > 1"> <label for="db" class="control-label">Database</label> - <select name="db" id="db" class="form-control" required="required" t-att-autofocus="'autofocus' if current_db not in databases else None" onchange="dbchanged(this.value)"> + <select name="db" id="db" class="form-control" required="required" t-att-autofocus="'autofocus' if request.db not in databases else None" onchange="dbchanged(this.value)"> <option></option> <t t-foreach="databases" t-as="db"> - <option t-att-selected="'selected' if db == current_db else None"> + <option t-att-selected="'selected' if db == request.db else None"> <t t-esc="db"/> </option> </t> @@ -121,5 +211,55 @@ </t> </template> + <template id="web.qunit_suite"> + <!DOCTYPE html> + <html style="height: 100%"> + <head> + <meta http-equiv="X-UA-Compatible" content="IE=edge"/> + <meta http-equiv="content-type" content="text/html; charset=utf-8" /> + <title>OpenERP Web Tests</title> + + <link rel="shortcut icon" href="/web/static/src/img/favicon.ico" type="image/x-icon"/> + <link rel="stylesheet" href="/web/static/lib/qunit/qunit.css"/> + <script src="/web/static/lib/qunit/qunit.js"></script> + + <t t-call="web.assets_backend"/> + + <script type="text/javascript" id="qunit_config"> + QUnit.config.testTimeout = 5 * 60 * 1000; + QUnit.moduleDone(function(result) { + console.log(result.name + " (" + result.passed + "/" + result.total + " passed tests)"); + }); + QUnit.done(function(result) { + if (result.failed === 0) { + console.log('ok'); + } + }); + openerp.web.qweb.add_template("/web/webclient/qweb"); + </script> + + <script type="text/javascript" src="/web/static/test/testing.js"></script> + <script type="text/javascript" src="/web/static/test/framework.js"></script> + <script type="text/javascript" src="/web/static/test/registry.js"></script> + <script type="text/javascript" src="/web/static/test/form.js"></script> + <script type="text/javascript" src="/web/static/test/data.js"></script> + <script type="text/javascript" src="/web/static/test/list-utils.js"></script> + <script type="text/javascript" src="/web/static/test/formats.js"></script> + <script type="text/javascript" src="/web/static/test/rpc-misordered.js"></script> + <script type="text/javascript" src="/web/static/test/evals.js"></script> + <script type="text/javascript" src="/web/static/test/search.js"></script> + <script type="text/javascript" src="/web/static/test/list.js"></script> + <script type="text/javascript" src="/web/static/test/list-editable.js"></script> + <script type="text/javascript" src="/web/static/test/mutex.js"></script> + </head> + + <body id="oe" class="openerp"> + <div id="qunit"></div> + <div id="qunit-fixture"></div> + </body> + </html> + + </template> + </data> </openerp> |
