Example #1
0
def mail_pending_refunds():
    # First find all the pending refunds and the addons for them.
    pending = dict((Refund.objects.filter(status=amo.REFUND_PENDING)
                                  .values_list('contribution__addon_id')
                                  .annotate(Count('id'))))
    if not pending:
        log.info('No refunds to email')
        return
    log.info('Mailing pending refunds: %s refunds found' % len(pending))

    # Find all owners of those addons.
    users = (AddonUser.objects.filter(role=amo.AUTHOR_ROLE_OWNER,
                                      addon__in=pending.keys())
                              .values_list('addon_id', 'user__email'))

    # Group up the owners. An owner could have more than one addon and each
    # addon can have more than one owner.
    owners = {}
    for addon_id, email in users:
        owners.setdefault(email, [])
        owners[email].append(addon_id)

    # Send the emails out.
    for owner, addon_ids in owners.items():
        log.info('Sending refund emails to: %s about %s' %
                 (email, ', '.join([str(i) for i in addon_ids])))
        addons = Addon.objects.filter(pk__in=addon_ids)
        ctx = {'addons': addons, 'refunds': pending}
        send_mail('Pending refund requests at the Mozilla Marketplace',
                  env.get_template('market/emails/refund-nag.txt').render(ctx),
                  from_email=settings.NOBODY_EMAIL,
                  recipient_list=[owner])
Example #2
0
 def test_send_attachment(self):
     path = os.path.join(ATTACHMENTS_DIR, 'bacon.txt')
     attachments = [(os.path.basename(path), storage.open(path),
                     mimetypes.guess_type(path)[0])]
     send_mail('test subject', 'test body', from_email='*****@*****.**',
               recipient_list=['*****@*****.**'], attachments=attachments)
     eq_(attachments, mail.outbox[0].attachments, 'Attachments not included')
Example #3
0
def refund_reason(request, contribution, wizard):
    addon = contribution.addon
    if not 'request' in wizard.get_progress():
        return redirect('users.support', contribution.pk, 'request')

    if contribution.is_instant_refund():
        paypal.refund(contribution.paykey)
        paypal_log.info('Refund issued for contribution %r' % contribution.pk)
        # Note: we have to wait for PayPal to issue an IPN before it's
        # completely refunded.
        messages.success(request, _('Refund is being processed.'))
        return redirect('users.purchases')

    form = forms.ContactForm(request.POST or None)
    if request.method == 'POST' and form.is_valid():
        url = absolutify(urlparams(addon.get_dev_url('issue_refund'),
                                   transaction_id=contribution.transaction_id))
        template = jingo.render_to_string(request,
            wizard.tpl('emails/refund-request.txt'),
            context={'addon': addon,
                     'form': form,
                     'user': request.amo_user,
                     'contribution': contribution,
                     'refund_url': url})
        log.info('Refund request sent by user: %s for addon: %s' %
                 (request.amo_user.pk, addon.pk))
        # L10n: %s is the addon name.
        send_mail(_(u'New Refund Request for %s' % addon.name),
                  template, settings.NOBODY_EMAIL,
                  [smart_str(addon.support_email)])
        return redirect(reverse('users.support',
                                args=[contribution.pk, 'refund-sent']))

    return wizard.render(request, wizard.tpl('refund.html'), {'form': form})
Example #4
0
def refund_reason(request, contribution, wizard):
    addon = contribution.addon
    if not 'request' in wizard.get_progress():
        return redirect(reverse('users.support',
                                args=[contribution.pk, 'request']))

    form = forms.ContactForm(request.POST or None)
    if request.method == 'POST':
        if form.is_valid():
            # if under 30 minutes, refund
            # TODO(ashort): add in the logic for under 30 minutes.
            template = jingo.render_to_string(request,
                                wizard.tpl('emails/refund-request.txt'),
                                context={'addon': addon, 'form': form,
                                         'user': request.amo_user,
                                         'contribution': contribution})
            log.info('Refund request sent by user: %s for addon: %s' %
                     (request.amo_user.pk, addon.pk))
            # L10n: %s is the addon name.
            send_mail(_(u'New Refund Request for %s' % addon.name),
                      template, request.amo_user.email,
                      [smart_str(addon.support_email)])
            return redirect(reverse('users.support',
                                    args=[contribution.pk, 'refund-sent']))

    return wizard.render(request, wizard.tpl('refund.html'),
                         {'contribut': addon, 'form': form})
Example #5
0
def tally_job_results(job_id, **kw):
    sql = """select sum(1),
                    sum(case when completed IS NOT NULL then 1 else 0 end)
             from validation_result
             where validation_job_id=%s"""
    cursor = connection.cursor()
    cursor.execute(sql, [job_id])
    total, completed = cursor.fetchone()
    if completed == total:
        # The job has finished.
        job = ValidationJob.objects.get(pk=job_id)
        job.update(completed=datetime.now())
        if job.finish_email:
            send_mail(
                u"Behold! Validation results for %s %s->%s"
                % (amo.APP_IDS[job.application.id].pretty, job.curr_max_version.version, job.target_version.version),
                textwrap.dedent(
                    """
                          Aww yeah
                          %s
                          """
                    % absolutify(reverse("zadmin.validation"))
                ),
                from_email=settings.DEFAULT_FROM_EMAIL,
                recipient_list=[job.finish_email],
            )
Example #6
0
def edit(request):
    webapp = settings.APP_PREVIEW
    # Don't use request.amo_user since it has too much caching.
    amouser = UserProfile.objects.get(pk=request.user.id)
    if request.method == "POST":
        # ModelForm alters the instance you pass in.  We need to keep a copy
        # around in case we need to use it below (to email the user)
        original_email = amouser.email
        form = forms.UserEditForm(request.POST, request.FILES, request=request, instance=amouser, webapp=webapp)
        if form.is_valid():
            messages.success(request, _("Profile Updated"))
            if amouser.email != original_email:
                # Temporarily block email changes.
                if settings.APP_PREVIEW:
                    messages.error(request, "Error", "You cannot change your email on the " "developer preview site.")
                    return jingo.render(request, "users/edit.html", {"form": form, "amouser": amouser})

                l = {"user": amouser, "mail1": original_email, "mail2": amouser.email}
                log.info(u"User (%(user)s) has requested email change from" "(%(mail1)s) to (%(mail2)s)" % l)
                messages.info(
                    request,
                    _("Email Confirmation Sent"),
                    _(
                        u"An email has been sent to {0} to confirm your new "
                        "email address. For the change to take effect, you "
                        "need to click on the link provided in this email. "
                        "Until then, you can keep logging in with your "
                        "current email address."
                    ).format(amouser.email),
                )

                domain = settings.DOMAIN
                token, hash = EmailResetCode.create(amouser.id, amouser.email)
                url = "%s%s" % (settings.SITE_URL, reverse("users.emailchange", args=[amouser.id, token, hash]))
                t = loader.get_template("users/email/emailchange.ltxt")
                c = {"domain": domain, "url": url}
                send_mail(
                    _("Please confirm your email address " "change at %s" % domain),
                    t.render(Context(c)),
                    None,
                    [amouser.email],
                    use_blacklist=False,
                    real_email=True,
                )

                # Reset the original email back.  We aren't changing their
                # address until they confirm the new one
                amouser.email = original_email
            form.save()
            return redirect("users.edit")
        else:

            messages.error(
                request,
                _("Errors Found"),
                _("There were errors in the changes " "you made. Please correct them and " "resubmit."),
            )
    else:
        form = forms.UserEditForm(instance=amouser, webapp=webapp)
    return jingo.render(request, "users/edit.html", {"form": form, "amouser": amouser, "webapp": webapp})
