Ticket #19235: admin_actions_as_buttons2.diff
File admin_actions_as_buttons2.diff, 20.6 KB (added by , 12 years ago) |
---|
-
django/contrib/admin/options.py
diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index 19c212d..565e79c 100644
a b class ModelAdmin(BaseModelAdmin): 339 339 actions_on_top = True 340 340 actions_on_bottom = False 341 341 actions_selection_counter = True 342 actions_template = 'admin/actions_as_buttons.html' 342 343 343 344 def __init__(self, model, admin_site): 344 345 self.model = model … … class ModelAdmin(BaseModelAdmin): 925 926 # There can be multiple action forms on the page (at the top 926 927 # and bottom of the change list, for example). Get the action 927 928 # whose button was pushed. 929 # The request will not contain 'index' field if button layout is used. 928 930 try: 929 931 action_index = int(request.POST.get('index', 0)) 930 932 except ValueError: … … class ModelAdmin(BaseModelAdmin): 1202 1204 return HttpResponseRedirect(request.path + '?' + ERROR_FLAG + '=1') 1203 1205 1204 1206 # If the request was POSTed, this might be a bulk action or a bulk 1205 # edit. Try to look up an action or confirmation first, but if this 1206 # isn't an action the POST will fall through to the bulk edit check, 1207 # below. 1208 action_failed = False 1209 selected = request.POST.getlist(helpers.ACTION_CHECKBOX_NAME) 1210 1211 # Actions with no confirmation 1212 if (actions and request.method == 'POST' and 1213 'index' in request.POST and '_save' not in request.POST): 1214 if selected: 1215 response = self.response_action(request, queryset=cl.get_query_set(request)) 1216 if response: 1217 return response 1218 else: 1219 action_failed = True 1220 else: 1221 msg = _("Items must be selected in order to perform " 1222 "actions on them. No items have been changed.") 1223 self.message_user(request, msg) 1224 action_failed = True 1225 1226 # Actions with confirmation 1227 if (actions and request.method == 'POST' and 1228 helpers.ACTION_CHECKBOX_NAME in request.POST and 1229 'index' not in request.POST and '_save' not in request.POST): 1230 if selected: 1231 response = self.response_action(request, queryset=cl.get_query_set(request)) 1232 if response: 1233 return response 1234 else: 1235 action_failed = True 1207 # edit. It is a bulk edit if '_save' is in request.POST and it is a 1208 # bulk action if '_save' is not in request.POST. 1209 1210 if actions and request.method == 'POST' and '_save' not in request.POST: 1211 response = self.response_action(request, queryset=cl.get_query_set(request)) 1212 if response: 1213 return response 1236 1214 1237 1215 # If we're allowing changelist editing, we need to construct a formset 1238 1216 # for the changelist given all the fields to be edited. Then we'll … … class ModelAdmin(BaseModelAdmin): 1240 1218 formset = cl.formset = None 1241 1219 1242 1220 # Handle POSTed bulk-edit data. 1243 if (request.method == "POST" and cl.list_editable and 1244 '_save' in request.POST and not action_failed): 1221 if request.method == 'POST' and cl.list_editable and '_save' in request.POST: 1245 1222 FormSet = self.get_changelist_formset(request) 1246 1223 formset = cl.formset = FormSet(request.POST, request.FILES, queryset=cl.result_list) 1247 1224 if formset.is_valid(): -
django/contrib/admin/static/admin/js/actions.js
diff --git a/django/contrib/admin/static/admin/js/actions.js b/django/contrib/admin/static/admin/js/actions.js index 8494970..c5ad2d7 100644
a b 104 104 $('form#changelist-form table#result_list tr').find('td:gt(0) :input').change(function() { 105 105 list_editable_changed = true; 106 106 }); 107 $('form#changelist-form button[name="index"]').click(function(event) { 107 /* button[name="index"] is for select layout and button[name="action"] is for button layout */ 108 $('form#changelist-form button[name="index"], form#changelist-form button[name="action"]').click(function(event) { 108 109 if (list_editable_changed) { 109 110 return confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost.")); 110 111 } … … 117 118 } 118 119 }); 119 120 if (action_changed) { 121 /* action_changed will always be false in button layout. Confirmations will only be shown in select layout. */ 120 122 if (list_editable_changed) { 121 123 return confirm(gettext("You have selected an action, but you haven't saved your changes to individual fields yet. Please click OK to save. You'll need to re-run the action.")); 122 124 } else { -
django/contrib/admin/static/admin/js/actions.min.js
diff --git a/django/contrib/admin/static/admin/js/actions.min.js b/django/contrib/admin/static/admin/js/actions.min.js index 6b1947c..4caaa65 100644
a b 1 (function(a){a.fn.actions=function( n){var b=a.extend({},a.fn.actions.defaults,n),e=a(this),g=false,k=function(c){c?i():j();a(e).attr("checked",c).parent().parent().toggleClass(b.selectedClass,c)},f=function(){var c=a(e).filter(":checked").length;a(b.counterContainer).html(interpolate(ngettext("%(sel)s of %(cnt)s selected","%(sel)s of %(cnt)s selected",c),{sel:c,cnt:_actions_icnt},true));a(b.allToggle).attr("checked",function(){if(c==e.length){value=true;i()}else{value=false;l()}return value})},i=2 function(){a(b.acrossClears).hide();a(b.acrossQuestions).show();a(b.allContainer).hide()},m=function(){a(b.acrossClears).show();a(b.acrossQuestions).hide();a(b.actionContainer).toggleClass(b.selectedClass);a(b.allContainer).show();a(b.counterContainer).hide()},j=function(){a(b.acrossClears).hide();a(b.acrossQuestions).hide();a(b.allContainer).hide();a(b.counterContainer).show()},l=function(){j();a(b.acrossInput).val(0);a(b.actionContainer).removeClass(b.selectedClass)};a(b.counterContainer).show();3 a(this).filter(":checked").each(function(){a(this).parent().parent().toggleClass(b.selectedClass);f();a(b.acrossInput).val()==1&&m()});a(b.allToggle).show().click(function(){k(a(this).attr("checked"));f()});a("div.actions span.question a").click(function(c){c.preventDefault();a(b.acrossInput).val(1);m()});a("div.actions span.clear a").click(function(c){c.preventDefault();a(b.allToggle).attr("checked",false);l();k(0);f()});lastChecked=null;a(e).click(function(c){if(!c)c=window.event;var d=c.target? 4 c.target:c.srcElement;if(lastChecked&&a.data(lastChecked)!=a.data(d)&&c.shiftKey===true){var h=false;a(lastChecked).attr("checked",d.checked).parent().parent().toggleClass(b.selectedClass,d.checked);a(e).each(function(){if(a.data(this)==a.data(lastChecked)||a.data(this)==a.data(d))h=h?false:true;h&&a(this).attr("checked",d.checked).parent().parent().toggleClass(b.selectedClass,d.checked)})}a(d).parent().parent().toggleClass(b.selectedClass,d.checked);lastChecked=d;f()});a("form#changelist-form table#result_list tr").find("td:gt(0) :input").change(function(){g= 5 true});a('form#changelist-form button[name="index"]').click(function(){if(g)return confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost."))});a('form#changelist-form input[name="_save"]').click(function(){var c=false;a("div.actions select option:selected").each(function(){if(a(this).val())c=true});if(c)return g?confirm(gettext("You have selected an action, but you haven't saved your changes to individual fields yet. Please click OK to save. You'll need to re-run the action.")): 6 confirm(gettext("You have selected an action, and you haven't made any changes on individual fields. You're probably looking for the Go button rather than the Save button."))})};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"}})(django.jQuery);1 (function(a){a.fn.actions=function(m){var b=a.extend({},a.fn.actions.defaults,m),f=a(this),e=!1,j=function(c){c?h():i();a(f).attr("checked",c).parent().parent().toggleClass(b.selectedClass,c)},g=function(){var c=a(f).filter(":checked").length;a(b.counterContainer).html(interpolate(ngettext("%(sel)s of %(cnt)s selected","%(sel)s of %(cnt)s selected",c),{sel:c,cnt:_actions_icnt},!0));a(b.allToggle).attr("checked",function(){c==f.length?(value=!0,h()):(value=!1,k());return value})},h=function(){a(b.acrossClears).hide(); 2 a(b.acrossQuestions).show();a(b.allContainer).hide()},l=function(){a(b.acrossClears).show();a(b.acrossQuestions).hide();a(b.actionContainer).toggleClass(b.selectedClass);a(b.allContainer).show();a(b.counterContainer).hide()},i=function(){a(b.acrossClears).hide();a(b.acrossQuestions).hide();a(b.allContainer).hide();a(b.counterContainer).show()},k=function(){i();a(b.acrossInput).val(0);a(b.actionContainer).removeClass(b.selectedClass)};a(b.counterContainer).show();a(this).filter(":checked").each(function(){a(this).parent().parent().toggleClass(b.selectedClass); 3 g();1==a(b.acrossInput).val()&&l()});a(b.allToggle).show().click(function(){j(a(this).attr("checked"));g()});a("div.actions span.question a").click(function(c){c.preventDefault();a(b.acrossInput).val(1);l()});a("div.actions span.clear a").click(function(c){c.preventDefault();a(b.allToggle).attr("checked",!1);k();j(0);g()});lastChecked=null;a(f).click(function(c){c||(c=window.event);var d=c.target?c.target:c.srcElement;if(lastChecked&&a.data(lastChecked)!=a.data(d)&&!0===c.shiftKey){var e=!1;a(lastChecked).attr("checked", 4 d.checked).parent().parent().toggleClass(b.selectedClass,d.checked);a(f).each(function(){if(a.data(this)==a.data(lastChecked)||a.data(this)==a.data(d))e=e?!1:!0;e&&a(this).attr("checked",d.checked).parent().parent().toggleClass(b.selectedClass,d.checked)})}a(d).parent().parent().toggleClass(b.selectedClass,d.checked);lastChecked=d;g()});a("form#changelist-form table#result_list tr").find("td:gt(0) :input").change(function(){e=!0});a('form#changelist-form button[name="index"], form#changelist-form button[name="action"]').click(function(){if(e)return confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost."))}); 5 a('form#changelist-form input[name="_save"]').click(function(){var b=!1;a("div.actions select option:selected").each(function(){a(this).val()&&(b=!0)});if(b)return e?confirm(gettext("You have selected an action, but you haven't saved your changes to individual fields yet. Please click OK to save. You'll need to re-run the action.")):confirm(gettext("You have selected an action, and you haven't made any changes on individual fields. You're probably looking for the Go button rather than the Save button."))})}; 6 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"}})(django.jQuery); -
deleted file django/contrib/admin/templates/admin/actions.html
diff --git a/django/contrib/admin/templates/admin/actions.html b/django/contrib/admin/templates/admin/actions.html deleted file mode 100644 index aaaa245..0000000
+ - 1 {% load i18n %}2 <div class="actions">3 {% for field in action_form %}{% if field.label %}<label>{{ field.label }} {% endif %}{{ field }}{% if field.label %}</label>{% endif %}{% endfor %}4 <button type="submit" class="button" title="{% trans "Run the selected action" %}" name="index" value="{{ action_index|default:0 }}">{% trans "Go" %}</button>5 {% if actions_selection_counter %}6 <script type="text/javascript">var _actions_icnt="{{ cl.result_list|length|default:"0" }}";</script>7 <span class="action-counter">{{ selection_note }}</span>8 {% if cl.result_count != cl.result_list|length %}9 <span class="all">{{ selection_note_all }}</span>10 <span class="question">11 <a href="javascript:;" title="{% trans "Click here to select the objects across all pages" %}">{% blocktrans with cl.result_count as total_count %}Select all {{ total_count }} {{ module_name }}{% endblocktrans %}</a>12 </span>13 <span class="clear"><a href="javascript:;">{% trans "Clear selection" %}</a></span>14 {% endif %}15 {% endif %}16 </div> -
new file django/contrib/admin/templates/admin/actions_as_buttons.html
diff --git a/django/contrib/admin/templates/admin/actions_as_buttons.html b/django/contrib/admin/templates/admin/actions_as_buttons.html new file mode 100644 index 0000000..174510d
- + 1 {% load i18n %} 2 <div class="actions"> 3 {{ action_form.select_across }} 4 {% for choice in action_form.fields.action.choices %} 5 {% if choice.0 %} 6 <button type="submit" class="button" name="action" value="{{ choice.0 }}">{{ choice.1 }}</button> 7 {% endif %} 8 {% endfor %} 9 {% if actions_selection_counter %} 10 <script type="text/javascript">var _actions_icnt="{{ cl.result_list|length|default:"0" }}";</script> 11 <span class="nowrap"> 12 <span class="action-counter">{{ selection_note }}</span> 13 {% if cl.result_count != cl.result_list|length %} 14 <span class="all">{{ selection_note_all }}</span> 15 <span class="question"> 16 <a href="javascript:;" title="{% trans "Click here to select the objects across all pages" %}">{% blocktrans with cl.result_count as total_count %}Select all {{ total_count }} {{ module_name }}{% endblocktrans %}</a> 17 </span> 18 <span class="clear"><a href="javascript:;">{% trans "Clear selection" %}</a></span> 19 </span> 20 {% endif %} 21 {% endif %} 22 </div> -
new file django/contrib/admin/templates/admin/actions_as_select.html
diff --git a/django/contrib/admin/templates/admin/actions_as_select.html b/django/contrib/admin/templates/admin/actions_as_select.html new file mode 100644 index 0000000..aaaa245
- + 1 {% load i18n %} 2 <div class="actions"> 3 {% for field in action_form %}{% if field.label %}<label>{{ field.label }} {% endif %}{{ field }}{% if field.label %}</label>{% endif %}{% endfor %} 4 <button type="submit" class="button" title="{% trans "Run the selected action" %}" name="index" value="{{ action_index|default:0 }}">{% trans "Go" %}</button> 5 {% if actions_selection_counter %} 6 <script type="text/javascript">var _actions_icnt="{{ cl.result_list|length|default:"0" }}";</script> 7 <span class="action-counter">{{ selection_note }}</span> 8 {% if cl.result_count != cl.result_list|length %} 9 <span class="all">{{ selection_note_all }}</span> 10 <span class="question"> 11 <a href="javascript:;" title="{% trans "Click here to select the objects across all pages" %}">{% blocktrans with cl.result_count as total_count %}Select all {{ total_count }} {{ module_name }}{% endblocktrans %}</a> 12 </span> 13 <span class="clear"><a href="javascript:;">{% trans "Clear selection" %}</a></span> 14 {% endif %} 15 {% endif %} 16 </div> -
django/contrib/admin/templates/admin/change_list.html
diff --git a/django/contrib/admin/templates/admin/change_list.html b/django/contrib/admin/templates/admin/change_list.html index c72b663..b310381 100644
a b 87 87 {% endif %} 88 88 89 89 {% block result_list %} 90 {% if action_form and actions_on_top and cl.full_result_count %}{% admin_actions %}{% endif %}90 {% if action_form and actions_on_top and cl.full_result_count %}{% admin_actions cl.model_admin %}{% endif %} 91 91 {% result_list cl %} 92 {% if action_form and actions_on_bottom and cl.full_result_count %}{% admin_actions %}{% endif %}92 {% if action_form and actions_on_bottom and cl.full_result_count %}{% admin_actions cl.model_admin %}{% endif %} 93 93 {% endblock %} 94 94 {% block pagination %}{% pagination cl %}{% endblock %} 95 95 </form> -
django/contrib/admin/templatetags/admin_list.py
diff --git a/django/contrib/admin/templatetags/admin_list.py b/django/contrib/admin/templatetags/admin_list.py index ce435de..2148fcf 100644
a b def admin_list_filter(cl, spec): 378 378 'spec': spec, 379 379 })) 380 380 381 @register. inclusion_tag('admin/actions.html',takes_context=True)382 def admin_actions(context ):381 @register.simple_tag(takes_context=True) 382 def admin_actions(context, model_admin): 383 383 """ 384 384 Track the number of times the action field has been rendered on the page, 385 385 so we know which value to use. 386 386 """ 387 387 context['action_index'] = context.get('action_index', -1) + 1 388 return context388 return get_template(model_admin.actions_template).render(context) -
docs/ref/contrib/admin/index.txt
diff --git a/docs/ref/contrib/admin/index.txt b/docs/ref/contrib/admin/index.txt index 29ee66b..c113971 100644
a b The `Overriding Admin Templates`_ section describes how to override or extend 940 940 the default admin templates. Use the following options to override the default 941 941 templates used by the :class:`ModelAdmin` views: 942 942 943 .. attribute:: ModelAdmin.actions_template 944 945 .. versionadded:: 1.4 946 947 Path to a template used for rendering action bar in change list view. 948 Django provides two templates: ``admin/actions_as_buttons.html`` (default) 949 and ``admin/actions_as_select.html``. 950 943 951 .. attribute:: ModelAdmin.add_form_template 944 952 945 953 Path to a custom template, used by :meth:`add_view`. -
tests/regressiontests/admin_views/admin.py
diff --git a/tests/regressiontests/admin_views/admin.py b/tests/regressiontests/admin_views/admin.py index a5476e9..c77d0d9 100644
a b class PersonAdmin(admin.ModelAdmin): 161 161 list_filter = ('gender',) 162 162 search_fields = ('^name',) 163 163 save_as = True 164 actions_template = 'admin/actions_as_select.html' 164 165 165 166 def get_changelist_formset(self, request, **kwargs): 166 167 return super(PersonAdmin, self).get_changelist_formset(request, … … redirect_to.short_description = 'Redirect to (Awesome action)' 229 230 230 231 class ExternalSubscriberAdmin(admin.ModelAdmin): 231 232 actions = [redirect_to, external_mail] 233 actions_template = 'admin/actions_as_select.html' 232 234 233 235 234 236 class Podcast(Media): -
tests/regressiontests/admin_views/tests.py
diff --git a/tests/regressiontests/admin_views/tests.py b/tests/regressiontests/admin_views/tests.py index 72dc6a3..7f6143e 100644
a b class AdminViewListEditable(TestCase): 1718 1718 # 2 inputs per object(the field and the hidden id field) = 6 1719 1719 # 3 management hidden fields = 3 1720 1720 # 4 action inputs (3 regular checkboxes, 1 checkbox to select all) 1721 # main form submit button = 1 1721 # main form submit button = 1 (assuming that select layout is used for actions) 1722 1722 # search field and search submit button = 2 1723 1723 # CSRF field = 1 1724 1724 # field to track 'select all' across paginated views = 1 … … class AdminActionsTest(TestCase): 2324 2324 2325 2325 def test_actions_ordering(self): 2326 2326 """ 2327 Ensure that actions are ordered as expected .2327 Ensure that actions are ordered as expected in select layout. 2328 2328 Refs #15964. 2329 2329 """ 2330 2330 response = self.client.get('/test_admin/admin/admin_views/externalsubscriber/') … … class AdminActionsTest(TestCase): 2335 2335 <option value="external_mail">External mail (Another awesome action)</option> 2336 2336 </select>''', html=True) 2337 2337 2338 def test_actions_as_buttons(self): 2339 """ 2340 Test that actions are displayed as buttons by default. 2341 Refs #19235. 2342 """ 2343 response = self.client.get('/test_admin/admin/admin_views/subscriber/') 2344 # Must check separately because HTML checking does not work with more than one top-level element. 2345 self.assertContains(response, ''' 2346 <button type="submit" class="button" name="action" value="delete_selected">Delete selected subscribers</button>''', html=True) 2347 self.assertContains(response, ''' 2348 <button type="submit" class="button" name="action" value="mail_admin">Mail admin</button>''', html=True) 2349 2338 2350 def test_model_without_action(self): 2339 2351 "Tests a ModelAdmin without any action" 2340 2352 response = self.client.get('/test_admin/admin/admin_views/oldsubscriber/')