Beispiel #1
0
def message_response(request):
    """AJAX view to send an email to the user of a response"""
    form = CrowdsourceMessageResponseForm(request.POST)
    if form.is_valid():
        response = form.cleaned_data['response']
        if not request.user.has_perm('crowdsource.change_crowdsource',
                                     response.crowdsource):
            return JsonResponse({'error': 'permission denied'}, status=403)
        if not response.user or not response.user.email:
            return JsonResponse({'error': 'no email'}, status=400)
        msg = TemplateEmail(
            subject=form.cleaned_data['subject'],
            from_email='*****@*****.**',
            reply_to=[request.user.email],
            user=response.user,
            text_template='crowdsource/email/message_user.txt',
            html_template='crowdsource/email/message_user.html',
            extra_context={
                'body': form.cleaned_data['body'],
                'assignment': response.crowdsource,
                'from_user': request.user,
            },
        )
        msg.send()
        return JsonResponse({'status': 'ok'})
    else:
        return JsonResponse({'error': 'form invalid'}, status=400)
Beispiel #2
0
def agency_redirect_login(request, agency_slug, agency_idx, foia_slug,
                          foia_idx):
    """View to redirect agency users to the correct page or offer to resend
    them their login token"""

    agency = get_object_or_404(Agency, slug=agency_slug, pk=agency_idx)
    foia = get_object_or_404(
        FOIARequest,
        agency=agency,
        slug=foia_slug,
        pk=foia_idx,
    )
    valid_emails = agency.get_all_known_emails()

    if request.method == 'POST':
        email = request.POST.get('email', '')
        if email.lower() in valid_emails:
            msg = TemplateEmail(
                subject='Login Token',
                from_email='*****@*****.**',
                to=[email],
                text_template='accounts/email/login_token.txt',
                html_template='accounts/email/login_token.html',
                extra_context={
                    'reply_link': foia.get_agency_reply_link(email=email),
                })
            msg.send(fail_silently=False)
            messages.success(
                request,
                'Fresh login token succesfully sent to %s.  '
                'Please check your email' % email,
            )
        else:
            messages.error(request, 'Invalid email')
        return redirect(foia)

    authed = request.user.is_authenticated()
    agency_user = authed and request.user.profile.acct_type == 'agency'
    agency_match = agency_user and request.user.profile.agency == agency
    email = request.GET.get('email', '')
    valid = email.lower() in valid_emails

    if agency_match:
        return redirect(foia)
    elif agency_user:
        return redirect('foia-agency-list')
    elif authed:
        return redirect('index')
    else:
        return render(
            request,
            'accounts/agency_redirect_login.html',
            {
                'email': email,
                'valid': valid
            },
        )
Beispiel #3
0
def support(user, message, _task):
    """Send a response to a user about a task."""
    context = {'message': message, 'task': _task}
    notification = TemplateEmail(
        user=user,
        extra_context=context,
        text_template='message/notification/support.txt',
        html_template='message/notification/support.html',
        subject=u'Support #%d' % _task.id)
    notification.send(fail_silently=False)
Beispiel #4
0
def notify_project_contributor(user, project, added_by):
    """Notify a user that they were added as a contributor to a project."""
    context = {'project': project, 'added_by': added_by}
    notification = TemplateEmail(
        user=user,
        extra_context=context,
        text_template='message/notification/project.txt',
        html_template='message/notification/project.html',
        subject=u'Added to a project')
    notification.send(fail_silently=False)
Beispiel #5
0
 def send_notification(self):
     """Send the user the link to their file"""
     notification = TemplateEmail(
         user=self.user,
         extra_context=self.get_context(),
         text_template=self.text_template,
         html_template=self.html_template,
         subject=self.subject,
     )
     notification.send(fail_silently=False)
