Kevin Veroneau avatar Kevin Veroneau committed 7aa8f3c Draft

Initial import of django-petition project.

Comments (0)

Files changed (22)

+# django-petition
+
+A super simple Django petition app.
+
+All you need to add to your settings.py is PETITION_FROM for when the confirm email is sent out.
+You will of course need to add it to your INSTALLED_APPS, and your urls.py.
+
+You should also be using the SMTP Emailback and configure it accordingly.
+

Empty file added.

petition/admin.py

+from django.contrib import admin
+from petition.models import Signature
+
+admin.site.register(Signature)

petition/context_processors.py

+from petition.forms import PetitionForm
+from petition.models import Signature
+
+def petition_form(req):
+    return {'petition_form':PetitionForm(), 'pledge_count':Signature.objects.filter(confirmed=True).count()}

petition/fields.py

+from django.utils.translation import ugettext as _
+from django.db import models
+
+# ISO 3166-1 country names and codes adapted from http://opencountrycodes.appspot.com/python/
+COUNTRIES = (
+    ('GB', _('United Kingdom')),
+    ('AF', _('Afghanistan')),
+    ('AX', _('Aland Islands')),
+    ('AL', _('Albania')),
+    ('DZ', _('Algeria')),
+    ('AS', _('American Samoa')),
+    ('AD', _('Andorra')),
+    ('AO', _('Angola')),
+    ('AI', _('Anguilla')),
+    ('AQ', _('Antarctica')),
+    ('AG', _('Antigua and Barbuda')),
+    ('AR', _('Argentina')),
+    ('AM', _('Armenia')),
+    ('AW', _('Aruba')),
+    ('AU', _('Australia')),
+    ('AT', _('Austria')),
+    ('AZ', _('Azerbaijan')),
+    ('BS', _('Bahamas')),
+    ('BH', _('Bahrain')),
+    ('BD', _('Bangladesh')),
+    ('BB', _('Barbados')),
+    ('BY', _('Belarus')),
+    ('BE', _('Belgium')),
+    ('BZ', _('Belize')),
+    ('BJ', _('Benin')),
+    ('BM', _('Bermuda')),
+    ('BT', _('Bhutan')),
+    ('BO', _('Bolivia')),
+    ('BA', _('Bosnia and Herzegovina')),
+    ('BW', _('Botswana')),
+    ('BV', _('Bouvet Island')),
+    ('BR', _('Brazil')),
+    ('IO', _('British Indian Ocean Territory')),
+    ('BN', _('Brunei Darussalam')),
+    ('BG', _('Bulgaria')),
+    ('BF', _('Burkina Faso')),
+    ('BI', _('Burundi')),
+    ('KH', _('Cambodia')),
+    ('CM', _('Cameroon')),
+    ('CA', _('Canada')),
+    ('CV', _('Cape Verde')),
+    ('KY', _('Cayman Islands')),
+    ('CF', _('Central African Republic')),
+    ('TD', _('Chad')),
+    ('CL', _('Chile')),
+    ('CN', _('China')),
+    ('CX', _('Christmas Island')),
+    ('CC', _('Cocos (Keeling) Islands')),
+    ('CO', _('Colombia')),
+    ('KM', _('Comoros')),
+    ('CG', _('Congo')),
+    ('CD', _('Congo, The Democratic Republic of the')),
+    ('CK', _('Cook Islands')),
+    ('CR', _('Costa Rica')),
+    ('CI', _('Cote d\'Ivoire')),
+    ('HR', _('Croatia')),
+    ('CU', _('Cuba')),
+    ('CY', _('Cyprus')),
+    ('CZ', _('Czech Republic')),
+    ('DK', _('Denmark')),
+    ('DJ', _('Djibouti')),
+    ('DM', _('Dominica')),
+    ('DO', _('Dominican Republic')),
+    ('EC', _('Ecuador')),
+    ('EG', _('Egypt')),
+    ('SV', _('El Salvador')),
+    ('GQ', _('Equatorial Guinea')),
+    ('ER', _('Eritrea')),
+    ('EE', _('Estonia')),
+    ('ET', _('Ethiopia')),
+    ('FK', _('Falkland Islands (Malvinas)')),
+    ('FO', _('Faroe Islands')),
+    ('FJ', _('Fiji')),
+    ('FI', _('Finland')),
+    ('FR', _('France')),
+    ('GF', _('French Guiana')),
+    ('PF', _('French Polynesia')),
+    ('TF', _('French Southern Territories')),
+    ('GA', _('Gabon')),
+    ('GM', _('Gambia')),
+    ('GE', _('Georgia')),
+    ('DE', _('Germany')),
+    ('GH', _('Ghana')),
+    ('GI', _('Gibraltar')),
+    ('GR', _('Greece')),
+    ('GL', _('Greenland')),
+    ('GD', _('Grenada')),
+    ('GP', _('Guadeloupe')),
+    ('GU', _('Guam')),
+    ('GT', _('Guatemala')),
+    ('GG', _('Guernsey')),
+    ('GN', _('Guinea')),
+    ('GW', _('Guinea-Bissau')),
+    ('GY', _('Guyana')),
+    ('HT', _('Haiti')),
+    ('HM', _('Heard Island and McDonald Islands')),
+    ('VA', _('Holy See (Vatican City State)')),
+    ('HN', _('Honduras')),
+    ('HK', _('Hong Kong')),
+    ('HU', _('Hungary')),
+    ('IS', _('Iceland')),
+    ('IN', _('India')),
+    ('ID', _('Indonesia')),
+    ('IR', _('Iran, Islamic Republic of')),
+    ('IQ', _('Iraq')),
+    ('IE', _('Ireland')),
+    ('IM', _('Isle of Man')),
+    ('IL', _('Israel')),
+    ('IT', _('Italy')),
+    ('JM', _('Jamaica')),
+    ('JP', _('Japan')),
+    ('JE', _('Jersey')),
+    ('JO', _('Jordan')),
+    ('KZ', _('Kazakhstan')),
+    ('KE', _('Kenya')),
+    ('KI', _('Kiribati')),
+    ('KP', _('Korea, Democratic People\'s Republic of')),
+    ('KR', _('Korea, Republic of')),
+    ('KW', _('Kuwait')),
+    ('KG', _('Kyrgyzstan')),
+    ('LA', _('Lao People\'s Democratic Republic')),
+    ('LV', _('Latvia')),
+    ('LB', _('Lebanon')),
+    ('LS', _('Lesotho')),
+    ('LR', _('Liberia')),
+    ('LY', _('Libyan Arab Jamahiriya')),
+    ('LI', _('Liechtenstein')),
+    ('LT', _('Lithuania')),
+    ('LU', _('Luxembourg')),
+    ('MO', _('Macao')),
+    ('MK', _('Macedonia, The Former Yugoslav Republic of')),
+    ('MG', _('Madagascar')),
+    ('MW', _('Malawi')),
+    ('MY', _('Malaysia')),
+    ('MV', _('Maldives')),
+    ('ML', _('Mali')),
+    ('MT', _('Malta')),
+    ('MH', _('Marshall Islands')),
+    ('MQ', _('Martinique')),
+    ('MR', _('Mauritania')),
+    ('MU', _('Mauritius')),
+    ('YT', _('Mayotte')),
+    ('MX', _('Mexico')),
+    ('FM', _('Micronesia, Federated States of')),
+    ('MD', _('Moldova')),
+    ('MC', _('Monaco')),
+    ('MN', _('Mongolia')),
+    ('ME', _('Montenegro')),
+    ('MS', _('Montserrat')),
+    ('MA', _('Morocco')),
+    ('MZ', _('Mozambique')),
+    ('MM', _('Myanmar')),
+    ('NA', _('Namibia')),
+    ('NR', _('Nauru')),
+    ('NP', _('Nepal')),
+    ('NL', _('Netherlands')),
+    ('AN', _('Netherlands Antilles')),
+    ('NC', _('New Caledonia')),
+    ('NZ', _('New Zealand')),
+    ('NI', _('Nicaragua')),
+    ('NE', _('Niger')),
+    ('NG', _('Nigeria')),
+    ('NU', _('Niue')),
+    ('NF', _('Norfolk Island')),
+    ('MP', _('Northern Mariana Islands')),
+    ('NO', _('Norway')),
+    ('OM', _('Oman')),
+    ('PK', _('Pakistan')),
+    ('PW', _('Palau')),
+    ('PS', _('Palestinian Territory, Occupied')),
+    ('PA', _('Panama')),
+    ('PG', _('Papua New Guinea')),
+    ('PY', _('Paraguay')),
+    ('PE', _('Peru')),
+    ('PH', _('Philippines')),
+    ('PN', _('Pitcairn')),
+    ('PL', _('Poland')),
+    ('PT', _('Portugal')),
+    ('PR', _('Puerto Rico')),
+    ('QA', _('Qatar')),
+    ('RE', _('Reunion')),
+    ('RO', _('Romania')),
+    ('RU', _('Russian Federation')),
+    ('RW', _('Rwanda')),
+    ('BL', _('Saint Barthelemy')),
+    ('SH', _('Saint Helena')),
+    ('KN', _('Saint Kitts and Nevis')),
+    ('LC', _('Saint Lucia')),
+    ('MF', _('Saint Martin')),
+    ('PM', _('Saint Pierre and Miquelon')),
+    ('VC', _('Saint Vincent and the Grenadines')),
+    ('WS', _('Samoa')),
+    ('SM', _('San Marino')),
+    ('ST', _('Sao Tome and Principe')),
+    ('SA', _('Saudi Arabia')),
+    ('SN', _('Senegal')),
+    ('RS', _('Serbia')),
+    ('SC', _('Seychelles')),
+    ('SL', _('Sierra Leone')),
+    ('SG', _('Singapore')),
+    ('SK', _('Slovakia')),
+    ('SI', _('Slovenia')),
+    ('SB', _('Solomon Islands')),
+    ('SO', _('Somalia')),
+    ('ZA', _('South Africa')),
+    ('GS', _('South Georgia and the South Sandwich Islands')),
+    ('ES', _('Spain')),
+    ('LK', _('Sri Lanka')),
+    ('SD', _('Sudan')),
+    ('SR', _('Suriname')),
+    ('SJ', _('Svalbard and Jan Mayen')),
+    ('SZ', _('Swaziland')),
+    ('SE', _('Sweden')),
+    ('CH', _('Switzerland')),
+    ('SY', _('Syrian Arab Republic')),
+    ('TW', _('Taiwan, Province of China')),
+    ('TJ', _('Tajikistan')),
+    ('TZ', _('Tanzania, United Republic of')),
+    ('TH', _('Thailand')),
+    ('TL', _('Timor-Leste')),
+    ('TG', _('Togo')),
+    ('TK', _('Tokelau')),
+    ('TO', _('Tonga')),
+    ('TT', _('Trinidad and Tobago')),
+    ('TN', _('Tunisia')),
+    ('TR', _('Turkey')),
+    ('TM', _('Turkmenistan')),
+    ('TC', _('Turks and Caicos Islands')),
+    ('TV', _('Tuvalu')),
+    ('UG', _('Uganda')),
+    ('UA', _('Ukraine')),
+    ('AE', _('United Arab Emirates')),
+    ('US', _('United States')),
+    ('UM', _('United States Minor Outlying Islands')),
+    ('UY', _('Uruguay')),
+    ('UZ', _('Uzbekistan')),
+    ('VU', _('Vanuatu')),
+    ('VE', _('Venezuela')),
+    ('VN', _('Viet Nam')),
+    ('VG', _('Virgin Islands, British')),
+    ('VI', _('Virgin Islands, U.S.')),
+    ('WF', _('Wallis and Futuna')),
+    ('EH', _('Western Sahara')),
+    ('YE', _('Yemen')),
+    ('ZM', _('Zambia')),
+    ('ZW', _('Zimbabwe')),
+)
+
+class CountryField(models.CharField):
+
+    def __init__(self, *args, **kwargs):
+        kwargs.setdefault('max_length', 2)
+        kwargs.setdefault('choices', COUNTRIES)
+
+        super(CountryField, self).__init__(*args, **kwargs)
+
+    def get_internal_type(self):
+        return "CharField"
+
+from south.modelsinspector import add_introspection_rules
+add_introspection_rules([], ["^petition\.fields\.CountryField"])

