import datetime from django.core.mail import EmailMessage from django.conf import settings from django.contrib.sites.models import Site, RequestSite from django.core import signing from django.core.mail import send_mail from django.template import Context from django.core.urlresolvers import reverse, reverse_lazy from django.shortcuts import get_object_or_404, redirect from django.http import Http404 from django.template import loader from django.utils import timezone from django.views import generic from django.template.loader import get_template from email.MIMEImage import MIMEImage from .forms import PasswordRecoveryForm, PasswordResetForm from .utils import get_user_model, get_username from .signals import user_recovers_password class SaltMixin(object): salt = 'password_recovery' url_salt = 'password_recovery_url' def loads_with_timestamp(value, salt): """Returns the unsigned value along with its timestamp, the time when it got dumped.""" try: signing.loads(value, salt=salt, max_age=-1) except signing.SignatureExpired as e: age = float(str(e).split('Signature age ')[1].split(' >')[0]) timestamp = timezone.now() - datetime.timedelta(seconds=age) return timestamp, signing.loads(value, salt=salt) class RecoverDone(SaltMixin, generic.TemplateView): template_name = "password_reset/reset_sent.html" def get_context_data(self, **kwargs): ctx = super(RecoverDone, self).get_context_data(**kwargs) try: ctx['timestamp'], ctx['email'] = loads_with_timestamp( self.kwargs['signature'], salt=self.url_salt, ) except signing.BadSignature: raise Http404 return ctx recover_done = RecoverDone.as_view() class Recover(SaltMixin, generic.FormView): case_sensitive = True form_class = PasswordRecoveryForm template_name = 'password_reset/recovery_form.html' success_url_name = 'password_reset_sent' email_template_name = 'password_reset/recovery_email_expo.html' email_subject_template_name = 'password_reset/recovery_email_subject.txt' search_fields = ['username', 'email'] def get_success_url(self): return reverse(self.success_url_name, args=[self.mail_signature]) def get_context_data(self, **kwargs): kwargs['url'] = self.request.get_full_path() return super(Recover, self).get_context_data(**kwargs) def get_form_kwargs(self): kwargs = super(Recover, self).get_form_kwargs() kwargs.update({ 'case_sensitive': self.case_sensitive, 'search_fields': self.search_fields, }) return kwargs def get_site(self): if Site._meta.installed: return Site.objects.get_current() else: return RequestSite(self.request) def send_notification(self): context = { 'site': self.get_site(), 'user': self.user, 'username': get_username(self.user), 'token': signing.dumps(self.user.pk, salt=self.salt), 'secure': self.request.is_secure(), } #body = loader.render_to_string(self.email_template_name, # context).strip() message = get_template(self.email_template_name).render(Context(context)) subject = loader.render_to_string(self.email_subject_template_name, context).strip() msg = EmailMessage(subject, message, settings.DEFAULT_FROM_EMAIL, [self.user.email]) msg.content_subtype = "html" images =(('/img/logo_reg.png', 'logo'), ('/img/soc-medias/sm-icon-rss.png', 'rss'), ('/img/soc-medias/sm-icon-fb.png', 'fb'), ('/img/soc-medias/sm-icon-lin.png', 'linkedin'), ('/img/soc-medias/sm-icon-vk.png', 'vk'), ('/img/soc-medias/sm-icon-twit.png', 'twit'), ('/img/mail-logo-2.jpg','logo2')) for img in images: fp = open(settings.STATIC_ROOT+img[0], 'rb') msg_img = MIMEImage(fp.read()) fp.close() msg_img.add_header('Content-ID', '<'+img[1]+'>') msg.attach(msg_img) msg.send() #send_mail(subject, body, settings.DEFAULT_FROM_EMAIL, # [self.user.email]) def form_valid(self, form): self.user = form.cleaned_data['user'] self.send_notification() if ( len(self.search_fields) == 1 and self.search_fields[0] == 'username' ): # if we only search by username, don't disclose the user email # since it may now be public information. email = self.user.username else: email = self.user.email self.mail_signature = signing.dumps(email, salt=self.url_salt) return super(Recover, self).form_valid(form) recover = Recover.as_view() class Reset(SaltMixin, generic.FormView): form_class = PasswordResetForm token_expires = 3600 * 48 # Two days template_name = 'password_reset/reset.html' success_url = reverse_lazy('password_reset_done') def dispatch(self, request, *args, **kwargs): self.request = request self.args = args self.kwargs = kwargs try: pk = signing.loads(kwargs['token'], max_age=self.token_expires, salt=self.salt) except signing.BadSignature: return self.invalid() self.user = get_object_or_404(get_user_model(), pk=pk) return super(Reset, self).dispatch(request, *args, **kwargs) def invalid(self): return self.render_to_response(self.get_context_data(invalid=True)) def get_form_kwargs(self): kwargs = super(Reset, self).get_form_kwargs() kwargs['user'] = self.user return kwargs def get_context_data(self, **kwargs): ctx = super(Reset, self).get_context_data(**kwargs) if 'invalid' not in ctx: ctx.update({ 'username': get_username(self.user), 'token': self.kwargs['token'], }) return ctx def form_valid(self, form): form.save() user_recovers_password.send( sender=get_user_model(), user=form.user, request=self.request ) return redirect(self.get_success_url()) reset = Reset.as_view() class ResetDone(generic.TemplateView): template_name = 'password_reset/recovery_done.html' reset_done = ResetDone.as_view()