@@ -37,11 +37,11 @@ default_application = 'init', applications = 'ALL', default_controller = 'default', controllers = 'DEFAULT', default_function = 'index', - functions = None, + functions = dict(), default_language = None, languages = None, root_static = ['favicon.ico', 'robots.txt'], domains = None, exclusive_domain = False, @@ -363,14 +363,10 @@ raise SyntaxError, "unknown key '%s' in router '%s'" % (key, app) if not router.controllers: router.controllers = set() elif not isinstance(router.controllers, str): router.controllers = set(router.controllers) - if router.functions: - router.functions = set(router.functions) - else: - router.functions = set() if router.languages: router.languages = set(router.languages) else: router.languages = set() if app != 'BASE': @@ -387,11 +383,19 @@ router.controllers.add(cname[:-3]) if router.controllers: router.controllers.add('static') router.controllers.add(router.default_controller) if router.functions: - router.functions.add(router.default_function) + if isinstance(router.functions, (set, tuple, list)): + functions = set(router.functions) + if isinstance(router.default_function, str): + functions.add(router.default_function) # legacy compatibility + router.functions = { router.default_controller: functions } + for controller in router.functions: + router.functions[controller] = set(router.functions[controller]) + else: + router.functions = dict() if isinstance(routers.BASE.applications, str) and routers.BASE.applications == 'ALL': routers.BASE.applications = list(all_apps) if routers.BASE.applications: routers.BASE.applications = set(routers.BASE.applications) @@ -423,15 +427,18 @@ for (domain, app) in [(d.strip(':'), a.strip('/')) for (d, a) in routers.BASE.domains.items()]: port = None if ':' in domain: (domain, port) = domain.split(':') ctlr = None + fcn = None if '/' in app: - (app, ctlr) = app.split('/') + (app, ctlr) = app.split('/', 1) + if ctlr and '/' in ctlr: + (ctlr, fcn) = ctlr.split('/') if app not in all_apps and app not in routers: raise SyntaxError, "unknown app '%s' in domains" % app - domains[(domain, port)] = (app, ctlr) + domains[(domain, port)] = (app, ctlr, fcn) routers.BASE.domains = domains def regex_uri(e, regexes, tag, default=None): "filter incoming URI against a list of regexes" path = e['PATH_INFO'] @@ -752,11 +759,11 @@ self.controller = None self.function = None self.extension = 'html' self.controllers = set() - self.functions = set() + self.functions = dict() self.languages = set() self.default_language = None self.map_hyphen = False self.exclusive_domain = False @@ -808,21 +815,24 @@ def map_app(self): "determine application name" base = routers.BASE # base router self.domain_application = None self.domain_controller = None + self.domain_function = None arg0 = self.harg0 - if base.applications and arg0 in base.applications: - self.application = arg0 - elif (self.host, self.port) in base.domains: - (self.application, self.domain_controller) = base.domains[(self.host, self.port)] + if (self.host, self.port) in base.domains: + (self.application, self.domain_controller, self.domain_function) = base.domains[(self.host, self.port)] + self.env['domain_application'] = self.application + self.env['domain_controller'] = self.domain_controller + self.env['domain_function'] = self.domain_function + elif (self.host, None) in base.domains: + (self.application, self.domain_controller, self.domain_function) = base.domains[(self.host, None)] self.env['domain_application'] = self.application self.env['domain_controller'] = self.domain_controller - elif (self.host, None) in base.domains: - (self.application, self.domain_controller) = base.domains[(self.host, None)] - self.env['domain_application'] = self.application - self.env['domain_controller'] = self.domain_controller + self.env['domain_function'] = self.domain_function + elif base.applications and arg0 in base.applications: + self.application = arg0 elif arg0 and not base.applications: self.application = arg0 else: self.application = base.default_application or '' self.pop_arg_if(self.application == arg0) @@ -926,12 +936,18 @@ return static_file def map_function(self): "handle function.extension" arg0 = self.harg0 # map hyphens - if not arg0 or self.functions and arg0 not in self.functions and self.controller == self.default_controller: - self.function = self.router.default_function or "" + functions = self.functions.get(self.controller, set()) + if isinstance(self.router.default_function, dict): + default_function = self.router.default_function.get(self.controller, None) + else: + default_function = self.router.default_function # str or None + default_function = self.domain_function or default_function + if not arg0 or functions and arg0 not in functions: + self.function = default_function or "" self.pop_arg_if(arg0 and self.function == arg0) else: func_ext = arg0.split('.') if len(func_ext) > 1: self.function = func_ext[0] @@ -1021,21 +1037,24 @@ self.host = host self.port = port self.applications = routers.BASE.applications self.controllers = self.router.controllers - self.functions = self.router.functions + self.functions = self.router.functions.get(self.controller, set()) self.languages = self.router.languages self.default_language = self.router.default_language self.exclusive_domain = self.router.exclusive_domain self.map_hyphen = self.router.map_hyphen self.map_static = self.router.map_static self.path_prefix = routers.BASE.path_prefix self.domain_application = request and self.request.env.domain_application self.domain_controller = request and self.request.env.domain_controller - self.default_function = self.router.default_function + if isinstance(self.router.default_function, dict): + self.default_function = self.router.default_function.get(self.controller, None) + else: + self.default_function = self.router.default_function if (self.router.exclusive_domain and self.domain_application and self.domain_application != self.application and not self.host): raise SyntaxError, 'cross-domain conflict: must specify host' lang = request and request.uri_language @@ -1060,11 +1079,11 @@ router = self.router # Handle the easy no-args case of tail-defaults: /a/c /a / # - if not self.args and self.function == router.default_function: + if not self.args and self.function == self.default_function: self.omit_function = True if self.controller == router.default_controller: self.omit_controller = True if self.application == self.default_application: self.omit_application = True @@ -1080,13 +1099,13 @@ # default_controller = ((self.application == self.domain_application) and self.domain_controller) or router.default_controller or '' if self.controller == default_controller: self.omit_controller = True - # omit function if default controller/function + # omit function if possible # - if self.functions and self.function == self.default_function and self.omit_controller: + if self.functions and self.function in self.functions and self.function == self.default_function: self.omit_function = True # prohibit ambiguous cases # # because we presume the lang string to be unambiguous, its presence protects application omission @@ -1203,11 +1222,11 @@ from URLs with function/args present, thus: /da/c/f/args => /c/f/args /da/dc/f/args => /f/args - We use [applications] and [controllers] and [functions] to suppress ambiguous omissions. + We use [applications] and [controllers] and {functions} to suppress ambiguous omissions. We assume that language names do not collide with a/c/f names. ''' map = MapUrlOut(request, env, application, controller, function, args, other, scheme, host, port) return map.acf() @@ -1215,6 +1234,8 @@ def get_effective_router(appname): "return a private copy of the effective router for the specified application" if not routers or appname not in routers: return None return Storage(routers[appname]) # return a copy + +