PageRenderTime 27ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/askbot/utils/forms.py

https://github.com/benoitl/askbot-devel
Python | 291 lines | 274 code | 5 blank | 12 comment | 17 complexity | cd2dca92b4a44f9f3f085169c60574c9 MD5 | raw file
  1. import re
  2. from django import forms
  3. from django.contrib.auth.models import User
  4. from django.conf import settings
  5. from django.http import Http404
  6. from django.shortcuts import get_object_or_404
  7. from django.utils.translation import ugettext_lazy as _
  8. from django.utils.safestring import mark_safe
  9. from askbot.conf import settings as askbot_settings
  10. from askbot.utils.slug import slugify
  11. from askbot.utils.functions import split_list
  12. from askbot import const
  13. from longerusername import MAX_USERNAME_LENGTH
  14. import logging
  15. import urllib
  16. DEFAULT_NEXT = '/' + getattr(settings, 'ASKBOT_URL')
  17. def clean_next(next, default = None):
  18. if next is None or not next.startswith('/'):
  19. if default:
  20. return default
  21. else:
  22. return DEFAULT_NEXT
  23. if isinstance(next, str):
  24. next = unicode(urllib.unquote(next), 'utf-8', 'replace')
  25. next = next.strip()
  26. logging.debug('next url is %s' % next)
  27. return next
  28. def get_next_url(request, default = None):
  29. return clean_next(request.REQUEST.get('next'), default)
  30. def get_db_object_or_404(params):
  31. """a utility function that returns an object
  32. in return to the model_name and object_id
  33. only specific models are accessible
  34. """
  35. from askbot import models
  36. try:
  37. model_name = params['model_name']
  38. assert(model_name=='Group')
  39. model = models.get_model(model_name)
  40. obj_id = forms.IntegerField().clean(params['object_id'])
  41. return get_object_or_404(model, id=obj_id)
  42. except Exception:
  43. #need catch-all b/c of the nature of the function
  44. raise Http404
  45. def format_errors(error_list):
  46. """If there is only one error - returns a string
  47. corresponding to that error, to remove the <ul> tag.
  48. If there is > 1 error - then convert the error_list into
  49. a string.
  50. """
  51. if len(error_list) == 1:
  52. return unicode(error_list[0])
  53. else:
  54. return unicode(error_list)
  55. class StrippedNonEmptyCharField(forms.CharField):
  56. def clean(self, value):
  57. value = value.strip()
  58. if self.required and value == '':
  59. raise forms.ValidationError(_('this field is required'))
  60. return value
  61. class NextUrlField(forms.CharField):
  62. def __init__(self):
  63. super(
  64. NextUrlField,
  65. self
  66. ).__init__(
  67. max_length = 255,
  68. widget = forms.HiddenInput(),
  69. required = False
  70. )
  71. def clean(self,value):
  72. return clean_next(value)
  73. login_form_widget_attrs = { 'class': 'required login' }
  74. class UserNameField(StrippedNonEmptyCharField):
  75. RESERVED_NAMES = (u'fuck', u'shit', u'ass', u'sex', u'add',
  76. u'edit', u'save', u'delete', u'manage', u'update', 'remove', 'new')
  77. def __init__(
  78. self,
  79. db_model=User,
  80. db_field='username',
  81. must_exist=False,
  82. skip_clean=False,
  83. label=_('Choose a screen name'),
  84. widget_attrs=None,
  85. **kw
  86. ):
  87. self.must_exist = must_exist
  88. self.skip_clean = skip_clean
  89. self.db_model = db_model
  90. self.db_field = db_field
  91. self.user_instance = None
  92. error_messages={
  93. 'required': _('user name is required'),
  94. 'taken': _('sorry, this name is taken, please choose another'),
  95. 'forbidden': _('sorry, this name is not allowed, please choose another'),
  96. 'missing': _('sorry, there is no user with this name'),
  97. 'multiple-taken': _('sorry, we have a serious error - user name is taken by several users'),
  98. 'invalid': _('user name can only consist of letters, empty space and underscore'),
  99. 'meaningless': _('please use at least some alphabetic characters in the user name'),
  100. 'noemail': _('symbol "@" is not allowed')
  101. }
  102. if 'error_messages' in kw:
  103. error_messages.update(kw['error_messages'])
  104. del kw['error_messages']
  105. if widget_attrs:
  106. widget_attrs.update(login_form_widget_attrs)
  107. else:
  108. widget_attrs = login_form_widget_attrs
  109. max_length = MAX_USERNAME_LENGTH()
  110. super(UserNameField,self).__init__(
  111. max_length=max_length,
  112. widget=forms.TextInput(attrs=widget_attrs),
  113. label=label,
  114. error_messages=error_messages,
  115. **kw
  116. )
  117. def clean(self,username):
  118. """ validate username """
  119. if self.skip_clean == True:
  120. logging.debug('username accepted with no validation')
  121. return username
  122. if self.user_instance is None:
  123. pass
  124. elif isinstance(self.user_instance, User):
  125. if username == self.user_instance.username:
  126. logging.debug('username valid')
  127. return username
  128. else:
  129. raise TypeError('user instance must be of type User')
  130. try:
  131. username = super(UserNameField, self).clean(username)
  132. except forms.ValidationError:
  133. raise forms.ValidationError(self.error_messages['required'])
  134. username_re_string = const.USERNAME_REGEX_STRING
  135. #attention: here we check @ symbol in two places: input and the regex
  136. if askbot_settings.ALLOW_EMAIL_ADDRESS_IN_USERNAME is False:
  137. if '@' in username:
  138. raise forms.ValidationError(self.error_messages['noemail'])
  139. username_re_string = username_re_string.replace('@', '')
  140. username_regex = re.compile(username_re_string, re.UNICODE)
  141. if self.required and not username_regex.search(username):
  142. raise forms.ValidationError(self.error_messages['invalid'])
  143. if username in self.RESERVED_NAMES:
  144. raise forms.ValidationError(self.error_messages['forbidden'])
  145. if slugify(username) == '':
  146. raise forms.ValidationError(self.error_messages['meaningless'])
  147. try:
  148. user = self.db_model.objects.get(
  149. **{'%s' % self.db_field : username}
  150. )
  151. if user:
  152. if self.must_exist:
  153. logging.debug('user exists and name accepted b/c here we validate existing user')
  154. return username
  155. else:
  156. raise forms.ValidationError(self.error_messages['taken'])
  157. except self.db_model.DoesNotExist:
  158. if self.must_exist:
  159. logging.debug('user must exist, so raising the error')
  160. raise forms.ValidationError(self.error_messages['missing'])
  161. else:
  162. logging.debug('user name valid!')
  163. return username
  164. except self.db_model.MultipleObjectsReturned:
  165. logging.debug('error - user with this name already exists')
  166. raise forms.ValidationError(self.error_messages['multiple-taken'])
  167. def email_is_allowed(
  168. email, allowed_emails='', allowed_email_domains=''
  169. ):
  170. """True, if email address is pre-approved or matches a allowed
  171. domain"""
  172. if allowed_emails:
  173. email_list = split_list(allowed_emails)
  174. allowed_emails = ' ' + ' '.join(email_list) + ' '
  175. email_match_re = re.compile(r'\s%s\s' % email)
  176. if email_match_re.search(allowed_emails):
  177. return True
  178. if allowed_email_domains:
  179. email_domain = email.split('@')[1]
  180. domain_list = split_list(allowed_email_domains)
  181. domain_match_re = re.compile(r'\s%s\s' % email_domain)
  182. allowed_email_domains = ' ' + ' '.join(domain_list) + ' '
  183. return domain_match_re.search(allowed_email_domains)
  184. return False
  185. class UserEmailField(forms.EmailField):
  186. def __init__(self, skip_clean=False, **kw):
  187. self.skip_clean = skip_clean
  188. hidden = kw.pop('hidden', False)
  189. if hidden is True:
  190. widget_class = forms.HiddenInput
  191. else:
  192. widget_class = forms.TextInput
  193. super(UserEmailField,self).__init__(
  194. widget=widget_class(
  195. attrs=dict(login_form_widget_attrs, maxlength=200)
  196. ),
  197. label=mark_safe(_('Your email <i>(never shared)</i>')),
  198. error_messages={
  199. 'required':_('email address is required'),
  200. 'invalid':_('please enter a valid email address'),
  201. 'taken':_('this email is already used by someone else, please choose another'),
  202. 'unauthorized':_('this email address is not authorized')
  203. },
  204. **kw
  205. )
  206. def clean(self, email):
  207. """ validate if email exist in database
  208. from legacy register
  209. return: raise error if it exist """
  210. email = super(UserEmailField,self).clean(email.strip())
  211. if self.skip_clean:
  212. return email
  213. allowed_domains = askbot_settings.ALLOWED_EMAIL_DOMAINS.strip()
  214. allowed_emails = askbot_settings.ALLOWED_EMAILS.strip()
  215. if allowed_emails or allowed_domains:
  216. if not email_is_allowed(
  217. email,
  218. allowed_emails=allowed_emails,
  219. allowed_email_domains=allowed_domains
  220. ):
  221. raise forms.ValidationError(self.error_messages['unauthorized'])
  222. try:
  223. user = User.objects.get(email__iexact=email)
  224. logging.debug('email taken')
  225. raise forms.ValidationError(self.error_messages['taken'])
  226. except User.DoesNotExist:
  227. logging.debug('email valid')
  228. return email
  229. except User.MultipleObjectsReturned:
  230. logging.critical('email taken many times over')
  231. raise forms.ValidationError(self.error_messages['taken'])
  232. class SetPasswordForm(forms.Form):
  233. password1 = forms.CharField(widget=forms.PasswordInput(attrs=login_form_widget_attrs),
  234. label=_('Password'),
  235. error_messages={'required':_('password is required')},
  236. )
  237. password2 = forms.CharField(widget=forms.PasswordInput(attrs=login_form_widget_attrs),
  238. label=mark_safe(_('Password <i>(please retype)</i>')),
  239. error_messages={'required':_('please, retype your password'),
  240. 'nomatch':_('sorry, entered passwords did not match, please try again')},
  241. )
  242. def __init__(self, data=None, user=None, *args, **kwargs):
  243. super(SetPasswordForm, self).__init__(data, *args, **kwargs)
  244. def clean_password2(self):
  245. """
  246. Validates that the two password inputs match.
  247. """
  248. if 'password1' in self.cleaned_data:
  249. if self.cleaned_data['password1'] == self.cleaned_data['password2']:
  250. self.password = self.cleaned_data['password2']
  251. self.cleaned_data['password'] = self.cleaned_data['password2']
  252. return self.cleaned_data['password2']
  253. else:
  254. del self.cleaned_data['password2']
  255. raise forms.ValidationError(self.fields['password2'].error_messages['nomatch'])
  256. else:
  257. return self.cleaned_data['password2']