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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. import zlib
  2. from django.conf import settings
  3. from django.db.models import get_model
  4. from oscar.core.loading import get_class
  5. Applicator = get_class('offer.utils', 'Applicator')
  6. Basket = get_model('basket', 'basket')
  7. class BasketMiddleware(object):
  8. def process_request(self, request):
  9. request.cookies_to_delete = []
  10. basket = self.get_basket(request)
  11. self.apply_offers_to_basket(request, basket)
  12. request.basket = basket
  13. def get_basket(self, request):
  14. manager = Basket.open
  15. cookie_basket = self.get_cookie_basket(
  16. settings.OSCAR_BASKET_COOKIE_OPEN, request, manager)
  17. if hasattr(request, 'user') and request.user.is_authenticated():
  18. # Signed-in user: if they have a cookie basket too, it means
  19. # that they have just signed in and we need to merge their cookie
  20. # basket into their user basket, then delete the cookie
  21. try:
  22. basket, _ = manager.get_or_create(owner=request.user)
  23. except Basket.MultipleObjectsReturned:
  24. # Not sure quite how we end up here with multiple baskets
  25. # We merge them and create a fresh one
  26. old_baskets = list(manager.filter(owner=request.user))
  27. basket = old_baskets[0]
  28. for other_basket in old_baskets[1:]:
  29. self.merge_baskets(basket, other_basket)
  30. # Assign user onto basket to prevent further SQL queries when
  31. # basket.owner is accessed.
  32. basket.owner = request.user
  33. if cookie_basket:
  34. self.merge_baskets(basket, cookie_basket)
  35. request.cookies_to_delete.append(
  36. settings.OSCAR_BASKET_COOKIE_OPEN)
  37. elif cookie_basket:
  38. # Anonymous user with a basket tied to the cookie
  39. basket = cookie_basket
  40. else:
  41. # Anonymous user with no basket - we don't save the basket until
  42. # we need to.
  43. basket = Basket()
  44. return basket
  45. def merge_baskets(self, master, slave):
  46. """
  47. Merge one basket into another.
  48. This is its own method to allow it to be overridden
  49. """
  50. master.merge(slave, add_quantities=False)
  51. def process_response(self, request, response):
  52. # Delete any surplus cookies
  53. if hasattr(request, 'cookies_to_delete'):
  54. for cookie_key in request.cookies_to_delete:
  55. response.delete_cookie(cookie_key)
  56. # If a basket has had products added to it, but the user is anonymous
  57. # then we need to assign it to a cookie
  58. if (hasattr(request, 'basket') and request.basket.id > 0
  59. and not request.user.is_authenticated()
  60. and settings.OSCAR_BASKET_COOKIE_OPEN not in request.COOKIES):
  61. cookie = "%s_%s" % (
  62. request.basket.id, self.get_basket_hash(request.basket.id))
  63. response.set_cookie(settings.OSCAR_BASKET_COOKIE_OPEN,
  64. cookie,
  65. max_age=settings.OSCAR_BASKET_COOKIE_LIFETIME,
  66. httponly=True)
  67. return response
  68. def process_template_response(self, request, response):
  69. if hasattr(response, 'context_data'):
  70. if response.context_data is None:
  71. response.context_data = {}
  72. if 'basket' not in response.context_data:
  73. response.context_data['basket'] = request.basket
  74. else:
  75. # Occasionally, a view will want to pass an alternative basket
  76. # to be rendered. This can happen as part of checkout
  77. # processes where the submitted basket is frozen when the
  78. # customer is redirected to another site (eg PayPal). When the
  79. # customer returns and we want to show the order preview
  80. # template, we need to ensure that the frozen basket gets
  81. # rendered (not request.basket). We still keep a reference to
  82. # the request basket (just in case).
  83. response.context_data['request_basket'] = request.basket
  84. return response
  85. def get_cookie_basket(self, cookie_key, request, manager):
  86. """
  87. Looks for a basket which is referenced by a cookie.
  88. If a cookie key is found with no matching basket, then we add
  89. it to the list to be deleted.
  90. """
  91. basket = None
  92. if cookie_key in request.COOKIES:
  93. parts = request.COOKIES[cookie_key].split("_")
  94. if len(parts) != 2:
  95. return basket
  96. basket_id, basket_hash = parts
  97. if basket_hash == self.get_basket_hash(basket_id):
  98. try:
  99. basket = Basket.objects.get(pk=basket_id, owner=None,
  100. status=Basket.OPEN)
  101. except Basket.DoesNotExist:
  102. request.cookies_to_delete.append(cookie_key)
  103. else:
  104. request.cookies_to_delete.append(cookie_key)
  105. return basket
  106. def apply_offers_to_basket(self, request, basket):
  107. if not basket.is_empty:
  108. Applicator().apply(request, basket)
  109. def get_basket_hash(self, basket_id):
  110. return str(zlib.crc32(str(basket_id) + settings.SECRET_KEY))