petition/forms.py

+from django import forms
+from petition.models import Signature
+
+class PetitionForm(forms.ModelForm):
+    class Meta:
+        model = Signature
+        exclude = ('confirmed',)

petition/migrations/0001_initial.py

+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        # Adding model 'Signature'
+        db.create_table('petition_signature', (
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('first_name', self.gf('django.db.models.fields.CharField')(max_length=60)),
+            ('last_name', self.gf('django.db.models.fields.CharField')(max_length=60)),
+            ('email', self.gf('django.db.models.fields.EmailField')(max_length=75)),
+            ('state', self.gf('django.db.models.fields.CharField')(max_length=40)),
+            ('country', self.gf('petition.fields.CountryField')(default='US', max_length=2)),
+            ('date_signed', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)),
+        ))
+        db.send_create_signal('petition', ['Signature'])
+
+
+    def backwards(self, orm):
+        # Deleting model 'Signature'
+        db.delete_table('petition_signature')
+
+
+    models = {
+        'petition.signature': {
+            'Meta': {'object_name': 'Signature'},
+            'country': ('petition.fields.CountryField', [], {'default': "'US'", 'max_length': '2'}),
+            'date_signed': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '60'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '60'}),
+            'state': ('django.db.models.fields.CharField', [], {'max_length': '40'})
+        }
+    }
+
+    complete_apps = ['petition']