Example #7
0
def notify_failed(file_pks, job_pk, data, **kw):
    log.info("[%s@None] Notifying failed for job %s." % (len(file_pks), job_pk))
    job = ValidationJob.objects.get(pk=job_pk)
    set_user(get_task_user())
    for result in ValidationResult.objects.filter(validation_job=job, file__pk__in=file_pks):
        file = result.file
        version = file.version
        addon = version.addon
        context = get_context(addon, version, job, [result], fileob=file)
        for author in addon.authors.all():
            log.info(
                u"Emailing %s%s for addon %s, file %s about "
                "error from bulk validation job %s"
                % (author.email, " [PREVIEW]" if data["preview_only"] else "", addon.pk, file.pk, job_pk)
            )
            args = (Template(data["subject"]).render(context), Template(data["text"]).render(context))
            kwargs = dict(from_email=settings.DEFAULT_FROM_EMAIL, recipient_list=[author.email])
            if data["preview_only"]:
                job.preview_failure_mail(*args, **kwargs)
            else:
                send_mail(*args, **kwargs)

        amo.log(
            amo.LOG.BULK_VALIDATION_EMAILED,
            addon,
            version,
            details={"version": version.version, "file": file.filename, "target": job.target_version.version},
        )
Example #8
0
def edit(request):
    webapp = settings.APP_PREVIEW
    # Don't use request.amo_user since it has too much caching.
    amouser = UserProfile.objects.get(pk=request.user.id)
    if request.method == 'POST':
        # ModelForm alters the instance you pass in.  We need to keep a copy
        # around in case we need to use it below (to email the user)
        original_email = amouser.email
        form = forms.UserEditForm(request.POST, request.FILES, request=request,
                                  instance=amouser, webapp=webapp)
        if form.is_valid():
            messages.success(request, _('Profile Updated'))
            if amouser.email != original_email:
                # Temporarily block email changes.
                if settings.APP_PREVIEW:
                    messages.error(request, 'Error',
                                   'You cannot change your email on the '
                                   'developer preview site.')
                    return jingo.render(request, 'users/edit.html',
                                        {'form': form, 'amouser': amouser})

                l = {'user': amouser,
                     'mail1': original_email,
                     'mail2': amouser.email}
                log.info(u"User (%(user)s) has requested email change from"
                          "(%(mail1)s) to (%(mail2)s)" % l)
                messages.info(request, _('Email Confirmation Sent'),
                    _(u'An email has been sent to {0} to confirm your new '
                       'email address. For the change to take effect, you '
                       'need to click on the link provided in this email. '
                       'Until then, you can keep logging in with your '
                       'current email address.').format(amouser.email))

                domain = settings.DOMAIN
                token, hash = EmailResetCode.create(amouser.id, amouser.email)
                url = "%s%s" % (settings.SITE_URL,
                                reverse('users.emailchange', args=[amouser.id,
                                                                token, hash]))
                t = loader.get_template('users/email/emailchange.ltxt')
                c = {'domain': domain, 'url': url}
                send_mail(_('Please confirm your email address '
                            'change at %s' % domain),
                    t.render(Context(c)), None, [amouser.email],
                    use_blacklist=False, real_email=True)

                # Reset the original email back.  We aren't changing their
                # address until they confirm the new one
                amouser.email = original_email
            form.save()
            return redirect('users.edit')
        else:

            messages.error(request, _('Errors Found'),
                                    _('There were errors in the changes '
                                      'you made. Please correct them and '
                                      'resubmit.'))
    else:
        form = forms.UserEditForm(instance=amouser, webapp=webapp)
    return jingo.render(request, 'users/edit.html',
                        {'form': form, 'amouser': amouser, 'webapp': webapp})
Example #9
0
    def delete(self, msg):
        if self.highest_status:
            log.debug('Adding guid to blacklist: %s' % self.guid)
            BlacklistedGuid(guid=self.guid, comments=msg).save()
            log.debug('Deleting add-on: %s' % self.id)

            authors = [u.email for u in self.authors.all()]
            to = [settings.FLIGTAR]
            user = amo.get_user()
            user_str = "%s, %s (%s)" % (user.display_name or user.username,
                    user.email, user.id) if user else "Unknown"

            email_msg = u"""
            The following add-on was deleted.
            ADD-ON: %s
            DELETED BY: %s
            ID: %s
            GUID: %s
            AUTHORS: %s
            TOTAL DOWNLOADS: %s
            AVERAGE DAILY USERS: %s
            NOTES: %s
            """ % (self.name, user_str, self.id, self.guid, authors,
                   self.total_downloads, self.average_daily_users, msg)
            log.debug('Sending delete email for add-on %s' % self.id)
            subject = 'Deleting add-on %s' % self.id

        rv = super(Addon, self).delete()

        # We want to ensure deletion before notification.
        if self.highest_status:
            send_mail(subject, email_msg, recipient_list=to)

        return rv
Example #10
0
def notify_failed(file_pks, job_pk, data, **kw):
    log.info('[%s@None] Notifying failed for job %s.'
             % (len(file_pks), job_pk))
    job = ValidationJob.objects.get(pk=job_pk)
    set_user(get_task_user())
    for result in ValidationResult.objects.filter(validation_job=job,
                                                  file__pk__in=file_pks):
        file = result.file
        version = file.version
        addon = version.addon
        context = get_context(addon, version, job, [result])
        for author in addon.authors.all():
            log.info(u'Emailing %s%s for addon %s, file %s about '
                     'error from bulk validation job %s'
                     % (author.email,
                        ' [PREVIEW]' if data['preview_only'] else '',
                        addon.pk, file.pk, job_pk))
            args = (Template(data['subject']).render(context),
                    Template(data['text']).render(context))
            kwargs = dict(from_email=settings.DEFAULT_FROM_EMAIL,
                          recipient_list=[author.email])
            if data['preview_only']:
                job.preview_failure_mail(*args, **kwargs)
            else:
                send_mail(*args, **kwargs)

        amo.log(amo.LOG.BULK_VALIDATION_EMAILED,
                addon, version,
                details={'version': version.version,
                         'file': file.filename,
                         'target': job.target_version.version})
Example #11
0
 def test_async_will_stop_retrying(self, backend):
     backend.side_effect = self.make_backend_class([True, True])
     with self.assertRaises(RuntimeError):
         send_mail('test subject',
                   'test body',
                   async=True,
                   max_retries=1,
                   recipient_list=['*****@*****.**'])
