=== modified file 'django/newforms/forms.py'
--- django/newforms/forms.py	2008-02-18 14:55:31 +0000
+++ django/newforms/forms.py	2008-02-09 00:13:55 +0000
@@ -8,6 +8,7 @@
 from django.utils.html import conditional_escape
 from django.utils.encoding import StrAndUnicode, smart_unicode, force_unicode
 from django.utils.safestring import mark_safe
+from django.utils.text import capfirst
 
 from fields import Field, FileField
 from widgets import TextInput, Textarea
@@ -24,9 +25,19 @@
 
 class FormOptions(InheritableOptions):
     _default_options = {
+        # main options
         'fieldsets': None,
         'fields': None,
         'exclude': None,
+        # other options
+        'error_class': ErrorList,
+        'formfield_for_formfield': lambda self, formfield: formfield,
+        'html_class_for_fields_with_errors': 'errors',
+        'html_class_for_hidden_fields_row': 'hidden',
+        'html_class_for_required_fields': 'required',
+        'label_capitalization': True,
+        'label_suffix': ':',
+        'validation_order': None,
     }
 
 class FormMetaclass(type):
@@ -73,7 +84,7 @@
             names = [name for name in new_cls._base_fields_pool if name not in new_cls._meta.exclude]
         else:
             names = new_cls._base_fields_pool.keys()
-        new_cls.base_fields = SortedDict([(name, new_cls._base_fields_pool[name]) for name in names])
+        new_cls.base_fields = SortedDict([(name, new_cls._meta.formfield_for_formfield(new_cls._meta, new_cls._base_fields_pool[name])) for name in names])
     create_base_fields_from_base_fields_pool = classmethod(create_base_fields_from_base_fields_pool)
 
     def __new__(cls, name, bases, attrs):
@@ -89,16 +100,13 @@
     # class is different than Form. See the comments by the Form class for more
     # information. Any improvements to the form API should be made to *this*
     # class, not to the Form class.
-    def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
-                 initial=None, error_class=ErrorList, label_suffix=':'):
+    def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, initial=None):
         self.is_bound = data is not None or files is not None
         self.data = data or {}
         self.files = files or {}
         self.auto_id = auto_id
         self.prefix = prefix
         self.initial = initial or {}
-        self.error_class = error_class
-        self.label_suffix = label_suffix
         self._errors = None # Stores the errors after clean() has been called.
 
         # The base_fields class attribute is the *class-wide* definition of
@@ -186,7 +194,11 @@
 
     def hidden_fields_html_output(self, hidden_fields, hidden_fields_row):
         "Helper function for outputting HTML from a hidden fields row. Used by _html_output."
-        return hidden_fields_row % u''.join(hidden_fields)
+        if self._meta.html_class_for_hidden_fields_row:
+            attrs = u' class="%s"' % self._meta.html_class_for_hidden_fields_row
+        else:
+            attrs = u''
+        return hidden_fields_row % (attrs, u''.join(hidden_fields))
 
     def _html_output(self, output_type, top_errors_row, fieldset_start, fieldset_end, hidden_fields_row):
         "Helper function for outputting HTML. Used by as_table(), as_ul(), as_p()."
@@ -221,15 +233,15 @@
 
     def as_table(self):
         "Returns this form rendered as HTML <tr>s -- excluding the <table></table>."
-        return self._html_output('table', u'<tr><td colspan="2">%s</td></tr>', u'%s\n<table>', u'</table>\n%s', u'<tr><td colspan="2">%s</td></tr>')
+        return self._html_output('table', u'<tr><td colspan="2">%s</td></tr>', u'%s\n<table>', u'</table>\n%s', u'<tr%s><td colspan="2">%s</td></tr>')
 
     def as_ul(self):
         "Returns this form rendered as HTML <li>s -- excluding the <ul></ul>."
-        return self._html_output('ul', u'<li>%s</li>', u'%s\n<ul>', u'</ul>\n%s', u'<li>%s</li>')
+        return self._html_output('ul', u'<li>%s</li>', u'%s\n<ul>', u'</ul>\n%s', u'<li%s>%s</li>')
 
     def as_p(self):
         "Returns this form rendered as HTML <p>s."
-        return self._html_output('p', u'%s', u'%s', u'%s', u'<p>%s</p>')
+        return self._html_output('p', u'%s', u'%s', u'%s', u'<p%s>%s</p>')
 
     def non_field_errors(self):
         """
@@ -237,7 +249,7 @@
         field -- i.e., from Form.clean(). Returns an empty ErrorList if there
         are none.
         """
-        return self.errors.get(NON_FIELD_ERRORS, self.error_class())
+        return self.errors.get(NON_FIELD_ERRORS, self._meta.error_class())
 
     def full_clean(self):
         """
@@ -248,7 +260,11 @@
         if not self.is_bound: # Stop further processing.
             return
         self.cleaned_data = {}
-        for name, field in self.fields.items():
+        if self._meta.validation_order:
+            items = [(name, self.fields[name]) for name in self._meta.validation_order]
+        else:
+            items = self.fields.items()
+        for name, field in items:
             # value_from_datadict() gets the data from the data dictionaries.
             # Each widget type knows how to retrieve its own data, because some
             # widgets split data over several HTML fields.
@@ -264,13 +280,13 @@
                     value = getattr(self, 'clean_%s' % name)()
                     self.cleaned_data[name] = value
             except ValidationError, e:
-                self._errors[name] = self.error_class(e.messages)
+                self._errors[name] = self._meta.error_class(e.messages)
                 if name in self.cleaned_data:
                     del self.cleaned_data[name]
         try:
             self.cleaned_data = self.clean()
         except ValidationError, e:
-            self._errors[NON_FIELD_ERRORS] = self.error_class(e.messages)
+            self._errors[NON_FIELD_ERRORS] = self._meta.error_class(e.messages)
         if self._errors:
             delattr(self, 'cleaned_data')
 
@@ -321,7 +337,7 @@
         Returns an ErrorList for this field. Returns an empty ErrorList
         if there are none.
         """
-        return self.form.errors.get(self.name, self.form.error_class())
+        return self.form.errors.get(self.name, self.form._meta.error_class())
     errors = property(_errors)
 
     def as_widget(self, widget=None, attrs=None):
@@ -372,16 +388,19 @@
         if self.field.label is None:
             return pretty_name(self.name)
         else:
-            return conditional_escape(force_unicode(self.field.label))
+            label = conditional_escape(force_unicode(self.field.label))
+            if self.form._meta.label_capitalization:
+                label = capfirst(label)
+            return label
     label = property(_label)
 
     def _label_tag(self):
         "Returns label tag for this field as safe HTML code."
         label = self.label
-        if self.form.label_suffix:
+        if self.form._meta.label_suffix:
             # Only add the suffix if the label does not end in punctuation.
             if label and label[-1] not in ':?.!':
-                label += self.form.label_suffix
+                label += self.form._meta.label_suffix
         id_ = self.widget.attrs.get('id') or self.auto_id
         if label and id_:
             id_ = self.widget.id_for_label(id_)
@@ -400,7 +419,15 @@
 
     def _row_attrs(self):
         "Returns row attributes for this field as safe HTML code."
-        return flatatt(self.widget.row_attrs)
+        attrs = self.widget.row_attrs.copy()
+        class_list = attrs.pop('class', '').split()
+        if self.form._meta.html_class_for_fields_with_errors and self.errors:
+            class_list.append(self.form._meta.html_class_for_fields_with_errors)
+        if self.form._meta.html_class_for_required_fields and self.field.required:
+            class_list.append(self.form._meta.html_class_for_required_fields)
+        if class_list:
+            attrs['class'] = u' '.join(class_list)
+        return flatatt(attrs)
     row_attrs = property(_row_attrs)
 
     def _is_hidden(self):

=== modified file 'django/newforms/models.py'
--- django/newforms/models.py	2008-02-18 14:53:53 +0000
+++ django/newforms/models.py	2008-02-18 15:00:59 +0000
@@ -9,7 +9,7 @@
 from django.utils.encoding import smart_unicode
 from django.utils.datastructures import SortedDict, InheritableOptions
 
-from util import ValidationError, ErrorList
+from util import ValidationError
 from forms import FormOptions, FormMetaclass, BaseForm
 from fields import Field, ChoiceField, EMPTY_VALUES
 from widgets import Select, SelectMultiple, MultipleHiddenInput
@@ -252,9 +252,7 @@
         return new_cls
 
 class BaseModelForm(BaseForm):
-    def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
-                 initial=None, error_class=ErrorList, label_suffix=':',
-                 instance=None):
+    def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, initial=None, instance=None):
         opts = self._meta
         if instance is None:
             # if we didn't get an instance, instantiate a new one
@@ -266,7 +264,7 @@
         # if initial was provided, it should override the values from instance
         if initial is not None:
             object_data.update(initial)
-        BaseForm.__init__(self, data, files, auto_id, prefix, object_data, error_class, label_suffix)
+        BaseForm.__init__(self, data, files, auto_id, prefix, object_data)
 
     def save(self, commit=True):
         """

