Commits

offline committed 4e82326

reimplementation with middleware

Comments (0)

Files changed (9)

confirmation/fields.py

+from django.core.serializers.json import DjangoJSONEncoder
+from django.utils import simplejson as json
+from django.db import models
+
+
+class JSONField(models.TextField):
+    """
+    JSONField is a generic textfield that neatly serializes/unserializes
+    JSON objects seamlessly.
+    Django snippet #1478
+    """
+
+    __metaclass__ = models.SubfieldBase
+
+    def to_python(self, value):
+        if value == "":
+            return None
+
+        try:
+            if isinstance(value, basestring):
+                return json.loads(value)
+        except ValueError:
+            pass
+        return value
+
+    def get_db_prep_save(self, value):
+        if value == "":
+            return None
+        if isinstance(value, dict):
+            value = json.dumps(value, cls=DjangoJSONEncoder)
+        return super(JSONField, self).get_db_prep_save(value)
+

confirmation/managers.py

-import datetime
+import random
 
 from django.db import models
 from django.contrib.contenttypes.models import ContentType
-from django.http import Http404
 
-import confirmation
-from confirmation.utils import generate_hash
+from confirmation import settings
 
 
 class ConfirmationManager(models.Manager):
-    def create(self, obj, confirmation_type, content="", redirect_url=""):
-        content_type = ContentType.objects.get_for_model(obj)
-        key = generate_hash()
-        return super(ConfirmationManager, self).create(content_type=content_type,
-                                                        object_id=obj.pk,
-                                                        key=key,
-                                                        confirmation_type=confirmation_type,
-                                                        content=content,
-                                                        redirect_url=redirect_url
-                                                        )
+    """
+    Auto generate key with length and allowed symobls from settings
+    """
 
-    def get(self, key, confirmation_type=1):
-        """
-        Method will return confirmation instance, none or raise 404 error 
-        according to settings.
-        """
-        now = datetime.datetime.now()
-        limit = now - datetime.timedelta(confirmation.settings.CONFIRMATION_DAYS_LIMIT)
+    def create(self, obj=None, token=1, data=None):
+        key = "".join(random.choice(settings.KEY_SYMBOLS) for i in range(0, settings.KEY_LENGTH))
+        confirmation = self.model(key=key, token=token, data=data)
+        if obj:
+            confirmation.content_type = ContentType.objects.get_for_model(obj)
+            confirmation.object_id = obj.pk
+        confirmation.save()
+        return confirmation
 
-        try:
-            return super(ConfirmationManager, self).get(key=key, 
-                                                        date__range=(limit, now),
-                                                        confirmation_type=confirmation_type)
-        except self.model.DoesNotExist:
-            if confirmation.settings.CONFIRMATION_RAISE_404:
-                raise Http404()
-
-
-        

confirmation/middleware.py

+import datetime
+
+from django.http import Http404
+
+from confirmation.models import Confirmation
+from confirmation import signals
+from confirmation import settings
+
+
+class ConfirmationMiddleware(object):
+
+    def process_request(self, request):
+        """
+        Check confirmation key and type. If match - emit succeffull_confirmation 
+        signal, then delete confirmation.
+        """
+        key = request.REQUEST.get(settings.KEY_NAME)
+        token =  request.REQUEST.get(settings.TOKEN_NAME)
+        if not key or not token:
+            return  # pass request to next middleware
+
+        try:
+            now = datetime.datetime.now()
+            limit = now - datetime.timedelta(settings.DAYS_LIMIT)
+
+            confirmation = Confirmation.objects.get(key=key, token=token, date__range=(limit, now))
+            signals.successfull_confirmation.send(Confirmation, 
+                                                  request=request,
+                                                  instance=confirmation.content_object, 
+                                                  token=confirmation.token)
+
+            confirmation.delete() 
+
+        except Confirmation.DoesNotExist:
+           if settings.RAISE_404:
+               raise Http404()
+

confirmation/models.py

-from django.db import models
+from django.core.urlresolvers import NoReverseMatch, reverse
 from django.contrib.contenttypes.models import ContentType
 from django.contrib.contenttypes import generic
-from django.core.urlresolvers import reverse
+from django.db import models
 
 from confirmation.managers import ConfirmationManager
