diff --git a/djblets/extensions/base.py b/djblets/extensions/base.py
--- a/djblets/extensions/base.py
+++ b/djblets/extensions/base.py
@@ -214,7 +214,11 @@ class ExtensionManager(object):
         for requirement_id in ext_class.requirements:
             self.enable_extension(requirement_id)
 
-        self.__install_extension(ext_class)
+        try:
+            self.__install_extension(ext_class)
+        except InstallExtensionError, e:
+            raise EnablingExtensionError(e.message)
+
         ext_class.registration.enabled = True
         ext_class.registration.save()
         return self.__init_extension(ext_class)
@@ -232,10 +236,10 @@ class ExtensionManager(object):
         for dependent_id in self.get_dependent_extensions(extension_id):
             self.disable_extension(dependent_id)
 
-        extension.registration.enabled = False
-        extension.registration.save()
         self.__uninstall_extension(extension)
         self.__uninit_extension(extension)
+        extension.registration.enabled = False
+        extension.registration.save()
 
     def load(self):
         """
@@ -397,6 +401,10 @@ class ExtensionManager(object):
             logging.error(e.message)
             raise InstallExtensionError(e.message)
 
+        # Remove this again, since we only needed it for syncdb and
+        # evolve.  __init_extension will add it again later in
+        # the install.
+        settings.INSTALLED_APPS.remove(ext_class.info.app_name)
         # Mark the extension as installed
         ext_class.registration.installed = True
         ext_class.registration.save()
diff --git a/djblets/extensions/hooks.py b/djblets/extensions/hooks.py
--- a/djblets/extensions/hooks.py
+++ b/djblets/extensions/hooks.py
@@ -1,5 +1,5 @@
 from django.conf import settings
-from django.core.urlresolvers import get_resolver
+from django.core.urlresolvers import get_resolver, NoReverseMatch, reverse
 
 from djblets.extensions.base import ExtensionHook, ExtensionHookPoint
 
@@ -44,10 +44,11 @@ class TemplateHook(ExtensionHook):
 
     _by_name = {}
 
-    def __init__(self, extension, name, template_name):
+    def __init__(self, extension, name, template_name, apply_to=[]):
         ExtensionHook.__init__(self, extension)
         self.name = name
         self.template_name = template_name
+        self.apply_to = apply_to
 
         if not name in self.__class__._by_name:
             self.__class__._by_name[name] = [self]
@@ -60,6 +61,41 @@ class TemplateHook(ExtensionHook):
         print "shutting down %s" % self.name
         self.__class__._by_name[self.name].remove(self)
 
+    def applies_to(self, context):
+        """Returns whether or not this TemplateHook should be applied given the
+        current context.
+        """
+
+        # If apply_to is empty, this means we apply to all - so
+        # return true
+        if not self.apply_to:
+            return True
+
+        # Extensions Middleware stashes the kwargs into the context
+        kwargs = context['request']._djblets_extensions_kwargs
+        current_url = context['request'].path_info
+
+        # For each URL name in apply_to, check to see if the reverse
+        # URL matches the current URL.
+        for applicable in self.apply_to:
+            try:
+                reverse_url = reverse(applicable, args=(), kwargs=kwargs)
+            except NoReverseMatch:
+                # It's possible that the URL we're reversing doesn't take
+                # any arguments.
+                try:
+                    reverse_url = reverse(applicable)
+                except NoReverseMatch:
+                    # No matches here, move along.
+                    continue
+
+            # If we got here, we found a reversal.  Let's compare to the
+            # current URL
+            if reverse_url == current_url:
+                return True
+
+        return False
+
     @classmethod
     def by_name(cls, name):
         if name in cls._by_name:
diff --git a/djblets/extensions/middleware.py b/djblets/extensions/middleware.py
--- /dev/null
+++ b/djblets/extensions/middleware.py
@@ -0,0 +1,6 @@
+class ExtensionsMiddleware(object):
+    """Middleware that takes the kwargs dict passed to a view, and
+    stashes it into the request.
+    """
+    def process_view(self, request, view, args, kwargs):
+        request._djblets_extensions_kwargs = kwargs
diff --git a/djblets/extensions/templatetags/djblets_extensions.py b/djblets/extensions/templatetags/djblets_extensions.py
--- a/djblets/extensions/templatetags/djblets_extensions.py
+++ b/djblets/extensions/templatetags/djblets_extensions.py
@@ -18,7 +18,8 @@ def template_hook_point(context, name):
     """
     s = ""
     for hook in TemplateHook.by_name(name):
-        s += render_to_string(hook.template_name, context)
+        if hook.applies_to(context):
+            s += render_to_string(hook.template_name, context)
 
     return s
 
diff --git a/djblets/webapi/resources.py b/djblets/webapi/resources.py
--- a/djblets/webapi/resources.py
+++ b/djblets/webapi/resources.py
@@ -18,10 +18,11 @@ from djblets.webapi.decorators import webapi_login_required, \
                                       webapi_permission_required, \
                                       webapi_request_fields, \
                                       webapi_response_errors
-from djblets.webapi.errors import WebAPIError, DISABLE_EXTENSION_FAILED, \
+from djblets.webapi.errors import DISABLE_EXTENSION_FAILED, \
                                   DOES_NOT_EXIST, \
                                   ENABLE_EXTENSION_FAILED, \
-                                  PERMISSION_DENIED
+                                  PERMISSION_DENIED, \
+                                  WebAPIError
 
 
 _model_to_resources = {}
