Merge branch 'master' of github.com:Gluejar/regluit

pull/1/head
Andromeda Yelton 2012-05-25 16:11:00 -04:00
commit 7f0f147b9f
25 changed files with 306 additions and 100 deletions

View File

@ -215,7 +215,6 @@ def add_by_isbn_from_google(isbn, work=None):
if len(isbn)==10:
isbn = regluit.core.isbn.convert_10_to_13(isbn)
logger.info("adding book by isbn %s", isbn)
# check if we already have this isbn
edition = get_edition_by_id(type='isbn',value=isbn)
@ -223,6 +222,7 @@ def add_by_isbn_from_google(isbn, work=None):
edition.new = False
return edition
logger.info("adding new book by isbn %s", isbn)
results=get_google_isbn_results(isbn)
if results:
try:

View File

@ -70,6 +70,7 @@ registration.signals.user_activated.connect(merge_emails)
def create_notice_types(app, created_models, verbosity, **kwargs):
notification.create_notice_type("comment_on_commented", _("Comment on Commented Work"), _("A comment has been received on a book that you've commented on."))
notification.create_notice_type("wishlist_comment", _("Wishlist Comment"), _("A comment has been received on one of your wishlist books."), default = 1)
notification.create_notice_type("wishlist_official_comment", _("Wishlist Comment"), _("An official comment has been received on one of your wishlist books."))
notification.create_notice_type("wishlist_work_claimed", _("Rights Holder is Active"), _("A rights holder has shown up for a book that you want unglued."), default = 1)
notification.create_notice_type("wishlist_active", _("New Campaign"), _("A book you've wishlisted has a newly launched campaign."))
notification.create_notice_type("wishlist_near_target", _("Campaign Near Target"), _("A book you want is near its ungluing target."))
@ -98,8 +99,12 @@ def notify_comment(comment, request, **kwargs):
logger.info('comment %s notifying' % comment.pk)
other_commenters = User.objects.filter(comment_comments__content_type=comment.content_type, comment_comments__object_pk=comment.object_pk).distinct().exclude(id=comment.user.id)
other_wishers = comment.content_object.wished_by().exclude(id=comment.user.id).exclude(id__in=other_commenters)
notification.queue(other_commenters, "comment_on_commented", {'comment':comment}, True)
notification.queue(other_wishers, "wishlist_comment", {'comment':comment}, True)
if comment.content_object.last_campaign() and comment.user in comment.content_object.last_campaign().managers.all():
#official
notification.queue(other_wishers, "wishlist_official_comment", {'comment':comment}, True)
else:
notification.queue(other_commenters, "comment_on_commented", {'comment':comment}, True)
notification.queue(other_wishers, "wishlist_comment", {'comment':comment}, True)
from regluit.core.tasks import emit_notifications
emit_notifications.delay()

View File

@ -171,11 +171,11 @@ class BookLoaderTests(TestCase):
user = User.objects.create_user('test', 'test@example.com', 'testpass')
user.wishlist.add_work(e1.work, 'test')
user.wishlist.add_work(e2.work, 'test')
manager = User.objects.create_user('manager', 'manager@example.com', 'managerpass')
# create campaigns for the stub works
c1 = models.Campaign.objects.create(
name=e1.work.title,
work=e2.work,
work=e1.work,
description='Test Campaign 1',
deadline=now(),
target=D('1000.00'),
@ -187,7 +187,9 @@ class BookLoaderTests(TestCase):
deadline=now(),
target=D('1000.00'),
)
c2.managers.add(manager)
c2.save()
self.assertEqual(c2.pk, e2.work.last_campaign().pk)
# comment on the works
site = Site.objects.all()[0]
wct = ContentType.objects.get_for_model(models.Work)
@ -207,6 +209,15 @@ class BookLoaderTests(TestCase):
site=site
)
comment2.save()
comment3 = Comment(
content_type=wct,
object_pk=e2.work.pk,
comment="test comment3",
user=manager,
site=site
)
comment3.save()
# now add related edition to make sure Works get merged
bookloader.add_related('0385722133')
@ -214,10 +225,12 @@ class BookLoaderTests(TestCase):
w3 = models.Edition.get_by_isbn('0385722133').work
# and that relevant Campaigns and Wishlists are updated
c1=Campaign.objects.get(pk=c1.pk)
c2=Campaign.objects.get(pk=c2.pk)
self.assertEqual(c1.work, c2.work)
self.assertEqual(user.wishlist.works.all().count(), 1)
self.assertEqual(Comment.objects.for_model(w3).count(), 2)
self.assertEqual(Comment.objects.for_model(w3).count(), 3)
anon_client = Client()
r = anon_client.get("/work/%s/" % w3.pk)

View File

