Index: django/contrib/admin/media/css/global.css
===================================================================
--- django/contrib/admin/media/css/global.css	(revision 4057)
+++ django/contrib/admin/media/css/global.css	(working copy)
@@ -135,6 +135,10 @@
 .object-tools a.viewsitelink:hover, .object-tools a.golink:hover { background:#5b80b2 url(../img/admin/tooltag-arrowright_over.gif) top right no-repeat; }
 .object-tools a.addlink { background:#999 url(../img/admin/tooltag-add.gif) top right no-repeat; padding-right:28px; }
 .object-tools a.addlink:hover { background:#5b80b2 url(../img/admin/tooltag-add_over.gif) top right no-repeat; }
+#delete_checked_submit { background:#999 url(../img/admin/tool-left.gif) top left no-repeat; float:right; }
+#delete_checked_submit:hover { background:#5b80b2 url(../img/admin/tool-left_over.gif) top left no-repeat; }
+#delete_checked_submit input { background:#999 url(../img/admin/tooltag-delete.gif) top right no-repeat; border:0; padding:1px 28px 0 0; font-size:11px; font-weight:bold; font-family:Arial,Helvetica,sans-serif; color:#FFF; margin:0 0 0 10px; }
+#delete_checked_submit input:hover { background:#5b80b2 url(../img/admin/tooltag-delete_over.gif) top right no-repeat; }
 
 /* OBJECT HISTORY */
 table#change-history { width:100%; }
Index: django/contrib/admin/templatetags/admin_list.py
===================================================================
--- django/contrib/admin/templatetags/admin_list.py	(revision 4057)
+++ django/contrib/admin/templatetags/admin_list.py	(working copy)
@@ -66,7 +66,7 @@
     }
 pagination = register.inclusion_tag('admin/pagination.html')(pagination)
 
-def result_headers(cl):
+def result_headers(cl, user):
     lookup_opts = cl.lookup_opts
 
     for i, field_name in enumerate(lookup_opts.admin.list_display):
@@ -100,8 +100,10 @@
                        "sortable": True,
                        "url": cl.get_query_string({ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}),
                        "class_attrib": (th_classes and ' class="%s"' % ' '.join(th_classes) or '')}
+    if user.has_perm(lookup_opts.app_label + '.' + lookup_opts.get_delete_permission()):
+        yield{"text":"Delete?"}
 
-def items_for_result(cl, result):
+def items_for_result(cl, result, user):
     first = True
     pk = cl.lookup_opts.pk.attname
     for field_name in cl.lookup_opts.admin.list_display:
@@ -173,15 +175,19 @@
                 (table_tag, row_class, url, (cl.is_popup and ' onclick="opener.dismissRelatedLookupPopup(window, %r); return false;"' % result_id or ''), result_repr, table_tag))
         else:
             yield ('<td%s>%s</td>' % (row_class, result_repr))
+    if user.has_perm(cl.lookup_opts.app_label + '.' + cl.lookup_opts.get_delete_permission()):
+        yield('<td><input type="checkbox" name="%s" /></td>' % result_id)
 
-def results(cl):
+def results(cl, user):
     for res in cl.result_list:
-        yield list(items_for_result(cl,res))
+        yield list(items_for_result(cl, res, user))
 
-def result_list(cl):
+def result_list(cl, user):
+    lookup_opts = cl.lookup_opts
     return {'cl': cl,
-            'result_headers': list(result_headers(cl)),
-            'results': list(results(cl))}
+            'has_delete_permission' : user.has_perm(lookup_opts.app_label + '.' + lookup_opts.get_delete_permission()),
+            'result_headers': list(result_headers(cl, user)),
+            'results': list(results(cl, user))}
 result_list = register.inclusion_tag("admin/change_list_results.html")(result_list)
 
 def date_hierarchy(cl):
