diff --git a/core/models.py b/core/models.py index 956dc00b..530ace93 100755 --- a/core/models.py +++ b/core/models.py @@ -462,8 +462,7 @@ class Campaign(models.Model): it's easier to return transactions than ungluers """ p = PaymentManager() - ungluers={"all":[],"supporters":[], "patrons":[], "bibliophiles":[]} - anons = 0 + ungluers={"all":[],"supporters":[], "anon_supporters": 0, "patrons":[], "anon_patrons": 0, "bibliophiles":[]} if self.status == "ACTIVE": translist = p.query_campaign(self, summary=False, pledged=True, authorized=True) elif self.status == "SUCCESSFUL": @@ -472,14 +471,18 @@ class Campaign(models.Model): translist = [] for transaction in translist: ungluers['all'].append(transaction.user) - if transaction.anonymous: - anons += 1 if transaction.amount >= Premium.TIERS["bibliophile"]: ungluers['bibliophiles'].append(transaction) elif transaction.amount >= Premium.TIERS["patron"]: - ungluers['patrons'].append(transaction) + if transaction.anonymous: + ungluers['anon_patrons'] += 1 + else: + ungluers['patrons'].append(transaction) elif transaction.amount >= Premium.TIERS["supporter"]: - ungluers['supporters'].append(transaction) + if transaction.anonymous: + ungluers['anon_supporters'] += 1 + else: + ungluers['supporters'].append(transaction) return ungluers diff --git a/frontend/templates/about_smashwords.html b/frontend/templates/about_smashwords.html new file mode 100644 index 00000000..92ade85e --- /dev/null +++ b/frontend/templates/about_smashwords.html @@ -0,0 +1,28 @@ +{% extends "basedocumentation.html" %} + +{% block doccontent %} +

Smashwords + Unglue.it

+ +

Are you a Smashwords author? Do you want to unglue your book? You can! Here's how:

+ + + +

Why Unglue Your Book?

+ +

As a self-published author, you know that innovative marketing and distribution are critical.

+ +

Ungluing campaigns guarantee you income when campaigns succeed, without risk to you if they don't. Whether campaigns succeed or fail, they're a tool for you to reach out to, discover, interact with, and increase your audience while raising the profile of your work. Successful campaigns mean wider distribution of your book -- our first unglued ebook reached #3 most-downloaded in Amazon's Kindle eBooks > Nonfiction > Social Sciences category. Your unglued book, in turn, becomes an ambassador for the rest of your work.

+ +

Rest assured that, when you unglue a book, you retain the copyright. You can choose whichever Creative Commons license serves your interests best, whether that's inviting your fans to remix your work or sharing it widely while reserving certain rights. You're still welcome to sell your book under the terms of your Smashwords agreement, and to separately license other editions. Ungluing isn't instead of your book's other opportunities -- it's in addition to them.

+ +

The Fine Print

+ +

Don't the Smashwords Terms of Service say that authors can't distribute Meatgrinder files through other channels without special permission?

+

Yes. You have that permission, courtesy of an agreement between Smashwords and Unglue.it.

+ +

Where can I learn more about Unglue.it's terms?

+

Please see our Terms of Service and our FAQ.

+{% endblock %} \ No newline at end of file diff --git a/frontend/templates/admins_only.html b/frontend/templates/admins_only.html index a979891e..98505409 100644 --- a/frontend/templates/admins_only.html +++ b/frontend/templates/admins_only.html @@ -4,6 +4,10 @@

Admin Users Only

-

This function is only available to Unglue.it administration.

+

This function is only available to Unglue.it administration or authorized campaign managers.

+ +

If you believe you should have been able to do something here, please make sure you're logged in.

+ +

Campaign managers can create new editions of works that are already in the Unglue.it system, but only Unglue.it staff can create editions for works not in the system. If you're a campaign manager trying to add your work to the database, please send us the ISBN-13, publisher, year of publication, and a cover image, and we'll set that up for you.

{% endblock %} \ No newline at end of file diff --git a/frontend/templates/base.html b/frontend/templates/base.html index 57aa62f6..6f9b4b4d 100644 --- a/frontend/templates/base.html +++ b/frontend/templates/base.html @@ -22,7 +22,7 @@ {% block base_js %} {% endblock %} - + {% block extra_js %} {% endblock %} @@ -139,7 +139,7 @@ {% block news %}
- We unglued one book. Amazon shut us down. Now we're back. Help us unglue four more books!
+ We unglued one book. Amazon shut us down. Now we're back. Help us unglue five more books!
{% endblock %} diff --git a/frontend/templates/faq.html b/frontend/templates/faq.html index 86f6891e..791de16d 100644 --- a/frontend/templates/faq.html +++ b/frontend/templates/faq.html @@ -562,9 +562,13 @@ Need more ideas? We're happy to work with rights holders personally to craft a
The campaign ends. Supporters' credit cards are not charged, so you are not paid, and not obligated to release an unglued ebook.

If you're concerned a campaign may not reach its goal you have several options. You can lower the target price to a level more likely to succeed. Or you are welcome to run a second campaign at a later time, perhaps with a different goal, duration, and publicity strategy. We'd be happy to consult with you about this.
+
Is there a minimum funding goal?
+ +
Yes, the minimum funding goal is $500.
+
What fees does Unglue.it charge?
-
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.
+
When a campaign succeeds, Unglue.it will deduct a 6% commission (or $60, whichever is greater) 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.
Does it cost money to start a campaign on Unglue.it?
diff --git a/frontend/templates/front_matter.html b/frontend/templates/front_matter.html index 27afe56d..51b820c5 100644 --- a/frontend/templates/front_matter.html +++ b/frontend/templates/front_matter.html @@ -1,7 +1,30 @@ {% extends "basedocumentation.html" %} {% block title %}Ungluers supporting the {{ campaign }}{% endblock %} {% block extra_extra_head %} - + + {% endblock %} {% block doccontent %} @@ -25,31 +48,53 @@ Unglued ebooks are made possible through the Unglue.it website by contributions from {{ transactions.all|length }} readers like you.

{% if transactions.supporters %}

Supporters of this edition:

-

{% for transaction in transactions.supporters %} - {{ transaction.ack_name }}
+

+ {{ transaction.ack_name }} +
{% endfor %} -

+ {% if transactions.anon_supporters %} +
+ Anonymous ({{ transactions.anon_supporters }}) +
+ {% endif %} +
{% endif %} {% if transactions.patrons %}

Benefactors of this edition:

{% for transaction in transactions.patrons %} - {{ transaction.ack_name }}
+
+ {% if transaction.ack_link %} + {{ transaction.ack_name }} + {% else %} + {{ transaction.ack_name }} + {% endif %} +
{% endfor %} + {% if transactions.anon_supporters %} +
+ Anonymous ({{ transactions.anon_patrons }}) +
+ {% endif %} +
{% endif %} {% if transactions.bibliophiles %}

Bibliophiles of this edition:

{% for transaction in transactions.bibliophiles %} -
{{ transaction.ack_name }}
-
{{ transaction.ack_dedication }}
+ {% if not transaction.anonymous %} +
{% if transaction.ack_link %}{{ transaction.ack_name }}{% else %}{{ transaction.ack_name }}{% endif %}
+ {% else %} +
Anonymous
+ {% endif %} +
{{ transaction.ack_dedication }}
{% endfor %}
{% endif %} {% endwith %}

- You can say thank you by supporting the ungluing of more books at https://unglue.it/ . + You can say thank you by supporting the ungluing of more books at https://unglue.it/ .

diff --git a/frontend/templates/metrics.html b/frontend/templates/metrics.html index 8afdd4fe..072e4d4c 100644 --- a/frontend/templates/metrics.html +++ b/frontend/templates/metrics.html @@ -92,5 +92,20 @@ +

Pledges

+
+
How much has been pledged to Unglue.it?
+

Excludes pledges canceled, failed, or otherwise uncollectable.

+
+ +
+
{% endblock %} diff --git a/frontend/templates/pledge_card_error.html b/frontend/templates/pledge_card_error.html index 47465874..cdb69eb8 100644 --- a/frontend/templates/pledge_card_error.html +++ b/frontend/templates/pledge_card_error.html @@ -15,9 +15,9 @@
-

Error: Card authorization for a pledge transaction

+

Error: Card authorization

-

Unglue.it would like to process your pledge, but there's been some problem processing your credit card ({{exception.message}}). Please try again, or contact unglue.it support. +

Unglue.it would like to accept your credit card, but we encountered the following problem: {{exception.message}}. Please try again, or contact unglue.it support.