@ -327,9 +327,14 @@ class GoodreadsShelfLoadingForm(forms.Form):
class LibraryThingForm(forms.Form):
lt_username = forms.CharField(max_length=30, required=True)
class PledgeCancelForm(forms.Form):
# which campaign whose active transaction to cancel?
campaign_id = forms.IntegerField(required=True, widget=forms.HiddenInput())
class CampaignAdminForm(forms.Form):
pass
campaign_id = forms.IntegerField()
class EmailShareForm(forms.Form):
recipient = forms.EmailField(error_messages={'required': 'Please specify a recipient.'})

View File

@ -58,7 +58,7 @@ comments rss?
{% for comment in latest_comments %}
{% with comment.content_object.id as id %}
{% with comment.user as user %}
<div class="comments {% cycle 'row1' 'row2' %}">
<div class="comments {% cycle 'row1' 'row2' %} {% if comment.content_object.last_campaign and comment.user in comment.content_object.last_campaign.managers.all %}official{% endif %}">
<div class="nonavatar">
<div class="image">
<a href="{% url work id %}?tab=2"><img src="{{ comment.content_object.cover_image_thumbnail }}" alt="cover image" /></a>

View File

@ -1,6 +1,7 @@
<div id="comments">
{% for comment in comment_list %}
<div class="work_supporter">
{% for comment in comment_list reversed %}
<div class="work_supporter {% if comment.content_object.last_campaign and comment.user in comment.content_object.last_campaign.managers.all %}official{% endif %}">
<a href="/supporter/{{comment.user.username}}">
<div class="work_supporter_avatar">
{% if comment.user.profile.pic_url %}
@ -10,6 +11,6 @@
{% endif %}
</div>
<span class="comment_username">{{comment.user.username }}</span></a> <span>({{ comment.submit_date }})</span> <br /><span class="comment">{{ comment.comment|linebreaksbr }}<br /></span>
</div>
</div>
{% endfor %}
</div>

View File

@ -0,0 +1,9 @@
We thought you might like to know there's an official comment for a book on your wishlist.
{{ comment.user.username }} has commented on {{ comment.content_object.title }}:
{{ comment.comment }}
To change your email preferences, visit https://unglue.it/notification/settings/
The Unglue.it team

View File

@ -0,0 +1,20 @@
{% with comment.content_object.id as id %}
{% with comment.user as user %}
<div class="comments clearfix">
<div class="comments_info clearfix">
<div class="comments_book">
<a href="{% url work id %}?tab=2"><img src="{{ comment.content_object.cover_image_small }}" alt="cover image for {{ comment.content_object.title }}" /></a>
</div>
<div class="comments_graphical">
<a href="{% url supporter supporter_username=user %}">{{ comment.user.username }}</a> has made an official comment on <a href="{% url work id %}?tab=2">{{ comment.content_object.title }}</a>
</div>
</div>
<div class="comments_textual">
{{ comment.comment|linebreaksbr }}
</div>
</div>
{% endwith %}
{% endwith %}

View File

@ -0,0 +1,5 @@
{{ comment.user.username }} on {{ comment.content_object.title }}
{{ comment.comment }}

View File

@ -0,0 +1 @@
{{ comment.user.username }} has mad an official comment on {{ comment.content_object.title }} at Unglue.it

View File

@ -101,7 +101,7 @@
</div>
</div>
{% if faqmenu == 'modify' %}<div class="cancel_notice">(We hope you won't, but of course you're also free to <a href="{% url pledge_cancel %}?tid={{ tid }}">cancel your pledge</a>.)</div>{% endif %}
{% if faqmenu == 'modify' %}<div class="cancel_notice">(We hope you won't, but of course you're also free to <a href="{% url pledge_cancel campaign_id=work.last_campaign.id %}">cancel your pledge</a>.)</div>{% endif %}
{% endblock %}

View File

@ -9,9 +9,19 @@
{% block doccontent %}
{% if error %}
{{error}}
{% else %}
{% if transaction %}
<div>You've asked to cancel your pledge of ${{ transaction.amount|intcomma }} to <a href="{% url work work.id %}">{{ work.title }}</a> Did you mean to do this?</div>
<div class="btn_support">If so, please click on the feedback form to the right to let us know, and we'll cancel the transaction for you.</div>
<div>You have asked to cancel your pledge of ${{ transaction.amount|intcomma }} to <a href="{% url work work.id %}">{{ work.title }}</a>. </div>
<form method="post" action="{% url pledge_cancel campaign_id=campaign.id %}">
{% csrf_token %}
<input type="hidden" name="campaign_id" value="{{campaign.id}}" />
<input type="submit" value="Confirm Pledge Cancellation" id="cancelsubmit" />
</form>
<div>or return to <a href="{% url work work.id %}">{{ work.title }}</a> without canceling your pledge.</div>
{% comment %}
"Yes" should trigger whatever functionality we need to complete cancellation -- may differ depending on whether we've hit the back button from Amazon or the cancel-my-pledge link in pledge_modify.
@ -24,8 +34,10 @@
{% endcomment %}
{% else %}
<div>We're sorry; we can't figure out which transaction you're talking about. If you meant to cancel a pledge (though we hope you don't!), please go to the book's page and hit the Modify Pledge button.</div>
<div>No relevant transaction to cancel for this campaign.</div>
{% endif %}
{% endif %}
{% endblock %}

