__init__.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. from django import forms
  2. from django.utils.html import conditional_escape, mark_safe
  3. from django.utils.translation import ugettext_lazy as _
  4. from mptt.forms import TreeNodeChoiceField
  5. from misago.forms.layouts import *
  6. from recaptcha.client.captcha import submit as recaptcha_submit
  7. class Form(forms.Form):
  8. """
  9. Custom form implementation extending Django Forms functionality
  10. """
  11. validate_repeats = []
  12. repeats_errors = []
  13. dont_strip = []
  14. error_source = None
  15. def __init__(self, data=None, file=None, request=None, *args, **kwargs):
  16. self.request = request
  17. # Extract request from first argument
  18. if data != None:
  19. super(Form, self).__init__(data, file, *args, **kwargs)
  20. else:
  21. super(Form, self).__init__(*args, **kwargs)
  22. # Kill captcha fields
  23. try:
  24. if self.request.settings['bots_registration'] != 'recaptcha' or self.request.session.get('captcha_passed'):
  25. del self.fields['recaptcha']
  26. except KeyError:
  27. pass
  28. try:
  29. if self.request.settings['bots_registration'] != 'qa' or self.request.session.get('captcha_passed'):
  30. del self.fields['captcha_qa']
  31. else:
  32. # Make sure we have any questions loaded
  33. self.fields['captcha_qa'].label = self.request.settings['qa_test']
  34. self.fields['captcha_qa'].help_text = self.request.settings['qa_test_help']
  35. except KeyError:
  36. pass
  37. # Let forms do mumbo-jumbo with fields removing
  38. self.finalize_form()
  39. def finalize_form(self):
  40. pass
  41. def full_clean(self):
  42. """
  43. Trim inputs and strip newlines
  44. """
  45. self.data = self.data.copy()
  46. for key, field in self.fields.iteritems():
  47. try:
  48. if field.__class__.__name__ in ['ModelChoiceField', 'TreeForeignKey'] and self.data[key]:
  49. self.data[key] = int(self.data[key])
  50. elif field.__class__.__name__ == 'ModelMultipleChoiceField':
  51. self.data.setlist(key, [int(x) for x in self.data.getlist(key, [])])
  52. elif field.__class__.__name__ not in ['DateField', 'DateTimeField']:
  53. if not key in self.dont_strip:
  54. if field.__class__.__name__ in ['MultipleChoiceField', 'TypedMultipleChoiceField']:
  55. self.data.setlist(key, [x.strip() for x in self.data.getlist(key, [])])
  56. else:
  57. self.data[key] = self.data[key].strip()
  58. if field.__class__.__name__ in ['MultipleChoiceField', 'TypedMultipleChoiceField']:
  59. self.data.setlist(key, [x.replace("\r\n", '') for x in self.data.getlist(key, [])])
  60. elif not field.widget.__class__.__name__ in ['Textarea']:
  61. self.data[key] = self.data[key].replace("\r\n", '')
  62. except (KeyError, AttributeError):
  63. pass
  64. super(Form, self).full_clean()
  65. def clean(self):
  66. """
  67. Clean data, do magic checks and stuff
  68. """
  69. cleaned_data = super(Form, self).clean()
  70. self._check_all()
  71. return cleaned_data
  72. def clean_recaptcha(self):
  73. """
  74. Test reCaptcha, scream if it went wrong
  75. """
  76. response = recaptcha_submit(
  77. self.request.POST.get('recaptcha_challenge_field'),
  78. self.request.POST.get('recaptcha_response_field'),
  79. self.request.settings['recaptcha_private'],
  80. self.request.session.get_ip(self.request)
  81. ).is_valid
  82. if not response:
  83. raise forms.ValidationError(_("Entered words are incorrect. Please try again."))
  84. self.request.session['captcha_passed'] = True
  85. return ''
  86. def clean_captcha_qa(self):
  87. """
  88. Test QA Captcha, scream if it went wrong
  89. """
  90. if not unicode(self.cleaned_data['captcha_qa']).lower() in (name.lower() for name in unicode(self.request.settings['qa_test_answers']).splitlines()):
  91. raise forms.ValidationError(_("The answer you entered is incorrect."))
  92. self.request.session['captcha_passed'] = True
  93. return self.cleaned_data['captcha_qa']
  94. def _check_all(self):
  95. # Check repeated fields
  96. self._check_repeats()
  97. # Check CSRF, we dont allow un-csrf'd forms in Misago
  98. self._check_csrf()
  99. # Check if we have any errors from fields, if we do, we will set fancy form-wide error message
  100. self._check_fields_errors()
  101. def _check_repeats(self):
  102. for index, repeat in enumerate(self.validate_repeats):
  103. # Check empty fields
  104. for field in repeat:
  105. if not field in self.data:
  106. try:
  107. if len(repeat) == 2:
  108. self.errors['_'.join(repeat)] = [self.repeats_errors[index]['fill_both']]
  109. else:
  110. self.errors['_'.join(repeat)] = [self.repeats_errors[index]['fill_all']]
  111. except (IndexError, KeyError):
  112. if len(repeat) == 2:
  113. self.errors['_'.join(repeat)] = [_("You have to fill in both fields.")]
  114. else:
  115. self.errors['_'.join(repeat)] = [_("You have to fill in all fields.")]
  116. break
  117. else:
  118. # Check different fields
  119. past_field = self.data[repeat[0]]
  120. for field in repeat:
  121. if self.data[field] != past_field:
  122. try:
  123. self.errors['_'.join(repeat)] = [self.repeats_errors[index]['different']]
  124. except (IndexError, KeyError):
  125. self.errors['_'.join(repeat)] = [_("Entered values differ from each other.")]
  126. break
  127. past_field = self.data[field]
  128. def _check_csrf(self):
  129. if not self.request.csrf.request_secure(self.request):
  130. raise forms.ValidationError(_("Request authorization is invalid. Please resubmit your form."))
  131. def _check_fields_errors(self):
  132. if self.errors:
  133. if self.error_source and self.error_source in self.errors:
  134. field_error, self.errors[self.error_source] = self.errors[self.error_source][0], []
  135. raise forms.ValidationError(field_error)
  136. raise forms.ValidationError(_("Form contains errors."))
  137. def empty_errors(self):
  138. for i in self.errors:
  139. self.errors[i] = []
  140. class YesNoSwitch(forms.CheckboxInput):
  141. """
  142. Custom Yes-No switch as fancier alternative to checkboxes
  143. """
  144. pass
  145. class ForumChoiceField(TreeNodeChoiceField):
  146. """
  147. Custom forum choice field
  148. """
  149. def __init__(self, *args, **kwargs):
  150. kwargs['level_indicator'] = u'- - '
  151. super(ForumChoiceField, self).__init__(*args, **kwargs)
  152. def _get_level_indicator(self, obj):
  153. level = getattr(obj, obj._mptt_meta.level_attr)
  154. return mark_safe(conditional_escape(self.level_indicator) * (level - 1))