petition/migrations/0002_auto__add_unique_signature_email__add_unique_signature_first_name_last.py

+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        # Adding unique constraint on 'Signature', fields ['email']
+        db.create_unique('petition_signature', ['email'])
+
+        # Adding unique constraint on 'Signature', fields ['first_name', 'last_name']
+        db.create_unique('petition_signature', ['first_name', 'last_name'])
+
+
+    def backwards(self, orm):
+        # Removing unique constraint on 'Signature', fields ['first_name', 'last_name']
+        db.delete_unique('petition_signature', ['first_name', 'last_name'])
+
+        # Removing unique constraint on 'Signature', fields ['email']
+        db.delete_unique('petition_signature', ['email'])
+
+
+    models = {
+        'petition.signature': {
+            'Meta': {'unique_together': "(('first_name', 'last_name'),)", 'object_name': 'Signature'},
+            'country': ('petition.fields.CountryField', [], {'default': "'US'", 'max_length': '2'}),
+            'date_signed': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '60'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '60'}),
+            'state': ('django.db.models.fields.CharField', [], {'max_length': '40'})
+        }
+    }
+
+    complete_apps = ['petition']

petition/migrations/0003_auto__add_field_signature_confirmed.py

+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        # Adding field 'Signature.confirmed'
+        db.add_column('petition_signature', 'confirmed',
+                      self.gf('django.db.models.fields.BooleanField')(default=False),
+                      keep_default=False)
+
+
+    def backwards(self, orm):
+        # Deleting field 'Signature.confirmed'
+        db.delete_column('petition_signature', 'confirmed')
+
+
+    models = {
+        'petition.signature': {
+            'Meta': {'unique_together': "(('first_name', 'last_name'),)", 'object_name': 'Signature'},
+            'confirmed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'country': ('petition.fields.CountryField', [], {'default': "'US'", 'max_length': '2'}),
+            'date_signed': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'email': ('django.db.models.fields.EmailField', [], {'unique': 'True', 'max_length': '75'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '60'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '60'}),
+            'state': ('django.db.models.fields.CharField', [], {'max_length': '40'})
+        }
+    }
+
+    complete_apps = ['petition']
Add a comment to this file

petition/migrations/__init__.py

Empty file added.

petition/models.py

+from django.db import models
+from petition.fields import CountryField
+import hashlib
+from django.conf import settings
+from django.dispatch.dispatcher import receiver
+from django.db.models.signals import post_save
+from django.core.mail import send_mail
+from django.template.loader import render_to_string
+
+class Signature(models.Model):
+    first_name = models.CharField(max_length=60)
+    last_name = models.CharField(max_length=60)
+    email = models.EmailField(unique=True, error_messages={'unique':'This email address has already signed.', 'blank':'Please fill in your email address.', 'invalid':'You entered in an invalid email address.'})
+    state = models.CharField(max_length=40)
+    country = CountryField(default='US')
+    date_signed = models.DateTimeField(auto_now_add=True)
+    confirmed = models.BooleanField()
+    class Meta:
+        unique_together = ('first_name', 'last_name')
+    def __unicode__(self):
+        return u"%s %s" % (self.first_name, self.last_name)
+    @models.permalink
+    def get_absolute_url(self):
+        return ('signatures', [])
+    def get_email_hash(self):
+        return hashlib.md5(self.email+settings.SECRET_KEY).hexdigest()
+    @models.permalink
+    def get_confirm_link(self):
+        return ('confirm-email', [str(self.pk), self.get_email_hash()])
+
+@receiver(post_save, sender=Signature)
+def send_confirm_email(sender, instance, created, **kwargs):
+    if created:
+        subject = render_to_string('petition/email_subject.txt', {'signature':instance})
+        body = render_to_string('petition/email_confirm.txt', {'signature':instance})
+        send_mail(subject, body, settings.PETITION_FROM, [instance.email], fail_silently=True)