diff --git a/frontend/templates/terms.html b/frontend/templates/terms.html index 8b31f41c..6508aa97 100644 --- a/frontend/templates/terms.html +++ b/frontend/templates/terms.html @@ -115,7 +115,7 @@ The Company's mission is to make copyrighted literary works freely and readily a

Rights Holders who have executed a Platform Services Agreement may claim works and may initiate and conduct Campaigns as described in the Rights Holder Tools page at https://unglue.it/rightsholders/.

-

There is no charge for Rights Holders to launch and fund a Campaign using the Service. However, the Company will withhold a “Sales Commission” of 6% of gross aggregate Contributions upon the completion of a successful Campaign. The Rights Holder is responsible for providing a Standard Ebook File at their own expense, as described in a Platform Services Agreement.

+

There is no charge for Rights Holders to launch and fund a Campaign using the Service. However, the Company will withhold a “Sales Commission” of 6% of gross aggregate Contributions (or $60, whichever is greater) upon the completion of a successful Campaign. The Rights Holder is responsible for providing a Standard Ebook File at their own expense, as described in a Platform Services Agreement.

The Rights Holder hereby authorizes the Company to use the Service to display and market the Subject Work, to collect Contributions or cause Contributions to be collected from Supporters on behalf of the Rights Holder, and to retain, reserve and/or distribute the Contributions to the Rights Holder, to the Company, to Designated Vendors (as defined below) or otherwise pursuant to the terms of this Agreement.