Beispiel #6
0
def gift(to_user, from_user, gift_description):
    """Notify the user when they have been gifted requests."""
    context = {'from': from_user, 'gift': gift_description}
    notification = TemplateEmail(
        user=to_user,
        extra_context=context,
        text_template='message/notification/gift.txt',
        html_template='message/notification/gift.html',
        subject=u'You got a gift!')
    notification.send(fail_silently=False)
Beispiel #7
0
 def reply(self, text, action="reply"):
     """Send an email reply to the user that raised the flag."""
     send_to = [contributor.email for contributor in self.project.contributors.all()]
     project_email = TemplateEmail(
         to=send_to,
         extra_context={"action": action, "message": text, "task": self},
         subject="%s %s" % (self.project, action),
         text_template="message/project/%s.txt" % action,
         html_template="message/project/%s.html" % action,
     )
     project_email.send(fail_silently=False)
     return project_email
Beispiel #8
0
def email_change(user, old_email):
    """Notify the user when their email is changed."""
    context = {'old_email': old_email, 'new_email': user.email}
    notification = TemplateEmail(
        user=user,
        extra_context=context,
        text_template='message/notification/email_change.txt',
        html_template='message/notification/email_change.html',
        subject=u'Changed email address')
    notification.to.append(
        old_email)  # Send to both the new and old email addresses
    notification.send(fail_silently=False)
Beispiel #9
0
def email_verify(user):
    """Verify the user's email by sending them a message."""
    url = reverse('acct-verify-email')
    key = user.profile.generate_confirmation_key()
    context = {'verification_link': user.profile.wrap_url(url, key=key)}
    notification = TemplateEmail(
        user=user,
        extra_context=context,
        text_template='message/notification/email_verify.txt',
        html_template='message/notification/email_verify.html',
        subject=u'Verify your email')
    notification.send(fail_silently=False)
Beispiel #10
0
 def send_intro_email(self, user):
     """Send an intro email to the user upon crowdfund creation"""
     msg = TemplateEmail(
         subject="Crowdfund Campaign Launched",
         user=user,
         bcc=["diagnostics@muckrock", "info@muckrock"],
         text_template="crowdfund/email/intro.txt",
         html_template="crowdfund/email/intro.html",
         extra_context={
             "amount": int(self.payment_required),
             "url": self.get_crowdfund_object().get_absolute_url(),
         },
     )
     msg.send(fail_silently=False)
Beispiel #11
0
def welcome(user):
    """Send a welcome notification to a new user. Hello!"""
    verification_url = reverse('acct-verify-email')
    key = user.profile.generate_confirmation_key()
    context = {
        'verification_link': user.profile.wrap_url(verification_url, key=key)
    }
    notification = TemplateEmail(
        user=user,
        extra_context=context,
        text_template='message/notification/welcome.txt',
        html_template='message/notification/welcome.html',
        subject=u'Welcome to MuckRock!')
    notification.send(fail_silently=False)
Beispiel #12
0
def welcome_miniregister(user):
    """Send a welcome notification to a new users who signed up with miniregister.
    Provide them a link to verify their email and update their username/password."""
    completion_url = reverse('accounts-complete-registration')
    key = user.profile.generate_confirmation_key()
    context = {
        'completion_url': user.profile.wrap_url(completion_url, key=key)
    }
    notification = TemplateEmail(
        user=user,
        extra_context=context,
        text_template='message/notification/welcome_miniregister.txt',
        html_template='message/notification/welcome_miniregister.html',
        subject=u'Welcome to MuckRock!')
    notification.send(fail_silently=False)
Beispiel #13
0
 def send_intro_email(self, user):
     """Send an intro email to the user upon crowdfund creation"""
     msg = TemplateEmail(
         subject='Crowdfund Campaign Launched',
         from_email='*****@*****.**',
         user=user,
         bcc=['diagnostics@muckrock', 'info@muckrock'],
         text_template='crowdfund/email/intro.txt',
         html_template='crowdfund/email/intro.html',
         extra_context={
             'amount': int(self.payment_required),
             'url': self.get_crowdfund_object().get_absolute_url(),
         }
     )
     msg.send(fail_silently=False)