View File

@ -0,0 +1,22 @@
{% extends "basepledge.html" %}
{% load humanize %}
{% block title %}Pledge Cancelled{% endblock %}
{% block extra_extra_head %}
<link type="text/css" rel="stylesheet" href="/static/css/campaign.css" />
{% endblock %}
{% block doccontent %}
{% if transaction %}
<div>You were about to pledge ${{transaction.amount}} to <a href="{% url work work.id %}">{{work.title}}</a> but hit the cancel link.
Naturally, we won't be charging your PayPal account for this campaign unless you give permission.</div>
<div>However, the campaign can definitely make use of your pledge -- so won't you reconsider?</div>
<div>You <a href="{{try_again_url}}">can finish the pledge transaction</a>.</div>
{% else %}
<div>We're sorry; we can't figure out which transaction you're talking about. If you meant to cancel a pledge, please go to the book's page and hit the Modify link.</div>
{% endif %}
{% endblock %}

View File

@ -7,7 +7,7 @@ from django.conf import settings
from regluit.core.feeds import SupporterWishlistFeed
from regluit.core.models import Campaign
from regluit.frontend.views import CampaignFormView, GoodreadsDisplayView, LibraryThingView, PledgeView, PledgeCompleteView, PledgeModifyView, PledgeCancelView, FAQView
from regluit.frontend.views import CampaignFormView, GoodreadsDisplayView, LibraryThingView, PledgeView, PledgeCompleteView, PledgeModifyView, PledgeCancelView, PledgeNeverMindView, FAQView
from regluit.frontend.views import CampaignListView, DonateView, WorkListView, UngluedListView, InfoPageView
urlpatterns = patterns(
@ -50,8 +50,9 @@ urlpatterns = patterns(
url(r"^new_edition/(?P<work_id>\d*)/(?P<edition_id>\d*)$", "new_edition", name="new_edition"),
url(r"^googlebooks/(?P<googlebooks_id>.+)/$", "googlebooks", name="googlebooks"),
url(r"^pledge/(?P<work_id>\d+)/$", login_required(PledgeView.as_view()), name="pledge"),
url(r"^pledge/cancel/$", login_required(PledgeCancelView.as_view()), name="pledge_cancel"),
url(r"^pledge/cancel/(?P<campaign_id>\d+)$", login_required(PledgeCancelView.as_view()), name="pledge_cancel"),
url(r"^pledge/complete/$", login_required(PledgeCompleteView.as_view()), name="pledge_complete"),
url(r"^pledge/nevermind/$", login_required(PledgeNeverMindView.as_view()), name="pledge_nevermind"),
url(r"^pledge/modify/(?P<work_id>\d+)$", login_required(PledgeModifyView.as_view()), name="pledge_modify"),
url(r"^subjects/$", "subjects", name="subjects"),
url(r"^librarything/$", LibraryThingView.as_view(), name="librarything"),

View File

@ -46,7 +46,7 @@ from regluit.core.goodreads import GoodreadsClient
from regluit.frontend.forms import UserData, UserEmail, ProfileForm, CampaignPledgeForm, GoodreadsShelfLoadingForm
from regluit.frontend.forms import RightsHolderForm, UserClaimForm, LibraryThingForm, OpenCampaignForm
from regluit.frontend.forms import getManageCampaignForm, DonateForm, CampaignAdminForm, EmailShareForm, FeedbackForm
from regluit.frontend.forms import EbookForm, CustomPremiumForm, EditManagersForm, EditionForm
from regluit.frontend.forms import EbookForm, CustomPremiumForm, EditManagersForm, EditionForm, PledgeCancelForm
from regluit.payment.manager import PaymentManager
from regluit.payment.models import Transaction
from regluit.payment.parameters import TARGET_TYPE_CAMPAIGN, TARGET_TYPE_DONATION, PAYMENT_TYPE_AUTHORIZATION
@ -619,7 +619,7 @@ class PledgeView(FormView):
if not self.embedded:
return_url = None
cancel_url = None
nevermind_url = None
# the recipients of this authorization is not specified here but rather by the PaymentManager.
# set the expiry date based on the campaign deadline
@ -627,17 +627,17 @@ class PledgeView(FormView):
paymentReason = "Unglue.it Pledge for {0}".format(campaign.name)
t, url = p.authorize('USD', TARGET_TYPE_CAMPAIGN, preapproval_amount, expiry=expiry, campaign=campaign, list=None, user=user,
return_url=return_url, cancel_url=cancel_url, anonymous=anonymous, premium=premium,
return_url=return_url, nevermind_url=nevermind_url, anonymous=anonymous, premium=premium,
paymentReason=paymentReason)
else: # embedded view -- which we're not actively using right now.
# embedded view triggerws instant payment: send to the partnering RH
receiver_list = [{'email':settings.PAYPAL_NONPROFIT_PARTNER_EMAIL, 'amount':preapproval_amount}]
return_url = None
cancel_url = None
nevermind_url = None
t, url = p.pledge('USD', TARGET_TYPE_CAMPAIGN, receiver_list, campaign=campaign, list=None, user=user,
return_url=return_url, cancel_url=cancel_url, anonymous=anonymous, premium=premium)
return_url=return_url, nevermind_url=nevermind_url, anonymous=anonymous, premium=premium)
if url:
logger.info("PledgeView paypal: " + url)
@ -857,12 +857,102 @@ class PledgeCompleteView(TemplateView):
return context
class PledgeCancelView(TemplateView):
"""A callback for PayPal to tell unglue.it that a payment transaction has been canceled by the user"""
class PledgeCancelView(FormView):
"""A view for allowing a user to cancel the active transaction for specified campaign"""
template_name="pledge_cancel.html"
form_class = PledgeCancelForm
def get_context_data(self, **kwargs):
context = super(PledgeCancelView, self).get_context_data(**kwargs)
# initialize error to be None
context["error"] = None
# the following should be true since PledgeCancelView.as_view is wrapped in login_required
if self.request.user.is_authenticated():
user = self.request.user
else:
context["error"] = "You are not logged in."
return context
campaign = get_object_or_404(models.Campaign, id=self.kwargs["campaign_id"])
if campaign.status != 'ACTIVE':
context["error"] = "{0} is not an active campaign".format(campaign)
return context
work = campaign.work
transactions = campaign.transactions().filter(user=user, status=TRANSACTION_STATUS_ACTIVE)
if transactions.count() < 1:
context["error"] = "You don't have an active transaction for this campaign."
return context
elif transactions.count() > 1:
logger.error("User {0} has {1} active transactions for campaign id {2}".format(user, transactions.count(), campaign.id))
context["error"] = "You have {0} active transactions for this campaign".format(transactions.count())
return context
transaction = transactions[0]
if transaction.type != PAYMENT_TYPE_AUTHORIZATION:
logger.error("Transaction id {0} transaction type, which should be {1}, is actually {2}".format(transaction.id, PAYMENT_TYPE_AUTHORIZATION, transaction.type))
context["error"] = "Your transaction type, which should be {0}, is actually {1}".format(PAYMENT_TYPE_AUTHORIZATION, transaction.type)
return context
# we've located the transaction, work, and campaign referenced in the view
context["transaction"] = transaction
context["work"] = work
context["campaign"] = campaign
context["faqmenu"] = "cancel"
return context
def form_valid(self, form):
# check that user does, in fact, have an active transaction for specified campaign
logger.info("arrived at pledge_cancel form_valid")
# pull campaign_id from form, not from URI as we do from GET
campaign_id = self.request.REQUEST.get('campaign_id')
# this following logic should be extraneous.
if self.request.user.is_authenticated():
user = self.request.user
else:
return HttpResponse("You need to be logged in.")
try:
# look up the specified campaign and attempt to pull up the appropriate transaction
# i.e., the transaction actually belongs to user, that the transaction is active
campaign = get_object_or_404(models.Campaign, id=self.kwargs["campaign_id"], status='ACTIVE')
transaction = campaign.transaction_set.get(user=user, status=TRANSACTION_STATUS_ACTIVE,
type=PAYMENT_TYPE_AUTHORIZATION)
# attempt to cancel the transaction and redirect to the Work page if cancel is successful
# here's a place that would be nice to use https://docs.djangoproject.com/en/dev/ref/contrib/messages/
# to display the success or failure of the cancel operation as a popup in the context of the work page
p = PaymentManager()
result = p.cancel_transaction(transaction)
# put a notification here for pledge cancellation?
if result:
# Now if we redirect the user to the Work page and the IPN hasn't arrived, the status of the
# transaction might be out of date. Let's try an explicit polling of the transaction result before redirecting
# We might want to remove this in a production system
if settings.DEBUG:
update_status = p.update_preapproval(transaction)
return HttpResponseRedirect(reverse('work', kwargs={'work_id': campaign.work.id}))
else:
logger.error("Attempt to cancel transaction id {0} failed".format(transaction.id))
return HttpResponse("Our attempt to cancel your transaction failed. We have logged this error.")
except Exception, e:
logger.error("Exception from attempt to cancel pledge for campaign id {0} for username {1}: {2}".format(campaign_id, user.username, e))
return HttpResponse("Sorry, something went wrong in canceling your campaign pledge. We have logged this error.")
class PledgeNeverMindView(TemplateView):
"""A callback for PayPal to tell unglue.it that a payment transaction has been canceled by the user"""
template_name="pledge_nevermind.html"
def get_context_data(self):
context = super(PledgeCancelView, self).get_context_data()
context = super(PledgeNeverMindView, self).get_context_data()
if self.request.user.is_authenticated():
user = self.request.user

View File

@ -316,7 +316,7 @@ def amazonPaymentReturn(request):
return_url = urlparse.urljoin(settings.BASE_URL, return_path)
return HttpResponseRedirect(return_url)
except:
except Exception, e:
logging.error("Amazon co-branded return-url FAILED with exception:")
traceback.print_exc()
@ -326,19 +326,6 @@ def amazonPaymentReturn(request):
if request.REQUEST.get("status") == AMAZON_STATUS_ADBANDONED:
return HttpResponseRedirect(settings.BASE_URL)
try:
cancel_path = "{0}?{1}".format(reverse('pledge_cancel'),
urllib.urlencode({'tid':transaction.id}))
cancel_url = urlparse.urljoin(settings.BASE_URL, cancel_path)
return HttpResponseRedirect(cancel_url)
except Exception, e:
# BUGBUG -- we should find a better place to send user...but back to front page is ok for now.
logging.error("Amazon co-branded return-url FAILED with exception: {0}".format(e))
return HttpResponseRedirect(settings.BASE_URL)
class AmazonRequest:
'''
@ -404,7 +391,7 @@ class Pay( AmazonRequest ):
The pay function generates a redirect URL to approve the transaction
'''
def __init__( self, transaction, return_url=None, cancel_url=None, amount=None, paymentReason=""):
def __init__( self, transaction, return_url=None, nevermind_url=None, amount=None, paymentReason=""):
try:
logging.debug("Amazon PAY operation for transaction ID %d" % transaction.id)
@ -471,7 +458,7 @@ class Pay( AmazonRequest ):
class Preapproval(Pay):
def __init__( self, transaction, amount, expiry=None, return_url=None, cancel_url=None, paymentReason=""):
def __init__( self, transaction, amount, expiry=None, return_url=None, nevermind_url=None, paymentReason=""):
# set the expiration date for the preapproval if not passed in. This is what the paypal library does
now_val = now()
@ -482,7 +469,7 @@ class Preapproval(Pay):
transaction.save()
# Call into our parent class
Pay.__init__(self, transaction, return_url=return_url, cancel_url=cancel_url, amount=amount, paymentReason=paymentReason)
Pay.__init__(self, transaction, return_url=return_url, nevermind_url=nevermind_url, amount=amount, paymentReason=paymentReason)
class Execute(AmazonRequest):

View File

@ -548,7 +548,7 @@ class PaymentManager( object ):
return False
def authorize(self, currency, target, amount, expiry=None, campaign=None, list=None, user=None,
return_url=None, cancel_url=None, anonymous=False, premium=None,
return_url=None, nevermind_url=None, anonymous=False, premium=None,
paymentReason="unglue.it Pledge"):
'''
authorize
@ -562,7 +562,7 @@ class PaymentManager( object ):
list: optional list object(to be set with TARGET_TYPE_LIST)
user: optional user object
return_url: url to redirect supporter to after a successful PayPal transaction
cancel_url: url to send supporter to if support hits cancel while in middle of PayPal transaction
nevermind_url: url to send supporter to if support hits cancel while in middle of PayPal transaction
anonymous: whether this pledge is anonymous
premium: the premium selected by the supporter for this transaction
paymentReason: a memo line that will show up in the Payer's Amazon (and Paypal?) account
@ -586,13 +586,13 @@ class PaymentManager( object ):
premium=premium
)
# we might want to not allow for a return_url or cancel_url to be passed in but calculated
# we might want to not allow for a return_url or nevermind_url to be passed in but calculated
# here because we have immediate access to the Transaction object.
if cancel_url is None:
cancel_path = "{0}?{1}".format(reverse('pledge_cancel'),
if nevermind_url is None:
nevermind_path = "{0}?{1}".format(reverse('pledge_nevermind'),
urllib.urlencode({'tid':t.id}))
cancel_url = urlparse.urljoin(settings.BASE_URL, cancel_path)
nevermind_url = urlparse.urljoin(settings.BASE_URL, nevermind_path)
if return_url is None:
return_path = "{0}?{1}".format(reverse('pledge_complete'),
@ -600,7 +600,7 @@ class PaymentManager( object ):
return_url = urlparse.urljoin(settings.BASE_URL, return_path)
method = getattr(t.get_payment_class(), "Preapproval")
p = method(t, amount, expiry, return_url=return_url, cancel_url=cancel_url, paymentReason=paymentReason)
p = method(t, amount, expiry, return_url=return_url, nevermind_url=nevermind_url, paymentReason=paymentReason)
# Create a response for this
envelope = p.envelope()
@ -629,7 +629,7 @@ class PaymentManager( object ):
return t, None
def modify_transaction(self, transaction, amount, expiry=None, anonymous=None, premium=None,
return_url=None, cancel_url=None,
return_url=None, nevermind_url=None,
paymentReason=None):
'''
modify
@ -641,7 +641,7 @@ class PaymentManager( object ):
anonymous: new anonymous value; if None, then keep old value
premium: new premium selected; if None, then keep old value
return_url: the return URL after the preapproval(if needed)
cancel_url: the cancel url after the preapproval(if needed)
nevermind_url: the cancel url after the preapproval(if needed)
paymentReason: a memo line that will show up in the Payer's Amazon (and Paypal?) account
return value: True if successful, False otherwise. An optional second parameter for the forward URL if a new authorhization is needed
@ -678,7 +678,7 @@ class PaymentManager( object ):
transaction.list,
transaction.user,
return_url,
cancel_url,
nevermind_url,
transaction.anonymous,
premium,
paymentReason)
@ -750,7 +750,7 @@ class PaymentManager( object ):
logger.info("Refund Transaction " + str(transaction.id) + " Failed with error: " + p.error_string())
return False
def pledge(self, currency, target, receiver_list, campaign=None, list=None, user=None, return_url=None, cancel_url=None, anonymous=False, premium=None):
def pledge(self, currency, target, receiver_list, campaign=None, list=None, user=None, return_url=None, nevermind_url=None, anonymous=False, premium=None):
'''
pledge
@ -769,7 +769,7 @@ class PaymentManager( object ):
list: optional list object(to be set with TARGET_TYPE_LIST)
user: optional user object
return_url: url to redirect supporter to after a successful PayPal transaction
cancel_url: url to send supporter to if support hits cancel while in middle of PayPal transaction
nevermind_url: url to send supporter to if support hits cancel while in middle of PayPal transaction
anonymous: whether this pledge is anonymous
premium: the premium selected by the supporter for this transaction
@ -800,7 +800,7 @@ class PaymentManager( object ):
t.create_receivers(receiver_list)
method = getattr(t.get_payment_class(), "Pay")
p = method(t,return_url=return_url, cancel_url=cancel_url)
p = method(t,return_url=return_url, nevermind_url=nevermind_url)
# Create a response for this
envelope = p.envelope()

View File

@ -1,9 +1,11 @@
from django.db import models
from django.contrib.auth.models import User
from django.conf import settings
from regluit.core.models import Campaign, Wishlist, Premium
from regluit.payment.parameters import *
from decimal import Decimal
import uuid
class Transaction(models.Model):
@ -11,7 +13,7 @@ class Transaction(models.Model):
type = models.IntegerField(default=PAYMENT_TYPE_NONE, null=False)
# host: the payment processor. Named after the payment module that hosts the payment processing functions
host = models.CharField(default=PAYMENT_HOST_AMAZON, max_length=32, null=False)
host = models.CharField(default=settings.PAYMENT_PROCESSOR, max_length=32, null=False)
# target: e.g, TARGET_TYPE_CAMPAIGN, TARGET_TYPE_LIST -- defined in parameters.py
target = models.IntegerField(default=TARGET_TYPE_NONE, null=False)

View File

@ -49,4 +49,4 @@ TRANSACTION_STATUS_FAILED = 'Failed'
# these two following parameters are probably extraneous since I think we will compute dynamically where to return each time.
COMPLETE_URL = '/paymentcomplete'
CANCEL_URL = '/paymentcancel'
NEVERMIND_URL = '/paymentnevermind'

View File

@ -404,7 +404,7 @@ class PaypalEnvelopeRequest:
return None
class Pay( PaypalEnvelopeRequest ):
def __init__( self, transaction, return_url=None, cancel_url=None, paymentReason=""):
def __init__( self, transaction, return_url=None, nevermind_url=None, paymentReason=""):
#BUGBUG: though I'm passing in paymentReason (to make it signature compatible with Amazon, it's not being wired in properly yet)
try:
@ -420,11 +420,11 @@ class Pay( PaypalEnvelopeRequest ):
if return_url is None:
return_url = settings.BASE_URL + COMPLETE_URL
if cancel_url is None:
cancel_url = settings.BASE_URL + CANCEL_URL
if nevermind_url is None:
nevermind_url = settings.BASE_URL + nevermind_url
logger.info("Return URL: " + return_url)
logger.info("Cancel URL: " + cancel_url)
logger.info("Cancel URL: " + nevermind_url)
receiver_list = []
receivers = transaction.receiver_set.all()
@ -470,7 +470,7 @@ class Pay( PaypalEnvelopeRequest ):
'receiverList': { 'receiver': receiver_list },
'currencyCode': transaction.currency,
'returnUrl': return_url,
'cancelUrl': cancel_url,
'cancelUrl': nevermind_url,
'requestEnvelope': { 'errorLanguage': 'en_US' },
'ipnNotificationUrl': settings.BASE_URL + reverse('HandleIPN', args=["paypal"]),
'feesPayer': feesPayer,
@ -541,9 +541,9 @@ class Execute(Pay):
For payapl, execute is the same as pay. The pay funciton detects whether an execute or a co-branded operation
is called for.
'''
def __init__(self, transaction, return_url=None, cancel_url=None):
def __init__(self, transaction, return_url=None, nevermind_url=None):
# Call our super class. In python 2.2+, we can't use super here, so just call init directly
Pay.__init__(self, transaction, return_url, cancel_url)
Pay.__init__(self, transaction, return_url, nevermind_url)
class Finish(PaypalEnvelopeRequest):
@ -787,7 +787,7 @@ class RefundPayment(PaypalEnvelopeRequest):
class Preapproval( PaypalEnvelopeRequest ):
def __init__( self, transaction, amount, expiry=None, return_url=None, cancel_url=None, paymentReason=""):
def __init__( self, transaction, amount, expiry=None, return_url=None, nevermind_url=None, paymentReason=""):
# BUGBUG: though I'm passing in paymentReason (to make it signature compatible with Amazon, it's not being wired in properly yet)
@ -804,8 +804,8 @@ class Preapproval( PaypalEnvelopeRequest ):
if return_url is None:
return_url = settings.BASE_URL + COMPLETE_URL
if cancel_url is None:
cancel_url = settings.BASE_URL + CANCEL_URL
if nevermind_url is None:
nevermind_url = settings.BASE_URL + NEVERMIND_URL
# set the expiration date for the preapproval if not passed in
now_val = now()
@ -823,7 +823,7 @@ class Preapproval( PaypalEnvelopeRequest ):
'maxAmountPerPayment': '%.2f' % transaction.amount,
'currencyCode': transaction.currency,
'returnUrl': return_url,
'cancelUrl': cancel_url,
'cancelUrl': nevermind_url,
'requestEnvelope': { 'errorLanguage': 'en_US' },
'ipnNotificationUrl': settings.BASE_URL + reverse('HandleIPN', args=["paypal"])
}