Example #12
0
def notify_compatibility(users, job, data, **kw):
    log.info('[%s@%s] Sending notification mail for job %s.'
             % (len(users), notify_compatibility.rate_limit, job.pk))
    set_user(get_task_user())
    dry_run = data['preview_only']
    app_id = job.target_version.application.pk
    stats = collections.defaultdict(int)
    stats['processed'] = 0
    stats['is_dry_run'] = int(dry_run)

    for user in users:
        stats['processed'] += 1

        for a in chain(user.passing_addons, user.failing_addons):
            results = job.result_set.filter(file__version__addon=a)

            a.links = ' '.join(absolutify(reverse('devhub.bulk_compat_result',
                                                  args=[a.slug, r.pk]))
                               for r in results)

            a.compat_link = absolutify(reverse('devhub.versions.edit',
                                               args=[a.pk,
                                                     a.current_version.pk]))


        context = Context({
            'APPLICATION': str(job.application),
            'VERSION': job.target_version.version,
            'PASSING_ADDONS': user.passing_addons,
            'FAILING_ADDONS': user.failing_addons,
        })

        log.info(u'Emailing %s%s for %d addons about '
                 'bulk validation job %s'
                 % (user.email,
                    ' [PREVIEW]' if dry_run else '',
                    len(user.passing_addons) + len(user.failing_addons),
                    job.pk))
        args = (Template(data['subject']).render(context),
                Template(data['text']).render(context))
        kwargs = dict(from_email=settings.DEFAULT_FROM_EMAIL,
                      recipient_list=[user.email])
        if dry_run:
            job.preview_notify_mail(*args, **kwargs)
        else:
            stats['author_emailed'] += 1
            send_mail(*args, **kwargs)
            amo.log(amo.LOG.BULK_VALIDATION_USER_EMAILED,
                    user,
                    details={'passing': [a.id for a in user.passing_addons],
                             'failing': [a.id for a in user.failing_addons],
                             'target': job.target_version.version,
                             'application': app_id})

    log.info('[%s@%s] bulk email stats for job %s: {%s}'
             % (len(users), notify_compatibility.rate_limit, job.pk,
                ', '.join('%s: %s' % (k, stats[k])
                          for k in sorted(stats.keys()))))
Example #13
0
    def email_confirmation_code(self):
        from amo.utils import send_mail

        log.debug("Sending account confirmation code for user (%s)", self)

        url = "%s%s" % (settings.SITE_URL, reverse("users.confirm", args=[self.id, self.confirmationcode]))
        domain = settings.DOMAIN
        t = loader.get_template("users/email/confirm.ltxt")
        c = {"domain": domain, "url": url}
        send_mail(_("Please confirm your email address"), t.render(Context(c)), None, [self.email], use_blacklist=False)
Example #14
0
    def test_blacklist_flag(self):
        to = "*****@*****.**"
        settings.EMAIL_BLACKLIST = (to,)
        success = send_mail("test subject", "test body", recipient_list=[to], fail_silently=False, use_blacklist=True)
        assert success
        eq_(len(mail.outbox), 0)

        success = send_mail("test subject", "test body", recipient_list=[to], fail_silently=False, use_blacklist=False)
        assert success
        eq_(len(mail.outbox), 1)
Example #15
0
 def test_send_attachment(self):
     path = os.path.join(ATTACHMENTS_DIR, "bacon.txt")
     attachments = [(os.path.basename(path), storage.open(path), mimetypes.guess_type(path)[0])]
     send_mail(
         "test subject",
         "test body",
         from_email="*****@*****.**",
         recipient_list=["*****@*****.**"],
         attachments=attachments,
     )
     eq_(attachments, mail.outbox[0].attachments, "Attachments not included")
Example #16
0
def refund_reason(request, contribution, wizard):
    addon = contribution.addon
    if not "request" in wizard.get_progress():
        return redirect("users.support", contribution.pk, "request")
    if contribution.transaction_id is None:
        messages.error(
            request,
            _(
                "A refund cannot be applied for yet. Please try again"
                " later. If this error persists contact "
                "[email protected]."
            ),
        )
        paypal_log.info("Refund requested for contribution with no " "transaction_id: %r" % (contribution.pk,))
        return redirect("users.purchases")

    if contribution.is_instant_refund():
        paypal.refund(contribution.paykey)
        # TODO: Consider requiring a refund reason for instant refunds.
        refund = contribution.enqueue_refund(amo.REFUND_APPROVED_INSTANT)
        paypal_log.info("Refund %r issued for contribution %r" % (refund.pk, contribution.pk))
        # Note: we have to wait for PayPal to issue an IPN before it's
        # completely refunded.
        messages.success(request, _("Refund is being processed."))
        return redirect("users.purchases")

    form = forms.ContactForm(request.POST or None)
    if request.method == "POST" and form.is_valid():
        reason = form.cleaned_data["text"]
        template = jingo.render_to_string(
            request,
            wizard.tpl("emails/refund-request.txt"),
            context={
                "addon": addon,
                "form": form,
                "user": request.amo_user,
                "contribution": contribution,
                "refund_url": contribution.get_absolute_refund_url(),
                "refund_reason": reason,
            },
        )
        log.info("Refund request sent by user: %s for addon: %s" % (request.amo_user.pk, addon.pk))
        # L10n: %s is the addon name.
        send_mail(
            _(u"New Refund Request for %s" % addon.name),
            template,
            settings.NOBODY_EMAIL,
            [smart_str(addon.support_email)],
        )
        # Add this refund request to the queue.
        contribution.enqueue_refund(amo.REFUND_PENDING, reason)
        return redirect(reverse("users.support", args=[contribution.pk, "refund-sent"]))

    return wizard.render(request, wizard.tpl("refund.html"), {"form": form})
Example #17
0
    def restrict(self):
        from amo.utils import send_mail
        log.info(u'User (%s: <%s>) is being restricted and '
                 'its user-generated content removed.' % (self, self.email))
        g = Group.objects.get(rules='Restricted:UGC')
        GroupUser.objects.create(user=self, group=g)
        self.reviews.all().delete()
        self.collections.all().delete()

        t = loader.get_template('users/email/restricted.ltxt')
        send_mail(_('Your account has been restricted'),
                  t.render(Context({})), None, [self.email],
                  use_blacklist=False, real_email=True)
Example #18
0
    def test_dont_localize(self, fake_Context):
        perm_setting = []

        def ctx(d, autoescape):
            perm_setting.append(unicode(d['perm_setting']))
            return TemplateContext(d, autoescape=autoescape)
        fake_Context.side_effect = ctx
        user = UserProfile.objects.all()[0]
        to = user.email
        translation.activate('zh_TW')
        send_mail('test subject', 'test body', perm_setting='reply',
                             recipient_list=[to], fail_silently=False)
        eq_(perm_setting[0], u'an add-on developer replies to my review')
Example #19
0
    def restrict(self):
        from amo.utils import send_mail
        log.info(u'User (%s: <%s>) is being restricted and '
                 'its user-generated content removed.' % (self, self.email))
        g = Group.objects.get(rules='Restricted:UGC')
        GroupUser.objects.create(user=self, group=g)
        self.reviews.all().delete()
        self.collections.all().delete()

        t = loader.get_template('users/email/restricted.ltxt')
        send_mail(_('Your account has been restricted'),
                  t.render(Context({})), None, [self.email],
                  use_blacklist=False)