Beispiel #14
0
 def reply(self, text, action='reply'):
     """Send an email reply to the user that raised the flag."""
     send_to = [
         contributor.email for contributor in self.project.contributors.all()
     ]
     project_email = TemplateEmail(
         to=send_to,
         extra_context={'action': action,
                        'message': text,
                        'task': self},
         subject=u'%s %s' % (self.project, action),
         text_template='message/project/%s.txt' % action,
         html_template='message/project/%s.html' % action
     )
     project_email.send(fail_silently=False)
     return project_email
Beispiel #15
0
def support(user_id, message, task_id):
    """Send a response to a user about a flag task."""
    # pylint: disable=import-outside-toplevel
    from muckrock.task.models import FlaggedTask

    user = User.objects.get(id=user_id)
    task_ = FlaggedTask.objects.get(id=task_id)
    context = {"message": message, "task": task_}
    notification = TemplateEmail(
        user=user,
        extra_context=context,
        text_template="message/notification/support.txt",
        html_template="message/notification/support.html",
        subject="Support #%d" % task_.id,
    )
    notification.send(fail_silently=False)
Beispiel #16
0
def notify_project_contributor(user_id, project_id, added_by_id):
    """Notify a user that they were added as a contributor to a project."""
    # pylint: disable=import-outside-toplevel
    from muckrock.project.models import Project

    user = User.objects.get(id=user_id)
    project = Project.objects.get(id=project_id)
    added_by = User.objects.get(id=added_by_id)
    context = {"project": project, "added_by": added_by}
    notification = TemplateEmail(
        user=user,
        extra_context=context,
        text_template="message/notification/project.txt",
        html_template="message/notification/project.html",
        subject="Added to a project",
    )
    notification.send(fail_silently=False)
Beispiel #17
0
def contact_user(request, idx):
    """Let staff send messages to users"""
    user = get_object_or_404(User, pk=idx)
    if request.method == 'POST':
        form = ContactForm(request.POST)
        if form.is_valid():
            email = TemplateEmail(
                user=user,
                extra_context={'message': form.cleaned_data['message']},
                subject=form.cleaned_data['subject'],
                text_template='message/accounts/contact.txt',
                html_template='message/accounts/contact.html',
            )
            email.send(fail_silently=False)
            messages.success(request, 'Message sent!')
        else:
            messages.error(request, 'Message failed!')
    return redirect(user)
Beispiel #18
0
def contact_user(request, idx):
    """Let staff send messages to users"""
    user = get_object_or_404(User, pk=idx)
    if request.method == "POST":
        form = ContactForm(request.POST)
        if form.is_valid():
            email = TemplateEmail(
                user=user,
                extra_context={"message": form.cleaned_data["message"]},
                subject=form.cleaned_data["subject"],
                text_template="message/accounts/contact.txt",
                html_template="message/accounts/contact.html",
            )
            email.send(fail_silently=False)
            messages.success(request, "Message sent!")
        else:
            messages.error(request, "Message failed!")
    return redirect(user)
Beispiel #19
0
 def _contact_user(self, request, foia):
     """Allow an admin to message the foia's owner"""
     text = request.POST.get('text')
     if request.user.is_staff and text:
         context = {
                 'text': text,
                 'foia_url': foia.user.profile.wrap_url(foia.get_absolute_url()),
                 'foia_title': foia.title,
                 }
         email = TemplateEmail(
             user=foia.user,
             extra_context=context,
             text_template='message/notification/contact_user.txt',
             html_template='message/notification/contact_user.html',
             subject='Message from MuckRock',
             )
         email.send(fail_silently=False)
         messages.success(request, 'Email sent to %s' % foia.user.email)
     return redirect(foia)
