diff --git a/addons/web/__openerp__.py b/addons/web/__openerp__.py index 6efc85f0be8..6e34424be8e 100644 --- a/addons/web/__openerp__.py +++ b/addons/web/__openerp__.py @@ -13,6 +13,7 @@ This module provides the core of the OpenERP Web Client. 'auto_install': True, 'post_load': 'wsgi_postload', 'js' : [ + "static/src/fixbind.js", "static/lib/datejs/globalization/en-US.js", "static/lib/datejs/core.js", "static/lib/datejs/parser.js", @@ -26,6 +27,7 @@ This module provides the core of the OpenERP Web Client. "static/lib/spinjs/spin.js", "static/lib/jquery.autosize/jquery.autosize.js", "static/lib/jquery.blockUI/jquery.blockUI.js", + "static/lib/jquery.placeholder/jquery.placeholder.js", "static/lib/jquery.ui/js/jquery-ui-1.9.1.custom.js", "static/lib/jquery.ui.timepicker/js/jquery-ui-timepicker-addon.js", "static/lib/jquery.ui.timepicker/js/jquery-ui-sliderAccess.js", @@ -76,6 +78,7 @@ This module provides the core of the OpenERP Web Client. "static/test/class.js", "static/test/registry.js", "static/test/form.js", + "static/test/data.js", "static/test/list-utils.js", "static/test/formats.js", "static/test/rpc.js", diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index e1a3c476a32..78aa85669be 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -13,7 +13,10 @@ import os import re import simplejson import time +import urllib import urllib2 +import urlparse +import xmlrpclib import zlib from xml.etree import ElementTree from cStringIO import StringIO @@ -90,16 +93,50 @@ def db_list(req): dbs = [i for i in dbs if re.match(r, i)] return dbs -def db_monodb(req): - # if only one db exists, return it else return False +def db_monodb_redirect(req): + db = False + redirect = False + + # 1 try the db in the url + db_url = req.params.get('db') + if db_url: + return (db_url, False) + try: dbs = db_list(req) - if len(dbs) == 1: - return dbs[0] except Exception: # ignore access denied - pass - return False + dbs = [] + + # 2 use the database from the cookie if it's listable and still listed + cookie_db = req.httprequest.cookies.get('last_used_database') + if cookie_db in dbs: + db = cookie_db + + # 3 use the first db + if dbs and not db: + db = dbs[0] + + # redirect to the chosen db if multiple are available + if db and len(dbs) > 1: + query = dict(urlparse.parse_qsl(req.httprequest.query_string, keep_blank_values=True)) + query.update({ 'db': db }) + redirect = req.httprequest.path + '?' + urllib.urlencode(query) + return (db, redirect) + +def db_monodb(req): + # if only one db exists, return it else return False + return db_monodb_redirect(req)[0] + +def redirect_with_hash(req, url, code=303): + if req.httprequest.user_agent.browser == 'msie': + try: + version = float(req.httprequest.user_agent.version) + if version < 10: + return "
" % url + except Exception: + pass + return werkzeug.utils.redirect(url, code) def module_topological_sort(modules): """ Return a list of module names sorted so that their dependencies of the @@ -290,20 +327,19 @@ def manifest_glob(req, extension, addons=None, db=None): return r def manifest_list(req, extension, mods=None, db=None): + """ list ressources to load specifying either: + mods: a comma separated string listing modules + db: a database name (return all installed modules in that database) + """ if not req.debug: path = '/web/webclient/' + extension if mods is not None: - path += '?mods=' + mods + path += '?' + urllib.urlencode({'mods': mods}) elif db: - path += '?db=' + db + path += '?' + urllib.urlencode({'db': db}) return [path] files = manifest_glob(req, extension, addons=mods, db=db) - i_am_diabetic = req.httprequest.environ["QUERY_STRING"].count("no_sugar") >= 1 or \ - req.httprequest.environ.get('HTTP_REFERER', '').count("no_sugar") >= 1 - if i_am_diabetic: - return [wp for _fp, wp in files] - else: - return ['%s?debug=%s' % (wp, os.path.getmtime(fp)) for fp, wp in files] + return [wp for _fp, wp in files] def get_last_modified(files): """ Returns the modification time of the most recently modified @@ -533,6 +569,10 @@ class Home(openerpweb.Controller): @openerpweb.httprequest def index(self, req, s_action=None, db=None, **kw): + db, redir = db_monodb_redirect(req) + if redir: + return redirect_with_hash(req, redir) + js = "\n ".join('' % i for i in manifest_list(req, 'js', db=db)) css = "\n ".join('' % i for i in manifest_list(req, 'css', db=db)) @@ -603,6 +643,18 @@ class WebClient(openerpweb.Controller): content, checksum = concat_files((f[0] for f in files), reader) + # move up all @import and @charset rules to the top + matches = [] + def push(matchobj): + matches.append(matchobj.group(0)) + return '' + + content = re.sub(re.compile("(@charset.+;$)", re.M), push, content) + content = re.sub(re.compile("(@import.+;$)", re.M), push, content) + + matches.append(content) + content = '\n'.join(matches) + return make_conditional( req, req.make_response(content, [('Content-Type', 'text/css')]), last_modified, checksum) @@ -683,7 +735,7 @@ class WebClient(openerpweb.Controller): @openerpweb.jsonrequest def version_info(self, req): - return openerp.service.web_services.RPC_VERSION_1 + return openerp.service.common.exp_version() class Proxy(openerpweb.Controller): _cp_path = '/web/proxy' @@ -868,7 +920,7 @@ class Session(openerpweb.Controller): """ saved_actions = req.httpsession.get('saved_actions') if not saved_actions: - saved_actions = {"next":0, "actions":{}} + saved_actions = {"next":1, "actions":{}} req.httpsession['saved_actions'] = saved_actions # we don't allow more than 10 stored actions if len(saved_actions["actions"]) >= 10: @@ -1310,7 +1362,7 @@ class Binary(openerpweb.Controller): 'id': attachment_id } except Exception: - args = {'error':e.faultCode } + args = {'error': "Something horrible happened"} return out % (simplejson.dumps(callback), simplejson.dumps(args)) @openerpweb.httprequest @@ -1323,19 +1375,30 @@ class Binary(openerpweb.Controller): elif dbname is None: dbname = db_monodb(req) - if uid is None: + if not uid: uid = openerp.SUPERUSER_ID if not dbname: image_data = self.placeholder(req, 'logo.png') else: - registry = openerp.modules.registry.RegistryManager.get(dbname) - with registry.cursor() as cr: - user = registry.get('res.users').browse(cr, uid, uid) - if user.company_id.logo_web: - image_data = user.company_id.logo_web.decode('base64') - else: - image_data = self.placeholder(req, 'nologo.png') + try: + # create an empty registry + registry = openerp.modules.registry.Registry(dbname.lower()) + with registry.cursor() as cr: + cr.execute("""SELECT c.logo_web + FROM res_users u + LEFT JOIN res_company c + ON c.id = u.company_id + WHERE u.id = %s + """, (uid,)) + row = cr.fetchone() + if row and row[0]: + image_data = str(row[0]).decode('base64') + else: + image_data = self.placeholder(req, 'nologo.png') + except Exception: + image_data = self.placeholder(req, 'logo.png') + headers = [ ('Content-Type', 'image/png'), ('Content-Length', len(image_data)), @@ -1380,7 +1443,7 @@ class Action(openerpweb.Controller): else: return False -class Export(View): +class Export(openerpweb.Controller): _cp_path = "/web/export" @openerpweb.jsonrequest @@ -1521,7 +1584,7 @@ class Export(View): (prefix + '/' + k, prefix_string + '/' + v) for k, v in self.fields_info(req, model, export_fields).iteritems()) - #noinspection PyPropertyDefinition +class ExportFormat(object): @property def content_type(self): """ Provides the format's content type """ @@ -1569,7 +1632,7 @@ class Export(View): ('Content-Type', self.content_type)], cookies={'fileToken': int(token)}) -class CSVExport(Export): +class CSVExport(ExportFormat, http.Controller): _cp_path = '/web/export/csv' fmt = {'tag': 'csv', 'label': 'CSV'} @@ -1604,7 +1667,7 @@ class CSVExport(Export): fp.close() return data -class ExcelExport(Export): +class ExcelExport(ExportFormat, http.Controller): _cp_path = '/web/export/xls' fmt = { 'tag': 'xls', @@ -1643,7 +1706,7 @@ class ExcelExport(Export): fp.close() return data -class Reports(View): +class Reports(openerpweb.Controller): _cp_path = "/web/report" POLLING_DELAY = 0.25 TYPES_MAPPING = { diff --git a/addons/web/controllers/testing.py b/addons/web/controllers/testing.py index 5692139f002..b6327bf4622 100644 --- a/addons/web/controllers/testing.py +++ b/addons/web/controllers/testing.py @@ -32,12 +32,12 @@ NOMODULE_TEMPLATE = Template(u"""