[Merge]with trunk

bzr revid: mba@tinyerp.com-20140507104547-p7pmav8pez08zf1h
This commit is contained in:
Barad Mahendra 2014-05-07 16:15:47 +05:30
commit d3410a6924
417 changed files with 2033 additions and 2805 deletions

View File

@ -14,83 +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.tipsy/jquery.tipsy.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.tipsy/tipsy.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"
],
}

View File

@ -1,2 +1 @@
from . import main
from . import testing

View File

@ -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:
@ -592,59 +497,6 @@ def content_disposition(filename):
#----------------------------------------------------------
# OpenERP Web web Controllers
#----------------------------------------------------------
# TODO: to remove once the database manager has been migrated server side
# and `edi` + `pos` addons has been adapted to use render_bootstrap_template()
html_template = """<!DOCTYPE html>
<html style="height: 100%%">
<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</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">
$(function() {
var s = new openerp.init(%(modules)s);
%(init)s
});
</script>
</head>
<body>
<!--[if lte IE 8]>
<script src="//ajax.googleapis.com/ajax/libs/chrome-frame/1/CFInstall.min.js"></script>
<script>CFInstall.check({mode: "overlay"});</script>
<![endif]-->
</body>
</html>
"""
def render_bootstrap_template(template, values=None, debug=False, db=None, **kw):
if not db:
db = request.db
if request.debug:
debug = True
if values is None:
values = {}
values['debug'] = debug
values['current_db'] = db
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'] = simplejson.dumps(values['modules'])
return request.render(template, values, **kw)
class Home(http.Controller):
@http.route('/', type='http', auth="none")
@ -658,12 +510,7 @@ class Home(http.Controller):
if request.session.uid:
if kw.get('redirect'):
return werkzeug.utils.redirect(kw.get('redirect'), 303)
headers = {
'Cache-Control': 'no-cache',
'Content-Type': 'text/html; charset=utf-8',
}
return render_bootstrap_template("web.webclient_bootstrap", headers=headers)
return request.render('web.webclient_bootstrap')
else:
return login_redirect()
@ -681,17 +528,57 @@ class Home(http.Controller):
if not redirect:
redirect = '/web?' + request.httprequest.query_string
values['redirect'] = redirect
try:
values['databases'] = http.db_list()
except openerp.exceptions.AccessDenied:
values['databases'] = None
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)
return request.render('web.login', values)
@http.route('/login', type='http', auth="none")
def login(self, db, login, key, redirect="/web", **kw):
return login_and_redirect(db, login, key, redirect_url=redirect)
@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)
response = request.make_response(
bundle.js(), [('Content-Type', 'application/javascript')])
# 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)
@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)
response = request.make_response(
bundle.css(), [('Content-Type', 'text/css')])
return make_conditional(
response, bundle.last_modified, bundle.checksum, max_age=60*60*24)
class WebClient(http.Controller):
@http.route('/web/webclient/csslist', type='json', auth="none")
@ -702,74 +589,6 @@ class WebClient(http.Controller):
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')
content, checksum = concat_files((f[0] for f in files), reader)
# move up all @import and @charset rules to the top
matches = []
def push(matchobj):
matches.append(matchobj.group(0))
return ''
content = re.sub(re.compile("(@charset.+;$)", re.M), push, content)
content = re.sub(re.compile("(@import.+;$)", re.M), push, content)
matches.append(content)
content = '\n'.join(matches)
return make_conditional(
request.make_response(content, [('Content-Type', 'text/css')]),
last_modified, checksum)
@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)
content, checksum = concat_js(files)
return make_conditional(
request.make_response(content, [('Content-Type', 'application/javascript')]),
last_modified, checksum)
@http.route('/web/webclient/qweb', type='http', auth="none")
def qweb(self, mods=None, db=None):
files = [f[0] for f in manifest_glob('qweb', addons=mods, db=db)]
@ -843,6 +662,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,19 +702,9 @@ 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))
r = html_template % {
'js': js,
'css': css,
return env.get_template("database_manager.html").render({
'modules': simplejson.dumps(module_boot()),
'init': """
var wc = new s.web.WebClient(null, { action: 'database_manager' });
wc.appendTo($(document.body));
"""
}
return r
})
@http.route('/web/database/get_list', type='json', auth="none")
def get_list(self):
@ -1088,16 +901,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)
@ -1225,7 +1029,7 @@ class DataSet(http.Controller):
records = getattr(request.session.model(model), method)(*args, **kwargs)
for record in records:
record['display_name'] = \
names.get(record['id']) or "%s#%d" % (model, (record['id']))
names.get(record['id']) or "{0}#{1}".format(model, (record['id']))
return records
if method.startswith('_'):

View File

@ -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">&lt;&lt; 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, '/')

View File

@ -14,8 +14,8 @@ 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-09 06:38+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:31+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:38+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:32+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:38+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:31+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:38+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:31+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:38+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:32+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:38+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:32+0000\n"
"X-Generator: Launchpad (build 16996)\n"
"X-Poedit-Language: Czech\n"
#. module: web

View File

@ -14,8 +14,8 @@ 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-09 06:38+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:32+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:38+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:32+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:40+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:34+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:40+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:34+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:39+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:34+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:40+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:34+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:40+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:34+0000\n"
"X-Generator: Launchpad (build 16996)\n"
"Language: es\n"
#. module: web

View File

