Skip to content

Commit

Permalink
Fixed #13068, #9264, #9983, #9784 - regression with pre-populated fie…
Browse files Browse the repository at this point in the history
…lds and javascript inlines, and related bugs.

Thanks to hejsan for the report, and to Sean Brant and Carl Meyer for the patch.

#13068 is a regression caused by the new javascript inline forms in the
admin.  The others were existing javascript bugs with prepopulated fields. 
Since the solution depends on jQuery and would likely be very hard to
backport without it, it will not be backported to 1.1.X.



git-svn-id: http://code.djangoproject.com/svn/django/trunk@12937 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information
spookylukey committed Apr 7, 2010
1 parent 97b9c75 commit 736b751
Show file tree
Hide file tree
Showing 8 changed files with 123 additions and 23 deletions.
12 changes: 11 additions & 1 deletion django/contrib/admin/media/js/admin/DateTimeShortcuts.js
Expand Up @@ -120,6 +120,7 @@ var DateTimeShortcuts = {
},
handleClockQuicklink: function(num, val) {
DateTimeShortcuts.clockInputs[num].value = val;
DateTimeShortcuts.clockInputs[num].focus();
DateTimeShortcuts.dismissClock(num);
},
// Add calendar widget to a given field.
Expand Down Expand Up @@ -247,12 +248,21 @@ var DateTimeShortcuts = {
format = format.replace('\n', '\\n');
format = format.replace('\t', '\\t');
format = format.replace("'", "\\'");
return "function(y, m, d) { DateTimeShortcuts.calendarInputs["+num+"].value = new Date(y, m-1, d).strftime('"+format+"');document.getElementById(DateTimeShortcuts.calendarDivName1+"+num+").style.display='none';}";
return ["function(y, m, d) { DateTimeShortcuts.calendarInputs[",
num,
"].value = new Date(y, m-1, d).strftime('",
format,
"');DateTimeShortcuts.calendarInputs[",
num,
"].focus();document.getElementById(DateTimeShortcuts.calendarDivName1+",
num,
").style.display='none';}"].join('');
},
handleCalendarQuickLink: function(num, offset) {
var d = new Date();
d.setDate(d.getDate() + offset)
DateTimeShortcuts.calendarInputs[num].value = d.strftime(get_format('DATE_INPUT_FORMATS')[0]);
DateTimeShortcuts.calendarInputs[num].focus();
DateTimeShortcuts.dismissCalendar(num);
},
cancelEventPropagation: function(e) {
Expand Down
41 changes: 29 additions & 12 deletions django/contrib/admin/media/js/inlines.js
Expand Up @@ -55,24 +55,41 @@
var totalForms = $("#id_" + options.prefix + "-TOTAL_FORMS");
var nextIndex = parseInt(totalForms.val());
var template = $("#" + options.prefix + "-empty");
var row = template.clone(true).get(0);
$(row).removeClass(options.emptyCssClass).removeAttr("id").insertBefore($(template));
$(row).html($(row).html().replace(/__prefix__/g, nextIndex));
$(row).addClass(options.formCssClass).attr("id", options.prefix + (nextIndex + 1));
if ($(row).is("TR")) {
var row = template.clone(true);
row.removeClass(options.emptyCssClass)
.addClass(options.formCssClass)
.attr("id", options.prefix + nextIndex)
.insertBefore($(template));
row.find("*")
.filter(function() {
var el = $(this);
return el.attr("id") && el.attr("id").search(/__prefix__/) >= 0;
}).each(function() {
var el = $(this);
el.attr("id", el.attr("id").replace(/__prefix__/g, nextIndex));
})
.end()
.filter(function() {
var el = $(this);
return el.attr("name") && el.attr("name").search(/__prefix__/) >= 0;
}).each(function() {
var el = $(this);
el.attr("name", el.attr("name").replace(/__prefix__/g, nextIndex));
});
if (row.is("tr")) {
// If the forms are laid out in table rows, insert
// the remove button into the last table cell:
$(row).children(":last").append('<div><a class="' + options.deleteCssClass +'" href="javascript:void(0)">' + options.deleteText + "</a></div>");
} else if ($(row).is("UL") || $(row).is("OL")) {
row.children(":last").append('<div><a class="' + options.deleteCssClass +'" href="javascript:void(0)">' + options.deleteText + "</a></div>");
} else if (row.is("ul") || row.is("ol")) {
// If they're laid out as an ordered/unordered list,
// insert an <li> after the last list item:
$(row).append('<li><a class="' + options.deleteCssClass +'" href="javascript:void(0)">' + options.deleteText + "</a></li>");
row.append('<li><a class="' + options.deleteCssClass +'" href="javascript:void(0)">' + options.deleteText + "</a></li>");
} else {
// Otherwise, just insert the remove button as the
// last child element of the form's container:
$(row).children(":first").append('<span><a class="' + options.deleteCssClass + '" href="javascript:void(0)">' + options.deleteText + "</a></span>");
row.children(":first").append('<span><a class="' + options.deleteCssClass + '" href="javascript:void(0)">' + options.deleteText + "</a></span>");
}
$(row).find("input,select,textarea,label,a").each(function() {
row.find("input,select,textarea,label,a").each(function() {
updateElementIndex(this, options.prefix, totalForms.val());
});
// Update number of total forms
Expand All @@ -82,7 +99,7 @@
addButton.parent().hide();
}
// The delete button of each row triggers a bunch of other things
$(row).find("a." + options.deleteCssClass).click(function() {
row.find("a." + options.deleteCssClass).click(function() {
// Remove the parent form containing this button:
var row = $(this).parents("." + options.formCssClass);
row.remove();
Expand All @@ -109,7 +126,7 @@
});
// If a post-add callback was supplied, call it with the added form:
if (options.added) {
options.added($(row));
options.added(row);
}
return false;
});
Expand Down
11 changes: 6 additions & 5 deletions django/contrib/admin/media/js/inlines.min.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 34 additions & 0 deletions django/contrib/admin/media/js/prepopulate.js
@@ -0,0 +1,34 @@
(function($) {
$.fn.prepopulate = function(dependencies, maxLength) {
/*
Depends on urlify.js
Populates a selected field with the values of the dependent fields,
URLifies and shortens the string.
dependencies - selected jQuery object of dependent fields
maxLength - maximum length of the URLify'd string
*/
return this.each(function() {
var field = $(this);

field.data('_changed', false);
field.change(function() {
field.data('_changed', true);
});

var populate = function () {
// Bail if the fields value has changed
if (field.data('_changed') == true) return;

var values = [];
dependencies.each(function() {
if ($(this).val().length > 0) {
values.push($(this).val());
}
});
field.val(URLify(values.join(' '), maxLength));
};

dependencies.keyup(populate).change(populate).focus(populate);
});
};
})(jQuery.noConflict());
2 changes: 2 additions & 0 deletions django/contrib/admin/options.py
Expand Up @@ -274,6 +274,7 @@ def _media(self):
js.extend(['js/jquery.min.js', 'js/actions.min.js'])
if self.prepopulated_fields:
js.append('js/urlify.js')
js.append('js/prepopulate.js')
if self.opts.get_ordered_objects():
js.extend(['js/getElementsBySelector.js', 'js/dom-drag.js' , 'js/admin/ordering.js'])

Expand Down Expand Up @@ -1201,6 +1202,7 @@ def _media(self):
js = ['js/jquery.min.js', 'js/inlines.min.js']
if self.prepopulated_fields:
js.append('js/urlify.js')
js.append('js/prepopulate.js')
if self.filter_vertical or self.filter_horizontal:
js.extend(['js/SelectBox.js' , 'js/SelectFilter2.js'])
return forms.Media(js=['%s%s' % (settings.ADMIN_MEDIA_PREFIX, url) for url in js])
Expand Down
12 changes: 12 additions & 0 deletions django/contrib/admin/templates/admin/edit_inline/stacked.html
Expand Up @@ -48,6 +48,17 @@ <h3><b>{{ inline_admin_formset.opts.verbose_name|title }}:</b>&nbsp;<span class=
})
}
}
var initPrepopulatedFields = function(row) {
row.find('.prepopulated_field').each(function() {
var field = $(this);
var input = field.find('input, select, textarea');
var dependency_list = input.data('dependency_list') || [];
var dependencies = row.find(dependency_list.join(',')).find('input, select, textarea');
if (dependencies.length) {
input.prepopulate(dependencies, input.attr('maxlength'));
}
});
}
$(rows).formset({
prefix: "{{ inline_admin_formset.formset.prefix }}",
addText: "{% blocktrans with inline_admin_formset.opts.verbose_name|title as verbose_name %}Add another {{ verbose_name }}{% endblocktrans %}",
Expand All @@ -57,6 +68,7 @@ <h3><b>{{ inline_admin_formset.opts.verbose_name|title }}:</b>&nbsp;<span class=
emptyCssClass: "empty-form",
removed: updateInlineLabel,
added: (function(row) {
initPrepopulatedFields(row);
reinitDateTimeShortCuts();
updateSelectFilter();
updateInlineLabel(row);
Expand Down
12 changes: 12 additions & 0 deletions django/contrib/admin/templates/admin/edit_inline/tabular.html
Expand Up @@ -94,6 +94,17 @@ <h2>{{ inline_admin_formset.opts.verbose_name_plural|capfirst }}</h2>
})
}
}
var initPrepopulatedFields = function(row) {
row.find('.prepopulated_field').each(function() {
var field = $(this);
var input = field.find('input, select, textarea');
var dependency_list = input.data('dependency_list') || [];
var dependencies = row.find(dependency_list.join(',')).find('input, select, textarea');
if (dependencies.length) {
input.prepopulate(dependencies, input.attr('maxlength'));
}
});
}
$(rows).formset({
prefix: "{{ inline_admin_formset.formset.prefix }}",
addText: "{% blocktrans with inline_admin_formset.opts.verbose_name|title as verbose_name %}Add another {{ verbose_name }}{% endblocktrans %}",
Expand All @@ -103,6 +114,7 @@ <h2>{{ inline_admin_formset.opts.verbose_name_plural|capfirst }}</h2>
emptyCssClass: "empty-form",
removed: alternatingRows,
added: (function(row) {
initPrepopulatedFields(row);
reinitDateTimeShortCuts();
updateSelectFilter();
alternatingRows(row);
Expand Down
22 changes: 17 additions & 5 deletions django/contrib/admin/templates/admin/prepopulated_fields_js.html
@@ -1,11 +1,23 @@
<script type="text/javascript">
(function($) {
var field = null;

{% for field in prepopulated_fields %}
document.getElementById("{{ field.field.auto_id }}").onchange = function() { this._changed = true; };
field = {
id: '#{{ field.field.auto_id }}',
dependency_ids: [],
dependency_list: [],
maxLength: {{ field.field.field.max_length|default_if_none:"50" }}
};

{% for dependency in field.dependencies %}
document.getElementById("{{ dependency.auto_id }}").onkeyup = function() {
var e = document.getElementById("{{ field.field.auto_id }}");
if (!e._changed) { e.value = URLify({% for innerdep in field.dependencies %}document.getElementById("{{ innerdep.auto_id }}").value{% if not forloop.last %} + ' ' + {% endif %}{% endfor %}, {{ field.field.field.max_length|default_if_none:"50" }}); }
}
field['dependency_ids'].push('#{{ dependency.auto_id }}');
field['dependency_list'].push('.{{ dependency.name }}');
{% endfor %}

$('.empty-form .{{ field.field.name }}').addClass('prepopulated_field');
$(field.id).data('dependency_list', field['dependency_list'])
.prepopulate($(field['dependency_ids'].join(',')), field.maxLength);
{% endfor %}
})(jQuery.noConflict());
</script>

0 comments on commit 736b751

Please sign in to comment.