From 04e9c7a19fbe13594478ca412451f0c1f10c74d0 Mon Sep 17 00:00:00 2001 From: niv-openerp Date: Fri, 18 Oct 2013 14:12:43 +0200 Subject: [PATCH 01/61] Removed small usage of openerp.http bzr revid: nicolas.vanhoren@openerp.com-20131018121243-o1ua7s85x75fwyuq --- addons/edi/edi_service.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/edi/edi_service.py b/addons/edi/edi_service.py index d5dc5c09f74..dd95196ef4f 100644 --- a/addons/edi/edi_service.py +++ b/addons/edi/edi_service.py @@ -53,7 +53,6 @@ def exp_import_edi_document(db_name, uid, passwd, edi_document, context=None): def exp_import_edi_url(db_name, uid, passwd, edi_url, context=None): return _edi_dispatch(db_name, 'import_edi', uid, None, edi_url) -@openerp.http.rpc('edi') def dispatch(method, params): if method in ['import_edi_document', 'import_edi_url']: (db, uid, passwd) = params[0:3] @@ -63,4 +62,6 @@ def dispatch(method, params): fn = globals()['exp_' + method] return fn(*params) +openerp.service.wsgi_server.register_rpc_endpoint('edi', dispatch) + # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: From 3d8b7984bece705354e774213064c9540f5280c5 Mon Sep 17 00:00:00 2001 From: Antony Lesuisse Date: Tue, 22 Oct 2013 19:06:59 +0200 Subject: [PATCH 02/61] import cleanups part1 bzr revid: al@openerp.com-20131022170659-kg9err029ha22evh --- addons/auth_oauth/controllers/main.py | 4 ++-- addons/auth_openid/controllers/main.py | 4 ++-- addons/auth_signup/controllers/main.py | 4 ++-- addons/edi/controllers/main.py | 15 ++++++++------- addons/im/im.py | 16 ++++++++-------- addons/im_livechat/im_livechat.py | 9 +++++---- addons/mail/controllers/main.py | 2 +- addons/mass_mailing/controllers/main.py | 5 ++--- addons/point_of_sale/controllers/main.py | 4 ++-- 9 files changed, 32 insertions(+), 31 deletions(-) diff --git a/addons/auth_oauth/controllers/main.py b/addons/auth_oauth/controllers/main.py index 35a06871696..bedf32ae776 100644 --- a/addons/auth_oauth/controllers/main.py +++ b/addons/auth_oauth/controllers/main.py @@ -7,8 +7,8 @@ from werkzeug.exceptions import BadRequest import openerp from openerp import SUPERUSER_ID -import openerp.addons.web.http as http -from openerp.addons.web.http import request +from openerp import http +from openerp.http import request from openerp.addons.web.controllers.main import db_monodb, set_cookie_and_redirect, login_and_redirect from openerp.modules.registry import RegistryManager diff --git a/addons/auth_openid/controllers/main.py b/addons/auth_openid/controllers/main.py index a4ef1bfa07b..534d1b01a9c 100644 --- a/addons/auth_openid/controllers/main.py +++ b/addons/auth_openid/controllers/main.py @@ -38,8 +38,8 @@ import openerp from openerp import SUPERUSER_ID from openerp.modules.registry import RegistryManager from openerp.addons.web.controllers.main import login_and_redirect, set_cookie_and_redirect -import openerp.addons.web.http as http -from openerp.addons.web.http import request +import openerp.http as http +from openerp.http import request from .. import utils diff --git a/addons/auth_signup/controllers/main.py b/addons/auth_signup/controllers/main.py index e42dc64151f..a778cbcf061 100644 --- a/addons/auth_signup/controllers/main.py +++ b/addons/auth_signup/controllers/main.py @@ -21,10 +21,10 @@ import logging import openerp +from openerp import http +from openerp.http import request from openerp.modules.registry import RegistryManager from ..res_users import SignupError -import openerp.addons.web.http as http -from openerp.addons.web.http import request _logger = logging.getLogger(__name__) diff --git a/addons/edi/controllers/main.py b/addons/edi/controllers/main.py index 7e27428b63b..3f77cced386 100644 --- a/addons/edi/controllers/main.py +++ b/addons/edi/controllers/main.py @@ -1,15 +1,15 @@ import simplejson import urllib -import openerp.addons.web.http as openerpweb +import openerp import openerp.addons.web.controllers.main as webmain class EDI(openerpweb.Controller): - # http://hostname:8069/edi/import_url?url=URIEncodedURL - _cp_path = "/edi" - @openerpweb.httprequest - def import_url(self, req, url): + @openerp.http.route('/edi/import_url', type='http', auth='none') + def import_url(self, url): + # http://hostname:8069/edi/import_url?url=URIEncodedURL + req = openerp.http.request modules = webmain.module_boot(req) + ['edi'] modules_str = ','.join(modules) modules_json = simplejson.dumps(modules) @@ -26,8 +26,9 @@ class EDI(openerpweb.Controller): 'init': 's.edi.edi_import("%s");' % safe_url, } - @openerpweb.jsonrequest - def import_edi_url(self, req, url): + @openerp.http.route('/edi/import_edi_url', type='http', auth='none') + def import_edi_url(self, url): + req = openerp.http.request result = req.session.proxy('edi').import_edi_url(req.session._db, req.session._uid, req.session._password, url) if len(result) == 1: return {"action": webmain.clean_action(req, result[0][2])} diff --git a/addons/im/im.py b/addons/im/im.py index 3c4f0bfcd14..fd87dbacbff 100644 --- a/addons/im/im.py +++ b/addons/im/im.py @@ -18,19 +18,19 @@ # along with this program. If not, see . # ############################################################################## +import datetime +import json +import logging +import select +import time import openerp import openerp.tools.config import openerp.modules.registry -import openerp.addons.web.http as http -from openerp.addons.web.http import request -from openerp.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT -import datetime +from openerp import http +from openerp.http import request from openerp.osv import osv, fields -import time -import logging -import json -import select +from openerp.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT _logger = logging.getLogger(__name__) diff --git a/addons/im_livechat/im_livechat.py b/addons/im_livechat/im_livechat.py index 81d3d61c6b4..cf8060596d7 100644 --- a/addons/im_livechat/im_livechat.py +++ b/addons/im_livechat/im_livechat.py @@ -19,15 +19,16 @@ # ############################################################################## -import openerp -import openerp.addons.im.im as im import json import random import jinja2 + +import openerp +import openerp.addons.im.im as im from openerp.osv import osv, fields from openerp import tools -import openerp.addons.web.http as http -from openerp.addons.web.http import request +from openerp.http as http +from openerp.http import request env = jinja2.Environment( loader=jinja2.PackageLoader('openerp.addons.im_livechat', "."), diff --git a/addons/mail/controllers/main.py b/addons/mail/controllers/main.py index c5ef8bac92d..c2e15c4a12f 100644 --- a/addons/mail/controllers/main.py +++ b/addons/mail/controllers/main.py @@ -3,7 +3,7 @@ import psycopg2 import openerp from openerp import SUPERUSER_ID -import openerp.addons.web.http as http +from openerp import http from openerp.addons.web.controllers.main import content_disposition diff --git a/addons/mass_mailing/controllers/main.py b/addons/mass_mailing/controllers/main.py index 3ca238256b1..ac1531d7192 100644 --- a/addons/mass_mailing/controllers/main.py +++ b/addons/mass_mailing/controllers/main.py @@ -1,7 +1,6 @@ -import openerp.addons.web.http as http -from openerp.addons.web.http import request - +from openerp import http +from openerp.http import request class MassMailController(http.Controller): @http.route('/mail/track//blank.gif', type='http', auth='admin') diff --git a/addons/point_of_sale/controllers/main.py b/addons/point_of_sale/controllers/main.py index 419f5bc2efa..2461fe90b79 100644 --- a/addons/point_of_sale/controllers/main.py +++ b/addons/point_of_sale/controllers/main.py @@ -6,8 +6,8 @@ import openerp import time import random -from openerp.addons.web import http -from openerp.addons.web.http import request +from openerp import http +from openerp.http import request from openerp.addons.web.controllers.main import manifest_list, module_boot, html_template _logger = logging.getLogger(__name__) From 2e3af6f3d0f799e4779f3d5e62bc7bfa118242f3 Mon Sep 17 00:00:00 2001 From: Antony Lesuisse Date: Wed, 23 Oct 2013 00:52:47 +0200 Subject: [PATCH 03/61] import cleanups part2 bzr revid: al@openerp.com-20131022225247-3zv7hjz1iidnb6zl --- addons/edi/controllers/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/edi/controllers/main.py b/addons/edi/controllers/main.py index 3f77cced386..149c0f62ff6 100644 --- a/addons/edi/controllers/main.py +++ b/addons/edi/controllers/main.py @@ -4,7 +4,7 @@ import urllib import openerp import openerp.addons.web.controllers.main as webmain -class EDI(openerpweb.Controller): +class EDI(openerp.http.Controller): @openerp.http.route('/edi/import_url', type='http', auth='none') def import_url(self, url): From 737586aa4e6f21540c8f18b8e02f2f9cd389021a Mon Sep 17 00:00:00 2001 From: Antony Lesuisse Date: Sun, 27 Oct 2013 17:40:23 +0100 Subject: [PATCH 04/61] typo bzr revid: al@openerp.com-20131027164023-ky1nthy56fmqr4f2 --- addons/im_livechat/im_livechat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/im_livechat/im_livechat.py b/addons/im_livechat/im_livechat.py index cf8060596d7..b21a31d8362 100644 --- a/addons/im_livechat/im_livechat.py +++ b/addons/im_livechat/im_livechat.py @@ -27,7 +27,7 @@ import openerp import openerp.addons.im.im as im from openerp.osv import osv, fields from openerp import tools -from openerp.http as http +from openerp import http from openerp.http import request env = jinja2.Environment( From 401099d35841bc347559eca86c195464d857f5d9 Mon Sep 17 00:00:00 2001 From: Antony Lesuisse Date: Sun, 27 Oct 2013 18:17:17 +0100 Subject: [PATCH 05/61] [IMP] move http layer from openerp-web to openobject-server, convert controllers to the new route api bzr revid: al@openerp.com-20131027171717-dzqx2264p152lq8y --- addons/web/__init__.py | 8 +- addons/web/controllers/main.py | 5 +- addons/web/controllers/testing.py | 7 +- addons/web/doc/web_controllers.rst | 4 +- addons/web/http.py | 1116 ------------------------ addons/web/tests/common.py | 2 +- addons/web/tests/test_dataset.py | 2 +- addons/web/tests/test_menu.py | 2 +- addons/web_diagram/controllers/main.py | 4 +- 9 files changed, 19 insertions(+), 1131 deletions(-) delete mode 100644 addons/web/http.py diff --git a/addons/web/__init__.py b/addons/web/__init__.py index e01969ddcca..5a0289cef6d 100644 --- a/addons/web/__init__.py +++ b/addons/web/__init__.py @@ -1,4 +1,10 @@ -import http +import sys + +# Mock deprecated openerp.addons.web.http module +import openerp.http +sys.modules['openerp.addons.web.http'] = openerp.http +http = openerp.http + import controllers import cli diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index 7a52f289f8f..4c95f4d5f32 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -33,10 +33,9 @@ import openerp import openerp.modules.registry from openerp.tools.translate import _ from openerp.tools import config +from openerp import http -from .. import http - -from openerp.addons.web.http import request +from openerp.http import request #---------------------------------------------------------- # OpenERP Web helpers diff --git a/addons/web/controllers/testing.py b/addons/web/controllers/testing.py index b51b12fc53d..0160f0f28cd 100644 --- a/addons/web/controllers/testing.py +++ b/addons/web/controllers/testing.py @@ -9,11 +9,10 @@ import os from mako.template import Template from openerp.modules import module +from openerp import http +from openerp.http import request from .main import module_topological_sort -from .. import http - -from ..http import request NOMODULE_TEMPLATE = Template(u""" @@ -171,4 +170,4 @@ class TestRunnerController(http.Controller): @http.route('/web/tests/get_session_value', type='json', auth="none") def get_session_value(self): - return request.session.some_test_value \ No newline at end of file + return request.session.some_test_value diff --git a/addons/web/doc/web_controllers.rst b/addons/web/doc/web_controllers.rst index 71ff9fb8416..442d2b0caa7 100644 --- a/addons/web/doc/web_controllers.rst +++ b/addons/web/doc/web_controllers.rst @@ -35,8 +35,8 @@ Now you can put the following content in ``controllers/my_controllers.py``: :: - import openerp.addons.web.http as http - from openerp.addons.web.http import request + import openerp.http as http + from openerp.http import request Controller Declaration diff --git a/addons/web/http.py b/addons/web/http.py deleted file mode 100644 index 7bad66cb7c4..00000000000 --- a/addons/web/http.py +++ /dev/null @@ -1,1116 +0,0 @@ -# -*- coding: utf-8 -*- -#---------------------------------------------------------- -# OpenERP Web HTTP layer -#---------------------------------------------------------- -import ast -import cgi -import contextlib -import functools -import getpass -import logging -import mimetypes -import os -import pprint -import random -import sys -import tempfile -import threading -import time -import traceback -import urlparse -import uuid -import errno -import re -import warnings - -import babel.core -import simplejson -import werkzeug.contrib.sessions -import werkzeug.datastructures -import werkzeug.exceptions -import werkzeug.utils -import werkzeug.wrappers -import werkzeug.wsgi -import werkzeug.routing as routing -import urllib -import urllib2 - -import openerp -from openerp.service import security, model as service_model -from openerp.tools import config - -import inspect -import functools - -_logger = logging.getLogger(__name__) - -#---------------------------------------------------------- -# RequestHandler -#---------------------------------------------------------- -class WebRequest(object): - """ Parent class for all OpenERP Web request types, mostly deals with - initialization and setup of the request object (the dispatching itself has - to be handled by the subclasses) - - :param request: a wrapped werkzeug Request object - :type request: :class:`werkzeug.wrappers.BaseRequest` - - .. attribute:: httprequest - - the original :class:`werkzeug.wrappers.Request` object provided to the - request - - .. attribute:: httpsession - - .. deprecated:: 8.0 - - Use ``self.session`` instead. - - .. attribute:: params - - :class:`~collections.Mapping` of request parameters, not generally - useful as they're provided directly to the handler method as keyword - arguments - - .. attribute:: session_id - - opaque identifier for the :class:`session.OpenERPSession` instance of - the current request - - .. attribute:: session - - a :class:`OpenERPSession` holding the HTTP session data for the - current http session - - .. attribute:: context - - :class:`~collections.Mapping` of context values for the current request - - .. attribute:: db - - ``str``, the name of the database linked to the current request. Can be ``None`` - if the current request uses the ``none`` authentication. - - .. attribute:: uid - - ``int``, the id of the user related to the current request. Can be ``None`` - if the current request uses the ``none`` authenticatoin. - """ - def __init__(self, httprequest): - self.httprequest = httprequest - self.httpresponse = None - self.httpsession = httprequest.session - self.session = httprequest.session - self.session_id = httprequest.session.sid - self.disable_db = False - self.uid = None - self.func = None - self.auth_method = None - self._cr_cm = None - self._cr = None - self.func_request_type = None - # set db/uid trackers - they're cleaned up at the WSGI - # dispatching phase in openerp.service.wsgi_server.application - if self.db: - threading.current_thread().dbname = self.db - if self.session.uid: - threading.current_thread().uid = self.session.uid - self.context = dict(self.session.context) - self.lang = self.context["lang"] - - def _authenticate(self): - if self.session.uid: - try: - self.session.check_security() - except SessionExpiredException, e: - self.session.logout() - raise SessionExpiredException("Session expired for request %s" % self.httprequest) - auth_methods[self.auth_method]() - @property - def registry(self): - """ - The registry to the database linked to this request. Can be ``None`` if the current request uses the - ``none'' authentication. - """ - return openerp.modules.registry.RegistryManager.get(self.db) if self.db else None - - @property - def db(self): - """ - The registry to the database linked to this request. Can be ``None`` if the current request uses the - ``none'' authentication. - """ - return self.session.db if not self.disable_db else None - - @property - def cr(self): - """ - The cursor initialized for the current method call. If the current request uses the ``none`` authentication - trying to access this property will raise an exception. - """ - # some magic to lazy create the cr - if not self._cr_cm: - self._cr_cm = self.registry.cursor() - self._cr = self._cr_cm.__enter__() - return self._cr - - def _call_function(self, *args, **kwargs): - self._authenticate() - try: - # ugly syntax only to get the __exit__ arguments to pass to self._cr - request = self - class with_obj(object): - def __enter__(self): - pass - def __exit__(self, *args): - if request._cr_cm: - request._cr_cm.__exit__(*args) - request._cr_cm = None - request._cr = None - - with with_obj(): - if self.func_request_type != self._request_type: - raise Exception("%s, %s: Function declared as capable of handling request of type '%s' but called with a request of type '%s'" \ - % (self.func, self.httprequest.path, self.func_request_type, self._request_type)) - return self.func(*args, **kwargs) - finally: - # just to be sure no one tries to re-use the request - self.disable_db = True - self.uid = None - - @property - def debug(self): - return 'debug' in self.httprequest.args - - @contextlib.contextmanager - def registry_cr(self): - warnings.warn('please use request.registry and request.cr directly', DeprecationWarning) - yield (self.registry, self.cr) - -def auth_method_user(): - request.uid = request.session.uid - if not request.uid: - raise SessionExpiredException("Session expired") - -def auth_method_admin(): - if not request.db: - raise SessionExpiredException("No valid database for request %s" % request.httprequest) - request.uid = openerp.SUPERUSER_ID - -def auth_method_none(): - request.disable_db = True - request.uid = None - -auth_methods = { - "user": auth_method_user, - "admin": auth_method_admin, - "none": auth_method_none, -} - -def route(route, type="http", auth="user"): - """ - Decorator marking the decorated method as being a handler for requests. The method must be part of a subclass - of ``Controller``. - - :param route: string or array. The route part that will determine which http requests will match the decorated - method. Can be a single string or an array of strings. See werkzeug's routing documentation for the format of - route expression ( http://werkzeug.pocoo.org/docs/routing/ ). - :param type: The type of request, can be ``'http'`` or ``'json'``. - :param auth: The type of authentication method, can on of the following: - - * ``user``: The user must be authenticated and the current request will perform using the rights of the - user. - * ``admin``: The user may not be authenticated and the current request will perform using the admin user. - * ``none``: The method is always active, even if there is no database. Mainly used by the framework and - authentication modules. There request code will not have any facilities to access the database nor have any - configuration indicating the current database nor the current user. - """ - assert type in ["http", "json"] - assert auth in auth_methods.keys() - def decorator(f): - if isinstance(route, list): - f.routes = route - else: - f.routes = [route] - f.exposed = type - if getattr(f, "auth", None) is None: - f.auth = auth - return f - return decorator - -def reject_nonliteral(dct): - if '__ref' in dct: - raise ValueError( - "Non literal contexts can not be sent to the server anymore (%r)" % (dct,)) - return dct - -class JsonRequest(WebRequest): - """ JSON-RPC2 over HTTP. - - Sucessful request:: - - --> {"jsonrpc": "2.0", - "method": "call", - "params": {"context": {}, - "arg1": "val1" }, - "id": null} - - <-- {"jsonrpc": "2.0", - "result": { "res1": "val1" }, - "id": null} - - Request producing a error:: - - --> {"jsonrpc": "2.0", - "method": "call", - "params": {"context": {}, - "arg1": "val1" }, - "id": null} - - <-- {"jsonrpc": "2.0", - "error": {"code": 1, - "message": "End user error message.", - "data": {"code": "codestring", - "debug": "traceback" } }, - "id": null} - - """ - _request_type = "json" - - def __init__(self, *args): - super(JsonRequest, self).__init__(*args) - - self.jsonp_handler = None - - args = self.httprequest.args - jsonp = args.get('jsonp') - self.jsonp = jsonp - request = None - request_id = args.get('id') - - if jsonp and self.httprequest.method == 'POST': - # jsonp 2 steps step1 POST: save call - def handler(): - self.session.jsonp_requests[request_id] = self.httprequest.form['r'] - self.session.modified = True - headers=[('Content-Type', 'text/plain; charset=utf-8')] - r = werkzeug.wrappers.Response(request_id, headers=headers) - return r - self.jsonp_handler = handler - return - elif jsonp and args.get('r'): - # jsonp method GET - request = args.get('r') - elif jsonp and request_id: - # jsonp 2 steps step2 GET: run and return result - request = self.session.jsonp_requests.pop(request_id, "") - else: - # regular jsonrpc2 - request = self.httprequest.stream.read() - - # Read POST content or POST Form Data named "request" - self.jsonrequest = simplejson.loads(request, object_hook=reject_nonliteral) - self.params = dict(self.jsonrequest.get("params", {})) - self.context = self.params.pop('context', self.session.context) - - def dispatch(self): - """ Calls the method asked for by the JSON-RPC2 or JSONP request - """ - if self.jsonp_handler: - return self.jsonp_handler() - response = {"jsonrpc": "2.0" } - error = None - - try: - response['id'] = self.jsonrequest.get('id') - response["result"] = self._call_function(**self.params) - except AuthenticationError, e: - _logger.exception("Exception during JSON request handling.") - se = serialize_exception(e) - error = { - 'code': 100, - 'message': "OpenERP Session Invalid", - 'data': se - } - except Exception, e: - _logger.exception("Exception during JSON request handling.") - se = serialize_exception(e) - error = { - 'code': 200, - 'message': "OpenERP Server Error", - 'data': se - } - if error: - response["error"] = error - - if self.jsonp: - # If we use jsonp, that's mean we are called from another host - # Some browser (IE and Safari) do no allow third party cookies - # We need then to manage http sessions manually. - response['session_id'] = self.session_id - mime = 'application/javascript' - body = "%s(%s);" % (self.jsonp, simplejson.dumps(response),) - else: - mime = 'application/json' - body = simplejson.dumps(response) - - r = werkzeug.wrappers.Response(body, headers=[('Content-Type', mime), ('Content-Length', len(body))]) - return r - -def serialize_exception(e): - tmp = { - "name": type(e).__module__ + "." + type(e).__name__ if type(e).__module__ else type(e).__name__, - "debug": traceback.format_exc(), - "message": u"%s" % e, - "arguments": to_jsonable(e.args), - } - if isinstance(e, openerp.osv.osv.except_osv): - tmp["exception_type"] = "except_osv" - elif isinstance(e, openerp.exceptions.Warning): - tmp["exception_type"] = "warning" - elif isinstance(e, openerp.exceptions.AccessError): - tmp["exception_type"] = "access_error" - elif isinstance(e, openerp.exceptions.AccessDenied): - tmp["exception_type"] = "access_denied" - return tmp - -def to_jsonable(o): - if isinstance(o, str) or isinstance(o,unicode) or isinstance(o, int) or isinstance(o, long) \ - or isinstance(o, bool) or o is None or isinstance(o, float): - return o - if isinstance(o, list) or isinstance(o, tuple): - return [to_jsonable(x) for x in o] - if isinstance(o, dict): - tmp = {} - for k, v in o.items(): - tmp[u"%s" % k] = to_jsonable(v) - return tmp - return u"%s" % o - -def jsonrequest(f): - """ - .. deprecated:: 8.0 - - Use the ``route()`` decorator instead. - """ - f.combine = True - base = f.__name__.lstrip('/') - if f.__name__ == "index": - base = "" - return route([base, base + "/"], type="json", auth="user")(f) - -class HttpRequest(WebRequest): - """ Regular GET/POST request - """ - _request_type = "http" - - def __init__(self, *args): - super(HttpRequest, self).__init__(*args) - params = dict(self.httprequest.args) - params.update(self.httprequest.form) - params.update(self.httprequest.files) - params.pop('session_id', None) - self.params = params - - def dispatch(self): - try: - r = self._call_function(**self.params) - except werkzeug.exceptions.HTTPException, e: - r = e - except Exception, e: - _logger.exception("An exception occured during an http request") - se = serialize_exception(e) - error = { - 'code': 200, - 'message': "OpenERP Server Error", - 'data': se - } - r = werkzeug.exceptions.InternalServerError(cgi.escape(simplejson.dumps(error))) - else: - if not r: - r = werkzeug.wrappers.Response(status=204) # no content - return r - - def make_response(self, data, headers=None, cookies=None): - """ Helper for non-HTML responses, or HTML responses with custom - response headers or cookies. - - While handlers can just return the HTML markup of a page they want to - send as a string if non-HTML data is returned they need to create a - complete response object, or the returned data will not be correctly - interpreted by the clients. - - :param basestring data: response body - :param headers: HTTP headers to set on the response - :type headers: ``[(name, value)]`` - :param collections.Mapping cookies: cookies to set on the client - """ - response = werkzeug.wrappers.Response(data, headers=headers) - if cookies: - for k, v in cookies.iteritems(): - response.set_cookie(k, v) - return response - - def not_found(self, description=None): - """ Helper for 404 response, return its result from the method - """ - return werkzeug.exceptions.NotFound(description) - -def httprequest(f): - """ - .. deprecated:: 8.0 - - Use the ``route()`` decorator instead. - """ - f.combine = True - base = f.__name__.lstrip('/') - if f.__name__ == "index": - base = "" - return route([base, base + "/"], type="http", auth="user")(f) - -#---------------------------------------------------------- -# Local storage of requests -#---------------------------------------------------------- -from werkzeug.local import LocalStack - -_request_stack = LocalStack() - - -@contextlib.contextmanager -def set_request(req): - _request_stack.push(req) - try: - yield - finally: - _request_stack.pop() - - -request = _request_stack() -""" - A global proxy that always redirect to the current request object. -""" - -#---------------------------------------------------------- -# Controller registration with a metaclass -#---------------------------------------------------------- -addons_module = {} -addons_manifest = {} -controllers_per_module = {} - -class ControllerType(type): - def __init__(cls, name, bases, attrs): - super(ControllerType, cls).__init__(name, bases, attrs) - - # flag old-style methods with req as first argument - for k, v in attrs.items(): - if inspect.isfunction(v): - spec = inspect.getargspec(v) - first_arg = spec.args[1] if len(spec.args) >= 2 else None - if first_arg in ["req", "request"]: - v._first_arg_is_req = True - - # store the controller in the controllers list - name_class = ("%s.%s" % (cls.__module__, cls.__name__), cls) - class_path = name_class[0].split(".") - if not class_path[:2] == ["openerp", "addons"]: - return - # we want to know all modules that have controllers - module = class_path[2] - # but we only store controllers directly inheriting from Controller - if not "Controller" in globals() or not Controller in bases: - return - controllers_per_module.setdefault(module, []).append(name_class) - -class Controller(object): - __metaclass__ = ControllerType - -############################# -# OpenERP Sessions # -############################# - -class AuthenticationError(Exception): - pass - -class SessionExpiredException(Exception): - pass - -class Service(object): - """ - .. deprecated:: 8.0 - Use ``openerp.netsvc.dispatch_rpc()`` instead. - """ - def __init__(self, session, service_name): - self.session = session - self.service_name = service_name - - def __getattr__(self, method): - def proxy_method(*args): - result = openerp.netsvc.dispatch_rpc(self.service_name, method, args) - return result - return proxy_method - -class Model(object): - """ - .. deprecated:: 8.0 - Use the resistry and cursor in ``openerp.addons.web.http.request`` instead. - """ - def __init__(self, session, model): - self.session = session - self.model = model - self.proxy = self.session.proxy('object') - - def __getattr__(self, method): - self.session.assert_valid() - def proxy(*args, **kw): - # Can't provide any retro-compatibility for this case, so we check it and raise an Exception - # to tell the programmer to adapt his code - if not request.db or not request.uid or self.session.db != request.db \ - or self.session.uid != request.uid: - raise Exception("Trying to use Model with badly configured database or user.") - - mod = request.registry.get(self.model) - if method.startswith('_'): - raise Exception("Access denied") - meth = getattr(mod, method) - cr = request.cr - result = meth(cr, request.uid, *args, **kw) - # reorder read - if method == "read": - if isinstance(result, list) and len(result) > 0 and "id" in result[0]: - index = {} - for r in result: - index[r['id']] = r - result = [index[x] for x in args[0] if x in index] - return result - return proxy - -class OpenERPSession(werkzeug.contrib.sessions.Session): - def __init__(self, *args, **kwargs): - self.inited = False - self.modified = False - super(OpenERPSession, self).__init__(*args, **kwargs) - self.inited = True - self._default_values() - self.modified = False - - def __getattr__(self, attr): - return self.get(attr, None) - def __setattr__(self, k, v): - if getattr(self, "inited", False): - try: - object.__getattribute__(self, k) - except: - return self.__setitem__(k, v) - object.__setattr__(self, k, v) - - def authenticate(self, db, login=None, password=None, uid=None): - """ - Authenticate the current user with the given db, login and password. If successful, store - the authentication parameters in the current session and request. - - :param uid: If not None, that user id will be used instead the login to authenticate the user. - """ - - if uid is None: - wsgienv = request.httprequest.environ - env = dict( - base_location=request.httprequest.url_root.rstrip('/'), - HTTP_HOST=wsgienv['HTTP_HOST'], - REMOTE_ADDR=wsgienv['REMOTE_ADDR'], - ) - uid = openerp.netsvc.dispatch_rpc('common', 'authenticate', [db, login, password, env]) - else: - security.check(db, uid, password) - self.db = db - self.uid = uid - self.login = login - self.password = password - request.uid = uid - request.disable_db = False - - if uid: self.get_context() - return uid - - def check_security(self): - """ - Chech the current authentication parameters to know if those are still valid. This method - should be called at each request. If the authentication fails, a ``SessionExpiredException`` - is raised. - """ - if not self.db or not self.uid: - raise SessionExpiredException("Session expired") - security.check(self.db, self.uid, self.password) - - def logout(self): - for k in self.keys(): - del self[k] - self._default_values() - - def _default_values(self): - self.setdefault("db", None) - self.setdefault("uid", None) - self.setdefault("login", None) - self.setdefault("password", None) - self.setdefault("context", {'tz': "UTC", "uid": None}) - self.setdefault("jsonp_requests", {}) - - def get_context(self): - """ - Re-initializes the current user's session context (based on - his preferences) by calling res.users.get_context() with the old - context. - - :returns: the new context - """ - assert self.uid, "The user needs to be logged-in to initialize his context" - self.context = request.registry.get('res.users').context_get(request.cr, request.uid) or {} - self.context['uid'] = self.uid - self._fix_lang(self.context) - return self.context - - def _fix_lang(self, context): - """ OpenERP provides languages which may not make sense and/or may not - be understood by the web client's libraries. - - Fix those here. - - :param dict context: context to fix - """ - lang = context['lang'] - - # inane OpenERP locale - if lang == 'ar_AR': - lang = 'ar' - - # lang to lang_REGION (datejs only handles lang_REGION, no bare langs) - if lang in babel.core.LOCALE_ALIASES: - lang = babel.core.LOCALE_ALIASES[lang] - - context['lang'] = lang or 'en_US' - - """ - Damn properties for retro-compatibility. All of that is deprecated, all - of that. - """ - @property - def _db(self): - return self.db - @_db.setter - def _db(self, value): - self.db = value - @property - def _uid(self): - return self.uid - @_uid.setter - def _uid(self, value): - self.uid = value - @property - def _login(self): - return self.login - @_login.setter - def _login(self, value): - self.login = value - @property - def _password(self): - return self.password - @_password.setter - def _password(self, value): - self.password = value - - def send(self, service_name, method, *args): - """ - .. deprecated:: 8.0 - Use ``openerp.netsvc.dispatch_rpc()`` instead. - """ - return openerp.netsvc.dispatch_rpc(service_name, method, args) - - def proxy(self, service): - """ - .. deprecated:: 8.0 - Use ``openerp.netsvc.dispatch_rpc()`` instead. - """ - return Service(self, service) - - def assert_valid(self, force=False): - """ - .. deprecated:: 8.0 - Use ``check_security()`` instead. - - Ensures this session is valid (logged into the openerp server) - """ - if self.uid and not force: - return - # TODO use authenticate instead of login - self.uid = self.proxy("common").login(self.db, self.login, self.password) - if not self.uid: - raise AuthenticationError("Authentication failure") - - def ensure_valid(self): - """ - .. deprecated:: 8.0 - Use ``check_security()`` instead. - """ - if self.uid: - try: - self.assert_valid(True) - except Exception: - self.uid = None - - def execute(self, model, func, *l, **d): - """ - .. deprecated:: 8.0 - Use the resistry and cursor in ``openerp.addons.web.http.request`` instead. - """ - model = self.model(model) - r = getattr(model, func)(*l, **d) - return r - - def exec_workflow(self, model, id, signal): - """ - .. deprecated:: 8.0 - Use the resistry and cursor in ``openerp.addons.web.http.request`` instead. - """ - self.assert_valid() - r = self.proxy('object').exec_workflow(self.db, self.uid, self.password, model, signal, id) - return r - - def model(self, model): - """ - .. deprecated:: 8.0 - Use the resistry and cursor in ``openerp.addons.web.http.request`` instead. - - Get an RPC proxy for the object ``model``, bound to this session. - - :param model: an OpenERP model name - :type model: str - :rtype: a model object - """ - if not self.db: - raise SessionExpiredException("Session expired") - - return Model(self, model) - -def session_gc(session_store): - if random.random() < 0.001: - # we keep session one week - last_week = time.time() - 60*60*24*7 - for fname in os.listdir(session_store.path): - path = os.path.join(session_store.path, fname) - try: - if os.path.getmtime(path) < last_week: - os.unlink(path) - except OSError: - pass - -#---------------------------------------------------------- -# WSGI Application -#---------------------------------------------------------- -# Add potentially missing (older ubuntu) font mime types -mimetypes.add_type('application/font-woff', '.woff') -mimetypes.add_type('application/vnd.ms-fontobject', '.eot') -mimetypes.add_type('application/x-font-ttf', '.ttf') - -class DisableCacheMiddleware(object): - def __init__(self, app): - self.app = app - def __call__(self, environ, start_response): - def start_wrapped(status, headers): - referer = environ.get('HTTP_REFERER', '') - parsed = urlparse.urlparse(referer) - debug = parsed.query.count('debug') >= 1 - - new_headers = [] - unwanted_keys = ['Last-Modified'] - if debug: - new_headers = [('Cache-Control', 'no-cache')] - unwanted_keys += ['Expires', 'Etag', 'Cache-Control'] - - for k, v in headers: - if k not in unwanted_keys: - new_headers.append((k, v)) - - start_response(status, new_headers) - return self.app(environ, start_wrapped) - -def session_path(): - try: - import pwd - username = pwd.getpwuid(os.geteuid()).pw_name - except ImportError: - try: - username = getpass.getuser() - except Exception: - username = "unknown" - path = os.path.join(tempfile.gettempdir(), "oe-sessions-" + username) - try: - os.mkdir(path, 0700) - except OSError as exc: - if exc.errno == errno.EEXIST: - # directory exists: ensure it has the correct permissions - # this will fail if the directory is not owned by the current user - os.chmod(path, 0700) - else: - raise - return path - -class Root(object): - """Root WSGI application for the OpenERP Web Client. - """ - def __init__(self): - self.addons = {} - self.statics = {} - - self.no_db_router = None - - self.load_addons() - - # Setup http sessions - path = session_path() - self.session_store = werkzeug.contrib.sessions.FilesystemSessionStore(path, session_class=OpenERPSession) - _logger.debug('HTTP sessions stored in: %s', path) - - - def __call__(self, environ, start_response): - """ Handle a WSGI request - """ - return self.dispatch(environ, start_response) - - def dispatch(self, environ, start_response): - """ - Performs the actual WSGI dispatching for the application. - """ - try: - httprequest = werkzeug.wrappers.Request(environ) - httprequest.parameter_storage_class = werkzeug.datastructures.ImmutableDict - httprequest.app = self - - session_gc(self.session_store) - - sid = httprequest.args.get('session_id') - explicit_session = True - if not sid: - sid = httprequest.headers.get("X-Openerp-Session-Id") - if not sid: - sid = httprequest.cookies.get('session_id') - explicit_session = False - if sid is None: - httprequest.session = self.session_store.new() - else: - httprequest.session = self.session_store.get(sid) - - self._find_db(httprequest) - - if not "lang" in httprequest.session.context: - lang = httprequest.accept_languages.best or "en_US" - lang = babel.core.LOCALE_ALIASES.get(lang, lang).replace('-', '_') - httprequest.session.context["lang"] = lang - - request = self._build_request(httprequest) - db = request.db - - if db: - openerp.modules.registry.RegistryManager.check_registry_signaling(db) - - with set_request(request): - self.find_handler() - result = request.dispatch() - - if db: - openerp.modules.registry.RegistryManager.signal_caches_change(db) - - if isinstance(result, basestring): - headers=[('Content-Type', 'text/html; charset=utf-8'), ('Content-Length', len(result))] - response = werkzeug.wrappers.Response(result, headers=headers) - else: - response = result - - if httprequest.session.should_save: - self.session_store.save(httprequest.session) - if not explicit_session and hasattr(response, 'set_cookie'): - response.set_cookie('session_id', httprequest.session.sid, max_age=90 * 24 * 60 * 60) - - return response(environ, start_response) - except werkzeug.exceptions.HTTPException, e: - return e(environ, start_response) - - def _find_db(self, httprequest): - db = db_monodb(httprequest) - if db != httprequest.session.db: - httprequest.session.logout() - httprequest.session.db = db - - def _build_request(self, httprequest): - if httprequest.args.get('jsonp'): - return JsonRequest(httprequest) - - if httprequest.mimetype == "application/json": - return JsonRequest(httprequest) - else: - return HttpRequest(httprequest) - - def load_addons(self): - """ Load all addons from addons patch containg static files and - controllers and configure them. """ - - for addons_path in openerp.modules.module.ad_paths: - for module in sorted(os.listdir(str(addons_path))): - if module not in addons_module: - manifest_path = os.path.join(addons_path, module, '__openerp__.py') - path_static = os.path.join(addons_path, module, 'static') - if os.path.isfile(manifest_path) and os.path.isdir(path_static): - manifest = ast.literal_eval(open(manifest_path).read()) - manifest['addons_path'] = addons_path - _logger.debug("Loading %s", module) - if 'openerp.addons' in sys.modules: - m = __import__('openerp.addons.' + module) - else: - m = __import__(module) - addons_module[module] = m - addons_manifest[module] = manifest - self.statics['/%s/static' % module] = path_static - - app = werkzeug.wsgi.SharedDataMiddleware(self.dispatch, self.statics) - self.dispatch = DisableCacheMiddleware(app) - - def _build_router(self, db): - _logger.info("Generating routing configuration for database %s" % db) - routing_map = routing.Map(strict_slashes=False) - - def gen(modules, nodb_only): - for module in modules: - for v in controllers_per_module[module]: - cls = v[1] - - subclasses = cls.__subclasses__() - subclasses = [c for c in subclasses if c.__module__.startswith('openerp.addons.') and - c.__module__.split(".")[2] in modules] - if subclasses: - name = "%s (extended by %s)" % (cls.__name__, ', '.join(sub.__name__ for sub in subclasses)) - cls = type(name, tuple(reversed(subclasses)), {}) - - o = cls() - members = inspect.getmembers(o) - for mk, mv in members: - if inspect.ismethod(mv) and getattr(mv, 'exposed', False) and \ - nodb_only == (getattr(mv, "auth", "none") == "none"): - for url in mv.routes: - if getattr(mv, "combine", False): - url = o._cp_path.rstrip('/') + '/' + url.lstrip('/') - if url.endswith("/") and len(url) > 1: - url = url[: -1] - routing_map.add(routing.Rule(url, endpoint=mv)) - - modules_set = set(controllers_per_module.keys()) - set(['web']) - # building all none methods - gen(["web"] + sorted(modules_set), True) - if not db: - return routing_map - - registry = openerp.modules.registry.RegistryManager.get(db) - with registry.cursor() as cr: - m = registry.get('ir.module.module') - ids = m.search(cr, openerp.SUPERUSER_ID, [('state', '=', 'installed'), ('name', '!=', 'web')]) - installed = set(x['name'] for x in m.read(cr, 1, ids, ['name'])) - modules_set = modules_set & installed - - # building all other methods - gen(["web"] + sorted(modules_set), False) - - return routing_map - - def get_db_router(self, db): - if db is None: - router = self.no_db_router - else: - router = getattr(openerp.modules.registry.RegistryManager.get(db), "werkzeug_http_router", None) - if not router: - router = self._build_router(db) - if db is None: - self.no_db_router = router - else: - openerp.modules.registry.RegistryManager.get(db).werkzeug_http_router = router - return router - - def find_handler(self): - """ - Tries to discover the controller handling the request for the path specified in the request. - """ - path = request.httprequest.path - urls = self.get_db_router(request.db).bind("") - func, arguments = urls.match(path) - arguments = dict([(k, v) for k, v in arguments.items() if not k.startswith("_ignored_")]) - - @service_model.check - def checked_call(dbname, *a, **kw): - return func(*a, **kw) - - def nfunc(*args, **kwargs): - kwargs.update(arguments) - if getattr(func, '_first_arg_is_req', False): - args = (request,) + args - - if request.db: - return checked_call(request.db, *args, **kwargs) - return func(*args, **kwargs) - - request.func = nfunc - request.auth_method = getattr(func, "auth", "user") - request.func_request_type = func.exposed - -root = None - -def db_list(force=False, httprequest=None): - httprequest = httprequest or request.httprequest - dbs = openerp.netsvc.dispatch_rpc("db", "list", [force]) - h = httprequest.environ['HTTP_HOST'].split(':')[0] - d = h.split('.')[0] - r = openerp.tools.config['dbfilter'].replace('%h', h).replace('%d', d) - dbs = [i for i in dbs if re.match(r, i)] - return dbs - -def db_monodb(httprequest=None): - """ - Magic function to find the current database. - - Implementation details: - - * Magic - * More magic - - Returns ``None`` if the magic is not magic enough. - """ - httprequest = httprequest or request.httprequest - db = None - redirect = None - - dbs = db_list(True, httprequest) - - # try the db already in the session - db_session = httprequest.session.db - if db_session in dbs: - return db_session - - # if dbfilters was specified when launching the server and there is - # only one possible db, we take that one - if openerp.tools.config['dbfilter'] != ".*" and len(dbs) == 1: - return dbs[0] - return None - -class CommonController(Controller): - - @route('/jsonrpc', type='json', auth="none") - def jsonrpc(self, service, method, args): - """ Method used by client APIs to contact OpenERP. """ - return openerp.netsvc.dispatch_rpc(service, method, args) - - @route('/gen_session_id', type='json', auth="none") - def gen_session_id(self): - nsession = root.session_store.new() - return nsession.sid - -def wsgi_postload(): - global root - root = Root() - openerp.wsgi.register_wsgi_handler(root) - -# vim:et:ts=4:sw=4: diff --git a/addons/web/tests/common.py b/addons/web/tests/common.py index f95bbbd42e1..ef0ad0987c7 100644 --- a/addons/web/tests/common.py +++ b/addons/web/tests/common.py @@ -3,7 +3,7 @@ import unittest2 import mock -from openerp.addons.web import http +from openerp import http class MockRequestCase(unittest2.TestCase): def setUp(self): diff --git a/addons/web/tests/test_dataset.py b/addons/web/tests/test_dataset.py index f017e5f609a..e89fadb13a7 100644 --- a/addons/web/tests/test_dataset.py +++ b/addons/web/tests/test_dataset.py @@ -2,7 +2,7 @@ from . import common import openerp.addons.web.controllers.main -from openerp.addons.web.http import request as req +from openerp.http import request as req class TestDataSetController(common.MockRequestCase): def setUp(self): diff --git a/addons/web/tests/test_menu.py b/addons/web/tests/test_menu.py index 8e8f3900670..fb9337a589b 100644 --- a/addons/web/tests/test_menu.py +++ b/addons/web/tests/test_menu.py @@ -4,7 +4,7 @@ import collections import mock import unittest2 -from openerp.addons.web.http import request as req +from openerp.http import request as req from . import common diff --git a/addons/web_diagram/controllers/main.py b/addons/web_diagram/controllers/main.py index 5dd625b80c0..d9a435f1924 100644 --- a/addons/web_diagram/controllers/main.py +++ b/addons/web_diagram/controllers/main.py @@ -1,8 +1,8 @@ import openerp -class DiagramView(openerp.addons.web.http.Controller): +class DiagramView(openerp.http.Controller): - @openerp.addons.web.http.route('/web_diagram/diagram/get_diagram_info', type='json', auth='user') + @openerp.http.route('/web_diagram/diagram/get_diagram_info', type='json', auth='user') def get_diagram_info(self, req, id, model, node, connector, src_node, des_node, label, **kw): From d5b4cd627c16930bd6798768ccd77af2bfcb9f87 Mon Sep 17 00:00:00 2001 From: Antony Lesuisse Date: Sun, 27 Oct 2013 18:31:43 +0100 Subject: [PATCH 06/61] more controller conversion to new route api bzr revid: al@openerp.com-20131027173143-tfxc7e7vqn7re6wb --- addons/base_import/controllers.py | 2 +- addons/board/controllers.py | 12 +++++------- addons/web_linkedin/web_linkedin.py | 9 +++------ 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/addons/base_import/controllers.py b/addons/base_import/controllers.py index cb41d27f44a..afaf06e99e0 100644 --- a/addons/base_import/controllers.py +++ b/addons/base_import/controllers.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- import simplejson -from openerp.addons.web.http import Controller, route +from openerp.http import Controller, route class ImportController(Controller): @route('/base_import/set_file') diff --git a/addons/board/controllers.py b/addons/board/controllers.py index 3bdebe1ef26..50148977216 100644 --- a/addons/board/controllers.py +++ b/addons/board/controllers.py @@ -4,14 +4,12 @@ from xml.etree import ElementTree import openerp from openerp.addons.web.controllers.main import load_actions_from_ir_values -class Board(openerp.addons.web.http.Controller): - _cp_path = '/board' - - @openerp.addons.web.http.jsonrequest - def add_to_dashboard(self, req, menu_id, action_id, context_to_save, domain, view_mode, name=''): +class Board(openerp.http.Controller): + @openerp.http.route('/board/add_to_dashboard', type='json', auth='user') + def add_to_dashboard(self, menu_id, action_id, context_to_save, domain, view_mode, name=''): + req = openerp.http.request # FIXME move this method to board.board model - dashboard_action = load_actions_from_ir_values( - req, 'action', 'tree_but_open', [('ir.ui.menu', menu_id)], False) + dashboard_action = load_actions_from_ir_values('action', 'tree_but_open', [('ir.ui.menu', menu_id)], False) if dashboard_action: action = dashboard_action[0][2] diff --git a/addons/web_linkedin/web_linkedin.py b/addons/web_linkedin/web_linkedin.py index 92adb30d3dd..a07dcd50ea0 100644 --- a/addons/web_linkedin/web_linkedin.py +++ b/addons/web_linkedin/web_linkedin.py @@ -26,13 +26,10 @@ from urlparse import urlparse, urlunparse import openerp from openerp.osv import fields, osv -class Binary(openerp.addons.web.http.Controller): - _cp_path = "/web_linkedin/binary" - - @openerp.addons.web.http.jsonrequest - def url2binary(self, req, url): +class Binary(openerp.http.Controller): + @openerp.http.route('/web_linkedin/binary/url2binary', type='json', auth='user') + def url2binary(self, url): """Used exclusively to load images from LinkedIn profiles, must not be used for anything else.""" - req.session.assert_valid(force=True) _scheme, _netloc, path, params, query, fragment = urlparse(url) # media.linkedin.com is the master domain for LinkedIn media (replicated to CDNs), # so forcing it should always work and prevents abusing this method to load arbitrary URLs From e142005a0169c9faebd585b28ba323b4c8691a79 Mon Sep 17 00:00:00 2001 From: Launchpad Translations on behalf of openerp <> Date: Mon, 28 Oct 2013 05:23:39 +0000 Subject: [PATCH 07/61] Launchpad automatic translations update. bzr revid: launchpad_translations_on_behalf_of_openerp-20131028052339-0v9n7leiirwlacok --- addons/purchase_double_validation/i18n/bs.po | 49 ++ addons/purchase_requisition/i18n/bs.po | 497 +++++++++++++++++++ addons/resource/i18n/bs.po | 364 ++++++++++++++ addons/sale_margin/i18n/bs.po | 48 ++ addons/sale_mrp/i18n/bs.po | 43 ++ addons/sale_order_dates/i18n/bs.po | 58 +++ 6 files changed, 1059 insertions(+) create mode 100644 addons/purchase_double_validation/i18n/bs.po create mode 100644 addons/purchase_requisition/i18n/bs.po create mode 100644 addons/resource/i18n/bs.po create mode 100644 addons/sale_margin/i18n/bs.po create mode 100644 addons/sale_mrp/i18n/bs.po create mode 100644 addons/sale_order_dates/i18n/bs.po diff --git a/addons/purchase_double_validation/i18n/bs.po b/addons/purchase_double_validation/i18n/bs.po new file mode 100644 index 00000000000..fd62a76192a --- /dev/null +++ b/addons/purchase_double_validation/i18n/bs.po @@ -0,0 +1,49 @@ +# Bosnian translation for openobject-addons +# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR , 2013. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-12-21 17:06+0000\n" +"PO-Revision-Date: 2013-10-27 14:43+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Bosnian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2013-10-28 05:23+0000\n" +"X-Generator: Launchpad (build 16810)\n" + +#. module: purchase_double_validation +#: model:ir.model,name:purchase_double_validation.model_purchase_config_settings +msgid "purchase.config.settings" +msgstr "purchase.config.settings" + +#. module: purchase_double_validation +#: view:purchase.order:0 +msgid "Purchase orders which are not approved yet." +msgstr "Prodajne narudžbe koje još nisu odobrene." + +#. module: purchase_double_validation +#: field:purchase.config.settings,limit_amount:0 +msgid "limit to require a second approval" +msgstr "ograniči da zahtjeva duplo odobrenje" + +#. module: purchase_double_validation +#: view:board.board:0 +#: model:ir.actions.act_window,name:purchase_double_validation.purchase_waiting +msgid "Purchase Orders Waiting Approval" +msgstr "Prodajne narudžbe koje čekaju odobrenje" + +#. module: purchase_double_validation +#: view:purchase.order:0 +msgid "To Approve" +msgstr "Za odobriti" + +#. module: purchase_double_validation +#: help:purchase.config.settings,limit_amount:0 +msgid "Amount after which validation of purchase is required." +msgstr "Iznos nakon kojeg je potrebno odobrenje kupovine" diff --git a/addons/purchase_requisition/i18n/bs.po b/addons/purchase_requisition/i18n/bs.po new file mode 100644 index 00000000000..58fe989b0a1 --- /dev/null +++ b/addons/purchase_requisition/i18n/bs.po @@ -0,0 +1,497 @@ +# Bosnian translation for openobject-addons +# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR , 2013. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-12-21 17:06+0000\n" +"PO-Revision-Date: 2013-10-27 14:23+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Bosnian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2013-10-28 05:23+0000\n" +"X-Generator: Launchpad (build 16810)\n" + +#. module: purchase_requisition +#: view:purchase.requisition:0 +msgid "Request a Quotation" +msgstr "Zahtjevaj predračun" + +#. module: purchase_requisition +#: selection:purchase.requisition,exclusive:0 +msgid "Multiple Requisitions" +msgstr "Višestruki zahtjevi" + +#. module: purchase_requisition +#: field:purchase.requisition.line,product_uom_id:0 +msgid "Product Unit of Measure" +msgstr "Jedinica mjere proizvoda" + +#. module: purchase_requisition +#: model:ir.actions.act_window,help:purchase_requisition.action_purchase_requisition +msgid "" +"

\n" +" Click to start a new purchase requisition process. \n" +"

\n" +" A purchase requisition is the step before a request for " +"quotation.\n" +" In a purchase requisition (or purchase tender), you can record " +"the\n" +" products you need to buy and trigger the creation of RfQs to\n" +" suppliers. After the negotiation, once you have reviewed all " +"the\n" +" supplier's offers, you can validate some and cancel others.\n" +"

\n" +" " +msgstr "" +"

\n" +" Kliknite da započnete novi proces zahtjeva nabavke. \n" +"

\n" +" Zahtjev nabavke je korak prije zahtjeva predračuna. U zahtjevu " +"nabavke\n" +" (ili nabavni tender) možete da zabilježite proizvode koje " +"trebate da\n" +" kupite i okinete kreiranje zahtjeva predračuna dobavljačima. " +"Nakon \n" +" pregovora, kada ste pregledali sve ponude, možete da odobrite " +"neke\n" +" i otkažete druge.\n" +"

\n" +" " + +#. module: purchase_requisition +#: view:purchase.requisition:0 +#: field:purchase.requisition,user_id:0 +msgid "Responsible" +msgstr "Odgovoran" + +#. module: purchase_requisition +#: view:purchase.requisition:0 +#: field:purchase.requisition,state:0 +msgid "Status" +msgstr "Status" + +#. module: purchase_requisition +#: view:purchase.requisition:0 +msgid "Send to Suppliers" +msgstr "Pošalji dobavljačima" + +#. module: purchase_requisition +#: view:purchase.requisition:0 +msgid "Group By..." +msgstr "Grupiši po..." + +#. module: purchase_requisition +#: view:purchase.requisition:0 +#: selection:purchase.requisition,state:0 +msgid "Purchase Done" +msgstr "Nabavka završena" + +#. module: purchase_requisition +#: field:purchase.requisition,message_follower_ids:0 +msgid "Followers" +msgstr "Pratioci" + +#. module: purchase_requisition +#: view:purchase.requisition:0 +msgid "Purchase Requisition in negociation" +msgstr "Nabavni zahtjevi u pregovorima" + +#. module: purchase_requisition +#: report:purchase.requisition:0 +#: field:purchase.requisition.partner,partner_id:0 +msgid "Supplier" +msgstr "Dobavljač" + +#. module: purchase_requisition +#: view:purchase.requisition:0 +#: selection:purchase.requisition,state:0 +msgid "New" +msgstr "Novi" + +#. module: purchase_requisition +#: report:purchase.requisition:0 +msgid "Product Detail" +msgstr "Detalji proizvoda" + +#. module: purchase_requisition +#: report:purchase.requisition:0 +msgid "Qty" +msgstr "Kol." + +#. module: purchase_requisition +#: report:purchase.requisition:0 +msgid "Type" +msgstr "Tip" + +#. module: purchase_requisition +#: model:ir.actions.act_window,name:purchase_requisition.action_purchase_requisition_partner +#: model:ir.actions.report.xml,name:purchase_requisition.report_purchase_requisition +#: model:ir.model,name:purchase_requisition.model_purchase_requisition +#: model:ir.module.category,name:purchase_requisition.module_category_purchase_requisition +#: field:product.product,purchase_requisition:0 +#: field:purchase.order,requisition_id:0 +#: view:purchase.requisition:0 +#: field:purchase.requisition.line,requisition_id:0 +#: view:purchase.requisition.partner:0 +msgid "Purchase Requisition" +msgstr "Zahtjev nabavke" + +#. module: purchase_requisition +#: model:ir.model,name:purchase_requisition.model_purchase_requisition_line +msgid "Purchase Requisition Line" +msgstr "Stavke zahtjeva nabavke" + +#. module: purchase_requisition +#: view:purchase.order:0 +msgid "Purchase Orders with requisition" +msgstr "Nabavne narudžbe sa zahtjevima" + +#. module: purchase_requisition +#: model:ir.model,name:purchase_requisition.model_product_product +#: field:purchase.requisition.line,product_id:0 +msgid "Product" +msgstr "Proizvod" + +#. module: purchase_requisition +#: view:purchase.requisition:0 +msgid "Quotations" +msgstr "Predračuni" + +#. module: purchase_requisition +#: view:purchase.requisition:0 +msgid "Terms and Conditions" +msgstr "Ugovori i Uslovi" + +#. module: purchase_requisition +#: report:purchase.requisition:0 +#: field:purchase.requisition,description:0 +msgid "Description" +msgstr "Opis" + +#. module: purchase_requisition +#: field:purchase.requisition,message_unread:0 +msgid "Unread Messages" +msgstr "Nepročitane poruke" + +#. module: purchase_requisition +#: field:purchase.requisition,company_id:0 +#: field:purchase.requisition.line,company_id:0 +msgid "Company" +msgstr "Kompanija" + +#. module: purchase_requisition +#: view:purchase.requisition.partner:0 +msgid "Create Quotation" +msgstr "Kreiraj predračun" + +#. module: purchase_requisition +#: help:purchase.requisition,message_ids:0 +msgid "Messages and communication history" +msgstr "Poruke i istorija komunikacije" + +#. module: purchase_requisition +#: view:purchase.requisition:0 +msgid "Approved by Supplier" +msgstr "Odobreno od strane dobavljača" + +#. module: purchase_requisition +#: view:purchase.requisition.partner:0 +msgid "or" +msgstr "ili" + +#. module: purchase_requisition +#: view:purchase.requisition:0 +msgid "Reset to Draft" +msgstr "Vrati u pripremu" + +#. module: purchase_requisition +#: view:purchase.requisition:0 +msgid "Current Purchase Requisition" +msgstr "Trenutni zahtjev nabavke" + +#. module: purchase_requisition +#: model:res.groups,name:purchase_requisition.group_purchase_requisition_user +msgid "User" +msgstr "Korisnik" + +#. module: purchase_requisition +#: report:purchase.requisition:0 +msgid "Order Reference" +msgstr "Referenca narudžbe" + +#. module: purchase_requisition +#: field:purchase.requisition,message_is_follower:0 +msgid "Is a Follower" +msgstr "Je pratilac" + +#. module: purchase_requisition +#: field:purchase.requisition.line,product_qty:0 +msgid "Quantity" +msgstr "Količina" + +#. module: purchase_requisition +#: view:purchase.requisition:0 +msgid "Unassigned Requisition" +msgstr "Nedodjeljeni zahtjevi" + +#. module: purchase_requisition +#: model:ir.actions.act_window,name:purchase_requisition.action_purchase_requisition +#: model:ir.ui.menu,name:purchase_requisition.menu_purchase_requisition_pro_mgt +msgid "Purchase Requisitions" +msgstr "Nabavni zahtjevi" + +#. module: purchase_requisition +#: report:purchase.requisition:0 +msgid "Quotation Detail" +msgstr "Detalji predračuna" + +#. module: purchase_requisition +#: code:addons/purchase_requisition/purchase_requisition.py:134 +#, python-format +msgid "" +"You have already one %s purchase order for this partner, you must cancel " +"this purchase order to create a new quotation." +msgstr "" +"Već imate jedan %s nabavnu narudžbu za tog partnera. Morate poništiti ovau " +"nabavnu narudžbu kako bi kreirali novi predračun." + +#. module: purchase_requisition +#: view:purchase.requisition:0 +msgid "End Date" +msgstr "Krajnji datum" + +#. module: purchase_requisition +#: report:purchase.requisition:0 +#: field:purchase.requisition,name:0 +msgid "Requisition Reference" +msgstr "Referenca zahtjeva" + +#. module: purchase_requisition +#: field:purchase.requisition,line_ids:0 +msgid "Products to Purchase" +msgstr "Proizvodi za nabavku" + +#. module: purchase_requisition +#: view:purchase.requisition:0 +#: selection:purchase.requisition,state:0 +msgid "Sent to Suppliers" +msgstr "Pošalji dobavljačima" + +#. module: purchase_requisition +#: view:purchase.requisition:0 +msgid "Search Purchase Requisition" +msgstr "Pretraži zahtjeve nabavke" + +#. module: purchase_requisition +#: code:addons/purchase_requisition/wizard/purchase_requisition_partner.py:41 +#, python-format +msgid "No Product in Tender." +msgstr "Nema proizvoda na tenderu." + +#. module: purchase_requisition +#: report:purchase.requisition:0 +msgid "Date Ordered" +msgstr "Datum naruđbe" + +#. module: purchase_requisition +#: field:purchase.requisition,message_ids:0 +msgid "Messages" +msgstr "Poruke" + +#. module: purchase_requisition +#: help:purchase.requisition,exclusive:0 +msgid "" +"Purchase Requisition (exclusive): On the confirmation of a purchase order, " +"it cancels the remaining purchase order.\n" +"Purchase Requisition(Multiple): It allows to have multiple purchase " +"orders.On confirmation of a purchase order it does not cancel the remaining " +"orders" +msgstr "" +"Zahtjev nabavke (ekskluzivni): Prilikom potvrde nabavne narudžbe , " +"poništava preostale nabavne narudžbe.\n" +"Zahtjev nabavke(višestruki): Omogućuje više nabavnih narudžbi. Prilikom " +"potvrde nabavne narudžbe ne poništava preostale narudžbe." + +#. module: purchase_requisition +#: view:purchase.requisition:0 +msgid "Cancel Purchase Order" +msgstr "Otkaži nabavnu narudžbu" + +#. module: purchase_requisition +#: model:ir.model,name:purchase_requisition.model_purchase_order +#: view:purchase.requisition:0 +msgid "Purchase Order" +msgstr "Nabavna narudžba" + +#. module: purchase_requisition +#: field:purchase.requisition,origin:0 +msgid "Source Document" +msgstr "Izvorni dokument" + +#. module: purchase_requisition +#: code:addons/purchase_requisition/wizard/purchase_requisition_partner.py:41 +#, python-format +msgid "Error!" +msgstr "Greška!" + +#. module: purchase_requisition +#: field:purchase.requisition,exclusive:0 +msgid "Requisition Type" +msgstr "Vrsta zahtjeva" + +#. module: purchase_requisition +#: view:purchase.requisition:0 +msgid "New Purchase Requisition" +msgstr "Novi zahtjev nabavke" + +#. module: purchase_requisition +#: view:purchase.requisition:0 +msgid "Products" +msgstr "Proizvodi" + +#. module: purchase_requisition +#: view:purchase.requisition:0 +msgid "Order Date" +msgstr "Datum narudžbe" + +#. module: purchase_requisition +#: selection:purchase.requisition,state:0 +msgid "Cancelled" +msgstr "Otkazano" + +#. module: purchase_requisition +#: model:ir.model,name:purchase_requisition.model_purchase_requisition_partner +msgid "Purchase Requisition Partner" +msgstr "Parner zahtjeva nabavke" + +#. module: purchase_requisition +#: help:purchase.requisition,message_unread:0 +msgid "If checked new messages require your attention." +msgstr "Ako je označeno, nove poruke će zahtjevati vašu pažnju." + +#. module: purchase_requisition +#: report:purchase.requisition:0 +msgid "Purchase for Requisitions" +msgstr "Nabava za zahtjeve" + +#. module: purchase_requisition +#: model:ir.actions.act_window,name:purchase_requisition.act_res_partner_2_purchase_order +msgid "Purchase orders" +msgstr "Nabavne narudžbe" + +#. module: purchase_requisition +#: field:purchase.requisition,date_end:0 +msgid "Requisition Deadline" +msgstr "Konačan rok zahtjeva" + +#. module: purchase_requisition +#: field:purchase.requisition,message_summary:0 +msgid "Summary" +msgstr "Rezime" + +#. module: purchase_requisition +#: view:purchase.requisition:0 +msgid "Reference" +msgstr "Referenca" + +#. module: purchase_requisition +#: model:ir.model,name:purchase_requisition.model_procurement_order +msgid "Procurement" +msgstr "Naručivanje" + +#. module: purchase_requisition +#: report:purchase.requisition:0 +#: view:purchase.requisition:0 +msgid "Source" +msgstr "Izvor" + +#. module: purchase_requisition +#: field:purchase.requisition,warehouse_id:0 +msgid "Warehouse" +msgstr "Skladište" + +#. module: purchase_requisition +#: field:procurement.order,requisition_id:0 +msgid "Latest Requisition" +msgstr "Zadnji zahtjev" + +#. module: purchase_requisition +#: model:res.groups,name:purchase_requisition.group_purchase_requisition_manager +msgid "Manager" +msgstr "Menadžer" + +#. module: purchase_requisition +#: selection:purchase.requisition,exclusive:0 +msgid "Purchase Requisition (exclusive)" +msgstr "Zahtjev nabavke (ekskluzivan)" + +#. module: purchase_requisition +#: help:purchase.requisition,message_summary:0 +msgid "" +"Holds the Chatter summary (number of messages, ...). This summary is " +"directly in html format in order to be inserted in kanban views." +msgstr "" +"Sadrži sažetak konverzacije (broj poruka,..). Ovaj sažetak je u html formatu " +"da bi mogao biti ubačen u kanban pogled." + +#. module: purchase_requisition +#: report:purchase.requisition:0 +msgid "Product UoM" +msgstr "Proizvod UoM" + +#. module: purchase_requisition +#: code:addons/purchase_requisition/purchase_requisition.py:134 +#, python-format +msgid "Warning!" +msgstr "Upozorenje!" + +#. module: purchase_requisition +#: view:purchase.requisition:0 +msgid "Confirm Purchase Order" +msgstr "Potvrdi nabavni nalog" + +#. module: purchase_requisition +#: view:purchase.requisition.partner:0 +msgid "Cancel" +msgstr "Otkaži" + +#. module: purchase_requisition +#: report:purchase.requisition:0 +#: field:purchase.requisition,date_start:0 +msgid "Requisition Date" +msgstr "Datum zahtjeva" + +#. module: purchase_requisition +#: view:purchase.requisition:0 +msgid "Start Date" +msgstr "Datum početka" + +#. module: purchase_requisition +#: view:purchase.requisition:0 +msgid "Unassigned" +msgstr "Nedodeljen" + +#. module: purchase_requisition +#: view:purchase.order:0 +msgid "Requisition" +msgstr "Zahtjev" + +#. module: purchase_requisition +#: help:product.product,purchase_requisition:0 +msgid "" +"Check this box to generates purchase requisition instead of generating " +"requests for quotation from procurement." +msgstr "" +"Označite ovu kućicu da generišete zahtjeve nabavke umjesto generisanja " +"zahtjeva za predračunom iz naručivanja." + +#. module: purchase_requisition +#: field:purchase.requisition,purchase_ids:0 +msgid "Purchase Orders" +msgstr "Nabavne narudžbe" diff --git a/addons/resource/i18n/bs.po b/addons/resource/i18n/bs.po new file mode 100644 index 00000000000..596d4c7d60d --- /dev/null +++ b/addons/resource/i18n/bs.po @@ -0,0 +1,364 @@ +# Bosnian translation for openobject-addons +# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR , 2013. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-12-21 17:06+0000\n" +"PO-Revision-Date: 2013-10-27 13:41+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Bosnian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2013-10-28 05:23+0000\n" +"X-Generator: Launchpad (build 16810)\n" + +#. module: resource +#: help:resource.calendar.leaves,resource_id:0 +msgid "" +"If empty, this is a generic holiday for the company. If a resource is set, " +"the holiday/leave is only for this resource" +msgstr "" +"Ako je prazno, ovo je generički praznik za kompaniju. Ako je resurs " +"postavljen, praznik/odsustvo je samo za taj resurs." + +#. module: resource +#: selection:resource.resource,resource_type:0 +msgid "Material" +msgstr "Materijal" + +#. module: resource +#: field:resource.resource,resource_type:0 +msgid "Resource Type" +msgstr "Tip resursa" + +#. module: resource +#: model:ir.model,name:resource.model_resource_calendar_leaves +#: view:resource.calendar.leaves:0 +msgid "Leave Detail" +msgstr "Detalji o odsustvu" + +#. module: resource +#: model:ir.actions.act_window,name:resource.resource_calendar_resources_leaves +msgid "Resources Leaves" +msgstr "Odsustva resursa" + +#. module: resource +#: field:resource.calendar.attendance,dayofweek:0 +msgid "Day of Week" +msgstr "Dan u sedmici" + +#. module: resource +#: selection:resource.calendar.attendance,dayofweek:0 +msgid "Thursday" +msgstr "Četvrtak" + +#. module: resource +#: view:resource.calendar.leaves:0 +#: view:resource.resource:0 +msgid "Group By..." +msgstr "Grupiši po..." + +#. module: resource +#: selection:resource.calendar.attendance,dayofweek:0 +msgid "Sunday" +msgstr "Nedjelja" + +#. module: resource +#: field:resource.resource,time_efficiency:0 +msgid "Efficiency Factor" +msgstr "Faktor efikasnosti" + +#. module: resource +#: view:resource.resource:0 +msgid "Search Resource" +msgstr "Pretraži resurse" + +#. module: resource +#: view:resource.resource:0 +msgid "Type" +msgstr "Tip" + +#. module: resource +#: model:ir.actions.act_window,name:resource.action_resource_resource_tree +#: view:resource.resource:0 +msgid "Resources" +msgstr "Resursi" + +#. module: resource +#: code:addons/resource/resource.py:455 +#, python-format +msgid "Make sure the Working time has been configured with proper week days!" +msgstr "" +"Obezbjedite da je radno vrijeme pravilno konfigurisano sa danima sedmice!" + +#. module: resource +#: code:addons/resource/resource.py:373 +#, python-format +msgid "%s (copy)" +msgstr "%s (kopija)" + +#. module: resource +#: view:resource.calendar:0 +msgid "Search Working Time" +msgstr "Pretraži radno vrijeme" + +#. module: resource +#: constraint:resource.calendar.leaves:0 +msgid "Error! leave start-date must be lower then leave end-date." +msgstr "" +"Greška! Datum početka odsustva mora biti manji od završnog datuma odsustva" + +#. module: resource +#: model:ir.model,name:resource.model_resource_calendar +msgid "Resource Calendar" +msgstr "Kalendar resursa" + +#. module: resource +#: field:resource.calendar,company_id:0 +#: view:resource.calendar.leaves:0 +#: field:resource.calendar.leaves,company_id:0 +#: view:resource.resource:0 +#: field:resource.resource,company_id:0 +msgid "Company" +msgstr "Kompanija" + +#. module: resource +#: selection:resource.calendar.attendance,dayofweek:0 +msgid "Friday" +msgstr "Petak" + +#. module: resource +#: view:resource.calendar.attendance:0 +msgid "Hours" +msgstr "Sati" + +#. module: resource +#: view:resource.calendar.leaves:0 +msgid "Reason" +msgstr "Razlog" + +#. module: resource +#: view:resource.resource:0 +#: field:resource.resource,user_id:0 +msgid "User" +msgstr "Korisnik" + +#. module: resource +#: view:resource.calendar.leaves:0 +msgid "Date" +msgstr "Datum" + +#. module: resource +#: view:resource.calendar.leaves:0 +msgid "Search Working Period Leaves" +msgstr "Pretraži odsustva u periodima rada" + +#. module: resource +#: field:resource.calendar.attendance,date_from:0 +msgid "Starting Date" +msgstr "Datum početka" + +#. module: resource +#: field:resource.calendar,manager:0 +msgid "Workgroup Manager" +msgstr "Voditelj radne grupe" + +#. module: resource +#: field:resource.calendar.leaves,date_to:0 +msgid "End Date" +msgstr "Datum Završetka" + +#. module: resource +#: model:ir.actions.act_window,name:resource.resource_calendar_closing_days +msgid "Closing Days" +msgstr "Dani zatvaranja" + +#. module: resource +#: model:ir.ui.menu,name:resource.menu_resource_config +#: view:resource.calendar.leaves:0 +#: field:resource.calendar.leaves,resource_id:0 +#: view:resource.resource:0 +msgid "Resource" +msgstr "Resurs" + +#. module: resource +#: field:resource.calendar,name:0 +#: field:resource.calendar.attendance,name:0 +#: field:resource.calendar.leaves,name:0 +#: field:resource.resource,name:0 +msgid "Name" +msgstr "Naziv" + +#. module: resource +#: model:ir.actions.act_window,name:resource.action_resource_calendar_form +#: view:resource.calendar:0 +#: field:resource.calendar,attendance_ids:0 +#: view:resource.calendar.attendance:0 +#: field:resource.calendar.leaves,calendar_id:0 +#: field:resource.resource,calendar_id:0 +msgid "Working Time" +msgstr "Radno Vrijeme" + +#. module: resource +#: help:resource.calendar.attendance,hour_from:0 +msgid "Start and End time of working." +msgstr "Početno i krajnje vrijeme rada." + +#. module: resource +#: view:resource.calendar.leaves:0 +#: view:resource.resource:0 +msgid "Working Period" +msgstr "Period rada" + +#. module: resource +#: selection:resource.calendar.attendance,dayofweek:0 +msgid "Wednesday" +msgstr "Srijeda" + +#. module: resource +#: model:ir.model,name:resource.model_resource_resource +msgid "Resource Detail" +msgstr "Detalji resursa" + +#. module: resource +#: field:resource.resource,active:0 +msgid "Active" +msgstr "Aktivan" + +#. module: resource +#: help:resource.resource,active:0 +msgid "" +"If the active field is set to False, it will allow you to hide the resource " +"record without removing it." +msgstr "" +"Ako je ovo polje postavljeno na neaktivno, moežte sakriti resurs bez da ga " +"uklonite." + +#. module: resource +#: field:resource.calendar.attendance,calendar_id:0 +msgid "Resource's Calendar" +msgstr "Kalendar resursa" + +#. module: resource +#: field:resource.calendar.attendance,hour_from:0 +msgid "Work from" +msgstr "Radi od" + +#. module: resource +#: model:ir.actions.act_window,help:resource.action_resource_calendar_form +msgid "" +"Define working hours and time table that could be scheduled to your project " +"members" +msgstr "" + +#. module: resource +#: help:resource.resource,user_id:0 +msgid "Related user name for the resource to manage its access." +msgstr "Povezano korisničko ime za resurs da upravlja njegovim pristupom." + +#. module: resource +#: help:resource.resource,calendar_id:0 +msgid "Define the schedule of resource" +msgstr "Definišite zakazivanje resursa" + +#. module: resource +#: view:resource.calendar.leaves:0 +msgid "Starting Date of Leave" +msgstr "Datum početka odsustva" + +#. module: resource +#: field:resource.resource,code:0 +msgid "Code" +msgstr "Šifra" + +#. module: resource +#: selection:resource.calendar.attendance,dayofweek:0 +msgid "Monday" +msgstr "Ponedjeljak" + +#. module: resource +#: field:resource.calendar.attendance,hour_to:0 +msgid "Work to" +msgstr "Radi do" + +#. module: resource +#: model:ir.model,name:resource.model_resource_calendar_attendance +msgid "Work Detail" +msgstr "Detalji rada" + +#. module: resource +#: selection:resource.calendar.attendance,dayofweek:0 +msgid "Tuesday" +msgstr "Utorak" + +#. module: resource +#: help:resource.resource,time_efficiency:0 +msgid "" +"This field depict the efficiency of the resource to complete tasks. e.g " +"resource put alone on a phase of 5 days with 5 tasks assigned to him, will " +"show a load of 100% for this phase by default, but if we put a efficiency of " +"200%, then his load will only be 50%." +msgstr "" +"Ovo polje razlikuje efikasnost resursa za završetak zadatka. npr.: resurs " +"postavljen sam na fazu od 5 dana sa 5 zadataka dodjelenih njemu, će pokazati " +"opterećenje od 100% za ovu fazu zadano, ali ako postavimo efikasnost od " +"200%, tada će ovo opterećenje iznositi samo 50%." + +#. module: resource +#: model:ir.actions.act_window,name:resource.action_resource_calendar_leave_tree +#: model:ir.ui.menu,name:resource.menu_view_resource_calendar_leaves_search +msgid "Resource Leaves" +msgstr "Odsustva resursa" + +#. module: resource +#: model:ir.actions.act_window,help:resource.action_resource_resource_tree +msgid "" +"Resources allow you to create and manage resources that should be involved " +"in a specific project phase. You can also set their efficiency level and " +"workload based on their weekly working hours." +msgstr "" +"Resursi Vam omogućuju kreiranje i upravljanje resursima koji trebaju biti " +"uključeni u neku fazu projekta. Također možete postaviti njihov nivo " +"efikasnosti i opterećenje na osnovi njihovih sedmičnih radnih sati." + +#. module: resource +#: view:resource.resource:0 +msgid "Inactive" +msgstr "Neaktivan" + +#. module: resource +#: code:addons/resource/faces/resource.py:340 +#, python-format +msgid "(vacation)" +msgstr "(odmor)" + +#. module: resource +#: code:addons/resource/resource.py:455 +#, python-format +msgid "Configuration Error!" +msgstr "Greška u konfiguraciji!" + +#. module: resource +#: selection:resource.resource,resource_type:0 +msgid "Human" +msgstr "Čovjek" + +#. module: resource +#: view:resource.calendar.leaves:0 +msgid "Duration" +msgstr "Trajanje" + +#. module: resource +#: field:resource.calendar.leaves,date_from:0 +msgid "Start Date" +msgstr "Datum početka" + +#. module: resource +#: selection:resource.calendar.attendance,dayofweek:0 +msgid "Saturday" +msgstr "Subota" diff --git a/addons/sale_margin/i18n/bs.po b/addons/sale_margin/i18n/bs.po new file mode 100644 index 00000000000..a8815bd00ff --- /dev/null +++ b/addons/sale_margin/i18n/bs.po @@ -0,0 +1,48 @@ +# Bosnian translation for openobject-addons +# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR , 2013. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-12-21 17:06+0000\n" +"PO-Revision-Date: 2013-10-27 13:07+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Bosnian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2013-10-28 05:23+0000\n" +"X-Generator: Launchpad (build 16810)\n" + +#. module: sale_margin +#: field:sale.order.line,purchase_price:0 +msgid "Cost Price" +msgstr "Cijena koštanja" + +#. module: sale_margin +#: model:ir.model,name:sale_margin.model_sale_order +msgid "Sales Order" +msgstr "Prodajna narudžba" + +#. module: sale_margin +#: field:sale.order,margin:0 +#: field:sale.order.line,margin:0 +msgid "Margin" +msgstr "Marža" + +#. module: sale_margin +#: model:ir.model,name:sale_margin.model_sale_order_line +msgid "Sales Order Line" +msgstr "Stavka prodajne narudžbe" + +#. module: sale_margin +#: help:sale.order,margin:0 +msgid "" +"It gives profitability by calculating the difference between the Unit Price " +"and the cost price." +msgstr "" +"Daje Vam profitabilnost računajući razliku između jedinične cijene i cijene " +"koštanja." diff --git a/addons/sale_mrp/i18n/bs.po b/addons/sale_mrp/i18n/bs.po new file mode 100644 index 00000000000..9012ade8ceb --- /dev/null +++ b/addons/sale_mrp/i18n/bs.po @@ -0,0 +1,43 @@ +# Bosnian translation for openobject-addons +# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR , 2013. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-12-21 17:06+0000\n" +"PO-Revision-Date: 2013-10-27 13:09+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Bosnian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2013-10-28 05:23+0000\n" +"X-Generator: Launchpad (build 16810)\n" + +#. module: sale_mrp +#: model:ir.model,name:sale_mrp.model_mrp_production +msgid "Manufacturing Order" +msgstr "Radni nalog proizvodnje" + +#. module: sale_mrp +#: help:mrp.production,sale_name:0 +msgid "Indicate the name of sales order." +msgstr "Indicira naziv prodajne narudžbe." + +#. module: sale_mrp +#: help:mrp.production,sale_ref:0 +msgid "Indicate the Customer Reference from sales order." +msgstr "Indicira referencu kupca iz prodajne narudžbe." + +#. module: sale_mrp +#: field:mrp.production,sale_ref:0 +msgid "Sale Reference" +msgstr "Prodajna referenca" + +#. module: sale_mrp +#: field:mrp.production,sale_name:0 +msgid "Sale Name" +msgstr "Naziv prodaje" diff --git a/addons/sale_order_dates/i18n/bs.po b/addons/sale_order_dates/i18n/bs.po new file mode 100644 index 00000000000..03dbb97c7f7 --- /dev/null +++ b/addons/sale_order_dates/i18n/bs.po @@ -0,0 +1,58 @@ +# Bosnian translation for openobject-addons +# Copyright (c) 2013 Rosetta Contributors and Canonical Ltd 2013 +# This file is distributed under the same license as the openobject-addons package. +# FIRST AUTHOR , 2013. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-12-21 17:06+0000\n" +"PO-Revision-Date: 2013-10-27 13:13+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Bosnian \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2013-10-28 05:23+0000\n" +"X-Generator: Launchpad (build 16810)\n" + +#. module: sale_order_dates +#: view:sale.order:0 +msgid "Dates" +msgstr "Datumi" + +#. module: sale_order_dates +#: field:sale.order,commitment_date:0 +msgid "Commitment Date" +msgstr "Obavezani datum" + +#. module: sale_order_dates +#: field:sale.order,effective_date:0 +msgid "Effective Date" +msgstr "Efektivni datum" + +#. module: sale_order_dates +#: help:sale.order,effective_date:0 +msgid "Date on which picking is created." +msgstr "Datum kada je prikupljanje proizvoda kreirano." + +#. module: sale_order_dates +#: help:sale.order,requested_date:0 +msgid "Date requested by the customer for the sale." +msgstr "Zahtjevani datum prodaje od kupca" + +#. module: sale_order_dates +#: field:sale.order,requested_date:0 +msgid "Requested Date" +msgstr "Zahtjevani datum" + +#. module: sale_order_dates +#: model:ir.model,name:sale_order_dates.model_sale_order +msgid "Sales Order" +msgstr "Prodajna narudžba" + +#. module: sale_order_dates +#: help:sale.order,commitment_date:0 +msgid "Committed date for delivery." +msgstr "Potvrđen datum dostave." From 6b7ccc16b7883367ef4810a823ba66b85e725b06 Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Mon, 28 Oct 2013 14:50:50 +0100 Subject: [PATCH 08/61] [FIX] mail: mail_message: avoid crash when checking access rules against invalid ids bzr revid: chs@openerp.com-20131028135050-4hqx129ouy7z5b03 --- addons/mail/mail_message.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 3719dbc1c63..de8e4c68fda 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -212,7 +212,7 @@ class mail_message(osv.Model): _defaults = { 'type': 'email', - 'date': fields.datetime.now(), + 'date': fields.datetime.now, 'author_id': lambda self, cr, uid, ctx=None: self._get_default_author(cr, uid, ctx), 'body': '', 'email_from': lambda self, cr, uid, ctx=None: self._get_default_from(cr, uid, ctx), @@ -698,8 +698,9 @@ class mail_message(osv.Model): """ model_record_ids = {} for id in msg_ids: - if msg_val[id]['model'] and msg_val[id]['res_id']: - model_record_ids.setdefault(msg_val[id]['model'], dict()).setdefault(msg_val[id]['res_id'], set()).add(msg_val[id]['res_id']) + vals = msg_val.get(id, {}) + if vals.get('model') and vals.get('res_id'): + model_record_ids.setdefault(vals['model'], set()).add(vals['res_id']) return model_record_ids if uid == SUPERUSER_ID: @@ -711,7 +712,7 @@ class mail_message(osv.Model): partner_id = self.pool['res.users'].browse(cr, SUPERUSER_ID, uid, context=None).partner_id.id # Read mail_message.ids to have their values - message_values = dict.fromkeys(ids) + message_values = dict.fromkeys(ids, {}) cr.execute('SELECT DISTINCT id, model, res_id, author_id, parent_id FROM "%s" WHERE id = ANY (%%s)' % self._table, (ids,)) for id, rmod, rid, author_id, parent_id in cr.fetchall(): message_values[id] = {'model': rmod, 'res_id': rid, 'author_id': author_id, 'parent_id': parent_id} @@ -745,10 +746,10 @@ class mail_message(osv.Model): ], context=context) notified_ids = [notification.message_id.id for notification in not_obj.browse(cr, SUPERUSER_ID, not_ids, context=context)] elif operation == 'create': - for doc_model, doc_dict in model_record_ids.items(): + for doc_model, doc_ids in model_record_ids.items(): fol_ids = fol_obj.search(cr, SUPERUSER_ID, [ ('res_model', '=', doc_model), - ('res_id', 'in', list(doc_dict.keys())), + ('res_id', 'in', list(doc_ids)), ('partner_id', '=', partner_id), ], context=context) fol_mids = [follower.res_id for follower in fol_obj.browse(cr, SUPERUSER_ID, fol_ids, context=context)] @@ -759,9 +760,9 @@ class mail_message(osv.Model): other_ids = other_ids.difference(set(notified_ids)) model_record_ids = _generate_model_record_ids(message_values, other_ids) document_related_ids = [] - for model, doc_dict in model_record_ids.items(): + for model, doc_ids in model_record_ids.items(): model_obj = self.pool[model] - mids = model_obj.exists(cr, uid, doc_dict.keys()) + mids = model_obj.exists(cr, uid, list(doc_ids)) if hasattr(model_obj, 'check_mail_message_access'): model_obj.check_mail_message_access(cr, uid, mids, operation, context=context) else: From a7ff75f94a168e26b9e9a208167b07d45dc751cb Mon Sep 17 00:00:00 2001 From: Stephane Wirtel Date: Mon, 28 Oct 2013 15:05:45 +0100 Subject: [PATCH 09/61] [FIX] Remove unused menu item in the mrp_securify file bzr revid: stw@openerp.com-20131028140545-xr3mav7cb3gk45qn --- addons/mrp/security/mrp_security.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/addons/mrp/security/mrp_security.xml b/addons/mrp/security/mrp_security.xml index 9e5dc919720..1320ea26906 100644 --- a/addons/mrp/security/mrp_security.xml +++ b/addons/mrp/security/mrp_security.xml @@ -19,11 +19,6 @@ - - - - - From 7c3ae6a157efaecf8fffea4d3eb9732d3826e531 Mon Sep 17 00:00:00 2001 From: niv-openerp Date: Mon, 28 Oct 2013 16:00:19 +0100 Subject: [PATCH 10/61] [FIX] Rare problem in select fields related to previous improvement in that widget. Sometimes, the value of the field was resetted to false. bzr revid: nicolas.vanhoren@openerp.com-20131028150019-i0nllpqu1i7wpxbr --- addons/web/static/src/js/view_form.js | 34 +++++++++++---------------- addons/web/static/src/xml/base.xml | 7 +++--- 2 files changed, 17 insertions(+), 24 deletions(-) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 1fa7cb6a4f8..2cb318dcb17 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -2804,7 +2804,7 @@ instance.web.form.FieldSelection = instance.web.form.AbstractField.extend(instan var self = this; this._super(field_manager, node); this.set("value", false); - this.set("values", [[false, '']]); + this.set("values", []); this.records_orderer = new instance.web.DropMisordered(); this.field_manager.on("view_content_has_changed", this, function() { var domain = new openerp.web.CompoundDomain(this.build_domain()).eval(); @@ -2826,14 +2826,9 @@ instance.web.form.FieldSelection = instance.web.form.AbstractField.extend(instan var model = new openerp.Model(openerp.session, this.field.relation); def = model.call("search", [this.get("domain")], {"context": this.build_context()}).then(function(record_ids) { return model.call("name_get", [record_ids] , {"context": self.build_context()}); - }).then(function(res) { - return [[false, '']].concat(res); }); } else { - var values = _(this.field.selection).chain() - .reject(function (v) { return v[0] === false && v[1] === ''; }) - .unshift([false, '']) - .value(); + var values = _.reject(this.field.selection, function (v) { return v[0] === false && v[1] === ''; }); def = $.when(values); } this.records_orderer.add(def).then(function(values) { @@ -2871,8 +2866,8 @@ instance.web.form.FieldSelection = instance.web.form.AbstractField.extend(instan }, store_dom_value: function () { if (!this.get('effective_readonly') && this.$('select').length) { - this.internal_set_value( - this.get("values")[this.$('select')[0].selectedIndex][0]); + var val = JSON.parse(this.$('select').val()); + this.internal_set_value(val); } }, set_value: function(value_) { @@ -2881,19 +2876,18 @@ instance.web.form.FieldSelection = instance.web.form.AbstractField.extend(instan this._super(value_); }, render_value: function() { + var values = this.get("values"); + values = [[false, this.node.attrs.placeholder || '']].concat(values); + var found = _.find(values, function(el) { return el[0] === this.get("value"); }, this); + if (! found) { + found = [this.get("value"), _t('Unknown')]; + values = [found].concat(values); + } if (! this.get("effective_readonly")) { - this.$().html(QWeb.render("FieldSelectionSelect", {widget: this})); - var index = 0; - _.each(this.get("values"), function(el, i) { - if (el[0] === this.get('value')) - index = i; - }, this); - this.$el.find('select')[0].selectedIndex = index; + this.$().html(QWeb.render("FieldSelectionSelect", {widget: this, values: values})); + this.$("select").val(JSON.stringify(found[0])); } else { - var self = this; - var option = _(this.get("values")) - .detect(function (record) { return record[0] === self.get('value'); }); - this.$el.text(option ? option[1] : this.get("values")[0][1]); + this.$el.text(found[1]); } }, focus: function() { diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 513e42bf899..cd96e9648a8 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -1107,10 +1107,9 @@ t-att-tabindex="widget.node.attrs.tabindex" t-att-autofocus="widget.node.attrs.autofocus" t-att-id="widget.id_for_label"> - -