Index: django/contrib/admin/options.py
===================================================================
--- django/contrib/admin/options.py	(revision 7875)
+++ django/contrib/admin/options.py	(working copy)
@@ -353,113 +353,105 @@
         for inline in self.inline_instances:
             yield inline.get_formset(request, obj)
 
-    def save_add(self, request, model, form, formsets, post_url_continue):
-        """
-        Saves the object in the "add" stage and returns an HttpResponseRedirect.
+    def save(self, request, form, formsets, original_obj, commit=True):
+        """ Save new or changed object and its inlines. """
+        new_obj = form.save(commit=commit)
+        for formset in formsets:
+            # HACK: it seems like the parent object should be passed into
+            # a method of something, not just set as an attribute
+            formset.instance = new_obj
+            formset.save()
+        return new_obj
 
-        `form` is a bound Form instance that's verified to be valid.
-        """
+    def log_add(self, request, form, formsets, new_obj):
+        """ Log saving of new object and its inlines. """
         from django.contrib.admin.models import LogEntry, ADDITION
-        from django.contrib.contenttypes.models import ContentType
-        opts = model._meta
-        new_object = form.save(commit=True)
+        user_id = request.user.id
+        content_type_id = ContentType.objects.get_for_model(self.model).id
+        object_id = new_obj._get_pk_val()
+        LogEntry.objects.log_action(user_id, content_type_id, object_id, force_unicode(new_obj), ADDITION)
 
-        if formsets:
-            for formset in formsets:
-                # HACK: it seems like the parent obejct should be passed into
-                # a method of something, not just set as an attribute
-                formset.instance = new_object
-                formset.save()
-
-        pk_value = new_object._get_pk_val()
-        LogEntry.objects.log_action(request.user.id, ContentType.objects.get_for_model(model).id, pk_value, force_unicode(new_object), ADDITION)
-        msg = _('The %(name)s "%(obj)s" was added successfully.') % {'name': opts.verbose_name, 'obj': new_object}
-        # Here, we distinguish between different save types by checking for
-        # the presence of keys in request.POST.
-        if request.POST.has_key("_continue"):
-            request.user.message_set.create(message=msg + ' ' + _("You may edit it again below."))
-            if request.POST.has_key("_popup"):
-                post_url_continue += "?_popup=1"
-            return HttpResponseRedirect(post_url_continue % pk_value)
+    def log_change(self, request, form, formsets, original_obj, new_obj):
+        """ Log saving of changed object and its inlines. """
+        from django.contrib.admin.models import LogEntry, CHANGE
+        user_id = request.user.id
+        content_type_id = ContentType.objects.get_for_model(self.model).id
+        object_id = new_obj._get_pk_val()  
+        # Construct the change message. TODO: Temporarily commented-out,
+        # as manipulator object doesn't exist anymore, and we don't yet
+        # have a way to get fields_added, fields_changed, fields_deleted.
+        #change_message = ' '.join(change_message)
+        change_message = None
+        if not change_message:
+            change_message = _('No fields changed.')
+        LogEntry.objects.log_action(user_id, content_type_id, object_id, force_unicode(new_obj), CHANGE, change_message)
         
-        if request.POST.has_key("_popup"):
-            return HttpResponse('<script type="text/javascript">opener.dismissAddAnotherPopup(window, "%s", "%s");</script>' % \
+    def redirect_after_save(self, request, new_obj, add=False, change=False):
+        """ Redirect after saving of new or changed object. """
+        opts = self.opts
+        pk_value = new_obj._get_pk_val()
+
+        # popup
+        if request.POST.has_key('_popup'):
+            if isinstance(pk_value, str): # Quote if string, so JavaScript doesn't think it's a variable.
+                pk_value = '"%s"' % pk_value.replace('"', '\\"')
+            return HttpResponse('<script type="text/javascript">opener.dismissAddAnotherPopup(window, %s, "%s");</script>' % \
                 # escape() calls force_unicode.
-                (escape(pk_value), escape(new_object)))
-        elif request.POST.has_key("_addanother"):
-            request.user.message_set.create(message=msg + ' ' + (_("You may add another %s below.") % opts.verbose_name))
-            return HttpResponseRedirect(request.path)
+                (escape(pk_value), escape(new_obj)))
+
+        # default message        
+        if add:
+            msg = _('The %(name)s "%(obj)s" was added successfully.') % {'name': opts.verbose_name, 'obj': new_obj}
         else:
-            request.user.message_set.create(message=msg)
-            # Figure out where to redirect. If the user has change permission,
-            # redirect to the change-list page for this object. Otherwise,
-            # redirect to the admin index.
-            if self.has_change_permission(request, None):
-                post_url = '../'
+            msg = _('The %(name)s "%(obj)s" was changed successfully.') % {'name': opts.verbose_name, 'obj': new_obj}
+
+        # save as new
+        if request.POST.has_key('_saveasnew'):
+            if self.has_change_permission(request, new_obj):
+                msg += ' ' + _('You may edit it again below.')
+                redirect_url = '../%s/' % pk_value
+            elif self.has_change_permission(request):
+                redirect_url = '../'
             else:
-                post_url = '../../../'
-            return HttpResponseRedirect(post_url)
-    save_add = transaction.commit_on_success(save_add)
+                redirect_url = '../../../'
+        # save and add another
+        elif request.POST.has_key('_addanother'):
+            msg += ' ' + (_('You may add another %s below.') % opts.verbose_name)
+            if add:
+                redirect_url = request.path
+            else:
+                redirect_url = '../add/'
+        # save and continue editing
+        elif request.POST.has_key('_continue'):
+            if self.has_change_permission(request, new_obj):
+                msg += ' ' + _('You may edit it again below.')
+                if add:
+                    redirect_url = '../%s/' % pk_value
+                else:
+                    redirect_url = request.path
+            elif self.has_change_permission(request):
+                redirect_url = '../'
+            else:
+                redirect_url = '../../../'
+        # save
+        else:
+            if self.has_change_permission(request):
+                redirect_url = '../'
+            else:
+                redirect_url = '../../../'
 
-    def save_change(self, request, model, form, formsets=None):
-        """
-        Saves the object in the "change" stage and returns an HttpResponseRedirect.
-
-        `form` is a bound Form instance that's verified to be valid.
+        # create message and redirect
+        request.user.message_set.create(message=msg)
+        return HttpResponseRedirect(redirect_url)
         
-        `formsets` is a sequence of InlineFormSet instances that are verified to be valid.
-        """
-        from django.contrib.admin.models import LogEntry, CHANGE
-        from django.contrib.contenttypes.models import ContentType
-        opts = model._meta
-        new_object = form.save(commit=True)
-        pk_value = new_object._get_pk_val()
+    def save_add(self, request, form, formsets, commit=True):
+        """ Save changed object and its inlines. """
+        return self.save(request, form, formsets, None, commit)
+    save_add = transaction.commit_on_success(save_add)
 
-        if formsets:
-            for formset in formsets:
-                formset.save()
-
-        # Construct the change message.                 
-        change_message = []
-        if form.changed_data:
-            change_message.append(_('Changed %s.') % get_text_list(form.changed_data, _('and')))
-            
-        if formsets:
-            for formset in formsets:
-                for added_object in formset.new_objects:
-                    change_message.append(_('Added %(name)s "%(object)s".') 
-                                          % {'name': added_object._meta.verbose_name,
-                                             'object': added_object})
-                for changed_object, changed_fields in formset.changed_objects:
-                    change_message.append(_('Changed %(list)s for %(name)s "%(object)s".') 
-                                          % {'list': get_text_list(changed_fields, _('and')), 
-                                             'name': changed_object._meta.verbose_name, 
-                                             'object': changed_object})
-                for deleted_object in formset.deleted_objects:
-                    change_message.append(_('Deleted %(name)s "%(object)s".') 
-                                          % {'name': deleted_object._meta.verbose_name,
-                                             'object': deleted_object})
-        change_message = ' '.join(change_message)
-        if not change_message:
-            change_message = _('No fields changed.')
-        LogEntry.objects.log_action(request.user.id, ContentType.objects.get_for_model(model).id, pk_value, force_unicode(new_object), CHANGE, change_message)
-
-        msg = _('The %(name)s "%(obj)s" was changed successfully.') % {'name': opts.verbose_name, 'obj': new_object}
-        if request.POST.has_key("_continue"):
-            request.user.message_set.create(message=msg + ' ' + _("You may edit it again below."))
-            if request.REQUEST.has_key('_popup'):
-                return HttpResponseRedirect(request.path + "?_popup=1")
-            else:
-                return HttpResponseRedirect(request.path)
-        elif request.POST.has_key("_saveasnew"):
-            request.user.message_set.create(message=_('The %(name)s "%(obj)s" was added successfully. You may edit it again below.') % {'name': opts.verbose_name, 'obj': new_object})
-            return HttpResponseRedirect("../%s/" % pk_value)
-        elif request.POST.has_key("_addanother"):
-            request.user.message_set.create(message=msg + ' ' + (_("You may add another %s below.") % opts.verbose_name))
-            return HttpResponseRedirect("../add/")
-        else:
-            request.user.message_set.create(message=msg)
-            return HttpResponseRedirect("../")
+    def save_change(self, request, form, formsets, original_obj, commit=True):
+        """ Save changed object and its inlines. """
+        return self.save(request, form, formsets, original_obj, commit)
     save_change = transaction.commit_on_success(save_change)
 
     def render_change_form(self, request, model, context, add=False, change=False, form_url='', obj=None):
