pull/1/head
eric 2012-09-28 10:59:04 -04:00
commit a53c0ac4f3
14 changed files with 273 additions and 149 deletions

View File

@ -210,6 +210,47 @@ class Campaign(models.Model):
except: except:
return u"Campaign %s (no associated work)" % self.name return u"Campaign %s (no associated work)" % self.name
def clone(self):
"""use a previous UNSUCCESSFUL campaign's data as the basis for a new campaign"""
if self.clonable():
old_managers= self.managers.all()
# copy custom premiums
new_premiums= self.premiums.filter(type='CU')
# setting pk to None will insert new copy http://stackoverflow.com/a/4736172/7782
self.pk = None
self.status = 'INITIALIZED'
# set deadline far in future -- presumably RH will set deadline to proper value before campaign launched
self.deadline = date_today() + timedelta(days=int(settings.UNGLUEIT_LONGEST_DEADLINE))
# allow created, activated dates to be autoset by db
self.created = None
self.name = 'copy of %s' % self.name
self.activated = None
self.update_left()
self.save()
self.managers=old_managers
# clone associated premiums
for premium in new_premiums:
premium.pk=None
premium.created = None
premium.campaign = self
premium.save()
return self
else:
return None
def clonable(self):
"""campaign clonable if it's UNSUCCESSFUL and is the last campaign associated with a Work"""
if self.status == 'UNSUCCESSFUL' and self.work.last_campaign().id==self.id:
return True
else:
return False
@property @property
def launchable(self): def launchable(self):
may_launch=True may_launch=True
@ -341,7 +382,9 @@ class Campaign(models.Model):
@property @property
def supporters_count(self): def supporters_count(self):
# avoid transmitting the whole list if you don't need to; let the db do the count. # avoid transmitting the whole list if you don't need to; let the db do the count.
return self.transactions().filter(status=TRANSACTION_STATUS_ACTIVE).values_list('user', flat=True).distinct().count() active = self.transactions().filter(status=TRANSACTION_STATUS_ACTIVE).values_list('user', flat=True).distinct().count()
complete = self.transactions().filter(status=TRANSACTION_STATUS_COMPLETE).values_list('user', flat=True).distinct().count()
return active+complete
def transaction_to_recharge(self, user): def transaction_to_recharge(self, user):
"""given a user, return the transaction to be recharged if there is one -- None otherwise""" """given a user, return the transaction to be recharged if there is one -- None otherwise"""

View File

@ -14,7 +14,7 @@ from django.contrib.sites.models import Site
from django.http import Http404 from django.http import Http404
from regluit.payment.models import Transaction from regluit.payment.models import Transaction
from regluit.core.models import Campaign, Work, UnglueitError, Edition, RightsHolder, Claim, Key, Ebook from regluit.core.models import Campaign, Work, UnglueitError, Edition, RightsHolder, Claim, Key, Ebook, Premium
from regluit.core import bookloader, models, search, goodreads, librarything from regluit.core import bookloader, models, search, goodreads, librarything
from regluit.core import isbn from regluit.core import isbn
from regluit.payment.parameters import PAYMENT_TYPE_AUTHORIZATION from regluit.payment.parameters import PAYMENT_TYPE_AUTHORIZATION
@ -388,6 +388,8 @@ class CampaignTests(TestCase):
w = Work() w = Work()
w.save() w.save()
w2 = Work()
w2.save()
# INITIALIZED # INITIALIZED
c1 = Campaign(target=D('1000.00'),deadline=datetime(2013,1,1),work=w) c1 = Campaign(target=D('1000.00'),deadline=datetime(2013,1,1),work=w)
c1.save() c1.save()
@ -402,6 +404,8 @@ class CampaignTests(TestCase):
rh.save() rh.save()
cl = Claim(rights_holder = rh, work = w, user = u, status = 'active') cl = Claim(rights_holder = rh, work = w, user = u, status = 'active')
cl.save() cl.save()
cl2 = Claim(rights_holder = rh, work = w2, user = u, status = 'active')
cl2.save()
c2.activate() c2.activate()
self.assertEqual(c2.status, 'ACTIVE') self.assertEqual(c2.status, 'ACTIVE')
# SUSPENDED # SUSPENDED
@ -414,14 +418,25 @@ class CampaignTests(TestCase):
# should not let me suspend a campaign that hasn't been initialized # should not let me suspend a campaign that hasn't been initialized
self.assertRaises(UnglueitError, c1.suspend, "for testing") self.assertRaises(UnglueitError, c1.suspend, "for testing")
# UNSUCCESSFUL # UNSUCCESSFUL
c3 = Campaign(target=D('1000.00'),deadline=now() - timedelta(days=1),work=w) c3 = Campaign(target=D('1000.00'),deadline=now() - timedelta(days=1),work=w2)
c3.save() c3.save()
c3.activate() c3.activate()
self.assertEqual(c3.status, 'ACTIVE') self.assertEqual(c3.status, 'ACTIVE')
# at this point, since the deadline has passed, the status should change and be UNSUCCESSFUL # at this point, since the deadline has passed, the status should change and be UNSUCCESSFUL
self.assertTrue(c3.update_status()) self.assertTrue(c3.update_status())
self.assertEqual(c3.status, 'UNSUCCESSFUL') self.assertEqual(c3.status, 'UNSUCCESSFUL')
# premiums
pr1= Premium(type='CU', campaign=c3, amount=10, description='botsnack', limit=1)
pr1.save()
self.assertEqual(pr1.premium_remaining,1)
#cloning (note we changed c3 to w2 to make it clonable)
c7= c3.clone()
self.assertEqual(c7.status, 'INITIALIZED')
self.assertEqual(c7.premiums.all()[0].description , 'botsnack')
# SUCCESSFUL # SUCCESSFUL
c4 = Campaign(target=D('1000.00'),deadline=now() - timedelta(days=1),work=w) c4 = Campaign(target=D('1000.00'),deadline=now() - timedelta(days=1),work=w)
c4.save() c4.save()
@ -629,11 +644,12 @@ class DownloadPageTest(TestCase):
eb2 = models.Ebook() eb2 = models.Ebook()
eb2.url = "http://example2.com" eb2.url = "http://example2.com"
eb2.edition = e2 eb2.edition = e2
eb2.format = 'mobi'
eb1.save() eb1.save()
eb2.save() eb2.save()
anon_client = Client() anon_client = Client()
response = anon_client.get("/work/%s/download/" % w.id) response = anon_client.get("/work/%s/download/" % w.id)
self.assertContains(response, "http://example.com", count=3) self.assertContains(response, "http://example.com", count=4)
self.assertContains(response, "http://example2.com", count=2) self.assertContains(response, "http://example2.com", count=3)

View File

@ -173,6 +173,9 @@ class UserData(forms.Form):
return username return username
raise forms.ValidationError(_("Your username is already "+username)) raise forms.ValidationError(_("Your username is already "+username))
class CloneCampaignForm(forms.Form):
campaign_id = forms.IntegerField(required = True, widget = forms.HiddenInput)
class OpenCampaignForm(forms.ModelForm): class OpenCampaignForm(forms.ModelForm):
managers = AutoCompleteSelectMultipleField( managers = AutoCompleteSelectMultipleField(
OwnerLookup, OwnerLookup,
@ -237,9 +240,9 @@ def getManageCampaignForm ( instance, data=None, *args, **kwargs ):
class ManageCampaignForm(forms.ModelForm): class ManageCampaignForm(forms.ModelForm):
paypal_receiver = forms.EmailField( paypal_receiver = forms.EmailField(
label=_("email address to collect Paypal funds"), label=_("contact email address for this campaign"),
max_length=100, max_length=100,
error_messages={'required': 'You must enter the email associated with your Paypal account.'}, error_messages={'required': 'You must enter the email we should contact you at for this campaign.'},
) )
target = forms.DecimalField( min_value= D(settings.UNGLUEIT_MINIMUM_TARGET), error_messages={'required': 'Please specify a target price.'} ) target = forms.DecimalField( min_value= D(settings.UNGLUEIT_MINIMUM_TARGET), error_messages={'required': 'Please specify a target price.'} )
edition = forms.ModelChoiceField(get_queryset(), widget=RadioSelect(),empty_label='no edition selected',required = False,) edition = forms.ModelChoiceField(get_queryset(), widget=RadioSelect(),empty_label='no edition selected',required = False,)

View File

@ -1,40 +0,0 @@
{% extends "base.html" %}
{% load humanize %}
{% url privacy as privacyurl %}
{% block content %}
<h2>Campaign: {{campaign.name}}</h2>
<!-- url for work: -->
<p>Work: <a href="{% url work work_id=campaign.work.id %}">{{campaign.work}}</a></p>
<p>Target: {{campaign.target|floatformat:0|intcomma}}</p>
<p>Deadline: {{campaign.deadline}}</p>
<p>Status: {{campaign.status}}</p>
<p>ISBN: {{campaign.work.first_isbn_13}} </p>
<p><a href="{% url widget isbn=campaign.work.first_isbn_13 %}">Widget Link</a></p>
<p>Embed a widget:</p>
<textarea rows="2" cols="80">&lt;iframe src="{{base_url}}/api/widget/{{campaign.work.first_isbn_13}}/" width="152" height="325" frameborder="0"&gt;</iframe></textarea>
<p>PayPal receiver: {{campaign.paypal_receiver}}</p>
<p>Current total (pledged/authorized): {{campaign.current_total|floatformat:0|intcomma}}</p>
<form method="POST" action="{% url campaign_by_id pk=campaign.id %}" onsubmit="test()">
{% csrf_token %}
{{form.as_p}}
<input type="submit" value="Pledge" id="pledgeBtn" />
</form>
<!-- All associated transactions -->
{% if campaign.transactions %}
<p>Associated transactions:</p>
<ul>
{% for transaction in campaign.transactions %}
<li>id:{{transaction.id}} | type:{{transaction.type}} | {{transaction.status}} | {{transaction.user}} | amount:{{transaction.amount}}</li>
{% endfor %}
</ul>
{% else %}
<p>No associated transactions</p>
{% endif %}
{% endblock %}

View File

@ -19,13 +19,13 @@
<h3>Read the unglued edition!</h3> <h3>Read the unglued edition!</h3>
<div class="ebook_download"> <div class="ebook_download">
{% for ebook in unglued_ebooks %} {% for ebook in unglued_ebooks %}
{% with ebook.url as url %} <a href="{{ ebook.url }}">
<a href="{{ url }}"><img src="{{ ebook.rights_badge }}"></a> <img src="{{ ebook.rights_badge }}" alt="{{ ebook.rights}}" title="{{ ebook.rights}}" /></a>
<a href="{{ url }}">{{ ebook.format }}</a> <a href="{{ ebook.url }}"><img src="/static/images/{{ ebook.format }}32.png" height="32" alt="{{ ebook.format }}" title="{{ ebook.format }}" /></a>
{% ifequal ebook.format 'mobi' %} (use for Kindle){% endifequal %} <a href="{{ ebook.url }}">{{ ebook.format }}</a>
{% ifequal ebook.format 'epub' %} (use for Nook, Apple, Sony){% endifequal %} {% ifequal ebook.format 'mobi' %} (use for Kindle){% endifequal %}
{% if not forloop.last %}<br /><br />{% endif %} {% ifequal ebook.format 'epub' %} (use for Nook, Apple, Sony){% endifequal %}
{% endwith %} {% if not forloop.last %}<br /><br />{% endif %}
{% endfor %} {% endfor %}
</div> </div>
</div> </div>
@ -38,13 +38,13 @@
<h4>Freely available editions</h4> <h4>Freely available editions</h4>
{% endif %} {% endif %}
{% for ebook in other_ebooks %} {% for ebook in other_ebooks %}
{% with ebook.url as url %} <a href="{{ ebook.url }}">
<a href="{{ url }}"><img src="{{ ebook.rights_badge }}"></a> <img src="{{ ebook.rights_badge }}" alt="{{ ebook.rights}}" title="{{ ebook.rights}}" /></a>
<a href="{{ url }}">{{ ebook.format }}</a> <a href="{{ ebook.url }}"><img src="/static/images/{{ ebook.format }}32.png" height="32" alt="{{ ebook.format }} at {{ebook.provider}}" title="{{ ebook.format }} at {{ebook.provider}}" /></a>
{% ifequal ebook.format 'mobi' %} (use for Kindle){% endifequal %} <a href="{{ ebook.url }}">{{ ebook.format }} at {{ebook.provider}}</a>
{% ifequal ebook.format 'epub' %} (use for Nook, Apple, Sony){% endifequal %} {% ifequal ebook.format 'mobi' %} (use for Kindle){% endifequal %}
{% if not forloop.last %}<br /><br />{% endif %} {% ifequal ebook.format 'epub' %} (use for Nook, Apple, Sony){% endifequal %}
{% endwith %} {% if not forloop.last %}<br /><br />{% endif %}
{% endfor %} {% endfor %}
</div> </div>
{% endif %} {% endif %}
@ -59,13 +59,13 @@
<p>If this doesn't work, use the instructions below for your device.</p> <p>If this doesn't work, use the instructions below for your device.</p>
{% if unglued_epub_url %} {% if readmill_epub_url %}
<h4>Readmill users</h4> <h4>Readmill users</h4>
<p>If you're a <a href="http://readmill.com/">Readmill</a> member, you can send unglued ebooks to your iPad with one click. Here you go:</p> <p>If you're a <a href="http://readmill.com/">Readmill</a> member, you can send unglued ebooks to your iPad with one click. Here you go:</p>
<div class="send-to-readmill" data-download-url="{{ unglued_epub_url }}" data-buy-url="{{ base_url }}{% url work work.id %}" data-display="small" onload="/static/js/readmill.js"></div> <div class="send-to-readmill" data-download-url="{{ readmill_epub_url }}" data-buy-url="{{ base_url }}{% url work work.id %}" data-display="small"></div>
{% endif %} {% endif %}
<h4>Android devices</h4> <h4>Android devices</h4>
<p class="ebook_download logo"><img src="/static/images/aldiko_logo.png">Aldiko</p> <p class="ebook_download logo"><img src="/static/images/aldiko_logo.png" alt="Aldiko Logo" />Aldiko</p>
<ul> <ul>
<li><a href="http://www.aldiko.com/download.html">Download the free Aldiko app.</a></li> <li><a href="http://www.aldiko.com/download.html">Download the free Aldiko app.</a></li>
@ -77,7 +77,7 @@
{% endcomment %} {% endcomment %}
<h4>iOS devices</h4> <h4>iOS devices</h4>
<p class="ebook_download logo"><img src="/static/images/ibooks_logo.jpeg">iBooks</p> <p class="ebook_download logo"><img src="/static/images/ibooks_logo.jpeg" alt="iBooks Logo" />iBooks</p>
{% comment %}test{% endcomment %} {% comment %}test{% endcomment %}
<ul> <ul>
<li><a href="http://itunes.apple.com/us/app/ibooks/id364709193?mt=8">Download the free iBooks app</a> from the App Store.</li> <li><a href="http://itunes.apple.com/us/app/ibooks/id364709193?mt=8">Download the free iBooks app</a> from the App Store.</li>
@ -86,7 +86,7 @@
</ul> </ul>
<h4>PC, Mac, or Linux</h4> <h4>PC, Mac, or Linux</h4>
<p class="ebook_download logo"><img src="/static/images/calibre_logo.png">Calibre</p> <p class="ebook_download logo"><img src="/static/images/calibre_logo.png" alt="Calibre Logo" />Calibre</p>
<ul> <ul>
<li><a href="http://calibre-ebook.com/download">Download the free Calibre app.</a></li> <li><a href="http://calibre-ebook.com/download">Download the free Calibre app.</a></li>
<li>Download your book from this page using your device's web browser.</li> <li>Download your book from this page using your device's web browser.</li>

View File

@ -218,9 +218,9 @@ If you want to find an interesting campaign and don't have a specific book in mi
{% if sublocation == 'supporting' or sublocation == 'all' %} {% if sublocation == 'supporting' or sublocation == 'all' %}
<h4>Supporting Campaigns</h4> <h4>Supporting Campaigns</h4>
<dl> <dl>
<dt>How do I pledge? Do I need an Amazon account?</dt> <dt>How do I pledge?</dt>
<dd>There's a Support button on all campaign pages, or you can just click on the premium you'd like to receive. You'll be asked to select your premium and specify your pledge amount, and then you'll be taken to Amazon to complete the transaction. At this time, you need an Amazon Account in order to pledge.</dd> <dd>There's a Support button on all campaign pages, or you can just click on the premium you'd like to receive. You'll be asked to select your premium and specify your pledge amount, and then enter your credit card information.</dd>
<dt>When will I be charged?</dt> <dt>When will I be charged?</dt>
@ -272,7 +272,7 @@ Rights holders are encouraged to offer additional premiums to engage supporters'
<dt>Is there a minimum or maximum for how much a premium can cost?</dt> <dt>Is there a minimum or maximum for how much a premium can cost?</dt>
<dd>There's a $1 minimum and a $2000 maximum (due to transaction size limits imposed by our payment processor).</dd> <dd>There's a $1 minimum and a $2000 maximum.</dd>
</dl> </dl>
{% endif %} {% endif %}
{% endif %} {% endif %}
@ -389,11 +389,31 @@ Unglue.it signs agreements assuring the copyright status of every work we unglue
<dl> <dl>
<dt>What do I need to do to become an authorized Rights Holder on Unglue.it?</dt> <dt>What do I need to do to become an authorized Rights Holder on Unglue.it?</dt>
<dd>Contact our Rights Holder Relations team, <a href="mailto:rights@gluejar.com">rights@gluejar.com</a>, to discuss signing our Platform Services Agreement. This is the first step in being able to make an offer to license, set a monetary goal, and run a campaign on Unglue.it.</dd> <dd>Contact our Rights Holder Relations team, <a href="mailto:rights@gluejar.com">rights@gluejar.com</a>, to discuss signing our Platform Services Agreement (PSA), setting a monetary goal, and running a campaign on Unglue.it to release your book under a <a href="http://creativecommons.org">Creative Commons</a> license.<br /><br />
The more Unglue.it knows about you and your books, the faster your campaign can be up and running.<br /><br />
For example, it would help to have you tell us the following in your initial query:<br /><br />
<em>About your books:</e><br />
Title(s) for which you hold the rights and which you might want to unglue. (ISBNs help too!)<br />
How long have print and/or ebook editions of these titles been for sale?<br />
Where are your books sold?<br />
If your title went out of print, how many copies had it sold until then?<br /><br />
<em>About the ebook rights:</em><br />
Have rights reverted, or have you already requested reversion?<br />
Have you self-published?<br /><br />
<em>About you as an author and marketer:</em><br />
What social media (blog, Twitter, Facebook, Goodreads, etc.) do you currently use?<br />
How have you worked with libraries to promote your books in the past?<br />
Where do you think your key supporters will come from? -- a community of fans you're already in touch with, fans who read your genre, readers who have yet to discover your work, scholars or students, readers who support the open access movement or free culture?
</dd>
<dt>Do I need to know the exact titles I might want to unglue before I sign the Platform Services Agreement?</dt> <dt>Do I need to know the exact titles I might want to unglue before I sign the Platform Services Agreement?</dt>
<dd>No. You only need to agree to the terms under which you will use Unglue.it to raise money to release an ebook using a <a href="http://creativecommons.org">Creative Commons</a> license. You can decide which specific titles you wish to make available for licensing later. You can run campaigns for as many, or as few, titles at a time as you like.</dd> <dd>No. You only need to agree to the terms under which you will use Unglue.it to raise money to release an ebook using a <a href="http://creativecommons.org">Creative Commons</a> license. You can decide which specific titles you wish to make available for licensing later. However, before you start a campaign, you must claim a specific title (through the Rights tab of its book page) and plan how you will attract ungluers to pledge toward your goal.</dd>
<dt>Can I run more than one campaign?</dt>
<dd>You can run campaigns for as many, or as few, titles at a time as you like. We encourage you to start with one at a time.</dd>
</dl> </dl>
{% endif %} {% endif %}
{% if sublocation == 'campaigns' or sublocation == 'all' %} {% if sublocation == 'campaigns' or sublocation == 'all' %}
@ -498,16 +518,6 @@ Need more ideas? We're happy to work with rights holders personally to craft a
{% if sublocation == 'funding' or sublocation == 'all' %} {% if sublocation == 'funding' or sublocation == 'all' %}
<h4>Funding</h4> <h4>Funding</h4>
<dt>Is a PayPal account required to launch a campaign?</dt>
<dd>At this time, Unglue.it requires that rights holders have PayPal accounts as it will simplify the process of paying you when a campaign succeeds. </dd>
<dt>Is an Amazon account required to launch a campaign?</dt>
<dd>No. When Unglue.it receives funds pledged through Amazon, we can cut a check to rights holders when a campaign succeeds.</dd>
<dt>Why does this FAQ mention both Amazon and Paypal? Who's the payment processor?</dt>
<dd>For more information on Unglue.it's decision-making process with regard to payment processors, please read <a href="http://blog.unglue.it/2012/05/03/unglue-it-payment-options-amazon-vs-paypal/">our blog post on Amazon and Paypal</a>.</dd>
<dt>Are a funding goal and deadline required?</dt> <dt>Are a funding goal and deadline required?</dt>
<dd>Yes.</dd> <dd>Yes.</dd>
@ -552,7 +562,7 @@ If you're concerned a campaign may not reach its goal you have several options.
<dt>What fees does Unglue.it charge?</dt> <dt>What fees does Unglue.it charge?</dt>
<dd>When a campaign succeeds, Unglue.it will deduct a 6% commission on the funds raised. Our payment processor also charges a small percentage fee on each transaction, plus (where relevant) currency conversion costs. If you do not have a suitable ePub version of your book available, you will also need to cover ebook conversion costs; please price this into your goal for a campaign. Details are spelled out in the Platform Services Agreement that rights holders must sign before launching an Unglue.it campaign.</dd> <dd>When a campaign succeeds, Unglue.it will deduct a 6% commission on the funds raised. Our payment processor also charges a small fee on each transaction, plus (where relevant) currency conversion costs. If you do not have a suitable ePub version of your book available, you will also need to cover ebook conversion costs; please price this into your goal for a campaign. Details are spelled out in the Platform Services Agreement that rights holders must sign before launching an Unglue.it campaign.</dd>
<dt>Does it cost money to start a campaign on Unglue.it?</dt> <dt>Does it cost money to start a campaign on Unglue.it?</dt>

View File

@ -175,7 +175,7 @@ Please fix the following before launching your campaign:
{% ifnotequal campaign_status 'ACTIVE' %} {% ifnotequal campaign_status 'ACTIVE' %}
<h3>Target Price</h3> <h3>Target Price</h3>
<p>This is the target price for your campaign. The <i>mimimum</i> target is ${{form.minimum_target|intcomma}}.</p> <p>This is the target price for your campaign. The <i>minimum</i> target is ${{form.minimum_target|intcomma}}.</p>
<p>Your target should be high enough to compensate you for potential lost future revenue from sales of this edition and reflect well on your brand, but low enough to seem realistic to supporters; people are more likely to back campaigns that they think will succeed.</p> <p>Your target should be high enough to compensate you for potential lost future revenue from sales of this edition and reflect well on your brand, but low enough to seem realistic to supporters; people are more likely to back campaigns that they think will succeed.</p>
@ -208,20 +208,22 @@ Please fix the following before launching your campaign:
{{ form.deadline.errors }}<span style="display: none">{{ form.deadline }}</span> {{ form.deadline.errors }}<span style="display: none">{{ form.deadline }}</span>
{% endifnotequal %} {% endifnotequal %}
<h3>Paypal collection address</h3> <h3>e-mail contact address</h3>
<p>Enter the email address associated with your PayPal account.</p> <p>Enter the email address where notifications about this campaign should be sent. If your campaign succeeds, this email needs to work if you want to get paid!</p>
<p>We don't support PayPal yet; if your campaign succeeds we'll be paying you by check. However, we've applied PayPal for a merchant account, and should that application be approved PayPal expects us to have this information.</p>
<p>{{ form.paypal_receiver.errors }}{{ form.paypal_receiver }}</p> <p>{{ form.paypal_receiver.errors }}{{ form.paypal_receiver }}</p>
{% ifequal campaign_status 'ACTIVE' %} {% if not is_preview or request.user.is_staff %}
<div class="yikes">When you click this button, your changes will be visible to supporters immediately. Make sure to proofread!</div><br /> {% ifequal campaign_status 'ACTIVE' %}
<input type="submit" name="save" value="Modify Campaign" /> <div class="yikes">When you click this button, your changes will be visible to supporters immediately. Make sure to proofread!</div><br />
{% else %} <input type="submit" name="save" value="Modify Campaign" />
<br /><br /><input type="submit" name="save" value="Save Campaign" /> {% else %}
{% endifequal %} <br /><br /><input type="submit" name="save" value="Save Campaign" />
{% if campaign_status == 'INITIALIZED' %} {% endifequal %}
<input id="campaign_launcher" type="submit" name="launch" value="Launch Campaign" /> {% if campaign_status == 'INITIALIZED' %}
<input id="campaign_launcher" type="submit" name="launch" value="Launch Campaign" />
{% endif %}
{% endif %} {% endif %}
</form> </form>
</div> </div>
@ -266,20 +268,24 @@ Please fix the following before launching your campaign:
{% ifequal campaign_status 'INITIALIZED' %} {% ifequal campaign_status 'INITIALIZED' %}
<div class="tabs-3"> <div class="tabs-3">
{% if campaign.description and campaign.target and campaign.deadline %} {% if not is_preview or request.user.is_staff %}
<p>Before you hit launch:</p> {% if campaign.description and campaign.target and campaign.deadline %}
<ul> <p>Before you hit launch:</p>
<li>Have you proofread your campaign? (Make sure to spellcheck!)</li> <ul>
<li>Have you <a href="{% url work_preview campaign.work.id %}">previewed your campaign</a>? Does it look how you want it to?</li> <li>Have you proofread your campaign? (Make sure to spellcheck!)</li>
</ul> <li>Have you <a href="{% url work_preview campaign.work.id %}">previewed your campaign</a>? Does it look how you want it to?</li>
</ul>
<p>If it doesn't look exactly the way you like, or you're having any trouble with your description or premiums, we're happy to help; please email unglue.it support (<a href="mailto:support@gluejar.com">support@gluejar.com</a>).</p>
<p>If it doesn't look exactly the way you like, or you're having any trouble with your description or premiums, we're happy to help; please email unglue.it support (<a href="mailto:support@gluejar.com">support@gluejar.com</a>).</p> <p>If you're happy with your campaign, here's your moment of truth!</p>
<p>If you're happy with your campaign, here's your moment of truth!</p> <div id="launchme"><a href="#" class="manage">Launch Campaign</a></div>
{% else %}
<div id="launchme"><a href="#" class="manage">Launch Campaign</a></div> <p>Please make sure you've selected your campaign's edition and entered its description, target, deadline, and premiums, and previewed your campaign, before launching.</p>
{% endif %}
{% else %} {% else %}
<p>Please make sure you've selected your campaign's edition and entered its description, target, deadline, and premiums, and previewed your campaign, before launching.</p> While you're free to edit your campaign, it won't be launchable until we've fully tested our new payment processor.
{% endif %} {% endif %}
</div> </div>

View File

@ -1,6 +1,6 @@
Alas. The campaign to unglue {{ campaign.work.title }} (https://{{site.domain}}{% url work campaign.work.id %}) has not succeeded. Alas. The campaign to unglue {{ campaign.work.title }} (https://{{site.domain}}{% url work campaign.work.id %}) has not succeeded.
If you pledged toward this work, your pledge will expire shortly and your credit card will not be charged, nor will you receive any premiums. You'll also receive an email from Amazon notifying you that your payment to Gluejar has been cancelled. If you pledged toward this work, your pledge will expire shortly and your credit card will not be charged, nor will you receive any premiums.
Still want to give {{ campaign.work.title }} to the world? Don't despair. Keep it on your wishlist and tell everyone why you love this book. The rights holder, {{ campaign.rightsholder }}, may run a campaign with different terms in the future. With your help, we may yet be able to unglue {{ campaign.work.title }}. Still want to give {{ campaign.work.title }} to the world? Don't despair. Keep it on your wishlist and tell everyone why you love this book. The rights holder, {{ campaign.rightsholder }}, may run a campaign with different terms in the future. With your help, we may yet be able to unglue {{ campaign.work.title }}.

View File

@ -20,21 +20,33 @@
Any questions not covered here? Please email us at <a href="mailto:rights@gluejar.com">rights@gluejar.com</a>. Any questions not covered here? Please email us at <a href="mailto:rights@gluejar.com">rights@gluejar.com</a>.
</div></div> </div></div>
{% if request.user.campaigns.all %} {% if campaigns %}
<h2>Campaigns You Manage</h2> <h2>Campaigns You Manage</h2>
<dl> <dl>
{% for campaign in request.user.campaigns.all %} {% for campaign in campaigns %}
<dt>Work: <a href="{% url work work_id=campaign.work.id %}">{{campaign.work.title }}</a></dt> <dt>Work: <a href="{% url work work_id=campaign.work.id %}">{{campaign.work.title }}</a></dt>
<dd> <dd>
<div class="work_campaigns clearfix"> <div class="work_campaigns clearfix">
<div class="campaign_info"> <div class="campaign_info">
Campaign: {{ campaign.name }}<br /> Campaign: {{ campaign.name }}<br />
Campaign status: {{ campaign.status }} <br /> Campaign status: {{ campaign.status }} <br />
Created: {{ campaign.created }} Created: {{ campaign.created }}<br />
</div> ${{ campaign.current_total }} pledged of ${{ campaign.target }}, {{ campaign.supporters_count }} supporters
<div>
<a href="{% url manage_campaign campaign.id %}" class="manage">Manage This Campaign</a>
</div> </div>
{% if campaign.status = 'ACTIVE' or campaign.status = 'INITIALIZED' %}
<div>
<a href="{% url manage_campaign campaign.id %}" class="manage">Manage This Campaign</a>
</div>
{% endif %}
{% if campaign.clone_form %}
<div>
<form method="POST" action="#">
{% csrf_token %}
{{ campaign.clone_form }}{{ campaign.clone_form.errors }}
<input type="submit" name="clone" value="Clone this Campaign">
</form>
</div>
{% endif %}
</div> </div>
</dd> </dd>
{% endfor %} {% endfor %}
@ -51,7 +63,7 @@ Any questions not covered here? Please email us at <a href="mailto:rights@gluej
<br />Date of Claim : {{ claim.created }} <br />Date of Claim : {{ claim.created }}
<br />Status of Claim: {{ claim.get_status_display }} <br />Status of Claim: {{ claim.get_status_display }}
{% if claim.can_open_new %} {% if claim.can_open_new %}
<h3>Open a campaign for this work</h3> <h3>Open a blank campaign for this work</h3>
<form method="POST" action="#"> <form method="POST" action="#">
{% csrf_token %} {% csrf_token %}
<p>Name the Campaign: {{ claim.campaign_form.name }}{{ claim.campaign_form.name.errors }}</p> <p>Name the Campaign: {{ claim.campaign_form.name }}{{ claim.campaign_form.name.errors }}</p>
@ -67,20 +79,30 @@ Any questions not covered here? Please email us at <a href="mailto:rights@gluej
<h3>Campaigns for this work</h3> <h3>Campaigns for this work</h3>
{% for campaign in claim.campaigns %} {% for campaign in claim.campaigns %}
<div class="work_campaigns clearfix"> <div class="work_campaigns clearfix">
<div class="campaign_info"> {% if campaign.status = 'ACTIVE' or campaign.status = 'INITIALIZED' %}
Name: Your campaign, "{{ campaign.name }}", is {{ campaign.status }}<br /> <div class="campaign_info">
Created: {{ campaign.created }}<br /> Name: Your campaign, "{{ campaign.name }}", is {{ campaign.status }}<br />
Manager(s): {% for user in campaign.managers.all %} <a href="{% url supporter user.username %}">{{ user.username }} </a> {% endfor %} Created: {{ campaign.created }}<br />
<form method="POST" action="#">{% csrf_token %} Manager(s): {% for user in campaign.managers.all %} <a href="{% url supporter user.username %}">{{ user.username }} </a> {% endfor %}
Add/Remove Managers: {{ campaign.edit_managers_form.managers }}{{ campaign.edit_managers_form.managers.errors }} <form method="POST" action="#">{% csrf_token %}
<input type="submit" name="edit_managers_{{campaign.id}}" value="Save Managers"> Add/Remove Managers: {{ campaign.edit_managers_form.managers }}{{ campaign.edit_managers_form.managers.errors }}
</form> <input type="submit" name="edit_managers_{{campaign.id}}" value="Save Managers">
</div> </form>
{% if request.user in campaign.managers.all %} </div>
<div> {% if request.user in campaign.managers.all %}
<a href="{% url manage_campaign campaign.id %}" class="manage">Manage This Campaign</a> <div>
</div> <a href="{% url manage_campaign campaign.id %}" class="manage">Manage This Campaign</a>
{% endif %} </div>
{% endif %}
{% else %}
<div class="campaign_info">
Name: Your campaign, "{{ campaign.name }}", is {{ campaign.status }}<br />
Created: {{ campaign.created }}<br />
Manager(s): {% for user in campaign.managers.all %} <a href="{% url supporter user.username %}">{{ user.username }} </a> {% endfor %}
<br />
${{ campaign.current_total }} pledged of ${{ campaign.target }}, {{ campaign.supporters_count }} supporters
</div>
{% endif %}
</div> </div>
{% endfor %} {% endfor %}
{% endif %} {% endif %}
@ -138,20 +160,19 @@ Any questions not covered here? Please email us at <a href="mailto:rights@gluej
</li> </li>
</ol> </ol>
<h2>Rightsholder social media tools</h2>
Needs to be written. What would you find helpful in a social media toolkit? <a href="/feedback">Send us feedback.</a>
<h2>Rewards</h2> <h2>Rewards</h2>
<p>Campaigns have rewards as a way to thank supporters. unglue.it includes a standard set of rewards in all campaigns. You are encouraged to add additional sweeteners to motivate people to contribute.</p> <p>Campaigns can have rewards as a way to motivate and thank supporters. You are strongly encouraged to add rewards - they are given special prominence on the campaign page.</p>
<p>Here are the standard rewards:</p> <p>What should you add as rewards? Anything you think you can reasonably deliver that will get supporters excited about the book. For example: other books, whether electronic or physical; artwork or multimedia relating to the book, its author, or its themes; in-person or online chats with the author; memorabilia.</p>
<h2>Acknowledgements</h2>
<p>Here are the standard acknowledgements. These automatically combine with your rewards. For example, if you offer a $30 reward, ungluers who pledge $30 will receive the $25 acknowledgement as well.</p>
<ul class="terms"> <ul class="terms">
<li><em>Any level ($1 minimum)</em> &#8212; The unglued ebook delivered to your inbox</li> <li><em>Any amount</em> &#8212; The unglued ebook</li>
<li><em>$25</em> &#8212; Your username under "supporters" in the acknowledgements section</li> <li><em>$25 and above</em> &#8212; Their name in the acknowledgements section under "supporters"</li>
<li><em>$50</em> &#8212; Your name &amp; profile link under "benefactors"</li> <li><em>$50 and above</em> &#8212; Their name &amp; profile link under "benefactors"</li>
<li><em>$100</em> &#8212; Your name, profile link, &amp; profile tagline under "bibliophiles"</li> <li><em>$100 and above</em> &#8212; Their name, profile link, &amp; a dedication under "bibliophiles"</li>
</ul> </ul>
<h2>More Questions</h2> <h2>More Questions</h2>

View File

@ -14,7 +14,6 @@
<script type="text/javascript" src="//platform.twitter.com/widgets.js"></script> <script type="text/javascript" src="//platform.twitter.com/widgets.js"></script>
<script type="text/javascript" src="/static/js/counter.js"></script> <script type="text/javascript" src="/static/js/counter.js"></script>
<script type="text/javascript" src="/static/js/embed.js"></script> <script type="text/javascript" src="/static/js/embed.js"></script>
<script type="text/javascript" src="/static/js/readmill.js"></script>
<script> <script>
var $j = jQuery.noConflict(); var $j = jQuery.noConflict();

View File

@ -48,7 +48,7 @@ from regluit.frontend.forms import UserData, UserEmail, ProfileForm, CampaignPle
from regluit.frontend.forms import RightsHolderForm, UserClaimForm, LibraryThingForm, OpenCampaignForm from regluit.frontend.forms import RightsHolderForm, UserClaimForm, LibraryThingForm, OpenCampaignForm
from regluit.frontend.forms import getManageCampaignForm, DonateForm, CampaignAdminForm, EmailShareForm, FeedbackForm from regluit.frontend.forms import getManageCampaignForm, DonateForm, CampaignAdminForm, EmailShareForm, FeedbackForm
from regluit.frontend.forms import EbookForm, CustomPremiumForm, EditManagersForm, EditionForm, PledgeCancelForm from regluit.frontend.forms import EbookForm, CustomPremiumForm, EditManagersForm, EditionForm, PledgeCancelForm
from regluit.frontend.forms import getTransferCreditForm, CCForm from regluit.frontend.forms import getTransferCreditForm, CCForm, CloneCampaignForm
from regluit.payment.manager import PaymentManager from regluit.payment.manager import PaymentManager
from regluit.payment.models import Transaction, Account from regluit.payment.models import Transaction, Account
from regluit.payment.parameters import TRANSACTION_STATUS_ACTIVE, TRANSACTION_STATUS_COMPLETE, TRANSACTION_STATUS_CANCELED, TRANSACTION_STATUS_ERROR, TRANSACTION_STATUS_FAILED, TRANSACTION_STATUS_INCOMPLETE, TRANSACTION_STATUS_NONE, TRANSACTION_STATUS_MODIFIED from regluit.payment.parameters import TRANSACTION_STATUS_ACTIVE, TRANSACTION_STATUS_COMPLETE, TRANSACTION_STATUS_CANCELED, TRANSACTION_STATUS_ERROR, TRANSACTION_STATUS_FAILED, TRANSACTION_STATUS_INCOMPLETE, TRANSACTION_STATUS_NONE, TRANSACTION_STATUS_MODIFIED
@ -393,7 +393,7 @@ def manage_campaign(request, id):
alerts.append(_('Campaign data has NOT been saved')) alerts.append(_('Campaign data has NOT been saved'))
if 'launch' in request.POST.keys(): if 'launch' in request.POST.keys():
activetab = '#3' activetab = '#3'
if (campaign.launchable and form.is_valid()): if (campaign.launchable and form.is_valid()) and (not settings.IS_PREVIEW or request.user.is_staff):
campaign.activate() campaign.activate()
alerts.append(_('Campaign has been launched')) alerts.append(_('Campaign has been launched'))
else: else:
@ -426,6 +426,7 @@ def manage_campaign(request, id):
'premium_form' : new_premium_form, 'premium_form' : new_premium_form,
'work': work, 'work': work,
'activetab': activetab, 'activetab': activetab,
'is_preview': settings.IS_PREVIEW
}) })
def googlebooks(request, googlebooks_id): def googlebooks(request, googlebooks_id):
@ -1160,7 +1161,7 @@ def rh_tools(request):
claim.campaigns = claim.work.campaigns.all() claim.campaigns = claim.work.campaigns.all()
else: else:
claim.campaigns = [] claim.campaigns = []
claim.can_open_new=True claim.can_open_new=False if claim.work.last_campaign_status in ['ACTIVE','INITIALIZED'] else True
for campaign in claim.campaigns: for campaign in claim.campaigns:
if campaign.status in ['ACTIVE','INITIALIZED']: if campaign.status in ['ACTIVE','INITIALIZED']:
claim.can_open_new=False claim.can_open_new=False
@ -1185,7 +1186,17 @@ def rh_tools(request):
claim.campaign_form = OpenCampaignForm(data={'work': claim.work, 'name': claim.work.title, 'userid': request.user.id, 'managers_1': request.user.id}) claim.campaign_form = OpenCampaignForm(data={'work': claim.work, 'name': claim.work.title, 'userid': request.user.id, 'managers_1': request.user.id})
else: else:
claim.can_open_new=False claim.can_open_new=False
return render(request, "rh_tools.html", {'claims': claims ,}) campaigns = request.user.campaigns.all()
new_campaign = None
for campaign in campaigns:
if campaign.clonable():
if request.method == 'POST' and request.POST.has_key('c%s-campaign_id'% campaign.id):
clone_form= CloneCampaignForm(data=request.POST, prefix = 'c%s' % campaign.id)
if clone_form.is_valid():
campaign.clone()
else:
campaign.clone_form= CloneCampaignForm(initial={'campaign_id':campaign.id}, prefix = 'c%s' % campaign.id)
return render(request, "rh_tools.html", {'claims': claims ,'campaigns': campaigns})
def rh_admin(request): def rh_admin(request):
if not request.user.is_authenticated() : if not request.user.is_authenticated() :
@ -2035,12 +2046,15 @@ def download(request, work_id):
unglued_ebooks = work.ebooks().filter(edition__unglued=True) unglued_ebooks = work.ebooks().filter(edition__unglued=True)
other_ebooks = work.ebooks().filter(edition__unglued=False) other_ebooks = work.ebooks().filter(edition__unglued=False)
unglued_epub_url = work.ebooks().filter(format='epub')[0].url if work.ebooks().filter(format='epub').count() else None try:
readmill_epub_url = work.ebooks().filter(format='epub').exclude(provider='Google Books')[0].url
except:
readmill_epub_url = None
context.update({ context.update({
'unglued_ebooks': unglued_ebooks, 'unglued_ebooks': unglued_ebooks,
'other_ebooks': other_ebooks, 'other_ebooks': other_ebooks,
'unglued_epub_url': unglued_epub_url, 'readmill_epub_url': readmill_epub_url,
'base_url': settings.BASE_URL 'base_url': settings.BASE_URL
}) })

52
requirements_relaunch.pip Normal file
View File

@ -0,0 +1,52 @@
Django==1.4.1
Fabric==1.4.3
MySQL-python==1.2.3
Pillow==1.7.7
Pyzotero==0.9.51
South==0.7.6
amqplib==1.0.2
anyjson==0.3.3
billiard==2.7.3.12
#boto==2.3.0
git+ssh://git@github.com/Gluejar/boto.git@2.3.0
celery==3.0.9
distribute==0.6.28
django-celery==3.0.9
django-ckeditor==3.6.2.1
django-endless-pagination==1.1
django-extensions==0.9
django-kombu==0.9.4
django-maintenancemode==0.10
django-nose-selenium==0.7.3
#django-notification==0.2
git+git://github.com/aladagemre/django-notification.git@2927346f4c513a217ac8ad076e494dd1adbf70e1
django-registration==0.8
django-selectable==0.5.2
django-smtp-ssl==1.0
django-social-auth==0.7.5
django-tastypie==0.9.11
feedparser==5.1.2
freebase==1.0.8
httplib2==0.7.5
kombu==2.4.5
lxml==2.3.5
mechanize==0.2.5
mimeparse==0.1.3
nose==1.1.2
oauth2==1.5.211
paramiko==1.7.7.2
pyasn1==0.1.4
pycrypto==2.6
python-dateutil==2.1
python-openid==2.2.5
pytz==2012d
rdflib==2.4.0
redis==2.6.2
requests==0.14.0
selenium==2.25.0
six==1.2.0
ssh==1.7.14
stripe==1.7.4
virtualenv==1.4.9
virtualenvwrapper==3.6
wsgiref==0.1.2

View File

@ -3,7 +3,7 @@ from regluit.settings.common import *
DEBUG = False DEBUG = False
TEMPLATE_DEBUG = DEBUG TEMPLATE_DEBUG = DEBUG
# we are launched! # we are launched!
IS_PREVIEW = False IS_PREVIEW = True
SITE_ID = 1 SITE_ID = 1

View File

@ -9,7 +9,7 @@ $j(document).ready(function() {
$j("#lightbox").load( $j(this).attr("href") + " #lightbox_content"); $j("#lightbox").load( $j(this).attr("href") + " #lightbox_content");
if ($j(this).attr("href").substr(-9,8) == "download") { if ($j(this).attr("href").substr(-9,8) == "download") {
jQuery.getScript('/static/js/readmill.js'); jQuery.getScript('https://platform.readmill.com/send.js');
} }
// fade-out rest of page elements on expand // fade-out rest of page elements on expand