diff --git a/addons/web/__openerp__.py b/addons/web/__openerp__.py index 4051e607e21..3d9d1a4333d 100644 --- a/addons/web/__openerp__.py +++ b/addons/web/__openerp__.py @@ -3,8 +3,10 @@ "category": "Hidden", "description": """ - OpenERP Web core module. - This module provides the core of the OpenERP Web Client. +OpenERP Web core module. +======================== + +This module provides the core of the OpenERP Web Client. """, "depends" : [], 'auto_install': True, diff --git a/addons/web/common/http.py b/addons/web/common/http.py index bb87d719bad..f1426d76d27 100644 --- a/addons/web/common/http.py +++ b/addons/web/common/http.py @@ -425,11 +425,14 @@ class DisableCacheMiddleware(object): def start_wrapped(status, headers): referer = environ.get('HTTP_REFERER', '') parsed = urlparse.urlparse(referer) - debug = not urlparse.parse_qs(parsed.query).has_key('debug') - filtered_headers = [(k,v) for k,v in headers if not (k=='Last-Modified' or (debug and k=='Cache-Control'))] + debug = parsed.query.count('debug') >= 1 + nh = dict(headers) + if 'Last-Modified' in nh: del nh['Last-Modified'] if debug: - filtered_headers.append(('Cache-Control', 'no-cache')) - start_response(status, filtered_headers) + if 'Expires' in nh: del nh['Expires'] + if 'Etag' in nh: del nh['Etag'] + nh['Cache-Control'] = 'no-cache' + start_response(status, nh.items()) return self.app(environ, start_wrapped) class Root(object): diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index 9c8451cd71e..e4c576fecae 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -194,9 +194,12 @@ class WebClient(openerpweb.Controller): if mods is not None: path += '?mods=' + mods return [path] - # old code to force cache reloading, this really works, sorry niv but we stop wasting time - return ['%s?debug=%s' % (wp, os.path.getmtime(fp)) for fp, wp in self.manifest_glob(req, mods, extension)] - return [el[1] for el in self.manifest_glob(req, mods, extension)] + no_sugar = req.httprequest.environ["QUERY_STRING"].count("no_sugar") >= 1 + no_sugar = no_sugar or req.httprequest.environ.get('HTTP_REFERER', '').count("no_sugar") >= 1 + if not no_sugar: + return ['%s?debug=%s' % (wp, os.path.getmtime(fp)) for fp, wp in self.manifest_glob(req, mods, extension)] + else: + return [el[1] for el in self.manifest_glob(req, mods, extension)] @openerpweb.jsonrequest def csslist(self, req, mods=None): @@ -994,6 +997,16 @@ class DataSet(openerpweb.Controller): elif isinstance(kwargs[k], common.nonliterals.BaseDomain): kwargs[k] = req.session.eval_domain(kwargs[k]) + # Temporary implements future display_name special field for model#read() + if method == 'read' and kwargs.get('context') and kwargs['context'].get('future_display_name'): + if 'display_name' in args[1]: + names = req.session.model(model).name_get(args[0], **kwargs) + args[1].remove('display_name') + r = getattr(req.session.model(model), method)(*args, **kwargs) + for i in range(len(r)): + r[i]['display_name'] = names[i][1] or "%s#%d" % (model, names[i][0]) + return r + return getattr(req.session.model(model), method)(*args, **kwargs) @openerpweb.jsonrequest diff --git a/addons/web/i18n/pt_BR.po b/addons/web/i18n/pt_BR.po index 789202e6474..b76288d0387 100644 --- a/addons/web/i18n/pt_BR.po +++ b/addons/web/i18n/pt_BR.po @@ -8,179 +8,183 @@ msgstr "" "Project-Id-Version: openerp-web\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2012-07-02 09:06+0200\n" -"PO-Revision-Date: 2012-02-16 01:36+0000\n" -"Last-Translator: Rafael Sales - http://www.tompast.com.br \n" +"PO-Revision-Date: 2012-07-30 00:28+0000\n" +"Last-Translator: Fábio Martinelli \n" "Language-Team: Brazilian Portuguese \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-07-03 05:54+0000\n" -"X-Generator: Launchpad (build 15531)\n" +"X-Launchpad-Export-Date: 2012-07-31 05:10+0000\n" +"X-Generator: Launchpad (build 15702)\n" #. openerp-web -#: addons/web/static/src/js/chrome.js:170 -#: addons/web/static/src/js/chrome.js:196 -#: addons/web/static/src/js/chrome.js:364 -#: addons/web/static/src/js/view_form.js:531 -#: addons/web/static/src/js/view_form.js:1665 -#: addons/web/static/src/xml/base.xml:1641 +#: addons/web/static/src/js/chrome.js:176 +#: addons/web/static/src/js/chrome.js:202 +#: addons/web/static/src/js/chrome.js:380 +#: addons/web/static/src/js/view_form.js:457 +#: addons/web/static/src/js/view_form.js:1292 +#: addons/web/static/src/xml/base.xml:1701 msgid "Ok" msgstr "Ok" #. openerp-web -#: addons/web/static/src/js/chrome.js:178 +#: addons/web/static/src/js/chrome.js:184 msgid "Send OpenERP Enterprise Report" msgstr "Enviar Relatório OpenERP Enterprise" #. openerp-web -#: addons/web/static/src/js/chrome.js:192 +#: addons/web/static/src/js/chrome.js:198 msgid "Dont send" msgstr "Não enviar" #. openerp-web -#: addons/web/static/src/js/chrome.js:212 -#: addons/web/static/src/js/chrome.js:914 +#: addons/web/static/src/js/chrome.js:1119 msgid "Client Error" msgstr "Erro no aplicativo cliente" #. openerp-web -#: addons/web/static/src/js/chrome.js:253 +#: addons/web/static/src/js/chrome.js:260 #, python-format msgid "Loading (%d)" msgstr "Carregando (%d)" #. openerp-web -#: addons/web/static/src/js/chrome.js:276 +#: addons/web/static/src/js/chrome.js:292 msgid "Invalid database name" msgstr "Nome de banco de dados inválido" #. openerp-web -#: addons/web/static/src/js/chrome.js:402 +#: addons/web/static/src/js/chrome.js:455 msgid "Backed" msgstr "Copiado" #. openerp-web -#: addons/web/static/src/js/chrome.js:402 +#: addons/web/static/src/js/chrome.js:456 msgid "Database backed up successfully" msgstr "Backup do banco de dado realizado com sucesso" #. openerp-web -#: addons/web/static/src/js/chrome.js:442 +#: addons/web/static/src/js/chrome.js:499 msgid "Restored" msgstr "Restaurado" #. openerp-web -#: addons/web/static/src/js/chrome.js:442 +#: addons/web/static/src/js/chrome.js:499 msgid "Database restored successfully" msgstr "Banco de dados restaurado com sucesso" #. openerp-web -#: addons/web/static/src/js/chrome.js:735 -#: addons/web/static/src/xml/base.xml:259 -#: addons/web/static/src/xml/base.xml:367 +#: addons/web/static/src/js/chrome.js:783 +#: addons/web/static/src/xml/base.xml:226 +#: addons/web/static/src/xml/base.xml:1735 msgid "Change Password" msgstr "Alterar Senha" #. openerp-web -#: addons/web/static/src/js/chrome.js:809 -#: addons/web/static/src/xml/base.xml:326 +#: addons/web/static/src/js/chrome.js:759 +#: addons/web/static/src/xml/base.xml:356 msgid "Preferences" msgstr "Preferências" #. openerp-web -#: addons/web/static/src/js/chrome.js:812 +#: addons/web/static/src/js/chrome.js:763 msgid "Change password" msgstr "Alterar Senha" #. openerp-web -#: addons/web/static/src/js/chrome.js:813 -#: addons/web/static/src/js/view_editor.js:96 -#: addons/web/static/src/js/view_editor.js:832 -#: addons/web/static/src/js/view_editor.js:960 -#: addons/web/static/src/js/view_form.js:1660 -#: addons/web/static/src/xml/base.xml:1468 -#: addons/web/static/src/xml/base.xml:1478 +#: addons/web/static/src/js/chrome.js:762 +#: addons/web/static/src/js/search.js:241 +#: addons/web/static/src/js/search.js:300 +#: addons/web/static/src/js/view_editor.js:95 +#: addons/web/static/src/js/view_editor.js:836 +#: addons/web/static/src/js/view_editor.js:962 +#: addons/web/static/src/js/view_form.js:1287 +#: addons/web/static/src/xml/base.xml:743 +#: addons/web/static/src/xml/base.xml:1502 +#: addons/web/static/src/xml/base.xml:1512 +#: addons/web/static/src/xml/base.xml:1521 msgid "Cancel" msgstr "Cancelar" #. openerp-web -#: addons/web/static/src/js/chrome.js:814 -#: addons/web/static/src/js/view_editor.js:74 -#: addons/web/static/src/js/views.js:899 addons/web/static/src/xml/base.xml:687 -#: addons/web/static/src/xml/base.xml:1384 -#: addons/web/static/src/xml/base.xml:1472 +#: addons/web/static/src/js/chrome.js:764 +#: addons/web/static/src/js/view_editor.js:73 +#: addons/web/static/src/js/views.js:967 addons/web/static/src/xml/base.xml:742 +#: addons/web/static/src/xml/base.xml:1506 +#: addons/web/static/src/xml/base.xml:1520 msgid "Save" msgstr "Salvar" #. openerp-web -#: addons/web/static/src/js/chrome.js:839 +#: addons/web/static/src/js/chrome.js:680 +#: addons/web/static/src/xml/base.xml:359 msgid "About" msgstr "Sobre" #. openerp-web -#: addons/web/static/src/js/chrome.js:856 +#: addons/web/static/src/js/chrome.js:1052 msgid "OpenERP - Unsupported/Community Version" -msgstr "OpenERP - Versão da Comunidade/Não Suportada" +msgstr "Versão da Comunidade - Sem suporte oficial da OpenERP S/A" #. openerp-web #: addons/web/static/src/js/coresetup.js:619 msgid "less than a minute ago" -msgstr "" +msgstr "menos de um minuto atrás" #. openerp-web #: addons/web/static/src/js/coresetup.js:620 msgid "about a minute ago" -msgstr "" +msgstr "por volta de um minuto atrás" #. openerp-web #: addons/web/static/src/js/coresetup.js:621 #, python-format msgid "%d minutes ago" -msgstr "" +msgstr "%d minutos atrás" #. openerp-web #: addons/web/static/src/js/coresetup.js:622 msgid "about an hour ago" -msgstr "" +msgstr "por volta de uma hora atrás" #. openerp-web #: addons/web/static/src/js/coresetup.js:623 #, python-format msgid "%d hours ago" -msgstr "" +msgstr "%d horas atrás" #. openerp-web #: addons/web/static/src/js/coresetup.js:624 msgid "a day ago" -msgstr "" +msgstr "há um dia" #. openerp-web #: addons/web/static/src/js/coresetup.js:625 #, python-format msgid "%d days ago" -msgstr "" +msgstr "%d dias atrás" #. openerp-web #: addons/web/static/src/js/coresetup.js:626 msgid "about a month ago" -msgstr "" +msgstr "por volta de um mês atrás" #. openerp-web #: addons/web/static/src/js/coresetup.js:627 #, python-format msgid "%d months ago" -msgstr "" +msgstr "%d meses atrás" #. openerp-web #: addons/web/static/src/js/coresetup.js:628 msgid "about a year ago" -msgstr "" +msgstr "por volta de um ano atrás" #. openerp-web #: addons/web/static/src/js/coresetup.js:629 #, python-format msgid "%d years ago" -msgstr "" +msgstr "%d anos atrás" #. openerp-web #: addons/web/static/src/js/data_export.js:6 @@ -191,10 +195,10 @@ msgstr "Exportar Dados" #: addons/web/static/src/js/data_export.js:19 #: addons/web/static/src/js/data_import.js:70 #: addons/web/static/src/js/view_editor.js:49 -#: addons/web/static/src/js/view_editor.js:399 -#: addons/web/static/src/js/view_form.js:891 -#: addons/web/static/src/js/view_form.js:3901 -#: addons/web/static/src/js/views.js:900 +#: addons/web/static/src/js/view_editor.js:398 +#: addons/web/static/src/js/view_form.js:734 +#: addons/web/static/src/js/view_form.js:3298 +#: addons/web/static/src/js/views.js:968 msgid "Close" msgstr "Fechar" @@ -204,17 +208,17 @@ msgid "Export To File" msgstr "Exportar para Arquivo" #. openerp-web -#: addons/web/static/src/js/data_export.js:125 +#: addons/web/static/src/js/data_export.js:126 msgid "Please enter save field list name" msgstr "Por favor, entre o nome da lista de campos salva" #. openerp-web -#: addons/web/static/src/js/data_export.js:360 +#: addons/web/static/src/js/data_export.js:362 msgid "Please select fields to save export list..." msgstr "Por favor, selecione os campos para salvar na lista de exportação" #. openerp-web -#: addons/web/static/src/js/data_export.js:373 +#: addons/web/static/src/js/data_export.js:375 msgid "Please select fields to export..." msgstr "Por favor, selecione os campos para exportar..." @@ -239,42 +243,44 @@ msgid "" "Destination fields should only be selected once, some fields are selected " "more than once:" msgstr "" +"Campos de destino devem ser selecionados apenas uma vez, alguns campos foram " +"selecionados mais de uma vez:" #. openerp-web #: addons/web/static/src/js/data_import.js:383 msgid "*Required Fields are not selected :" -msgstr "" +msgstr "*Campos Obrigatórios não selecionados :" #. openerp-web -#: addons/web/static/src/js/formats.js:141 +#: addons/web/static/src/js/formats.js:139 #, python-format msgid "(%d records)" -msgstr "" +msgstr "(%d registos)" #. openerp-web -#: addons/web/static/src/js/formats.js:327 -#: addons/web/static/src/js/view_form.js:4301 +#: addons/web/static/src/js/formats.js:325 +#: addons/web/static/src/js/view_page.js:268 msgid "Download" msgstr "Download" #. openerp-web -#: addons/web/static/src/js/formats.js:332 +#: addons/web/static/src/js/formats.js:330 #, python-format msgid "Download \"%s\"" msgstr "Download \"%s\"" #. openerp-web -#: addons/web/static/src/js/search.js:798 +#: addons/web/static/src/js/search.js:437 msgid "Invalid Search" msgstr "Pesquisa Inválida" #. openerp-web -#: addons/web/static/src/js/search.js:798 +#: addons/web/static/src/js/search.js:437 msgid "triggered from search view" msgstr "desencadeada a partir da visão de busca" #. openerp-web -#: addons/web/static/src/js/search.js:839 +#: addons/web/static/src/js/search.js:528 #, python-format msgid "Incorrect value for field %(fieldname)s: [%(value)s] is %(message)s" msgstr "Valor incorreto para campo %(fieldname)s: [%(value)s] é %(message)s" @@ -283,48 +289,48 @@ msgstr "Valor incorreto para campo %(fieldname)s: [%(value)s] é %(message)s" #: addons/web/static/src/js/search.js:948 #, python-format msgid "Filter on: %s" -msgstr "" +msgstr "Filtrar por: %s" #. openerp-web #: addons/web/static/src/js/search.js:999 msgid "Filter" -msgstr "" +msgstr "Filtrar" #. openerp-web #: addons/web/static/src/js/search.js:1108 #, python-format msgid "Group by: %s" -msgstr "" +msgstr "Agrupar por: %s" #. openerp-web #: addons/web/static/src/js/search.js:1132 msgid "GroupBy" -msgstr "" +msgstr "AgruparPor" #. openerp-web #: addons/web/static/src/js/search.js:1267 #, python-format msgid "Search %(field)s for: %(value)s" -msgstr "" +msgstr "Procurar %(field)s por: %(value)s" #. openerp-web -#: addons/web/static/src/js/search.js:1301 +#: addons/web/static/src/js/search.js:869 msgid "not a valid integer" msgstr "não é um número inteiro válido" #. openerp-web -#: addons/web/static/src/js/search.js:1315 +#: addons/web/static/src/js/search.js:883 msgid "not a valid number" msgstr "não é um número válido" #. openerp-web -#: addons/web/static/src/js/search.js:1398 -#: addons/web/static/src/xml/base.xml:840 +#: addons/web/static/src/js/search.js:962 +#: addons/web/static/src/xml/base.xml:973 msgid "Yes" msgstr "Sim" #. openerp-web -#: addons/web/static/src/js/search.js:1399 +#: addons/web/static/src/js/search.js:963 msgid "No" msgstr "Não" @@ -332,97 +338,106 @@ msgstr "Não" #: addons/web/static/src/js/search.js:1416 #, python-format msgid "Search %(field)s at: %(value)s" -msgstr "" +msgstr "Procurar %(field)s em: %(value)s" #. openerp-web -#: addons/web/static/src/js/search.js:1622 -#: addons/web/static/src/xml/base.xml:1229 +#: addons/web/static/src/xml/base.xml:1286 msgid "Filters" msgstr "Filtros" #. openerp-web #: addons/web/static/src/js/search.js:1762 msgid "Advanced" -msgstr "" +msgstr "Avançado" #. openerp-web #: addons/web/static/src/js/search.js:1853 #, python-format msgid "%(field)s %(operator)s \"%(value)s\"" -msgstr "" +msgstr "%(field)s %(operator)s \"%(value)s\"" #. openerp-web -#: addons/web/static/src/js/search.js:1888 +#: addons/web/static/src/js/search.js:1341 msgid "contains" msgstr "contém" #. openerp-web -#: addons/web/static/src/js/search.js:1889 +#: addons/web/static/src/js/search.js:1342 msgid "doesn't contain" msgstr "não contém" #. openerp-web -#: addons/web/static/src/js/search.js:1890 -#: addons/web/static/src/js/search.js:1900 -#: addons/web/static/src/js/search.js:1928 -#: addons/web/static/src/js/search.js:1952 +#: addons/web/static/src/js/search.js:1343 +#: addons/web/static/src/js/search.js:1359 +#: addons/web/static/src/js/search.js:1380 +#: addons/web/static/src/js/search.js:1401 +#: addons/web/static/src/js/search.js:1424 msgid "is equal to" msgstr "é igual a" #. openerp-web -#: addons/web/static/src/js/search.js:1891 -#: addons/web/static/src/js/search.js:1901 -#: addons/web/static/src/js/search.js:1929 -#: addons/web/static/src/js/search.js:1953 +#: addons/web/static/src/js/search.js:1344 +#: addons/web/static/src/js/search.js:1360 +#: addons/web/static/src/js/search.js:1381 +#: addons/web/static/src/js/search.js:1402 +#: addons/web/static/src/js/search.js:1425 msgid "is not equal to" msgstr "é diferente de" #. openerp-web -#: addons/web/static/src/js/search.js:1902 -#: addons/web/static/src/js/search.js:1930 -#: addons/web/static/src/js/search.js:1954 +#: addons/web/static/src/js/search.js:1345 +#: addons/web/static/src/js/search.js:1361 +#: addons/web/static/src/js/search.js:1382 +#: addons/web/static/src/js/search.js:1403 +#: addons/web/static/src/js/search.js:1426 msgid "greater than" msgstr "maior que" #. openerp-web -#: addons/web/static/src/js/search.js:1903 -#: addons/web/static/src/js/search.js:1931 -#: addons/web/static/src/js/search.js:1955 +#: addons/web/static/src/js/search.js:1346 +#: addons/web/static/src/js/search.js:1362 +#: addons/web/static/src/js/search.js:1383 +#: addons/web/static/src/js/search.js:1404 +#: addons/web/static/src/js/search.js:1427 msgid "less than" msgstr "menor que" #. openerp-web -#: addons/web/static/src/js/search.js:1904 -#: addons/web/static/src/js/search.js:1932 -#: addons/web/static/src/js/search.js:1956 +#: addons/web/static/src/js/search.js:1347 +#: addons/web/static/src/js/search.js:1363 +#: addons/web/static/src/js/search.js:1384 +#: addons/web/static/src/js/search.js:1405 +#: addons/web/static/src/js/search.js:1428 msgid "greater or equal than" msgstr "maior ou igual a" #. openerp-web -#: addons/web/static/src/js/search.js:1905 -#: addons/web/static/src/js/search.js:1933 -#: addons/web/static/src/js/search.js:1957 +#: addons/web/static/src/js/search.js:1348 +#: addons/web/static/src/js/search.js:1364 +#: addons/web/static/src/js/search.js:1385 +#: addons/web/static/src/js/search.js:1406 +#: addons/web/static/src/js/search.js:1429 msgid "less or equal than" msgstr "menor ou igual a" #. openerp-web -#: addons/web/static/src/js/search.js:1947 -#: addons/web/static/src/js/search.js:1973 +#: addons/web/static/src/js/search.js:1419 +#: addons/web/static/src/js/search.js:1444 msgid "is" msgstr "é" #. openerp-web -#: addons/web/static/src/js/search.js:1974 +#: addons/web/static/src/js/search.js:1445 msgid "is not" msgstr "não é" #. openerp-web -#: addons/web/static/src/js/search.js:1988 +#: addons/web/static/src/js/search.js:1459 msgid "is true" msgstr "é verdadeiro" #. openerp-web -#: addons/web/static/src/js/search.js:1989 +#: addons/web/static/src/js/search.js:1460 msgid "is false" msgstr "é falso" @@ -435,195 +450,194 @@ msgstr "Administrar Visões (%s)" #. openerp-web #: addons/web/static/src/js/view_editor.js:46 #: addons/web/static/src/js/view_list.js:17 -#: addons/web/static/src/xml/base.xml:121 -#: addons/web/static/src/xml/base.xml:164 -#: addons/web/static/src/xml/base.xml:684 +#: addons/web/static/src/xml/base.xml:100 +#: addons/web/static/src/xml/base.xml:327 +#: addons/web/static/src/xml/base.xml:761 msgid "Create" msgstr "Criar" #. openerp-web #: addons/web/static/src/js/view_editor.js:47 -#: addons/web/static/src/xml/base.xml:456 -#: addons/web/static/src/xml/base.xml:683 +#: addons/web/static/src/xml/base.xml:483 +#: addons/web/static/src/xml/base.xml:760 msgid "Edit" msgstr "Editar" #. openerp-web #: addons/web/static/src/js/view_editor.js:48 -#: addons/web/static/src/xml/base.xml:1595 +#: addons/web/static/src/xml/base.xml:1653 msgid "Remove" msgstr "Remover" #. openerp-web -#: addons/web/static/src/js/view_editor.js:72 +#: addons/web/static/src/js/view_editor.js:71 #, python-format msgid "Create a view (%s)" msgstr "Criar a visão (%s)" #. openerp-web -#: addons/web/static/src/js/view_editor.js:169 +#: addons/web/static/src/js/view_editor.js:168 msgid "Do you really want to remove this view?" msgstr "Deseja remover essa visão?" #. openerp-web -#: addons/web/static/src/js/view_editor.js:365 +#: addons/web/static/src/js/view_editor.js:364 #, python-format msgid "View Editor %d - %s" msgstr "Editar Visão %d - %s" #. openerp-web -#: addons/web/static/src/js/view_editor.js:368 +#: addons/web/static/src/js/view_editor.js:367 msgid "Inherited View" msgstr "View herdada" #. openerp-web -#: addons/web/static/src/js/view_editor.js:372 +#: addons/web/static/src/js/view_editor.js:371 msgid "Do you really wants to create an inherited view here?" msgstr "Quer realmente criar uma view herdada aqui?" #. openerp-web -#: addons/web/static/src/js/view_editor.js:382 +#: addons/web/static/src/js/view_editor.js:381 msgid "Preview" msgstr "Pré-visualizar" #. openerp-web -#: addons/web/static/src/js/view_editor.js:502 +#: addons/web/static/src/js/view_editor.js:501 msgid "Do you really want to remove this node?" msgstr "Deseja remover esse nó?" #. openerp-web -#: addons/web/static/src/js/view_editor.js:811 -#: addons/web/static/src/js/view_editor.js:937 +#: addons/web/static/src/js/view_editor.js:815 +#: addons/web/static/src/js/view_editor.js:939 msgid "Properties" msgstr "Propriedades" #. openerp-web -#: addons/web/static/src/js/view_editor.js:814 -#: addons/web/static/src/js/view_editor.js:940 +#: addons/web/static/src/js/view_editor.js:818 +#: addons/web/static/src/js/view_editor.js:942 msgid "Update" msgstr "Atualizar" #. openerp-web -#: addons/web/static/src/js/view_form.js:43 +#: addons/web/static/src/js/view_form.js:16 +#: addons/web/static/src/js/view_form.js:210 msgid "Form" msgstr "Formulário" #. openerp-web -#: addons/web/static/src/js/view_form.js:147 -#: addons/web/static/src/js/view_list.js:358 -#: addons/web/static/src/xml/base.xml:1653 +#: addons/web/static/src/xml/base.xml:632 +#: addons/web/static/src/xml/base.xml:763 +#: addons/web/static/src/xml/base.xml:1714 msgid "Delete" msgstr "Excluir" #. openerp-web -#: addons/web/static/src/js/view_form.js:148 +#: addons/web/static/src/xml/base.xml:762 msgid "Duplicate" msgstr "Duplicar" #. openerp-web -#: addons/web/static/src/js/view_form.js:149 -#: addons/web/static/src/js/view_form.js:885 +#: addons/web/static/src/js/view_form.js:133 +#: addons/web/static/src/js/view_form.js:728 msgid "Set Default" msgstr "Definir como Padrão" #. openerp-web -#: addons/web/static/src/js/view_form.js:643 +#: addons/web/static/src/js/view_page.js:59 msgid "Do you really want to delete this record?" msgstr "Deseja remover esse registro?" #. openerp-web -#: addons/web/static/src/js/view_form.js:657 +#: addons/web/static/src/js/view_form.js:508 msgid "" "Warning, the record has been modified, your changes will be discarded." msgstr "Aviso, o registro foi modificado, suas alterações serão descartadas." #. openerp-web -#: addons/web/static/src/js/view_form.js:892 +#: addons/web/static/src/js/view_form.js:735 msgid "Save default" msgstr "Salvar padrão" #. openerp-web -#: addons/web/static/src/js/view_form.js:1402 +#: addons/web/static/src/js/view_form.js:867 #, python-format msgid "Unknown operator %s in domain %s" msgstr "Operador desconhecido %s no domínio %s" #. openerp-web -#: addons/web/static/src/js/view_form.js:1410 +#: addons/web/static/src/js/view_form.js:875 #, python-format msgid "Unknown field %s in domain %s" msgstr "Campo desconhecido %s no domínio %s" #. openerp-web -#: addons/web/static/src/js/view_form.js:1448 +#: addons/web/static/src/js/view_form.js:913 #, python-format msgid "Unsupported operator %s in domain %s" msgstr "Operador não suportado %s no domínio %s" #. openerp-web -#: addons/web/static/src/js/view_form.js:1657 +#: addons/web/static/src/js/view_form.js:1284 msgid "Confirm" msgstr "Confirmar" #. openerp-web -#: addons/web/static/src/js/view_form.js:2478 +#: addons/web/static/src/js/view_form.js:2193 msgid "   Search More..." msgstr "   Procurar Mais..." #. openerp-web -#: addons/web/static/src/js/view_form.js:2488 +#: addons/web/static/src/js/view_form.js:2211 #, python-format msgid "   Create \"%s\"" msgstr "   Criar \"%s\"" #. openerp-web -#: addons/web/static/src/js/view_form.js:2494 +#: addons/web/static/src/js/view_form.js:2217 msgid "   Create and Edit..." msgstr "   Criar e Editar..." #. openerp-web -#: addons/web/static/src/js/view_form.js:2527 +#: addons/web/static/src/js/view_form.js:2250 +#: addons/web/static/src/js/views.js:680 msgid "Search: " msgstr "Pesquisar: " #. openerp-web -#: addons/web/static/src/js/view_form.js:2527 -#: addons/web/static/src/js/view_form.js:3210 +#: addons/web/static/src/js/view_form.js:2250 +#: addons/web/static/src/js/view_form.js:2738 msgid "Create: " msgstr "Criar: " #. openerp-web -#: addons/web/static/src/js/view_form.js:2596 -#: addons/web/static/src/js/view_form.js:3125 -#: addons/web/static/src/js/view_form.js:3238 -#: addons/web/static/src/js/view_form.js:3586 -#: addons/web/static/src/js/view_form.js:3702 +#: addons/web/static/src/js/view_form.js:2040 +#: addons/web/static/src/js/view_form.js:2766 +#: addons/web/static/src/js/view_form.js:2991 msgid "Open: " msgstr "Aberto: " #. openerp-web -#: addons/web/static/src/js/view_form.js:3517 -#: addons/web/static/src/js/view_form.js:3644 -#: addons/web/static/src/xml/base.xml:1184 -#: addons/web/static/src/xml/base.xml:1594 +#: addons/web/static/src/js/view_form.js:2911 +#: addons/web/static/src/xml/base.xml:755 +#: addons/web/static/src/xml/base.xml:777 +#: addons/web/static/src/xml/base.xml:1652 msgid "Add" msgstr "Adicionar" #. openerp-web -#: addons/web/static/src/js/view_form.js:3566 -#: addons/web/static/src/js/view_form.js:3684 +#: addons/web/static/src/js/view_form.js:2971 msgid "Add: " msgstr "Adicionar: " #. openerp-web #: addons/web/static/src/js/view_form.js:4230 msgid "Save As..." -msgstr "" +msgstr "Salvar como..." #. openerp-web #: addons/web/static/src/js/view_form.js:4230 msgid "The field is empty, there's nothing to save !" -msgstr "" +msgstr "O campo está vazio, não tem nada para salvar!" #. openerp-web #: addons/web/static/src/js/view_list.js:8 @@ -631,51 +645,49 @@ msgid "List" msgstr "Lista" #. openerp-web -#: addons/web/static/src/js/view_list.js:338 +#: addons/web/static/src/js/view_list.js:277 msgid "Unlimited" msgstr "Ilimitado" #. openerp-web -#: addons/web/static/src/js/view_list.js:356 -#: addons/web/static/src/xml/base.xml:622 -#: addons/web/static/src/xml/base.xml:1657 +#: addons/web/static/src/js/views.js:819 +#: addons/web/static/src/xml/base.xml:1742 msgid "Import" msgstr "Importar" #. openerp-web -#: addons/web/static/src/js/view_list.js:357 -#: addons/web/static/src/xml/base.xml:1554 +#: addons/web/static/src/js/views.js:822 +#: addons/web/static/src/xml/base.xml:1612 msgid "Export" msgstr "Exportar" #. openerp-web -#: addons/web/static/src/js/view_list.js:436 +#: addons/web/static/src/js/view_list.js:374 msgid "Group" msgstr "Grupo" #. openerp-web -#: addons/web/static/src/js/view_list.js:623 +#: addons/web/static/src/js/view_list.js:549 msgid "Do you really want to remove these records?" msgstr "Deseja remover esse registro?" #. openerp-web -#: addons/web/static/src/js/view_list.js:716 -#: addons/web/static/src/js/views.js:820 +#: addons/web/static/src/js/views.js:925 msgid "Warning" msgstr "Atenção" #. openerp-web #: addons/web/static/src/js/view_list.js:716 msgid "You must select at least one record." -msgstr "" +msgstr "Você precisa selecionar ao menos um registro." #. openerp-web -#: addons/web/static/src/js/view_list.js:1332 +#: addons/web/static/src/js/view_list.js:1243 msgid "Undefined" msgstr "Indefinida" #. openerp-web -#: addons/web/static/src/js/view_list.js:1428 +#: addons/web/static/src/js/view_list.js:1342 #, python-format msgid "%(page)d/%(page_count)d" msgstr "%(page)d/%(page_count)d" @@ -686,84 +698,84 @@ msgid "Tree" msgstr "Árvore" #. openerp-web -#: addons/web/static/src/js/views.js:566 addons/web/static/src/xml/base.xml:452 +#: addons/web/static/src/js/views.js:570 addons/web/static/src/xml/base.xml:480 msgid "Fields View Get" msgstr "Fields View Get" #. openerp-web -#: addons/web/static/src/js/views.js:574 +#: addons/web/static/src/js/views.js:578 #, python-format msgid "View Log (%s)" msgstr "Ver o Log (%s)" #. openerp-web -#: addons/web/static/src/js/views.js:604 +#: addons/web/static/src/js/views.js:605 #, python-format msgid "Model %s fields" msgstr "Modelo %s campos" #. openerp-web -#: addons/web/static/src/js/views.js:614 addons/web/static/src/xml/base.xml:455 +#: addons/web/static/src/js/views.js:615 addons/web/static/src/xml/base.xml:482 msgid "Manage Views" msgstr "Administrar Visões" #. openerp-web -#: addons/web/static/src/js/views.js:615 +#: addons/web/static/src/js/views.js:616 msgid "Could not find current view declaration" msgstr "Não foi possível encontrar a declaração da visão atual" #. openerp-web #: addons/web/static/src/js/views.js:716 msgid "Print" -msgstr "" +msgstr "Imprimir" #. openerp-web #: addons/web/static/src/js/views.js:717 msgid "Attachment" -msgstr "" +msgstr "Anexo" #. openerp-web #: addons/web/static/src/js/views.js:718 addons/web/static/src/xml/base.xml:276 msgid "More" -msgstr "" +msgstr "Mais" #. openerp-web -#: addons/web/static/src/js/views.js:726 +#: addons/web/static/src/js/views.js:810 msgid "Translate" msgstr "Traduzir" #. openerp-web -#: addons/web/static/src/js/views.js:726 +#: addons/web/static/src/js/views.js:812 msgid "Technical translation" msgstr "Tradução Técnica" #. openerp-web -#: addons/web/static/src/js/views.js:820 +#: addons/web/static/src/js/views.js:924 msgid "You must choose at least one record." msgstr "Você deve escolher pelo menos um registro." #. openerp-web #: addons/web/static/src/js/views.js:875 msgid "Uploading..." -msgstr "" +msgstr "Enviando..." #. openerp-web #: addons/web/static/src/js/views.js:885 msgid "Do you really want to delete this attachment ?" -msgstr "" +msgstr "Você deseja excluir este anexo?" #. openerp-web -#: addons/web/static/src/js/views.js:894 +#: addons/web/static/src/js/views.js:962 msgid "Translations" msgstr "Traduções" #. openerp-web -#: addons/web/static/src/xml/base.xml:9 +#: addons/web/static/src/xml/base.xml:52 msgid "Loading..." msgstr "Carregando..." #. openerp-web -#: addons/web/static/src/xml/base.xml:48 +#: addons/web/static/src/xml/base.xml:251 msgid "" "Your version of OpenERP is unsupported. Support & maintenance services are " "available here:" @@ -772,265 +784,266 @@ msgstr "" "disponíveis aqui:" #. openerp-web -#: addons/web/static/src/xml/base.xml:48 +#: addons/web/static/src/xml/base.xml:251 msgid "OpenERP Entreprise" msgstr "OpenERP Entreprise" #. openerp-web -#: addons/web/static/src/xml/base.xml:53 +#: addons/web/static/src/xml/base.xml:256 msgid "OpenERP Enterprise Contract." msgstr "Contrato Corporativo OpenERP" #. openerp-web -#: addons/web/static/src/xml/base.xml:54 +#: addons/web/static/src/xml/base.xml:257 msgid "Your report will be sent to the OpenERP Enterprise team." msgstr "Seu relatório será enviado à equipe do OpenERP" #. openerp-web -#: addons/web/static/src/xml/base.xml:56 +#: addons/web/static/src/xml/base.xml:259 msgid "Summary:" msgstr "Resumo:" #. openerp-web -#: addons/web/static/src/xml/base.xml:60 +#: addons/web/static/src/xml/base.xml:263 msgid "Description:" msgstr "Descrição :" #. openerp-web -#: addons/web/static/src/xml/base.xml:64 +#: addons/web/static/src/xml/base.xml:267 msgid "What you did:" msgstr "O que você fez:" #. openerp-web -#: addons/web/static/src/xml/base.xml:80 +#: addons/web/static/src/xml/base.xml:297 msgid "Invalid username or password" msgstr "Nome de usuário ou senha inválidos" #. openerp-web -#: addons/web/static/src/xml/base.xml:84 addons/web/static/src/xml/base.xml:174 -#: addons/web/static/src/xml/base.xml:200 +#: addons/web/static/src/xml/base.xml:116 +#: addons/web/static/src/xml/base.xml:150 +#: addons/web/static/src/xml/base.xml:301 msgid "Database:" msgstr "Banco de Dados:" #. openerp-web -#: addons/web/static/src/xml/base.xml:89 +#: addons/web/static/src/xml/base.xml:306 msgid "Username" msgstr "Nome do usuário" #. openerp-web -#: addons/web/static/src/xml/base.xml:91 addons/web/static/src/xml/base.xml:125 +#: addons/web/static/src/xml/base.xml:308 +#: addons/web/static/src/xml/base.xml:331 msgid "Password" msgstr "Senha" #. openerp-web -#: addons/web/static/src/xml/base.xml:93 +#: addons/web/static/src/xml/base.xml:310 msgid "Log in" msgstr "Autenticar" #. openerp-web -#: addons/web/static/src/xml/base.xml:97 +#: addons/web/static/src/xml/base.xml:314 msgid "Manage Databases" msgstr "Administrar bancos de dados" #. openerp-web -#: addons/web/static/src/xml/base.xml:98 addons/web/static/src/xml/base.xml:389 +#: addons/web/static/src/xml/base.xml:44 addons/web/static/src/xml/base.xml:315 msgid "Powered by" msgstr "Desenvolvido Por" #. openerp-web -#: addons/web/static/src/xml/base.xml:98 addons/web/static/src/xml/base.xml:334 +#: addons/web/static/src/xml/base.xml:44 addons/web/static/src/xml/base.xml:315 +#: addons/web/static/src/xml/base.xml:1819 msgid "OpenERP" msgstr "OpenERP" #. openerp-web -#: addons/web/static/src/xml/base.xml:122 -#: addons/web/static/src/xml/base.xml:189 +#: addons/web/static/src/xml/base.xml:132 +#: addons/web/static/src/xml/base.xml:328 msgid "Drop" msgstr "Soltar" #. openerp-web -#: addons/web/static/src/xml/base.xml:123 -#: addons/web/static/src/xml/base.xml:215 +#: addons/web/static/src/xml/base.xml:166 +#: addons/web/static/src/xml/base.xml:329 msgid "Backup" msgstr "Cópia de Segurança" #. openerp-web -#: addons/web/static/src/xml/base.xml:124 -#: addons/web/static/src/xml/base.xml:237 +#: addons/web/static/src/xml/base.xml:195 +#: addons/web/static/src/xml/base.xml:330 msgid "Restore" msgstr "Restaurar" #. openerp-web -#: addons/web/static/src/xml/base.xml:126 +#: addons/web/static/src/xml/base.xml:332 msgid "Back to Login" msgstr "Voltar ao Login" #. openerp-web -#: addons/web/static/src/xml/base.xml:131 +#: addons/web/static/src/xml/base.xml:61 msgid "CREATE DATABASE" msgstr "CRIAR BANCO DE DADOS" #. openerp-web -#: addons/web/static/src/xml/base.xml:134 -#: addons/web/static/src/xml/base.xml:247 +#: addons/web/static/src/xml/base.xml:68 addons/web/static/src/xml/base.xml:211 msgid "Master password:" msgstr "Senha Super Admin:" #. openerp-web -#: addons/web/static/src/xml/base.xml:138 -#: addons/web/static/src/xml/base.xml:233 +#: addons/web/static/src/xml/base.xml:72 addons/web/static/src/xml/base.xml:191 msgid "New database name:" msgstr "Nome do Novo banco de dados:" #. openerp-web -#: addons/web/static/src/xml/base.xml:142 +#: addons/web/static/src/xml/base.xml:77 msgid "Load Demonstration data:" msgstr "Carregar dados de demonstração:" #. openerp-web -#: addons/web/static/src/xml/base.xml:146 +#: addons/web/static/src/xml/base.xml:81 msgid "Default language:" msgstr "Idioma padrão:" #. openerp-web -#: addons/web/static/src/xml/base.xml:156 +#: addons/web/static/src/xml/base.xml:91 msgid "Admin password:" msgstr "Senha do Administrador" #. openerp-web -#: addons/web/static/src/xml/base.xml:160 +#: addons/web/static/src/xml/base.xml:95 msgid "Confirm password:" msgstr "Confirmar Senha:" #. openerp-web -#: addons/web/static/src/xml/base.xml:171 +#: addons/web/static/src/xml/base.xml:109 msgid "DROP DATABASE" msgstr "EXCLUIR BANCO DE DADOS" #. openerp-web -#: addons/web/static/src/xml/base.xml:185 -#: addons/web/static/src/xml/base.xml:211 -#: addons/web/static/src/xml/base.xml:229 +#: addons/web/static/src/xml/base.xml:128 +#: addons/web/static/src/xml/base.xml:162 +#: addons/web/static/src/xml/base.xml:187 msgid "Master Password:" msgstr "Senha do Super Admin" #. openerp-web -#: addons/web/static/src/xml/base.xml:197 +#: addons/web/static/src/xml/base.xml:143 msgid "BACKUP DATABASE" msgstr "BACKUP" #. openerp-web -#: addons/web/static/src/xml/base.xml:222 +#: addons/web/static/src/xml/base.xml:175 msgid "RESTORE DATABASE" msgstr "RESTAURAR BANCO DE DADOS" #. openerp-web -#: addons/web/static/src/xml/base.xml:225 +#: addons/web/static/src/xml/base.xml:182 msgid "File:" msgstr "Arquivo:" #. openerp-web -#: addons/web/static/src/xml/base.xml:244 +#: addons/web/static/src/xml/base.xml:204 msgid "CHANGE MASTER PASSWORD" msgstr "ALTERAR SENHA DO SUPER ADMIN" #. openerp-web -#: addons/web/static/src/xml/base.xml:251 +#: addons/web/static/src/xml/base.xml:216 msgid "New master password:" msgstr "Nova Senha do Super Admin:" #. openerp-web -#: addons/web/static/src/xml/base.xml:255 +#: addons/web/static/src/xml/base.xml:221 msgid "Confirm new master password:" msgstr "Confirme a Senha do Super Admin:" #. openerp-web #: addons/web/static/src/xml/base.xml:325 msgid "About OpenERP" -msgstr "" +msgstr "Sobre OpenERP" #. openerp-web #: addons/web/static/src/xml/base.xml:327 msgid "Log out" -msgstr "" +msgstr "Sair" #. openerp-web #: addons/web/static/src/xml/base.xml:333 msgid "Activate the developer mode" -msgstr "" +msgstr "Ativar o modo de desenvolvedor" #. openerp-web -#: addons/web/static/src/xml/base.xml:335 +#: addons/web/static/src/xml/base.xml:1820 msgid "Version" msgstr "Versão" #. openerp-web -#: addons/web/static/src/xml/base.xml:336 +#: addons/web/static/src/xml/base.xml:1821 msgid "Copyright © 2004-TODAY OpenERP SA. All Rights Reserved." msgstr "Copyright © 2004-HOJE OpenERP SA. Alguns Direitos Reservados." #. openerp-web -#: addons/web/static/src/xml/base.xml:337 +#: addons/web/static/src/xml/base.xml:1822 msgid "OpenERP is a trademark of the" msgstr "OpenERP é uma marca de" #. openerp-web -#: addons/web/static/src/xml/base.xml:338 +#: addons/web/static/src/xml/base.xml:1823 msgid "OpenERP SA Company" msgstr "Empresa OpenERP SA" #. openerp-web -#: addons/web/static/src/xml/base.xml:340 +#: addons/web/static/src/xml/base.xml:1825 msgid "Licenced under the terms of" msgstr "Licenciado sobre os termos de" #. openerp-web -#: addons/web/static/src/xml/base.xml:341 +#: addons/web/static/src/xml/base.xml:1826 msgid "GNU Affero General Public License" msgstr "Licença Pública Geral GNU Affero" #. openerp-web -#: addons/web/static/src/xml/base.xml:343 +#: addons/web/static/src/xml/base.xml:1828 msgid "For more information visit" msgstr "Para mais informações visite" #. openerp-web -#: addons/web/static/src/xml/base.xml:344 +#: addons/web/static/src/xml/base.xml:1829 msgid "OpenERP.com" msgstr "OpenERP.com" #. openerp-web -#: addons/web/static/src/xml/base.xml:352 +#: addons/web/static/src/xml/base.xml:1720 msgid "Old Password:" msgstr "Senha Anterior" #. openerp-web -#: addons/web/static/src/xml/base.xml:357 +#: addons/web/static/src/xml/base.xml:1725 msgid "New Password:" msgstr "Nova Senha" #. openerp-web -#: addons/web/static/src/xml/base.xml:362 +#: addons/web/static/src/xml/base.xml:1730 msgid "Confirm Password:" msgstr "Confirmar Senha:" #. openerp-web #: addons/web/static/src/xml/base.xml:390 msgid "Open" -msgstr "" +msgstr "Abrir" #. openerp-web #: addons/web/static/src/xml/base.xml:390 msgid "ERP" -msgstr "" +msgstr "ERP" #. openerp-web -#: addons/web/static/src/xml/base.xml:448 +#: addons/web/static/src/xml/base.xml:477 msgid "Debug View#" msgstr "Visão de Depuração#" #. openerp-web -#: addons/web/static/src/xml/base.xml:449 +#: addons/web/static/src/xml/base.xml:478 msgid "View Log (perm_read)" msgstr "Visualiza Log (perm_read)" @@ -1040,70 +1053,69 @@ msgid "Toggle Form Layout Outline" msgstr "" #. openerp-web -#: addons/web/static/src/xml/base.xml:451 +#: addons/web/static/src/xml/base.xml:479 msgid "View Fields" msgstr "Visualiza Campos" #. openerp-web -#: addons/web/static/src/xml/base.xml:453 -#: addons/web/static/src/xml/base.xml:1242 +#: addons/web/static/src/xml/base.xml:1300 msgid "Manage Filters" msgstr "Gerenciar Filtros" #. openerp-web -#: addons/web/static/src/xml/base.xml:456 +#: addons/web/static/src/xml/base.xml:483 msgid "View" msgstr "Visão" #. openerp-web -#: addons/web/static/src/xml/base.xml:457 +#: addons/web/static/src/xml/base.xml:484 msgid "Edit SearchView" msgstr "Editar Busca" #. openerp-web -#: addons/web/static/src/xml/base.xml:458 +#: addons/web/static/src/xml/base.xml:485 msgid "Edit Action" msgstr "Editar Ação" #. openerp-web -#: addons/web/static/src/xml/base.xml:459 +#: addons/web/static/src/xml/base.xml:486 msgid "Edit Workflow" msgstr "Editar Workflow" #. openerp-web -#: addons/web/static/src/xml/base.xml:464 +#: addons/web/static/src/xml/base.xml:491 msgid "ID:" msgstr "ID:" #. openerp-web -#: addons/web/static/src/xml/base.xml:467 +#: addons/web/static/src/xml/base.xml:494 msgid "XML ID:" msgstr "ID XML:" #. openerp-web -#: addons/web/static/src/xml/base.xml:470 +#: addons/web/static/src/xml/base.xml:497 msgid "Creation User:" msgstr "Usuário de criação:" #. openerp-web -#: addons/web/static/src/xml/base.xml:473 +#: addons/web/static/src/xml/base.xml:500 msgid "Creation Date:" msgstr "Data de Criação:" #. openerp-web -#: addons/web/static/src/xml/base.xml:476 +#: addons/web/static/src/xml/base.xml:503 msgid "Latest Modification by:" msgstr "Última Modificação por:" #. openerp-web -#: addons/web/static/src/xml/base.xml:479 +#: addons/web/static/src/xml/base.xml:506 msgid "Latest Modification Date:" msgstr "Data da Última Modificação:" #. openerp-web #: addons/web/static/src/xml/base.xml:518 msgid "Delete this attachment" -msgstr "" +msgstr "Excluir este anexo" #. openerp-web #: addons/web/static/src/xml/base.xml:523 @@ -1113,297 +1125,301 @@ msgstr "" #. openerp-web #: addons/web/static/src/xml/base.xml:527 msgid "Add..." -msgstr "" +msgstr "Adicionar..." #. openerp-web #: addons/web/static/src/xml/base.xml:622 #: addons/web/static/src/xml/base.xml:687 msgid "or" -msgstr "" +msgstr "ou" #. openerp-web #: addons/web/static/src/xml/base.xml:687 msgid "Discard" -msgstr "" +msgstr "Descartar" #. openerp-web -#: addons/web/static/src/xml/base.xml:704 +#: addons/web/static/src/xml/base.xml:806 msgid "Default:" msgstr "Padrão:" #. openerp-web -#: addons/web/static/src/xml/base.xml:721 +#: addons/web/static/src/xml/base.xml:823 msgid "Condition:" msgstr "Condição:" #. openerp-web -#: addons/web/static/src/xml/base.xml:740 +#: addons/web/static/src/xml/base.xml:842 msgid "Only you" msgstr "Somente voce" #. openerp-web -#: addons/web/static/src/xml/base.xml:747 +#: addons/web/static/src/xml/base.xml:849 msgid "All users" msgstr "Todos os usuários" #. openerp-web -#: addons/web/static/src/xml/base.xml:797 +#: addons/web/static/src/xml/base.xml:856 msgid "Unhandled widget" msgstr "Widget não utilizado" #. openerp-web -#: addons/web/static/src/xml/base.xml:803 +#: addons/web/static/src/xml/base.xml:936 msgid "(nolabel)" msgstr "(nolabel)" #. openerp-web -#: addons/web/static/src/xml/base.xml:808 +#: addons/web/static/src/xml/base.xml:941 msgid "Field:" msgstr "Campo:" #. openerp-web -#: addons/web/static/src/xml/base.xml:812 +#: addons/web/static/src/xml/base.xml:945 msgid "Object:" msgstr "Objeto:" #. openerp-web -#: addons/web/static/src/xml/base.xml:816 +#: addons/web/static/src/xml/base.xml:949 msgid "Type:" msgstr "Tipo:" #. openerp-web -#: addons/web/static/src/xml/base.xml:820 +#: addons/web/static/src/xml/base.xml:953 msgid "Widget:" msgstr "Componente:" #. openerp-web -#: addons/web/static/src/xml/base.xml:824 +#: addons/web/static/src/xml/base.xml:957 msgid "Size:" msgstr "Tamanho:" #. openerp-web -#: addons/web/static/src/xml/base.xml:828 +#: addons/web/static/src/xml/base.xml:961 msgid "Context:" msgstr "Contexto:" #. openerp-web -#: addons/web/static/src/xml/base.xml:832 +#: addons/web/static/src/xml/base.xml:965 msgid "Domain:" msgstr "Domínio:" #. openerp-web -#: addons/web/static/src/xml/base.xml:836 +#: addons/web/static/src/xml/base.xml:910 +#: addons/web/static/src/xml/base.xml:969 msgid "Modifiers:" msgstr "Modificadores:" #. openerp-web -#: addons/web/static/src/xml/base.xml:840 +#: addons/web/static/src/xml/base.xml:973 msgid "Change default:" msgstr "Trocar padrão" #. openerp-web -#: addons/web/static/src/xml/base.xml:844 +#: addons/web/static/src/xml/base.xml:977 msgid "On change:" msgstr "Ao alterar:" #. openerp-web -#: addons/web/static/src/xml/base.xml:848 +#: addons/web/static/src/xml/base.xml:981 msgid "Relation:" msgstr "Relação:" #. openerp-web -#: addons/web/static/src/xml/base.xml:852 +#: addons/web/static/src/xml/base.xml:985 msgid "Selection:" msgstr "Seleção:" #. openerp-web -#: addons/web/static/src/xml/base.xml:895 +#: addons/web/static/src/xml/base.xml:1040 msgid "Open this resource" msgstr "Abrir este recurso" #. openerp-web -#: addons/web/static/src/xml/base.xml:922 +#: addons/web/static/src/xml/base.xml:1063 msgid "Select date" msgstr "Data selecionada" #. openerp-web #: addons/web/static/src/xml/base.xml:948 msgid "Open Resource" -msgstr "" +msgstr "Abrir Recurso" #. openerp-web -#: addons/web/static/src/xml/base.xml:1026 -#: addons/web/static/src/xml/base.xml:1067 +#: addons/web/static/src/xml/base.xml:1162 +#: addons/web/static/src/xml/base.xml:1205 msgid "Set Image" msgstr "Definir Imagem" #. openerp-web -#: addons/web/static/src/xml/base.xml:1030 -#: addons/web/static/src/xml/base.xml:1082 -#: addons/web/static/src/xml/base.xml:1084 +#: addons/web/static/src/js/view_form.js:1620 +#: addons/web/static/src/xml/base.xml:1170 +#: addons/web/static/src/xml/base.xml:1220 +#: addons/web/static/src/xml/base.xml:1222 +#: addons/web/static/src/xml/base.xml:1279 msgid "Clear" msgstr "Limpar" #. openerp-web -#: addons/web/static/src/xml/base.xml:1035 -#: addons/web/static/src/xml/base.xml:1092 +#: addons/web/static/src/xml/base.xml:1179 +#: addons/web/static/src/xml/base.xml:1230 msgid "Uploading ..." msgstr "Tranferindo..." #. openerp-web #: addons/web/static/src/xml/base.xml:1066 msgid "width: 83px;" -msgstr "" +msgstr "largura: 83px;" #. openerp-web -#: addons/web/static/src/xml/base.xml:1069 -#: addons/web/static/src/xml/base.xml:1467 +#: addons/web/static/src/xml/base.xml:1207 +#: addons/web/static/src/xml/base.xml:1501 msgid "Select" msgstr "Selecionar" #. openerp-web -#: addons/web/static/src/xml/base.xml:1075 -#: addons/web/static/src/xml/base.xml:1077 +#: addons/web/static/src/xml/base.xml:1214 +#: addons/web/static/src/xml/base.xml:1216 msgid "Save As" msgstr "Salvar Como" #. openerp-web -#: addons/web/static/src/xml/base.xml:1126 +#: addons/web/static/src/xml/base.xml:1245 msgid "Button" msgstr "Botão" #. openerp-web -#: addons/web/static/src/xml/base.xml:1129 +#: addons/web/static/src/xml/base.xml:1248 msgid "(no string)" msgstr "(sem string)" #. openerp-web -#: addons/web/static/src/xml/base.xml:1136 +#: addons/web/static/src/xml/base.xml:1255 msgid "Special:" msgstr "Especial:" #. openerp-web -#: addons/web/static/src/xml/base.xml:1141 +#: addons/web/static/src/xml/base.xml:1260 msgid "Button Type:" msgstr "Tipo do Botão:" #. openerp-web -#: addons/web/static/src/xml/base.xml:1145 +#: addons/web/static/src/xml/base.xml:1264 msgid "Method:" msgstr "Método:" #. openerp-web -#: addons/web/static/src/xml/base.xml:1149 +#: addons/web/static/src/xml/base.xml:1268 msgid "Action ID:" msgstr "ID da Ação:" #. openerp-web -#: addons/web/static/src/xml/base.xml:1159 +#: addons/web/static/src/xml/base.xml:542 msgid "Field" msgstr "Campo" #. openerp-web #: addons/web/static/src/xml/base.xml:1205 msgid "Advanced Search..." -msgstr "" +msgstr "Busca Avançada..." #. openerp-web -#: addons/web/static/src/xml/base.xml:1230 +#: addons/web/static/src/xml/base.xml:1287 msgid "-- Filters --" msgstr "-- Filtros --" #. openerp-web -#: addons/web/static/src/xml/base.xml:1239 +#: addons/web/static/src/xml/base.xml:1296 msgid "-- Actions --" msgstr "-- Ações --" #. openerp-web -#: addons/web/static/src/xml/base.xml:1240 +#: addons/web/static/src/xml/base.xml:1297 msgid "Add Advanced Filter" msgstr "Adiciona Filtro Avançado" #. openerp-web -#: addons/web/static/src/xml/base.xml:1241 +#: addons/web/static/src/xml/base.xml:1298 msgid "Save Filter" msgstr "Salvar Filtro" #. openerp-web -#: addons/web/static/src/xml/base.xml:1247 +#: addons/web/static/src/xml/base.xml:1305 msgid "Filter Name:" msgstr "Nome do Filtro" #. openerp-web -#: addons/web/static/src/xml/base.xml:1249 +#: addons/web/static/src/xml/base.xml:1307 msgid "(Any existing filter with the same name will be replaced)" msgstr "(Qualquer filtro existente com o mesmo nome será substituído)" #. openerp-web #: addons/web/static/src/xml/base.xml:1376 msgid "Custom Filters" -msgstr "" +msgstr "Filtros personalizados" #. openerp-web #: addons/web/static/src/xml/base.xml:1379 msgid "Save current filter" -msgstr "" +msgstr "Salvar o filtro atual" #. openerp-web #: addons/web/static/src/xml/base.xml:1381 msgid "Filter name" -msgstr "" +msgstr "Nome do filtro" #. openerp-web #: addons/web/static/src/xml/base.xml:1383 msgid "Share with all users" -msgstr "" +msgstr "Compartilhar com todos os usuários" #. openerp-web -#: addons/web/static/src/xml/base.xml:1392 +#: addons/web/static/src/js/search.js:298 +#: addons/web/static/src/xml/base.xml:1299 msgid "Add to Dashboard" msgstr "Adicionar ao Painel" #. openerp-web #: addons/web/static/src/xml/base.xml:1394 msgid "Title of new Dashboard item" -msgstr "" +msgstr "Título do novo item do painel" #. openerp-web #: addons/web/static/src/xml/base.xml:1395 msgid "save" -msgstr "" +msgstr "salvar" #. openerp-web #: addons/web/static/src/xml/base.xml:1399 msgid "Select Dashboard to add this filter to" -msgstr "" +msgstr "Escolha o Painel para adicionar este filtro" #. openerp-web #: addons/web/static/src/xml/base.xml:1406 msgid "Advanced Search" -msgstr "" +msgstr "Busca Avançada" #. openerp-web #: addons/web/static/src/xml/base.xml:1411 msgid "Add a condition" -msgstr "" +msgstr "Adicionar uma condição" #. openerp-web #: addons/web/static/src/xml/base.xml:1412 msgid "Apply" -msgstr "" +msgstr "Aplicar" #. openerp-web -#: addons/web/static/src/xml/base.xml:1475 +#: addons/web/static/src/xml/base.xml:1509 msgid "Save & New" msgstr "Salvar & Novo" #. openerp-web -#: addons/web/static/src/xml/base.xml:1476 +#: addons/web/static/src/xml/base.xml:1510 msgid "Save & Close" msgstr "Salvar & Fechar" #. openerp-web -#: addons/web/static/src/xml/base.xml:1559 +#: addons/web/static/src/xml/base.xml:1617 msgid "" "This wizard will export all data that matches the current search criteria to " "a CSV file.\n" @@ -1416,67 +1432,67 @@ msgstr "" "ser reimportados após a modificação." #. openerp-web -#: addons/web/static/src/xml/base.xml:1566 +#: addons/web/static/src/xml/base.xml:1624 msgid "Export Type:" msgstr "Tipo de Exportação:" #. openerp-web -#: addons/web/static/src/xml/base.xml:1568 +#: addons/web/static/src/xml/base.xml:1626 msgid "Import Compatible Export" msgstr "Importar Exportação Compatível" #. openerp-web -#: addons/web/static/src/xml/base.xml:1569 +#: addons/web/static/src/xml/base.xml:1627 msgid "Export all Data" msgstr "Exportar todos os Dados" #. openerp-web -#: addons/web/static/src/xml/base.xml:1572 +#: addons/web/static/src/xml/base.xml:1630 msgid "Export Formats" msgstr "Formatos de Exportação" #. openerp-web -#: addons/web/static/src/xml/base.xml:1578 +#: addons/web/static/src/xml/base.xml:1636 msgid "Available fields" msgstr "Campos Disponíveis" #. openerp-web -#: addons/web/static/src/xml/base.xml:1580 +#: addons/web/static/src/xml/base.xml:1638 msgid "Fields to export" msgstr "Campos para exportar" #. openerp-web -#: addons/web/static/src/xml/base.xml:1582 +#: addons/web/static/src/xml/base.xml:1640 msgid "Save fields list" msgstr "Salvar lista de campos" #. openerp-web -#: addons/web/static/src/xml/base.xml:1596 +#: addons/web/static/src/xml/base.xml:1654 msgid "Remove All" msgstr "Remover Tudo" #. openerp-web -#: addons/web/static/src/xml/base.xml:1607 +#: addons/web/static/src/xml/base.xml:1666 msgid "Name" msgstr "Nome" #. openerp-web -#: addons/web/static/src/xml/base.xml:1639 +#: addons/web/static/src/xml/base.xml:1699 msgid "Save as:" msgstr "Salvar como:" #. openerp-web -#: addons/web/static/src/xml/base.xml:1645 +#: addons/web/static/src/xml/base.xml:1706 msgid "Saved exports:" msgstr "Exportações salvas" #. openerp-web -#: addons/web/static/src/xml/base.xml:1663 +#: addons/web/static/src/xml/base.xml:1748 msgid "1. Import a .CSV file" msgstr "Importar um arquivo CSV." #. openerp-web -#: addons/web/static/src/xml/base.xml:1664 +#: addons/web/static/src/xml/base.xml:1749 msgid "" "Select a .CSV file to import. If you need a sample of file to import,\n" " you should use the export tool with the \"Import Compatible\" option." @@ -1487,57 +1503,57 @@ msgstr "" "Compatível\"." #. openerp-web -#: addons/web/static/src/xml/base.xml:1668 +#: addons/web/static/src/xml/base.xml:1753 msgid "CSV File:" msgstr "Arquivo CSV:" #. openerp-web -#: addons/web/static/src/xml/base.xml:1671 +#: addons/web/static/src/xml/base.xml:1756 msgid "2. Check your file format" msgstr "Verifique seu formato de arquivo" #. openerp-web -#: addons/web/static/src/xml/base.xml:1674 +#: addons/web/static/src/xml/base.xml:1759 msgid "Import Options" msgstr "Opções de importação" #. openerp-web -#: addons/web/static/src/xml/base.xml:1678 +#: addons/web/static/src/xml/base.xml:1763 msgid "Does your file have titles?" msgstr "O seu arquivo tem títulos?" #. openerp-web -#: addons/web/static/src/xml/base.xml:1684 +#: addons/web/static/src/xml/base.xml:1769 msgid "Separator:" msgstr "Separador:" #. openerp-web -#: addons/web/static/src/xml/base.xml:1686 +#: addons/web/static/src/xml/base.xml:1771 msgid "Delimiter:" msgstr "Delimitador:" #. openerp-web -#: addons/web/static/src/xml/base.xml:1690 +#: addons/web/static/src/xml/base.xml:1775 msgid "Encoding:" msgstr "Codificação:" #. openerp-web -#: addons/web/static/src/xml/base.xml:1693 +#: addons/web/static/src/xml/base.xml:1778 msgid "UTF-8" msgstr "UTF-8" #. openerp-web -#: addons/web/static/src/xml/base.xml:1694 +#: addons/web/static/src/xml/base.xml:1779 msgid "Latin 1" msgstr "Latin 1" #. openerp-web -#: addons/web/static/src/xml/base.xml:1697 +#: addons/web/static/src/xml/base.xml:1782 msgid "Lines to skip" msgstr "Linhas para ignorar" #. openerp-web -#: addons/web/static/src/xml/base.xml:1697 +#: addons/web/static/src/xml/base.xml:1782 msgid "" "For use if CSV files have titles on multiple lines, skips more than a single " "line during import" @@ -1548,15 +1564,15 @@ msgstr "" #. openerp-web #: addons/web/static/src/xml/base.xml:1713 msgid "--- Don't Import ---" -msgstr "" +msgstr "--- Não Importar ---" #. openerp-web -#: addons/web/static/src/xml/base.xml:1724 +#: addons/web/static/src/xml/base.xml:1809 msgid "The import failed due to:" msgstr "A importação falhou devido a:" #. openerp-web -#: addons/web/static/src/xml/base.xml:1726 +#: addons/web/static/src/xml/base.xml:1811 msgid "Here is a preview of the file we could not import:" msgstr "Aqui está uma visualização do arquivo que não pode ser importado:" diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css index a15d8ca4b22..1aa6f8aa54a 100644 --- a/addons/web/static/src/css/base.css +++ b/addons/web/static/src/css/base.css @@ -1320,6 +1320,9 @@ .openerp .oe_view_manager .oe_view_manager_pager { line-height: 26px; } +.openerp .oe_view_manager .oe_view_manager_pager .oe_list_pager_single_page .oe_pager_group { + display: none; +} .openerp .oe_view_manager .oe_pager_value { float: left; margin-right: 8px; diff --git a/addons/web/static/src/css/base.sass b/addons/web/static/src/css/base.sass index 8723d9d6f2e..0eb4472164f 100644 --- a/addons/web/static/src/css/base.sass +++ b/addons/web/static/src/css/base.sass @@ -1019,6 +1019,8 @@ $sheet-max-width: 860px // ViewManager.pager {{{ .oe_view_manager_pager line-height: 26px + .oe_list_pager_single_page .oe_pager_group + display: none .oe_pager_value float: left margin-right: 8px diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js index 23efe271d04..4f6e3c047f8 100644 --- a/addons/web/static/src/js/chrome.js +++ b/addons/web/static/src/js/chrome.js @@ -57,9 +57,7 @@ instance.web.Dialog = instance.web.Widget.extend({ init: function (parent, options, content) { var self = this; this._super(parent); - if (content) { - this.$element = content instanceof $ ? content : $(content); - } + this.setElement(content || this.make(this.tagName)); this.dialog_options = { modal: true, destroy_on_close: true, @@ -584,25 +582,21 @@ instance.web.Login = instance.web.Widget.extend({ localStorage.setItem('last_password_login_success', ''); } } - self.do_action("login_sucessful"); + self.trigger('login_successful'); },function () { self.$(".oe_login_pane").fadeIn("fast"); self.$element.addClass("oe_login_invalid"); }); + }, + show: function () { + this.$element.show(); + }, + hide: function () { + this.$element.hide(); } }); instance.web.client_actions.add("login", "instance.web.Login"); -instance.web.LoginSuccessful = instance.web.Widget.extend({ - init: function(parent) { - this._super(parent); - }, - start: function() { - this.getParent().getParent().show_application(); - }, -}); -instance.web.client_actions.add("login_sucessful", "instance.web.LoginSuccessful"); - instance.web.Menu = instance.web.Widget.extend({ template: 'Menu', init: function() { @@ -890,12 +884,11 @@ instance.web.Client = instance.web.Widget.extend({ }, start: function() { var self = this; - return instance.connection.session_bind(this.origin).then(function() { + return instance.connection.session_bind(this.origin).pipe(function() { var $e = $(QWeb.render(self._template, {})); - self.$element.replaceWith($e); - self.$element = $e; + self.replaceElement($e); self.bind_events(); - self.show_common(); + return self.show_common(); }); }, bind_events: function() { @@ -906,7 +899,8 @@ instance.web.Client = instance.web.Widget.extend({ this.$element.on('click', '.oe_dropdown_toggle', function(ev) { ev.preventDefault(); var $toggle = $(this); - var $menu = $toggle.find('.oe_dropdown_menu'); + var $menu = $toggle.siblings('.oe_dropdown_menu'); + $menu = $menu.size() >= 1 ? $menu : $toggle.find('.oe_dropdown_menu'); var state = $menu.is('.oe_opened'); setTimeout(function() { // Do not alter propagation @@ -923,8 +917,10 @@ instance.web.Client = instance.web.Widget.extend({ } }, 0); }); - instance.web.bus.on('click', this, function() { - self.$element.find('.oe_dropdown_menu.oe_opened').removeClass('oe_opened'); + instance.web.bus.on('click', this, function(ev) { + if (!$(ev.target).is('input[type=file]')) { + self.$element.find('.oe_dropdown_menu.oe_opened').removeClass('oe_opened'); + } }); }, show_common: function() { @@ -979,10 +975,9 @@ instance.web.WebClient = instance.web.Client.extend({ }; }, show_login: function() { - var self = this; - self.$('.oe_topbar').hide(); - self.action_manager.do_action("login"); - //self.login.appendTo(self.$element); + this.$('.oe_topbar').hide(); + this.action_manager.do_action("login"); + this.action_manager.inner_widget.on('login_successful', this, this.show_application); }, show_application: function() { var self = this; diff --git a/addons/web/static/src/js/corelib.js b/addons/web/static/src/js/corelib.js index 1ed4c4788e3..846ae3e4758 100644 --- a/addons/web/static/src/js/corelib.js +++ b/addons/web/static/src/js/corelib.js @@ -378,7 +378,8 @@ instance.web.PropertiesMixin = _.extend({}, instance.web.EventDispatcherMixin, { instance.web.EventDispatcherMixin.init.call(this); this.__getterSetterInternalMap = {}; }, - set: function(map) { + set: function(map, options) { + options = options || {}; var self = this; var changed = false; _.each(map, function(val, key) { @@ -387,10 +388,11 @@ instance.web.PropertiesMixin = _.extend({}, instance.web.EventDispatcherMixin, { return; changed = true; self.__getterSetterInternalMap[key] = val; - self.trigger("change:" + key, self, { - oldValue: tmp, - newValue: val - }); + if (! options.silent) + self.trigger("change:" + key, self, { + oldValue: tmp, + newValue: val + }); }); if (changed) self.trigger("change", self); @@ -489,23 +491,19 @@ instance.web.CallbackEnabledMixin = _.extend({}, instance.web.PropertiesMixin, { * * The semantics of this precisely replace closing over the method call. * - * @param {String} method_name name of the method to invoke + * @param {String|Function} method function or name of the method to invoke * @returns {Function} proxied method */ - proxy: function (method_name) { + proxy: function (method) { var self = this; return function () { - return self[method_name].apply(self, arguments); + var fn = (typeof method === 'string') ? self[method] : method; + return fn.apply(self, arguments); } } }); instance.web.WidgetMixin = _.extend({},instance.web.CallbackEnabledMixin, { - /** - * Tag name when creating a default $element. - * @type string - */ - tagName: 'div', /** * Constructs the widget and sets its parent if a parent is given. * @@ -515,14 +513,9 @@ instance.web.WidgetMixin = _.extend({},instance.web.CallbackEnabledMixin, { * @param {instance.web.Widget} parent Binds the current instance to the given Widget instance. * When that widget is destroyed by calling destroy(), the current instance will be * destroyed too. Can be null. - * @param {String} element_id Deprecated. Sets the element_id. Only useful when you want - * to bind the current Widget to an already existing part of the DOM, which is not compatible - * with the DOM insertion methods provided by the current implementation of Widget. So - * for new components this argument should not be provided any more. */ init: function(parent) { instance.web.CallbackEnabledMixin.init.call(this); - this.$element = $(document.createElement(this.tagName)); this.setParent(parent); }, /** @@ -532,7 +525,7 @@ instance.web.WidgetMixin = _.extend({},instance.web.CallbackEnabledMixin, { _.each(this.getChildren(), function(el) { el.destroy(); }); - if(this.$element != null) { + if(this.$element) { this.$element.remove(); } instance.web.PropertiesMixin.destroy.call(this); @@ -611,6 +604,7 @@ instance.web.WidgetMixin = _.extend({},instance.web.CallbackEnabledMixin, { * @returns {jQuery.Deferred} */ start: function() { + return $.when(); } }); @@ -671,6 +665,12 @@ instance.web.CallbackEnabled = instance.web.Class.extend(instance.web.CallbackEn * That will kill the widget in a clean way and erase its content from the dom. */ instance.web.Widget = instance.web.Class.extend(instance.web.WidgetMixin, { + // Backbone-ish API + tagName: 'div', + id: null, + className: null, + attributes: {}, + events: {}, /** * The name of the QWeb template that will be used for rendering. Must be * redefined in subclasses or the default render() method can not be used. @@ -687,13 +687,11 @@ instance.web.Widget = instance.web.Class.extend(instance.web.WidgetMixin, { * @param {instance.web.Widget} parent Binds the current instance to the given Widget instance. * When that widget is destroyed by calling destroy(), the current instance will be * destroyed too. Can be null. - * @param {String} element_id Deprecated. Sets the element_id. Only useful when you want - * to bind the current Widget to an already existing part of the DOM, which is not compatible - * with the DOM insertion methods provided by the current implementation of Widget. So - * for new components this argument should not be provided any more. */ init: function(parent) { instance.web.WidgetMixin.init.call(this,parent); + // FIXME: this should not be + this.setElement(this._make_descriptive()); this.session = instance.connection; }, /** @@ -702,20 +700,120 @@ instance.web.Widget = instance.web.Class.extend(instance.web.WidgetMixin, { * key that references `this`. */ renderElement: function() { - var rendered = null; - if (this.template) - rendered = instance.web.qweb.render(this.template, {widget: this}); - if (_.str.trim(rendered)) { - var elem = $(rendered); - this.$element.replaceWith(elem); - this.$element = elem; + var $el; + if (this.template) { + $el = $(_.str.trim(instance.web.qweb.render( + this.template, {widget: this}))); + } else { + $el = this._make_descriptive(); } + this.replaceElement($el); + }, + + /** + * Re-sets the widget's root element and replaces the old root element + * (if any) by the new one in the DOM. + * + * @param {HTMLElement | jQuery} $el + * @returns {*} this + */ + replaceElement: function ($el) { + var $oldel = this.$element; + this.setElement($el); + if ($oldel && !$oldel.is(this.$element)) { + $oldel.replaceWith(this.$element); + } + return this; }, /** - * Shortcut for $element.find() like backbone + * Re-sets the widget's root element (el/$el/$element). + * + * Includes: + * * re-delegating events + * * re-binding sub-elements + * * if the widget already had a root element, replacing the pre-existing + * element in the DOM + * + * @param {HTMLElement | jQuery} element new root element for the widget + * @return {*} this */ - "$": function() { - return this.$element.find.apply(this.$element,arguments); + setElement: function (element) { + // NB: completely useless, as WidgetMixin#init creates a $element + // always + if (this.$element) { + this.undelegateEvents(); + } + + this.$element = (element instanceof $) ? element : $(element); + this.el = this.$element[0]; + + this.delegateEvents(); + + return this; + }, + /** + * Utility function to build small DOM elements. + * + * @param {String} tagName name of the DOM element to create + * @param {Object} [attributes] map of DOM attributes to set on the element + * @param {String} [content] HTML content to set on the element + * @return {Element} + */ + make: function (tagName, attributes, content) { + var el = document.createElement(tagName); + if (!_.isEmpty(attributes)) { + $(el).attr(attributes); + } + if (content) { + $(el).html(content); + } + return el; + }, + /** + * Makes a potential root element from the declarative builder of the + * widget + * + * @return {jQuery} + * @private + */ + _make_descriptive: function () { + var attrs = _.extend({}, this.attributes || {}); + if (this.id) { attrs.id = this.id; } + if (this.className) { attrs['class'] = this.className; } + return $(this.make(this.tagName, attrs)); + }, + delegateEvents: function () { + var events = this.events; + if (_.isEmpty(events)) { return; } + + for(var key in events) { + if (!events.hasOwnProperty(key)) { continue; } + + var method = this.proxy(events[key]); + + var match = /^(\S+)(\s+(.*))?$/.exec(key); + var event = match[1]; + var selector = match[3]; + + event += '.widget_events'; + if (!selector) { + this.$element.on(event, method); + } else { + this.$element.on(event, selector, method); + } + } + }, + undelegateEvents: function () { + this.$element.off('.widget_events'); + }, + /** + * Shortcut for ``this.$element.find(selector)`` + * + * @param {String} selector CSS selector, rooted in $el + * @returns {jQuery} selector match + */ + $: function(selector) { + return this.$element.find(selector); }, /** * Informs the action manager to do an action. This supposes that diff --git a/addons/web/static/src/js/coresetup.js b/addons/web/static/src/js/coresetup.js index 24441dc509f..0011b7c2450 100644 --- a/addons/web/static/src/js/coresetup.js +++ b/addons/web/static/src/js/coresetup.js @@ -19,15 +19,14 @@ instance.web.OldWidget = instance.web.Widget.extend({ this._super(parent); this.element_id = element_id; this.element_id = this.element_id || _.uniqueId('widget-'); + var tmp = document.getElementById(this.element_id); - this.$element = tmp ? $(tmp) : $(document.createElement(this.tagName)); + this.setElement(tmp || this._make_descriptive()); }, renderElement: function() { var rendered = this.render(); if (rendered) { - var elem = $(rendered); - this.$element.replaceWith(elem); - this.$element = elem; + this.replaceElement($(rendered)); } return this; }, @@ -416,12 +415,12 @@ instance.web.Bus = instance.web.Class.extend(instance.web.EventDispatcherMixin, // check gtk bindings // http://unixpapa.com/js/key.html _.each('click,dblclick,keydown,keypress,keyup'.split(','), function(evtype) { - $('html').on(evtype, self, function(ev) { + $('html').on(evtype, function(ev) { self.trigger(evtype, ev); }); }); _.each('resize,scroll'.split(','), function(evtype) { - $(window).on(evtype, self, function(ev) { + $(window).on(evtype, function(ev) { self.trigger(evtype, ev); }); }); diff --git a/addons/web/static/src/js/search.js b/addons/web/static/src/js/search.js index 8ffd672c143..53daa40ae31 100644 --- a/addons/web/static/src/js/search.js +++ b/addons/web/static/src/js/search.js @@ -516,7 +516,16 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea this.$element.addClass('oe_focused'); }, childBlurred: function () { - this.$element.removeClass('oe_focused'); + var val = this.$element.val(); + this.$element.val(''); + var complete = this.$element.data('autocomplete'); + if ((val && complete.term === undefined) || complete.previous !== undefined) { + throw new Error("new jquery.ui version altering implementation" + + " details relied on"); + } + delete complete.term; + this.$element.removeClass('oe_focused') + .trigger('blur'); }, /** * @@ -652,7 +661,7 @@ instance.web.SearchView = instance.web.Widget.extend(/** @lends instance.web.Sea null, _(this.select_for_drawer()).invoke( 'appendTo', this.$element.find('.oe_searchview_drawer'))); - new instance.web.search.AddToDashboard(this).appendTo($('.oe_searchview_drawer', this.$element)); + new instance.web.search.AddToReporting(this).appendTo($('.oe_searchview_drawer', this.$element)); // load defaults var defaults_fetched = $.when.apply(null, _(this.inputs).invoke( @@ -852,13 +861,13 @@ instance.web.search.Invalid = instance.web.Class.extend( /** @lends instance.web ); } }); -instance.web.search.Widget = instance.web.OldWidget.extend( /** @lends instance.web.search.Widget# */{ +instance.web.search.Widget = instance.web.Widget.extend( /** @lends instance.web.search.Widget# */{ template: null, /** * Root class of all search widgets * * @constructs instance.web.search.Widget - * @extends instance.web.OldWidget + * @extends instance.web.Widget * * @param view the ancestor view of this widget */ @@ -1669,42 +1678,39 @@ instance.web.search.Filters = instance.web.search.Input.extend({ })); } }); -instance.web.search.AddToDashboard = instance.web.Widget.extend({ - template: 'SearchView.addtodashboard', +instance.web.search.AddToReporting = instance.web.Widget.extend({ + template: 'SearchView.addtoreporting', _in_drawer: true, start: function () { var self = this; - this.data_loaded = $.Deferred(); - this.dashboard_data =[]; this.$element .on('click', 'h4', this.proxy('show_option')) .on('submit', 'form', function (e) { e.preventDefault(); self.add_dashboard(); }); - return $.when(this.load_data(),this.data_loaded).pipe(this.proxy("render_data")); + return this.load_data().then(this.proxy("render_data")); }, load_data:function(){ if (!instance.webclient) { return $.Deferred().reject(); } - var self = this,dashboard_menu = instance.webclient.menu.data.data.children; - var ir_model_data = new instance.web.Model('ir.model.data',{},[['name','=','menu_reporting_dashboard']]).query(['res_id']); - var map_data = function(result){ - _.detect(dashboard_menu, function(dash){ - var id = _.pluck(dash.children, "id"),indexof = _.indexOf(id, result.res_id); - if(indexof !== -1){ - self.dashboard_data = dash.children[indexof].children - self.data_loaded.resolve(); - return; - } - }); - }; - return ir_model_data._execute().done(function(result){map_data(result[0])}); + var dashboard_menu = instance.webclient.menu.data.data.children; + return new instance.web.Model('ir.model.data') + .query(['res_id']) + .filter([['name','=','menu_reporting_dashboard']]) + .first().pipe(function (result) { + var menu = _(dashboard_menu).chain() + .pluck('children') + .flatten(true) + .find(function (child) { return child.id === result.res_id; }) + .value(); + return menu ? menu.children : []; + }); }, - - render_data: function(){ - var self = this; - var selection = instance.web.qweb.render("SearchView.addtodashboard.selection",{selections:this.dashboard_data}); - this.$element.find("input").before(selection) + render_data: function(dashboard_choices){ + var selection = instance.web.qweb.render( + "SearchView.addtoreporting.selection", { + selections: dashboard_choices}); + this.$("input").before(selection) }, add_dashboard:function(){ var self = this; @@ -1712,11 +1718,11 @@ instance.web.search.AddToDashboard = instance.web.Widget.extend({ var view_parent = this.getParent().getParent(); if (! view_parent.action || ! this.$element.find("select").val()) return this.do_warn("Can't find dashboard action"); - data = getParent.build_search_data(), - context = new instance.web.CompoundContext(getParent.dataset.get_context() || []), - domain = new instance.web.CompoundDomain(getParent.dataset.get_domain() || []); - _.each(data.contexts, function(x) {context.add(x);}); - _.each(data.domains, function(x) {domain.add(x);}); + var data = getParent.build_search_data(); + var context = new instance.web.CompoundContext(getParent.dataset.get_context() || []); + var domain = new instance.web.CompoundDomain(getParent.dataset.get_domain() || []); + _.each(data.contexts, context.add, context); + _.each(data.domains, domain.add, domain); this.rpc('/web/searchview/add_to_dashboard', { menu_id: this.$element.find("select").val(), action_id: view_parent.action.id, @@ -1737,7 +1743,7 @@ instance.web.search.AddToDashboard = instance.web.Widget.extend({ this.$element.toggleClass('oe_opened'); if (! this.$element.hasClass('oe_opened')) return; - this.$element.find("input").val(this.getParent().fields_view.name || "" ); + this.$("input").val(this.getParent().fields_view.name || "" ); } }); diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 5f73f34e723..99df34d6c6c 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -33,6 +33,11 @@ instance.web.form.FieldManagerMixin = { }; instance.web.views.add('form', 'instance.web.FormView'); +/** + * Properties: + * - actual_mode: always "view", "edit" or "create". Read-only property. Determines + * the mode used by the view. + */ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerMixin, { /** * Indicates that this view is not searchable, and thus that no search @@ -55,6 +60,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM * @property {instance.web.Registry} registry=instance.web.form.widgets widgets registry for this form view instance */ init: function(parent, dataset, view_id, options) { + var self = this; this._super(parent); this.set_default_options(options); this.dataset = dataset; @@ -82,13 +88,22 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM this.__blur_timeout = null; this.rendering_engine = new instance.web.form.FormRenderingEngine(this); this.qweb = null; // A QWeb instance will be created if the view is a QWeb template + self.set({actual_mode: self.options.initial_mode}); + this.has_been_loaded.then(function() { + self.on("change:actual_mode", self, self.check_actual_mode); + self.check_actual_mode(); + self.on("change:actual_mode", self, self.init_pager); + self.init_pager(); + }); }, destroy: function() { _.each(this.get_widgets(), function(w) { w.off('focused blurred'); w.destroy(); }); - this.$element.off('.formBlur'); + if (this.$element) { + this.$element.off('.formBlur'); + } this._super(); }, on_loaded: function(data) { @@ -125,17 +140,6 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM this.$buttons.on('click','.oe_form_button_save',this.on_button_save); this.$buttons.on('click','.oe_form_button_cancel',this.on_button_cancel); - this.$pager = $(QWeb.render("FormView.pager", {'widget':self})); - if (this.options.$pager) { - this.$pager.appendTo(this.options.$pager); - } else { - this.$element.find('.oe_form_pager').replaceWith(this.$pager); - } - this.$pager.on('click','a[data-pager-action]',function() { - var action = $(this).data('pager-action'); - self.on_pager_action(action); - }); - this.$sidebar = this.options.$sidebar || this.$element.find('.oe_form_sidebar'); if (!this.sidebar && this.options.$sidebar) { this.sidebar = new instance.web.Sidebar(this); @@ -152,14 +156,12 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM // Add bounce effect on button 'Edit' when click on readonly page view. this.$element.find(".oe_form_field, .oe_form_group_cell").on('click', function (e) { - if(self.get("mode") == "view") { + if(self.get("actual_mode") == "view") { var $button = self.options.$buttons.find(".oe_form_button_edit"); $button.wrap('
').css('margin-right','4px').addClass('oe_left oe_bounce'); } }); - this.on("change:mode", this, this.switch_mode); - this.set({mode: this.options.initial_mode}); this.has_been_loaded.resolve(); return $.when(); }, @@ -267,8 +269,9 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM if (this.$pager) { this.$pager.show(); } - this.$element.show().css('visibility', 'hidden'); this.$element.add(this.$buttons).removeClass('oe_form_dirty'); + this.$element.css('visibility', 'visible'); + this._super(); var shown = this.has_been_loaded; if (options.reload !== false) { @@ -277,16 +280,17 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM // null index means we should start a new record return self.on_button_new(); } - return self.dataset.read_index(_.keys(self.fields_view.fields), { - context: { 'bin_size': true } + var fields = _.keys(self.fields_view.fields); + fields.push('display_name'); + return self.dataset.read_index(fields, { + context: { 'bin_size': true, 'future_display_name' : true } }).pipe(self.on_record_loaded); }); } return shown.pipe(function() { if (options.editable) { - self.set({mode: "edit"}); + self.to_edit_mode(); } - self.$element.css('visibility', 'visible'); }); }, do_hide: function () { @@ -309,7 +313,8 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM return $.Deferred().reject(); } this.datarecord = record; - this.set({ 'title' : record.id ? record.name : "New record" }); + this._actualize_mode(); + this.set({ 'title' : record.id ? record.display_name : "New record" }); if (this.qweb) { this.kill_current_form(); @@ -386,6 +391,24 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM this.reload(); } }, + init_pager: function() { + var self = this; + if (this.$pager) + this.$pager.remove(); + if (this.get("actual_mode") === "create") + return; + this.$pager = $(QWeb.render("FormView.pager", {'widget':self})); + if (this.options.$pager) { + this.$pager.appendTo(this.options.$pager); + } else { + this.$element.find('.oe_form_pager').replaceWith(this.$pager); + } + this.$pager.on('click','a[data-pager-action]',function() { + var action = $(this).data('pager-action'); + self.on_pager_action(action); + }); + this.do_update_pager(); + }, do_update_pager: function(hide_index) { var index = hide_index ? '-' : this.dataset.index + 1; this.$pager.find('button').prop('disabled', this.dataset.ids.length < 2).end() @@ -585,9 +608,32 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM return $.Deferred().reject(); } }, - switch_mode: function() { + /** + * Ask the view to switch to view mode if possible. The view may not do it + * if the current record is not yet saved. It will then stay in create mode. + */ + to_view_mode: function() { + this._actualize_mode("view"); + }, + /** + * Ask the view to switch to edit mode if possible. The view may not do it + * if the current record is not yet saved. It will then stay in create mode. + */ + to_edit_mode: function() { + this._actualize_mode("edit"); + }, + /** + * Reactualize actual_mode. + */ + _actualize_mode: function(switch_to) { + var mode = switch_to || this.get("actual_mode"); + if (! this.datarecord.id) + mode = "create"; + this.set({actual_mode: mode}); + }, + check_actual_mode: function(source, options) { var self = this; - if(this.get("mode") == "view") { + if(this.get("actual_mode") === "view") { self.$element.removeClass('oe_form_editable').addClass('oe_form_readonly'); self.$buttons.find('.oe_form_buttons_edit').hide(); self.$buttons.find('.oe_form_buttons_view').show(); @@ -605,11 +651,12 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM }); var fields_order = self.fields_order.slice(0); if (self.default_focus_field) { - fields_order.unshift(self.default_focus_field); + fields_order.unshift(self.default_focus_field.name); } for (var i = 0; i < fields_order.length; i += 1) { var field = self.fields[fields_order[i]]; - if (!field.get('effective_invisible') && !field.get('effective_readonly') && field.focus() !== false) { + if (!field.get('effective_invisible') && !field.get('effective_readonly')) { + field.focus(); break; } } @@ -618,19 +665,19 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM on_button_save: function() { var self = this; return this.do_save().then(function(result) { - self.set({mode: "view"}); + self.to_view_mode(); }); }, on_button_cancel: function(event) { if (this.can_be_discarded()) { - this.set({mode: "view"}); + this.to_view_mode(); this.on_record_loaded(this.datarecord); } return false; }, on_button_new: function() { var self = this; - this.set({mode: "edit"}); + this.to_edit_mode(); return $.when(this.has_been_loaded).pipe(function() { if (self.can_be_discarded()) { return self.load_defaults(); @@ -638,7 +685,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM }); }, on_button_edit: function() { - return this.set({mode: "edit"}); + return this.to_edit_mode(); }, on_button_create: function() { this.dataset.index = null; @@ -651,7 +698,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM self.dataset.call('copy', [self.datarecord.id, {}, self.dataset.context]).then(function(new_id) { return self.on_created({ result : new_id }); }).then(function() { - return self.set({mode: "edit"}); + return self.to_edit_mode(); }).then(function() { def.resolve(); }); @@ -813,8 +860,10 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM if (self.dataset.index == null || self.dataset.index < 0) { return $.when(self.on_button_new()); } else { - return self.dataset.read_index(_.keys(self.fields_view.fields), { - context : { 'bin_size' : true } + var fields = _.keys(self.fields_view.fields); + fields.push('display_name'); + return self.dataset.read_index(fields, { + context : { 'bin_size' : true, 'future_display_name' : true } }).pipe(self.on_record_loaded); } }); @@ -944,7 +993,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM register_field: function(field, name) { this.fields[name] = field; this.fields_order.push(name); - if (field.node.attrs.default_focus == '1') { + if (JSON.parse(field.node.attrs.default_focus || "0")) { this.default_focus_field = field; } @@ -966,7 +1015,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM return this.fields_view.fields[field_name]; }, is_create_mode: function() { - return !this.datarecord.id; + return this.get("actual_mode") === "create"; }, open_translate_dialog: function(field) { return this._super(field); @@ -1650,7 +1699,7 @@ instance.web.form.WidgetButton = instance.web.form.FormWidget.extend({ this._super(view, node); this.force_disabled = false; this.string = (this.node.attrs.string || '').replace(/_/g, ''); - if (this.node.attrs.default_focus == '1') { + if (JSON.parse(this.node.attrs.default_focus || "0")) { // TODO fme: provide enter key binding to widgets this.view.default_focus_button = this; } @@ -2019,7 +2068,7 @@ instance.web.form.FieldChar = instance.web.form.AbstractField.extend(instance.we return this.get('value') === '' || this._super(); }, focus: function() { - this.delay_focus(this.$element.find('input:first')); + this.$element.find('input:first')[0].focus(); } }); @@ -2992,6 +3041,7 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({ } if(view.view_type === "list") { _.extend(view.options, { + addable: null, selectable: self.multi_selection, sortable: false, import_enabled: false, @@ -2999,7 +3049,6 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({ }); if (self.get("effective_readonly")) { _.extend(view.options, { - addable: null, deletable: null, reorderable: false, }); @@ -3029,7 +3078,6 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({ this.views = views; this.viewmanager = new instance.web.form.One2ManyViewManager(this, this.dataset, views, {}); - this.viewmanager.$element.addClass("oe_view_manager_one2many"); this.viewmanager.o2m = self; var once = $.Deferred().then(function() { self.init_form_last_update.resolve(); @@ -3424,6 +3472,34 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({ this._super.apply(this, arguments); } }); +instance.web.form.One2ManyList = instance.web.ListView.List.extend({ + pad_table_to: function (count) { + this._super(count > 0 ? count - 1 : 0); + + // magical invocation of wtf does that do + if (this.view.o2m.get('effective_readonly')) { + return; + } + + var self = this; + var columns = _(this.columns).filter(function (column) { + return column.invisible !== '1'; + }).length; + if (this.options.selectable) { columns++; } + if (this.options.deletable) { columns++; } + var $cell = $('', { + colspan: columns, + 'class': 'oe_form_field_one2many_list_row_add' + }).text(_t("Add a row")) + .click(function (e) { + e.preventDefault(); + e.stopPropagation(); + self.view.do_add_record(); + }); + this.$current.append( + $('').append($cell)) + } +}); instance.web.form.One2ManyFormView = instance.web.FormView.extend({ form_template: 'One2Many.formview', @@ -3551,12 +3627,13 @@ instance.web.form.FieldMany2ManyTags = instance.web.form.AbstractField.extend(in render_value: function() { var self = this; var dataset = new instance.web.DataSetStatic(this, this.field.relation, self.view.dataset.get_context()); + var values = self.get("value") var handle_names = function(data) { var indexed = {}; _.each(data, function(el) { indexed[el[0]] = el; }); - data = _.map(self.get("value"), function(el) { return indexed[el]; }); + data = _.map(values, function(el) { return indexed[el]; }); if (! self.get("effective_readonly")) { self.tags.containerElement().children().remove(); $("textarea", self.$element).css("padding-left", "3px"); @@ -3565,8 +3642,8 @@ instance.web.form.FieldMany2ManyTags = instance.web.form.AbstractField.extend(in self.$element.html(QWeb.render("FieldMany2ManyTag", {elements: data})); } }; - if (! self.get('values') || self.get('values').length > 0) { - this._display_orderer.add(dataset.name_get(self.get("value"))).then(handle_names); + if (! values || values.length > 0) { + this._display_orderer.add(dataset.name_get(values)).then(handle_names); } else { handle_names([]); } @@ -4228,7 +4305,7 @@ instance.web.form.FieldReference = instance.web.form.AbstractField.extend(instan this.selection.view = this.view; this.selection.set({force_readonly: this.get('effective_readonly')}); this.selection.on("change:value", this, this.on_selection_changed); - this.selection.$element = $(".oe_form_view_reference_selection", this.$element); + this.selection.setElement(this.$(".oe_form_view_reference_selection")); this.selection.renderElement(); this.selection.start(); this.selection @@ -4241,7 +4318,7 @@ instance.web.form.FieldReference = instance.web.form.AbstractField.extend(instan this.m2o.view = this.view; this.m2o.set({force_readonly: this.get("effective_readonly")}); this.m2o.on("change:value", this, this.data_changed); - this.m2o.$element = $(".oe_form_view_reference_m2o", this.$element); + this.m2o.setElement(this.$(".oe_form_view_reference_m2o")); this.m2o.renderElement(); this.m2o.start(); this.m2o diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js index 485b4e8d59b..9c7bf88db1a 100644 --- a/addons/web/static/src/js/view_list.js +++ b/addons/web/static/src/js/view_list.js @@ -376,6 +376,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi var total = dataset.size(); var limit = this.limit() || total; + this.$pager.toggle(total !== 0); this.$pager.toggleClass('oe_list_pager_single_page', (total <= limit)); var spager = '-'; if (total) { @@ -932,7 +933,6 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web. '[data-id=' + record.get('id') + ']'); var index = $row.data('index'); $row.remove(); - self.refresh_zebra(index); }, 'reset': function () { return self.on_records_reset(); }, 'change': function (event, record, attribute, value, old_value) { @@ -967,8 +967,6 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web. '[data-id=' + previous_record.get('id') + ']'); $new_row.insertAfter($previous_sibling); } - - self.refresh_zebra(index, 1); } }; _(this.record_callbacks).each(function (callback, event) { @@ -1108,7 +1106,6 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web. this.$current .children('tr:not([data-id])').remove().end() .append(new Array(count - this.records.length + 1).join(row)); - this.refresh_zebra(this.records.length); }, /** * Gets the ids of all currently selected records, if any @@ -1182,25 +1179,6 @@ instance.web.ListView.List = instance.web.Class.extend( /** @lends instance.web. render_cell: function () { return self.render_cell.apply(self, arguments); } }); - }, - /** - * Fixes fixes the even/odd classes - * - * @param {Number} [from_index] index from which to resequence - * @param {Number} [offset = 0] selection offset for DOM, in case there are rows to ignore in the table - */ - refresh_zebra: function (from_index, offset) { - offset = offset || 0; - from_index = from_index || 0; - var dom_offset = offset + from_index; - var sel = dom_offset ? ':gt(' + (dom_offset - 1) + ')' : null; - this.$current.children(sel).each(function (i, e) { - var index = from_index + i; - // reset record-index accelerators on rows and even/odd - var even = index%2 === 0; - $(e).toggleClass('even', even) - .toggleClass('odd', !even); - }); } }); instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.web.ListView.Groups# */{ @@ -1533,8 +1511,6 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we }(dataset, record.get('id'), seq)); record.set('sequence', seq); } - - list.refresh_zebra(); } }); }, diff --git a/addons/web/static/src/js/view_list_editable.js b/addons/web/static/src/js/view_list_editable.js index c7654479864..6c38495d0dc 100644 --- a/addons/web/static/src/js/view_list_editable.js +++ b/addons/web/static/src/js/view_list_editable.js @@ -96,9 +96,6 @@ openerp.web.list_editable = function (instance) { }, on_loaded: function (data, grouped) { var self = this; - if (this.editor) { - this.editor.destroy(); - } // tree/@editable takes priority on everything else if present. var result = this._super(data, grouped); if (this.editable()) { @@ -118,6 +115,7 @@ openerp.web.list_editable = function (instance) { self.start_edition(); } }); + this.editor.destroy(); // Editor is not restartable due to formview not being // restartable this.editor = this.make_editor(); @@ -425,26 +423,64 @@ openerp.web.list_editable = function (instance) { keyup_ESCAPE: function () { return this.cancel_edition(); }, + /** + * Gets the selection range (start, end) for the provided element, + * returns ``null`` if it can't get a range. + * + * @private + */ _text_selection_range: function (el) { - if (el.selectionStart !== undefined) { + var selectionStart; + try { + selectionStart = el.selectionStart; + } catch (e) { + // radio or checkbox throw on selectionStart access + return null; + } + if (selectionStart !== undefined) { return { - start: el.selectionStart, + start: selectionStart, end: el.selectionEnd }; - } else if(document.body.createTextRange) { + } else if (document.body.createTextRange) { throw new Error("Implement text range handling for MSIE"); var sel = document.body.createTextRange(); if (sel.parentElement() === el) { } } + // Element without selection ranges (select, div/@contenteditable) + return null; }, _text_cursor: function (el) { var selection = this._text_selection_range(el); - if (selection.start !== selection.end) { + if (!selection) { return null; } - return selection.start; + if (selection.start !== selection.end) { + return {position: null, collapsed: false}; + } + return {position: selection.start, collapsed: true}; + }, + /** + * Checks if the cursor is at the start of the provided el + * + * @param {HTMLInputElement | HTMLTextAreaElement} + * @returns {Boolean} + * @private + */ + _at_start: function (cursor, el) { + return cursor.collapsed && (cursor.position === 0); + }, + /** + * Checks if the cursor is at the end of the provided el + * + * @param {HTMLInputElement | HTMLTextAreaElement} + * @returns {Boolean} + * @private + */ + _at_end: function (cursor, el) { + return cursor.collapsed && (cursor.position === el.value.length); }, /** * @param DOMEvent event @@ -454,10 +490,11 @@ openerp.web.list_editable = function (instance) { */ _key_move_record: function (event, record_direction, is_valid_move) { if (!this.editor.is_editing('edit')) { return $.when(); } - // FIXME: assumes editable widgets are input-type elements - var index = this._text_cursor(event.target); - // If selecting or not at the start of the input - if (!is_valid_move(event.target, index)) { return $.when(); } + var cursor = this._text_cursor(event.target); + // if text-based input (has a cursor) + // and selecting (not collapsed) or not at a field boundary + // don't move to the next record + if (cursor && !is_valid_move(event.target, cursor)) { return $.when(); } event.preventDefault(); var source_field = $(event.target).closest('[data-fieldname]') @@ -466,13 +503,15 @@ openerp.web.list_editable = function (instance) { }, keydown_UP: function (e) { - return this._key_move_record(e, 'pred', function (el, index) { - return index === 0; + var self = this; + return this._key_move_record(e, 'pred', function (el, cursor) { + return self._at_start(cursor, el); }); }, keydown_DOWN: function (e) { - return this._key_move_record(e, 'succ', function (el, index) { - return index === el.value.length; + var self = this; + return this._key_move_record(e, 'succ', function (el, cursor) { + return self._at_end(cursor, el); }); }, @@ -480,8 +519,8 @@ openerp.web.list_editable = function (instance) { // If the cursor is at the beginning of the field var source_field = $(e.target).closest('[data-fieldname]') .attr('data-fieldname'); - var index = this._text_cursor(e.target); - if (index !== 0) { return $.when(); } + var cursor = this._text_cursor(e.target); + if (cursor && !this._at_start(cursor, e.target)) { return $.when(); } var fields_order = this.editor.form.fields_order; var field_index = _(fields_order).indexOf(source_field); @@ -504,8 +543,8 @@ openerp.web.list_editable = function (instance) { // looking for new fields at the right var source_field = $(e.target).closest('[data-fieldname]') .attr('data-fieldname'); - var index = this._text_cursor(e.target); - if (index !== e.target.value.length) { return $.when(); } + var cursor = this._text_cursor(e.target); + if (cursor && !this._at_end(cursor, e.target)) { return $.when(); } var fields_order = this.editor.form.fields_order; var field_index = _(fields_order).indexOf(source_field); diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js index 6b4bc44df64..2b79701ba4d 100644 --- a/addons/web/static/src/js/views.js +++ b/addons/web/static/src/js/views.js @@ -214,21 +214,30 @@ instance.web.ActionManager = instance.web.Widget.extend({ this.dialog_stop(); this.clear_breadcrumbs(); }, - ir_actions_act_window: function (action, on_close) { - var self = this; - if (_(['base.module.upgrade', 'base.setup.installer']) - .contains(action.res_model)) { - var old_close = on_close; - on_close = function () { - instance.webclient.do_reload().then(old_close); - }; + + do_ir_actions_common: function(action, on_close) { + var self = this, klass, widget, add_breadcrumb; + if (action.type === 'ir.actions.client') { + var ClientWidget = instance.web.client_actions.get_object(action.tag); + widget = new ClientWidget(this, action.params); + klass = 'oe_act_client'; + add_breadcrumb = function() { + self.push_breadcrumb({ + widget: widget, + title: action.name + }); + } + } else { + widget = new instance.web.ViewManagerAction(this, action); + klass = 'oe_act_window'; + add_breadcrumb = widget.proxy('add_breadcrumb'); } if (action.target === 'new') { if (this.dialog === null) { // These buttons will be overwrited by
if any this.dialog = new instance.web.Dialog(this, { buttons: { "Close": function() { $(this).dialog("close"); }}, - dialogClass: 'oe_act_window' + dialogClass: klass }); if(on_close) this.dialog.on_close.add(on_close); @@ -236,7 +245,7 @@ instance.web.ActionManager = instance.web.Widget.extend({ this.dialog_widget.destroy(); } this.dialog.dialog_title = action.name; - this.dialog_widget = new instance.web.ViewManagerAction(this, action); + this.dialog_widget = widget; this.dialog_widget.appendTo(this.dialog.$element); this.dialog.open(); } else { @@ -247,11 +256,34 @@ instance.web.ActionManager = instance.web.Widget.extend({ }); } this.inner_action = action; - var inner_widget = this.inner_widget = new instance.web.ViewManagerAction(this, action); - inner_widget.add_breadcrumb(); + this.inner_widget = widget; + add_breadcrumb(); this.inner_widget.appendTo(this.$element); } }, + + ir_actions_act_window: function (action, on_close) { + var self = this; + if (_(['base.module.upgrade', 'base.setup.installer']) + .contains(action.res_model)) { + var old_close = on_close; + on_close = function () { + instance.webclient.do_reload().then(old_close); + }; + } + if (action.target !== 'new') { + if(action.menu_id) { + this.dialog_stop(); + return this.getParent().do_action(action, function () { + instance.webclient.menu.open_menu(action.menu_id); + }); + } + } + return this.do_ir_actions_common(action, on_close); + }, + ir_actions_client: function (action, on_close) { + return this.do_ir_actions_common(action, on_close); + }, ir_actions_act_window_close: function (action, on_closed) { if (!this.dialog && on_closed) { on_closed(); @@ -267,18 +299,6 @@ instance.web.ActionManager = instance.web.Widget.extend({ self.do_action(action, on_closed) }); }, - ir_actions_client: function (action) { - this.dialog_stop(); - var ClientWidget = instance.web.client_actions.get_object(action.tag); - this.inner_widget = new ClientWidget(this, action.params); - this.push_breadcrumb({ - widget: this.inner_widget, - title: action.name - }); - this.inner_action = action; - this.do_push_state({}); - this.inner_widget.appendTo(this.$element); - }, ir_actions_report_xml: function(action, on_closed) { var self = this; instance.web.blockUI(); @@ -403,7 +423,7 @@ instance.web.ViewManager = instance.web.Widget.extend({ .find('.oe_view_manager_switch a').filter('[data-view-type="' + view_type + '"]') .parent().addClass('active'); - $.when(view_promise).then(function () { + return $.when(view_promise).then(function () { _.each(_.keys(self.views), function(view_name) { var controller = self.views[view_name].controller; if (controller) { @@ -425,7 +445,6 @@ instance.web.ViewManager = instance.web.Widget.extend({ } }); }); - return view_promise; }, do_create_view: function(view_type) { // Lazy loading of views @@ -485,6 +504,7 @@ instance.web.ViewManager = instance.web.Widget.extend({ }); this.getParent().push_breadcrumb({ widget: this, + action: this.action, show: function(index, $e) { var view_to_select = views[index]; self.$element.show(); @@ -493,9 +513,27 @@ instance.web.ViewManager = instance.web.Widget.extend({ } }, get_title: function() { - return _.map(views, function(v) { - return self.views[v].controller.get('title'); + var id; + var currentIndex; + _.each(self.getParent().breadcrumbs, function(bc, i) { + if (bc.widget === self) { + currentIndex = i; + } }); + var next = self.getParent().breadcrumbs.slice(currentIndex + 1)[0]; + var titles = _.map(views, function(v) { + var controller = self.views[v].controller; + if (v === 'form') { + id = controller.datarecord.id; + } + return controller.get('title'); + }); + if (next && next.action.res_id && self.active_view === 'form' && self.model === next.action.res_model && id === next.action.res_id) { + // If the current active view is a formview and the next item in the breadcrumbs + // is an action on same object (model / res_id), then we omit the current formview's title + titles.pop(); + } + return titles; } }); }, @@ -1136,7 +1174,7 @@ instance.web.View = instance.web.Widget.extend({ this.view_id = view_id; this.set_default_options(options); }, - start: function() { + start: function () { return this.load_view(); }, load_view: function() { diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index c6b8b20c9bd..014868eac90 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -518,25 +518,25 @@
+
    +
  • + + + + x +
  • +
  • + + + /web/binary/upload_attachment + + + + Add... + +
  • +
@@ -645,10 +645,9 @@ - - @@ -1444,18 +1443,18 @@
-
-

Add to Dashboard

+
+

Add to Reporting

-

- +

+
- - +
diff --git a/addons/web/static/test/Widget.js b/addons/web/static/test/Widget.js new file mode 100644 index 00000000000..100b1fa6633 --- /dev/null +++ b/addons/web/static/test/Widget.js @@ -0,0 +1,239 @@ +$(document).ready(function () { + var $fix = $('#qunit-fixture'); + var mod = { + setup: function () { + instance = window.openerp.init([]); + window.openerp.web.corelib(instance); + + instance.web.qweb = new QWeb2.Engine(); + instance.web.qweb.add_template( + '' + + '' + + '
    ' + + '
  1. ' + + '' + + '' + + '
  2. ' + + '
' + + '
' + + '' + + '

' + + '
' + + '
'); + } + }; + var instance; + + module('Widget.proxy', mod); + test('(String)', function () { + var W = instance.web.Widget.extend({ + exec: function () { + this.executed = true; + } + }); + var w = new W; + var fn = w.proxy('exec'); + fn(); + ok(w.executed, 'should execute the named method in the right context'); + }); + test('(String)(*args)', function () { + var W = instance.web.Widget.extend({ + exec: function (arg) { + this.executed = arg; + } + }); + var w = new W; + var fn = w.proxy('exec'); + fn(42); + ok(w.executed, "should execute the named method in the right context"); + equal(w.executed, 42, "should be passed the proxy's arguments"); + }); + test('(String), include', function () { + // the proxy function should handle methods being changed on the class + // and should always proxy "by name", to the most recent one + var W = instance.web.Widget.extend({ + exec: function () { + this.executed = 1; + } + }); + var w = new W; + var fn = w.proxy('exec'); + W.include({ + exec: function () { this.executed = 2; } + }); + + fn(); + equal(w.executed, 2, "should be lazily resolved"); + }); + + test('(Function)', function () { + var w = new (instance.web.Widget.extend({ })); + + var fn = w.proxy(function () { this.executed = true; }); + fn(); + ok(w.executed, "should set the function's context (like Function#bind)"); + }); + test('(Function)(*args)', function () { + var w = new (instance.web.Widget.extend({ })); + + var fn = w.proxy(function (arg) { this.executed = arg; }); + fn(42); + equal(w.executed, 42, "should be passed the proxy's arguments"); + }); + + module('Widget.renderElement', mod); + test('no template, default', function () { + var w = new (instance.web.Widget.extend({ })); + + var $original = w.$element; + ok($original, "should initially have a root element"); + w.renderElement(); + ok(w.$element, "should have generated a root element"); + ok($original !== w.$element, "should have generated a new root element"); + strictEqual(w.$element, w.$element, "should provide $element alias"); + ok(w.$element.is(w.el), "should provide raw DOM alias"); + + equal(w.el.nodeName, 'DIV', "should have generated the default element"); + equal(w.el.attributes.length, 0, "should not have generated any attribute"); + ok(_.isEmpty(w.$element.html(), "should not have generated any content")); + }); + test('no template, custom tag', function () { + var w = new (instance.web.Widget.extend({ + tagName: 'ul' + })); + w.renderElement(); + + equal(w.el.nodeName, 'UL', "should have generated the custom element tag"); + }); + test('no template, @id', function () { + var w = new (instance.web.Widget.extend({ + id: 'foo' + })); + w.renderElement(); + + equal(w.el.attributes.length, 1, "should have one attribute"); + equal(w.$element.attr('id'), 'foo', "should have generated the id attribute"); + equal(w.el.id, 'foo', "should also be available via property"); + }); + test('no template, @className', function () { + var w = new (instance.web.Widget.extend({ + className: 'oe_some_class' + })); + w.renderElement(); + + equal(w.el.className, 'oe_some_class', "should have the right property"); + equal(w.$element.attr('class'), 'oe_some_class', "should have the right attribute"); + }); + test('no template, bunch of attributes', function () { + var w = new (instance.web.Widget.extend({ + attributes: { + 'id': 'some_id', + 'class': 'some_class', + 'data-foo': 'data attribute', + 'clark': 'gable', + 'spoiler': 'snape kills dumbledore' + } + })); + w.renderElement(); + + equal(w.el.attributes.length, 5, "should have all the specified attributes"); + + equal(w.el.id, 'some_id'); + equal(w.$element.attr('id'), 'some_id'); + + equal(w.el.className, 'some_class'); + equal(w.$element.attr('class'), 'some_class'); + + equal(w.$element.attr('data-foo'), 'data attribute'); + equal(w.$element.data('foo'), 'data attribute'); + + equal(w.$element.attr('clark'), 'gable'); + equal(w.$element.attr('spoiler'), 'snape kills dumbledore'); + }); + + test('template', function () { + var w = new (instance.web.Widget.extend({ + template: 'test.widget.template' + })); + w.renderElement(); + + equal(w.el.nodeName, 'OL'); + equal(w.$element.children().length, 5); + equal(w.el.textContent, '01234'); + }); + + module('Widget.$', mod); + test('basic-alias', function () { + var w = new (instance.web.Widget.extend({ + template: 'test.widget.template' + })); + w.renderElement(); + + ok(w.$('li:eq(3)').is(w.$element.find('li:eq(3)')), + "should do the same thing as calling find on the widget root"); + }); + + module('Widget.events', mod); + test('delegate', function () { + var a = []; + var w = new (instance.web.Widget.extend({ + template: 'test.widget.template', + events: { + 'click': function () { + a[0] = true; + strictEqual(this, w, "should trigger events in widget") + }, + 'click li.class-3': 'class3', + 'change input': function () { a[2] = true; } + }, + class3: function () { a[1] = true; } + })); + w.renderElement(); + + w.$element.click(); + w.$('li:eq(3)').click(); + w.$('input:last').val('foo').change(); + + for(var i=0; i<3; ++i) { + ok(a[i], "should pass test " + i); + } + }); + test('undelegate', function () { + var clicked = false, newclicked = false; + var w = new (instance.web.Widget.extend({ + template: 'test.widget.template', + events: { 'click li': function () { clicked = true; } } + })); + w.renderElement(); + w.$element.on('click', 'li', function () { newclicked = true }); + + w.$('li').click(); + ok(clicked, "should trigger bound events"); + ok(newclicked, "should trigger bound events"); + clicked = newclicked = false; + + w.undelegateEvents(); + w.$('li').click(); + ok(!clicked, "undelegate should unbind events delegated"); + ok(newclicked, "undelegate should only unbind events it created"); + }); + + module('Widget.renderElement', mod); + test('repeated', function () { + var w = new (instance.web.Widget.extend({ + template: 'test.widget.template-value' + })); + w.value = 42; + w.appendTo($fix) + .always(start) + .done(function () { + equal($fix.find('p').text(), '42', "DOM fixture should contain initial value"); + equal(w.$element.text(), '42', "should set initial value"); + w.value = 36; + w.renderElement(); + equal($fix.find('p').text(), '36', "DOM fixture should use new value"); + equal(w.$element.text(), '36', "should set new value"); + }); + }); +}); diff --git a/addons/web/static/test/list-editable.js b/addons/web/static/test/list-editable.js index 2313195f626..62f31a55833 100644 --- a/addons/web/static/test/list-editable.js +++ b/addons/web/static/test/list-editable.js @@ -72,7 +72,7 @@ $(document).ready(function () { }); asyncTest('base-state', 2, function () { var e = new instance.web.list.Editor({ - dataset: {}, + dataset: {ids: []}, edition_view: function () { return makeFormView(); } @@ -240,8 +240,7 @@ $(document).ready(function () { }; var ds = new instance.web.DataSetStatic(null, 'demo', null, [1]); - var l = new instance.web.ListView({}, ds); - l.set_editable(true); + var l = new instance.web.ListView({}, ds, false, {editable: 'top'}); l.appendTo($fix) .pipe(l.proxy('reload_content')) @@ -305,8 +304,7 @@ $(document).ready(function () { counter: 0, onEvent: function (e) { this.counter++; } }; - var l = new instance.web.ListView({}, ds); - l.set_editable(true); + var l = new instance.web.ListView({}, ds, false, {editable: 'top'}); l.on('edit:before edit:after', o, o.onEvent); l.appendTo($fix) .pipe(l.proxy('reload_content')) @@ -326,8 +324,7 @@ $(document).ready(function () { asyncTest('edition events: cancelling', 3, function () { var edit_after = false; var ds = new instance.web.DataSetStatic(null, 'demo', null, [1]); - var l = new instance.web.ListView({}, ds); - l.set_editable(true); + var l = new instance.web.ListView({}, ds, false, {editable: 'top'}); l.on('edit:before', {}, function (e) { e.cancel = true; }); diff --git a/addons/web/static/test/test.html b/addons/web/static/test/test.html index cab1586f240..35d57d3c3a6 100644 --- a/addons/web/static/test/test.html +++ b/addons/web/static/test/test.html @@ -42,7 +42,7 @@ @@ -61,5 +61,6 @@ + diff --git a/addons/web_calendar/__openerp__.py b/addons/web_calendar/__openerp__.py index 755d6fdb1f7..807d7927dc0 100644 --- a/addons/web_calendar/__openerp__.py +++ b/addons/web_calendar/__openerp__.py @@ -1,10 +1,7 @@ { "name": "Web Calendar", "category": "Hidden", - "description": - """ - OpenERP Web Calendar view. - """, + "description":"""OpenERP Web Calendar view.""", "version": "2.0", "depends": ['web'], "js": [ diff --git a/addons/web_calendar/static/src/js/calendar.js b/addons/web_calendar/static/src/js/calendar.js index 59ca58bec86..ae92585f67e 100644 --- a/addons/web_calendar/static/src/js/calendar.js +++ b/addons/web_calendar/static/src/js/calendar.js @@ -492,15 +492,17 @@ instance.web_calendar.Sidebar = instance.web.Widget.extend({ } }); instance.web_calendar.SidebarFilter = instance.web.Widget.extend({ + events: { + 'change input:checkbox': 'on_filter_click' + }, init: function(parent, view) { this._super(parent); this.view = view; - this.$element.delegate('input:checkbox', 'change', this.on_filter_click); }, on_events_loaded: function(filters) { var selected_filters = this.view.selected_filters.slice(0); this.$element.html(QWeb.render('CalendarView.sidebar.responsible', { filters: filters })); - this.$element.find('div.oe_calendar_responsible input').each(function() { + this.$('div.oe_calendar_responsible input').each(function() { if (_.indexOf(selected_filters, $(this).val()) > -1) { $(this).click(); } @@ -511,7 +513,7 @@ instance.web_calendar.SidebarFilter = instance.web.Widget.extend({ responsibles = [], $e = $(e.target); this.view.selected_filters = []; - this.$element.find('div.oe_calendar_responsible input:checked').each(function() { + this.$('div.oe_calendar_responsible input:checked').each(function() { responsibles.push($(this).val()); self.view.selected_filters.push($(this).val()); }); diff --git a/addons/web_dashboard/__openerp__.py b/addons/web_dashboard/__openerp__.py index a48b3f9c0f5..54f9ae294ac 100644 --- a/addons/web_dashboard/__openerp__.py +++ b/addons/web_dashboard/__openerp__.py @@ -1,10 +1,7 @@ { "name": "Web Dashboard", "category": "Hidden", - "description": - """ - OpenERP Web Dashboard view. - """, + "description":"""OpenERP Web Dashboard view.""", "version": "2.0", "depends": ['web'], "js": [ diff --git a/addons/web_dashboard/static/src/js/dashboard.js b/addons/web_dashboard/static/src/js/dashboard.js index a19151af33c..fea775d4ee8 100644 --- a/addons/web_dashboard/static/src/js/dashboard.js +++ b/addons/web_dashboard/static/src/js/dashboard.js @@ -226,6 +226,8 @@ instance.web.form.DashBoard = instance.web.form.FormWidget.extend({ } }, renderElement: function() { + this._super(); + var check = _.detect(this.node.children, function(column, column_index) { return _.detect(column.children,function(element){ return element.tag === "action"? element: false; diff --git a/addons/web_diagram/__openerp__.py b/addons/web_diagram/__openerp__.py index 247506cf581..eda044f686f 100644 --- a/addons/web_diagram/__openerp__.py +++ b/addons/web_diagram/__openerp__.py @@ -1,7 +1,7 @@ { "name" : "OpenERP Web Diagram", "category" : "Hidden", - "description":'Openerp Web Diagram view', + "description":"""Openerp Web Diagram view.""", "version" : "2.0", "depends" : ["web"], "js": [ diff --git a/addons/web_gantt/__openerp__.py b/addons/web_gantt/__openerp__.py index 3357dbae53f..09528016c4c 100644 --- a/addons/web_gantt/__openerp__.py +++ b/addons/web_gantt/__openerp__.py @@ -1,10 +1,7 @@ { "name": "Web Gantt", "category": "Hidden", - "description": - """ - OpenERP Web Gantt chart view. - """, + "description":"""OpenERP Web Gantt chart view.""", "version": "2.0", "depends": ['web'], "js": [ diff --git a/addons/web_graph/__openerp__.py b/addons/web_graph/__openerp__.py index 84412a9c240..31c66863ebf 100644 --- a/addons/web_graph/__openerp__.py +++ b/addons/web_graph/__openerp__.py @@ -1,14 +1,16 @@ { "name": "Graph Views", "category" : "Hidden", - "description":"""Graph Views for Web Client + "description":""" +Graph Views for Web Client. +=========================== -* Parse a view but allows changing dynamically the presentation -* Graph Types: pie, lines, areas, bars, radar -* Stacked/Not Stacked for areas and bars -* Legends: top, inside (top/left), hidden -* Features: download as PNG or CSV, browse data grid, switch orientation -* Unlimited "Group By" levels (not stacked), two cross level analysis (stacked) + * Parse a view but allows changing dynamically the presentation + * Graph Types: pie, lines, areas, bars, radar + * Stacked/Not Stacked for areas and bars + * Legends: top, inside (top/left), hidden + * Features: download as PNG or CSV, browse data grid, switch orientation + * Unlimited "Group By" levels (not stacked), two cross level analysis (stacked) """, "version": "3.0", "depends": ['web'], diff --git a/addons/web_hello/__openerp__.py b/addons/web_hello/__openerp__.py index b7e17a520a8..0c733fb6b64 100644 --- a/addons/web_hello/__openerp__.py +++ b/addons/web_hello/__openerp__.py @@ -1,10 +1,7 @@ { "name": "Hello", "category": "Hidden", - "description": - """ - OpenERP Web example module. - """, + "description":"""OpenERP Web example module.""", "version": "2.0", "depends": [], "js": ["static/*/*.js", "static/*/js/*.js"], diff --git a/addons/web_kanban/__openerp__.py b/addons/web_kanban/__openerp__.py index 8a769342461..d283acfe91f 100644 --- a/addons/web_kanban/__openerp__.py +++ b/addons/web_kanban/__openerp__.py @@ -1,10 +1,7 @@ { "name" : "Base Kanban", "category": "Hidden", - "description": - """ - OpenERP Web kanban view. - """, + "description":"""OpenERP Web kanban view.""", "version" : "2.0", "depends" : ["web"], "js": [ diff --git a/addons/web_kanban/static/src/css/kanban.css b/addons/web_kanban/static/src/css/kanban.css index 246475a2b7f..695de3d5e28 100644 --- a/addons/web_kanban/static/src/css/kanban.css +++ b/addons/web_kanban/static/src/css/kanban.css @@ -39,6 +39,9 @@ .openerp .oe_kanban_view .oe_kanban_groups { height: inherit; } +.openerp .oe_kanban_view.oe_kanban_ungrouped .oe_kanban_groups { + width: 100%; +} .openerp .oe_kanban_view .oe_kanban_header:hover .oe_dropdown_kanban { display: inline-block; } @@ -64,7 +67,7 @@ .openerp .oe_kanban_view .oe_kanban_group_header.oe_kanban_no_group { padding: 0px; } -.openerp .oe_kanban_view .oe_kanban_column.oe_kanban_grouped, .openerp .oe_kanban_view .oe_kanban_group_header { +.openerp .oe_kanban_view.oe_kanban_grouped .oe_kanban_column, .openerp .oe_kanban_view .oe_kanban_group_header { background: #f0eeee; border-left: 1px solid #f0f8f8; border-right: 1px solid #b9b9b9; @@ -188,7 +191,7 @@ font-weight: bold; margin: 2px 4px; } -.openerp .oe_kanban_view .oe_kanban_grouped .oe_kanban_record { +.openerp .oe_kanban_view.oe_kanban_grouped .oe_kanban_record { margin-bottom: 6px; } .openerp .oe_kanban_view .oe_kanban_gravatar { @@ -239,13 +242,13 @@ clear: both; text-align: center; } -.openerp .oe_kanban_view .oe_kanban_grouped .oe_kanban_show_more .oe_button { +.openerp .oe_kanban_view.oe_kanban_grouped .oe_kanban_show_more .oe_button { width: 100%; } -.openerp .oe_kanban_view .oe_kanban_ungrouped { +.openerp .oe_kanban_view.oe_kanban_ungrouped .oe_kanban_column { background: white; } -.openerp .oe_kanban_view .oe_kanban_ungrouped .oe_kanban_record { +.openerp .oe_kanban_view.oe_kanban_ungrouped .oe_kanban_column .oe_kanban_record { float: left; padding: 2px; box-sizing: border-box; diff --git a/addons/web_kanban/static/src/css/kanban.sass b/addons/web_kanban/static/src/css/kanban.sass index ae387244024..6e5526c06d5 100644 --- a/addons/web_kanban/static/src/css/kanban.sass +++ b/addons/web_kanban/static/src/css/kanban.sass @@ -63,6 +63,8 @@ // KanbanGroups {{{ .oe_kanban_groups height: inherit + &.oe_kanban_ungrouped .oe_kanban_groups + width: 100% .oe_kanban_header &:hover .oe_dropdown_kanban @@ -86,7 +88,7 @@ .oe_kanban_group_header.oe_kanban_no_group padding: 0px - .oe_kanban_column.oe_kanban_grouped, .oe_kanban_group_header + &.oe_kanban_grouped .oe_kanban_column, .oe_kanban_group_header background: #f0eeee border-left: 1px solid #f0f8f8 border-right: 1px solid #b9b9b9 @@ -190,7 +192,7 @@ .oe_kanban_title font-weight: bold margin: 2px 4px - .oe_kanban_grouped .oe_kanban_record + &.oe_kanban_grouped .oe_kanban_record margin-bottom: 6px .oe_kanban_gravatar display: block @@ -225,9 +227,9 @@ .oe_kanban_show_more clear: both text-align: center - .oe_kanban_grouped .oe_kanban_show_more .oe_button + &.oe_kanban_grouped .oe_kanban_show_more .oe_button width: 100% - .oe_kanban_ungrouped + &.oe_kanban_ungrouped .oe_kanban_column background: white .oe_kanban_record float: left diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js index 568e6890522..735039b01e1 100644 --- a/addons/web_kanban/static/src/js/kanban.js +++ b/addons/web_kanban/static/src/js/kanban.js @@ -174,6 +174,7 @@ instance.web_kanban.KanbanView = instance.web.View.extend({ }, do_process_groups: function(groups) { var self = this; + this.$element.remove('oe_kanban_ungrouped').addClass('oe_kanban_grouped'); this.add_group_mutex.exec(function() { self.do_clear_groups(); self.dataset.ids = []; @@ -195,6 +196,7 @@ instance.web_kanban.KanbanView = instance.web.View.extend({ }, do_process_dataset: function(dataset) { var self = this; + this.$element.remove('oe_kanban_grouped').addClass('oe_kanban_ungrouped'); this.add_group_mutex.exec(function() { var def = $.Deferred(); self.do_clear_groups(); @@ -296,10 +298,10 @@ instance.web_kanban.KanbanView = instance.web.View.extend({ if (!group.state.folded) { if (182*unfolded>=self.$element.width()) { group.$element.css('width', "170px"); - } else if (262*unfolded>self.$element.width()) { - group.$element.css('width', Math.round(100/unfolded) + '%'); - } else { + } else if (262*unfolded - +
diff --git a/addons/web_mobile/__openerp__.py b/addons/web_mobile/__openerp__.py index cd5a4836a5a..614315a8a83 100644 --- a/addons/web_mobile/__openerp__.py +++ b/addons/web_mobile/__openerp__.py @@ -1,10 +1,7 @@ { "name" : "OpenERP Web Mobile", "category": "Hidden", - "description": - """ - OpenERP Web Mobile. - """, + "description":"""OpenERP Web Mobile.""", "version" : "2.0", "depends" : [], 'auto_install': True, diff --git a/addons/web_mobile/static/src/js/chrome_mobile.js b/addons/web_mobile/static/src/js/chrome_mobile.js index a507e3b5943..029e4bce49b 100644 --- a/addons/web_mobile/static/src/js/chrome_mobile.js +++ b/addons/web_mobile/static/src/js/chrome_mobile.js @@ -48,8 +48,7 @@ instance.web_mobile.Login = instance.web.OldWidget.extend({ jQuery("#oe_header").children().remove(); this.rpc("/web/database/get_list", {}, function(result) { self.db_list = result.db_list; - $('#'+self.element_id).html(self.render(self)); - self.$element = $('#'+self.element_id); + this.setElement($('#'+self.element_id).html(self.render(self))); if(self.session.db!=""){ self.$element.find("#database").val(self.session.db); } diff --git a/addons/web_process/__openerp__.py b/addons/web_process/__openerp__.py index b488ddb95d2..9aea38a0676 100644 --- a/addons/web_process/__openerp__.py +++ b/addons/web_process/__openerp__.py @@ -1,10 +1,7 @@ { "name" : "Process", "version": "2.0", - "description": - """ - OpenERP Web process view. - """, + "description":"""OpenERP Web process view.""", "depends" : ["web_diagram"], "js": [ 'static/lib/dracula/*.js', diff --git a/addons/web_process/static/src/js/process.js b/addons/web_process/static/src/js/process.js index bb32d300be8..60259f26b64 100644 --- a/addons/web_process/static/src/js/process.js +++ b/addons/web_process/static/src/js/process.js @@ -3,12 +3,13 @@ openerp.web_process = function (instance) { _t = instance.web._t; instance.web.ViewManager.include({ start: function() { - this._super(); + var _super = this._super(); this.process_check(); this.process_help = this.action ? this.action.help : 'Help: Not Defined'; this.model = this.dataset.model; if(this.action) this.process_model = this.action.res_model; else this.process_model = this.model; + return _super; }, process_check: function() { var self = this, diff --git a/addons/web_rpc/__openerp__.py b/addons/web_rpc/__openerp__.py index 27ec39f5748..44946d27cb5 100644 --- a/addons/web_rpc/__openerp__.py +++ b/addons/web_rpc/__openerp__.py @@ -1,7 +1,7 @@ { "name" : "OpenERP Web Web", "category" : "Hidden", - "description":'Openerp Web Web', + "description":"""Openerp Web Web.""", "version" : "2.0", "depends" : [], "installable" : False, diff --git a/addons/web_tests/__openerp__.py b/addons/web_tests/__openerp__.py index dc26ecc42ad..b76028b9418 100644 --- a/addons/web_tests/__openerp__.py +++ b/addons/web_tests/__openerp__.py @@ -1,10 +1,7 @@ { "name": "Tests", "category": "Hidden", - "description": - """ - OpenERP Web test suite. - """, + "description":"""OpenERP Web test suite.""", "version": "2.0", "depends": [], "js": ["static/src/js/*.js"], diff --git a/doc/addons.rst b/doc/addons.rst index da1f1f28769..35878b04430 100644 --- a/doc/addons.rst +++ b/doc/addons.rst @@ -113,103 +113,6 @@ initializing the addon. Creating new standard roles --------------------------- -Widget -++++++ - -This is the base class for all visual components. It provides a number of -services for the management of a DOM subtree: - -* Rendering with QWeb - -* Parenting-child relations - -* Life-cycle management (including facilitating children destruction when a - parent object is removed) - -* DOM insertion, via jQuery-powered insertion methods. Insertion targets can - be anything the corresponding jQuery method accepts (generally selectors, - DOM nodes and jQuery objects): - - :js:func:`~openerp.base.Widget.appendTo` - Renders the widget and inserts it as the last child of the target, uses - `.appendTo()`_ - - :js:func:`~openerp.base.Widget.prependTo` - Renders the widget and inserts it as the first child of the target, uses - `.prependTo()`_ - - :js:func:`~openerp.base.Widget.insertAfter` - Renders the widget and inserts it as the preceding sibling of the target, - uses `.insertAfter()`_ - - :js:func:`~openerp.base.Widget.insertBefore` - Renders the widget and inserts it as the following sibling of the target, - uses `.insertBefore()`_ - -:js:class:`~openerp.base.Widget` inherits from -:js:class:`~openerp.base.SessionAware`, so subclasses can easily access the -RPC layers. - -Subclassing Widget -~~~~~~~~~~~~~~~~~~ - -:js:class:`~openerp.base.Widget` is subclassed in the standard manner (via the -:js:func:`~openerp.base.Class.extend` method), and provides a number of -abstract properties and concrete methods (which you may or may not want to -override). Creating a subclass looks like this: - -.. code-block:: javascript - - var MyWidget = openerp.base.Widget.extend({ - // QWeb template to use when rendering the object - template: "MyQWebTemplate", - - init: function(parent) { - this._super(parent); - // insert code to execute before rendering, for object - // initialization - }, - start: function() { - this._super(); - // post-rendering initialization code, at this point - // ``this.$element`` has been initialized - this.$element.find(".my_button").click(/* an example of event binding * /); - - // if ``start`` is asynchronous, return a promise object so callers - // know when the object is done initializing - return this.rpc(/* … */) - } - }); - -The new class can then be used in the following manner: - -.. code-block:: javascript - - // Create the instance - var my_widget = new MyWidget(this); - // Render and insert into DOM - my_widget.appendTo(".some-div"); - -After these two lines have executed (and any promise returned by ``appendTo`` -has been resolved if needed), the widget is ready to be used. - -.. note:: the insertion methods will start the widget themselves, and will - return the result of :js:func:`~openerp.base.Widget.start()`. - - If for some reason you do not want to call these methods, you will - have to first call :js:func:`~openerp.base.Widget.render()` on the - widget, then insert it into your DOM and start it. - -If the widget is not needed anymore (because it's transient), simply terminate -it: - -.. code-block:: javascript - - my_widget.stop(); - -will unbind all DOM events, remove the widget's content from the DOM and -destroy all widget data. - Views +++++ @@ -541,18 +444,6 @@ Python .. _promise object: http://api.jquery.com/deferred.promise/ -.. _.appendTo(): - http://api.jquery.com/appendTo/ - -.. _.prependTo(): - http://api.jquery.com/prependTo/ - -.. _.insertAfter(): - http://api.jquery.com/insertAfter/ - -.. _.insertBefore(): - http://api.jquery.com/insertBefore/ - .. _Rosetta: .. _Launchpad's own translation tool: https://help.launchpad.net/Translations diff --git a/doc/index.rst b/doc/index.rst index 8608af59d3d..0c98815797b 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -16,6 +16,7 @@ Contents: async rpc + widget search-view list-view diff --git a/doc/widget.rst b/doc/widget.rst new file mode 100644 index 00000000000..ddfe525227d --- /dev/null +++ b/doc/widget.rst @@ -0,0 +1,274 @@ +User Interaction: Widget +======================== + +This is the base class for all visual components. It corresponds to an MVC +view. It provides a number of services to handle a section of a page: + +* Rendering with QWeb + +* Parenting-child relations + +* Life-cycle management (including facilitating children destruction when a + parent object is removed) + +* DOM insertion, via jQuery-powered insertion methods. Insertion targets can + be anything the corresponding jQuery method accepts (generally selectors, + DOM nodes and jQuery objects): + + :js:func:`~openerp.base.Widget.appendTo` + Renders the widget and inserts it as the last child of the target, uses + `.appendTo()`_ + + :js:func:`~openerp.base.Widget.prependTo` + Renders the widget and inserts it as the first child of the target, uses + `.prependTo()`_ + + :js:func:`~openerp.base.Widget.insertAfter` + Renders the widget and inserts it as the preceding sibling of the target, + uses `.insertAfter()`_ + + :js:func:`~openerp.base.Widget.insertBefore` + Renders the widget and inserts it as the following sibling of the target, + uses `.insertBefore()`_ + +* Backbone-compatible shortcuts + +DOM Root +-------- + +A :js:class:`~openerp.web.Widget` is responsible for a section of the +page materialized by the DOM root of the widget. The DOM root is +available via the :js:attr:`~openerp.web.Widget.el` and +:js:attr:`~openerp.web.Widget.$element` attributes, which are +respectively the raw DOM Element and the jQuery wrapper around the DOM +element. + +There are two main ways to define and generate this DOM root: + +.. js:attribute:: openerp.web.Widget.template + + Should be set to the name of a QWeb template (a + :js:class:`String`). If set, the template will be rendered after + the widget has been initialized but before it has been + started. The root element generated by the template will be set as + the DOM root of the widget. + +.. js:attribute:: openerp.web.Widget.tagName + + Used if the widget has no template defined. Defaults to ``div``, + will be used as the tag name to create the DOM element to set as + the widget's DOM root. It is possible to further customize this + generated DOM root with the following attributes: + + .. js:attribute:: openerp.web.Widget.id + + Used to generate an ``id`` attribute on the generated DOM + root. + + .. js:attribute:: openerp.web.Widget.className + + Used to generate a ``class`` attribute on the generated DOM root. + + .. js:attribute:: openerp.web.Widget.attributes + + Mapping (object literal) of attribute names to attribute + values. Each of these k:v pairs will be set as a DOM attribute + on the generated DOM root. + + None of these is used in case a template is specified on the widget. + +The DOM root can also be defined programmatically by overridding + +.. js:function:: openerp.web.Widget.renderElement + + Renders the widget's DOM root and sets it. The default + implementation will render a set template or generate an element + as described above, and will call + :js:func:`~openerp.web.Widget.setElement` on the result. + + Any override to :js:func:`~openerp.web.Widget.renderElement` which + does not call its ``_super`` **must** call + :js:func:`~openerp.web.Widget.setElement` with whatever it + generated or the widget's behavior is undefined.r + + .. note:: + + The default :js:func:`~openerp.web.Widget.renderElement` can + be called repeatedly, it will *replace* the previous DOM root + (using ``replaceWith``). However, this requires that the + widget correctly sets and unsets its events (and children + widgets). Generally, + :js:func:`~openerp.web.Widget.renderElement` should not be + called repeatedly unless the widget advertizes this feature. + +Accessing DOM content +~~~~~~~~~~~~~~~~~~~~~ + +Because a widget is only responsible for the content below its DOM +root, there is a shortcut for selecting sub-sections of a widget's +DOM: + +.. js:function:: openerp.web.Widget.$(selector) + + Applies the CSS selector specified as parameter to the widget's + DOM root. + + .. code-block:: javascript + + this.$(selector); + + is functionally identical to: + + .. code-block:: javascript + + this.$element.find(selector); + + :param String selector: CSS selector + :returns: jQuery object + + .. note:: this helper method is compatible with + ``Backbone.View.$`` + +Resetting the DOM root +~~~~~~~~~~~~~~~~~~~~~~ + +.. js:function:: openerp.web.Widget.setElement(element) + + Re-sets the widget's DOM root to the provided element, also + handles re-setting the various aliases of the DOM root as well as + unsetting and re-setting delegated events. + + :param Element element: a DOM element or jQuery object to set as + the widget's DOM root + + .. note:: should be mostly compatible with `Backbone's + setElement`_ + +DOM events handling +------------------- + +A widget will generally need to respond to user action within its +section of the page. This entails binding events to DOM elements. + +To this end, :js:class:`~openerp.web.Widget` provides an shortcut: + +.. js:attribute:: openerp.web.Widget.events + + Events are a mapping of ``event selector`` (an event name and a + CSS selector separated by a space) to a callback. The callback can + be either a method name in the widget or a function. In either + case, the ``this`` will be set to the widget. + + The selector is used for jQuery's `event delegation`_, the + callback will only be triggered for descendants of the DOM root + matching the selector [0]_. If the selector is left out (only an + event name is specified), the event will be set directly on the + widget's DOM root. + +.. js:function:: openerp.web.Widget.delegateEvents + + This method is in charge of binding + :js:attr:`~openerp.web.Widget.events` to the DOM. It is + automatically called after setting the widget's DOM root. + + It can be overridden to set up more complex events than the + :js:attr:`~openerp.web.Widget.events` map allows, but the parent + should always be called (or :js:attr:`~openerp.web.Widget.events` + won't be handled correctly). + +.. js:function:: openerp.web.Widget.undelegateEvents + + This method is in charge of unbinding + :js:attr:`~openerp.web.Widget.events` from the DOM root when the + widget is destroyed or the DOM root is reset, in order to avoid + leaving "phantom" events. + + It should be overridden to un-set any event set in an override of + :js:func:`~openerp.web.Widget.delegateEvents`. + +.. note:: this behavior should be compatible with `Backbone's + delegateEvents`_, apart from not accepting any argument. + +Subclassing Widget +------------------ + +:js:class:`~openerp.base.Widget` is subclassed in the standard manner (via the +:js:func:`~openerp.base.Class.extend` method), and provides a number of +abstract properties and concrete methods (which you may or may not want to +override). Creating a subclass looks like this: + +.. code-block:: javascript + + var MyWidget = openerp.base.Widget.extend({ + // QWeb template to use when rendering the object + template: "MyQWebTemplate", + + init: function(parent) { + this._super(parent); + // insert code to execute before rendering, for object + // initialization + }, + start: function() { + this._super(); + // post-rendering initialization code, at this point + // ``this.$element`` has been initialized + this.$element.find(".my_button").click(/* an example of event binding * /); + + // if ``start`` is asynchronous, return a promise object so callers + // know when the object is done initializing + return this.rpc(/* … */) + } + }); + +The new class can then be used in the following manner: + +.. code-block:: javascript + + // Create the instance + var my_widget = new MyWidget(this); + // Render and insert into DOM + my_widget.appendTo(".some-div"); + +After these two lines have executed (and any promise returned by ``appendTo`` +has been resolved if needed), the widget is ready to be used. + +.. note:: the insertion methods will start the widget themselves, and will + return the result of :js:func:`~openerp.base.Widget.start()`. + + If for some reason you do not want to call these methods, you will + have to first call :js:func:`~openerp.base.Widget.render()` on the + widget, then insert it into your DOM and start it. + +If the widget is not needed anymore (because it's transient), simply terminate +it: + +.. code-block:: javascript + + my_widget.destroy(); + +will unbind all DOM events, remove the widget's content from the DOM and +destroy all widget data. + +.. [0] not all DOM events are compatible with events delegation + +.. _.appendTo(): + http://api.jquery.com/appendTo/ + +.. _.prependTo(): + http://api.jquery.com/prependTo/ + +.. _.insertAfter(): + http://api.jquery.com/insertAfter/ + +.. _.insertBefore(): + http://api.jquery.com/insertBefore/ + +.. _event delegation: + http://api.jquery.com/delegate/ + +.. _Backbone's setElement: + http://backbonejs.org/#View-setElement + +.. _Backbone's delegateEvents: + http://backbonejs.org/#View-delegateEvents +