updated Django form with honeypot from master and styled form errors.
authorBryan <btbonval@gmail.com>
Wed, 26 Feb 2014 08:13:27 +0000 (03:13 -0500)
committerBryan <btbonval@gmail.com>
Wed, 26 Feb 2014 08:13:27 +0000 (03:13 -0500)
karmaworld/apps/courses/forms.py
karmaworld/apps/courses/models.py
karmaworld/assets/css/global.css
karmaworld/settings/common.py
karmaworld/templates/partial/add_course.html

index e2642d35eb234943a647419a417121d595fbf3ee..42c50667156ea562444e809eb86dc75dd294e21e 100644 (file)
@@ -2,9 +2,12 @@
 # -*- coding:utf8 -*-
 # Copyright (C) 2012  FinalsClub Foundation
 
+import random
+
 from django.conf import settings
 from django.forms import ModelForm
 from django.forms import CharField
+from django.forms.util import ErrorList
 
 from ajax_select.fields import AutoCompleteSelectField
 from ajax_select.fields import AutoCompleteSelectWidget
@@ -13,7 +16,33 @@ from ajax_select_cascade.fields import AutoCompleteDependentSelectWidget
 
 from karmaworld.apps.courses.models import Course
 
-class CourseForm(ModelForm):
+
+# Django hard codes CSS attributes into ModelForm returned ErrorList
+# https://github.com/django/django/blob/1.5.5/django/forms/util.py#L54-L60
+# https://docs.djangoproject.com/en/1.5/ref/forms/api/#customizing-the-error-list-format
+# so this unfortunately doesn't do anything with ModelForms:
+# https://docs.djangoproject.com/en/1.5/ref/forms/api/#django.forms.Form.error_css_class
+class CSSErrorList(ErrorList):
+    """ Override ErrorList classes. """
+    def as_ul(self, *args, **kwargs):
+        errorhtml = super(CSSErrorList, self).as_ul(*args, **kwargs)
+        # insert <label> around the error
+        errorhtml = errorhtml.replace('<li>', "<li><label class='validation_error'>")
+        errorhtml = errorhtml.replace('</li>', '</label></li>')
+        # replace hard coded "errorlist" with something in our CSS:
+        errorhtml = errorhtml.replace('errorlist', 'validation_error')
+        return errorhtml
+
+
+class NiceErrorModelForm(ModelForm):
+    """ By default use CSSErrorList for displaying errors. """
+    def __init__(self, *args, **kwargs):
+        if 'error_class' not in kwargs:
+            kwargs['error_class'] = CSSErrorList
+        super(NiceErrorModelForm, self).__init__(*args, **kwargs)
+
+
+class CourseForm(NiceErrorModelForm):
     school = AutoCompleteSelectField(
         'school',
         widget=AutoCompleteSelectWidget(
@@ -32,7 +61,11 @@ class CourseForm(ModelForm):
     def __init__(self, *args, **kwargs):
         """ Add a dynamically named field. """
         super(CourseForm, self).__init__(*args, **kwargs)
-        self.fields[settings.HONEYPOT_FIELD_NAME] = CharField(required=False)
+        # insert honeypot into a random order on the form.
+        idx = random.randint(0, len(self.fields))
+        self.fields.insert(idx, settings.HONEYPOT_FIELD_NAME,
+            CharField(required=False, label=settings.HONEYPOT_LABEL)
+        )
 
     class Meta:
         model = Course
@@ -51,9 +84,7 @@ class CourseForm(ModelForm):
         hfn = settings.HONEYPOT_FIELD_NAME
         formhoneypot = cleaned_data.get(hfn, None)
         if formhoneypot and (formhoneypot != settings.HONEYPOT_VALUE):
-            # Highlight a failure to follow instructions.
-            # When the template dynamically generates the form, replace
-            # 'honeypot' with hfn
-            self._errors['honeypot'] = [u'You did not follow directions.']
+            # Highlight the failure to follow instructions.
+            self._errors[hfn] = [settings.HONEYPOT_ERROR]
             del cleaned_data[hfn]
         return cleaned_data
index 6ef836c2d566068a155695a12303b1a3ae1d1e1f..f04cf1e1ab70f5bc31c5ee3aa3456eb5df36078b 100644 (file)
@@ -19,8 +19,6 @@ from ajax_select import LookupChannel
 from ajax_select_cascade import DependentLookupChannel
 from ajax_select_cascade import register_channel_name
 
-from karmaworld.utils.ajax_selects import register_channel_name
-
 
 class SchoolManager(models.Manager):
     """ Handle restoring data. """
index ae7e0835cf052d0274e3db4c9b661153fc993645..822a67c538d4d76887fa630a249d7ad98f18a69e 100644 (file)
@@ -645,6 +645,10 @@ a.activity_target:hover
 .validation_error {
   color:red;
 }
+ul.validation_error {
+  display: inline;
+  list-style-type: none;
+}
 
 #add-note-form, #add-course-form, #edit-course-form
 {
@@ -891,7 +895,6 @@ hr.midrule
 legend, label
 {
   font: 12px/2em "MuseoSlab-300";
-  margin-top: 20px;
 }
 
 .qq-upload-button {
index d47ff22f6b2da23013d7f4e4d52ef5d012e67818..d5f9b855e38520a4c4c8a52e912dfc3116b5b5c9 100644 (file)
@@ -381,6 +381,8 @@ TAGGIT_STOPWORDS = [u'a', u'an', u'and', u'be', u'from', u'of']
 # https://github.com/sunlightlabs/django-honeypot
 HONEYPOT_FIELD_NAME = "instruction_url" # see that "_url"? bots gotta want that.
 HONEYPOT_VALUE = ""
+HONEYPOT_LABEL = "Do not fill in this field:"
+HONEYPOT_ERROR = "You did not follow directions."
 ########## END HONEYPOT CONFIGURATION
 
 
index 7b52b5812a384a5fae60fce1994cdaab1552965f..2c2dbee5e5a10f68c5c68df0123fd27485fd0d6c 100644 (file)
@@ -21,7 +21,7 @@
     </div>
     {% endfor %}
 
-    {{ course_form }}
+    {{ course_form.as_p }}
 
     <div class="row">
       <div class="small-4 large-8 columns small-centered text-center">