from logging import getLogger from django import http from django.apps import apps from django.db.models import Q from django.conf import settings from django.contrib.sites.shortcuts import get_current_site from django.core.exceptions import ImproperlyConfigured from .models import Redirect from .utils import handle_seo_redirects logger = getLogger(__name__) # TODO: replace after removing support for old versions of Django class MiddlewareMixin(object): """ This mixin a full copy of Django 1.10 django.utils.deprecation.MiddlewareMixin. Needed for compatibility reasons. """ def __init__(self, get_response=None): self.get_response = get_response super(MiddlewareMixin, self).__init__() def __call__(self, request): response = None if hasattr(self, 'process_request'): response = self.process_request(request) if not response: response = self.get_response(request) if hasattr(self, 'process_response'): response = self.process_response(request, response) return response class RedirectsMiddleware(MiddlewareMixin): def process_exception(self, request, exception): if not getattr(settings, 'SEO_USE_REDIRECTS', False): return if request.method == 'GET' and isinstance(exception, http.Http404): handle_seo_redirects(request) return class RedirectFallbackMiddleware(MiddlewareMixin): # Defined as class-level attributes to be subclassing-friendly. response_gone_class = http.HttpResponseGone response_redirect_class = http.HttpResponsePermanentRedirect def __init__(self, get_response=None): if not apps.is_installed('django.contrib.sites'): raise ImproperlyConfigured( 'You cannot use RedirectFallbackMiddleware when ' 'django.contrib.sites is not installed.' ) super(RedirectFallbackMiddleware, self).__init__(get_response) def process_response(self, request, response): # No need to check for a redirect for non-404 responses. if response.status_code != 404: return response subdomain = getattr(request, 'subdomain', '') full_path = request.get_full_path() current_site = get_current_site(request) redirect = Redirect.objects.filter( Q(site=current_site), Q(old_path=full_path), Q(subdomain=subdomain) | Q(all_subdomains=True) ).order_by('all_subdomains').first() if redirect is None and settings.APPEND_SLASH and not request.path.endswith('/'): redirect = Redirect.objects.filter( Q(site=current_site), Q(old_path=request.get_full_path(force_append_slash=True)), Q(subdomain=subdomain) | Q(all_subdomains=True) ).order_by('all_subdomains').first() if redirect is not None: if redirect.new_path == '': return self.response_gone_class() return self.response_redirect_class(redirect.new_path) # No redirect was found. Return the response. return response