petition/templates/petition/email_confirm.txt

+Thanks for signing the pledge {{signature.first_name}}, you're almost done!
+Please confirm your signature by clicking on the link below
+
+http://gab.pythondiary.com{{signature.get_confirm_link}}
+
+Sincerely
+
+Gamers Against Bigotry

petition/templates/petition/email_confirmed.html

+{% extends "base.html" %}
+
+{% block body %}
+Your pledge has been confirmed.
+{% endblock %}

petition/templates/petition/email_failure.html

+{% extends "base.html" %}
+
+{% block body %}
+Your pledge could not be confirmed as the security hash is invalid.
+{% endblock %}

petition/templates/petition/email_subject.txt

+Confirm your Pledge

petition/templates/petition/petition_form.html

+              <form action="{% url sign %}" method="post" name="form1">{% csrf_token %}
+                {% for field in petition_form %}
+                {{field.errors}}
+                {% if field.label == 'Country' %}
+                {{field}}
+                {% else %}
+                <input type="text" name="{{field.html_name}}" value="{{field.value|default:field.label}}" size="32" onfocus="this.style.color='white'; if(this.value=='{{field.value|default:field.label}}')this.value='';" onblur="if(this.value.replace(/\s/g,'')==''){this.style.color='gray';this.value='{{field.value|default:field.label}}';}" />
+                {% endif %}
+                {% endfor %}
+                <input id="submit" type="submit" value="SIGN PETITION" />
+              </form>

petition/templates/petition/pledge_form.html

+        <div id="content_box" class="no_sidebars">
+          <div id="content">
+            <div class="post_box top" id="post-4">
+              <div class="headline_area">
+                <h2>Home</h2>
+              </div>
+              <div class="format_text"></div>
+            </div>
+          </div>
+        </div>
+          <div id="pledge_container">
+            <div id="pledge">
+              <h2><span>Sign the</span> Pledge</h2>
+              <p></p>
+{% block petition_form %}
+{% include "petition/petition_form.html" %}
+{% endblock %}
+              <p></p>
+              <p />
+            </div>
+            <div id="pledge_about">
+              <h2><span>The</span> Pledge:</h2>
+              <p>As a gamer, I realize I contribute to an incredibly diverse social network of gamers around the world, and that my actions have the ability to impact others.  In effort to make a positive impact, and to create a community that is welcoming to all, I pledge to not use bigoted language while gaming, online and otherwise.</p><br /><br />
+              <p>Bigoted language includes, but is not limited to, slurs based on 
+              <span>race<span class="example_button">?<span class="example">e.g, &quot;chink,&quot; &quot;nigger,&quot; &quot;wetback&quot;</span></span>, 
+              <span>ethnicity<span class="example_button">?<span class="example">e.g., &quot;jew,&quot; &quot;kyke,&quot; &quot;polock&quot;</span></span>,       
+              <span>gender<span class="example_button">?<span class="example">e.g., &quot;cunt,&quot; &quot;whore&quot;</span></span>, 
+              <span>sexual orientation<span class="example_button">?<span class="example">e.g., &quot;gay,&quot; &quot;fag(got),&quot; &quot;dyke&quot;</span></span>, and
+              <span>disability<span class="example_button">?<span class="example">e.g., &quot;retard(ed)&quot;</span></span>
+              </span></span></span></span></span></p><br />
+              <p class="dark">Read more about the pledge, including what is and isn't included, and the overall purpose <a href="{% url about-pledge %}" alt="About the Pledge">here</a>.</p>
+              <br />
+              <p class="dark">Read why you shouldn't use the word &quot;rape&quot; casually <a href="#" alt="Don't say rape">here</a>.</p>
+            </div>
+          </div>

