diff options
| author | Vo Minh Thu <vmt@openerp.com> | 2012-01-16 12:06:31 +0100 |
|---|---|---|
| committer | Vo Minh Thu <vmt@openerp.com> | 2012-01-16 12:06:31 +0100 |
| commit | 3203d264aecd751a8c261cea4f72fa323a3098be (patch) | |
| tree | 93cdf7c89795b809963ef08a68d84d293cf41a7a /addons/web | |
| parent | 1497062ba1dbfd814c69fa4250c4eb6a09a5206a (diff) | |
| parent | 064a50bcde5f1c65a17f4feb42348b5809081085 (diff) | |
[MERGE] merged trunk.
bzr revid: vmt@openerp.com-20120116110631-lrzp6uxsjki7by22
Diffstat (limited to 'addons/web')
30 files changed, 917 insertions, 703 deletions
diff --git a/addons/web/__init__.py b/addons/web/__init__.py index 06e7fa1f0e3..b09c8c213bc 100644 --- a/addons/web/__init__.py +++ b/addons/web/__init__.py @@ -12,11 +12,16 @@ def wsgi_postload(): import openerp import os import tempfile + import getpass _logger.info("embedded mode") o = Options() o.dbfilter = openerp.tools.config['dbfilter'] o.server_wide_modules = openerp.conf.server_wide_modules or ['web'] - o.session_storage = os.path.join(tempfile.gettempdir(), "oe-sessions") + try: + username = getpass.getuser() + except Exception: + username = "unknown" + o.session_storage = os.path.join(tempfile.gettempdir(), "oe-sessions-" + username) o.addons_path = openerp.modules.module.ad_paths o.serve_static = True o.backend = 'local' diff --git a/addons/web/common/http.py b/addons/web/common/http.py index f3ac8d0bcd9..17ccfb63d8a 100644 --- a/addons/web/common/http.py +++ b/addons/web/common/http.py @@ -10,6 +10,7 @@ import os import pprint import sys import threading +import time import traceback import urllib import uuid @@ -331,8 +332,9 @@ def session_context(request, storage_path, session_cookie='sessionid'): if (isinstance(value, session.OpenERPSession) and not value._uid and not value.jsonp_requests + and value._creation_time + (60*5) < time.time() # FIXME do not use a fixed value ): - _logger.info('remove session %s: %r', key, value.jsonp_requests) + _logger.debug('remove session %s', key) del request.session[key] with session_lock: diff --git a/addons/web/common/openerplib/main.py b/addons/web/common/openerplib/main.py index 2cbf9692063..93c2d1da69d 100644 --- a/addons/web/common/openerplib/main.py +++ b/addons/web/common/openerplib/main.py @@ -50,15 +50,6 @@ class Connector(object): __logger = _getChildLogger(_logger, 'connector') - def __init__(self, hostname, port): - """ - Initilize by specifying an hostname and a port. - :param hostname: Host name of the server. - :param port: Port for the connection to the server. - """ - self.hostname = hostname - self.port = port - def get_service(self, service_name): """ Returns a Service instance to allow easy manipulation of one of the services offered by the remote server. @@ -81,8 +72,7 @@ class XmlRPCConnector(Connector): :param hostname: The hostname of the computer holding the instance of OpenERP. :param port: The port used by the OpenERP instance for XMLRPC (default to 8069). """ - Connector.__init__(self, hostname, port) - self.url = 'http://%s:%d/xmlrpc' % (self.hostname, self.port) + self.url = 'http://%s:%d/xmlrpc' % (hostname, port) def send(self, service_name, method, *args): url = '%s/%s' % (self.url, service_name) @@ -97,9 +87,9 @@ class XmlRPCSConnector(XmlRPCConnector): __logger = _getChildLogger(_logger, 'connector.xmlrpcs') - def __init__(self, hostname, port=8071): + def __init__(self, hostname, port=8069): super(XmlRPCSConnector, self).__init__(hostname, port) - self.url = 'https://%s:%d/xmlrpc' % (self.hostname, self.port) + self.url = 'https://%s:%d/xmlrpc' % (hostname, port) class Service(object): """ @@ -294,7 +284,7 @@ def get_connector(hostname=None, protocol="xmlrpc", port="auto"): :param port: The number of the port. Defaults to auto. """ if port == 'auto': - port = 8069 if protocol=="xmlrpc" else 8071 + port = 8069 if protocol == "xmlrpc": return XmlRPCConnector(hostname, port) elif protocol == "xmlrpcs": diff --git a/addons/web/common/release.py b/addons/web/common/release.py index 47570c032bb..fdc30ee33d7 100644 --- a/addons/web/common/release.py +++ b/addons/web/common/release.py @@ -1,5 +1,5 @@ name = 'openerp-web' -version = '6.1.0 alpha' +version = '6.1rc1' description = "Web Client of OpenERP, the Enterprise Management Software" long_description = "OpenERP Web is the web client of the OpenERP, a free enterprise management software" author = "OpenERP SA" diff --git a/addons/web/common/session.py b/addons/web/common/session.py index ac0489b2ff3..ca23a08da4a 100644 --- a/addons/web/common/session.py +++ b/addons/web/common/session.py @@ -29,6 +29,7 @@ class OpenERPSession(object): round-tripped to the client browser. """ def __init__(self): + self._creation_time = time.time() self.config = None self._db = False self._uid = False diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index e8760eca203..3a23556794a 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -18,6 +18,10 @@ from cStringIO import StringIO import babel.messages.pofile import werkzeug.utils +try: + import xlwt +except ImportError: + xlwt = None from .. import common openerpweb = common.http @@ -33,6 +37,9 @@ def concat_xml(file_list): concat: concatenation of file content timestamp: max(os.path.getmtime of file_list) """ + if not file_list: + return '', None + root = None files_timestamp = 0 for fname in file_list: @@ -52,12 +59,15 @@ def concat_xml(file_list): return ElementTree.tostring(root, 'utf-8'), files_timestamp -def concat_files(file_list, reader=None): +def concat_files(file_list, reader=None, intersperse=""): """ Concatenate file content return (concat,timestamp) concat: concatenation of file content, read by `reader` timestamp: max(os.path.getmtime of file_list) """ + if not file_list: + return '', None + if reader is None: def reader(f): with open(f) as fp: @@ -71,7 +81,7 @@ def concat_files(file_list, reader=None): files_timestamp = ftime files_content.append(reader(fname)) - files_concat = "".join(files_content) + files_concat = intersperse.join(files_content) return files_concat,files_timestamp html_template = """<!DOCTYPE html> @@ -176,7 +186,7 @@ class WebClient(openerpweb.Controller): @openerpweb.httprequest def js(self, req, mods=None): files = [f[0] for f in self.manifest_glob(req, mods, 'js')] - content,timestamp = concat_files(files) + content, timestamp = concat_files(files, intersperse=';') # TODO use timestamp to set Last mofified date and E-tag return req.make_response(content, [('Content-Type', 'application/javascript')]) @@ -427,7 +437,7 @@ class Session(openerpweb.Controller): @openerpweb.jsonrequest def modules(self, req): # Compute available candidates module - loadable = openerpweb.addons_manifest.iterkeys() + loadable = openerpweb.addons_manifest loaded = req.config.server_wide_modules candidates = [mod for mod in loadable if mod not in loaded] @@ -1184,23 +1194,40 @@ class Binary(openerpweb.Controller): return open(os.path.join(addons_path, 'web', 'static', 'src', 'img', 'placeholder.png'), 'rb').read() @openerpweb.httprequest - def saveas(self, req, model, id, field, fieldname, **kw): + def saveas(self, req, model, field, id=None, filename_field=None, **kw): + """ Download link for files stored as binary fields. + + If the ``id`` parameter is omitted, fetches the default value for the + binary field (via ``default_get``), otherwise fetches the field for + that precise record. + + :param req: OpenERP request + :type req: :class:`web.common.http.HttpRequest` + :param str model: name of the model to fetch the binary from + :param str field: binary field + :param str id: id of the record from which to fetch the binary + :param str filename_field: field holding the file's name, if any + :returns: :class:`werkzeug.wrappers.Response` + """ Model = req.session.model(model) context = req.session.eval_context(req.context) + fields = [field] + if filename_field: + fields.append(filename_field) if id: - res = Model.read([int(id)], [field, fieldname], context)[0] + res = Model.read([int(id)], fields, context)[0] else: - res = Model.default_get([field, fieldname], context) + res = Model.default_get(fields, context) filecontent = base64.b64decode(res.get(field, '')) if not filecontent: return req.not_found() else: filename = '%s_%s' % (model.replace('.', '_'), id) - if fieldname: - filename = res.get(fieldname, '') or filename + if filename_field: + filename = res.get(filename_field, '') or filename return req.make_response(filecontent, [('Content-Type', 'application/octet-stream'), - ('Content-Disposition', 'attachment; filename=' + filename)]) + ('Content-Disposition', 'attachment; filename="%s"' % filename)]) @openerpweb.httprequest def upload(self, req, callback, ufile): @@ -1240,6 +1267,7 @@ class Binary(openerpweb.Controller): attachment_id = Model.create({ 'name': ufile.filename, 'datas': base64.encodestring(ufile.read()), + 'datas_fname': ufile.filename, 'res_model': model, 'res_id': int(id) }, context) @@ -1290,7 +1318,7 @@ class Export(View): for path, controller in openerpweb.controllers_path.iteritems() if path.startswith(self._cp_path) if hasattr(controller, 'fmt') - ], key=operator.itemgetter(1)) + ], key=operator.itemgetter("label")) def fields_get(self, req, model): Model = req.session.model(model) @@ -1466,7 +1494,7 @@ class Export(View): class CSVExport(Export): _cp_path = '/web/export/csv' - fmt = ('csv', 'CSV') + fmt = {'tag': 'csv', 'label': 'CSV'} @property def content_type(self): @@ -1501,7 +1529,11 @@ class CSVExport(Export): class ExcelExport(Export): _cp_path = '/web/export/xls' - fmt = ('xls', 'Excel') + fmt = { + 'tag': 'xls', + 'label': 'Excel', + 'error': None if xlwt else "XLWT required" + } @property def content_type(self): @@ -1511,8 +1543,6 @@ class ExcelExport(Export): return base + '.xls' def from_data(self, fields, rows): - import xlwt - workbook = xlwt.Workbook() worksheet = workbook.add_sheet('Sheet 1') diff --git a/addons/web/po/ar.po b/addons/web/po/ar.po index dba4f6ffb8b..e52b58ca375 100644 --- a/addons/web/po/ar.po +++ b/addons/web/po/ar.po @@ -8,14 +8,14 @@ msgstr "" "Project-Id-Version: openerp-web\n" "Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" "POT-Creation-Date: 2011-12-20 18:48+0100\n" -"PO-Revision-Date: 2012-01-08 20:20+0000\n" +"PO-Revision-Date: 2012-01-13 21:31+0000\n" "Last-Translator: kifcaliph <Unknown>\n" "Language-Team: Arabic <ar@li.org>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-01-09 05:08+0000\n" -"X-Generator: Launchpad (build 14640)\n" +"X-Launchpad-Export-Date: 2012-01-14 05:40+0000\n" +"X-Generator: Launchpad (build 14664)\n" #: addons/web/static/src/js/chrome.js:162 #: addons/web/static/src/js/chrome.js:175 @@ -727,11 +727,11 @@ msgstr "خيار:" #: addons/web/static/src/xml/base.xml:0 msgid "[" -msgstr "]" +msgstr "[" #: addons/web/static/src/xml/base.xml:0 msgid "]" -msgstr "[" +msgstr "]" #: addons/web/static/src/xml/base.xml:0 msgid "-" diff --git a/addons/web/po/de.po b/addons/web/po/de.po index 18f69022ef8..bfbb97fa7dc 100644 --- a/addons/web/po/de.po +++ b/addons/web/po/de.po @@ -8,14 +8,14 @@ msgstr "" "Project-Id-Version: openerp-web\n" "Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" "POT-Creation-Date: 2011-12-20 18:48+0100\n" -"PO-Revision-Date: 2011-12-26 13:55+0000\n" +"PO-Revision-Date: 2012-01-12 17:29+0000\n" "Last-Translator: Ferdinand @ Camptocamp <Unknown>\n" "Language-Team: German <de@li.org>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-01-04 05:17+0000\n" -"X-Generator: Launchpad (build 14616)\n" +"X-Launchpad-Export-Date: 2012-01-13 05:01+0000\n" +"X-Generator: Launchpad (build 14664)\n" #: addons/web/static/src/js/chrome.js:162 #: addons/web/static/src/js/chrome.js:175 @@ -666,7 +666,7 @@ msgstr "/" #: addons/web/static/src/xml/base.xml:0 msgid "Duplicate" -msgstr "Kopieren" +msgstr "Duplizieren" #: addons/web/static/src/xml/base.xml:0 msgid "Unhandled widget" diff --git a/addons/web/po/pt_BR.po b/addons/web/po/pt_BR.po index 5246b219971..4be0c66826c 100644 --- a/addons/web/po/pt_BR.po +++ b/addons/web/po/pt_BR.po @@ -8,14 +8,15 @@ msgstr "" "Project-Id-Version: openerp-web\n" "Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" "POT-Creation-Date: 2011-12-20 18:48+0100\n" -"PO-Revision-Date: 2011-11-12 18:57+0000\n" -"Last-Translator: Cristiano Gavião <Unknown>\n" +"PO-Revision-Date: 2012-01-15 02:47+0000\n" +"Last-Translator: Renato Lima - http://www.akretion.com " +"<renatonlima@gmail.com>\n" "Language-Team: Brazilian Portuguese <pt_BR@li.org>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-01-04 05:17+0000\n" -"X-Generator: Launchpad (build 14616)\n" +"X-Launchpad-Export-Date: 2012-01-16 05:27+0000\n" +"X-Generator: Launchpad (build 14664)\n" #: addons/web/static/src/js/chrome.js:162 #: addons/web/static/src/js/chrome.js:175 @@ -28,15 +29,15 @@ msgstr "Ok" #: addons/web/static/src/js/chrome.js:668 msgid "About" -msgstr "" +msgstr "Sobre" #: addons/web/static/src/js/chrome.js:748 msgid "Preferences" -msgstr "" +msgstr "Preferências" #: addons/web/static/src/js/chrome.js:752 msgid "Change password" -msgstr "" +msgstr "Alterar Senha" #: addons/web/static/src/js/chrome.js:753 #: addons/web/static/src/js/search.js:235 @@ -53,15 +54,15 @@ msgstr "Cancelar" #: addons/web/static/src/js/view_editor.js:75 #: addons/web/static/src/js/views.js:871 addons/web/static/src/xml/base.xml:0 msgid "Save" -msgstr "" +msgstr "Salvar" #: addons/web/static/src/js/chrome.js:774 addons/web/static/src/xml/base.xml:0 msgid "Change Password" -msgstr "" +msgstr "Alterar Senha" #: addons/web/static/src/js/data_export.js:6 msgid "Export Data" -msgstr "" +msgstr "Exportar Dados" #: addons/web/static/src/js/data_export.js:23 #: addons/web/static/src/js/data_import.js:73 @@ -74,36 +75,36 @@ msgstr "Fechar" #: addons/web/static/src/js/data_export.js:24 msgid "Export To File" -msgstr "" +msgstr "Exportar para Arquivo" #: addons/web/static/src/js/data_import.js:34 msgid "Import Data" -msgstr "" +msgstr "Importar Dados" #: addons/web/static/src/js/data_import.js:74 msgid "Import File" -msgstr "" +msgstr "Importar Arquivo" #: addons/web/static/src/js/data_import.js:109 msgid "External ID" -msgstr "" +msgstr "ID Externo" #: addons/web/static/src/js/search.js:233 msgid "Filter Entry" -msgstr "" +msgstr "Filtrar Entrada" #: addons/web/static/src/js/search.js:238 #: addons/web/static/src/js/search.js:279 msgid "OK" -msgstr "" +msgstr "OK" #: addons/web/static/src/js/search.js:274 addons/web/static/src/xml/base.xml:0 msgid "Add to Dashboard" -msgstr "" +msgstr "Adicionar ao Dashboard" #: addons/web/static/src/js/search.js:403 msgid "Invalid Search" -msgstr "" +msgstr "Pesquisa Inválida" #: addons/web/static/src/js/search.js:403 msgid "triggered from search view" @@ -112,31 +113,31 @@ msgstr "" #: addons/web/static/src/js/search.js:490 #, python-format msgid "Incorrect value for field %(fieldname)s: [%(value)s] is %(message)s" -msgstr "" +msgstr "Valor incorreto para campo %(fieldname)s: [%(value)s] é %(message)s" #: addons/web/static/src/js/search.js:822 msgid "not a valid integer" -msgstr "" +msgstr "Não é um número inteiro" #: addons/web/static/src/js/search.js:836 msgid "not a valid number" -msgstr "" +msgstr "Não é um número válido" #: addons/web/static/src/js/search.js:898 msgid "Yes" -msgstr "" +msgstr "Sim" #: addons/web/static/src/js/search.js:899 msgid "No" -msgstr "" +msgstr "Não" #: addons/web/static/src/js/search.js:1252 msgid "contains" -msgstr "" +msgstr "Contém" #: addons/web/static/src/js/search.js:1253 msgid "doesn't contain" -msgstr "" +msgstr "Não contém" #: addons/web/static/src/js/search.js:1254 #: addons/web/static/src/js/search.js:1269 @@ -144,7 +145,7 @@ msgstr "" #: addons/web/static/src/js/search.js:1309 #: addons/web/static/src/js/search.js:1331 msgid "is equal to" -msgstr "" +msgstr "É igual a" #: addons/web/static/src/js/search.js:1255 #: addons/web/static/src/js/search.js:1270 @@ -152,7 +153,7 @@ msgstr "" #: addons/web/static/src/js/search.js:1310 #: addons/web/static/src/js/search.js:1332 msgid "is not equal to" -msgstr "" +msgstr "É diferente de" #: addons/web/static/src/js/search.js:1256 #: addons/web/static/src/js/search.js:1271 @@ -160,7 +161,7 @@ msgstr "" #: addons/web/static/src/js/search.js:1311 #: addons/web/static/src/js/search.js:1333 msgid "greater than" -msgstr "" +msgstr "Maior que" #: addons/web/static/src/js/search.js:1257 #: addons/web/static/src/js/search.js:1272 @@ -168,7 +169,7 @@ msgstr "" #: addons/web/static/src/js/search.js:1312 #: addons/web/static/src/js/search.js:1334 msgid "less than" -msgstr "" +msgstr "Menor que" #: addons/web/static/src/js/search.js:1258 #: addons/web/static/src/js/search.js:1273 @@ -176,7 +177,7 @@ msgstr "" #: addons/web/static/src/js/search.js:1313 #: addons/web/static/src/js/search.js:1335 msgid "greater or equal than" -msgstr "" +msgstr "Maior ou igual que" #: addons/web/static/src/js/search.js:1259 #: addons/web/static/src/js/search.js:1274 @@ -184,28 +185,28 @@ msgstr "" #: addons/web/static/src/js/search.js:1314 #: addons/web/static/src/js/search.js:1336 msgid "less or equal than" -msgstr "" +msgstr "Menor ou igual que" #: addons/web/static/src/js/search.js:1325 #: addons/web/static/src/js/search.js:1350 msgid "is" -msgstr "" +msgstr "É" #: addons/web/static/src/js/search.js:1351 msgid "is not" -msgstr "" +msgstr "Não é" #: addons/web/static/src/js/search.js:1364 msgid "is true" -msgstr "" +msgstr "Verdadeiro" #: addons/web/static/src/js/search.js:1365 msgid "is false" -msgstr "" +msgstr "Falso" #: addons/web/static/src/js/view_editor.js:42 msgid "ViewEditor" -msgstr "" +msgstr "Editor de Visão" #: addons/web/static/src/js/view_editor.js:46 #: addons/web/static/src/js/view_list.js:17 @@ -216,7 +217,7 @@ msgstr "Criar" #: addons/web/static/src/js/view_editor.js:47 #: addons/web/static/src/xml/base.xml:0 msgid "Edit" -msgstr "" +msgstr "Editar" #: addons/web/static/src/js/view_editor.js:48 #: addons/web/static/src/xml/base.xml:0 @@ -226,38 +227,38 @@ msgstr "Remover" #: addons/web/static/src/js/view_editor.js:71 #, python-format msgid "Create a view (%s)" -msgstr "" +msgstr "Criar a visão (%s)" #: addons/web/static/src/js/view_editor.js:170 msgid "Do you really want to remove this view?" -msgstr "" +msgstr "Deseja remover essa visão?" #: addons/web/static/src/js/view_editor.js:367 #, python-format msgid "View Editor %d - %s" -msgstr "" +msgstr "Editar Visão %d - %s" #: addons/web/static/src/js/view_editor.js:371 msgid "Preview" -msgstr "" +msgstr "Pré-visualizar" #: addons/web/static/src/js/view_editor.js:442 msgid "Do you really want to remove this node?" -msgstr "" +msgstr "Deseja remover esse nó?" #: addons/web/static/src/js/view_editor.js:756 #: addons/web/static/src/js/view_editor.js:883 msgid "Properties" -msgstr "" +msgstr "Propriedades" #: addons/web/static/src/js/view_editor.js:760 #: addons/web/static/src/js/view_editor.js:887 msgid "Update" -msgstr "" +msgstr "Atualizar" #: addons/web/static/src/js/view_form.js:17 msgid "Form" -msgstr "" +msgstr "Formulário" #: addons/web/static/src/js/view_form.js:401 msgid "" @@ -266,16 +267,16 @@ msgstr "Aviso, o registro foi modificado, suas alterações serão descartadas." #: addons/web/static/src/js/view_form.js:612 msgid "Attachments" -msgstr "" +msgstr "Anexos" #: addons/web/static/src/js/view_form.js:650 #, python-format msgid "Do you really want to delete the attachment %s?" -msgstr "" +msgstr "Deseja remover esse anexo %s?" #: addons/web/static/src/js/view_form.js:1075 msgid "Confirm" -msgstr "" +msgstr "Confirmar" #: addons/web/static/src/js/view_form.js:1838 msgid "<em> Search More...</em>" @@ -284,11 +285,11 @@ msgstr "<em> Procurar Mais...</em>" #: addons/web/static/src/js/view_form.js:1851 #, python-format msgid "<em> Create \"<strong>%s</strong>\"</em>" -msgstr "" +msgstr "<em> Criar \"<strong>%s</strong>\"</em>" #: addons/web/static/src/js/view_form.js:1857 msgid "<em> Create and Edit...</em>" -msgstr "" +msgstr "<em> Criar e Editar...</em>" #: addons/web/static/src/js/view_form.js:2404 #: addons/web/static/src/xml/base.xml:0 @@ -297,72 +298,72 @@ msgstr "Adicionar" #: addons/web/static/src/js/view_list.js:8 msgid "List" -msgstr "" +msgstr "Lista" #: addons/web/static/src/js/view_list.js:269 msgid "Unlimited" -msgstr "" +msgstr "Ilimitado" #: addons/web/static/src/js/view_list.js:516 msgid "Do you really want to remove these records?" -msgstr "" +msgstr "Deseja remover esse registro?" #: addons/web/static/src/js/view_list.js:1202 msgid "Undefined" -msgstr "" +msgstr "Indefinida" #: addons/web/static/src/js/view_page.js:8 msgid "Page" -msgstr "" +msgstr "Página" #: addons/web/static/src/js/view_page.js:52 msgid "Do you really want to delete this record?" -msgstr "" +msgstr "Deseja remover esse registro?" #: addons/web/static/src/js/view_page.js:227 msgid "Download" -msgstr "" +msgstr "Download" #: addons/web/static/src/js/view_tree.js:11 msgid "Tree" -msgstr "" +msgstr "Árvore" #: addons/web/static/src/js/views.js:590 msgid "Search: " -msgstr "" +msgstr "Pesquisar: " #: addons/web/static/src/js/views.js:710 msgid "Customize" -msgstr "" +msgstr "Customizar" #: addons/web/static/src/js/views.js:713 msgid "Manage Views" -msgstr "" +msgstr "Administrar Visões" #: addons/web/static/src/js/views.js:715 addons/web/static/src/js/views.js:719 #: addons/web/static/src/js/views.js:724 msgid "Manage views of the current object" -msgstr "" +msgstr "Adminsitrar visões do objeto atual" #: addons/web/static/src/js/views.js:717 msgid "Edit Workflow" -msgstr "" +msgstr "Editar Workflow" #: addons/web/static/src/js/views.js:722 msgid "Customize Object" -msgstr "" +msgstr "Customizar Objeto" #: addons/web/static/src/js/views.js:726 msgid "Translate" -msgstr "" +msgstr "Traduzir" #: addons/web/static/src/js/views.js:728 msgid "Technical translation" -msgstr "" +msgstr "Tradução Técnica" #: addons/web/static/src/js/views.js:733 msgid "Other Options" -msgstr "" +msgstr "Outras Opções" #: addons/web/static/src/js/views.js:736 addons/web/static/src/xml/base.xml:0 msgid "Import" @@ -374,55 +375,55 @@ msgstr "Exportar" #: addons/web/static/src/js/views.js:742 msgid "View Log" -msgstr "" +msgstr "Vizualizar Log" #: addons/web/static/src/js/views.js:751 msgid "Reports" -msgstr "" +msgstr "Relatórios" #: addons/web/static/src/js/views.js:751 msgid "Actions" -msgstr "" +msgstr "Ações" #: addons/web/static/src/js/views.js:751 msgid "Links" -msgstr "" +msgstr "Links" #: addons/web/static/src/js/views.js:831 msgid "You must choose at least one record." -msgstr "" +msgstr "Você deve escolher pelo menos um registro." #: addons/web/static/src/js/views.js:832 msgid "Warning" -msgstr "" +msgstr "Atenção" #: addons/web/static/src/js/views.js:866 msgid "Translations" -msgstr "" +msgstr "Traduções" #: addons/web/static/src/xml/base.xml:0 msgid "x" -msgstr "" +msgstr "x" #: addons/web/static/src/xml/base.xml:0 msgid "#{title}" -msgstr "" +msgstr "#{title}" #: addons/web/static/src/xml/base.xml:0 msgid "#{text}" -msgstr "" +msgstr "#{text}" #: addons/web/static/src/xml/base.xml:0 msgid "Powered by" -msgstr "" +msgstr "Desenvolvido Por" #: addons/web/static/src/xml/base.xml:0 msgid "openerp.com" -msgstr "" +msgstr "openerp.com" #: addons/web/static/src/xml/base.xml:0 msgid "." -msgstr "" +msgstr "." #: addons/web/static/src/xml/base.xml:0 msgid "Loading..." @@ -450,15 +451,15 @@ msgstr "Voltar ao Login" #: addons/web/static/src/xml/base.xml:0 msgid "CREATE DATABASE" -msgstr "" +msgstr "CRIAR BANCO DE DADOS" #: addons/web/static/src/xml/base.xml:0 msgid "Master password:" -msgstr "Senha mestre" +msgstr "Senha Super Admin:" #: addons/web/static/src/xml/base.xml:0 msgid "New database name:" -msgstr "Nome da nova base de dados:" +msgstr "Nome do Novo banco de dados:" #: addons/web/static/src/xml/base.xml:0 msgid "Load Demonstration data:" @@ -470,31 +471,31 @@ msgstr "Idioma padrão:" #: addons/web/static/src/xml/base.xml:0 msgid "Admin password:" -msgstr "Senha de Administrador" +msgstr "Senha do Administrador" #: addons/web/static/src/xml/base.xml:0 msgid "Confirm password:" -msgstr "Confirme a senha:" +msgstr "Confirmar Senha:" #: addons/web/static/src/xml/base.xml:0 msgid "DROP DATABASE" -msgstr "" +msgstr "EXCLUIR BANCO DE DADOS" #: addons/web/static/src/xml/base.xml:0 msgid "Database:" -msgstr "Base de dados:" +msgstr "Banco de Dados:" #: addons/web/static/src/xml/base.xml:0 msgid "Master Password:" -msgstr "Senha Mestre" +msgstr "Senha do Super Admin" #: addons/web/static/src/xml/base.xml:0 msgid "BACKUP DATABASE" -msgstr "" +msgstr "BACKUP" #: addons/web/static/src/xml/base.xml:0 msgid "RESTORE DATABASE" -msgstr "" +msgstr "RESTAURAR BANCO DE DADOS" #: addons/web/static/src/xml/base.xml:0 msgid "File:" @@ -502,15 +503,15 @@ msgstr "Arquivo:" #: addons/web/static/src/xml/base.xml:0 msgid "CHANGE MASTER PASSWORD" -msgstr "" +msgstr "ALTERAR SENHA DO SUPER ADMIN" #: addons/web/static/src/xml/base.xml:0 msgid "New master password:" -msgstr "Nova senha mestre:" +msgstr "Nova Senha do Super Admin:" #: addons/web/static/src/xml/base.xml:0 msgid "Confirm new master password:" -msgstr "Confirme nova senha mestre" +msgstr "Confirme a Senha do Super Admin:" #: addons/web/static/src/xml/base.xml:0 msgid "User:" @@ -522,7 +523,7 @@ msgstr "Senha:" #: addons/web/static/src/xml/base.xml:0 msgid "Database" -msgstr "Base de dados" +msgstr "Bando de Dados" #: addons/web/static/src/xml/base.xml:0 msgid "Login" @@ -542,11 +543,11 @@ msgstr "" #: addons/web/static/src/xml/base.xml:0 msgid "OpenERP's vision to be:" -msgstr "OpenERP vislumbra ser:" +msgstr "Visão do OpenERP" #: addons/web/static/src/xml/base.xml:0 msgid "Full featured" -msgstr "" +msgstr "Todos os recursos" #: addons/web/static/src/xml/base.xml:0 msgid "" @@ -576,14 +577,16 @@ msgstr "Uso Amigável" msgid "" "In order to be productive, people need clean and easy to use interface." msgstr "" +"Para serem produtivas, as pessoas precisam de uma interface limpa e fácil de " +"usar." #: addons/web/static/src/xml/base.xml:0 msgid "(" -msgstr "" +msgstr "(" #: addons/web/static/src/xml/base.xml:0 msgid ")" -msgstr "" +msgstr ")" #: addons/web/static/src/xml/base.xml:0 msgid "LOGOUT" @@ -591,35 +594,35 @@ msgstr "DESCONECTAR" #: addons/web/static/src/xml/base.xml:0 msgid "«" -msgstr "" +msgstr "«" #: addons/web/static/src/xml/base.xml:0 msgid "»" -msgstr "" +msgstr "»" #: addons/web/static/src/xml/base.xml:0 msgid "oe_secondary_menu_item" -msgstr "" +msgstr "oe_secondary_menu_item" #: addons/web/static/src/xml/base.xml:0 msgid "oe_secondary_submenu_item" -msgstr "" +msgstr "oe_secondary_submenu_item" #: addons/web/static/src/xml/base.xml:0 msgid "Hide this tip" -msgstr "Esconder dica" +msgstr "Desativar esta dica" #: addons/web/static/src/xml/base.xml:0 msgid "Disable all tips" -msgstr "Desligar todas as dicas" +msgstr "Desativar todas as dicas" #: addons/web/static/src/xml/base.xml:0 msgid "More…" -msgstr "" +msgstr "Mais..." #: addons/web/static/src/xml/base.xml:0 msgid "Debug View#" -msgstr "" +msgstr "Visão de Depuração#" #: addons/web/static/src/xml/base.xml:0 msgid "- Fields View Get" @@ -627,27 +630,27 @@ msgstr "" #: addons/web/static/src/xml/base.xml:0 msgid "- Edit" -msgstr "" +msgstr "Editar" #: addons/web/static/src/xml/base.xml:0 msgid "View" -msgstr "" +msgstr "Visão" #: addons/web/static/src/xml/base.xml:0 msgid "- Edit SearchView" -msgstr "" +msgstr "- Editar Visão de Busca" #: addons/web/static/src/xml/base.xml:0 msgid "- Edit Action" -msgstr "" +msgstr "- Editar Ação" #: addons/web/static/src/xml/base.xml:0 msgid "Field" -msgstr "" +msgstr "Campo" #: addons/web/static/src/xml/base.xml:0 msgid ":" -msgstr "" +msgstr ":" #: addons/web/static/src/xml/base.xml:0 msgid "Delete" @@ -655,11 +658,11 @@ msgstr "Excluir" #: addons/web/static/src/xml/base.xml:0 msgid "0" -msgstr "" +msgstr "0" #: addons/web/static/src/xml/base.xml:0 msgid "/" -msgstr "" +msgstr "/" #: addons/web/static/src/xml/base.xml:0 msgid "Duplicate" @@ -675,75 +678,75 @@ msgstr "" #: addons/web/static/src/xml/base.xml:0 msgid "\"" -msgstr "" +msgstr "\"" #: addons/web/static/src/xml/base.xml:0 msgid "Modifiers:" -msgstr "" +msgstr "Modificadores:" #: addons/web/static/src/xml/base.xml:0 msgid "?" -msgstr "" +msgstr "?" #: addons/web/static/src/xml/base.xml:0 msgid "(nolabel)" -msgstr "" +msgstr "(nolabel)" #: addons/web/static/src/xml/base.xml:0 msgid "Field:" -msgstr "" +msgstr "Campo:" #: addons/web/static/src/xml/base.xml:0 msgid "Object:" -msgstr "" +msgstr "Objeto:" #: addons/web/static/src/xml/base.xml:0 msgid "Type:" -msgstr "" +msgstr "Tipo:" #: addons/web/static/src/xml/base.xml:0 msgid "Widget:" -msgstr "" +msgstr "Componente:" #: addons/web/static/src/xml/base.xml:0 msgid "Size:" -msgstr "" +msgstr "Tamanho:" #: addons/web/static/src/xml/base.xml:0 msgid "Context:" -msgstr "" +msgstr "Contexto:" #: addons/web/static/src/xml/base.xml:0 msgid "Domain:" -msgstr "" +msgstr "Domínio:" #: addons/web/static/src/xml/base.xml:0 msgid "On change:" -msgstr "" +msgstr "Ao alterar:" #: addons/web/static/src/xml/base.xml:0 msgid "Relation:" -msgstr "" +msgstr "Relação:" #: addons/web/static/src/xml/base.xml:0 msgid "Selection:" -msgstr "" +msgstr "Seleção:" #: addons/web/static/src/xml/base.xml:0 msgid "[" -msgstr "" +msgstr "[" #: addons/web/static/src/xml/base.xml:0 msgid "]" -msgstr "" +msgstr "]" #: addons/web/static/src/xml/base.xml:0 msgid "-" -msgstr "" +msgstr "-" #: addons/web/static/src/xml/base.xml:0 msgid "#" -msgstr "" +msgstr "#" #: addons/web/static/src/xml/base.xml:0 msgid "Open..." @@ -763,7 +766,7 @@ msgstr "..." #: addons/web/static/src/xml/base.xml:0 msgid "Uploading ..." -msgstr "Transferindo ..." +msgstr "Tranferindo..." #: addons/web/static/src/xml/base.xml:0 msgid "Select" @@ -771,7 +774,7 @@ msgstr "Selecionar" #: addons/web/static/src/xml/base.xml:0 msgid "Save As" -msgstr "Salvar como" +msgstr "Salvar Como" #: addons/web/static/src/xml/base.xml:0 msgid "Clear" @@ -779,7 +782,7 @@ msgstr "Limpar" #: addons/web/static/src/xml/base.xml:0 msgid "Button" -msgstr "" +msgstr "Botão" #: addons/web/static/src/xml/base.xml:0 msgid "(no string)" @@ -787,23 +790,23 @@ msgstr "" #: addons/web/static/src/xml/base.xml:0 msgid "Special:" -msgstr "" +msgstr "Especial:" #: addons/web/static/src/xml/base.xml:0 msgid "Button Type:" -msgstr "" +msgstr "Tipo do Botão:" #: addons/web/static/src/xml/base.xml:0 msgid "Method:" -msgstr "" +msgstr "Método:" #: addons/web/static/src/xml/base.xml:0 msgid "Action ID:" -msgstr "" +msgstr "ID da Ação:" #: addons/web/static/src/xml/base.xml:0 msgid "Search" -msgstr "" +msgstr "Pesquisar" #: addons/web/static/src/xml/base.xml:0 msgid "Advanced Filter" @@ -811,7 +814,7 @@ msgstr "Filtro Avançado" #: addons/web/static/src/xml/base.xml:0 msgid "Save Filter" -msgstr "Salvar filtro" +msgstr "Salvar Filtro" #: addons/web/static/src/xml/base.xml:0 msgid "Manage Filters" @@ -827,15 +830,15 @@ msgstr "(Qualquer filtro existente com o mesmo nome será substituído)" #: addons/web/static/src/xml/base.xml:0 msgid "Select Dashboard to add this filter to:" -msgstr "" +msgstr "Selecione o Dashboard para adicionar esse filtro para:" #: addons/web/static/src/xml/base.xml:0 msgid "Title of new Dashboard item:" -msgstr "" +msgstr "Título do novo Dashboard:" #: addons/web/static/src/xml/base.xml:0 msgid "Advanced Filters" -msgstr "" +msgstr "Filtros Avançados" #: addons/web/static/src/xml/base.xml:0 msgid "Any of the following conditions must match" @@ -919,19 +922,19 @@ msgstr "Salvar como:" #: addons/web/static/src/xml/base.xml:0 msgid "Saved exports:" -msgstr "" +msgstr "Exportações salvas" #: addons/web/static/src/xml/base.xml:0 msgid "Old Password:" -msgstr "Senha antiga" +msgstr "Senha Anterior" #: addons/web/static/src/xml/base.xml:0 msgid "New Password:" -msgstr "Nova Senha:" +msgstr "Nova Senha" #: addons/web/static/src/xml/base.xml:0 msgid "Confirm Password:" -msgstr "" +msgstr "Confirmar Senha:" #: addons/web/static/src/xml/base.xml:0 msgid "1. Import a .CSV file" @@ -945,7 +948,7 @@ msgstr "" #: addons/web/static/src/xml/base.xml:0 msgid "CSV File:" -msgstr "Arquivo CSV." +msgstr "Arquivo CSV:" #: addons/web/static/src/xml/base.xml:0 msgid "2. Check your file format" @@ -953,11 +956,11 @@ msgstr "Verifique seu formato de arquivo" #: addons/web/static/src/xml/base.xml:0 msgid "Import Options" -msgstr "" +msgstr "Opções de importação" #: addons/web/static/src/xml/base.xml:0 msgid "Does your file have titles?" -msgstr "" +msgstr "O seu arquivo tem títulos?" #: addons/web/static/src/xml/base.xml:0 msgid "Separator:" @@ -985,11 +988,11 @@ msgstr "Linhas para ignorar" #: addons/web/static/src/xml/base.xml:0 msgid "The import failed due to:" -msgstr "" +msgstr "A importação falhou devido a:" #: addons/web/static/src/xml/base.xml:0 msgid "Here is a preview of the file we could not import:" -msgstr "" +msgstr "Aqui está uma visualização do arquivo que não pode ser importado:" #: addons/web/static/src/xml/base.xml:0 msgid "OpenERP Web" @@ -1001,7 +1004,7 @@ msgstr "Versão" #: addons/web/static/src/xml/base.xml:0 msgid "Copyright © 2011-TODAY OpenERP SA. All Rights Reserved." -msgstr "" +msgstr "Copyright © 2011-TODAY OpenERP SA. Todos Direitos Reservados." #: addons/web/static/src/xml/base.xml:0 msgid "OpenERP is a trademark of the" @@ -1009,7 +1012,7 @@ msgstr "OpenERP é uma marca de" #: addons/web/static/src/xml/base.xml:0 msgid "OpenERP SA Company" -msgstr "" +msgstr "Empresa OpenERP SA" #: addons/web/static/src/xml/base.xml:0 msgid "Licenced under the terms of" @@ -1017,7 +1020,7 @@ msgstr "Licenciado sobre os termos de" #: addons/web/static/src/xml/base.xml:0 msgid "GNU Affero General Public License" -msgstr "" +msgstr "Licença Pública Geral GNU Affero" #: addons/web/static/src/xml/base.xml:0 msgid "About OpenERP" @@ -1054,3 +1057,5 @@ msgid "" "Depending on your needs, OpenERP is available through a web or application " "client." msgstr "" +"Dependendo de suas necessidades, OpenERP está disponível através da web ou " +"de uma aplicação cliente." diff --git a/addons/web/po/ru.po b/addons/web/po/ru.po index beecb0203c1..f218d259876 100644 --- a/addons/web/po/ru.po +++ b/addons/web/po/ru.po @@ -8,14 +8,14 @@ msgstr "" "Project-Id-Version: openerp-web\n" "Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" "POT-Creation-Date: 2011-12-20 18:48+0100\n" -"PO-Revision-Date: 2011-12-06 08:05+0000\n" -"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" +"PO-Revision-Date: 2012-01-13 12:50+0000\n" +"Last-Translator: Aleksei Motsik <Unknown>\n" "Language-Team: Russian <ru@li.org>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-01-04 05:17+0000\n" -"X-Generator: Launchpad (build 14616)\n" +"X-Launchpad-Export-Date: 2012-01-14 05:40+0000\n" +"X-Generator: Launchpad (build 14664)\n" #: addons/web/static/src/js/chrome.js:162 #: addons/web/static/src/js/chrome.js:175 @@ -24,19 +24,19 @@ msgstr "" #: addons/web/static/src/js/view_form.js:1078 #: addons/web/static/src/xml/base.xml:0 msgid "Ok" -msgstr "" +msgstr "Ок" #: addons/web/static/src/js/chrome.js:668 msgid "About" -msgstr "" +msgstr "О программе" #: addons/web/static/src/js/chrome.js:748 msgid "Preferences" -msgstr "" +msgstr "Настройки" #: addons/web/static/src/js/chrome.js:752 msgid "Change password" -msgstr "" +msgstr "Изменить пароль" #: addons/web/static/src/js/chrome.js:753 #: addons/web/static/src/js/search.js:235 @@ -47,21 +47,21 @@ msgstr "" #: addons/web/static/src/js/view_form.js:1085 #: addons/web/static/src/xml/base.xml:0 msgid "Cancel" -msgstr "" +msgstr "Отмена" #: addons/web/static/src/js/chrome.js:754 #: addons/web/static/src/js/view_editor.js:75 #: addons/web/static/src/js/views.js:871 addons/web/static/src/xml/base.xml:0 msgid "Save" -msgstr "" +msgstr "Сохранить" #: addons/web/static/src/js/chrome.js:774 addons/web/static/src/xml/base.xml:0 msgid "Change Password" -msgstr "" +msgstr "Изменить пароль" #: addons/web/static/src/js/data_export.js:6 msgid "Export Data" -msgstr "" +msgstr "Экспорт данных" #: addons/web/static/src/js/data_export.js:23 #: addons/web/static/src/js/data_import.js:73 @@ -70,40 +70,40 @@ msgstr "" #: addons/web/static/src/js/view_form.js:2775 #: addons/web/static/src/js/views.js:872 msgid "Close" -msgstr "" +msgstr "Закрыть" #: addons/web/static/src/js/data_export.js:24 msgid "Export To File" -msgstr "" +msgstr "Экспортировать в файл" #: addons/web/static/src/js/data_import.js:34 msgid "Import Data" -msgstr "" +msgstr "Импорт данных" #: addons/web/static/src/js/data_import.js:74 msgid "Import File" -msgstr "" +msgstr "Импортировать файл" #: addons/web/static/src/js/data_import.js:109 msgid "External ID" -msgstr "" +msgstr "Внешний идентификатор" #: addons/web/static/src/js/search.js:233 msgid "Filter Entry" -msgstr "" +msgstr "Входящий фильтр" #: addons/web/static/src/js/search.js:238 #: addons/web/static/src/js/search.js:279 msgid "OK" -msgstr "" +msgstr "OK" #: addons/web/static/src/js/search.js:274 addons/web/static/src/xml/base.xml:0 msgid "Add to Dashboard" -msgstr "" +msgstr "Добавить на Панель" #: addons/web/static/src/js/search.js:403 msgid "Invalid Search" -msgstr "" +msgstr "Ошибка поиска" #: addons/web/static/src/js/search.js:403 msgid "triggered from search view" @@ -113,30 +113,31 @@ msgstr "" #, python-format msgid "Incorrect value for field %(fieldname)s: [%(value)s] is %(message)s" msgstr "" +"Некорректное значение для поля %(fieldname)s: [%(value)s] в %(message)s" #: addons/web/static/src/js/search.js:822 msgid "not a valid integer" -msgstr "" +msgstr "не является целым числом" #: addons/web/static/src/js/search.js:836 msgid "not a valid number" -msgstr "" +msgstr "не является числом" #: addons/web/static/src/js/search.js:898 msgid "Yes" -msgstr "" +msgstr "Да" #: addons/web/static/src/js/search.js:899 msgid "No" -msgstr "" +msgstr "Нет" #: addons/web/static/src/js/search.js:1252 msgid "contains" -msgstr "" +msgstr "содержит" #: addons/web/static/src/js/search.js:1253 msgid "doesn't contain" -msgstr "" +msgstr "не содержит" #: addons/web/static/src/js/search.js:1254 #: addons/web/static/src/js/search.js:1269 @@ -144,7 +145,7 @@ msgstr "" #: addons/web/static/src/js/search.js:1309 #: addons/web/static/src/js/search.js:1331 msgid "is equal to" -msgstr "" +msgstr "соответствует" #: addons/web/static/src/js/search.js:1255 #: addons/web/static/src/js/search.js:1270 @@ -152,7 +153,7 @@ msgstr "" #: addons/web/static/src/js/search.js:1310 #: addons/web/static/src/js/search.js:1332 msgid "is not equal to" -msgstr "" +msgstr "не соответствует" #: addons/web/static/src/js/search.js:1256 #: addons/web/static/src/js/search.js:1271 @@ -160,7 +161,7 @@ msgstr "" #: addons/web/static/src/js/search.js:1311 #: addons/web/static/src/js/search.js:1333 msgid "greater than" -msgstr "" +msgstr "больше чем" #: addons/web/static/src/js/search.js:1257 #: addons/web/static/src/js/search.js:1272 @@ -168,7 +169,7 @@ msgstr "" #: addons/web/static/src/js/search.js:1312 #: addons/web/static/src/js/search.js:1334 msgid "less than" -msgstr "" +msgstr "меньше чем" #: addons/web/static/src/js/search.js:1258 #: addons/web/static/src/js/search.js:1273 @@ -176,7 +177,7 @@ msgstr "" #: addons/web/static/src/js/search.js:1313 #: addons/web/static/src/js/search.js:1335 msgid "greater or equal than" -msgstr "" +msgstr "больше или равен" #: addons/web/static/src/js/search.js:1259 #: addons/web/static/src/js/search.js:1274 @@ -184,432 +185,437 @@ msgstr "" #: addons/web/static/src/js/search.js:1314 #: addons/web/static/src/js/search.js:1336 msgid "less or equal than" -msgstr "" +msgstr "меньше или равен" #: addons/web/static/src/js/search.js:1325 #: addons/web/static/src/js/search.js:1350 msgid "is" -msgstr "" +msgstr "-" #: addons/web/static/src/js/search.js:1351 msgid "is not" -msgstr "" +msgstr "не" #: addons/web/static/src/js/search.js:1364 msgid "is true" -msgstr "" +msgstr "истинно" #: addons/web/static/src/js/search.js:1365 msgid "is false" -msgstr "" +msgstr "ложно" #: addons/web/static/src/js/view_editor.js:42 msgid "ViewEditor" -msgstr "" +msgstr "Редактор Видов" #: 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:0 msgid "Create" -msgstr "" +msgstr "Создать" #: addons/web/static/src/js/view_editor.js:47 #: addons/web/static/src/xml/base.xml:0 msgid "Edit" -msgstr "" +msgstr "Изменить" #: addons/web/static/src/js/view_editor.js:48 #: addons/web/static/src/xml/base.xml:0 msgid "Remove" -msgstr "" +msgstr "Удалить" #: addons/web/static/src/js/view_editor.js:71 #, python-format msgid "Create a view (%s)" -msgstr "" +msgstr "Создать вид (%s)" #: addons/web/static/src/js/view_editor.js:170 msgid "Do you really want to remove this view?" -msgstr "" +msgstr "Вы действительно хотите удалить этот Вид?" #: addons/web/static/src/js/view_editor.js:367 #, python-format msgid "View Editor %d - %s" -msgstr "" +msgstr "Редактор Вида %d - %s" #: addons/web/static/src/js/view_editor.js:371 msgid "Preview" -msgstr "" +msgstr "Предпросмотр" #: addons/web/static/src/js/view_editor.js:442 msgid "Do you really want to remove this node?" -msgstr "" +msgstr "Вы действительно хотите удалить этот Узел?" #: addons/web/static/src/js/view_editor.js:756 #: addons/web/static/src/js/view_editor.js:883 msgid "Properties" -msgstr "" +msgstr "Свойства" #: addons/web/static/src/js/view_editor.js:760 #: addons/web/static/src/js/view_editor.js:887 msgid "Update" -msgstr "" +msgstr "Обновить" #: addons/web/static/src/js/view_form.js:17 msgid "Form" -msgstr "" +msgstr "Форма" #: addons/web/static/src/js/view_form.js:401 msgid "" "Warning, the record has been modified, your changes will be discarded." -msgstr "" +msgstr "Внимание. Эта запись была изменена. Ваши изменения будут потеряны." #: addons/web/static/src/js/view_form.js:612 msgid "Attachments" -msgstr "" +msgstr "Вложения" #: addons/web/static/src/js/view_form.js:650 #, python-format msgid "Do you really want to delete the attachment %s?" -msgstr "" +msgstr "Вы действительно хотите удалить вложение %s?" #: addons/web/static/src/js/view_form.js:1075 msgid "Confirm" -msgstr "" +msgstr "Подтвердить" #: addons/web/static/src/js/view_form.js:1838 msgid "<em> Search More...</em>" -msgstr "" +msgstr "<em> Найти еще...</em>" #: addons/web/static/src/js/view_form.js:1851 #, python-format msgid "<em> Create \"<strong>%s</strong>\"</em>" -msgstr "" +msgstr "<em> Создать \"<strong>%s</strong>\"</em>" #: addons/web/static/src/js/view_form.js:1857 msgid "<em> Create and Edit...</em>" -msgstr "" +msgstr "<em> Создать и Изменить...</em>" #: addons/web/static/src/js/view_form.js:2404 #: addons/web/static/src/xml/base.xml:0 msgid "Add" -msgstr "" +msgstr "Добавить" #: addons/web/static/src/js/view_list.js:8 msgid "List" -msgstr "" +msgstr "Список" #: addons/web/static/src/js/view_list.js:269 msgid "Unlimited" -msgstr "" +msgstr "Неограниченно" #: addons/web/static/src/js/view_list.js:516 msgid "Do you really want to remove these records?" -msgstr "" +msgstr "Вы действительно хотите удалить эту Запись?" #: addons/web/static/src/js/view_list.js:1202 msgid "Undefined" -msgstr "" +msgstr "Не определено" #: addons/web/static/src/js/view_page.js:8 msgid "Page" -msgstr "" +msgstr "Страница" #: addons/web/static/src/js/view_page.js:52 msgid "Do you really want to delete this record?" -msgstr "" +msgstr "Вы действительно хотите удалить эту Запись?" #: addons/web/static/src/js/view_page.js:227 msgid "Download" -msgstr "" +msgstr "Загрузить" #: addons/web/static/src/js/view_tree.js:11 msgid "Tree" -msgstr "" +msgstr "Дерево" #: addons/web/static/src/js/views.js:590 msgid "Search: " -msgstr "" +msgstr "Найти: " #: addons/web/static/src/js/views.js:710 msgid "Customize" -msgstr "" +msgstr "Настроить" #: addons/web/static/src/js/views.js:713 msgid "Manage Views" -msgstr "" +msgstr "Управление Видами" #: addons/web/static/src/js/views.js:715 addons/web/static/src/js/views.js:719 #: addons/web/static/src/js/views.js:724 msgid "Manage views of the current object" -msgstr "" +msgstr "Управление Видами текущего объекта" #: addons/web/static/src/js/views.js:717 msgid "Edit Workflow" -msgstr "" +msgstr "Редактировать Процесс" #: addons/web/static/src/js/views.js:722 msgid "Customize Object" -msgstr "" +msgstr "Настроить Объект" #: addons/web/static/src/js/views.js:726 msgid "Translate" -msgstr "" +msgstr "Перевести" #: addons/web/static/src/js/views.js:728 msgid "Technical translation" -msgstr "" +msgstr "Технический перевод" #: addons/web/static/src/js/views.js:733 msgid "Other Options" -msgstr "" +msgstr "Прочие Настройки" #: addons/web/static/src/js/views.js:736 addons/web/static/src/xml/base.xml:0 msgid "Import" -msgstr "" +msgstr "Импорт" #: addons/web/static/src/js/views.js:739 addons/web/static/src/xml/base.xml:0 msgid "Export" -msgstr "" +msgstr "Экспорт" #: addons/web/static/src/js/views.js:742 msgid "View Log" -msgstr "" +msgstr "Просмотреть журнал" #: addons/web/static/src/js/views.js:751 msgid "Reports" -msgstr "" +msgstr "Отчёты" #: addons/web/static/src/js/views.js:751 msgid "Actions" -msgstr "" +msgstr "Действия" #: addons/web/static/src/js/views.js:751 msgid "Links" -msgstr "" +msgstr "Связи" #: addons/web/static/src/js/views.js:831 msgid "You must choose at least one record." -msgstr "" +msgstr "Вы должны выбрать хотя-бы одну запись." #: addons/web/static/src/js/views.js:832 msgid "Warning" -msgstr "" +msgstr "Внимание" #: addons/web/static/src/js/views.js:866 msgid "Translations" -msgstr "" +msgstr "Переводы" #: addons/web/static/src/xml/base.xml:0 msgid "x" -msgstr "" +msgstr "x" #: addons/web/static/src/xml/base.xml:0 msgid "#{title}" -msgstr "" +msgstr "#{title}" #: addons/web/static/src/xml/base.xml:0 msgid "#{text}" -msgstr "" +msgstr "#{text}" #: addons/web/static/src/xml/base.xml:0 msgid "Powered by" -msgstr "" +msgstr "На базе" #: addons/web/static/src/xml/base.xml:0 msgid "openerp.com" -msgstr "" +msgstr "openerp.com" #: addons/web/static/src/xml/base.xml:0 msgid "." -msgstr "" +msgstr "." #: addons/web/static/src/xml/base.xml:0 msgid "Loading..." -msgstr "" +msgstr "Загрузка..." #: addons/web/static/src/xml/base.xml:0 msgid "Drop" -msgstr "" +msgstr "Удалить" #: addons/web/static/src/xml/base.xml:0 msgid "Backup" -msgstr "" +msgstr "Создать Резервную Копию" #: addons/web/static/src/xml/base.xml:0 msgid "Restore" -msgstr "" +msgstr "Востановить" #: addons/web/static/src/xml/base.xml:0 msgid "Password" -msgstr "" +msgstr "Пароль" #: addons/web/static/src/xml/base.xml:0 msgid "Back to Login" -msgstr "" +msgstr "Вернутся к Авторизации" #: addons/web/static/src/xml/base.xml:0 msgid "CREATE DATABASE" -msgstr "" +msgstr "СОЗДАТЬ БАЗУ ДАННЫХ" #: addons/web/static/src/xml/base.xml:0 msgid "Master password:" -msgstr "" +msgstr "Мастер пароль:" #: addons/web/static/src/xml/base.xml:0 msgid "New database name:" -msgstr "" +msgstr "Название новой базы данных:" #: addons/web/static/src/xml/base.xml:0 msgid "Load Demonstration data:" -msgstr "" +msgstr "Загрузить Демонстрационные данные:" #: addons/web/static/src/xml/base.xml:0 msgid "Default language:" -msgstr "" +msgstr "Язык по умолчанию:" #: addons/web/static/src/xml/base.xml:0 msgid "Admin password:" -msgstr "" +msgstr "Пароль Администратора:" #: addons/web/static/src/xml/base.xml:0 msgid "Confirm password:" -msgstr "" +msgstr "Подтверждение пароля:" #: addons/web/static/src/xml/base.xml:0 msgid "DROP DATABASE" -msgstr "" +msgstr "УДАЛИТЬ БАЗУ ДАННЫХ" #: addons/web/static/src/xml/base.xml:0 msgid "Database:" -msgstr "" +msgstr "База данных:" #: addons/web/static/src/xml/base.xml:0 msgid "Master Password:" -msgstr "" +msgstr "Мастер Пароль:" #: addons/web/static/src/xml/base.xml:0 msgid "BACKUP DATABASE" -msgstr "" +msgstr "РЕЗЕРВНАЯ КОПИЯ БД" #: addons/web/static/src/xml/base.xml:0 msgid "RESTORE DATABASE" -msgstr "" +msgstr "ВОССТАНОВИТЬ БД" #: addons/web/static/src/xml/base.xml:0 msgid "File:" -msgstr "" +msgstr "Файл:" #: addons/web/static/src/xml/base.xml:0 msgid "CHANGE MASTER PASSWORD" -msgstr "" +msgstr "ИЗМЕНИТЬ МАСТЕР ПАРОЛЬ" #: addons/web/static/src/xml/base.xml:0 msgid "New master password:" -msgstr "" +msgstr "Новый Мастер Пароль:" #: addons/web/static/src/xml/base.xml:0 msgid "Confirm new master password:" -msgstr "" +msgstr "Подтверждение Мастер Пароля:" #: addons/web/static/src/xml/base.xml:0 msgid "User:" -msgstr "" +msgstr "Пользователь:" #: addons/web/static/src/xml/base.xml:0 msgid "Password:" -msgstr "" +msgstr "Пароль:" #: addons/web/static/src/xml/base.xml:0 msgid "Database" -msgstr "" +msgstr "База данных" #: addons/web/static/src/xml/base.xml:0 msgid "Login" -msgstr "" +msgstr "Авторизация" #: addons/web/static/src/xml/base.xml:0 msgid "Bad username or password" -msgstr "" +msgstr "Неверное имя пользователя или пароль" #: addons/web/static/src/xml/base.xml:0 msgid "" "We think that daily job activities can be more intuitive, efficient, " "automated, .. and even fun." msgstr "" +"Мы считаем, что повседневная деятельность может быть более простой, " +"эффективной, автоматизированной... и даже веселой." #: addons/web/static/src/xml/base.xml:0 msgid "OpenERP's vision to be:" -msgstr "" +msgstr "OpenERP's видение" #: addons/web/static/src/xml/base.xml:0 msgid "Full featured" -msgstr "" +msgstr "Полнофункциональный" #: addons/web/static/src/xml/base.xml:0 msgid "" "Today's enterprise challenges are multiple. We provide one module for each " "need." msgstr "" +"Настоящее бросает вызов Предприятиям. Мы предоставляем один модуль для " +"удовлетворения всех потребностей." #: addons/web/static/src/xml/base.xml:0 msgid "Open Source" -msgstr "" +msgstr "Открытое ПО" #: addons/web/static/src/xml/base.xml:0 msgid "" "To Build a great product, we rely on the knowledge of thousands of " "contributors." msgstr "" +"Для создать отличного продукта, мы опирались на знания тысяч участников." #: addons/web/static/src/xml/base.xml:0 msgid "User Friendly" -msgstr "" +msgstr "Дружественный" #: addons/web/static/src/xml/base.xml:0 msgid "" "In order to be productive, people need clean and easy to use interface." -msgstr "" +msgstr "Для продуктивной работы, людям необходим легкий и простой интерфейс." #: addons/web/static/src/xml/base.xml:0 msgid "(" -msgstr "" +msgstr "(" #: addons/web/static/src/xml/base.xml:0 msgid ")" -msgstr "" +msgstr ")" #: addons/web/static/src/xml/base.xml:0 msgid "LOGOUT" -msgstr "" +msgstr "ВЫЙТИ" #: addons/web/static/src/xml/base.xml:0 msgid "«" -msgstr "" +msgstr "«" #: addons/web/static/src/xml/base.xml:0 msgid "»" -msgstr "" +msgstr "»" #: addons/web/static/src/xml/base.xml:0 msgid "oe_secondary_menu_item" -msgstr "" +msgstr "oe_secondary_menu_item" #: addons/web/static/src/xml/base.xml:0 msgid "oe_secondary_submenu_item" -msgstr "" +msgstr "oe_secondary_submenu_item" #: addons/web/static/src/xml/base.xml:0 msgid "Hide this tip" -msgstr "" +msgstr "Скрыть подсказку" #: addons/web/static/src/xml/base.xml:0 msgid "Disable all tips" -msgstr "" +msgstr "Отключить все подсказки" #: addons/web/static/src/xml/base.xml:0 msgid "More…" -msgstr "" +msgstr "Больше..." #: addons/web/static/src/xml/base.xml:0 msgid "Debug View#" diff --git a/addons/web/po/zh_CN.po b/addons/web/po/zh_CN.po index 536a3761360..087c66bd3b3 100644 --- a/addons/web/po/zh_CN.po +++ b/addons/web/po/zh_CN.po @@ -8,14 +8,14 @@ msgstr "" "Project-Id-Version: openerp-web\n" "Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n" "POT-Creation-Date: 2011-12-20 18:48+0100\n" -"PO-Revision-Date: 2012-01-07 15:30+0000\n" +"PO-Revision-Date: 2012-01-12 05:44+0000\n" "Last-Translator: Wei \"oldrev\" Li <oldrev@gmail.com>\n" "Language-Team: Chinese (Simplified) <zh_CN@li.org>\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2012-01-08 05:27+0000\n" -"X-Generator: Launchpad (build 14640)\n" +"X-Launchpad-Export-Date: 2012-01-13 05:01+0000\n" +"X-Generator: Launchpad (build 14664)\n" #: addons/web/static/src/js/chrome.js:162 #: addons/web/static/src/js/chrome.js:175 @@ -358,7 +358,7 @@ msgstr "翻译" #: addons/web/static/src/js/views.js:728 msgid "Technical translation" -msgstr "" +msgstr "技术翻译" #: addons/web/static/src/js/views.js:733 msgid "Other Options" @@ -585,19 +585,19 @@ msgstr "注销" #: addons/web/static/src/xml/base.xml:0 msgid "«" -msgstr "" +msgstr "«" #: addons/web/static/src/xml/base.xml:0 msgid "»" -msgstr "" +msgstr "»" #: addons/web/static/src/xml/base.xml:0 msgid "oe_secondary_menu_item" -msgstr "" +msgstr "oe_secondary_menu_item" #: addons/web/static/src/xml/base.xml:0 msgid "oe_secondary_submenu_item" -msgstr "" +msgstr "oe_secondary_submenu_item" #: addons/web/static/src/xml/base.xml:0 msgid "Hide this tip" @@ -661,7 +661,7 @@ msgstr "复制" #: addons/web/static/src/xml/base.xml:0 msgid "Unhandled widget" -msgstr "" +msgstr "未处理的窗口部件" #: addons/web/static/src/xml/base.xml:0 msgid "Notebook Page \"" @@ -725,19 +725,19 @@ msgstr "" #: addons/web/static/src/xml/base.xml:0 msgid "[" -msgstr "" +msgstr "[" #: addons/web/static/src/xml/base.xml:0 msgid "]" -msgstr "" +msgstr "]" #: addons/web/static/src/xml/base.xml:0 msgid "-" -msgstr "" +msgstr "-" #: addons/web/static/src/xml/base.xml:0 msgid "#" -msgstr "" +msgstr "#" #: addons/web/static/src/xml/base.xml:0 msgid "Open..." @@ -817,7 +817,7 @@ msgstr "过滤器名称:" #: addons/web/static/src/xml/base.xml:0 msgid "(Any existing filter with the same name will be replaced)" -msgstr "" +msgstr "(任何已存在的同名过滤器都会被替换)" #: addons/web/static/src/xml/base.xml:0 msgid "Select Dashboard to add this filter to:" @@ -866,6 +866,8 @@ msgid "" " You can export all data or only the fields that can be " "reimported after modification." msgstr "" +"此向导将导出所有符合当前搜索条件的数据到 CSV 文件。\n" +" 您也可以导出所有数据或选择在修改了 CSV 文件以后还能够重新导入的那些字段。" #: addons/web/static/src/xml/base.xml:0 msgid "Export Type:" @@ -1007,7 +1009,7 @@ msgstr "OpenERP SA 公司" #: addons/web/static/src/xml/base.xml:0 msgid "Licenced under the terms of" -msgstr "" +msgstr "采用的授权协议" #: addons/web/static/src/xml/base.xml:0 msgid "GNU Affero General Public License" diff --git a/addons/web/static/lib/jquery.contextmenu/jquery.contextmenu.r2.packed.js b/addons/web/static/lib/jquery.contextmenu/jquery.contextmenu.r2.packed.js index 2356f3b606b..47f542e5d39 100644 --- a/addons/web/static/lib/jquery.contextmenu/jquery.contextmenu.r2.packed.js +++ b/addons/web/static/lib/jquery.contextmenu/jquery.contextmenu.r2.packed.js @@ -65,8 +65,10 @@ display(index,this,e,options); return false; }; - $(this).bind('contextmenu', callback); - if(options.leftClickToo) { + if (!options.noRightClick) { + $(this).bind('contextmenu', callback); + } + if (options.leftClickToo || options.noRightClick) { $(this).click(callback); } return this @@ -123,4 +125,4 @@ })(jQuery); $( function() { $('div.contextMenu').hide() -});
\ No newline at end of file +}); diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css index df6d78e8aa5..d4bd7a6ff1b 100644 --- a/addons/web/static/src/css/base.css +++ b/addons/web/static/src/css/base.css @@ -437,6 +437,7 @@ label.error { position: absolute; cursor: pointer; border-left: 1px solid #282828; + border-bottom: 1px solid #282828; width: 21px; height: 21px; z-index: 10; @@ -518,6 +519,11 @@ label.error { .openerp a.oe_secondary_submenu_item { padding: 0 5px 2px 10px; } +.openerp a.oe_secondary_submenu_item, +.openerp a.oe_secondary_menu_item { + overflow: hidden; + text-overflow: ellipsis; +} .openerp a.oe_secondary_submenu_item:hover, .openerp a.oe_secondary_submenu_item.leaf.active { display: block; @@ -2033,6 +2039,7 @@ ul.oe-arrow-list li.oe-arrow-list-selected .oe-arrow-list-after { .openerp .oe_vm_switch_form, .openerp .oe_vm_switch_page, +.openerp .oe_vm_switch_tree, .openerp .oe_vm_switch_list, .openerp .oe_vm_switch_graph, .openerp .oe_vm_switch_gantt, @@ -2049,6 +2056,7 @@ ul.oe-arrow-list li.oe-arrow-list-selected .oe-arrow-list-after { .openerp .oe_vm_switch_form span, .openerp .oe_vm_switch_page span, +.openerp .oe_vm_switch_tree span, .openerp .oe_vm_switch_list span, .openerp .oe_vm_switch_graph span, .openerp .oe_vm_switch_gantt span, @@ -2068,6 +2076,16 @@ ul.oe-arrow-list li.oe-arrow-list-selected .oe-arrow-list-after { background-position: 0px -21px; } +.openerp .oe_vm_switch_tree { + background-position: 0px 0px; +} +.openerp .oe_vm_switch_tree:active, +.openerp .oe_vm_switch_tree:hover, +.openerp .oe_vm_switch_tree:focus, +.openerp .oe_vm_switch_tree[disabled="disabled"] { + background-position: 0px -21px; +} + .openerp .oe_vm_switch_form { background-position: -22px 0px; } @@ -2078,6 +2096,15 @@ ul.oe-arrow-list li.oe-arrow-list-selected .oe-arrow-list-after { background-position: -22px -21px; } +.openerp .oe_vm_switch_page { + background-position: -22px 0px; +} +.openerp .oe_vm_switch_page:active, +.openerp .oe_vm_switch_page:hover, +.openerp .oe_vm_switch_page:focus, +.openerp .oe_vm_switch_page[disabled="disabled"] { + background-position: -22px -21px; +} .openerp .oe_vm_switch_graph { background-position: -44px 0px; } diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js index 34b413526f0..39bc0181e16 100644 --- a/addons/web/static/src/js/chrome.js +++ b/addons/web/static/src/js/chrome.js @@ -36,7 +36,7 @@ openerp.web.Notification = openerp.web.Widget.extend(/** @lends openerp.web.Not }); -openerp.web.Dialog = openerp.web.OldWidget.extend(/** @lends openerp.web.Dialog# */{ +openerp.web.Dialog = openerp.web.Widget.extend(/** @lends openerp.web.Dialog# */{ dialog_title: "", identifier_prefix: 'dialog', /** @@ -44,11 +44,14 @@ openerp.web.Dialog = openerp.web.OldWidget.extend(/** @lends openerp.web.Dialog# * @extends openerp.web.OldWidget * * @param parent - * @param dialog_options + * @param options */ - init: function (parent, dialog_options) { + init: function (parent, options, content) { var self = this; this._super(parent); + if (content) { + this.$element = content instanceof $ ? content : $(content); + } this.dialog_options = { modal: true, destroy_on_close: true, @@ -57,10 +60,9 @@ openerp.web.Dialog = openerp.web.OldWidget.extend(/** @lends openerp.web.Dialog# max_width: '95%', height: 'auto', min_height: 0, - max_height: '95%', + max_height: this.get_height('100%') - 140, autoOpen: false, position: [false, 50], - autoResize : 'auto', buttons: {}, beforeClose: function () { self.on_close(); }, resizeStop: this.on_resized @@ -70,31 +72,29 @@ openerp.web.Dialog = openerp.web.OldWidget.extend(/** @lends openerp.web.Dialog# this.dialog_options.buttons[f.substr(10)] = this[f]; } } - if (dialog_options) { - this.set_options(dialog_options); - } - }, - set_options: function(options) { - options = options || {}; - options.width = this.get_width(options.width || this.dialog_options.width); - options.min_width = this.get_width(options.min_width || this.dialog_options.min_width); - options.max_width = this.get_width(options.max_width || this.dialog_options.max_width); - options.height = this.get_height(options.height || this.dialog_options.height); - options.min_height = this.get_height(options.min_height || this.dialog_options.min_height); - options.max_height = this.get_height(options.max_height || this.dialog_options.max_height); - - if (options.width !== 'auto') { - if (options.width > options.max_width) options.width = options.max_width; - if (options.width < options.min_width) options.width = options.min_width; + if (options) { + _.extend(this.dialog_options, options); } - if (options.height !== 'auto') { - if (options.height > options.max_height) options.height = options.max_height; - if (options.height < options.min_height) options.height = options.min_height; + if (this.dialog_options.autoOpen) { + this.open(); + } else { + this.$element.dialog(this.get_options()); } - if (!options.title && this.dialog_title) { - options.title = this.dialog_title; + }, + get_options: function(options) { + var self = this, + o = _.extend({}, this.dialog_options, options || {}); + _.each(['width', 'height'], function(unit) { + o[unit] = self['get_' + unit](o[unit]); + o['min_' + unit] = self['get_' + unit](o['min_' + unit] || 0); + o['max_' + unit] = self['get_' + unit](o['max_' + unit] || 0); + if (o[unit] !== 'auto' && o['min_' + unit] && o[unit] < o['min_' + unit]) o[unit] = o['min_' + unit]; + if (o[unit] !== 'auto' && o['max_' + unit] && o[unit] > o['max_' + unit]) o[unit] = o['max_' + unit]; + }); + if (!o.title && this.dialog_title) { + o.title = this.dialog_title; } - _.extend(this.dialog_options, options); + return o; }, get_width: function(val) { return this.get_size(val.toString(), $(window.top).width()); @@ -111,18 +111,16 @@ openerp.web.Dialog = openerp.web.OldWidget.extend(/** @lends openerp.web.Dialog# return parseInt(val, 10); } }, - start: function () { - this.$element.dialog(this.dialog_options); - this._super(); - return this; - }, - open: function(dialog_options) { + open: function(options) { // TODO fme: bind window on resize if (this.template) { this.$element.html(this.render()); } - this.set_options(dialog_options); - this.$element.dialog(this.dialog_options).dialog('open'); + var o = this.get_options(options); + this.$element.dialog(o).dialog('open'); + if (o.height === 'auto' && o.max_height) { + this.$element.css({ 'max-height': o.max_height, 'overflow-y': 'auto' }); + } return this; }, close: function() { @@ -134,9 +132,7 @@ openerp.web.Dialog = openerp.web.OldWidget.extend(/** @lends openerp.web.Dialog# } }, on_resized: function() { - if (openerp.connection.debug) { - console.log("Dialog resized to %d x %d", this.$element.width(), this.$element.height()); - } + //openerp.log("Dialog resized to %d x %d", this.$element.width(), this.$element.height()); }, stop: function () { // Destroy widget @@ -179,7 +175,7 @@ openerp.web.CrashManager = openerp.web.CallbackEnabled.extend({ var buttons = {}; if (openerp.connection.openerp_entreprise) { buttons[_t("Send OpenERP Enterprise Report")] = function() { - $this = $(this); + var $this = $(this); var issuename = $('#issuename').val(); var explanation = $('#explanation').val(); var remark = $('#remark').val(); @@ -202,13 +198,12 @@ openerp.web.CrashManager = openerp.web.CallbackEnabled.extend({ } var dialog = new openerp.web.Dialog(this, { title: "OpenERP " + _.str.capitalize(this.error.type), - autoOpen: true, width: '80%', height: '50%', min_width: '800px', min_height: '600px', buttons: buttons - }).start(); + }).open(); dialog.$element.html(QWeb.render('CrashManagerError', {session: openerp.connection, error: error})); }, }); @@ -481,7 +476,10 @@ openerp.web.Database = openerp.web.Widget.extend(/** @lends openerp.web.Database error: error[1] }); }, - complete: $.proxy(self, 'unblockUI') + complete: function() { + self.unblockUI(); + self.do_notify(_t("Backed"), _t("Database backed up successfully")); + } }); } }); @@ -517,7 +515,10 @@ openerp.web.Database = openerp.web.Widget.extend(/** @lends openerp.web.Database }) } }, - complete: $.proxy(self, 'unblockUI') + complete: function() { + self.unblockUI(); + self.do_notify(_t("Restored"), _t("Database restored successfully")); + } }); } }); @@ -688,6 +689,11 @@ openerp.web.Header = openerp.web.Widget.extend(/** @lends openerp.web.Header# * var self = this; self.rpc("/web/webclient/version_info", {}).then(function(res) { var $help = $(QWeb.render("About-Page", {version_info: res})); + $help.find('a.oe_activate_debug_mode').click(function (e) { + e.preventDefault(); + window.location = $.param.querystring( + window.location.href, 'debug'); + }); $help.dialog({autoOpen: true, modal: true, width: 960, title: _t("About")}); }); @@ -771,20 +777,20 @@ openerp.web.Header = openerp.web.Widget.extend(/** @lends openerp.web.Header# * title: _t("Preferences"), width: '700px', buttons: [ - {text: _t("Change password"), click: function(){ self.change_password(); }}, {text: _t("Cancel"), click: function(){ $(this).dialog('destroy'); }}, + {text: _t("Change password"), click: function(){ self.change_password(); }}, {text: _t("Save"), click: function(){ var inner_viewmanager = action_manager.inner_viewmanager; inner_viewmanager.views[inner_viewmanager.active_view].controller.do_save() .then(function() { self.dialog.stop(); + // needs to refresh interface in case language changed window.location.reload(); }); } } ] - }); - this.dialog.start().open(); + }).open(); action_manager.appendTo(this.dialog); action_manager.render(this.dialog); }, @@ -794,8 +800,7 @@ openerp.web.Header = openerp.web.Widget.extend(/** @lends openerp.web.Header# * this.dialog = new openerp.web.Dialog(this, { title: _t("Change Password"), width : 'auto' - }); - this.dialog.start().open(); + }).open(); this.dialog.$element.html(QWeb.render("Change_Pwd", self)); this.dialog.$element.find("form[name=change_password_form]").validate({ submitHandler: function (form) { @@ -1102,11 +1107,11 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie self.$table = $(QWeb.render("Interface", {})); self.$element.append(self.$table); self.header = new openerp.web.Header(self); - self.header.on_logout.add(self.on_logout); - self.header.on_action.add(self.on_menu_action); + self.header.on_logout.add(this.proxy('on_logout')); + self.header.on_action.add(this.proxy('on_menu_action')); self.header.appendTo($("#oe_header")); self.menu = new openerp.web.Menu(self, "oe_menu", "oe_secondary_menu"); - self.menu.on_action.add(self.on_menu_action); + self.menu.on_action.add(this.proxy('on_menu_action')); self.menu.start(); }, show_common: function() { @@ -1124,7 +1129,11 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie this.$element.children().remove(); }, do_reload: function() { - return this.session.session_init().pipe(_.bind(function() {this.menu.do_reload();}, this)); + var self = this; + return this.session.session_reload().pipe(function () { + openerp.connection.load_modules(true).pipe( + self.menu.proxy('do_reload')); }); + }, do_notify: function() { var n = this.notification; @@ -1175,7 +1184,7 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie self.menu.on_menu_click(null, action.menu_id); }); } - }, + } }); openerp.web.EmbeddedClient = openerp.web.Widget.extend({ diff --git a/addons/web/static/src/js/core.js b/addons/web/static/src/js/core.js index 819385288a7..b36e576ae47 100644 --- a/addons/web/static/src/js/core.js +++ b/addons/web/static/src/js/core.js @@ -348,6 +348,31 @@ openerp.web.CallbackEnabled = openerp.web.Class.extend(/** @lends openerp.web.Ca } } } + }, + /** + * Proxies a method of the object, in order to keep the right ``this`` on + * method invocations. + * + * This method is similar to ``Function.prototype.bind`` or ``_.bind``, and + * even more so to ``jQuery.proxy`` with a fundamental difference: its + * resolution of the method being called is lazy, meaning it will use the + * method as it is when the proxy is called, not when the proxy is created. + * + * Other methods will fix the bound method to what it is when creating the + * binding/proxy, which is fine in most javascript code but problematic in + * OpenERP Web where developers may want to replace existing callbacks with + * theirs. + * + * The semantics of this precisely replace closing over the method call. + * + * @param {String} method_name name of the method to invoke + * @returns {Function} proxied method + */ + proxy: function (method_name) { + var self = this; + return function () { + return self[method_name].apply(self, arguments); + } } }); @@ -547,7 +572,24 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp. var self = this; // TODO: session store in cookie should be optional this.session_id = this.get_cookie('session_id'); - return this.rpc("/web/session/get_session_info", {}).pipe(function(result) { + return this.session_reload().pipe(function(result) { + var modules = openerp._modules.join(','); + var deferred = self.rpc('/web/webclient/qweblist', {mods: modules}).pipe(self.do_load_qweb); + if(self.session_is_valid()) { + return deferred.pipe(function() { return self.load_modules(); }); + } + return deferred; + }); + }, + /** + * (re)loads the content of a session: db name, username, user id, session + * context and status of the support contract + * + * @returns {$.Deferred} deferred indicating the session is done reloading + */ + session_reload: function () { + var self = this; + return this.rpc("/web/session/get_session_info", {}).then(function(result) { // If immediately follows a login (triggered by trying to restore // an invalid session or no session at all), refresh session data // (should not change, but just in case...) @@ -558,12 +600,6 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp. user_context: result.context, openerp_entreprise: result.openerp_entreprise }); - var modules = openerp._modules.join(','); - var deferred = self.rpc('/web/webclient/qweblist', {mods: modules}).pipe(self.do_load_qweb); - if(self.session_is_valid()) { - return deferred.pipe(function() { self.load_modules(); }); - } - return deferred; }); }, session_is_valid: function() { @@ -640,26 +676,31 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp. }, /** * Load additional web addons of that instance and init them + * + * @param {Boolean} [no_session_valid_signal=false] prevents load_module from triggering ``on_session_valid``. */ - load_modules: function() { + load_modules: function(no_session_valid_signal) { var self = this; return this.rpc('/web/session/modules', {}).pipe(function(result) { - self.module_list = result; var lang = self.user_context.lang; var params = { mods: ["web"].concat(result), lang: lang}; - var modules = self.module_list.join(','); + var to_load = _.difference(result, self.module_list).join(','); + self.module_list = result; return $.when( - self.rpc('/web/webclient/csslist', {mods: modules}, self.do_load_css), - self.rpc('/web/webclient/qweblist', {mods: modules}).pipe(self.do_load_qweb), + self.rpc('/web/webclient/csslist', {mods: to_load}, self.do_load_css), + self.rpc('/web/webclient/qweblist', {mods: to_load}).pipe(self.do_load_qweb), self.rpc('/web/webclient/translations', params).pipe(function(trans) { openerp.web._t.database.set_bundle(trans); var file_list = ["/web/static/lib/datejs/globalization/" + lang.replace("_", "-") + ".js"]; - return self.rpc('/web/webclient/jslist', {mods: modules}).pipe(function(files) { + return self.rpc('/web/webclient/jslist', {mods: to_load}).pipe(function(files) { return self.do_load_js(file_list.concat(files)); }); }) ).then(function() { - self.on_session_valid(); + self.on_modules_loaded(); + if (!no_session_valid_signal) { + self.on_session_valid(); + } }); }); }, @@ -692,7 +733,6 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp. var head = document.head || document.getElementsByTagName('head')[0]; head.appendChild(tag); } else { - self.on_modules_loaded(); d.resolve(); } return d; @@ -702,6 +742,7 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp. _.each(files, function(file) { self.qweb_mutex.exec(function() { return self.rpc('/web/proxy/load', {path: file}).pipe(function(xml) { + if (!xml) { return; } openerp.web.qweb.add_template(_.str.trim(xml)); }); }); diff --git a/addons/web/static/src/js/data.js b/addons/web/static/src/js/data.js index c24325a9327..447b95d17cf 100644 --- a/addons/web/static/src/js/data.js +++ b/addons/web/static/src/js/data.js @@ -293,16 +293,16 @@ openerp.web.DataSet = openerp.web.Widget.extend( /** @lends openerp.web.DataSet * * @param {Array} ids identifiers of the records to read * @param {Array} fields fields to read and return, by default all fields are returned - * @param {Function} callback function called with read result * @returns {$.Deferred} */ - read_ids: function (ids, fields, callback) { + read_ids: function (ids, fields, options) { + var options = options || {}; return this.rpc('/web/dataset/get', { model: this.model, ids: ids, fields: fields, - context: this.get_context() - }, callback); + context: this.get_context(options.context) + }); }, /** * Read a slice of the records represented by this DataSet, based on its @@ -312,26 +312,25 @@ openerp.web.DataSet = openerp.web.Widget.extend( /** @lends openerp.web.DataSet * @params {Object} options * @param {Number} [options.offset=0] The index from which selected records should be returned * @param {Number} [options.limit=null] The maximum number of records to return - * @param {Function} callback function called with read_slice result * @returns {$.Deferred} */ - read_slice: function (fields, options, callback) { - return null; + read_slice: function (fields, options) { + return null; }, /** * Reads the current dataset record (from its index) * * @params {Array} [fields] fields to read and return, by default all fields are returned - * @params {Function} callback function called with read_index result + * @param {Object} [options.context] context data to add to the request payload, on top of the DataSet's own context * @returns {$.Deferred} */ - read_index: function (fields, callback) { - var def = $.Deferred().then(callback); + read_index: function (fields, options) { + var def = $.Deferred(); if (_.isEmpty(this.ids)) { def.reject(); } else { fields = fields || false; - this.read_ids([this.ids[this.index]], fields).then(function(records) { + this.read_ids([this.ids[this.index]], fields, options).then(function(records) { def.resolve(records[0]); }, function() { def.reject.apply(def, arguments); @@ -343,15 +342,16 @@ openerp.web.DataSet = openerp.web.Widget.extend( /** @lends openerp.web.DataSet * Reads default values for the current model * * @param {Array} [fields] fields to get default values for, by default all defaults are read - * @param {Function} callback function called with default_get result + * @param {Object} [options.context] context data to add to the request payload, on top of the DataSet's own context * @returns {$.Deferred} */ - default_get: function(fields, callback) { + default_get: function(fields, options) { + var options = options || {}; return this.rpc('/web/dataset/default_get', { model: this.model, fields: fields, - context: this.get_context() - }, callback); + context: this.get_context(options.context) + }); }, /** * Creates a new record in db @@ -422,7 +422,7 @@ openerp.web.DataSet = openerp.web.Widget.extend( /** @lends openerp.web.DataSet * @param {Number} [domain_index] index of a domain to evaluate in the args array * @param {Number} [context_index] index of a context to evaluate in the args array * @param {Function} callback - * @param {Function }error_callback + * @param {Function} error_callback * @returns {$.Deferred} */ call_and_eval: function (method, args, domain_index, context_index, callback, error_callback) { @@ -503,14 +503,14 @@ openerp.web.DataSetStatic = openerp.web.DataSet.extend({ // all local records this.ids = ids || []; }, - read_slice: function (fields, options, callback) { + read_slice: function (fields, options) { // TODO remove fields from options var self = this, offset = options.offset || 0, limit = options.limit || false, fields = fields || false; var end_pos = limit && limit !== -1 ? offset + limit : this.ids.length; - return this.read_ids(this.ids.slice(offset, end_pos), fields, callback); + return this.read_ids(this.ids.slice(offset, end_pos), fields); }, set_ids: function (ids) { this.ids = ids; @@ -557,10 +557,9 @@ openerp.web.DataSetSearch = openerp.web.DataSet.extend(/** @lends openerp.web.D * @param {Array} [options.domain] domain data to add to the request payload, ANDed with the dataset's domain * @param {Number} [options.offset=0] The index from which selected records should be returned * @param {Number} [options.limit=null] The maximum number of records to return - * @param {Function} callback function called with read_slice result * @returns {$.Deferred} */ - read_slice: function (fields, options, callback) { + read_slice: function (fields, options) { var self = this; var options = options || {}; var offset = options.offset || 0; @@ -576,7 +575,7 @@ openerp.web.DataSetSearch = openerp.web.DataSet.extend(/** @lends openerp.web.D self.ids = result.ids; self.offset = offset; return result.records; - }).then(callback); + }); }, get_domain: function (other_domain) { if (other_domain) { @@ -630,8 +629,8 @@ openerp.web.BufferedDataSet = openerp.web.DataSetStatic.extend({ this.reset_ids([]); this.last_default_get = {}; }, - default_get: function(fields, callback) { - return this._super(fields).then(this.on_default_get).then(callback); + default_get: function(fields, options) { + return this._super(fields, options).then(this.on_default_get); }, on_default_get: function(res) { this.last_default_get = res; @@ -699,7 +698,7 @@ openerp.web.BufferedDataSet = openerp.web.DataSetStatic.extend({ this.delete_all = false; }, on_change: function() {}, - read_ids: function (ids, fields, callback) { + read_ids: function (ids, fields, options) { var self = this; var to_get = []; _.each(ids, function(id) { @@ -713,7 +712,7 @@ openerp.web.BufferedDataSet = openerp.web.DataSetStatic.extend({ to_get.push(id); } }); - var completion = $.Deferred().then(callback); + var completion = $.Deferred(); var return_records = function() { var records = _.map(ids, function(id) { return _.extend({}, _.detect(self.cache, function(c) {return c.id === id;}).values, {"id": id}); @@ -726,7 +725,7 @@ openerp.web.BufferedDataSet = openerp.web.DataSetStatic.extend({ completion.resolve(records); }; if(to_get.length > 0) { - var rpc_promise = this._super(to_get, fields, function(records) { + var rpc_promise = this._super(to_get, fields, options).then(function(records) { _.each(records, function(record, index) { var id = to_get[index]; var cached = _.detect(self.cache, function(x) {return x.id === id;}); @@ -775,8 +774,8 @@ openerp.web.ProxyDataSet = openerp.web.DataSetSearch.extend({ return this._super.apply(this, arguments); } }, - default_get: function(fields, callback) { - return this._super(fields, callback).then(this.on_default_get); + default_get: function(fields, options) { + return this._super(fields, options).then(this.on_default_get); }, on_default_get: function(result) {}, create: function(data, callback, error_callback) { diff --git a/addons/web/static/src/js/data_export.js b/addons/web/static/src/js/data_export.js index 78dc3bef1f9..78be735edc7 100644 --- a/addons/web/static/src/js/data_export.js +++ b/addons/web/static/src/js/data_export.js @@ -63,7 +63,15 @@ openerp.web.DataExport = openerp.web.Dialog.extend({ do_setup_export_formats: function (formats) { var $fmts = this.$element.find('#export_format'); _(formats).each(function (format) { - $fmts.append(new Option(format[1], format[0])); + var opt = new Option(format.label, format.tag); + if (format.error) { + opt.disabled = true; + opt.replaceChild( + document.createTextNode( + _.str.sprintf("%s — %s", format.label, format.error)), + opt.childNodes[0]) + } + $fmts.append(opt); }); }, show_exports_list: function() { @@ -74,7 +82,7 @@ openerp.web.DataExport = openerp.web.Dialog.extend({ } return this.exports.read_slice(['name'], { domain: [['resource', '=', this.dataset.model]] - }, function (export_list) { + }).then(function (export_list) { if (!export_list.length) { return; } @@ -114,7 +122,7 @@ openerp.web.DataExport = openerp.web.Dialog.extend({ if (value) { self.do_save_export_list(value); } else { - alert("Pleae Enter Save Field List Name"); + alert(_t("Please enter save field list name")); } }); } else { @@ -349,24 +357,27 @@ openerp.web.DataExport = openerp.web.Dialog.extend({ export_field.push($(this).val()); }); if (!export_field.length) { - alert('Please select fields to save export list...'); + alert(_t("Please select fields to save export list...")); } return export_field; }, on_click_export_data: function() { - $.blockUI(this.$element); - var exported_fields = [], self = this; - this.$element.find("#fields_list option").each(function() { - var fieldname = self.records[$(this).val()]; - exported_fields.push({name: fieldname, label: $(this).text()}); - }); + var self = this; + var exported_fields = this.$element.find('#fields_list option').map(function () { + var name = self.records[this.value] || this.value + // DOM property is textContent, but IE8 only knows innerText + return {name: this.value, + label: this.textContent || this.innerText}; + }).get(); + if (_.isEmpty(exported_fields)) { - alert('Please select fields to export...'); + alert(_t("Please select fields to export...")); return; } exported_fields.unshift({name: 'id', label: 'External ID'}); var export_format = this.$element.find("#export_format").val(); + $.blockUI(); this.session.get_file({ url: '/web/export/' + export_format, data: {data: JSON.stringify({ @@ -375,7 +386,7 @@ openerp.web.DataExport = openerp.web.Dialog.extend({ ids: this.dataset.ids, domain: this.dataset.domain, import_compat: Boolean( - this.$element.find("#import_compat").val()) + this.$element.find("#import_compat").val()) })}, complete: $.unblockUI }); diff --git a/addons/web/static/src/js/data_import.js b/addons/web/static/src/js/data_import.js index 2b252454869..18870ca2157 100644 --- a/addons/web/static/src/js/data_import.js +++ b/addons/web/static/src/js/data_import.js @@ -83,7 +83,7 @@ openerp.web.DataImport = openerp.web.Dialog.extend({ 'fields_get', [], function (fields) { self.graft_fields(fields); self.ready.push(new openerp.web.DataSet(self, self.model) - .default_get(_.pluck(self.fields, 'id'), function (fields) { + .default_get(_.pluck(self.fields, 'id')).then(function (fields) { _.each(fields, function(val, key) { if (val) { self.fields_with_defaults.push(key); diff --git a/addons/web/static/src/js/formats.js b/addons/web/static/src/js/formats.js index 37d1e4cf7b5..7f220d0aa54 100644 --- a/addons/web/static/src/js/formats.js +++ b/addons/web/static/src/js/formats.js @@ -102,10 +102,6 @@ openerp.web.format_value = function (value, descriptor, value_if_empty) { return _.str.sprintf("%02d:%02d", Math.floor(value), Math.round((value % 1) * 60)); - case 'progressbar': - return _.str.sprintf( - '<progress value="%.2f" max="100.0">%.2f%%</progress>', - value, value); case 'many2one': // name_get value format return value[1]; @@ -237,7 +233,15 @@ openerp.web.auto_date_to_str = function(value, type) { }; /** - * Formats a provided cell based on its field type + * Formats a provided cell based on its field type. Most of the field types + * return a correctly formatted value, but some tags and fields are + * special-cased in their handling: + * + * * buttons will return an actual ``<button>`` tag with a bunch of error handling + * + * * boolean fields will return a checkbox input, potentially disabled + * + * * binary fields will return a link to download the binary data as a file * * @param {Object} row_data record whose values should be displayed in the cell * @param {Object} column column descriptor @@ -245,15 +249,21 @@ openerp.web.auto_date_to_str = function(value, type) { * @param {String} column.type widget type for a field control * @param {String} [column.string] button label * @param {String} [column.icon] button icon - * @param {String} [value_if_empty=''] what to display if the field's value is ``false`` - * @param {Boolean} [process_modifiers=true] should the modifiers be computed ? + * @param {Object} [options] + * @param {String} [options.value_if_empty=''] what to display if the field's value is ``false`` + * @param {Boolean} [options.process_modifiers=true] should the modifiers be computed ? + * @param {String} [options.model] current record's model + * @param {Number} [options.id] current record's id + * */ -openerp.web.format_cell = function (row_data, column, value_if_empty, process_modifiers) { +openerp.web.format_cell = function (row_data, column, options) { + options = options || {}; var attrs = {}; - if (process_modifiers !== false) { + if (options.process_modifiers !== false) { attrs = column.modifiers_for(row_data); } if (attrs.invisible) { return ''; } + if (column.tag === 'button') { return _.template('<button type="button" title="<%-title%>" <%=additional_attributes%> >' + '<img src="<%-prefix%>/web/static/src/img/icons/<%-icon%>.png" alt="<%-alt%>"/>' + @@ -263,14 +273,41 @@ openerp.web.format_cell = function (row_data, column, value_if_empty, process_mo 'disabled="disabled" class="oe-listview-button-disabled"' : '', prefix: openerp.connection.prefix, icon: column.icon, - alt: column.string || '', + alt: column.string || '' }); } if (!row_data[column.id]) { - return value_if_empty === undefined ? '' : value_if_empty; + return options.value_if_empty === undefined ? '' : options.value_if_empty; } - return openerp.web.format_value( - row_data[column.id].value, column, value_if_empty); + + switch (column.widget || column.type) { + case "boolean": + return _.str.sprintf('<input type="checkbox" %s disabled="disabled"/>', + row_data[column.id].value ? 'checked="checked"' : ''); + case "binary": + var text = _t("Download"), + download_url = _.str.sprintf('/web/binary/saveas?session_id=%s&model=%s&field=%s&id=%d', openerp.connection.session_id, options.model, column.id, options.id); + if (column.filename) { + download_url += '&filename_field=' + column.filename; + if (row_data[column.filename]) { + text = _.str.sprintf(_t("Download \"%s\""), openerp.web.format_value( + row_data[column.filename].value, {type: 'char'})); + } + } + return _.template('<a href="<%-href%>"><%-text%></a> (%<-size%>)', { + text: text, + href: download_url, + size: row_data[column.id].value + }); + case 'progressbar': + return _.template( + '<progress value="<%-value%>" max="100"><%-value%>%</progress>', { + value: row_data[column.id].value + }); + } + + return _.escape(openerp.web.format_value( + row_data[column.id].value, column, options.value_if_empty)); } - + }; diff --git a/addons/web/static/src/js/view_editor.js b/addons/web/static/src/js/view_editor.js index 649f324744d..bf920380ff9 100644 --- a/addons/web/static/src/js/view_editor.js +++ b/addons/web/static/src/js/view_editor.js @@ -16,9 +16,10 @@ openerp.web.ViewEditor = openerp.web.Widget.extend({ this.init_view_editor(); }, init_view_editor: function() { - var self = this; + var self = this, + action_title = _.str.sprintf(_t("Manage Views (%s)"), this.model); var action = { - name: _.str.sprintf("Manage Views (%s)", this.model), + name: action_title, context: this.session.user_context, domain: [["model", "=", this.model]], res_model: 'ir.ui.view', @@ -38,7 +39,7 @@ openerp.web.ViewEditor = openerp.web.Widget.extend({ } }; this.view_edit_dialog = new openerp.web.Dialog(this, { - title: _t("ViewEditor"), + title: action_title, width: 850, buttons: [ {text: _t("Create"), click: function() { self.on_create_view(); }}, @@ -46,7 +47,7 @@ openerp.web.ViewEditor = openerp.web.Widget.extend({ {text: _t("Remove"), click: function() { self.do_delete_view(); }}, {text: _t("Close"), click: function() { self.view_edit_dialog.close(); }} ] - }).start().open(); + }).open(); this.main_view_id = this.parent.fields_view.view_id; this.action_manager = new openerp.web.ActionManager(this); this.action_manager.appendTo(this.view_edit_dialog); @@ -91,8 +92,7 @@ openerp.web.ViewEditor = openerp.web.Widget.extend({ }}, {text: _t("Cancel"), click: function () { self.create_view_dialog.close(); }} ] - }); - this.create_view_dialog.start().open(); + }).open(); var view_widget = [{'name': 'view_name', 'string':'View Name', 'type': 'char', 'required': true, 'value' : this.model + '.custom_' + Math.round(Math.random() * 1000)}, {'name': 'view_type', 'string': 'View Type', 'type': 'selection', 'required': true, 'value': 'Form', 'selection': [['',''],['tree', 'Tree'],['form', 'Form'],['graph', 'Graph'],['calendar', 'Calender']]}, {'name': 'proirity', 'string': 'Priority', 'type': 'float', 'required': true, 'value':'16'}]; @@ -124,7 +124,7 @@ openerp.web.ViewEditor = openerp.web.Widget.extend({ } }); if (field_name) { - model_dataset.read_slice(['name','field_id'], {"domain": [['model','=',self.model]]}, function(records) { + model_dataset.read_slice(['name','field_id'], {"domain": [['model','=',self.model]]}).then(function(records) { if (records) {view_string = records[0].name;} var arch = _.str.sprintf("<?xml version='1.0'?>\n<%s string='%s'>\n\t<field name='%s'/>\n</%s>", values.view_type, view_string, field_name, values.view_type); var vals = {'model': self.model, 'name': values.view_name, 'priority': values.priority, 'type': values.view_type, 'arch': arch}; @@ -227,12 +227,12 @@ openerp.web.ViewEditor = openerp.web.Widget.extend({ get_arch: function() { var self = this; var view_arch_list = []; - this.dataset.read_ids([parseInt(self.main_view_id)], ['arch', 'type'], function(arch) { + this.dataset.read_ids([parseInt(self.main_view_id)], ['arch', 'type']).then(function(arch) { if (arch.length) { var arch_object = self.parse_xml(arch[0].arch, self.main_view_id); self.main_view_type = arch[0].type == 'tree'? 'list': arch[0].type; view_arch_list.push({"view_id": self.main_view_id, "arch": arch[0].arch}); - self.dataset.read_slice([], {domain: [['inherit_id','=', parseInt(self.main_view_id)]]}, function(result) { + self.dataset.read_slice([], {domain: [['inherit_id','=', parseInt(self.main_view_id)]]}).then(function(result) { _.each(result, function(res) { view_arch_list.push({"view_id": res.id, "arch": res.arch}); self.inherit_view(arch_object, res); @@ -381,7 +381,7 @@ openerp.web.ViewEditor = openerp.web.Widget.extend({ self.edit_xml_dialog.close(); }} ] - }).start().open(); + }).open(); var no_property_att = []; _.each(_PROPERTIES, function(val, key) { if (! val.length) no_property_att.push(key); @@ -768,8 +768,7 @@ openerp.web.ViewEditor = openerp.web.Widget.extend({ }}, {text: _t("Cancel"), click: function () { self.edit_node_dialog.close(); }} ] - }); - this.edit_node_dialog.start().open(); + }).open(); var _PROPERTIES_ATTRIBUTES = { 'name' : {'name':'name', 'string': 'Name', 'type': 'char'}, 'string' : {'name':'string', 'string': 'String', 'type': 'char'}, @@ -895,7 +894,7 @@ openerp.web.ViewEditor = openerp.web.Widget.extend({ }}, {text: _t("Cancel"), click: function() { self.add_node_dialog.close(); }} ] - }).start().open(); + }).open(); this.add_node_dialog.$element.append('<table id="rec_table" style="width:420px" class="oe_forms"><tbody><tr></tbody></table>'); var table_selector = self.add_node_dialog.$element.find('table[id=rec_table] tbody'); _.each(render_list, function(node) { @@ -915,7 +914,7 @@ openerp.web.ViewEditor = openerp.web.Widget.extend({ table_selector.find("td[id^=]").attr("width","100px"); self.add_node_dialog.$element.find('#new_field').click(function() { model_data = new openerp.web.DataSetSearch(self,'ir.model', null, null); - model_data.read_slice([], {domain: [['model','=', self.model]]}, function(result) { + model_data.read_slice([], {domain: [['model','=', self.model]]}).then(function(result) { self.render_new_field(result[0].id); }); }); @@ -939,7 +938,7 @@ openerp.web.ViewEditor = openerp.web.Widget.extend({ controller.do_set_readonly.add_last(function(){ action_manager.stop(); new_fields_name = new openerp.web.DataSetSearch(self,'ir.model.fields', null, null); - new_fields_name.read_ids([controller.datarecord.id], ['name'], function(result) { + new_fields_name.read_ids([controller.datarecord.id], ['name']).then(function(result) { self.add_node_dialog.$element.find('select[id=field_value]').append($("<option selected></option>").attr("value", result[0].name).text(result[0].name)); _.detect(self.add_widget,function(widget){ widget.name == "field_value"? widget.selection.push(result[0].name): false; diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 4505fe671e4..ebe1ca26785 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -50,10 +50,10 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# _.defaults(this.options, { "not_interactible_on_create": false }); - this.mutating_lock = $.Deferred(); - this.initial_mutating_lock = this.mutating_lock; - this.on_change_lock = $.Deferred().resolve(); - this.reload_lock = $.Deferred().resolve(); + this.is_initialized = $.Deferred(); + this.mutating_mutex = new $.Mutex(); + this.on_change_mutex = new $.Mutex(); + this.reload_mutex = new $.Mutex(); }, start: function() { this._super(); @@ -136,17 +136,19 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# do_show: function () { var self = this; - this.$element.hide(); + this.$element.show().css('visibility', 'hidden'); return this.has_been_loaded.pipe(function() { var result; if (self.dataset.index === null) { // null index means we should start a new record result = self.on_button_new(); } else { - result = self.dataset.read_index(_.keys(self.fields_view.fields)).pipe(self.on_record_loaded); + result = self.dataset.read_index(_.keys(self.fields_view.fields), { + context : { 'bin_size' : true } + }).pipe(self.on_record_loaded); } result.pipe(function() { - self.$element.show(); + self.$element.css('visibility', 'visible'); }); if (self.sidebar) { self.sidebar.$element.show(); @@ -189,7 +191,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# }); } self.on_form_changed(); - self.initial_mutating_lock.resolve(); + self.is_initialized.resolve(); self.show_invalid = true; self.do_update_pager(record.id == null); if (self.sidebar) { @@ -207,6 +209,9 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# for (var w in this.widgets) { w = this.widgets[w]; w.process_modifiers(); + if (w.field) { + w.validate(); + } w.update_dom(); } }, @@ -255,7 +260,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# 'None': function () {return null;}, 'context': function (i) { context_index = i; - var ctx = widget.build_context ? widget.build_context() : {}; + var ctx = new openerp.web.CompoundContext(self.dataset.get_context(), widget.build_context() ? widget.build_context() : {}); return ctx; } }; @@ -306,7 +311,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# }, do_onchange: function(widget, processed) { var self = this; - var act = function() { + return this.on_change_mutex.exec(function() { try { processed = processed || []; var on_change = widget.node.attrs.on_change; @@ -333,9 +338,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# console.error(e); return $.Deferred().reject(); } - }; - this.on_change_lock = this.on_change_lock.pipe(act, act); - return this.on_change_lock; + }); }, on_processed_onchange: function(response, processed) { try { @@ -417,10 +420,8 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# */ do_save: function(success, prepend_on_create) { var self = this; - var action = function() { + return this.mutating_mutex.exec(function() { return self.is_initialized.pipe(function() { try { - if (!self.initial_mutating_lock.isResolved() && !self.initial_mutating_lock.isRejected()) - return; var form_invalid = false, values = {}, first_invalid_field = null; @@ -446,15 +447,15 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# } else { var save_deferral; if (!self.datarecord.id) { - console.log("FormView(", self, ") : About to create", values); + //console.log("FormView(", self, ") : About to create", values); save_deferral = self.dataset.create(values).pipe(function(r) { return self.on_created(r, undefined, prepend_on_create); }, null); } else if (_.isEmpty(values)) { - console.log("FormView(", self, ") : Nothing to save"); + //console.log("FormView(", self, ") : Nothing to save"); save_deferral = $.Deferred().resolve({}).promise(); } else { - console.log("FormView(", self, ") : About to save", values); + //console.log("FormView(", self, ") : About to save", values); save_deferral = self.dataset.write(self.datarecord.id, values, {}).pipe(function(r) { return self.on_saved(r); }, null); @@ -465,9 +466,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# console.error(e); return $.Deferred().reject(); } - }; - this.mutating_lock = this.mutating_lock.pipe(action, action); - return this.mutating_lock; + });}); }, on_invalid: function() { var msg = "<ul>"; @@ -518,7 +517,7 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# if (this.sidebar) { this.sidebar.attachments.do_update(); } - openerp.log("The record has been created with id #" + this.datarecord.id); + //openerp.log("The record has been created with id #" + this.datarecord.id); this.reload(); return $.when(_.extend(r, {created: true})).then(success); } @@ -528,15 +527,15 @@ openerp.web.FormView = openerp.web.View.extend( /** @lends openerp.web.FormView# }, reload: function() { var self = this; - var act = function() { + return this.reload_mutex.exec(function() { 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)).pipe(self.on_record_loaded); + return self.dataset.read_index(_.keys(self.fields_view.fields), { + context : { 'bin_size' : true } + }).pipe(self.on_record_loaded); } - }; - this.reload_lock = this.reload_lock.pipe(act, act); - return this.reload_lock; + }); }, get_fields_values: function(blacklist) { blacklist = blacklist || []; @@ -633,7 +632,7 @@ openerp.web.form.SidebarAttachments = openerp.web.Widget.extend({ ['res_model', '=', this.view.dataset.model], ['res_id', '=', this.view.datarecord.id], ['type', 'in', ['binary', 'url']] - ])).read_slice(['name', 'url', 'type'], {}, this.on_attachments_loaded); + ])).read_slice(['name', 'url', 'type'], {}).then(this.on_attachments_loaded); } }, on_attachments_loaded: function(attachments) { @@ -834,23 +833,17 @@ openerp.web.form.Widget = openerp.web.Widget.extend(/** @lends openerp.web.form. * the fields'context with the action's context. */ build_context: function(blacklist) { - var f_context = (this.field || {}).context || {}; - if (!!f_context.__ref || true) { //TODO: remove true - var fields_values = this._build_eval_context(blacklist); - f_context = new openerp.web.CompoundContext(f_context).set_eval_context(fields_values); - } - // maybe the default_get should only be used when we do a default_get? - var v_contexts = _.compact([this.node.attrs.default_get || null, - this.node.attrs.context || null]); - var v_context = new openerp.web.CompoundContext(); - _.each(v_contexts, function(x) {v_context.add(x);}); - if (_.detect(v_contexts, function(x) {return !!x.__ref;}) || true) { //TODO: remove true + // only use the model's context if there is not context on the node + var v_context = this.node.attrs.context; + if (! v_context) { + v_context = (this.field || {}).context || {}; + } + + if (v_context.__ref || true) { //TODO: remove true var fields_values = this._build_eval_context(blacklist); - v_context.set_eval_context(fields_values); + v_context = new openerp.web.CompoundContext(v_context).set_eval_context(fields_values); } - // if there is a context on the node, overrides the model's context - var ctx = v_contexts.length > 0 ? v_context : f_context; - return ctx; + return v_context; }, build_domain: function() { var f_domain = this.field.domain || []; @@ -1272,7 +1265,12 @@ openerp.web.form.Field = openerp.web.form.Widget.extend(/** @lends openerp.web.f validate: function() { this.invalid = false; }, - focus: function() { + focus: function($element) { + if ($element) { + setTimeout(function() { + $element.focus(); + }, 50); + } }, reset: function() { this.dirty = false; @@ -1319,8 +1317,8 @@ openerp.web.form.FieldChar = openerp.web.form.Field.extend({ this.invalid = true; } }, - focus: function() { - this.$element.find('input').focus(); + focus: function($element) { + this._super($element || this.$element.find('input:first')); } }); @@ -1444,9 +1442,6 @@ openerp.web.DateTimeWidget = openerp.web.Widget.extend({ } } }, - focus: function() { - this.$input.focus(); - }, parse_client: function(v) { return openerp.web.parse_value(v, {"widget": this.type_of_date}); }, @@ -1491,8 +1486,8 @@ openerp.web.form.FieldDatetime = openerp.web.form.Field.extend({ validate: function() { this.invalid = !this.datewidget.is_valid(this.required); }, - focus: function() { - this.datewidget.focus(); + focus: function($element) { + this._super($element || this.datewidget.$input); } }); @@ -1535,8 +1530,8 @@ openerp.web.form.FieldText = openerp.web.form.Field.extend({ this.invalid = true; } }, - focus: function() { - this.$element.find('textarea').focus(); + focus: function($element) { + this._super($element || this.$element.find('textarea:first')); }, do_resize: function(max_height) { max_height = parseInt(max_height, 10); @@ -1582,8 +1577,8 @@ openerp.web.form.FieldBoolean = openerp.web.form.Field.extend({ this._super.apply(this, arguments); this.$element.find('input').prop('disabled', this.readonly); }, - focus: function() { - this.$element.find('input').focus(); + focus: function($element) { + this._super($element || this.$element.find('input:first')); } }); @@ -1602,7 +1597,8 @@ openerp.web.form.FieldProgressBar = openerp.web.form.Field.extend({ if (isNaN(show_value)) { show_value = 0; } - this.$element.find('div').progressbar('option', 'value', show_value).find('span').html(show_value + '%'); + var formatted_value = openerp.web.format_value(show_value, { type : 'float' }, '0'); + this.$element.find('div').progressbar('option', 'value', show_value).find('span').html(formatted_value + '%'); } }); @@ -1669,8 +1665,8 @@ openerp.web.form.FieldSelection = openerp.web.form.Field.extend({ var value = this.values[this.$element.find('select')[0].selectedIndex]; this.invalid = !(value && !(this.required && value[0] === false)); }, - focus: function() { - this.$element.find('select').focus(); + focus: function($element) { + this._super($element || this.$element.find('select:first')); } }); @@ -1708,15 +1704,11 @@ openerp.web.form.FieldSelection = openerp.web.form.Field.extend({ openerp.web.form.dialog = function(content, options) { options = _.extend({ - autoOpen: true, width: '90%', - height: '90%', - min_width: '800px', - min_height: '600px' + height: 'auto', + min_width: '800px' }, options || {}); - options.autoOpen = true; - var dialog = new openerp.web.Dialog(null, options); - dialog.$element = $(content).dialog(dialog.dialog_options); + var dialog = new openerp.web.Dialog(null, options, content).open(); return dialog.$element; }; @@ -1776,7 +1768,7 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({ self.open_related(self.related_entries[i]); }; }); - var cmenu = self.$menu_btn.contextMenu(self.cm_id, {'leftClickToo': true, + var cmenu = self.$menu_btn.contextMenu(self.cm_id, {'noRightClick': true, bindings: bindings, itemStyle: {"color": ""}, onContextMenu: function() { if(self.value) { @@ -1796,7 +1788,6 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({ }); }); var ctx_callback = function(e) {init_context_menu_def.resolve(e); e.preventDefault()}; - this.$menu_btn.bind('contextmenu', ctx_callback); this.$menu_btn.click(ctx_callback); // some behavior for input @@ -2026,8 +2017,8 @@ openerp.web.form.FieldMany2One = openerp.web.form.Field.extend({ self.do_action(result.result); }); }, - focus: function () { - this.$input.focus(); + focus: function ($element) { + this._super($element || this.$input); }, update_dom: function() { this._super.apply(this, arguments); @@ -2306,6 +2297,9 @@ openerp.web.form.FieldOne2Many = openerp.web.form.Field.extend({ this.viewmanager.views[this.viewmanager.active_view].controller) { var view = this.viewmanager.views[this.viewmanager.active_view].controller; if (this.viewmanager.active_view === "form") { + if (!view.is_initialized.isResolved()) { + return false; + } var res = $.when(view.do_save()); if (!res.isResolved() && !res.isRejected()) { console.warn("Asynchronous get_value() is not supported in form view."); @@ -2437,7 +2431,10 @@ openerp.web.form.One2ManyFormView = openerp.web.FormView.extend({ form_template: 'One2Many.formview', on_loaded: function(data) { this._super(data); - this.$form_header.find('button.oe_form_button_create').click(this.on_button_new); + var self = this; + this.$form_header.find('button.oe_form_button_create').click(function() { + self.do_save().then(self.on_button_new); + }); } }); @@ -2753,7 +2750,7 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope var $nbutton = $buttons.find(".oe_selectcreatepopup-form-save-new"); $nbutton.click(function() { $.when(self.view_form.do_save()).then(function() { - self.view_form.reload_lock.then(function() { + self.view_form.reload_mutex.exec(function() { self.view_form.on_button_new(); }); }); @@ -2761,7 +2758,7 @@ openerp.web.form.SelectCreatePopup = openerp.web.OldWidget.extend(/** @lends ope var $nbutton = $buttons.find(".oe_selectcreatepopup-form-save"); $nbutton.click(function() { $.when(self.view_form.do_save()).then(function() { - self.view_form.reload_lock.then(function() { + self.view_form.reload_mutex.exec(function() { self.check_exit(); }); }); @@ -3023,7 +3020,7 @@ openerp.web.form.FieldBinary = openerp.web.form.Field.extend({ on_save_as: function() { var url = '/web/binary/saveas?session_id=' + this.session.session_id + '&model=' + this.view.dataset.model +'&id=' + (this.view.datarecord.id || '') + '&field=' + this.name + - '&fieldname=' + (this.node.attrs.filename || '') + '&t=' + (new Date().getTime()); + '&filename_field=' + (this.node.attrs.filename || '') + '&t=' + (new Date().getTime()); window.open(url); }, on_clear: function() { diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js index 2ab1152a0fa..aa970c31649 100644 --- a/addons/web/static/src/js/view_list.js +++ b/addons/web/static/src/js/view_list.js @@ -217,11 +217,11 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# }); this.$element.find('.oe-list-add') - .click(this.do_add_record) + .click(this.proxy('do_add_record')) .attr('disabled', grouped && this.options.editable); this.$element.find('.oe-list-delete') .attr('disabled', true) - .click(this.do_delete_selected); + .click(this.proxy('do_delete_selected')); this.$element.find('thead').delegate('th.oe-sortable[data-id]', 'click', function (e) { e.stopPropagation(); @@ -512,7 +512,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# this.no_leaf = !!context['group_by_no_leaf']; this.reload_view(!!group_by, context).then( - $.proxy(this, 'reload_content')); + this.proxy('reload_content')); }, /** * Handles the signal to delete lines from the records list @@ -685,6 +685,7 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# this.display_aggregates(aggregates); }, display_aggregates: function (aggregation) { + var self = this; var $footer_cells = this.$element.find('.oe-list-footer'); _(this.aggregate_columns).each(function (column) { if (!column['function']) { @@ -692,7 +693,9 @@ openerp.web.ListView = openerp.web.View.extend( /** @lends openerp.web.ListView# } $footer_cells.filter(_.str.sprintf('[data-field=%s]', column.id)) - .html(openerp.web.format_cell(aggregation, column, undefined, false)); + .html(openerp.web.format_cell(aggregation, column, { + process_modifiers: false + })); }); }, get_selected_ids: function() { @@ -792,7 +795,7 @@ openerp.web.ListView.List = openerp.web.Class.extend( /** @lends openerp.web.Lis $row.remove(); self.refresh_zebra(index); }, - 'reset': $.proxy(this, 'on_records_reset'), + 'reset': function () { return self.on_records_reset(); }, 'change': function (event, record) { var $row = self.$current.find('[data-id=' + record.get('id') + ']'); $row.replaceWith(self.render_record(record)); @@ -846,6 +849,9 @@ openerp.web.ListView.List = openerp.web.Class.extend( /** @lends openerp.web.Lis return self.reload_record(self.records.get(record_id)); }]); }) + .delegate('a', 'click', function (e) { + e.stopPropagation(); + }) .delegate('tr', 'click', function (e) { e.stopPropagation(); var row_id = self.row_id(e.currentTarget); @@ -905,16 +911,22 @@ openerp.web.ListView.List = openerp.web.Class.extend( /** @lends openerp.web.Lis }); } } - return openerp.web.format_cell(record.toForm().data, column); + return openerp.web.format_cell(record.toForm().data, column, { + model: this.dataset.model, + id: record.get('id') + }); }, render: function () { + var self = this; if (this.$current) { this.$current.remove(); } this.$current = this.$_element.clone(true); this.$current.empty().append( QWeb.render('ListView.rows', _.extend({ - render_cell: $.proxy(this, 'render_cell')}, this))); + render_cell: function () { + return self.render_cell.apply(self, arguments); } + }, this))); this.pad_table_to(5); }, pad_table_to: function (count) { @@ -1007,14 +1019,13 @@ openerp.web.ListView.List = openerp.web.Class.extend( /** @lends openerp.web.Lis [record.get('id')], _.pluck(_(this.columns).filter(function (r) { return r.tag === 'field'; - }), 'name'), - function (records) { - _(records[0]).each(function (value, key) { - record.set(key, value, {silent: true}); - }); - record.trigger('change', record); - } - ); + }), 'name') + ).then(function (records) { + _(records[0]).each(function (value, key) { + record.set(key, value, {silent: true}); + }); + record.trigger('change', record); + }); }, /** * Renders a list record to HTML @@ -1030,7 +1041,8 @@ openerp.web.ListView.List = openerp.web.Class.extend( /** @lends openerp.web.Lis record: record, row_parity: (index % 2 === 0) ? 'even' : 'odd', view: this.view, - render_cell: $.proxy(this, 'render_cell') + render_cell: function () { + return this.render_cell.apply(this, arguments); } }); }, /** @@ -1084,7 +1096,9 @@ openerp.web.ListView.Groups = openerp.web.Class.extend( /** @lends openerp.web.L this.page = 0; - this.records.bind('reset', $.proxy(this, 'on_records_reset')); + var self = this; + this.records.bind('reset', function () { + return self.on_records_reset(); }); }, make_fragment: function () { return document.createDocumentFragment(); @@ -1211,7 +1225,10 @@ openerp.web.ListView.Groups = openerp.web.Class.extend( /** @lends openerp.web.L return column.id === group.grouped_on; }); try { $group_column.html(openerp.web.format_cell( - row_data, group_column, _t("Undefined"), false)); + row_data, group_column, { + value_if_empty: _t("Undefined"), + process_modifiers: false + })); } catch (e) { $group_column.html(row_data[group_column.id].value); } @@ -1232,6 +1249,9 @@ openerp.web.ListView.Groups = openerp.web.Class.extend( /** @lends openerp.web.L if (self.options.selectable) { $row.append('<td>'); } + if (self.options.isClarkGable) { + $row.append('<td>'); + } _(self.columns).chain() .filter(function (column) {return !column.invisible;}) .each(function (column) { @@ -1286,9 +1306,9 @@ openerp.web.ListView.Groups = openerp.web.Class.extend( /** @lends openerp.web.L page = this.datagroup.openable ? this.page : view.page; var fields = _.pluck(_.select(this.columns, function(x) {return x.tag == "field"}), 'name'); - var options = { offset: page * limit, limit: limit }; + var options = { offset: page * limit, limit: limit, context: {bin_size: true} }; //TODO xmo: investigate why we need to put the setTimeout - $.async_when().then(function() {dataset.read_slice(fields, options , function (records) { + $.async_when().then(function() {dataset.read_slice(fields, options).then(function (records) { // FIXME: ignominious hacks, parents (aka form view) should not send two ListView#reload_content concurrently if (self.records.length) { self.records.reset(null, {silent: true}); @@ -1674,6 +1694,7 @@ var Collection = openerp.web.Class.extend(/** @lends Collection# */{ proxy.reset(); }); this._proxies = {}; + _(this.records).invoke('unbind', null, this._onRecordEvent); this.length = 0; this.records = []; this._byId = {}; @@ -1692,7 +1713,6 @@ var Collection = openerp.web.Class.extend(/** @lends Collection# */{ * @returns this */ remove: function (record) { - var self = this; var index = _(this.records).indexOf(record); if (index === -1) { _(this._proxies).each(function (proxy) { @@ -1701,6 +1721,7 @@ var Collection = openerp.web.Class.extend(/** @lends Collection# */{ return this; } + record.unbind(null, this._onRecordEvent); this.records.splice(index, 1); delete this._byId[record.get('id')]; this.length--; diff --git a/addons/web/static/src/js/view_list_editable.js b/addons/web/static/src/js/view_list_editable.js index daf708104e8..a26626d8630 100644 --- a/addons/web/static/src/js/view_list_editable.js +++ b/addons/web/static/src/js/view_list_editable.js @@ -211,7 +211,8 @@ openerp.web.list_editable = function (openerp) { .delegate('button', 'keyup', function (e) { e.stopImmediatePropagation(); }) - .keyup($.proxy(self, 'on_row_keyup')); + .keyup(function () { + return self.on_row_keyup.apply(self, arguments); }); if (row) { $new_row.replaceAll(row); } else if (self.options.editable) { @@ -359,7 +360,8 @@ openerp.web.list_editable = function (openerp) { this.render_row_as_form(); }, render_record: function (record) { - var index = this.records.indexOf(record); + var index = this.records.indexOf(record), + self = this; // FIXME: context dict should probably be extracted cleanly return QWeb.render('ListView.row', { columns: this.columns, @@ -367,7 +369,8 @@ openerp.web.list_editable = function (openerp) { record: record, row_parity: (index % 2 === 0) ? 'even' : 'odd', view: this.view, - render_cell: $.proxy(this, 'render_cell'), + render_cell: function () { + return self.render_cell.apply(self, arguments); }, edited: !!this.edition_form }); } diff --git a/addons/web/static/src/js/view_page.js b/addons/web/static/src/js/view_page.js index 6ac5bc9f72b..d3ee79effca 100644 --- a/addons/web/static/src/js/view_page.js +++ b/addons/web/static/src/js/view_page.js @@ -208,6 +208,12 @@ openerp.web.page = function (openerp) { openerp.web.page.FieldOne2ManyReadonly = openerp.web.form.FieldOne2Many.extend({ force_readonly: true }); + openerp.web.page.FieldBinaryImageReaonly = openerp.web.form.FieldBinaryImage.extend({ + update_dom: function() { + this._super.apply(this, arguments); + this.$element.find('.oe-binary').hide(); + } + }); openerp.web.page.FieldBinaryFileReadonly = openerp.web.form.FieldBinary.extend({ template: 'FieldURI.readonly', start: function() { @@ -249,5 +255,6 @@ openerp.web.page = function (openerp) { 'integer': 'openerp.web.page.FieldCharReadonly', 'float_time': 'openerp.web.page.FieldCharReadonly', 'binary': 'openerp.web.page.FieldBinaryFileReadonly', + 'image': 'openerp.web.page.FieldBinaryImageReaonly' }); }; diff --git a/addons/web/static/src/js/view_tree.js b/addons/web/static/src/js/view_tree.js index 528a31c7869..902b5a99526 100644 --- a/addons/web/static/src/js/view_tree.js +++ b/addons/web/static/src/js/view_tree.js @@ -86,7 +86,7 @@ openerp.web.TreeView = openerp.web.View.extend(/** @lends openerp.web.TreeView# 'toolbar': has_toolbar })); - this.dataset.read_slice(this.fields_list(), {}, function (records) { + this.dataset.read_slice(this.fields_list()).then(function(records) { if (!has_toolbar) { // WARNING: will do a second read on the same ids, but only on // first load so not very important @@ -192,7 +192,7 @@ openerp.web.TreeView = openerp.web.View.extend(/** @lends openerp.web.TreeView# getdata: function (id, children_ids) { var self = this; - self.dataset.read_ids(children_ids, this.fields_list(), function (records) { + self.dataset.read_ids(children_ids, this.fields_list()).then(function(records) { _(records).each(function (record) { self.records[record.id] = record; }); diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js index 060d12e39b6..cc108837f88 100644 --- a/addons/web/static/src/js/views.js +++ b/addons/web/static/src/js/views.js @@ -128,19 +128,18 @@ session.web.ActionManager = session.web.Widget.extend({ .contains(action.res_model)) { var old_close = on_close; on_close = function () { - session.webclient.do_reload(); - if (old_close) { old_close(); } + session.webclient.do_reload().then(old_close); }; } if (action.target === 'new') { if (this.dialog == null) { - this.dialog = new session.web.Dialog(this, { title: action.name, width: '80%' }); + this.dialog = new session.web.Dialog(this, { width: '80%' }); if(on_close) this.dialog.on_close.add(on_close); - this.dialog.start(); } else { this.dialog_viewmanager.stop(); } + this.dialog.dialog_title = action.name; this.dialog_viewmanager = new session.web.ViewManagerAction(this, action); this.dialog_viewmanager.appendTo(this.dialog.$element); this.dialog.open(); @@ -356,9 +355,12 @@ session.web.ViewManager = session.web.Widget.extend(/** @lends session.web.View on_prev_view: function (created) { var current_view = this.views_history.pop(); var previous_view = this.views_history[this.views_history.length - 1]; - // APR special case: "If creation mode from list (and only from a list), - // after saving, go to page view (don't come back in list)" if (created && current_view === 'form' && previous_view === 'list') { + // APR special case: "If creation mode from list (and only from a list), + // after saving, go to page view (don't come back in list)" + return this.on_mode_switch('page'); + } else if (created && !previous_view && this.action && this.action.flags.default_view === 'form') { + // APR special case: "If creation from dashboard, we have no previous view return this.on_mode_switch('page'); } return this.on_mode_switch(previous_view, true); @@ -515,7 +517,7 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner } }); if (!(self.action.id in self.session.hidden_menutips)) { - Users.read_ids([this.session.uid], ['menu_tips'], function(users) { + Users.read_ids([this.session.uid], ['menu_tips']).then(function(users) { var user = users[0]; if (!(user && user.id === self.session.uid)) { return; @@ -539,41 +541,70 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner return manager_ready; }, on_debug_changed: function (evt) { - var $sel = $(evt.currentTarget), + var self = this, + $sel = $(evt.currentTarget), $option = $sel.find('option:selected'), - val = $sel.val(); + val = $sel.val(), + current_view = this.views[this.active_view].controller; switch (val) { case 'fvg': - $('<pre>').text(session.web.json_node_to_xml( - this.views[this.active_view].controller.fields_view.arch, true) - ).dialog({ width: '95%'}); + var dialog = new session.web.Dialog(this, { title: _t("Fields View Get"), width: '95%' }).open(); + $('<pre>').text(session.web.json_node_to_xml(current_view.fields_view.arch, true)).appendTo(dialog.$element); break; - case 'edit': - var model = $option.data('model'), - id = $option.data('id'), - domain = $option.data('domain'), - action = { - res_model : model, - type : 'ir.actions.act_window', - view_type : 'form', - view_mode : 'form', - target : 'new', - flags : { - action_buttons : true, - form : { - resize_textareas : true + case 'fields': + this.dataset.call_and_eval( + 'fields_get', [false, {}], null, 1).then(function (fields) { + var $root = $('<dl>'); + _(fields).each(function (attributes, name) { + $root.append($('<dt>').append($('<h4>').text(name))); + var $attrs = $('<dl>').appendTo( + $('<dd>').appendTo($root)); + _(attributes).each(function (def, name) { + if (def instanceof Object) { + def = JSON.stringify(def); } - } - }; - if (id) { - action.res_id = id, - action.views = [[false, 'form']]; - } else if (domain) { - action.views = [[false, 'list'], [false, 'form']]; - action.domain = domain; - action.flags.views_switcher = true; + $attrs + .append($('<dt>').text(name)) + .append($('<dd style="white-space: pre-wrap;">').text(def)); + }); + }); + new session.web.Dialog(self, { + title: _.str.sprintf(_t("Model %s fields"), + self.dataset.model), + width: '95%'}, $root).open(); + }); + break; + case 'customize_object': + this.rpc('/web/dataset/search_read', { + model: 'ir.model', + fields: ['id'], + domain: [['model', '=', this.dataset.model]] + }, function (result) { + self.do_edit_resource('ir.model', result.ids[0], { + name : _t("Customize Object") }); + }); + break; + case 'manage_views': + if (current_view.fields_view && current_view.fields_view.arch) { + var view_editor = new session.web.ViewEditor(current_view, current_view.$element, this.dataset, current_view.fields_view.arch); + view_editor.start(); + } else { + this.do_warn(_t("Manage Views"), + _t("Could not find current view declaration")); } - this.do_action(action); + break; + case 'edit_workflow': + return this.do_action({ + res_model : 'workflow', + domain : [['osv', '=', this.dataset.model]], + views: [[false, 'list'], [false, 'form'], [false, 'diagram']], + type : 'ir.actions.act_window', + view_type : 'list', + view_mode : 'list' + }); + break; + case 'edit': + this.do_edit_resource($option.data('model'), $option.data('id'), { name : $option.text() }); break; default: if (val) { @@ -582,6 +613,24 @@ session.web.ViewManagerAction = session.web.ViewManager.extend(/** @lends oepner } evt.currentTarget.selectedIndex = 0; }, + do_edit_resource: function(model, id, action) { + var action = _.extend({ + res_model : model, + res_id : id, + type : 'ir.actions.act_window', + view_type : 'form', + view_mode : 'form', + views : [[false, 'form']], + target : 'new', + flags : { + action_buttons : true, + form : { + resize_textareas : true + } + } + }, action || {}); + this.do_action(action); + }, on_mode_switch: function (view_type, no_store) { var self = this; @@ -729,26 +778,11 @@ session.web.Sidebar = session.web.Widget.extend({ action = view_manager.action; if (this.session.uid === 1) { this.add_section(_t('Customize'), 'customize'); - this.add_items('customize', [ - { - label: _t("Manage Views"), - callback: view.on_sidebar_manage_views, - title: _t("Manage views of the current object") - }, { - label: _t("Edit Workflow"), - callback: view.on_sidebar_edit_workflow, - title: _t("Manage views of the current object"), - classname: 'oe_sidebar_edit_workflow' - }, { - label: _t("Customize Object"), - callback: view.on_sidebar_customize_object, - title: _t("Manage views of the current object") - }, { - label: _t("Translate"), - callback: view.on_sidebar_translate, - title: _t("Technical translation") - } - ]); + this.add_items('customize', [{ + label: _t("Translate"), + callback: view.on_sidebar_translate, + title: _t("Technical translation") + }]); } this.add_section(_t('Other Options'), 'other'); @@ -907,7 +941,7 @@ session.web.TranslateDialog = session.web.Dialog.extend({ this.languages = null; this.languages_loaded = $.Deferred(); (new session.web.DataSetSearch(this, 'res.lang', this.view.dataset.get_context(), - [['translatable', '=', '1']])).read_slice(['code', 'name'], { sort: 'id' }, this.on_languages_loaded); + [['translatable', '=', '1']])).read_slice(['code', 'name'], { sort: 'id' }).then(this.on_languages_loaded); }, start: function() { var self = this; @@ -1132,34 +1166,6 @@ session.web.View = session.web.Widget.extend(/** @lends session.web.View# */{ set_common_sidebar_sections: function(sidebar) { sidebar.add_default_sections(); }, - on_sidebar_manage_views: function() { - if (this.fields_view && this.fields_view.arch) { - var view_editor = new session.web.ViewEditor(this, this.$element, this.dataset, this.fields_view.arch); - view_editor.start(); - } else { - this.do_warn("Manage Views", "Could not find current view declaration"); - } - }, - on_sidebar_edit_workflow: function() { - return this.do_action({ - res_model : 'workflow', - domain : [['osv', '=', this.dataset.model]], - views: [[false, 'list'], [false, 'form']], - type : 'ir.actions.act_window', - view_type : "list", - view_mode : "list" - }); - }, - on_sidebar_customize_object: function() { - var self = this; - this.rpc('/web/dataset/search_read', { - model: 'ir.model', - fields: ['id'], - domain: [['model', '=', self.dataset.model]] - }, function (result) { - self.on_sidebar_edit_resource('ir.model', result.ids[0]); - }); - }, on_sidebar_import: function() { var import_view = new session.web.DataImport(this, this.dataset); import_view.start(); @@ -1178,27 +1184,6 @@ session.web.View = session.web.Widget.extend(/** @lends session.web.View# */{ view_mode : "list" }); }, - on_sidebar_edit_resource: function(model, id, domain) { - var action = { - res_model : model, - type : 'ir.actions.act_window', - view_type : 'form', - view_mode : 'form', - target : 'new', - flags : { - action_buttons : true - } - } - if (id) { - action.res_id = id, - action.views = [[false, 'form']]; - } else if (domain) { - action.views = [[false, 'list'], [false, 'form']]; - action.domain = domain; - action.flags.views_switcher = true; - } - this.do_action(action); - }, on_sidebar_view_log: function() { }, sidebar_context: function () { diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 7238124ef49..5158fda62c2 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -470,10 +470,16 @@ </t> <t t-name="ViewManagerDebug"> <option value="">Debug View#<t t-esc="view.fields_view.view_id"/></option> - <option value="fvg"> - Fields View Get</option> - <option value="edit" data-model="ir.ui.view" t-att-data-id="view.fields_view.view_id"> - Edit <t t-esc="_.str.capitalize(view.fields_view.type)"/>View</option> - <option t-if="view_manager.searchview" value="edit" data-model="ir.ui.view" t-att-data-id="view_manager.searchview.view_id"> - Edit SearchView</option> - <option t-if="view_manager.action" value="edit" t-att-data-model="view_manager.action.type" t-att-data-id="view_manager.action.id"> - Edit Action</option> + <option value="fvg">Fields View Get</option> + <option value="fields">View Fields</option> + <t t-if="view_manager.session.uid === 1"> + <option value="manage_views">Manage Views</option> + <option value="edit" data-model="ir.ui.view" t-att-data-id="view.fields_view.view_id">Edit <t t-esc="_.str.capitalize(view.fields_view.type)"/>View</option> + <option t-if="view_manager.searchview" value="edit" data-model="ir.ui.view" t-att-data-id="view_manager.searchview.view_id">Edit SearchView</option> + <option t-if="view_manager.action" value="edit" t-att-data-model="view_manager.action.type" t-att-data-id="view_manager.action.id">Edit Action</option> + <option value="customize_object">Customize Object</option> + <option value="edit_workflow">Edit Workflow</option> + </t> </t> <t t-extend="ViewManager" t-name="One2Many.viewmanager"> <t t-jquery="span.oe_view_title_text" t-operation="replace"/> @@ -743,7 +749,7 @@ <li t-foreach="attachments" t-as="attachment"> <t t-if="attachment.type == 'binary'" t-set="attachment.url" t-value="_s + '/web/binary/saveas?session_id=' + session.session_id + '&model=ir.attachment&id=' + attachment.id - + '&field=datas&fieldname=name&t=' + (new Date().getTime())"/> + + '&field=datas&filename_field=name&t=' + (new Date().getTime())"/> <a class="oe-sidebar-attachments-link" t-att-href="attachment.url" target="_blank"> <t t-esc="attachment.name"/> </a> @@ -1411,14 +1417,10 @@ <button type="button" class="oe_button oe_formopenpopup-form-close">Cancel</button> </t> <t t-extend="ListView.row"> - <!-- adds front & back padding to row being rendered after edition, if - necessary (if not selectable add front padding and if not deletable - add back padding), otherwise the row being added is missing columns + <!-- adds back padding to row being rendered after edition, if necessary + (if not deletable add back padding), otherwise the row being added is + missing columns --> - <t t-jquery="> :first" t-operation="before"> - <td t-if="edited and !options.selectable" class="oe-listview-padding"/> - <td t-if="edited and !options.isClarkGable" class="oe-listview-padding"/> - </t> <t t-jquery="> :last" t-operation="after"> <td t-if="edited and !options.deletable" class="oe-listview-padding"/> </t> @@ -1644,7 +1646,7 @@ <t t-name="ImportDataView"> <form name="import_data" id="import_data" action="" method="post" enctype="multipart/form-data" class="oe-import oe-import-no-result"> - <input type="hidden" name="session_id" t-att-value="session.session_id"/> + <input type="hidden" name="session_id" t-att-value="widget.session.session_id"/> <h2 class="separator horizontal">1. Import a .CSV file</h2> <p>Select a .CSV file to import. If you need a sample of file to import, you should use the export tool with the "Import Compatible" option. @@ -1715,6 +1717,7 @@ <t t-name="About-Page"> <div> + <a class="oe_activate_debug_mode" href="?debug">Debug mode</a> <h1>OpenERP Web</h1> <h3 style="padding:0 5px 5px">Version <t t-esc="version_info.version"/></h3> <p> diff --git a/addons/web/static/test/formats.js b/addons/web/static/test/formats.js index 69557e98d5e..b841d12a6b2 100644 --- a/addons/web/static/test/formats.js +++ b/addons/web/static/test/formats.js @@ -61,6 +61,17 @@ $(document).ready(function () { var str = openerp.web.format_value(date, {type:"time"}); equal(str, date.toString("HH:mm:ss")); }); + test("format_float_time", function () { + strictEqual( + openerp.web.format_value(1.0, {type:'float', widget:'float_time'}), + '01:00'); + strictEqual( + openerp.web.format_value(0.9853, {type:'float', widget:'float_time'}), + '00:59'); + strictEqual( + openerp.web.format_value(0.0085, {type:'float', widget:'float_time'}), + '00:01'); + }); test("format_float", function () { var fl = 12.1234; var str = openerp.web.format_value(fl, {type:"float"}); diff --git a/addons/web/static/test/list-utils.js b/addons/web/static/test/list-utils.js index f71da82a560..8cc34454cd5 100644 --- a/addons/web/static/test/list-utils.js +++ b/addons/web/static/test/list-utils.js @@ -170,6 +170,16 @@ $(document).ready(function () { equal(c.get(2), undefined); strictEqual(c.at(1).get('value'), 20); }); + test('Remove unbind', function () { + var changed = false, + c = new openerp.web.list.Collection([ {id: 1, value: 5} ]); + c.bind('change', function () { changed = true; }); + var record = c.get(1); + c.remove(record); + record.set('value', 42); + ok(!changed, 'removed records should not trigger events in their ' + + 'parent collection'); + }); test('Reset', function () { var event, obj, c = new openerp.web.list.Collection([ {id: 1, value: 5}, @@ -190,6 +200,16 @@ $(document).ready(function () { strictEqual(c.length, 1); strictEqual(c.get(42).get('value'), 55); }); + test('Reset unbind', function () { + var changed = false, + c = new openerp.web.list.Collection([ {id: 1, value: 5} ]); + c.bind('change', function () { changed = true; }); + var record = c.get(1); + c.reset(); + record.set('value', 42); + ok(!changed, 'removed records should not trigger events in their ' + + 'parent collection'); + }); test('Events propagation', function () { var values = []; diff --git a/addons/web/static/test/test.html b/addons/web/static/test/test.html index b1018bc00ca..da06aba2364 100644 --- a/addons/web/static/test/test.html +++ b/addons/web/static/test/test.html @@ -14,6 +14,7 @@ <!-- jquery --> <script src="/web/static/lib/jquery/jquery-1.6.4.js"></script> <script src="/web/static/lib/jquery.ui/js/jquery-ui-1.8.9.custom.min.js"></script> + <script src="/web/static/lib/jquery.ba-bbq/jquery.ba-bbq.js"></script> <script src="/web/static/lib/datejs/globalization/en-US.js"></script> <script src="/web/static/lib/datejs/core.js"></script> |