+from confirmation.fields import JSONField
+from confirmation import settings
 
 
 class Confirmation(models.Model):
     Can be used for user activation, but also for
     other purposes where confirmation is required.
     """
-    content_type = models.ForeignKey(ContentType)
-    object_id = models.PositiveIntegerField()
+    content_type = models.ForeignKey(ContentType, blank=True, null=True)
+    object_id = models.PositiveIntegerField(blank=True, null=True)
     content_object = generic.GenericForeignKey('content_type', 'object_id')
     date = models.DateTimeField(auto_now_add=True)
     key = models.CharField(max_length=32)
-    confirmation_type = models.IntegerField(default=1)
-    content = models.TextField(blank=True)
-    redirect_url = models.SlugField(max_length=255, blank=True)
+    token = models.IntegerField(default=1)
+    data = JSONField(blank=True, null=True)
 
     # overriding get and create methods
     objects = ConfirmationManager()
     
-    def get_url(self, request):
-        url = request.build_absolute_uri(reverse('confirmation'))
-        url += '?key=%s&type=%s' % (self.key, self.confirmation_type)
+    def get_url(self, request, url, *args, **kwargs):
+        """
+        url parameter is name of named url or relative url path
+        """
+        try:
+            url = reverse(url, args=args, kwargs=kwargs)
+        except NoReverseMatch:
+            pass
+
+        url = request.build_absolute_uri(url)
+        url += '?%(key_name)s=%(key_value)s&%(token_name)s=%(token_value)s' % {
+                                                                                'key_name': settings.KEY_NAME, 
+                                                                                'key_value': self.key, 
+                                                                                'token_name': settings.TOKEN_NAME,
+                                                                                'token_value': self.token,
+                                                                                }
         return url
     
     class Meta:

confirmation/settings.py

+from string import ascii_lowercase, digits
+
 from django.conf import settings
 
 
-CONFIRMATION_RAISE_404 = getattr(settings, "CONFIRMATION_RAISE_404", False)
-CONFIRMATION_DAYS_LIMIT = getattr(settings, "CONFIRMATION_DAYS_LIMIT", 30)
+RAISE_404 = getattr(settings, "CONFIRMATION_RAISE_404", False)
+DAYS_LIMIT = getattr(settings, "CONFIRMATION_DAYS_LIMIT", 30)
+TOKEN_NAME = getattr(settings, "CONFIRMATION_TOKEN_NAME", "token")
+KEY_NAME = getattr(settings, "CONFIRMATION_KEY_NAME", "confrimkey")
+KEY_SYMBOLS = getattr(settings, "CONFIRMATION_KEY_SYMBOLS", digits + ascii_lowercase)
+KEY_LENGTH = getattr(settings, "CONFIRMATION_KEY_SYMBOLS", 32)

confirmation/templates/confirmation/confirmation.html

-{% extends 'base.html' %}
-
-{% block content %}
-{{ content }}
-{% endblock %}

confirmation/urls.py

-from django.conf.urls.defaults import *
-
-from confirmation import views
-
-urlpatterns = patterns('',
-   url(r'^confirmation/$', views.confirmation_verify, name='confirmation'),
-)

confirmation/utils.py

-import time
-try:
-    from hashlib import md5
-except ImportError:
-    import md5
-    md5 = md5.new
-    
-from django.conf import settings
-
-
-
-def generate_hash(string=None):
-    """
-    Generate md5 hash from string if provided otherwise generate random hash.
-    """
-    if not string:
-        string = str(time.time()) + settings.SECRET_KEY
-    md5hash = md5(string).hexdigest()
-    return md5hash

confirmation/views.py

-from django.shortcuts import redirect
-
-from annoying.decorators import render_to
-
-from confirmation.models import Confirmation
-from confirmation import signals
-
-
-@render_to("confirmation/confirmation.html")
-def confirmation_verify(request, content=None):
-    """
-    Check confirmation key and type. If match - emit succeffull_confirmation 
-    signal, then delete confirmation.
-    """
-    key = request.GET.get("key")
-    confirmation_type = int(request.GET.get("type", 1))
-    
-    confirmation = Confirmation.objects.get(key=key, confirmation_type=confirmation_type)
-    
-    if confirmation:
-        signals.successfull_confirmation.send(Confirmation, 
-                                              request=request,
-                                              instance=confirmation.content_object, 
-                                              confirmation_type=confirmation.confirmation_type)
-
-        redirect_url = confirmation.redirect_url
-        content = confirmation.content
-
-        confirmation.delete() 
-
-        if redirect_url:
-            return redirect(redirect_url)
-
-    return {'content': content}
-