=== modified file 'django/newforms/forms.py'
--- django/newforms/forms.py	2008-03-05 22:14:58 +0000
+++ django/newforms/forms.py	2008-03-05 22:50:21 +0000
@@ -7,6 +7,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 FileField
 from widgets import TextInput, Textarea
@@ -24,9 +25,18 @@
 
 class FormOptions(object):
     def __init__(self, options=None):
+        # main options
         self.fieldsets = getattr(options, 'fieldsets', None)
         self.fields = getattr(options, 'fields', None)
         self.exclude = getattr(options, 'exclude', None)
+        # other options
+        self.error_class = getattr(options, 'error_class', ErrorList)
+        self.error_row_class = getattr(options, 'error_row_class', 'error')
+        self.hidden_row_class = getattr(options, 'hidden_row_class', 'hidden')
+        self.label_capfirst = getattr(options, 'label_capfirst', True)
+        self.label_suffix = getattr(options, 'label_suffix', ':')
+        self.required_row_class = getattr(options, 'required_row_class', 'required')
+        self.validation_order = getattr(options, 'validation_order', None)
 
 class FormMetaclass(type):
     def __new__(cls, name, bases, attrs):
@@ -42,16 +52,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
@@ -120,8 +127,8 @@
     def _label_tag_html_output(self, bf, label_tag_html):
         "Helper function for outputting HTML from a label. Used by _widget_html_output."
         label, label_id = bf.label, bf.label_id
-        if self.label_suffix and label and label[-1] not in ':?.!':
-            label += self.label_suffix
+        if self._meta.label_suffix and label and label[-1] not in ':?.!':
+            label += self._meta.label_suffix
         if label and label_id:
             return label_tag_html % {
                 'label': label,
@@ -178,7 +185,12 @@
 
     def _hidden_fields_html_output(self, hidden_fields, hidden_fields_html):
         "Helper function for outputting HTML from a hidden fields. Used by _html_output."
+        if self._meta.hidden_row_class:
+            attrs = u' class="%s"' % self._meta.hidden_row_class
+        else:
+            attrs = u''
         return hidden_fields_html % {
+            'attrs': attrs,
             'hidden_fields': u''.join(hidden_fields),
         }
 
@@ -222,7 +234,7 @@
             'fieldset_start_html': u'<fieldset%(attrs)s>\n%(legend_tag)s<table>',
             'fieldset_end_html': u'</table>\n</fieldset>',
             'legend_tag_html': u'<legend>%(legend)s</legend>\n',
-            'hidden_fields_html': u'<tr><td colspan="2">%(hidden_fields)s</td></tr>',
+            'hidden_fields_html': u'<tr%(attrs)s><td colspan="2">%(hidden_fields)s</td></tr>',
         }
         return self._html_output(**kwargs)
 
@@ -236,7 +248,7 @@
             'fieldset_start_html': u'<fieldset%(attrs)s>\n%(legend_tag)s<ul>',
             'fieldset_end_html': u'</ul>\n</fieldset>',
             'legend_tag_html': u'<legend>%(legend)s</legend>\n',
-            'hidden_fields_html': u'<li>%(hidden_fields)s</li>',
+            'hidden_fields_html': u'<li%(attrs)s>%(hidden_fields)s</li>',
         }
         return self._html_output(**kwargs)
 
@@ -250,7 +262,7 @@
             'fieldset_start_html': u'<fieldset%(attrs)s>\n%(legend_tag)s',
             'fieldset_end_html': u'</fieldset>',
             'legend_tag_html': u'<legend>%(legend)s</legend>\n',
-            'hidden_fields_html': u'<p>%(hidden_fields)s</p>',
+            'hidden_fields_html': u'<p%(attrs)>%(hidden_fields)s</p>',
         }
         return self._html_output(**kwargs)
 
@@ -260,7 +272,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):
         """
@@ -271,7 +283,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.
@@ -287,13 +303,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')
 
@@ -344,7 +360,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):
@@ -395,7 +411,10 @@
         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_capfirst:
+                label = capfirst(label)
+            return label
     label = property(_label)
 
     def _label_id(self):
@@ -414,8 +433,16 @@
     help_text = property(_help_text)
 
     def _row_attrs(self):
-        "Returns row attributes for this field as safe HTML."
-        return flatatt(self.widget.row_attrs)
+        "Returns row attributes for this field as safe HTML code."
+        attrs = self.widget.row_attrs.copy()
+        class_list = attrs.pop('class', '').split()
+        if self.form._meta.error_row_class and self.errors:
+            class_list.append(self.form._meta.error_row_class)
+        if self.form._meta.required_row_class and self.field.required:
+            class_list.append(self.form._meta.required_row_class)
+        if class_list:
+            attrs['class'] = u' '.join(class_list)
+        return flatatt(attrs)
     row_attrs = property(_row_attrs)
 
     def label_tag(self, contents=None, attrs=None):

=== modified file 'django/newforms/models.py'
--- django/newforms/models.py	2008-03-05 12:15:56 +0000
+++ django/newforms/models.py	2008-03-05 12:40:42 +0000
@@ -9,7 +9,7 @@
 from django.utils.encoding import smart_unicode
 from django.utils.datastructures import SortedDict
 
-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
@@ -221,9 +221,7 @@
         return new_class
 
 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
@@ -235,7 +233,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):
         """