@@ -514,7 +506,9 @@
                     instance=obj, save_as_new=request.POST.has_key("_saveasnew"))
                 inline_formsets.append(inline_formset)
             if all_valid(inline_formsets) and form.is_valid():
-                return self.save_add(request, model, form, inline_formsets, '../%s/')
+                new_obj = self.save_add(request, form, inline_formsets)
+                self.log_add(request, form, inline_formsets, new_obj)
+                return self.redirect_after_save(request, new_obj, add=True)
         else:
             form = ModelForm(initial=dict(request.GET.items()))
             for FormSet in self.get_formsets(request):
@@ -576,7 +570,9 @@
                 inline_formsets.append(inline_formset)
 
             if all_valid(inline_formsets) and form.is_valid():
-                return self.save_change(request, model, form, inline_formsets)
+                new_obj = self.save_change(request, form, inline_formsets, obj)
+                self.log_change(request, form, inline_formsets, obj, new_obj)
+                return self.redirect_after_save(request, new_obj, change=True)
         else:
             form = ModelForm(instance=obj)
             for FormSet in self.get_formsets(request, obj):
Index: django/contrib/admin/templates/admin_doc/view_detail.html
===================================================================
--- django/contrib/admin/templates/admin_doc/view_detail.html	(revision 7875)
+++ django/contrib/admin/templates/admin_doc/view_detail.html	(working copy)
@@ -1,4 +1,4 @@
-{% extends "admin/base_site.html" %}
+{% extends "admin_doc/base.html" %}
 {% load i18n %}
 {% block breadcrumbs %}<div class="breadcrumbs"><a href="../../../">Home</a> &rsaquo; <a href="../../">Documentation</a> &rsaquo; <a href="../">Views</a> &rsaquo; {{ name }}</div>{% endblock %}
 {% block userlinks %}<a href="../../../password_change/">{% trans 'Change password' %}</a> / <a href="../../../logout/">{% trans 'Log out' %}</a>{% endblock %}
Index: django/contrib/admin/templates/admin_doc/bookmarklets.html
===================================================================
--- django/contrib/admin/templates/admin_doc/bookmarklets.html	(revision 7875)
+++ django/contrib/admin/templates/admin_doc/bookmarklets.html	(working copy)
@@ -1,7 +1,7 @@
-{% extends "admin/base_site.html" %}
+{% extends "admin_doc/base.html" %}
 
-{% block breadcrumbs %}{% load i18n %}<div class="breadcrumbs"><a href="../../">{% trans "Home" %}</a> &rsaquo; <a href="../">{% trans "Documentation" %}</a> &rsaquo; {% trans "Bookmarklets" %}</div>{% endblock %}
-{% block userlinks %}<a href="../../password_change/">{% trans 'Change password' %}</a> / <a href="../../logout/">{% trans 'Log out' %}</a>{% endblock %}
+{% block breadcrumbs %}{% load i18n %}<div class="breadcrumbs"><a href="{{ root_path }}">{% trans "Home" %}</a> &rsaquo; <a href="{{ root_path }}doc/">{% trans "Documentation" %}</a> &rsaquo; {% trans "Bookmarklets" %}</div>{% endblock %}
+{% block userlinks %}<a href="{{ root_path }}password_change/">{% trans 'Change password' %}</a> / <a href="{{ root_path }}logout/">{% trans 'Log out' %}</a>{% endblock %}
 {% block title %}{% trans "Documentation bookmarklets" %}{% endblock %}
 
 {% block content %}