View File

@ -127,7 +127,8 @@ def paySandbox(test, selenium, url, authorize=False, already_at_url=False, sleep
def payAmazonSandbox(sel):
# login to Amazon payments
print "Expected title: {0} \n Actual Title: {1}".format('Amazon.com Sign In', sel.title)
# does it make sense to throw up if there is problem....what better invariants?
login_email = WebDriverWait(sel,20).until(lambda d: d.find_element_by_css_selector("input#ap_email"))
login_email.click()
login_email.clear()
@ -141,16 +142,17 @@ def payAmazonSandbox(sel):
time.sleep(2)
# sel.find_element_by_css_selector("input[type='image']")
print "Expected title: {0} \n Actual Title: {1}".format('Amazon Payments', sel.title)
print "looking for credit_card_confirm", sel.current_url
credit_card_confirm = WebDriverWait(sel,20).until(lambda d: d.find_elements_by_css_selector("input[type='image']"))
credit_card_confirm[0].click()
credit_card_confirm[-1].click()
print "looking for payment_confirm", sel.current_url
payment_confirm = WebDriverWait(sel,20).until(lambda d: d.find_elements_by_css_selector("input[type='image']"))
print "payment_confirm ", payment_confirm
print "len(payment_confirm)", len(payment_confirm)
time.sleep(1)
payment_confirm[-1].click()
#print "looking for payment_confirm", sel.current_url
#payment_confirm = WebDriverWait(sel,20).until(lambda d: d.find_elements_by_css_selector("input[type='image']"))
#print "payment_confirm ", payment_confirm
#print "len(payment_confirm)", len(payment_confirm)
#time.sleep(1)
#payment_confirm[-1].click()
class PledgeTest(TestCase):

View File

@ -1,6 +1,7 @@
from os.path import dirname, realpath, join
import regluit
import datetime
from regluit.payment.parameters import PAYMENT_HOST_PAYPAL, PAYMENT_HOST_AMAZON
PROJECT_DIR = dirname(dirname(realpath(__file__)))

View File

@ -453,6 +453,10 @@ div#content-block-content #tabs-1 img {
vertical-align: middle;
margin-left: -5px;
}
.official {
border: 3px #B8DDE0 solid;
margin: 3px auto;
}
.editions {
clear: both;
}

View File

@ -47,3 +47,7 @@
margin: 0 auto;
padding-top: 5px;
}
.official {
border: 3px #B8DDE0 solid;
margin: 3px auto;
}

View File

@ -3,6 +3,7 @@ from regluit.payment.models import Transaction, PaymentResponse, Receiver
from regluit.payment.manager import PaymentManager
from regluit.payment.paypal import IPN_PREAPPROVAL_STATUS_ACTIVE, IPN_PAY_STATUS_INCOMPLETE, IPN_PAY_STATUS_COMPLETED
import django
from django.conf import settings
from selenium import selenium, webdriver
@ -13,6 +14,8 @@ import unittest, time, re
import logging
import os
# PayPal developer sandbox
from regluit.payment.tests import loginSandbox, paySandbox, payAmazonSandbox
def setup_selenium():
# Set the display window for our xvfb
@ -152,32 +155,29 @@ def recipient_status(clist):
# res = [pm.finish_campaign(c) for c in campaigns_incomplete()]
def support_campaign(unglue_it_url = settings.LIVE_SERVER_TEST_URL, do_local=True, backend='amazon'):
def support_campaign(unglue_it_url = settings.LIVE_SERVER_TEST_URL, do_local=True, backend='amazon', browser='firefox'):
"""
programatically fire up selenium to make a Pledge
do_local should be True only if you are running support_campaign on db tied to LIVE_SERVER_TEST_URL
"""
import django
django.db.transaction.enter_transaction_management()
UNGLUE_IT_URL = unglue_it_url
# unglue.it login info
USER = settings.UNGLUEIT_TEST_USER
PASSWORD = settings.UNGLUEIT_TEST_PASSWORD
# PayPal developer sandbox
from regluit.payment.tests import loginSandbox, paySandbox, payAmazonSandbox
setup_selenium()
# we can experiment with different webdrivers
sel = webdriver.Firefox()
# Chrome
#sel = webdriver.Chrome(executable_path='/Users/raymondyee/C/src/Gluejar/regluit/test/chromedriver')
# HTMLUNIT with JS -- not successful
#sel = webdriver.Remote("http://localhost:4444/wd/hub", webdriver.DesiredCapabilities.HTMLUNITWITHJS)
if browser == 'firefox':
sel = webdriver.Firefox()
elif browser == 'chrome':
sel = webdriver.Chrome(executable_path='/Users/raymondyee/C/src/Gluejar/regluit/test/chromedriver')
elif browser == 'htmlunit':
# HTMLUNIT with JS -- not successful
sel = webdriver.Remote("http://localhost:4444/wd/hub", webdriver.DesiredCapabilities.HTMLUNITWITHJS)
else:
sel = webdriver.Firefox()
time.sleep(10)
@ -202,7 +202,9 @@ def support_campaign(unglue_it_url = settings.LIVE_SERVER_TEST_URL, do_local=Tru
sel.find_element_by_css_selector("input[value*='sign in']").click()
# click on biggest campaign list
biggest_campaign_link = WebDriverWait(sel,20).until(lambda d: d.find_element_by_css_selector("a[href*='/campaigns/ending']"))
# I have no idea why selenium thinks a is not displayed....so that's why I'm going up one element.
# http://stackoverflow.com/a/6141678/7782
biggest_campaign_link = WebDriverWait(sel,20).until(lambda d: d.find_element_by_css_selector("li > a[href*='/campaigns/ending']"))
biggest_campaign_link.click()
time.sleep(1)
@ -245,10 +247,7 @@ def support_campaign(unglue_it_url = settings.LIVE_SERVER_TEST_URL, do_local=Tru
print "Now trying to pay PayPal", sel.current_url
paySandbox(None, sel, sel.current_url, authorize=True, already_at_url=True, sleep_time=5)
elif backend == 'amazon':
print "before payAmazonSandbox"
payAmazonSandbox(sel)
print "after payAmazonSandbox"
payAmazonSandbox(sel)
# should be back on a pledge complete page
print sel.current_url, re.search(r"/pledge/complete",sel.current_url)
@ -304,12 +303,35 @@ def support_campaign(unglue_it_url = settings.LIVE_SERVER_TEST_URL, do_local=Tru
# wait a bit to allow PayPal sandbox to be update the status of the Transaction
time.sleep(10)
django.db.transaction.commit()
# time out to simulate an IPN -- update all the transactions
if do_local:
django.db.transaction.enter_transaction_management()
pm = PaymentManager()
print pm.checkStatus()
django.db.transaction.commit()
django.db.transaction.enter_transaction_management()
# now go back to the work page, hit modify pledge, and then the cancel link
work_url = WebDriverWait(sel,20).until(lambda d: d.find_element_by_css_selector('p > a[href*="/work/"]'))
work_url.click()
change_pledge_button = WebDriverWait(sel,20).until(lambda d: d.find_element_by_css_selector("input[value*='Modify Pledge']"))
change_pledge_button.click()
cancel_url = WebDriverWait(sel,20).until(lambda d: d.find_element_by_css_selector('a[href*="/pledge/cancel"]'))
cancel_url.click()
# hit the confirm cancellation button
cancel_pledge_button = WebDriverWait(sel,20).until(lambda d: d.find_element_by_css_selector("input[value*='Confirm Pledge Cancellation']"))
cancel_pledge_button.click()
django.db.transaction.commit()
# Why is the status of the new transaction not being updated?
django.db.transaction.commit()
# force a db lookup -- see whether there are 1 or 2 transactions
# they should both be cancelled
if do_local:
transactions = list(Transaction.objects.all())
print "number of transactions", Transaction.objects.count()