Example #20
0
    def email_confirmation_code(self):
        from amo.utils import send_mail
        log.debug("Sending account confirmation code for user (%s)", self)

        url = "%s%s" % (settings.SITE_URL,
                        reverse('users.confirm',
                                args=[self.id, self.confirmationcode]))
        domain = settings.DOMAIN
        t = loader.get_template('users/email/confirm.ltxt')
        c = {'domain': domain, 'url': url, }
        send_mail(_("Please confirm your email address"),
                  t.render(Context(c)), None, [self.email],
                  use_blacklist=False)
Example #21
0
def edit(request):
    # Don't use request.amo_user since it has too much caching.
    amouser = UserProfile.objects.get(pk=request.user.id)
    if request.method == 'POST':
        # ModelForm alters the instance you pass in.  We need to keep a copy
        # around in case we need to use it below (to email the user)
        original_email = amouser.email
        form = forms.UserEditForm(request.POST, request.FILES, request=request,
                                  instance=amouser)
        if form.is_valid():
            messages.success(request, _('Profile Updated'))
            if amouser.email != original_email:

                l = {'user': amouser,
                     'mail1': original_email,
                     'mail2': amouser.email}
                log.info(u"User (%(user)s) has requested email change from "
                          "(%(mail1)s) to (%(mail2)s)" % l)
                messages.info(request, _('Email Confirmation Sent'),
                    _(u'An email has been sent to {0} to confirm your new '
                       'email address. For the change to take effect, you '
                       'need to click on the link provided in this email. '
                       'Until then, you can keep logging in with your '
                       'current email address.').format(amouser.email))

                token, hash_ = EmailResetCode.create(amouser.id, amouser.email)
                url = '%s%s' % (settings.SITE_URL,
                                reverse('users.emailchange',
                                        args=[amouser.id, token, hash_]))
                t = loader.get_template('users/email/emailchange.ltxt')
                c = {'domain': settings.DOMAIN, 'url': url}
                send_mail(_('Please confirm your email address '
                            'change at %s' % settings.DOMAIN),
                    t.render(Context(c)), None, [amouser.email],
                    use_blacklist=False, real_email=True)

                # Reset the original email back.  We aren't changing their
                # address until they confirm the new one
                amouser.email = original_email
            form.save()
            return redirect('users.edit')
        else:

            messages.error(request, _('Errors Found'),
                                    _('There were errors in the changes '
                                      'you made. Please correct them and '
                                      'resubmit.'))
    else:
        form = forms.UserEditForm(instance=amouser, request=request)
    return render(request, 'users/edit.html',
                  {'form': form, 'amouser': amouser})
Example #22
0
    def email_confirmation_code(self):
        from amo.utils import send_mail
        log.debug("Sending account confirmation code for user (%s)", self)

        url = "%s%s" % (settings.SITE_URL,
                        reverse('users.confirm',
                                args=[self.id, self.confirmationcode]))
        log.debug("confirmation link (%s)", url)
        domain = settings.DOMAIN
        t = loader.get_template('users/email/confirm.ltxt')
        c = {'domain': domain, 'url': url, }
        send_mail(_("Please confirm your email address"),
                  t.render(Context(c)), None, [self.email],
                  use_blacklist=False, real_email=True)
Example #23
0
def refund_reason(request, contribution, wizard):
    addon = contribution.addon
    if not 'request' in wizard.get_progress():
        return redirect('users.support', contribution.pk, 'request')
    if contribution.transaction_id is None:
        messages.error(
            request,
            _('A refund cannot be applied for yet. Please try again'
              ' later. If this error persists contact '
              '[email protected].'))
        paypal_log.info('Refund requested for contribution with no '
                        'transaction_id: %r' % (contribution.pk, ))
        return redirect('users.purchases')

    if contribution.is_instant_refund():
        paypal.refund(contribution.paykey)
        # TODO: Consider requiring a refund reason for instant refunds.
        refund = contribution.enqueue_refund(amo.REFUND_APPROVED_INSTANT)
        paypal_log.info('Refund %r issued for contribution %r' %
                        (refund.pk, contribution.pk))
        # Note: we have to wait for PayPal to issue an IPN before it's
        # completely refunded.
        messages.success(request, _('Refund is being processed.'))
        return redirect('users.purchases')

    form = forms.ContactForm(request.POST or None)
    if request.method == 'POST' and form.is_valid():
        reason = form.cleaned_data['text']
        template = jingo.render_to_string(
            request,
            wizard.tpl('emails/refund-request.txt'),
            context={
                'addon': addon,
                'form': form,
                'user': request.amo_user,
                'contribution': contribution,
                'refund_url': contribution.get_absolute_refund_url(),
                'refund_reason': reason
            })
        log.info('Refund request sent by user: %s for addon: %s' %
                 (request.amo_user.pk, addon.pk))
        # L10n: %s is the addon name.
        send_mail(_(u'New Refund Request for %s' % addon.name), template,
                  settings.NOBODY_EMAIL, [smart_str(addon.support_email)])
        # Add this refund request to the queue.
        contribution.enqueue_refund(amo.REFUND_PENDING, reason)
        return redirect(
            reverse('users.support', args=[contribution.pk, 'refund-sent']))

    return wizard.render(request, wizard.tpl('refund.html'), {'form': form})
Example #24
0
    def test_blacklist_flag(self):
        to = '*****@*****.**'
        settings.EMAIL_BLACKLIST = (to,)
        success = send_mail('test subject', 'test body',
                            recipient_list=[to], fail_silently=False,
                            use_blacklist=True)
        assert success
        eq_(len(mail.outbox), 0)

        success = send_mail('test subject', 'test body',
                            recipient_list=[to], fail_silently=False,
                            use_blacklist=False)
        assert success
        eq_(len(mail.outbox), 1)
Example #25
0
 def test_qa_whitelist_with_mixed_emails(self):
     assert send_mail('test subject', 'test body',
                      recipient_list=['*****@*****.**', '*****@*****.**'],
                      fail_silently=False)
     eq_(len(mail.outbox), 1)
     eq_(mail.outbox[0].to, set(['*****@*****.**']))
     eq_(FakeEmail.objects.count(), 1)
Example #26
0
    def mail_thankyou(self, request=None):
        """
        Mail a thankyou note for a completed contribution.

        Raises a ``ContributionError`` exception when the contribution
        is not complete or email addresses are not found.
        """
        locale = self._switch_locale()

        # Thankyous must be enabled.
        if not self.addon.enable_thankyou:
            # Not an error condition, just return.
            return

        # Contribution must be complete.
        if not self.transaction_id:
            raise ContributionError('Transaction not complete')

        # Send from support_email, developer's email, or default.
        from_email = settings.DEFAULT_FROM_EMAIL
        if self.addon.support_email:
            from_email = str(self.addon.support_email)
        else:
            try:
                author = self.addon.listed_authors[0]
                if author.email and not author.emailhidden:
                    from_email = author.email
            except (IndexError, TypeError):
                # This shouldn't happen, but the default set above is still ok.
                pass

        # We need the contributor's email.
        to_email = self.post_data['payer_email']
        if not to_email:
            raise ContributionError('Empty payer email')

        # Make sure the url uses the right language.
        # Setting a prefixer would be nicer, but that requires a request.
        url_parts = self.addon.meet_the_dev_url().split('/')
        url_parts[1] = locale.language

        # Buildup the email components.
        t = loader.get_template('stats/contribution-thankyou-email.ltxt')
        c = {
            'thankyou_note': self.addon.thankyou_note,
            'addon_name': self.addon.name,
            'learn_url': '%s%s?src=emailinfo' % (settings.SITE_URL,
                                                 '/'.join(url_parts)),
            'domain': settings.DOMAIN,
        }
        body = t.render(Context(c))
        subject = _('Thanks for contributing to {addon_name}').format(
                    addon_name=self.addon.name)

        # Send the email
        if send_mail(subject, body, from_email, [to_email],
                     fail_silently=True, perm_setting='dev_thanks'):
            # Clear out contributor identifying information.
            del(self.post_data['payer_email'])
            self.save()