@ -14,8 +14,8 @@ 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-09 06:40+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:34+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:40+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:34+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:40+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:34+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:40+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:34+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:38+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:32+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:38+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:31+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:39+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:33+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:38+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:32+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:38+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:32+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:40+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:34+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:38+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:32+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:38+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:32+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:39+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:32+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-10 05:56+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:32+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:39+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:33+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:39+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:32+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:39+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:33+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:39+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:33+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:39+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:33+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:38+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:32+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:39+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:33+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web
@ -68,14 +68,14 @@ msgstr "기존 암호를 입력해 주세요."
#: code:addons/web/static/src/xml/base.xml:300
#, python-format
msgid "Master password:"
msgstr "마스터 암호:"
msgstr "마스터 비밀번호"
#. module: web
#. openerp-web
#: code:addons/web/static/src/xml/base.xml:292
#, python-format
msgid "Change Master Password"
msgstr "마스터 암호 변경"
msgstr "마스터 비밀번호 변경하기"
#. module: web
#. openerp-web
@ -110,7 +110,7 @@ msgstr "업로드 오류"
#: code:addons/web/static/src/js/coresetup.js:593
#, python-format
msgid "about an hour ago"
msgstr "약 시간 전"
msgstr "약 1시간 전"
#. module: web
#. openerp-web
@ -161,14 +161,14 @@ msgstr "파일"
#: code:addons/web/controllers/main.py:869
#, python-format
msgid "You cannot leave any password empty."
msgstr "호는 비워둘 수 없습니다."
msgstr "비밀번호는 비워둘 수 없습니다."
#. module: web
#. openerp-web
#: code:addons/web/static/src/js/chrome.js:732
#, python-format
msgid "Invalid username or password"
msgstr "유효하지 않은 사용자명 또는 호"
msgstr "유효하지 않은 사용자명 또는 비밀번호"
#. module: web
#. openerp-web
@ -177,7 +177,7 @@ msgstr "유효하지 않은 사용자명 또는 암호"
#: code:addons/web/static/src/xml/base.xml:278
#, python-format
msgid "Master Password:"
msgstr "마스터 암호:"
msgstr "마스터 비밀번호"
#. module: web
#. openerp-web
@ -185,7 +185,7 @@ msgstr "마스터 암호:"
#: code:addons/web/static/src/xml/base.xml:1402
#, python-format
msgid "Select"
msgstr "선택"
msgstr "선택하기"
#. module: web
#. openerp-web
@ -235,7 +235,7 @@ msgstr "모든 사용자들과 공유"
#: code:addons/web/static/src/js/view_form.js:320
#, python-format
msgid "Form"
msgstr "양식"
msgstr ""
#. module: web
#. openerp-web
@ -263,7 +263,7 @@ msgstr "유효한 숫자가 아님"
#: code:addons/web/static/src/xml/base.xml:343
#, python-format
msgid "New Password:"
msgstr "새로운 비밀번호"
msgstr "새 비밀번호:"
#. module: web
#. openerp-web
@ -348,7 +348,7 @@ msgstr "중복 데이터베이스"
#: code:addons/web/static/src/xml/base.xml:354
#, python-format
msgid "Change Password"
msgstr "암호 변경"
msgstr "비밀번호 변경하기"
#. module: web
#. openerp-web
@ -1601,7 +1601,7 @@ msgstr "데이터 베이스 삭제"
#: code:addons/web/static/src/xml/base.xml:467
#, python-format
msgid "Powered by"
msgstr ""
msgstr "Powered by"
#. module: web
#. openerp-web
@ -1644,7 +1644,7 @@ msgstr "--- 가져오지 않기 ---"
#: code:addons/web/static/src/xml/base.xml:1697
#, python-format
msgid "Import-Compatible Export"
msgstr ""
msgstr "가져오기-호환 파일 내보내기"
#. module: web
#. openerp-web
@ -2364,7 +2364,7 @@ msgstr "o2m 기록은 액션을 사용하기 전에 저장되어야 합니다."
#: code:addons/web/static/src/js/chrome.js:531
#, python-format
msgid "Backed"
msgstr ""
msgstr "백업됨"
#. module: web
#. openerp-web
@ -2420,7 +2420,7 @@ msgstr "ID:"
#: code:addons/web/static/src/xml/base.xml:892
#, python-format
msgid "Only you"
msgstr ""
msgstr "Only you"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:39+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:33+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:39+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:33+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:39+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:33+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:39+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:33+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:39+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:33+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:39+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:33+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:38+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:32+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:40+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:34+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:39+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:33+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:39+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:33+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -15,8 +15,8 @@ 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-09 06:40+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:34+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:39+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:33+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:39+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:33+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:39+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:33+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:39+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:33+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:38+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:31+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:40+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:35+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:39+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:34+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:39+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:34+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web
@ -2593,7 +2593,7 @@ msgstr "สร้างฐานข้อมูล"
#: code:addons/web/static/src/xml/base.xml:442
#, python-format
msgid "GNU Affero General Public License"
msgstr ""
msgstr "GNU Affero General Public License"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:40+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:34+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:40+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:34+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:40+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:34+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:40+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:34+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:40+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:34+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web
#. openerp-web

View File

@ -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";
}

View File

@ -1,250 +0,0 @@
// tipsy, facebook style tooltips for jquery
// version 1.0.0a
// (c) 2008-2010 jason frame [jason@onehackoranother.com]
// released under the MIT license
(function($) {
function maybeCall(thing, ctx) {
return (typeof thing == 'function') ? (thing.call(ctx)) : thing;
};
function Tipsy(element, options) {
this.$element = $(element);
this.options = options;
this.enabled = true;
this.fixTitle();
};
Tipsy.prototype = {
show: function() {
$.fn.tipsy.clear();
if (!this.$element.parent().length) {
return;
}
var title = this.getTitle();
if (title && this.enabled) {
var $tip = this.tip();
$tip.find('.tipsy-inner')[this.options.html ? 'html' : 'text'](title);
$tip[0].className = 'tipsy '; // reset classname in case of dynamic gravity
$tip.openerpClass('oe_tooltip');
$tip.remove().css({top: 0, left: 0, visibility: 'hidden', display: 'block'}).prependTo(document.body);
var pos = $.extend({}, this.$element.offset(), {
width: this.$element[0].offsetWidth,
height: this.$element[0].offsetHeight
});
var actualWidth = $tip[0].offsetWidth,
actualHeight = $tip[0].offsetHeight,
gravity = maybeCall(this.options.gravity, this.$element[0]);
var tp;
switch (gravity.charAt(0)) {
case 'n':
tp = {top: pos.top + pos.height + this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2};
break;
case 's':
tp = {top: pos.top - actualHeight - this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2};
break;
case 'e':
tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth - this.options.offset};
break;
case 'w':
tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width + this.options.offset};
break;
}
if (gravity.length == 2) {
if (gravity.charAt(1) == 'w') {
tp.left = pos.left + pos.width / 2 - 15;
} else {
tp.left = pos.left + pos.width / 2 - actualWidth + 15;
}
}
$tip.css(tp).addClass('tipsy-' + gravity);
$tip.find('.tipsy-arrow')[0].className = 'tipsy-arrow tipsy-arrow-' + gravity.charAt(0);
if (this.options.className) {
$tip.addClass(maybeCall(this.options.className, this.$element[0]));
}
if (this.options.fade) {
$tip.stop().css({opacity: 0, display: 'block', visibility: 'visible'}).animate({opacity: this.options.opacity});
} else {
$tip.css({visibility: 'visible', opacity: this.options.opacity});
}
}
},
hide: function() {
if (this.options.fade) {
this.tip().stop().fadeOut(function() { $(this).remove(); });
} else {
this.tip().remove();
}
},
fixTitle: function() {
var $e = this.$element;
if ($e.attr('title') || typeof($e.attr('original-title')) != 'string') {
$e.attr('original-title', $e.attr('title') || '').removeAttr('title');
}
},
getTitle: function() {
var title, $e = this.$element, o = this.options;
this.fixTitle();
var title, o = this.options;
if (typeof o.title == 'string') {
title = $e.attr(o.title == 'title' ? 'original-title' : o.title);
} else if (typeof o.title == 'function') {
title = o.title.call($e[0]);
}
title = ('' + title).replace(/(^\s*|\s*$)/, "");
return title || o.fallback;
},
tip: function() {
if (!this.$tip) {
this.$tip = $('<div class="tipsy"></div>').html('<div class="tipsy-arrow"></div><div class="tipsy-inner"></div>');
}
return this.$tip;
},
validate: function() {
if (!this.$element[0].parentNode) {
this.hide();
this.$element = null;
this.options = null;
}
},
enable: function() { this.enabled = true; },
disable: function() { this.enabled = false; },
toggleEnabled: function() { this.enabled = !this.enabled; }
};
$.fn.tipsy = function(options) {
if (options === true) {
return this.data('tipsy');
} else if (typeof options == 'string') {
var tipsy = this.data('tipsy');
if (tipsy) tipsy[options]();
return this;
}
options = $.extend({}, $.fn.tipsy.defaults, options);
function get(ele) {
var tipsy = $.data(ele, 'tipsy');
if (!tipsy) {
tipsy = new Tipsy(ele, $.fn.tipsy.elementOptions(ele, options));
$.data(ele, 'tipsy', tipsy);
}
return tipsy;
}
function enter() {
var tipsy = get(this);
tipsy.hoverState = 'in';
if (options.delayIn == 0) {
tipsy.show();
} else {
tipsy.fixTitle();
setTimeout(function() { if (tipsy.hoverState == 'in') tipsy.show(); }, options.delayIn);
}
};
function leave() {
var tipsy = get(this);
tipsy.hoverState = 'out';
if (options.delayOut == 0) {
tipsy.hide();
} else {
setTimeout(function() { if (tipsy.hoverState == 'out') tipsy.hide(); }, options.delayOut);
}
};
if (!options.live) this.each(function() { get(this); });
if (options.trigger != 'manual') {
var binder = options.live ? 'live' : 'bind',
eventIn = options.trigger == 'hover' ? 'mouseenter' : 'focus',
eventOut = options.trigger == 'hover' ? 'mouseleave' : 'blur';
this[binder](eventIn, enter)[binder](eventOut, leave);
}
return this;
};
$.fn.tipsy.clear = function() {
$('div.tipsy').stop().remove();
}
$.fn.tipsy.defaults = {
className: null,
delayIn: 0,
delayOut: 0,
fade: false,
fallback: '',
gravity: 'n',
html: false,
live: false,
offset: 0,
opacity: 0.8,
title: 'title',
trigger: 'hover'
};
// Overwrite this method to provide options on a per-element basis.
// For example, you could store the gravity in a 'tipsy-gravity' attribute:
// return $.extend({}, options, {gravity: $(ele).attr('tipsy-gravity') || 'n' });
// (remember - do not modify 'options' in place!)
$.fn.tipsy.elementOptions = function(ele, options) {
return $.metadata ? $.extend({}, options, $(ele).metadata()) : options;
};
$.fn.tipsy.autoNS = function() {
return $(this).offset().top > ($(document).scrollTop() + $(window).height() / 2) ? 's' : 'n';
};
$.fn.tipsy.autoWE = function() {
return $(this).offset().left > ($(document).scrollLeft() + $(window).width() / 2) ? 'e' : 'w';
};
/**
* yields a closure of the supplied parameters, producing a function that takes
* no arguments and is suitable for use as an autogravity function like so:
*
* @param margin (int) - distance from the viewable region edge that an
* element should be before setting its tooltip's gravity to be away
* from that edge.
* @param prefer (string, e.g. 'n', 'sw', 'w') - the direction to prefer
* if there are no viewable region edges effecting the tooltip's
* gravity. It will try to vary from this minimally, for example,
* if 'sw' is preferred and an element is near the right viewable
* region edge, but not the top edge, it will set the gravity for
* that element's tooltip to be 'se', preserving the southern
* component.
*/
$.fn.tipsy.autoBounds = function(margin, prefer) {
return function() {
var dir = {ns: prefer[0], ew: (prefer.length > 1 ? prefer[1] : false)},
boundTop = $(document).scrollTop() + margin,
boundLeft = $(document).scrollLeft() + margin,
$this = $(this);
if ($this.offset().top < boundTop) dir.ns = 'n';
if ($this.offset().left < boundLeft) dir.ew = 'w';
if ($(window).width() + $(document).scrollLeft() - $this.offset().left < margin) dir.ew = 'e';
if ($(window).height() + $(document).scrollTop() - $this.offset().top < margin) dir.ns = 's';
return dir.ns + (dir.ew ? dir.ew : '');
}
};
})(jQuery);