petition/templates/petition/sign_petition.html

+{% extends "base.html" %}
+
+{% block petition_form %}
+{% if form.errors %}
+<p>Please correct the following errors and try again.</p>
+{{ form.non_field_errors }}
+{% endif %}
+{% with form as petition_form %}
+{% include "petition/petition_form.html" %}
+{% endwith %}
+{% endblock %}

petition/templates/petition/signatures.html

+{% extends "base.html" %}
+
+{% block body %}
+<script type="text/javascript" src="http://code.jquery.com/jquery-1.7.2.min.js"></script>
+<script type="text/javascript" src="http://gamersagainstbigotry.org/wp-content/themes/thesis/custom/includes/jquery.tablesorter.min.js"></script>
+<script type="text/javascript">
+    $(document).ready(function() {
+        $("#signatures").tablesorter({sortList: [[0,0], [1,0]]});
+    }
+    );
+</script>
+<div id="signatures_container">
+    <div class="hint">(hint: click the headers to sort the signatures by that column)</div>
+    <table width="100%" border="0" cellpadding="10" id="signatures" class="tablesorter">
+        <thead>
+          <tr class="header">
+            <th width="10%"><span class="style1">ID</span></th>
+            <th width="30%"><span class="style1">First Name</span></th>
+            <th width="40%"><span class="style1">Last Name</span></th>
+            <th width="20%"><span class="style1">State</span></th>
+            <th width="5%"><span class="style1">Country</span></th>
+          </tr>
+        </thead>
+        <tbody>
+          {% for signature in object_list %}
+            <tr>
+              <td>{{signature.pk}}</td>
+              <td>{{signature.first_name}}</td>
+              <td>{{signature.last_name}}</td>
+              <td>{{signature.state}}</td>
+              <td>{{signature.get_country_display}}</td>
+            </tr>
+          {% endfor %}
+        </tbody>
+      </table>
+</div>
+{% endblock %}

petition/tests.py

+"""
+This file demonstrates writing tests using the unittest module. These will pass
+when you run "manage.py test".
+
+Replace this with more appropriate tests for your application.
+"""
+
+from django.test import TestCase
+
+
+class SimpleTest(TestCase):
+    def test_basic_addition(self):
+        """
+        Tests that 1 + 1 always equals 2.
+        """
+        self.assertEqual(1 + 1, 2)
+from django.conf.urls import patterns, url
+from petition.views import SignPetition, verify_email
+from django.views.generic.base import TemplateView
+
+urlpatterns = patterns('',
+    url(r'^Sign$', SignPetition.as_view(), name='sign'),
+    url(r'^Confirm/(?P<signature_id>[0-9]+):(?P<email_hash>[a-f0-9]+)$', verify_email, name='confirm-email'),
+    url(r'^Confirmed$', TemplateView.as_view(template_name='petition/email_confirmed.html'), name='email-confirmed'),
+    url(r'^VerificationFailed$', TemplateView.as_view(template_name='petition/email_failure.html'), name='email-failure'),
+)

petition/views.py

+from django.views.generic.edit import CreateView
+from petition.forms import PetitionForm
+from django.views.generic.list import ListView
+from petition.models import Signature
+from django.shortcuts import get_object_or_404, redirect
+
+class SignPetition(CreateView):
+    form_class = PetitionForm
+    template_name = 'petition/sign_petition.html'
+
+class Signatures(ListView):
+    queryset = Signature.objects.filter(confirmed=True)
+    template_name = 'petition/signatures.html'
+
+def verify_email(req, signature_id, email_hash):
+    signature = get_object_or_404(Signature, pk=signature_id, confirmed=False)
+    if email_hash == signature.get_email_hash():
+        signature.confirmed = True
+        signature.save()
+        return redirect('email-confirmed')
+    return redirect('email-failure')
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.