Index: django/contrib/admin/urls.py
===================================================================
--- django/contrib/admin/urls.py	(revision 4057)
+++ django/contrib/admin/urls.py	(working copy)
@@ -33,6 +33,7 @@
     # Add/change/delete/history
     ('^([^/]+)/([^/]+)/$', 'django.contrib.admin.views.main.change_list'),
     ('^([^/]+)/([^/]+)/add/$', 'django.contrib.admin.views.main.add_stage'),
+    ('^([^/]+)/([^/]+)/delete/$', 'django.contrib.admin.views.main.delete_checked'),
     ('^([^/]+)/([^/]+)/(.+)/history/$', 'django.contrib.admin.views.main.history'),
     ('^([^/]+)/([^/]+)/(.+)/delete/$', 'django.contrib.admin.views.main.delete_stage'),
     ('^([^/]+)/([^/]+)/(.+)/$', 'django.contrib.admin.views.main.change_stage'),
Index: django/contrib/admin/views/main.py
===================================================================
--- django/contrib/admin/views/main.py	(revision 4057)
+++ django/contrib/admin/views/main.py	(working copy)
@@ -742,6 +742,9 @@
 
 def change_list(request, app_label, model_name):
     model = models.get_model(app_label, model_name)
+    opts = model._meta
+    auto_populated_fields = [f for f in opts.fields if f.prepopulate_from]
+    field_sets = opts.admin.get_field_sets(opts)
     if model is None:
         raise Http404, "App %r, model %r, not found" % (app_label, model_name)
     if not request.user.has_perm(app_label + '.' + model._meta.get_change_permission()):
@@ -763,7 +766,51 @@
         'cl': cl,
     })
     c.update({'has_add_permission': c['perms'][app_label][cl.opts.get_add_permission()]}),
+    c.update({'has_delete_permission': c['perms'][app_label][cl.opts.get_delete_permission()]}),
+    c.update({'javascript_imports': get_javascript_imports(opts, auto_populated_fields, field_sets)}),
     return render_to_response(['admin/%s/%s/change_list.html' % (app_label, cl.opts.object_name.lower()),
                                'admin/%s/change_list.html' % app_label,
                                'admin/change_list.html'], context_instance=c)
 change_list = staff_member_required(never_cache(change_list))
+
+def delete_checked (request,app_label, model_name):
+    "Essentially the same as delete stage only for a list of objects in post parameters"
+    import sets
+    model = models.get_model(app_label, model_name)
+    if model is None:
+        raise Http404, "App %r, model %r, not found" % (app_label, model_name)
+    opts = model._meta
+    if not request.user.has_perm(app_label + '.' + opts.get_delete_permission()):
+        raise PermissionDenied
+    perms_needed = sets.Set()
+    #Fetch all the objects
+    objects = [get_object_or_404(model, pk = unquote(o)) for o in request.POST if o != "doit"]
+
+    perms_needed = sets.Set()
+    for obj in objects:
+        obj.deleted_objects = ['%s: <a href="../../%s/">%s</a>' % (capfirst(opts.verbose_name), getattr(obj,opts.pk.attname), escape(str(obj))), []]
+        obj.deleteid = getattr(obj,opts.pk.attname)
+        _get_deleted_objects(obj.deleted_objects, perms_needed, request.user, obj, opts, 1)
+
+    if request.POST.get('doit', 0): # The user has already confirmed the deletion.
+        if perms_needed:
+            raise PermissionDenied
+        obj_display = []
+        for obj in objects:
+            obj_display.append(str(obj))
+            obj.delete()
+            LogEntry.objects.log_action(request.user.id, ContentType.objects.get_for_model(model).id, obj.deleteid, str(obj), DELETION)
+        request.user.message_set.create(message=_('The %(name)s objects: "%(obj)s" were deleted successfully.') % {'name': opts.verbose_name, 'obj': ",".join(obj_display)})
+        return HttpResponseRedirect("../")      
+
+    extra_context = {
+        "title": _("Are you sure?"),
+        "object_name": opts.verbose_name,
+        "daobjects": objects,
+        "perms_lacking": perms_needed,
+        "opts": model._meta,
+    }
+    return render_to_response(["admin/%s/%s/deletelist_confirmation.html" % (app_label, opts.object_name.lower() ),
+                               "admin/%s/deletelist_confirmation.html" % app_label ,
+                               "admin/deletelist_confirmation.html"], extra_context, context_instance=template.RequestContext(request))
+delete_checked = staff_member_required(delete_checked)
Index: django/contrib/admin/templates/admin/change_list.html
===================================================================
--- django/contrib/admin/templates/admin/change_list.html	(revision 4057)
+++ django/contrib/admin/templates/admin/change_list.html	(working copy)
@@ -14,8 +14,12 @@
 {% block search %}{% search_form cl %}{% endblock %}
 {% block date_hierarchy %}{% date_hierarchy cl %}{% endblock %}
 {% block filters %}{% filters cl %}{% endblock %}
