""" Gets URLs for Services, and other DomainObjects. """ import os.path import kforge.ioc import kforge.exceptions import routes class UrlScheme(object): """The URL layout 'scheme'. Wrap up routes functions so that we can use them nicely. The Interface ============= A scheme must provide the following mount points (with associated arguments): * media * system_admin * person * project * project_admin * project_service """ def __init__(self): self.dictionary = kforge.ioc.RequiredFeature('SystemDictionary') self.fqdn = self.dictionary['domain_name'] if self.fqdn.endswith('/'): self.fqdn= self.host[:-1] self.port = self.dictionary['www.port_http'] self.host = 'http://' + self.fqdn if self.port != '80': self.host += ':' + self.port # TODO: support for uri_prefix (from environ or from sysdict) self.uri_prefix = self.dictionary['www.uri_prefix'] # set up the mapper self.mapper = routes.Mapper() self._configure_mapper() def _configure_mapper(self): self.mapper.connect('home', '') self.mapper.connect('media', 'media*offset', controller='media', offset='') self.mapper.connect('logout', 'logout', controller='account', action='logout') self.mapper.connect('login', 'login', controller='account', action='login') self.mapper.connect('access_denied', 'accessDenied', controller='accessDenied') self.mapper.connect('person', 'person/:action/:id', controller='person', action='index', requirements=dict(action='index|create|search|find|list|home') ) self.mapper.connect('person', 'person/:id/:action', controller='person', action='read', requirements=dict(action='edit|delete|read') ) self.mapper.connect('project', 'project/:action/:id', controller='project', action='index', requirements=dict(action='index|create|search|find|list|home') ) self.mapper.connect('project', 'project/:id/:action', controller='project', action='read', requirements=dict(action='edit|delete|read') ) self.mapper.connect('project.admin', 'project/:project/:subcontroller/:action', controller='project.admin', id=None, requirements=dict(subcontroller='services|members', action='create') ) self.mapper.connect('project.admin', 'project/:project/:subcontroller/:id/:action', controller='project.admin', action='read', requirements=dict(subcontroller='services|members', action='delete|edit|read') ) # do not really need the controller here since this map is only used in # url generation but need it for routes self.mapper.connect('project.service', ':project/:service', controller='project.service', service=None) # this is not fully complete as we don't properly deal with subitems. # would like to have several maps and recurse but routes does not # support multiple mappers very nicely (if only url_for had a a config # argument ....) self.mapper.connect('admin', 'admin/:model/:offset', controller='admin', model='model', action=None, offset=None ) def url_for(self, *args, **kwargs): # have to set up request_config as url_for uses it ... # have to set it here as it must be set on each request # o/w get exceptions talking about thread local issues config = routes.request_config() config.mapper = self.mapper url = routes.url_for(*args, **kwargs) url = self.uri_prefix + url return url def url_for_qualified(self, *args, **kwargs): url = self.url_for(*args, **kwargs) url = self.host + url return url def getServicePath(self, service): if service.plugin.name == 'www': return self.url_for('project.service', project=service.project.name) else: return self.url_for('project.service', project=service.project.name, service=service.name) def getServiceUrl(self, service): return self.url_for_qualified('project.service', project=service.project.name, service=service.name) def media_path(self, offset=''): return self.url_for('media', offset=offset) def decodeServicePath(self, offset): # do *not* use mapper.match as may have nonstandard stuff # (e.g. from svn) # TODO: move back to using mapper # (this is more robust to future changes in layout of urls ...) # one way to do this: since problem is only caused by svn and its !svn # url part could deal with this by auto-replacing 'bad' characters (as # this will not affect service or project part) # # out = self.mapper.match(offset) # if out is None: # no match ... # msg = 'UrlScheme.decodeServicePath: failed to decode %s' % offset # raise Exception(msg) # project = out['project'] # service = out['service'] if self.uri_prefix: if not offset.startswith(self.uri_prefix): msg = 'No match as %s does not begin with %s' % (offset, self.uri_prefix) raise Exception(msg) offset = offset[len(self.uri_prefix):] parts = offset.split('/') # leading slash will be an item if len(parts) < 3: msg = '%s is not long enough to be a service path' % offset raise Exception(msg) project = parts[1] service = parts[2] # try to convert to standard strings ... # domainmodel does not use unicode by default but routes does # this leads to problems when later looking up projects # e.g. registry.projects[u'annakarenina'] will not find a project # called 'annakarenina' try: project = str(project) except: pass try: service = str(service) except: pass return project, service def getFqdn(self): return self.fqdn def get_host(self): return self.host