選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

abstract_models.py 9.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. import hashlib
  2. import random
  3. from django.db import models
  4. from django.utils.translation import ugettext_lazy as _
  5. from django.template import Template, Context, TemplateDoesNotExist
  6. from django.template.loader import get_template
  7. from django.conf import settings
  8. from django.utils.timezone import now
  9. from django.core.urlresolvers import reverse
  10. from oscar.apps.customer.managers import CommunicationTypeManager
  11. class AbstractEmail(models.Model):
  12. """
  13. This is a record of all emails sent to a customer.
  14. Normally, we only record order-related emails.
  15. """
  16. user = models.ForeignKey('auth.User', related_name='emails', verbose_name=_("User"))
  17. subject = models.TextField(_('Subject'), max_length=255)
  18. body_text = models.TextField(_("Body Text"))
  19. body_html = models.TextField(_("Body HTML"), blank=True, null=True)
  20. date_sent = models.DateTimeField(_("Date Sent"), auto_now_add=True)
  21. class Meta:
  22. abstract = True
  23. verbose_name = _('Email')
  24. verbose_name_plural = _('Emails')
  25. def __unicode__(self):
  26. return _("Email to %(user)s with subject '%(subject)s'") % {
  27. 'user': self.user.username, 'subject': self.subject}
  28. class AbstractCommunicationEventType(models.Model):
  29. # Code used for looking up this event programmatically.
  30. # eg. PASSWORD_RESET
  31. code = models.SlugField(_('Code'), max_length=128)
  32. # Name is the friendly description of an event for use in the admin
  33. name = models.CharField(_('Name'), max_length=255)
  34. # We allow communication types to be categorised
  35. ORDER_RELATED = _('Order related')
  36. USER_RELATED = _('User related')
  37. category = models.CharField(_('Category'), max_length=255, default=ORDER_RELATED)
  38. # Template content for emails
  39. email_subject_template = models.CharField(_('Email Subject Template'), max_length=255, blank=True)
  40. email_body_template = models.TextField(_('Email Body Template'), blank=True, null=True)
  41. email_body_html_template = models.TextField(_('Email Body HTML Temlate'), blank=True, null=True,
  42. help_text=_("HTML template"))
  43. # Template content for SMS messages
  44. sms_template = models.CharField(_('SMS Template'), max_length=170, blank=True, help_text=_("SMS template"))
  45. date_created = models.DateTimeField(_("Date Created"), auto_now_add=True)
  46. date_updated = models.DateTimeField(_("Date Updated"), auto_now=True)
  47. objects = CommunicationTypeManager()
  48. # File templates
  49. email_subject_template_file = 'customer/emails/commtype_%s_subject.txt'
  50. email_body_template_file = 'customer/emails/commtype_%s_body.txt'
  51. email_body_html_template_file = 'customer/emails/commtype_%s_body.html'
  52. sms_template_file = 'customer/sms/commtype_%s_body.txt'
  53. class Meta:
  54. abstract = True
  55. verbose_name = _("Communication Event Type")
  56. verbose_name_plural = _("Communication Event Types")
  57. def get_messages(self, ctx=None):
  58. """
  59. Return a dict of templates with the context merged in
  60. We look first at the field templates but fail over to
  61. a set of file templates that follow a conventional path.
  62. """
  63. code = self.code.lower()
  64. # Build a dict of message name to Template instance
  65. templates = {'subject': 'email_subject_template',
  66. 'body': 'email_body_template',
  67. 'html': 'email_body_html_template',
  68. 'sms': 'sms_template'}
  69. for name, attr_name in templates.items():
  70. field = getattr(self, attr_name, None)
  71. if field:
  72. # Template content is in a model field
  73. templates[name] = Template(field)
  74. else:
  75. # Model field is empty - look for a file template
  76. template_name = getattr(self, "%s_file" % attr_name) % code
  77. try:
  78. templates[name] = get_template(template_name)
  79. except TemplateDoesNotExist:
  80. templates[name] = None
  81. # Pass base URL for serving images within HTML emails
  82. if ctx is None:
  83. ctx = {}
  84. ctx['static_base_url'] = getattr(settings, 'OSCAR_STATIC_BASE_URL', None)
  85. messages = {}
  86. for name, template in templates.items():
  87. messages[name] = template.render(Context(ctx)) if template else ''
  88. # Ensure the email subject doesn't contain any newlines
  89. messages['subject'] = messages['subject'].replace("\n", "")
  90. return messages
  91. def __unicode__(self):
  92. return self.name
  93. def is_order_related(self):
  94. return self.category == self.ORDER_RELATED
  95. def is_user_related(self):
  96. return self.category == self.USER_RELATED
  97. class AbstractNotification(models.Model):
  98. recipient = models.ForeignKey('auth.User', related_name='notifications',
  99. db_index=True)
  100. # Not all notifications will have a sender.
  101. sender = models.ForeignKey('auth.User', null=True)
  102. # HTML is allowed in this field as it can contain links
  103. subject = models.CharField(max_length=255)
  104. body = models.TextField()
  105. # Some projects may want to categorise their notifications. You may want
  106. # to use this field to show a different icons next to the notification.
  107. category = models.CharField(max_length=255, null=True)
  108. INBOX, ARCHIVE = 'Inbox', 'Archive'
  109. choices = (
  110. (INBOX, _(INBOX)),
  111. (ARCHIVE, _(ARCHIVE)))
  112. location = models.CharField(max_length=32, choices=choices,
  113. default=INBOX)
  114. date_sent = models.DateTimeField(auto_now_add=True)
  115. date_read = models.DateTimeField(null=True)
  116. class Meta:
  117. ordering = ('-date_sent',)
  118. abstract = True
  119. def __unicode__(self):
  120. return self.subject
  121. def archive(self):
  122. self.location = self.ARCHIVE
  123. self.save()
  124. archive.alters_data = True
  125. @property
  126. def is_read(self):
  127. return self.date_read is not None
  128. class AbstractProductAlert(models.Model):
  129. """
  130. An alert for when a product comes back in stock
  131. """
  132. product = models.ForeignKey('catalogue.Product')
  133. # A user is only required if the notification is created by a
  134. # registered user, anonymous users will only have an email address
  135. # attached to the notification
  136. user = models.ForeignKey('auth.User', db_index=True, blank=True, null=True,
  137. related_name="alerts", verbose_name=_('User'))
  138. email = models.EmailField(_("Email"), db_index=True, blank=True, null=True)
  139. # This key are used to confirm and cancel alerts for anon users
  140. key = models.CharField(_("Key"), max_length=128, null=True, db_index=True)
  141. # An alert can have two different statuses for authenticated
  142. # users ``ACTIVE`` and ``INACTIVE`` and anonymous users have an
  143. # additional status ``UNCONFIRMED``. For anonymous users a confirmation
  144. # and unsubscription key are generated when an instance is saved for
  145. # the first time and can be used to confirm and unsubscribe the
  146. # notifications.
  147. UNCONFIRMED, ACTIVE, CANCELLED, CLOSED = (
  148. 'Unconfirmed', 'Active', 'Cancelled', 'Closed')
  149. STATUS_CHOICES = (
  150. (UNCONFIRMED, _('Not yet confirmed')),
  151. (ACTIVE, _('Active')),
  152. (CANCELLED, _('Cancelled')),
  153. (CLOSED, _('Closed')),
  154. )
  155. status = models.CharField(_("Status"), max_length=20,
  156. choices=STATUS_CHOICES, default=ACTIVE)
  157. date_created = models.DateTimeField(_("Date created"), auto_now_add=True)
  158. date_confirmed = models.DateTimeField(_("Date confirmed"), blank=True,
  159. null=True)
  160. date_cancelled = models.DateTimeField(_("Date cancelled"), blank=True,
  161. null=True)
  162. date_closed = models.DateTimeField(_("Date closed"), blank=True, null=True)
  163. class Meta:
  164. abstract = True
  165. @property
  166. def is_anonymous(self):
  167. return self.user is None
  168. @property
  169. def can_be_confirmed(self):
  170. return self.status == self.UNCONFIRMED
  171. @property
  172. def can_be_cancelled(self):
  173. return self.status == self.ACTIVE
  174. @property
  175. def is_cancelled(self):
  176. return self.status == self.CANCELLED
  177. @property
  178. def is_active(self):
  179. return self.status == self.ACTIVE
  180. def confirm(self):
  181. self.status = self.ACTIVE
  182. self.date_confirmed = now()
  183. self.save()
  184. confirm.alters_data = True
  185. def cancel(self):
  186. self.status = self.CANCELLED
  187. self.date_cancelled = now()
  188. self.save()
  189. cancel.alters_data = True
  190. def close(self):
  191. self.status = self.CLOSED
  192. self.date_closed = now()
  193. self.save()
  194. close.alters_data = True
  195. def get_email_address(self):
  196. if self.user:
  197. return self.user.email
  198. else:
  199. return self.email
  200. def save(self, *args, **kwargs):
  201. if not self.id and not self.user:
  202. self.key = self.get_random_key()
  203. self.status = self.UNCONFIRMED
  204. # Ensure date fields get updated when saving from modelform (which just
  205. # calls save, and doesn't call the methods cancel(), confirm() etc).
  206. if self.status == self.CANCELLED and self.date_cancelled is None:
  207. self.date_cancelled = now()
  208. if not self.user and self.status == self.ACTIVE and self.date_confirmed is None:
  209. self.date_confirmed = now()
  210. if self.status == self.CLOSED and self.date_closed is None:
  211. self.date_closed = now()
  212. return super(AbstractProductAlert, self).save(*args, **kwargs)
  213. def get_random_key(self):
  214. """
  215. Get a random generated key based on SHA-1 and email address
  216. """
  217. salt = hashlib.sha1(str(random.random())).hexdigest()
  218. return hashlib.sha1(salt + self.email).hexdigest()
  219. def get_confirm_url(self):
  220. return reverse('customer:alerts-confirm', kwargs={'key': self.key})
  221. def get_cancel_url(self):
  222. return reverse('customer:alerts-cancel', kwargs={'key': self.key})