View File

@ -1,25 +0,0 @@
.tipsy { font-size: 90%; position: absolute; padding: 5px; z-index: 100000; overflow: hidden;}
.tipsy-inner { background-color: #000; color: #FFF; max-width: 500px; padding: 5px 8px 4px 8px; }
/* Rounded corners */
.tipsy-inner { border-radius: 3px; -moz-border-radius: 3px; -webkit-border-radius: 3px; }
/* Uncomment for shadow */
.tipsy-inner { box-shadow: 0 0 5px #000000; -webkit-box-shadow: 0 0 5px #000000; -moz-box-shadow: 0 0 5px #000000; }
.tipsy-arrow { position: absolute; width: 0; height: 0; line-height: 0; border: 5px dashed #000; }
/* Rules to colour arrows */
.tipsy-arrow-n { border-bottom-color: #000; }
.tipsy-arrow-s { border-top-color: #000; }
.tipsy-arrow-e { border-left-color: #000; }
.tipsy-arrow-w { border-right-color: #000; }
.tipsy-n .tipsy-arrow { top: 0px; left: 50%; margin-left: -5px; border-bottom-style: solid; border-top: none; border-left-color: transparent; border-right-color: transparent; }
.tipsy-nw .tipsy-arrow { top: 0; left: 10px; border-bottom-style: solid; border-top: none; border-left-color: transparent; border-right-color: transparent;}
.tipsy-ne .tipsy-arrow { top: 0; right: 10px; border-bottom-style: solid; border-top: none; border-left-color: transparent; border-right-color: transparent;}
.tipsy-s .tipsy-arrow { bottom: 0; left: 50%; margin-left: -5px; border-top-style: solid; border-bottom: none; border-left-color: transparent; border-right-color: transparent; }
.tipsy-sw .tipsy-arrow { bottom: 0; left: 10px; border-top-style: solid; border-bottom: none; border-left-color: transparent; border-right-color: transparent; }
.tipsy-se .tipsy-arrow { bottom: 0; right: 10px; border-top-style: solid; border-bottom: none; border-left-color: transparent; border-right-color: transparent; }
.tipsy-e .tipsy-arrow { right: 0; top: 50%; margin-top: -5px; border-left-style: solid; border-right: none; border-top-color: transparent; border-bottom-color: transparent; }
.tipsy-w .tipsy-arrow { left: 0; top: 50%; margin-top: -5px; border-right-style: solid; border-left: none; border-top-color: transparent; border-bottom-color: transparent; }

View File

@ -79,7 +79,7 @@
vertical-align: top;
}
.openerp .oe_title {
width: 50%;
width: 38%;
float: left;
}
.openerp .oe_title:after {
@ -314,47 +314,57 @@
.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 svg {
.openerp .oe_stat_button .oe_form_field_percent_pie {
width: 42px;
}
.openerp .oe_stat_button .oe_form_field_bar_chart {
width: 42px;
}
.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;
@ -470,7 +480,7 @@
display: inline-block;
height: 12px;
width: 12px;
vertical-align: bottom;
vertical-align: top;
border-radius: 10px;
margin: 1px 0;
}
@ -496,40 +506,6 @@
font-style: italic;
text-decoration: none;
}
.openerp.oe_tooltip {
font-size: 12px;
}
.openerp.oe_tooltip .oe_tooltip_string {
color: #ffdd55;
font-weight: bold;
font-size: 13px;
}
.openerp.oe_tooltip .oe_tooltip_help {
white-space: pre-wrap;
}
.openerp.oe_tooltip .oe_tooltip_technical {
padding: 0 0 4px 0;
margin: 5px 0 0 15px;
}
.openerp.oe_tooltip .oe_tooltip_technical li {
list-style: circle;
}
.openerp.oe_tooltip .oe_tooltip_technical_title {
font-weight: bold;
}
.openerp.oe_tooltip .oe_tooltip_close {
margin: -5px 0 0 2px;
cursor: default;
float: right;
color: white;
}
.openerp.oe_tooltip .oe_tooltip_close:hover {
color: #999999;
cursor: pointer;
}
.openerp.oe_tooltip .oe_tooltip_message {
max-width: 310px;
}
.openerp .oe_notebook {
margin: 8px 0;
padding: 0 16px;
@ -746,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;
@ -1916,7 +1892,7 @@
.openerp .oe_form > :not(.oe_form_nosheet) header {
padding-left: 2px;
}
.openerp .oe_form > :not(.oe_form_nosheet) header ul {
.openerp .oe_form > :not(.oe_form_nosheet) header ul:not(.oe_tooltip_technical):not(.oe_dropdown_menu) {
display: inline-block;
float: right;
}
@ -2772,9 +2748,11 @@
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;
@ -3319,6 +3297,88 @@ body.oe_single_form .oe_single_form_container {
overflow: hidden !important;
}
}
.tooltip {
padding: 0;
margin: 0;
font-family: "Lucida Grande", Helvetica, Verdana, Arial, sans-serif;
color: #4c4c4c;
font-size: 12px;
background: white;
text-shadow: 0 1px 1px rgba(255, 255, 255, 0.5);
background-color: transparent;
}
.tooltip .tooltip-inner {
text-align: left !important;
max-width: 350px;
}
.tooltip .tooltip-inner .oe_tooltip_string {
color: #ffdd55;
font-weight: bold;
font-size: 13px;
}
.tooltip .tooltip-inner .oe_tooltip_help {
white-space: pre-wrap;
}
.tooltip .tooltip-inner .oe_tooltip_technical {
padding: 0 0 4px 0;
margin: 5px 0 0 15px;
}
.tooltip .tooltip-inner .oe_tooltip_technical li {
list-style: circle;
}
.tooltip .tooltip-inner .oe_tooltip_technical_title {
font-weight: bold;
}
.tooltip .tooltip-inner .oe_tooltip_close {
margin: -5px 0 0 2px;
cursor: default;
float: right;
color: white;
}
.tooltip .tooltip-inner .oe_tooltip_close:hover {
color: #999999;
cursor: pointer;
}
.tooltip .tooltip-inner .oe_tooltip_message {
max-width: 310px;
}
.ui-icon {
width: 18px;
height: 18px;
}
.modal .modal-header button.close {
border: none;
background: none;
padding: 1px;
height: 18px;
font-size: 20px;
}
.modal .modal-footer {
text-align: left;
}
.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;
}
.blockUI.blockOverlay {
background-color: black;
opacity: 0.6;
}
.openerp .dropdown-menu.state {
background: white;
background: white !important;
@ -3330,7 +3390,6 @@ body.oe_single_form .oe_single_form_container {
color: #333333;
padding-left: 5px;
padding-right: 0px;
font-size: 13px;
}
.openerp .btn-group.state {
padding-top: 3px;
@ -3379,30 +3438,3 @@ body.oe_single_form .oe_single_form_container {
float: left;
margin-right: 8px;
}
.ui-icon {
width: 18px;
height: 18px;
}
.modal .modal-header button.close {
border: none;
background: none;
padding: 1px;
height: 18px;
font-size: 20px;
}
.modal .modal-footer {
text-align: left;
}
.modal .oe_act_window.modal-body{
padding: 0;
}
input[type="radio"], input[type="checkbox"] {
margin-right: 4px;
margin-left: 4px;
}
.blockUI.blockOverlay {
background-color: black;
opacity: 0.6;
}

View File

@ -190,7 +190,7 @@ $sheet-padding: 16px
td
vertical-align: top
.oe_title
width: 50%
width: 38%
float: left
.oe_title:after
content: "."
@ -330,41 +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
svg
width: 38px
height: 38px
.oe_avatar
> img
max-height: 90px
@ -446,7 +453,7 @@ $sheet-padding: 16px
display: inline-block
height: 12px
width: 12px
vertical-align: bottom
vertical-align: top
border-radius: 10px
margin: 1px 0
&.oe_form_invalid
@ -466,33 +473,6 @@ $sheet-padding: 16px
text-decoration: none
margin-bottom: 1px
// }}}
// Tooltips {{{
&.oe_tooltip
font-size: 12px
.oe_tooltip_string
color: #FD5
font-weight: bold
font-size: 13px
.oe_tooltip_help
white-space: pre-wrap
.oe_tooltip_technical
padding: 0 0 4px 0
margin: 5px 0 0 15px
li
list-style: circle
.oe_tooltip_technical_title
font-weight: bold
.oe_tooltip_close
margin: -5px 0 0 2px
cursor: default
float: right
color: white
&:hover
color: #999
cursor: pointer
.oe_tooltip_message
max-width: 310px
// }}}
// Notebook {{{
.oe_notebook
margin: 8px 0
@ -660,7 +640,8 @@ $sheet-padding: 16px
// }}}
// Notifications {{{
.oe_notification
z-index: 1050
z-index: 1500
.oe_webclient_timezone_notification
a
color: white
@ -1571,7 +1552,7 @@ $sheet-padding: 16px
// FormView.header {{{
.oe_form > :not(.oe_form_nosheet) header
padding-left: 2px
ul
ul:not(.oe_tooltip_technical):not(.oe_dropdown_menu)
display: inline-block
float: right
.oe_button
@ -2245,7 +2226,10 @@ $sheet-padding: 16px
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
@ -2697,6 +2681,76 @@ body.oe_single_form
overflow: hidden !important
// }}}
// End of customize
// Customize bootstrap3 for tooltip
.tooltip
padding: 0
margin: 0
font-family: "Lucida Grande", Helvetica, Verdana, Arial, sans-serif
color: #4c4c4c
font-size: 12px
background: white
text-shadow: 0 1px 1px rgba(255, 255, 255, 0.5)
background-color: transparent
.tooltip-inner
text-align: left !important
max-width: 350px
.oe_tooltip_string
color: #FD5
font-weight: bold
font-size: 13px
.oe_tooltip_help
white-space: pre-wrap
.oe_tooltip_technical
padding: 0 0 4px 0
margin: 5px 0 0 15px
li
list-style: circle
.oe_tooltip_technical_title
font-weight: bold
.oe_tooltip_close
margin: -5px 0 0 2px
cursor: default
float: right
color: white
&:hover
color: #999
cursor: pointer
.oe_tooltip_message
max-width: 310px
// Hack for ui icon {{{
.ui-icon
width: 18px
height: 18px
// End hack}}}
// Customized modal according bootstrap3
.modal
.modal-header button.close
border: none
background: none
padding: 1px
height: 18px
font-size: 20px
.modal-footer
text-align: left
.oe_button
margin: 0 4px 0 0
.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
.blockUI.blockOverlay
background-color: black
opacity: 0.6000000238418579
.openerp
.dropdown-menu.state
background: white
@ -2751,34 +2805,5 @@ body.oe_single_form
.oe_star_left
float: left
margin-right: 8px
// End hack }}}
// Hack for ui icon {{{
.ui-icon
width: 18px
height: 18px
// End hack}}}
// Customized modal according bootstrap3
.modal
.modal-header button.close
border: none
background: none
padding: 1px
height: 18px
font-size: 20px
.modal-footer
text-align: left
.oe_act_window.modal-body
padding: 0
input[type="radio"], input[type="checkbox"]
margin-right: 4px
margin-left: 4px
.blockUI.blockOverlay
background-color: black
opacity: 0.6000000238418579
// au BufWritePost,FileWritePost *.sass :!sass --style expanded --line-numbers <afile> > "%:p:r.css"
// vim:tabstop=4:shiftwidth=4:softtabstop=4:fdm=marker:

View File

@ -166,7 +166,7 @@ instance.web.Dialog = instance.web.Widget.extend({
$dialog_content.openerpClass();
this.$dialog_box.on('hidden.bs.modal', this, function(){
self.trigger("closing");
self.close();
});
this.$dialog_box.modal('show');
@ -178,8 +178,11 @@ instance.web.Dialog = instance.web.Widget.extend({
Closes the popup, if destroy_on_close was passed to the constructor, it is also destroyed.
*/
close: function(reason) {
if (this.dialog_inited && this.$el.is(":data(bs.modal)")) {
this.$el.parents('.modal').modal('hide');
if (this.dialog_inited) {
this.trigger("closing", reason);
if (this.$el.is(":data(bs.modal)")) { // may have been destroyed by closing signal
this.$el.parents('.modal').modal('hide');
}
}
},
_closing: function() {
@ -209,8 +212,9 @@ instance.web.Dialog = instance.web.Widget.extend({
//we need this to put the instruction to remove modal from DOM at the end
//of the queue, otherwise it might already have been removed before the modal-backdrop
//is removed when pressing escape key
var $parent = this.$el.parents('.modal');
setTimeout(function () {
self.$el.parents('.modal').remove();
$parent.remove();
},0);
}
this._super();
@ -1074,8 +1078,8 @@ instance.web.Client = instance.web.Widget.extend({
},
bind_events: function() {
var self = this;
this.$el.on('mouseenter', '.oe_systray > div:not([data-tipsy=true])', function() {
$(this).attr('data-tipsy', 'true').tipsy().trigger('mouseenter');
this.$el.on('mouseenter', '.oe_systray > div:not([data-toggle=tooltip])', function() {
$(this).attr('data-toggle', 'tooltip').tooltip().trigger('mouseenter');
});
this.$el.on('click', '.oe_dropdown_toggle', function(ev) {
ev.preventDefault();
@ -1099,7 +1103,7 @@ instance.web.Client = instance.web.Widget.extend({
}, 0);
});
instance.web.bus.on('click', this, function(ev) {
$.fn.tipsy.clear();
$.fn.tooltip('destroy');
if (!$(ev.target).is('input[type=file]')) {
self.$el.find('.oe_dropdown_menu.oe_opened, .oe_dropdown_toggle.oe_opened').removeClass('oe_opened');
}
@ -1453,7 +1457,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) {

View File

@ -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);
})
@ -345,44 +345,27 @@ instance.web.Session.include( /** @lends instance.web.Session# */{
load_css: function (files) {
var self = this;
_.each(files, function (file) {
$('head').append($('<link>', {
'href': self.url(file, null),
'rel': 'stylesheet',
'type': 'text/css'
}));
openerp.loadCSS(self.url(file, null));
});
},
load_js: function(files) {
var self = this;
var d = $.Deferred();
if(files.length !== 0) {
if (files.length !== 0) {
var file = files.shift();
var tag = document.createElement('script');
tag.type = 'text/javascript';
tag.src = self.url(file, null);
tag.onload = tag.onreadystatechange = function() {
if ( (tag.readyState && tag.readyState != "loaded" && tag.readyState != "complete") || tag.onload_done )
return;
tag.onload_done = true;
self.load_js(files).done(function () {
d.resolve();
});
};
var head = document.head || document.getElementsByTagName('head')[0];
head.appendChild(tag);
var url = self.url(file, null);
openerp.loadJS(url).done(d.resolve);
} else {
d.resolve();
}
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 +443,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();
}
@ -788,6 +768,22 @@ instance.web.unblockUI = function() {
return $.unblockUI.apply($, arguments);
};
/* Bootstrap defaults overwrite */
$.fn.tooltip.Constructor.DEFAULTS.placement = 'auto top';
$.fn.tooltip.Constructor.DEFAULTS.html = true;
$.fn.tooltip.Constructor.DEFAULTS.container = 'body';
//overwrite bootstrap tooltip method to fix bug when using placement
//auto and the parent element does not exist anymore resulting in
//an error. This should be remove once bootstrap fix the bug
var bootstrap_show_function = $.fn.tooltip.Constructor.prototype.show;
$.fn.tooltip.Constructor.prototype.show = function (e) {
if (this.$element.parent().length === 0){
return;
}
return bootstrap_show_function.call(this, e);
};
/**
* Registry for all the client actions key: tag value: widget
*/

View File

@ -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();}

View File

@ -933,6 +933,46 @@ openerp.jsonpRpc = function(url, fct_name, params, settings) {
});
};
openerp.loadCSS = function (url) {
if (!$('link[href="' + url + '"]').length) {
$('head').append($('<link>', {
'href': url,
'rel': 'stylesheet',
'type': 'text/css'
}));
}
};
openerp.loadJS = function (url) {
var def = $.Deferred();
if ($('script[src="' + url + '"]').length) {
def.resolve();
} else {
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = url;
script.onload = script.onreadystatechange = function() {
if ((script.readyState && script.readyState != "loaded" && script.readyState != "complete") || script.onload_done) {
return;
}
script.onload_done = true;
def.resolve(url);
};
script.onerror = function () {
console.error("Error loading file", script.src);
def.reject(url);
};
var head = document.head || document.getElementsByTagName('head')[0];
head.appendChild(script);
}
return def;
};
openerp.loadBundle = function (name) {
return $.when(
openerp.loadCSS('/web/css/' + name),
openerp.loadJS('/web/js/' + name)
);
};
var realSetTimeout = function(fct, millis) {
var finished = new Date().getTime() + millis;
var wait = function() {

View File

@ -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);
},

View File

@ -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;

View File

@ -631,6 +631,9 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
});
}
return $.when();
}).fail(function() {
self.save_list.pop();
return $.when();
});
}
return iterate();
@ -727,8 +730,9 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
}
}
},
on_button_save: function() {
on_button_save: function(e) {
var self = this;
$(e.target).attr("disabled", true);
return this.save().done(function(result) {
self.trigger("save", result);
self.reload().then(function() {
@ -738,6 +742,8 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM
parent.menu.do_reload_needaction();
}
});
}).always(function(){
$(e.target).attr("disabled", false);
});
},
on_button_cancel: function(event) {
@ -1832,7 +1838,7 @@ instance.web.form.FormWidget = instance.web.Widget.extend(instance.web.form.Invi
this.$el.addClass(this.node.attrs["class"] || "");
},
destroy: function() {
$.fn.tipsy.clear();
$.fn.tooltip('destroy');
this._super.apply(this, arguments);
},
/**
@ -1863,10 +1869,25 @@ instance.web.form.FormWidget = instance.web.Widget.extend(instance.web.form.Invi
do_attach_tooltip: function(widget, trigger, options) {
widget = widget || this;
trigger = trigger || this.$el;
var container = 'body';
/*TODO: need to be refactor
in the case we can find the view form in the parent,
attach the element to it (to prevent tooltip to keep showing
when switching view) or if we have a modal currently showing,
attach tooltip to the modal to prevent the tooltip to show in the body in the
case we close the modal too fast*/
if ($(trigger).parents('.oe_view_manager_view_form').length > 0){
container = $(trigger).parents('.oe_view_manager_view_form');
}
else {
if (window.$('.modal.in').length>0){
container = window.$('.modal.in:last()');
}
}
options = _.extend({
delayIn: 500,
delayOut: 0,
fade: true,
delay: { show: 500, hide: 0 },
trigger: 'hover',
container: container,
title: function() {
var template = widget.template + '.tooltip';
if (!QWeb.has_template(template)) {
@ -1877,12 +1898,12 @@ instance.web.form.FormWidget = instance.web.Widget.extend(instance.web.form.Invi
widget: widget
});
},
gravity: $.fn.tipsy.autoBounds(50, 'nw'),
html: true,
opacity: 0.85,
trigger: 'hover'
}, options || {});
$(trigger).tipsy(options);
//only show tooltip if we are in debug or if we have a help to show, otherwise it will display
//as empty
if (instance.session.debug || widget.node.attrs.help || (widget.field && widget.field.help)){
$(trigger).tooltip(options);
}
},
/**
* Builds a new context usable for operations related to fields by merging
@ -1919,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, '');
@ -1955,7 +1976,7 @@ instance.web.form.WidgetButton = instance.web.form.FormWidget.extend({
var exec_action = function() {
if (self.node.attrs.confirm) {
var def = $.Deferred();
var dialog = instance.web.Dialog(this, {
var dialog = new instance.web.Dialog(this, {
title: _t('Confirm'),
buttons: [
{text: _t("Cancel"), click: function() {
@ -2119,8 +2140,8 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.w
this.$el.find('.oe_field_translate').click(this.on_translate);
}
this.$label = this.view ? this.view.$el.find('label[for=' + this.id_for_label + ']') : $();
this.do_attach_tooltip(this, this.$label[0] || this.$el);
if (instance.session.debug) {
this.do_attach_tooltip(this, this.$label[0] || this.$el);
this.$label.off('dblclick').on('dblclick', function() {
console.log("Field '%s' of type '%s' in View: %o", self.name, (self.node.attrs.widget || self.field.type), self.view);
window.w = self;
@ -2535,6 +2556,76 @@ instance.web.form.FieldFloat = instance.web.form.FieldChar.extend({
}
});
instance.web.form.FieldCharDomain = instance.web.form.AbstractField.extend(instance.web.form.ReinitializeFieldMixin, {
init: function(field_manager, node) {
this._super.apply(this, arguments);
},
start: function() {
var self = this;
this._super.apply(this, arguments);
this.on("change:effective_readonly", this, function () {
this.display_field();
this.render_value();
});
this.display_field();
return this._super();
},
render_value: function() {
this.$('button.select_records').css('visibility', this.get('effective_readonly') ? 'hidden': '');
},
set_value: function(value_) {
var self = this;
this.set('value', value_ || false);
this.display_field();
},
display_field: function() {
var self = this;
this.$el.html(instance.web.qweb.render("FieldCharDomain", {widget: this}));
if (this.get('value')) {
var model = this.options.model || this.field_manager.get_field_value(this.options.model_field);
var domain = instance.web.pyeval.eval('domain', this.get('value'));
var ds = new instance.web.DataSetStatic(self, model, self.build_context());
ds.call('search_count', [domain]).then(function (results) {
$('.oe_domain_count', self.$el).text(results + ' records selected');
$('button span', self.$el).text(' Change selection');
});
} else {
$('.oe_domain_count', this.$el).text('0 record selected');
$('button span', this.$el).text(' Select records');
};
this.$('.select_records').on('click', self.on_click);
},
on_click: function(ev) {
var self = this;
var model = this.options.model || this.field_manager.get_field_value(this.options.model_field);
this.pop = new instance.web.form.SelectCreatePopup(this);
this.pop.select_element(
model, {title: 'Select records...'},
[], this.build_context());
this.pop.on("elements_selected", self, function(element_ids) {
if (this.pop.$('input.oe_list_record_selector').prop('checked')) {
var search_data = this.pop.searchview.build_search_data();
var domain_done = instance.web.pyeval.eval_domains_and_contexts({
domains: search_data.domains,
contexts: search_data.contexts,
group_by_seq: search_data.groupbys || []
}).then(function (results) {
return results.domain;
});
}
else {
var domain = ["id", "in", element_ids];
var domain_done = $.Deferred().resolve(domain);
}
$.when(domain_done).then(function (domain) {
var domain = self.pop.dataset.domain.concat(domain || []);
self.set_value(JSON.stringify(domain))
});
});
event.preventDefault();
},
});
instance.web.DateTimeWidget = instance.web.Widget.extend({
template: "web.datepicker",
jqueryui_object: 'datetimepicker',
@ -2930,10 +3021,10 @@ instance.web.form.FieldPercentPie = instance.web.form.AbstractField.extend({
svg.innerHTML = "";
nv.addGraph(function() {
var size=43;
var width = 42, height = 42;
var chart = nv.models.pieChart()
.width(size)
.height(size)
.width(width)
.height(height)
.margin({top: 0, right: 0, bottom: 0, left: 0})
.donut(true)
.showLegend(false)
@ -2946,11 +3037,11 @@ instance.web.form.FieldPercentPie = instance.web.form.AbstractField.extend({
.datum([{'x': 'value', 'y': value}, {'x': 'complement', 'y': 100 - value}])
.transition()
.call(chart)
.attr({width:size, height:size});
.attr('style', 'width: ' + width + 'px; height:' + height + 'px;');
d3.select(svg)
.append("text")
.attr({x: size/2, y: size/2 + 3, 'text-anchor': 'middle'})
.attr({x: width/2, y: height/2 + 3, 'text-anchor': 'middle'})
.style({"font-size": "10px", "font-weight": "bold"})
.text(formatted_value);
@ -2960,6 +3051,43 @@ instance.web.form.FieldPercentPie = instance.web.form.AbstractField.extend({
}
});
/**
The FieldBarChart expectsa list of values (indeed)
*/
instance.web.form.FieldBarChart = instance.web.form.AbstractField.extend({
template: 'FieldBarChart',
render_value: function() {
var value = JSON.parse(this.get('value'));
var svg = this.$('svg')[0];
svg.innerHTML = "";
nv.addGraph(function() {
var width = 34, height = 34;
var chart = nv.models.discreteBarChart()
.x(function (d) { return d.tooltip })
.y(function (d) { return d.value })
.width(width)
.height(height)
.margin({top: 0, right: 0, bottom: 0, left: 0})
.tooltips(false)
.showValues(false)
.transitionDuration(350)
.showXAxis(false)
.showYAxis(false);
d3.select(svg)
.datum([{key: 'values', values: value}])
.transition()
.call(chart)
.attr('style', 'width: ' + (width + 4) + 'px; height: ' + (height + 8) + 'px;');
nv.utils.windowResize(chart.update);
return chart;
});
}
});
instance.web.form.FieldSelection = instance.web.form.AbstractField.extend(instance.web.form.ReinitializeFieldMixin, {
@ -3328,13 +3456,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(){
@ -3441,20 +3572,25 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
return;
}
var pop = new instance.web.form.FormOpenPopup(self);
pop.show_element(
self.field.relation,
self.get("value"),
self.build_context(),
{
title: _t("Open: ") + self.string
}
);
pop.on('write_completed', self, function(){
self.display_value = {};
self.display_value_backup = {};
self.render_value();
self.focus();
self.view.do_onchange(self);
var context = self.build_context().eval();
var model_obj = new instance.web.Model(self.field.relation);
model_obj.call('get_formview_id', [self.get("value"), context]).then(function(view_id){
pop.show_element(
self.field.relation,
self.get("value"),
self.build_context(),
{
title: _t("Open: ") + self.string,
view_id: view_id
}
);
pop.on('write_completed', self, function(){
self.display_value = {};
self.display_value_backup = {};
self.render_value();
self.focus();
self.trigger('changed_value');
});
});
});
@ -3516,7 +3652,7 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
}
self.floating = false;
}
if (used && self.get("value") === false && ! self.no_ed) {
if (used && self.get("value") === false && ! self.no_ed && (self.options.no_create === false || self.options.no_create === undefined)) {
self.ed_def.reject();
self.uned_def.reject();
self.ed_def = $.Deferred();
@ -3588,6 +3724,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) {
@ -3650,13 +3788,10 @@ instance.web.form.FieldMany2One = instance.web.form.AbstractField.extend(instanc
.html(link);
if (! this.options.no_open)
$link.click(function () {
self.do_action({
type: 'ir.actions.act_window',
res_model: self.field.relation,
res_id: self.get("value"),
views: [[false, 'form']],
target: 'current',
context: self.build_context().eval(),
var context = self.build_context().eval();
var model_obj = new instance.web.Model(self.field.relation);
model_obj.call('get_formview_action', [self.get("value"), context]).then(function(action){
self.do_action(action);
});
return false;
});
@ -6016,20 +6151,30 @@ instance.web.form.X2ManyCounter = instance.web.form.AbstractField.extend(instanc
display a simple string "<value of field> <label of the field>"
*/
instance.web.form.StatInfo = instance.web.form.AbstractField.extend({
is_field_number: true,
init: function() {
this._super.apply(this, arguments);
this.set("value", 0);
this.internal_set_value(0);
},
set_value: function(value_) {
if (value_ === false || value_ === undefined) {
value_ = 0;
}
this._super.apply(this, [value_]);
},
render_value: function() {
var options = {
value: this.get("value") || 0,
text: this.string,
};
if (! this.node.attrs.nolabel) {
options.text = this.string
}
this.$el.html(QWeb.render("StatInfo", options));
},
});
/**
* Registry of form fields, called by :js:`instance.web.FormView`.
*
@ -6043,6 +6188,7 @@ instance.web.form.widgets = new instance.web.Registry({
'url' : 'instance.web.form.FieldUrl',
'text' : 'instance.web.form.FieldText',
'html' : 'instance.web.form.FieldTextHtml',
'char_domain': 'instance.web.form.FieldCharDomain',
'date' : 'instance.web.form.FieldDate',
'datetime' : 'instance.web.form.FieldDatetime',
'selection' : 'instance.web.form.FieldSelection',
@ -6058,6 +6204,7 @@ instance.web.form.widgets = new instance.web.Registry({
'boolean' : 'instance.web.form.FieldBoolean',
'float' : 'instance.web.form.FieldFloat',
'percentpie': 'instance.web.form.FieldPercentPie',
'barchart': 'instance.web.form.FieldBarChart',
'integer': 'instance.web.form.FieldFloat',
'float_time': 'instance.web.form.FieldFloat',
'progressbar': 'instance.web.form.FieldProgressBar',

View File

@ -2261,8 +2261,8 @@ instance.web.list.Button = instance.web.list.Column.extend({
attrs = this.modifiers_for(row_data);
}
if (attrs.invisible) { return ''; }
return QWeb.render('ListView.row.button', {
var template = this.icon && 'ListView.row.button' || 'ListView.row.text_button';
return QWeb.render(template, {
widget: this,
prefix: instance.session.prefix,
disabled: attrs.readonly

View File

@ -451,6 +451,9 @@ instance.web.ActionManager = instance.web.Widget.extend({
ir_actions_client: function (action, options) {
var self = this;
var ClientWidget = instance.web.client_actions.get_object(action.tag);
if (!ClientWidget) {
return self.do_warn("Action Error", "Could not find client action '" + action.tag + "'.");
}
if (!(ClientWidget.prototype instanceof instance.web.Widget)) {
var next;
@ -581,7 +584,7 @@ instance.web.ViewManager = instance.web.Widget.extend({
var self = this;
this.$el.find('.oe_view_manager_switch a').click(function() {
self.switch_mode($(this).data('view-type'));
}).tipsy();
}).tooltip();
var views_ids = {};
_.each(this.views_src, function(view) {
self.views[view.view_type] = $.extend({}, view, {
@ -1157,10 +1160,8 @@ instance.web.Sidebar = instance.web.Widget.extend({
this.$('.oe_form_dropdown_section').each(function() {
$(this).toggle(!!$(this).find('li').length);
});
self.$("[title]").tipsy({
'html': true,
'delayIn': 500,
self.$("[title]").tooltip({
delay: { show: 500, hide: 0}
});
},
/**

View File

@ -0,0 +1,58 @@
(function() {
/*
* object.watch polyfill
*
* 2012-04-03
*
* By Eli Grey, http://eligrey.com
* Public Domain.
* NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
*/
// object.watch
if (!Object.prototype.watch) {
Object.defineProperty(Object.prototype, "watch", {
enumerable: false
, configurable: true
, writable: false
, value: function (prop, handler) {
var
oldval = this[prop]
, newval = oldval
, getter = function () {
return newval;
}
, setter = function (val) {
oldval = newval;
return newval = handler.call(this, prop, oldval, val);
}
;
if (delete this[prop]) { // can't watch constants
Object.defineProperty(this, prop, {
get: getter
, set: setter
, enumerable: true
, configurable: true
});
}
}
});
}
// object.unwatch
if (!Object.prototype.unwatch) {
Object.defineProperty(Object.prototype, "unwatch", {
enumerable: false
, configurable: true
, writable: false
, value: function (prop) {
var val = this[prop];
delete this[prop]; // remove accessors
this[prop] = val;
}
});
}
})();

View File

@ -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>
@ -612,10 +612,16 @@
<div class="oe_sidebar">
<t t-foreach="widget.sections" t-as="section">
<div class="oe_form_dropdown_section">
<button class="oe_dropdown_toggle oe_dropdown_arrow">
<button class="oe_dropdown_toggle oe_dropdown_arrow" t-if="section.name != 'buttons'">
<t t-if="section.name == 'files'" t-raw="widget.items[section.name].length || ''"/>
<t t-esc="section.label"/>
</button>
<t t-if="section.name == 'buttons'" t-foreach="widget.items[section.name]" t-as="item" t-att-class="item.classname">
<button t-att-title="item.title or ''" t-att-data-section="section.name" t-att-data-index="item_index" t-att-href="item.url"
target="_blank" class="oe_sidebar_button oe_highlight">
<t t-raw="item.label"/>
</button>
</t>
<ul class="oe_dropdown_menu">
<li t-foreach="widget.items[section.name]" t-as="item" t-att-class="item.classname">
<t t-if="section.name == 'files'">
@ -792,11 +798,15 @@
</span>
</t>
</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_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' : undefined"
><img t-attf-src="#{prefix}/web/static/src/img/icons/#{widget.icon}.png"
t-att-alt="widget.string"/></button>
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">
<!-- adds back padding to row being rendered after edition, if necessary
(if not deletable add back padding), otherwise the row being added is
@ -932,7 +942,6 @@
</t>
<t t-name="FormRenderingLabel">
<label t-att-for="_for"
t-att-title="help"
t-attf-class="#{classnames} oe_form_label#{help ? '_help' : ''} oe_align_#{align}">
<t t-esc="string"/>
</label>
@ -994,7 +1003,7 @@
</li>
<li t-if="widget.field and widget.field.selection" data-item="selection">
<span class="oe_tooltip_technical_title">Selection:</span>
<ul>
<ul class="oe_tooltip_technical">
<li t-foreach="widget.field.selection" t-as="option">
[<t t-esc="option[0]"/>]
<t t-if="option[1]"> - </t>
@ -1104,6 +1113,16 @@
</t>
</div>
</t>
<t t-name="FieldCharDomain">
<div class="oe_form_field">
<span class="oe_domain_count"/>
<button class="oe_button oe_link select_records" type="button"
t-att-style="widget.node.attrs.style"
t-att-accesskey="widget.node.attrs.accesskey">
<span class="fa fa-arrow-right"/>
</button>
</div>
</t>
<t t-name="web.datepicker">
<span>
<t t-set="placeholder" t-value="widget.getParent().node and widget.getParent().node.attrs.placeholder"/>
@ -1244,9 +1263,16 @@
</span>
</t>
<t t-name="FieldPercentPie">
<span class="oe_form_field oe_form_field_percent_pie" t-att-style="widget.node.attrs.style">
<div class="oe_form_field oe_form_field_percent_pie" t-att-style="widget.node.attrs.style">
<svg></svg>
</span>
<span t-if="widget.string"><t t-esc="widget.string"/></span>
</div>
</t>
<t t-name="FieldBarChart">
<div class="oe_form_field oe_form_field_bar_chart" t-att-style="widget.node.attrs.style">
<svg></svg>
<span t-if="widget.string"><t t-esc="widget.string"/></span>
</div>
</t>
<t t-name="FieldStatus">
<ul t-att-class="'oe_form_field_status ' + (widget.options.clickable ? 'oe_form_status_clickable' : 'oe_form_status')" t-att-style="widget.node.attrs.style"/>
@ -1417,7 +1443,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" class="stat_button_icon"><t t-if="widget.icon" 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>
@ -1956,13 +1982,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">

View File

@ -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');
});
});

View File

@ -2,4 +2,3 @@
import test_js
import test_menu
import test_serving_base
import test_ui

View File

@ -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/

View File

@ -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);

View File

@ -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.

View File

@ -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 dont 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;
};
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,79 @@
<!DOCTYPE html>
<html style="height: 100%">
<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</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" />
<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>
<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"/>
<script type="text/javascript">
$(function() {
var s = new openerp.init({{ modules|safe }});
var wc = new s.web.WebClient(null, { action: 'database_manager' });
wc.appendTo($(document.body));
});
</script>
</head>
<body>
<!--[if lte IE 8]>
<script src="//ajax.googleapis.com/ajax/libs/chrome-frame/1/CFInstall.min.js"></script>
<script>CFInstall.check({mode: "overlay"});</script>
<![endif]-->
</body>
</html>

View File

@ -3,6 +3,114 @@
-->
<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="jqueryui_conflict" name="jquery.ui.conflict">
<!-- TODO: get rid of this hack once jQuery.ui is removed -->
<script type="text/javascript" charset="utf-8">
$.fn.bstooltip = $.fn.tooltip;
</script>
<t t-raw="0"/>
<script type="text/javascript" charset="utf-8">
$.fn.tooltip = $.fn.bstooltip;
</script>
</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>
<link rel="stylesheet" href="/web/static/lib/bootstrap/css/bootstrap.css"/>
<script type="text/javascript" src="/web/static/lib/bootstrap/js/bootstrap.js"></script>
<!-- jQuery ui -->
<link rel="stylesheet" href="/web/static/lib/jquery.ui.bootstrap/css/custom-theme/jquery-ui-1.9.0.custom.css"/>
<t t-call="web.jqueryui_conflict">
<script type="text/javascript" src="/web/static/lib/jquery.ui/js/jquery-ui-1.9.1.custom.js"></script>
</t>
<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>
<!-- 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">&lt;!DOCTYPE html&gt;
<html style="height: 100%">
@ -23,12 +131,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 +181,10 @@
</script>
<div class="form-group field-db" t-if="databases and len(databases) &gt; 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 +224,55 @@
</t>
</template>
<template id="web.qunit_suite">
&lt;!DOCTYPE html&gt;
<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>

View File

@ -10,8 +10,4 @@ Openerp Web API.
'depends': ['web'],
'installable': True,
'auto_install': False,
'js' : [
],
'css' : [
],
}

View File

@ -9,14 +9,8 @@ OpenERP Web Calendar view.
'author': 'OpenERP SA, Valentino Lab (Kalysto)',
'version': '2.0',
'depends': ['web'],
'data' : [],
'js': [
'static/lib/fullcalendar/js/fullcalendar.js',
'static/src/js/*.js'
],
'css': [
'static/lib/fullcalendar/css/*.css',
'static/src/css/*.css'
'data' : [
'views/web_calendar.xml',
],
'qweb': [
'static/src/xml/*.xml',

View File

@ -14,8 +14,8 @@ 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-09 06:40+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:35+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web_calendar
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:40+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:35+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web_calendar
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:40+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:35+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web_calendar
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:40+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:35+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web_calendar
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:40+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:35+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web_calendar
#. openerp-web

View File

@ -14,8 +14,8 @@ 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-09 06:40+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:35+0000\n"
"X-Generator: Launchpad (build 16996)\n"
"X-Poedit-Language: Czech\n"
#. module: web_calendar

View File

@ -14,8 +14,8 @@ 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-09 06:40+0000\n"
"X-Generator: Launchpad (build 16976)\n"
"X-Launchpad-Export-Date: 2014-05-06 06:35+0000\n"
"X-Generator: Launchpad (build 16996)\n"
#. module: web_calendar
#. openerp-web

Some files were not shown because too many files have changed in this diff Show More