You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. import urlparse
  2. from django.shortcuts import get_object_or_404
  3. from django.views.generic import TemplateView, ListView, DetailView, CreateView, UpdateView, DeleteView, FormView
  4. from django.core.urlresolvers import reverse
  5. from django.core.exceptions import ObjectDoesNotExist
  6. from django.http import HttpResponseRedirect, Http404
  7. from django.contrib import messages
  8. from django.utils.translation import ugettext as _
  9. from django.contrib.auth import authenticate, login as auth_login
  10. from django.contrib.auth.forms import PasswordChangeForm
  11. from django.contrib.sites.models import get_current_site
  12. from django.conf import settings
  13. from django.db.models import get_model
  14. from oscar.views.generic import PostActionMixin
  15. from oscar.apps.customer.forms import EmailAuthenticationForm, EmailUserCreationForm, SearchByDateRangeForm
  16. from oscar.core.loading import import_module, get_class, get_profile_class
  17. import_module('customer.utils', ['Dispatcher'], locals())
  18. ProfileForm = get_class('customer.forms', 'ProfileForm')
  19. UserAddressForm = get_class('address.forms', 'UserAddressForm')
  20. order_model = get_model('order', 'Order')
  21. order_line_model = get_model('order', 'Line')
  22. basket_model = get_model('basket', 'Basket')
  23. user_address_model = get_model('address', 'UserAddress')
  24. Email = get_model('customer', 'email')
  25. UserAddress = get_model('address', 'UserAddress')
  26. communicationtype_model = get_model('customer', 'communicationeventtype')
  27. class ProfileUpdateView(FormView):
  28. form_class = ProfileForm
  29. template_name = 'customer/profile-form.html'
  30. def get_form_kwargs(self):
  31. kwargs = super(ProfileUpdateView, self).get_form_kwargs()
  32. kwargs['user'] = self.request.user
  33. return kwargs
  34. def form_valid(self, form):
  35. form.save()
  36. messages.success(self.request, "Profile updated")
  37. return HttpResponseRedirect(self.get_success_url())
  38. def get_success_url(self):
  39. return reverse('customer:summary')
  40. class AccountSummaryView(ListView):
  41. """
  42. Customer order history
  43. """
  44. context_object_name = "orders"
  45. template_name = 'customer/profile.html'
  46. paginate_by = 20
  47. model = order_model
  48. def get_queryset(self):
  49. return self.model._default_manager.filter(user=self.request.user)[0:5]
  50. def get_context_data(self, **kwargs):
  51. ctx = super(AccountSummaryView, self).get_context_data(**kwargs)
  52. ctx['addressbook_size'] = self.request.user.addresses.all().count()
  53. ctx['default_shipping_address'] = self.get_default_shipping_address(self.request.user)
  54. ctx['default_billing_address'] = self.get_default_billing_address(self.request.user)
  55. ctx['emails'] = Email.objects.filter(user=self.request.user)
  56. self.add_profile_fields(ctx)
  57. return ctx
  58. def add_profile_fields(self, ctx):
  59. if not hasattr(settings, 'AUTH_PROFILE_MODULE'):
  60. return
  61. try:
  62. profile = self.request.user.get_profile()
  63. except ObjectDoesNotExist:
  64. profile = get_profile_class()()
  65. field_data = []
  66. for field_name in profile._meta.get_all_field_names():
  67. if field_name in ('user', 'id'):
  68. continue
  69. field = profile._meta.get_field(field_name)
  70. if field.choices:
  71. value = getattr(profile, 'get_%s_display' % field_name)()
  72. else:
  73. value = getattr(profile, field_name)
  74. field_data.append({
  75. 'name': getattr(field, 'verbose_name'),
  76. 'value': value,
  77. })
  78. ctx['profile_fields'] = field_data
  79. def get_default_billing_address(self, user):
  80. return self.get_user_address(user, is_default_for_billing=True)
  81. def get_default_shipping_address(self, user):
  82. return self.get_user_address(user, is_default_for_shipping=True)
  83. def get_user_address(self, user, **filters):
  84. try:
  85. return user.addresses.get(**filters)
  86. except UserAddress.DoesNotExist:
  87. return None
  88. class AccountAuthView(TemplateView):
  89. template_name = 'customer/login_registration.html'
  90. redirect_field_name = 'next'
  91. login_prefix = 'login'
  92. registration_prefix = 'registration'
  93. communication_type_code = 'REGISTRATION'
  94. def get_logged_in_redirect(self):
  95. return reverse('customer:summary')
  96. def get_context_data(self, *args, **kwargs):
  97. context = super(AccountAuthView, self).get_context_data(*args, **kwargs)
  98. redirect_to = self.request.REQUEST.get(self.redirect_field_name, '')
  99. context[self.redirect_field_name] = redirect_to
  100. context['login_form'] = EmailAuthenticationForm(prefix=self.login_prefix)
  101. context['registration_form'] = EmailUserCreationForm(prefix=self.registration_prefix)
  102. return context
  103. def check_redirect(self, context):
  104. redirect_to = context.get(self.redirect_field_name)
  105. netloc = urlparse.urlparse(redirect_to)[1]
  106. if not redirect_to:
  107. redirect_to = settings.LOGIN_REDIRECT_URL
  108. elif netloc and netloc != self.request.get_host():
  109. redirect_to = settings.LOGIN_REDIRECT_URL
  110. return redirect_to
  111. def send_registration_email(self, user):
  112. code = self.communication_type_code
  113. ctx = {'user': user,
  114. 'site': get_current_site(self.request)}
  115. try:
  116. event_type = communicationtype_model.objects.get(code=code)
  117. except communicationtype_model.DoesNotExist:
  118. # No event in database, attempt to find templates for this type
  119. messages = communicationtype_model.objects.get_and_render(code, ctx)
  120. else:
  121. # Create order event
  122. messages = event_type.get_messages(ctx)
  123. if messages and messages['body']:
  124. dispatcher = Dispatcher()
  125. dispatcher.dispatch_user_messages(user, messages)
  126. def get(self, request, *args, **kwargs):
  127. context = self.get_context_data(*args, **kwargs)
  128. if request.user.is_authenticated():
  129. return HttpResponseRedirect(self.get_logged_in_redirect())
  130. self.request.session.set_test_cookie()
  131. return self.render_to_response(context)
  132. def post(self, request, *args, **kwargs):
  133. context = self.get_context_data(*args, **kwargs)
  134. redirect_to = self.check_redirect(context)
  135. if u'login_submit' in self.request.POST:
  136. login_form = EmailAuthenticationForm(prefix=self.login_prefix, data=request.POST)
  137. if login_form.is_valid():
  138. auth_login(request, login_form.get_user())
  139. if request.session.test_cookie_worked():
  140. request.session.delete_test_cookie()
  141. return HttpResponseRedirect(redirect_to)
  142. context['login_form'] = login_form
  143. if u'registration_submit' in self.request.POST:
  144. registration_form = EmailUserCreationForm(prefix=self.registration_prefix, data=request.POST)
  145. context['registration_form'] = registration_form
  146. if registration_form.is_valid():
  147. user = registration_form.save()
  148. if getattr(settings, 'OSCAR_SEND_REGISTRATION_EMAIL', True):
  149. self.send_registration_email(user)
  150. user = authenticate(username=user.email, password=registration_form.cleaned_data['password1'])
  151. auth_login(self.request, user)
  152. if self.request.session.test_cookie_worked():
  153. self.request.session.delete_test_cookie()
  154. return HttpResponseRedirect(redirect_to)
  155. self.request.session.set_test_cookie()
  156. return self.render_to_response(context)
  157. class EmailHistoryView(ListView):
  158. """Customer email history"""
  159. context_object_name = "emails"
  160. template_name = 'customer/email-history.html'
  161. paginate_by = 20
  162. def get_queryset(self):
  163. """Return a customer's orders"""
  164. return Email._default_manager.filter(user=self.request.user)
  165. class EmailDetailView(DetailView):
  166. """Customer order details"""
  167. template_name = "customer/email.html"
  168. context_object_name = 'email'
  169. def get_object(self):
  170. """Return an order object or 404"""
  171. return get_object_or_404(Email, user=self.request.user, id=self.kwargs['email_id'])
  172. class OrderHistoryView(ListView):
  173. """
  174. Customer order history
  175. """
  176. context_object_name = "orders"
  177. template_name = 'customer/order-history.html'
  178. paginate_by = 20
  179. model = order_model
  180. form_class = SearchByDateRangeForm
  181. def get(self, request, *args, **kwargs):
  182. if 'date_from' in request.GET:
  183. self.form = SearchByDateRangeForm(self.request.GET)
  184. if not self.form.is_valid():
  185. ctx = self.get_context_data()
  186. return self.render_to_response(ctx)
  187. else:
  188. self.form = SearchByDateRangeForm()
  189. return super(OrderHistoryView, self).get(request, *args, **kwargs)
  190. def get_queryset(self):
  191. qs = self.model._default_manager.filter(user=self.request.user)
  192. if self.form.is_bound:
  193. qs = qs.filter(**self.form.get_filters())
  194. return qs
  195. def get_context_data(self, *args, **kwargs):
  196. ctx = super(OrderHistoryView, self).get_context_data(*args, **kwargs)
  197. ctx['form'] = self.form
  198. return ctx
  199. class OrderDetailView(DetailView, PostActionMixin):
  200. """Customer order details"""
  201. model = order_model
  202. def get_template_names(self):
  203. return ["customer/order.html"]
  204. def get_object(self):
  205. return get_object_or_404(self.model, user=self.request.user, number=self.kwargs['order_number'])
  206. def do_reorder(self, order):
  207. self.response = HttpResponseRedirect(reverse('basket:summary'))
  208. basket = self.request.basket
  209. # Convert line attributes into basket options
  210. for line in order.lines.all():
  211. if not line.product:
  212. messages.warning(self.request, "'%s' unavailable for re-order" % line.title)
  213. continue
  214. options = []
  215. for attribute in line.attributes.all():
  216. if attribute.option:
  217. options.append({'option': attribute.option, 'value': attribute.value})
  218. basket.add_product(line.product, 1, options)
  219. messages.info(self.request, "Order %s reordered" % order.number)
  220. class OrderLineView(DetailView, PostActionMixin):
  221. """Customer order line"""
  222. def get_object(self):
  223. """Return an order object or 404"""
  224. order = get_object_or_404(order_model, user=self.request.user, number=self.kwargs['order_number'])
  225. return order.lines.get(id=self.kwargs['line_id'])
  226. def do_reorder(self, line):
  227. if not line.product:
  228. messages.info(self.request, _("This product is no longer available for re-order"))
  229. return
  230. # We need to pass response to the get_or_create... method
  231. # as a new basket might need to be created
  232. self.response = HttpResponseRedirect(reverse('basket:summary'))
  233. basket = self.request.basket
  234. # Convert line attributes into basket options
  235. options = []
  236. for attribute in line.attributes.all():
  237. if attribute.option:
  238. options.append({'option': attribute.option, 'value': attribute.value})
  239. basket.add_product(line.product, 1, options)
  240. messages.info(self.request, "Line reordered")
  241. class AddressListView(ListView):
  242. """Customer address book"""
  243. context_object_name = "addresses"
  244. template_name = 'customer/address-book.html'
  245. paginate_by = 40
  246. def get_queryset(self):
  247. """Return a customer's addresses"""
  248. return user_address_model._default_manager.filter(user=self.request.user)
  249. class AddressCreateView(CreateView):
  250. form_class = UserAddressForm
  251. mode = user_address_model
  252. template_name = 'customer/address-form.html'
  253. def get_context_data(self, **kwargs):
  254. ctx = super(AddressCreateView, self).get_context_data(**kwargs)
  255. ctx['title'] = _('Add a new address')
  256. return ctx
  257. def form_valid(self, form):
  258. self.object = form.save(commit=False)
  259. self.object.user = self.request.user
  260. self.object.save()
  261. return HttpResponseRedirect(self.get_success_url())
  262. def get_success_url(self):
  263. messages.success(self.request, _("Address saved"))
  264. return reverse('customer:address-list')
  265. class AddressUpdateView(UpdateView):
  266. form_class = UserAddressForm
  267. model = user_address_model
  268. template_name = 'customer/address-form.html'
  269. def get_context_data(self, **kwargs):
  270. ctx = super(AddressUpdateView, self).get_context_data(**kwargs)
  271. ctx['title'] = _('Edit address')
  272. return ctx
  273. def get_queryset(self):
  274. return user_address_model._default_manager.filter(user=self.request.user)
  275. def get_success_url(self):
  276. messages.success(self.request, _("Address saved"))
  277. return reverse('customer:address-detail', kwargs={'pk': self.get_object().pk })
  278. class AddressDeleteView(DeleteView):
  279. model = user_address_model
  280. def get_queryset(self):
  281. """Return a customer's addresses"""
  282. return user_address_model._default_manager.filter(user=self.request.user)
  283. def get_success_url(self):
  284. return reverse('customer:address-list')
  285. def get_template_names(self):
  286. return ["customer/address-delete.html"]
  287. class AnonymousOrderDetailView(DetailView):
  288. model = order_model
  289. def get_template_names(self):
  290. return ["customer/anon-order.html"]
  291. def get_object(self):
  292. order = get_object_or_404(self.model, user=None, number=self.kwargs['order_number'])
  293. if self.kwargs['hash'] != order.verification_hash():
  294. raise Http404()
  295. return order
  296. class ChangePasswordView(FormView):
  297. form_class = PasswordChangeForm
  298. template_name = 'customer/change-password.html'
  299. def get_form_kwargs(self):
  300. kwargs = super(ChangePasswordView, self).get_form_kwargs()
  301. kwargs['user'] = self.request.user
  302. return kwargs
  303. def form_valid(self, form):
  304. form.save()
  305. messages.success(self.request, "Password updated")
  306. return HttpResponseRedirect(self.get_success_url())
  307. def get_success_url(self):
  308. return reverse('customer:summary')