Beispiel #20
0
def contact_user(request, foia):
    """Allow an admin to message the foia's owner"""
    form = FOIAContactUserForm(request.POST)
    if request.user.is_staff and form.is_valid() and form.cleaned_data["text"]:
        context = {
            "text": form.cleaned_data["text"],
            "foia_url": foia.user.profile.wrap_url(foia.get_absolute_url()),
            "foia_title": foia.title,
        }
        email = TemplateEmail(
            user=foia.user,
            extra_context=context,
            text_template="message/notification/contact_user.txt",
            html_template="message/notification/contact_user.html",
            subject="Message from MuckRock",
        )
        email.send(fail_silently=False)
        messages.success(request, "Email sent to %s" % foia.user.email)
    return _get_redirect(request, foia)
Beispiel #21
0
def failed_payment(invoice_id):
    """Notify a customer about a failed subscription invoice."""
    invoice = stripe_retry_on_error(
        stripe.Invoice.retrieve,
        invoice_id,
    )
    attempt = invoice.attempt_count
    subscription_type = get_subscription_type(invoice)
    profile = Profile.objects.get(customer_id=invoice.customer)
    user = profile.user
    # raise the failed payment flag on the profile
    profile.payment_failed = True
    profile.save()
    subject = u'Your payment has failed'
    org = None
    if subscription_type == 'org':
        org = Organization.objects.get(owner=user)
    if attempt == 4:
        # on last attempt, cancel the user's subscription and lower the failed payment flag
        if subscription_type == 'pro':
            profile.cancel_pro_subscription()
        elif subscription_type == 'org':
            org.cancel_subscription()
        profile.payment_failed = False
        profile.save()
        logger.info('%s subscription has been cancelled due to failed payment',
                    user.username)
        subject = u'Your %s subscription has been cancelled' % subscription_type
        context = {'attempt': 'final', 'type': subscription_type, 'org': org}
    else:
        logger.info('Failed payment by %s, attempt %s', user.username, attempt)
        context = {'attempt': attempt, 'type': subscription_type, 'org': org}
    notification = TemplateEmail(
        user=user,
        extra_context=context,
        text_template='message/notification/failed_payment.txt',
        html_template='message/notification/failed_payment.html',
        subject=subject,
    )
    notification.send(fail_silently=False)
Beispiel #22
0
def failed_payment(invoice_id):
    """Notify a customer about a failed subscription invoice."""
    # pylint: disable=too-many-branches
    # pylint: disable=too-many-statements
    invoice = stripe_retry_on_error(stripe.Invoice.retrieve, invoice_id)
    attempt = invoice.attempt_count
    subscription_type = get_subscription_type(invoice)
    recurring_donation = None
    crowdfund = None
    email_to = []
    if subscription_type == "donate":
        recurring_donation = RecurringDonation.objects.filter(
            subscription_id=invoice.subscription).first()
        if recurring_donation:
            user = recurring_donation.user
            if user is None:
                email_to = [recurring_donation.email]
            recurring_donation.payment_failed = True
            recurring_donation.save()
        else:
            user = None
            logger.error("No recurring crowdfund found for %s",
                         invoice.subscription)
    elif subscription_type.startswith("crowdfund"):
        recurring_payment = RecurringCrowdfundPayment.objects.filter(
            subscription_id=invoice.subscription).first()
        if recurring_payment:
            user = recurring_payment.user
            if user is None:
                email_to = [recurring_payment.email]
            crowdfund = recurring_payment.crowdfund
            recurring_payment.payment_failed = True
            recurring_payment.save()
        else:
            user = None
            logger.error("No recurring crowdfund found for %s",
                         invoice.subscription)
    else:
        # squarelet handles other types
        return
    subject = "Your payment has failed"
    context = {
        "attempt": attempt,
        "type": subscription_type,
        "crowdfund": crowdfund
    }
    if subscription_type.startswith("crowdfund"):
        context["type"] = "crowdfund"
    if attempt == 4:
        # on last attempt, cancel the user's subscription and lower the failed payment flag
        if subscription_type == "donate" and recurring_donation:
            recurring_donation.cancel()
        elif subscription_type.startswith("crowdfund") and recurring_payment:
            recurring_payment.cancel()
        logger.info("%s subscription has been cancelled due to failed payment",
                    user)
        subject = "Your %s subscription has been cancelled" % subscription_type
        context["attempt"] = "final"
    else:
        logger.info("Failed payment by %s, attempt %s", user, attempt)
    notification = TemplateEmail(
        user=user,
        to=email_to,
        extra_context=context,
        text_template="message/notification/failed_payment.txt",
        html_template="message/notification/failed_payment.html",
        subject=subject,
    )
    notification.send(fail_silently=False)
