regluit/frontend/views/__init__.py

3107 lines
128 KiB
Python
Raw Normal View History

2013-03-26 17:54:03 +00:00
'''
2013-06-03 16:31:39 +00:00
external library imports
2013-03-26 17:54:03 +00:00
'''
import re
import sys
import json
import logging
import urllib
2013-03-26 17:54:03 +00:00
import requests
from datetime import timedelta, date
2013-03-26 17:54:03 +00:00
from decimal import Decimal as D
2016-08-15 19:22:32 +00:00
from itertools import chain
2013-03-26 17:54:03 +00:00
from notification import models as notification
from random import randint
2013-03-26 17:54:03 +00:00
from tastypie.models import ApiKey
2016-08-15 19:22:32 +00:00
#django imports
from django.apps import apps
from django.conf import settings
2012-01-02 14:39:11 +00:00
from django.contrib import messages
2012-02-23 20:40:45 +00:00
from django.contrib.auth.decorators import login_required
2013-06-03 16:31:39 +00:00
from django.contrib.auth.models import User
2016-08-15 19:22:32 +00:00
from django.contrib.auth.views import redirect_to_login
from django_comments.models import Comment
from django.contrib.sites.models import Site
2013-03-26 17:54:03 +00:00
from django.core import signing
2016-08-15 19:22:32 +00:00
from django.core.exceptions import ValidationError
from django.core.files.storage import default_storage
from django.core.mail import EmailMessage
from django.core.urlresolvers import reverse, reverse_lazy
2013-06-10 15:57:59 +00:00
from django.core.validators import validate_email
from django.db.models import Q, Count, Sum
from django.forms import Select
2017-11-13 20:30:00 +00:00
from django.forms.models import inlineformset_factory
2013-06-03 16:31:39 +00:00
from django.http import (
HttpResponseRedirect,
Http404,
HttpResponse,
HttpResponseNotFound
)
2016-08-15 19:22:32 +00:00
from django.shortcuts import render, get_object_or_404
from django.template import TemplateDoesNotExist
from django.template.loader import render_to_string
2013-03-26 17:54:03 +00:00
from django.utils.http import urlencode
from django.utils.translation import ugettext_lazy as _
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST
2017-11-13 20:30:00 +00:00
from django.views.generic.edit import FormView
from django.views.generic.list import ListView
from django.views.generic.base import (
TemplateView,
)
2013-03-26 17:54:03 +00:00
2016-08-15 19:22:32 +00:00
#regluit imports
2013-06-03 16:31:39 +00:00
from regluit.core import (
tasks,
models,
bookloader,
librarything,
userlists,
)
import regluit.core.cc as cc
2013-04-16 20:46:25 +00:00
from regluit.core.bookloader import merge_works, detach_edition
2013-03-26 17:54:03 +00:00
from regluit.core.goodreads import GoodreadsClient
from regluit.core.isbn import ISBN
2013-03-26 17:54:03 +00:00
from regluit.core.search import gluejar_search
from regluit.core.signals import supporter_message
2016-08-15 19:22:32 +00:00
from regluit.core.tasks import send_mail_task, watermark_acq
from regluit.core.parameters import *
2014-12-05 23:36:45 +00:00
from regluit.core.facets import get_facet_object, get_order_by
2013-03-26 17:54:03 +00:00
2013-05-31 19:19:58 +00:00
from regluit.frontend.forms import (
ProfileForm,
CampaignPledgeForm,
2013-08-16 19:49:44 +00:00
CampaignPurchaseForm,
2014-02-20 03:18:23 +00:00
CampaignThanksForm,
2013-05-31 19:19:58 +00:00
GoodreadsShelfLoadingForm,
RightsHolderForm,
UserClaimForm,
LibraryThingForm,
CampaignAdminForm,
EmailShareForm,
FeedbackForm,
EbookForm,
EbookFileForm,
2013-05-31 19:19:58 +00:00
PledgeCancelForm,
getTransferCreditForm,
CCForm,
2014-02-20 03:18:23 +00:00
AnonCCForm,
AccountCCForm,
2013-05-31 19:19:58 +00:00
CloneCampaignForm,
PlainCCForm,
WorkForm,
OtherWorkForm,
MsgForm,
PressForm,
KindleEmailForm,
2014-10-27 23:11:44 +00:00
LibModeForm,
2014-12-18 16:41:06 +00:00
DateCalculatorForm,
2014-12-18 18:37:28 +00:00
RegiftForm,
2015-03-12 15:58:49 +00:00
SubjectSelectForm,
MapSubjectForm,
2016-05-26 16:19:33 +00:00
SurveyForm,
2017-02-13 18:33:26 +00:00
DonationForm,
2013-05-31 19:19:58 +00:00
)
2013-03-26 17:54:03 +00:00
from regluit.payment import baseprocessor, stripelib
from regluit.payment.credit import credit_transaction
from regluit.payment.manager import PaymentManager
2016-08-15 19:22:32 +00:00
from regluit.payment.models import Transaction, CreditLog
2013-05-31 19:19:58 +00:00
from regluit.payment.parameters import (
TRANSACTION_STATUS_ACTIVE,
TRANSACTION_STATUS_COMPLETE,
TRANSACTION_STATUS_CANCELED,
TRANSACTION_STATUS_INCOMPLETE,
TRANSACTION_STATUS_NONE,
TRANSACTION_STATUS_MODIFIED,
PAYMENT_TYPE_AUTHORIZATION,
2017-02-13 18:33:26 +00:00
PAYMENT_HOST_NONE,
COMPANY_TITLE
2013-05-31 19:19:58 +00:00
)
2013-03-26 17:54:03 +00:00
from regluit.utils.localdatetime import now, date_today
from regluit.libraryauth.forms import UserNamePass
from regluit.libraryauth.views import Authenticator, superlogin, login_user
from regluit.libraryauth.models import Library
2014-10-27 15:55:46 +00:00
from regluit.marc.views import qs_marc_records
2017-06-20 15:08:14 +00:00
from questionnaire.models import Landing, Questionnaire
from questionnaire.views import export_summary as answer_summary, export_csv as export_answers
from .bibedit import edit_edition, user_can_edit_work, safe_get_work, get_edition
2017-11-28 02:45:45 +00:00
from .rh_views import campaign_results, claim, manage_campaign, rh_admin, RHAgree, rh_tools
2017-07-25 11:29:23 +00:00
logger = logging.getLogger(__name__)
def static_redirect_view(request, file_name, dir=""):
return HttpResponseRedirect('/static/'+dir+"/"+file_name)
2013-12-14 18:24:29 +00:00
def slideshow():
max = 8
ending = models.Campaign.objects.filter(status='ACTIVE').order_by('deadline')
count = ending.count()
j = 0
2016-08-15 19:22:32 +00:00
worklist = []
if max > count:
# add all the works with active campaigns
for campaign in ending:
worklist.append(campaign.work)
2012-09-11 03:47:06 +00:00
2013-12-14 18:24:29 +00:00
# then fill out the rest of the list with slide works
remainder = max - count
2013-12-14 18:24:29 +00:00
remainder_works = models.Work.objects.filter(campaigns__status="SUCCESSFUL").order_by('-campaigns__deadline')[:remainder]
worklist.extend(remainder_works)
else:
2016-08-15 19:22:32 +00:00
# if the active campaign list has more works than we can fit
# in our slideshow, it's the only source we need to draw from
while j < max:
worklist.append(ending[j].work)
2016-08-15 19:22:32 +00:00
j += 1
return (worklist[:4], worklist[4:8])
def process_kindle_email(request):
"""
check for kindle_email in session in case this is a redirect after
download + login/account creation; add kindle email to profile
"""
user = request.user
if user.is_authenticated() and request.session.has_key('kindle_email'):
user.profile.kindle_email = request.session['kindle_email']
user.profile.save()
request.session.pop('kindle_email')
def next(request):
2012-03-23 15:15:37 +00:00
if request.COOKIES.has_key('next'):
response = HttpResponseRedirect(urllib.unquote(urllib.unquote(request.COOKIES['next'])))
2012-03-23 15:15:37 +00:00
response.delete_cookie('next')
return response
else:
return HttpResponseRedirect('/')
2016-08-15 19:22:32 +00:00
def cover_width(work):
if work.percent_of_goal() < 100:
cover_width = 100 - work.percent_of_goal()
else:
cover_width = 0
2016-08-15 19:22:32 +00:00
return cover_width
def home(request, landing=False):
faves = None
if request.user.is_authenticated() :
2016-08-15 19:22:32 +00:00
next = request.GET.get('next', False)
2015-07-09 20:32:52 +00:00
if next:
# should happen only for new users
return HttpResponseRedirect(next)
else:
wishes = request.user.wishlist.wishes_set.all().order_by('-created')[:4]
faves = [wish.work for wish in wishes]
"""
use campaigns instead of works so that we can order by amount left,
drive interest toward most-nearly-successful
"""
2014-07-03 19:43:49 +00:00
try:
featured = models.Work.objects.filter(featured__isnull=False).distinct().order_by('-featured')[0]
except:
#shouldn't occur except in tests
featured = models.Work.objects.all()[0]
2016-08-15 19:22:32 +00:00
top_pledge = models.Campaign.objects.filter(status="ACTIVE", type=REWARDS).order_by('left')[:4]
top_b2u = models.Campaign.objects.filter(status="ACTIVE", type=BUY2UNGLUE).order_by('-work__num_wishes')[:4]
2016-08-15 19:22:32 +00:00
top_t4u = models.Campaign.objects.exclude(id = featured.id).filter(status="ACTIVE", type=THANKS).order_by('-work__num_wishes')[:4]
most_wished = models.Work.objects.order_by('-num_wishes')[:4]
2016-08-15 19:22:32 +00:00
unglued_books = models.Work.objects.filter(campaigns__status="SUCCESSFUL").order_by('-campaigns__deadline')[:4]
2016-08-15 19:22:32 +00:00
2014-07-03 19:43:49 +00:00
cc_books = models.Work.objects.exclude(id = featured.id).filter(
2014-07-01 17:07:07 +00:00
featured__isnull=False,
).distinct().order_by('-featured')[:4]
2013-03-26 17:54:03 +00:00
"""
get various recent types of site activity
"""
latest_comments = Comment.objects.order_by(
'-submit_date'
)[:10]
latest_pledges = Transaction.objects.filter(
2017-02-13 18:33:26 +00:00
anonymous=False, campaign__isnull=False
).exclude(
type=0 #incomplete
2013-03-28 15:38:40 +00:00
).only(
'date_created', 'user', 'campaign'
2013-03-26 17:54:03 +00:00
).order_by(
'-date_created'
)[:10]
latest_wishes = models.Wishes.objects.filter(source='user').order_by(
2013-03-26 17:54:03 +00:00
'-created'
)[:10]
2013-03-26 17:54:03 +00:00
"""
for each event, we'll be passing its object and type to the template
2013-03-26 17:54:03 +00:00
(and preserving its date for sorting purposes)
"""
latest_comments_tuple = map(
2013-03-28 15:38:40 +00:00
lambda x: (x.submit_date, x, 'comment'),
2013-03-26 17:54:03 +00:00
latest_comments
)
2016-08-15 19:22:32 +00:00
2013-03-26 17:54:03 +00:00
latest_pledges_tuple = map(
2013-03-28 15:38:40 +00:00
lambda x: (x.date_created, x, 'pledge'),
2013-03-26 17:54:03 +00:00
latest_pledges
)
2016-08-15 19:22:32 +00:00
2013-03-26 17:54:03 +00:00
latest_wishes_tuple = map(
2013-03-28 15:38:40 +00:00
lambda x: (x.created, x, 'wish'),
2013-03-26 17:54:03 +00:00
latest_wishes
)
2016-08-15 19:22:32 +00:00
"""
merge latest actions into a single list, sorted by date, to loop through in template
"""
2013-03-26 17:54:03 +00:00
latest_actions = sorted(
2016-08-15 19:22:32 +00:00
chain(latest_comments_tuple, latest_pledges_tuple, latest_wishes_tuple),
2013-03-26 17:54:03 +00:00
key=lambda instance: instance[0],
reverse=True
)
2016-08-15 19:22:32 +00:00
if request.user.is_authenticated():
2013-03-26 17:54:03 +00:00
events = latest_actions[:12]
else:
2013-03-26 17:54:03 +00:00
events = latest_actions[:6]
2016-08-15 19:22:32 +00:00
return render(
request,
2016-08-15 19:22:32 +00:00
'home.html',
{
2016-08-15 19:22:32 +00:00
'events': events,
'top_pledge': top_pledge,
'top_b2u': top_b2u,
'top_t4u': top_t4u,
'unglued_books': unglued_books,
'cc_books': cc_books,
'most_wished': most_wished,
2014-07-03 19:43:49 +00:00
'featured': featured,
'faves': faves,
}
)
def stub(request):
path = request.path[6:] # get rid of /stub/
2016-08-15 19:22:32 +00:00
return render(request, 'stub.html', {'path': path})
def acks(request, work):
2016-08-15 19:22:32 +00:00
return render(request, 'front_matter.html', {'campaign': work.last_campaign()})
2011-11-06 23:54:48 +00:00
def work(request, work_id, action='display'):
work = safe_get_work(work_id)
2016-08-15 19:22:32 +00:00
alert = ''
formset = None
if action == "acks":
2016-08-15 19:22:32 +00:00
return acks(request, work)
elif action == "editions":
2016-08-15 19:22:32 +00:00
EditionFormSet = inlineformset_factory(models.Work, models.Edition, fields=(), extra=0)
2014-11-28 16:12:29 +00:00
if request.method == "POST" and (request.user.is_staff or (work.last_campaign() and request.user in work.last_campaign().managers.all())):
formset = EditionFormSet(data=request.POST, instance=work)
if formset.is_valid():
for form in formset.deleted_forms:
detach_edition(form.instance)
alert = 'editions have been split'
if request.POST.has_key('select_edition'):
2016-08-15 19:22:32 +00:00
selected_id = request.POST['select_edition']
try:
2016-08-15 19:22:32 +00:00
work.selected_edition = work.editions.get(id=selected_id)
work.title = work.selected_edition.title
work.save()
alert = alert + 'edition selected'
except models.Edition.DoesNotExist:
pass
formset = EditionFormSet(instance=work)
# process waiting add request
if not request.user.is_anonymous() and request.session.has_key("add_wishlist"):
add_url = request.session["add_wishlist"]
if add_url == request.path:
request.user.wishlist.add_work(work, "login", notify=True)
request.session.pop("add_wishlist")
2016-08-15 19:22:32 +00:00
process_kindle_email(request)
2016-08-15 19:22:32 +00:00
2012-02-28 22:28:33 +00:00
if request.method == 'POST' and not request.user.is_anonymous():
activetab = '4'
elif action == 'editions':
activetab = '4'
2012-02-28 22:28:33 +00:00
else:
try:
activetab = request.GET['tab']
if activetab not in ['1', '2', '3', '4']:
2016-08-15 19:22:32 +00:00
activetab = '1'
2012-02-28 22:28:33 +00:00
except:
2016-08-15 19:22:32 +00:00
activetab = '1'
2011-11-06 22:44:50 +00:00
campaign = work.last_campaign()
editions = work.editions.all().order_by('-publication_date')[:10]
try:
2017-12-14 21:24:26 +00:00
supported = campaign.transactions().filter(user=request.user)
pledged = supported.filter(status="ACTIVE")
except:
2017-12-14 21:24:26 +00:00
supported = None
2012-01-31 15:07:52 +00:00
pledged = None
2016-08-15 19:22:32 +00:00
cover_width_number = 0
2016-08-15 19:22:32 +00:00
2012-05-13 19:33:54 +00:00
if work.last_campaign_status() == 'ACTIVE':
cover_width_number = cover_width(work)
2016-08-15 19:22:32 +00:00
2012-05-14 02:13:45 +00:00
if action == 'preview':
work.last_campaign_status = 'ACTIVE'
2016-08-15 19:22:32 +00:00
if not request.user.is_anonymous():
2017-12-08 16:04:00 +00:00
claimform = UserClaimForm(request.user, initial={'work':work.pk, 'user': request.user.id}, prefix = 'claim')
else:
claimform = None
2016-08-15 19:22:32 +00:00
2011-11-06 22:44:50 +00:00
if campaign:
# pull up premiums explicitly tied to the campaign
# mandatory premiums are only displayed in pledge process
premiums = campaign.custom_premiums()
else:
premiums = None
2016-08-15 19:22:32 +00:00
wishers = work.num_wishes
base_url = request.build_absolute_uri("/")[:-1]
2016-08-15 19:22:32 +00:00
2012-04-03 13:56:41 +00:00
active_claims = work.claim.all().filter(status='active')
if active_claims.count() == 1:
claimstatus = 'one_active'
rights_holder_name = active_claims[0].rights_holder.rights_holder_name
2012-04-03 13:56:41 +00:00
else:
rights_holder_name = None
pending_claims = work.claim.all().filter(status='pending')
pending_claims_count = pending_claims.count()
if pending_claims_count > 1:
2016-08-15 19:22:32 +00:00
claimstatus = 'disputed'
elif pending_claims_count == 1:
2016-08-15 19:22:32 +00:00
claimstatus = 'one_pending'
rights_holder_name = pending_claims[0].rights_holder.rights_holder_name
else:
2016-08-15 19:22:32 +00:00
claimstatus = 'open'
2012-03-23 15:09:51 +00:00
return render(request, 'work.html', {
2016-08-15 19:22:32 +00:00
'work': work,
'user_can_edit_work': user_can_edit_work(request.user, work),
2016-08-15 19:22:32 +00:00
'premiums': premiums,
'ungluers': userlists.supporting_users(work, 5),
2012-03-23 15:09:51 +00:00
'claimform': claimform,
'wishers': wishers,
'base_url': base_url,
'editions': editions,
2012-04-03 13:56:41 +00:00
'pledged': pledged,
2017-12-14 21:24:26 +00:00
'supported': supported,
2012-03-23 15:09:51 +00:00
'activetab': activetab,
2012-04-03 13:56:41 +00:00
'alert': alert,
'claimstatus': claimstatus,
'rights_holder_name': rights_holder_name,
'cover_width': cover_width_number,
'action': action,
'formset': formset,
2015-01-14 20:07:54 +00:00
'kwform': SubjectSelectForm()
2016-08-15 19:22:32 +00:00
})
def edition_uploads(request, edition_id):
context = {}
if not request.user.is_authenticated() :
return render(request, "admins_only.html")
2017-07-25 12:21:26 +00:00
edition = get_edition(edition_id)
2014-01-15 13:32:55 +00:00
campaign_type = edition.work.last_campaign().type
if not user_can_edit_work(request.user, edition.work):
return render(request, "admins_only.html")
if request.method == 'POST' :
2014-01-15 13:32:55 +00:00
form = EbookFileForm(data=request.POST, files=request.FILES, campaign_type=campaign_type)
if form.is_valid() :
2014-01-15 13:32:55 +00:00
logger.info("EbookFileForm is_valid")
form.save()
edition.work.last_campaign().save()
2016-08-15 19:22:32 +00:00
context['uploaded'] = True
2014-01-15 13:32:55 +00:00
if campaign_type == BUY2UNGLUE:
if edition.work.last_campaign().status == 'SUCCESSFUL':
try:
edition.work.last_campaign().watermark_success()
except Exception as e:
2016-08-15 19:22:32 +00:00
context['upload_error'] = e
form.instance.delete()
else:
# campaign mangager gets a copy
2016-08-15 19:22:32 +00:00
test_acq = models.Acq.objects.create(user=request.user, work=edition.work, license= TESTING)
try:
test_acq.get_watermarked()
2016-08-15 19:22:32 +00:00
context['watermarked'] = test_acq.watermarked
except Exception as e:
2016-08-15 19:22:32 +00:00
context['upload_error'] = e
form.instance.delete()
2014-01-15 13:32:55 +00:00
if campaign_type == THANKS:
e = form.instance.check_file()
if e != None:
logger.error(e)
2016-08-15 19:22:32 +00:00
context['upload_error'] = e
2014-01-15 13:32:55 +00:00
form.instance.delete()
else:
tasks.process_ebfs.delay(edition.work.last_campaign())
if form.instance.id:
new_ebook = models.Ebook.objects.create(
edition=edition,
format=form.instance.format,
url=form.instance.file.url,
rights=edition.work.last_campaign().license,
2016-09-23 18:53:54 +00:00
version_label=form.cleaned_data.get('version_label', ''),
active=False,
provider="Unglue.it",
)
form.instance.ebook = new_ebook
2016-09-23 18:53:54 +00:00
form.instance.ebook.set_next_iter()
2017-09-06 22:02:40 +00:00
form.instance.save()
2014-01-15 13:32:55 +00:00
else:
2016-08-15 19:22:32 +00:00
context['upload_error'] = form.errors
form = EbookFileForm(initial={'edition':edition, 'format':'epub'}, campaign_type=campaign_type)
context.update({
2016-08-15 19:22:32 +00:00
'form': form, 'edition': edition,
'ebook_files': models.EbookFile.objects.filter(edition = edition)
})
2016-08-15 19:22:32 +00:00
return render(request, 'edition_uploads.html', context)
@login_required
def manage_ebooks(request, edition_id, by=None):
2015-09-21 20:19:31 +00:00
if edition_id:
try:
edition = models.Edition.objects.get(id = edition_id)
except models.Edition.DoesNotExist:
raise Http404
work = edition.work
else:
raise Http404
2016-08-15 19:22:32 +00:00
# if the work and edition are set, we save the edition and set the work
alert = ''
admin = user_can_edit_work(request.user, work)
if request.method == 'POST' :
ebook_form = EbookForm(data = request.POST, files=request.FILES,)
if ebook_form.is_valid():
if ebook_form.cleaned_data.get('file', None):
new_ebf = models.EbookFile.objects.create(
file=ebook_form.cleaned_data['file'],
format=ebook_form.cleaned_data['format'],
edition=edition,
active=True,
2017-07-25 12:14:05 +00:00
)
ebook_form.instance.url = new_ebf.file.url
ebook_form.instance.provider = "Unglue.it"
ebook_form.instance.save()
new_ebf.ebook = ebook_form.instance
new_ebf.save()
2016-08-15 19:22:32 +00:00
else:
ebook_form.save()
2016-09-23 18:53:54 +00:00
ebook_form.instance.set_next_iter()
edition.work.remove_old_ebooks()
alert = 'Thanks for adding an ebook to unglue.it!'
else:
alert = 'your submitted ebook had errors'
else:
ebook_form = EbookForm(instance=models.Ebook(user=request.user, edition=edition, provider='x'))
2014-03-12 23:32:56 +00:00
try:
show_ebook_form = admin or edition.work.last_campaign().status not in ['ACTIVE','INITIALIZED']
2014-03-12 23:32:56 +00:00
except:
show_ebook_form = True
return render(request, 'manage_ebooks.html', {
'edition': edition, 'admin':admin, 'alert':alert,
'ebook_form': ebook_form, 'show_ebook_form':show_ebook_form,
})
2016-08-15 19:22:32 +00:00
def googlebooks(request, googlebooks_id):
2016-08-15 19:22:32 +00:00
try:
edition = models.Identifier.objects.get(type='goog', value=googlebooks_id).edition
except models.Identifier.DoesNotExist:
2012-01-31 15:08:43 +00:00
try:
edition = bookloader.add_by_googlebooks_id(googlebooks_id)
if edition.new:
# add related editions asynchronously
2012-02-16 18:19:36 +00:00
tasks.populate_edition.delay(edition.isbn_13)
if request.user.is_authenticated():
request.user.profile.works.add(edition.work)
2012-01-31 15:08:43 +00:00
except bookloader.LookupFailure:
logger.warning("failed to load googlebooks_id %s" % googlebooks_id)
return HttpResponseNotFound("failed looking up googlebooks id %s" % googlebooks_id)
if not edition:
return HttpResponseNotFound("invalid googlebooks id")
work_url = reverse('work', kwargs={'work_id': edition.work_id})
# process waiting add request
if not request.user.is_anonymous() and request.session.has_key("add_wishlist"):
add_url = request.session["add_wishlist"]
if add_url == request.path:
request.user.wishlist.add_work(edition.work, "login", notify=True)
request.session.pop("add_wishlist")
return HttpResponseRedirect(work_url)
def subjects(request):
order = request.GET.get('order')
subjects = models.Subject.objects.all()
subjects = subjects
if request.GET.get('subset') == 'free':
subjects = models.Subject.objects.filter(works__is_free = True).annotate(Count('works__is_free'))
if request.GET.get('order') == 'count':
subjects = subjects.order_by('-works__is_free__count')
else:
subjects = subjects.order_by('name')
else:
subjects = models.Subject.objects.all().annotate(Count('works'))
if request.GET.get('order') == 'count':
subjects = subjects.order_by('-works__count')
else:
subjects = subjects.order_by('name')
return render(request, 'subjects.html', {'subjects': subjects})
2011-11-07 21:01:08 +00:00
2015-03-12 15:58:49 +00:00
class MapSubjectView(FormView):
"""
Allows a staffer to add given subject to all works with given the onto_subject keyword.
e.g., subject = "Language" onto_subject="English language"
"""
2016-08-15 19:22:32 +00:00
template_name = "map_subject.html"
2015-03-12 15:58:49 +00:00
form_class = MapSubjectForm
2016-08-15 19:22:32 +00:00
2015-03-12 15:58:49 +00:00
def dispatch(self, request, *args, **kwargs):
if not request.user.is_staff:
return render(request, "admins_only.html")
else:
return super(MapSubjectView, self).dispatch(request, *args, **kwargs)
2016-08-15 19:22:32 +00:00
2015-03-12 15:58:49 +00:00
def form_valid(self, form):
2016-08-15 19:22:32 +00:00
context = self.get_context_data()
context['subject'] = form.cleaned_data['subject']
context['onto_subject'] = form.cleaned_data['onto_subject']
2015-03-12 15:58:49 +00:00
if self.request.POST.has_key('confirm_map_subject'):
initial_count = context['onto_subject'].works.all().count()
initial_free_count = context['onto_subject'].works.filter(is_free=True).count()
context['onto_subject'].works.add(*list(context['subject'].works.all()))
2016-08-15 19:22:32 +00:00
context['map_complete'] = True
2015-03-12 15:58:49 +00:00
context['form'] = MapSubjectForm(initial=form.cleaned_data)
context['added'] = context['onto_subject'].works.all().count() - initial_count
context['added_free'] = context['onto_subject'].works.filter(is_free=True).count() - initial_free_count
else:
2016-08-15 19:22:32 +00:00
context['form'] = MapSubjectForm(initial=form.cleaned_data)
2015-03-12 15:58:49 +00:00
return render(self.request, self.template_name, context)
class FilterableListView(ListView):
2014-10-27 15:55:46 +00:00
send_marc = False
def get_queryset(self):
if self.request.GET.has_key('pub_lang'):
if self.model is models.Campaign:
return self.get_queryset_all().filter(work__language=self.request.GET['pub_lang'])
else:
return self.get_queryset_all().filter(language=self.request.GET['pub_lang'])
else:
return self.get_queryset_all()
2016-08-15 19:22:32 +00:00
def get_context_data(self, **kwargs):
context = super(FilterableListView, self).get_context_data(**kwargs)
if self.request.GET.has_key('pub_lang'):
2016-08-15 19:22:32 +00:00
context['pub_lang'] = self.request.GET['pub_lang']
else:
2016-08-15 19:22:32 +00:00
context['pub_lang'] = ''
context['show_langs'] = True
context['WISHED_LANGS'] = settings.WISHED_LANGS
return context
2016-08-15 19:22:32 +00:00
2014-10-27 15:55:46 +00:00
def render_to_response(self, context, **response_kwargs):
if self.send_marc:
return qs_marc_records(self.request, qs=self.object_list)
else:
2016-08-15 19:22:32 +00:00
return super(FilterableListView, self).render_to_response(context, **response_kwargs)
2014-11-18 18:33:17 +00:00
2016-08-15 19:22:32 +00:00
recommended_user = User.objects.filter(username=settings.UNGLUEIT_RECOMMENDED_USERNAME)
class WorkListView(FilterableListView):
template_name = "work_list.html"
context_object_name = "work_list"
2016-08-15 19:22:32 +00:00
max_works = 100000
def get_queryset_all(self):
2014-11-18 18:33:17 +00:00
facet = self.kwargs.get('facet', None)
if (facet == 'popular'):
return models.Work.objects.exclude(num_wishes=0).order_by('-num_wishes', 'id')
elif (facet == 'recommended'):
self.template_name = "recommended.html"
return models.Work.objects.filter(wishlists__user=recommended_user).order_by('-num_wishes')
elif (facet == 'new'):
return models.Work.objects.exclude(num_wishes=0).order_by('-created', '-num_wishes' ,'id')
else:
return models.Work.objects.all().order_by('-created', 'id')
2016-08-15 19:22:32 +00:00
def get_context_data(self, **kwargs):
2016-08-15 19:22:32 +00:00
context = super(WorkListView, self).get_context_data(**kwargs)
qs = self.get_queryset()
context['ungluers'] = userlists.work_list_users(qs, 5)
context['facet'] = self.kwargs.get('facet','')
works_unglued = qs.filter(is_free = True).distinct() | qs.filter(campaigns__status='SUCCESSFUL').distinct()
context['works_unglued'] = works_unglued[:self.max_works]
context['works_active'] = qs.filter(campaigns__status='ACTIVE').distinct()[:self.max_works]
context['works_wished'] = qs.filter(is_free=False).exclude(campaigns__status='ACTIVE').exclude(campaigns__status='SUCCESSFUL').distinct()[:self.max_works]
counts = {}
counts['unglued'] = context['works_unglued'].count()
counts['unglueing'] = context['works_active'].count()
counts['wished'] = context['works_wished'].count()
context['counts'] = counts
if counts['unglueing']:
context['activetab'] = "#2"
elif counts['unglued']:
context['activetab'] = "#1"
else:
context['activetab'] = "#3"
return context
2012-01-15 21:48:26 +00:00
2014-11-18 18:33:17 +00:00
class FacetedView(FilterableListView):
template_name = "faceted_list.html"
def get_queryset_all(self):
2014-11-21 02:11:15 +00:00
if not hasattr(self,'vertex'):
facet_path = self.kwargs.get('path', '')
2014-12-05 23:36:45 +00:00
self.vertex = get_facet_object(facet_path)
2016-08-15 19:22:32 +00:00
2014-11-18 21:54:19 +00:00
order_by = self.request.GET.get('order_by', 'newest')
2015-01-26 19:46:37 +00:00
#special cases
if order_by == 'subjects':
return self.vertex.get_query_set().annotate(kws=Count('subjects')).order_by('kws')
2014-11-18 21:54:19 +00:00
return self.vertex.get_query_set().distinct().order_by(*get_order_by(order_by))
2014-11-18 18:33:17 +00:00
def get_context_data(self, **kwargs):
context = super(FacetedView, self).get_context_data(**kwargs)
facet = self.kwargs.get('facet','all')
2016-08-15 19:22:32 +00:00
qs = self.get_queryset()
if self.request.GET.has_key('setkw') and self.request.user.is_staff:
setkw = self.request.GET['setkw']
try:
context['setkw'] = models.Subject.objects.get(name=setkw)
except models.Subject.DoesNotExist:
pass
2014-11-18 18:33:17 +00:00
context['activetab'] = "#1"
context['tab_override'] = 'tabs-1'
2014-12-02 21:09:30 +00:00
context['path'] = self.vertex.get_facet_path().replace('//','/').strip('/')
2014-11-21 02:11:15 +00:00
context['vertex'] = self.vertex
2014-11-19 02:54:47 +00:00
context['order_by'] = self.request.GET.get('order_by', 'newest')
2015-04-11 12:25:41 +00:00
context['view_as'] = self.request.GET.get('view_as', None)
2014-11-18 18:33:17 +00:00
return context
2016-08-15 19:22:32 +00:00
class ByPubView(WorkListView):
template_name = "bypub_list.html"
context_object_name = "work_list"
max_works = 100000
publisher_name = None
publisher = None
2016-08-15 19:22:32 +00:00
def get_publisher_name(self):
try:
self.publisher_name = get_object_or_404(models.PublisherName, id=self.kwargs['pubname'])
except ValueError:
raise Http404
self.set_publisher()
2016-08-15 19:22:32 +00:00
def set_publisher(self):
if self.publisher_name.key_publisher.count():
self.publisher = self.publisher_name.key_publisher.all()[0]
elif self.publisher_name.publisher:
self.publisher = self.publisher_name.publisher
self.publisher_name = self.publisher.name
2016-08-15 19:22:32 +00:00
def get_queryset_all(self):
facet = self.kwargs.get('facet','')
self.get_publisher_name()
objects = models.Work.objects.filter(editions__publisher_name__id=self.publisher_name.id).distinct()
if (facet == 'popular'):
return objects.order_by('-num_wishes', 'id')
elif (facet == 'pubdate'):
return objects.order_by('-editions__publication_date') # turns out this messes up distinct, and MySQL doesn't support DISTINCT ON
elif (facet == 'new'):
return objects.filter(num_wishes__gt=0).order_by('-created', '-num_wishes' ,'id')
else:
return objects.order_by('title', 'id')
def get_context_data(self, **kwargs):
2016-08-15 19:22:32 +00:00
context = super(ByPubView, self).get_context_data(**kwargs)
context['pubname'] = self.publisher_name
context['publisher'] = self.publisher
context['facet'] = self.kwargs.get('facet','all')
2014-10-27 15:55:46 +00:00
2016-08-15 19:22:32 +00:00
return context
class ByPubListView(ByPubView):
def get_publisher_name(self):
self.publisher_name = get_object_or_404(models.PublisherName, name=self.kwargs['pubname'])
self.set_publisher()
class UngluedListView(FilterableListView):
2012-01-15 21:48:26 +00:00
template_name = "unglued_list.html"
context_object_name = "work_list"
2016-08-15 19:22:32 +00:00
def get_queryset_all(self):
2012-01-15 21:48:26 +00:00
facet = self.kwargs['facet']
if (facet == 'popular'):
2014-12-14 15:00:10 +00:00
return models.Work.objects.filter(is_free = True).distinct().order_by('-num_wishes')
elif (facet == 'cc' or facet == 'creativecommons'):
# assumes all ebooks have a PD or CC license. compare rights_badge property
2013-02-04 18:32:21 +00:00
return models.Work.objects.filter(
2014-12-14 15:00:10 +00:00
is_free = True,
editions__ebooks__rights__in=cc.LICENSE_LIST
).exclude(campaigns__status="SUCCESSFUL").distinct().order_by('-num_wishes')
elif (facet == 'pd' or facet == 'publicdomain'):
2013-02-04 18:32:21 +00:00
return models.Work.objects.filter(
2014-12-14 15:00:10 +00:00
is_free = True,
2013-02-04 18:32:21 +00:00
editions__ebooks__rights__in=['PD-US', 'CC0', '']
).distinct().order_by('-num_wishes')
2013-02-06 23:14:56 +00:00
else :
#(facet == '' or facet == 'unglued' or facet is other)
return models.Work.objects.filter(campaigns__status="SUCCESSFUL").distinct().order_by('-campaigns__deadline')
2012-01-15 21:48:26 +00:00
def get_context_data(self, **kwargs):
context = super(UngluedListView, self).get_context_data(**kwargs)
2016-08-15 19:22:32 +00:00
qs = self.get_queryset()
context['ungluers'] = userlists.work_list_users(qs, 5)
facet = self.kwargs['facet']
context['facet'] = facet
if facet == 'cc' or facet == 'creativecommons':
context['activetab'] = "#2"
elif facet == 'pd' or facet == 'publicdomain':
context['activetab'] = "#3"
else:
context['activetab'] = "#1"
return context
FACET_LABELS = {
'b2u': "Buy to Unglue",
't4u': "Thanks for Ungluing",
'pledge': "Pledge to Unglue",
'unglued': "Successful",
}
class CampaignListView(FilterableListView):
template_name = "campaign_list.html"
context_object_name = "campaign_list"
model = models.Campaign
2016-08-15 19:22:32 +00:00
def get_queryset_all(self):
facet = self.kwargs['facet']
if (facet == 'newest'):
return models.Campaign.objects.filter(status='ACTIVE').order_by('-activated')
elif (facet == 'pledged'):
return models.Campaign.objects.filter(status='ACTIVE').annotate(total_pledge=Sum('transaction__amount')).order_by('-total_pledge')
elif (facet == 'pledges'):
return models.Campaign.objects.filter(status='ACTIVE').annotate(pledges=Count('transaction')).order_by('-pledges')
elif (facet == 'almost'):
2016-08-15 19:22:32 +00:00
return models.Campaign.objects.filter(status='ACTIVE').all() # STUB: will need to make db changes to make this work
elif (facet == 'ending'):
return models.Campaign.objects.filter(status='ACTIVE').order_by('deadline')
elif (facet == 'soon'):
return models.Campaign.objects.filter(status='INITIALIZED').order_by('-work__num_wishes')
elif (facet == 'b2u'):
return models.Campaign.objects.filter(status='ACTIVE', type=BUY2UNGLUE).annotate(pledges=Count('transaction')).order_by('-pledges')
elif (facet == 't4u'):
return models.Campaign.objects.filter(status='ACTIVE', type=THANKS).annotate(pledges=Count('transaction')).order_by('-pledges')
elif (facet == 'pledge'):
return models.Campaign.objects.filter(status='ACTIVE', type=REWARDS).annotate(pledges=Count('transaction')).order_by('-pledges')
elif (facet == 'unglued'):
return models.Campaign.objects.filter(status='SUCCESSFUL').annotate(pledges=Count('transaction')).order_by('-pledges')
else:
return models.Campaign.objects.all()
def get_context_data(self, **kwargs):
2016-08-15 19:22:32 +00:00
context = super(CampaignListView, self).get_context_data(**kwargs)
qs = self.get_queryset()
context['ungluers'] = userlists.campaign_list_users(qs, 5)
facet = self.kwargs['facet']
context['facet'] = facet
context['facet_label'] = FACET_LABELS.get(facet, 'Active')
return context
class MergeView(FormView):
2016-08-15 19:22:32 +00:00
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)
2016-08-15 19:22:32 +00:00
def get_context_data(self, **kwargs):
context = super(MergeView, self).get_context_data(**kwargs)
2016-08-15 19:22:32 +00:00
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
2016-08-15 19:22:32 +00:00
def get_form_kwargs(self):
self.work = safe_get_work(self.kwargs["work_id"])
2016-08-15 19:22:32 +00:00
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):
2016-08-15 19:22:32 +00:00
other_work = form.cleaned_data['other_work']
context = self.get_context_data()
if self.request.POST.has_key('confirm_merge_works'):
2016-08-15 19:22:32 +00:00
context['old_work_id'] = other_work.id
self.work = merge_works(self.work, other_work, self.request.user)
2016-08-15 19:22:32 +00:00
context['merge_complete'] = True
else:
2016-08-15 19:22:32 +00:00
context['form'] = WorkForm(initial={'other_work':other_work})
context['other_work'] = other_work
return render(self.request, self.template_name, context)
class GiftView(TemplateView):
template_name = "gift.html"
2016-08-15 19:22:32 +00:00
def get(self, request, *args, **kwargs):
context = self.get_context_data()
2016-08-15 19:22:32 +00:00
context['transfer_form'] = getTransferCreditForm(self.request.user.credit.available)
return self.render_to_response(context)
2016-08-15 19:22:32 +00:00
def post(self, request, *args, **kwargs):
context = self.get_context_data()
2016-08-15 19:22:32 +00:00
transfer_form = getTransferCreditForm(self.request.user.credit.available, data=self.request.POST)
if transfer_form.is_valid():
if self.request.user.credit.transfer_to(transfer_form.cleaned_data['recipient'], transfer_form.cleaned_data['amount']):
#successful transfer
context['transfer_message'] = 'Your transfer has been successfully executed.'
2016-08-15 19:22:32 +00:00
context['recipient'] = transfer_form.cleaned_data['recipient']
context['transfer_amount'] = transfer_form.cleaned_data['amount']
2016-08-15 19:22:32 +00:00
context['transfer_form'] = getTransferCreditForm(self.request.user.credit.available)
else:
#unsuccessful transfer
context['transfer_message'] = 'Your transfer was not successful.'
2016-08-15 19:22:32 +00:00
context['transfer_form'] = transfer_form
else:
#not valid
2016-08-15 19:22:32 +00:00
context['transfer_form'] = transfer_form
return self.render_to_response(context)
2016-08-15 19:22:32 +00:00
def get_context_data(self, *args, **kwargs):
context = {'user' : self.request.user}
return context
2016-08-15 19:22:32 +00:00
class PledgeView(FormView):
2013-08-16 19:49:44 +00:00
action = "pledge"
2016-08-15 19:22:32 +00:00
template_name = "pledge.html"
form_class = CampaignPledgeForm
transaction = None
campaign = None
work = None
premiums = None
data = {}
2016-08-15 19:22:32 +00:00
def get_preapproval_amount(self):
2016-07-25 15:32:04 +00:00
preapproval_amount = self.request.GET.get('preapproval_amount', self.request.POST.get('preapproval_amount', None))
if preapproval_amount:
return preapproval_amount
2016-07-25 15:32:04 +00:00
premium_id = self.request.GET.get('premium_id', self.request.POST.get('premium_id', None))
if premium_id != None:
try:
preapproval_amount = D(models.Premium.objects.get(id=premium_id).amount)
except:
preapproval_amount = None
if self.transaction:
2016-08-15 19:22:32 +00:00
if preapproval_amount:
preapproval_amount = preapproval_amount if preapproval_amount > self.transaction.amount else self.transaction.amount
else:
preapproval_amount = self.transaction.amount
return preapproval_amount
2016-08-15 19:22:32 +00:00
def get_form_kwargs(self):
2016-08-15 19:22:32 +00:00
assert self.request.user.is_authenticated()
self.work = safe_get_work(self.kwargs["work_id"])
2016-08-15 19:22:32 +00:00
# if there is no campaign or if campaign is not active, we should raise an error
2012-05-01 13:56:19 +00:00
try:
self.campaign = self.work.last_campaign()
# TODO need to sort the premiums
self.premiums = self.campaign.custom_premiums() | models.Premium.objects.filter(id=150)
# Campaign must be ACTIVE
2016-08-15 19:22:32 +00:00
assert self.campaign.status == 'ACTIVE'
except Exception, e:
# this used to raise an exception, but that seemed pointless. This now has the effect of preventing any pledges.
return {}
2012-05-01 13:56:19 +00:00
transactions = self.campaign.transactions().filter(user=self.request.user, status=TRANSACTION_STATUS_ACTIVE, type=PAYMENT_TYPE_AUTHORIZATION)
2016-07-25 15:32:04 +00:00
premium_id = self.request.GET.get('premium_id', self.request.POST.get('premium_id', 150))
if transactions.count() == 0:
2016-08-15 19:22:32 +00:00
ack_name = self.request.user.profile.ack_name
ack_dedication = ''
anonymous = self.request.user.profile.anon_pref
else:
2016-08-15 19:22:32 +00:00
self.transaction = transactions[0]
if premium_id == 150 and self.transaction.premium is not None:
premium_id = self.transaction.premium.id
if self.transaction.extra :
ack_name = self.transaction.extra.get('ack_name', self.request.user.profile.ack_name)
ack_dedication = self.transaction.extra.get('ack_dedication','')
else:
ack_name = self.request.user.profile.ack_name
ack_dedication = ''
2016-08-15 19:22:32 +00:00
anonymous = self.transaction.anonymous
2016-08-15 19:22:32 +00:00
self.data = {'preapproval_amount':self.get_preapproval_amount(), 'premium_id':premium_id,
'ack_name':ack_name, 'ack_dedication':ack_dedication, 'anonymous':anonymous}
if self.request.method == 'POST':
self.data.update(self.request.POST.dict())
if not self.request.POST.has_key('anonymous'):
del self.data['anonymous']
if not self.request.POST.has_key('ack_name'):
del self.data['ack_name']
if not self.request.POST.has_key('ack_dedication'):
del self.data['ack_dedication']
return {'data':self.data}
else:
return {'initial':self.data}
2016-08-15 19:22:32 +00:00
def get_context_data(self, **kwargs):
"""set up the pledge page"""
2016-08-15 19:22:32 +00:00
context = super(PledgeView, self).get_context_data(**kwargs)
2016-08-15 19:22:32 +00:00
context.update({
'work':self.work,
2016-08-15 19:22:32 +00:00
'campaign':self.campaign,
'premiums':self.premiums,
'premium_id':self.data.get('premium_id', None),
'faqmenu': 'modify' if self.transaction else 'pledge',
'transaction': self.transaction,
'tid': self.transaction.id if self.transaction else None,
'cover_width': cover_width(self.work)
})
2016-08-15 19:22:32 +00:00
return context
2016-08-15 19:22:32 +00:00
def form_valid(self, form):
# right now, if there is a non-zero pledge amount, go with that. otherwise, do the pre_approval
2016-08-15 19:22:32 +00:00
p = PaymentManager()
if self.transaction:
# modifying the transaction...
2016-08-15 19:22:32 +00:00
assert self.transaction.type == PAYMENT_TYPE_AUTHORIZATION and self.transaction.status == TRANSACTION_STATUS_ACTIVE
status, url = p.modify_transaction(self.transaction, form.cleaned_data["preapproval_amount"],
paymentReason="Unglue.it %s for %s"% (self.action, self.campaign.name) ,
pledge_extra = form.trans_extra
)
logger.info("status: {0}, url:{1}".format(status, url))
2016-08-15 19:22:32 +00:00
if status and url is not None:
logger.info("PledgeView (Modify): " + url)
return HttpResponseRedirect(url)
elif status and url is None:
return HttpResponseRedirect("{0}?tid={1}".format(reverse('pledge_modified'), self.transaction.id))
else:
return HttpResponse("No modification made")
else:
2016-08-15 19:22:32 +00:00
t, url = p.process_transaction('USD', form.amount(),
host = PAYMENT_HOST_NONE,
campaign=self.campaign,
user=self.request.user,
2016-08-15 19:22:32 +00:00
paymentReason="Unglue.it Pledge for {0}".format(self.campaign.name),
2017-12-14 21:24:26 +00:00
pledge_extra=form.trans_extra,
donation = form.cleaned_data['donation']
2016-08-15 19:22:32 +00:00
)
if url:
logger.info("PledgeView url: " + url)
return HttpResponseRedirect(url)
else:
logger.error("Attempt to produce transaction id {0} failed".format(t.id))
return HttpResponse("Our attempt to enable your transaction failed. We have logged this error.")
2016-08-15 19:22:32 +00:00
class PurchaseView(PledgeView):
template_name = "purchase.html"
2013-08-16 19:49:44 +00:00
form_class = CampaignPurchaseForm
action = "purchase"
2013-08-27 22:03:35 +00:00
offer_id = None
2013-08-16 19:49:44 +00:00
def get_context_data(self, **kwargs):
context = super(PledgeView, self).get_context_data(**kwargs)
context.update({
'work':self.work,
2016-08-15 19:22:32 +00:00
'campaign':self.campaign,
'faqmenu': 'purchase' ,
2013-08-16 19:49:44 +00:00
'transaction': self.transaction,
'tid': self.transaction.id if self.transaction else None,
2013-08-27 22:03:35 +00:00
'cover_width': cover_width(self.work),
'offer_id':self.offer_id,
'user_license': self.work.get_user_license(self.request.user),
2014-12-15 05:56:08 +00:00
'give': self.give
2013-08-16 19:49:44 +00:00
})
2016-08-15 19:22:32 +00:00
2013-08-16 19:49:44 +00:00
return context
def get_form_kwargs(self):
assert self.request.user.is_authenticated()
self.work = safe_get_work(self.kwargs["work_id"])
2016-08-15 19:22:32 +00:00
# if there is no campaign or if campaign is not active, we should raise an error
try:
self.campaign = self.work.last_campaign()
# Campaign must be ACTIVE
2016-08-15 19:22:32 +00:00
assert self.campaign.status == 'ACTIVE'
except Exception, e:
# this used to raise an exception, but that seemed pointless. This now has the effect of preventing any pledges.
return {}
self.data = {
'preapproval_amount':self.get_preapproval_amount(),
2013-08-27 22:03:35 +00:00
'anonymous':self.request.user.profile.anon_pref,
'offer_id': self.offer_id,
}
if self.request.method == 'POST':
data = self.request.POST.dict()
data.update(self.data)
self.data = data
2014-12-16 19:18:51 +00:00
self.data['give'] = self.give
if not self.request.POST.has_key('anonymous'):
del self.data['anonymous']
return {'data':self.data}
else:
return {'initial':self.data}
def get_preapproval_amount(self):
2016-07-25 15:32:04 +00:00
self.offer_id = self.request.GET.get('offer_id', self.request.POST.get('offer_id', None))
self.give = self.offer_id.startswith('give') if self.offer_id else False
2014-12-16 19:18:51 +00:00
if self.give:
self.offer_id = self.offer_id[4:]
2014-02-21 18:16:55 +00:00
if not self.offer_id and self.work.last_campaign() and self.work.last_campaign().individual_offer:
self.offer_id = self.work.last_campaign().individual_offer.id
2013-08-22 20:12:27 +00:00
preapproval_amount = None
2013-08-27 22:03:35 +00:00
if self.offer_id != None:
try:
2013-08-27 22:03:35 +00:00
preapproval_amount = D(models.Offer.objects.get(id=self.offer_id).price)
except:
preapproval_amount = None
return preapproval_amount
def form_valid(self, form):
p = PaymentManager()
2016-08-15 19:22:32 +00:00
t, url = p.process_transaction('USD', form.amount(),
host = PAYMENT_HOST_NONE,
campaign=self.campaign,
user=self.request.user,
2016-08-15 19:22:32 +00:00
paymentReason="Unglue.it Purchase for {0}".format(self.campaign.name),
pledge_extra=form.trans_extra
2016-08-15 19:22:32 +00:00
)
if url:
return HttpResponseRedirect(url)
else:
logger.error("Attempt to produce transaction id {0} failed".format(t.id))
return HttpResponse("Our attempt to enable your transaction failed. We have logged this error.")
2016-08-15 19:22:32 +00:00
2017-02-13 18:33:26 +00:00
class NewDonationView(FormView):
template_name = "fund_the_pledge.html"
form_class = DonationForm
def form_valid(self, form):
p = PaymentManager()
t, url = p.process_transaction('USD', form.cleaned_data["amount"],
user = self.request.user,
paymentReason="Donation to {}".format(COMPANY_TITLE),
)
if url:
return HttpResponseRedirect(url)
else:
logger.error("Attempt to produce transaction id {0} failed".format(t.id))
return HttpResponse("Our attempt to set up your donation failed. We have logged this problem.")
class FundView(FormView):
2016-08-15 19:22:32 +00:00
template_name = "fund_the_pledge.html"
transaction = None
action = None
2016-08-15 19:22:32 +00:00
2014-02-20 03:18:23 +00:00
def get_form_class(self):
if self.request.user.is_anonymous():
return AnonCCForm
elif self.request.user.profile.account:
return AccountCCForm
2014-02-20 03:18:23 +00:00
else:
return CCForm
def get_form_kwargs(self):
kwargs = super(FundView, self).get_form_kwargs()
2017-02-13 18:33:26 +00:00
if kwargs.has_key('data'):
data = kwargs['data'].copy()
kwargs['data'] = data
else:
data = {}
kwargs['initial'] = data
2017-07-25 12:14:05 +00:00
t_id=self.kwargs["t_id"]
if self.transaction is None:
try:
self.transaction = get_object_or_404(Transaction, id=t_id)
except ValueError:
raise Http404
2012-10-13 17:45:46 +00:00
2017-02-13 18:33:26 +00:00
if not self.transaction.campaign:
self.action = 'donation'
elif self.transaction.campaign.type == REWARDS:
2017-12-14 21:24:26 +00:00
self.action = 'donation' if self.transaction.donation else 'pledge'
2014-02-20 03:18:23 +00:00
elif self.transaction.campaign.type == THANKS:
self.action = 'contribution'
else:
self.action = 'purchase'
2016-08-15 19:22:32 +00:00
data.update(
{'preapproval_amount':self.transaction.needed_amount,
2017-02-13 18:33:26 +00:00
'username':self.request.user.username if self.request.user.is_authenticated() else None,
'work_id':self.transaction.campaign.work_id if self.transaction.campaign else None,
2017-02-13 18:33:26 +00:00
'title':self.transaction.campaign.work.title if self.transaction.campaign else COMPANY_TITLE}
2016-08-15 19:22:32 +00:00
)
return kwargs
def get_context_data(self, **kwargs):
context = super(FundView, self).get_context_data(**kwargs)
2016-08-15 19:22:32 +00:00
context['modified'] = self.transaction.status == TRANSACTION_STATUS_MODIFIED
context['preapproval_amount'] = self.transaction.max_amount
context['needed'] = self.transaction.needed_amount
2016-08-15 19:22:32 +00:00
context['transaction'] = self.transaction
2012-09-11 22:17:37 +00:00
context['STRIPE_PK'] = stripelib.STRIPE_PK
context['action'] = self.action
return context
2016-08-15 19:22:32 +00:00
def form_valid(self, form):
p = PaymentManager()
stripe_token = form.cleaned_data.get("stripe_token", None)
2012-10-13 17:45:46 +00:00
self.transaction.host = settings.PAYMENT_PROCESSOR
2016-08-15 19:22:32 +00:00
return_url = "%s?tid=%s" % (reverse('pledge_complete'), self.transaction.id)
2014-02-20 03:18:23 +00:00
2017-02-13 18:33:26 +00:00
if not self.transaction.campaign:
if self.request.user.is_authenticated():
self.transaction.user = self.request.user
# if there's an email address, put it in the receipt column, so far unused.
self.transaction.receipt = form.cleaned_data.get("email", None)
t, url = p.charge(self.transaction, return_url = return_url, token=stripe_token)
2017-07-25 12:14:05 +00:00
2017-02-13 18:33:26 +00:00
elif self.transaction.campaign.type == THANKS and self.transaction.user == None:
2014-02-20 03:18:23 +00:00
#anonymous user, just charge the card!
if self.request.user.is_authenticated():
self.transaction.user = self.request.user
2014-02-20 03:18:23 +00:00
# if there's an email address, put it in the receipt column, so far unused.
2016-08-15 19:22:32 +00:00
self.transaction.receipt = form.cleaned_data.get("email", None)
t, url = p.charge(self.transaction, return_url = return_url, token=stripe_token)
2014-02-20 03:18:23 +00:00
elif self.request.user.is_anonymous():
#somehow the user lost their login
return HttpResponseRedirect(reverse('superlogin'))
elif self.transaction.user.id != self.request.user.id:
# other sort of strange trouble!
2016-08-15 19:22:32 +00:00
return render(self.request, "pledge_user_error.html", {'transaction': self.transaction, 'action': self.action })
else:
2014-02-20 03:18:23 +00:00
# if the user has active account, use it. Otherwise...
if not self.request.user.profile.account:
2016-08-15 19:22:32 +00:00
2014-02-20 03:18:23 +00:00
# if we get a stripe_token, create a new stripe account for the user
if stripe_token:
try:
p.make_account(user=self.request.user, host=settings.PAYMENT_PROCESSOR, token=stripe_token)
except baseprocessor.ProcessorError as e:
return render(self.request, "pledge_card_error.html", {'transaction': self.transaction, 'exception':e })
else: # empty token
e = baseprocessor.ProcessorError("Empty token")
return render(self.request, "pledge_card_error.html", {'transaction': self.transaction, 'exception':e })
# with the Account in hand, now do the transaction
if self.action == 'pledge':
2016-08-15 19:22:32 +00:00
t, url = p.authorize(self.transaction, return_url = return_url)
2014-02-20 03:18:23 +00:00
else:
2016-08-15 19:22:32 +00:00
t, url = p.charge(self.transaction, return_url = return_url)
# redirecting user to pledge_complete/payment_complete on successful preapproval (in the case of stripe)
if url is not None:
return HttpResponseRedirect(url)
2012-10-13 04:46:34 +00:00
else:
2016-08-15 19:22:32 +00:00
return render(self.request, "pledge_card_error.html", {'transaction': self.transaction })
class GiftCredit(TemplateView):
2016-08-15 19:22:32 +00:00
template_name = "gift_credit.html"
def get_context_data(self, **kwargs):
context = super(GiftCredit, self).get_context_data(**kwargs)
2016-08-15 19:22:32 +00:00
context['faqmenu'] = "gift"
try:
2016-08-15 19:22:32 +00:00
envelope = signing.loads(kwargs['token'])
context['envelope'] = envelope
except signing.BadSignature:
2016-08-15 19:22:32 +00:00
self.template_name = "gift_error.html"
return context
try:
work = models.Work.objects.get(id=envelope['work_id'])
2016-08-15 19:22:32 +00:00
campaign = work.last_campaign()
except models.Work.DoesNotExist:
campaign = None
2016-08-15 19:22:32 +00:00
context['work'] = work
try:
user = User.objects.get(username=envelope['username'])
except User.DoesNotExist:
2016-08-15 19:22:32 +00:00
self.template_name = "gift_user_error.html"
context['error'] = 'user does not exist'
return context
if user != self.request.user:
2016-08-15 19:22:32 +00:00
self.template_name = "gift_user_error.html"
context['error'] = 'wrong user logged in'
return context
try:
# check token not used
CreditLog.objects.get(sent=envelope['sent'])
2016-08-15 19:22:32 +00:00
context['error'] = 'credit already registered'
return context
except CreditLog.DoesNotExist:
#not used yet!
2016-08-15 19:22:32 +00:00
amount = envelope['amount']+envelope['cents']/D(100)
CreditLog.objects.create(user=user, amount=amount, action='deposit', sent=envelope['sent'])
ts = Transaction.objects.filter(user=user, campaign=campaign, status=TRANSACTION_STATUS_NONE).order_by('-pk')
if ts.count()==0:
2016-08-15 19:22:32 +00:00
ts = Transaction.objects.filter(user=user, campaign=campaign, status=TRANSACTION_STATUS_MODIFIED).order_by('-pk')
if ts.count()>0:
2016-08-15 19:22:32 +00:00
t = ts[0]
credit_transaction(t, user, amount)
for t in ts[1:]:
2016-08-15 19:22:32 +00:00
t.status = TRANSACTION_STATUS_CANCELED
t.save()
2016-08-15 19:22:32 +00:00
context['transaction'] = t
return context
else:
2012-09-06 15:36:13 +00:00
user.credit.add_to_balance(amount)
return context
2016-08-15 19:22:32 +00:00
class PledgeRechargeView(TemplateView):
"""
a view to allow for recharge of a transaction for failed transactions or ones with errors
"""
2016-08-15 19:22:32 +00:00
template_name = "pledge_recharge.html"
def get_context_data(self, **kwargs):
2016-08-15 19:22:32 +00:00
2012-07-12 02:51:36 +00:00
context = super(PledgeRechargeView, self).get_context_data(**kwargs)
2016-08-15 19:22:32 +00:00
# the following should be true since PledgeView.as_view is wrapped in login_required
assert self.request.user.is_authenticated()
user = self.request.user
2016-08-15 19:22:32 +00:00
work = safe_get_work(self.kwargs["work_id"])
campaign = work.last_campaign()
2016-08-15 19:22:32 +00:00
if campaign is None:
raise Http404
2016-08-15 19:22:32 +00:00
transaction = campaign.transaction_to_recharge(user)
2016-08-15 19:22:32 +00:00
# calculate a URL to do a preapproval -- in the future, we may want to do a straight up payment
2016-08-15 19:22:32 +00:00
return_url = None
nevermind_url = None
2016-08-15 19:22:32 +00:00
if transaction is not None:
2016-08-15 19:22:32 +00:00
# the recipients of this authorization is not specified here but rather by the PaymentManager.
paymentReason = "Unglue.it Recharge for {0}".format(campaign.name)
2016-08-15 19:22:32 +00:00
p = PaymentManager()
t, url = p.authorize(transaction, return_url=return_url, paymentReason=paymentReason)
logger.info("Recharge url: {0}".format(url))
else:
url = None
2016-08-15 19:22:32 +00:00
context.update({
'work':work,
'transaction':transaction,
'payment_processor':transaction.host if transaction is not None else None,
'recharge_url': url
})
return context
2016-08-15 19:22:32 +00:00
class FundCompleteView(TemplateView):
"""A callback for Payment to tell unglue.it that a payment transaction has completed successfully.
2016-08-15 19:22:32 +00:00
Possible things to implement:
2016-08-15 19:22:32 +00:00
after pledging, supporter receives email including thanks, work pledged, amount, expiry date, any next steps they should expect; others?
study other confirmation emails for their contents
should note that a confirmation email has been sent to $email from $sender
2016-08-15 19:22:32 +00:00
should briefly note next steps (e.g. if this campaign succeeds you will be emailed on date X)
"""
2016-08-15 19:22:32 +00:00
template_name = "pledge_complete.html"
2014-02-20 03:18:23 +00:00
def get(self, request, *args, **kwargs):
context = self.get_context_data(**kwargs)
2016-08-15 19:22:32 +00:00
if self.transaction:
2017-02-13 18:33:26 +00:00
if not self.transaction.campaign:
2017-07-25 12:14:05 +00:00
return self.render_to_response(context)
if self.transaction.campaign.type == THANKS:
return DownloadView.as_view()(request, work=self.transaction.campaign.work)
2016-08-15 19:22:32 +00:00
2014-02-21 18:16:55 +00:00
else:
if request.user.is_authenticated():
if self.user_is_ok():
return self.render_to_response(context)
else:
return HttpResponseRedirect(reverse('work', kwargs={'work_id': self.transaction.campaign.work_id}))
else:
return redirect_to_login(request.get_full_path())
else:
return HttpResponseRedirect(reverse('home'))
2016-08-15 19:22:32 +00:00
def user_is_ok(self):
if not self.transaction:
2016-08-15 19:22:32 +00:00
return False
2017-02-13 18:33:26 +00:00
if (not self.transaction.campaign or self.transaction.campaign.type == THANKS) and self.transaction.user == None:
# to handle anonymous donors- leakage not an issue
return True
2016-08-15 19:22:32 +00:00
else:
return self.request.user.id == self.transaction.user_id
2016-08-15 19:22:32 +00:00
2014-02-20 03:18:23 +00:00
def get_context_data(self):
# pick up all get and post parameters and display
context = super(FundCompleteView, self).get_context_data()
self.transaction = None
2016-08-15 19:22:32 +00:00
# pull out the transaction id and try to get the corresponding Transaction
2016-08-15 19:22:32 +00:00
transaction_id = self.request.POST.get("tid", self.request.GET.get("tid", None))
if not transaction_id:
return context
try:
self.transaction = Transaction.objects.get(id=transaction_id)
except (ValueError, Transaction.DoesNotExist):
self.transaction = None
2016-08-15 19:22:32 +00:00
if not self.transaction:
return context
2016-08-15 19:22:32 +00:00
# work and campaign in question
try:
campaign = self.transaction.campaign
work = campaign.work
except Exception, e:
campaign = None
work = None
2016-08-15 19:22:32 +00:00
# # we need to check whether the user tied to the transaction is indeed the authenticated user.
2016-08-15 19:22:32 +00:00
if not self.user_is_ok():
return context
2016-08-15 19:22:32 +00:00
2014-12-18 18:37:28 +00:00
gift = self.transaction.extra.has_key('give_to')
2014-12-18 16:41:06 +00:00
if not gift:
# add the work corresponding to the Transaction on the user's wishlist if it's not already on the wishlist
if self.transaction.user is not None and (campaign is not None) and (work is not None):
self.transaction.user.wishlist.add_work(work, 'pledging', notify=True)
#put info into session for download page to pick up.
2016-08-15 19:22:32 +00:00
self.request.session['amount'] = int(self.transaction.amount * 100)
2014-12-18 16:41:06 +00:00
if self.transaction.receipt:
2016-08-15 19:22:32 +00:00
self.request.session['receipt'] = self.transaction.receipt
context["transaction"] = self.transaction
context["work"] = work
context["campaign"] = campaign
context["faqmenu"] = "complete"
context["site"] = Site.objects.get_current()
2016-08-15 19:22:32 +00:00
return context
class PledgeModifiedView(FundCompleteView):
def get_context_data(self):
context = super(PledgeModifiedView, self).get_context_data()
2016-08-15 19:22:32 +00:00
context['modified'] = True
return context
2016-08-15 19:22:32 +00:00
class PledgeCancelView(FormView):
"""A view for allowing a user to cancel the active transaction for specified campaign"""
2016-08-15 19:22:32 +00:00
template_name = "pledge_cancel.html"
form_class = PledgeCancelForm
2016-08-15 19:22:32 +00:00
def get_context_data(self, **kwargs):
context = super(PledgeCancelView, self).get_context_data(**kwargs)
2016-08-15 19:22:32 +00:00
2012-05-23 14:22:48 +00:00
# initialize error to be None
context["error"] = None
2016-08-15 19:22:32 +00:00
# the following should be true since PledgeCancelView.as_view is wrapped in login_required
2016-08-15 19:22:32 +00:00
2012-05-23 14:22:48 +00:00
if self.request.user.is_authenticated():
user = self.request.user
else:
context["error"] = "You are not logged in."
2012-05-23 14:22:48 +00:00
return context
try:
campaign = get_object_or_404(models.Campaign, id=self.kwargs["campaign_id"])
except ValueError:
raise Http404
2016-08-15 19:22:32 +00:00
2012-05-23 14:22:48 +00:00
if campaign.status != 'ACTIVE':
context["error"] = "{0} is not an active campaign".format(campaign)
return context
2016-08-15 19:22:32 +00:00
work = campaign.work
transactions = campaign.transactions().filter(user=user, status=TRANSACTION_STATUS_ACTIVE)
2016-08-15 19:22:32 +00:00
2012-05-23 14:22:48 +00:00
if transactions.count() < 1:
context["error"] = "You don't have an active transaction for this campaign."
2012-05-23 14:22:48 +00:00
return context
elif transactions.count() > 1:
logger.error("User {0} has {1} active transactions for campaign id {2}".format(user, transactions.count(), campaign.id))
2012-05-23 14:22:48 +00:00
context["error"] = "You have {0} active transactions for this campaign".format(transactions.count())
return context
transaction = transactions[0]
2012-05-23 14:22:48 +00:00
if transaction.type != PAYMENT_TYPE_AUTHORIZATION:
logger.error("Transaction id {0} transaction type, which should be {1}, is actually {2}".format(transaction.id, PAYMENT_TYPE_AUTHORIZATION, transaction.type))
2012-05-23 14:22:48 +00:00
context["error"] = "Your transaction type, which should be {0}, is actually {1}".format(PAYMENT_TYPE_AUTHORIZATION, transaction.type)
return context
2016-08-15 19:22:32 +00:00
2012-05-23 14:22:48 +00:00
# we've located the transaction, work, and campaign referenced in the view
2016-08-15 19:22:32 +00:00
context["transaction"] = transaction
context["work"] = work
context["campaign"] = campaign
context["faqmenu"] = "cancel"
2016-08-15 19:22:32 +00:00
return context
2016-08-15 19:22:32 +00:00
2012-05-23 14:22:48 +00:00
def form_valid(self, form):
# check that user does, in fact, have an active transaction for specified campaign
2016-08-15 19:22:32 +00:00
2012-05-23 14:22:48 +00:00
logger.info("arrived at pledge_cancel form_valid")
# pull campaign_id from form, not from URI as we do from GET
2016-08-15 19:22:32 +00:00
campaign_id = self.request.POST.get('campaign_id', self.request.GET.get('campaign_id'))
# this following logic should be extraneous.
if self.request.user.is_authenticated():
user = self.request.user
else:
return HttpResponse("You need to be logged in.")
2016-08-15 19:22:32 +00:00
2012-05-23 14:22:48 +00:00
try:
# look up the specified campaign and attempt to pull up the appropriate transaction
# i.e., the transaction actually belongs to user, that the transaction is active
try:
campaign = get_object_or_404(models.Campaign, id=self.kwargs["campaign_id"], status='ACTIVE')
except ValueError:
raise Http404
2012-05-23 14:22:48 +00:00
transaction = campaign.transaction_set.get(user=user, status=TRANSACTION_STATUS_ACTIVE,
type=PAYMENT_TYPE_AUTHORIZATION)
# attempt to cancel the transaction and redirect to the Work page if cancel is successful
2012-05-23 14:22:48 +00:00
# here's a place that would be nice to use https://docs.djangoproject.com/en/dev/ref/contrib/messages/
# to display the success or failure of the cancel operation as a popup in the context of the work page
2012-05-23 14:22:48 +00:00
p = PaymentManager()
result = p.cancel_transaction(transaction)
# put a notification here for pledge cancellation?
if result:
# Now if we redirect the user to the Work page and the IPN hasn't arrived, the status of the
# transaction might be out of date. Let's try an explicit polling of the transaction result before redirecting
# We might want to remove this in a production system
if settings.DEBUG:
update_status = p.update_preapproval(transaction)
# send a notice out that the transaction has been canceled -- leverage the pledge_modify notice for now
# BUGBUG: should have a pledge cancel notice actually since I think it's different
from regluit.payment.signals import pledge_modified
pledge_modified.send(sender=self, transaction=transaction, up_or_down="canceled")
logger.info("pledge_modified notice for cancellation: sender {0}, transaction {1}".format(self, transaction))
return HttpResponseRedirect(reverse('work', kwargs={'work_id': campaign.work_id}))
else:
logger.error("Attempt to cancel transaction id {0} failed".format(transaction.id))
return HttpResponse("Our attempt to cancel your transaction failed. We have logged this error.")
except Exception, e:
logger.error("Exception from attempt to cancel pledge for campaign id {0} for username {1}: {2}".format(campaign_id, user.username, e))
2016-08-15 19:22:32 +00:00
return HttpResponse("Sorry, something went wrong in canceling your campaign pledge. We have logged this error.")
def works_user_can_admin(user):
return models.Work.objects.filter(
Q(claim__user = user) | Q(claim__rights_holder__owner = user)
)
2017-05-02 21:10:06 +00:00
def works_user_can_admin_filter(request, work_id):
def work_survey_filter(answers):
works = works_user_can_admin(request.user)
if work_id == '0' and request.user.is_staff:
return answers
elif work_id:
work = safe_get_work(work_id)
if user_can_edit_work(request.user, work):
return answers.filter(run__run_info_histories__landing__works=work)
else:
return answers.none()
else:
return answers.filter(run__run_info_histories__landing__works__in=works)
2017-05-02 21:10:06 +00:00
return work_survey_filter
def export_surveys(request, qid, work_id):
def extra_entries(subject, run):
2017-05-19 17:46:12 +00:00
landing = completed = None
try:
landing = run.run_info_histories.all()[0].landing
2017-05-19 17:46:12 +00:00
completed = run.run_info_histories.all()[0].completed
except IndexError:
try:
landing = run.run_infos.all()[0].landing
2017-05-19 17:46:12 +00:00
completed = run.run_infos.all()[0].created
except IndexError:
label = wid = "error"
if landing:
label = landing.label
wid = landing.object_id
2017-05-19 17:46:12 +00:00
return [wid, subject.ip_address, run.id, completed, label]
if not request.user.is_authenticated() :
return HttpResponseRedirect(reverse('surveys'))
2017-05-19 17:46:12 +00:00
extra_headings = [u'work id', u'subject ip address', u'run id', u'date completed', u'landing label']
return export_answers(request, qid,
2017-05-02 21:10:06 +00:00
answer_filter=works_user_can_admin_filter(request, work_id),
extra_entries=extra_entries,
extra_headings=extra_headings,
filecode=work_id)
2017-05-02 21:10:06 +00:00
def surveys_summary(request, qid, work_id):
if not request.user.is_authenticated() :
return HttpResponseRedirect(reverse('surveys'))
return answer_summary(
2017-06-08 21:35:48 +00:00
request,
2017-05-02 21:10:06 +00:00
qid,
answer_filter=works_user_can_admin_filter(request, work_id),
)
2017-07-25 12:14:05 +00:00
2017-10-22 01:18:32 +00:00
2016-05-26 16:19:33 +00:00
def new_survey(request, work_id):
if not request.user.is_authenticated() :
return HttpResponseRedirect(reverse('surveys'))
my_works = works_user_can_admin( request.user)
2016-05-26 16:19:33 +00:00
if work_id:
work = safe_get_work(work_id)
2016-05-26 16:19:33 +00:00
for my_work in my_works:
2016-08-15 19:22:32 +00:00
if my_work == work:
form = SurveyForm()
2016-05-26 16:19:33 +00:00
break
else:
return HttpResponseRedirect(reverse('surveys'))
else:
work = None
form = SurveyForm()
2016-08-15 19:22:32 +00:00
if request.method == 'POST':
form = SurveyForm(data=request.POST)
2016-05-26 16:19:33 +00:00
if form.is_valid():
if not work and form.work:
for my_work in my_works:
print '{} {}'.format(my_work.id, form.work_id)
2016-08-15 19:22:32 +00:00
if my_work == form.work:
2016-05-26 16:19:33 +00:00
work = form.work
break
else:
print 'not mine'
return HttpResponseRedirect(reverse('surveys'))
print "create landing"
landing = Landing.objects.create(label=form.cleaned_data['label'], questionnaire=form.cleaned_data['survey'], content_object=work)
return HttpResponseRedirect(reverse('surveys'))
return render(request, "manage_survey.html", {"work":work, "form":form})
def surveys(request):
if not request.user.is_authenticated() :
return render(request, "surveys.html")
works = works_user_can_admin(request.user)
work_ids = [work.id for work in works]
surveys = Questionnaire.objects.filter(landings__object_id__in=work_ids).distinct()
return render(request, "surveys.html", {"works":works, "surveys":surveys})
2016-08-15 19:22:32 +00:00
def campaign_admin(request):
if not request.user.is_authenticated() :
2016-08-15 19:22:32 +00:00
return render(request, "admins_only.html")
if not request.user.is_staff :
return render(request, "admins_only.html")
2016-08-15 19:22:32 +00:00
context = {}
2016-08-15 19:22:32 +00:00
def campaigns_types():
# pull out Campaigns with Transactions that are ACTIVE -- and hence can be executed
# Campaign.objects.filter(transaction__status='ACTIVE')
2016-08-15 19:22:32 +00:00
campaigns_with_active_transactions = models.Campaign.objects.filter(transaction__status=TRANSACTION_STATUS_ACTIVE)
2016-08-15 19:22:32 +00:00
# pull out Campaigns with Transactions that are INCOMPLETE
2016-08-15 19:22:32 +00:00
campaigns_with_incomplete_transactions = models.Campaign.objects.filter(transaction__status=TRANSACTION_STATUS_INCOMPLETE)
2016-08-15 19:22:32 +00:00
# show all Campaigns with Transactions that are COMPLETED
2016-08-15 19:22:32 +00:00
campaigns_with_completed_transactions = models.Campaign.objects.filter(transaction__status=TRANSACTION_STATUS_COMPLETE)
2016-08-15 19:22:32 +00:00
# show Campaigns with Transactions that are CANCELED
2016-08-15 19:22:32 +00:00
campaigns_with_canceled_transactions = models.Campaign.objects.filter(transaction__status=TRANSACTION_STATUS_CANCELED)
2016-08-15 19:22:32 +00:00
return (campaigns_with_active_transactions, campaigns_with_incomplete_transactions, campaigns_with_completed_transactions, campaigns_with_canceled_transactions)
2016-08-15 19:22:32 +00:00
form = CampaignAdminForm()
pm = PaymentManager()
check_status_results = None
command_status = None
2016-08-15 19:22:32 +00:00
if request.method == 'GET':
pass
elif request.method == 'POST':
if 'campaign_checkstatus' in request.POST.keys():
# campaign_checkstatus
try:
status = pm.checkStatus()
check_status_results = ""
# parse the output to display chat transaction statuses have been updated
if len(status["preapprovals"]):
for t in status["preapprovals"]:
check_status_results += "<p>Preapproval key: %s updated</p>" % (t["key"])
else:
check_status_results += "<p>No preapprovals needed updating</p>"
if len(status["payments"]):
for t in status["payments"]:
2016-08-15 19:22:32 +00:00
info = ", ".join(["%s:%s" % (k, v) for (k, v) in t.items()])
check_status_results += "<p>Payment updated: %s </p>" % (info)
2016-08-15 19:22:32 +00:00
else:
2016-08-15 19:22:32 +00:00
check_status_results += "<p>No payments needed updating</p>"
command_status = _("Transactions updated based on PaymentDetails and PreapprovalDetails")
except Exception, e:
check_status_results = e
2016-08-15 19:22:32 +00:00
elif 'execute_campaigns' in request.POST.keys():
c_id = request.POST.get('active_campaign', None)
if c_id is not None:
try:
campaign = models.Campaign.objects.get(id=c_id)
results = pm.execute_campaign(campaign)
command_status = str(results)
except Exception, e:
command_status = "Error in executing transactions for campaign %s " % (str(e))
elif 'finish_campaigns' in request.POST.keys():
c_id = request.POST.get('incomplete_campaign', None)
if c_id is not None:
try:
campaign = models.Campaign.objects.get(id=c_id)
results = pm.finish_campaign(campaign)
command_status = str(results)
except Exception, e:
2016-08-15 19:22:32 +00:00
command_status = "Error in finishing transactions for campaign %s " % (str(e))
elif 'cancel_campaigns' in request.POST.keys():
c_id = request.POST.get('active_campaign', None)
if c_id is not None:
try:
campaign = models.Campaign.objects.get(id=c_id)
results = pm.cancel_campaign(campaign)
command_status = str(results)
except Exception, e:
2016-08-15 19:22:32 +00:00
command_status = "Error in canceling transactions for campaign %s " % (str(e))
(campaigns_with_active_transactions, campaigns_with_incomplete_transactions, campaigns_with_completed_transactions,
campaigns_with_canceled_transactions) = campaigns_types()
2016-08-15 19:22:32 +00:00
context.update({
'form': form,
'check_status_results':check_status_results,
'campaigns_with_active_transactions': campaigns_with_active_transactions,
'campaigns_with_incomplete_transactions': campaigns_with_incomplete_transactions,
'campaigns_with_completed_transactions': campaigns_with_completed_transactions,
'campaigns_with_canceled_transactions': campaigns_with_canceled_transactions,
'command_status': command_status
})
return render(request, "campaign_admin.html", context)
2013-10-17 16:44:47 +00:00
def supporter(request, supporter_username, template_name, extra_context={}):
supporter = get_object_or_404(User, username=supporter_username)
wishlist = supporter.wishlist
works_unglued = []
works_active = []
works_wished = []
2012-12-31 18:46:23 +00:00
works_on_wishlist = wishlist.works.all()
2016-08-15 19:22:32 +00:00
2012-12-31 18:46:23 +00:00
if (works_on_wishlist):
# querysets for tabs
2012-12-31 18:46:23 +00:00
# unglued tab is anything with an existing ebook or successful campaign
## .order_by() may clash with .distinct() and this should be fixed
2012-12-31 18:46:23 +00:00
unglueit_works = works_on_wishlist.filter(campaigns__status="SUCCESSFUL").distinct()
2014-12-14 15:00:10 +00:00
works_otherwise_available = works_on_wishlist.filter(is_free = True).distinct()
2012-12-31 18:46:23 +00:00
works_unglued = unglueit_works | works_otherwise_available
works_unglued = works_unglued.order_by('-campaigns__status', 'campaigns__deadline', '-num_wishes')
works_active = works_on_wishlist.filter(campaigns__status='ACTIVE').order_by('campaigns__deadline').distinct()
2016-08-15 19:22:32 +00:00
# everything else goes in tab 3
2012-12-31 18:46:23 +00:00
works_wished = works_on_wishlist.exclude(pk__in=works_active.values_list('pk', flat=True)).exclude(pk__in=works_unglued.values_list('pk', flat=True)).order_by('-num_wishes')
2016-08-15 19:22:32 +00:00
2013-12-14 18:24:29 +00:00
slidelist = []
# badge counts
backed = works_unglued.count()
backing = works_active.count()
wished = works_wished.count()
2016-08-15 19:22:32 +00:00
else:
backed = 0
backing = 0
wished = 0
2013-12-14 18:24:29 +00:00
slidelist = slideshow()
# default to showing the Active tab if there are active campaigns, else show Wishlist
if backing > 0:
activetab = "#2"
2012-12-31 20:20:03 +00:00
elif wished == 0:
activetab = "#1"
else:
activetab = "#3"
2016-08-15 19:22:32 +00:00
# following block to support profile admin form in supporter page
if request.user.is_authenticated() and request.user.username == supporter_username:
2016-08-15 19:22:32 +00:00
profile_obj = request.user.profile
2016-08-15 19:22:32 +00:00
if request.method == 'POST':
profile_form = ProfileForm(data=request.POST, instance=profile_obj)
if profile_form.is_valid():
if profile_form.cleaned_data['clear_facebook'] or profile_form.cleaned_data['clear_twitter'] or profile_form.cleaned_data['clear_goodreads'] :
if profile_form.cleaned_data['clear_facebook']:
2016-08-15 19:22:32 +00:00
profile_obj.facebook_id = 0
2013-03-18 18:56:27 +00:00
if profile_obj.avatar_source == models.FACEBOOK:
profile_obj.avatar_source = models.UNGLUEITAR
if profile_form.cleaned_data['clear_twitter']:
2016-08-15 19:22:32 +00:00
profile_obj.twitter_id = ""
2013-03-18 18:56:27 +00:00
if profile_obj.avatar_source == models.TWITTER:
profile_obj.avatar_source = models.UNGLUEITAR
if profile_form.cleaned_data['clear_goodreads']:
profile_obj.goodreads_user_id = None
profile_obj.goodreads_user_name = None
profile_obj.goodreads_user_link = None
profile_obj.goodreads_auth_token = None
profile_obj.goodreads_auth_secret = None
profile_obj.save()
profile_form.save()
else:
2016-08-15 19:22:32 +00:00
profile_form = ProfileForm(instance=profile_obj)
else:
profile_form = ''
process_kindle_email(request)
context = {
"supporter": supporter,
"wishlist": wishlist,
"works_unglued": works_unglued,
"works_active": works_active,
"works_wished": works_wished,
2013-12-14 18:24:29 +00:00
"slidelist": slidelist,
"backed": backed,
"backing": backing,
"wished": wished,
"profile_form": profile_form,
2016-08-15 19:22:32 +00:00
"ungluers": userlists.other_users(supporter, 5),
"activetab": activetab,
}
2013-10-17 16:44:47 +00:00
context.update(extra_context)
return render(request, template_name, context)
2016-08-15 19:22:32 +00:00
def library(request, library_name):
context = {}
2013-10-17 16:44:47 +00:00
try:
# determine if the supporter is a library
2016-08-15 19:22:32 +00:00
authenticator = Authenticator(request, library_name)
2013-10-17 16:44:47 +00:00
context['authenticator'] = authenticator
context['library'] = library = authenticator.library
2013-10-17 16:44:47 +00:00
except Library.DoesNotExist:
raise Http404
2016-08-15 19:22:32 +00:00
works_active = models.Work.objects.filter(acqs__user=library.user, acqs__license=LIBRARY).distinct()
if works_active.count() > 0:
context['works_active'] = works_active
context['activetab'] = "#2"
2016-08-15 19:22:32 +00:00
context['ungluers'] = userlists.library_users(library, 5)
return supporter(request, library_name, template_name='libraryauth/library.html', extra_context=context)
2013-10-17 16:44:47 +00:00
class ManageAccount(FormView):
2016-08-15 19:22:32 +00:00
template_name = "manage_account.html"
form_class = PlainCCForm
def get_context_data(self, **kwargs):
context = super(ManageAccount, self).get_context_data(**kwargs)
context['STRIPE_PK'] = stripelib.STRIPE_PK
return context
2016-08-15 19:22:32 +00:00
def form_valid(self, form):
""" save the token, make an account"""
p = PaymentManager()
stripe_token = form.cleaned_data["stripe_token"]
# if we get a stripe_token, create a new stripe account for the user
if stripe_token:
try:
p.make_account(user=self.request.user, host=settings.PAYMENT_PROCESSOR, token=stripe_token)
except baseprocessor.ProcessorError as e:
return render(self.request, "pledge_card_error.html", {'exception':e})
2016-07-25 15:32:04 +00:00
next = self.request.GET.get('next', self.request.POST.get('next', None))
if next :
return HttpResponseRedirect(next)
else:
return render(self.request, self.template_name, self.get_context_data())
def search(request):
2013-03-30 01:11:01 +00:00
q = request.GET.get('q', '')
2016-06-20 17:08:23 +00:00
ty = request.GET.get('ty', 'g') # ge= 'general, au= 'author'
2016-08-15 19:22:32 +00:00
request.session['q'] = q
2016-06-20 17:16:37 +00:00
try:
page = int(request.GET.get('page', 1))
except ValueError:
# garbage in page
page = 1
gbo = request.GET.get('gbo', 'n') # gbo is flag for google books only
2016-08-15 19:22:32 +00:00
our_stuff = Q(is_free=True) | Q(campaigns__isnull=False)
if q != '' and page == 1 and not gbo == 'y':
isbnq = ISBN(q)
if isbnq.valid:
work_query = Q(identifiers__value=str(isbnq), identifiers__type="isbn")
2016-08-15 19:22:32 +00:00
elif ty == 'au':
work_query = Q(editions__authors__name=q)
else:
work_query = Q(title__icontains=q) | Q(editions__authors__name__icontains=q) | Q(subjects__name__iexact=q)
2015-02-12 14:46:58 +00:00
campaign_works = models.Work.objects.filter(our_stuff).filter(work_query).distinct()
for work in campaign_works:
results = models.Work.objects.none()
break
else:
results = gluejar_search(q, user_ip=request.META['REMOTE_ADDR'], page=1)
gbo = 'y'
else:
if gbo == 'n':
2016-08-15 19:22:32 +00:00
page = page-1 # because page=1 is the unglue.it results
results = gluejar_search(q, user_ip=request.META['REMOTE_ADDR'], page=page)
campaign_works = None
# flag search result as on wishlist as appropriate
2016-08-15 19:22:32 +00:00
works = []
for result in results:
2012-01-31 15:07:52 +00:00
try:
2016-08-15 19:22:32 +00:00
work = models.Identifier.objects.get(type='goog', value=result['googlebooks_id']).work
2012-01-31 15:07:52 +00:00
works.append(work)
2016-08-15 19:22:32 +00:00
except models.Identifier.DoesNotExist:
2012-01-31 15:07:52 +00:00
works.append(result)
context = {
"q": q,
"gbo": gbo,
2016-06-06 17:49:22 +00:00
"ty": ty,
"results": works,
"campaign_works": campaign_works
}
return render(request, 'search.html', context)
2011-09-29 11:44:03 +00:00
# TODO: perhaps this functionality belongs in the API?
@require_POST
@login_required
@csrf_exempt
def wishlist(request):
googlebooks_id = request.POST.get('googlebooks_id', None)
2011-09-29 11:44:03 +00:00
remove_work_id = request.POST.get('remove_work_id', None)
add_work_id = request.POST.get('add_work_id', None)
setkw = request.POST.get('setkw', None)
if setkw and request.user.is_staff:
try:
subject = models.Subject.objects.get(name=setkw)
except models.Subject.DoesNotExist:
return HttpResponse('invalid subject')
if remove_work_id:
work = safe_get_work(int(remove_work_id))
work.subjects.remove(subject)
return HttpResponse('removed work from '+setkw)
elif add_work_id:
2016-08-15 19:22:32 +00:00
work = safe_get_work(add_work_id)
work.subjects.add(subject)
return HttpResponse('added work to '+setkw)
2016-08-15 19:22:32 +00:00
if googlebooks_id:
2012-01-31 19:54:48 +00:00
try:
edition = bookloader.add_by_googlebooks_id(googlebooks_id)
if edition.new:
# add related editions asynchronously
2012-02-16 18:19:36 +00:00
tasks.populate_edition.delay(edition.isbn_13)
2016-08-15 19:22:32 +00:00
request.user.wishlist.add_work(edition.work, 'user', notify=True)
2013-02-26 20:30:14 +00:00
return HttpResponse('added googlebooks id')
2012-01-31 19:54:48 +00:00
except bookloader.LookupFailure:
logger.warning("failed to load googlebooks_id %s" % googlebooks_id)
return HttpResponse('error adding googlebooks id')
2012-01-31 19:54:48 +00:00
except Exception, e:
2016-08-15 19:22:32 +00:00
logger.warning("Error in wishlist adding %s" % (e))
2013-02-26 20:30:14 +00:00
return HttpResponse('error adding googlebooks id')
2011-09-29 11:44:03 +00:00
# TODO: redirect to work page, when it exists
elif remove_work_id:
work = safe_get_work(int(remove_work_id))
request.user.wishlist.remove_work(work)
2013-02-26 20:30:14 +00:00
return HttpResponse('removed work from wishlist')
elif add_work_id:
2011-12-31 18:49:23 +00:00
# if adding from work page, we have may work.id, not googlebooks_id
2016-08-15 19:22:32 +00:00
work = safe_get_work(add_work_id)
request.user.wishlist.add_work(work, 'user', notify=True)
2013-02-26 20:30:14 +00:00
return HttpResponse('added work to wishlist')
2015-01-13 01:24:32 +00:00
@require_POST
@login_required
def kw_edit(request, work_id):
work = safe_get_work(work_id)
remove_kw = request.POST.get('remove_kw', None)
2015-01-14 20:07:54 +00:00
add_form = request.POST.get('kw_add', False) # signal to process form
if user_can_edit_work(request.user, work):
2015-01-13 01:24:32 +00:00
if remove_kw:
try:
subject = models.Subject.objects.get(name=remove_kw)
except models.Subject.DoesNotExist:
return HttpResponse('invalid subject')
work.subjects.remove(subject)
2016-08-15 19:22:32 +00:00
return HttpResponse('removed ' + remove_kw)
2015-01-14 20:07:54 +00:00
elif add_form:
2016-08-15 19:22:32 +00:00
form = SubjectSelectForm(data=request.POST)
2015-01-14 20:07:54 +00:00
if form.is_valid():
add_kw = form.cleaned_data['add_kw']
try:
subject = models.Subject.objects.get(name=add_kw)
except models.Subject.DoesNotExist:
return HttpResponse('invalid subject')
work.subjects.add(subject)
2016-08-15 19:22:32 +00:00
return HttpResponse(add_kw.name)
2015-01-14 20:07:54 +00:00
else:
2016-08-15 19:22:32 +00:00
return HttpResponse('xxbadform')
2015-01-14 20:07:54 +00:00
else:
return HttpResponse(str(add_form))
return HttpResponse(str(add_form))
2015-01-13 01:24:32 +00:00
2016-08-15 19:22:32 +00:00
class InfoPageView(TemplateView):
2016-08-15 19:22:32 +00:00
2017-06-12 16:05:49 +00:00
template_name = 'metrics.html'
def get_template_names(self, **kwargs):
if self.kwargs['template_name']:
2017-06-12 16:05:49 +00:00
return [self.kwargs['template_name'], self.template_name]
else:
2017-06-12 16:05:49 +00:00
return [self.template_name]
2016-08-15 19:22:32 +00:00
def get_context_data(self, **kwargs):
users = User.objects
users.today = users.filter(date_joined__range = (date_today(), now()))
users.days7 = users.filter(date_joined__range = (date_today()-timedelta(days=7), now()))
users.year = users.filter(date_joined__year = date_today().year)
users.month = users.year.filter(date_joined__month = date_today().month)
users.yesterday = users.filter(date_joined__range = (date_today()-timedelta(days=1), date_today()))
users.gr = users.filter(profile__goodreads_user_id__isnull = False)
users.lt = users.exclude(profile__librarything_id = '')
users.fb = users.filter(profile__facebook_id__isnull = False)
users.tw = users.exclude(profile__twitter_id = '')
2014-11-19 21:03:21 +00:00
users.libtools = users.filter(libpref__isnull = False)
works = models.Work.objects
works.today = works.filter(created__range = (date_today(), now()))
works.days7 = works.filter(created__range = (date_today()-timedelta(days=7), now()))
works.year = works.filter(created__year = date_today().year)
works.month = works.year.filter(created__month = date_today().month)
works.yesterday = works.filter(created__range = (date_today()-timedelta(days=1), date_today()))
works.wishedby2 = works.filter(num_wishes__gte = 2)
works.wishedby20 = works.filter(num_wishes__gte = 20)
works.wishedby5 = works.filter(num_wishes__gte = 5)
works.wishedby50 = works.filter(num_wishes__gte = 50)
works.wishedby10 = works.filter(num_wishes__gte = 10)
works.wishedby100 = works.filter(num_wishes__gte = 100)
2015-03-31 17:50:39 +00:00
works.free = works.filter(is_free = True)
ebooks = models.Ebook.objects
ebooks.today = ebooks.filter(created__range = (date_today(), now()))
ebooks.days7 = ebooks.filter(created__range = (date_today()-timedelta(days=7), now()))
ebooks.year = ebooks.filter(created__year = date_today().year)
ebooks.month = ebooks.year.filter(created__month = date_today().month)
ebooks.yesterday = ebooks.filter(created__range = (date_today()-timedelta(days=1), date_today()))
2014-11-19 21:03:21 +00:00
ebooks.downloads = ebooks.aggregate(total=Sum('download_count'))['total']
ebooks.pdfdownloads = ebooks.filter(format='pdf').aggregate(total=Sum('download_count'))['total']
ebooks.epubdownloads = ebooks.filter(format='epub').aggregate(total=Sum('download_count'))['total']
ebooks.mobidownloads = ebooks.filter(format='mobi').aggregate(total=Sum('download_count'))['total']
ebookfiles = models.EbookFile.objects
ebookfiles.today = ebookfiles.filter(created__range = (date_today(), now()))
ebookfiles.days7 = ebookfiles.filter(created__range = (date_today()-timedelta(days=7), now()))
ebookfiles.year = ebookfiles.filter(created__year = date_today().year)
ebookfiles.month = ebookfiles.year.filter(created__month = date_today().month)
ebookfiles.yesterday = ebookfiles.filter(created__range = (date_today()-timedelta(days=1), date_today()))
2016-08-15 19:22:32 +00:00
wishlists = models.Wishlist.objects.exclude(wishes__isnull=True)
wishlists.today = wishlists.filter(created__range = (date_today(), now()))
wishlists.days7 = wishlists.filter(created__range = (date_today()-timedelta(days=7), now()))
wishlists.year = wishlists.filter(created__year = date_today().year)
wishlists.month = wishlists.year.filter(created__month = date_today().month)
if date_today().day==1:
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)
2016-08-15 19:22:32 +00:00
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.year.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']
marc = apps.get_model('marc','MARCRecord').objects
2014-11-19 21:03:21 +00:00
marc.today = marc.filter(created__range = (date_today(), now()))
marc.days7 = marc.filter(created__range = (date_today()-timedelta(days=7), now()))
marc.year = marc.filter(created__year = date_today().year)
marc.month = marc.year.filter(created__month = date_today().month)
2016-08-15 19:22:32 +00:00
marc.yesterday = marc.filter(created__range = (date_today()-timedelta(days=1), date_today()))
return {
2016-08-15 19:22:32 +00:00
'users': users,
'works': works,
'ebooks': ebooks,
'ebookfiles': ebookfiles,
'wishlists': wishlists,
2012-12-11 15:53:12 +00:00
'transactions': transactions,
2014-11-19 21:03:21 +00:00
'marc': marc,
}
2017-06-12 16:05:49 +00:00
class InfoLangView(InfoPageView):
2016-08-15 19:22:32 +00:00
2017-06-12 16:05:49 +00:00
template_name = 'languages.html'
2016-08-15 19:22:32 +00:00
2012-07-20 18:29:04 +00:00
def get_context_data(self, **kwargs):
2016-08-15 19:22:32 +00:00
languages = models.Work.objects.filter(num_wishes__gte = 1).values('language').annotate(lang_count=Count('language')).order_by('-lang_count')
2012-07-20 18:29:04 +00:00
return {
2016-08-15 19:22:32 +00:00
'wished_languages': languages,
2012-07-20 18:29:04 +00:00
}
2016-08-15 19:22:32 +00:00
2013-11-03 04:27:52 +00:00
class FAQView(FormView):
template_name = "faq.html"
form_class = DateCalculatorForm
def form_valid(self, form):
2016-08-15 19:22:32 +00:00
form.instance.status = 'DEMO'
form.instance.type = BUY2UNGLUE
form.instance.set_dollar_per_day()
2014-04-09 18:32:25 +00:00
form.instance.update_left()
2016-08-15 19:22:32 +00:00
form.instance._current_total = form.cleaned_data['revenue']
return self.render_to_response(self.get_context_data(form=form))
2016-08-15 19:22:32 +00:00
def get_initial(self):
2016-08-15 19:22:32 +00:00
return {'target':10000, 'cc_date_initial': date_today()+timedelta(days=1461), 'revenue':0, 'type':BUY2UNGLUE, 'status':'DEMO'}
2013-11-03 04:07:52 +00:00
def get_context_data(self, **kwargs):
2016-08-15 19:22:32 +00:00
cd = super(FAQView, self).get_context_data(**kwargs)
2013-11-03 04:07:52 +00:00
cd.update({
2016-08-15 19:22:32 +00:00
'location': self.kwargs["location"],
2013-11-03 04:07:52 +00:00
'sublocation': self.kwargs["sublocation"],
})
return cd
class GoodreadsDisplayView(TemplateView):
template_name = "goodreads_display.html"
def get_context_data(self, **kwargs):
context = super(GoodreadsDisplayView, self).get_context_data(**kwargs)
session = self.request.session
gr_client = GoodreadsClient(key=settings.GOODREADS_API_KEY, secret=settings.GOODREADS_API_SECRET)
2016-08-15 19:22:32 +00:00
2011-10-29 22:40:00 +00:00
user = self.request.user
if user.is_authenticated():
api_key = ApiKey.objects.filter(user=user)[0].key
context['api_key'] = api_key
2016-08-15 19:22:32 +00:00
if user.profile.goodreads_user_id is None:
# calculate the Goodreads authorization URL
(context["goodreads_auth_url"], request_token) = gr_client.begin_authorization(self.request.build_absolute_uri(reverse('goodreads_cb')))
2011-10-29 22:40:00 +00:00
logger.info("goodreads_auth_url: %s" %(context["goodreads_auth_url"]))
# store request token in session so that we can redeem it for auth_token if authorization works
session['goodreads_request_token'] = request_token['oauth_token']
session['goodreads_request_secret'] = request_token['oauth_token_secret']
2011-10-29 22:40:00 +00:00
else:
gr_shelves = gr_client.shelves_list(user_id=user.profile.goodreads_user_id)
context["shelves_info"] = gr_shelves
gr_shelf_load_form = GoodreadsShelfLoadingForm()
# load the shelves into the form
choices = [('all:%d' % (gr_shelves["total_book_count"]),'all (%d)' % (gr_shelves["total_book_count"]))] + \
2016-08-15 19:22:32 +00:00
[("%s:%d" % (s["name"], s["book_count"]) ,"%s (%d)" % (s["name"], s["book_count"])) for s in gr_shelves["user_shelves"]]
gr_shelf_load_form.fields['goodreads_shelf_name_number'].widget = Select(choices=tuple(choices))
2016-08-15 19:22:32 +00:00
context["gr_shelf_load_form"] = gr_shelf_load_form
2016-08-15 19:22:32 +00:00
# also load any CeleryTasks associated with the user
context["celerytasks"] = models.CeleryTask.objects.filter(user=user)
2016-08-15 19:22:32 +00:00
return context
2011-10-29 22:40:00 +00:00
@login_required
def goodreads_auth(request):
# calculate the Goodreads authorization URL
gr_client = GoodreadsClient(key=settings.GOODREADS_API_KEY, secret=settings.GOODREADS_API_SECRET)
(goodreads_auth_url, request_token) = gr_client.begin_authorization(request.build_absolute_uri(reverse('goodreads_cb')))
logger.info("goodreads_auth_url: %s" %(goodreads_auth_url))
# store request token in session so that we can redeem it for auth_token if authorization works
request.session['goodreads_request_token'] = request_token['oauth_token']
request.session['goodreads_request_secret'] = request_token['oauth_token_secret']
2016-08-15 19:22:32 +00:00
return HttpResponseRedirect(goodreads_auth_url)
2016-08-15 19:22:32 +00:00
@login_required
def goodreads_cb(request):
2011-10-29 22:40:00 +00:00
"""handle callback from Goodreads"""
2016-08-15 19:22:32 +00:00
session = request.session
authorized_flag = request.GET['authorize'] # is it '1'?
request_oauth_token = request.GET['oauth_token']
if authorized_flag == '1':
request_token = {'oauth_token': session.get('goodreads_request_token'),
'oauth_token_secret': session.get('goodreads_request_secret')}
gr_client = GoodreadsClient(key=settings.GOODREADS_API_KEY, secret=settings.GOODREADS_API_SECRET)
2016-08-15 19:22:32 +00:00
access_token = gr_client.complete_authorization(request_token)
2016-08-15 19:22:32 +00:00
2011-10-29 22:40:00 +00:00
# store the access token in the user profile
profile = request.user.profile
profile.goodreads_auth_token = access_token["oauth_token"]
profile.goodreads_auth_secret = access_token["oauth_token_secret"]
2016-08-15 19:22:32 +00:00
# let's get the userid, username
user = gr_client.auth_user()
2016-08-15 19:22:32 +00:00
2011-10-29 22:40:00 +00:00
profile.goodreads_user_id = user["userid"]
profile.goodreads_user_name = user["name"]
profile.goodreads_user_link = user["link"]
2016-08-15 19:22:32 +00:00
2011-10-29 22:40:00 +00:00
profile.save() # is this needed?
# redirect to the Goodreads display page -- should observe some next later
return HttpResponseRedirect(reverse('home'))
@require_POST
@login_required
2016-08-15 19:22:32 +00:00
@csrf_exempt
def goodreads_flush_assoc(request):
2011-10-29 22:40:00 +00:00
user = request.user
if user.is_authenticated():
profile = user.profile
profile.goodreads_user_id = None
profile.goodreads_user_name = None
profile.goodreads_user_link = None
profile.goodreads_auth_token = None
profile.goodreads_auth_secret = None
profile.save()
logger.info('Goodreads association flushed for user %s', user)
return HttpResponseRedirect(reverse('goodreads_display'))
2016-08-15 19:22:32 +00:00
@require_POST
2016-08-15 19:22:32 +00:00
@login_required
@csrf_exempt
def goodreads_load_shelf(request):
"""
a view to allow user load goodreads shelf into her wishlist
"""
# Should be moved to the API
goodreads_shelf_name_number = request.POST.get('goodreads_shelf_name_number', 'all:0')
user = request.user
try:
# parse out shelf name and expected number of books
(shelf_name, expected_number_of_books) = re.match(r'^(.*):(\d+)$', goodreads_shelf_name_number).groups()
expected_number_of_books = int(expected_number_of_books)
logger.info('Adding task to load shelf %s to user %s with %d books', shelf_name, user, expected_number_of_books)
load_task_name = "load_goodreads_shelf_into_wishlist"
load_task = getattr(tasks, load_task_name)
2012-02-16 18:19:36 +00:00
task_id = load_task.delay(user.id, shelf_name, expected_number_of_books=expected_number_of_books)
2016-08-15 19:22:32 +00:00
ct = models.CeleryTask()
ct.task_id = task_id
ct.function_name = load_task_name
ct.user = user
ct.description = "Loading Goodread shelf %s to user %s with %s books" % (shelf_name, user, expected_number_of_books)
ct.save()
2016-08-15 19:22:32 +00:00
return HttpResponse("<span style='margin: auto 10px auto 36px;vertical-align: middle;display: inline-block;'>We're on it! <a href='JavaScript:window.location.reload()'>Reload the page</a> to see the books we've snagged so far.</span>")
2016-08-15 19:22:32 +00:00
except Exception, e:
return HttpResponse("Error in loading shelf: %s " % (e))
logger.info("Error in loading shelf for user %s: %s ", user, e)
@login_required
def goodreads_calc_shelves(request):
# we should move towards calculating this only if needed (perhaps with Ajax), caching previous results, etc to speed up
# performance
2016-08-15 19:22:32 +00:00
if request.user.profile.goodreads_user_id is not None:
gr_client = GoodreadsClient(key=settings.GOODREADS_API_KEY, secret=settings.GOODREADS_API_SECRET)
goodreads_shelves = gr_client.shelves_list(user_id=request.user.profile.goodreads_user_id)
#goodreads_shelf_load_form = GoodreadsShelfLoadingForm()
## load the shelves into the form
#choices = [('all:%d' % (goodreads_shelves["total_book_count"]),'all (%d)' % (goodreads_shelves["total_book_count"]))] + \
2016-08-15 19:22:32 +00:00
# [("%s:%d" % (s["name"], s["book_count"]) ,"%s (%d)" % (s["name"], s["book_count"])) for s in goodreads_shelves["user_shelves"]]
#goodreads_shelf_load_form.fields['goodreads_shelf_name_number'].widget = Select(choices=tuple(choices))
else:
goodreads_shelf_load_form = None
2016-08-15 19:22:32 +00:00
return HttpResponse(json.dumps(goodreads_shelves), content_type="application/json")
2016-08-15 19:22:32 +00:00
@require_POST
2016-08-15 19:22:32 +00:00
@login_required
@csrf_exempt
def librarything_load(request):
"""
a view to allow user load librarything library into her wishlist
"""
# Should be moved to the API
user = request.user
2016-08-15 19:22:32 +00:00
try:
# figure out expected_number_of_books later
2016-08-15 19:22:32 +00:00
lt_username = request.user.profile.librarything_id
2016-08-15 19:22:32 +00:00
logger.info('Adding task to load librarything %s to user %s', lt_username, user)
load_task_name = "load_librarything_into_wishlist"
load_task = getattr(tasks, load_task_name)
2012-02-16 18:19:36 +00:00
task_id = load_task.delay(user.id, lt_username, None)
2016-08-15 19:22:32 +00:00
ct = models.CeleryTask()
ct.task_id = task_id
ct.function_name = load_task_name
ct.user = user
ct.description = "Loading LibraryThing collection of %s to user %s." % (lt_username, user)
ct.save()
2016-08-15 19:22:32 +00:00
return HttpResponse("<span style='margin: auto 10px auto 36px;vertical-align: middle;display: inline-block;'>We're on it! <a href='JavaScript:window.location.reload()'>Reload the page</a> to see the books we've snagged so far.</span>")
2016-08-15 19:22:32 +00:00
except Exception, e:
return HttpResponse("Error in loading LibraryThing library: %s " % (e))
logger.info("Error in loading LibraryThing for user %s: %s ", user, e)
@require_POST
2016-08-15 19:22:32 +00:00
@login_required
2011-11-01 17:41:39 +00:00
@csrf_exempt
def clear_wishlist(request):
2011-11-01 17:41:39 +00:00
try:
request.user.wishlist.works.clear()
logger.info("Wishlist for user %s cleared", request.user)
2011-11-01 17:41:39 +00:00
return HttpResponse('wishlist cleared')
except Exception, e:
logger.info("Error in clearing wishlist for user %s: %s ", request.user, e)
2016-08-15 19:22:32 +00:00
return HttpResponse("Error in clearing wishlist: %s " % (e))
@require_POST
2016-08-15 19:22:32 +00:00
@login_required
def msg(request):
form = MsgForm(data=request.POST)
if form.is_valid():
if not request.user.is_staff and request.user not in form.cleaned_data['work'].last_campaign().managers.all():
2016-08-15 19:22:32 +00:00
logger.warning("unauthorized attempt to send message by %s for %s"% (request.user, form.cleaned_data['work']))
raise Http404
2016-08-15 19:22:32 +00:00
supporter_message.send(sender=request.user, msg=form.cleaned_data["msg"], work=form.cleaned_data["work"], supporter=form.cleaned_data["supporter"])
return HttpResponse("message sent")
else:
logger.info("Invalid form for user %s", request.user)
raise Http404
2016-08-15 19:22:32 +00:00
class LibraryThingView(FormView):
2016-08-15 19:22:32 +00:00
template_name = "librarything.html"
form_class = LibraryThingForm
2016-08-15 19:22:32 +00:00
def get_context_data(self, **kwargs):
context = super(LibraryThingView, self).get_context_data(**kwargs)
form = kwargs['form']
# get the books for the lt_username in the form
2016-08-15 19:22:32 +00:00
lt_username = self.request.GET.get("lt_username", None)
if lt_username is not None:
lt = librarything.LibraryThing(username=lt_username)
context.update({'books':list(lt.parse_user_catalog(view_style=5))})
else:
context.update({'books':None})
2016-08-15 19:22:32 +00:00
return context
2016-08-15 19:22:32 +00:00
def form_valid(self, form):
return super(LibraryThingView, self).form_valid(form)
2016-08-15 19:22:32 +00:00
@require_POST
2016-08-15 19:22:32 +00:00
@login_required
@csrf_exempt
def clear_celery_tasks(request):
try:
request.user.tasks.clear()
logger.info("Celery tasks for user %s cleared", request.user)
return HttpResponse('Celery Tasks List cleared')
except Exception, e:
2016-08-15 19:22:32 +00:00
logger.info("Error in clearing Celery Tasks for user %s: %s ", request.user, e)
return HttpResponse("Error in clearing Celery Tasks: %s " % (e))
def celery_test(request):
return HttpResponse("celery_test")
# routing views that try to redirect to the works page on a 3rd party site
#
2016-08-15 19:22:32 +00:00
# TODO: need to queue up a task to look up IDs if we have to fallback to
# routing based on ISBN or search
def work_librarything(request, work_id):
work = safe_get_work(work_id)
isbn = work.first_isbn_13()
if work.librarything_id:
url = work.librarything_url
elif isbn:
# TODO: do the redirect here and capture the work id?
2017-07-27 14:33:13 +00:00
url = "https://www.librarything.com/isbn/%s" % isbn
else:
term = work.title + " " + work.author()
q = urlencode({'searchtpe': 'work', 'term': term})
2017-07-27 14:33:13 +00:00
url = "https://www.librarything.com/search.php?" + q
return HttpResponseRedirect(url)
def work_openlibrary(request, work_id):
work = safe_get_work(work_id)
isbns = ["ISBN:" + i.value for i in work.identifiers.filter(type='isbn')]
url = None
if work.openlibrary_id:
url = work.openlibrary_url
elif len(isbns) > 0:
isbns = ",".join(isbns)
2017-07-27 14:33:13 +00:00
u = 'https://openlibrary.org/api/books?bibkeys=%s&jscmd=data&format=json' % isbns
try:
j = json.loads(requests.get(u).content)
# as long as there were some matches get the first one and route to it
if len(j.keys()) > 0:
first = j.keys()[0]
2017-07-27 14:33:13 +00:00
url = "https://openlibrary.org" + j[first]['key']
except ValueError:
# fail at openlibrary
2016-08-15 19:22:32 +00:00
logger.warning("failed to get OpenLibrary json at %s" % u)
# fall back to doing a search on openlibrary
if not url:
q = urlencode({'q': work.title + " " + work.author()})
2017-07-27 14:33:13 +00:00
url = "https://openlibrary.org/search?" + q
return HttpResponseRedirect(url)
def work_goodreads(request, work_id):
work = safe_get_work(work_id)
isbn = work.first_isbn_13()
if work.goodreads_id:
url = work.goodreads_url
elif isbn:
2017-07-27 14:33:13 +00:00
url = "https://www.goodreads.com/book/isbn/%s" % isbn
else:
q = urlencode({'query': work.title + " " + work.author()})
2017-07-27 14:33:13 +00:00
url = "https://www.goodreads.com/search?" + q
return HttpResponseRedirect(url)
2011-12-29 01:43:52 +00:00
2012-01-02 14:39:11 +00:00
@login_required
def emailshare(request, action):
2011-12-31 18:49:23 +00:00
if request.method == 'POST':
2016-08-15 19:22:32 +00:00
form = EmailShareForm(request.POST)
2011-12-31 18:49:23 +00:00
if form.is_valid():
subject = form.cleaned_data['subject']
message = form.cleaned_data['message']
2016-08-15 19:22:32 +00:00
sender = '%s via Unglue.it <%s>' % (request.user.username, request.user.email)
2011-12-31 18:49:23 +00:00
recipient = form.cleaned_data['recipient']
send_mail_task.delay(subject, message, sender, [recipient])
2011-12-31 18:49:23 +00:00
try:
next = form.cleaned_data['next']
except:
# if we totally failed to have a next value, we should still redirect somewhere useful
next = 'https://unglue.it'
2011-12-31 18:49:23 +00:00
return HttpResponseRedirect(next)
2016-08-15 19:22:32 +00:00
else:
work = None
status = None
2016-08-15 19:22:32 +00:00
2011-12-31 18:49:23 +00:00
try:
next = request.GET['next']
work_id = next.split('/')[-2]
work_id = int(work_id)
work = models.Work.objects.get(pk=work_id)
2014-02-21 19:12:20 +00:00
if not action:
status = work.last_campaign().status
2011-12-31 18:49:23 +00:00
except:
pass
2016-08-15 19:22:32 +00:00
context = {'request':request, 'work':work, 'site': Site.objects.get_current(), 'action': action}
2014-02-21 19:12:20 +00:00
if work and action :
message = render_to_string('emails/i_just_pledged.txt', context)
subject = "Help me unglue "+work.title
2016-08-15 19:22:32 +00:00
else:
# customize the call to action depending on campaign status
if status == 'ACTIVE':
2014-02-21 19:12:20 +00:00
message = render_to_string('emails/pledge_this.txt', context)
subject = 'Please help me give this book to the world'
elif work:
2014-02-21 19:12:20 +00:00
message = render_to_string('emails/wish_this.txt', context)
subject = 'This is one of my favorite books on Unglue.it'
else:
# for email shares not bound to a campaign or pledge
2014-02-21 19:12:20 +00:00
message = render_to_string('emails/join_me.txt', context)
subject = "Help me give books to the world"
form = EmailShareForm(initial={ 'next':next, 'subject': subject, 'message': message})
2011-12-29 01:43:52 +00:00
2016-08-15 19:22:32 +00:00
return render(request, "emailshare.html", {'form':form})
def ask_rh(request, campaign_id):
try:
campaign = get_object_or_404(models.Campaign, id=campaign_id)
except ValueError:
raise Http404
2016-08-15 19:22:32 +00:00
return feedback(request, recipient=campaign.email, template="ask_rh.html",
message_template="ask_rh.txt",
redirect_url = reverse('work', args=[campaign.work_id]),
2016-08-15 19:22:32 +00:00
extra_context={'campaign':campaign, 'subject':campaign })
2017-11-18 21:34:56 +00:00
def feedback(request, recipient='unglueit@ebookfoundation.org', template='feedback.html', message_template='feedback.txt', extra_context=None, redirect_url=None):
context = extra_context or {}
2016-08-15 19:22:32 +00:00
context['num1'] = randint(0, 10)
context['num2'] = randint(0, 10)
context['answer'] = context['num1'] + context['num2']
2016-08-15 19:22:32 +00:00
if request.method == 'POST':
2016-08-15 19:22:32 +00:00
form = FeedbackForm(request.POST)
if form.is_valid():
context.update(form.cleaned_data)
2016-08-15 19:22:32 +00:00
context['request'] = request
if extra_context:
context.update(extra_context)
2016-08-15 19:22:32 +00:00
message = render_to_string(message_template, context)
send_mail_task.delay(context['subject'], message, context['sender'], [recipient])
if redirect_url:
return HttpResponseRedirect(redirect_url)
else:
2016-08-15 19:22:32 +00:00
return render(request, "thanks.html", context)
else:
context['num1'] = request.POST['num1']
context['num2'] = request.POST['num2']
2016-08-15 19:22:32 +00:00
else:
2012-01-31 15:07:52 +00:00
if request.user.is_authenticated():
2016-08-15 19:22:32 +00:00
context['sender'] = request.user.email
2012-01-31 15:07:52 +00:00
try:
context['page'] = request.GET['page']
2012-01-31 15:07:52 +00:00
except:
context['page'] = '/'
if not context.has_key('subject'):
context['subject'] = "Feedback on page "+context['page']
form = FeedbackForm(initial=context)
context['form'] = form
2016-08-15 19:22:32 +00:00
return render(request, template, context)
2012-02-03 15:22:53 +00:00
def comment(request):
2012-02-23 20:40:45 +00:00
latest_comments = Comment.objects.all().order_by('-submit_date')[:20]
return render(request, "comments.html", {'latest_comments': latest_comments})
2012-04-04 16:15:18 +00:00
def campaign_archive_js(request):
""" proxy for mailchimp js"""
response = HttpResponse()
r = requests.get(settings.CAMPAIGN_ARCHIVE_JS)
response.status_code = r.status_code
response.content = r.content
response["Content-Type"] = "text/javascript"
return response
2012-08-15 13:40:37 +00:00
def lockss(request, work_id):
"""
manifest pages for lockss harvester -- individual works
"""
work = safe_get_work(work_id)
try:
ebooks = work.ebooks().filter(edition__unglued=True)
except:
ebooks = None
authors = work.authors()
2016-08-15 19:22:32 +00:00
return render(request, "lockss.html", {'work':work, 'ebooks':ebooks, 'authors':authors})
2016-08-15 19:22:32 +00:00
def lockss_manifest(request, year):
"""
manifest pages for lockss harvester -- yearly indices
2016-08-15 19:22:32 +00:00
(lockss needs pages listing all books unglued by year, with
programmatically determinable URLs)
"""
year = int(year)
start_date = date(year, 1, 1)
end_date = date(year, 12, 31)
try:
ebooks = models.Edition.objects.filter(unglued=True).filter(created__range=(start_date, end_date))
except:
ebooks = None
2016-08-15 19:22:32 +00:00
return render(request, "lockss_manifest.html", {'ebooks':ebooks, 'year': year})
2013-06-27 17:10:33 +00:00
2014-02-20 03:18:23 +00:00
class DownloadView(PurchaseView):
2016-08-15 19:22:32 +00:00
template_name = "download.html"
form_class = CampaignThanksForm
2014-02-20 03:18:23 +00:00
def show_beg(self):
if not self.campaign or self.campaign.type != THANKS:
return False
elif self.user_license and self.user_license.thanked:
2016-07-25 15:32:04 +00:00
return self.request.GET.has_key('offer_id') or self.request.POST.has_key('offer_id')
elif self.lib_thanked:
return False
elif self.campaign.status != 'ACTIVE':
2016-07-25 15:32:04 +00:00
return self.request.GET.has_key('testmode') or self.request.POST.has_key('testmode')
2016-08-15 19:22:32 +00:00
else:
2014-02-20 03:18:23 +00:00
return True
2016-08-15 19:22:32 +00:00
2014-02-20 03:18:23 +00:00
def form_valid(self, form):
p = PaymentManager()
2016-08-15 19:22:32 +00:00
t, url = p.process_transaction('USD', form.cleaned_data["preapproval_amount"],
host = PAYMENT_HOST_NONE,
campaign = self.campaign,
user = self.request.user,
paymentReason="Unglue.it Contribution for {0}".format(self.campaign.name),
pledge_extra = form.trans_extra,
)
2014-02-20 03:18:23 +00:00
if url:
return HttpResponseRedirect(url)
else:
logger.error("Attempt to produce transaction id {0} failed".format(t.id))
return HttpResponse("Our attempt to set up your contribution failed. We have logged this problem.")
def get_form_kwargs(self):
if self.kwargs.has_key('work'):
self.work = self.kwargs["work"]
2016-08-15 19:22:32 +00:00
self.show_beg = lambda: False
2014-02-20 03:18:23 +00:00
else:
self.work = safe_get_work(self.kwargs["work_id"])
self.campaign = self.work.last_campaign()
self.user_license = self.work.get_user_license(self.request.user)
self.lib_thanked = self.work.lib_thanked(self.request.user)
2014-02-20 03:18:23 +00:00
self.data = {
'preapproval_amount':self.get_preapproval_amount(),
'anonymous':True if self.request.user.is_anonymous() else self.request.user.profile.anon_pref,
}
if self.request.method == 'POST':
self.data.update(self.request.POST.dict())
if not self.request.POST.has_key('anonymous'):
del self.data['anonymous']
return {'data':self.data}
else:
return {'initial':self.data}
def get_context_data(self, **kwargs):
context = super(FormView, self).get_context_data(**kwargs)
# adapt funtion view to class view
2016-08-15 19:22:32 +00:00
work = self.work
2014-02-20 03:18:23 +00:00
request = self.request
site = Site.objects.get_current()
unglued_ebooks = work.ebooks().filter(edition__unglued=True)
other_ebooks = work.ebooks().filter(edition__unglued=False)
xfer_url = kindle_url = None
2016-08-15 19:22:32 +00:00
acq = None
2014-02-20 03:18:23 +00:00
formats = {} # a dict of format name and url
for ebook in work.ebooks().all():
2016-08-15 19:22:32 +00:00
formats[ebook.format] = reverse('download_ebook', args=[ebook.id])
if request.user.is_authenticated():
#add a fave
request.user.wishlist.add_work(work,'download')
2016-08-15 19:22:32 +00:00
all_acqs = request.user.acqs.filter(work=work).order_by('-created')
2014-02-20 03:18:23 +00:00
for an_acq in all_acqs:
if not an_acq.expired:
2014-05-05 14:15:29 +00:00
# skip for THANKS
if an_acq.license == THANKED:
acq = None
break
2014-02-20 03:18:23 +00:00
# prepare this acq for download
if not an_acq.watermarked or an_acq.watermarked.expired:
if not an_acq.on_reserve:
watermark_acq.delay(an_acq)
acq = an_acq
2016-08-15 19:22:32 +00:00
formats['epub'] = reverse('download_acq', kwargs={'nonce':acq.nonce, 'format':'epub'})
formats['mobi'] = reverse('download_acq', kwargs={'nonce':acq.nonce, 'format':'mobi'})
2014-05-20 16:19:34 +00:00
xfer_url = settings.BASE_URL_SECURE + formats['epub']
2014-05-23 02:35:29 +00:00
kindle_url = settings.BASE_URL_SECURE + formats['mobi']
2014-02-20 03:18:23 +00:00
can_kindle = True
break
2016-08-15 19:22:32 +00:00
2014-02-20 03:18:23 +00:00
if not acq:
# google ebooks have a captcha which breaks some of our services
non_google_ebooks = work.ebooks().exclude(provider='Google Books')
2016-08-15 19:22:32 +00:00
#send to kindle
try:
kindle_ebook = non_google_ebooks.filter(format='mobi')[0]
can_kindle = kindle_ebook.kindle_sendable()
except IndexError:
2014-02-20 03:18:23 +00:00
try:
kindle_ebook = non_google_ebooks.filter(format='pdf')[0]
can_kindle = kindle_ebook.kindle_sendable()
2014-02-20 03:18:23 +00:00
except IndexError:
can_kindle = False
2014-05-20 16:19:34 +00:00
# configure the xfer url
2014-02-20 03:18:23 +00:00
try:
2014-05-20 16:19:34 +00:00
xfer_epub_ebook = non_google_ebooks.filter(format='epub')[0]
2016-08-15 19:22:32 +00:00
xfer_url = settings.BASE_URL_SECURE + reverse('download_ebook', args=[xfer_epub_ebook.id])
2014-02-20 03:18:23 +00:00
except:
2014-05-20 16:19:34 +00:00
xfer_url = None
2016-08-15 19:22:32 +00:00
agent = request.META.get('HTTP_USER_AGENT','')
2014-02-20 03:18:23 +00:00
iOS = 'iPad' in agent or 'iPhone' in agent or 'iPod' in agent
iOS_app = iOS and not 'Safari' in agent
android = 'Android' in agent
desktop = not iOS and not android
context.update({
'unglued_ebooks': unglued_ebooks,
'other_ebooks': other_ebooks,
'formats': formats,
2014-05-20 16:19:34 +00:00
'xfer_url': xfer_url,
2014-05-23 02:35:29 +00:00
'kindle_url': kindle_url,
2014-05-20 16:19:34 +00:00
'dropbox_key': settings.DROPBOX_KEY,
2014-02-20 03:18:23 +00:00
'can_kindle': can_kindle,
'base_url': settings.BASE_URL_SECURE,
'iOS': iOS,
'iOS_app': iOS_app,
'iphone': 'iPhone' in agent,
2014-02-20 03:18:23 +00:00
'android': android,
'desktop': desktop,
2015-03-06 03:26:25 +00:00
'mac_ibooks': 'Mac OS X 10.9' in agent or 'Mac OS X 10_9' in agent or 'Mac OS X 10.10' in agent or 'Mac OS X 10_10' in agent,
2014-02-20 03:18:23 +00:00
'acq':acq,
'show_beg': self.show_beg,
'preapproval_amount': self.get_preapproval_amount(),
2016-08-15 19:22:32 +00:00
'work': work,
2014-02-20 03:18:23 +00:00
'site': site,
'action': "Contribution",
'user_license': self.user_license,
'lib_thanked': self.lib_thanked,
2016-05-10 18:27:24 +00:00
'amount': D(self.request.session.pop('amount')/100) if self.request.session.has_key('amount') else None,
2016-07-25 15:32:04 +00:00
'testmode': self.request.GET.has_key('testmode') or self.request.POST.has_key('testmode'),
'source': self.request.GET.get('source', self.request.POST.get('source', '')),
2014-02-20 03:18:23 +00:00
})
return context
2013-10-15 20:18:30 +00:00
2014-07-01 18:09:21 +00:00
@login_required
def feature(request, work_id):
if not request.user.is_staff:
return render(request, "admins_only.html")
else:
work = safe_get_work(work_id)
if work.is_free:
2014-07-01 18:09:21 +00:00
work.featured = now()
work.save()
2016-08-15 19:22:32 +00:00
return HttpResponseRedirect(reverse('landing', args=[]))
2014-07-01 18:09:21 +00:00
else:
return HttpResponse('can\'t feature an work without an ebook')
@login_required
2013-10-15 20:18:30 +00:00
def borrow(request, work_id):
work = safe_get_work(work_id)
library = request.GET.get('library', '')
libuser = None
acq = None
if library:
try:
libuser = User.objects.get(username = library)
except User.DoesNotExist:
libuser = None
if libuser:
acq = work.get_user_license(libuser).borrowable_acq
if not libuser or not acq:
2016-08-15 19:22:32 +00:00
acq = work.get_lib_license(request.user).borrowable_acq
if acq:
borrowed = acq.borrow(request.user)
2014-03-20 13:23:17 +00:00
return DownloadView.as_view()(request, work=work)
else:
# shouldn't happen
return work(request, work_id)
@login_required
def reserve(request, work_id):
work = safe_get_work(work_id)
lib = request.GET.get('library', '')
library = None
try:
library = Library.objects.get(user__username = lib)
except Library.DoesNotExist:
try:
library = work.get_lib_license(request.user).next_acq.library
except:
library = None
2016-08-15 19:22:32 +00:00
models.Hold.objects.get_or_create(library=library, work=work, user=request.user)
return PurchaseView.as_view()(request, work_id=work_id)
def download_ebook(request, ebook_id):
try:
ebook = get_object_or_404(models.Ebook, id=ebook_id)
except ValueError:
raise Http404
ebook.increment()
logger.info("ebook: {0}, user_ip: {1}".format(ebook_id, request.META['REMOTE_ADDR']))
return HttpResponseRedirect(ebook.url)
2013-06-27 17:10:33 +00:00
def download_purchased(request, work_id):
if request.user.is_anonymous():
2013-06-27 17:10:33 +00:00
HttpResponseRedirect('/accounts/login/download/')
2014-04-07 16:17:21 +00:00
return DownloadView.as_view()(request, work_id=work_id)
def download_campaign(request, work_id, format):
work = safe_get_work(work_id)
2016-08-15 19:22:32 +00:00
# Raise 404 unless there is a SUCCESSFUL BUY2UNGLUE campaign associated with work
try:
campaign = work.campaigns.get(status='SUCCESSFUL', type=BUY2UNGLUE)
2017-11-28 01:12:42 +00:00
except models.Campaign.DoesNotExist as e:
raise Http404
2016-08-15 19:22:32 +00:00
ebfs = models.EbookFile.objects.filter(edition__work=campaign.work, format=format).exclude(file='').order_by('-created')
logger.info(ebfs.count())
2016-08-15 19:22:32 +00:00
# return the link to the most recently created EbookFile (if any) with specified format for the campaign
for ebf in ebfs:
logger.info(ebf.file.url)
return HttpResponseRedirect(ebf.file.url)
# if ebfs.count() is 0
raise Http404
def download_acq(request, nonce, format):
2016-08-15 19:22:32 +00:00
acq = get_object_or_404(models.Acq, nonce=nonce)
2013-10-17 02:53:47 +00:00
if acq.on_reserve:
acq.borrow()
if format == 'epub':
2016-08-15 19:22:32 +00:00
return HttpResponseRedirect(acq.get_epub_url())
else:
2016-08-15 19:22:32 +00:00
return HttpResponseRedirect(acq.get_mobi_url())
def about(request, facet):
template = "about_" + facet + ".html"
try:
return render(request, template)
except TemplateDoesNotExist:
2017-11-18 21:35:19 +00:00
return render(request, "about_main.html")
def receive_gift(request, nonce):
try:
gift = models.Gift.objects.get(acq__nonce=nonce)
except models.Gift.DoesNotExist:
2016-08-15 19:22:32 +00:00
return render(request, 'gift_error.html',)
2014-12-18 23:10:04 +00:00
context = {'gift': gift, "site": Site.objects.get_current() }
work = gift.acq.work
context['work'] = work
2014-12-18 16:41:06 +00:00
# put nonce in session so we know that a user has redeemed a Gift
request.session['gift_nonce'] = nonce
if gift.used:
if request.user.is_authenticated():
#check that user hasn't redeemed the gift themselves
if (gift.acq.user_id == request.user.id) and not gift.acq.expired:
2016-08-15 19:22:32 +00:00
return HttpResponseRedirect(reverse('display_gift', args=[gift.id,'existing']))
return render(request, 'gift_error.html', context)
if request.user.is_authenticated():
user_license = work.get_user_license(request.user)
if user_license and user_license.purchased:
# check if previously purchased- there would be two user licenses if so.
2014-12-22 23:08:13 +00:00
if user_license.is_duplicate or request.user.id == gift.giver.id:
# regift
if request.method == 'POST':
2016-08-15 19:22:32 +00:00
form = RegiftForm(data=request.POST)
if form.is_valid():
giftee = models.Gift.giftee(form.cleaned_data['give_to'], request.user.username)
new_acq = models.Acq.objects.create(user=giftee, work=gift.acq.work, license= gift.acq.license)
new_gift = models.Gift.objects.create(acq=new_acq, message=form.cleaned_data['give_message'], giver=request.user , to = form.cleaned_data['give_to'])
context['gift'] = new_gift
gift.acq.expire_in(0)
gift.use()
notification.send([giftee], "purchase_gift", context, True)
return render(request, 'gift_duplicate.html', context)
2016-08-15 19:22:32 +00:00
context['form'] = RegiftForm()
return render(request, 'gift_duplicate.html', context)
else:
# new book!
2016-08-15 19:22:32 +00:00
gift.use()
request.user.wishlist.add_work(gift.acq.work, 'gift')
2016-08-15 19:22:32 +00:00
return HttpResponseRedirect(reverse('display_gift', args=[gift.id,'existing']))
else:
# we'll just leave the old user inactive.
gift.acq.user = request.user
gift.acq.save()
2016-08-15 19:22:32 +00:00
gift.use()
request.user.wishlist.add_work(gift.acq.work, 'gift')
2016-08-15 19:22:32 +00:00
return HttpResponseRedirect(reverse('display_gift', args=[gift.id,'existing']))
2014-12-22 19:21:40 +00:00
if (gift.acq.created - gift.acq.user.date_joined) > timedelta(minutes=1) or gift.used:
# giftee is established user (or gift has been used), ask them to log in
return superlogin(request, extra_context=context, template_name='gift_login.html')
else:
2014-12-22 19:24:49 +00:00
# giftee is a new user, log them in
gift.use()
gift.acq.user.wishlist.add_work(gift.acq.work, 'gift')
login_user(request, gift.acq.user)
2016-08-15 19:22:32 +00:00
return HttpResponseRedirect(reverse('display_gift', args=[gift.id, 'newuser']))
@login_required
def display_gift(request, gift_id, message):
try:
gift = models.Gift.objects.get(id=gift_id)
except models.Gift.DoesNotExist:
2016-08-15 19:22:32 +00:00
return render(request, 'gift_error.html',)
if request.user.id != gift.acq.user_id :
return HttpResponse("this is not your gift")
2014-12-18 16:41:06 +00:00
redeemed_gift = request.session.get('gift_nonce', None) == gift.acq.nonce
context = {'gift': gift, 'work': gift.acq.work , 'message':message }
2014-12-18 16:41:06 +00:00
if request.method == 'POST' and redeemed_gift:
2016-08-15 19:22:32 +00:00
form = UserNamePass(data=request.POST)
2014-12-18 16:41:06 +00:00
form.oldusername = request.user.username
context['form'] = form
if form.is_valid():
request.user.username = form.cleaned_data['username']
request.user.set_password(form.cleaned_data['password1'])
request.user.save()
context.pop('form')
context['passmessage'] = "changed userpass"
return render(request, 'gift_welcome.html', context)
else:
if redeemed_gift:
2016-08-15 19:22:32 +00:00
form = UserNamePass(initial={'username':request.user.username})
2014-12-18 16:41:06 +00:00
form.oldusername = request.user.username
context['form'] = form
return render(request, 'gift_welcome.html', context)
2016-08-15 19:22:32 +00:00
@login_required
@csrf_exempt
def ml_status(request):
return render(request, "ml_status.html")
@require_POST
2016-08-15 19:22:32 +00:00
@login_required
def ml_subscribe(request):
2016-08-15 19:22:32 +00:00
request.user.profile.ml_subscribe(
double_optin=False,
send_welcome=True,
merge_vars = {"OPTIN_IP":request.META['REMOTE_ADDR'], "OPTIN_TIME":now().isoformat()}
)
return HttpResponseRedirect(reverse("notification_notice_settings"))
@require_POST
2016-08-15 19:22:32 +00:00
@login_required
def ml_unsubscribe(request):
request.user.profile.ml_unsubscribe()
return HttpResponseRedirect(reverse("notification_notice_settings"))
2016-08-15 19:22:32 +00:00
def press(request):
2013-04-04 14:15:29 +00:00
latest_items = models.Press.objects.order_by('-date')[:3]
highlighted_items = models.Press.objects.filter(highlight=True).order_by('-date')
all_items = models.Press.objects.exclude(highlight=True).order_by('-date')
2013-04-04 14:15:29 +00:00
return render(request, "press_new.html", {
'latest_items': latest_items,
'highlighted_items': highlighted_items,
'all_items': all_items
})
2016-08-15 19:22:32 +00:00
def press_submitterator(request):
if not request.user.is_staff:
return render(request, "admins_only.html")
else:
title = ''
if request.method == 'POST':
form = PressForm(request.POST)
if form.is_valid():
form.save()
title = form.cleaned_data['title']
else:
form = PressForm()
2016-08-15 19:22:32 +00:00
return render(request, 'press_submitterator.html', {
'form':form,
'title':title
})
2013-05-31 15:34:51 +00:00
2013-05-31 19:19:58 +00:00
@login_required
def kindle_config(request, work_id=None):
2013-09-09 19:48:27 +00:00
if work_id:
work = safe_get_work(work_id)
else:
work = None
template = "kindle_config.html"
2013-05-31 19:19:58 +00:00
if request.method == 'POST':
form = KindleEmailForm(request.POST)
if form.is_valid():
request.user.profile.kindle_email = form.cleaned_data['kindle_email']
request.user.profile.save()
template = "kindle_change_successful.html"
2013-05-31 19:19:58 +00:00
else:
2016-08-15 19:22:32 +00:00
form = KindleEmailForm()
return render(request, template, {
2016-08-15 19:22:32 +00:00
'form': form,
'work': work,
'ok_email': request.user.profile.kindle_email and ('kindle' in request.user.profile.kindle_email),
})
@require_POST
@csrf_exempt
def send_to_kindle(request, work_id, javascript='0'):
# make sure to gracefully communicate with both js and non-js (kindle!) users
def local_response(request, javascript, context, message):
context['message'] = message
if javascript == '1':
2016-08-15 19:22:32 +00:00
return render(request, 'kindle_response_message.html', context)
else:
return render(request, 'kindle_response_graceful_degradation.html', context)
2016-08-15 19:22:32 +00:00
work = safe_get_work(work_id)
context = {'work':work}
acq = None
2016-08-15 19:22:32 +00:00
if request.user.is_authenticated():
all_acqs = request.user.acqs.filter(work=work).order_by('-created')
2014-05-05 16:21:59 +00:00
for an_acq in all_acqs:
if not an_acq.expired:
# skip for THANKS
if an_acq.license == THANKED:
acq = None
break
# prepare this acq for download
if not an_acq.watermarked or an_acq.watermarked.expired:
if not an_acq.on_reserve:
watermark_acq.delay(an_acq)
acq = an_acq
break
2016-08-15 19:22:32 +00:00
if acq:
2015-07-09 19:08:16 +00:00
ebook = acq.ebook()
2015-03-03 22:39:23 +00:00
title = acq.work.kindle_safe_title()
else:
non_google_ebooks = work.ebooks().exclude(provider='Google Books')
try:
ebook = non_google_ebooks.filter(format='mobi')[0]
except IndexError:
try:
ebook = non_google_ebooks.filter(format='pdf')[0]
except IndexError:
raise Http404
2013-06-10 15:57:59 +00:00
# don't forget to increment the download counter!
ebook.increment()
logger.info('ebook: {0}, user_ip: {1}'.format(work_id, request.META['REMOTE_ADDR']))
2015-03-03 22:39:23 +00:00
title = ebook.edition.work.kindle_safe_title()
2015-07-02 02:06:33 +00:00
context['ebook'] = ebook
2013-06-10 15:57:59 +00:00
if request.POST.has_key('kindle_email'):
kindle_email = request.POST['kindle_email']
try:
validate_email(kindle_email)
except ValidationError:
return local_response(request, javascript, context, 3)
2013-06-10 15:57:59 +00:00
request.session['kindle_email'] = kindle_email
elif request.user.is_authenticated():
2016-08-15 19:22:32 +00:00
kindle_email = request.user.profile.kindle_email
context['kindle_email'] = kindle_email
2016-08-15 19:22:32 +00:00
2013-07-05 12:43:02 +00:00
"""
2017-07-27 14:33:13 +00:00
Amazon SES has a 10 MB size limit (https://aws.amazon.com/ses/faqs/#49) in messages sent
2013-07-05 12:43:02 +00:00
to determine whether the file will meet this limit, we probably need to compare the
size of the mime-encoded file to 10 MB. (and it's unclear exactly what the Amazon FAQ means precisely by
2017-07-27 14:33:13 +00:00
MB either: https://en.wikipedia.org/wiki/Megabyte) http://www.velocityreviews.com/forums/t335208-how-to-get-size-of-email-attachment.html might help
2013-07-05 12:43:02 +00:00
for the moment, we will hardwire a 749229 limit in filesize:
* assume conservative size of megabyte, 1000000B
* leave 1KB for headers
* mime encoding will add 33% to filesize
This won't perfectly measure size of email, but should be safe, and is much faster than doing the check after download.
"""
2015-07-02 02:07:04 +00:00
try:
filehandle = ebook.get_archive()
2015-07-02 02:07:04 +00:00
except IOError:
# problems connection to the ebook source
logger.error("couldn't connect error: %s", ebook.url)
return local_response(request, javascript, context, 5)
2015-07-02 02:06:33 +00:00
if not ebook.filesize:
return local_response(request, javascript, context, 4)
2015-07-02 02:06:33 +00:00
if ebook.filesize > models.send_to_kindle_limit:
logger.info('ebook %s is too large to be emailed' % work.id)
return local_response(request, javascript, context, 0)
2016-08-15 19:22:32 +00:00
2013-06-03 21:18:42 +00:00
try:
2013-06-24 19:42:51 +00:00
email = EmailMessage(from_email='notices@gluejar.com',
to=[kindle_email])
2015-07-02 02:06:33 +00:00
email.attach(title + '.' + ebook.format, filehandle.read())
2013-06-03 21:18:42 +00:00
email.send()
except:
2015-06-03 03:45:30 +00:00
logger.error('Unexpected error: %s', sys.exc_info())
return local_response(request, javascript, context, 1)
2013-06-10 15:57:59 +00:00
if request.POST.has_key('kindle_email') and not request.user.is_authenticated():
return HttpResponseRedirect(reverse('superlogin'))
return local_response(request, javascript, context, 2)
2016-08-15 19:22:32 +00:00
2014-10-27 15:55:46 +00:00
def userlist_marc(request, userlist=None):
if userlist:
2016-08-15 19:22:32 +00:00
user = get_object_or_404(User, username=userlist)
2014-10-27 15:55:46 +00:00
return qs_marc_records(request, qs=user.wishlist.works.all())
else:
2014-10-27 15:55:46 +00:00
return qs_marc_records(request, qs=request.user.wishlist.works.all())
2016-08-15 19:22:32 +00:00
return render(request, 'marc.html', {'userlist' : [] })
2014-10-27 15:55:46 +00:00
def work_marc(request, work_id):
work = safe_get_work(work_id)
return qs_marc_records(request, qs=[ work ])
2016-08-15 19:22:32 +00:00
2014-10-27 23:11:44 +00:00
class LibModeView(FormView):
2013-07-23 16:23:04 +00:00
template_name = 'marc_config.html'
2014-10-27 23:11:44 +00:00
form_class = LibModeForm
2013-07-23 16:23:04 +00:00
success_url = reverse_lazy('marc')
2016-08-15 19:22:32 +00:00
2013-07-23 16:23:04 +00:00
def form_valid(self, form):
2016-08-15 19:22:32 +00:00
enable = form.data.has_key('enable')
2014-10-27 15:55:46 +00:00
if enable:
try:
libpref = self.request.user.libpref
except:
libpref = models.Libpref(user=self.request.user)
libpref.save()
messages.success(self.request,"Tools are enabled.")
else:
try:
self.request.user.libpref.delete()
except:
pass
2016-08-15 19:22:32 +00:00
messages.success(self.request,"Tools are disabled.")
2013-07-23 16:23:04 +00:00
if reverse('marc_config', args=[]) in self.request.META['HTTP_REFERER']:
return HttpResponseRedirect(reverse('marc_config', args=[]))
else:
2014-10-27 23:11:44 +00:00
return super(LibModeView, self).form_valid(form)
2016-08-15 19:22:32 +00:00