Code

Ticket #12282: admin-selectacross.diff

File admin-selectacross.diff, 10.0 KB (added by jezdez, 4 years ago)

Working example, from http://github.com/jezdez/django/tree/admin-selectacross

Line 
1diff --git a/django/contrib/admin/helpers.py b/django/contrib/admin/helpers.py
2index d047d89..29b777f 100644
3--- a/django/contrib/admin/helpers.py
4+++ b/django/contrib/admin/helpers.py
5@@ -17,6 +17,8 @@ ACTION_CHECKBOX_NAME = '_selected_action'
6 
7 class ActionForm(forms.Form):
8     action = forms.ChoiceField(label=_('Action:'))
9+    select_across = forms.BooleanField(label='', required=False, initial=0,
10+        widget=forms.HiddenInput({'class': 'select-across'}))
11 
12 checkbox = forms.CheckboxInput({'class': 'action-select'}, lambda value: False)
13 
14diff --git a/django/contrib/admin/media/css/changelists.css b/django/contrib/admin/media/css/changelists.css
15index a9d7543..99ff8bc 100644
16--- a/django/contrib/admin/media/css/changelists.css
17+++ b/django/contrib/admin/media/css/changelists.css
18@@ -228,12 +228,6 @@
19     border-right: 1px solid #ddd;
20 }
21 
22-.action_counter{
23-    font-size: 11px;
24-    margin: 0 0.5em;
25-    display: none;
26-}
27-
28 #changelist table input {
29     margin: 0;
30 }
31@@ -250,6 +244,21 @@
32     background: white url(../img/admin/nav-bg-reverse.gif) 0 -10px repeat-x;
33 }
34 
35+#changelist .actions.selected {
36+    background: #fffccf;
37+    border-top: 1px solid #fffee8;
38+    border-bottom: 1px solid #edecd6;
39+}
40+
41+#changelist .actions span.all,
42+#changelist .actions span.action-counter,
43+#changelist .actions span.clear,
44+#changelist .actions span.question {
45+    font-size: 11px;
46+    margin: 0 0.5em;
47+    display: none;
48+}
49+
50 #changelist .actions:last-child {
51     border-bottom: none;
52 }
53diff --git a/django/contrib/admin/media/js/actions.js b/django/contrib/admin/media/js/actions.js
54index 658f015..c50bdf5 100644
55--- a/django/contrib/admin/media/js/actions.js
56+++ b/django/contrib/admin/media/js/actions.js
57@@ -1,20 +1,42 @@
58 var Actions = {
59     init: function() {
60         counterSpans = document.getElementsBySelector('span._acnt');
61-        counterContainer = document.getElementsBySelector('span.action_counter');
62+        counterContainer = document.getElementsBySelector('span.action-counter');
63+        allContainer = document.getElementsBySelector('div.actions span.all');
64+        actionContainer = document.getElementsBySelector('div.actions');
65         actionCheckboxes = document.getElementsBySelector('tr input.action-select');
66+        acrossInputs = document.getElementsBySelector('div.actions input.select-across');
67+        acrossQuestions = document.getElementsBySelector('div.actions span.question');
68+        acrossClears = document.getElementsBySelector('div.actions span.clear');
69+        acrossQuestionLinks = document.getElementsBySelector('div.actions span.question a');
70+        acrossClearsLinks = document.getElementsBySelector('div.actions span.clear a');
71+
72+        Actions.setDisplay(counterContainer, 'inline');
73         selectAll = document.getElementById('action-toggle');
74-        lastChecked = null;
75-        for(var i = 0; i < counterContainer.length; i++) {
76-            counterContainer[i].style.display = 'inline';
77-        }
78         if (selectAll) {
79-            selectAll.style.display = 'inline';
80+            Actions.setDisplay([selectAll], 'inline');
81             addEvent(selectAll, 'click', function() {
82                 Actions.checker(selectAll.checked);
83                 Actions.counter();
84             });
85+            for(var i = 0; i < acrossQuestionLinks.length; i++) {
86+                addEvent(acrossQuestionLinks[i], 'click', function() {
87+                    Actions.setAcrossInputs(1);
88+                    Actions.showClear()
89+                    return false;
90+                });
91+            }
92+            for(var i = 0; i < acrossClearsLinks.length; i++) {
93+                addEvent(acrossClearsLinks[i], 'click', function() {
94+                    selectAll.checked = false;
95+                    Actions.clearAcross();
96+                    Actions.checker(0);
97+                    Actions.counter();
98+                    return false;
99+                });
100+            }
101         }
102+        lastChecked = null;
103         for(var i = 0; i < actionCheckboxes.length; i++) {
104             addEvent(actionCheckboxes[i], 'click', function(e) {
105                 if (!e) { var e = window.event; }
106@@ -57,15 +79,54 @@ var Actions = {
107             tr.className = tr.className.replace(' selected', '');
108         } 
109     },
110-    checked: function() {
111-        selectAll.checked = false;
112+    setAcrossInputs: function(checked) {
113+        for(var i = 0; i < acrossInputs.length; i++) {
114+            acrossInputs[i].value = checked;
115+        }
116     },
117     checker: function(checked) {
118+        if (checked) {
119+            Actions.showQuestion()
120+        } else {
121+            Actions.reset();
122+        }
123         for(var i = 0; i < actionCheckboxes.length; i++) {
124             actionCheckboxes[i].checked = checked;
125             Actions.toggleRow(actionCheckboxes[i].parentNode.parentNode, checked);
126         }
127     },
128+    setDisplay: function(elements, value) {
129+        for(var i = 0; i < elements.length; i++) {
130+            elements[i].style.display = value;
131+        }
132+    },
133+    showQuestion: function() {
134+        Actions.setDisplay(acrossQuestions, 'inline');
135+        Actions.setDisplay(acrossClears, 'none');
136+        Actions.setDisplay(allContainer, 'none');
137+    },
138+    showClear: function() {
139+        Actions.setDisplay(acrossQuestions, 'none');
140+        Actions.setDisplay(acrossClears, 'inline');
141+        Actions.setDisplay(counterContainer, 'none');
142+        Actions.setDisplay(allContainer, 'inline');
143+        for(var i = 0; i < actionContainer.length; i++) {
144+            Actions.toggleRow(actionContainer[i], true)
145+        }
146+    },
147+    reset: function() {
148+        Actions.setDisplay(allContainer, 'none')
149+        Actions.setDisplay(acrossQuestions, 'none');
150+        Actions.setDisplay(acrossClears, 'none');
151+        Actions.setDisplay(counterContainer, 'inline');
152+    },
153+    clearAcross: function() {
154+        Actions.reset()
155+        Actions.setAcrossInputs(0);
156+        for(var i = 0; i < actionContainer.length; i++) {
157+            Actions.toggleRow(actionContainer[i], false)
158+        }
159+    },
160     counter: function() {
161         counter = 0;
162         for(var i = 0; i < actionCheckboxes.length; i++) {
163@@ -76,7 +137,13 @@ var Actions = {
164         for(var i = 0; i < counterSpans.length; i++) {
165             counterSpans[i].innerHTML = counter;
166         }
167-        selectAll.checked = (counter == actionCheckboxes.length);
168+        if (counter == actionCheckboxes.length) {
169+            selectAll.checked = true;
170+            Actions.showQuestion()
171+        } else {
172+            selectAll.checked = false;
173+            Actions.clearAcross()
174+        }
175     }
176 };
177 
178diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
179index 6dc707e..7c00a39 100644
180--- a/django/contrib/admin/options.py
181+++ b/django/contrib/admin/options.py
182@@ -721,16 +721,20 @@ class ModelAdmin(BaseModelAdmin):
183             action = action_form.cleaned_data['action']
184             func, name, description = self.get_actions(request)[action]
185 
186-            # Get the list of selected PKs. If nothing's selected, we can't
187-            # perform an action on it, so bail.
188-            selected = request.POST.getlist(helpers.ACTION_CHECKBOX_NAME)
189-            if not selected:
190+            selected = None
191+            if not action_form.cleaned_data.get('select_across'):
192+                queryset = queryset.filter(pk__in=selected)
193+            else:
194+                # Get the list of selected PKs. If nothing's selected, we can't
195+                # perform an action on it, so bail.
196+                selected = request.POST.getlist(helpers.ACTION_CHECKBOX_NAME)
197+            if not selected or not queryset:
198                 # Reminder that something needs to be selected or nothing will happen
199                 msg = _("Items must be selected in order to perform actions on them. No items have been changed.")
200                 self.message_user(request, msg)
201                 return None
202 
203-            response = func(self, request, queryset.filter(pk__in=selected))
204+            response = func(self, request, queryset)
205 
206             # Actions may return an HttpResponse, which will be used as the
207             # response from the POST. If not, we'll be a good little HTTP
208diff --git a/django/contrib/admin/templates/admin/actions.html b/django/contrib/admin/templates/admin/actions.html
209index 6d96616..41966fb 100644
210--- a/django/contrib/admin/templates/admin/actions.html
211+++ b/django/contrib/admin/templates/admin/actions.html
212@@ -1,10 +1,28 @@
213 {% load i18n %}
214 <div class="actions">
215-    {% for field in action_form %}<label>{{ field.label }} {{ field }}</label>{% endfor %}
216+    {% for field in action_form %}{% if field.label %}<label>{{ field.label }} {% endif %}{{ field }}{% if field.label %}</label>{% endif %}{% endfor %}
217     <button type="submit" class="button" title="{% trans "Run the selected action" %}" name="index" value="{{ action_index|default:0 }}">{% trans "Go" %}</button>
218     {% if actions_selection_counter %}
219-    <span class="action_counter">
220-      {% blocktrans with cl.result_count as total_count %}<span class="_acnt">0</span> of {{ total_count }} {{ module_name }} selected{% endblocktrans %}
221-    </span>
222+        <span class="action-counter">
223+            {% blocktrans with cl.result_count as total_count %}
224+            <span class="_acnt">0</span> of {{ total_count }} {{ module_name }} selected
225+            {% endblocktrans %}
226+        </span>
227+        {% if cl.result_count != cl.result_list|length %}
228+        <span class="all">
229+            {% blocktrans with cl.result_count as total_count %}
230+            All {{ total_count }} {{ module_name }} selected
231+            {% endblocktrans %}
232+        </span>
233+        <span class="question">
234+            <a href="#" title="{% trans "Click here to select all objects across all pages" %}">
235+                {% blocktrans with cl.result_count as total_count %}
236+                    Select all {{ total_count }} {{ module_name }}
237+                {% endblocktrans %}
238+            </a>
239+        </span>
240+        <span class="clear"><a href="#">{% trans "Clear selection" %}</a></span>
241+        {% endif %}
242     {% endif %}
243 </div>
244+