Ejemplo n.º 1
0
def account_settings(request):
    ctx = {}

    # Don't use `request.amo_user` because it's too cached.
    user = request.user
    if user.is_authenticated():
        amo_user = user.get_profile()
        if request.method == 'POST':
            if 'authorized_apps' in request.POST:
                ids = request.POST.getlist('authorized_apps')
                Token.objects.filter(pk__in=ids).delete()
                form = forms.UserEditForm(None, instance=amo_user)
            else:
                form = forms.UserEditForm(request.POST, instance=amo_user)
                if form.is_valid():
                    form.save()
                    messages.success(request, _('Settings Updated.'))
                    amo.log(amo.LOG.USER_EDITED)
                    response = redirect('account.settings')
                    # TODO: Detect when we're changing the user's
                    # locale and region and bust on '/', bust on
                    # '/settings' for everything else.
                    bust_fragments(response, '/')
                    return response
                else:
                    messages.form_errors(request)
        else:
            form = forms.UserEditForm(None, instance=amo_user)
        tokens = Token.objects.filter(user=user, token_type=ACCESS_TOKEN)
        ctx = {'form': form, 'amouser': amo_user, 'tokens': tokens}
    else:
        if request.method == 'POST':
            messages.success(request, _('Settings Updated.'))

    return jingo.render(request, 'account/settings.html', ctx)
Ejemplo n.º 2
0
def refund_reason(request, contribution, wizard):
    addon = contribution.addon
    if not 'request' in wizard.get_progress():
        return redirect('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('account.purchases')

    if contribution.is_instant_refund():
        try:
            paypal.refund(contribution.paykey)
        except PaypalError, e:
            paypal_log.error('Paypal error with refund', exc_info=True)
            messages.error(request, _('There was an error with your instant '
                                      'refund.'))
            contribution.record_failed_refund(e)
            return redirect('account.purchases')

        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('account.purchases')
Ejemplo n.º 3
0
def add(request, addon):
    if addon.has_author(request.user):
        # Don't let app owners review their own apps.
        return http.HttpResponseForbidden()

    data = request.POST or None
    form = ReviewForm(data)
    if data and form.is_valid():
        review = Review.objects.create(**_review_details(request, addon, form))
        Addon.objects.invalidate(*[addon])
        amo.log(amo.LOG.ADD_REVIEW, addon, review)
        log.debug('New review: %s' % review.id)
        messages.success(request, _('Your review was successfully added!'))

        # reply_url = addon.get_ratings_url('reply', args=[addon, review.id],
        #                                   add_prefix=False)
        # data = {'name': addon.name,
        #         'rating': '%s out of 5 stars' % details['rating'],
        #         'review': details['body'],
        #         'reply_url': absolutify(reply_url)}

        # emails = addon.values_list('email', flat=True)
        # send_mail('reviews/emails/add_review.ltxt',
        #           u'Mozilla Marketplace User Review: %s' % addon.name,
        #           emails, Context(data), 'new_review')

        return redirect(addon.get_ratings_url('list'))

    return jingo.render(request, 'ratings/add.html', {
        'product': addon,
        'form': form
    })
Ejemplo n.º 4
0
def reply(request, addon, review_id):
    is_admin = acl.action_allowed(request, 'Addons', 'Edit')
    is_author = acl.check_addon_ownership(request, addon, dev=True)
    if not (is_admin or is_author):
        raise PermissionDenied

    review = get_object_or_404(Review.objects, pk=review_id, addon=addon)
    form = ReviewReplyForm(request.POST or None)
    if form.is_valid():
        d = dict(reply_to=review,
                 addon=addon,
                 defaults=dict(user=request.amo_user))
        reply, new = Review.objects.get_or_create(**d)
        for k, v in _review_details(request, addon, form).items():
            setattr(reply, k, v)
        reply.save()
        action = 'New' if new else 'Edited'
        if new:
            amo.log(amo.LOG.ADD_REVIEW, addon, reply)
        else:
            amo.log(amo.LOG.EDIT_REVIEW, addon, reply)

        log.debug('%s reply to %s: %s' % (action, review_id, reply.id))
        messages.success(
            request,
            _('Your reply was successfully added.')
            if new else _('Your reply was successfully updated.'))

    return http.HttpResponse()
Ejemplo n.º 5
0
def _refresh_mdn(request):
    if settings.MDN_LAZY_REFRESH and 'refresh' in request.GET:
        # If you can delay this, please teach me. I give up.
        refresh_mdn_cache()
        messages.success(request,
            'Pulling new content from MDN. Please check back in a few minutes.'
            ' Thanks for all your awesome work! Devs appreciate it!')
Ejemplo n.º 6
0
def account_settings(request):
    ctx = {}

    # Don't use `request.amo_user` because it's too cached.
    user = request.user
    if user.is_authenticated():
        amo_user = user.get_profile()
        form = forms.UserEditForm(request.POST or None, instance=amo_user)
        if request.method == 'POST':
            if form.is_valid():
                form.save()
                messages.success(request, _('Settings Updated.'))
                amo.log(amo.LOG.USER_EDITED)
                response = redirect('account.settings')
                # TODO: Detect when we're changing the user's locale and region
                # and bust on '/', bust on '/settings' for everything else.
                bust_fragments(response, '/')
                return response
            else:
                messages.form_errors(request)
        ctx = {'form': form, 'amouser': amo_user}
    else:
        if request.method == 'POST':
            messages.success(request, _('Settings Updated.'))

    return jingo.render(request, 'account/settings.html', ctx)
Ejemplo n.º 7
0
def refund_reason(request, contribution, wizard):
    if not contribution.can_we_refund():
        return http.HttpResponseForbidden()

    addon = contribution.addon
    if not 'request' in wizard.get_progress():
        return redirect('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('account.purchases')

    if contribution.is_instant_refund():
        try:
            paypal.refund(contribution.paykey)
        except PaypalError:
            paypal_log.error('Paypal error with refund', exc_info=True)
            messages.error(request, _('There was an error with your instant '
                                      'refund.'))
            return redirect('account.purchases')

        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('account.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={'product': 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 app name.
        support_mail(_(u'New Refund Request for %s' % addon.name),
                     template, sender=settings.NOBODY_EMAIL,
                     recipients=[smart_str(addon.support_email)])

        # Add this refund request to the queue.
        contribution.enqueue_refund(amo.REFUND_PENDING, reason)
        return redirect(reverse('support',
                                args=[contribution.pk, 'refund-sent']))

    return wizard.render(request, wizard.tpl('refund.html'),
                         {'product': addon, 'contribution': contribution,
                          'form': form, 'title': _('Request Refund')})
Ejemplo n.º 8
0
def account_settings(request):
    ctx = {}

    # Don't use `request.amo_user` because it's too cached.
    user = request.user
    if user.is_authenticated():
        amo_user = user.get_profile()
        form = forms.UserEditForm(request.POST or None, instance=amo_user)
        if request.method == 'POST':
            if form.is_valid():
                form.save()
                messages.success(request, _('Settings Updated.'))
                amo.log(amo.LOG.USER_EDITED)
                response = redirect('account.settings')
                # TODO: Detect when we're changing the user's locale and region
                # and bust on '/', bust on '/settings' for everything else.
                bust_fragments(response, '/')
                return response
            else:
                messages.form_errors(request)
        ctx = {'form': form, 'amouser': amo_user}
    else:
        if request.method == 'POST':
            messages.success(request, _('Settings Updated.'))

    return jingo.render(request, 'account/settings.html', ctx)
Ejemplo n.º 9
0
def delete_photo(request):
    request.amo_user.update(picture_type='')
    delete_photo_task.delay(request.amo_user.picture_path)
    log.debug(u'User (%s) deleted photo' % request.amo_user)
    messages.success(request, _('Photo Deleted'))
    amo.log(amo.LOG.USER_EDITED)
    return http.HttpResponse()
Ejemplo n.º 10
0
def delete_photo(request):
    request.amo_user.update(picture_type='')
    delete_photo_task.delay(request.amo_user.picture_path)
    log.debug(u'User (%s) deleted photo' % request.amo_user)
    messages.success(request, _('Photo Deleted'))
    amo.log(amo.LOG.USER_EDITED)
    return http.HttpResponse()
Ejemplo n.º 11
0
def add(request, addon):
    if addon.has_author(request.user):
        # Don't let app owners review their own apps.
        return http.HttpResponseForbidden()

    data = request.POST or None
    form = ReviewForm(data)
    if data and form.is_valid():
        review = Review.objects.create(**_review_details(request, addon, form))
        Addon.objects.invalidate(*[addon])
        amo.log(amo.LOG.ADD_REVIEW, addon, review)
        log.debug('New review: %s' % review.id)
        messages.success(request, _('Your review was successfully added!'))

        # reply_url = addon.get_ratings_url('reply', args=[addon, review.id],
        #                                   add_prefix=False)
        # data = {'name': addon.name,
        #         'rating': '%s out of 5 stars' % details['rating'],
        #         'review': details['body'],
        #         'reply_url': absolutify(reply_url)}

        # emails = addon.values_list('email', flat=True)
        # send_mail('reviews/emails/add_review.ltxt',
        #           u'Mozilla Marketplace User Review: %s' % addon.name,
        #           emails, Context(data), 'new_review')

        return redirect(addon.get_ratings_url('list'))

    return jingo.render(request, 'ratings/add.html',
                        {'product': addon, 'form': form})
Ejemplo n.º 12
0
def reply(request, addon, review_id):
    is_admin = acl.action_allowed(request, 'Addons', 'Edit')
    is_author = acl.check_addon_ownership(request, addon, dev=True)
    if not (is_admin or is_author):
        raise PermissionDenied

    review = get_object_or_404(Review.objects, pk=review_id, addon=addon)
    form = ReviewReplyForm(request.POST or None)
    if form.is_valid():
        d = dict(reply_to=review, addon=addon,
                 defaults=dict(user=request.amo_user))
        reply, new = Review.objects.get_or_create(**d)
        for k, v in _review_details(request, addon, form).items():
            setattr(reply, k, v)
        reply.save()
        action = 'New' if new else 'Edited'
        if new:
            amo.log(amo.LOG.ADD_REVIEW, addon, reply)
        else:
            amo.log(amo.LOG.EDIT_REVIEW, addon, reply)

        log.debug('%s reply to %s: %s' % (action, review_id, reply.id))
        messages.success(request,
                         _('Your reply was successfully added.') if new else
                         _('Your reply was successfully updated.'))

    return http.HttpResponse()
Ejemplo n.º 13
0
def abuse(request, profile):
    form = AbuseForm(request.POST or None, request=request)
    if request.method == "POST" and form.is_valid():
        send_abuse_report(request, profile, form.cleaned_data["text"])
        messages.success(request, _("Abuse reported."))
        return redirect(profile.get_url_path())
    else:
        return jingo.render(request, "account/abuse.html", {"abuse_form": form, "profile": profile})
Ejemplo n.º 14
0
def admin_edit(request, user_id):
    amouser = get_object_or_404(UserProfile, pk=user_id)
    form = forms.AdminUserEditForm(request.POST or None, request.FILES or None, instance=amouser)
    if request.method == "POST" and form.is_valid():
        form.save()
        messages.success(request, _("Profile Updated"))
        return redirect("zadmin.index")
    return jingo.render(request, "account/settings.html", {"form": form, "amouser": amouser})
Ejemplo n.º 15
0
def abuse_recaptcha(request, addon):
    form = AbuseForm(request.POST or None, request=request)
    if request.method == "POST" and form.is_valid():
        send_abuse_report(request, addon, form.cleaned_data["text"])
        messages.success(request, _("Abuse reported."))
        return redirect(addon.get_url_path())
    else:
        return jingo.render(request, "detail/abuse_recaptcha.html", {"product": addon, "abuse_form": form})
Ejemplo n.º 16
0
def abuse(request, addon):
    form = AbuseForm(request.POST or None, request=request)
    if request.method == 'POST' and form.is_valid():
        send_abuse_report(request, addon, form.cleaned_data['text'])
        messages.success(request, _('Abuse reported.'))
        return redirect(addon.get_url_path())
    else:
        return jingo.render(request, 'detail/abuse.html',
                            {'product': addon, 'abuse_form': form})
Ejemplo n.º 17
0
def abuse(request, profile):
    form = AbuseForm(request.POST or None, request=request)
    if request.method == 'POST' and form.is_valid():
        send_abuse_report(request, profile, form.cleaned_data['text'])
        messages.success(request, _('Abuse reported.'))
        return redirect(reverse('users.profile', args=[profile.username]))
    else:
        return jingo.render(request, 'account/abuse.html',
                            {'abuse_form': form, 'profile': profile})
Ejemplo n.º 18
0
def abuse(request, profile):
    form = AbuseForm(request.POST or None, request=request)
    if request.method == 'POST' and form.is_valid():
        send_abuse_report(request, profile, form.cleaned_data['text'])
        messages.success(request, _('Abuse reported.'))
        return redirect(profile.get_url_path())
    else:
        return jingo.render(request, 'account/abuse.html',
                            {'abuse_form': form, 'profile': profile})
Ejemplo n.º 19
0
def admin_edit(request, user_id):
    amouser = get_object_or_404(UserProfile, pk=user_id)
    form = forms.AdminUserEditForm(request.POST or None, request.FILES or None,
                                   instance=amouser)
    if request.method == 'POST' and form.is_valid():
        form.save()
        messages.success(request, _('Profile Updated'))
        return redirect('zadmin.index')
    return jingo.render(request, 'account/settings.html',
                        {'form': form, 'amouser': amouser})
Ejemplo n.º 20
0
def admin_edit(request, user_id):
    amouser = get_object_or_404(UserProfile, pk=user_id)
    form = forms.AdminUserEditForm(request.POST or None, request.FILES or None,
                                   instance=amouser)
    if request.method == 'POST' and form.is_valid():
        form.save()
        messages.success(request, _('Profile Updated'))
        return redirect('zadmin.index')
    return jingo.render(request, 'account/settings.html',
                        {'form': form, 'amouser': amouser})
Ejemplo n.º 21
0
def delete(request):
    amouser = request.amo_user
    form = forms.UserDeleteForm(request.POST or None, request=request)
    if request.method == "POST" and form.is_valid():
        messages.success(request, _("Profile Deleted"))
        amouser.anonymize()
        logout(request)
        form = None
        return redirect("users.login")
    return jingo.render(request, "account/delete.html", {"form": form, "amouser": amouser})
Ejemplo n.º 22
0
def delete(request):
    amouser = request.amo_user
    form = forms.UserDeleteForm(request.POST or None, request=request)
    if request.method == 'POST' and form.is_valid():
        messages.success(request, _('Profile Deleted'))
        amouser.anonymize()
        logout(request)
        form = None
        return redirect('users.login')
    return jingo.render(request, 'account/delete.html',
                        {'form': form, 'amouser': amouser})
Ejemplo n.º 23
0
def abuse(request, addon):
    form = AbuseForm(request.POST or None, request=request)
    if request.method == 'POST' and form.is_valid():
        send_abuse_report(request, addon, form.cleaned_data['text'])
        messages.success(request, _('Abuse reported.'))
        return redirect(addon.get_url_path())
    else:
        return jingo.render(request, 'detail/abuse.html', {
            'product': addon,
            'abuse_form': form
        })
Ejemplo n.º 24
0
def motd(request):
    form = None
    motd = unmemoized_get_config('mkt_reviewers_motd')
    if acl.action_allowed(request, 'AppReviewerMOTD', 'Edit'):
        form = MOTDForm(request.POST or None, initial={'motd': motd})
    if form and request.method == 'POST' and form.is_valid():
        set_config(u'mkt_reviewers_motd', form.cleaned_data['motd'])
        messages.success(request, _('Changes successfully saved.'))
        return redirect(reverse('reviewers.apps.motd'))
    data = context(request, form=form)
    return jingo.render(request, 'reviewers/motd.html', data)
Ejemplo n.º 25
0
def motd(request):
    form = None
    motd = unmemoized_get_config('mkt_reviewers_motd')
    if acl.action_allowed(request, 'AppReviewerMOTD', 'Edit'):
        form = MOTDForm(request.POST or None, initial={'motd': motd})
    if form and request.method == 'POST' and form.is_valid():
        set_config(u'mkt_reviewers_motd', form.cleaned_data['motd'])
        messages.success(request, _('Changes successfully saved.'))
        return redirect(reverse('reviewers.apps.motd'))
    data = context(request, form=form)
    return render(request, 'reviewers/motd.html', data)
Ejemplo n.º 26
0
def motd(request):
    form = None
    motd = unmemoized_get_config("mkt_reviewers_motd")
    if acl.action_allowed(request, "AppReviewerMOTD", "Edit"):
        form = MOTDForm(request.POST or None, initial={"motd": motd})
    if form and request.method == "POST" and form.is_valid():
        set_config(u"mkt_reviewers_motd", form.cleaned_data["motd"])
        messages.success(request, _("Changes successfully saved."))
        return redirect(reverse("reviewers.apps.motd"))
    data = context(request, form=form)
    return jingo.render(request, "reviewers/motd.html", data)
Ejemplo n.º 27
0
def delete(request):
    amouser = request.amo_user
    form = forms.UserDeleteForm(request.POST or None, request=request)
    if request.method == 'POST' and form.is_valid():
        messages.success(request, _('Profile Deleted'))
        amouser.anonymize()
        logout(request)
        form = None
        return redirect('users.login')
    return jingo.render(request, 'account/delete.html',
                        {'form': form, 'amouser': amouser})
Ejemplo n.º 28
0
def currency(request, do_redirect=True):
    pre, created = PreApprovalUser.objects.safer_get_or_create(user=request.amo_user)
    currency = CurrencyForm(request.POST or {}, initial={"currency": pre.currency or "USD"})
    if currency.is_valid():
        pre.update(currency=currency.cleaned_data["currency"])
        if do_redirect:
            messages.success(request, _("Currency saved."))
            amo.log(amo.LOG.CURRENCY_UPDATED)
            return redirect(reverse("account.payment"))
    else:
        return jingo.render(request, "account/payment.html", {"preapproval": pre, "currency": currency})
Ejemplo n.º 29
0
def abuse(request, user_id):
    user = get_object_or_404(UserProfile, pk=user_id)
    form = AbuseForm(request.POST or None, request=request)
    if request.method == 'POST' and form.is_valid():
        send_abuse_report(request, user, form.cleaned_data['text'])
        messages.success(request, _('Abuse reported.'))
        # We don't have a profile page to redirect back to. Once the abuse
        # is reported, that would be the place I'd recommend redirecting
        # back to.
        return redirect('/')
    else:
        return jingo.render(request, 'account/abuse.html',
                            {'user': user, 'abuse_form': form})
Ejemplo n.º 30
0
def account_settings(request):
    # Don't use `request.amo_user` because it's too cached.
    amo_user = request.amo_user.user.get_profile()
    form = forms.UserEditForm(request.POST or None, instance=amo_user)
    if request.method == 'POST':
        if form.is_valid():
            form.save()
            messages.success(request, _('Profile Updated'))
            amo.log(amo.LOG.USER_EDITED)
            return redirect('account.settings')
        else:
            messages.form_errors(request)
    return jingo.render(request, 'account/settings.html',
                        {'form': form, 'amouser': amo_user})
Ejemplo n.º 31
0
def currency(request, do_redirect=True):
    pre, created = (PreApprovalUser.objects
                        .safer_get_or_create(user=request.amo_user))
    currency = CurrencyForm(request.POST or {},
                            initial={'currency': pre.currency or 'USD'})
    if currency.is_valid():
        pre.update(currency=currency.cleaned_data['currency'])
        if do_redirect:
            messages.success(request, _('Currency saved.'))
            return redirect(reverse('account.payment'))
    else:
        return jingo.render(request, 'account/payment.html',
                            {'preapproval': pre,
                             'currency': currency})
Ejemplo n.º 32
0
def transaction_refund(request, uuid):
    contrib = get_object_or_404(Contribution, uuid=uuid,
                                type=amo.CONTRIB_PURCHASE)
    if contrib.has_refund():
        messages.error(request, _('A refund has already been processed.'))
        return redirect(reverse('lookup.transaction_summary', args=[uuid]))

    form = TransactionRefundForm(request.POST)
    if not form.is_valid():
        messages.error(request, str(form.errors))
        return redirect(reverse('lookup.transaction_summary', args=[uuid]))

    try:
        res = client.api.bango.refund.post({'uuid': contrib.transaction_id})
    except (HttpClientError, HttpServerError):
        # Either doing something not supposed to or Solitude had an issue.
        log.exception('Refund error: %s' % uuid)
        messages.error(
            request,
            _('You cannot make a refund request for this transaction.'))
        return redirect(reverse('lookup.transaction_summary', args=[uuid]))

    if res['status'] == STATUS_PENDING:
        # Create pending Refund.
        contrib.enqueue_refund(
            status=amo.REFUND_PENDING,
            refund_reason=form.cleaned_data['refund_reason'],
            user=request.amo_user)
        log.info('Refund pending: %s' % uuid)
        email_buyer_refund_pending(contrib)
        messages.success(
            request, _('Refund for this transaction now pending.'))
    elif res['status'] == STATUS_COMPLETED:
        # Create approved Refund.
        contrib.enqueue_refund(
            status=amo.REFUND_APPROVED,
            refund_reason=form.cleaned_data['refund_reason'],
            user=request.amo_user)
        log.info('Refund approved: %s' % uuid)
        email_buyer_refund_approved(contrib)
        messages.success(
            request, _('Refund for this transaction successfully approved.'))
    elif res['status'] == STATUS_FAILED:
        # Bango no like.
        log.error('Refund failed: %s' % uuid)
        messages.error(
            request, _('Refund request for this transaction failed.'))

    return redirect(reverse('lookup.transaction_summary', args=[uuid]))
Ejemplo n.º 33
0
def currency(request, do_redirect=True):
    pre, created = (PreApprovalUser.objects
                        .safer_get_or_create(user=request.amo_user))
    currency = CurrencyForm(request.POST or {},
                            initial={'currency': pre.currency or 'USD'})
    if currency.is_valid():
        pre.update(currency=currency.cleaned_data['currency'])
        if do_redirect:
            messages.success(request, _('Currency saved.'))
            amo.log(amo.LOG.CURRENCY_UPDATED)
            return redirect(reverse('account.payment'))
    else:
        return jingo.render(request, 'account/payment.html',
                            {'preapproval': pre,
                             'currency': currency})
Ejemplo n.º 34
0
Archivo: views.py Proyecto: vdt/zamboni
def account_settings(request):
    # Don't use `request.amo_user` because it's too cached.
    amo_user = request.amo_user.user.get_profile()
    form = forms.UserEditForm(request.POST or None, request.FILES or None,
                              request=request, instance=amo_user)
    if request.method == 'POST':
        if form.is_valid():
            form.save()
            messages.success(request, _('Profile Updated'))
            amo.log(amo.LOG.USER_EDITED)
            return redirect('account.settings')
        else:
            messages.form_errors(request)
    return jingo.render(request, 'account/settings.html',
                        {'form': form, 'amouser': amo_user})
Ejemplo n.º 35
0
def add(request, addon):
    if addon.has_author(request.user):
        # Don't let app owners review their own apps.
        return http.HttpResponseForbidden()

    data = request.POST or None
    form = ReviewForm(data)
    if data and form.is_valid():
        review = Review.objects.create(**_review_details(request, addon, form))
        Addon.objects.invalidate(*[addon])
        amo.log(amo.LOG.ADD_REVIEW, addon, review)
        log.debug('New review: %s' % review.id)
        messages.success(request, _('Your review was successfully added!'))

        return redirect(addon.get_ratings_url('list'))

    return jingo.render(request, 'ratings/add.html',
                        {'product': addon, 'form': form})
Ejemplo n.º 36
0
def add(request, addon):
    if addon.has_author(request.user):
        # Don't let app owners review their own apps.
        return http.HttpResponseForbidden()

    data = request.POST or None
    form = forms.RatingForm(data)
    if data and form.is_valid():
        rating = Rating.objects.create(**_review_details(request, addon, form))
        amo.log(amo.LOG.ADD_REVIEW, addon, rating)
        log.debug('New rating: %s' % rating.id)
        messages.success(request, _('Your review was successfully added!'))
        return redirect(addon.get_detail_url() + '#reviews')
        # TODO: When rating list is done uncomment this (bug 755954).
        #return redirect(addon.get_ratings_url('list'))

    return jingo.render(request, 'ratings/add.html', {
        'product': addon,
        'form': form
    })
Ejemplo n.º 37
0
def payment(request, status=None):
    # Note this is not post required, because PayPal does not reply with a
    # POST but a GET, that's a sad face.
    pre, created = PreApprovalUser.objects.safer_get_or_create(user=request.amo_user)

    context = {"preapproval": pre, "currency": CurrencyForm(initial={"currency": pre.currency or "USD"})}

    if status:
        data = request.session.get("setup-preapproval", {})

        context["status"] = status

        if status == "complete":
            # The user has completed the setup at PayPal and bounced back.
            if "setup-preapproval" in request.session:
                if waffle.flag_is_active(request, "solitude-payments"):
                    client.put_preapproval(data={"uuid": request.amo_user}, pk=data["solitude-key"])

                paypal_log.info(u"Preapproval key created: %s" % request.amo_user.pk)
                amo.log(amo.LOG.PREAPPROVAL_ADDED)
                # TODO(solitude): once this is turned off, we will want to
                # keep preapproval table populated with something, perhaps
                # a boolean inplace of pre-approval key.
                pre.update(paypal_key=data.get("key"), paypal_expiry=data.get("expiry"))

                # If there is a target, bounce to it and don't show a message
                # we'll let whatever set this up worry about that.
                if data.get("complete"):
                    return http.HttpResponseRedirect(data["complete"])

                messages.success(request, _("You're all set for instant app purchases with PayPal."))
                del request.session["setup-preapproval"]

        elif status == "cancel":
            # The user has chosen to cancel out of PayPal. Nothing really
            # to do here, PayPal just bounce to the cancel page if defined.
            if data.get("cancel"):
                return http.HttpResponseRedirect(data["cancel"])

            messages.success(request, _("Your payment pre-approval has been cancelled."))

        elif status == "remove":
            # The user has an pre approval key set and chooses to remove it
            if waffle.flag_is_active(request, "solitude-payments"):
                other = client.lookup_buyer_paypal(request.amo_user)
                if other:
                    client.patch_buyer_paypal(pk=other["resource_pk"], data={"key": ""})

            if pre.paypal_key:
                # TODO(solitude): again, we'll want to maintain some local
                # state in zamboni, so this will probably change to a
                # boolean in the future.
                pre.update(paypal_key="")

                amo.log(amo.LOG.PREAPPROVAL_REMOVED)
                messages.success(request, _("Your payment pre-approval has been disabled."))
                paypal_log.info(u"Preapproval key removed for user: %s" % request.amo_user)

    return jingo.render(request, "account/payment.html", context)
Ejemplo n.º 38
0
Archivo: views.py Proyecto: vdt/zamboni
def refund_reason(request, contribution, wizard):
    addon = contribution.addon
    if not 'request' in wizard.get_progress():
        return redirect('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('account.purchases')

    if contribution.is_refunded():
        messages.error(request, _('This has already been refunded.'))
        paypal_log.info('Already refunded: %s' % contribution.pk)
        return redirect('account.purchases')

    if contribution.is_instant_refund():
        try:
            paypal.refund(contribution.paykey)
        except PaypalError, e:
            paypal_log.error('Paypal error with refund', exc_info=True)
            messages.error(request, _('There was an error with your instant '
                                      'refund.'))
            contribution.record_failed_refund(e)
            return redirect('account.purchases')

        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.'))
        amo.log(amo.LOG.REFUND_INSTANT, addon)
        return redirect('account.purchases')
Ejemplo n.º 39
0
Archivo: views.py Proyecto: vdt/zamboni
def payment(request, status=None):
    # Note this is not post required, because PayPal does not reply with a
    # POST but a GET, that's a sad face.
    pre, created = (PreApprovalUser.objects
                                   .safer_get_or_create(user=request.amo_user))

    context = {'preapproval': pre,
               'currency': CurrencyForm(initial={'currency':
                                                 pre.currency or 'USD'})}

    if status:
        data = request.session.get('setup-preapproval', {})

        context['status'] = status

        if status == 'complete':
            # The user has completed the setup at PayPal and bounced back.
            if 'setup-preapproval' in request.session:
                paypal_log.info(u'Preapproval key created for user: %s, %s.' %
                                (request.amo_user.pk, data['key'][:5]))
                amo.log(amo.LOG.PREAPPROVAL_ADDED)
                pre.update(paypal_key=data.get('key'),
                           paypal_expiry=data.get('expiry'))

                # If there is a target, bounce to it and don't show a message
                # we'll let whatever set this up worry about that.
                if data.get('complete'):
                    return redirect(data['complete'])

                messages.success(request,
                    _("You're all set for instant app purchases with PayPal."))
                del request.session['setup-preapproval']

        elif status == 'cancel':
            # The user has chosen to cancel out of PayPal. Nothing really
            # to do here, PayPal just bounce to the cancel page if defined.
            if data.get('cancel'):
                return redirect(data['cancel'])

            messages.success(request,
                _('Your payment pre-approval has been cancelled.'))

        elif status == 'remove':
            # The user has an pre approval key set and chooses to remove it
            if pre.paypal_key:
                pre.update(paypal_key='')
                amo.log(amo.LOG.PREAPPROVAL_REMOVED)
                messages.success(request,
                    _('Your payment pre-approval has been disabled.'))
                paypal_log.info(u'Preapproval key removed for user: %s'
                                % request.amo_user)

    return jingo.render(request, 'account/payment.html', context)
Ejemplo n.º 40
0
def payment(request, status=None):
    # Note this is not post required, because PayPal does not reply with a
    # POST but a GET, that's a sad face.
    if status:
        pre, created = (PreApprovalUser.objects
                        .safer_get_or_create(user=request.amo_user))
        data = request.session.get('setup-preapproval', {})

        if status == 'complete':
            # The user has completed the setup at PayPal and bounced back.
            if 'setup-preapproval' in request.session:
                paypal_log.info(u'Preapproval key created for user: %s'
                                % request.amo_user)
                pre.update(paypal_key=data.get('key'),
                           paypal_expiry=data.get('expiry'))

                # If there is a target, bounce to it and don't show a message
                # we'll let whatever set this up worry about that.
                if data.get('complete'):
                    return redirect(data['complete'])

                messages.success(request, _('Wallet set up.'))
                del request.session['setup-preapproval']

        elif status == 'cancel':
            # The user has chosen to cancel out of PayPal. Nothing really
            # to do here, PayPal just bounce to the cancel page if defined.
            if data.get('cancel'):
                return redirect(data['cancel'])

            messages.success(request, _('Wallet changes cancelled.'))

        elif status == 'remove':
            # The user has an pre approval key set and chooses to remove it
            if pre.paypal_key:
                pre.update(paypal_key='')
                messages.success(request, _('Wallet removed.'))
                paypal_log.info(u'Preapproval key removed for user: %s'
                                % request.amo_user)

        context = {'preapproval': pre}
    else:
        context = {'preapproval': request.amo_user.get_preapproval()}

    return jingo.render(request, 'account/payment.html', context)
Ejemplo n.º 41
0
def add(request, addon):
    if addon.has_author(request.user):
        # Don't let app owners review their own apps.
        raise PermissionDenied

    # Get user agent of user submitting review. If there is an install with
    # logged user agent that matches the current user agent, hook up that
    # install's client data with the rating. If there aren't any install that
    # match, use the most recent install. This implies that user must have an
    # install to submit a review, but not sure if that logic is worked in, so
    # default client_data to None.
    client_data = None
    user_agent = request.META.get('HTTP_USER_AGENT', '')
    install = (Installed.objects.filter(user=request.user, addon=addon)
                                .order_by('-created'))
    install_w_user_agent = (install.filter(client_data__user_agent=user_agent)
                                   .order_by('-created'))
    has_review = False
    try:
        if install_w_user_agent:
            client_data = install_w_user_agent[0].client_data
        elif install:
            client_data = install[0].client_data
    except ClientData.DoesNotExist:
        client_data = None

    data = request.POST or None

    # Try to get an existing review of the app by this user if we can.
    try:
        existing_review = Review.objects.valid().filter(addon=addon,
                                                        user=request.user)[0]
    except IndexError:
        # If one doesn't exist, set it to None.
        existing_review = None

    # If the user is posting back, try to process the submission.
    if data:
        form = ReviewForm(data)
        if form.is_valid():
            cleaned = form.cleaned_data
            if existing_review:
                # If there's a review to overwrite, overwrite it.
                if (cleaned['body'] != existing_review.body or
                    cleaned['rating'] != existing_review.rating):
                    existing_review.body = cleaned['body']
                    existing_review.rating = cleaned['rating']
                    ip = request.META.get('REMOTE_ADDR', '')
                    existing_review.ip_address = ip
                    if 'flag' in cleaned and cleaned['flag']:
                        existing_review.flag = True
                        existing_review.editorreview = True
                        rf = ReviewFlag(review=existing_review,
                                user_id=request.user.id,
                                flag=ReviewFlag.OTHER,
                                note='URLs')
                        rf.save()
                    existing_review.save()
                    # Update ratings and review counts.
                    addon_review_aggregates.delay(addon.id,
                                                  using='default')

                amo.log(amo.LOG.EDIT_REVIEW, addon, existing_review)
                log.debug('[Review:%s] Edited by %s' % (existing_review.id,
                                                        request.user.id))
                messages.success(request,
                                 _('Your review was updated successfully!'))

                # If there is a developer reply to the review, delete it. We do
                # this per bug 777059.
                try:
                    reply = existing_review.replies.all()[0]
                except IndexError:
                    pass
                else:
                    log.debug('[Review:%s] Deleted reply to %s' %
                                  (reply.id, existing_review.id))
                    reply.delete()

            else:
                # If there isn't a review to overwrite, create a new review.
                review = Review.objects.create(client_data=client_data,
                        **_review_details(request, addon, form))
                if 'flag' in cleaned and cleaned['flag']:
                    rf = ReviewFlag(review=review,
                                user_id=request.user.id,
                                flag=ReviewFlag.OTHER,
                                note='URLs')
                    rf.save()
                amo.log(amo.LOG.ADD_REVIEW, addon, review)
                log.debug('[Review:%s] Created by user %s ' %
                          (review.id, request.user.id))
                messages.success(request,
                                 _('Your review was successfully added!'))

            Addon.objects.invalidate(*[addon])
            return redirect(addon.get_ratings_url('list'))

        # If the form isn't valid, we've set `form` so that it can be used when
        # the template is rendered below.

    elif existing_review:
        # If the user isn't posting back but has an existing review, populate
        # the form with their existing review and rating.
        form = ReviewForm({'rating': existing_review.rating or 1,
                           'body': existing_review.body})
        has_review = True
    else:
        # If the user isn't posting back and doesn't have an existing review,
        # just show a blank version of the form.
        form = ReviewForm()

    # Get app's support url, either from support flow if contribution exists or
    # author's support url.
    support_email = str(addon.support_email) if addon.support_email else None
    try:
        contrib_id = (Contribution.objects
                      .filter(user=request.user, addon=addon,
                              type__in=(amo.CONTRIB_PURCHASE,
                                        amo.CONTRIB_INAPP,
                                        amo.CONTRIB_REFUND))
                      .order_by('-created')[0].id)
        support_url = reverse('support', args=[contrib_id])
    except IndexError:
        support_url = addon.support_url

    return jingo.render(request, 'ratings/add.html',
                        {'product': addon, 'form': form,
                         'support_url': support_url,
                         'has_review': has_review,
                         'support_email': support_email})
Ejemplo n.º 42
0
def transaction_refund(request, tx_uuid):
    contrib = get_object_or_404(Contribution, uuid=tx_uuid, type=amo.CONTRIB_PURCHASE)
    refund_contribs = contrib.get_refund_contribs()
    refund_contrib = refund_contribs[0] if refund_contribs.exists() else None

    if refund_contrib:
        messages.error(request, _("A refund has already been processed."))
        return redirect(reverse("lookup.transaction_summary", args=[tx_uuid]))

    form = TransactionRefundForm(request.POST)
    if not form.is_valid():
        return jingo.render(
            request,
            "lookup/transaction_summary.html",
            dict(
                {"uuid": tx_uuid, "tx_refund_form": form, "tx_form": TransactionSearchForm()}.items()
                + _transaction_summary(tx_uuid).items()
            ),
        )

    data = {"uuid": contrib.transaction_id, "manual": form.cleaned_data["manual"]}
    if settings.BANGO_FAKE_REFUNDS:
        data["fake_response_status"] = {"responseCode": form.cleaned_data["fake"]}

    try:
        res = client.api.bango.refund.post(data)
    except (HttpClientError, HttpServerError):
        # Either doing something not supposed to or Solitude had an issue.
        log.exception("Refund error: %s" % tx_uuid)
        messages.error(request, _("You cannot make a refund request for this transaction."))
        return redirect(reverse("lookup.transaction_summary", args=[tx_uuid]))

    if res["status"] in [PENDING, COMPLETED]:
        # Create refund Contribution by cloning the payment Contribution.
        refund_contrib = Contribution.objects.get(id=contrib.id)
        refund_contrib.id = None
        refund_contrib.save()
        refund_contrib.update(
            type=amo.CONTRIB_REFUND,
            related=contrib,
            uuid=hashlib.md5(str(uuid.uuid4())).hexdigest(),
            amount=-refund_contrib.amount if refund_contrib.amount else None,
            transaction_id=res["uuid"],
        )

    if res["status"] == PENDING:
        # Create pending Refund.
        refund_contrib.enqueue_refund(
            amo.REFUND_PENDING, request.amo_user, refund_reason=form.cleaned_data["refund_reason"]
        )
        log.info("Refund pending: %s" % tx_uuid)
        email_buyer_refund_pending(contrib)
        messages.success(request, _("Refund for this transaction now pending."))
    elif res["status"] == COMPLETED:
        # Create approved Refund.
        refund_contrib.enqueue_refund(
            amo.REFUND_APPROVED, request.amo_user, refund_reason=form.cleaned_data["refund_reason"]
        )
        log.info("Refund approved: %s" % tx_uuid)
        email_buyer_refund_approved(contrib)
        messages.success(request, _("Refund for this transaction successfully approved."))
    elif res["status"] == FAILED:
        # Bango no like.
        log.error("Refund failed: %s" % tx_uuid)
        messages.error(request, _("Refund request for this transaction failed."))

    return redirect(reverse("lookup.transaction_summary", args=[tx_uuid]))
Ejemplo n.º 43
0
def add(request, addon):
    if addon.has_author(request.user):
        # Don't let app owners review their own apps.
        raise PermissionDenied

    if (request.user.is_authenticated() and request.method == 'GET'
            and (not request.MOBILE or request.TABLET)):
        return detail(request, app_slug=addon.app_slug, add_review=True)

    # Get user agent of user submitting review. If there is an install with
    # logged user agent that matches the current user agent, hook up that
    # install's client data with the rating. If there aren't any install that
    # match, use the most recent install. This implies that user must have an
    # install to submit a review, but not sure if that logic is worked in, so
    # default client_data to None.
    client_data = None
    user_agent = request.META.get('HTTP_USER_AGENT', '')
    install = (Installed.objects.filter(user=request.user,
                                        addon=addon).order_by('-created'))
    install_w_user_agent = (install.filter(
        client_data__user_agent=user_agent).order_by('-created'))
    has_review = False
    try:
        if install_w_user_agent:
            client_data = install_w_user_agent[0].client_data
        elif install:
            client_data = install[0].client_data
    except ClientData.DoesNotExist:
        client_data = None

    data = request.POST or None

    # Try to get an existing review of the app by this user if we can.
    filters = dict(addon=addon, user=request.user)
    if addon.is_packaged:
        filters['version'] = addon.current_version

    try:
        existing_review = Review.objects.valid().filter(**filters)[0]
    except IndexError:
        existing_review = None

    # If the user is posting back, try to process the submission.
    if data:
        form = ReviewForm(data)
        if form.is_valid():
            cleaned = form.cleaned_data
            if existing_review:
                # If there's a review to overwrite, overwrite it.
                if (cleaned['body'] != existing_review.body
                        or cleaned['rating'] != existing_review.rating):
                    existing_review.body = cleaned['body']
                    existing_review.rating = cleaned['rating']
                    ip = request.META.get('REMOTE_ADDR', '')
                    existing_review.ip_address = ip
                    if 'flag' in cleaned and cleaned['flag']:
                        existing_review.flag = True
                        existing_review.editorreview = True
                        rf = ReviewFlag(review=existing_review,
                                        user_id=request.user.id,
                                        flag=ReviewFlag.OTHER,
                                        note='URLs')
                        rf.save()
                    existing_review.save()

                amo.log(amo.LOG.EDIT_REVIEW, addon, existing_review)
                log.debug('[Review:%s] Edited by %s' %
                          (existing_review.id, request.user.id))
                messages.success(request,
                                 _('Your review was updated successfully!'))
            else:
                # If there isn't a review to overwrite, create a new review.
                review = Review.objects.create(client_data=client_data,
                                               **_review_details(
                                                   request, addon, form))
                if 'flag' in cleaned and cleaned['flag']:
                    rf = ReviewFlag(review=review,
                                    user_id=request.user.id,
                                    flag=ReviewFlag.OTHER,
                                    note='URLs')
                    rf.save()
                amo.log(amo.LOG.ADD_REVIEW, addon, review)
                log.debug('[Review:%s] Created by user %s ' %
                          (review.id, request.user.id))
                messages.success(request,
                                 _('Your review was successfully added!'))
                record_action('new-review', request, {'app-id': addon.id})

            return redirect(addon.get_ratings_url('list'))

        # If the form isn't valid, we've set `form` so that it can be used when
        # the template is rendered below.

    elif existing_review:
        # If the user isn't posting back but has an existing review, populate
        # the form with their existing review and rating.
        form = ReviewForm({
            'rating': existing_review.rating or 1,
            'body': existing_review.body
        })
        has_review = True
    else:
        # If the user isn't posting back and doesn't have an existing review,
        # just show a blank version of the form.
        form = ReviewForm()

    support_email = str(addon.support_email) if addon.support_email else None
    return jingo.render(
        request, 'ratings/add.html', {
            'product': addon,
            'form': form,
            'support_url': addon.support_url,
            'has_review': has_review,
            'support_email': support_email,
            'page_parent':
            addon.get_detail_url() if not existing_review else ''
        })
Ejemplo n.º 44
0
def add(request, addon):
    if addon.has_author(request.user):
        # Don't let app owners review their own apps.
        raise PermissionDenied

    # Get user agent of user submitting review. If there is an install with
    # logged user agent that matches the current user agent, hook up that
    # install's client data with the rating. If there aren't any install that
    # match, use the most recent install. This implies that user must have an
    # install to submit a review, but not sure if that logic is worked in, so
    # default client_data to None.
    client_data = None
    user_agent = request.META.get('HTTP_USER_AGENT', '')
    install = (Installed.objects.filter(user=request.user,
                                        addon=addon).order_by('-created'))
    install_w_user_agent = (install.filter(
        client_data__user_agent=user_agent).order_by('-created'))
    has_review = False
    try:
        if install_w_user_agent:
            client_data = install_w_user_agent[0].client_data
        elif install:
            client_data = install[0].client_data
    except ClientData.DoesNotExist:
        client_data = None

    data = request.POST or None

    # Try to get an existing review of the app by this user if we can.
    try:
        existing_review = Review.objects.valid().filter(addon=addon,
                                                        user=request.user)[0]
    except IndexError:
        # If one doesn't exist, set it to None.
        existing_review = None

    # If the user is posting back, try to process the submission.
    if data:
        form = ReviewForm(data)
        if form.is_valid():
            cleaned = form.cleaned_data
            if existing_review:
                # If there's a review to overwrite, overwrite it.
                if (cleaned['body'] != existing_review.body
                        or cleaned['rating'] != existing_review.rating):
                    existing_review.body = cleaned['body']
                    existing_review.rating = cleaned['rating']
                    ip = request.META.get('REMOTE_ADDR', '')
                    existing_review.ip_address = ip
                    if 'flag' in cleaned and cleaned['flag']:
                        existing_review.flag = True
                        existing_review.editorreview = True
                        rf = ReviewFlag(review=existing_review,
                                        user_id=request.user.id,
                                        flag=ReviewFlag.OTHER,
                                        note='URLs')
                        rf.save()
                    existing_review.save()
                    # Update ratings and review counts.
                    addon_review_aggregates.delay(addon.id, using='default')

                amo.log(amo.LOG.EDIT_REVIEW, addon, existing_review)
                log.debug('[Review:%s] Edited by %s' %
                          (existing_review.id, request.user.id))
                messages.success(request,
                                 _('Your review was updated successfully!'))

                # If there is a developer reply to the review, delete it. We do
                # this per bug 777059.
                try:
                    reply = existing_review.replies.all()[0]
                except IndexError:
                    pass
                else:
                    log.debug('[Review:%s] Deleted reply to %s' %
                              (reply.id, existing_review.id))
                    reply.delete()

            else:
                # If there isn't a review to overwrite, create a new review.
                review = Review.objects.create(client_data=client_data,
                                               **_review_details(
                                                   request, addon, form))
                if 'flag' in cleaned and cleaned['flag']:
                    rf = ReviewFlag(review=review,
                                    user_id=request.user.id,
                                    flag=ReviewFlag.OTHER,
                                    note='URLs')
                    rf.save()
                amo.log(amo.LOG.ADD_REVIEW, addon, review)
                log.debug('[Review:%s] Created by user %s ' %
                          (review.id, request.user.id))
                messages.success(request,
                                 _('Your review was successfully added!'))

            Addon.objects.invalidate(*[addon])
            return redirect(addon.get_ratings_url('list'))

        # If the form isn't valid, we've set `form` so that it can be used when
        # the template is rendered below.

    elif existing_review:
        # If the user isn't posting back but has an existing review, populate
        # the form with their existing review and rating.
        form = ReviewForm({
            'rating': existing_review.rating or 1,
            'body': existing_review.body
        })
        has_review = True
    else:
        # If the user isn't posting back and doesn't have an existing review,
        # just show a blank version of the form.
        form = ReviewForm()

    # Get app's support url, either from support flow if contribution exists or
    # author's support url.
    support_email = str(addon.support_email) if addon.support_email else None
    try:
        contrib_id = (Contribution.objects.filter(
            user=request.user,
            addon=addon,
            type__in=(amo.CONTRIB_PURCHASE, amo.CONTRIB_INAPP,
                      amo.CONTRIB_REFUND)).order_by('-created')[0].id)
        support_url = reverse('support', args=[contrib_id])
    except IndexError:
        support_url = addon.support_url

    return jingo.render(
        request, 'ratings/add.html', {
            'product': addon,
            'form': form,
            'support_url': support_url,
            'has_review': has_review,
            'support_email': support_email
        })
Ejemplo n.º 45
0
def transaction_refund(request, tx_uuid):
    contrib = get_object_or_404(Contribution,
                                uuid=tx_uuid,
                                type=amo.CONTRIB_PURCHASE)
    refund_contribs = contrib.get_refund_contribs()
    refund_contrib = refund_contribs[0] if refund_contribs.exists() else None

    if refund_contrib:
        messages.error(request, _('A refund has already been processed.'))
        return redirect(reverse('lookup.transaction_summary', args=[tx_uuid]))

    form = TransactionRefundForm(request.POST)
    if not form.is_valid():
        return jingo.render(
            request, 'lookup/transaction_summary.html',
            dict({
                'uuid': tx_uuid,
                'tx_refund_form': form,
                'tx_form': TransactionSearchForm()
            }.items() + _transaction_summary(tx_uuid).items()))

    try:
        res = client.api.bango.refund.post({'uuid': contrib.transaction_id})
    except (HttpClientError, HttpServerError):
        # Either doing something not supposed to or Solitude had an issue.
        log.exception('Refund error: %s' % tx_uuid)
        messages.error(
            request,
            _('You cannot make a refund request for this transaction.'))
        return redirect(reverse('lookup.transaction_summary', args=[tx_uuid]))

    if res['status'] in [PENDING, COMPLETED]:
        # Create refund Contribution by cloning the payment Contribution.
        refund_contrib = Contribution.objects.get(id=contrib.id)
        refund_contrib.id = None
        refund_contrib.save()
        refund_contrib.update(
            type=amo.CONTRIB_REFUND,
            related=contrib,
            uuid=hashlib.md5(str(uuid.uuid4())).hexdigest(),
            amount=-refund_contrib.amount if refund_contrib.amount else None,
            transaction_id=client.get(res['transaction'])['uuid'])

    if res['status'] == PENDING:
        # Create pending Refund.
        refund_contrib.enqueue_refund(
            amo.REFUND_PENDING,
            request.amo_user,
            refund_reason=form.cleaned_data['refund_reason'])
        log.info('Refund pending: %s' % tx_uuid)
        email_buyer_refund_pending(contrib)
        messages.success(request,
                         _('Refund for this transaction now pending.'))
    elif res['status'] == COMPLETED:
        # Create approved Refund.
        refund_contrib.enqueue_refund(
            amo.REFUND_APPROVED,
            request.amo_user,
            refund_reason=form.cleaned_data['refund_reason'])
        log.info('Refund approved: %s' % tx_uuid)
        email_buyer_refund_approved(contrib)
        messages.success(
            request, _('Refund for this transaction successfully approved.'))
    elif res['status'] == FAILED:
        # Bango no like.
        log.error('Refund failed: %s' % tx_uuid)
        messages.error(request,
                       _('Refund request for this transaction failed.'))

    return redirect(reverse('lookup.transaction_summary', args=[tx_uuid]))
Ejemplo n.º 46
0
def _review(request, addon, version):

    if (not settings.ALLOW_SELF_REVIEWS and
        not acl.action_allowed(request, 'Admin', '%') and
        addon.has_author(request.amo_user)):
        messages.warning(request, _('Self-reviews are not allowed.'))
        return redirect(reverse('reviewers.home'))

    if (addon.status == amo.STATUS_BLOCKED and
        not acl.action_allowed(request, 'Apps', 'ReviewEscalated')):
        messages.warning(
            request, _('Only senior reviewers can review blocklisted apps.'))
        return redirect(reverse('reviewers.home'))

    attachment_formset = CommAttachmentFormSet(data=request.POST or None,
                                               files=request.FILES or None,
                                               prefix='attachment')
    form = forms.get_review_form(data=request.POST or None,
                                 files=request.FILES or None, request=request,
                                 addon=addon, version=version,
                                 attachment_formset=attachment_formset)
    postdata = request.POST if request.method == 'POST' else None
    all_forms = [form, attachment_formset]

    if version:
        features_list = [unicode(f) for f in version.features.to_list()]
        appfeatures_form = AppFeaturesForm(data=postdata,
                                           instance=version.features)
        all_forms.append(appfeatures_form)
    else:
        appfeatures_form = None
        features_list = None

    queue_type = form.helper.review_type
    redirect_url = reverse('reviewers.apps.queue_%s' % queue_type)
    is_admin = acl.action_allowed(request, 'Apps', 'Edit')

    if request.method == 'POST' and all(f.is_valid() for f in all_forms):

        old_types = set(o.id for o in addon.device_types)
        new_types = set(form.cleaned_data.get('device_override'))

        old_features = set(features_list)
        new_features = set(unicode(f) for f
                           in appfeatures_form.instance.to_list())

        if form.cleaned_data.get('action') == 'public':
            if old_types != new_types:
                # The reviewer overrode the device types. We need to not
                # publish this app immediately.
                if addon.make_public == amo.PUBLIC_IMMEDIATELY:
                    addon.update(make_public=amo.PUBLIC_WAIT)

                # And update the device types to what the reviewer set.
                AddonDeviceType.objects.filter(addon=addon).delete()
                for device in form.cleaned_data.get('device_override'):
                    addon.addondevicetype_set.create(device_type=device)

                # Log that the reviewer changed the device types.
                added_devices = new_types - old_types
                removed_devices = old_types - new_types
                msg = _(u'Device(s) changed by '
                         'reviewer: {0}').format(', '.join(
                    [_(u'Added {0}').format(unicode(amo.DEVICE_TYPES[d].name))
                     for d in added_devices] +
                    [_(u'Removed {0}').format(
                     unicode(amo.DEVICE_TYPES[d].name))
                     for d in removed_devices]))
                amo.log(amo.LOG.REVIEW_DEVICE_OVERRIDE, addon,
                        addon.current_version, details={'comments': msg})

            if old_features != new_features:
                # The reviewer overrode the requirements. We need to not
                # publish this app immediately.
                if addon.make_public == amo.PUBLIC_IMMEDIATELY:
                    addon.update(make_public=amo.PUBLIC_WAIT)

                appfeatures_form.save(mark_for_rereview=False)

                # Log that the reviewer changed the minimum requirements.
                added_features = new_features - old_features
                removed_features = old_features - new_features

                fmt = ', '.join(
                      [_(u'Added {0}').format(f) for f in added_features] +
                      [_(u'Removed {0}').format(f) for f in removed_features])
                # L10n: {0} is the list of requirements changes.
                msg = _(u'Requirements changed by reviewer: {0}').format(fmt)
                amo.log(amo.LOG.REVIEW_FEATURES_OVERRIDE, addon,
                        addon.current_version, details={'comments': msg})

        score = form.helper.process()

        if form.cleaned_data.get('notify'):
            # TODO: bug 741679 for implementing notifications in Marketplace.
            EditorSubscription.objects.get_or_create(user=request.amo_user,
                                                     addon=addon)

        is_tarako = form.cleaned_data.get('is_tarako', False)
        if is_tarako:
            Tag(tag_text='tarako').save_tag(addon)
        else:
            Tag(tag_text='tarako').remove_tag(addon)

        # Success message.
        if score:
            score = ReviewerScore.objects.filter(user=request.amo_user)[0]
            # L10N: {0} is the type of review. {1} is the points they earned.
            #       {2} is the points they now have total.
            success = _(
               u'"{0}" successfully processed (+{1} points, {2} total).'
                .format(unicode(amo.REVIEWED_CHOICES[score.note_key]),
                        score.score,
                        ReviewerScore.get_total(request.amo_user)))
        else:
            success = _('Review successfully processed.')
        messages.success(request, success)

        return redirect(redirect_url)

    canned = CannedResponse.objects.all()
    actions = form.helper.actions.items()

    try:
        if not version:
            raise Version.DoesNotExist
        show_diff = (addon.versions.exclude(id=version.id)
                                   .filter(files__isnull=False,
                                           created__lt=version.created,
                                           files__status=amo.STATUS_PUBLIC)
                                   .latest())
    except Version.DoesNotExist:
        show_diff = None

    # The actions we should show a minimal form from.
    actions_minimal = [k for (k, a) in actions if not a.get('minimal')]

    # We only allow the user to check/uncheck files for "pending"
    allow_unchecking_files = form.helper.review_type == "pending"

    versions = (Version.with_deleted.filter(addon=addon)
                                    .order_by('-created')
                                    .transform(Version.transformer_activity)
                                    .transform(Version.transformer))

    product_attrs = {
        'product': json.dumps(
            product_as_dict(request, addon, False, 'reviewer'),
            cls=JSONEncoder),
        'manifest_url': addon.manifest_url,
    }

    pager = paginate(request, versions, 10)

    num_pages = pager.paginator.num_pages
    count = pager.paginator.count

    ctx = context(request, version=version, product=addon, pager=pager,
                  num_pages=num_pages, count=count,
                  form=form, canned=canned, is_admin=is_admin,
                  status_types=amo.MKT_STATUS_CHOICES, show_diff=show_diff,
                  allow_unchecking_files=allow_unchecking_files,
                  actions=actions, actions_minimal=actions_minimal,
                  tab=queue_type, product_attrs=product_attrs,
                  attachment_formset=attachment_formset,
                  appfeatures_form=appfeatures_form)

    if features_list is not None:
        ctx['feature_list'] = features_list

    return render(request, 'reviewers/review.html', ctx)
Ejemplo n.º 47
0
def payment(request, status=None):
    # Note this is not post required, because PayPal does not reply with a
    # POST but a GET, that's a sad face.
    pre, created = (PreApprovalUser.objects.safer_get_or_create(
        user=request.amo_user))

    context = {
        'preapproval': pre,
        'currency': CurrencyForm(initial={'currency': pre.currency or 'USD'})
    }

    if status:
        data = request.session.get('setup-preapproval', {})

        context['status'] = status

        if status == 'complete':
            # The user has completed the setup at PayPal and bounced back.
            if 'setup-preapproval' in request.session:
                if waffle.flag_is_active(request, 'solitude-payments'):
                    client.put_preapproval(data={'uuid': request.amo_user},
                                           pk=data['solitude-key'])

                paypal_log.info(u'Preapproval key created: %s' %
                                request.amo_user.pk)
                amo.log(amo.LOG.PREAPPROVAL_ADDED)
                # TODO(solitude): once this is turned off, we will want to
                # keep preapproval table populated with something, perhaps
                # a boolean inplace of pre-approval key.
                pre.update(paypal_key=data.get('key'),
                           paypal_expiry=data.get('expiry'))

                # If there is a target, bounce to it and don't show a message
                # we'll let whatever set this up worry about that.
                if data.get('complete'):
                    return http.HttpResponseRedirect(data['complete'])

                messages.success(
                    request,
                    _("You're all set for instant app purchases with PayPal."))
                del request.session['setup-preapproval']

        elif status == 'cancel':
            # The user has chosen to cancel out of PayPal. Nothing really
            # to do here, PayPal just bounce to the cancel page if defined.
            if data.get('cancel'):
                return http.HttpResponseRedirect(data['cancel'])

            messages.success(
                request, _('Your payment pre-approval has been cancelled.'))

        elif status == 'remove':
            # The user has an pre approval key set and chooses to remove it
            if waffle.flag_is_active(request, 'solitude-payments'):
                other = client.lookup_buyer_paypal(request.amo_user)
                if other:
                    client.patch_buyer_paypal(pk=other['resource_pk'],
                                              data={'key': ''})

            if pre.paypal_key:
                # TODO(solitude): again, we'll want to maintain some local
                # state in zamboni, so this will probably change to a
                # boolean in the future.
                pre.update(paypal_key='')

                amo.log(amo.LOG.PREAPPROVAL_REMOVED)
                messages.success(
                    request, _('Your payment pre-approval has been disabled.'))
                paypal_log.info(u'Preapproval key removed for user: %s' %
                                request.amo_user)

    return jingo.render(request, 'account/payment.html', context)
Ejemplo n.º 48
0
                paypal.refund(contribution.paykey)
            except PaypalError, e:
                paypal_log.error('Paypal error with refund', exc_info=True)
                messages.error(
                    request,
                    _('There was an error with your '
                      'instant refund.'))
                contribution.record_failed_refund(e, request.amo_user)
                return redirect('account.purchases')

        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.'))
        amo.log(amo.LOG.REFUND_INSTANT, addon)
        return redirect('account.purchases')

    form = forms.ContactForm(request.POST or None)
    if request.method == 'POST' and form.is_valid():
        reason = form.cleaned_data['text']
        context = {
            'product': addon,
            'form': form,
            'user': request.amo_user,
            'contribution': contribution,
            'refund_url': contribution.get_absolute_refund_url(),
            'refund_reason': reason,
            'request': request
        }
Ejemplo n.º 49
0
def _review(request, addon, version):

    if (not settings.ALLOW_SELF_REVIEWS
            and not acl.action_allowed(request, 'Admin', '%')
            and addon.has_author(request.amo_user)):
        messages.warning(request, _('Self-reviews are not allowed.'))
        return redirect(reverse('reviewers.home'))

    if (addon.status == amo.STATUS_BLOCKED
            and not acl.action_allowed(request, 'Apps', 'ReviewEscalated')):
        messages.warning(
            request, _('Only senior reviewers can review blocklisted apps.'))
        return redirect(reverse('reviewers.home'))

    attachment_formset = CommAttachmentFormSet(data=request.POST or None,
                                               files=request.FILES or None,
                                               prefix='attachment')
    form = forms.get_review_form(data=request.POST or None,
                                 files=request.FILES or None,
                                 request=request,
                                 addon=addon,
                                 version=version,
                                 attachment_formset=attachment_formset)
    postdata = request.POST if request.method == 'POST' else None
    all_forms = [form, attachment_formset]

    if version:
        features_list = [unicode(f) for f in version.features.to_list()]
        appfeatures_form = AppFeaturesForm(data=postdata,
                                           instance=version.features)
        all_forms.append(appfeatures_form)
    else:
        appfeatures_form = None
        features_list = None

    queue_type = form.helper.review_type
    redirect_url = reverse('reviewers.apps.queue_%s' % queue_type)
    is_admin = acl.action_allowed(request, 'Addons', 'Edit')

    if request.method == 'POST' and all(f.is_valid() for f in all_forms):

        old_types = set(o.id for o in addon.device_types)
        new_types = set(form.cleaned_data.get('device_override'))

        old_features = set(features_list)
        new_features = set(
            unicode(f) for f in appfeatures_form.instance.to_list())

        if form.cleaned_data.get('action') == 'public':
            if old_types != new_types:
                # The reviewer overrode the device types. We need to not
                # publish this app immediately.
                if addon.make_public == amo.PUBLIC_IMMEDIATELY:
                    addon.update(make_public=amo.PUBLIC_WAIT)

                # And update the device types to what the reviewer set.
                AddonDeviceType.objects.filter(addon=addon).delete()
                for device in form.cleaned_data.get('device_override'):
                    addon.addondevicetype_set.create(device_type=device)

                # Log that the reviewer changed the device types.
                added_devices = new_types - old_types
                removed_devices = old_types - new_types
                msg = _(
                    u'Device(s) changed by '
                    'reviewer: {0}'
                ).format(', '.join([
                    _(u'Added {0}').format(unicode(amo.DEVICE_TYPES[d].name))
                    for d in added_devices
                ] + [
                    _(u'Removed {0}').format(unicode(amo.DEVICE_TYPES[d].name))
                    for d in removed_devices
                ]))
                amo.log(amo.LOG.REVIEW_DEVICE_OVERRIDE,
                        addon,
                        addon.current_version,
                        details={'comments': msg})

            if old_features != new_features:
                # The reviewer overrode the requirements. We need to not
                # publish this app immediately.
                if addon.make_public == amo.PUBLIC_IMMEDIATELY:
                    addon.update(make_public=amo.PUBLIC_WAIT)

                appfeatures_form.save(mark_for_rereview=False)

                # Log that the reviewer changed the minimum requirements.
                added_features = new_features - old_features
                removed_features = old_features - new_features

                fmt = ', '.join(
                    [_(u'Added {0}').format(f) for f in added_features] +
                    [_(u'Removed {0}').format(f) for f in removed_features])
                # L10n: {0} is the list of requirements changes.
                msg = _(u'Requirements changed by reviewer: {0}').format(fmt)
                amo.log(amo.LOG.REVIEW_FEATURES_OVERRIDE,
                        addon,
                        addon.current_version,
                        details={'comments': msg})

        score = form.helper.process()

        if form.cleaned_data.get('notify'):
            EditorSubscription.objects.get_or_create(user=request.amo_user,
                                                     addon=addon)

        # Success message.
        if score:
            score = ReviewerScore.objects.filter(user=request.amo_user)[0]
            rev_str = amo.REVIEWED_CHOICES[score.note_key]
            try:
                rev_str = str(unicode(rev_str))
            except UnicodeEncodeError:
                pass
            # L10N: {0} is the type of review. {1} is the points they earned.
            #       {2} is the points they now have total.
            success = _(
                '"{0}" successfully processed (+{1} points, {2} total).'.
                format(rev_str, score.score,
                       ReviewerScore.get_total(request.amo_user)))
        else:
            success = _('Review successfully processed.')
        messages.success(request, success)

        return redirect(redirect_url)

    canned = AppCannedResponse.objects.all()
    actions = form.helper.actions.items()

    try:
        if not version:
            raise Version.DoesNotExist
        show_diff = (addon.versions.exclude(id=version.id).filter(
            files__isnull=False,
            created__lt=version.created,
            files__status=amo.STATUS_PUBLIC).latest())
    except Version.DoesNotExist:
        show_diff = None

    # The actions we should show a minimal form from.
    actions_minimal = [k for (k, a) in actions if not a.get('minimal')]

    # We only allow the user to check/uncheck files for "pending"
    allow_unchecking_files = form.helper.review_type == "pending"

    versions = (Version.with_deleted.filter(
        addon=addon).order_by('-created').transform(
            Version.transformer_activity).transform(Version.transformer))

    product_attrs = {
        'product':
        json.dumps(product_as_dict(request, addon, False, 'reviewer'),
                   cls=JSONEncoder),
        'manifest_url':
        addon.manifest_url,
    }

    pager = paginate(request, versions, 10)

    num_pages = pager.paginator.num_pages
    count = pager.paginator.count

    ctx = context(request,
                  version=version,
                  product=addon,
                  pager=pager,
                  num_pages=num_pages,
                  count=count,
                  flags=Review.objects.filter(addon=addon, flag=True),
                  form=form,
                  canned=canned,
                  is_admin=is_admin,
                  status_types=amo.MKT_STATUS_CHOICES,
                  show_diff=show_diff,
                  allow_unchecking_files=allow_unchecking_files,
                  actions=actions,
                  actions_minimal=actions_minimal,
                  tab=queue_type,
                  product_attrs=product_attrs,
                  attachment_formset=attachment_formset,
                  appfeatures_form=appfeatures_form)

    if features_list is not None:
        ctx['feature_list'] = features_list

    return jingo.render(request, 'reviewers/review.html', ctx)