Example #27
0
 def test_success_real_mail(self):
     assert send_mail('test subject', 'test body',
                      recipient_list=['*****@*****.**'],
                      fail_silently=False)
     eq_(len(mail.outbox), 1)
     eq_(mail.outbox[0].subject.find('test subject'), 0)
     eq_(mail.outbox[0].body.find('test body'), 0)
Example #28
0
 def test_success_fake_mail(self):
     assert send_mail('test subject', 'test body',
                      recipient_list=['*****@*****.**'],
                      fail_silently=False)
     eq_(len(mail.outbox), 0)
     eq_(FakeEmail.objects.count(), 1)
     eq_(FakeEmail.objects.get().message.endswith('test body'), True)
Example #29
0
    def mail_thankyou(self, request=None):
        """
        Mail a thankyou note for a completed contribution.

        Raises a ``ContributionError`` exception when the contribution
        is not complete or email addresses are not found.
        """
        locale = self._switch_locale()

        # Thankyous must be enabled.
        if not self.addon.enable_thankyou:
            # Not an error condition, just return.
            return

        # Contribution must be complete.
        if not self.transaction_id:
            raise ContributionError('Transaction not complete')

        # Send from support_email, developer's email, or default.
        from_email = settings.DEFAULT_FROM_EMAIL
        if self.addon.support_email:
            from_email = str(self.addon.support_email)
        else:
            try:
                author = self.addon.listed_authors[0]
                if author.email and not author.emailhidden:
                    from_email = author.email
            except (IndexError, TypeError):
                # This shouldn't happen, but the default set above is still ok.
                pass

        # We need the contributor's email.
        to_email = self.post_data['payer_email']
        if not to_email:
            raise ContributionError('Empty payer email')

        # Make sure the url uses the right language.
        # Setting a prefixer would be nicer, but that requires a request.
        url_parts = self.addon.meet_the_dev_url().split('/')
        url_parts[1] = locale.language

        # Buildup the email components.
        t = loader.get_template('stats/contribution-thankyou-email.ltxt')
        c = {
            'thankyou_note': self.addon.thankyou_note,
            'addon_name': self.addon.name,
            'learn_url': '%s%s?src=emailinfo' % (settings.SITE_URL,
                                                 '/'.join(url_parts)),
            'domain': settings.DOMAIN,
        }
        body = t.render(Context(c))
        subject = _('Thanks for contributing to {addon_name}').format(
                    addon_name=self.addon.name)

        # Send the email
        if send_mail(subject, body, from_email, [to_email],
                     fail_silently=True, perm_setting='dev_thanks'):
            # Clear out contributor identifying information.
            del(self.post_data['payer_email'])
            self.save()
Example #30
0
def refund_reason(request, contribution, wizard):
    addon = contribution.addon
    if not 'request' in wizard.get_progress():
        return redirect('users.support', contribution.pk, 'request')
    if contribution.transaction_id is None:
        messages.error(request,
                       _('A refund cannot be applied for yet. Please try again'
                         ' later. If this error persists contact '
                         '[email protected].'))
        paypal_log.info('Refund requested for contribution with no '
                        'transaction_id: %r' % (contribution.pk,))
        return redirect('users.purchases')

    if contribution.is_instant_refund():
        paypal.refund(contribution.paykey)
        # TODO: Consider requiring a refund reason for instant refunds.
        refund = contribution.enqueue_refund(amo.REFUND_APPROVED_INSTANT)
        paypal_log.info('Refund %r issued for contribution %r' %
                        (refund.pk, contribution.pk))
        # Note: we have to wait for PayPal to issue an IPN before it's
        # completely refunded.
        messages.success(request, _('Refund is being processed.'))
        return redirect('users.purchases')

    form = forms.ContactForm(request.POST or None)
    if request.method == 'POST' and form.is_valid():
        reason = form.cleaned_data['text']
        template = jingo.render_to_string(request,
            wizard.tpl('emails/refund-request.txt'),
            context={'addon': addon,
                     'form': form,
                     'user': request.amo_user,
                     'contribution': contribution,
                     'refund_url': contribution.get_absolute_refund_url(),
                     'refund_reason': reason})
        log.info('Refund request sent by user: %s for addon: %s' %
                 (request.amo_user.pk, addon.pk))
        # L10n: %s is the addon name.
        send_mail(_(u'New Refund Request for %s' % addon.name),
                  template, settings.NOBODY_EMAIL,
                  [smart_str(addon.support_email)])
        # Add this refund request to the queue.
        contribution.enqueue_refund(amo.REFUND_PENDING, reason)
        return redirect(reverse('users.support',
                                args=[contribution.pk, 'refund-sent']))

    return wizard.render(request, wizard.tpl('refund.html'), {'form': form})
Example #31
0
 def send_notification(self, version):
     user_log.info('Sending addon update notice to %s for %s' %
                   (self.user.email, self.addon.pk))
     context = Context({
         'name': self.addon.name,
         'url': absolutify(reverse('addons.detail', args=[self.addon.pk])),
         'number': version.version,
         'review': absolutify(reverse('editors.review', args=[version.pk])),
         'SITE_URL': settings.SITE_URL,
     })
     # Not being localised because we don't know the editors locale.
     subject = 'Mozilla Add-ons: %s Updated' % self.addon.name
     template = loader.get_template('editors/emails/notify_update.ltxt')
     send_mail(subject, template.render(Context(context)),
               recipient_list=[self.user.email],
               from_email=settings.EDITORS_EMAIL,
               use_blacklist=False)
Example #32
0
    def test_dont_localize(self, fake_Context):
        perm_setting = []

        def ctx(d, autoescape):
            perm_setting.append(unicode(d['perm_setting']))
            return TemplateContext(d, autoescape=autoescape)

        fake_Context.side_effect = ctx
        user = UserProfile.objects.all()[0]
        to = user.email
        translation.activate('zh_TW')
        send_mail('test subject',
                  'test body',
                  perm_setting='reply',
                  recipient_list=[to],
                  fail_silently=False)
        eq_(perm_setting[0], u'an add-on developer replies to my review')
Example #33
0
 def test_qa_whitelist_with_mixed_emails(self):
     assert send_mail('test subject',
                      'test body',
                      recipient_list=['*****@*****.**', '*****@*****.**'],
                      fail_silently=False)
     eq_(len(mail.outbox), 1)
     eq_(mail.outbox[0].to, ['*****@*****.**'])
     eq_(FakeEmail.objects.count(), 1)
Example #34
0
    def test_blacklist(self):
        to = '*****@*****.**'
        settings.EMAIL_BLACKLIST = (to,)
        success = send_mail('test subject', 'test body',
                            recipient_list=[to], fail_silently=False)

        assert success
        eq_(len(mail.outbox), 0)
Example #35
0
 def test_success_fake_mail(self):
     assert send_mail('test subject',
                      'test body',
                      recipient_list=['*****@*****.**'],
                      fail_silently=False)
     eq_(len(mail.outbox), 0)
     eq_(FakeEmail.objects.count(), 1)
     eq_(FakeEmail.objects.get().message.endswith('test body'), True)
Example #36
0
 def test_success_real_mail(self):
     assert send_mail('test subject',
                      'test body',
                      recipient_list=['*****@*****.**'],
                      fail_silently=False)
     eq_(len(mail.outbox), 1)
     eq_(mail.outbox[0].subject.find('test subject'), 0)
     eq_(mail.outbox[0].body.find('test body'), 0)
Example #37
0
 def send_notification(self, version):
     user_log.info('Sending addon update notice to %s for %s' %
                   (self.user.email, self.addon.pk))
     context = Context({
         'name': self.addon.name,
         'url': absolutify(reverse('addons.detail', args=[self.addon.pk])),
         'number': version.version,
         'review': absolutify(reverse('editors.review',
                                      args=[self.addon.pk])),
         'SITE_URL': settings.SITE_URL,
     })
     # Not being localised because we don't know the editors locale.
     subject = 'Mozilla Add-ons: %s Updated' % self.addon.name
     template = loader.get_template('editors/emails/notify_update.ltxt')
     send_mail(subject, template.render(Context(context)),
               recipient_list=[self.user.email],
               from_email=settings.EDITORS_EMAIL,
               use_blacklist=False)
Example #38
0
 def test_qa_whitelist(self):
     assert send_mail('test subject', 'test body',
                      recipient_list=['*****@*****.**'],
                      fail_silently=False)
     eq_(len(mail.outbox), 1)
     eq_(mail.outbox[0].subject.find('test subject'), 0)
     eq_(mail.outbox[0].body.find('test body'), 0)
     eq_(FakeEmail.objects.count(), 1)
     eq_(FakeEmail.objects.get().message.endswith('test body'), True)
Example #39
0
 def test_qa_whitelist(self):
     assert send_mail('test subject',
                      'test body',
                      recipient_list=['*****@*****.**'],
                      fail_silently=False)
     eq_(len(mail.outbox), 1)
     eq_(mail.outbox[0].subject.find('test subject'), 0)
     eq_(mail.outbox[0].body.find('test body'), 0)
     eq_(FakeEmail.objects.count(), 1)
     eq_(FakeEmail.objects.get().message.endswith('test body'), True)
Example #40
0
    def test_success(self):
        to = '*****@*****.**'
        settings.EMAIL_BLACKLIST = ()
        success = send_mail('test subject', 'test body',
                            recipient_list=[to], fail_silently=False)

        assert success
        eq_(len(mail.outbox), 1)
        assert mail.outbox[0].subject.find('test subject') == 0
        assert mail.outbox[0].body.find('test body') == 0
Example #41
0
 def test_blacklist_flag(self):
     to = '*****@*****.**'
     to2 = '*****@*****.**'
     settings.EMAIL_BLACKLIST = (to,)
     success = send_mail('test subject', 'test body',
                         recipient_list=[to, to2], fail_silently=False,
                         use_blacklist=True)
     assert success
     eq_(len(mail.outbox), 1)
     eq_(mail.outbox[0].to, [to2])
Example #42
0
def support_mozilla(request, contribution, wizard):
    addon = contribution.addon
    form = forms.ContactForm(request.POST)
    if request.method == 'POST':
        if form.is_valid():
            template = jingo.render_to_string(request,
                                wizard.tpl('emails/support-request.txt'),
                                context={'addon': addon, 'form': form,
                                         'contribution': contribution,
                                         'user': request.amo_user})
            log.info('Support request to mozilla by user: %s for addon: %s' %
                     (request.amo_user.pk, addon.pk))
            # L10n: %s is the addon name.
            send_mail(_(u'New Support Request for %s' % addon.name),
                      template, request.amo_user.email, [settings.FLIGTAR])
            return redirect(reverse('users.support',
                                    args=[contribution.pk, 'mozilla-sent']))

    return wizard.render(request, wizard.tpl('mozilla.html'),
                         {'addon': addon, 'form': form})
Example #43
0
def support_mozilla(request, contribution, wizard):
    addon = contribution.addon
    form = forms.ContactForm(request.POST or None)
    if request.method == 'POST':
        if form.is_valid():
            template = jingo.render_to_string(request,
                                wizard.tpl('emails/support-request.txt'),
                                context={'addon': addon, 'form': form,
                                         'contribution': contribution,
                                         'user': request.amo_user})
            log.info('Support request to mozilla by user: %s for addon: %s' %
                     (request.amo_user.pk, addon.pk))
            # L10n: %s is the addon name.
            send_mail(_(u'New Support Request for %s' % addon.name),
                      template, request.amo_user.email,
                      [settings.MARKETPLACE_EMAIL])
            return redirect(reverse('users.support',
                                    args=[contribution.pk, 'mozilla-sent']))

    return wizard.render(request, wizard.tpl('mozilla.html'),
                         {'addon': addon, 'form': form})
Example #44
0
def support_author(request, contribution, wizard):
    addon = contribution.addon
    form = forms.ContactForm(request.POST or None)
    if request.method == 'POST':
        if form.is_valid():
            template = jingo.render_to_string(request,
                                wizard.tpl('emails/support-request.txt'),
                                context={'contribution': contribution,
                                         'addon': addon, 'form': form,
                                         'user': request.amo_user})
            log.info('Support request to dev. by user: %s for addon: %s' %
                     (request.amo_user.pk, addon.pk))
            # L10n: %s is the addon name.
            send_mail(_(u'New Support Request for %s' % addon.name),
                      template, request.amo_user.email,
                      [smart_str(addon.support_email)])
            return redirect(reverse('users.support',
                                    args=[contribution.pk, 'author-sent']))

    return wizard.render(request, wizard.tpl('author.html'),
                         {'addon': addon, 'webapp': addon.is_webapp(),
                          'form': form})
Example #45
0
def tally_job_results(job_id, **kw):
    sql = """select sum(1),
                    sum(case when completed IS NOT NULL then 1 else 0 end)
             from validation_result
             where validation_job_id=%s"""
    cursor = connection.cursor()
    cursor.execute(sql, [job_id])
    total, completed = cursor.fetchone()
    if completed == total:
        # The job has finished.
        job = ValidationJob.objects.get(pk=job_id)
        job.update(completed=datetime.now())
        if job.finish_email:
            send_mail(
                u'Behold! Validation results for %s %s->%s' %
                (amo.APP_IDS[job.application.id].pretty,
                 job.curr_max_version.version, job.target_version.version),
                textwrap.dedent("""
                          Aww yeah
                          %s
                          """ % absolutify(reverse('zadmin.validation'))),
                from_email=settings.DEFAULT_FROM_EMAIL,
                recipient_list=[job.finish_email])
Example #46
0
def refund_reason(request, contribution, wizard):
    addon = contribution.addon
    if not 'request' in wizard.get_progress():
        return redirect('users.support', contribution.pk, 'request')

    if contribution.is_instant_refund():
        paypal.refund(contribution.paykey)
        paypal_log.info('Refund issued for contribution %r' % contribution.pk)
        # Note: we have to wait for PayPal to issue an IPN before it's
        # completely refunded.
        messages.success(request, _('Refund is being processed.'))
        return redirect('users.purchases')

    form = forms.ContactForm(request.POST or None)
    if request.method == 'POST' and form.is_valid():
        url = absolutify(
            urlparams(addon.get_dev_url('issue_refund'),
                      transaction_id=contribution.transaction_id))
        template = jingo.render_to_string(
            request,
            wizard.tpl('emails/refund-request.txt'),
            context={
                'addon': addon,
                'form': form,
                'user': request.amo_user,
                'contribution': contribution,
                'refund_url': url
            })
        log.info('Refund request sent by user: %s for addon: %s' %
                 (request.amo_user.pk, addon.pk))
        # L10n: %s is the addon name.
        send_mail(_(u'New Refund Request for %s' % addon.name), template,
                  settings.NOBODY_EMAIL, [smart_str(addon.support_email)])
        return redirect(
            reverse('users.support', args=[contribution.pk, 'refund-sent']))

    return wizard.render(request, wizard.tpl('refund.html'), {'form': form})
Example #47
0
    def test_user_setting_unchecked(self):
        user = UserProfile.objects.all()[0]
        to = user.email
        n = users.notifications.NOTIFICATIONS_BY_SHORT['reply']
        UserNotification.objects.get_or_create(notification_id=n.id,
                user=user, enabled=False)

        # Confirm we're reading from the database
        eq_(UserNotification.objects.filter(notification_id=n.id).count(), 1)

        success = send_mail('test subject', 'test body', perm_setting='reply',
                            recipient_list=[to], fail_silently=False)

        assert success, "Email wasn't sent"
        eq_(len(mail.outbox), 0)
Example #48
0
    def test_user_mandatory(self):
        """ Make sure there's no unsubscribe link in mandatory emails. """
        user = UserProfile.objects.all()[0]
        to = user.email
        n = users.notifications.NOTIFICATIONS_BY_SHORT['individual_contact']

        UserNotification.objects.get_or_create(notification_id=n.id,
                user=user, enabled=True)

        assert n.mandatory, "Notification isn't mandatory"

        success = send_mail('test subject', 'test body', perm_setting=n,
                            recipient_list=[to], fail_silently=False)

        body = mail.outbox[0].body
        assert "Unsubscribe:" not in body
        assert "You can't unsubscribe from" in body
Example #49
0
    def test_user_setting_default(self):
        user = UserProfile.objects.all()[0]
        to = user.email

        # Confirm there's nothing in the DB and we're using the default
        eq_(UserNotification.objects.count(), 0)

        # Make sure that this is True by default
        setting = users.notifications.NOTIFICATIONS_BY_SHORT['reply']
        eq_(setting.default_checked, True)

        success = send_mail('test subject', 'test body', perm_setting='reply',
                            recipient_list=[to], fail_silently=False)

        assert success, "Email wasn't sent"
        eq_(len(mail.outbox), 1)

        eq_(mail.outbox[0].body.count('users/unsubscribe'), 1)  # bug 676601
Example #50
0
 def test_send_string(self):
     to = '*****@*****.**'
     with self.assertRaises(ValueError):
         send_mail('subj', 'body', recipient_list=to)
Example #51
0
def notify_success(version_pks, job_pk, data, **kw):
    log.info('[%s@None] Updating max version for job %s.' %
             (len(version_pks), job_pk))
    job = ValidationJob.objects.get(pk=job_pk)
    set_user(get_task_user())
    for version in Version.objects.filter(pk__in=version_pks):
        addon = version.addon
        file_pks = version.files.values_list('pk', flat=True)
        errors = (ValidationResult.objects.filter(
            validation_job=job, file__pk__in=file_pks).values_list('errors',
                                                                   flat=True))
        if any(errors):
            log.info('Version %s for addon %s not updated, '
                     'one of the files did not pass validation' %
                     (version.pk, version.addon.pk))
            continue

        app_flag = False
        dry_run = data['preview_only']
        for app in version.apps.filter(
                application=job.curr_max_version.application):
            if (app.max.version == job.curr_max_version.version
                    and job.target_version.version != app.max.version):
                log.info('Updating version %s%s for addon %s from version %s '
                         'to version %s' %
                         (version.pk, ' [DRY RUN]' if dry_run else '',
                          version.addon.pk, job.curr_max_version.version,
                          job.target_version.version))
                app.max = job.target_version
                if not dry_run:
                    app.save()
                app_flag = True

            else:
                log.info('Version %s for addon %s not updated, '
                         'current max version is %s not %s' %
                         (version.pk, version.addon.pk, app.max.version,
                          job.curr_max_version.version))

        if app_flag:
            results = job.result_set.filter(file__version=version)
            context = get_context(addon, version, job, results)
            for author in addon.authors.all():
                log.info(u'Emailing %s%s for addon %s, version %s about '
                         'success from bulk validation job %s' %
                         (author.email, ' [PREVIEW]' if dry_run else '',
                          addon.pk, version.pk, job_pk))
                args = (Template(data['subject']).render(context),
                        Template(data['text']).render(context))
                kwargs = dict(from_email=settings.DEFAULT_FROM_EMAIL,
                              recipient_list=[author.email])
                if dry_run:
                    job.preview_success_mail(*args, **kwargs)
                else:
                    send_mail(*args, **kwargs)
                    amo.log(amo.LOG.BULK_VALIDATION_UPDATED,
                            version.addon,
                            version,
                            details={
                                'version': version.version,
                                'target': job.target_version.version
                            })
Example #52
0
def _notify(context):
    """
    Notify the admins or the developers what happened. Performing a sanity
    check in case something went wrong.
    """
    log.info('Completed run of paypal addon checks: %s' % context['checked'])
    failure_list = []

    if context['limit'] and context['rate'] > context['limit']:
        # This is too cope with something horrible going wrong like, paypal
        # goes into maintenance mode, or netops changes something and all of
        # a sudden all the apps start failing their paypal checks.
        #
        # It would really suck if one errant current job disabled every app
        # on the Marketplace. So this is an attempt to sanity check this.
        for f in context['failed_addons']:
            failure_list.append(absolutify(f.addon.get_url_path()))

        context['failure_list'] = failure_list
        log.info('Too many failed: %s%%, aborting.' % context['limit'])
        template = 'market/emails/check_error.txt'
        send_mail('Cron job error on checking addons',
                  env.get_template(template).render(context),
                  recipient_list=[settings.FLIGTAR],
                  from_email=settings.NOBODY_EMAIL)

    else:
        if not context['failed_addons']:
            return

        for f in context['failed_addons']:

            addon = f.addon
            url = absolutify(addon.get_url_path())
            # Add this to a list so we can tell the admins who got disabled.
            failure_list.append(url)
            if not context['do_disable']:
                continue

            # Write to the developers log that it failed to pass and update
            # the status of the addon.
            amo.log(amo.LOG.PAYPAL_FAILED, addon, user=get_task_user())
            addon.update(status=amo.STATUS_DISABLED)
            authors = [u.email for u in addon.authors.all()]
            if addon.is_webapp():
                template = 'market/emails/check_developer_app.txt'
                subject = _('App disabled on the Firefox Marketplace.')
            else:
                template = 'market/emails/check_developer_addon.txt'
                subject = _('Add-on disabled on the Firefox Marketplace.')

            # Now email the developer and tell them the bad news.
            send_mail(subject,
                      env.get_template(template).render({
                          'addon':
                          addon,
                          'errors':
                          json.loads(f.failure_data)
                      }),
                      recipient_list=authors,
                      from_email=settings.NOBODY_EMAIL)

        context['failure_list'] = failure_list
        # Now email the admins and tell them what happened.
        template = 'market/emails/check_summary.txt'
        send_mail('Cron job disabled %s add-ons' % context['failed'],
                  env.get_template(template).render(context),
                  recipient_list=[settings.FLIGTAR],
                  from_email=settings.NOBODY_EMAIL)
Example #53
0
def notify_compatibility_chunk(users, job, data, **kw):
    log.info('[%s@%s] Sending notification mail for job %s.' %
             (len(users), notify_compatibility.rate_limit, job.pk))
    set_user(get_task_user())
    dry_run = data['preview_only']
    app_id = job.target_version.application
    stats = collections.defaultdict(int)
    stats['processed'] = 0
    stats['is_dry_run'] = int(dry_run)

    for user in users:
        stats['processed'] += 1

        try:
            for a in chain(user.passing_addons, user.failing_addons):
                try:
                    results = job.result_set.filter(file__version__addon=a)

                    a.links = [
                        absolutify(
                            reverse('devhub.bulk_compat_result',
                                    args=[a.slug, r.pk])) for r in results
                    ]

                    v = a.current_version or a.latest_version
                    a.compat_link = absolutify(
                        reverse('devhub.versions.edit', args=[a.pk, v.pk]))
                except:
                    task_error = sys.exc_info()
                    log.error(
                        u'Bulk validation email error for user %s, '
                        u'addon %s: %s: %s' %
                        (user.email, a.slug, task_error[0], task_error[1]),
                        exc_info=False)

            context = Context({
                'APPLICATION':
                unicode(amo.APP_IDS[job.application].pretty),
                'VERSION':
                job.target_version.version,
                'PASSING_ADDONS':
                user.passing_addons,
                'FAILING_ADDONS':
                user.failing_addons,
            })

            log.info(
                u'Emailing %s%s for %d addons about '
                'bulk validation job %s' %
                (user.email, ' [PREVIEW]' if dry_run else '',
                 len(user.passing_addons) + len(user.failing_addons), job.pk))
            args = (Template(data['subject']).render(context),
                    Template(data['text']).render(context))
            kwargs = dict(from_email=settings.DEFAULT_FROM_EMAIL,
                          recipient_list=[user.email])
            if dry_run:
                job.preview_notify_mail(*args, **kwargs)
            else:
                stats['author_emailed'] += 1
                send_mail(*args, **kwargs)
                amo.log(amo.LOG.BULK_VALIDATION_USER_EMAILED,
                        user,
                        details={
                            'passing': [a.id for a in user.passing_addons],
                            'failing': [a.id for a in user.failing_addons],
                            'target': job.target_version.version,
                            'application': app_id
                        })
        except:
            task_error = sys.exc_info()
            log.error(u'Bulk validation email error for user %s: %s: %s' %
                      (user.email, task_error[0], task_error[1]),
                      exc_info=False)

    log.info('[%s@%s] bulk email stats for job %s: {%s}' %
             (len(users), notify_compatibility.rate_limit, job.pk, ', '.join(
                 '%s: %s' % (k, stats[k]) for k in sorted(stats.keys()))))
Example #54
0
 def test_send_multilines_subjects(self):
     send_mail('test\nsubject',
               'test body',
               from_email='*****@*****.**',
               recipient_list=['*****@*****.**'])
     eq_('test subject', mail.outbox[0].subject, 'Subject not stripped')
Example #55
0
 def _mail(self, template, subject, context):
     template = env.get_template(template)
     body = template.render(context)
     send_mail(subject, body, settings.MARKETPLACE_EMAIL,
               [self.user.email], fail_silently=True)
Example #56
0
def notify_success(version_pks, job_pk, data, **kw):
    log.info('[%s@%s] Updating max version for job %s.' %
             (len(version_pks), notify_success.rate_limit, job_pk))
    job = ValidationJob.objects.get(pk=job_pk)
    set_user(get_task_user())
    dry_run = data['preview_only']
    stats = collections.defaultdict(int)
    stats['processed'] = 0
    stats['is_dry_run'] = int(dry_run)
    for version in Version.objects.filter(pk__in=version_pks):
        stats['processed'] += 1
        addon = version.addon
        file_pks = version.files.values_list('pk', flat=True)
        errors = (ValidationResult.objects.filter(
            validation_job=job, file__pk__in=file_pks).values_list('errors',
                                                                   flat=True))
        if any(errors):
            stats['invalid'] += 1
            log.info('Version %s for addon %s not updated, '
                     'one of the files did not pass validation' %
                     (version.pk, version.addon.pk))
            continue

        app_flag = False
        for app in version.apps.filter(
                application=job.curr_max_version.application):
            if (app.max.version_int >= job.curr_max_version.version_int
                    and app.max.version_int < job.target_version.version_int):
                stats['bumped'] += 1
                log.info('Updating version %s%s for addon %s from version %s '
                         'to version %s' %
                         (version.pk, ' [DRY RUN]' if dry_run else '',
                          version.addon.pk, job.curr_max_version.version,
                          job.target_version.version))
                app.max = job.target_version
                if not dry_run:
                    app.save()
                app_flag = True

            else:
                stats['missed_targets'] += 1
                log.info('Version %s for addon %s not updated, '
                         'current max version is %s not %s' %
                         (version.pk, version.addon.pk, app.max.version,
                          job.curr_max_version.version))

        if app_flag:
            results = job.result_set.filter(file__version=version)
            context = get_context(addon, version, job, results)
            for author in addon.authors.all():
                log.info(u'Emailing %s%s for addon %s, version %s about '
                         'success from bulk validation job %s' %
                         (author.email, ' [PREVIEW]' if dry_run else '',
                          addon.pk, version.pk, job_pk))
                args = (Template(data['subject']).render(context),
                        Template(data['text']).render(context))
                kwargs = dict(from_email=settings.DEFAULT_FROM_EMAIL,
                              recipient_list=[author.email])
                if dry_run:
                    job.preview_success_mail(*args, **kwargs)
                else:
                    stats['author_emailed'] += 1
                    send_mail(*args, **kwargs)
                    app_id = job.target_version.application.pk
                    amo.log(amo.LOG.MAX_APPVERSION_UPDATED,
                            version.addon,
                            version,
                            details={
                                'version': version.version,
                                'target': job.target_version.version,
                                'application': app_id
                            })
    log.info('[%s@%s] bulk update stats for job %s: {%s}' %
             (len(version_pks), notify_success.rate_limit, job_pk, ', '.join(
                 '%s: %s' % (k, stats[k]) for k in sorted(stats.keys()))))