Merge branch 'master' of github.com:Gluejar/regluit
commit
7f0f147b9f
|
@ -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:
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.'})
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
|
@ -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 %}
|
|
@ -0,0 +1,5 @@
|
|||
{{ comment.user.username }} on {{ comment.content_object.title }}
|
||||
|
||||
{{ comment.comment }}
|
||||
|
||||
|
|
@ -0,0 +1 @@
|
|||
{{ comment.user.username }} has mad an official comment on {{ comment.content_object.title }} at Unglue.it
|
|
@ -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 %}
|
||||
|
||||
|
|
|
@ -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 %}
|
||||
|
||||
|
|
|
@ -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 %}
|
|
@ -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"),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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"])
|
||||
}
|
||||
|
|
|
@ -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):
|
||||
|
||||
|
|
|
@ -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__)))
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -47,3 +47,7 @@
|
|||
margin: 0 auto;
|
||||
padding-top: 5px;
|
||||
}
|
||||
.official {
|
||||
border: 3px #B8DDE0 solid;
|
||||
margin: 3px auto;
|
||||
}
|
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue