Fixed #12282 - When paginated allow selecting all items in the admin changlist.
Thanks to Martin Mahner, Rob Hudson and Zain Memon for providing inital patches and guidance. git-svn-id: http://code.djangoproject.com/svn/django/trunk@12298 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
c14937cf7a
commit
f109f36a06
|
@ -17,6 +17,8 @@ ACTION_CHECKBOX_NAME = '_selected_action'
|
||||||
|
|
||||||
class ActionForm(forms.Form):
|
class ActionForm(forms.Form):
|
||||||
action = forms.ChoiceField(label=_('Action:'))
|
action = forms.ChoiceField(label=_('Action:'))
|
||||||
|
select_across = forms.BooleanField(label='', required=False, initial=0,
|
||||||
|
widget=forms.HiddenInput({'class': 'select-across'}))
|
||||||
|
|
||||||
checkbox = forms.CheckboxInput({'class': 'action-select'}, lambda value: False)
|
checkbox = forms.CheckboxInput({'class': 'action-select'}, lambda value: False)
|
||||||
|
|
||||||
|
|
|
@ -228,12 +228,6 @@
|
||||||
border-right: 1px solid #ddd;
|
border-right: 1px solid #ddd;
|
||||||
}
|
}
|
||||||
|
|
||||||
.action_counter{
|
|
||||||
font-size: 11px;
|
|
||||||
margin: 0 0.5em;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#changelist table input {
|
#changelist table input {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
@ -250,6 +244,21 @@
|
||||||
background: white url(../img/admin/nav-bg-reverse.gif) 0 -10px repeat-x;
|
background: white url(../img/admin/nav-bg-reverse.gif) 0 -10px repeat-x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#changelist .actions.selected {
|
||||||
|
background: #fffccf;
|
||||||
|
border-top: 1px solid #fffee8;
|
||||||
|
border-bottom: 1px solid #edecd6;
|
||||||
|
}
|
||||||
|
|
||||||
|
#changelist .actions span.all,
|
||||||
|
#changelist .actions span.action-counter,
|
||||||
|
#changelist .actions span.clear,
|
||||||
|
#changelist .actions span.question {
|
||||||
|
font-size: 11px;
|
||||||
|
margin: 0 0.5em;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
#changelist .actions:last-child {
|
#changelist .actions:last-child {
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,83 +1,111 @@
|
||||||
var Actions = {
|
(function($) {
|
||||||
init: function() {
|
$.fn.actions = function(opts) {
|
||||||
counterSpans = document.getElementsBySelector('span._acnt');
|
var options = $.extend({}, $.fn.actions.defaults, opts);
|
||||||
counterContainer = document.getElementsBySelector('span.action_counter');
|
var actionCheckboxes = $(this);
|
||||||
actionCheckboxes = document.getElementsBySelector('tr input.action-select');
|
checker = function(checked) {
|
||||||
selectAll = document.getElementById('action-toggle');
|
if (checked) {
|
||||||
lastChecked = null;
|
showQuestion();
|
||||||
for(var i = 0; i < counterContainer.length; i++) {
|
} else {
|
||||||
counterContainer[i].style.display = 'inline';
|
reset();
|
||||||
}
|
}
|
||||||
if (selectAll) {
|
$(actionCheckboxes).attr("checked", checked)
|
||||||
selectAll.style.display = 'inline';
|
.parent().parent().toggleClass(options.selectedClass, checked);
|
||||||
addEvent(selectAll, 'click', function() {
|
}
|
||||||
Actions.checker(selectAll.checked);
|
updateCounter = function() {
|
||||||
Actions.counter();
|
var count = $(actionCheckboxes).filter(":checked").length;
|
||||||
});
|
$("span._acnt").html(count);
|
||||||
}
|
$(options.allToggle).attr("checked", function() {
|
||||||
for(var i = 0; i < actionCheckboxes.length; i++) {
|
if (count == actionCheckboxes.length) {
|
||||||
addEvent(actionCheckboxes[i], 'click', function(e) {
|
value = true;
|
||||||
if (!e) { var e = window.event; }
|
showQuestion();
|
||||||
var target = e.target ? e.target : e.srcElement;
|
} else {
|
||||||
if (lastChecked && lastChecked != target && e.shiftKey == true) {
|
value = false;
|
||||||
var inrange = false;
|
clearAcross();
|
||||||
lastChecked.checked = target.checked;
|
}
|
||||||
Actions.toggleRow(lastChecked.parentNode.parentNode, target.checked);
|
return value
|
||||||
for (var i = 0; i < actionCheckboxes.length; i++) {
|
});
|
||||||
if (actionCheckboxes[i] == lastChecked || actionCheckboxes[i] == target) {
|
}
|
||||||
inrange = (inrange) ? false : true;
|
showQuestion = function() {
|
||||||
}
|
$(options.acrossClears).hide();
|
||||||
if (inrange) {
|
$(options.acrossQuestions).show();
|
||||||
actionCheckboxes[i].checked = target.checked;
|
$(options.allContainer).hide();
|
||||||
Actions.toggleRow(actionCheckboxes[i].parentNode.parentNode, target.checked);
|
}
|
||||||
}
|
showClear = function() {
|
||||||
}
|
$(options.acrossClears).show();
|
||||||
}
|
$(options.acrossQuestions).hide();
|
||||||
lastChecked = target;
|
$(options.actionContainer).toggleClass(options.selectedClass);
|
||||||
Actions.counter();
|
$(options.allContainer).show();
|
||||||
});
|
$(options.counterContainer).hide();
|
||||||
}
|
}
|
||||||
var changelistTable = document.getElementsBySelector('#changelist table')[0];
|
reset = function() {
|
||||||
if (changelistTable) {
|
$(options.acrossClears).hide();
|
||||||
addEvent(changelistTable, 'click', function(e) {
|
$(options.acrossQuestions).hide();
|
||||||
if (!e) { var e = window.event; }
|
$(options.allContainer).hide();
|
||||||
var target = e.target ? e.target : e.srcElement;
|
$(options.counterContainer).show();
|
||||||
if (target.nodeType == 3) { target = target.parentNode; }
|
}
|
||||||
if (target.className == 'action-select') {
|
clearAcross = function() {
|
||||||
var tr = target.parentNode.parentNode;
|
reset();
|
||||||
Actions.toggleRow(tr, target.checked);
|
$(options.acrossInput).val(0);
|
||||||
}
|
$(options.actionContainer).removeClass(options.selectedClass);
|
||||||
});
|
}
|
||||||
}
|
// Show counter by default
|
||||||
},
|
$(options.counterContainer).show();
|
||||||
toggleRow: function(tr, checked) {
|
// Check state of checkboxes and reinit state if needed
|
||||||
if (checked && tr.className.indexOf('selected') == -1) {
|
$(this).filter(":checked").each(function(i) {
|
||||||
tr.className += ' selected';
|
$(this).parent().parent().toggleClass(options.selectedClass);
|
||||||
} else if (!checked) {
|
updateCounter();
|
||||||
tr.className = tr.className.replace(' selected', '');
|
if ($(options.acrossInput).val() == 1) {
|
||||||
}
|
showClear();
|
||||||
},
|
}
|
||||||
checked: function() {
|
});
|
||||||
selectAll.checked = false;
|
$(options.allToggle).show().click(function() {
|
||||||
},
|
checker($(this).attr("checked"));
|
||||||
checker: function(checked) {
|
updateCounter();
|
||||||
for(var i = 0; i < actionCheckboxes.length; i++) {
|
});
|
||||||
actionCheckboxes[i].checked = checked;
|
$("div.actions span.question a").click(function(event) {
|
||||||
Actions.toggleRow(actionCheckboxes[i].parentNode.parentNode, checked);
|
event.preventDefault();
|
||||||
}
|
$(options.acrossInput).val(1);
|
||||||
},
|
showClear();
|
||||||
counter: function() {
|
});
|
||||||
counter = 0;
|
$("div.actions span.clear a").click(function(event) {
|
||||||
for(var i = 0; i < actionCheckboxes.length; i++) {
|
event.preventDefault();
|
||||||
if(actionCheckboxes[i].checked){
|
$(options.allToggle).attr("checked", false);
|
||||||
counter++;
|
clearAcross();
|
||||||
}
|
checker(0);
|
||||||
}
|
updateCounter();
|
||||||
for(var i = 0; i < counterSpans.length; i++) {
|
});
|
||||||
counterSpans[i].innerHTML = counter;
|
lastChecked = null;
|
||||||
}
|
$(actionCheckboxes).click(function(event) {
|
||||||
selectAll.checked = (counter == actionCheckboxes.length);
|
if (!event) { var event = window.event; }
|
||||||
}
|
var target = event.target ? event.target : event.srcElement;
|
||||||
};
|
if (lastChecked && $.data(lastChecked) != $.data(target) && event.shiftKey == true) {
|
||||||
|
var inrange = false;
|
||||||
addEvent(window, 'load', Actions.init);
|
$(lastChecked).attr("checked", target.checked)
|
||||||
|
.parent().parent().toggleClass(options.selectedClass, target.checked);
|
||||||
|
$(actionCheckboxes).each(function() {
|
||||||
|
if ($.data(this) == $.data(lastChecked) || $.data(this) == $.data(target)) {
|
||||||
|
inrange = (inrange) ? false : true;
|
||||||
|
}
|
||||||
|
if (inrange) {
|
||||||
|
$(this).attr("checked", target.checked)
|
||||||
|
.parent().parent().toggleClass(options.selectedClass, target.checked);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
$(target).parent().parent().toggleClass(options.selectedClass, target.checked);
|
||||||
|
lastChecked = target;
|
||||||
|
updateCounter();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/* Setup plugin defaults */
|
||||||
|
$.fn.actions.defaults = {
|
||||||
|
actionContainer: "div.actions",
|
||||||
|
counterContainer: "span.action-counter",
|
||||||
|
allContainer: "div.actions span.all",
|
||||||
|
acrossInput: "div.actions input.select-across",
|
||||||
|
acrossQuestions: "div.actions span.question",
|
||||||
|
acrossClears: "div.actions span.clear",
|
||||||
|
allToggle: "#action-toggle",
|
||||||
|
selectedClass: "selected"
|
||||||
|
}
|
||||||
|
})(jQuery);
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
(function(a){a.fn.actions=function(d){var c=a.extend({},a.fn.actions.defaults,d);var b=a(this);checker=function(e){if(e){showQuestion()}else{reset()}a(b).attr("checked",e).parent().parent().toggleClass(c.selectedClass,e)};updateCounter=function(){var e=a(b).filter(":checked").length;a("span._acnt").html(e);a(c.allToggle).attr("checked",function(){if(e==b.length){value=true;showQuestion()}else{value=false;clearAcross()}return value})};showQuestion=function(){a(c.acrossClears).hide();a(c.acrossQuestions).show();a(c.allContainer).hide()};showClear=function(){a(c.acrossClears).show();a(c.acrossQuestions).hide();a(c.actionContainer).toggleClass(c.selectedClass);a(c.allContainer).show();a(c.counterContainer).hide()};reset=function(){a(c.acrossClears).hide();a(c.acrossQuestions).hide();a(c.allContainer).hide();a(c.counterContainer).show()};clearAcross=function(){reset();a(c.acrossInput).val(0);a(c.actionContainer).removeClass(c.selectedClass)};a(c.counterContainer).show();a(this).filter(":checked").each(function(e){a(this).parent().parent().toggleClass(c.selectedClass);updateCounter();if(a(c.acrossInput).val()==1){showClear()}});a(c.allToggle).show().click(function(){checker(a(this).attr("checked"));updateCounter()});a("div.actions span.question a").click(function(e){e.preventDefault();a(c.acrossInput).val(1);showClear()});a("div.actions span.clear a").click(function(e){e.preventDefault();a(c.allToggle).attr("checked",false);clearAcross();checker(0);updateCounter()});lastChecked=null;a(b).click(function(f){if(!f){var f=window.event}var g=f.target?f.target:f.srcElement;if(lastChecked&&a.data(lastChecked)!=a.data(g)&&f.shiftKey==true){var e=false;a(lastChecked).attr("checked",g.checked).parent().parent().toggleClass(c.selectedClass,g.checked);a(b).each(function(){if(a.data(this)==a.data(lastChecked)||a.data(this)==a.data(g)){e=(e)?false:true}if(e){a(this).attr("checked",g.checked).parent().parent().toggleClass(c.selectedClass,g.checked)}})}a(g).parent().parent().toggleClass(c.selectedClass,g.checked);lastChecked=g;updateCounter()})};a.fn.actions.defaults={actionContainer:"div.actions",counterContainer:"span.action-counter",allContainer:"div.actions span.all",acrossInput:"div.actions input.select-across",acrossQuestions:"div.actions span.question",acrossClears:"div.actions span.clear",allToggle:"#action-toggle",selectedClass:"selected"}})(jQuery);
|
|
@ -268,7 +268,7 @@ class ModelAdmin(BaseModelAdmin):
|
||||||
|
|
||||||
js = ['js/core.js', 'js/admin/RelatedObjectLookups.js']
|
js = ['js/core.js', 'js/admin/RelatedObjectLookups.js']
|
||||||
if self.actions is not None:
|
if self.actions is not None:
|
||||||
js.extend(['js/getElementsBySelector.js', 'js/actions.js'])
|
js.extend(['js/jquery.min.js', 'js/actions.min.js'])
|
||||||
if self.prepopulated_fields:
|
if self.prepopulated_fields:
|
||||||
js.append('js/urlify.js')
|
js.append('js/urlify.js')
|
||||||
if self.opts.get_ordered_objects():
|
if self.opts.get_ordered_objects():
|
||||||
|
@ -724,18 +724,24 @@ class ModelAdmin(BaseModelAdmin):
|
||||||
# If the form's valid we can handle the action.
|
# If the form's valid we can handle the action.
|
||||||
if action_form.is_valid():
|
if action_form.is_valid():
|
||||||
action = action_form.cleaned_data['action']
|
action = action_form.cleaned_data['action']
|
||||||
|
select_across = action_form.cleaned_data['select_across']
|
||||||
func, name, description = self.get_actions(request)[action]
|
func, name, description = self.get_actions(request)[action]
|
||||||
|
|
||||||
# Get the list of selected PKs. If nothing's selected, we can't
|
# Get the list of selected PKs. If nothing's selected, we can't
|
||||||
# perform an action on it, so bail.
|
# perform an action on it, so bail. Except we want to perform
|
||||||
|
# the action explicitely on all objects.
|
||||||
selected = request.POST.getlist(helpers.ACTION_CHECKBOX_NAME)
|
selected = request.POST.getlist(helpers.ACTION_CHECKBOX_NAME)
|
||||||
if not selected:
|
if not selected and not select_across:
|
||||||
# Reminder that something needs to be selected or nothing will happen
|
# Reminder that something needs to be selected or nothing will happen
|
||||||
msg = _("Items must be selected in order to perform actions on them. No items have been changed.")
|
msg = _("Items must be selected in order to perform actions on them. No items have been changed.")
|
||||||
self.message_user(request, msg)
|
self.message_user(request, msg)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
response = func(self, request, queryset.filter(pk__in=selected))
|
if not select_across:
|
||||||
|
# Perform the action only on the selected objects
|
||||||
|
queryset = queryset.filter(pk__in=selected)
|
||||||
|
|
||||||
|
response = func(self, request, queryset)
|
||||||
|
|
||||||
# Actions may return an HttpResponse, which will be used as the
|
# Actions may return an HttpResponse, which will be used as the
|
||||||
# response from the POST. If not, we'll be a good little HTTP
|
# response from the POST. If not, we'll be a good little HTTP
|
||||||
|
|
|
@ -1,10 +1,20 @@
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
{% for field in action_form %}<label>{{ field.label }} {{ field }}</label>{% endfor %}
|
{% for field in action_form %}{% if field.label %}<label>{{ field.label }} {% endif %}{{ field }}{% if field.label %}</label>{% endif %}{% endfor %}
|
||||||
<button type="submit" class="button" title="{% trans "Run the selected action" %}" name="index" value="{{ action_index|default:0 }}">{% trans "Go" %}</button>
|
<button type="submit" class="button" title="{% trans "Run the selected action" %}" name="index" value="{{ action_index|default:0 }}">{% trans "Go" %}</button>
|
||||||
{% if actions_selection_counter %}
|
{% if actions_selection_counter %}
|
||||||
<span class="action_counter">
|
<span class="action-counter">
|
||||||
{% blocktrans with cl.result_count as total_count %}<span class="_acnt">0</span> of {{ total_count }} {{ module_name }} selected{% endblocktrans %}
|
{% blocktrans with cl.result_count as total_count %}<span class="_acnt">0</span> of {{ total_count }} {{ module_name }} selected{% endblocktrans %}
|
||||||
</span>
|
</span>
|
||||||
|
{% if cl.result_count != cl.result_list|length %}
|
||||||
|
<span class="all">
|
||||||
|
{% blocktrans with cl.result_count as total_count %}All {{ total_count }} {{ module_name }} selected{% endblocktrans %}
|
||||||
|
</span>
|
||||||
|
<span class="question">
|
||||||
|
<a href="javascript:;" title="{% trans "Click here to select all objects across all pages" %}">{% blocktrans with cl.result_count as total_count %}Select all {{ total_count }} {{ module_name }}{% endblocktrans %}</a>
|
||||||
|
</span>
|
||||||
|
<span class="clear"><a href="javascript:;">{% trans "Clear selection" %}</a></span>
|
||||||
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,16 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block extrahead %}
|
||||||
|
{{ media }}
|
||||||
|
<script type="text/javascript">
|
||||||
|
jQuery.noConflict();
|
||||||
|
jQuery(document).ready(function($) {
|
||||||
|
$("tr input.action-select").actions();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block bodyclass %}change-list{% endblock %}
|
{% block bodyclass %}change-list{% endblock %}
|
||||||
|
|
||||||
{% if not is_popup %}
|
{% if not is_popup %}
|
||||||
|
|
Loading…
Reference in New Issue