__init__.py 6.8 KB

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