Beispiel #23
0
def agency_redirect_login(
    request, agency_slug, agency_idx, foia_slug, foia_idx
):
    """View to redirect agency users to the correct page or offer to resend
    them their login token"""

    agency = get_object_or_404(Agency, slug=agency_slug, pk=agency_idx)
    foia = get_object_or_404(
        FOIARequest,
        agency=agency,
        slug=foia_slug,
        pk=foia_idx,
    )

    if request.method == 'POST':
        email = request.POST.get('email', '')
        # valid if this email is associated with the agency
        valid = (
            EmailAddress.objects.fetch(email).agencies.filter(id=agency.pk)
            .exists()
        )
        if valid:
            msg = TemplateEmail(
                subject='Login Token',
                from_email='*****@*****.**',
                to=[email],
                text_template='accounts/email/login_token.txt',
                html_template='accounts/email/login_token.html',
                extra_context={
                    'reply_link': foia.get_agency_reply_link(email=email),
                }
            )
            msg.send(fail_silently=False)
            messages.success(
                request,
                'Fresh login token succesfully sent to %s.  '
                'Please check your email' % email,
            )
        else:
            messages.error(request, 'Invalid email')
        return redirect(foia)

    authed = request.user.is_authenticated()
    agency_user = authed and request.user.profile.is_agency_user
    agency_match = agency_user and request.user.profile.agency == agency
    email = request.GET.get('email', '')
    # valid if this email is associated with the agency
    email_address = EmailAddress.objects.fetch(email)
    valid = (
        email_address is not None
        and email_address.agencies.filter(id=agency.pk).exists()
    )

    if agency_match:
        return redirect(foia.get_absolute_url() + "#agency-reply")
    elif agency_user:
        return redirect('foia-agency-list')
    elif authed:
        return redirect('index')
    else:
        return render(
            request,
            'accounts/agency_redirect_login.html',
            {'email': email,
             'valid': valid},
        )
Beispiel #24
0
def agency_redirect_login(request, agency_slug, agency_idx, foia_slug,
                          foia_idx):
    """View to redirect agency users to the correct page or offer to resend
    them their login token"""
    # pylint: disable=too-many-locals

    agency = get_object_or_404(Agency, slug=agency_slug, pk=agency_idx)
    foia = get_object_or_404(FOIARequest,
                             agency=agency,
                             slug=foia_slug,
                             pk=foia_idx)

    if request.method == "POST":
        email = request.POST.get("email", "")
        # valid if this email is associated with the agency
        email_addr = EmailAddress.objects.fetch(email)
        valid = email_addr.agencies.filter(id=agency.pk).exists()
        if valid:
            # rate limit to once per 5 minutes
            new_uuid = uuid4()
            old_uuid = cache.get_or_set(f"token:{email_addr.pk}", new_uuid,
                                        300)
            if new_uuid != old_uuid:
                messages.error(request, "Please wait 5 minutes to try again")
                return redirect(foia)

            msg = TemplateEmail(
                subject="Login Token",
                to=[email],
                text_template="accounts/email/login_token.txt",
                html_template="accounts/email/login_token.html",
                extra_context={
                    "reply_link": foia.get_agency_reply_link(email=email)
                },
            )
            msg.send(fail_silently=False)
            messages.success(
                request,
                "Fresh login token succesfully sent to %s.  "
                "Please check your email" % email,
            )
        else:
            messages.error(request, "Invalid email")
        return redirect(foia)

    authed = request.user.is_authenticated
    agency_user = authed and request.user.profile.is_agency_user
    agency_match = agency_user and request.user.profile.agency == agency
    email = request.GET.get("email", "")
    # valid if this email is associated with the agency
    email_address = EmailAddress.objects.fetch(email)
    valid = (email_address is not None
             and email_address.agencies.filter(id=agency.pk).exists())

    if agency_match:
        return redirect(foia.get_absolute_url() + "#agency-reply")
    elif agency_user:
        return redirect("foia-agency-list")
    elif authed:
        return redirect("index")
    else:
        return render(
            request,
            "accounts/agency_redirect_login.html",
            {
                "email": email,
                "valid": valid
            },
        )
