import json import re from django.conf import settings from django.http import HttpResponse from django.urls import set_urlconf from django.utils.cache import patch_vary_headers from django.utils.deprecation import MiddlewareMixin from urllib import parse as urlparse from cms.utils.apphook_reload import reload_urlconf class CMSMultiSiteMiddleware(MiddlewareMixin): """ Picks the urlconf for the requested host (MULTISITE_CMS_URLS, with MULTISITE_CMS_ALIASES and MULTISITE_CMS_FALLBACK). Derived from djangocms-multisite (BSD, Copyright (c) 2013, Iacopo Spalletti), whose ungleich fork is no longer installed. """ def process_request(self, request): urls = getattr(settings, 'MULTISITE_CMS_URLS', {}) aliases = getattr(settings, 'MULTISITE_CMS_ALIASES', {}) fallback = getattr(settings, 'MULTISITE_CMS_FALLBACK', '') host = urlparse.urlparse( request.build_absolute_uri()).hostname.split(':')[0] urlconf = urls.get(host) if not urlconf: for domain, hosts in aliases.items(): if host in hosts and domain in urls: urlconf = urls[domain] break if not urlconf and fallback in urls: urlconf = urls[fallback] if urlconf: request.urlconf = urlconf # for code that does not know about the request (get_absolute_url()) set_urlconf(urlconf) reload_urlconf() def process_response(self, request, response): patch_vary_headers(response, ('Host',)) # back to the default urlconf set_urlconf(None) return response class MultipleProxyMiddleware(MiddlewareMixin): FORWARDED_FOR_FIELDS = [ 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED_HOST', 'HTTP_X_FORWARDED_SERVER', ] def process_request(self, request): """ Rewrites the proxy headers so that only the most recent proxy is used. """ for field in self.FORWARDED_FOR_FIELDS: if field in request.META: if ',' in request.META[field]: parts = request.META[field].split(',') request.META[field] = parts[-1].strip() class DevSiteSwitcherMiddleware(MiddlewareMixin): """ Dev/test only (enabled from settings/local.py, i.e. DEBUG=True). The project picks a site from the request's Host header. To test every site from one server without DNS or /etc/hosts entries, the site is chosen, in this order, from: 1. the host: ".", e.g. digitalglarus.ch.localhost:8000 or digitalglarus.ch.203.0.113.5.sslip.io:8000. Every site is then its own browser origin, so tabs and cookies never interfere. Preferred. 2. ?site=, remembered in a cookie ("?site=" clears it). Redirects keep the parameter so the choice survives them. 3. the cookie set by 2. /dev-sites/ lists every configured site as a link. """ COOKIE = 'dev_site' def domains(self): try: found = [d for d in json.loads(settings.UNGLEICH_SITE_CONFIGS) if ':' not in d] except ValueError: found = [] return sorted(found, key=len, reverse=True) def site_from_host(self, request): host = request.get_host().split(':')[0].lower() for domain in self.domains(): if host == domain or host.startswith(domain + '.'): return domain return '' def process_request(self, request): if request.path == '/dev-sites/': return self.sites_page(request) site = self.site_from_host(request) if not site: if 'site' in request.GET: site = request.GET['site'].strip() else: site = request.COOKIES.get(self.COOKIE, '') request.META['DEV_SITE_SWITCH'] = site if site: request.META['HTTP_HOST'] = site request.META.pop('HTTP_X_FORWARDED_HOST', None) def process_response(self, request, response): site = request.META.get('DEV_SITE_SWITCH', '') # dev only: never let a browser reuse a page or redirect from another # site, and show which site the server picked (see browser dev tools) response['Cache-Control'] = 'no-store' response['X-Dev-Site'] = site or '(default)' if 'site' not in request.GET: return response if site: response.set_cookie(self.COOKIE, site) location = response.get('Location', '') if response.status_code in (301, 302) and location.startswith('/') \ and 'site=' not in location: response['Location'] = location + ( '&' if '?' in location else '?') + 'site=' + site else: response.delete_cookie(self.COOKIE) return response def sites_page(self, request): """A page listing every configured site as a link, on any host""" host, _, port = request.get_host().partition(':') port = ':' + port if port else '' # opened on a site's own address (e.g. blog.ungleich.ch.localhost): # drop that site's prefix so links are not nested for domain in self.domains(): if host.lower().startswith(domain + '.'): host = host[len(domain) + 1:] break if re.match(r'^\d+\.\d+\.\d+\.\d+$', host): # an IP has no subdomains; sslip.io resolves ..sslip.io base = host + '.sslip.io' else: base = host items = ''.join( '
  • {0}
  • '.format( d, base, port) for d in sorted(self.domains())) return HttpResponse( 'Test sites' '

    Test sites

    Each site opens on its own address, so ' 'you can open several at once.

      {}
    ' '

    Fallback without wildcard names: ' '/?site=<domain> (clear)' '

    '.format(items))