Commits

Dan Watson committed aebee03

Add redirects, update middleware to find most specific Site

Comments (0)

Files changed (4)

 from django.contrib import admin
-from leaves.models import Comment, LeafMeta, Preferences, Tag, Translation
+from leaves.models import Comment, LeafMeta, Preferences, Tag, Translation, Redirect
 
 class MetaInline (admin.TabularInline):
 	model = LeafMeta
 	search_fields = ('title', 'comment', 'author_name', 'email')
 	actions = ('approve_comments',)
 	ordering = ('-pub_date',)
-	
+
 	def approve_comments(self, request, queryset):
 		update_count = queryset.update(status='published')
 		term = 'comment was' if update_count == 1 else 'comments were'
 class PreferencesAdmin (admin.ModelAdmin):
 	list_display = ('site', 'theme')
 
+class RedirectAdmin (admin.ModelAdmin):
+	list_display = ('old_path', 'new_path', 'site', 'redirect_type')
+	list_filter = ('site', 'redirect_type')
+
 admin.site.register(Comment, CommentAdmin)
 admin.site.register(Tag, TagAdmin)
 admin.site.register(Translation, TranslationAdmin)
 admin.site.register(Preferences, PreferencesAdmin)
+admin.site.register(Redirect, RedirectAdmin)

leaves/middleware.py

-from django.http import Http404, HttpResponseRedirect
+from django.shortcuts import get_object_or_404
 from django.core.urlresolvers import resolve
-from django.shortcuts import get_object_or_404
 from django.contrib.sites.models import Site
+from django.utils.encoding import iri_to_uri
 from django.conf import settings
-from leaves.models import Leaf
+from django import http
+from leaves.models import Leaf, Redirect
 from leaves.views import view_leaf
 import threading
 
 request_context = threading.local()
 
-class LeavesMiddleware (object):
+class LeavesSiteMiddleware (object):
 
 	def process_request(self, request):
-		try:
-			request.site = Site.objects.select_related('preferences').get(domain=request.META['HTTP_HOST'])
-		except Site.DoesNotExist:
-			request.site = Site.objects.select_related('preferences').get(pk=settings.SITE_ID)
+		host = request.get_host().lower()
+		if host.endswith(':80'):
+			host = host[:-3]
+		# In order to support subdomains, we need to check all the sites
+		# to find the most specific.
+		all_sites = {}
+		default_site = None
+		for s in Site.objects.select_related('preferences'):
+			all_sites[s.domain.lower()] = s
+			if s.pk == settings.SITE_ID:
+				default_site = s
+		# If all else fails, fall back to using SITE_ID.
+		request.site = default_site
+		if host in all_sites:
+			request.site = all_sites[host]
+		else:
+			# Find the most specific match, determined by match suffix length.
+			best_match = None
+			match_len = 0
+			for domain, site in all_sites.items():
+				if host.endswith(domain) and len(domain) > match_len:
+					best_match = site
+					match_len = len(domain)
+			if best_match:
+				request.site = best_match
 		request_context.site = request.site
 
 	def process_response(self, request, response):
+		if hasattr(request_context, 'site'):
+			del request_context.site
+		return response
+
+class LeavesFallbackMiddleware (object):
+
+	def process_response(self, request, response):
 		if response.status_code != 404:
-			if hasattr(request_context, 'site'):
-				del request_context.site
 			return response
 		try:
 			url = request.path_info
 			if not url.endswith('/') and settings.APPEND_SLASH:
-				return HttpResponseRedirect('%s/' % request.path)
+				return http.HttpResponseRedirect('%s/' % request.path)
 			if not url.startswith('/'):
 				url = '/' + url
 			leaf = get_object_or_404(Leaf.on_site.active(), custom_url=url)
 				r = match.func(request, *match.args, **match.kwargs)
 			except:
 				r = view_leaf(request, leaf.pk)
+			# If the response is a TemplateResponse, we need to bake it.
 			if hasattr(r, 'render'):
 				r.render()
 			return r
-		except Http404:
-			return response
+		except http.Http404:
+			# If we still haven't found anything, check the redirects.
+			try:
+				url = request.get_full_path()
+				r = Redirect.objects.get(old_path=url, site=request.site)
+				if not r.new_path:
+					return http.HttpResponseGone()
+				resp = http.HttpResponse(status=r.redirect_type)
+				resp['Location'] = iri_to_uri(r.new_path)
+				return resp
+			except Redirect.DoesNotExist:
+				return response
 		except:
 			if settings.DEBUG:
 				raise
 			return response
-		finally:
-			if hasattr(request_context, 'site'):
-				del request_context.site
 	subclass = models.ForeignKey(ContentType, related_name='leaves')
 	num_views = models.PositiveIntegerField(default=0, editable=False)
 	last_viewed = models.DateTimeField(blank=True, null=True, editable=False)
-	
+
 	objects = models.Manager()
 	on_site = LeafManager()
 
 	def __unicode__(self):
 		return '%s-%s.%s (%s)' % (self.content_type.model, self.object_id, self.field, self.language)
 
+class Redirect (models.Model):
+	REDIRECT_TYPES = (
+		(301, '301 - Moved Permanently'),
+		(302, '302 - Found'),
+		(307, '307 - Temporary Redirect'),
+	)
+	site = models.ForeignKey(Site, related_name='redirects')
+	old_path = models.CharField(max_length=250, db_index=True)
+	new_path = models.CharField(max_length=250, blank=True)
+	redirect_type = models.IntegerField(choices=REDIRECT_TYPES, default=301)
+
+	class Meta:
+		unique_together = ('site', 'old_path')
+		ordering = ('old_path',)
+
+	def __unicode__(self):
+		return '%s --> %s (%s)' % (self.old_path, self.new_path, self.redirect_type)
+
 @receiver(post_init, dispatch_uid='leaves.models.handle_model_init')
 def handle_model_init(sender, **kwargs):
 	obj = kwargs['instance']
 )
 
 MIDDLEWARE_CLASSES = (
+	'leaves.middleware.LeavesSiteMiddleware',
 	'django.middleware.common.CommonMiddleware',
 	'django.contrib.sessions.middleware.SessionMiddleware',
 	'django.middleware.csrf.CsrfViewMiddleware',
 	'django.contrib.auth.middleware.AuthenticationMiddleware',
 	'django.contrib.messages.middleware.MessageMiddleware',
 	'django.middleware.locale.LocaleMiddleware',
-	'leaves.middleware.LeavesMiddleware',
+	'leaves.middleware.LeavesFallbackMiddleware',
 )
 
 ROOT_URLCONF = 'urls'