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 %} +
Are you a Smashwords author? Do you want to unglue your book? You can! Here's how:
+ +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.
+ +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 @@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 %}
{% for transaction in transactions.supporters %}
- {{ transaction.ack_name }}
+
- 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 @@ +Excludes pledges canceled, failed, or otherwise uncollectable.
+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.
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