Beispiel #25
0
def export_csv(foia_pks, user_pk):
    """Export a csv of the selected FOIA requests"""
    fields = (
        (lambda f: f.user.username, 'User'),
        (lambda f: f.title, 'Title'),
        (lambda f: f.get_status_display(), 'Status'),
        (lambda f: settings.MUCKROCK_URL + f.get_absolute_url(), 'URL'),
        (lambda f: f.jurisdiction.name, 'Jurisdiction'),
        (lambda f: f.jurisdiction.pk, 'Jurisdiction ID'),
        (
            lambda f: f.jurisdiction.get_level_display(),
            'Jurisdiction Level',
        ),
        (
            lambda f: f.jurisdiction.parent.name
            if f.jurisdiction.level == 'l' else f.jurisdiction.name,
            'Jurisdiction State',
        ),
        (lambda f: f.agency.name if f.agency else '', 'Agency'),
        (lambda f: f.agency.pk if f.agency else '', 'Agency ID'),
        (lambda f: f.date_followup, 'Followup Date'),
        (lambda f: f.date_estimate, 'Estimated Completion Date'),
        (lambda f: f.composer.requested_docs, 'Requested Documents'),
        (lambda f: f.current_tracking_id(), 'Tracking Number'),
        (lambda f: f.embargo, 'Embargo'),
        (lambda f: f.days_since_submitted, 'Days since submitted'),
        (lambda f: f.days_since_updated, 'Days since updated'),
        (lambda f: f.project_names, 'Projects'),
        (lambda f: f.tag_names, 'Tags'),
        (lambda f: f.price, 'Price'),
        (lambda f: f.composer.datetime_submitted, 'Date Submitted'),
        (lambda f: f.date_due, 'Date Due'),
        (lambda f: f.datetime_done, 'Date Done'),
    )
    foias = (FOIARequest.objects.filter(pk__in=foia_pks).select_related(
        'composer__user',
        'agency__jurisdiction__parent',
    ).only(
        'composer__user__username',
        'title',
        'status',
        'slug',
        'agency__jurisdiction__name',
        'agency__jurisdiction__slug',
        'agency__jurisdiction__id',
        'agency__jurisdiction__parent__name',
        'agency__jurisdiction__parent__id',
        'agency__name',
        'agency__id',
        'date_followup',
        'date_estimate',
        'embargo',
        'composer__requested_docs',
    ).annotate(
        days_since_submitted=ExtractDay(
            Cast(Now() - F('composer__datetime_submitted'), DurationField())),
        days_since_updated=ExtractDay(
            Cast(Now() - F('datetime_updated'), DurationField())),
        project_names=StringAgg('projects__title', ',', distinct=True),
        tag_names=StringAgg('tags__name', ',', distinct=True),
    ))
    conn = S3Connection(
        settings.AWS_ACCESS_KEY_ID,
        settings.AWS_SECRET_ACCESS_KEY,
    )
    bucket = conn.get_bucket(settings.AWS_STORAGE_BUCKET_NAME)
    today = date.today()
    file_key = 'exported_csv/{y:4d}/{m:02d}/{d:02d}/{md5}/requests.csv'.format(
        y=today.year,
        m=today.month,
        d=today.day,
        md5=md5('{}{}'.format(
            int(timestamp()),
            ''.join(str(pk) for pk in foia_pks[:100]),
        )).hexdigest(),
    )
    key = bucket.new_key(file_key)
    with smart_open(key, 'wb') as csv_file:
        writer = csv.writer(csv_file)
        writer.writerow(f[1] for f in fields)
        for foia in foias.iterator():
            writer.writerow(f[0](foia) for f in fields)
    key.set_acl('public-read')

    notification = TemplateEmail(
        user=User.objects.get(pk=user_pk),
        extra_context={'file': file_key},
        text_template='message/notification/csv_export.txt',
        html_template='message/notification/csv_export.html',
        subject='Your CSV Export',
    )
    notification.send(fail_silently=False)
