Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. import string
  2. import random
  3. from django import forms
  4. from django.conf import settings
  5. from django.contrib.auth import forms as auth_forms
  6. from django.contrib.auth.forms import AuthenticationForm
  7. from django.contrib.sites.models import get_current_site
  8. from django.core.exceptions import ValidationError
  9. from django.utils.http import is_safe_url
  10. from django.utils.translation import ugettext_lazy as _
  11. from django.utils.translation import pgettext_lazy
  12. from oscar.core.loading import get_profile_class, get_class, get_model
  13. from oscar.core.compat import get_user_model, existing_user_fields
  14. from oscar.apps.customer.utils import get_password_reset_url, normalise_email
  15. from oscar.core.validators import password_validators
  16. Dispatcher = get_class('customer.utils', 'Dispatcher')
  17. CommunicationEventType = get_model('customer', 'communicationeventtype')
  18. ProductAlert = get_model('customer', 'ProductAlert')
  19. User = get_user_model()
  20. def generate_username():
  21. # Python 3 uses ascii_letters. If not available, fallback to letters
  22. try:
  23. letters = string.ascii_letters
  24. except AttributeError:
  25. letters = string.letters
  26. uname = ''.join([random.choice(letters + string.digits + '_')
  27. for i in range(30)])
  28. try:
  29. User.objects.get(username=uname)
  30. return generate_username()
  31. except User.DoesNotExist:
  32. return uname
  33. class PasswordResetForm(auth_forms.PasswordResetForm):
  34. """
  35. This form takes the same structure as its parent from django.contrib.auth
  36. """
  37. communication_type_code = "PASSWORD_RESET"
  38. def save(self, domain_override=None, use_https=False, request=None,
  39. **kwargs):
  40. """
  41. Generates a one-use only link for resetting password and sends to the
  42. user.
  43. """
  44. site = get_current_site(request)
  45. if domain_override is not None:
  46. site.domain = site.name = domain_override
  47. email = self.cleaned_data['email']
  48. active_users = User._default_manager.filter(
  49. email__iexact=email, is_active=True)
  50. for user in active_users:
  51. reset_url = self.get_reset_url(site, request, user, use_https)
  52. ctx = {
  53. 'user': user,
  54. 'site': site,
  55. 'reset_url': reset_url}
  56. messages = CommunicationEventType.objects.get_and_render(
  57. code=self.communication_type_code, context=ctx)
  58. Dispatcher().dispatch_user_messages(user, messages)
  59. def get_reset_url(self, site, request, user, use_https):
  60. # the request argument isn't used currently, but implementors might
  61. # need it to determine the correct subdomain
  62. reset_url = "%s://%s%s" % (
  63. 'https' if use_https else 'http',
  64. site.domain,
  65. get_password_reset_url(user))
  66. return reset_url
  67. class SetPasswordForm(auth_forms.SetPasswordForm):
  68. def __init__(self, *args, **kwargs):
  69. super(SetPasswordForm, self).__init__(*args, **kwargs)
  70. # Enforce password validations for the new password
  71. self.fields['new_password1'].validators += password_validators
  72. class PasswordChangeForm(auth_forms.PasswordChangeForm):
  73. def __init__(self, *args, **kwargs):
  74. super(PasswordChangeForm, self).__init__(*args, **kwargs)
  75. # Enforce password validations for the new password
  76. self.fields['new_password1'].validators += password_validators
  77. class EmailAuthenticationForm(AuthenticationForm):
  78. """
  79. Extends the standard django AuthenticationForm, to support 75 character
  80. usernames. 75 character usernames are needed to support the EmailOrUsername
  81. auth backend.
  82. """
  83. username = forms.EmailField(label=_('Email address'))
  84. redirect_url = forms.CharField(
  85. widget=forms.HiddenInput, required=False)
  86. def __init__(self, host, *args, **kwargs):
  87. self.host = host
  88. super(EmailAuthenticationForm, self).__init__(*args, **kwargs)
  89. def clean_redirect_url(self):
  90. url = self.cleaned_data['redirect_url'].strip()
  91. if url and is_safe_url(url):
  92. return url
  93. class ConfirmPasswordForm(forms.Form):
  94. """
  95. Extends the standard django AuthenticationForm, to support 75 character
  96. usernames. 75 character usernames are needed to support the EmailOrUsername
  97. auth backend.
  98. """
  99. password = forms.CharField(label=_("Password"), widget=forms.PasswordInput)
  100. def __init__(self, user, *args, **kwargs):
  101. super(ConfirmPasswordForm, self).__init__(*args, **kwargs)
  102. self.user = user
  103. def clean_password(self):
  104. password = self.cleaned_data['password']
  105. if not self.user.check_password(password):
  106. raise forms.ValidationError(
  107. _("The entered password is not valid!"))
  108. return password
  109. class EmailUserCreationForm(forms.ModelForm):
  110. email = forms.EmailField(label=_('Email address'))
  111. password1 = forms.CharField(
  112. label=_('Password'), widget=forms.PasswordInput,
  113. validators=password_validators)
  114. password2 = forms.CharField(
  115. label=_('Confirm password'), widget=forms.PasswordInput)
  116. redirect_url = forms.CharField(
  117. widget=forms.HiddenInput, required=False)
  118. class Meta:
  119. model = User
  120. fields = ('email',)
  121. def __init__(self, host=None, *args, **kwargs):
  122. self.host = host
  123. super(EmailUserCreationForm, self).__init__(*args, **kwargs)
  124. def clean_email(self):
  125. """
  126. Checks for existing users with the supplied email address.
  127. """
  128. email = normalise_email(self.cleaned_data['email'])
  129. if User._default_manager.filter(email__iexact=email).exists():
  130. raise forms.ValidationError(
  131. _("A user with that email address already exists"))
  132. return email
  133. def clean_password2(self):
  134. password1 = self.cleaned_data.get('password1', '')
  135. password2 = self.cleaned_data.get('password2', '')
  136. if password1 != password2:
  137. raise forms.ValidationError(
  138. _("The two password fields didn't match."))
  139. return password2
  140. def clean_redirect_url(self):
  141. url = self.cleaned_data['redirect_url'].strip()
  142. if url and is_safe_url(url):
  143. return url
  144. return settings.LOGIN_REDIRECT_URL
  145. def save(self, commit=True):
  146. user = super(EmailUserCreationForm, self).save(commit=False)
  147. user.set_password(self.cleaned_data['password1'])
  148. if 'username' in [f.name for f in User._meta.fields]:
  149. user.username = generate_username()
  150. if commit:
  151. user.save()
  152. return user
  153. class OrderSearchForm(forms.Form):
  154. date_from = forms.DateField(
  155. required=False, label=pgettext_lazy("start date", "From"))
  156. date_to = forms.DateField(
  157. required=False, label=pgettext_lazy("end date", "To"))
  158. order_number = forms.CharField(required=False, label=_("Order number"))
  159. def clean(self):
  160. if self.is_valid() and not any([self.cleaned_data['date_from'],
  161. self.cleaned_data['date_to'],
  162. self.cleaned_data['order_number']]):
  163. raise forms.ValidationError(_("At least one field is required."))
  164. return super(OrderSearchForm, self).clean()
  165. def description(self):
  166. """
  167. Uses the form's data to build a useful description of what orders
  168. are listed.
  169. """
  170. if not self.is_bound or not self.is_valid():
  171. return _('All orders')
  172. else:
  173. date_from = self.cleaned_data['date_from']
  174. date_to = self.cleaned_data['date_to']
  175. order_number = self.cleaned_data['order_number']
  176. return self._orders_description(date_from, date_to, order_number)
  177. def _orders_description(self, date_from, date_to, order_number):
  178. if date_from and date_to:
  179. if order_number:
  180. desc = _('Orders placed between %(date_from)s and '
  181. '%(date_to)s and order number containing '
  182. '%(order_number)s')
  183. else:
  184. desc = _('Orders placed between %(date_from)s and '
  185. '%(date_to)s')
  186. elif date_from:
  187. if order_number:
  188. desc = _('Orders placed since %(date_from)s and '
  189. 'order number containing %(order_number)s')
  190. else:
  191. desc = _('Orders placed since %(date_from)s')
  192. elif date_to:
  193. if order_number:
  194. desc = _('Orders placed until %(date_to)s and '
  195. 'order number containing %(order_number)s')
  196. else:
  197. desc = _('Orders placed until %(date_to)s')
  198. elif order_number:
  199. desc = _('Orders with order number containing %(order_number)s')
  200. else:
  201. return None
  202. params = {
  203. 'date_from': date_from,
  204. 'date_to': date_to,
  205. 'order_number': order_number,
  206. }
  207. return desc % params
  208. def get_filters(self):
  209. date_from = self.cleaned_data['date_from']
  210. date_to = self.cleaned_data['date_to']
  211. order_number = self.cleaned_data['order_number']
  212. kwargs = {}
  213. if date_from and date_to:
  214. kwargs['date_placed__range'] = [date_from, date_to]
  215. elif date_from and not date_to:
  216. kwargs['date_placed__gt'] = date_from
  217. elif not date_from and date_to:
  218. kwargs['date_placed__lt'] = date_to
  219. if order_number:
  220. kwargs['number__contains'] = order_number
  221. return kwargs
  222. class UserForm(forms.ModelForm):
  223. def __init__(self, user, *args, **kwargs):
  224. self.user = user
  225. kwargs['instance'] = user
  226. super(UserForm, self).__init__(*args, **kwargs)
  227. if 'email' in self.fields:
  228. self.fields['email'].required = True
  229. def clean_email(self):
  230. """
  231. Make sure that the email address is aways unique as it is
  232. used instead of the username. This is necessary because the
  233. unique-ness of email addresses is *not* enforced on the model
  234. level in ``django.contrib.auth.models.User``.
  235. """
  236. email = normalise_email(self.cleaned_data['email'])
  237. if User._default_manager.filter(
  238. email__iexact=email).exclude(id=self.user.id).exists():
  239. raise ValidationError(
  240. _("A user with this email address already exists"))
  241. # Save the email unaltered
  242. return email
  243. class Meta:
  244. model = User
  245. fields = existing_user_fields(['first_name', 'last_name', 'email'])
  246. Profile = get_profile_class()
  247. if Profile:
  248. class UserAndProfileForm(forms.ModelForm):
  249. def __init__(self, user, *args, **kwargs):
  250. try:
  251. instance = Profile.objects.get(user=user)
  252. except Profile.DoesNotExist:
  253. # User has no profile, try a blank one
  254. instance = Profile(user=user)
  255. kwargs['instance'] = instance
  256. super(UserAndProfileForm, self).__init__(*args, **kwargs)
  257. # Get profile field names to help with ordering later
  258. profile_field_names = list(self.fields.keys())
  259. # Get user field names (we look for core user fields first)
  260. core_field_names = set([f.name for f in User._meta.fields])
  261. user_field_names = ['email']
  262. for field_name in ('first_name', 'last_name'):
  263. if field_name in core_field_names:
  264. user_field_names.append(field_name)
  265. user_field_names.extend(User._meta.additional_fields)
  266. # Store user fields so we know what to save later
  267. self.user_field_names = user_field_names
  268. # Add additional user form fields
  269. additional_fields = forms.fields_for_model(
  270. User, fields=user_field_names)
  271. self.fields.update(additional_fields)
  272. # Ensure email is required and initialised correctly
  273. self.fields['email'].required = True
  274. # Set initial values
  275. for field_name in user_field_names:
  276. self.fields[field_name].initial = getattr(user, field_name)
  277. # Ensure order of fields is email, user fields then profile fields
  278. self.fields.keyOrder = user_field_names + profile_field_names
  279. class Meta:
  280. model = Profile
  281. exclude = ('user',)
  282. def clean_email(self):
  283. email = normalise_email(self.cleaned_data['email'])
  284. users_with_email = User._default_manager.filter(
  285. email__iexact=email).exclude(id=self.instance.user.id)
  286. if users_with_email.exists():
  287. raise ValidationError(
  288. _("A user with this email address already exists"))
  289. return email
  290. def save(self, *args, **kwargs):
  291. user = self.instance.user
  292. # Save user also
  293. for field_name in self.user_field_names:
  294. setattr(user, field_name, self.cleaned_data[field_name])
  295. user.save()
  296. return super(ProfileForm, self).save(*args, **kwargs)
  297. ProfileForm = UserAndProfileForm
  298. else:
  299. ProfileForm = UserForm
  300. class ProductAlertForm(forms.ModelForm):
  301. email = forms.EmailField(required=True, label=_(u'Send notification to'),
  302. widget=forms.TextInput(attrs={
  303. 'placeholder': _('Enter your email')
  304. }))
  305. def __init__(self, user, product, *args, **kwargs):
  306. self.user = user
  307. self.product = product
  308. super(ProductAlertForm, self).__init__(*args, **kwargs)
  309. # Only show email field to unauthenticated users
  310. if user and user.is_authenticated():
  311. self.fields['email'].widget = forms.HiddenInput()
  312. self.fields['email'].required = False
  313. def save(self, commit=True):
  314. alert = super(ProductAlertForm, self).save(commit=False)
  315. if self.user.is_authenticated():
  316. alert.user = self.user
  317. alert.product = self.product
  318. if commit:
  319. alert.save()
  320. return alert
  321. def clean(self):
  322. cleaned_data = self.cleaned_data
  323. email = cleaned_data.get('email')
  324. if email:
  325. try:
  326. ProductAlert.objects.get(
  327. product=self.product, email__iexact=email,
  328. status=ProductAlert.ACTIVE)
  329. except ProductAlert.DoesNotExist:
  330. pass
  331. else:
  332. raise forms.ValidationError(_(
  333. "There is already an active stock alert for %s") % email)
  334. elif self.user.is_authenticated():
  335. try:
  336. ProductAlert.objects.get(product=self.product,
  337. user=self.user,
  338. status=ProductAlert.ACTIVE)
  339. except ProductAlert.DoesNotExist:
  340. pass
  341. else:
  342. raise forms.ValidationError(_(
  343. "You already have an active alert for this product"))
  344. return cleaned_data
  345. class Meta:
  346. model = ProductAlert
  347. exclude = ('user', 'key',
  348. 'status', 'date_confirmed', 'date_cancelled', 'date_closed',
  349. 'product')