1 from functools import wraps
 
   3 from django.http import (HttpResponse, HttpResponseRedirect,
 
   5 from django.shortcuts import render_to_response
 
   6 from django.template import RequestContext
 
   7 from django.utils.encoding import force_unicode
 
   8 from django.utils.functional import Promise
 
   9 from django.utils.http import urlquote_plus
 
  10 from django.utils import simplejson
 
  11 from django.utils.translation import ugettext_lazy as _
 
  12 from django.views.decorators.vary import vary_on_headers
 
  15 class LazyEncoder(simplejson.JSONEncoder):
 
  16     def default(self, obj):
 
  17         if isinstance(obj, Promise):
 
  18             return force_unicode(obj)
 
  21 # shortcut for JSON reponses
 
  22 class JSONResponse(HttpResponse):
 
  23     def __init__(self, data={}, callback=None, **kwargs):
 
  25         kwargs.pop('mimetype', None)
 
  26         data = simplejson.dumps(data)
 
  28             data = callback + "(" + data + ");" 
 
  29         super(JSONResponse, self).__init__(data, mimetype="application/json", **kwargs)
 
  32 def method_decorator(function_decorator):
 
  33     """Converts a function decorator to a method decorator.
 
  35     It just makes it ignore first argument.
 
  37     def decorator(method):
 
  39         def wrapped_method(self, *args, **kwargs):
 
  40             def function(*fargs, **fkwargs):
 
  41                 return method(self, *fargs, **fkwargs)
 
  42             return function_decorator(function)(*args, **kwargs)
 
  47 def require_login(request):
 
  48     """Return 403 if request is AJAX. Redirect to login page if not."""
 
  50         return HttpResponseForbidden('Not logged in')
 
  52         return HttpResponseRedirect('/uzytkownicy/zaloguj')# next?=request.build_full_path())
 
  55 def placeholdized(form):
 
  56     for field in form.fields.values():
 
  57         field.widget.attrs['placeholder'] = field.label
 
  61 class AjaxableFormView(object):
 
  62     """Subclass this to create an ajaxable view for any form.
 
  64     In the subclass, provide at least form_class.
 
  69     # override to customize form look
 
  70     template = "ajaxable/form.html"
 
  78     full_template = "ajaxable/form_on_page.html"
 
  80     @method_decorator(vary_on_headers('X-Requested-With'))
 
  81     def __call__(self, request, *args, **kwargs):
 
  82         """A view displaying a form, or JSON if request is AJAX."""
 
  83         obj = self.get_object(request, *args, **kwargs)
 
  84         form_args, form_kwargs = self.form_args(request, obj)
 
  86             form_kwargs['prefix'] = self.form_prefix
 
  88         if request.method == "POST":
 
  89             # do I need to be logged in?
 
  90             if self.POST_login and not request.user.is_authenticated():
 
  91                 return require_login(request)
 
  93             form_kwargs['data'] = request.POST
 
  94             form = self.form_class(*form_args, **form_kwargs)
 
  96                 add_args = self.success(form, request)
 
  97                 redirect = request.GET.get('next')
 
  98                 if not request.is_ajax() and redirect:
 
  99                     return HttpResponseRedirect(urlquote_plus(
 
 100                             redirect, safe='/?=&'))
 
 101                 response_data = {'success': True, 
 
 102                     'message': self.success_message, 'redirect': redirect}
 
 104                     response_data.update(add_args)
 
 105             elif request.is_ajax():
 
 106                 # Form was sent with errors. Send them back.
 
 109                     for key, value in form.errors.items():
 
 110                         errors["%s-%s" % (self.form_prefix, key)] = value
 
 113                 response_data = {'success': False, 'errors': errors}
 
 116             if request.is_ajax():
 
 117                 return HttpResponse(LazyEncoder(ensure_ascii=False).encode(response_data))
 
 119             if (self.POST_login and not request.user.is_authenticated()
 
 120                     and not request.is_ajax()):
 
 121                 return require_login(request)
 
 123             form = self.form_class(*form_args, **form_kwargs)
 
 127         if request.is_ajax():
 
 128             template = self.template
 
 130             template = self.full_template
 
 131             cd = self.context_description(request, obj)
 
 134         if self.placeholdize:
 
 135             form = placeholdized(form)
 
 139                 "placeholdize": self.placeholdize,
 
 140                 "submit": self.submit,
 
 141                 "response_data": response_data,
 
 142                 "ajax_template": self.template,
 
 144                 "view_kwargs": kwargs,
 
 146         context.update(self.extra_context(request, obj))
 
 147         return render_to_response(template, context,
 
 148             context_instance=RequestContext(request))
 
 150     def redirect_or_refresh(self, request, path, message=None):
 
 151         """If the form is AJAX, refresh the page. If not, go to `path`.""" 
 
 152         if request.is_ajax():
 
 153             output = "<script>window.location.reload()</script>"
 
 155                 output = "<div class='normal-text'>" + message + "</div>" + output
 
 156             return HttpResponse(output);
 
 158             return HttpResponseRedirect(path)
 
 160     def get_object(self, request, *args, **kwargs):
 
 161         """Override to parse view args and get some associated data."""
 
 164     def form_args(self, request, obj):
 
 165         """Override to parse view args and give additional args to the form."""
 
 168     def extra_context(self, request, obj):
 
 169         """Override to pass something to template."""
 
 172     def context_description(self, request, obj):
 
 173         """Description to appear in standalone form, but not in AJAX form."""
 
 176     def success(self, form, request):
 
 177         """What to do when the form is valid.
 
 179         By default, just save the form.
 
 182         return form.save(request)