Beispiel #26
0
def failed_payment(invoice_id):
    """Notify a customer about a failed subscription invoice."""
    # pylint: disable=too-many-branches
    # pylint: disable=too-many-statements
    invoice = stripe_retry_on_error(
        stripe.Invoice.retrieve,
        invoice_id,
    )
    attempt = invoice.attempt_count
    subscription_type = get_subscription_type(invoice)
    recurring_donation = None
    profile = None
    crowdfund = None
    if subscription_type == 'donate':
        recurring_donation = RecurringDonation.objects.filter(
            subscription_id=invoice.subscription, ).first()
        if recurring_donation:
            user = recurring_donation.user
            recurring_donation.payment_failed = True
            recurring_donation.save()
        else:
            user = None
            logger.error(
                'No recurring crowdfund found for %s',
                invoice.subscription,
            )
    elif subscription_type.startswith('crowdfund'):
        recurring_payment = RecurringCrowdfundPayment.objects.filter(
            subscription_id=invoice.subscription, ).first()
        if recurring_payment:
            user = recurring_payment.user
            crowdfund = recurring_payment.crowdfund
            recurring_payment.payment_failed = True
            recurring_payment.save()
        else:
            user = None
            logger.error(
                'No recurring crowdfund found for %s',
                invoice.subscription,
            )
    else:
        profile = Profile.objects.get(customer_id=invoice.customer)
        user = profile.user
        # raise the failed payment flag on the profile
        profile.payment_failed = True
        profile.save()
    subject = u'Your payment has failed'
    org = None
    if subscription_type == 'org':
        org = Organization.objects.get(owner=user)
    context = {
        'attempt': attempt,
        'type': subscription_type,
        'org': org,
        'crowdfund': crowdfund,
    }
    if subscription_type.startswith('crowdfund'):
        context['type'] = 'crowdfund'
    if attempt == 4:
        # on last attempt, cancel the user's subscription and lower the failed payment flag
        if subscription_type == 'pro':
            profile.cancel_pro_subscription()
        elif subscription_type == 'org':
            org.cancel_subscription()
        elif subscription_type == 'donate' and recurring_donation:
            recurring_donation.cancel()
        elif subscription_type.startswith('crowdfund') and recurring_payment:
            recurring_payment.cancel()
        if subscription_type in ('pro', 'org'):
            profile.payment_failed = False
            profile.save()
        logger.info('%s subscription has been cancelled due to failed payment',
                    user)
        subject = u'Your %s subscription has been cancelled' % subscription_type
        context['attempt'] = 'final'
    else:
        logger.info('Failed payment by %s, attempt %s', user, attempt)
    notification = TemplateEmail(
        user=user,
        extra_context=context,
        text_template='message/notification/failed_payment.txt',
        html_template='message/notification/failed_payment.html',
        subject=subject,
    )
    notification.send(fail_silently=False)