diff --git a/frontend/views.py b/frontend/views.py index 581d9b21..28b256e7 100755 --- a/frontend/views.py +++ b/frontend/views.py @@ -1595,11 +1595,26 @@ class InfoPageView(TemplateView): wishlists.yesterday = wishlists.filter(created__range = (date_today()-timedelta(days=1), date_today())) else: wishlists.yesterday = wishlists.month.filter(created__day = date_today().day-1) + + transactions = Transaction.objects.filter(status__in = [TRANSACTION_STATUS_ACTIVE, TRANSACTION_STATUS_COMPLETE]) + transactions.sum = transactions.aggregate(Sum('amount'))['amount__sum'] + transactions.today = transactions.filter(date_created__range = (date_today(), now())) + transactions.today.sum = transactions.today.aggregate(Sum('amount'))['amount__sum'] + transactions.days7 = transactions.filter(date_created__range = (date_today()-timedelta(days=7), now())) + transactions.days7.sum = transactions.days7.aggregate(Sum('amount'))['amount__sum'] + transactions.year = transactions.filter(date_created__year = date_today().year) + transactions.year.sum = transactions.year.aggregate(Sum('amount'))['amount__sum'] + transactions.month = transactions.filter(date_created__month = date_today().month) + transactions.month.sum = transactions.month.aggregate(Sum('amount'))['amount__sum'] + transactions.yesterday = transactions.filter(date_created__range = (date_today()-timedelta(days=1), date_today())) + transactions.yesterday.sum = transactions.yesterday.aggregate(Sum('amount'))['amount__sum'] + return { 'users': users, 'works': works, 'ebooks': ebooks, 'wishlists': wishlists, + 'transactions': transactions, } class InfoLangView(TemplateView): diff --git a/payment/models.py b/payment/models.py index ed9f3715..4cc3cf6b 100644 --- a/payment/models.py +++ b/payment/models.py @@ -105,7 +105,7 @@ class Transaction(models.Model): @property def ack_link(self): - return 'https://unglue.it/supporter/%s'%urllib.urlencode(self.user.username) if not self.anonymous else '' + return 'https://unglue.it/supporter/%s' % urllib.quote(self.user.username) if not self.anonymous else '' def save(self, *args, **kwargs): if not self.secret: diff --git a/payment/stripelib.py b/payment/stripelib.py index b3b1925a..764d5401 100644 --- a/payment/stripelib.py +++ b/payment/stripelib.py @@ -478,7 +478,6 @@ class StripeErrorTest(TestCase): self.assertEqual(e.code, "missing") self.assertEqual(e.message, "Cannot charge a customer that has no active card") - class PledgeScenarioTest(TestCase): @classmethod def setUpClass(cls): @@ -658,6 +657,14 @@ class Processor(baseprocessor.Processor): transaction.error = e.message transaction.save() + # fire off the fact that transaction failed -- should actually do so only if not a transient error + # if card_declined or expired card, ask user to update account + if e.code in ('card_declined', 'expired_card'): + transaction_failed.send(sender=self, transaction=transaction) + # otherwise, report exception to us + else: + logger.exception("transaction id {0}, exception: {1}".format(transaction.id, e.message)) + raise StripelibError(e.message, e) else: @@ -667,6 +674,11 @@ class Processor(baseprocessor.Processor): transaction.pay_key = charge.id transaction.date_payment = now() transaction.save() + + # fire signal for sucessful transaction + transaction_charged.send(sender=self, transaction=transaction) + + else: # nothing to charge raise StripeLibError("No customer id available to charge for transaction {0}".format(transaction.id), None) @@ -751,7 +763,9 @@ class Processor(baseprocessor.Processor): pass elif resource == 'charge': # we need to handle: succeeded, failed, refunded, disputed + if action == 'succeeded': + # TO DO: delete this logic since we don't do anything but look up transaction. logger.info("charge.succeeded webhook for {0}".format(ev_object.get("id"))) # try to parse description of object to pull related transaction if any # wrapping this in a try statement because it possible that we have a charge.succeeded outside context of unglue.it @@ -764,12 +778,11 @@ class Processor(baseprocessor.Processor): logger.info("ev_object.id == transaction.pay_key: {0}".format(ev_object.id)) else: logger.warning("ev_object.id {0} <> transaction.pay_key {1}".format(ev_object.id, transaction.pay_key)) - # now -- should fire off transaction_charged here -- if so we need to move it from ? - transaction_charged.send(sender=self, transaction=transaction) except Exception, e: - logger.warning(e) + logger.warning(e) elif action == 'failed': + # TO DO: delete this logic since we don't do anything but look up transaction. logger.info("charge.failed webhook for {0}".format(ev_object.get("id"))) try: charge_meta = json.loads(ev_object["description"]) @@ -780,8 +793,7 @@ class Processor(baseprocessor.Processor): logger.info("ev_object.id == transaction.pay_key: {0}".format(ev_object.id)) else: logger.warning("ev_object.id {0} <> transaction.pay_key {1}".format(ev_object.id, transaction.pay_key)) - # now -- should fire off transaction_charged here -- if so we need to move it from ? - transaction_failed.send(sender=self, transaction=transaction) + except Exception, e: logger.warning(e) elif action == 'refunded': diff --git a/settings/common.py b/settings/common.py index 71b70371..155cae1a 100644 --- a/settings/common.py +++ b/settings/common.py @@ -233,7 +233,7 @@ GOODREADS_API_KEY = "" GOODREADS_API_SECRET = "" # unglue.it parameters -UNGLUEIT_MINIMUM_TARGET = '1000' # in US Dollars +UNGLUEIT_MINIMUM_TARGET = '500' # in US Dollars UNGLUEIT_LONGEST_DEADLINE = '180' # number of days allowed for a campaign UNGLUEIT_RECOMMENDED_USERNAME = 'unglueit' diff --git a/static/css/vanilla.css b/static/css/vanilla.css index 3b8bca0c..ba6b6e0f 100755 --- a/static/css/vanilla.css +++ b/static/css/vanilla.css @@ -223,12 +223,11 @@ sub { div.picture-right { float: right ; margin-bottom: 1em; - margin-left: 1em;"} + margin-left: 1em; } .aut { text-align: center ; font-style: italic; } .agate-info { - font-size: 80%; } - + font-size: 80%; } \ No newline at end of file diff --git a/static/js/hijax.js b/static/js/hijax.js index 6cc6596d..3dc27112 100644 --- a/static/js/hijax.js +++ b/static/js/hijax.js @@ -22,7 +22,7 @@ $j(document).ready(function() { $j('#feedback').css({"opacity": "0.07"}); $j('#js-page-wrap').css({"opacity": "0.07"}); $j('#footer').css({"opacity": "0.07"}); - $j('#about_expandable').css({'position': 'absolute'}); + $j('#about_expandable').css({'position': 'fixed'}); $j('#about_expandable').fadeTo("slow", 1); // if we're on a supporter page, personalize our about box diff --git a/static/js/loader-gif.js b/static/js/loader-gif.js new file mode 100644 index 00000000..0a5b381e --- /dev/null +++ b/static/js/loader-gif.js @@ -0,0 +1,6 @@ +var $j = jQuery.noConflict(); +$j().ready(function() { + $j(".loader-gif").click(function(event) { + $j(this).addClass("show-loading"); + }); +}); \ No newline at end of file