Merge branch 'master' into successful_works_in_inglued_tab
commit
bf6cfb7cb2
|
@ -2,9 +2,25 @@ from selectable.base import ModelLookup
|
||||||
from selectable.registry import registry
|
from selectable.registry import registry
|
||||||
|
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
from regluit.core.models import Work
|
||||||
|
|
||||||
class OwnerLookup(ModelLookup):
|
class OwnerLookup(ModelLookup):
|
||||||
model = User
|
model = User
|
||||||
search_fields = ('username__icontains',)
|
search_fields = ('username__icontains',)
|
||||||
|
|
||||||
registry.register(OwnerLookup)
|
class WorkLookup(ModelLookup):
|
||||||
|
model = Work
|
||||||
|
search_fields = ('title__istartswith',)
|
||||||
|
filters = {'language': 'en', }
|
||||||
|
def get_item_label(self,item):
|
||||||
|
return "%s (%s)"%(item.title,item.id)
|
||||||
|
|
||||||
|
def get_query(self, request, term):
|
||||||
|
results = super(WorkLookup, self).get_query(request, term)
|
||||||
|
language = request.GET.get('language', 'en')
|
||||||
|
results = results.filter(language=language)
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
registry.register(OwnerLookup)
|
||||||
|
registry.register(WorkLookup)
|
|
@ -249,15 +249,15 @@ class BookLoaderTests(TestCase):
|
||||||
#ebook_epub = edition.ebooks.all()[0]
|
#ebook_epub = edition.ebooks.all()[0]
|
||||||
ebook_epub = edition.ebooks.filter(format='epub')[0]
|
ebook_epub = edition.ebooks.filter(format='epub')[0]
|
||||||
self.assertEqual(ebook_epub.format, 'epub')
|
self.assertEqual(ebook_epub.format, 'epub')
|
||||||
#self.assertEqual(ebook_epub.url, 'http://books.google.com/books/download/The_Latin_language.epub?id=U3FXAAAAYAAJ&ie=ISO-8859-1&output=epub&source=gbs_api')
|
#self.assertEqual(ebook_epub.url, 'http://books.google.com/books/download/The_Latin_language.epub?id=N1RfAAAAMAAJ&ie=ISO-8859-1&output=epub&source=gbs_api')
|
||||||
self.assertEqual(parse_qs(urlparse(ebook_epub.url).query).get("id"), ['U3FXAAAAYAAJ'])
|
self.assertEqual(parse_qs(urlparse(ebook_epub.url).query).get("id"), ['N1RfAAAAMAAJ'])
|
||||||
self.assertEqual(parse_qs(urlparse(ebook_epub.url).query).get("output"), ['epub'])
|
self.assertEqual(parse_qs(urlparse(ebook_epub.url).query).get("output"), ['epub'])
|
||||||
self.assertEqual(ebook_epub.provider, 'Google Books')
|
self.assertEqual(ebook_epub.provider, 'Google Books')
|
||||||
self.assertEqual(ebook_epub.set_provider(), 'Google Books')
|
self.assertEqual(ebook_epub.set_provider(), 'Google Books')
|
||||||
ebook_pdf = edition.ebooks.filter(format='pdf')[0]
|
ebook_pdf = edition.ebooks.filter(format='pdf')[0]
|
||||||
self.assertEqual(ebook_pdf.format, 'pdf')
|
self.assertEqual(ebook_pdf.format, 'pdf')
|
||||||
#self.assertEqual(ebook_pdf.url, 'http://books.google.com/books/download/The_Latin_language.pdf?id=U3FXAAAAYAAJ&ie=ISO-8859-1&output=pdf&sig=ACfU3U2yLt3nmTncB8ozxOWUc4iHKUznCA&source=gbs_api')
|
#self.assertEqual(ebook_pdf.url, 'http://books.google.com/books/download/The_Latin_language.pdf?id=N1RfAAAAMAAJ&ie=ISO-8859-1&output=pdf&sig=ACfU3U2yLt3nmTncB8ozxOWUc4iHKUznCA&source=gbs_api')
|
||||||
self.assertEqual(parse_qs(urlparse(ebook_pdf.url).query).get("id"), ['U3FXAAAAYAAJ'])
|
self.assertEqual(parse_qs(urlparse(ebook_pdf.url).query).get("id"), ['N1RfAAAAMAAJ'])
|
||||||
self.assertEqual(parse_qs(urlparse(ebook_pdf.url).query).get("output"), ['pdf'])
|
self.assertEqual(parse_qs(urlparse(ebook_pdf.url).query).get("output"), ['pdf'])
|
||||||
self.assertEqual(ebook_pdf.provider, 'Google Books')
|
self.assertEqual(ebook_pdf.provider, 'Google Books')
|
||||||
self.assertEqual(edition.public_domain, True)
|
self.assertEqual(edition.public_domain, True)
|
||||||
|
|
|
@ -13,8 +13,8 @@ from decimal import Decimal as D
|
||||||
from selectable.forms import AutoCompleteSelectMultipleWidget,AutoCompleteSelectMultipleField
|
from selectable.forms import AutoCompleteSelectMultipleWidget,AutoCompleteSelectMultipleField
|
||||||
from selectable.forms import AutoCompleteSelectWidget,AutoCompleteSelectField
|
from selectable.forms import AutoCompleteSelectWidget,AutoCompleteSelectField
|
||||||
|
|
||||||
from regluit.core.models import UserProfile, RightsHolder, Claim, Campaign, Premium, Ebook, Edition, PledgeExtra
|
from regluit.core.models import UserProfile, RightsHolder, Claim, Campaign, Premium, Ebook, Edition, PledgeExtra, Work
|
||||||
from regluit.core.lookups import OwnerLookup
|
from regluit.core.lookups import OwnerLookup, WorkLookup
|
||||||
|
|
||||||
from regluit.utils.localdatetime import now
|
from regluit.utils.localdatetime import now
|
||||||
|
|
||||||
|
@ -208,6 +208,36 @@ def getTransferCreditForm(maximum, data=None, *args, **kwargs ):
|
||||||
)
|
)
|
||||||
return TransferCreditForm( data=data )
|
return TransferCreditForm( data=data )
|
||||||
|
|
||||||
|
|
||||||
|
class WorkForm(forms.Form):
|
||||||
|
other_work = forms.ModelChoiceField(queryset=Work.objects.all(),
|
||||||
|
widget=forms.HiddenInput(),
|
||||||
|
required=True,
|
||||||
|
error_messages={'required': 'Missing work to merge with.'},
|
||||||
|
)
|
||||||
|
work=None
|
||||||
|
|
||||||
|
def clean_other_work(self):
|
||||||
|
if self.cleaned_data["other_work"].id== self.work.id:
|
||||||
|
raise forms.ValidationError(_("You can't merge a work into itself"))
|
||||||
|
return self.cleaned_data["other_work"]
|
||||||
|
|
||||||
|
def __init__(self, work=None, *args, **kwargs):
|
||||||
|
super(WorkForm, self).__init__(*args, **kwargs)
|
||||||
|
self.work=work
|
||||||
|
|
||||||
|
class OtherWorkForm(WorkForm):
|
||||||
|
other_work = AutoCompleteSelectField(
|
||||||
|
WorkLookup,
|
||||||
|
label='Other Work',
|
||||||
|
widget=AutoCompleteSelectWidget(WorkLookup),
|
||||||
|
required=True,
|
||||||
|
error_messages={'required': 'Missing work to merge with.'},
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(OtherWorkForm, self).__init__(*args, **kwargs)
|
||||||
|
self.fields['other_work'].widget.update_query_parameters({'language':self.work.language})
|
||||||
|
|
||||||
class EditManagersForm(forms.ModelForm):
|
class EditManagersForm(forms.ModelForm):
|
||||||
managers = AutoCompleteSelectMultipleField(
|
managers = AutoCompleteSelectMultipleField(
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
{% extends "basedocumentation.html" %}
|
||||||
|
|
||||||
|
{% block doccontent %}
|
||||||
|
<h1>Smashwords + Unglue.it</h1>
|
||||||
|
|
||||||
|
<p>Are you a Smashwords author? Do you want to unglue your book? You can! Here's how:</p>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li>Make sure your book meets Smashwords' <a href="http://www.smashwords.com/distribution">standards for inclusion</a> in their Premium Catalog. Consult the <a href="http://www.smashwords.com/books/view/52">Smashwords Style Guide</a> for details.</li>
|
||||||
|
<li>Contact <a href="mailto:rights@gluejar.com">rights@gluejar.com</a> about signing a Platform Services Agreement with Unglue.it, and follow the instructions on the <a href="/rightsholders/">rights holder tools page</a> to claim your book, develop a publicity strategy, and start a campaign.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2>Why Unglue Your Book?</h2>
|
||||||
|
|
||||||
|
<p>As a self-published author, you know that innovative marketing and distribution are critical.</p>
|
||||||
|
|
||||||
|
<p>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 <a href="https://unglue.it/work/81834/">first unglued ebook</a> 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.</p>
|
||||||
|
|
||||||
|
<p>Rest assured that, when you unglue a book, you retain the copyright. You can choose whichever <a href="http://creativecommons.org/licenses/">Creative Commons license</a> 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.</p>
|
||||||
|
|
||||||
|
<h2>The Fine Print</h2>
|
||||||
|
|
||||||
|
<p><I>Don't the <a href="http://www.smashwords.com/about/tos">Smashwords Terms of Service</a> say that authors can't distribute Meatgrinder files through other channels without special permission?</i></p>
|
||||||
|
<p>Yes. You have that permission, courtesy of an agreement between Smashwords and Unglue.it.</p>
|
||||||
|
|
||||||
|
<p><I>Where can I learn more about Unglue.it's terms?</I></p>
|
||||||
|
<p>Please see our <a href="{% url terms %}">Terms of Service</a> and our <a href="{% url faq %}">FAQ</a>.</p>
|
||||||
|
{% endblock %}
|
|
@ -4,6 +4,10 @@
|
||||||
|
|
||||||
<h2>Admin Users Only</h2>
|
<h2>Admin Users Only</h2>
|
||||||
|
|
||||||
<p>This function is only available to Unglue.it administration.</p>
|
<p>This function is only available to Unglue.it administration or authorized campaign managers.</p>
|
||||||
|
|
||||||
|
<p>If you believe you should have been able to do something here, please make sure you're logged in.</p>
|
||||||
|
|
||||||
|
<p>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.</p>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
|
@ -22,7 +22,7 @@
|
||||||
{% block base_js %}
|
{% block base_js %}
|
||||||
<script type="text/javascript" src="{{ jquery_home }}"></script>
|
<script type="text/javascript" src="{{ jquery_home }}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
<script type="text/javascript" src="/static/js/hijax.js?v=1"></script>
|
<script type="text/javascript" src="/static/js/hijax.js?v=2"></script>
|
||||||
{% block extra_js %}
|
{% block extra_js %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
<script type="text/javascript" src="/static/js/watermark_init.js"></script>
|
<script type="text/javascript" src="/static/js/watermark_init.js"></script>
|
||||||
|
@ -139,7 +139,7 @@
|
||||||
|
|
||||||
{% block news %}
|
{% block news %}
|
||||||
<div class="launch_top">
|
<div class="launch_top">
|
||||||
We unglued <a href="/work/81834/">one book</a>. Amazon <a href="http://blog.unglue.it/2012/08/09/open-thread-amazon-forces-unglue-it-to-suspend-crowdfunding-for-creative-commons-ebooks/">shut us down</a>. Now we're back. Help us unglue <a href="https://unglue.it/campaigns/ending">four more books</a>! <br />
|
We've unglued our <a href="/work/113782/">second</a> and <a href="/work/76348/">third</a> books. Help us <a href="https://unglue.it/campaigns/ending">unglue even more</a>! <br />
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -562,9 +562,13 @@ Need more ideas? We're happy to work with rights holders personally to craft a
|
||||||
<dd>The campaign ends. Supporters' credit cards are not charged, so you are not paid, and not obligated to release an unglued ebook.<br /><br />
|
<dd>The campaign ends. Supporters' credit cards are not charged, so you are not paid, and not obligated to release an unglued ebook.<br /><br />
|
||||||
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.</dd>
|
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.</dd>
|
||||||
|
|
||||||
|
<dt>Is there a minimum funding goal?</dt>
|
||||||
|
|
||||||
|
<dd>Yes, the minimum funding goal is $500.</dd>
|
||||||
|
|
||||||
<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 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 (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.</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>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
{% extends "basedocumentation.html" %}
|
||||||
|
|
||||||
|
{% block extra_extra_head %}
|
||||||
|
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.13/themes/ui-lightness/jquery-ui.css" type="text/css" media="screen">
|
||||||
|
{{ form.media.css }}
|
||||||
|
<script type="text/javascript" src="{{ jquery_home }}"></script>
|
||||||
|
<script type="text/javascript" src="{{ jquery_ui_home }}" ></script>
|
||||||
|
{{ form.media.js }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block doccontent %}
|
||||||
|
{% if merge_complete %}
|
||||||
|
<h2>Merge {{ old_work_id }} into {{ work.id }} Complete</h2>
|
||||||
|
{% include 'workbox.html' %}
|
||||||
|
{% else %}
|
||||||
|
<h2>Merge Works</h2>
|
||||||
|
<form method="POST" action="#">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ form.as_p }}
|
||||||
|
<h3>Merge this work...</h3>
|
||||||
|
{% if other_work %}
|
||||||
|
{% with other_work as work%}
|
||||||
|
{% include 'workbox.html' %}
|
||||||
|
{% endwith %}
|
||||||
|
{% endif %}
|
||||||
|
<h3>... into the current work</h3>
|
||||||
|
{% include 'workbox.html' %}
|
||||||
|
{% if other_work %}
|
||||||
|
<input type="submit" name="confirm_merge_works" value="Confirm Merge" id="submit" />
|
||||||
|
{% else %}
|
||||||
|
<input type="submit" name="merge_works" value="Preview Merge Works" id="submit" />
|
||||||
|
{% endif %}
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
|
@ -92,5 +92,20 @@
|
||||||
</dd>
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
|
<h2>Pledges</h2>
|
||||||
|
<dl>
|
||||||
|
<dt>How much has been pledged to Unglue.it?</dt>
|
||||||
|
<p>Excludes pledges canceled, failed, or otherwise uncollectable.</p>
|
||||||
|
<dd>
|
||||||
|
<ul class="terms">
|
||||||
|
<li>{{ transactions.today.count }} pledges have been made today totaling ${% if transactions.today.sum %}{{ transactions.today.sum }}{% else %}0{% endif %}.
|
||||||
|
<li>{{ transactions.yesterday.count }} pledges were made yesterday totaling ${% if transactions.yesterday.sum %}{{ transactions.yesterday.sum }}{% else %}0{% endif %}.
|
||||||
|
<li>{{ transactions.days7.count }} pledges have been made in the past 7 days totaling ${% if transactions.days7.sum %}{{ transactions.days7.sum }}{% else %}0{% endif %}.
|
||||||
|
<li>{{ transactions.month.count }} pledges have been made in the past month totaling ${% if transactions.month.sum %}{{ transactions.month.sum }}{% else %}0{% endif %}.
|
||||||
|
<li>{{ transactions.count }} pledges have been made altogether totaling ${{ transactions.sum }}.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -9,4 +9,4 @@ If necessary to provide you with any premiums you requested, {{ campaign.rightsh
|
||||||
|
|
||||||
Thank you for your support.
|
Thank you for your support.
|
||||||
|
|
||||||
{{ campaign.rightsholder }} (rights holder for {{ campaign.work.title }} and the Unglue.it team
|
{{ campaign.rightsholder }} (rights holder for {{ campaign.work.title }}) and the Unglue.it team
|
|
@ -15,9 +15,9 @@
|
||||||
<div class="jsmodule rounded clearfix">
|
<div class="jsmodule rounded clearfix">
|
||||||
<div class="jsmod-content">
|
<div class="jsmod-content">
|
||||||
|
|
||||||
<div><h2>Error: Card authorization for a pledge transaction</h2>
|
<div><h2>Error: Card authorization</h2>
|
||||||
<div>
|
<div>
|
||||||
<p>Unglue.it would like to process your pledge, but there's been some problem processing your credit card (<b>{{exception.message}}</b>). Please <a href="{{ request.get_full_path }}">try again</a>, or contact <a href="mailto:support@gluejar.com?subject={{ request.get_full_path|urlencode }}">unglue.it support</a>.
|
<p>Unglue.it would like to accept your credit card, but we encountered the following problem: <b>{{exception.message}}</b>. Please <a href="{{ request.get_full_path }}">try again</a>, or contact <a href="mailto:support@gluejar.com?subject={{ request.get_full_path|urlencode }}">unglue.it support</a>.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -115,7 +115,7 @@ The Company's mission is to make copyrighted literary works freely and readily a
|
||||||
|
|
||||||
<p>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 <a href="https://unglue.it/rightsholders/">https://unglue.it/rightsholders/</a>.</p>
|
<p>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 <a href="https://unglue.it/rightsholders/">https://unglue.it/rightsholders/</a>.</p>
|
||||||
|
|
||||||
<p>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. </p>
|
<p>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. </p>
|
||||||
|
|
||||||
<p>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. </p>
|
<p>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. </p>
|
||||||
|
|
||||||
|
|
|
@ -321,6 +321,9 @@ $j(document).ready(function(){
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<p>If you'd like to contact us regarding rights for this work, please email <a href="mailto:rights@gluejar.com">rights@gluejar.com</a>.</p>
|
<p>If you'd like to contact us regarding rights for this work, please email <a href="mailto:rights@gluejar.com">rights@gluejar.com</a>.</p>
|
||||||
|
{% if user.is_staff %}<h4>Related Works</h4>
|
||||||
|
<a href="{% url merge work_id %}">Merge other works into this one</a><br />
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if work.subjects.all.count > 0 %}
|
{% if work.subjects.all.count > 0 %}
|
||||||
<h4>Subjects</h4>
|
<h4>Subjects</h4>
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
<div class="workbox">
|
||||||
|
<a href="{% url work work.id %}">{{ work.title }}</a> # {{work.id }}<br />
|
||||||
|
Language: {{ work.language }}<br />
|
||||||
|
Editions: <ol>
|
||||||
|
{% for edition in work.editions.all %}
|
||||||
|
<li>
|
||||||
|
<div class="editionbox">{{ edition.title }}, published by {{edition.publisher}} in {{ edition.publication_date }} with authors
|
||||||
|
{% for author in edition.authors.all %}
|
||||||
|
{{author}},
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ol>
|
||||||
|
</div>
|
|
@ -10,7 +10,7 @@ from regluit.core.feeds import SupporterWishlistFeed
|
||||||
from regluit.core.models import Campaign
|
from regluit.core.models import Campaign
|
||||||
from regluit.frontend.views import GoodreadsDisplayView, LibraryThingView, PledgeView, PledgeCompleteView, PledgeCancelView, PledgeRechargeView, FAQView
|
from regluit.frontend.views import GoodreadsDisplayView, LibraryThingView, PledgeView, PledgeCompleteView, PledgeCancelView, PledgeRechargeView, FAQView
|
||||||
from regluit.frontend.views import CampaignListView, WorkListView, UngluedListView, InfoPageView, InfoLangView, DonationView, FundPledgeView
|
from regluit.frontend.views import CampaignListView, WorkListView, UngluedListView, InfoPageView, InfoLangView, DonationView, FundPledgeView
|
||||||
from regluit.frontend.views import NonprofitCampaign, DonationCredit, PledgeModifiedView, ManageAccount
|
from regluit.frontend.views import NonprofitCampaign, DonationCredit, PledgeModifiedView, ManageAccount, MergeView
|
||||||
|
|
||||||
urlpatterns = patterns(
|
urlpatterns = patterns(
|
||||||
"regluit.frontend.views",
|
"regluit.frontend.views",
|
||||||
|
@ -49,6 +49,7 @@ urlpatterns = patterns(
|
||||||
url(r"^work/(?P<work_id>\d+)/lockss/$", "lockss", name="lockss"),
|
url(r"^work/(?P<work_id>\d+)/lockss/$", "lockss", name="lockss"),
|
||||||
url(r"^lockss/(?P<year>\d+)/$", "lockss_manifest", name="lockss_manifest"),
|
url(r"^lockss/(?P<year>\d+)/$", "lockss_manifest", name="lockss_manifest"),
|
||||||
url(r"^work/(?P<work_id>\d+)/download/$", "download", name="download"),
|
url(r"^work/(?P<work_id>\d+)/download/$", "download", name="download"),
|
||||||
|
url(r"^work/(?P<work_id>\d+)/merge/$", login_required(MergeView.as_view()), name="merge"),
|
||||||
url(r"^work/\d+/acks/images/(?P<file_name>[\w\.]*)$", "static_redirect_view",{'dir': 'images'}),
|
url(r"^work/\d+/acks/images/(?P<file_name>[\w\.]*)$", "static_redirect_view",{'dir': 'images'}),
|
||||||
url(r"^work/(?P<work_id>\d+)/librarything/$", "work_librarything", name="work_librarything"),
|
url(r"^work/(?P<work_id>\d+)/librarything/$", "work_librarything", name="work_librarything"),
|
||||||
url(r"^work/(?P<work_id>\d+)/goodreads/$", "work_goodreads", name="work_goodreads"),
|
url(r"^work/(?P<work_id>\d+)/goodreads/$", "work_goodreads", name="work_goodreads"),
|
||||||
|
|
|
@ -44,11 +44,12 @@ from regluit.core import models, bookloader, librarything
|
||||||
from regluit.core import userlists
|
from regluit.core import userlists
|
||||||
from regluit.core.search import gluejar_search
|
from regluit.core.search import gluejar_search
|
||||||
from regluit.core.goodreads import GoodreadsClient
|
from regluit.core.goodreads import GoodreadsClient
|
||||||
|
from regluit.core.bookloader import merge_works
|
||||||
from regluit.frontend.forms import UserData, UserEmail, ProfileForm, CampaignPledgeForm, GoodreadsShelfLoadingForm
|
from regluit.frontend.forms import UserData, UserEmail, ProfileForm, CampaignPledgeForm, GoodreadsShelfLoadingForm
|
||||||
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, CloneCampaignForm, PlainCCForm
|
from regluit.frontend.forms import getTransferCreditForm, CCForm, CloneCampaignForm, PlainCCForm, WorkForm, OtherWorkForm
|
||||||
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 import baseprocessor
|
from regluit.payment import baseprocessor
|
||||||
|
@ -568,6 +569,46 @@ class CampaignListView(FilterableListView):
|
||||||
context['facet'] =self.kwargs['facet']
|
context['facet'] =self.kwargs['facet']
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
class MergeView(FormView):
|
||||||
|
template_name="merge.html"
|
||||||
|
work=None
|
||||||
|
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
if not request.user.is_staff:
|
||||||
|
return render(request, "admins_only.html")
|
||||||
|
else:
|
||||||
|
return super(MergeView, self).dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super(MergeView, self).get_context_data(**kwargs)
|
||||||
|
context['work']=self.work
|
||||||
|
return context
|
||||||
|
|
||||||
|
def get_form_class(self):
|
||||||
|
if self.request.method == 'POST' and self.request.POST.has_key('confirm_merge_works'):
|
||||||
|
return WorkForm
|
||||||
|
else:
|
||||||
|
return OtherWorkForm
|
||||||
|
|
||||||
|
def get_form_kwargs(self):
|
||||||
|
self.work = get_object_or_404(models.Work, id=self.kwargs["work_id"])
|
||||||
|
form_kwargs= {'work':self.work}
|
||||||
|
if self.request.method == 'POST':
|
||||||
|
form_kwargs.update({'data':self.request.POST})
|
||||||
|
return form_kwargs
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
other_work=form.cleaned_data['other_work']
|
||||||
|
context=self.get_context_data()
|
||||||
|
if self.request.POST.has_key('confirm_merge_works'):
|
||||||
|
context['old_work_id']=other_work.id
|
||||||
|
merge_works(self.work,other_work,self.request.user)
|
||||||
|
context['merge_complete']=True
|
||||||
|
else:
|
||||||
|
context['form']=WorkForm(initial={'other_work':other_work})
|
||||||
|
context['other_work']=other_work
|
||||||
|
return render(self.request, self.template_name, context)
|
||||||
|
|
||||||
class DonationView(TemplateView):
|
class DonationView(TemplateView):
|
||||||
template_name = "donation.html"
|
template_name = "donation.html"
|
||||||
|
|
||||||
|
@ -1597,11 +1638,26 @@ class InfoPageView(TemplateView):
|
||||||
wishlists.yesterday = wishlists.filter(created__range = (date_today()-timedelta(days=1), date_today()))
|
wishlists.yesterday = wishlists.filter(created__range = (date_today()-timedelta(days=1), date_today()))
|
||||||
else:
|
else:
|
||||||
wishlists.yesterday = wishlists.month.filter(created__day = date_today().day-1)
|
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 {
|
return {
|
||||||
'users': users,
|
'users': users,
|
||||||
'works': works,
|
'works': works,
|
||||||
'ebooks': ebooks,
|
'ebooks': ebooks,
|
||||||
'wishlists': wishlists,
|
'wishlists': wishlists,
|
||||||
|
'transactions': transactions,
|
||||||
}
|
}
|
||||||
|
|
||||||
class InfoLangView(TemplateView):
|
class InfoLangView(TemplateView):
|
||||||
|
|
|
@ -478,7 +478,6 @@ class StripeErrorTest(TestCase):
|
||||||
self.assertEqual(e.code, "missing")
|
self.assertEqual(e.code, "missing")
|
||||||
self.assertEqual(e.message, "Cannot charge a customer that has no active card")
|
self.assertEqual(e.message, "Cannot charge a customer that has no active card")
|
||||||
|
|
||||||
|
|
||||||
class PledgeScenarioTest(TestCase):
|
class PledgeScenarioTest(TestCase):
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
|
@ -658,6 +657,14 @@ class Processor(baseprocessor.Processor):
|
||||||
transaction.error = e.message
|
transaction.error = e.message
|
||||||
transaction.save()
|
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)
|
raise StripelibError(e.message, e)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
@ -667,6 +674,11 @@ class Processor(baseprocessor.Processor):
|
||||||
transaction.pay_key = charge.id
|
transaction.pay_key = charge.id
|
||||||
transaction.date_payment = now()
|
transaction.date_payment = now()
|
||||||
transaction.save()
|
transaction.save()
|
||||||
|
|
||||||
|
# fire signal for sucessful transaction
|
||||||
|
transaction_charged.send(sender=self, transaction=transaction)
|
||||||
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# nothing to charge
|
# nothing to charge
|
||||||
raise StripeLibError("No customer id available to charge for transaction {0}".format(transaction.id), None)
|
raise StripeLibError("No customer id available to charge for transaction {0}".format(transaction.id), None)
|
||||||
|
@ -751,7 +763,9 @@ class Processor(baseprocessor.Processor):
|
||||||
pass
|
pass
|
||||||
elif resource == 'charge':
|
elif resource == 'charge':
|
||||||
# we need to handle: succeeded, failed, refunded, disputed
|
# we need to handle: succeeded, failed, refunded, disputed
|
||||||
|
|
||||||
if action == 'succeeded':
|
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")))
|
logger.info("charge.succeeded webhook for {0}".format(ev_object.get("id")))
|
||||||
# try to parse description of object to pull related transaction if any
|
# 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
|
# 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))
|
logger.info("ev_object.id == transaction.pay_key: {0}".format(ev_object.id))
|
||||||
else:
|
else:
|
||||||
logger.warning("ev_object.id {0} <> transaction.pay_key {1}".format(ev_object.id, transaction.pay_key))
|
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:
|
except Exception, e:
|
||||||
logger.warning(e)
|
logger.warning(e)
|
||||||
|
|
||||||
elif action == 'failed':
|
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")))
|
logger.info("charge.failed webhook for {0}".format(ev_object.get("id")))
|
||||||
try:
|
try:
|
||||||
charge_meta = json.loads(ev_object["description"])
|
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))
|
logger.info("ev_object.id == transaction.pay_key: {0}".format(ev_object.id))
|
||||||
else:
|
else:
|
||||||
logger.warning("ev_object.id {0} <> transaction.pay_key {1}".format(ev_object.id, transaction.pay_key))
|
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:
|
except Exception, e:
|
||||||
logger.warning(e)
|
logger.warning(e)
|
||||||
elif action == 'refunded':
|
elif action == 'refunded':
|
||||||
|
|
|
@ -233,7 +233,7 @@ GOODREADS_API_KEY = ""
|
||||||
GOODREADS_API_SECRET = ""
|
GOODREADS_API_SECRET = ""
|
||||||
|
|
||||||
# unglue.it parameters
|
# 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_LONGEST_DEADLINE = '180' # number of days allowed for a campaign
|
||||||
UNGLUEIT_RECOMMENDED_USERNAME = 'unglueit'
|
UNGLUEIT_RECOMMENDED_USERNAME = 'unglueit'
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,11 @@ $j(document).ready(function() {
|
||||||
var windowWidth = $j(document).width();
|
var windowWidth = $j(document).width();
|
||||||
var marginWidth = (windowWidth - hijaxWidth)/2;
|
var marginWidth = (windowWidth - hijaxWidth)/2;
|
||||||
$j('#about_expandable').css({'margin-left': marginWidth});
|
$j('#about_expandable').css({'margin-left': marginWidth});
|
||||||
|
|
||||||
|
// position div vertically relative to top of viewport, to ensure visibility
|
||||||
|
// regardless of where on the page the user clicked to activate it
|
||||||
|
var marginTop = window.pageYOffset;
|
||||||
|
$j('#about_expandable').css({'margin-top': marginTop});
|
||||||
});
|
});
|
||||||
|
|
||||||
if ($j(this).attr("href").substr(-9,8) == "download") {
|
if ($j(this).attr("href").substr(-9,8) == "download") {
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
var $j = jQuery.noConflict();
|
||||||
|
$j().ready(function() {
|
||||||
|
$j(".loader-gif").click(function(event) {
|
||||||
|
$j(this).addClass("show-loading");
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue