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)
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')
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 })
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()
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!')
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)
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')})
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()
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})
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()
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})
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})
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})
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})
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})
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})
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})
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})
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})
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 })
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)
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)
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)
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})
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})
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})
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})
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]))
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})
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})
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})
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 })
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)
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')
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)
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)
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})
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]))
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 '' })
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 })
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]))
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)
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)
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 }
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)