Ticket #13614: 13614.filterselect-bug.3.diff
File 13614.filterselect-bug.3.diff, 16.6 KB (added by , 13 years ago) |
---|
-
django/contrib/admin/static/admin/js/SelectFilter2.js
diff --git a/django/contrib/admin/static/admin/js/SelectFilter2.js b/django/contrib/admin/static/admin/js/SelectFilter2.js index 0accd08..cf72679 100644
a b 1 1 /* 2 2 SelectFilter2 - Turns a multiple-select box into a filter interface. 3 3 4 Requires core.js , SelectBox.jsand addevent.js.4 Requires core.js and addevent.js. 5 5 */ 6 6 (function($) { 7 7 function findForm(node) { … … window.SelectFilter = { 19 19 return; 20 20 } 21 21 var from_box = document.getElementById(field_id); 22 from_box.id += '_from'; // change its ID 22 23 // -------------------------------------------------------------------- 24 // Below is a hack to get around a bug in jQuery, which has been fixed 25 // so we could remove that hack once we upgrade jQuery. 26 // See http://bugs.jquery.com/ticket/8129 27 // The bug is that selected options aren't preserved after cloning the 28 // select widget when simply doing `actual_box = $(from_box).clone()`. 29 var actual_box = $('<select></select>').insertBefore(from_box).hide(); 30 var i; 31 var attributes = from_box.attributes; 32 for(i=0; i<attributes.length; i++) { 33 actual_box.attr(attributes[i].name, attributes[i].value); 34 } 35 actual_box[0].innerHTML = from_box.innerHTML; 36 $(from_box).find('option').each(function(index, option){ 37 $(option).data('index', index); 38 }); 39 // -------------------------------------------------------------------- 40 41 from_box.id += '_from'; 42 from_box.name += '_from'; 23 43 from_box.className = 'filtered'; 24 44 25 45 var ps = from_box.parentNode.getElementsByTagName('p'); 26 for ( vari=0; i<ps.length; i++) {46 for (i=0; i<ps.length; i++) { 27 47 if (ps[i].className.indexOf("info") != -1) { 28 48 // Remove <p class="info">, because it just gets in the way. 29 49 from_box.parentNode.removeChild(ps[i]); … … window.SelectFilter = { 58 78 filter_input.id = field_id + '_input'; 59 79 60 80 selector_available.appendChild(from_box); 61 var choose_all = quickElement('a', selector_available, gettext('Choose all'), 'title', interpolate(gettext('Click to choose all %s at once.'), [field_name]), 'href', 'javascript: (function(){ Select Box.move_all("' + field_id + '_from", "' + field_id + '_to"); SelectFilter.refresh_icons("' + field_id + '");})()', 'id', field_id + '_add_all_link');81 var choose_all = quickElement('a', selector_available, gettext('Choose all'), 'title', interpolate(gettext('Click to choose all %s at once.'), [field_name]), 'href', 'javascript: (function(){ SelectFilter.move("'+ field_id + '", "' + field_id + '_from", "' + field_id + '_to", true);})()', 'id', field_id + '_add_all_link'); 62 82 choose_all.className = 'selector-chooseall'; 63 83 64 84 // <ul class="selector-chooser"> 65 85 var selector_chooser = quickElement('ul', selector_div, ''); 66 86 selector_chooser.className = 'selector-chooser'; 67 var add_link = quickElement('a', quickElement('li', selector_chooser, ''), gettext('Choose'), 'title', gettext('Choose'), 'href', 'javascript: (function(){ Select Box.move("' + field_id + '_from","' + field_id + '_to"); SelectFilter.refresh_icons("' + field_id + '");})()', 'id', field_id + '_add_link');87 var add_link = quickElement('a', quickElement('li', selector_chooser, ''), gettext('Choose'), 'title', gettext('Choose'), 'href', 'javascript: (function(){ SelectFilter.move("'+ field_id + '", "'+ field_id + '_from", "' + field_id + '_to");})()', 'id', field_id + '_add_link'); 68 88 add_link.className = 'selector-add'; 69 var remove_link = quickElement('a', quickElement('li', selector_chooser, ''), gettext('Remove'), 'title', gettext('Remove'), 'href', 'javascript: (function(){ Select Box.move("' + field_id + '_to","' + field_id + '_from"); SelectFilter.refresh_icons("' + field_id + '");})()', 'id', field_id + '_remove_link');89 var remove_link = quickElement('a', quickElement('li', selector_chooser, ''), gettext('Remove'), 'title', gettext('Remove'), 'href', 'javascript: (function(){ SelectFilter.move("'+ field_id + '", "' + field_id + '_to", "' + field_id + '_from");})()', 'id', field_id + '_remove_link'); 70 90 remove_link.className = 'selector-remove'; 71 91 72 92 // <div class="selector-chosen"> … … window.SelectFilter = { 75 95 var title_chosen = quickElement('h2', selector_chosen, interpolate(gettext('Chosen %s') + ' ', [field_name])); 76 96 quickElement('img', title_chosen, '', 'src', admin_media_prefix + 'img/icon-unknown.gif', 'width', '10', 'height', '10', 'class', 'help help-tooltip', 'title', interpolate(gettext('This is the list of chosen %s. You may remove some by selecting them in the box below and then clicking the "Remove" arrow between the two boxes.'), [field_name])); 77 97 78 var to_box = quickElement('select', selector_chosen, '', 'id', field_id + '_to', 'multiple', 'multiple', 'size', from_box.size, 'name', from_box.getAttribute('name'));98 var to_box = quickElement('select', selector_chosen, '', 'id', field_id + '_to', 'multiple', 'multiple', 'size', from_box.size, 'name', actual_box.attr('name') + '_to'); 79 99 to_box.className = 'filtered'; 80 var clear_all = quickElement('a', selector_chosen, gettext('Remove all'), 'title', interpolate(gettext('Click to remove all chosen %s at once.'), [field_name]), 'href', 'javascript: (function() { Select Box.move_all("' + field_id + '_to", "' + field_id + '_from"); SelectFilter.refresh_icons("' + field_id + '");})()', 'id', field_id + '_remove_all_link');100 var clear_all = quickElement('a', selector_chosen, gettext('Remove all'), 'title', interpolate(gettext('Click to remove all chosen %s at once.'), [field_name]), 'href', 'javascript: (function() { SelectFilter.move("'+ field_id + '", "' + field_id + '_to", "' + field_id + '_from", true);})()', 'id', field_id + '_remove_all_link'); 81 101 clear_all.className = 'selector-clearall'; 82 102 83 from_box.setAttribute('name', from_box.getAttribute('name') + '_old');84 85 103 // Set up the JavaScript event handlers for the select box filter interface 86 104 addEvent(filter_input, 'keyup', function(e) { SelectFilter.filter_key_up(e, field_id); }); 87 105 addEvent(filter_input, 'keydown', function(e) { SelectFilter.filter_key_down(e, field_id); }); 88 addEvent(from_box, 'change', function(e) { SelectFilter.refresh_icons(field_id) }); 89 addEvent(to_box, 'change', function(e) { SelectFilter.refresh_icons(field_id) }); 90 addEvent(from_box, 'dblclick', function() { SelectBox.move(field_id + '_from', field_id + '_to'); SelectFilter.refresh_icons(field_id); }); 91 addEvent(to_box, 'dblclick', function() { SelectBox.move(field_id + '_to', field_id + '_from'); SelectFilter.refresh_icons(field_id); }); 92 addEvent(findForm(from_box), 'submit', function() { SelectBox.select_all(field_id + '_to'); }); 93 SelectBox.init(field_id + '_from'); 94 SelectBox.init(field_id + '_to'); 95 // Move selected from_box options to to_box 96 SelectBox.move(field_id + '_from', field_id + '_to'); 106 addEvent(from_box, 'change', function(e) { SelectFilter.refresh_state(field_id) }); 107 addEvent(to_box, 'change', function(e) { SelectFilter.refresh_state(field_id) }); 108 addEvent(from_box, 'dblclick', function() { 109 SelectFilter.move(field_id, field_id + '_from', field_id + '_to'); 110 }); 111 addEvent(to_box, 'dblclick', function() { 112 SelectFilter.move(field_id, field_id + '_to', field_id + '_from'); 113 }); 114 115 // Move selected from from_box options to to_box 116 SelectFilter.move(field_id, field_id + '_from', field_id + '_to'); 97 117 98 118 if (!is_stacked) { 99 119 // In horizontal mode, give the same height to the two boxes. … … window.SelectFilter = { 109 129 } 110 130 111 131 // Initial icon refresh 112 SelectFilter.refresh_icons(field_id); 132 SelectFilter.refresh_state(field_id); 133 }, 134 move: function(field_id, from_id, to_id, all) { 135 var options; 136 var from_box = $('#' + from_id); 137 var to_box = $('#' + to_id); 138 options = from_box.find("option"); 139 if (!all) { 140 options = options.filter(':selected'); 141 } 142 to_box.append(options); 143 to_box.find('option').removeAttr('selected'); 144 145 SelectFilter.refresh_state(field_id); 113 146 }, 114 147 refresh_icons: function(field_id) { 115 148 var from = $('#' + field_id + '_from'); … … window.SelectFilter = { 123 156 $('#' + field_id + '_add_all_link').toggleClass('active', from.find('option').length > 0); 124 157 $('#' + field_id + '_remove_all_link').toggleClass('active', to.find('option').length > 0); 125 158 }, 159 refresh_state: function(field_id) { 160 SelectFilter.refresh_icons(field_id); 161 162 // Re-generate the content of the actual, hidden, box. 163 var actual_box = $('#' + field_id); 164 var actual_options = actual_box.find('option'); 165 var to_box = $('#' + field_id + '_to'); 166 167 // De-select all options. 168 actual_box.find('option').removeAttr('selected', 'selected'); 169 170 // Select all the same options as those from the 'to' box. 171 to_box.find('option').each(function(){ 172 var option = actual_options[$(this).data('index')]; 173 $(option).attr('selected', 'selected'); 174 }); 175 }, 126 176 filter_key_up: function(event, field_id) { 127 var from = document.getElementById(field_id + '_from');177 var from_box = document.getElementById(field_id + '_from'); 128 178 // don't submit form if user pressed Enter 129 179 if ((event.which && event.which == 13) || (event.keyCode && event.keyCode == 13)) { 130 from .selectedIndex = 0;131 Select Box.move(field_id + '_from', field_id + '_to');132 from .selectedIndex = 0;180 from_box.selectedIndex = 0; 181 SelectFilter.move(field_id, field_id + '_from', field_id + '_to'); 182 from_box.selectedIndex = 0; 133 183 return false; 134 184 } 135 var temp = from.selectedIndex; 136 SelectBox.filter(field_id + '_from', document.getElementById(field_id + '_input').value); 137 from.selectedIndex = temp; 185 var temp = from_box.selectedIndex; 186 // FIXME! 187 // SelectBox.filter(field_id + '_from', document.getElementById(field_id + '_input').value); 188 from_box.selectedIndex = temp; 138 189 return true; 139 190 }, 140 191 filter_key_down: function(event, field_id) { 141 var from = document.getElementById(field_id + '_from');192 var from_box = document.getElementById(field_id + '_from'); 142 193 // right arrow -- move across 143 194 if ((event.which && event.which == 39) || (event.keyCode && event.keyCode == 39)) { 144 var old_index = from .selectedIndex;145 Select Box.move(field_id + '_from', field_id + '_to');146 from .selectedIndex = (old_index == from.length) ? from.length - 1 : old_index;195 var old_index = from_box.selectedIndex; 196 SelectFilter.move(field_id, field_id + '_from', field_id + '_to'); 197 from_box.selectedIndex = (old_index == from_box.length) ? from_box.length - 1 : old_index; 147 198 return false; 148 199 } 149 200 // down arrow -- wrap around 150 201 if ((event.which && event.which == 40) || (event.keyCode && event.keyCode == 40)) { 151 from .selectedIndex = (from.length == from.selectedIndex + 1) ? 0 : from.selectedIndex + 1;202 from_box.selectedIndex = (from_box.length == from_box.selectedIndex + 1) ? 0 : from_box.selectedIndex + 1; 152 203 } 153 204 // up arrow -- wrap around 154 205 if ((event.which && event.which == 38) || (event.keyCode && event.keyCode == 38)) { 155 from .selectedIndex = (from.selectedIndex == 0) ? from.length - 1 : from.selectedIndex - 1;206 from_box.selectedIndex = (from_box.selectedIndex == 0) ? from_box.length - 1 : from_box.selectedIndex - 1; 156 207 } 157 208 return true; 158 209 } -
django/contrib/auth/admin.py
diff --git a/django/contrib/auth/admin.py b/django/contrib/auth/admin.py index f14b3d2..9d68ed8 100644
a b csrf_protect_m = method_decorator(csrf_protect) 21 21 class GroupAdmin(admin.ModelAdmin): 22 22 search_fields = ('name',) 23 23 ordering = ('name',) 24 filter_ horizontal = ('permissions',)24 filter_vertical = ('permissions',) 25 25 26 26 def formfield_for_manytomany(self, db_field, request=None, **kwargs): 27 27 if db_field.name == 'permissions': -
tests/regressiontests/admin_widgets/tests.py
diff --git a/tests/regressiontests/admin_widgets/tests.py b/tests/regressiontests/admin_widgets/tests.py index b0b3209..f0d948e 100644
a b class HorizontalVerticalFilterSeleniumFirefoxTests(AdminSeleniumWebDriverTestCas 478 478 self.cliff = models.Student.objects.create(name='Cliff') 479 479 self.arthur = models.Student.objects.create(name='Arthur') 480 480 self.school = models.School.objects.create(name='School of Awesome') 481 self.school.students = [self.lisa, self.peter] 482 self.school.alumni = [self.lisa, self.peter] 483 self.school.save() 481 484 super(HorizontalVerticalFilterSeleniumFirefoxTests, self).setUp() 482 485 483 486 def execute_basic_operations(self, field_name, mode): … … class HorizontalVerticalFilterSeleniumFirefoxTests(AdminSeleniumWebDriverTestCas 505 508 for option in self.selenium.find_elements_by_css_selector(from_box + ' option'): 506 509 option.click() 507 510 self.selenium.find_element_by_id(choose_link).click() 511 508 512 self.assertSelectOptions(from_box, []) 509 513 self.assertSelectOptions(to_box, 510 514 [str(self.lisa.id), str(self.peter.id), … … class HorizontalVerticalFilterSeleniumFirefoxTests(AdminSeleniumWebDriverTestCas 567 571 str(self.arthur.id), str(self.cliff.id)]) 568 572 569 573 def test_basic(self): 570 self. school.students = [self.lisa, self.peter]571 self.s chool.alumni = [self.lisa, self.peter]572 self.school.save()574 self.admin_login(username='super', password='secret', login_url='/') 575 self.selenium.get( 576 '%s%s' % (self.live_server_url, '/admin_widgets/school/%s/' % self.school.id)) 573 577 578 self.execute_basic_operations('students', 'vertical') 579 self.execute_basic_operations('alumni', 'horizontal') 580 581 # Save and check that everything is properly stored in the database --- 582 self.selenium.find_element_by_xpath('//input[@value="Save"]').click() 583 self.school = models.School.objects.get(id=self.school.id) # Reload from database 584 self.assertEqual(list(self.school.students.all()), 585 [self.arthur, self.cliff, self.jason, self.john]) 586 self.assertEqual(list(self.school.alumni.all()), 587 [self.arthur, self.cliff, self.jason, self.john]) 588 589 def test_webkit_bug_13614(self): 574 590 self.admin_login(username='super', password='secret', login_url='/') 575 591 self.selenium.get( 576 592 '%s%s' % (self.live_server_url, '/admin_widgets/school/%s/' % self.school.id)) 577 593 578 594 self.execute_basic_operations('students', 'vertical') 579 595 self.execute_basic_operations('alumni', 'horizontal') 596 # Navigate away and go back to the change form page ------------------- 597 self.selenium.find_element_by_link_text('Home').click() 598 self.selenium.back() 599 600 # Check that everything is still in place ----------------------------- 601 from_box = '#id_students_from' 602 to_box = '#id_students_to' 603 self.assertSelectOptions(from_box, 604 [str(self.bob.id), str(self.jenny.id), 605 str(self.lisa.id), str(self.peter.id)]) 606 self.assertSelectOptions(to_box, 607 [str(self.arthur.id), str(self.cliff.id), 608 str(self.jason.id), str(self.john.id)]) 609 610 from_box = '#id_alumni_from' 611 to_box = '#id_alumni_to' 612 self.assertSelectOptions(from_box, 613 [str(self.bob.id), str(self.jenny.id), 614 str(self.lisa.id), str(self.peter.id)]) 615 self.assertSelectOptions(to_box, 616 [str(self.arthur.id), str(self.cliff.id), 617 str(self.jason.id), str(self.john.id)]) 580 618 581 619 # Save and check that everything is properly stored in the database --- 582 620 self.selenium.find_element_by_xpath('//input[@value="Save"]').click()