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 = """ OpenERP + %(css)s %(js)s ' % i for i in manifest_list('js', debug=request.debug)) - css = "\n ".join('' % i for i in manifest_list('css', debug=request.debug)) + css = """ + + + + + + + + + + + """ + js = """ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + """ 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""" - - - - - OpenERP Testing - - -
- - -
- - -""", default_filters=['h']) -NOTFOUND = Template(u""" -

Unable to find the module [${module}], please check that the module - name is correct and the module is on OpenERP's path.

-<< Back to tests -""", default_filters=['h']) -TESTING = Template(u""" - -<%def name="to_path(module, p)">/${module}/${p} - - - - OpenERP Web Tests - - - - - - - - -
-
- - -% for module, jss, tests, templates in files: - % for js in jss: - % if not js.endswith('/apps.js'): - - % endif - % endfor - % if tests or templates: - - % endif - % if tests: - % for test in tests: - - % endfor - % endif -% endfor - -""", 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{ - padding: 0; -} -.modal .oe_button{ +.modal .oe_button { margin: 0 4px 0 0; } +.modal .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/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($('', { '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"; + 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 @@ - @@ -800,12 +800,11 @@ + t-att-class="disabled ? 'oe_list_button_disabled btn_txt oe_link' : 'btn_txt oe_link'" + > @@ -1409,7 +1408,7 @@ t-att-autofocus="widget.node.attrs.autofocus" t-att-accesskey="widget.node.attrs.accesskey"> -
+
@@ -1948,13 +1947,14 @@
+

Name:

- - - + + or + @@ -1985,6 +1985,5 @@ - - +
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 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 '' % (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/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 @@ --> + + + + +