-{% block result_list %}{% result_list cl %}{% endblock %}
+{% block result_list %}{% result_list cl user %}{% endblock %}
 {% block pagination %}{% pagination cl %}{% endblock %}
 </div>
 </div>
+{% if has_delete_permission %}
+<div id="delete_checked_submit"><input type="submit" value="{% trans 'Delete checked' %}" /></div>
+</form>
+{% endif %}
 {% endblock %}
Index: django/contrib/admin/templates/admin/change_list_results.html
===================================================================
--- django/contrib/admin/templates/admin/change_list_results.html	(revision 4057)
+++ django/contrib/admin/templates/admin/change_list_results.html	(working copy)
@@ -1,4 +1,7 @@
 {% if results %}
+{% if has_delete_permission %}
+<form name="delete_checked_form" id="delete_checked_form" method="post" action="delete/">
+{% endif %}
 <table cellspacing="0">
 <thead>
 <tr>
Index: django/contrib/admin/templates/admin/deletelist_confirmation.html
===================================================================
--- django/contrib/admin/templates/admin/deletelist_confirmation.html	(revision 0)
+++ django/contrib/admin/templates/admin/deletelist_confirmation.html	(revision 0)
@@ -0,0 +1,32 @@
+{% extends "admin/base_site.html" %}
+{% load i18n %}
+{% block userlinks %}<a href="../../../../doc/">{% trans 'Documentation' %}</a> / <a href="../../../../password_change/">{% trans 'Change password' %}</a> / <a href="../../../../logout/">{% trans 'Log out' %}</a>{% endblock %}
+{% block breadcrumbs %}
+<div class="breadcrumbs">
+     <a href="../../../">{% trans "Home" %}</a> &rsaquo;
+     <a href="../">{{ opts.verbose_name_plural|capfirst|escape }}</a> &rsaquo;
+     {% trans 'Delete' %}
+</div>
+{% endblock %}
+{% block content %}
+{% if perms_lacking %}
+    <p>{% blocktrans with object|escape as escaped_object %}Deleting the {{ object_name }} '{{ escaped_object }}' would result in deleting related objects, but your account doesn't have permission to delete the following types of objects:{% endblocktrans %}</p>
+    <ul>
+    {% for obj in perms_lacking %}
+        <li>{{ obj|escape }}</li>
+    {% endfor %}
+    </ul>
+{% else %}
+	<form action="" method="post">
+    <p>{% blocktrans with object|escape as escaped_object %}Are you sure you want to delete the following {{ object_name }} objects? All of the following related items will be deleted:{% endblocktrans %}</p>
+    {% for object in daobjects%}
+    <ul>{{ object.deleted_objects|unordered_list }}</ul>
+    <div>
+	<input type="hidden" name="{{object.deleteid}}" value="on">
+	{%endfor%}
+    <input type="hidden" name="doit" value="yes" />
+    <input type="submit" value="{% trans "Yes, I'm sure" %}" />
+    </div>
+    </form>
+{% endif %}
+{% endblock %}