Index: django/contrib/admin/templates/admin_doc/template_detail.html
===================================================================
--- django/contrib/admin/templates/admin_doc/template_detail.html	(revision 7875)
+++ django/contrib/admin/templates/admin_doc/template_detail.html	(working copy)
@@ -1,4 +1,4 @@
-{% extends "admin/base_site.html" %}
+{% extends "admin_doc/base.html" %}
 {% load i18n %}
 {% block breadcrumbs %}<div class="breadcrumbs"><a href="../../../">Home</a> &rsaquo; <a href="../../">Documentation</a> &rsaquo; Templates &rsaquo; {{ name|escape }}</div>{% endblock %}
 {% block userlinks %}<a href="../../../password_change/">{% trans 'Change password' %}</a> / <a href="../../../logout/">{% trans 'Log out' %}</a>{% endblock %}
Index: django/contrib/admin/templates/admin_doc/template_tag_index.html
===================================================================
--- django/contrib/admin/templates/admin_doc/template_tag_index.html	(revision 7875)
+++ django/contrib/admin/templates/admin_doc/template_tag_index.html	(working copy)
@@ -1,4 +1,4 @@
-{% extends "admin/base_site.html" %}
+{% extends "admin_doc/base.html" %}
 {% load i18n %}
 {% block coltype %}colSM{% endblock %}
 {% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">Home</a> &rsaquo; <a href="../">Documentation</a> &rsaquo; Tags</div>{% endblock %}
Index: django/contrib/admin/templates/admin_doc/missing_docutils.html
===================================================================
--- django/contrib/admin/templates/admin_doc/missing_docutils.html	(revision 7875)
+++ django/contrib/admin/templates/admin_doc/missing_docutils.html	(working copy)
@@ -1,7 +1,6 @@
-{% extends "admin/base_site.html" %}
+{% extends "admin_doc/base.html" %}
 {% load i18n %}
 {% block breadcrumbs %}<div class="breadcrumbs"><a href="../">Home</a> &rsaquo; Documentation</div>{% endblock %}
-{% block userlinks %}<a href="../password_change/">{% trans 'Change password' %}</a> / <a href="../logout/">{% trans 'Log out' %}</a>{% endblock %}
 {% block title %}Please install docutils{% endblock %}
 
 {% block content %}
Index: django/contrib/admin/templates/admin_doc/view_index.html
===================================================================
--- django/contrib/admin/templates/admin_doc/view_index.html	(revision 7875)
+++ django/contrib/admin/templates/admin_doc/view_index.html	(working copy)
@@ -1,4 +1,4 @@
-{% extends "admin/base_site.html" %}
+{% extends "admin_doc/base.html" %}
 {% load i18n %}
 {% block coltype %}colSM{% endblock %}
 {% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">Home</a> &rsaquo; <a href="../">Documentation</a> &rsaquo; Views</div>{% endblock %}
Index: django/contrib/admin/templates/admin_doc/model_detail.html
===================================================================
--- django/contrib/admin/templates/admin_doc/model_detail.html	(revision 7875)
+++ django/contrib/admin/templates/admin_doc/model_detail.html	(working copy)
@@ -1,4 +1,4 @@
-{% extends "admin/base_site.html" %}
+{% extends "admin_doc/base.html" %}
 {% load i18n %}
 {% block userlinks %}<a href="../../../password_change/">{% trans 'Change password' %}</a> / <a href="../../../logout/">{% trans 'Log out' %}</a>{% endblock %}
 {% block extrahead %}
Index: django/contrib/admin/templates/admin_doc/index.html
===================================================================
--- django/contrib/admin/templates/admin_doc/index.html	(revision 7875)
+++ django/contrib/admin/templates/admin_doc/index.html	(working copy)
@@ -1,4 +1,4 @@
-{% extends "admin/base_site.html" %}
+{% extends "admin_doc/base.html" %}
 {% load i18n %}
 {% block breadcrumbs %}<div class="breadcrumbs"><a href="../">Home</a> &rsaquo; Documentation</div>{% endblock %}
 {% block title %}Documentation{% endblock %}
Index: django/contrib/admin/templates/admin_doc/model_index.html
===================================================================
--- django/contrib/admin/templates/admin_doc/model_index.html	(revision 7875)
+++ django/contrib/admin/templates/admin_doc/model_index.html	(working copy)
@@ -1,4 +1,4 @@
-{% extends "admin/base_site.html" %}
+{% extends "admin_doc/base.html" %}
 {% load i18n %}
 {% block coltype %}colSM{% endblock %}
 {% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">Home</a> &rsaquo; <a href="../">Documentation</a> &rsaquo; Models</div>{% endblock %}
Index: django/contrib/admin/templates/admin_doc/template_filter_index.html
===================================================================
--- django/contrib/admin/templates/admin_doc/template_filter_index.html	(revision 7875)
+++ django/contrib/admin/templates/admin_doc/template_filter_index.html	(working copy)
@@ -1,4 +1,4 @@
-{% extends "admin/base_site.html" %}
+{% extends "admin_doc/base.html" %}
 {% load i18n %}
 {% block coltype %}colSM{% endblock %}
 {% block breadcrumbs %}<div class="breadcrumbs"><a href="../../">Home</a> &rsaquo; <a href="../">Documentation</a> &rsaquo; filters</div>{% endblock %}
Index: django/contrib/admindocs/views.py
===================================================================
--- django/contrib/admindocs/views.py	(revision 7875)
+++ django/contrib/admindocs/views.py	(working copy)
@@ -8,6 +8,7 @@
 from django.http import Http404, get_host
 from django.core import urlresolvers
 from django.contrib.admindocs import utils
+from django.contrib import admin
 from django.contrib.sites.models import Site
 from django.utils.translation import ugettext as _
 from django.utils.safestring import mark_safe
@@ -23,14 +24,14 @@
 def doc_index(request):
     if not utils.docutils_is_available:
         return missing_docutils_page(request)
-    return render_to_response('admin_doc/index.html', context_instance=RequestContext(request))
+    return render_to_response('admin_doc/index.html', context_instance=RequestContext(request),
+                              dictionary={'root_path': admin_root(request, 'doc/')})
 doc_index = staff_member_required(doc_index)
 
 def bookmarklets(request):
     # Hack! This couples this view to the URL it lives at.
-    admin_root = request.path[:-len('doc/bookmarklets/')]
     return render_to_response('admin_doc/bookmarklets.html', {
-        'admin_url': mark_safe("%s://%s%s" % (request.is_secure() and 'https' or 'http', request.get_host(), admin_root)),
+        'root_path': admin_root(request, 'doc/bookmarklets/'),
     }, context_instance=RequestContext(request))
 bookmarklets = staff_member_required(bookmarklets)
 
@@ -39,7 +40,6 @@
         return missing_docutils_page(request)
 
     load_all_installed_template_libraries()
-
     tags = []
     for module_name, library in template.libraries.items():
         for tag_name, tag_func in library.tags.items():
@@ -62,7 +62,7 @@
                 'library': tag_library,
             })
 
-    return render_to_response('admin_doc/template_tag_index.html', {'tags': tags}, context_instance=RequestContext(request))
+    return render_to_response('admin_doc/template_tag_index.html', {'tags': tags, 'root_path': admin_root(request, 'doc/tags/')}, context_instance=RequestContext(request))
 template_tag_index = staff_member_required(template_tag_index)
 
 def template_filter_index(request):
@@ -92,7 +92,7 @@
                 'meta': metadata,
                 'library': tag_library,
             })
-    return render_to_response('admin_doc/template_filter_index.html', {'filters': filters}, context_instance=RequestContext(request))
+    return render_to_response('admin_doc/template_filter_index.html', {'filters': filters, 'root_path': admin_root(request, 'doc/filters/')}, context_instance=RequestContext(request))
 template_filter_index = staff_member_required(template_filter_index)
 
 def view_index(request):
@@ -120,7 +120,7 @@
                 'site': site_obj,
                 'url': simplify_regex(regex),
             })
-    return render_to_response('admin_doc/view_index.html', {'views': views}, context_instance=RequestContext(request))
+    return render_to_response('admin_doc/view_index.html', {'views': views, 'root_path': admin_root(request, 'doc/views/')}, context_instance=RequestContext(request))
 view_index = staff_member_required(view_index)
 
 def view_detail(request, view):
@@ -152,7 +152,7 @@
         return missing_docutils_page(request)
 
     m_list = [m._meta for m in models.get_models()]
-    return render_to_response('admin_doc/model_index.html', {'models': m_list}, context_instance=RequestContext(request))
+    return render_to_response('admin_doc/model_index.html', {'models': m_list, 'root_path': admin_root(request, 'doc/models/')}, context_instance=RequestContext(request))
 model_index = staff_member_required(model_index)
 
 def model_detail(request, app_label, model_name):
@@ -262,9 +262,12 @@
 # Helper functions #
 ####################
 
+def admin_root(request, current_path):
+    return request.path[:-len(current_path)]
+
 def missing_docutils_page(request):
     """Display an error message for people without docutils"""
-    return render_to_response('admin_doc/missing_docutils.html')
+    return render_to_response('admin_doc/missing_docutils.html', dictionary={'root_path': admin_root(request, 'doc/')})
 
 def load_all_installed_template_libraries():
     # Load/register all template tag libraries from installed apps.
