[#23019867] First pass at allow for pledges to be modified

pull/1/head
Raymond Yee 2012-03-19 16:32:37 -07:00
parent 97f3d01ab4
commit 46ca784030
5 changed files with 211 additions and 15 deletions

View File

@ -0,0 +1,99 @@
{% extends "basepledge.html" %}
{% block title %}Pledge (Modify){% endblock %}
{% block extra_extra_head %}
<link type="text/css" rel="stylesheet" href="/static/css/campaign.css" />
<link type="text/css" rel="stylesheet" href="/static/css/pledge.css" />
{% endblock %}
{% block doccontent %}
<div style="height:10px";></div>
<div class="book-detail">
<div class="book-detail-img">
<a href="#"><img src="{{ work.cover_image_thumbnail }}" alt="{{ work.title }}" title="{{ work.title }}" width="131" height="192" /></a>
</div>
<div class="book-detail-info">
<h2 class="book-name">{{ work.title }}</h2>
<h3 class="book-author">{{ work.author }}</h3>
<h3 class="book-year">{{ work.publication_date }}</h3>
<div class="find-book">
<label>Find it here</label>
<div class="find-link">
<a class="find-google" href="{{ work.googlebooks_url }}"><img src="/static/images/supporter_icons/googlebooks_square.png" title="Find on Google Books" alt="Find on Google Books" /></a>
<a rel="nofollow" class="find-openlibrary" href="{% url work_openlibrary work.id %}"><img src="/static/images/supporter_icons/openlibrary_square.png" title="Find on OpenLibrary" alt="Find on OpenLibrary"></a>
{% if not request.user.is_anonymous %}
{% if request.user.profile.goodreads_user_link %}
<a rel="nofollow" class="find-goodreads" href="{% url work_goodreads work.id %}"><img src="/static/images/supporter_icons/goodreads_square.png" title="Find on GoodReads" alt="Find on GoodReads"></a>
{% endif %}
{% if request.user.profile.librarything_id %}
<a rel="nofollow" class="find-librarything" href="{% url work_librarything work.id %}"><img src="/static/images/supporter_icons/librarything_square.png" title="Find on LibraryThing" alt="Find on LibraryThing" /></a>
{% endif %}
{% endif %}
</div>
</div>
<div class="pledged-info">
<div class="pledged-group">
{{ work.last_campaign.supporters.count }} Ungluers have pledged ${{ work.last_campaign.current_total }}
</div>
<div class="status">
<img src="/static/images/images/icon-book-37by25-{{ work.percent_unglued }}.png" title="book list status" alt="book list status" />
</div>
</div>
</div>
</div>
<div class="jsmodule rounded pledge">
<div class="jsmod-content">
${{ work.last_campaign.target }} needed by<br />
{{ work.last_campaign.deadline }}
</div>
</div>
<div class="jsmodule rounded">
<div class="jsmod-content">
<p>You modifying your current pledge.</p>
{% comment %}
Even there is a CampaignPledgeForm in frontend/forms.py , the "widget" for premium_id is implemented in HTML here for now.
{% endcomment %}
<form method="POST" action="{% url pledge_modify work_id=work.id %}">
{% csrf_token %}
{{ form.non_field_errors }}
{{ form.preapproval_amount.errors }}
<div class="pledge_amount">{{ form.preapproval_amount.label_tag }}: ${{ form.preapproval_amount }}</div>
{{ form.anonymous.errors }}
{% comment %}
not supported yet; don't display
{{ form.anonymous.label_tag }}: {{ form.anonymous }}
{% endcomment %}
<ul class="support menu">
{% for premium in premiums %}
<label for="{{premium.id}}">
<li class="{% if forloop.first %}first{% else %}{% if forloop.last %}last{% endif %}{% endif %}">
<input type="radio" name="premium_id" value="{{premium.id}}" id="{{premium.id}}" {% ifequal request.REQUEST.premium_id premium.id|stringformat:"s" %}checked="checked"{% endifequal %}" />
<span class="menu-item-price">
${{ premium.amount }}
</span>
<span class="menu-item-desc">
{{ premium.description }}
</span>
</a></li></label>
{% endfor %}
</ul>
<input type="submit" value="Modify Pledge" id="pledgesubmit"/>
</form>
</div>
{% endblock %}

View File

@ -101,7 +101,7 @@ $j(document).ready(function(){
</div> </div>
{% if status == 'ACTIVE' %} {% if status == 'ACTIVE' %}
{% if pledged %} {% if pledged %}
<div class="btn_support modify"><form action="/stub/modify_pledge" method="get"><input type="submit" value="Change Pledge"/></form></div> <div class="btn_support modify"><form action="{% url pledge_modify work_id=work.id %}" method="get"><input type="submit" value="Change Pledge"/></form></div>
{% else %} {% else %}
<div class="btn_support"><form action="{% url pledge work_id=work.id %}" method="get"><input type="submit" value="Support"/></form></div> <div class="btn_support"><form action="{% url pledge work_id=work.id %}" method="get"><input type="submit" value="Support"/></form></div>
{% endif %} {% endif %}

View File

@ -7,7 +7,7 @@ from django.conf import settings
from regluit.core.feeds import SupporterWishlistFeed from regluit.core.feeds import SupporterWishlistFeed
from regluit.core.models import Campaign from regluit.core.models import Campaign
from regluit.frontend.views import CampaignFormView, GoodreadsDisplayView, LibraryThingView, PledgeView, PledgeCompleteView, PledgeCancelView, FAQView from regluit.frontend.views import CampaignFormView, GoodreadsDisplayView, LibraryThingView, PledgeView, PledgeCompleteView, PledgeModifyView, PledgeCancelView, FAQView
from regluit.frontend.views import CampaignListView, DonateView, WorkListView, UngluedListView, InfoPageView from regluit.frontend.views import CampaignListView, DonateView, WorkListView, UngluedListView, InfoPageView
urlpatterns = patterns( urlpatterns = patterns(
@ -48,7 +48,8 @@ urlpatterns = patterns(
url(r"^setup/work/(?P<work_id>\d+)/$", "work", {'action':'setup_campaign'}, name="setup_campaign"), url(r"^setup/work/(?P<work_id>\d+)/$", "work", {'action':'setup_campaign'}, name="setup_campaign"),
url(r"^pledge/(?P<work_id>\d+)/$", login_required(PledgeView.as_view()), name="pledge"), 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/$", login_required(PledgeCancelView.as_view()), name="pledge_cancel"),
url(r"^pledge/complete/$", PledgeCompleteView.as_view(), name="pledge_complete"), url(r"^pledge/complete/$", login_required(PledgeCompleteView.as_view()), name="pledge_complete"),
url(r"^pledge/modify/(?P<work_id>\d+)$", login_required(PledgeModifyView.as_view()), name="pledge_modify"),
url(r"^subjects/$", "subjects", name="subjects"), url(r"^subjects/$", "subjects", name="subjects"),
url(r"^librarything/$", LibraryThingView.as_view(), name="librarything"), url(r"^librarything/$", LibraryThingView.as_view(), name="librarything"),
url(r"^librarything/load/$","librarything_load", name="librarything_load"), url(r"^librarything/load/$","librarything_load", name="librarything_load"),

View File

@ -330,7 +330,7 @@ class PledgeView(FormView):
embedded = False embedded = False
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
# change https://code.djangoproject.com/browser/django/tags/releases/1.3.1/django/views/generic/edit.py#L129 # change the default behavior from https://code.djangoproject.com/browser/django/tags/releases/1.3.1/django/views/generic/edit.py#L129
# don't automatically bind the data to the form on GET, only on POST # don't automatically bind the data to the form on GET, only on POST
# compare with https://code.djangoproject.com/browser/django/tags/releases/1.3.1/django/views/generic/edit.py#L34 # compare with https://code.djangoproject.com/browser/django/tags/releases/1.3.1/django/views/generic/edit.py#L34
form_class = self.get_form_class() form_class = self.get_form_class()
@ -417,21 +417,117 @@ class PledgeView(FormView):
logger.info("PledgeView paypal: Error " + str(t.reference)) logger.info("PledgeView paypal: Error " + str(t.reference))
return HttpResponse(response) return HttpResponse(response)
class PledgeModifyView(FormView):
"""
A view to handle request to change an existing pledge
"""
template_name="pledge_modify.html"
form_class = CampaignPledgeForm
embedded = False
def get_context_data(self, **kwargs):
context = super(PledgeModifyView, self).get_context_data(**kwargs)
# the following should be true since PledgeModifyView.as_view is wrapped in login_required
assert self.request.user.is_authenticated()
user = self.request.user
work = get_object_or_404(models.Work, id=self.kwargs["work_id"])
try:
campaign = work.last_campaign()
premiums = campaign.effective_premiums()
# which combination of campaign and transaction status required?
# Campaign must be ACTIVE
assert campaign.status == 'ACTIVE'
transactions = campaign.transactions().filter(user=user, status=IPN_PAY_STATUS_ACTIVE)
assert transactions.count() == 1
transaction = transactions[0]
assert transaction.type == PAYMENT_TYPE_AUTHORIZATION and transaction.status == IPN_PAY_STATUS_ACTIVE
except Exception, e:
raise e
# what stuff do we need to pull out to populate form?
# preapproval_amount, premium_id (which we don't have stored yet)
premium_id = None
# is there a Transaction for an ACTIVE campaign for this
# should make sure Transaction is modifiable.
preapproval_amount = transaction.amount
data = {'preapproval_amount':preapproval_amount, 'premium_id':premium_id}
form_class = self.get_form_class()
# no validation errors, please, when we're only doing a GET
# to avoid validation errors, don't bind the form
if preapproval_amount is not None:
form = form_class(data)
else:
form = form_class()
context.update({'work':work,'campaign':campaign, 'premiums':premiums, 'form':form, 'premium_id':premium_id, 'faqmenu': 'pledge'})
return context
def form_valid(self, form):
# What are the situations we need to deal with?
# 2 main situations: if the new amount is less than max_amount, no need to go out to PayPal again
# if new amount is greater than max_amount...need to go out and get new approval.
# to start with, we can use the standard pledge_complete, pledge_cancel machinery
# might have to modify the pledge_complete, pledge_cancel because the messages are going to be
# different because we're modifying a pledge rather than a new one.
work_id = self.kwargs["work_id"]
preapproval_amount = form.cleaned_data["preapproval_amount"]
anonymous = form.cleaned_data["anonymous"]
assert self.request.user.is_authenticated()
user = self.request.user
# right now, if there is a non-zero pledge amount, go with that. otherwise, do the pre_approval
campaign = models.Work.objects.get(id=int(work_id)).last_campaign()
assert campaign.status == 'ACTIVE'
transactions = campaign.transactions().filter(user=user, status=IPN_PAY_STATUS_ACTIVE)
assert transactions.count() == 1
transaction = transactions[0]
assert transaction.type == PAYMENT_TYPE_AUTHORIZATION and transaction.status == IPN_PAY_STATUS_ACTIVE
p = PaymentManager(embedded=self.embedded)
status, url = p.modify_transaction(transaction, preapproval_amount)
logger.info("status: {0}, url:{1}".format(status, url))
if status and url is not None:
logger.info("PledgeModifyView paypal: " + url)
return HttpResponseRedirect(url)
elif status and url is None:
return HttpResponse("Your modification is noted. No need to go to PayPal")
else:
return HttpResponse("No mods made")
class PledgeCompleteView(TemplateView): class PledgeCompleteView(TemplateView):
"""A callback for PayPal to tell unglue.it that a payment transaction has completed successfully. """A callback for PayPal to tell unglue.it that a payment transaction has completed successfully.
Possible things to implement: Possible things to implement:
after pledging, supporter receives email including thanks, work pledged, amount, expiry date, any next steps they should expect; others? after pledging, supporter receives email including thanks, work pledged, amount, expiry date, any next steps they should expect; others?
study other confirmation emails for their contents study other confirmation emails for their contents
after pledging, supporters are returned to a thank-you screen after pledging, supporters are returned to a thank-you screen
should have prominent "thank you" or "congratulations" message should have prominent "thank you" or "congratulations" message
should have prominent share options should have prominent share options
should suggest other works for supporters to explore (on what basis?) should suggest other works for supporters to explore (on what basis?)
link to work page? or to page on which supporter entered the process? (if the latter, how does that work with widgets?) link to work page? or to page on which supporter entered the process? (if the latter, how does that work with widgets?)
should note that a confirmation email has been sent to $email from $sender should note that a confirmation email has been sent to $email from $sender
should briefly note next steps (e.g. if this campaign succeeds you will be emailed on date X) should briefly note next steps (e.g. if this campaign succeeds you will be emailed on date X)
""" """
template_name="pledge_complete.html" template_name="pledge_complete.html"

View File

@ -714,7 +714,7 @@ class PaymentManager( object ):
if amount > transaction.max_amount or expiry != transaction.date_expired: if amount > transaction.max_amount or expiry != transaction.date_expired:
# Increase or expiuration change, cancel and start again # Increase or expiration change, cancel and start again
self.cancel_transaction(transaction) self.cancel_transaction(transaction)
# Start a new authorization for the new amount # Start a new authorization for the new amount