Merge newfoundation
commit
fc1d12c5d5
|
@ -13,5 +13,4 @@ logs/*
|
|||
celerybeat.pid
|
||||
celerybeat-schedule
|
||||
.gitignore~
|
||||
static/scss/*.css
|
||||
static/scss/*.css.map
|
||||
|
|
|
@ -41,11 +41,13 @@ to install python-setuptools in step 1:
|
|||
1. `django-admin.py celeryd --loglevel=INFO` start the celery daemon to perform asynchronous tasks like adding related editions, and display logging information in the foreground.
|
||||
1. `django-admin.py celerybeat -l INFO` to start the celerybeat daemon to handle scheduled tasks.
|
||||
1. `django-admin.py runserver 0.0.0.0:8000` (you can change the port number from the default value of 8000)
|
||||
1. make sure a [redis server](https://redis.io/topics/quickstart) is running
|
||||
1. Point your browser to http://localhost:8000/
|
||||
|
||||
CSS development
|
||||
|
||||
1. We are using Less version 2.8 for CSS. http://incident57.com/less/. We use minified CSS.
|
||||
1. We used Less version 2.8 for CSS. http://incident57.com/less/. We use minified CSS.
|
||||
1. New CSS development is using SCSS. Install libsass and django-compressor.
|
||||
|
||||
Production Deployment
|
||||
---------------------
|
||||
|
|
|
@ -42,7 +42,7 @@ def header(facet=None):
|
|||
header_node = etree.Element("Header")
|
||||
sender_node = etree.Element("Sender")
|
||||
sender_node.append(text_node("SenderName", "unglue.it"))
|
||||
sender_node.append(text_node("EmailAddress", "support@gluejar.com"))
|
||||
sender_node.append(text_node("EmailAddress", "unglueit@ebookfoundation.org"))
|
||||
header_node.append(sender_node)
|
||||
header_node.append(text_node("SentDateTime", pytz.utc.localize(datetime.datetime.utcnow()).strftime('%Y%m%dT%H%M%SZ')))
|
||||
header_node.append(text_node("MessageNote", facet.title if facet else "Unglue.it Editions"))
|
||||
|
|
|
@ -76,6 +76,20 @@ XML: <a href="/api/v1/identifier/?format=xml&api_key={{api_key}}&usernam
|
|||
|
||||
<h3>OPDS Catalog Feeds</h3>
|
||||
<p>We have a basic implementation of <a href="http://opds-spec.org/specs/opds-catalog-1-1-20110627/">OPDS</a> feeds. You don't need a key to use them. The starting point is <code><a href="{% url 'opds' %}">{{base_url}}{% url 'opds' %}</a></code></p>
|
||||
<p>
|
||||
Examples:
|
||||
<dl>
|
||||
<dt>filtered by format</dt>
|
||||
<dd><code><a href="{% url 'opds_acqusition' 'epub' %}">{{base_url}}{% url 'opds_acqusition' 'epub' %}</a></code></dd>
|
||||
<dt>filtered by license</dt>
|
||||
<dd><code><a href="{% url 'opds_acqusition' 'by-sa' %}">{{base_url}}{% url 'opds_acqusition' 'by-sa' %}</a></code></dd>
|
||||
<dt>filtered by title search</dt>
|
||||
<dd><code><a href="{% url 'opds_acqusition' 's.open' %}">{{base_url}}{% url 'opds_acqusition' 's.open' %}</a></code></dd>
|
||||
<dt>filtered by keyword</dt>
|
||||
<dd><code><a href="{% url 'opds_acqusition' 'kw.fiction' %}">{{base_url}}{% url 'opds_acqusition' 'kw.fiction' %}</a></code></dd>
|
||||
<dt>filtered by ungluer</dt>
|
||||
<dd><code><a href="{% url 'opds_acqusition' '@eric' %}">{{base_url}}{% url 'opds_acqusition' '@eric' %}</a></code></dd>
|
||||
</p>
|
||||
<p>There's also an OPDS record available for every work on unglue.it. For example, requesting, <code><a href="{% url 'opds_acqusition' 'all'%}?work=13950">{{base_url}}{% url 'opds_acqusition' 'all'%}?work=13950</a></code> get you to the web page or opds record for <i>A Christmas Carol</i>.</p>
|
||||
|
||||
<h3>ONIX Catalog Feeds</h3>
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<!DOCTYPE html>
|
||||
<!DOCTYPE html>{% load sass_tags %}
|
||||
<html>
|
||||
<head>
|
||||
<title>unglue.it: {{work.title}}</title>
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/sitewide4.css" />
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/book_panel2.css" />
|
||||
<link type="text/css" rel="stylesheet" href="{% sass_src 'scss/sitewide4.scss' %}" />
|
||||
<link type="text/css" rel="stylesheet" href="{% sass_src 'scss/book_panel2.scss' %}" />
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
|
|
|
@ -7,7 +7,7 @@ from django.contrib import auth
|
|||
from django.contrib.auth.models import User
|
||||
from django.contrib.sites.models import Site
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.shortcuts import render, render_to_response, get_object_or_404
|
||||
from django.shortcuts import render, render_to_response
|
||||
from django.template import RequestContext
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from django.views.generic.base import View, TemplateView
|
||||
|
|
148
core/admin.py
148
core/admin.py
|
@ -1,11 +1,10 @@
|
|||
"""
|
||||
django imports
|
||||
"""
|
||||
#
|
||||
#django imports
|
||||
#
|
||||
from django import forms
|
||||
from django.contrib.admin import ModelAdmin
|
||||
from django.contrib.admin import site as admin_site
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.admin import ModelAdmin, register
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
from selectable.forms import (
|
||||
AutoCompleteSelectWidget,
|
||||
AutoCompleteSelectField,
|
||||
|
@ -13,18 +12,17 @@ from selectable.forms import (
|
|||
AutoCompleteSelectMultipleField,
|
||||
)
|
||||
|
||||
"""
|
||||
regluit imports
|
||||
"""
|
||||
from . import models
|
||||
from regluit.core.lookups import (
|
||||
#
|
||||
# regluit imports
|
||||
#"""
|
||||
from .lookups import (
|
||||
PublisherNameLookup,
|
||||
WorkLookup,
|
||||
OwnerLookup,
|
||||
EditionLookup
|
||||
EditionLookup,
|
||||
EbookLookup,
|
||||
)
|
||||
|
||||
|
||||
from . import models
|
||||
|
||||
class ClaimAdminForm(forms.ModelForm):
|
||||
work = AutoCompleteSelectField(
|
||||
|
@ -41,6 +39,7 @@ class ClaimAdminForm(forms.ModelForm):
|
|||
model = models.Claim
|
||||
exclude = ()
|
||||
|
||||
@register(models.Claim)
|
||||
class ClaimAdmin(ModelAdmin):
|
||||
list_display = ('work', 'rights_holder', 'status')
|
||||
date_hierarchy = 'created'
|
||||
|
@ -56,36 +55,44 @@ class RightsHolderAdminForm(forms.ModelForm):
|
|||
model = models.RightsHolder
|
||||
exclude = ()
|
||||
|
||||
@register(models.RightsHolder)
|
||||
class RightsHolderAdmin(ModelAdmin):
|
||||
date_hierarchy = 'created'
|
||||
form = RightsHolderAdminForm
|
||||
|
||||
@register(models.Acq)
|
||||
class AcqAdmin(ModelAdmin):
|
||||
readonly_fields = ('work', 'user', 'lib_acq', 'watermarked')
|
||||
search_fields = ['user__username']
|
||||
date_hierarchy = 'created'
|
||||
|
||||
@register(models.Premium)
|
||||
class PremiumAdmin(ModelAdmin):
|
||||
list_display = ('campaign', 'amount', 'description')
|
||||
date_hierarchy = 'created'
|
||||
fields = ('type', 'amount', 'description', 'limit')
|
||||
|
||||
class CampaignAdminForm(forms.ModelForm):
|
||||
managers = AutoCompleteSelectMultipleField(
|
||||
lookup_class=OwnerLookup,
|
||||
widget= AutoCompleteSelectMultipleWidget(lookup_class=OwnerLookup),
|
||||
widget=AutoCompleteSelectMultipleWidget(lookup_class=OwnerLookup),
|
||||
required=True,
|
||||
)
|
||||
class Meta(object):
|
||||
model = models.Campaign
|
||||
fields = ('managers', 'name', 'description', 'details', 'license', 'activated', 'paypal_receiver',
|
||||
'status', 'type', 'email', 'do_watermark', 'use_add_ask', )
|
||||
fields = (
|
||||
'managers', 'name', 'description', 'details', 'license', 'paypal_receiver',
|
||||
'status', 'type', 'email', 'do_watermark', 'use_add_ask', 'charitable',
|
||||
)
|
||||
|
||||
@register(models.Campaign)
|
||||
class CampaignAdmin(ModelAdmin):
|
||||
list_display = ('work', 'created', 'status')
|
||||
date_hierarchy = 'created'
|
||||
search_fields = ['work']
|
||||
form = CampaignAdminForm
|
||||
|
||||
@register(models.Work)
|
||||
class WorkAdmin(ModelAdmin):
|
||||
search_fields = ['title']
|
||||
ordering = ('title',)
|
||||
|
@ -94,20 +101,27 @@ class WorkAdmin(ModelAdmin):
|
|||
fields = ('title', 'description', 'language', 'featured', 'publication_range',
|
||||
'age_level', 'openlibrary_lookup')
|
||||
|
||||
@register(models.Author)
|
||||
class AuthorAdmin(ModelAdmin):
|
||||
search_fields = ('name',)
|
||||
date_hierarchy = 'created'
|
||||
ordering = ('name',)
|
||||
exclude = ('editions',)
|
||||
|
||||
subject_authorities = (('','keywords'),('lcsh', 'LC subjects'), ('lcc', 'LC classifications'), ('bisacsh', 'BISAC heading'), )
|
||||
subject_authorities = (
|
||||
('', 'keywords'),
|
||||
('lcsh', 'LC subjects'),
|
||||
('lcc', 'LC classifications'),
|
||||
('bisacsh', 'BISAC heading'),
|
||||
)
|
||||
|
||||
class SubjectAdminForm(forms.ModelForm):
|
||||
authority = forms.ChoiceField(choices=subject_authorities, required=False )
|
||||
authority = forms.ChoiceField(choices=subject_authorities, required=False)
|
||||
class Meta(object):
|
||||
model = models.Subject
|
||||
fields = 'name', 'authority', 'is_visible'
|
||||
|
||||
|
||||
@register(models.Subject)
|
||||
class SubjectAdmin(ModelAdmin):
|
||||
search_fields = ('name',)
|
||||
date_hierarchy = 'created'
|
||||
|
@ -131,6 +145,7 @@ class EditionAdminForm(forms.ModelForm):
|
|||
model = models.Edition
|
||||
exclude = ()
|
||||
|
||||
@register(models.Edition)
|
||||
class EditionAdmin(ModelAdmin):
|
||||
list_display = ('title', 'publisher_name', 'created')
|
||||
date_hierarchy = 'created'
|
||||
|
@ -149,69 +164,107 @@ class PublisherAdminForm(forms.ModelForm):
|
|||
model = models.Publisher
|
||||
exclude = ()
|
||||
|
||||
@register(models.Publisher)
|
||||
class PublisherAdmin(ModelAdmin):
|
||||
list_display = ('name', 'url', 'logo_url', 'description')
|
||||
ordering = ('name',)
|
||||
form = PublisherAdminForm
|
||||
|
||||
@register(models.PublisherName)
|
||||
class PublisherNameAdmin(ModelAdmin):
|
||||
list_display = ('name', 'publisher')
|
||||
ordering = ('name',)
|
||||
search_fields = ['name']
|
||||
|
||||
@register(models.Relation)
|
||||
class RelationAdmin(ModelAdmin):
|
||||
list_display = ('code', 'name')
|
||||
search_fields = ['name']
|
||||
|
||||
class EbookAdminForm(forms.ModelForm):
|
||||
edition = AutoCompleteSelectField(
|
||||
lookup_class=EditionLookup,
|
||||
label='Edition',
|
||||
widget=AutoCompleteSelectWidget(lookup_class=EditionLookup, attrs={'size':60}),
|
||||
required=True,
|
||||
)
|
||||
class Meta(object):
|
||||
model = models.Ebook
|
||||
exclude = ('user', 'filesize', 'download_count')
|
||||
|
||||
@register(models.Ebook)
|
||||
class EbookAdmin(ModelAdmin):
|
||||
search_fields = ('edition__title','^url') # search by provider using leading url
|
||||
list_display = ('__unicode__','created', 'user','edition')
|
||||
form = EbookAdminForm
|
||||
search_fields = ('edition__title', '^url') # search by provider using leading url
|
||||
list_display = ('__unicode__', 'created', 'user', 'edition')
|
||||
date_hierarchy = 'created'
|
||||
ordering = ('edition__title',)
|
||||
exclude = ('edition','user', 'filesize')
|
||||
readonly_fields = ('user', 'filesize', 'download_count')
|
||||
|
||||
class EbookFileAdminForm(forms.ModelForm):
|
||||
edition = AutoCompleteSelectField(
|
||||
lookup_class=EditionLookup,
|
||||
label='Edition',
|
||||
widget=AutoCompleteSelectWidget(lookup_class=EditionLookup, attrs={'size':60}),
|
||||
required=True,
|
||||
)
|
||||
ebook = AutoCompleteSelectField(
|
||||
lookup_class=EbookLookup,
|
||||
label='Ebook',
|
||||
widget=AutoCompleteSelectWidget(lookup_class=EbookLookup, attrs={'size':60}),
|
||||
required=False,
|
||||
)
|
||||
class Meta(object):
|
||||
model = models.EbookFile
|
||||
fields = ('file', 'format', 'edition', 'ebook', 'source')
|
||||
|
||||
@register(models.EbookFile)
|
||||
class EbookFileAdmin(ModelAdmin):
|
||||
form = EbookFileAdminForm
|
||||
search_fields = ('ebook__edition__title', 'source') # search by provider using leading url
|
||||
list_display = ('created', 'format', 'ebook_link', 'asking')
|
||||
date_hierarchy = 'created'
|
||||
ordering = ('edition__work',)
|
||||
fields = ('file', 'format', 'edition_link', 'ebook_link', 'source')
|
||||
readonly_fields = ('file', 'edition_link', 'ebook_link', 'ebook')
|
||||
fields = ('file', 'format', 'edition', 'edition_link', 'ebook', 'ebook_link', 'source')
|
||||
readonly_fields = ('file', 'edition_link', 'ebook_link',)
|
||||
def edition_link(self, obj):
|
||||
if obj.edition:
|
||||
link = reverse("admin:core_edition_change", args=[obj.edition_id])
|
||||
return u'<a href="%s">%s</a>' % (link,obj.edition)
|
||||
else:
|
||||
return u'<a href="%s">%s</a>' % (link, obj.edition)
|
||||
return u''
|
||||
def ebook_link(self, obj):
|
||||
if obj.ebook:
|
||||
link = reverse("admin:core_ebook_change", args=[obj.ebook_id])
|
||||
return u'<a href="%s">%s</a>' % (link,obj.ebook)
|
||||
else:
|
||||
return u'<a href="%s">%s</a>' % (link, obj.ebook)
|
||||
return u''
|
||||
edition_link.allow_tags=True
|
||||
ebook_link.allow_tags=True
|
||||
edition_link.allow_tags = True
|
||||
ebook_link.allow_tags = True
|
||||
|
||||
|
||||
@register(models.Wishlist)
|
||||
class WishlistAdmin(ModelAdmin):
|
||||
date_hierarchy = 'created'
|
||||
|
||||
@register(models.UserProfile)
|
||||
class UserProfileAdmin(ModelAdmin):
|
||||
search_fields = ('user__username',)
|
||||
date_hierarchy = 'created'
|
||||
exclude = ('user',)
|
||||
|
||||
@register(models.Gift)
|
||||
class GiftAdmin(ModelAdmin):
|
||||
list_display = ('to', 'acq_admin_link', 'giver', )
|
||||
list_display = ('to', 'acq_admin_link', 'giver',)
|
||||
search_fields = ('giver__username', 'to')
|
||||
readonly_fields = ('giver', 'acq',)
|
||||
def acq_admin_link(self, gift):
|
||||
return "<a href='/admin/core/acq/%s/'>%s</a>" % (gift.acq_id, gift.acq)
|
||||
acq_admin_link.allow_tags = True
|
||||
|
||||
@register(models.CeleryTask)
|
||||
class CeleryTaskAdmin(ModelAdmin):
|
||||
pass
|
||||
|
||||
@register(models.Press)
|
||||
class PressAdmin(ModelAdmin):
|
||||
list_display = ('title', 'source', 'date')
|
||||
ordering = ('-date',)
|
||||
|
@ -233,6 +286,7 @@ class WorkRelationAdminForm(forms.ModelForm):
|
|||
model = models.WorkRelation
|
||||
exclude = ()
|
||||
|
||||
@register(models.WorkRelation)
|
||||
class WorkRelationAdmin(ModelAdmin):
|
||||
form = WorkRelationAdminForm
|
||||
list_display = ('to_work', 'relation', 'from_work')
|
||||
|
@ -248,44 +302,20 @@ class IdentifierAdminForm(forms.ModelForm):
|
|||
lookup_class=EditionLookup,
|
||||
label='Edition',
|
||||
widget=AutoCompleteSelectWidget(lookup_class=EditionLookup, attrs={'size':60}),
|
||||
required=True,
|
||||
required=False,
|
||||
)
|
||||
class Meta(object):
|
||||
model = models.Identifier
|
||||
exclude = ()
|
||||
|
||||
@register(models.Identifier)
|
||||
class IdentifierAdmin(ModelAdmin):
|
||||
form = IdentifierAdminForm
|
||||
list_display = ('type', 'value')
|
||||
search_fields = ('type', 'value')
|
||||
|
||||
@register(models.Offer)
|
||||
class OfferAdmin(ModelAdmin):
|
||||
list_display = ('work', 'license', 'price', 'active')
|
||||
search_fields = ('work__title',)
|
||||
readonly_fields = ('work',)
|
||||
|
||||
|
||||
admin_site.register(models.Acq, AcqAdmin)
|
||||
admin_site.register(models.Author, AuthorAdmin)
|
||||
admin_site.register(models.Badge, ModelAdmin)
|
||||
admin_site.register(models.Campaign, CampaignAdmin)
|
||||
admin_site.register(models.CeleryTask, CeleryTaskAdmin)
|
||||
admin_site.register(models.Claim, ClaimAdmin)
|
||||
admin_site.register(models.Ebook, EbookAdmin)
|
||||
admin_site.register(models.EbookFile, EbookFileAdmin)
|
||||
admin_site.register(models.Edition, EditionAdmin)
|
||||
admin_site.register(models.Gift, GiftAdmin)
|
||||
admin_site.register(models.Identifier, IdentifierAdmin)
|
||||
admin_site.register(models.Offer, OfferAdmin)
|
||||
admin_site.register(models.Premium, PremiumAdmin)
|
||||
admin_site.register(models.Press, PressAdmin)
|
||||
admin_site.register(models.Publisher, PublisherAdmin)
|
||||
admin_site.register(models.PublisherName, PublisherNameAdmin)
|
||||
admin_site.register(models.Relation, RelationAdmin)
|
||||
admin_site.register(models.RightsHolder, RightsHolderAdmin)
|
||||
admin_site.register(models.Subject, SubjectAdmin)
|
||||
admin_site.register(models.UserProfile, UserProfileAdmin)
|
||||
admin_site.register(models.Wishlist, WishlistAdmin)
|
||||
admin_site.register(models.Work, WorkAdmin)
|
||||
admin_site.register(models.WorkRelation, WorkRelationAdmin)
|
||||
|
||||
|
|
|
@ -4,24 +4,22 @@ external library imports
|
|||
import json
|
||||
import logging
|
||||
import re
|
||||
import requests
|
||||
from django.core.files.base import ContentFile
|
||||
from django.core.files.storage import default_storage
|
||||
from regluit.core.validation import test_file
|
||||
|
||||
from datetime import timedelta
|
||||
from xml.etree import ElementTree
|
||||
from urlparse import (urljoin, urlparse)
|
||||
|
||||
import requests
|
||||
|
||||
|
||||
# django imports
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.files.base import ContentFile
|
||||
from django_comments.models import Comment
|
||||
from django.core.files.storage import default_storage
|
||||
from django.db import IntegrityError
|
||||
from django.forms import ValidationError
|
||||
|
||||
from django_comments.models import Comment
|
||||
from github3 import (login, GitHub)
|
||||
from github3.repos.release import Release
|
||||
|
||||
|
@ -31,6 +29,7 @@ from gitenberg.metadata.pandata import Pandata
|
|||
|
||||
import regluit
|
||||
import regluit.core.isbn
|
||||
from regluit.core.validation import test_file
|
||||
from regluit.marc.models import inverse_marc_rels
|
||||
from regluit.utils.localdatetime import now
|
||||
|
||||
|
@ -38,7 +37,6 @@ from . import cc
|
|||
from . import models
|
||||
from .parameters import WORK_IDENTIFIERS
|
||||
from .validation import identifier_cleaner, unreverse_name
|
||||
from .loaders.scrape import get_scraper, scrape_sitemap
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
request_log = logging.getLogger("requests")
|
||||
|
@ -51,7 +49,7 @@ def add_by_oclc(isbn, work=None):
|
|||
|
||||
def add_by_oclc_from_google(oclc):
|
||||
if oclc:
|
||||
logger.info("adding book by oclc %s" , oclc)
|
||||
logger.info("adding book by oclc %s", oclc)
|
||||
else:
|
||||
return None
|
||||
try:
|
||||
|
@ -63,8 +61,8 @@ def add_by_oclc_from_google(oclc):
|
|||
except LookupFailure, e:
|
||||
logger.exception("lookup failure for %s", oclc)
|
||||
return None
|
||||
if not results.has_key('items') or len(results['items']) == 0:
|
||||
logger.warn("no google hits for %s" , oclc)
|
||||
if not results.has_key('items') or not results['items']:
|
||||
logger.warn("no google hits for %s", oclc)
|
||||
return None
|
||||
|
||||
try:
|
||||
|
@ -133,10 +131,9 @@ def get_google_isbn_results(isbn):
|
|||
except LookupFailure:
|
||||
logger.exception("lookup failure for %s", isbn)
|
||||
return None
|
||||
if not results.has_key('items') or len(results['items']) == 0:
|
||||
logger.warn("no google hits for %s" , isbn)
|
||||
if not results.has_key('items') or not results['items']:
|
||||
logger.warn("no google hits for %s", isbn)
|
||||
return None
|
||||
else:
|
||||
return results
|
||||
|
||||
def add_ebooks(item, edition):
|
||||
|
@ -175,7 +172,8 @@ def update_edition(edition):
|
|||
except (models.Identifier.DoesNotExist, IndexError):
|
||||
return edition
|
||||
|
||||
# do a Google Books lookup on the isbn associated with the edition (there should be either 0 or 1 isbns associated
|
||||
# do a Google Books lookup on the isbn associated with the edition
|
||||
# (there should be either 0 or 1 isbns associated
|
||||
# with an edition because of integrity constraint in Identifier)
|
||||
|
||||
# if we get some data about this isbn back from Google, update the edition data accordingly
|
||||
|
@ -189,8 +187,9 @@ def update_edition(edition):
|
|||
title = d['title']
|
||||
else:
|
||||
title = ''
|
||||
if len(title) == 0:
|
||||
# need a title to make an edition record; some crap records in GB. use title from parent if available
|
||||
if not title:
|
||||
# need a title to make an edition record; some crap records in GB.
|
||||
# use title from parent if available
|
||||
title = edition.work.title
|
||||
|
||||
# check for language change
|
||||
|
@ -199,9 +198,11 @@ def update_edition(edition):
|
|||
if len(language) > 5:
|
||||
language = language[0:5]
|
||||
|
||||
# if the language of the edition no longer matches that of the parent work, attach edition to the
|
||||
# if the language of the edition no longer matches that of the parent work,
|
||||
# attach edition to the
|
||||
if edition.work.language != language:
|
||||
logger.info("reconnecting %s since it is %s instead of %s" %(googlebooks_id, language, edition.work.language))
|
||||
logger.info("reconnecting %s since it is %s instead of %s",
|
||||
googlebooks_id, language, edition.work.language)
|
||||
old_work = edition.work
|
||||
|
||||
new_work = models.Work(title=title, language=language)
|
||||
|
@ -209,10 +210,10 @@ def update_edition(edition):
|
|||
edition.work = new_work
|
||||
edition.save()
|
||||
for identifier in edition.identifiers.all():
|
||||
logger.info("moving identifier %s" % identifier.value)
|
||||
logger.info("moving identifier %s", identifier.value)
|
||||
identifier.work = new_work
|
||||
identifier.save()
|
||||
if old_work and old_work.editions.count()==0:
|
||||
if old_work and old_work.editions.count() == 0:
|
||||
#a dangling work; make sure nothing else is attached!
|
||||
merge_works(new_work, old_work)
|
||||
|
||||
|
@ -223,7 +224,12 @@ def update_edition(edition):
|
|||
edition.save()
|
||||
|
||||
# create identifier if needed
|
||||
models.Identifier.get_or_add(type='goog', value=googlebooks_id, edition=edition, work=edition.work)
|
||||
models.Identifier.get_or_add(
|
||||
type='goog',
|
||||
value=googlebooks_id,
|
||||
edition=edition,
|
||||
work=edition.work
|
||||
)
|
||||
|
||||
for a in d.get('authors', []):
|
||||
edition.add_author(a)
|
||||
|
@ -240,7 +246,7 @@ def add_by_isbn_from_google(isbn, work=None):
|
|||
"""
|
||||
if not isbn:
|
||||
return None
|
||||
if len(isbn)==10:
|
||||
if len(isbn) == 10:
|
||||
isbn = regluit.core.isbn.convert_10_to_13(isbn)
|
||||
|
||||
|
||||
|
@ -254,13 +260,17 @@ def add_by_isbn_from_google(isbn, work=None):
|
|||
results = get_google_isbn_results(isbn)
|
||||
if results:
|
||||
try:
|
||||
return add_by_googlebooks_id(results['items'][0]['id'], work=work, results=results['items'][0], isbn=isbn)
|
||||
return add_by_googlebooks_id(
|
||||
results['items'][0]['id'],
|
||||
work=work,
|
||||
results=results['items'][0],
|
||||
isbn=isbn
|
||||
)
|
||||
except LookupFailure, e:
|
||||
logger.exception("failed to add edition for %s", isbn)
|
||||
except IntegrityError, e:
|
||||
logger.exception("google books data for %s didn't fit our db", isbn)
|
||||
return None
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_work_by_id(type, value):
|
||||
|
@ -296,7 +306,12 @@ def add_by_googlebooks_id(googlebooks_id, work=None, results=None, isbn=None):
|
|||
models.Identifier.objects.get(type='isbn', value=isbn).edition
|
||||
# not going to worry about isbn_edition != edition
|
||||
except models.Identifier.DoesNotExist:
|
||||
models.Identifier.objects.create(type='isbn', value=isbn, edition=edition, work=edition.work)
|
||||
models.Identifier.objects.create(
|
||||
type='isbn',
|
||||
value=isbn,
|
||||
edition=edition,
|
||||
work=edition.work
|
||||
)
|
||||
return edition
|
||||
except models.Identifier.DoesNotExist:
|
||||
pass
|
||||
|
@ -314,8 +329,9 @@ def add_by_googlebooks_id(googlebooks_id, work=None, results=None, isbn=None):
|
|||
title = d['title']
|
||||
else:
|
||||
title = ''
|
||||
if len(title)==0:
|
||||
# need a title to make an edition record; some crap records in GB. use title from parent if available
|
||||
if not title:
|
||||
# need a title to make an edition record; some crap records in GB.
|
||||
# use title from parent if available
|
||||
if work:
|
||||
title = work.title
|
||||
else:
|
||||
|
@ -327,8 +343,8 @@ def add_by_googlebooks_id(googlebooks_id, work=None, results=None, isbn=None):
|
|||
if len(language) > 5:
|
||||
language = language[0:5]
|
||||
if work and work.language != language:
|
||||
logger.info("not connecting %s since it is %s instead of %s" %
|
||||
(googlebooks_id, language, work.language))
|
||||
logger.info("not connecting %s since it is %s instead of %s",
|
||||
googlebooks_id, language, work.language)
|
||||
work = None
|
||||
# isbn = None
|
||||
if not isbn:
|
||||
|
@ -355,7 +371,7 @@ def add_by_googlebooks_id(googlebooks_id, work=None, results=None, isbn=None):
|
|||
try:
|
||||
e = models.Identifier.objects.get(type='goog', value=googlebooks_id).edition
|
||||
e.new = False
|
||||
logger.warning( " whoa nellie, somebody else created an edition while we were working.")
|
||||
logger.warning(" whoa nellie, somebody else created an edition while we were working.")
|
||||
if work.new:
|
||||
work.delete()
|
||||
return e
|
||||
|
@ -385,8 +401,8 @@ def add_by_googlebooks_id(googlebooks_id, work=None, results=None, isbn=None):
|
|||
|
||||
|
||||
def relate_isbn(isbn, cluster_size=1):
|
||||
"""add a book by isbn and then see if there's an existing work to add it to so as to make a cluster
|
||||
bigger than cluster_size.
|
||||
"""add a book by isbn and then see if there's an existing work to add it to so as to make a
|
||||
cluster bigger than cluster_size.
|
||||
"""
|
||||
logger.info("finding a related work for %s", isbn)
|
||||
|
||||
|
@ -396,12 +412,12 @@ def relate_isbn(isbn, cluster_size=1):
|
|||
if edition.work is None:
|
||||
logger.info("didn't add related to null work")
|
||||
return None
|
||||
if edition.work.editions.count()>cluster_size:
|
||||
if edition.work.editions.count() > cluster_size:
|
||||
return edition.work
|
||||
for other_isbn in thingisbn(isbn):
|
||||
# 979's come back as 13
|
||||
logger.debug("other_isbn: %s", other_isbn)
|
||||
if len(other_isbn)==10:
|
||||
if len(other_isbn) == 10:
|
||||
other_isbn = regluit.core.isbn.convert_10_to_13(other_isbn)
|
||||
related_edition = add_by_isbn(other_isbn, work=edition.work)
|
||||
if related_edition:
|
||||
|
@ -411,9 +427,9 @@ def relate_isbn(isbn, cluster_size=1):
|
|||
related_edition.work = edition.work
|
||||
related_edition.save()
|
||||
elif related_edition.work_id != edition.work_id:
|
||||
logger.debug("merge_works path 1 %s %s", edition.work_id, related_edition.work_id )
|
||||
logger.debug("merge_works path 1 %s %s", edition.work_id, related_edition.work_id)
|
||||
merge_works(related_edition.work, edition.work)
|
||||
if related_edition.work.editions.count()>cluster_size:
|
||||
if related_edition.work.editions.count() > cluster_size:
|
||||
return related_edition.work
|
||||
return edition.work
|
||||
|
||||
|
@ -438,7 +454,7 @@ def add_related(isbn):
|
|||
for other_isbn in thingisbn(isbn):
|
||||
# 979's come back as 13
|
||||
logger.debug("other_isbn: %s", other_isbn)
|
||||
if len(other_isbn)==10:
|
||||
if len(other_isbn) == 10:
|
||||
other_isbn = regluit.core.isbn.convert_10_to_13(other_isbn)
|
||||
related_edition = add_by_isbn(other_isbn, work=work)
|
||||
|
||||
|
@ -450,7 +466,7 @@ def add_related(isbn):
|
|||
related_edition.work = work
|
||||
related_edition.save()
|
||||
elif related_edition.work_id != work.id:
|
||||
logger.debug("merge_works path 1 %s %s", work.id, related_edition.work_id )
|
||||
logger.debug("merge_works path 1 %s %s", work.id, related_edition.work_id)
|
||||
work = merge_works(work, related_edition.work)
|
||||
else:
|
||||
if other_editions.has_key(related_language):
|
||||
|
@ -461,13 +477,13 @@ def add_related(isbn):
|
|||
# group the other language editions together
|
||||
for lang_group in other_editions.itervalues():
|
||||
logger.debug("lang_group (ed, work): %s", [(ed.id, ed.work_id) for ed in lang_group])
|
||||
if len(lang_group)>1:
|
||||
if len(lang_group) > 1:
|
||||
lang_edition = lang_group[0]
|
||||
logger.debug("lang_edition.id: %s", lang_edition.id)
|
||||
# compute the distinct set of works to merge into lang_edition.work
|
||||
works_to_merge = set([ed.work for ed in lang_group[1:]]) - set([lang_edition.work])
|
||||
for w in works_to_merge:
|
||||
logger.debug("merge_works path 2 %s %s", lang_edition.work_id, w.id )
|
||||
logger.debug("merge_works path 2 %s %s", lang_edition.work_id, w.id)
|
||||
merged_work = merge_works(lang_edition.work, w)
|
||||
models.WorkRelation.objects.get_or_create(
|
||||
to_work=lang_group[0].work,
|
||||
|
@ -479,9 +495,10 @@ def add_related(isbn):
|
|||
|
||||
def thingisbn(isbn):
|
||||
"""given an ISBN return a list of related edition ISBNs, according to
|
||||
Library Thing. (takes isbn_10 or isbn_13, returns isbn_10, except for 979 isbns, which come back as isbn_13')
|
||||
Library Thing. (takes isbn_10 or isbn_13, returns isbn_10, except for 979 isbns,
|
||||
which come back as isbn_13')
|
||||
"""
|
||||
logger.info("looking up %s at ThingISBN" , isbn)
|
||||
logger.info("looking up %s at ThingISBN", isbn)
|
||||
url = "https://www.librarything.com/api/thingISBN/%s" % isbn
|
||||
xml = requests.get(url, headers={"User-Agent": settings.USER_AGENT}).content
|
||||
doc = ElementTree.fromstring(xml)
|
||||
|
@ -492,16 +509,17 @@ def merge_works(w1, w2, user=None):
|
|||
"""will merge the second work (w2) into the first (w1)
|
||||
"""
|
||||
logger.info("merging work %s into %s", w2.id, w1.id)
|
||||
# don't merge if the works are the same or at least one of the works has no id (for example, when w2 has already been deleted)
|
||||
# don't merge if the works are the same or at least one of the works has no id
|
||||
#(for example, when w2 has already been deleted)
|
||||
if w1 is None or w2 is None or w1.id == w2.id or w1.id is None or w2.id is None:
|
||||
return w1
|
||||
if w2.selected_edition != None and w1.selected_edition == None:
|
||||
if w2.selected_edition is not None and w1.selected_edition is None:
|
||||
#the merge should be reversed
|
||||
temp = w1
|
||||
w1 = w2
|
||||
w2 = temp
|
||||
models.WasWork(was=w2.pk, work=w1, user=user).save()
|
||||
for ww in models.WasWork.objects.filter(work = w2):
|
||||
for ww in models.WasWork.objects.filter(work=w2):
|
||||
ww.work = w1
|
||||
ww.save()
|
||||
if w2.description and not w1.description:
|
||||
|
@ -561,10 +579,12 @@ def merge_works(w1, w2, user=None):
|
|||
return w1
|
||||
|
||||
def detach_edition(e):
|
||||
"""will detach edition from its work, creating a new stub work. if remerge=true, will see if there's another work to attach to
|
||||
"""
|
||||
will detach edition from its work, creating a new stub work. if remerge=true, will see if
|
||||
there's another work to attach to
|
||||
"""
|
||||
logger.info("splitting edition %s from %s", e, e.work)
|
||||
w = models.Work(title=e.title, language = e.work.language)
|
||||
w = models.Work(title=e.title, language=e.work.language)
|
||||
w.save()
|
||||
|
||||
for identifier in e.identifiers.all():
|
||||
|
@ -574,9 +594,12 @@ def detach_edition(e):
|
|||
e.work = w
|
||||
e.save()
|
||||
|
||||
SPAM_STRINGS = ["GeneralBooksClub.com", "AkashaPublishing.Com"]
|
||||
def despam_description(description):
|
||||
""" a lot of descriptions from openlibrary have free-book promotion text; this removes some of it."""
|
||||
if description.find("GeneralBooksClub.com")>-1 or description.find("AkashaPublishing.Com")>-1:
|
||||
""" a lot of descriptions from openlibrary have free-book promotion text;
|
||||
this removes some of it."""
|
||||
for spam in SPAM_STRINGS:
|
||||
if description.find(spam) > -1:
|
||||
return ""
|
||||
pieces = description.split("1stWorldLibrary.ORG -")
|
||||
if len(pieces) > 1:
|
||||
|
@ -586,7 +609,7 @@ def despam_description(description):
|
|||
return pieces[1]
|
||||
return description
|
||||
|
||||
def add_openlibrary(work, hard_refresh = False):
|
||||
def add_openlibrary(work, hard_refresh=False):
|
||||
if (not hard_refresh) and work.openlibrary_lookup is not None:
|
||||
# don't hit OL if we've visited in the past month or so
|
||||
if now()- work.openlibrary_lookup < timedelta(days=30):
|
||||
|
@ -616,17 +639,38 @@ def add_openlibrary(work, hard_refresh = False):
|
|||
if e[isbn_key].has_key('details'):
|
||||
if e[isbn_key]['details'].has_key('oclc_numbers'):
|
||||
for oclcnum in e[isbn_key]['details']['oclc_numbers']:
|
||||
models.Identifier.get_or_add(type='oclc', value=oclcnum, work=work, edition=edition)
|
||||
models.Identifier.get_or_add(
|
||||
type='oclc',
|
||||
value=oclcnum,
|
||||
work=work,
|
||||
edition=edition
|
||||
)
|
||||
if e[isbn_key]['details'].has_key('identifiers'):
|
||||
ids = e[isbn_key]['details']['identifiers']
|
||||
if ids.has_key('goodreads'):
|
||||
models.Identifier.get_or_add(type='gdrd', value=ids['goodreads'][0], work=work, edition=edition)
|
||||
models.Identifier.get_or_add(
|
||||
type='gdrd',
|
||||
value=ids['goodreads'][0],
|
||||
work=work, edition=edition
|
||||
)
|
||||
if ids.has_key('librarything'):
|
||||
models.Identifier.get_or_add(type='ltwk', value=ids['librarything'][0], work=work)
|
||||
models.Identifier.get_or_add(
|
||||
type='ltwk',
|
||||
value=ids['librarything'][0],
|
||||
work=work
|
||||
)
|
||||
if ids.has_key('google'):
|
||||
models.Identifier.get_or_add(type='goog', value=ids['google'][0], work=work)
|
||||
models.Identifier.get_or_add(
|
||||
type='goog',
|
||||
value=ids['google'][0],
|
||||
work=work
|
||||
)
|
||||
if ids.has_key('project_gutenberg'):
|
||||
models.Identifier.get_or_add(type='gute', value=ids['project_gutenberg'][0], work=work)
|
||||
models.Identifier.get_or_add(
|
||||
type='gute',
|
||||
value=ids['project_gutenberg'][0],
|
||||
work=work
|
||||
)
|
||||
if e[isbn_key]['details'].has_key('works'):
|
||||
work_key = e[isbn_key]['details']['works'].pop(0)['key']
|
||||
logger.info("got openlibrary work %s for isbn %s", work_key, isbn_key)
|
||||
|
@ -639,7 +683,9 @@ def add_openlibrary(work, hard_refresh = False):
|
|||
if description.has_key('value'):
|
||||
description = description['value']
|
||||
description = despam_description(description)
|
||||
if not work.description or work.description.startswith('{') or len(description) > len(work.description):
|
||||
if not work.description or \
|
||||
work.description.startswith('{') or \
|
||||
len(description) > len(work.description):
|
||||
work.description = description
|
||||
work.save()
|
||||
if w.has_key('subjects') and len(w['subjects']) > len(subjects):
|
||||
|
@ -670,19 +716,20 @@ def _get_json(url, params={}, type='gb'):
|
|||
if response.status_code == 200:
|
||||
return json.loads(response.content)
|
||||
else:
|
||||
logger.error("unexpected HTTP response: %s" % response)
|
||||
logger.error("unexpected HTTP response: %s", response)
|
||||
if response.content:
|
||||
logger.error("response content: %s" % response.content)
|
||||
logger.error("response content: %s", response.content)
|
||||
raise LookupFailure("GET failed: url=%s and params=%s" % (url, params))
|
||||
|
||||
|
||||
def load_gutenberg_edition(title, gutenberg_etext_id, ol_work_id, seed_isbn, url, format, license, lang, publication_date):
|
||||
|
||||
# let's start with instantiating the relevant Work and Edition if they don't already exist
|
||||
def load_gutenberg_edition(title, gutenberg_etext_id, ol_work_id, seed_isbn, url,
|
||||
format, license, lang, publication_date):
|
||||
''' let's start with instantiating the relevant Work and Edition if they don't already exist'''
|
||||
|
||||
try:
|
||||
work = models.Identifier.objects.get(type='olwk', value=ol_work_id).work
|
||||
except models.Identifier.DoesNotExist: # try to find an Edition with the seed_isbn and use that work to hang off of
|
||||
except models.Identifier.DoesNotExist:
|
||||
# try to find an Edition with the seed_isbn and use that work to hang off of
|
||||
sister_edition = add_by_isbn(seed_isbn)
|
||||
if sister_edition.new:
|
||||
# add related editions asynchronously
|
||||
|
@ -694,14 +741,18 @@ def load_gutenberg_edition(title, gutenberg_etext_id, ol_work_id, seed_isbn, url
|
|||
|
||||
# Now pull out any existing Gutenberg editions tied to the work with the proper Gutenberg ID
|
||||
try:
|
||||
edition = models.Identifier.objects.get(type='gtbg', value=gutenberg_etext_id ).edition
|
||||
edition = models.Identifier.objects.get(type='gtbg', value=gutenberg_etext_id).edition
|
||||
except models.Identifier.DoesNotExist:
|
||||
edition = models.Edition()
|
||||
edition.title = title
|
||||
edition.work = work
|
||||
|
||||
edition.save()
|
||||
models.Identifier.get_or_add(type='gtbg', value=gutenberg_etext_id, edition=edition, work=work)
|
||||
models.Identifier.get_or_add(
|
||||
type='gtbg',
|
||||
value=gutenberg_etext_id,
|
||||
edition=edition, work=work
|
||||
)
|
||||
|
||||
# check to see whether the Edition hasn't already been loaded first
|
||||
# search by url
|
||||
|
@ -709,9 +760,9 @@ def load_gutenberg_edition(title, gutenberg_etext_id, ol_work_id, seed_isbn, url
|
|||
|
||||
# format: what's the controlled vocab? -- from Google -- alternative would be mimetype
|
||||
|
||||
if len(ebooks):
|
||||
if ebooks:
|
||||
ebook = ebooks[0]
|
||||
elif len(ebooks) == 0: # need to create new ebook
|
||||
else: # need to create new ebook
|
||||
ebook = models.Ebook()
|
||||
|
||||
if len(ebooks) > 1:
|
||||
|
@ -735,8 +786,8 @@ class LookupFailure(Exception):
|
|||
|
||||
IDTABLE = [('librarything', 'ltwk'), ('goodreads', 'gdrd'), ('openlibrary', 'olwk'),
|
||||
('gutenberg', 'gtbg'), ('isbn', 'isbn'), ('oclc', 'oclc'),
|
||||
('edition_id', 'edid'), ('googlebooks', 'goog'), ('doi', 'doi'), ('http','http'),
|
||||
]
|
||||
('googlebooks', 'goog'), ('doi', 'doi'), ('http', 'http'), ('edition_id', 'edid'),
|
||||
]
|
||||
|
||||
def load_from_yaml(yaml_url, test_mode=False):
|
||||
"""
|
||||
|
@ -836,7 +887,6 @@ class BasePandataLoader(object):
|
|||
# only need to create edid if there is no edition id for the edition
|
||||
new_ids.append((identifier, id_code, value))
|
||||
|
||||
|
||||
if not work:
|
||||
work = models.Work.objects.create(title=metadata.title, language=metadata.language)
|
||||
if not edition:
|
||||
|
@ -858,24 +908,31 @@ class BasePandataLoader(object):
|
|||
)
|
||||
if metadata.publisher: #always believe yaml
|
||||
edition.set_publisher(metadata.publisher)
|
||||
|
||||
if metadata.publication_date: #always believe yaml
|
||||
edition.publication_date = metadata.publication_date
|
||||
if metadata.description and len(metadata.description) > len(work.description):
|
||||
|
||||
#be careful about overwriting the work description
|
||||
if metadata.description and len(metadata.description) > len(work.description):
|
||||
# don't over-write reasonably long descriptions
|
||||
if len(work.description) < 500:
|
||||
work.description = metadata.description
|
||||
|
||||
if metadata.creator and not edition.authors.count():
|
||||
edition.authors.clear()
|
||||
for key in metadata.creator.keys():
|
||||
creators = metadata.creator[key]
|
||||
rel_code = inverse_marc_rels.get(key, 'aut')
|
||||
rel_code = inverse_marc_rels.get(key, None)
|
||||
if not rel_code:
|
||||
rel_code = inverse_marc_rels.get(key.rstrip('s'), 'auth')
|
||||
creators = creators if isinstance(creators, list) else [creators]
|
||||
for creator in creators:
|
||||
edition.add_author(unreverse_name(creator.get('agent_name', '')), relation=rel_code)
|
||||
for yaml_subject in metadata.subjects: #always add yaml subjects (don't clear)
|
||||
if isinstance(yaml_subject, tuple):
|
||||
(authority, heading) = yaml_subject
|
||||
elif isinstance(yaml_subject, str):
|
||||
(authority, heading) = ( '', yaml_subject)
|
||||
elif isinstance(yaml_subject, str) or isinstance(yaml_subject, unicode) :
|
||||
(authority, heading) = ('', yaml_subject)
|
||||
else:
|
||||
continue
|
||||
subject = models.Subject.set_by_name(heading, work=work, authority=authority)
|
||||
|
@ -950,13 +1007,13 @@ class GithubLoader(BasePandataLoader):
|
|||
ebooks_in_release = ebooks_in_github_release(repo_owner, repo_name, repo_tag, token=token)
|
||||
|
||||
for (ebook_format, ebook_name) in ebooks_in_release:
|
||||
(book_name_prefix, _ ) = re.search(r'(.*)\.([^\.]*)$', ebook_name).groups()
|
||||
(book_name_prefix, _) = re.search(r'(.*)\.([^\.]*)$', ebook_name).groups()
|
||||
(ebook, created) = models.Ebook.objects.get_or_create(
|
||||
url=git_download_from_yaml_url(
|
||||
self.base_url,
|
||||
metadata._version,
|
||||
edition_name=book_name_prefix,
|
||||
format_= ebook_format
|
||||
format_=ebook_format
|
||||
),
|
||||
provider='Github',
|
||||
rights=cc.match_license(metadata.rights),
|
||||
|
@ -967,8 +1024,10 @@ class GithubLoader(BasePandataLoader):
|
|||
|
||||
|
||||
def git_download_from_yaml_url(yaml_url, version, edition_name='book', format_='epub'):
|
||||
# go from https://github.com/GITenberg/Adventures-of-Huckleberry-Finn_76/raw/master/metadata.yaml
|
||||
# to https://github.com/GITenberg/Adventures-of-Huckleberry-Finn_76/releases/download/v0.0.3/Adventures-of-Huckleberry-Finn.epub
|
||||
'''
|
||||
go from https://github.com/GITenberg/Adventures-of-Huckleberry-Finn_76/raw/master/metadata.yaml
|
||||
to https://github.com/GITenberg/Adventures-of-Huckleberry-Finn_76/releases/download/v0.0.3/Adventures-of-Huckleberry-Finn.epub
|
||||
'''
|
||||
if yaml_url.endswith('raw/master/metadata.yaml'):
|
||||
repo_url = yaml_url[0:-24]
|
||||
#print (repo_url,version,edition_name)
|
||||
|
@ -1014,21 +1073,10 @@ def ebooks_in_github_release(repo_owner, repo_name, tag, token=None):
|
|||
for asset in release.iter_assets()
|
||||
if EBOOK_FORMATS.get(asset.content_type) is not None]
|
||||
|
||||
def add_by_webpage(url, work=None, user=None):
|
||||
edition = None
|
||||
scraper = get_scraper(url)
|
||||
loader = BasePandataLoader(url)
|
||||
pandata = Pandata()
|
||||
pandata.metadata = scraper.metadata
|
||||
for metadata in pandata.get_edition_list():
|
||||
edition = loader.load_from_pandata(metadata, work)
|
||||
work = edition.work
|
||||
loader.load_ebooks(pandata, edition, user=user)
|
||||
return edition if edition else None
|
||||
|
||||
def add_by_sitemap(url, maxnum=None):
|
||||
def add_from_bookdatas(bookdatas):
|
||||
''' bookdatas are iterators of scrapers '''
|
||||
editions = []
|
||||
for bookdata in scrape_sitemap(url, maxnum=maxnum):
|
||||
for bookdata in bookdatas:
|
||||
edition = work = None
|
||||
loader = BasePandataLoader(bookdata.base)
|
||||
pandata = Pandata()
|
||||
|
@ -1040,6 +1088,3 @@ def add_by_sitemap(url, maxnum=None):
|
|||
if edition:
|
||||
editions.append(edition)
|
||||
return editions
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from django.apps import apps
|
||||
from django.contrib.auth.models import User
|
||||
from django.db.models import Q
|
||||
from regluit.core import cc
|
||||
|
||||
|
@ -129,8 +130,10 @@ class FormatFacetGroup(FacetGroup):
|
|||
return "These eBooks available in %s format." % self.facet_name
|
||||
return FormatFacet
|
||||
|
||||
idtitles = {'doab': 'indexed in DOAB', 'gtbg':'available in Project Gutenberg'}
|
||||
idlabels = {'doab': 'DOAB', 'gtbg':'Project Gutenberg'}
|
||||
idtitles = {'doab': 'indexed in DOAB', 'gtbg':'available in Project Gutenberg',
|
||||
'-doab': 'not in DOAB', '-gtbg':'not from Project Gutenberg', }
|
||||
idlabels = {'doab': 'DOAB', 'gtbg':'Project Gutenberg',
|
||||
'-doab': 'not DOAB', '-gtbg':'not Project Gutenberg'}
|
||||
class IdFacetGroup(FacetGroup):
|
||||
def __init__(self):
|
||||
super(FacetGroup,self).__init__()
|
||||
|
@ -143,9 +146,15 @@ class IdFacetGroup(FacetGroup):
|
|||
def set_name(self):
|
||||
self.facet_name=facet_name
|
||||
def id_filter(query_set):
|
||||
if facet_name[0] == '-':
|
||||
return query_set.exclude(identifiers__type=facet_name[1:])
|
||||
else:
|
||||
return query_set.filter(identifiers__type=facet_name)
|
||||
model_filters = {}
|
||||
def get_query_set(self):
|
||||
if facet_name[0] == '-':
|
||||
return self._get_query_set().exclude(identifiers__type=self.facet_name[1:])
|
||||
else:
|
||||
return self._get_query_set().filter(identifiers__type=self.facet_name)
|
||||
def template(self):
|
||||
return 'facets/id.html'
|
||||
|
@ -277,6 +286,42 @@ class SearchFacetGroup(FacetGroup):
|
|||
return "eBooks for {}".format(self.term)
|
||||
return KeywordFacet
|
||||
|
||||
class SupporterFacetGroup(FacetGroup):
|
||||
|
||||
def __init__(self):
|
||||
super(FacetGroup,self).__init__()
|
||||
self.title = 'Supporter Faves'
|
||||
# make facets in TOPKW available for display
|
||||
self.facets = []
|
||||
self.label = '{} are ...'.format(self.title)
|
||||
|
||||
def has_facet(self, facet_name):
|
||||
|
||||
# recognize any facet_name that starts with "@" as a valid facet name
|
||||
return facet_name.startswith('@')
|
||||
|
||||
def get_facet_class(self, facet_name):
|
||||
class SupporterFacet(NamedFacet):
|
||||
def set_name(self):
|
||||
self.facet_name = facet_name
|
||||
self.username = self.facet_name[1:]
|
||||
try:
|
||||
user = User.objects.get(username=self.username)
|
||||
self.fave_set = user.wishlist.works.all()
|
||||
except User.DoesNotExist:
|
||||
self.fave_set = self.model.objects.none()
|
||||
|
||||
def get_query_set(self):
|
||||
return self._get_query_set().filter(pk__in=self.fave_set)
|
||||
|
||||
def template(self):
|
||||
return 'facets/supporter.html'
|
||||
|
||||
@property
|
||||
def description(self):
|
||||
return "eBooks faved by @{}".format(self.username)
|
||||
return SupporterFacet
|
||||
|
||||
class PublisherFacetGroup(FacetGroup):
|
||||
|
||||
def __init__(self):
|
||||
|
@ -325,7 +370,7 @@ class PublisherFacetGroup(FacetGroup):
|
|||
return PublisherFacet
|
||||
|
||||
# order of groups in facet_groups determines order of display on /free/
|
||||
facet_groups = [KeywordFacetGroup(), FormatFacetGroup(), LicenseFacetGroup(), PublisherFacetGroup(), IdFacetGroup(), SearchFacetGroup()]
|
||||
facet_groups = [KeywordFacetGroup(), FormatFacetGroup(), LicenseFacetGroup(), PublisherFacetGroup(), IdFacetGroup(), SearchFacetGroup(), SupporterFacetGroup()]
|
||||
|
||||
def get_facet(facet_name):
|
||||
for facet_group in facet_groups:
|
||||
|
|
|
@ -465,7 +465,7 @@
|
|||
"pk": 150,
|
||||
"model": "core.premium",
|
||||
"fields": {
|
||||
"description": "No premium, thanks! I just want to help unglue.",
|
||||
"description": "Nothing extra, thanks! I just want to support this campaign.",
|
||||
"campaign": null,
|
||||
"created": "2011-11-17T22:03:37",
|
||||
"amount": "0",
|
||||
|
|
|
@ -98,7 +98,7 @@
|
|||
"campaign": null,
|
||||
"amount": 0,
|
||||
"type": "00",
|
||||
"description": "No premium, thanks! I just want to help unglue.",
|
||||
"description": "Nothing extra, thanks! I just want to support this campaign",
|
||||
"created": "2011-11-17 22:03:37"
|
||||
}
|
||||
},
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
import requests
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
from gitenberg.metadata.pandata import Pandata
|
||||
|
||||
from regluit.core.bookloader import add_from_bookdatas, BasePandataLoader
|
||||
from .scrape import BaseScraper
|
||||
from .hathitrust import HathitrustScraper
|
||||
from .pressbooks import PressbooksScraper
|
||||
from .springer import SpringerScraper
|
||||
from .ubiquity import UbiquityScraper
|
||||
from .smashwords import SmashwordsScraper
|
||||
|
||||
def get_scraper(url):
|
||||
scrapers = [
|
||||
PressbooksScraper,
|
||||
HathitrustScraper,
|
||||
SpringerScraper,
|
||||
UbiquityScraper,
|
||||
SmashwordsScraper,
|
||||
BaseScraper,
|
||||
]
|
||||
for scraper in scrapers:
|
||||
if scraper.can_scrape(url):
|
||||
return scraper(url)
|
||||
|
||||
def scrape_sitemap(url, maxnum=None):
|
||||
try:
|
||||
response = requests.get(url, headers={"User-Agent": settings.USER_AGENT})
|
||||
doc = BeautifulSoup(response.content, 'lxml')
|
||||
for page in doc.find_all('loc')[0:maxnum]:
|
||||
scraper = get_scraper(page.text)
|
||||
if scraper.metadata.get('genre', None) == 'book':
|
||||
yield scraper
|
||||
except requests.exceptions.RequestException as e:
|
||||
logger.error(e)
|
||||
|
||||
def add_by_webpage(url, work=None, user=None):
|
||||
edition = None
|
||||
scraper = get_scraper(url)
|
||||
loader = BasePandataLoader(url)
|
||||
pandata = Pandata()
|
||||
pandata.metadata = scraper.metadata
|
||||
for metadata in pandata.get_edition_list():
|
||||
edition = loader.load_from_pandata(metadata, work)
|
||||
work = edition.work
|
||||
loader.load_ebooks(pandata, edition, user=user)
|
||||
return edition if edition else None
|
||||
|
||||
|
||||
def add_by_sitemap(url, maxnum=None):
|
||||
return add_from_bookdatas(scrape_sitemap(url, maxnum=maxnum))
|
|
@ -0,0 +1,63 @@
|
|||
import re
|
||||
|
||||
import requests
|
||||
from RISparser import read as readris
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
from regluit.core.validation import identifier_cleaner
|
||||
|
||||
from .scrape import BaseScraper
|
||||
|
||||
|
||||
class HathitrustScraper(BaseScraper):
|
||||
|
||||
can_scrape_hosts = ['hathitrust.org']
|
||||
can_scrape_strings = ['hdl.handle.net/2027/']
|
||||
CATALOG = re.compile(r'catalog.hathitrust.org/Record/(\d+)')
|
||||
|
||||
def setup(self):
|
||||
catalog_a = self.doc.find('a', href=self.CATALOG)
|
||||
if catalog_a:
|
||||
catalog_num = self.CATALOG.search(catalog_a['href']).group(1)
|
||||
ris_url = 'https://catalog.hathitrust.org/Search/SearchExport?handpicked={}&method=ris'.format(catalog_num)
|
||||
response = requests.get(ris_url, headers={"User-Agent": settings.USER_AGENT})
|
||||
records = readris(response.text.splitlines()) if response.status_code == 200 else []
|
||||
for record in records:
|
||||
self.record = record
|
||||
return
|
||||
self.record = {}
|
||||
|
||||
|
||||
def get_downloads(self):
|
||||
dl_a = self.doc.select_one('#fullPdfLink')
|
||||
value = dl_a['href'] if dl_a else None
|
||||
if value:
|
||||
self.set(
|
||||
'download_url_{}'.format('pdf'),
|
||||
'https://babel.hathitrust.org{}'.format(value)
|
||||
)
|
||||
|
||||
def get_isbns(self):
|
||||
isbn = self.record.get('issn', [])
|
||||
value = identifier_cleaner('isbn', quiet=True)(isbn)
|
||||
return {'print': value} if value else {}
|
||||
|
||||
def get_title(self):
|
||||
self.set('title', self.record.get('title', ''))
|
||||
|
||||
def get_keywords(self):
|
||||
self.set('subjects', self.record.get('keywords', []))
|
||||
|
||||
def get_publisher(self):
|
||||
self.set('publisher', self.record.get('publisher', ''))
|
||||
|
||||
def get_pubdate(self):
|
||||
self.set('publication_date', self.record.get('year', ''))
|
||||
|
||||
def get_description(self):
|
||||
notes = self.record.get('notes', [])
|
||||
self.set('description', '\r'.join(notes))
|
||||
|
||||
def get_genre(self):
|
||||
self.set('genre', self.record.get('type_of_reference', '').lower())
|
|
@ -0,0 +1,43 @@
|
|||
from regluit.core.validation import identifier_cleaner
|
||||
from . import BaseScraper
|
||||
|
||||
class PressbooksScraper(BaseScraper):
|
||||
can_scrape_hosts = ['bookkernel.com', 'milnepublishing.geneseo.edu',
|
||||
'press.rebus.community', 'pb.unizin.org']
|
||||
can_scrape_strings = ['pressbooks']
|
||||
|
||||
def get_downloads(self):
|
||||
for dl_type in ['epub', 'mobi', 'pdf']:
|
||||
download_el = self.doc.select_one('.{}'.format(dl_type))
|
||||
if download_el and download_el.find_parent():
|
||||
value = download_el.find_parent().get('href')
|
||||
if value:
|
||||
self.set('download_url_{}'.format(dl_type), value)
|
||||
|
||||
def get_publisher(self):
|
||||
value = self.get_dt_dd('Publisher')
|
||||
if not value:
|
||||
value = self.doc.select_one('.cie-name')
|
||||
value = value.text if value else None
|
||||
if value:
|
||||
self.set('publisher', value)
|
||||
else:
|
||||
super(PressbooksScraper, self).get_publisher()
|
||||
|
||||
def get_title(self):
|
||||
value = self.doc.select_one('.entry-title a[title]')
|
||||
value = value['title'] if value else None
|
||||
if value:
|
||||
self.set('title', value)
|
||||
else:
|
||||
super(PressbooksScraper, self).get_title()
|
||||
|
||||
def get_isbns(self):
|
||||
'''add isbn identifiers and return a dict of edition keys and ISBNs'''
|
||||
isbns = {}
|
||||
for (key, label) in [('electronic', 'Ebook ISBN'), ('paper', 'Print ISBN')]:
|
||||
isbn = identifier_cleaner('isbn', quiet=True)(self.get_dt_dd(label))
|
||||
if isbn:
|
||||
self.identifiers['isbn_{}'.format(key)] = isbn
|
||||
isbns[key] = isbn
|
||||
return isbns
|
|
@ -1,14 +1,14 @@
|
|||
import re
|
||||
import logging
|
||||
from urlparse import urlparse
|
||||
import requests
|
||||
from bs4 import BeautifulSoup
|
||||
#from gitenberg.metadata.pandata import Pandata
|
||||
from django.conf import settings
|
||||
from urlparse import urljoin
|
||||
from RISparser import read as readris
|
||||
|
||||
from regluit.core import models
|
||||
from regluit.core.validation import identifier_cleaner, authlist_cleaner
|
||||
from regluit.core.validation import authlist_cleaner, identifier_cleaner, validate_date
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -18,8 +18,27 @@ CONTAINS_OCLCNUM = re.compile('worldcat.org/oclc/(\d+)')
|
|||
|
||||
class BaseScraper(object):
|
||||
'''
|
||||
designed to make at least a decent gues for webpages that embed metadata
|
||||
designed to make at least a decent guess for webpages that embed metadata
|
||||
'''
|
||||
can_scrape_hosts = False
|
||||
can_scrape_strings = False
|
||||
@classmethod
|
||||
def can_scrape(cls, url):
|
||||
''' return True if the class can scrape the URL '''
|
||||
if not (cls.can_scrape_hosts or cls.can_scrape_strings):
|
||||
return True
|
||||
if cls.can_scrape_hosts:
|
||||
urlhost = urlparse(url).hostname
|
||||
if urlhost:
|
||||
for host in cls.can_scrape_hosts:
|
||||
if urlhost.endswith(host):
|
||||
return True
|
||||
if cls.can_scrape_strings:
|
||||
for pass_str in cls.can_scrape_strings:
|
||||
if url.find(pass_str) >= 0:
|
||||
return True
|
||||
return False
|
||||
|
||||
def __init__(self, url):
|
||||
self.metadata = {}
|
||||
self.identifiers = {'http': url}
|
||||
|
@ -30,6 +49,8 @@ class BaseScraper(object):
|
|||
if response.status_code == 200:
|
||||
self.base = response.url
|
||||
self.doc = BeautifulSoup(response.content, 'lxml')
|
||||
for review in self.doc.find_all(itemtype="http://schema.org/Review"):
|
||||
review.clear()
|
||||
self.setup()
|
||||
self.get_genre()
|
||||
self.get_title()
|
||||
|
@ -70,8 +91,7 @@ class BaseScraper(object):
|
|||
value = ''
|
||||
list_mode = attrs.pop('list_mode', 'longest')
|
||||
for meta_name in meta_list:
|
||||
attrs['name'] = meta_name
|
||||
|
||||
attrs['name'] = re.compile('^{}$'.format(meta_name), flags=re.I)
|
||||
metas = self.doc.find_all('meta', attrs=attrs)
|
||||
if len(metas) == 0:
|
||||
# some sites put schema.org metadata in metas
|
||||
|
@ -79,6 +99,11 @@ class BaseScraper(object):
|
|||
attrs['itemprop'] = meta_name
|
||||
metas = self.doc.find_all('meta', attrs=attrs)
|
||||
del(attrs['itemprop'])
|
||||
if len(metas) == 0:
|
||||
# og metadata in often in 'property' not name
|
||||
attrs['property'] = meta_name
|
||||
metas = self.doc.find_all('meta', attrs=attrs)
|
||||
del(attrs['property'])
|
||||
for meta in metas:
|
||||
el_value = meta.get('content', '').strip()
|
||||
if list_mode == 'longest':
|
||||
|
@ -99,12 +124,21 @@ class BaseScraper(object):
|
|||
dd = dt.find_next_sibling('dd') if dt else None
|
||||
return dd.text if dd else None
|
||||
|
||||
def get_itemprop(self, name):
|
||||
def get_itemprop(self, name, **attrs):
|
||||
value_list = []
|
||||
list_mode = attrs.pop('list_mode', 'list')
|
||||
attrs = {'itemprop': name}
|
||||
props = self.doc.find_all(attrs=attrs)
|
||||
attrs = {'property': name}
|
||||
props = props if props else self.doc.find_all(attrs=attrs)
|
||||
for el in props:
|
||||
if list_mode == 'one_item':
|
||||
return el.text if el.text else el.get('content')
|
||||
else:
|
||||
if el.text:
|
||||
value_list.append(el.text)
|
||||
elif el.has_key('content'):
|
||||
value_list.append(el['content'])
|
||||
return value_list
|
||||
|
||||
def setup(self):
|
||||
|
@ -115,24 +149,23 @@ class BaseScraper(object):
|
|||
#
|
||||
|
||||
def get_genre(self):
|
||||
value = self.check_metas(['DC.Type', 'dc.type', 'og:type'])
|
||||
value = self.check_metas([r'dc\.type', 'og:type'])
|
||||
if value and value in ('Text.Book', 'book'):
|
||||
self.set('genre', 'book')
|
||||
|
||||
def get_title(self):
|
||||
value = self.check_metas(['DC.Title', 'dc.title', 'citation_title', 'og:title', 'title'])
|
||||
value = self.check_metas([r'dc\.title', 'citation_title', 'og:title', 'title'])
|
||||
if not value:
|
||||
value = self.fetch_one_el_content('title')
|
||||
self.set('title', value)
|
||||
|
||||
def get_language(self):
|
||||
value = self.check_metas(['DC.Language', 'dc.language', 'language', 'inLanguage'])
|
||||
value = self.check_metas([r'dc\.language', 'language', 'inLanguage'])
|
||||
self.set('language', value)
|
||||
|
||||
def get_description(self):
|
||||
value = self.check_metas([
|
||||
'DC.Description',
|
||||
'dc.description',
|
||||
r'dc\.description',
|
||||
'og:description',
|
||||
'description'
|
||||
])
|
||||
|
@ -141,27 +174,34 @@ class BaseScraper(object):
|
|||
def get_isbns(self):
|
||||
'''return a dict of edition keys and ISBNs'''
|
||||
isbns = {}
|
||||
isbn_cleaner = identifier_cleaner('isbn', quiet=True)
|
||||
label_map = {'epub': 'EPUB', 'mobi': 'Mobi',
|
||||
'paper': 'Paperback', 'pdf':'PDF', 'hard':'Hardback'}
|
||||
for key in label_map.keys():
|
||||
isbn_key = 'isbn_{}'.format(key)
|
||||
value = self.check_metas(['citation_isbn'], type=label_map[key])
|
||||
value = identifier_cleaner('isbn')(value)
|
||||
value = isbn_cleaner(value)
|
||||
if value:
|
||||
isbns[isbn_key] = value
|
||||
self.identifiers[isbn_key] = value
|
||||
if not isbns:
|
||||
values = self.check_metas(['book:isbn', 'books:isbn'], list_mode='list')
|
||||
values = values if values else self.get_itemprop('isbn')
|
||||
if values:
|
||||
value = isbn_cleaner(values[0])
|
||||
isbns = {'':value} if value else {}
|
||||
return isbns
|
||||
|
||||
def get_identifiers(self):
|
||||
value = self.check_metas(['DC.Identifier.URI'])
|
||||
value = self.check_metas([r'DC\.Identifier\.URI'])
|
||||
if not value:
|
||||
value = self.doc.select_one('link[rel=canonical]')
|
||||
value = value['href'] if value else None
|
||||
value = identifier_cleaner('http')(value)
|
||||
value = identifier_cleaner('http', quiet=True)(value)
|
||||
if value:
|
||||
self.identifiers['http'] = value
|
||||
value = self.check_metas(['DC.Identifier.DOI', 'citation_doi'])
|
||||
value = identifier_cleaner('doi')(value)
|
||||
value = self.check_metas([r'DC\.Identifier\.DOI', 'citation_doi'])
|
||||
value = identifier_cleaner('doi', quiet=True)(value)
|
||||
if value:
|
||||
self.identifiers['doi'] = value
|
||||
|
||||
|
@ -170,7 +210,7 @@ class BaseScraper(object):
|
|||
for link in links:
|
||||
oclcmatch = CONTAINS_OCLCNUM.search(link['href'])
|
||||
if oclcmatch:
|
||||
value = identifier_cleaner('oclc')(oclcmatch.group(1))
|
||||
value = identifier_cleaner('oclc', quiet=True)(oclcmatch.group(1))
|
||||
if value:
|
||||
self.identifiers['oclc'] = value
|
||||
break
|
||||
|
@ -189,7 +229,7 @@ class BaseScraper(object):
|
|||
value = self.check_metas(['citation_isbn'], list_mode='list')
|
||||
if len(value):
|
||||
for isbn in value:
|
||||
isbn = identifier_cleaner('isbn')(isbn)
|
||||
isbn = identifier_cleaner('isbn', quiet=True)(isbn)
|
||||
if isbn:
|
||||
ed_list.append({
|
||||
'_edition': isbn,
|
||||
|
@ -204,43 +244,60 @@ class BaseScraper(object):
|
|||
self.set('subjects', re.split(' *[;,] *', value))
|
||||
|
||||
def get_publisher(self):
|
||||
value = self.check_metas(['citation_publisher', 'DC.Source'])
|
||||
value = self.check_metas(['citation_publisher', r'DC\.Source'])
|
||||
if value:
|
||||
self.set('publisher', value)
|
||||
|
||||
def get_pubdate(self):
|
||||
value = self.check_metas(['citation_publication_date', 'DC.Date.issued', 'datePublished'])
|
||||
value = self.get_itemprop('datePublished', list_mode='one_item')
|
||||
if not value:
|
||||
value = self.check_metas([
|
||||
'citation_publication_date', r'DC\.Date\.issued', 'datePublished',
|
||||
'books:release_date', 'book:release_date'
|
||||
])
|
||||
if value:
|
||||
value = validate_date(value)
|
||||
if value:
|
||||
self.set('publication_date', value)
|
||||
|
||||
def get_authors(self):
|
||||
def get_author_list(self):
|
||||
value_list = self.get_itemprop('author')
|
||||
if not value_list:
|
||||
value_list = self.check_metas([
|
||||
'DC.Creator.PersonalName',
|
||||
r'DC\.Creator\.PersonalName',
|
||||
'citation_author',
|
||||
'author',
|
||||
], list_mode='list')
|
||||
if not value_list:
|
||||
value_list = self.get_itemprop('author')
|
||||
if not value_list:
|
||||
return
|
||||
return []
|
||||
return value_list
|
||||
|
||||
def get_role(self):
|
||||
return 'author'
|
||||
|
||||
def get_authors(self):
|
||||
role = self.get_role()
|
||||
value_list = self.get_author_list()
|
||||
creator_list = []
|
||||
value_list = authlist_cleaner(value_list)
|
||||
if len(value_list) == 0:
|
||||
return
|
||||
if len(value_list) == 1:
|
||||
self.set('creator', {'author': {'agent_name': value_list[0]}})
|
||||
self.set('creator', {role: {'agent_name': value_list[0]}})
|
||||
return
|
||||
for auth in value_list:
|
||||
creator_list.append({'agent_name': auth})
|
||||
|
||||
self.set('creator', {'authors': creator_list })
|
||||
self.set('creator', {'{}s'.format(role): creator_list })
|
||||
|
||||
def get_cover(self):
|
||||
image_url = self.check_metas(['og.image', 'image', 'twitter:image'])
|
||||
image_url = self.check_metas(['og:image', 'image', 'twitter:image'])
|
||||
if not image_url:
|
||||
block = self.doc.find(class_=CONTAINS_COVER)
|
||||
block = block if block else self.doc
|
||||
img = block.find_all('img', src=CONTAINS_COVER)
|
||||
if img:
|
||||
cover_uri = img[0].get('src', None)
|
||||
image_url = img[0].get('src', None)
|
||||
if image_url:
|
||||
if not image_url.startswith('http'):
|
||||
image_url = urljoin(self.base, image_url)
|
||||
|
@ -259,123 +316,7 @@ class BaseScraper(object):
|
|||
for link in links:
|
||||
self.set('rights_url', link['href'])
|
||||
|
||||
@classmethod
|
||||
def can_scrape(cls, url):
|
||||
''' return True if the class can scrape the URL '''
|
||||
return True
|
||||
|
||||
class PressbooksScraper(BaseScraper):
|
||||
def get_downloads(self):
|
||||
for dl_type in ['epub', 'mobi', 'pdf']:
|
||||
download_el = self.doc.select_one('.{}'.format(dl_type))
|
||||
if download_el and download_el.find_parent():
|
||||
value = download_el.find_parent().get('href')
|
||||
if value:
|
||||
self.set('download_url_{}'.format(dl_type), value)
|
||||
|
||||
def get_publisher(self):
|
||||
value = self.get_dt_dd('Publisher')
|
||||
if not value:
|
||||
value = self.doc.select_one('.cie-name')
|
||||
value = value.text if value else None
|
||||
if value:
|
||||
self.set('publisher', value)
|
||||
else:
|
||||
super(PressbooksScraper, self).get_publisher()
|
||||
|
||||
def get_title(self):
|
||||
value = self.doc.select_one('.entry-title a[title]')
|
||||
value = value['title'] if value else None
|
||||
if value:
|
||||
self.set('title', value)
|
||||
else:
|
||||
super(PressbooksScraper, self).get_title()
|
||||
|
||||
def get_isbns(self):
|
||||
'''add isbn identifiers and return a dict of edition keys and ISBNs'''
|
||||
isbns = {}
|
||||
for (key, label) in [('electronic', 'Ebook ISBN'), ('paper', 'Print ISBN')]:
|
||||
isbn = identifier_cleaner('isbn')(self.get_dt_dd(label))
|
||||
if isbn:
|
||||
self.identifiers['isbn_{}'.format(key)] = isbn
|
||||
isbns[key] = isbn
|
||||
return isbns
|
||||
|
||||
@classmethod
|
||||
def can_scrape(cls, url):
|
||||
''' return True if the class can scrape the URL '''
|
||||
return url.find('press.rebus.community') > 0 or url.find('pressbooks.com') > 0
|
||||
|
||||
|
||||
class HathitrustScraper(BaseScraper):
|
||||
|
||||
CATALOG = re.compile(r'catalog.hathitrust.org/Record/(\d+)')
|
||||
|
||||
def setup(self):
|
||||
catalog_a = self.doc.find('a', href=self.CATALOG)
|
||||
if catalog_a:
|
||||
catalog_num = self.CATALOG.search(catalog_a['href']).group(1)
|
||||
ris_url = 'https://catalog.hathitrust.org/Search/SearchExport?handpicked={}&method=ris'.format(catalog_num)
|
||||
response = requests.get(ris_url, headers={"User-Agent": settings.USER_AGENT})
|
||||
records = readris(response.text.splitlines()) if response.status_code == 200 else []
|
||||
for record in records:
|
||||
self.record = record
|
||||
return
|
||||
self.record = {}
|
||||
|
||||
|
||||
def get_downloads(self):
|
||||
dl_a = self.doc.select_one('#fullPdfLink')
|
||||
value = dl_a['href'] if dl_a else None
|
||||
if value:
|
||||
self.set(
|
||||
'download_url_{}'.format('pdf'),
|
||||
'https://babel.hathitrust.org{}'.format(value)
|
||||
)
|
||||
|
||||
def get_isbns(self):
|
||||
isbn = self.record.get('issn', [])
|
||||
value = identifier_cleaner('isbn')(isbn)
|
||||
return {'print': value} if value else {}
|
||||
|
||||
def get_title(self):
|
||||
self.set('title', self.record.get('title', ''))
|
||||
|
||||
def get_keywords(self):
|
||||
self.set('subjects', self.record.get('keywords', []))
|
||||
|
||||
def get_publisher(self):
|
||||
self.set('publisher', self.record.get('publisher', ''))
|
||||
|
||||
def get_pubdate(self):
|
||||
self.set('publication_date', self.record.get('year', ''))
|
||||
|
||||
def get_description(self):
|
||||
notes = self.record.get('notes', [])
|
||||
self.set('description', '\r'.join(notes))
|
||||
|
||||
def get_genre(self):
|
||||
self.set('genre', self.record.get('type_of_reference', '').lower())
|
||||
|
||||
@classmethod
|
||||
def can_scrape(cls, url):
|
||||
''' return True if the class can scrape the URL '''
|
||||
return url.find('hathitrust.org') > 0 or url.find('hdl.handle.net/2027/') > 0
|
||||
|
||||
|
||||
def get_scraper(url):
|
||||
scrapers = [PressbooksScraper, HathitrustScraper, BaseScraper]
|
||||
for scraper in scrapers:
|
||||
if scraper.can_scrape(url):
|
||||
return scraper(url)
|
||||
|
||||
def scrape_sitemap(url, maxnum=None):
|
||||
try:
|
||||
response = requests.get(url, headers={"User-Agent": settings.USER_AGENT})
|
||||
doc = BeautifulSoup(response.content, 'lxml')
|
||||
for page in doc.find_all('loc')[0:maxnum]:
|
||||
scraper = get_scraper(page.text)
|
||||
if scraper.metadata.get('genre', None) == 'book':
|
||||
yield scraper
|
||||
except requests.exceptions.RequestException as e:
|
||||
logger.error(e)
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
import re
|
||||
from urlparse import urljoin
|
||||
from regluit.core.loaders.scrape import BaseScraper
|
||||
|
||||
SWCAT = re.compile(r'^https://www\.smashwords\.com/books/category.*')
|
||||
class SmashwordsScraper(BaseScraper):
|
||||
can_scrape_strings =['smashwords.com']
|
||||
|
||||
def get_keywords(self):
|
||||
kws = self.doc.find_all('a', href=SWCAT)
|
||||
value = list(set(kw.string.strip() for kw in kws))
|
||||
if value:
|
||||
self.set('subjects', value)
|
||||
|
||||
def get_description(self):
|
||||
desc = self.doc.select_one('#longDescription')
|
||||
if desc:
|
||||
value = desc.get_text() if hasattr(desc, 'get_text') else desc.string
|
||||
if value.strip():
|
||||
self.set('description', value.strip())
|
||||
|
||||
def get_downloads(self):
|
||||
dldiv = self.doc.select_one('#download')
|
||||
if dldiv:
|
||||
for dl_type in ['epub', 'mobi', 'pdf']:
|
||||
dl_link = dldiv.find('a', href=re.compile(r'.*\.{}'.format(dl_type)))
|
||||
if dl_link:
|
||||
url = urljoin(self.base,dl_link['href'])
|
||||
self.set('download_url_{}'.format(dl_type), url)
|
||||
def get_publisher(self):
|
||||
self.set('publisher', 'Smashwords')
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
import re
|
||||
from urlparse import urljoin
|
||||
|
||||
import requests
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
from regluit.core.validation import identifier_cleaner
|
||||
from regluit.core.bookloader import add_from_bookdatas
|
||||
|
||||
from .scrape import BaseScraper, CONTAINS_CC
|
||||
|
||||
MENTIONS_CC = re.compile(r'CC BY(-NC)?(-ND|-SA)?', flags=re.I)
|
||||
HAS_YEAR = re.compile(r'(19|20)\d\d')
|
||||
|
||||
class SpringerScraper(BaseScraper):
|
||||
can_scrape_strings =['10.1007', '10.1057']
|
||||
def get_downloads(self):
|
||||
for dl_type in ['epub', 'mobi', 'pdf']:
|
||||
download_el = self.doc.find('a', title=re.compile(dl_type.upper()))
|
||||
if download_el:
|
||||
value = download_el.get('href')
|
||||
if value:
|
||||
value = urljoin(self.base, value)
|
||||
self.set('download_url_{}'.format(dl_type), value)
|
||||
|
||||
def get_description(self):
|
||||
desc = self.doc.select_one('#book-description')
|
||||
if desc:
|
||||
value = ''
|
||||
for div in desc.contents:
|
||||
text = div.get_text() if hasattr(div, 'get_text') else div.string
|
||||
if text:
|
||||
text = text.replace(u'\xa0', u' ')
|
||||
value = u'{}<p>{}</p>'.format(value, text)
|
||||
self.set('description', value)
|
||||
|
||||
def get_keywords(self):
|
||||
value = []
|
||||
for kw in self.doc.select('.Keyword'):
|
||||
value.append(kw.text.strip())
|
||||
if value:
|
||||
if 'Open Access' in value:
|
||||
value.remove('Open Access')
|
||||
self.set('subjects', value)
|
||||
|
||||
def get_identifiers(self):
|
||||
super(SpringerScraper, self).get_identifiers()
|
||||
el = self.doc.select_one('#doi-url')
|
||||
if el:
|
||||
value = identifier_cleaner('doi', quiet=True)(el.text)
|
||||
if value:
|
||||
self.identifiers['doi'] = value
|
||||
|
||||
def get_isbns(self):
|
||||
isbns = {}
|
||||
el = self.doc.select_one('#print-isbn')
|
||||
if el:
|
||||
value = identifier_cleaner('isbn', quiet=True)(el.text)
|
||||
if value:
|
||||
isbns['paper'] = value
|
||||
el = self.doc.select_one('#electronic-isbn')
|
||||
if el:
|
||||
value = identifier_cleaner('isbn', quiet=True)(el.text)
|
||||
if value:
|
||||
isbns['electronic'] = value
|
||||
return isbns
|
||||
|
||||
def get_title(self):
|
||||
el = self.doc.select_one('#book-title')
|
||||
value = ''
|
||||
if el:
|
||||
value = el.text.strip()
|
||||
if value:
|
||||
value = value.replace('\n', ': ', 1)
|
||||
self.set('title', value)
|
||||
if not value:
|
||||
super(SpringerScraper, self).get_title()
|
||||
|
||||
def get_role(self):
|
||||
if self.doc.select_one('#editors'):
|
||||
return 'editor'
|
||||
return 'author'
|
||||
|
||||
def get_author_list(self):
|
||||
for el in self.doc.select('.authors__name'):
|
||||
yield el.text.strip().replace(u'\xa0', u' ')
|
||||
|
||||
def get_license(self):
|
||||
'''only looks for cc licenses'''
|
||||
links = self.doc.find_all(href=CONTAINS_CC)
|
||||
for link in links:
|
||||
self.set('rights_url', link['href'])
|
||||
return
|
||||
mention = self.doc.find(string=MENTIONS_CC)
|
||||
if mention:
|
||||
lic = MENTIONS_CC.search(mention).group(0)
|
||||
lic_url = 'https://creativecommons.org/licenses/{}/'.format(lic[3:].lower())
|
||||
self.set('rights_url', lic_url)
|
||||
|
||||
def get_pubdate(self):
|
||||
pubinfo = self.doc.select_one('#copyright-info')
|
||||
if pubinfo:
|
||||
yearmatch = HAS_YEAR.search(pubinfo.string)
|
||||
if yearmatch:
|
||||
self.set('publication_date', yearmatch.group(0))
|
||||
|
||||
def get_publisher(self):
|
||||
self.set('publisher', 'Springer')
|
||||
|
||||
search_url = 'https://link.springer.com/search/page/{}?facet-content-type=%22Book%22&package=openaccess'
|
||||
def load_springer(num_pages):
|
||||
def springer_open_books(num_pages):
|
||||
for page in range(1, num_pages+1):
|
||||
url = search_url.format(page)
|
||||
response = requests.get(url, headers={"User-Agent": settings.USER_AGENT})
|
||||
if response.status_code == 200:
|
||||
base = response.url
|
||||
doc = BeautifulSoup(response.content, 'lxml')
|
||||
for link in doc.select('a.title'):
|
||||
book_url = urljoin(base, link['href'])
|
||||
yield SpringerScraper(book_url)
|
||||
return add_from_bookdatas(springer_open_books(num_pages))
|
|
@ -0,0 +1,31 @@
|
|||
import re
|
||||
from urlparse import urlparse
|
||||
|
||||
from regluit.utils.lang import get_language_code
|
||||
from . import BaseScraper
|
||||
|
||||
|
||||
HAS_EDS = re.compile(r'\(eds?\.\)')
|
||||
UBIQUITY_HOSTS = ["ubiquitypress.com", "kriterium.se", "oa.finlit.fi", "humanities-map.net",
|
||||
"oa.psupress.org", "larcommons.net", "uwestminsterpress.co.uk", "stockholmuniversitypress.se",
|
||||
"luminosoa.org",
|
||||
]
|
||||
|
||||
class UbiquityScraper(BaseScraper):
|
||||
can_scrape_hosts = UBIQUITY_HOSTS
|
||||
def get_role(self):
|
||||
descs = self.doc.select('section.book-description')
|
||||
for desc in descs:
|
||||
if desc.find(string=HAS_EDS):
|
||||
return 'editor'
|
||||
return super(self, UbiquityScraper).get_role()
|
||||
|
||||
def get_language(self):
|
||||
langlabel = self.doc.find(string='Language')
|
||||
lang = langlabel.parent.parent.find_next_sibling() if langlabel else ''
|
||||
lang = lang.get_text() if lang else ''
|
||||
lang = get_language_code(lang) if lang else ''
|
||||
if lang:
|
||||
self.set('language', lang)
|
||||
else:
|
||||
super(self, UbiquityScraper).get_language()
|
|
@ -3,7 +3,7 @@ from selectable.registry import registry
|
|||
|
||||
from django.contrib.auth.models import User
|
||||
from django.db.models import Count
|
||||
from regluit.core.models import Work, PublisherName, Edition, Subject, EditionNote
|
||||
from regluit.core.models import Work, PublisherName, Edition, Subject, EditionNote, Ebook
|
||||
from regluit.utils.text import sanitize_line
|
||||
|
||||
class OwnerLookup(ModelLookup):
|
||||
|
@ -13,11 +13,11 @@ class OwnerLookup(ModelLookup):
|
|||
class WorkLookup(ModelLookup):
|
||||
model = Work
|
||||
search_fields = ('title__istartswith',)
|
||||
def get_item_label(self,item):
|
||||
return "%s (%s, %s)"%(item.title,item.id,item.language)
|
||||
def get_item_label(self, item):
|
||||
return "%s (%s, %s)"%(item.title, item.id, item.language)
|
||||
|
||||
def get_item_value(self,item):
|
||||
return "%s (%s, %s)"%(item.title,item.id,item.language)
|
||||
def get_item_value(self, item):
|
||||
return "%s (%s, %s)"%(item.title, item.id, item.language)
|
||||
|
||||
def get_query(self, request, term):
|
||||
results = super(WorkLookup, self).get_query(request, term)
|
||||
|
@ -32,6 +32,20 @@ class PublisherNameLookup(ModelLookup):
|
|||
publisher_name.save()
|
||||
return publisher_name
|
||||
|
||||
class EbookLookup(ModelLookup):
|
||||
model = Ebook
|
||||
search_fields = ('edition__title__icontains',)
|
||||
filters = {'edition__isnull': False, }
|
||||
|
||||
def get_item(self, value):
|
||||
item = None
|
||||
if value:
|
||||
try:
|
||||
item = Ebook.objects.get(pk=value)
|
||||
except (ValueError, Ebook.DoesNotExist):
|
||||
item = None
|
||||
return item
|
||||
|
||||
class EditionLookup(ModelLookup):
|
||||
model = Edition
|
||||
search_fields = ('title__icontains',)
|
||||
|
@ -54,7 +68,9 @@ class SubjectLookup(ModelLookup):
|
|||
search_fields = ('name__icontains',)
|
||||
|
||||
def get_query(self, request, term):
|
||||
return super(SubjectLookup, self).get_query( request, term).annotate(Count('works')).order_by('-works__count')
|
||||
return super(SubjectLookup, self).get_query(
|
||||
request, term
|
||||
).annotate(Count('works')).order_by('-works__count')
|
||||
|
||||
class EditionNoteLookup(ModelLookup):
|
||||
model = EditionNote
|
||||
|
@ -70,3 +86,4 @@ registry.register(PublisherNameLookup)
|
|||
registry.register(EditionLookup)
|
||||
registry.register(SubjectLookup)
|
||||
registry.register(EditionNoteLookup)
|
||||
registry.register(EbookLookup)
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
from __future__ import print_function
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.db import IntegrityError
|
||||
|
||||
from regluit.core import models
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "clean work and edition titles, work descriptions, and author and publisher names"
|
||||
|
||||
def handle(self, **options):
|
||||
for ident in models.Identifier.objects.filter(type='http', edition__isnull=False):
|
||||
ident.edition = None
|
||||
ident.save()
|
||||
for edition in models.Edition.objects.filter(work__isnull=True):
|
||||
for ident in edition.identifiers.all():
|
||||
if ident.work:
|
||||
edition.work = work
|
||||
edition.save()
|
||||
break
|
||||
if not edition.work:
|
||||
edition.delete()
|
|
@ -1,6 +1,6 @@
|
|||
from django.core.management.base import BaseCommand
|
||||
|
||||
from regluit.core.bookloader import add_by_sitemap
|
||||
from regluit.core.loaders import add_by_sitemap
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "load books based on a website sitemap"
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
from django.core.management.base import BaseCommand
|
||||
|
||||
from regluit.core.loaders.springer import load_springer
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "load books from springer open"
|
||||
args = "<pages>"
|
||||
|
||||
|
||||
def handle(self, pages, **options):
|
||||
books = load_springer(int(pages))
|
||||
print "loaded {} books".format(len(books))
|
|
@ -0,0 +1,36 @@
|
|||
from django.core.management.base import BaseCommand
|
||||
from regluit.core.models import Work
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = "generate mobi ebooks where needed and possible."
|
||||
args = "<max>"
|
||||
|
||||
def handle(self, max=None, **options):
|
||||
if max:
|
||||
try:
|
||||
max = int(max)
|
||||
except ValueError:
|
||||
max = 1
|
||||
else:
|
||||
max = 1
|
||||
epubs = Work.objects.filter(editions__ebooks__format='epub').distinct().order_by('-id')
|
||||
|
||||
i = 0
|
||||
for work in epubs:
|
||||
if not work.ebooks().filter(format="mobi"):
|
||||
for ebook in work.ebooks().filter(format="epub"):
|
||||
ebf = ebook.get_archive_ebf()
|
||||
if ebf:
|
||||
try:
|
||||
print u'making mobi for {}'.format(work.title)
|
||||
if ebf.make_mobi():
|
||||
print 'made mobi'
|
||||
i = i + 1
|
||||
break
|
||||
else:
|
||||
print 'failed to make mobi'
|
||||
except:
|
||||
print 'failed to make mobi'
|
||||
if i >= max:
|
||||
break
|
|
@ -0,0 +1,59 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0010_userprofile_works'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='rightsholder',
|
||||
old_name='can_sell',
|
||||
new_name='approved',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='rightsholder',
|
||||
name='address',
|
||||
field=models.CharField(default=b'', max_length=400),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='rightsholder',
|
||||
name='mailing',
|
||||
field=models.CharField(default=b'', max_length=400),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='rightsholder',
|
||||
name='signature',
|
||||
field=models.CharField(default=b'', max_length=100),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='rightsholder',
|
||||
name='signer',
|
||||
field=models.CharField(default=b'', max_length=100),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='rightsholder',
|
||||
name='signer_ip',
|
||||
field=models.CharField(max_length=40, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='rightsholder',
|
||||
name='signer_title',
|
||||
field=models.CharField(default=b'', max_length=30),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='rightsholder',
|
||||
name='telephone',
|
||||
field=models.CharField(max_length=30, blank=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='rightsholder',
|
||||
name='email',
|
||||
field=models.CharField(default=b'', max_length=100),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,19 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0011_auto_20171110_1253'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='campaign',
|
||||
name='charitable',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
|
@ -93,6 +93,7 @@ from .bibmodels import (
|
|||
WorkRelation,
|
||||
)
|
||||
|
||||
from .rh_models import Claim, RightsHolder
|
||||
pm = PostMonkey(settings.MAILCHIMP_API_KEY)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
@ -148,65 +149,6 @@ class CeleryTask(models.Model):
|
|||
f = getattr(regluit.core.tasks, self.function_name)
|
||||
return f.AsyncResult(self.task_id).info
|
||||
|
||||
class Claim(models.Model):
|
||||
STATUSES = ((u'active', u'Claim has been accepted.'),
|
||||
(u'pending', u'Claim is pending acceptance.'),
|
||||
(u'release', u'Claim has not been accepted.'),
|
||||
)
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
rights_holder = models.ForeignKey("RightsHolder", related_name="claim", null=False)
|
||||
work = models.ForeignKey("Work", related_name="claim", null=False)
|
||||
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="claim", null=False)
|
||||
status = models.CharField(max_length=7, choices=STATUSES, default='active')
|
||||
|
||||
@property
|
||||
def can_open_new(self):
|
||||
# whether a campaign can be opened for this claim
|
||||
|
||||
#must be an active claim
|
||||
if self.status != 'active':
|
||||
return False
|
||||
#can't already be a campaign
|
||||
for campaign in self.campaigns:
|
||||
if campaign.status in ['ACTIVE', 'INITIALIZED']:
|
||||
return 0 # cannot open a new campaign
|
||||
if campaign.status in ['SUCCESSFUL']:
|
||||
return 2 # can open a THANKS campaign
|
||||
return 1 # can open any type of campaign
|
||||
|
||||
def __unicode__(self):
|
||||
return self.work.title
|
||||
|
||||
@property
|
||||
def campaign(self):
|
||||
return self.work.last_campaign()
|
||||
|
||||
@property
|
||||
def campaigns(self):
|
||||
return self.work.campaigns.all()
|
||||
|
||||
def notify_claim(sender, created, instance, **kwargs):
|
||||
if 'example.org' in instance.user.email or hasattr(instance, 'dont_notify'):
|
||||
return
|
||||
try:
|
||||
(rights, new_rights) = User.objects.get_or_create(email='rights@gluejar.com', defaults={'username':'RightsatUnglueit'})
|
||||
except:
|
||||
rights = None
|
||||
if instance.user == instance.rights_holder.owner:
|
||||
ul = (instance.user, rights)
|
||||
else:
|
||||
ul = (instance.user, instance.rights_holder.owner, rights)
|
||||
notification.send(ul, "rights_holder_claim", {'claim': instance,})
|
||||
post_save.connect(notify_claim, sender=Claim)
|
||||
|
||||
class RightsHolder(models.Model):
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
email = models.CharField(max_length=100, blank=True)
|
||||
rights_holder_name = models.CharField(max_length=100, blank=False)
|
||||
owner = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="rights_holder", null=False)
|
||||
can_sell = models.BooleanField(default=False)
|
||||
def __unicode__(self):
|
||||
return self.rights_holder_name
|
||||
|
||||
class Premium(models.Model):
|
||||
PREMIUM_TYPES = ((u'00', u'Default'), (u'CU', u'Custom'), (u'XX', u'Inactive'))
|
||||
|
@ -458,6 +400,7 @@ class Campaign(models.Model):
|
|||
publisher = models.ForeignKey("Publisher", related_name="campaigns", null=True)
|
||||
do_watermark = models.BooleanField(default=True)
|
||||
use_add_ask = models.BooleanField(default=True)
|
||||
charitable = models.BooleanField(default=False)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.problems = []
|
||||
|
|
|
@ -149,6 +149,14 @@ class Work(models.Model):
|
|||
def doab(self):
|
||||
return id_for(self, 'doab')
|
||||
|
||||
@property
|
||||
def doi(self):
|
||||
return self.id_for('doi')
|
||||
|
||||
@property
|
||||
def http_id(self):
|
||||
return self.id_for('http')
|
||||
|
||||
@property
|
||||
def googlebooks_id(self):
|
||||
try:
|
||||
|
@ -887,6 +895,8 @@ class Edition(models.Model):
|
|||
return regluit.core.isbn.convert_13_to_10(self.isbn_13)
|
||||
|
||||
def id_for(self, type):
|
||||
if type in WORK_IDENTIFIERS:
|
||||
return self.work.id_for(type)
|
||||
return id_for(self, type)
|
||||
|
||||
@property
|
||||
|
@ -905,18 +915,10 @@ class Edition(models.Model):
|
|||
def oclc(self):
|
||||
return self.id_for('oclc')
|
||||
|
||||
@property
|
||||
def doi(self):
|
||||
return self.id_for('doi')
|
||||
|
||||
@property
|
||||
def goodreads_id(self):
|
||||
return self.id_for('gdrd')
|
||||
|
||||
@property
|
||||
def http_id(self):
|
||||
return self.id_for('http')
|
||||
|
||||
@staticmethod
|
||||
def get_by_isbn(isbn):
|
||||
if len(isbn) == 10:
|
||||
|
@ -1070,13 +1072,24 @@ class EbookFile(models.Model):
|
|||
def make_mobi(self):
|
||||
if not self.format == 'epub' or not settings.MOBIGEN_URL:
|
||||
return False
|
||||
new_mobi_ebf = EbookFile.objects.create(edition=self.edition, format='mobi', asking=self.asking)
|
||||
new_mobi_ebf.file.save(path_for_file('ebf', None), ContentFile(mobi.convert_to_mobi(self.file.url)))
|
||||
try:
|
||||
mobi_cf = ContentFile(mobi.convert_to_mobi(self.file.url))
|
||||
except:
|
||||
return False
|
||||
new_mobi_ebf = EbookFile.objects.create(
|
||||
edition=self.edition,
|
||||
format='mobi',
|
||||
asking=self.asking,
|
||||
source=self.file.url
|
||||
)
|
||||
|
||||
new_mobi_ebf.file.save(path_for_file('ebf', None), mobi_cf)
|
||||
new_mobi_ebf.save()
|
||||
if self.ebook:
|
||||
new_ebook = Ebook.objects.create(
|
||||
edition=self.edition,
|
||||
format='mobi',
|
||||
provider='Unglue.it',
|
||||
url=new_mobi_ebf.file.url,
|
||||
rights=self.ebook.rights,
|
||||
version_label=self.ebook.version_label,
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
from notification import models as notification
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.db import models
|
||||
from django.db.models.signals import post_save
|
||||
from django.template.loader import render_to_string
|
||||
from django.utils.html import strip_tags
|
||||
|
||||
class Claim(models.Model):
|
||||
STATUSES = ((u'active', u'Claim has been accepted.'),
|
||||
(u'pending', u'Claim is pending acceptance.'),
|
||||
(u'release', u'Claim has not been accepted.'),
|
||||
)
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
rights_holder = models.ForeignKey("RightsHolder", related_name="claim", null=False)
|
||||
work = models.ForeignKey("Work", related_name="claim", null=False)
|
||||
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="claim", null=False)
|
||||
status = models.CharField(max_length=7, choices=STATUSES, default='active')
|
||||
|
||||
@property
|
||||
def can_open_new(self):
|
||||
# whether a campaign can be opened for this claim
|
||||
|
||||
#must be an active claim
|
||||
if self.status != 'active':
|
||||
return False
|
||||
#can't already be a campaign
|
||||
for campaign in self.campaigns:
|
||||
if campaign.status in ['ACTIVE', 'INITIALIZED']:
|
||||
return 0 # cannot open a new campaign
|
||||
if campaign.status in ['SUCCESSFUL']:
|
||||
return 2 # can open a THANKS campaign
|
||||
return 1 # can open any type of campaign
|
||||
|
||||
def __unicode__(self):
|
||||
return self.work.title
|
||||
|
||||
@property
|
||||
def campaign(self):
|
||||
return self.work.last_campaign()
|
||||
|
||||
@property
|
||||
def campaigns(self):
|
||||
return self.work.campaigns.all()
|
||||
|
||||
def notify_claim(sender, created, instance, **kwargs):
|
||||
if 'example.org' in instance.user.email or hasattr(instance, 'dont_notify'):
|
||||
return
|
||||
try:
|
||||
(rights, new_rights) = User.objects.get_or_create(
|
||||
email='rights@ebookfoundation.org',
|
||||
defaults={'username':'RightsatFEF'}
|
||||
)
|
||||
except:
|
||||
rights = None
|
||||
if instance.user == instance.rights_holder.owner:
|
||||
user_list = (instance.user, rights)
|
||||
else:
|
||||
user_list = (instance.user, instance.rights_holder.owner, rights)
|
||||
notification.send(user_list, "rights_holder_claim", {'claim': instance,})
|
||||
|
||||
post_save.connect(notify_claim, sender=Claim)
|
||||
|
||||
class RightsHolder(models.Model):
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
email = models.CharField(max_length=100, blank=False, default='')
|
||||
rights_holder_name = models.CharField(max_length=100, blank=False)
|
||||
owner = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="rights_holder", null=False)
|
||||
approved = models.BooleanField(default=False)
|
||||
address = models.CharField(max_length=400, blank=False, default='')
|
||||
mailing = models.CharField(max_length=400, blank=False, default='')
|
||||
telephone = models.CharField(max_length=30, blank=True)
|
||||
signer = models.CharField(max_length=100, blank=False, default='')
|
||||
signer_ip = models.CharField(max_length=40, null=True)
|
||||
signer_title = models.CharField(max_length=30, blank=False, default='')
|
||||
signature = models.CharField(max_length=100, blank=False, default='' )
|
||||
|
||||
def __unicode__(self):
|
||||
return self.rights_holder_name
|
||||
|
||||
def notify_rh(sender, created, instance, **kwargs):
|
||||
# don't notify for tests or existing rights holders
|
||||
if 'example.org' in instance.email or instance.id < 47:
|
||||
return
|
||||
try:
|
||||
(rights, new_rights) = User.objects.get_or_create(
|
||||
email='rights@ebookfoundation.org',
|
||||
defaults={'username':'RightsatFEF'}
|
||||
)
|
||||
except:
|
||||
rights = None
|
||||
user_list = (instance.owner, rights)
|
||||
if created:
|
||||
notification.send(user_list, "rights_holder_created", {'rights_holder': instance,})
|
||||
elif instance.approved:
|
||||
agreement = strip_tags(
|
||||
render_to_string(
|
||||
'accepted_agreement.html',
|
||||
{'rights_holder': instance,}
|
||||
)
|
||||
)
|
||||
signature = ''
|
||||
notification.send(
|
||||
user_list,
|
||||
"rights_holder_accepted",
|
||||
{'rights_holder': instance, 'agreement':agreement, 'signature':signature, }
|
||||
)
|
||||
for claim in instance.claim.filter(status='pending'):
|
||||
claim.status = 'active'
|
||||
claim.save()
|
||||
|
||||
post_save.connect(notify_rh, sender=RightsHolder)
|
|
@ -42,7 +42,7 @@ OTHER_ID_CHOICES = (
|
|||
('edid', 'pragmatic edition ID'),
|
||||
)
|
||||
|
||||
WORK_IDENTIFIERS = ('doi','olwk','glue','ltwk')
|
||||
WORK_IDENTIFIERS = ('doi','olwk','glue','ltwk', 'http')
|
||||
|
||||
ID_CHOICES_MAP = dict(ID_CHOICES)
|
||||
|
||||
|
|
|
@ -60,6 +60,8 @@ def gluejar_search(q, user_ip='69.243.24.29', page=1):
|
|||
|
||||
|
||||
def googlebooks_search(q, user_ip, page):
|
||||
if len(q) < 2 or len(q) > 2000:
|
||||
return {}
|
||||
# XXX: need to pass IP address of user in from the frontend
|
||||
headers = {'X-Forwarded-For': user_ip}
|
||||
start = (page - 1) * 10
|
||||
|
|
|
@ -81,7 +81,8 @@ def create_notice_types( **kwargs):
|
|||
notification.create_notice_type("pledge_status_change", _("Your Pledge Has Been Modified"), _("Your ungluing pledge has been modified."))
|
||||
notification.create_notice_type("pledge_charged", _("Your Pledge has been Executed"), _("You have contributed to a successful ungluing campaign."))
|
||||
notification.create_notice_type("pledge_failed", _("Unable to charge your credit card"), _("A charge to your credit card did not go through."))
|
||||
notification.create_notice_type("rights_holder_created", _("Agreement Accepted"), _("You have become a verified Unglue.it rights holder."))
|
||||
notification.create_notice_type("rights_holder_created", _("Agreement Accepted"), _("You have applied to become an Unglue.it rights holder."))
|
||||
notification.create_notice_type("rights_holder_accepted", _("Agreement Accepted"), _("You have become a verified Unglue.it rights holder."))
|
||||
notification.create_notice_type("rights_holder_claim", _("Claim Entered"), _("A claim has been entered."))
|
||||
notification.create_notice_type("wishlist_unsuccessful_amazon", _("Campaign shut down"), _("An ungluing campaign that you supported had to be shut down due to an Amazon Payments policy change."))
|
||||
notification.create_notice_type("pledge_gift_credit", _("Gift Credit Balance"), _("You have a gift credit balance"))
|
||||
|
|
|
@ -200,9 +200,11 @@ def notify_unclaimed_gifts():
|
|||
unclaimed = Gift.objects.filter(used=None)
|
||||
for gift in unclaimed:
|
||||
"""
|
||||
send notice every 7 days
|
||||
send notice every 7 days, but stop at 10x
|
||||
"""
|
||||
unclaimed_duration = (now() - gift.acq.created ).days
|
||||
if unclaimed_duration > 70:
|
||||
return
|
||||
if unclaimed_duration > 0 and unclaimed_duration % 7 == 0 : # first notice in 7 days
|
||||
notification.send_now([gift.acq.user], "purchase_gift_waiting", {'gift':gift}, True)
|
||||
notification.send_now([gift.giver], "purchase_notgot_gift", {'gift':gift}, True)
|
||||
|
|
|
@ -890,10 +890,10 @@ class WorkTests(TestCase):
|
|||
self.assertEqual(e2, self.w2.preferred_edition)
|
||||
|
||||
def test_valid_subject(self):
|
||||
self.assertTrue(valid_subject('A, valid, suj\xc3t'))
|
||||
self.assertFalse(valid_subject('A, valid, suj\xc3t, '))
|
||||
self.assertFalse(valid_subject('A valid suj\xc3t \x01'))
|
||||
Subject.set_by_name('A, valid, suj\xc3t; A, valid, suj\xc3t, ', work=self.w1)
|
||||
self.assertTrue(valid_subject(u'A, valid, suj\xc3t'))
|
||||
self.assertFalse(valid_subject(u'A, valid, suj\xc3t, '))
|
||||
self.assertFalse(valid_subject(u'A valid suj\xc3t \x01'))
|
||||
Subject.set_by_name(u'A, valid, suj\xc3t; A, valid, suj\xc3t, ', work=self.w1)
|
||||
self.assertEqual(1, self.w1.subjects.count())
|
||||
sub = Subject.set_by_name('nyt:hardcover_advice=2011-06-18', work=self.w1)
|
||||
self.assertEqual(sub.name, 'NYT Bestseller - Hardcover advice')
|
||||
|
|
|
@ -3,17 +3,21 @@
|
|||
methods to validate and clean identifiers
|
||||
'''
|
||||
import re
|
||||
import datetime
|
||||
|
||||
from dateutil.parser import parse
|
||||
from PyPDF2 import PdfFileReader
|
||||
|
||||
from django.forms import ValidationError
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from regluit.pyepub import EPUB
|
||||
from regluit.mobi import Mobi
|
||||
from .isbn import ISBN
|
||||
|
||||
ID_VALIDATION = {
|
||||
'http': (re.compile(r"(https?|ftp)://(-\.)?([^\s/?\.#]+\.?)+(/[^\s]*)?$",
|
||||
flags=re.IGNORECASE|re.S ),
|
||||
flags=re.IGNORECASE|re.S),
|
||||
"The Web Address must be a valid http(s) URL."),
|
||||
'isbn': (r'^([\dxX\-–— ]+|delete)$',
|
||||
"The ISBN must be a valid ISBN-13."),
|
||||
|
@ -46,7 +50,7 @@ def isbn_cleaner(value):
|
|||
raise ValidationError('no identifier value found')
|
||||
elif value == 'delete':
|
||||
return value
|
||||
isbn=ISBN(value)
|
||||
isbn = ISBN(value)
|
||||
if isbn.error:
|
||||
raise ValidationError(isbn.error)
|
||||
isbn.validate()
|
||||
|
@ -57,20 +61,23 @@ def olwk_cleaner(value):
|
|||
value = '/works/{}'.format(value)
|
||||
return value
|
||||
|
||||
doi_match = re.compile( r'10\.\d+/\S+')
|
||||
doi_match = re.compile(r'10\.\d+/\S+')
|
||||
|
||||
def doi_cleaner(value):
|
||||
if not value == 'delete' and not value.startswith('10.'):
|
||||
return doi_match.match(value).group(0)
|
||||
try:
|
||||
return doi_match.search(value).group(0)
|
||||
except AttributeError:
|
||||
return ''
|
||||
return value
|
||||
|
||||
ID_MORE_VALIDATION = {
|
||||
'isbn': isbn_cleaner,
|
||||
'olwk': olwk_cleaner,
|
||||
'olwk': doi_cleaner,
|
||||
'doi': doi_cleaner,
|
||||
}
|
||||
|
||||
def identifier_cleaner(id_type):
|
||||
def identifier_cleaner(id_type, quiet=False):
|
||||
if ID_VALIDATION.has_key(id_type):
|
||||
(regex, err_msg) = ID_VALIDATION[id_type]
|
||||
extra = ID_MORE_VALIDATION.get(id_type, None)
|
||||
|
@ -79,12 +86,18 @@ def identifier_cleaner(id_type):
|
|||
def cleaner(value):
|
||||
if not value:
|
||||
return None
|
||||
try:
|
||||
if regex.match(value):
|
||||
if extra:
|
||||
value = extra(value)
|
||||
return value
|
||||
else:
|
||||
raise ValidationError(err_msg)
|
||||
except ValidationError as ve:
|
||||
if quiet:
|
||||
return None
|
||||
else:
|
||||
raise ve
|
||||
return cleaner
|
||||
return lambda value: value
|
||||
|
||||
|
@ -94,18 +107,18 @@ def test_file(the_file, fformat):
|
|||
try:
|
||||
book = EPUB(the_file.file)
|
||||
except Exception as e:
|
||||
raise ValidationError(_('Are you sure this is an EPUB file?: %s' % e) )
|
||||
raise ValidationError(_('Are you sure this is an EPUB file?: %s' % e))
|
||||
elif fformat == 'mobi':
|
||||
try:
|
||||
book = Mobi(the_file.file)
|
||||
book.parse()
|
||||
except Exception as e:
|
||||
raise ValidationError(_('Are you sure this is a MOBI file?: %s' % e) )
|
||||
raise ValidationError(_('Are you sure this is a MOBI file?: %s' % e))
|
||||
elif fformat == 'pdf':
|
||||
try:
|
||||
doc = PdfFileReader(the_file.file)
|
||||
PdfFileReader(the_file.file)
|
||||
except Exception, e:
|
||||
raise ValidationError(_('%s is not a valid PDF file' % the_file.name) )
|
||||
raise ValidationError(_('%s is not a valid PDF file' % the_file.name))
|
||||
return True
|
||||
|
||||
def valid_xml_char_ordinal(c):
|
||||
|
@ -118,7 +131,7 @@ def valid_xml_char_ordinal(c):
|
|||
0x10000 <= codepoint <= 0x10FFFF
|
||||
)
|
||||
|
||||
def valid_subject( subject_name ):
|
||||
def valid_subject(subject_name):
|
||||
num_commas = 0
|
||||
for c in subject_name:
|
||||
if not valid_xml_char_ordinal(c):
|
||||
|
@ -173,3 +186,20 @@ def auth_cleaner(auth):
|
|||
for auth in authlist:
|
||||
cleaned.append(spaces.sub(' ', auth.strip()))
|
||||
return cleaned
|
||||
|
||||
MATCHYEAR = re.compile(r'(1|2)\d\d\d')
|
||||
MATCHYMD = re.compile(r'(1|2)\d\d\d-\d\d-\d\d')
|
||||
|
||||
def validate_date(date_string):
|
||||
ymd = MATCHYMD.search(date_string)
|
||||
if ymd:
|
||||
return ymd.group(0)
|
||||
try:
|
||||
date = parse(date_string.strip(), default=datetime.date(999, 1, 1))
|
||||
if date.year != 999:
|
||||
return date.strftime('%Y')
|
||||
except ValueError:
|
||||
year = MATCHYEAR.search(date_string)
|
||||
if year:
|
||||
return year.group(0)
|
||||
return ''
|
||||
|
|
|
@ -4,7 +4,7 @@ WSGISocketPrefix /opt/regluit
|
|||
<VirtualHost *:80>
|
||||
|
||||
ServerName localvm
|
||||
ServerAdmin info@gluejar.com
|
||||
ServerAdmin info@ebookfoundation.org
|
||||
|
||||
Redirect permanent / https://192.168.33.10.xip.io:443/
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ import logging
|
|||
import re
|
||||
import unicodedata
|
||||
|
||||
from datetime import timedelta, date
|
||||
from datetime import date
|
||||
from decimal import Decimal as D
|
||||
|
||||
#django imports
|
||||
|
@ -16,11 +16,7 @@ from django.forms.widgets import RadioSelect
|
|||
from django.forms.extras.widgets import SelectDateWidget
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from ckeditor.widgets import CKEditorWidget
|
||||
|
||||
from selectable.forms import (
|
||||
AutoCompleteSelectMultipleWidget,
|
||||
AutoCompleteSelectMultipleField,
|
||||
AutoCompleteSelectWidget,
|
||||
AutoCompleteSelectField
|
||||
)
|
||||
|
@ -59,11 +55,21 @@ from regluit.core.lookups import (
|
|||
SubjectLookup,
|
||||
)
|
||||
from regluit.core.validation import test_file
|
||||
from regluit.utils.localdatetime import now
|
||||
from regluit.utils.fields import ISBNField
|
||||
|
||||
|
||||
from .bibforms import EditionForm, IdentifierForm
|
||||
from .rh_forms import (
|
||||
CCDateForm,
|
||||
CloneCampaignForm,
|
||||
date_selector,
|
||||
DateCalculatorForm,
|
||||
EditManagersForm,
|
||||
ManageCampaignForm,
|
||||
OpenCampaignForm,
|
||||
RightsHolderForm,
|
||||
UserClaimForm
|
||||
)
|
||||
from questionnaire.models import Questionnaire
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
@ -148,12 +154,12 @@ class EbookForm(forms.ModelForm):
|
|||
new_label = self.data.get('new_version_label','')
|
||||
return new_label if new_label else self.cleaned_data['version_label']
|
||||
|
||||
def clean_provider(self):
|
||||
def set_provider(self):
|
||||
url = self.cleaned_data['url']
|
||||
new_provider = Ebook.infer_provider(url)
|
||||
if url and not new_provider:
|
||||
raise forms.ValidationError(_("At this time, ebook URLs must point at Internet Archive, Wikisources, Wikibooks, Hathitrust, Project Gutenberg, raw files at Github, Google Books, or OApen."))
|
||||
return new_provider if new_provider else "Unglue.it"
|
||||
self.cleaned_data['provider'] = new_provider if new_provider else "Unglue.it"
|
||||
|
||||
def clean_url(self):
|
||||
url = self.cleaned_data['url']
|
||||
|
@ -164,6 +170,7 @@ class EbookForm(forms.ModelForm):
|
|||
raise forms.ValidationError(_("There's already an ebook with that url."))
|
||||
|
||||
def clean(self):
|
||||
self.set_provider()
|
||||
format = self.cleaned_data.get('format', '')
|
||||
the_file = self.cleaned_data.get('file', None)
|
||||
url = self.cleaned_data.get('url', None)
|
||||
|
@ -174,50 +181,6 @@ class EbookForm(forms.ModelForm):
|
|||
self.cleaned_data['url'] = ''
|
||||
return self.cleaned_data
|
||||
|
||||
def UserClaimForm ( user_instance, *args, **kwargs ):
|
||||
class ClaimForm(forms.ModelForm):
|
||||
i_agree = forms.BooleanField(error_messages={'required': 'You must agree to the Terms in order to claim a work.'})
|
||||
rights_holder = forms.ModelChoiceField(queryset=user_instance.rights_holder.all(), empty_label=None)
|
||||
|
||||
class Meta:
|
||||
model = Claim
|
||||
exclude = ('status',)
|
||||
widgets = {
|
||||
'user': forms.HiddenInput,
|
||||
'work': forms.HiddenInput,
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
super(ClaimForm, self).__init__(*args, **kwargs)
|
||||
|
||||
return ClaimForm()
|
||||
|
||||
class RightsHolderForm(forms.ModelForm):
|
||||
owner = AutoCompleteSelectField(
|
||||
OwnerLookup,
|
||||
label='Owner',
|
||||
widget=AutoCompleteSelectWidget(OwnerLookup),
|
||||
required=True,
|
||||
error_messages={'required': 'Please ensure the owner is a valid Unglue.it account.'},
|
||||
)
|
||||
email = forms.EmailField(
|
||||
label=_("notification email address for rights holder"),
|
||||
max_length=100,
|
||||
error_messages={'required': 'Please enter an email address for the rights holder.'},
|
||||
)
|
||||
class Meta:
|
||||
model = RightsHolder
|
||||
exclude = ()
|
||||
|
||||
def clean_rights_holder_name(self):
|
||||
rights_holder_name = self.data["rights_holder_name"]
|
||||
try:
|
||||
RightsHolder.objects.get(rights_holder_name__iexact=rights_holder_name)
|
||||
except RightsHolder.DoesNotExist:
|
||||
return rights_holder_name
|
||||
raise forms.ValidationError(_("Another rights holder with that name already exists."))
|
||||
|
||||
|
||||
class ProfileForm(forms.ModelForm):
|
||||
clear_facebook = forms.BooleanField(required=False)
|
||||
clear_twitter = forms.BooleanField(required=False)
|
||||
|
@ -251,23 +214,6 @@ class ProfileForm(forms.ModelForm):
|
|||
self.cleaned_data["avatar_source"] == UNGLUEITAR
|
||||
return self.cleaned_data
|
||||
|
||||
class CloneCampaignForm(forms.Form):
|
||||
campaign_id = forms.IntegerField(required = True, widget = forms.HiddenInput)
|
||||
|
||||
class OpenCampaignForm(forms.ModelForm):
|
||||
managers = AutoCompleteSelectMultipleField(
|
||||
OwnerLookup,
|
||||
label='Campaign Managers',
|
||||
widget=AutoCompleteSelectMultipleWidget(OwnerLookup),
|
||||
required=True,
|
||||
error_messages = {'required': "You must have at least one manager for a campaign."},
|
||||
)
|
||||
userid = forms.IntegerField( required = True, widget = forms.HiddenInput )
|
||||
class Meta:
|
||||
model = Campaign
|
||||
fields = 'name', 'work', 'managers', 'type'
|
||||
widgets = { 'work': forms.HiddenInput, "name": forms.HiddenInput, }
|
||||
|
||||
def getTransferCreditForm(maximum, data=None, *args, **kwargs ):
|
||||
class TransferCreditForm(forms.Form):
|
||||
recipient = AutoCompleteSelectField(
|
||||
|
@ -320,19 +266,6 @@ class OtherWorkForm(WorkForm):
|
|||
super(OtherWorkForm, self).__init__(*args, **kwargs)
|
||||
self.fields['other_work'].widget.update_query_parameters({'language':self.work.language})
|
||||
|
||||
class EditManagersForm(forms.ModelForm):
|
||||
managers = AutoCompleteSelectMultipleField(
|
||||
OwnerLookup,
|
||||
label='Campaign Managers',
|
||||
widget=AutoCompleteSelectMultipleWidget(OwnerLookup),
|
||||
required=True,
|
||||
error_messages = {'required': "You must have at least one manager for a campaign."},
|
||||
)
|
||||
class Meta:
|
||||
model = Campaign
|
||||
fields = ('id', 'managers')
|
||||
widgets = { 'id': forms.HiddenInput }
|
||||
|
||||
class CustomPremiumForm(forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
|
@ -357,140 +290,6 @@ class OfferForm(forms.ModelForm):
|
|||
'license': forms.HiddenInput,
|
||||
}
|
||||
|
||||
date_selector = range(date.today().year, settings.MAX_CC_DATE.year+1)
|
||||
|
||||
class CCDateForm(object):
|
||||
target = forms.DecimalField(
|
||||
min_value= D(settings.UNGLUEIT_MINIMUM_TARGET),
|
||||
error_messages={'required': 'Please specify a Revenue Target.'}
|
||||
)
|
||||
minimum_target = settings.UNGLUEIT_MINIMUM_TARGET
|
||||
maximum_target = settings.UNGLUEIT_MAXIMUM_TARGET
|
||||
max_cc_date = settings.MAX_CC_DATE
|
||||
|
||||
def clean_target(self):
|
||||
new_target = self.cleaned_data['target']
|
||||
if new_target < D(settings.UNGLUEIT_MINIMUM_TARGET):
|
||||
raise forms.ValidationError(_('A campaign may not be launched with a target less than $%s' % settings.UNGLUEIT_MINIMUM_TARGET))
|
||||
if new_target > D(settings.UNGLUEIT_MAXIMUM_TARGET):
|
||||
raise forms.ValidationError(_('A campaign may not be launched with a target more than $%s' % settings.UNGLUEIT_MAXIMUM_TARGET))
|
||||
return new_target
|
||||
|
||||
def clean_cc_date_initial(self):
|
||||
new_cc_date_initial = self.cleaned_data['cc_date_initial']
|
||||
if new_cc_date_initial.date() > settings.MAX_CC_DATE:
|
||||
raise forms.ValidationError('The initial Ungluing Date cannot be after %s'%settings.MAX_CC_DATE)
|
||||
elif new_cc_date_initial - now() < timedelta(days=0):
|
||||
raise forms.ValidationError('The initial Ungluing date must be in the future!')
|
||||
return new_cc_date_initial
|
||||
|
||||
class DateCalculatorForm(CCDateForm, forms.ModelForm):
|
||||
revenue = forms.DecimalField()
|
||||
cc_date_initial = forms.DateTimeField(
|
||||
widget = SelectDateWidget(years=date_selector)
|
||||
)
|
||||
class Meta:
|
||||
model = Campaign
|
||||
fields = 'target', 'cc_date_initial', 'revenue',
|
||||
|
||||
def getManageCampaignForm ( instance, data=None, initial=None, *args, **kwargs ):
|
||||
|
||||
def get_queryset():
|
||||
work = instance.work
|
||||
return Edition.objects.filter(work = work)
|
||||
|
||||
class ManageCampaignForm(CCDateForm, forms.ModelForm):
|
||||
target = forms.DecimalField( required= (instance.type in {REWARDS, BUY2UNGLUE}))
|
||||
deadline = forms.DateTimeField(
|
||||
required = (instance.type==REWARDS),
|
||||
widget = SelectDateWidget(years=date_selector) if instance.status=='INITIALIZED' else forms.HiddenInput
|
||||
)
|
||||
cc_date_initial = forms.DateTimeField(
|
||||
required = (instance.type==BUY2UNGLUE) and instance.status=='INITIALIZED',
|
||||
widget = SelectDateWidget(years=date_selector) if instance.status=='INITIALIZED' else forms.HiddenInput
|
||||
)
|
||||
paypal_receiver = forms.EmailField(
|
||||
label=_("contact email address for this campaign"),
|
||||
max_length=100,
|
||||
error_messages={'required': 'You must enter the email we should contact you at for this campaign.'},
|
||||
)
|
||||
edition = forms.ModelChoiceField(
|
||||
get_queryset(),
|
||||
widget=RadioSelect(),
|
||||
empty_label='no edition selected',
|
||||
required=False,
|
||||
)
|
||||
publisher = forms.ModelChoiceField(
|
||||
instance.work.publishers(),
|
||||
empty_label='no publisher selected',
|
||||
required=False,
|
||||
)
|
||||
work_description = forms.CharField( required=False , widget=CKEditorWidget())
|
||||
|
||||
class Meta:
|
||||
model = Campaign
|
||||
fields = ('description', 'details', 'license', 'target', 'deadline', 'paypal_receiver',
|
||||
'edition', 'email', 'publisher', 'cc_date_initial', "do_watermark", "use_add_ask",
|
||||
)
|
||||
widgets = { 'deadline': SelectDateWidget }
|
||||
|
||||
def clean_target(self):
|
||||
if self.instance.type == THANKS:
|
||||
return None
|
||||
new_target = super(ManageCampaignForm, self).clean_target()
|
||||
if self.instance:
|
||||
if self.instance.status == 'ACTIVE' and self.instance.target < new_target:
|
||||
raise forms.ValidationError(_('The fundraising target for an ACTIVE campaign cannot be increased.'))
|
||||
return new_target
|
||||
|
||||
def clean_cc_date_initial(self):
|
||||
if self.instance.type in {REWARDS, THANKS} :
|
||||
return None
|
||||
if self.instance:
|
||||
if self.instance.status != 'INITIALIZED':
|
||||
# can't change this once launched
|
||||
return self.instance.cc_date_initial
|
||||
return super(ManageCampaignForm, self).clean_cc_date_initial()
|
||||
|
||||
def clean_deadline(self):
|
||||
if self.instance.type in {BUY2UNGLUE, THANKS} :
|
||||
return None
|
||||
new_deadline_date = self.cleaned_data['deadline']
|
||||
new_deadline = new_deadline_date + timedelta(hours=23, minutes=59)
|
||||
if self.instance:
|
||||
if self.instance.status == 'ACTIVE':
|
||||
return self.instance.deadline
|
||||
if new_deadline_date - now() > timedelta(days=int(settings.UNGLUEIT_LONGEST_DEADLINE)):
|
||||
raise forms.ValidationError(_('The chosen closing date is more than %s days from now' % settings.UNGLUEIT_LONGEST_DEADLINE))
|
||||
elif new_deadline - now() < timedelta(days=0):
|
||||
raise forms.ValidationError(_('The chosen closing date is in the past'))
|
||||
return new_deadline
|
||||
|
||||
def clean_license(self):
|
||||
new_license = self.cleaned_data['license']
|
||||
if self.instance:
|
||||
if self.instance.status == 'ACTIVE' and self.instance.license != new_license:
|
||||
# should only allow change to a less restrictive license
|
||||
if self.instance.license == 'CC BY-ND' and new_license in ['CC BY-NC-ND', 'CC BY-NC-SA', 'CC BY-NC']:
|
||||
raise forms.ValidationError(_('The proposed license for an ACTIVE campaign may not add restrictions.'))
|
||||
elif self.instance.license == 'CC BY' and new_license != 'CC0':
|
||||
raise forms.ValidationError(_('The proposed license for an ACTIVE campaign may not add restrictions.'))
|
||||
elif self.instance.license == 'CC BY-NC' and new_license in ['CC BY-NC-ND', 'CC BY-NC-SA', 'CC BY-SA', 'CC BY-ND']:
|
||||
raise forms.ValidationError(_('The proposed license for an ACTIVE campaign may not add restrictions.'))
|
||||
elif self.instance.license == 'CC BY-ND' and new_license in ['CC BY-NC-ND', 'CC BY-NC-SA', 'CC BY-SA', 'CC BY-NC']:
|
||||
raise forms.ValidationError(_('The proposed license for an ACTIVE campaign may not add restrictions.'))
|
||||
elif self.instance.license == 'CC BY-SA' and new_license in ['CC BY-NC-ND', 'CC BY-NC-SA', 'CC BY-ND', 'CC BY-NC']:
|
||||
raise forms.ValidationError(_('The proposed license for an ACTIVE campaign may not add restrictions.'))
|
||||
elif self.instance.license == 'CC BY-NC-SA' and new_license in ['CC BY-NC-ND', 'CC BY-ND']:
|
||||
raise forms.ValidationError(_('The proposed license for an ACTIVE campaign may not add restrictions.'))
|
||||
elif self.instance.license == 'CC0' :
|
||||
raise forms.ValidationError(_('The proposed license for an ACTIVE campaign may not add restrictions.'))
|
||||
elif self.instance.license in ['GDFL', 'LAL']:
|
||||
raise forms.ValidationError(_('Once you start a campaign with GDFL or LAL, you can\'t use any other license.'))
|
||||
return new_license
|
||||
if initial and not initial.get('edition', None) and not instance.edition:
|
||||
initial['edition'] = instance.work.editions.all()[0]
|
||||
return ManageCampaignForm(instance=instance, data=data, initial=initial)
|
||||
|
||||
class CampaignPurchaseForm(forms.Form):
|
||||
anonymous = forms.BooleanField(required=False, label=_("Make this purchase anonymous, please"))
|
||||
|
@ -587,12 +386,12 @@ class CampaignPledgeForm(forms.Form):
|
|||
min_value=D('1.00'),
|
||||
max_value=D('2000.00'),
|
||||
decimal_places=2,
|
||||
label="Pledge Amount",
|
||||
label="Support Amount",
|
||||
)
|
||||
def amount(self):
|
||||
return self.cleaned_data["preapproval_amount"] if self.cleaned_data else None
|
||||
|
||||
anonymous = forms.BooleanField(required=False, label=_("Make this pledge anonymous, please"))
|
||||
anonymous = forms.BooleanField(required=False, label=_("Make this support anonymous, please"))
|
||||
ack_name = forms.CharField(
|
||||
required=False,
|
||||
max_length=64,
|
||||
|
@ -601,6 +400,7 @@ class CampaignPledgeForm(forms.Form):
|
|||
ack_dedication = forms.CharField(required=False, max_length=140, label=_("Your dedication:"))
|
||||
|
||||
premium_id = forms.IntegerField(required=False)
|
||||
donation = forms.BooleanField(required=False, label=_("Make this a donation, not a pledge."))
|
||||
premium = None
|
||||
|
||||
@property
|
||||
|
@ -637,6 +437,10 @@ class CampaignPledgeForm(forms.Form):
|
|||
elif preapproval_amount < self.premium.amount:
|
||||
logger.info("raising form validating error")
|
||||
raise forms.ValidationError(_("Sorry, you must pledge at least $%s to select that premium." % (self.premium.amount)))
|
||||
donation = self.cleaned_data.get('donation', False)
|
||||
if donation and self.premium.amount > 0:
|
||||
raise forms.ValidationError(_("Sorry, donations are not eligible for premiums."))
|
||||
|
||||
return self.cleaned_data
|
||||
|
||||
class TokenCCMixin(forms.Form):
|
||||
|
|
|
@ -21,6 +21,7 @@ from regluit.core.lookups import (
|
|||
EditionNoteLookup,
|
||||
)
|
||||
from regluit.bisac.models import BisacHeading
|
||||
from regluit.core.cc import CHOICES as RIGHTS_CHOICES
|
||||
from regluit.core.models import Edition, Identifier
|
||||
from regluit.core.parameters import (
|
||||
AGE_LEVEL_CHOICES,
|
||||
|
@ -122,6 +123,8 @@ class EditionForm(forms.ModelForm):
|
|||
required=False,
|
||||
allow_new=True,
|
||||
)
|
||||
set_rights = forms.CharField(widget=forms.Select(choices=RIGHTS_CHOICES), required=False)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(EditionForm, self).__init__(*args, **kwargs)
|
||||
self.relators = []
|
||||
|
@ -133,12 +136,6 @@ class EditionForm(forms.ModelForm):
|
|||
)
|
||||
self.relators.append({'relator':relator, 'select':select})
|
||||
|
||||
def clean_doi(self):
|
||||
doi = self.cleaned_data["doi"]
|
||||
if doi and doi.startswith("http"):
|
||||
return doi.split('/', 3)[3]
|
||||
return doi
|
||||
|
||||
def clean_title(self):
|
||||
return sanitize_line(self.cleaned_data["title"])
|
||||
|
||||
|
@ -157,7 +154,7 @@ class EditionForm(forms.ModelForm):
|
|||
err_msg = "{} is a duplicate for work #{}.".format(identifier[0], identifier[0].work_id)
|
||||
self.add_error('id_value', forms.ValidationError(err_msg))
|
||||
try:
|
||||
self.cleaned_data['value'] = identifier_cleaner(id_type)(id_value)
|
||||
self.cleaned_data['id_value'] = identifier_cleaner(id_type)(id_value)
|
||||
except forms.ValidationError, ve:
|
||||
self.add_error('id_value', forms.ValidationError('{}: {}'.format(ve.message, id_value)))
|
||||
return self.cleaned_data
|
||||
|
|
|
@ -0,0 +1,315 @@
|
|||
from datetime import date, timedelta
|
||||
from decimal import Decimal as D
|
||||
|
||||
from ckeditor.widgets import CKEditorWidget
|
||||
|
||||
from selectable.forms import (
|
||||
AutoCompleteSelectMultipleWidget,
|
||||
AutoCompleteSelectMultipleField,
|
||||
)
|
||||
|
||||
from django import forms
|
||||
from django.conf import settings
|
||||
from django.forms.extras.widgets import SelectDateWidget
|
||||
from django.forms.widgets import RadioSelect
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from regluit.core.lookups import OwnerLookup
|
||||
from regluit.core.models import Campaign, Edition, Claim, RightsHolder, WasWork
|
||||
from regluit.core.parameters import *
|
||||
from regluit.utils.localdatetime import now
|
||||
|
||||
class RightsHolderForm(forms.ModelForm):
|
||||
email = forms.EmailField(
|
||||
help_text='notification email address for rights holder',
|
||||
max_length=100,
|
||||
error_messages={
|
||||
'required': 'Please enter a contact email address for the rights holder.'
|
||||
},
|
||||
)
|
||||
use4both = forms.BooleanField(label='use business address as mailing address', required=False)
|
||||
def clean_rights_holder_name(self):
|
||||
rights_holder_name = self.data["rights_holder_name"]
|
||||
try:
|
||||
RightsHolder.objects.get(rights_holder_name__iexact=rights_holder_name)
|
||||
except RightsHolder.DoesNotExist:
|
||||
return rights_holder_name
|
||||
raise forms.ValidationError('Another rights holder with that name already exists.')
|
||||
|
||||
class Meta:
|
||||
model = RightsHolder
|
||||
exclude = ('approved', 'signer_ip')
|
||||
widgets = {
|
||||
'address': forms.Textarea(attrs={'rows': 4}),
|
||||
'mailing': forms.Textarea(attrs={'rows': 4}),
|
||||
'owner': forms.HiddenInput()
|
||||
}
|
||||
help_texts = {
|
||||
'signature': 'Type your name to enter your electronic signature.',
|
||||
'rights_holder_name': 'Enter the rights holder\'s legal name.',
|
||||
}
|
||||
error_messages = {
|
||||
'address': {
|
||||
'required': 'A business address for the rights holder is required.'
|
||||
},
|
||||
'mailing': {
|
||||
'required': 'Enter a mailing address for the rights holder, if different from the \
|
||||
business address.'
|
||||
},
|
||||
'rights_holder_name': {
|
||||
'required': 'Enter the rights holder\'s legal name.'
|
||||
},
|
||||
'signature': {
|
||||
'required': 'Type your name to enter your electronic signature.'
|
||||
},
|
||||
'signer': {
|
||||
'required': 'Please enter the name of the person signing on behalf of the rights \
|
||||
holder.'
|
||||
},
|
||||
'signer_title': {
|
||||
'required': 'Please enter the signer\'s title. (Use \'self\' if you are \
|
||||
personally the rights holder.)'
|
||||
},
|
||||
}
|
||||
|
||||
class UserClaimForm (forms.ModelForm):
|
||||
i_agree = forms.BooleanField(
|
||||
error_messages={'required': 'You must agree to the Terms in order to claim a work.'}
|
||||
)
|
||||
|
||||
def __init__(self, user_instance, *args, **kwargs):
|
||||
super(UserClaimForm, self).__init__(*args, **kwargs)
|
||||
self.fields['rights_holder'] = forms.ModelChoiceField(
|
||||
queryset=user_instance.rights_holder.all(),
|
||||
empty_label=None,
|
||||
)
|
||||
|
||||
def clean_work(self):
|
||||
work = self.cleaned_data.get('work', None)
|
||||
if not work:
|
||||
try:
|
||||
workids = self.data['claim-work']
|
||||
if workids:
|
||||
work = WasWork.objects.get(was = workids[0]).work
|
||||
else:
|
||||
raise forms.ValidationError('That work does not exist.')
|
||||
except WasWork.DoesNotExist:
|
||||
raise forms.ValidationError('That work does not exist.')
|
||||
return work
|
||||
|
||||
class Meta:
|
||||
model = Claim
|
||||
exclude = ('status',)
|
||||
widgets = {
|
||||
'user': forms.HiddenInput,
|
||||
'work': forms.HiddenInput,
|
||||
}
|
||||
|
||||
class EditManagersForm(forms.ModelForm):
|
||||
managers = AutoCompleteSelectMultipleField(
|
||||
OwnerLookup,
|
||||
label='Campaign Managers',
|
||||
widget=AutoCompleteSelectMultipleWidget(OwnerLookup),
|
||||
required=True,
|
||||
error_messages = {'required': "You must have at least one manager for a campaign."},
|
||||
)
|
||||
class Meta:
|
||||
model = Campaign
|
||||
fields = ('id', 'managers')
|
||||
widgets = { 'id': forms.HiddenInput }
|
||||
|
||||
class OpenCampaignForm(forms.ModelForm):
|
||||
managers = AutoCompleteSelectMultipleField(
|
||||
OwnerLookup,
|
||||
label='Campaign Managers',
|
||||
widget=AutoCompleteSelectMultipleWidget(OwnerLookup),
|
||||
required=True,
|
||||
error_messages = {'required': "You must have at least one manager for a campaign."},
|
||||
)
|
||||
userid = forms.IntegerField( required = True, widget = forms.HiddenInput )
|
||||
class Meta:
|
||||
model = Campaign
|
||||
fields = 'name', 'work', 'managers', 'type'
|
||||
widgets = { 'work': forms.HiddenInput, "name": forms.HiddenInput, }
|
||||
|
||||
class CloneCampaignForm(forms.Form):
|
||||
campaign_id = forms.IntegerField(required = True, widget = forms.HiddenInput)
|
||||
|
||||
date_selector = range(date.today().year, settings.MAX_CC_DATE.year+1)
|
||||
|
||||
class CCDateForm(object):
|
||||
target = forms.DecimalField(
|
||||
min_value= D(settings.UNGLUEIT_MINIMUM_TARGET),
|
||||
error_messages={'required': 'Please specify a Revenue Target.'}
|
||||
)
|
||||
minimum_target = settings.UNGLUEIT_MINIMUM_TARGET
|
||||
maximum_target = settings.UNGLUEIT_MAXIMUM_TARGET
|
||||
max_cc_date = settings.MAX_CC_DATE
|
||||
|
||||
def clean_target(self):
|
||||
new_target = self.cleaned_data['target']
|
||||
if new_target < D(settings.UNGLUEIT_MINIMUM_TARGET):
|
||||
raise forms.ValidationError(_(
|
||||
'A campaign may not be launched with a target less than $%s' % settings.UNGLUEIT_MINIMUM_TARGET
|
||||
))
|
||||
if new_target > D(settings.UNGLUEIT_MAXIMUM_TARGET):
|
||||
raise forms.ValidationError(_(
|
||||
'A campaign may not be launched with a target more than $%s' % settings.UNGLUEIT_MAXIMUM_TARGET
|
||||
))
|
||||
return new_target
|
||||
|
||||
def clean_cc_date_initial(self):
|
||||
new_cc_date_initial = self.cleaned_data['cc_date_initial']
|
||||
if new_cc_date_initial.date() > settings.MAX_CC_DATE:
|
||||
raise forms.ValidationError('The initial Ungluing Date cannot be after %s'%settings.MAX_CC_DATE)
|
||||
elif new_cc_date_initial - now() < timedelta(days=0):
|
||||
raise forms.ValidationError('The initial Ungluing date must be in the future!')
|
||||
return new_cc_date_initial
|
||||
|
||||
class DateCalculatorForm(CCDateForm, forms.ModelForm):
|
||||
revenue = forms.DecimalField()
|
||||
cc_date_initial = forms.DateTimeField(
|
||||
widget = SelectDateWidget(years=date_selector)
|
||||
)
|
||||
class Meta:
|
||||
model = Campaign
|
||||
fields = 'target', 'cc_date_initial', 'revenue',
|
||||
|
||||
|
||||
class ManageCampaignForm(CCDateForm, forms.ModelForm):
|
||||
def __init__(self, instance=None , **kwargs):
|
||||
super(ManageCampaignForm, self).__init__(instance=instance, **kwargs)
|
||||
work = instance.work
|
||||
edition_qs = Edition.objects.filter(work=work)
|
||||
self.fields['edition'] = forms.ModelChoiceField(
|
||||
edition_qs,
|
||||
widget=RadioSelect(),
|
||||
empty_label='no edition selected',
|
||||
required=False,
|
||||
)
|
||||
self.fields['target'] = forms.DecimalField(
|
||||
required=(instance.type in {REWARDS, BUY2UNGLUE})
|
||||
)
|
||||
self.fields['deadline'] = forms.DateTimeField(
|
||||
required = (instance.type==REWARDS),
|
||||
widget = SelectDateWidget(years=date_selector) if instance.status=='INITIALIZED' \
|
||||
else forms.HiddenInput
|
||||
)
|
||||
self.fields['cc_date_initial'] = forms.DateTimeField(
|
||||
required = (instance.type==BUY2UNGLUE) and instance.status=='INITIALIZED',
|
||||
widget = SelectDateWidget(years=date_selector) if instance.status=='INITIALIZED' \
|
||||
else forms.HiddenInput
|
||||
)
|
||||
self.fields['publisher'] = forms.ModelChoiceField(
|
||||
instance.work.publishers(),
|
||||
empty_label='no publisher selected',
|
||||
required=False,
|
||||
)
|
||||
if self.initial and not self.initial.get('edition', None) and not instance.edition:
|
||||
self.initial['edition'] = instance.work.editions.all()[0]
|
||||
|
||||
paypal_receiver = forms.EmailField(
|
||||
label=_("contact email address for this campaign"),
|
||||
max_length=100,
|
||||
error_messages={
|
||||
'required': 'You must enter the email we should contact you at for this campaign.'
|
||||
},
|
||||
)
|
||||
work_description = forms.CharField(required=False , widget=CKEditorWidget())
|
||||
|
||||
class Meta:
|
||||
model = Campaign
|
||||
fields = ('description', 'details', 'license', 'target', 'deadline', 'paypal_receiver',
|
||||
'edition', 'email', 'publisher', 'cc_date_initial', "do_watermark", "use_add_ask",
|
||||
)
|
||||
widgets = { 'deadline': SelectDateWidget }
|
||||
|
||||
def clean_target(self):
|
||||
if self.instance.type == THANKS:
|
||||
return None
|
||||
new_target = super(ManageCampaignForm, self).clean_target()
|
||||
if self.instance:
|
||||
if self.instance.status == 'ACTIVE' and self.instance.target < new_target:
|
||||
raise forms.ValidationError(
|
||||
_('The fundraising target for an ACTIVE campaign cannot be increased.')
|
||||
)
|
||||
return new_target
|
||||
|
||||
def clean_cc_date_initial(self):
|
||||
if self.instance.type in {REWARDS, THANKS} :
|
||||
return None
|
||||
if self.instance:
|
||||
if self.instance.status != 'INITIALIZED':
|
||||
# can't change this once launched
|
||||
return self.instance.cc_date_initial
|
||||
return super(ManageCampaignForm, self).clean_cc_date_initial()
|
||||
|
||||
def clean_deadline(self):
|
||||
if self.instance.type in {BUY2UNGLUE, THANKS} :
|
||||
return None
|
||||
new_deadline_date = self.cleaned_data['deadline']
|
||||
new_deadline = new_deadline_date + timedelta(hours=23, minutes=59)
|
||||
if self.instance:
|
||||
if self.instance.status == 'ACTIVE':
|
||||
return self.instance.deadline
|
||||
if new_deadline_date - now() > timedelta(days=int(settings.UNGLUEIT_LONGEST_DEADLINE)):
|
||||
raise forms.ValidationError(_(
|
||||
'The chosen closing date is more than {} days from now'.format(
|
||||
settings.UNGLUEIT_LONGEST_DEADLINE
|
||||
)
|
||||
))
|
||||
elif new_deadline - now() < timedelta(days=0):
|
||||
raise forms.ValidationError(_('The chosen closing date is in the past'))
|
||||
return new_deadline
|
||||
|
||||
def clean_license(self):
|
||||
NO_ADDED_RESTRICTIONS = _(
|
||||
'The proposed license for an ACTIVE campaign may not add restrictions.'
|
||||
)
|
||||
new_license = self.cleaned_data['license']
|
||||
if self.instance:
|
||||
if self.instance.status == 'ACTIVE' and self.instance.license != new_license:
|
||||
# should only allow change to a less restrictive license
|
||||
if self.instance.license == 'CC BY-ND' and new_license in [
|
||||
'CC BY-NC-ND',
|
||||
'CC BY-NC-SA',
|
||||
'CC BY-NC'
|
||||
]:
|
||||
raise forms.ValidationError(NO_ADDED_RESTRICTIONS)
|
||||
elif self.instance.license == 'CC BY' and new_license != 'CC0':
|
||||
raise forms.ValidationError(NO_ADDED_RESTRICTIONS)
|
||||
elif self.instance.license == 'CC BY-NC' and new_license in [
|
||||
'CC BY-NC-ND',
|
||||
'CC BY-NC-SA',
|
||||
'CC BY-SA',
|
||||
'CC BY-ND'
|
||||
]:
|
||||
raise forms.ValidationError(NO_ADDED_RESTRICTIONS)
|
||||
elif self.instance.license == 'CC BY-ND' and new_license in [
|
||||
'CC BY-NC-ND',
|
||||
'CC BY-NC-SA',
|
||||
'CC BY-SA',
|
||||
'CC BY-NC'
|
||||
]:
|
||||
raise forms.ValidationError(NO_ADDED_RESTRICTIONS)
|
||||
elif self.instance.license == 'CC BY-SA' and new_license in [
|
||||
'CC BY-NC-ND',
|
||||
'CC BY-NC-SA',
|
||||
'CC BY-ND',
|
||||
'CC BY-NC'
|
||||
]:
|
||||
raise forms.ValidationError(NO_ADDED_RESTRICTIONS)
|
||||
elif self.instance.license == 'CC BY-NC-SA' and new_license in [
|
||||
'CC BY-NC-ND',
|
||||
'CC BY-ND'
|
||||
]:
|
||||
raise forms.ValidationError(NO_ADDED_RESTRICTIONS)
|
||||
elif self.instance.license == 'CC0' :
|
||||
raise forms.ValidationError(NO_ADDED_RESTRICTIONS)
|
||||
elif self.instance.license in ['GDFL', 'LAL']:
|
||||
raise forms.ValidationError(_(
|
||||
'Once you start a campaign with GDFL or LAL, you can\'t use any other license.'
|
||||
))
|
||||
return new_license
|
||||
|
||||
|
|
@ -36,6 +36,7 @@ base.html extra_css(empty) extra_js(empty) extra_head(empty)
|
|||
about.html
|
||||
about_smashwords.html
|
||||
admins_only.html
|
||||
agreed.html
|
||||
api_help.html
|
||||
ask_rh.html
|
||||
campaign_admin.html extra_extra_head
|
||||
|
@ -69,7 +70,13 @@ base.html extra_css(empty) extra_js(empty) extra_head(empty)
|
|||
press_new.html
|
||||
press_submitterator.html
|
||||
privacy.html
|
||||
programs.html
|
||||
rh_agree.html
|
||||
rh_tools.html extra_extra_head
|
||||
rh_campaigns.html
|
||||
rh_intro.html
|
||||
rh_works.html
|
||||
rh_yours.html
|
||||
rights_holders.html extra_extra_head
|
||||
surveys.html
|
||||
terms.html extra_css
|
||||
|
@ -132,6 +139,8 @@ base.html extra_css(empty) extra_js(empty) extra_head(empty)
|
|||
|
||||
COMPONENT TEMPLATES
|
||||
about_lightbox_footer.html
|
||||
accepted_agreement.html
|
||||
add_your_books.html
|
||||
book_plain.html
|
||||
book_panel_addbutton.html
|
||||
cardform.html
|
||||
|
@ -158,6 +167,7 @@ registration/login_form.html
|
|||
registration/password_reset_email.html
|
||||
registration/registration_closed.html
|
||||
registration/test_template_name.html
|
||||
rights_holder_agreement.html
|
||||
sidebar_pledge_complete.html
|
||||
slideshow.html
|
||||
split.html
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
{% extends "basedocumentation.html" %}
|
||||
{% block title %} Everything You Always Wanted to Know {% endblock %}
|
||||
{% block doccontent %}
|
||||
<h2>About</h2>
|
||||
<p><a href="https://unglue.it">Unglue.it</a> is a service provided by <a href="https://ebookfoundation.org">The Free Ebook Foundation</a> It's a place for individuals and institutions to join together to liberate specific ebooks and other types of digital content by paying authors and publishers to relicense their works under <a href="https://creativecommons.org">Creative Commons</a> licenses.</p>
|
||||
|
||||
<p>What does this mean?</p>
|
||||
<ul>
|
||||
<li>Book-lovers and libraries everywhere can join together to set books free.</li>
|
||||
<li>Authors and publishers get the compensation they deserve.</li>
|
||||
<li>Books that are out of print, not available as ebooks, or otherwise hard to enjoy will be available for everyone to read, share, learn from, and love -- freely and legally.</li>
|
||||
</ul>
|
||||
|
||||
<p>You can learn more about us in our <a href="{% url 'faq' %}">FAQs</a> and our <a href="{% url 'press' %}">press page</a>.
|
||||
<h3>Team</h3>
|
||||
<div class="pressimages">
|
||||
<div class="outer">
|
||||
<div><img src="/static/images/headshots/eric.jpg" class="mediaborder" alt="eric" /></div>
|
||||
<div class="text"><b>Eric Hellman</b>, President of the Free Ebook Foundation, is a technologist, entrepreneur, and writer. After 10 years at Bell Labs in physics research, Eric became interested in technologies surrounding e-journals and libraries. His first business, Openly Informatics, developed OpenURL linking software and knowledgebases, and was acquired by OCLC in 1996. At OCLC, he led the effort to productize and expand the xISBN service, and began the development of OCLC's Electronic Resource Management offerings. After leaving OCLC, Eric began blogging at <a href="https://go-to-hellman.blogspot.com">Go To Hellman</a>. He covers the intersection of technology, libraries and ebooks, and has written extensively on the Semantic Web and Linked Data. Eric has a B.S.E. from Princeton University, and a Ph. D. in Electrical Engineering from Stanford University</div>
|
||||
</div>
|
||||
|
||||
<div class="outer">
|
||||
<div><img src="/static/images/headshots/amanda2.jpg" class="mediaborder" alt="amanda" /></div>
|
||||
<div class="text"><b>Amanda Mecke</b> is an expert in literary rights management. Before founding her own <a href="http://www.ameckeco.com/">literary agency</a>, Amanda was VP, Director of Subsidiary Rights for Bantam Dell, a division of Random House Inc. from 1989-2003, where she led a department that sold international and domestic book rights and pioneered early electronic licenses for subscription databases, CD-ROMs, audiobooks, and ebooks. She was also a co-leader of the Random House/SAP Contracts and Royalties software development team. Prior to joining Bantam Dell, Amanda ran the New York marketing office of the University of California Press. While there she served the board of the American Association of University Presses and was President of Women in Scholarly Publishing. Amanda has been a speaker at the Frankfurt Book Messe Rights Workshop, NYU Summer Publishing Program, American Independent Writers conference, and the International Women’s Writers Guild. She has a B.A. from Pitzer College, Claremont, California and a Ph.D. in English from UCLA. Amanda continues to represent original work by her literary agency clients.<br /><br />
|
||||
|
||||
Amanda will be spending much of her time reaching out to authors, publishers, and other rights holders and identifying works that will attract financial support from book lovers who want to see the ebooks available for free to anyone, anywhere. </div>
|
||||
</div>
|
||||
|
||||
<div class="outer">
|
||||
<div><img src="/static/images/headshots/raymond.jpg" class="mediaborder" alt="raymond" /></div>
|
||||
<div class="text"><b>Raymond Yee</b> is a data architect, author, consultant, and teacher. He is author of the leading book on web mashups, <a href="http://blog.mashupguide.net/2008/02/29/the-book-is-available-now/">Pro Web 2.0 Mashups: Remixing Data and Web Services</a> (published by Apress and licensed under a Creative Commons license), and has numerous blogs at his <a href="http://raymondyee.net/">personal site</a>. At the UC Berkeley School of Information, he taught Mixing and Remixing Information, a course on using APIs to create mashups. An open data and open government afficionado, he recently co-wrote three influential reports on how the US government can improve its efforts to make data and services available through APIs. Raymond served as the Integration Advisor for the Zotero Project (a widely used open source research tool) and managed the <a href="https://www.archive.org/details/zoterocommons">Zotero Commons</a>, a collaboration between George Mason University and the Internet Archive. Raymond has been an invited speaker about web technology at the Library of Congress, Fashion Institute of Technology, the O'Reilly Emerging Technology Conference, American Library Association, the Open Education conference, Code4lib, Educause, and NISO. While earning a Ph.D. in biophysics, he taught computer science, philosophy, and personal development to middle and high school students in the Academic Talent Development Program on the Berkeley campus. Raymond is an erstwhile tubaist, admirer of J. S. Bach, and son of industrious Chinese-Canadian restaurateurs.<br /><br />
|
||||
|
||||
Recently, Raymond has been teaching a course at UC Berkeley's School of Information science on working with Open Data; with any luck, the textbook he's working on will be a subject of a future ungluing campaign!</div>
|
||||
</div>
|
||||
|
||||
<h3>Past Team</h3>
|
||||
<div class="outer">
|
||||
<div class="text"><b>Andromeda Yelton</b> was one of Unglue.it's founding staff members; she's gone on to pursue another passion, <a href="https://blog.unglue.it/2013/07/24/teaching-libraries-to-code/">teaching libraries to code</a>. She blogs at <a href="http://andromedayelton.com/">Across Divided Networks</a>. Check out her web site if you think she can help you!</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="outer">
|
||||
<div class="text"><b><a href="http://www.designanthem.com/">Design Anthem</a></b> helped us make the site pretty. </div>
|
||||
</div>
|
||||
|
||||
<div class="outer">
|
||||
<div class="text"><b>Jason Kace</b> wrote code. <b>Ed Summers</b> wrote some more code. Both of them helped us write even more code.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
<ul>
|
||||
<li>Make sure your book meets Smashwords' <a href="http://www.smashwords.com/distribution">standards for inclusion</a> in their Premium Catalog. Consult the <a href="http://www.smashwords.com/books/view/52">Smashwords Style Guide</a> for details.</li>
|
||||
<li>Contact <a href="mailto:rights@gluejar.com">rights@gluejar.com</a> about signing a Platform Services Agreement with Unglue.it, and follow the instructions on the <a href="/rightsholders/">rights holder tools page</a> to claim your book, develop a publicity strategy, and start a campaign.</li>
|
||||
<li>Contact <a href="mailto:rights@ebookfoundation.org">rights@ebookfoundation.org</a> about signing a Platform Services Agreement with Unglue.it, and follow the instructions on the <a href="/rightsholders/">rights holder tools page</a> to claim your book, develop a publicity strategy, and start a campaign.</li>
|
||||
</ul>
|
||||
|
||||
<h2>Why Unglue Your Book?</h2>
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
{% with form=rights_holder rights_holder_name=rights_holder.rights_holder_name signer_title=rights_holder.signer_title owner=rights_holder.owner.username created=rights_holder.created fef_sig='ERIC HELLMAN' %}
|
||||
{% include 'rights_holder_agreement.html' %}
|
||||
{% endwith %}
|
|
@ -0,0 +1,20 @@
|
|||
<p id="add_your_books"><b>Claiming a work</b></p>
|
||||
<p>If your book is indexed in Google books, we can add it to our database automagically. Click on the result list to add your book to our database.</p>
|
||||
<form action="{% url 'search' %}" method="get">
|
||||
<div class="inputalign">
|
||||
<input type="text" id="watermark" size="25" onfocus="imgfocus()" onblur="imgblur(15)" class="inputbox" name="q" value="{{ q }}"><input type="hidden" name="page" value="2">
|
||||
<input type="submit" class="button">
|
||||
</div>
|
||||
</form>
|
||||
<ul class="bullets">
|
||||
<li>Use the Claim option on the More... tab of each book's page.</li>
|
||||
<li>Agree to our <a href="{% url 'terms' %}">Terms</a> on the following page. This includes agreeing that you are making the claim in good faith.</li>
|
||||
<li>If you have any questions or you claim a work by mistake, <a href="mailto:rights@ebookfoundation.org?subject=claiming%20works">email us</a>.</li>
|
||||
</ul>
|
||||
<p><b>Entering a work</b></p>
|
||||
<p>If your book is not in Google books, you'll need to enter the metadata yourself.</p>
|
||||
<ul class="bullets">
|
||||
<li>Use <a href="{% url 'new_edition' '' '' %}">this form</a> to enter the metadata.</li>
|
||||
<li>Your metadata should have title, authors, language, description.</li>
|
||||
<li>If you have a lot of books to enter, <a href="{% url 'feedback' %}?page={{request.build_absolute_uri|urlencode:""}}">contact us</a> to load ONIX or CSV files for you.</li>
|
||||
</ul>
|
|
@ -0,0 +1,19 @@
|
|||
{% extends 'basedocumentation.html' %}
|
||||
|
||||
{% block title %} Agreement Submitted {% endblock %}
|
||||
{% block extra_extra_head %}
|
||||
{{ block.super }}
|
||||
<link rel="stylesheet" href="/static/css/ui-lightness/jquery-ui-1.8.16.custom.css" type="text/css" media="screen">
|
||||
<script type="text/javascript" src="{{ jquery_ui_home }}"></script>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block topsection %}
|
||||
{% endblock %}
|
||||
|
||||
{% block doccontent %}
|
||||
<h2>You're a pending Unglue.it Rights Holder!</h2>
|
||||
|
||||
Your Unglue.it Rights Holder Agreement has been submitted. Our staff will verify your existence and email you a countersigned agreement. At that point, you'll be able to use all of the Unglue.it rights holder tools.
|
||||
|
||||
{% endblock %}
|
|
@ -1,14 +1,16 @@
|
|||
<!DOCTYPE html>
|
||||
{% load truncatechars %}
|
||||
{% load sass_tags %}
|
||||
{% load truncatechars %}{% load sass_tags %}
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="referrer" content="origin" />
|
||||
<title>unglue.it {% block title %}{% endblock %}</title>
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/sitewide4.css" />
|
||||
<link REL="SHORTCUT ICON" HREF="/static/images/favicon.ico">
|
||||
<meta name="twitter:card" content="summary" />
|
||||
<meta name="twitter:site" content="@unglueit" />
|
||||
{% block extra_meta %}{% endblock %}
|
||||
<link type="text/css" rel="stylesheet" href="{% sass_src 'scss/sitewide4.scss' %}" />
|
||||
{% block extra_css %}{% endblock %}
|
||||
|
||||
<link href="{% sass_src 'scss/global.scss' %}" rel="stylesheet" type="text/css" />
|
||||
|
@ -133,25 +135,32 @@
|
|||
{% block content %}{% endblock %}
|
||||
|
||||
{% block footer %}
|
||||
<div id="footer">
|
||||
<div class="js-main">
|
||||
<div class="footer hide utilityheaders">
|
||||
<ul>
|
||||
<li><a href="{% url 'about' %}">Team</a></li>
|
||||
<li><a href="https://blog.unglue.it">Blog</a></li>
|
||||
<li><a href="{% url 'terms' %}">Terms of Use</a></li>
|
||||
<li><a href="{% url 'faq' %}">General FAQ</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer utilityheaders">
|
||||
<div class="column">
|
||||
<span>About Unglue.it</span>
|
||||
<ul>
|
||||
<li><a href="{% url 'about_specific' 'main' %}" class="hijax">Concept</a></li>
|
||||
<li><a href="{% url 'about' %}">Team</a></li>
|
||||
<li><a href="https://blog.unglue.it">Blog</a></li>
|
||||
<li><a href="{% url 'press' %}">Press</a></li>
|
||||
<li><a href="http://eepurl.com/fKLfI">Newsletter</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="column">
|
||||
<div class="column show-for-medium">
|
||||
<span>Your account</span>
|
||||
<ul>
|
||||
{% if user.is_authenticated %}
|
||||
<li><a href="{% url 'manage_account' %}">Account Settings</a></li>
|
||||
<li><a href="{% url 'new_edition' '' '' %}">Add Books to Unglue.it</a></li>
|
||||
{% endif %}
|
||||
<li><a href="{% url 'rightsholders' %}">Rights Holder Tools</a></li>
|
||||
<li><a href="{% url 'rightsholders' %}">For Rights Holders</a></li>
|
||||
<li><a href="{% url 'privacy' %}">Privacy</a></li>
|
||||
<li><a href="{% url 'terms' %}">Terms of Use</a></li>
|
||||
{% for library in user.libraries.all %}
|
||||
|
@ -159,7 +168,6 @@
|
|||
{% endfor %}
|
||||
{% if user.is_staff %}
|
||||
<li><a href="{% url 'rh_admin' %}">Unglue.it Administration</a></li>
|
||||
<li><a href="{% url 'new_edition' '' '' %}">Create New Editions</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
|
@ -170,19 +178,15 @@
|
|||
<li><a href="{% url 'faq_location' 'rightsholders' %}">Author/Publisher FAQ</a></li>
|
||||
<li><a href="{% url 'api_help' %}">API</a></li>
|
||||
<li><a href="{% url 'feedback' %}?page={{request.build_absolute_uri|urlencode:""}}">Support</a>
|
||||
<li><a href="{% url 'libraries' %}">Unglue.it ♥ Libraries</a>
|
||||
<li><a href="{% url 'libraries' %}">Unglue.it for Libraries</a>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="column">
|
||||
<div class="column show-for-medium">
|
||||
<span>Contact</span>
|
||||
<ul>
|
||||
<li>General inquiries</li>
|
||||
<li><a href="mailto:faq@gluejar.com">faq@gluejar.com</a></li>
|
||||
<li>Rights Holders</li>
|
||||
<li><a href="mailto:rights@gluejar.com">rights@gluejar.com</a></li>
|
||||
<li> <a href="mailto:info@ebookfoundation.org"><i class="fa fa-envelope fa-2x"></i></a> <a href="https://twitter.com/unglueit"><i class="fa fa-twitter fa-2x"></i></a> <a href="https://facebook/com/unglueit"><i class="fa fa-facebook fa-2x"></i></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
</div>
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
{% extends "registration/registration_base.html" %}
|
||||
|
||||
{% load sass_tags %}
|
||||
{% block extra_js %}
|
||||
|
||||
<script type="text/javascript" src="/static/js/definitions.js"></script>
|
||||
{% endblock %}
|
||||
{% block extra_extra_head %}
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/landingpage4.css" />
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/book_panel2.css" />
|
||||
<link type="text/css" rel="stylesheet" href="{% sass_src 'scss/landingpage4.scss' %}" />
|
||||
<link type="text/css" rel="stylesheet" href="{% sass_src 'scss/book_panel2.scss' %}" />
|
||||
<script type="text/javascript" src="/static/js/greenpanel.js"></script>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
{% extends "registration/registration_base.html" %}
|
||||
{% load sass_tags %}
|
||||
|
||||
{% block extra_css %}
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/campaign2.css" />
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/pledge.css" />
|
||||
<link type="text/css" rel="stylesheet" href="{% sass_src 'scss/campaign2.scss' %}" />
|
||||
<link type="text/css" rel="stylesheet" href="{% sass_src 'scss/pledge.scss' %}" />
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
{% load purchased %}
|
||||
{% load lib_acqs %}
|
||||
{% load bookpanel %}
|
||||
{% with first_ebook=work.first_ebook supporters=work.last_campaign.supporters thumbnail=work.cover_image_thumbnail author=work.authors_short title=work.title last_campaign=work.last_campaign status=work.last_campaign.status deadline=work.last_campaign.deadline workid=work.id wishlist=request.user.wishlist.works.all %}
|
||||
{% with first_ebook=work.first_ebook thumbnail=work.cover_image_thumbnail author=work.authors_short title=work.title last_campaign=work.last_campaign status=work.last_campaign.status deadline=work.last_campaign.deadline workid=work.id wishlist=request.user.wishlist.works.all %}
|
||||
{% purchased %}{% lib_acqs %}{% bookpanel %}
|
||||
<div class="thewholebook listview tabs {% if tab_override %}{{tab_override}}{% elif first_ebook or status == 'SUCCESSFUL' %}tabs-1{% elif status == 'ACTIVE' %}tabs-2{% else %}tabs-3{% endif %}">
|
||||
<div class="listview book-list">
|
||||
|
@ -109,7 +109,7 @@
|
|||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% if request.user.id in supporters %}
|
||||
{% if not supported %}
|
||||
<div class="white_text bottom_button">
|
||||
{% include "book_panel_addbutton.html" %}
|
||||
</div>
|
||||
|
@ -119,7 +119,7 @@
|
|||
</div>
|
||||
<div class="white_text bottom_button" >
|
||||
{% if work.last_campaign.type == 1 %}
|
||||
<a href="{% url 'pledge' work_id=workid %}"><span class="read_itbutton pledge button_text"><span>Pledge</span></span></a>
|
||||
<a href="{% url 'pledge' work_id=workid %}"><span class="read_itbutton pledge button_text"><span>Support</span></span></a>
|
||||
{% elif work.last_campaign.type == 2 %}
|
||||
{% if in_library %}
|
||||
<a href="{% url 'purchase' work_id=workid %}"><span class="read_itbutton pledge button_text"><span>Reserve It</span></span></a>
|
||||
|
@ -219,7 +219,7 @@
|
|||
{% endif %}
|
||||
{% elif show_pledge %}
|
||||
<div class="listview panelfront side1 add-wishlist">
|
||||
<span class="booklist_pledge"><a href="{% url 'pledge' work_id=workid %}" class="fakeinput">Pledge</a></span>
|
||||
<span class="booklist_pledge"><a href="{% url 'pledge' work_id=workid %}" class="fakeinput">Support</a></span>
|
||||
</div>
|
||||
{% elif show_purchase %}
|
||||
<div class="listview panelfront side1 add-wishlist">
|
||||
|
@ -241,8 +241,8 @@
|
|||
<span>{% if purchased.gifts.all.count %}A gift to you!{% else %}Purchased!{% endif %}</span>
|
||||
{% elif borrowed %}
|
||||
<span>Borrowed! ...until</span>
|
||||
{% elif request.user.id in supporters %}
|
||||
<span>Pledged!</span>
|
||||
{% elif supported %}
|
||||
<span>Supported!</span>
|
||||
{% else %}
|
||||
<span>Faved!</span>
|
||||
{% endif %}
|
||||
|
|
|
@ -12,9 +12,9 @@
|
|||
<div class="moreinfo create-account">
|
||||
<span title="{% if workid %}{% url 'work' workid %}{% else %}{% url 'googlebooks' googlebooks_id %}{% endif %}">Login to Fave</span>
|
||||
</div>
|
||||
{% elif request.user.id in supporters %}
|
||||
{% elif supported %}
|
||||
<div class="moreinfo on-wishlist">
|
||||
<a href="{% url 'work' workid %}">Pledged!</a>
|
||||
<a href="{% url 'work' workid %}">Supported!</a>
|
||||
</div>
|
||||
{% elif supporter == request.user %}
|
||||
{% if wishlist %}
|
||||
|
|
|
@ -2,13 +2,14 @@
|
|||
|
||||
{% load endless %}
|
||||
{% load lang_utils %}
|
||||
{% load sass_tags %}
|
||||
|
||||
{% block title %} {{ facet_label }} Campaigns {% endblock %}
|
||||
{% block extra_css %}
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/supporter_layout.css" />
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/book_list.css" />
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/book_panel2.css" />
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/lists.css">
|
||||
<link type="text/css" rel="stylesheet" href="{% sass_src 'scss/supporter_layout.scss' %}" />
|
||||
<link type="text/css" rel="stylesheet" href="{% sass_src 'scss/book_list.scss' %}" />
|
||||
<link type="text/css" rel="stylesheet" href="{% sass_src 'scss/book_panel2.scss' %}" />
|
||||
<link type="text/css" rel="stylesheet" href="{% sass_src 'scss/lists.scss' %}">
|
||||
{% endblock %}
|
||||
{% block extra_head %}
|
||||
<script>
|
||||
|
|
|
@ -2,13 +2,14 @@
|
|||
|
||||
{% load endless %}
|
||||
{% load lang_utils %}
|
||||
{% load sass_tags %}
|
||||
|
||||
{% block title %} Creative Commons {{license}} Books {% if pub_lang %} in {{pub_lang|ez_lang_name}}{% endif %}{% endblock %}
|
||||
{% block extra_css %}
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/supporter_layout.css" />
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/book_list.css" />
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/book_panel2.css" />
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/lists.css" />
|
||||
<link type="text/css" rel="stylesheet" href="{% sass_src 'scss/supporter_layout.scss' %}" />
|
||||
<link type="text/css" rel="stylesheet" href="{% sass_src 'scss/book_list.scss' %}" />
|
||||
<link type="text/css" rel="stylesheet" href="{% sass_src 'scss/book_panel2.scss' %}" />
|
||||
<link type="text/css" rel="stylesheet" href="{% sass_src 'scss/lists.scss' %}" />
|
||||
{% endblock %}
|
||||
{% block extra_head %}
|
||||
<script type="text/javascript" src="/static/js/wishlist.js"></script>
|
||||
|
|
|
@ -29,6 +29,10 @@
|
|||
{% endifequal %}
|
||||
{% endfor %}
|
||||
|
||||
<h3> Interfering claims </h3>
|
||||
<p>
|
||||
When a rights holder claims a work in unglue.it, they warrant and represent that they have sufficient rights to administer the work. Mistakes and umisunderstandings can occur, especially for older works with unclear contracts and hard-to-find rights holders. For that reason, rights holders also agree to respond promptly to rights inquiries, and unglue.it reserves the right to suspend campaigns if disputes over rights arise. If you have a question about claims for this work, please contact rights@ebookfoundation.org
|
||||
</p>
|
||||
{% else %}
|
||||
|
||||
<form method="POST" action="#">
|
||||
|
@ -38,8 +42,4 @@
|
|||
<input type="submit" name="submit" value="Confirm Claim">
|
||||
</form>
|
||||
{% endif %}
|
||||
<h3> Interfering claims </h3>
|
||||
<p>
|
||||
When a rights holder claims a work in unglue.it, they warrant and represent that they have sufficient rights to release a Creative Commons edition of that work. Unfortunately, mistakes can occur, especially for older works with unclear contracts and hard-to-find rights holders. For that reason, rights holders also agree to respond promptly to rights inquiries, and unglue.it reserves the right to suspend campaigns when disputes over rights arise. If you have a question about claims for this work, please contact rights@gluejar.com
|
||||
</p>
|
||||
{% endblock %}
|
|
@ -1,3 +1,3 @@
|
|||
<h3> Terms and Conditions for Claiming Works </h3>
|
||||
|
||||
<p>By claiming this work, you agree that your claim is governed by a Platform Services Agreement in effect between you (or an entity that you act as agent for) and the Free Ebook Foundation, Inc., the operator of the Unglue.it website, and by the <a href="{% url 'terms' %}">unglue.it Website Terms </a>.</p>
|
||||
<p>By claiming this work, you agree that your claim is governed by a Rights Holder Agreement in effect between you (or an entity that you act as agent for) and the Free Ebook Foundation, Inc., the operator of the Unglue.it website, and by the <a href="{% url 'terms' %}">unglue.it Website Terms </a>.</p>
|
|
@ -1,9 +1,11 @@
|
|||
{% extends 'base.html' %}
|
||||
|
||||
{% load sass_tags %}
|
||||
|
||||
{% block title %} Comments {% endblock %}
|
||||
{% block extra_css %}
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/supporter_layout.css" />
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/comments.css" />
|
||||
<link type="text/css" rel="stylesheet" href="{% sass_src 'scss/supporter_layout.scss' %}" />
|
||||
<link type="text/css" rel="stylesheet" href="{% sass_src 'scss/comments.scss' %}" />
|
||||
{% endblock %}
|
||||
{% block extra_head %}
|
||||
<script type="text/javascript" src="/static/js/wishlist.js"></script>
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
{% extends 'comments/base.html' %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load sass_tags %}
|
||||
|
||||
{% block title %}{% trans "Preview your comment" %}{% endblock %}
|
||||
{% block extra_css %}
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/campaign2.css" />
|
||||
<link type="text/css" rel="stylesheet" href="{% sass_src 'scss/campaign2.scss' %}" />
|
||||
{% endblock %}
|
||||
|
||||
{% block doccontent %}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
{% extends 'base.html' %}
|
||||
|
||||
{% load humanize %}
|
||||
{% load sass_tags %}
|
||||
|
||||
{% with work.title as title %}
|
||||
{% block title %}
|
||||
— Downloads for {{ work.title }}
|
||||
|
@ -8,6 +10,7 @@
|
|||
|
||||
{% block extra_js %}
|
||||
<script type="text/javascript" src="/static/js/embed.js"></script>
|
||||
<!-- select {% sass_src 'scss/enhanced_download.scss' %} or {% sass_src 'scss/enhanced_download_ie.scss' %} -->
|
||||
<script type="text/javascript" src="/static/js/download_page.js"></script>
|
||||
<script type="text/javascript">
|
||||
var $j = jQuery.noConflict();
|
||||
|
|
|
@ -3,16 +3,18 @@
|
|||
<ul>
|
||||
{% for ebook in work.ebooks_all %}
|
||||
<li>
|
||||
<a href="{{ ebook.url }}">{{ ebook.format }}, {{ ebook.rights }}</a>, created {{ ebook.created }}{% if ebook.user %},
|
||||
<a href="{{ ebook.url }}">{{ ebook.format }}{%if ebook.rights %}, {{ ebook.rights }}{% endif %}</a>, created {{ ebook.created }}{% if ebook.user %},
|
||||
by <a href="{% url 'supporter' ebook.user_id %}">{{ ebook.user }}</a>{% endif %}.
|
||||
{% if ebook.filesize %}{{ ebook.filesize }}{% else %}??{% endif %}B
|
||||
{% if ebook.version_label %}{{ ebook.version }}{% endif %}
|
||||
{% if ebook.active %}<input type="submit" name="deactivate_ebook_{{ ebook.id }}" value="deactivate" class="deletebutton" title="deactivate ebook" />{% else %}<input type="submit" name="activate_ebook_{{ ebook.id }}" value="activate" class="deletebutton" title="activate ebook" />{% endif %}
|
||||
{% if ebook.active %}<input type="submit" name="deactivate_ebook_{{ ebook.id }}" value="deactivate" class="deletebutton" title="deactivate ebook" />{% else %}<input type="submit" name="activate_ebook_{{ ebook.id }}" value="activate" class="deletebutton" title="activate ebook" />{% endif %}{% if request.user.is_staff %} <a href="{% url 'admin:core_ebook_change' ebook.id %}">admin</a>{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<br />
|
||||
<br />
|
||||
<input type="submit" name="activate_all_ebooks" value="activate all ebooks" class="deletebutton" title="activate all ebooks" />
|
||||
<input type="submit" name="deactivate_all_ebooks" value="deactivate all ebooks" class="deletebutton" title="deactivate all ebooks" />
|
||||
<br />
|
||||
<br />
|
||||
{{ form.set_rights}}<input type="submit" name="set_ebook_rights" value="set ebook licenses" class="deletebutton" title="set ebook licenses" />
|
||||
{% endif %}
|
|
@ -1,7 +1,6 @@
|
|||
<ul class="terms">
|
||||
<li><i>For Thanks-For-Ungluing Campaigns</i>, the rightsholder is responsible for providing valid, high-quality EPUB, MOBI, and PDF files.</li>
|
||||
<li><i>For Buy-To-Unglue Campaigns</i> and <i>Pledge Campaigns</i> ebook files should use the EPUB standard format format according to best practice at time of submission. At minimum, the files should pass the <a href=https://code.google.com/p/epubcheck/">epubcheck</a> tool. Exceptions will be made for content requiring more page layout than available in prevailing EPUB implementations; in those cases, the files will usually be PDF.</li>
|
||||
<li>Files shall be compliant where ever reasonable with the QED inspection checklist specified at <a href="http://qed.digitalbookworld.com/why-qed/criteria">http://qed.digitalbookworld.com/why-qed/criteria</a> </li>
|
||||
<li>The file should have no more than 0.2 typographical or scanning errors per page of English text.</li>
|
||||
<li>The file shall contain front matter which includes the following:
|
||||
<ul class="terms">
|
||||
|
|
|
@ -32,12 +32,6 @@
|
|||
{% if edition.oclc %}
|
||||
OCLC: <a href="https://www.worldcat.org/oclc/{{ edition.oclc }}">{{ edition.oclc }}</a><br />
|
||||
{% endif %}
|
||||
{% if edition.doi %}
|
||||
DOI: <a href="https://doi.org/{{ edition.doi }}">{{ edition.doi }}</a><br />
|
||||
{% endif %}
|
||||
{% if edition.http_id %}
|
||||
web: <a href="{{ edition.http_id }}">{{ edition.http_id }}</a><br />
|
||||
{% endif %}
|
||||
{% if not managing %}
|
||||
{% if user_can_edit_work %}
|
||||
<a href="{% url 'new_edition' work_id edition.id %}">Edit this edition</a><br />
|
||||
|
|
|
@ -9,10 +9,6 @@
|
|||
<style type="text/css">
|
||||
.header-text{height:36px;line-height:36px;display:block;text-decoration:none;font-weight:bold;letter-spacing:-0.05em}
|
||||
.panelborders{border-width:1px 0;border-style:solid none;border-color:#fff}
|
||||
.roundedspan{border:1px solid #d4d4d4;-moz-border-radius:7px;-webkit-border-radius:7px;border-radius:7px;padding:1px;color:#fff;margin:0 8px 0 0;display:inline-block}
|
||||
.roundedspan>span{padding:7px 7px;min-width:15px;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;text-align:center;display:inline-block}
|
||||
.roundedspan>span .hovertext{display:none}
|
||||
.roundedspan>span:hover .hovertext{display:inline}
|
||||
.mediaborder{padding:5px;border:solid 5px #edf3f4}
|
||||
.actionbuttons{width:auto;height:36px;line-height:36px;background:#8dc63f;-moz-border-radius:32px;-webkit-border-radius:32px;border-radius:32px;color:white;cursor:pointer;font-size:13px;font-weight:bold;padding:0 15px;border:0;margin:5px 0}
|
||||
.header-text{height:36px;line-height:36px;display:block;text-decoration:none;font-weight:bold;letter-spacing:-0.05em}
|
||||
|
@ -44,7 +40,6 @@
|
|||
.launch_top.alert{border-color:#e35351;font-size:13px}
|
||||
.preview_content{border:solid 3px #e35351;-moz-border-radius:7px;-webkit-border-radius:7px;border-radius:7px;clear:both;padding:5px 10px;font-size:13px;width:90%;width:80%;margin:10px auto}
|
||||
.preview_content a{color:#8dc63f}
|
||||
.utilityheaders{text-transform:uppercase;color:#3d4e53;font-size:15px;display:block}
|
||||
html,body{height:100%}
|
||||
body{padding:0 0 20px 0;margin:0;font-size:13px;line-height:16.900000000000002px;font-family:"Lucida Grande","Lucida Sans Unicode","Lucida Sans",Arial,Helvetica,sans-serif;color:#3d4e53}
|
||||
a{font-weight:bold;font-size:inherit;text-decoration:none;cursor:pointer;color:#6994a3}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
{% load truncatechars %}
|
||||
{% load lang_utils %}
|
||||
{% load explore %}{% explore %}
|
||||
<div class="jsmodule">
|
||||
<h3 class="jsmod-title"><span>Explore</span></h3>
|
||||
<div class="jsmod-content">
|
||||
|
@ -32,8 +33,26 @@
|
|||
</ul>
|
||||
{% endif %}
|
||||
</li>
|
||||
<li><a href="{% url 'campaign_list' 'ending' %}"><span>Active Campaigns</span></a></li>
|
||||
{% if top_pledge %}
|
||||
<li>Pledge to...
|
||||
<ul class="menu level3">
|
||||
{% for campaign in top_pledge %}
|
||||
<li><a href="{% url 'work' campaign.work.id %}">
|
||||
<img class="thumbnail" src="{{ campaign.work.cover_image_thumbnail }}" title="{{ campaign.work.title }}"/>
|
||||
<span class="thumbnail-caption">{{ campaign.work.title }}</span>
|
||||
</a></li>
|
||||
{% if forloop.counter == 4 %}
|
||||
<li><a href="{% url 'campaign_list' 'pledge' %}"><span>...more</span></a></li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
{% endif %}
|
||||
<li><a href="{% url 'campaign_list' 'b2u' %}"><span>Buy to unglue...</span></a></li>
|
||||
<li><a href="{% url 'campaign_list' 't4u' %}"><span>Thank for ...</span></a></li>
|
||||
{% if not request.user.is_anonymous %}
|
||||
<li><a href="{% url 'comment' %}"><span>Latest Comments</span></a></li>
|
||||
{% endif %}
|
||||
<li><a href="{% url 'work_list' 'popular' %}"><span>Site Favorites</span></a></li>
|
||||
<li><a href="{% url 'work_list' 'new' %}"><span>Latest Favorites</span></a></li>
|
||||
<!--<li><a href="{% url 'work_list' 'recommended' %}"><span>Noteworthy</span></a></li>-->
|
||||
|
|
|
@ -2,13 +2,14 @@
|
|||
|
||||
{% load endless %}
|
||||
{% load lang_utils %}
|
||||
{% load sass_tags %}
|
||||
|
||||
{% block title %} Browse Free Books {% endblock %}
|
||||
{% block extra_css %}
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/supporter_layout.css" />
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/book_list.css" />
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/book_panel2.css" />
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/lists.css" />
|
||||
<link type="text/css" rel="stylesheet" href="{% sass_src 'scss/supporter_layout.scss' %}" />
|
||||
<link type="text/css" rel="stylesheet" href="{% sass_src 'scss/book_list.scss' %}" />
|
||||
<link type="text/css" rel="stylesheet" href="{% sass_src 'scss/book_panel2.scss' %}" />
|
||||
<link type="text/css" rel="stylesheet" href="{% sass_src 'scss/lists.scss' %}" />
|
||||
{% endblock %}
|
||||
{% block extra_head %}
|
||||
<script type="text/javascript" src="/static/js/wishlist.js"></script>
|
||||
|
|
|
@ -7,5 +7,11 @@
|
|||
{% if facet.facet_name == 'doab' %}
|
||||
These books are included in the <a href="http://doabooks.org/">Directory of Open Access Books</a>. This means that they have been peer-reviewed and are of interest to scholars.
|
||||
{% endif %}
|
||||
{% if facet.facet_name == '-gtbg' %}
|
||||
These books are not included in <a href="https://www.gutenberg.org">Project Gutenberg</a>.
|
||||
{% endif %}
|
||||
{% if facet.facet_name == '-doab' %}
|
||||
These books do not seem to be included in the <a href="http://doabooks.org/">Directory of Open Access Books</a>.
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
|
@ -0,0 +1,5 @@
|
|||
<div>
|
||||
<p style="">
|
||||
These books were faved by <a href="{% url 'supporter' facet.username %}"> {{ facet.username }}</a>
|
||||
</p>
|
||||
</div>
|
|
@ -16,48 +16,37 @@
|
|||
</dl>
|
||||
{% endif %}
|
||||
{% if location == 'basics' or location == 'faq' %}
|
||||
<h3>Basics</h3>
|
||||
<h3>About the site</h3>
|
||||
{% if sublocation == 'howitworks' or sublocation == 'all' %}
|
||||
<h4>How It Works</h4>
|
||||
<h4>About Unglue.it</h4>
|
||||
<dl>
|
||||
<dt>What is Unglue.it?</dt>
|
||||
|
||||
<dd><a href="/">Unglue.it</a> is a place for individuals and institutions to join together to make ebooks free to the world. We work together to support authors, publishers, or other rights holders who want their ebooks to be free. We support <a href="https://creativecommons.org">Creative Commons</a> licensing as an enabling tool to "unglue" the ebooks. </dd>
|
||||
<dd><a href="/">Unglue.it</a> is a place for individuals and institutions to join together in support of free ebooks. We work together to support authors, publishers, or other rights holders who want their ebooks to be free. We work together to provide durable and effective distribution for ebooks that are free. We support <a href="https://creativecommons.org">Creative Commons</a> licensing as an enabling tool to "unglue" the ebooks. We are part of the <a href="https://ebookfoundation.org">Free Ebook Foundation</a>, a non-profit charitable organization.</dd>
|
||||
|
||||
<dt>What are Ungluing Campaigns?</dt>
|
||||
<dt>Why are all these ebooks free?</dt>
|
||||
|
||||
<dd>We have three types of Ungluing Campaigns: Pledge Campaigns, Buy-to-Unglue Campaigns and Thanks-for-Ungluing campaigns.
|
||||
<ul class="bullets">
|
||||
<li> In a <i><a href="{% url 'faq_sublocation' 'campaigns' 'supporting' %}">Pledge Campaign</a></i>, book lovers pledge their support for ungluing a book. If enough support is found to reach the goal (and only then), the supporter's credit cards are charged, and an unglued ebook is released.</li>
|
||||
<li> In a <i><a href="{% url 'faq_sublocation' 'campaigns' 'b2u' %}">Buy-to-Unglue Campaign</a></i>, every ebook copy sold moves the book's ungluing date closer to the present. And you can donate ebooks to your local library- that's something you can't do in the Kindle or Apple Stores!</li>
|
||||
<li> In a <i><a href="{% url 'faq_sublocation' 'campaigns' 't4u' %}">Thanks-for-Ungluing Campaign</a></i>, the ebook is already released with a Creative Commons license. Supporters can express their thanks by paying what they wish for the license and the ebook.</li>
|
||||
</ul>
|
||||
We also support distribution and preservation of free ebooks through our growing catalog of free-licensed ebooks.
|
||||
<dd>The ebooks available on Unglue.it have missions to accomplish. There are textbooks to help you learn. There is literature to open up your mind. There are books on technology to help you create. There are academic books that are spreading ideas. There are books that want to change your mind. And there are books that belong to all of us, that want to live into the future.<br /><br />
|
||||
|
||||
Freedom can help a book accomplish its mission, and that why the ebook is free. We call these books "unglued" and we call the process of making them free, "ungluing".</dd>
|
||||
|
||||
|
||||
<dt>So how do you make money?</dt>
|
||||
|
||||
<dd>We don't - we're an non-profit. If you like what we do, <a href="{% url 'newdonation' %}">you can donate!</a></dd>
|
||||
|
||||
<dt>What about the authors and publishers, how do they make money?</dt>
|
||||
|
||||
|
||||
<dd>That's the million dollar question. You don't make money by giving away ebooks. Many authors make money other ways - by teaching, by consulting, by getting tenure. Some authors do well be selling printed books alongside their free ebooks. We started Unglue.it because we hoped to give ebook creators additional ways to support their work: <a href="{% url 'faq_location' 'campaigns' %}">Ungluing Campaigns</a>.
|
||||
<br /><br /> We hope you'll choose to support some of these Campaigns, but if you like an ebook you find on Unglue.it, don't hesitate to lend your support in other ways.
|
||||
</dd>
|
||||
|
||||
|
||||
<a id="crowdfunding"></a><dt>What is Crowdfunding?</dt>
|
||||
|
||||
<dd>Crowdfunding is collectively pooling contributions (or pledges) to support some cause. Using the internet for coordination means that complete strangers can work together, drawn by a common cause. This also means the number of supporters can be vast, so individual contributions can be as large or as small as people are comfortable with, and still add up to enough to do something amazing.<br /><br />
|
||||
Want to see some examples? <a href="http://www.kickstarter.com/">Kickstarter</a> lets artists and inventors solicit funds to make their projects a reality. For instance, webcomic artist Rich Burlew sought $57,750 to reprint his comics in paper form -- and raised <a href="http://www.kickstarter.com/projects/599092525/the-order-of-the-stick-reprint-drive">close to a million</a>.<br /><br />
|
||||
In other words, crowdfunding is working together to support something you love. By pooling resources, big and small, from all over the world, we can make huge things happen.</dd>
|
||||
|
||||
<dt>What does it cost?</dt>
|
||||
|
||||
<dd>Unglue.it is free to join. Many of the things you can do here -- discovering books, adding them to your ebook list, downloading unglued books, commenting, sharing -- are free too.
|
||||
<br /><br /> If you choose to support a Campaign, please do so!.<br /><br /> If you choose to buy or download an unglue.it ebook, you can read it anywhere you like - there's no restrictive DRM.
|
||||
<br /><br />
|
||||
If you hold the electronic rights to a book, starting campaigns or listing your ebooks is free, too. You only pay Unglue.it a fraction of your revenue. For the basics on campaigns, see the FAQ on <a href="/faq/campaigns/">Campaigns</a>; for more details, see the <a href="/faq/rightsholders/">FAQ for Rights Holders</a>.</dd>
|
||||
|
||||
<dt>Who can use Unglue.it?</dt>
|
||||
|
||||
<dd>Anyone! We're located in the United States, but we welcome participants from all over the world.<br /><br />
|
||||
|
||||
To fund a campaign, or buy an ebook, you'll need a valid credit card. To start a campaign, you'll need to establish yourself with us as a rights holder, including demonstrating that you have the rights to the content you want to unglue. See the FAQs <a href="/faq/rightsholders/">for Rights Holders</a> for more.</dd>
|
||||
|
||||
<dt>Why should I support a book at Unglue.it when I can just buy it somewhere else?</dt>
|
||||
|
||||
<dd>When you buy a book, you get a copy for yourself. When you unglue it, you give a copy to yourself, reward its creators and contribute towards making it free to everyone.</dd>
|
||||
To fund a campaign, or buy an ebook, you'll need a valid credit card. To use our fund raising tools for your books, you'll need to establish yourself with us as a <a href="{% url 'rightsholders' %}">rights holder</a>. </dd>
|
||||
|
||||
<dt>What do I get when I help unglue a book?</dt>
|
||||
<dd>You get a better ebook. Your unglued ebook has no restrictive DRM: you can put it on any device you want, in any format you want. You can read it on the network or off, with no one tracking your reading.<br /><br />
|
||||
|
@ -65,48 +54,31 @@ You may get premiums as part of a Pledge Campaign, depending on the amount you p
|
|||
|
||||
<dt>Does Unglue.it own the copyright of unglued books?</dt>
|
||||
|
||||
<dd>No. When you unglue a book, the copyright stays with its current owner. Unglue.it does not buy copyrights. A <a href="https://creativecommons.org">Creative Commons</a> license is a licensing agreement. As with other licensing agreements, it grants certain rights to others but does not affect the status of the copyright.<br /><br />
|
||||
<dd>No. Books available on unglue.it are licensed by the the copyright owner. <a href="https://creativecommons.org">Creative Commons</a> licenses are licensing agreements. They grant certain rights but do not affect the status of the copyright. You can read more about these licenses at the <a href="https://wiki.creativecommons.org/Frequently_Asked_Questions">Creative Commons FAQ</a>.</dd>
|
||||
|
||||
Just like many traditional publishing transactions, ungluing is about licensing rights. We use <a href="https://creativecommons.org">Creative Commons</a> licenses which allow creators to choose the ways in which their works can be copied, shared and re-used.<br /><br />
|
||||
<dt>If I'm a rights holder and unglue my book, can I still offer it for sale?</dt>
|
||||
|
||||
If you are a copyright holder, you retain your copyright when you unglue a book. Creative Commons licenses are non-exclusive, so you also retain the right to enter into separate licensing agreements. You can read more about these licenses at the <a href="https://wiki.creativecommons.org/Frequently_Asked_Questions">Creative Commons FAQ</a>.</dd>
|
||||
|
||||
<dt>If I'm a rights holder and my book is unglued, does that mean I can never make money from it again?</dt>
|
||||
|
||||
<dd>No! You are free to enter into additional licensing agreements for other, non-unglued, editions of the work, including translation and film rights. You may continue to sell both print and ebook editions. You may use your unglued books as free samples to market your other works -- for instance, later works in the same series. You can use them to attract fans who may be interested in your speaking engagements, merchandise, or other materials. You absolutely may continue to profit from ungluing books -- and we hope you do!
|
||||
</dd>
|
||||
|
||||
<dt>Why do authors, publishers, and other rights holders want their books unglued?</dt>
|
||||
|
||||
<dd>
|
||||
Lots of reasons:
|
||||
<ul class="bullets">
|
||||
<li>To publicize their other books, gain new fans, or otherwise increase the value of an author's brand.</li>
|
||||
<li>To get income from books that are no longer in print.</li>
|
||||
<li>To have a digital strategy that pays for itself, even if they don't have expertise in producing ebooks.</li>
|
||||
<li>To advance a cause, add to the public conversation, or increase human knowledge.</li>
|
||||
<li>To leave a legacy that enriches everyone, now and in the future.</li>
|
||||
</ul>
|
||||
<dd>Yes. You are free to enter into additional licensing agreements for the work and its derivatives, including translation and film adaptations. You may continue to sell both print and ebook editions. Unglued ebooks can be free samples to market you and your work -- for instance, later works in the same series.
|
||||
</dd>
|
||||
|
||||
<dt>Does Unglue.it publish books? Can I self-publish here?</dt>
|
||||
|
||||
<dd>Unglue.it is not a publisher. If you self-publish, Unglue.it can help you distribute your ebooks, if they meet our technical quality standards. </dd>
|
||||
<dd>Unglue.it is a distributor, not a publisher. If you self-publish and want your books to be free, Unglue.it can help you distribute your ebooks. </dd>
|
||||
|
||||
<dt>What's a rights holder? How can I tell if I'm one?</dt>
|
||||
|
||||
<dd>A rights holder is the person (or entity) that holds the legal right to copy, distribute, or sell a book in some way. There may be one entity with all the rights to a work, or the rights may be split up among different entities: for example, one who publishes the print edition in the United States, another who publishes the print edition in the EU, another who holds electronic rights, et cetera. For ungluing purposes, the rights holder is the entity who has uncontested worldwide commercial rights to a work.<br /><br />
|
||||
<dd>A rights holder is a person (or entity) that has a right to copy, distribute, or sell a book in some way. There may be one entity with all the rights to a work, or the rights may be split up among different entities: for example, one who publishes the print edition in the United States, another who publishes the print edition in the EU, another who holds electronic rights, et cetera. For ungluing purposes, the rights holder is the entity who has uncontested worldwide commercial rights to a work.<br /><br />
|
||||
If you have written a book and not signed any contracts about it, you hold all the rights. If you have signed a contract with a publisher to produce, distribute, publish, or sell your work in a particular format or a particular territory, whether or not you still hold rights depends on the language of that contract. We encourage you to check your contracts. It may be in your interest to explore launching an Unglue.it campaign jointly with your publisher.<br /><br />
|
||||
If you haven't written a book but you have had business or family relationships with someone who has, it's possible that you are a rights holder (for instance, you may have inherited rights from the author's estate, or your company may have acquired them during a merger). Again, this all depends on the specific language of your contracts. We encourage you to read them closely. Unglue.it cannot provide legal advice.<br /><br />
|
||||
If you believe you are a rights holder and would like to your works to be unglued, please contact us at <a href="mailto:rights@gluejar.com">rights@gluejar.com</a>.</dd>
|
||||
If you believe you are a rights holder and would like us to help unglue your works, get started at <a href="{% url 'rightsholders' %}">the right holder tools page</a>.</dd>
|
||||
|
||||
<dt>I know a book that should be unglued.</dt>
|
||||
|
||||
<dd>Great! Find it in our database (using the search box above) and add it to your favorites, so we know it should be on our list, too. And encourage your friends to add it to their favorites. The more people that fave a book on Unglue.it, the more attractive it will be for authors and publishers to launch an ungluing campaign. You can also contact us at <a href="mailto:rights@gluejar.com">rights@gluejar.com</a>.</dd>
|
||||
<dd>Great! Find it in our database (using the search box above) and add it to your favorites, so we know it should be on our list, too. And encourage your friends to add it to their favorites. You can also contact us at <a href="mailto:rights@ebookfoundation.org">rights@ebookfoundation.org</a>.</dd>
|
||||
|
||||
<dt>I know a book that should be unglued, and I own the commercial rights.</dt>
|
||||
<dt>I know a book that should be unglued, and it's mine!</dt>
|
||||
|
||||
<dd>Fabulous! Please refer to the <a href="/faq/rightsholders/">FAQ for Rights Holders</a> and then contact us at <a href="mailto:rights@gluejar.com">rights@gluejar.com</a>. Let's talk.</dd>
|
||||
<dd>Fabulous! Please refer to the <a href="/faq/rightsholders/">FAQ for Rights Holders</a> and then contact us at <a href="mailto:rights@ebookfoundation.org">rights@ebookfoundation.org</a>. </dd>
|
||||
|
||||
<dt>Is there a widget that can be put on my own site to share my favorite books or campaigns?</dt>
|
||||
|
||||
|
@ -150,33 +122,40 @@ If you receive our newsletter, there's a link at the bottom of every message to
|
|||
|
||||
<dt>Is my personal information shared?</dt>
|
||||
|
||||
<dd>Short version: no, except as absolutely required to deliver services you have opted into. If you have pledged to a campaign and are owed a premium, we will share your email with campaign managers so that they can deliver premiums to you; you are not required to share any further information and they are required to comply with our privacy policy in handling your information. We do use third-party providers to support some of our business functions, but they are only allowed to use your information in order to enable your use of the service. For the long version, please read our <a href="/privacy/">privacy policy</a>.
|
||||
<dd>Short version: no, except as absolutely required to deliver services you have opted into. If you have pledged to a campaign and are owed a premium, we will share your email with campaign managers so that they can deliver premiums to you; you are not required to share any further information and they are required to comply with our privacy policy in handling your information. We do use third-party providers to support some of our business functions, but they are only allowed to use your information in order to enable your use of the service. For a long, opinionated version of this answer, please read our <a href="{% url 'privacy' %}">privacy policy</a>.
|
||||
</dl>
|
||||
{% endif %}
|
||||
{% if sublocation == 'company' or sublocation == 'all' %}
|
||||
<h4>The Company</h4>
|
||||
<h4>The Organization</h4>
|
||||
<dl>
|
||||
<dt>How can I contact Unglue.it?</dt>
|
||||
|
||||
<dd>For support requests, <a href="{% url 'feedback' %}?page=need+support">use our feedback form</a>. For general inquiries, use our Ask Questions Frequently account, <a href="mailto:aqf@gluejar.com">aqf@gluejar.com</a>. For rights inquiries, <a href="mailto:rights@gluejar.com">rights@gluejar.com</a>. Media requests, <a href="mailto:press@gluejar.com">press@gluejar.com</a></dd>
|
||||
<dd>For support requests, <a href="{% url 'feedback' %}?page=need+support">use our feedback form</a>. You can also contact us on <a href="mailto:info@ebookfoundation.org">email</a>, <a href="https://twitter.com/unglueit">Twitter</a> or <a href="https://facebook/com/unglueit">Facebook</a>.
|
||||
|
||||
<dt>Who is Unglue.it?</dt>
|
||||
|
||||
<dd>We are a small group that believes strongly that ebooks can make the world richer in new and diverse ways, and that we all benefit from the hard work of their creators. We come from the worlds of entrepreneurship, linked data, physics, publishing, education, and library science, to name a few. You can learn more about us at our <a href="/about">about page</a> or our Unglue.it supporter profiles: <a href="{% url 'supporter' 'eric' %}">Eric Hellman</a>, <a href="{% url 'supporter' 'AmandaM' %}">Amanda Mecke</a> and <a href="{% url 'supporter' 'rdhyee' %}">Raymond Yee</a>. Our alumni include <a href="{% url 'supporter' 'luclewitanski' %}">Luc Lewitanski</a> and <a href="{% url 'supporter' 'andromeda' %}">Andromeda Yelton</a>.</dd>
|
||||
</dd>
|
||||
|
||||
<dt>Are you a non-profit company?</dt>
|
||||
|
||||
<dd>Yes. Unglue.it is a program of the Free Ebook Foundation, which is a not-for-profit corporation. We work with both non-profit and commercial partners.</dd>
|
||||
<dd>Yes. Unglue.it is a program of the <a href="https://ebookfoundation.org">Free Ebook Foundation</a>, which is a charitable, not-for-profit corporation. We work with both non-profit and commercial partners.</dd>
|
||||
|
||||
<dt>Why does Unglue.it exist?</dt>
|
||||
|
||||
<dd>Because legal stickiness means you can't reliably lend or share your ebooks, borrow them from the library, or read them on the device of your choice. Because -- like public radio -- ebooks are expensive to create but cheap to distribute, so covering their fixed costs and reasonable profit up front can be an appealing system for authors, publishers, readers, and libraries. Because we all have books we love so much we want to give them to the world.</dd>
|
||||
</dl>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if location == 'campaigns' or location == 'faq' %}
|
||||
<h3>Campaigns</h3>
|
||||
<dl>
|
||||
<dt>What are Ungluing Campaigns?</dt>
|
||||
|
||||
<dd>Unglue.it has three programs that support ebook creators: Pledge Campaigns, Buy-to-Unglue Campaigns and Thanks-for-Ungluing Campaigns.
|
||||
<ul class="bullets">
|
||||
<li> In a <i><a href="{% url 'faq_sublocation' 'campaigns' 'supporting' %}">Pledge Campaign</a></i>, book lovers pledge their support for ungluing a book. If enough support is found to reach the goal (and only then), the supporter's credit cards are charged, and an unglued ebook is released.</li>
|
||||
<li> In a <i><a href="{% url 'faq_sublocation' 'campaigns' 'b2u' %}">Buy-to-Unglue Campaign</a></i>, every ebook copy sold moves the book's ungluing date closer to the present. And you can donate ebooks to your local library- that's something you can't do in the Kindle or Apple Stores!</li>
|
||||
<li> In a <i><a href="{% url 'faq_sublocation' 'campaigns' 't4u' %}">Thanks-for-Ungluing Campaign</a></i>, the ebook is already released with a Creative Commons license. Supporters can express their thanks by paying what they wish for the license and the ebook.</li>
|
||||
</ul>
|
||||
We also support distribution and preservation of free ebooks through our growing catalog of free-licensed ebooks.
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
{% if sublocation == 'all' %}
|
||||
{% include "faq_t4u.html" %}
|
||||
|
@ -191,6 +170,7 @@ If you receive our newsletter, there's a link at the bottom of every message to
|
|||
{% endif %}
|
||||
{% if sublocation == 'supporting' or sublocation == 'all' %}
|
||||
<a class="more_featured_books short" href="{% url 'campaign_list' 'pledge' %}" title="Pledge to Unglue list"><i class="fa fa-arrow-circle-o-right fa-3x"></i></a>
|
||||
|
||||
<h4>Pledge Campaigns</h4>
|
||||
<div class="faq_tldr">
|
||||
Support the books you've loved and make them free to everyone.
|
||||
|
@ -206,23 +186,23 @@ Support the books you've loved and make them free to everyone.
|
|||
|
||||
<dt>How do I pledge?</dt>
|
||||
|
||||
<dd>When the creators of a book have been persuaded to launch a campaign, there will be a "Support" button on the book's unglue.it page. You'll be asked to select your premium and specify your pledge amount; choose how you'd like to be acknowledged, if your pledge is $25 or more; and enter your credit card information.</dd>
|
||||
<dd>When the creators of a book have been persuaded to launch a campaign, there will be a "Support" button on the book's unglue.it page. You'll be asked to select your premium and specify your pledge amount; choose how you'd like to be acknowledged, if your pledge is $25 or more; and enter your credit card information. If tax deductions are important to you, consider donating instead.</dd>
|
||||
|
||||
<dt>How do I support a campaign with a donation?</dt>
|
||||
|
||||
<dd>Campaigns that meet the Free Ebook Foundation's guidelines for charitable support are eligible for support via donations to the Foundation. Essentially, the Foundation will pledge to the campaign based on your direction. </dd>
|
||||
|
||||
<dt>When will I be charged?</dt>
|
||||
|
||||
<dd>In most cases, your account will be charged by the end of the next business day after a Pledge Campaign succeeds. If a campaign doesn't succeed, you won't be charged.</dd>
|
||||
<dd>For pledges, your account will be charged by the end of the next business day after a Pledge Campaign succeeds. If a campaign doesn't succeed, you won't be charged. For donations, your account will be charged immediately. If a campaign doesn't succeed, the Foundation will use your donation in support of other ebooks.</dd>
|
||||
|
||||
<dt>What if I want to change or cancel a pledge?</dt>
|
||||
|
||||
<dd>You can modify your pledge by going to the book's page and clicking on the "Modify Pledge" button. (The "Support" button you clicked on to make a pledge is replaced by the "Modify Pledge" button after you pledge.)</dd>
|
||||
|
||||
<dt>What happens to my pledge if a Pledge Campaign does not succeed?</dt>
|
||||
|
||||
<dd>Your credit card will only be charged when campaigns succeed. If they do not, your pledge will expire and you will not be charged.</dd>
|
||||
<dd>You can modify your pledge by going to the book's page and clicking on the "Modify Pledge" button. (The "Support" button you clicked on to make a pledge is replaced by the "Modify Pledge" button after you pledge.) You can't modify a donation, but you can make another.</dd>
|
||||
|
||||
<dt>How will I be recognized for my support?</dt>
|
||||
|
||||
<dd>Pledgers are indicated on the book's page. In addition, when ungluing campaigns are successful, you'll be acknowledged in the unglued ebook as follows:<br />
|
||||
<dd>Pledgers and donors are indicated on the book's page. In addition, when ungluing campaigns are successful, you'll be acknowledged in the unglued ebook as follows:<br />
|
||||
<ul class="terms">
|
||||
<li>$25+ // Your name under "supporters" in the acknowledgements section.</li>
|
||||
<li>$50+ // Your name and link to your profile page under "benefactors"</li>
|
||||
|
@ -233,13 +213,9 @@ Support the books you've loved and make them free to everyone.
|
|||
|
||||
<dd>Yes. Anonymous donors are included in the total count of supporters, but their names and links are not included in the acknowledgements.</dd>
|
||||
|
||||
<dt>If I have pledged to a suspended or withdrawn Pledge Campaign, what happens to my pledge?</dt>
|
||||
|
||||
<dd>Your pledge will time out according to its original time limit. If the campaign is resolved and reactivated before your pledge has timed out, your pledge will become active again. If the campaign is not reactivated before your pledge's time limit, your pledge will expire and you will not be charged. As always, you will only be charged if a campaign is successful, within its original time limit.</dd>
|
||||
|
||||
<dt>Is my contribution tax deductible?</dt>
|
||||
|
||||
<dd>If the campaign manager for the work you're supporting is a nonprofit, please contact them directly to inquire. Unglue.it cannot offer tax deductions.</dd>
|
||||
<dd>Donations to the Free Ebook Foundation are tax-deductible. Pledges to campaigns might be tax-deductible if the recipient of funds is a charity, otherwise probably not. Consult your tax advisor. </dd>
|
||||
</dl>
|
||||
{% endif %}
|
||||
{% if sublocation == 'premiums' or sublocation == 'all' %}
|
||||
|
@ -260,6 +236,10 @@ Support the books you've loved and make them free to everyone.
|
|||
<dt>Is there a minimum or maximum for how much a premium can cost?</dt>
|
||||
|
||||
<dd>There's a $1 minimum and a $2000 maximum.</dd>
|
||||
|
||||
<dt>Do I get a premium for a donation?</dt>
|
||||
|
||||
<dd>No, just the good feeling you get for doing your part.</dd>
|
||||
</dl>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
@ -271,30 +251,15 @@ Support the books you've loved and make them free to everyone.
|
|||
<dl>
|
||||
<dt>So what is an unglued ebook?</dt>
|
||||
|
||||
<dd>An unglued ebook is a book that's added a <a href="https://creativecommons.org">Creative Commons</a> license, after payments to the author, publisher, or other rights holder.<br /><br />
|
||||
<dd>
|
||||
An unglued ebook is a <i>free</i> ebook, not an ebook that you <i>don't pay for</i>!
|
||||
<br /><br />
|
||||
|
||||
What does this mean for you? If you're a book lover, you can read unglued ebooks for free, on the device of your choice, in the format of your choice, and share them with all your friends. If you're a library, you can lend them to your patrons with no checkout limits or simultaneous user restrictions, and preserve them however you think best. If you're a rights holder, you get a payments in lieu of further royalties, while retaining copyright and all interests in your work.
|
||||
Unglued ebooks use a <a href="https://creativecommons.org">Creative Commons</a> or other free license to counteract the stucked-ness of "all rights reserved" copyrights. Sometimes, payments are made to the author, publisher, or other rights holder to facilitate the ungluing.<br /><br />
|
||||
|
||||
What does this mean for you? If you're a book lover, you can read unglued ebooks for free, on the device of your choice, in the format of your choice, and share them with all your friends. If you're a library, you can lend them to your patrons with no checkout limits or simultaneous user restrictions, and preserve them however you think best. If you're a rights holder, you retain copyright in your work.
|
||||
</dd>
|
||||
|
||||
<dt>Is the unglued ebook edition the same as one of the existing editions?</dt>
|
||||
|
||||
<dd>Not necessarily. Sometimes the existing editions include content that we can't get the rights to include in the unglued ebook, especially cover art or illustrations. Sometimes the unglued ebook edition will have improvements, like copy-editing fixes or bonus features. These differences, if any, will be specified in the More... tab of the campaign page. In addition, unglued ebooks may include acknowledgements of supporters and information on their Creative Commons license.</dd>
|
||||
|
||||
<dt>How much does a book need to earn?</dt>
|
||||
|
||||
<dd>For Pledge and Buy-to-Unglue Campaigns, the author or publisher sets an earnings total for making the book free. Once you and your fellow ungluers raise enough money to meet that price through pledges or spend enough money on paid copies, the unglued ebook becomes available at no charge, for everyone, everywhere! Thanks-for-Ungluing books can be downloaded for free now, but the creators would love to have your support.</dd>
|
||||
|
||||
<dt>Will Unglue.it have campaigns for all kinds of books?</dt>
|
||||
|
||||
<dd>Yes. You choose what books to support. </dd>
|
||||
|
||||
<dt>Does an unglued book have to be out-of-print?</dt>
|
||||
|
||||
<dd>No. Unglued books can be anything in copyright, whether 75 years or 7 days old, whether they sell in paperback or hardcover and or can only be found in used bookstores -- or nowhere. But the copyright owners and any one else with a claim on the book need to agree.</dd>
|
||||
|
||||
<dt>Will Unglue.it help raise funds for books not yet written?</dt>
|
||||
|
||||
<dd>No. There are other crowdfunding sites devoted to the creative process, and we encourage you to investigate them. Once your book is ready to sell as an ebook, we'd love to talk to you.</dd>
|
||||
</dl>
|
||||
{% endif %}
|
||||
{% if sublocation == 'using' or sublocation == 'all' %}
|
||||
|
@ -302,7 +267,7 @@ What does this mean for you? If you're a book lover, you can read unglued ebook
|
|||
<dl>
|
||||
<dt>What can I do, legally, with an unglued ebook? What can I <I>not</I> do?</dt>
|
||||
|
||||
<dd>All unglued ebooks are released under a Creative Commons license. The rights holder chooses which Creative Commons license to apply. Books that we distribute in a Buy-to-Unglue Campaign also have creative commons licenses, but the effective date of these licenses is set in the future.<br /><br />
|
||||
<dd>All unglued ebooks are released under a Creative Commons or other free license. The rights holder chooses which license to apply. Books that we distribute in a Buy-to-Unglue Campaign have creative commons licenses, but the effective date of these licenses is set in the future.<br /><br />
|
||||
|
||||
Creative Commons licenses mean that once the license is effective you <b>can</b>: make copies; keep them for as long as you like; shift them to other formats (like .mobi or PDF); share them with friends or on the internet; download them for free.<br /><br />
|
||||
|
||||
|
@ -312,49 +277,41 @@ Under ND (no derivatives) licenses, you <b>cannot</b>: make derivative works, s
|
|||
|
||||
Under all CC licenses (except CC0), you <b>cannot</b>: remove the author's name from the book, or otherwise pass it off as someone else's work; or remove or change the CC license.</dd>
|
||||
|
||||
<dt>What does non-commercial mean under Creative Commons?</dt>
|
||||
|
||||
<dd>Creative Commons doesn't define "commercial" thoroughly (though it's worth reading what <a href="https://wiki.creativecommons.org/FAQ#Does_my_use_violate_the_NonCommercial_clause_of_the_licenses.3F">they have to say on the topic</a>). So as part of the Unglue.it process, ungluing rights holders agree that "For purposes of interpreting the CC License, Rights Holder agrees that 'non-commercial' use shall include, without limitation, distribution by a commercial entity without charge for access to the Work."<br /><br />
|
||||
|
||||
Unglue.it can't provide legal advice about how to interpret Creative Commons licenses, and we encourage you to read about the licenses yourself and, if necessary, seek qualified legal advice.</dd>
|
||||
|
||||
<dt>Are the unglued ebooks compatible with my device? Do I need to own an ereader, like a Kindle or a Nook, to read them?</dt>
|
||||
|
||||
<dd>Unglued ebooks are distributed with NO RESTRICTIVE DRM, so they'll work on Kindle, iPad, Kobo, Nook, Android, Mac, Windows, Linux... you get the idea. Whether you have an ereader, a tablet, a desktop or laptop computer, or a smartphone, there are reading apps that work for you. Ebook editions issued through Unglue.it will be available in ePub format. If this format doesn't work for you, today or in the future, you are welcome to shift unglued books to one that does, and to copy and distribute it in that format.</dd>
|
||||
<dd>Unglued ebooks are distributed with NO RESTRICTIVE DRM, so they'll work on Kindle, iPad, Kobo, Nook, Android, Mac, Windows, Linux... you get the idea. Whether you have an ereader, a tablet, a desktop or laptop computer, or a smartphone, there are reading apps that work for you. </dd>
|
||||
|
||||
<dt>Do I need to have a library card to read an unglued ebook?</dt>
|
||||
|
||||
<dd>No. (Though we hope you have a library card anyway!) <br /><br />If your library has bought an ebook as part of a Buy-to-Unglue Campaign, you may need your library card if you want to borrow them. </dd>
|
||||
|
||||
<dt>How long do I have to read my unglued book? When does it expire?</dt>
|
||||
<dt>How long do I have to read my "buy-to-unglue" ebook? When does it expire?</dt>
|
||||
|
||||
<dd>If you're reading a book purchased from unglue.it by a library, your borrowing license expires after 2 weeks. But otherwise there's no expiration. And you delete the file on the honor system.<br /><br />
|
||||
If you're reading a book you've purchased from unglue.it, you may keep it as long as you like. There's no need to return it, and it won't stop working after some deadline. You own it.<br /><br />
|
||||
<dd>If you're reading a "buy-to-unglue" book purchased by a library, your borrowing license expires after 2 weeks. But otherwise there's no expiration. And you delete the file on the honor system.<br /><br />
|
||||
If you're reading a "buy-to-unglue" book you've purchased , you may keep it as long as you like. There's no need to return it, and it won't stop working after some deadline. You own it.<br /><br />
|
||||
If you're reading a book that's been unglued, you can keep the file forever AND you can make copies for friends.</dd>
|
||||
|
||||
<dt>Can I download ebooks directly from Unglue.it?</dt>
|
||||
|
||||
<dd>If you've purchased an ebook from unglue.it, you'll by able to download it through the website. All formats are included (where available), and you can download the ebooks for your personal use as many times as you need to.<br /><br />
|
||||
|
||||
Unglue.it also provides links to download previously unglued books, creative commons licensed books, and public domain ebooks. You don't need our permission to host these free ebooks on your own site.</dd>
|
||||
Unglue.it provides links to download thousands of previously unglued books, creative commons licensed books, and public domain ebooks. You don't need our permission to host these free ebooks on your own site, but you must abide by the book's license terms</dd>
|
||||
</dl>
|
||||
{% endif %}
|
||||
{% if sublocation == 'copyright' or sublocation == 'all' %}
|
||||
<h4>Ungluing and Copyright</h4>
|
||||
<dl>
|
||||
<dt>Why does an unglued book have to be in copyright?</dt>
|
||||
<dt>Are Unglued Ebooks in the Public Domain?</dt>
|
||||
|
||||
<dd>Because books out of copyright are already free for you to copy, remix, and share! If a book is in the <a href="https://en.wikipedia.org/wiki/Public_domain">public domain</a>, it doesn't need to be unglued.</dd>
|
||||
<dd>No. Books in the public domain, because their copyrights have expired, or because they were created by the US government, are already free for you to copy, remix, and share! If a book is in the <a href="https://en.wikipedia.org/wiki/Public_domain">public domain</a>, it doesn't need to be unglued, it's already unglued. But lots of other books are free-licensed even though their copyrights are still in force; if you don't adhere to the license terms, you are infringing that copyright.</dd>
|
||||
|
||||
<dt>How can I tell if a book is in copyright or not?</dt>
|
||||
|
||||
<dd>Unfortunately, it can be complicated -- which is why Unglue.it wants to simplify things, by making unglued ebooks unambiguously legal to use in certain ways. The laws governing copyright and the public domain vary by country, so a book can be copyrighted in one place and not in another. In the United States, the <a href="http://www.librarycopyright.net/digitalslider/">Library Copyright Slider</a> gives a quick estimate. <br /><br />
|
||||
|
||||
Unglue.it signs agreements assuring the copyright status of every work we unglue, so you can be confident when reading and sharing an unglued ebook.</dd>
|
||||
|
||||
<dt>Unglue.it lists a book that I know is already Creative Commons licensed or available in the public domain. How can I get the link included?</dt>
|
||||
|
||||
<dd>If you are logged in to Unglue.it, the "Rights" page for every work lists the edition we know about. There a link for every edition that you can use to tell us about Creative Commons or public domain downloads. We do require that these books be hosted at certain known, reputable repositories: Internet Archive, Wikisources, Hathi Trust, Project Gutenberg, or Google Books.</dd></dl>
|
||||
<dd>If you are logged in to Unglue.it, the "More..." tab for every work lists the edition we know about. There is a link for every edition that you can use to tell us about Creative Commons or public domain downloads. We require that these books be hosted at certain known, reputable repositories such as Internet Archive, Wikisources, Hathi Trust, Project Gutenberg, Github or Google Books.</dd></dl>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
|
@ -363,23 +320,15 @@ Unglue.it signs agreements assuring the copyright status of every work we unglue
|
|||
{% if sublocation == 'authorization' or sublocation == 'all' %}
|
||||
<h4>Becoming Authorized</h4>
|
||||
<dl>
|
||||
<dt>Who is eligible to start a campaign or sell ebooks on Unglue.it?</dt>
|
||||
<dt>What do I need to do to become a Rights Holder on Unglue.it?</dt>
|
||||
|
||||
<dd>To start a campaign, you need to be a verified rights holder who has signed a Platform Services Agreement with us. </dd>
|
||||
<dt>What do I need to do to become an authorized Rights Holder on Unglue.it?</dt>
|
||||
<dd>To start a campaign, you need to be a verified rights holder who has signed a Rights Holder Agreement with us. <br /><br />
|
||||
|
||||
<dd>Contact our Rights Holder Relations team, <a href="mailto:rights@gluejar.com">rights@gluejar.com</a>, to discuss signing our <a href="https://www.docracy.com/1mud3ve9w8/unglue-it-non-exclusive-platform-services-agreement">Platform Services Agreement (PSA)</a>, setting a revenue goal, and running a campaign on Unglue.it to release your book under a <a href="https://creativecommons.org">Creative Commons</a> license, or to raise money for a book you've already released under such a license.</dd>
|
||||
Start by visiting the <a href="{% url 'rightsholders' %}">Rights Holders Tools</a> page.You'll sign our <a href="{% url 'agree' %}">Rights Holder Agreement (RHA)</a> and learn about our various programs.</dd>
|
||||
|
||||
<dt>I haven't signed an agreement, but my books are listed on Unglue.it. What's going on?</dt>
|
||||
<dd>If your book is listed on Unglue.it, it's because an unglue.it user has indicated some interested in the book. Perhaps your book has been added to a user's favorites list. Users can also use the site to post links to Creative Commons licensed or public domain books. Unglue.it works with non-profit sites such as Internet Archive to improve their coverage and preservation of Creative Commons licensed books.
|
||||
<dd>If your book is listed on Unglue.it, it's either because an unglue.it user has indicated some interested in the book, or it's because we've determined that your book is already available with a free license.
|
||||
</dd>
|
||||
<dt>I didn't give you permission to offer my CC-licensed book for download. Why is it there?</dt>
|
||||
<dd>When we don't have an agreement with a rights-holder, our download links lead to sites such as Internet Archive, Project Gutenberg, or Google Books that facilitate this linking and free use of the works by our users. We strive to keep our information accurate, so if the license we report is inaccurate, please <a href="mailto:rights@gluejar.com?subject=corrections">let us know</a> immediately. If for some reason you don't want us to provide download links, let us know using the "feedback" tab next to your book's page and we'll make a notation to that effect in our metadata. We may require evidence that you are authorized to act for the rights holders of the book.
|
||||
</dd>
|
||||
|
||||
<dt>Do I need to know the exact titles I might want to unglue before I sign the Platform Services Agreement?</dt>
|
||||
|
||||
<dd>No. You only need to agree to the terms under which you will use Unglue.it to raise money. You can decide which specific titles you wish to make available for licensing later. However, before you start a campaign, you must claim a specific title (through the More... tab of its book page) and plan how you will attract ungluers to pledge toward your goal.</dd>
|
||||
|
||||
<dt>Can I run more than one campaign?</dt>
|
||||
<dd>You can run campaigns for as many, or as few, titles at a time as you like. </dd>
|
||||
|
@ -389,22 +338,17 @@ Unglue.it signs agreements assuring the copyright status of every work we unglue
|
|||
<h4>Claiming Works</h4>
|
||||
<dl>
|
||||
|
||||
<dt>How can I claim a work for which I am the rights holder?</dt>
|
||||
<dt>How can I claim a work for which I am a rights holder?</dt>
|
||||
|
||||
<dd>On every book page there is a More... tab. If you have a signed Platform Services Agreement on file, one of the options on the More... tab will be "Claim This Work". If you represent more than one rights holder, choose the correct one for this work and click "Claim".
|
||||
<dd>On every book page there is a More... tab. If you have a signed Rights Holder Agreement on file, one of the options on the More... tab will be "Claim This Work". If you represent more than one rights holder, choose the correct one for this work and click "Claim".
|
||||
<br /><br />If the book is not already in Unglue.it or in Google Books, you'll need to enter the metatadata for the book by hand or work with us to load the data from a spreadsheet or Onix file.
|
||||
|
||||
<ul class="bullets">
|
||||
<li>Use <a href="{% url 'new_edition' '' '' %}">this form</a> to enter the metadata.</li>
|
||||
<li>Your ebooks must have ISBNs or OCLC numbers assigned.</li>
|
||||
<li>Your metadata should have title, authors, language, description.</li>
|
||||
<li>Your metadata should have title, identifiers, authors, language, description.</li>
|
||||
<li>If you have a lot of books to enter, <a href="{% url 'feedback' %}?page={{request.build_absolute_uri|urlencode:""}}">contact us</a> to load ONIX or CSV files for you.</li>
|
||||
</ul>
|
||||
<br /><br />If you expect to see a "Claim" button and do not, either we do not have a PSA from you yet, or we have not yet verified and filed it. Please contact us at <a href="mailto:rights@gluejar.com">rights@gluejar.com</a>.</dd>
|
||||
|
||||
<dt>Why should I claim my works?</dt>
|
||||
|
||||
<dd>You need to claim a work before you will be able to start a campaign for it. Additionally, we're adding features for verified rights holders which will help you show off your works and connect to your readers. Claiming your works will let you take advantage of these features in the future.</dd>
|
||||
<br /><br />If you expect to see a "Claim" button and do not, either we do not have a RHA from you yet, or we have not yet verified and filed it. Please contact us at <a href="mailto:rights@ebookfoundation.org">rights@ebookfoundation.org</a>.</dd>
|
||||
|
||||
<dt>Where can I find a campaign page?</dt>
|
||||
|
||||
|
@ -418,15 +362,12 @@ If you want to find an interesting campaign and don't have a specific book in mi
|
|||
<dl>
|
||||
<dt>What do I need to create a campaign?</dt>
|
||||
|
||||
<dd>First, you need to be an authorized rights holder with a signed Platform Services Agreement on file. Please contact <a href="mailto:rights@gluejar.com">rights@gluejar.com</a> to start this process. Once we have your PSA on file, you'll be able to claim your works and you'll have access to tools for launching and monitoring campaigns. We'll be happy to walk you through the process personally.</dd>
|
||||
|
||||
<dt>Can I unglue only one of my books? Can I unglue all of them?</dt>
|
||||
|
||||
<dd>Yes! It's entirely up to you. Each Campaign is for a individual title and a separate fundraising parameters.</dd>
|
||||
<dd>First, you need to be an authorized rights holder. To get authorized, <a href="{% url 'agree' %}">sign a Rights Holder Agreement</a>. Please contact <a href="mailto:rights@ebookfoundation.org">rights@ebookfoundation.org</a> if you have any questions. Once we have approved your Agreement, you'll be able to claim works and you'll have access to tools for launching and monitoring campaigns.
|
||||
</dd>
|
||||
|
||||
<dt>Can I raise any amount of money I want?</dt>
|
||||
|
||||
<dd>You can set any goal that you want in a Campaign. But don't be ridiculous! Unglue.it cannot guarantee that a goal will be reached.</dd>
|
||||
<dd>You can set any goal that you want in a Campaign. But don't be ridiculous! Unglue.it cannot guarantee that a goal will be reached. Also, Campaigns with excessive goals will not be eligible for support via donations.</dd>
|
||||
|
||||
<dt>What should I include in my campaign page?</dt>
|
||||
|
||||
|
@ -444,6 +385,20 @@ If you want to find an interesting campaign and don't have a specific book in mi
|
|||
|
||||
<dd>You can offer anything you are capable of delivering, so long as it is not illegal or otherwise restricted in the United States. For instance, your premiums may not include alcohol, drugs, or firearms. For full details, consult our <a href="/terms/">Terms of Use</a>.</dd>
|
||||
|
||||
<dt id='donation_support'>Is my campaign eligible for charitable donation support?</dt>
|
||||
|
||||
<dd>
|
||||
The Free Ebook Foundation may provide support for some campaigns using donations. These campaigns are subject to the following guidelines:
|
||||
<ol>
|
||||
<li>Proceeds of a campaign must not benefit a candidate for political office and books to be unglued shall not be primarily aimed at influencing legislation.</li>
|
||||
<li>Proceeds of a campaign must not benefit organizations or individuals subject to regulation by the US Treasury’s Office of Foreign Assets Control.</li>
|
||||
<li>Books to be unglued with Foundation support should clearly benefit the public at large - by advancing scholarship and learning, spreading scientific knowledge, achieving artistic, literary or cultural goals, educating students, promoting literacy and reading, documenting history, meeting the needs of underserved communities, raising awareness and understanding of the world around us. </li>
|
||||
<li>The amount of support requested should be limited to the reasonable costs of producing the book and procuring associated rights. When a campaign is offered using a license with a “non-commercial” restriction, it is expected that the rights holders will bear part of these expenses themselves. When the campaign beneficiary is not a US-based non-profit, documentation of expenses may be requested.</li>
|
||||
</ol>
|
||||
|
||||
|
||||
</dd>
|
||||
|
||||
<dt>Who is responsible for making sure a rights holder delivers premiums?</dt>
|
||||
|
||||
<dd>The rights holder.</dd>
|
||||
|
@ -534,15 +489,15 @@ Need more ideas? We're happy to work with rights holders personally to craft a
|
|||
<dl>
|
||||
<dt>I am a copyright holder. Do I need to already have a digital file for a book I want to unglue?</dt>
|
||||
|
||||
<dd>For a Buy-to-Unglue Campaign, yes, you'll need to upload an epub file for each book. For Pledge Campaigns, no, you may run campaigns for books that exist only in print copies or are out of print. Any print book can be scanned to create a digital file that can then become an ePub-format unglued ebook. For Thanks-for-Ungluing campaigns, you can use ePub, Mobi, pdf and html files.</dd>
|
||||
<dd>For a Buy-to-Unglue Campaign, yes, you'll need to upload an EPUB file for each book. For Pledge Campaigns, no, you may run campaigns for books that exist only in print copies or are out of print. Any print book can be scanned to create a digital file that can then become an EPUB-format unglued ebook. For Thanks-for-Ungluing campaigns, you can use EPUB, MOBI, pdf and html files.</dd>
|
||||
|
||||
<dt>Will Unglue.it scan books and produce ePub files?</dt>
|
||||
<dt>Will Unglue.it scan books and produce EPUB files?</dt>
|
||||
|
||||
<dd>No. We can help you find third parties who will contract for conversion services.</dd>
|
||||
|
||||
<dt>Will Unglue.it raise money to help pay for conversion costs?</dt>
|
||||
|
||||
<dd>A Pledge campaign target should include conversion costs. For a Buy-to-Unglue and Thanks-for-Ungluing campaigns, you'll need to have pre-existing epub files.</dd>
|
||||
<dd>A Pledge campaign target should include conversion costs. For a Buy-to-Unglue and Thanks-for-Ungluing campaigns, you'll need to have pre-existing files.</dd>
|
||||
|
||||
<dt>What are the requirements for my ebook files?</dt>
|
||||
|
||||
|
@ -569,12 +524,12 @@ Need more ideas? We're happy to work with rights holders personally to craft a
|
|||
|
||||
<dt>Are contributions refundable?</dt>
|
||||
|
||||
<dd>Unglue.it contributions are non-refundable. Once a campaign succeeds, supporters' credit cards will be charged the full amount of their pledge. However, they will not be charged until, and unless, the campaign succeeds. Before that time they may modify or cancel their pledges without charge. Buy-to-Unglue license purchases are not returnable or refundable by Unglue.it, but rights holders need to keep customers happy with respect to the products they buy, as customers are free to post comments on the work page.
|
||||
<dd>Unglue.it contributions are non-refundable. Once a campaign succeeds, supporters' credit cards will be charged the full amount of their pledge. However, they will not be charged until, and unless, the campaign succeeds. Before that time they may modify or cancel their pledges without charge. Buy-to-Unglue license purchases are not returnable or refundable by Unglue.it, but rights holders need to keep customers happy with respect to the products they buy, as customers are free to post comments on the work page. Donations are not refundable.
|
||||
</dd>
|
||||
|
||||
<dt>What if an ungluer contests a charge?</dt>
|
||||
|
||||
<dd>Ungluers may contest charges, either with the payment processor or with their credit card issuer. Unglue.it is not liable for this and rights holders are liable for any chargebacks. We encourage rights holders to provide clear, timely communication to their supporters throughout the campaign to ensure they remember what they pledged to and why.</dd>
|
||||
<dd>Ungluers may contest charges, either with the payment processor or with their credit card issuer. Unglue.it is not liable for this and rights holders are liable for any pledge chargebacks. We encourage rights holders to provide clear, timely communication to their supporters throughout the campaign to ensure they remember what they pledged to and why.</dd>
|
||||
|
||||
<dt>What happens if a supporter's credit card is declined?</dt>
|
||||
|
||||
|
@ -582,19 +537,19 @@ Need more ideas? We're happy to work with rights holders personally to craft a
|
|||
|
||||
<dt>What happens if a buyer wants a refund?</dt>
|
||||
|
||||
<dd>Our policy is for 100% buyer satisfaction. We hold back a 30% reserve for 90 days to cover possible customer refunds.</dd>
|
||||
<dd>Our policy is for 100% supporter satisfaction. We may hold back a 30% reserve for 90 days to cover possible customer refunds.</dd>
|
||||
|
||||
<dt>Can I offer tax deductions to people who pledge to my campaign?</dt>
|
||||
|
||||
<dd>If you are a 501(c)3 or similar, please consult your own attorneys and accountants about the tax deductibility of ungluers' contributions. Unglue.it cannot offer tax deductions or advice.</dd>
|
||||
<dd>If you are a 501(c)3 or similar, please consult your own attorneys and accountants about the tax deductibility of ungluers' pledged. Donations to the Free Ebook Foundation are tax deductible in the USA; rights holders are not involved. </dd>
|
||||
|
||||
<dt>How do I know when I have started fundraising?</dt>
|
||||
|
||||
<dd>Unglue.it will be in communication with you about when campaigns should go live. On your <a href="/rightsholders/">rights holder dashboard</a>, you will be able to see all works you have claimed and the status of any associated campaigns.</dd>
|
||||
<dd>Unglue.it will be in communication with you about when campaigns should go live. On your <a href="{% url 'rightsholders' %}">rights holder tools page</a>, you will be able to see all works you have claimed and the status of any associated campaigns.</dd>
|
||||
|
||||
<dt>When and how will I be paid?</dt>
|
||||
|
||||
<dd><ul><li>For <i>Pledge Campaigns</i>: After we reach the threshold price Unglue.it will issue you a closing memo, which will cover your gross and net proceeds. Unglue.it will hold the proceeds until you deliver an ePub file meeting Unglue.it's quality standards, at which point Unglue.it will send you a check. If Unglue.it does not receive a suitable ePub file within 90 days, we will deduct conversion costs from your funds and release the rest to you.</li>
|
||||
<dd><ul><li>For <i>Pledge Campaigns</i>: After we reach the threshold price Unglue.it will issue you a closing memo, which will cover your gross and net proceeds. Unglue.it will hold the proceeds until you deliver an EPUB file meeting Unglue.it's quality standards, at which point Unglue.it will send you a check. If Unglue.it does not receive a suitable EPUB file within 90 days, we will deduct conversion costs from your funds and release the rest to you.</li>
|
||||
<li>For <i>Buy-to-Unglue Campaigns</i> and <i>Thanks-for-Ungluing Campaigns</i>: We make payments quarterly to rights holders who have accrued more than $100 in earnings. If we are able to set you up with ACH transfers, we will send payments more frequently. 70% of earnings accrue immediately; the rest accrues after 90 days.</li>
|
||||
</ul></dd>
|
||||
|
||||
|
@ -615,17 +570,9 @@ If you're concerned a campaign may not reach its goal you have several options.
|
|||
|
||||
<dd>Yes, the minimum funding goal is $500.</dd>
|
||||
|
||||
<dt>What fees does Unglue.it charge in a Pledge Campaign?</dt>
|
||||
<dt>What fees does Unglue.it charge in a Fundraising Campaign?</dt>
|
||||
|
||||
<dd>When a campaign succeeds, Unglue.it will deduct a 6% commission (or $60, whichever is greater) on the funds raised. Our payment processor also charges a separate small fee on each transaction, plus (where relevant) currency conversion costs. If you do not have a suitable ePub version of your book available, you will also need to cover ebook conversion costs; please price this into your goal for a campaign. Details are spelled out in the Platform Services Agreement that rights holders must sign before launching an Unglue.it campaign.</dd>
|
||||
|
||||
<dt>What fees does Unglue.it charge in a Buy-to-Unglue Campaign?</dt>
|
||||
|
||||
<dd>For Buy-to-Unglue Campaigns, Unglue.it charges (1) a flat 25% of of revenue from ebook licenses it provides through its site or through partner sites and (2) a per-transaction charge of $0.25. These amounts <i>include</i> payment processor fees and any fees due to partner sites.</dd>
|
||||
|
||||
<dt>What fees does Unglue.it charge in a Thanks-for-Ungluing Campaign?</dt>
|
||||
|
||||
<dd>For Buy-to-Unglue Campaigns, Unglue.it charges (1) a flat 8% of of revenue from "thanks" payments it receives through its site and (2) a per-transaction charge of $0.25. These amounts <i>include</i> payment processor fees.</dd>
|
||||
<dd>Fees and terms for each type of campaign are detailed on the <a href="{% url 'programs'%}">program terms page</a>.</dd>
|
||||
|
||||
<dt>Does it cost money to start a campaign on Unglue.it?</dt>
|
||||
|
||||
|
@ -648,9 +595,9 @@ If you're concerned a campaign may not reach its goal you have several options.
|
|||
|
||||
<dd>It is your responsibility to get advice on the current status of any contracts you may have ever had for the right to publish your work, whether or not a book is in print now. <a href="https://creativecommons.org">Creative Commons</a> licenses are media neutral and worldwide (English). You may need waivers from other parties who have exclusive licenses for this book.</dd>
|
||||
|
||||
<dt>If I am a publisher, but I do not have an ebook royalty in my contract, can I sign your Platform Services Agreement?</dt>
|
||||
<dt>If I am a publisher, but I do not have an ebook royalty in my contract, can I sign your Rights Holder Agreement?</dt>
|
||||
|
||||
<dd>We can't interpret your particular contract regarding subsidiary rights and your ability to use a Creative Commons license. Please seek qualified independent advice regarding the terms of your contract. In any event, you will also want the author to cooperate with you on a successful fundraising campaign, and you can work together to meet the warranties of the PSA.</dd>
|
||||
<dd>We can't interpret your particular contract regarding subsidiary rights and your ability to use a Creative Commons license. Please seek qualified independent advice regarding the terms of your contract. In any event, you will also want the author to cooperate with you on a successful fundraising campaign, and you can work together to meet the warranties of the RHA.</dd>
|
||||
|
||||
<dt>Are the copyright holder, and the rights holder who is eligible to start an Unglue.it campaign, the same person?</dt>
|
||||
|
||||
|
|
|
@ -1,12 +1,26 @@
|
|||
<div class="jsmodule">
|
||||
<h3 class="jsmod-title"><span>Pledging FAQs</span></h3>
|
||||
<h3 class="jsmod-title"><span>Campaign Support FAQs</span></h3>
|
||||
<div class="jsmod-content">
|
||||
<ul class="menu level1">
|
||||
|
||||
<li class="first parent">
|
||||
<span class="faq">How do I pledge?</span>
|
||||
<span class="menu level2 answer">
|
||||
Enter your pledge amount and select a premium. (You may select a premium at any level up to and including the amount you pledge.) If you pledge enough, you're also eligible to be credited in the unglued ebook and to include a dedication, and toward the bottom of this page you can specify what you'd like those to say. If this is your first pledge, we'll collect your card information after you click Pledge Now. Otherwise, we'll use the card you used last time -- no need to type in your info again!
|
||||
Enter your pledge amount and select a premium. (You may select a premium at any level up to and including the amount you pledge.) If you pledge enough, you're also eligible to be credited in the unglued ebook and to include a dedication, and toward the bottom of this page you can specify what you'd like those to say. If this is your first time supporting a campaign, we'll collect your card information after you click Pledge Now. Otherwise, we'll use the card you used last time -- no need to type in your info again!
|
||||
</span>
|
||||
</li>
|
||||
|
||||
<li class="parent">
|
||||
<span class="faq">Donation or Pledge?</span>
|
||||
<span class="menu level2 answer">
|
||||
Donations are fully tax-deductible in the US, but we
|
||||
</span>
|
||||
</li>
|
||||
|
||||
<li class="parent">
|
||||
<span class="faq">How do I donate?</span>
|
||||
<span class="menu level2 answer">
|
||||
Enter your donation amount and check the donation box. If you donate enough, you're also eligible to be credited in the unglued ebook and to include a dedication, and toward the bottom of this page you can specify what you'd like those to say. If this is your first time supporting a campaign, we'll collect your card information after you click Donate Now. Otherwise, we'll use the card you used last time -- no need to type in your info again! Remember, donations are tax-deductible in the US.
|
||||
</span>
|
||||
</li>
|
||||
|
||||
|
@ -20,12 +34,19 @@
|
|||
<li class="parent">
|
||||
<span class="faq">When will I be charged?</span>
|
||||
<span class="menu level2 answer">
|
||||
If this campaign reaches its target before its deadline ({{ campaign.deadline }}), you'll be charged within a day of when the target is reached. Otherwise, your pledge will expire at midnight on {{ campaign.deadline }} (Eastern US time) and you will not be charged.
|
||||
Donations will be charged right away. Pledges aren't charged unless the campaign succeeds. If this campaign reaches its target before its deadline ({{ campaign.deadline }}), you'll be charged within a day of when the target is reached. Otherwise, your pledge will expire at midnight on {{ campaign.deadline }} (Eastern US time) and you will not be charged.
|
||||
</span>
|
||||
</li>
|
||||
|
||||
<li class="parent">
|
||||
<span class="faq">Will I be charged if the campaign doesn't succeed?</span>
|
||||
<span class="faq">Will donations be refunded if the campaign doesn't succeed?</span>
|
||||
<span class="menu level2 answer">
|
||||
Sorry, no. Your donation will be used to support other qualifying Unglue.it comapigns.
|
||||
</span>
|
||||
</li>
|
||||
|
||||
<li class="parent">
|
||||
<span class="faq">Will pledges be charged if the campaign doesn't succeed?</span>
|
||||
<span class="menu level2 answer">
|
||||
Nope!
|
||||
</span>
|
||||
|
|
|
@ -8,11 +8,20 @@
|
|||
</li>
|
||||
|
||||
<li class="parent {% if location != 'basics' %}collapse{% endif %}">
|
||||
<a href="{% url 'faq_location' 'basics' %}"><span>Basics</span></a>
|
||||
<a href="{% url 'faq_location' 'basics' %}"><span>About the site</span></a>
|
||||
<ul class="menu level2">
|
||||
<li class="first"><a href="{% url 'faq_sublocation' 'basics' 'howitworks' %}"><span>How it Works</span></a></li>
|
||||
<li class="first"><a href="{% url 'faq_sublocation' 'basics' 'howitworks' %}"><span>About Unglue.it</span></a></li>
|
||||
<li><a href="{% url 'faq_sublocation' 'basics' 'account' %}"><span>Your Account</span></a></li>
|
||||
<li class="last"><a href="{% url 'faq_sublocation' 'basics' 'company' %}"><span>The Company</span></a></li>
|
||||
<li class="last"><a href="{% url 'faq_sublocation' 'basics' 'company' %}"><span>The Organization</span></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="parent {% if location != 'unglued_ebooks' %}collapse{% endif %}">
|
||||
<a href="{% url 'faq_location' 'unglued_ebooks' %}"><span>Unglued Ebooks</span></a>
|
||||
<ul class="menu level2">
|
||||
<li class="first"><a href="{% url 'faq_sublocation' 'unglued_ebooks' 'general' %}"><span>General Questions</span></a></li>
|
||||
<li><a href="{% url 'faq_sublocation' 'unglued_ebooks' 'using' %}"><span>Using Your Unglued Ebook</span></a></li>
|
||||
<li class="last"><a href="{% url 'faq_sublocation' 'unglued_ebooks' 'copyright' %}"><span>Ungluing and Copyright</span></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
@ -26,14 +35,6 @@
|
|||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="parent {% if location != 'unglued_ebooks' %}collapse{% endif %}">
|
||||
<a href="{% url 'faq_location' 'unglued_ebooks' %}"><span>Unglued Ebooks</span></a>
|
||||
<ul class="menu level2">
|
||||
<li class="first"><a href="{% url 'faq_sublocation' 'unglued_ebooks' 'general' %}"><span>General Questions</span></a></li>
|
||||
<li><a href="{% url 'faq_sublocation' 'unglued_ebooks' 'using' %}"><span>Using Your Unglued Ebook</span></a></li>
|
||||
<li class="last"><a href="{% url 'faq_sublocation' 'unglued_ebooks' 'copyright' %}"><span>Ungluing and Copyright</span></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="parent {% if location != 'rightsholders' and 'smashwords' not in request.path %}collapse{% endif %}">
|
||||
<a href="{% url 'faq_location' 'rightsholders' %}"><span>For Rights Holders</span></a>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
<p>Love something? Hate something? Found something broken or confusing? Thanks for telling us!</p>
|
||||
|
||||
To: support@gluejar.com<br /><br />
|
||||
To: support@ebookfoundation.org<br /><br />
|
||||
<form method="POST" action="{% url 'feedback' %}">
|
||||
{% csrf_token %}
|
||||
{{ form.sender.errors }}
|
||||
|
@ -29,5 +29,5 @@
|
|||
<input type="submit" value="Submit" />
|
||||
</form>
|
||||
|
||||
<p>If for some reason this form doesn't work, you can send email to unglue.it support at <a href="mailto:support@gluejar.com">support@gluejar.com</a>.</p>
|
||||
<p>If for some reason this form doesn't work, you can send email to unglue.it support at <a href="mailto:unglueit@ebookfoundation.org">unglueit@ebookfoundation.org</a>.</p>
|
||||
{% endblock %}
|
|
@ -1,12 +1,21 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% load truncatechars %}
|
||||
{% load sass_tags %}
|
||||
{% block title %}— Support Free eBooks{% endblock %}
|
||||
|
||||
{% block extra_meta %}
|
||||
<meta property="og:title" content="Unglue.it - A Community Supporting Free eBooks" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content="https://unglue.it" />
|
||||
<meta property="og:image" content="https://unglue.it/static/images/logo.png" />
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block extra_css %}
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/landingpage4.css" />
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/searchandbrowse2.css" />
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/book_panel2.css" />
|
||||
<link type="text/css" rel="stylesheet" href="{% sass_src 'scss/landingpage4.scss' %}" />
|
||||
<link type="text/css" rel="stylesheet" href="{% sass_src 'scss/searchandbrowse2.scss' %}" />
|
||||
<link type="text/css" rel="stylesheet" href="{% sass_src 'scss/book_panel2.scss' %}" />
|
||||
{% endblock %}
|
||||
{% block extra_js %}
|
||||
|
||||
|
@ -211,7 +220,7 @@ function put_un_in_cookie2(){
|
|||
<div class="jsmodule">
|
||||
<h3 class="module-title">News</h3>
|
||||
<div class="jsmod-content">
|
||||
Unglue.it is now <a href="https://blog.unglue.it/2017/01/19/unglue-it-website-is-now-open-source/"> Open Source</a>
|
||||
<a href="https://blog.unglue.it/2018/01/24/unglue-it-has-resumed-crowdfunding/">Unglue.it has resumed crowdfunding</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="jsmodule">
|
||||
|
@ -286,12 +295,12 @@ function put_un_in_cookie2(){
|
|||
|
||||
<h3 class="featured_books">As seen on</h3>
|
||||
<ul id="as_seen_on">
|
||||
<li><a href="http://boingboing.net/2012/06/28/release-a-deadly-monster-a-dr.html"><img src="{{ STATIC_URL }}images/press_logos/boingboing_logo.png"></a></li>
|
||||
<li><a href="http://www.zeit.de/digital/internet/2012-07/unglue-ebook-creative-commons"><img src="{{ STATIC_URL }}images/press_logos/die_zeit_logo.png"></a></li>
|
||||
<li><a href="http://www.huffingtonpost.com/2012/05/21/unglueit-free-ebooks-crowdfunding_n_1532644.html"><img src="{{ STATIC_URL }}images/press_logos/huffington_post_logo.png"></a></li>
|
||||
<li><a href="http://techcrunch.com/2014/05/06/unglue-it-sets-books-free-after-authors-get-paid/"><img src="{{ STATIC_URL }}images/press_logos/techcrunch_logo.png"></a></li>
|
||||
<li><a href="http://www.thedigitalshift.com/2014/02/ebooks/buy-unglue-ebook-crowdfunding-model-goes-beta/"><img src="{{ STATIC_URL }}images/press_logos/library_journal_logo.png"></a></li>
|
||||
<li><a href="http://www.networkworld.com/community/node/85329"><img src="{{ STATIC_URL }}images/press_logos/networkworld_logo.png"></a></li>
|
||||
<li><a href="http://boingboing.net/2012/06/28/release-a-deadly-monster-a-dr.html"><img alt="boingboing" src="{{ STATIC_URL }}images/press_logos/boingboing_logo.png"></a></li>
|
||||
<li><a href="http://www.zeit.de/digital/internet/2012-07/unglue-ebook-creative-commons"><img alt="die zeit" src="{{ STATIC_URL }}images/press_logos/die_zeit_logo.png"></a></li>
|
||||
<li><a href="http://www.huffingtonpost.com/2012/05/21/unglueit-free-ebooks-crowdfunding_n_1532644.html"><img alt="huffington post" src="{{ STATIC_URL }}images/press_logos/huffington_post_logo.png"></a></li>
|
||||
<li><a href="http://techcrunch.com/2014/05/06/unglue-it-sets-books-free-after-authors-get-paid/"><img alt="techcrunch" src="{{ STATIC_URL }}images/press_logos/techcrunch_logo.png"></a></li>
|
||||
<li><a href="http://www.thedigitalshift.com/2014/02/ebooks/buy-unglue-ebook-crowdfunding-model-goes-beta/"><img alt="library journal" src="{{ STATIC_URL }}images/press_logos/library_journal_logo.png"></a></li>
|
||||
<li><a href="http://www.networkworld.com/community/node/85329"><img alt="networkworld" src="{{ STATIC_URL }}images/press_logos/networkworld_logo.png"></a></li>
|
||||
</ul>
|
||||
<div class="speech_bubble"><span>For readers it’s a gold mine of great books they can have a say in bringing to market.</span></div>
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div {%if request.user.is_authenticated or hide_learn_more %}id="user-block-hide" {% endif %}class="user-block-hide learnmore_block ">
|
||||
<h1 style="text-align: center; padding-top: 1em; width: 70%">3 ways <i>we</i> can make ebooks <i>free</i></h1>
|
||||
<h1 style="text-align: center;padding-top: 1em;width: 70%; line-height: 1.2em;">Find over 10,000 <i>free</i> ebooks here.<br />Help us make more ebooks <i>free</i>!</h1>
|
||||
|
||||
<div class="quicktour panelview" >
|
||||
<div class="panelview panelfront side1" >
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
{% extends 'basedocumentation.html' %}
|
||||
|
||||
{% load sass_tags %}
|
||||
|
||||
{% block title %} ♥ Libraries{% endblock %}
|
||||
{% block extra_css %}
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/campaign2.css" />
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/libraries.css" />
|
||||
<link type="text/css" rel="stylesheet" href="{% sass_src 'scss/campaign2.scss' %}" />
|
||||
<link type="text/css" rel="stylesheet" href="{% sass_src 'scss/libraries.scss' %}" />
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
|
@ -60,7 +62,7 @@ The library license gives download access to one library member at a time for 14
|
|||
<dt>Support <a href="{% url 'campaign_list' 'ending' %}">our active campaigns</a>.</dt>
|
||||
<dd>Ultimately ebooks can't be unglued unless authors and publishers are paid for their work. Many of our staunchest supporters are librarians. There are also several libraries which have supported campaigns, including Leddy Library (University of Windsor, Ontario); the University of Alberta library ; and the Z. Smith Reynolds library (Wake Forest University).</dd>
|
||||
<dt>Give feedback and ask questions.</dt>
|
||||
<dd>Want to know more? Need help? Have ideas for how we could improve the site or make it more library-friendly? Contact us at: <a href="mailto:libraries@gluejar.com">libraries@gluejar.com</a>.</dd>
|
||||
<dd>Want to know more? Need help? Have ideas for how we could improve the site or make it more library-friendly? Contact us at: <a href="mailto:info@ebookfoundation.org">info@ebookfoundation.org</a>.</dd>
|
||||
</dl>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -1,13 +1,15 @@
|
|||
{% extends 'base.html' %}
|
||||
|
||||
{% load endless %}
|
||||
{% load sass_tags %}
|
||||
{% load truncatechars %}
|
||||
|
||||
{% block title %} — {{ library }}{% endblock %}
|
||||
{% block extra_css %}
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/supporter_layout.css" />
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/searchandbrowse2.css" />
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/book_list.css" />
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/book_panel2.css" />
|
||||
<link type="text/css" rel="stylesheet" href="{% sass_src 'scss/supporter_layout.scss' %}" />
|
||||
<link type="text/css" rel="stylesheet" href="{% sass_src 'scss/searchandbrowse2.scss' %}" />
|
||||
<link type="text/css" rel="stylesheet" href="{% sass_src 'scss/book_list.scss' %}" />
|
||||
<link type="text/css" rel="stylesheet" href="{% sass_src 'scss/book_panel2.scss' %}" />
|
||||
{% endblock %}
|
||||
{% block extra_js %}
|
||||
<script type="text/javascript" src="/static/js/wishlist.js"></script>
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load sass_tags %}
|
||||
|
||||
{% load libraryauthtags %}
|
||||
{% block title %} Libraries {% endblock %}
|
||||
{% block extra_css %}
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/supporter_layout.css" />
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/liblist.css" />
|
||||
<link type="text/css" rel="stylesheet" href="{% sass_src 'scss/supporter_layout.scss' %}" />
|
||||
<link type="text/css" rel="stylesheet" href="{% sass_src 'scss/liblist.scss' %}" />
|
||||
{% endblock %}
|
||||
{% block extra_head %}
|
||||
<script type="text/javascript" src="{{ jquery_ui_home }}"></script>
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load sass_tags %}
|
||||
|
||||
{% block title %} Users of {{ library }} {% endblock %}
|
||||
{% block extra_css %}
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/supporter_layout.css" />
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/liblist.css" />
|
||||
<link type="text/css" rel="stylesheet" href="{% sass_src 'scss/supporter_layout.scss' %}" />
|
||||
<link type="text/css" rel="stylesheet" href="{% sass_src 'scss/liblist.scss' %}" />
|
||||
{% endblock %}
|
||||
{% block extra_head %}
|
||||
<script type="text/javascript" src="{{ jquery_ui_home }}"></script>
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
{% extends 'basedocumentation.html' %}
|
||||
{% load sass_tags %}
|
||||
|
||||
{% block title %}Your Unglue.it Account{% endblock %}
|
||||
{% block extra_extra_head %}
|
||||
{{ block.super }}
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/pledge.css" />
|
||||
<link type="text/css" rel="stylesheet" href="{% sass_src 'scss/pledge.scss' %}" />
|
||||
|
||||
{% include "stripe_stuff.html" %}
|
||||
<script>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
{% extends 'basedocumentation.html' %}
|
||||
|
||||
{% load humanize %}
|
||||
{% load sass_tags %}
|
||||
|
||||
{% block title %}Campaign Management{% endblock %}
|
||||
|
||||
|
@ -11,8 +12,8 @@ textarea {
|
|||
width: 90%;
|
||||
}
|
||||
</style>
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/manage_campaign.css" />
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/campaign2.css" />
|
||||
<link type="text/css" rel="stylesheet" href="{% sass_src 'scss/manage_campaign.scss' %}" />
|
||||
<link type="text/css" rel="stylesheet" href="{% sass_src 'scss/campaign2.scss' %}" />
|
||||
|
||||
<script type="text/javascript" src="/static/js/tabs.js"></script>
|
||||
|
||||
|
@ -83,21 +84,30 @@ Please fix the following before launching your campaign:
|
|||
|
||||
<div class="pledged-info">
|
||||
<div class="pledged-group">
|
||||
{% ifequal work.last_campaign.type 1 %}
|
||||
{% if work.last_campaign.type == 1 %}
|
||||
{{ work.last_campaign.supporters_count }} Ungluers have pledged ${{ work.last_campaign.current_total|intcomma }}
|
||||
{% else %}
|
||||
Total revenue: ${{ work.last_campaign.current_total|intcomma }} from {{ work.last_campaign.supporters_count }} Ungluers and {{ work.last_campaign.anon_count }} others
|
||||
{% endifequal %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% if campaign.charitable %}
|
||||
<div class="pledged-info">
|
||||
This campaign is eligible for <a href="{% url 'faq_sublocation' 'rightsholders' 'campaigns' %}#donation_support">charitable donation support</a>.
|
||||
</div>
|
||||
{% elif campaign.type == 1 %}
|
||||
<div class="pledged-group">
|
||||
If you believe your campaign meets <a href="{% url 'faq_sublocation' 'rightsholders' 'campaigns' %}#donation_support">the criteria for charitable donation support</a>, use <a href="{% url 'feedback' %}?page={{request.build_absolute_uri|urlencode:""}}">the feedback form</a> to request a review by Free Ebook Foundation staff.
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="preview_campaign">
|
||||
{% ifequal campaign_status 'INITIALIZED' %}
|
||||
{% if campaign_status == 'INITIALIZED' %}
|
||||
<a href="{% url 'work_preview' campaign.work_id %}" class="manage" target="_blank">Preview Your Campaign</a>
|
||||
{% else %}
|
||||
<a href="{% url 'work' campaign.work_id %}" class="manage" target="_blank">See Your Campaign</a>
|
||||
{% endifequal %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
@ -105,7 +115,7 @@ Please fix the following before launching your campaign:
|
|||
<div class="content-block-heading" id="tabs">
|
||||
<ul class="tabs">
|
||||
<li class="tabs1 {% if activetab == '1' %}active{% endif %}"><a href="#">Description</a></li>
|
||||
<li class="tabs2 {% if activetab == '2' %}active{% endif %}"><a href="#">{% ifequal campaign.type 1 %}Premiums{% endifequal %}{% ifequal campaign.type 2 %}Pricing{% endifequal %}{% ifequal campaign.type 3 %}Amounts{% endifequal %}</a></li>
|
||||
<li class="tabs2 {% if activetab == '2' %}active{% endif %}"><a href="#">{% if campaign.type == 1 %}Premiums{% elif campaign.type == 2 %}Pricing{% elif campaign.type == 3 %}Amounts{% endif %}</a></li>
|
||||
<li class="tabs3 {% if activetab == '3' %}active{% endif %}"><a href="#">{% if campaign_status == 'ACTIVE' or campaign_status == 'SUCCESSFUL' %}Progress{% else %}Launch{% endif %}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
@ -117,16 +127,13 @@ Please fix the following before launching your campaign:
|
|||
{% csrf_token %}
|
||||
{{ form.media }}
|
||||
<h3>Edit the editions (if needed)</h3>
|
||||
{% if campaign.rh.can_sell %}
|
||||
{% if campaign.work.ebookfiles.0 %}
|
||||
<p>You have uploaded ebook files for this work. </p>
|
||||
{% else %}
|
||||
{% ifequal work.last_campaign.type 2 %}
|
||||
{% if work.last_campaign.type == 2 %}
|
||||
<p> To sell ebooks as part of a buy to unglue campaign, you will need to upload an EPUB file for the ebook you want to sell. </p>
|
||||
{% endifequal %}
|
||||
{% ifequal work.last_campaign.type 3 %}
|
||||
{% elif work.last_campaign.type == 3 %}
|
||||
<p> To distribute ebooks as part of a thanks for ungluing campaign, you will need to upload the ebook files to unglue.it. </p>
|
||||
{% endifequal %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<p> Please choose the edition that most closely matches the edition to be unglued. This is the edition whose cover image will display on your book's page. Your unglued edition should be identical to this edition if possible; you should note any differences under Rights Details below.</p>
|
||||
|
@ -134,18 +141,16 @@ Please fix the following before launching your campaign:
|
|||
{{ form.edition.errors }}
|
||||
{% for edition in campaign.work.editions.all %}
|
||||
<div class="edition_form" id="edition_{{edition.id}}">
|
||||
<p> Edition {{ edition.id }}: <input type="radio" {% ifequal edition.id form.edition.value %}checked="checked" {% endifequal %}id="id_edition_{{forloop.counter}}" value="{{edition.id}}" name="edition" /><label for="id_edition_{{forloop.counter}}"> Prefer this edition </label>
|
||||
<p> Edition {{ edition.id }}: <input type="radio" {% if edition.id == form.edition.value %}checked="checked" {% endif %}id="id_edition_{{forloop.counter}}" value="{{edition.id}}" name="edition" /><label for="id_edition_{{forloop.counter}}"> Prefer this edition </label>
|
||||
<ul style="text-indent:1em">
|
||||
<li style="text-indent:2.5em"><a href="{% url 'new_edition' edition.work_id edition.id %}"> Edit </a> this edition</li>
|
||||
{% ifnotequal campaign.type 1 %}
|
||||
{% if campaign.rh.can_sell %}
|
||||
{% if campaign.type != 1 %}
|
||||
{% if edition.ebook_files.all.0 %}
|
||||
<li style="text-indent:2.5em">You have uploaded ebook files for this edition. You can <a href="{% url 'edition_uploads' edition.id %}"> manage its ebooks or upload another</a></li>
|
||||
{% else %}
|
||||
<li style="text-indent:2.5em">You can <a href="{% url 'edition_uploads' edition.id %}"> Manage ebooks</a> for this edition.</li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endifnotequal %}
|
||||
</ul>
|
||||
</p>
|
||||
{% with managing='True' %}{% include "edition_display.html" %}{% endwith %}
|
||||
|
@ -159,7 +164,7 @@ Please fix the following before launching your campaign:
|
|||
{% endif %}
|
||||
{% if campaign.work.epubfiles.0 %}
|
||||
{% for ebf in campaign.work.epubfiles %}
|
||||
<p>{% if ebf.active %}<span class="yikes">ACTIVE</span> {% elif ebf.ebook.active %} MIRROR {% endif %}EPUB file: <a href="{{ebf.file.url}}">{{ebf.file}}</a> <br />created {{ebf.created}} for edition <a href="#edition_{{ebf.edition_id}}">{{ebf.edition_id}}</a> {% if ebf.asking %}(This file has had the campaign 'ask' added.){% endif %}<br />{% if ebf.active %}{% ifequal action 'mademobi' %}<span class="yikes">A MOBI file is being generated. </span> (Takes a minute or two.) {% else %}You can <a href="{% url 'makemobi' campaign.id ebf.id %}">generate a MOBI file.</a> {% endifequal %}{% endif %}</p>
|
||||
<p>{% if ebf.active %}<span class="yikes">ACTIVE</span> {% elif ebf.ebook.active %} MIRROR {% endif %}EPUB file: <a href="{{ebf.file.url}}">{{ebf.file}}</a> <br />created {{ebf.created}} for edition <a href="#edition_{{ebf.edition_id}}">{{ebf.edition_id}}</a> {% if ebf.asking %}(This file has had the campaign 'ask' added.){% endif %}<br />{% if ebf.active %}{% if action == 'mademobi' %}<span class="yikes">A MOBI file is being generated. </span> (Takes a minute or two.) {% else %}You can <a href="{% url 'makemobi' campaign.id ebf.id %}">generate a MOBI file.</a> {% endif %}{% endif %}</p>
|
||||
{% endfor %}
|
||||
{% if campaign.work.test_acqs.0 %}
|
||||
<ul>
|
||||
|
@ -178,8 +183,8 @@ Please fix the following before launching your campaign:
|
|||
<p>{% if ebf.active %}<span class="yikes">ACTIVE</span> {% endif %}PDF file: <a href="{{ebf.file.url}}">{{ebf.file}}</a> <br />created {{ebf.created}} for edition <a href="#edition_{{ebf.edition_id}}">{{ebf.edition_id}}</a> {% if ebf.asking %}(This file has had the campaign 'ask' added.){% endif %}</p>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% ifnotequal campaign_status 'ACTIVE' %}
|
||||
{% ifnotequal campaign.type 3 %}
|
||||
{% if campaign_status != 'ACTIVE' %}
|
||||
{% if campaign.type != 3 %}
|
||||
<h3>License being offered</h3>
|
||||
<p> This is the license you are offering to use once the campaign succeeds. For more information on the licenses you can use, see <a href="https://creativecommons.org/licenses">Creative Commons: About the Licenses</a>. Once your campaign is active, you'll be able to switch to a less restrictive license, but not a more restrictive one. We encourage you to pick the least restrictive license you are comfortable with, as this will increase the ways people can use your unglued ebook and motivate more people to donate.</p>
|
||||
{% else %}
|
||||
|
@ -188,11 +193,11 @@ Please fix the following before launching your campaign:
|
|||
Once your campaign is active, you'll be able to switch to a less restrictive license, but not a more restrictive one.
|
||||
We encourage you to pick the least restrictive license you are comfortable with, as this will increase the ways people can use your unglued ebook and motivate more people to participate.
|
||||
</p>
|
||||
{% endifnotequal %}
|
||||
{% endif %}
|
||||
<div>
|
||||
{{ form.license.errors }}{{ form.license }}
|
||||
</div>
|
||||
{% ifequal campaign.type 1 %}
|
||||
{% if campaign.type == 1 %}
|
||||
<h3>Target Price</h3>
|
||||
<p>This is the target price for your campaign. The <i>minimum</i> target is ${{form.minimum_target|intcomma}}.</p>
|
||||
|
||||
|
@ -209,8 +214,7 @@ Please fix the following before launching your campaign:
|
|||
|
||||
<p>The ending date can't be more than six months away- that's a practical limit for credit card authorizations. The <i>latest</i> ending you can choose <i>right now</i> is {{ campaign.latest_ending }}</p>
|
||||
{{ form.deadline.errors }}{{ form.deadline }}
|
||||
{% endifequal %}
|
||||
{% ifequal campaign.type 2 %}
|
||||
{% elif campaign.type == 2 %}
|
||||
<h3>Revenue Goal</h3>
|
||||
<p>This is the initial revenue goal for your campaign. Once the campaign starts, the actual revenue goal will decrement every day. When your actual revenue meets your actual revenue goal, your book gets released immediately, for free, under the Creative Commons License that you've specified. </p>
|
||||
|
||||
|
@ -230,43 +234,40 @@ Please fix the following before launching your campaign:
|
|||
<p>Before launching a campaign, you'll need to select Your initial Ungluing Date. Together with your campaign revenue goal, this will define when your book becomes "unglued". Check out the <a href="{% url 'faq_sublocation' 'campaigns' 'b2u' %}#calculator">the ungluing date calculator</a> to see how this works. Your starting Ungluing Date must be before {{ form.max_cc_date }}</p>
|
||||
{{ form.cc_date_initial.errors }}{{ form.cc_date_initial }}
|
||||
<!--{{ form.deadline.errors }}-->
|
||||
{% endifequal %}
|
||||
{% ifequal campaign.type 3 %}
|
||||
{% elif campaign.type == 3 %}
|
||||
<!--{{ form.target.errors }}--><!--{{ form.cc_date_initial.errors }}--><!--{{ form.deadline.errors }}-->
|
||||
{% endifequal %}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<h3>License being offered</h3>
|
||||
{% ifnotequal campaign.type 3 %}
|
||||
{% if campaign.type != 3 %}
|
||||
<p>If your campaign succeeds, you will be offering your ebook under a <b><a href="{{campaign.license_url }}">{{ campaign.license }}</a></b> license.</p>
|
||||
{% else %}
|
||||
<p>You are offering your ebook under a <b><a href="{{campaign.license_url }}">{{ campaign.license }}</a></b> license.</p>
|
||||
{% endifnotequal %}
|
||||
{% endif %}
|
||||
<p>Since your campaign is active, you may only change the license to remove restrictions. For more information on the licenses you can use, see <a href="https://creativecommons.org/licenses">Creative Commons: About the Licenses</a>.</p>
|
||||
<div>
|
||||
{{ form.license.errors }}<span>{{ form.license }}</span>
|
||||
</div>
|
||||
{% ifequal campaign.type 1 %}
|
||||
{% if campaign.type == 1 %}
|
||||
<h3>Target Revenue</h3>
|
||||
<p>The current target revenue for your campaign is <b>${{ campaign.target|intcomma }}</b>. Since your campaign is active, you may lower, but not raise, this target. You can get a feel for the interplay between revenue target and ungluing date with the <a href="{% url 'faq_sublocation' 'campaigns' 'b2u' %}#calculator">the ungluing date calculator</a></p>
|
||||
<div class="std_form">
|
||||
${{ form.target.errors }}{{ form.target }}
|
||||
</div>
|
||||
{% endifequal %}
|
||||
{% ifequal campaign.type 2 %}
|
||||
{% elif campaign.type == 2 %}
|
||||
<h3>Initial Revenue Goal</h3>
|
||||
<p>The initial revenue goal for your campaign was <b>${{ campaign.target|intcomma }}</b>; the current amount remaining is <b>${{ campaign.left|intcomma }}</b>. Since your campaign is active, you may lower, but not raise, this goal. Before you change this, try different parameters with <a href="{% url 'faq_sublocation' 'campaigns' 'b2u' %}#calculator">the ungluing date calculator</a>. </p>
|
||||
<div class="std_form">
|
||||
${{ form.target.errors }}{{ form.target }}
|
||||
</div>
|
||||
{% endifequal %}
|
||||
{% ifequal campaign.type 1 %}
|
||||
{% endif %}
|
||||
{% if campaign.type == 1 %}
|
||||
<h3>Ending date</h3>
|
||||
<p>The ending date of your campaign is <b>{{ campaign.deadline }}</b>. Your campaign will conclude on this date or when you meet your target price, whichever is earlier. You may not change the ending date of an active campaign.</p>
|
||||
<div class="std_form">
|
||||
{{ form.deadline.errors }}<span style="display: none">{{ form.deadline }}</span>
|
||||
</div>
|
||||
{% endifequal %}
|
||||
{% ifequal campaign.type 2 %}
|
||||
{% elif campaign.type == 2 %}
|
||||
<h3>Ungluing Date</h3>
|
||||
<p> This campaign was launched with a Ungluing Date of {{ campaign.cc_date_initial }}.</p>
|
||||
<p> Based on a total revenue of {{ campaign.current_total }} the Ungluing Date has been advanced to {{ campaign.cc_date }}.</p>
|
||||
|
@ -276,20 +277,19 @@ Please fix the following before launching your campaign:
|
|||
|
||||
<!--{{ form.deadline.errors }}-->
|
||||
|
||||
{% endifequal %}
|
||||
{% ifequal campaign.type 3 %}
|
||||
{% elif campaign.type == 3 %}
|
||||
<!--{{ form.deadline.errors }}--><!--{{ form.cc_date_initial.errors }}-->
|
||||
{% endifequal %}
|
||||
{% endifnotequal %}
|
||||
{% ifequal campaign.type 2 %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if campaign.type == 2 %}
|
||||
<h3>Personalization</h3>
|
||||
<p>If you do not want Unglue.it to use digital watermarking techniques to encode a transaction number into the ebook files, uncheck this box. Either way, ebook files will be personalized; the difference is whether personalization is easy or hard to remove.</p>
|
||||
<div class="std_form">Use watermarking: {{ form.do_watermark }}</div>
|
||||
{% endifequal %}{{ form.do_watermark.errors }}
|
||||
{% endif %}{{ form.do_watermark.errors }}
|
||||
|
||||
|
||||
<h3>Your Pitch</h3>
|
||||
{% ifequal campaign.type 3 %}
|
||||
{% if campaign.type == 3 %}
|
||||
<p>In a "thanks for ungluing" campaign, you want to first "motivate" your book- that is, you want to get the user to download the book.
|
||||
Once the user has clicked a "Download" button or a "Read it Now" button, you have a chance for an "ask" - that's where a user can decide to also make a thank-you contribution.
|
||||
The "ask" will be displayed to a user who has clicked a "Download" button. It's your chance to ask for their support.
|
||||
|
@ -299,40 +299,38 @@ Please fix the following before launching your campaign:
|
|||
{% else %}
|
||||
<p>This will be displayed in the Campaign tab for your work. It's your main pitch to supporters/purchasers, and your chance to share your passion for this work, and to inspire readers..</p>
|
||||
<p>A strong ask:</p>
|
||||
{% endifequal %}
|
||||
{% endif %}
|
||||
<ul class="terms">
|
||||
{% ifequal campaign.type 3 %}
|
||||
{% if campaign.type == 3 %}
|
||||
<li>Thanks the user for their interest in the book.</li>
|
||||
<li>Makes a connection to the user while explaining how a contribution makes your work possible.</li>
|
||||
<li>Speaks visually to the user (photos and/or videos). The FAQ has <a hef="{% url 'faq_sublocation' 'campaigns' 'addingmedia' %}">instruction on adding media</a>.</li>
|
||||
<li>Speaks visually to the user (photos and/or videos). The FAQ has <a href="{% url 'faq_sublocation' 'rightsholders' 'addingmedia' %}">instructions on adding media</a>.</li>
|
||||
{% else %}
|
||||
<li>Introduces the work. What's this book like?</li>
|
||||
<li>Shows why it matters. How will someone or something -- the world, readers, some cause that matters -- be better off if this book becomes freely available? What kind of impact has the book had already? What will ungluers get out of supporting this campaign?</li>
|
||||
<li>Has visual appeal (photos and/or videos). The FAQ has <a hef="{% url 'faq_sublocation' 'campaigns' 'addingmedia' %}">instruction on adding media</a>.</li>
|
||||
<li>Defines important but potentially unfamiliar things. What's an ungluing campaign, and why are you running one? Is there anything unusual about the book, its genre, et cetera? For those who aren't already familiar with you (or the author), who are you? {% ifequal campaign.type 1 %}Are you offering any particularly great premiums you want to call people's attention to?{% endifequal %}</li>
|
||||
<li>Gives examples of the author's work. This could be links to your site or places people can find more information about you or the book. You can also add quotes, embed a free sample chapter or story, display images, et cetera. These work samples might be from the campaign book itself, or from your (or the author's) other creative works.</li>
|
||||
{% endifequal %}
|
||||
<li>Has personality. The writing should be thoroughly proofread but it should have a point of view. This is the place to be conversational, opinionated, funny, quirky -- whatever reflects your style. Be you.</li>
|
||||
<li>Introduces the work. What's this book about?</li>
|
||||
<li>Introduces you, the person or organization who will receive support. Who (or what) are you?</li>
|
||||
<li>Says why it matters. How will someone or something -- the world, readers, some cause that matters -- be better off if this book becomes freely available? What will ungluers get out of supporting this campaign?</li>
|
||||
{% if campaign.type == 1 %}<li>Motivates support. Are you offering any premiums you want to call people's attention to?</li>{% endif %}
|
||||
<li>Gives a taste of the work. This could be links to more information about you or the book. You can also add quotes, embed a free sample chapter or story, display images, et cetera. These work samples might be from the campaign book itself, or from your (or the author's) other creative works.</li>
|
||||
<li>Has visual appeal (photos and/or videos). The FAQ has <a href="{% url 'faq_sublocation' 'rightsholders' 'addingmedia' %}">instruction on adding media</a>.</li>
|
||||
<li>Explains how the funds to be raised will be used.</li>
|
||||
{% endif %}
|
||||
|
||||
{% ifequal campaign.type 1 %}
|
||||
<li>Optionally, provides extra incentives (like new or improved premiums) that will kick in if the campaign is unusually successful. Will you do something special when you reach 10% or 50% or 75% of your goal? Or if you reach that milestone before a particular deadline (e.g. in the first week of your campaign)?</li>
|
||||
{% endifequal %}
|
||||
</ul>
|
||||
<br />
|
||||
{% ifequal campaign.type 3 %}
|
||||
{% if campaign.type == 3 %}
|
||||
<div style="padding-top:2em;padding-bottom:0.5em">First, the <b>Motivation</b>. Note that users who immediately click on a "Read it Now" button won't see this.:</div>
|
||||
{{ form.work_description.errors }}{{ form.work_description }}
|
||||
<div style="padding-top:2em;padding-bottom:0.5em">Next, the <b>Ask</b>. A "thank you" form will float right in this area, so don't use wide graphic elements.</div>
|
||||
{% endifequal %}
|
||||
{% endif %}
|
||||
{{ form.description.errors }}{{ form.description }}
|
||||
{% ifequal campaign.type 3 %}
|
||||
{% if campaign.type == 3 %}
|
||||
<h3>Enable "Thanks" in your ebook files</h3>
|
||||
<p> Unglue.it can add your "Ask" along with a link to your book's "thank the creators" page into your ebook files. That way, people who download the book without making a contribution will be reminded that they can do it later.</p>
|
||||
<div class="std_form">Add your ask to files: {{ form.use_add_ask.errors }}{{ form.use_add_ask }}</div>
|
||||
|
||||
{% endifequal %}
|
||||
{% endif %}
|
||||
<h3>Edition and Rights Details</h3>
|
||||
<p>This will be displayed on the More... tab for your work. It's the fine print for your campaign. {% ifequal campaign.type 1 %}Make sure to disclose any ways the unglued edition will differ from the existing edition; for example:
|
||||
<p>This will be displayed on the More... tab for your work. It's the fine print for your campaign. {% if campaign.type == 1 %}Make sure to disclose any ways the unglued edition will differ from the existing edition; for example:
|
||||
<ul class="bullets">
|
||||
<li>Any material that may have to be excluded due to permissions issues: illustrations, forewords, etc.</li>
|
||||
<li>Any additional materials that will be included, if not already covered in your pitch -- but we encourage you to cover them there to show supporters the value of ungluing!</li>
|
||||
|
@ -342,7 +340,7 @@ Please fix the following before launching your campaign:
|
|||
<ul class="bullets">
|
||||
<li>If the text is to be CC BY, but illustrations are only licensed as part of the ebook.</li>
|
||||
</ul>
|
||||
{% endifequal %}
|
||||
{% endif %}
|
||||
|
||||
<p>In short, is there a fact about this campaign that you think would matter to your agent or another publishing wonk, but that no one else is likely to care about? Put it here. If your campaign doesn't have any fine print, you can leave this blank.</p>
|
||||
{{ form.details.errors }}{{ form.details }}
|
||||
|
@ -359,12 +357,12 @@ Please fix the following before launching your campaign:
|
|||
<p>If you are set up as an unglue.it publisher (send us a url, logo, description and list of ways your name might appear) you can link your campaign by selecting the publisher here:
|
||||
<p class="std_form">{{ form.publisher.errors }}{{ form.publisher }}</p>
|
||||
{% endif %}
|
||||
{% ifequal campaign_status 'ACTIVE' %}
|
||||
{% if campaign_status == 'ACTIVE' %}
|
||||
<div class="yikes">When you click this button, your changes will be visible to supporters immediately. Make sure to proofread!</div><br />
|
||||
<input type="submit" name="save" value="Modify Campaign" />
|
||||
{% else %}
|
||||
<br /><br /><input type="submit" name="save" value="Save Campaign" />
|
||||
{% endifequal %}
|
||||
{% endif %}
|
||||
|
||||
{% if not is_preview or request.user.is_staff %}
|
||||
{% if campaign_status == 'INITIALIZED' %}
|
||||
|
@ -375,20 +373,20 @@ Please fix the following before launching your campaign:
|
|||
</div>
|
||||
|
||||
<div class="tabs-2">
|
||||
{% ifequal campaign.type 1 %}
|
||||
{% if campaign.type == 1 %}
|
||||
<h3>Premiums</h3>
|
||||
<div class="jsmod-content">
|
||||
<form action="#" method="POST">
|
||||
{% csrf_token %}
|
||||
<ul class="support menu">
|
||||
{% for premium in premiums %}
|
||||
<li class="{% if forloop.first %}first{% else %}{% if forloop.last %}last{% endif %}{% endif %}">
|
||||
<li class="{% if forloop.first %}first{% elif forloop.last %}last{% endif %}">
|
||||
<i>
|
||||
<span class="menu-item-price">${{ premium.amount|intcomma }}</span>
|
||||
<span class="menu-item-desc">{{ premium.description }}</span>
|
||||
</i>
|
||||
{% ifnotequal premium.limit 0 %}<br />Limit: {{ premium.limit }}{% endifnotequal %}
|
||||
{% ifequal premium.type 'CU' %}<br />Deactivate? <input type="checkbox" name="premium_id" value="{{ premium.id }}" />{% endifequal %}
|
||||
{% if premium.limit != 0 %}<br />Limit: {{ premium.limit }}{% endif %}
|
||||
{% if premium.type == 'CU' %}<br />Deactivate? <input type="checkbox" name="premium_id" value="{{ premium.id }}" />{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
@ -417,13 +415,14 @@ Please fix the following before launching your campaign:
|
|||
|
||||
<p>A few things to keep in mind:</p>
|
||||
<ul class="bullets">
|
||||
<li>For tax status reasons, premiums are not currently available to supporters who use donations instead of pledges.</li>
|
||||
<li>Are your premiums cumulative? That is, if you have a $10 and a $25 premium, does the $25 pledger get everything that the $10 pledger gets also? Either cumulative or not-cumulative is fine, but make sure you've communicated clearly</li>
|
||||
<li>Adding new premiums during your campaign is a great way to build momentum. If you do, make sure to leave a comment in the Comments tab of your campaign page to tell supporters (it will be automatically emailed to them). Some of them may want to change (hopefully increase) their pledge to take advantage of it.</li>
|
||||
<li>Also make sure to think about how your new premiums interact with old ones. If you add a new premium at $10, will people who have already pledged $25 be automatically eligible for it or not? Again, you can choose whatever you want; just be sure to communicate clearly.</li>
|
||||
</ul>
|
||||
|
||||
<h4>Acknowledgements</h4>
|
||||
<p>Your ungluers will also automatically receive the following acknowledgements:</p>
|
||||
<p>Your ungluers (including thos who use donations, will also automatically receive the following acknowledgements:</p>
|
||||
<ul class="terms">
|
||||
<li><em>Any amount</em> — The unglued ebook</li>
|
||||
<li><em>$25 and above</em> — Their name in the acknowledgements section under "supporters"</li>
|
||||
|
@ -431,8 +430,7 @@ Please fix the following before launching your campaign:
|
|||
<li><em>$100 and above</em> — Their name, profile link, & a dedication under "bibliophiles"</li>
|
||||
</ul>
|
||||
<p>Your premium values may be any amounts -- you do not need to offer premiums at $25/$50/$100. For example, if you offer a $30 premium, anyone pledging to it will be eligible for the $25 reward. This will be communicated to them during the pledging process; you do not need to explain it in your pitch.</p>
|
||||
{% endifequal %}
|
||||
{% ifequal campaign.type 2 %}
|
||||
{% elif campaign.type == 2 %}
|
||||
<h3> Offers to sell </h3>
|
||||
{% if not campaign.work.ebookfiles.0 %}
|
||||
<p> <b>An EPUB file for this work <a class="tabs1">needs to be loaded</a>!</b></p>
|
||||
|
@ -455,8 +453,7 @@ Please fix the following before launching your campaign:
|
|||
<p />
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endifequal %}
|
||||
{% ifequal campaign.type 3 %}
|
||||
{% elif campaign.type == 3 %}
|
||||
<h3> Suggested Contributions </h3>
|
||||
{% if not campaign.work.ebooks.0 %}
|
||||
<p> <b>ebook files for this work <a class="tabs1">need to be loaded</a>!</b></p>
|
||||
|
@ -476,10 +473,10 @@ Please fix the following before launching your campaign:
|
|||
{% endfor %}
|
||||
<p>When a contribution>$1 is made by a library, the library's verified users on unglue.it are not asked to make another contribution.</p>
|
||||
</div>
|
||||
{% endifequal %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% ifequal campaign_status 'INITIALIZED' %}
|
||||
{% if campaign_status == 'INITIALIZED' %}
|
||||
<div class="tabs-3">
|
||||
{% if campaign.launchable %}
|
||||
<p>Before you hit launch:</p>
|
||||
|
@ -494,47 +491,45 @@ Please fix the following before launching your campaign:
|
|||
|
||||
<div id="launchme"><a href="#" class="manage">Launch Campaign</a></div>
|
||||
{% else %}
|
||||
{% ifequal campaign.type 1 %}
|
||||
{% if campaign.type == 1 %}
|
||||
<p>Please make sure you've selected your campaign's edition and entered its description, funding goal, deadline, premiums, and previewed your campaign, before launching.</p>
|
||||
{% endifequal %}
|
||||
{% ifequal campaign.type 2 %}
|
||||
{% elif campaign.type == 2 %}
|
||||
<p>Please make sure you've selected your campaign's edition and entered its description, funding goal, initial ungluing date, prices, and previewed your campaign, before launching.</p>
|
||||
<p> Buy To Unglue campaigns can't be launched until ebook files <a class="tabs1">have been loaded</a> and <a class="tabs2">pricing has been set and made active</a></p>
|
||||
{% endifequal %}
|
||||
{% ifequal campaign.type 3 %}
|
||||
{% elif campaign.type == 3 %}
|
||||
<p>Please make sure you've selected your campaign's edition and entered its description and previewed your campaign, before launching.</p>
|
||||
<p> Thanks for Ungluing campaigns can't be launched until ebook files <a class="tabs1">have been loaded</a> and <a class="tabs2">a suggested contribution has been set </a></p>
|
||||
{% endifequal %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
{% endifequal %}
|
||||
{% endif %}
|
||||
|
||||
<div class="tabs-3">
|
||||
{% if campaign_status == 'ACTIVE' or campaign_status == 'SUCCESSFUL' %}
|
||||
{% if campaign_status == 'ACTIVE' %}
|
||||
<h2 class="thank-you">Your campaign is now active! Hooray!</h2>
|
||||
<h2 class="thank-you">Your campaign is now active! Hooray!</h2>
|
||||
|
||||
<h3>What to do next</h3>
|
||||
<ul class="bullets">
|
||||
<li>Tell your friends, relatives, media contacts, professional organizations, social media networks -- everyone!</li>
|
||||
{% ifequal campaign.type 1 %}
|
||||
<li>Check in with your campaign frequently. Use comments, description updates, and maybe new premiums to spark additional interest, keep supporters engaged, and keep the momentum going.</li>
|
||||
{% else %}
|
||||
<li>Check in on your sales frequently. Remember, you control the per-copy pricing, so think about the promotional value of a temporary price reduction.</li>
|
||||
{% endifequal %}
|
||||
<li>Watch media and social networks for mentions of your campaign, and engage in those conversations.</li>
|
||||
<li>Need help doing any of this? Talk to us.</li>
|
||||
</ul>
|
||||
<h3>What to do next</h3>
|
||||
<ul class="bullets">
|
||||
<li>Tell your friends, relatives, media contacts, professional organizations, social media networks -- everyone!</li>
|
||||
{% if campaign.type == 1 %}
|
||||
<li>Check in with your campaign frequently. Use comments, description updates, and maybe new premiums to spark additional interest, keep supporters engaged, and keep the momentum going.</li>
|
||||
{% else %}
|
||||
<li>Check in on your sales frequently. Remember, you control the per-copy pricing, so think about the promotional value of a temporary price reduction.</li>
|
||||
{% endif %}
|
||||
<li>Watch media and social networks for mentions of your campaign, and engage in those conversations.</li>
|
||||
<li>Need help doing any of this? Talk to us.</li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% ifequal campaign.type 1 %}
|
||||
<h3>Acknowledgements</h3>
|
||||
<p>When you're logged in, the "Ungluers" tab on the <a href="{% url 'work' work.id %}">campaign page</a> will tell you a bit about each ungluer- when they last pledged, for example, and you can send individual messages to each ungluer. Use this tool with care! You can see who your biggest supporters are by looking at the <a href="{% url 'work_acks' campaign.work_id %}">sample acknowledgement page</a>.
|
||||
After your campaign succeeds, you can used this page to generate epub code for the acknowledgements section of your unglued ebook.
|
||||
</p>
|
||||
{% if campaign.type == 1 %}
|
||||
<h3>Acknowledgements</h3>
|
||||
<p>When you're logged in, the "Ungluers" tab on the <a href="{% url 'work' work.id %}">campaign page</a> will tell you a bit about each ungluer- when they last pledged, for example, and you can send individual messages to each ungluer. Use this tool with care! You can see who your biggest supporters are by looking at the <a href="{% url 'work_acks' campaign.work_id %}">sample acknowledgement page</a>.
|
||||
After your campaign succeeds, you can used this page to generate epub code for the acknowledgements section of your unglued ebook.
|
||||
</p>
|
||||
{% else %}
|
||||
{% comment %}This might be a good place to put a sales report. {% endcomment %}
|
||||
{% endifequal %}
|
||||
{% comment %}This might be a good place to put a sales report. {% endcomment %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ You are subscribed to the Unglue.it Newsletter. It comes roughly twice a month.
|
|||
<input type="submit" name="ml_unsubscribe" value="Unsubscribe" />
|
||||
</form>
|
||||
{% else %}
|
||||
You are NOT subscribed to the Unglue.it Newsletter. It comes roughly twice a month. If you have just become an ungluer, your list invitation should be on its way. Put "gluenews@gluejar.com" in your contact list to make sure you get it.<br />
|
||||
You are NOT subscribed to the Unglue.it Newsletter. It comes roughly twice a month. If you have just become an ungluer, your list invitation should be on its way. Put "unglueit@ebookfoundation.org" in your contact list to make sure you get it.<br />
|
||||
<form id="ml_subscribe" action="{% url 'ml_subscribe' %}" method="POST">
|
||||
{% csrf_token %}
|
||||
<input type="submit" name="ml_subscribe" value="Subscribe" />
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{% load humanize %}
|
||||
As you requested, we've updated your account with the payment method you provided.
|
||||
|
||||
If you have any questions, we are happy to help. Simply email us at support@gluejar.com.
|
||||
If you have any questions, we are happy to help. Simply email us at unglueit@ebookfoundation.org.
|
||||
|
||||
{% if user.profile.account %}
|
||||
The current card we have on file:
|
||||
|
|
|
@ -3,7 +3,7 @@ We want to let you know that your {{ user.profile.account.card_type }} card endi
|
|||
|
||||
When you receive your new card, simply go to https://{{ site.domain }}{% url 'manage_account' %} to enter your card information. Thank you!
|
||||
|
||||
If you have any questions, we are happy to help. Simply email us at support@gluejar.com.
|
||||
If you have any questions, we are happy to help. Simply email us at unglueit@ebookfoundation.org.
|
||||
|
||||
{% if user.profile.account %}
|
||||
The current card we have on file:
|
||||
|
|
|
@ -3,7 +3,7 @@ We want to give you advance notice that your {{ user.profile.account.card_type }
|
|||
|
||||
When you receive your new card, simply go to https://{{ site.domain }}{% url 'manage_account' %} to enter your card information. Thank you!
|
||||
|
||||
If you have any questions, we are happy to help. Simply email us at support@gluejar.com.
|
||||
If you have any questions, we are happy to help. Simply email us at unglueit@ebookfoundation.org.
|
||||
|
||||
{% if user.profile.account %}
|
||||
The current card we have on file:
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block comments_graphical %}
|
||||
{% ifequal transaction.host 'credit' %}
|
||||
{% if transaction.host == 'credit' %}
|
||||
Your Unglue.it transaction has completed and ${{transaction.max_amount|floatformat:2|intcomma}} has been deducted from your Unglue.it credit balance.
|
||||
You have ${{transaction.user.credit.available|default:"0"}} of credit left.
|
||||
{% else %}
|
||||
|
@ -19,7 +19,7 @@
|
|||
{% else %}
|
||||
Your Unglue.it credit card transaction has completed and your credit card has been charged ${{ transaction.amount|floatformat:2|intcomma }}.
|
||||
{% endif %}
|
||||
{% endifequal %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block comments_textual %}
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
{% extends 'notification/base.html' %}
|
||||
|
||||
|
||||
{% load i18n %}
|
||||
{% load sass_tags %}
|
||||
{% load truncatechars %}
|
||||
|
||||
{% block title %}{% trans "Notification Settings" %}{% endblock %}
|
||||
|
||||
{% block extra_css %}
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/notices.css" />
|
||||
<link type="text/css" rel="stylesheet" href="{% sass_src 'scss/notices.scss' %}" />
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
|
|
|
@ -1,4 +1,19 @@
|
|||
{% load humanize %}An Ungluing!
|
||||
{% load humanize %}{% if transaction.donation %}{% ifequal transaction.host 'credit' %}Your Unglue.it transaction has completed and ${{transaction.max_amount|default:"0"}} has been deducted from your Unglue.it credit balance. You have ${{transaction.user.credit.available|default:"0"}} of credit left. {% else %}{% if transaction.max_amount > transaction.amount %}Your transaction for ${{transaction.max_amount|default:"0"}} has completed. Your credit card has been charged ${{transaction.amount}} and the rest has been deducted from your unglue.it credit balance. You have ${{transaction.user.credit.available|default:"0"}} of credit left. {% else %}Your Unglue.it credit card transaction has completed and your credit card has been charged ${{ transaction.amount|default:"0" }}. {% endif %}{% endifequal %}
|
||||
|
||||
Your donation of ${{transaction.max_amount|default:"0"}} to the Free Ebook Foundation will support our effort to release {{ transaction.campaign.work.title }} to the world in an unglued ebook edition. We'll email you if the campaign succeeds, and when the ebook is available for download. If you'd like to visit the campaign page, click here:
|
||||
https://{{ current_site.domain }}{% url 'work' transaction.campaign.work_id %}
|
||||
|
||||
In case the campaign for {{ transaction.campaign.work.title }} does not succeed, we'll use your donation in support of other ungluing campaigns which qualify for charitable support.
|
||||
|
||||
The Free Ebook Foundation is a US 501(c)3 non-profit organization. Our tax ID number is 61-1767266. Your gift is tax deductible to the full extent provided by the law.
|
||||
|
||||
For more information about the Free Ebook Foundation, visit https://ebookfoundation.org/
|
||||
|
||||
Thank you again for your generous support.
|
||||
|
||||
{{ transaction.campaign.rightsholder }} and the Unglue.it team
|
||||
|
||||
{% else %}An Ungluing!
|
||||
|
||||
Thanks to you and other ungluers, {{ transaction.campaign.work.title }} will be released to the world in an unglued ebook edition. Your credit card has been charged ${{ transaction.amount|floatformat:2|intcomma }}.
|
||||
|
||||
|
@ -13,4 +28,4 @@ https://{{ current_site.domain }}{% url 'work' transaction.campaign.work_id %}
|
|||
Thank you again for your support.
|
||||
|
||||
{{ transaction.campaign.rightsholder }} and the Unglue.it team
|
||||
|
||||
{% endif %}
|
||||
|
|
|
@ -7,10 +7,37 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block comments_graphical %}
|
||||
Hooray! The campaign for <a href="{% url 'work' transaction.campaign.work_id %}">{{ transaction.campaign.work.title }}</a> has succeeded. Your credit card has been charged ${{ transaction.amount|floatformat:2|intcomma }}. Thank you again for your help.
|
||||
{% if transaction.donation %}
|
||||
{% if transaction.host == 'credit' %}
|
||||
Your Unglue.it transaction has completed and ${{transaction.max_amount|floatformat:2|intcomma}} has been deducted from your Unglue.it credit balance.
|
||||
You have ${{transaction.user.credit.available|default:"0"}} of credit left.
|
||||
{% elif transaction.max_amount > transaction.amount %}
|
||||
Your transaction for ${{transaction.max_amount|floatformat:2|intcomma}} has completed.
|
||||
Your credit card has been charged ${{transaction.amount}} and the
|
||||
rest has been deducted from your unglue.it credit balance.
|
||||
You have ${{transaction.user.credit.available|intcomma}} of credit left.
|
||||
{% else %}
|
||||
Your Unglue.it credit card transaction has completed and your credit card has been charged ${{ transaction.amount|floatformat:2|intcomma }}.
|
||||
{% endif %}
|
||||
{% else %} Hooray! The campaign for <a href="{% url 'work' transaction.campaign.work_id %}">{{ transaction.campaign.work.title }}</a> has succeeded. Your credit card has been charged ${{ transaction.amount|floatformat:2|intcomma }}. Thank you again for your help.
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block comments_textual %}
|
||||
{% if transaction.donation %}
|
||||
<p>Your donation of ${{transaction.max_amount|default:"0"}} to the Free Ebook Foundation will support our effort to release {{ transaction.campaign.work.title }} to the world in an unglued ebook edition. We'll email you if the campaign succeeds, and when the ebook is available for download. If you'd like to visit the campaign page, <a href="{% url 'work' transaction.campaign.work_id %}">click here</a>. </p>
|
||||
|
||||
<p>In case the campaign for {{ transaction.campaign.work.title }} does not succeed, we'll use your donation in support of other ungluing campaigns which qualify for charitable support.</p>
|
||||
|
||||
<p>The Free Ebook Foundation is a US 501(c)3 non-profit organization. Our tax ID number is 61-1767266. Your gift is tax deductible to the full extent provided by the law.</p>
|
||||
|
||||
<p>For more information about the Free Ebook Foundation, visit <a href="https://ebookfoundation.org/">https://ebookfoundation.org/</a></p>
|
||||
|
||||
<p>Thank you again for your generous support.</p>
|
||||
|
||||
<p>{{ transaction.campaign.rightsholder }} and the Unglue.it team</p>
|
||||
|
||||
{% else %}
|
||||
<p>Congratulations!</p>
|
||||
|
||||
<p>Thanks to you and other ungluers, {{ transaction.campaign.work.title }} will be released to the world in an unglued ebook edition. {{ transaction.host|capfirst }} has been charged to your credit card.</p>
|
||||
|
@ -28,4 +55,5 @@
|
|||
</p>
|
||||
<p>{{ transaction.campaign.rightsholder }} and the Unglue.it team
|
||||
</p>
|
||||
{% endif %}
|
||||
{% endblock %}
|
|
@ -1 +1 @@
|
|||
Your pledge to the campaign to unglue {{transaction.campaign.work.title}} has been charged.
|
||||
Your {% if transaction.donation %}donation{% else %}pledge{% endif %} for the campaign to unglue {{transaction.campaign.work.title}} has been charged.
|
|
@ -17,7 +17,7 @@ Or the best idea: talk about it with those you love. We'll need lots of help fr
|
|||
|
||||
If you want to change your pledge, just use the button at https://{{ current_site.domain }}{% url 'work' transaction.campaign.work_id %}
|
||||
|
||||
If you have any problems with your pledge, don't hesitate to contact us at support@gluejar.com
|
||||
If you have any problems with your pledge, don't hesitate to contact us at unglueit@ebookfoundation.org
|
||||
|
||||
Thanks for being part of Unglue.it.
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ You can send the link yourself to make sure that it gets to the right place.
|
|||
You can also "regift" the ebook to a different email address. To do so, FIRST log in to the {{ gift.giver }} account on Unglue.it, and then click on
|
||||
https://{{ current_site.domain }}{% url 'receive_gift' gift.acq.nonce %}
|
||||
|
||||
If you have any problems or questions, don't hesitate to contact Unglue.it support at support@gluejar.com
|
||||
If you have any problems or questions, don't hesitate to contact Unglue.it support at unglueit@ebookfoundation.org
|
||||
|
||||
the Unglue.it team
|
||||
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
The Rights Holder Agreement, reproduced in plain text below, for {{ rights_holder.rights_holder_name }} has been accepted and is now an official Unglue.it rights holder.
|
||||
|
||||
Here's what to do next: Find on Unglue.it. On the More... tab of the book page, you'll now see an option to claim the book. Once you've claimed the book, you can edit its metadata.
|
||||
|
||||
If you can't find your books Unglue.it, that's okay. You can add your books to Unglue.it directly - use this link: https://unglue.it{% url 'rightsholders' %}#add_your_books
|
||||
|
||||
Need help with any of this? Email us at rights@ebookfoundation.org and we'll do our best to help.
|
||||
|
||||
The Unglue.it team
|
||||
|
||||
##################
|
||||
{{ agreement }}
|
||||
##################
|
||||
{{ signature }}
|
|
@ -0,0 +1,4 @@
|
|||
{% extends "notification/notice_template.html" %}
|
||||
{% block comments_textual %}
|
||||
You are now an approved rights holder on Unglue.it. For your next step, <a href="{% url 'rightsholders' %}#add_your_books">find your works in our database and claim them or add them directly</a>.
|
||||
{% endblock %}
|
|
@ -0,0 +1 @@
|
|||
You're now an accepted rights holder on Unglue.it.
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
You are now free to start a campaign to sell or unglue your work. If you're logged in, you will see the option to open a campaign at https://{{ current_site.domain }}/rightsholders . (You can also find this page by clicking on "Rights Holder Tools" at the bottom of any Unglue.it page.)
|
||||
|
||||
To run a campaign, you'll need to set up campaign parameters. You'll also need to write a pitch. For Pledge-to-Unglue and Buy-to-Unglue campaigns, this will appear in the Description tab on your book's page (https://{{ current_site.domain }}{% url 'work' claim.work_id %}). Think about who your book's audience is, and remind them why they'll love this book -- your pitch is not a catalog page! We encourage video, audio, and links to make your pitch come alive. For Thanks-for-Ungluing, your pitch will occur when the user clicks a Download button. You should emphasize how the ungluer's support enables you to keep doing what you do. Feel free to email us (rights@gluejar.com) if you need any help with this.
|
||||
To run a campaign, you'll need to set up campaign parameters. You'll also need to write a pitch. For Pledge-to-Unglue and Buy-to-Unglue campaigns, this will appear in the Description tab on your book's page (https://{{ current_site.domain }}{% url 'work' claim.work_id %}). Think about who your book's audience is, and remind them why they'll love this book -- your pitch is not a catalog page! We encourage video, audio, and links to make your pitch come alive. For Thanks-for-Ungluing, your pitch will occur when the user clicks a Download button. You should emphasize how the ungluer's support enables you to keep doing what you do. Feel free to email us (rights@ebookfoundation.org) if you need any help with this.
|
||||
|
||||
If you're running a Buy-to-Unglue or Thanks-for-Ungluing Campaign, now is the time to upload your digital files. For Buy-to-Unglue, you need to decide on revenue targets and pricing for individual and library licenses.
|
||||
|
||||
|
@ -11,13 +11,12 @@ If you're running a Pledge Campaign, you need to decide on a funding target, and
|
|||
|
||||
Finally, think about how you're going to publicize your campaign: social media, newsletters, media contacts, professional organizations, et cetera. Have a plan for how to reach out to these potential supporters before you launch your campaign. Your supporters' sense of connection with you and your book is key to your campaign's success. Again, email us if you'd like help.
|
||||
|
||||
We're thrilled to be working with you.
|
||||
{% endifequal %}
|
||||
{% ifequal claim.status 'pending' %}
|
||||
{{ claim.rights_holder }}'s claim to {{ claim.work.title }} (https://{{ current_site.domain }}{% url 'work' claim.work_id %}) on Unglue.it has been entered. Our team will examine the claim and get back to you soon.
|
||||
{{ claim.rights_holder }}'s claim to {{ claim.work.title }} (https://{{ current_site.domain }}{% url 'work' claim.work_id %}) on Unglue.it has been entered.
|
||||
{% endifequal %}
|
||||
{% ifequal claim.status 'release' %}
|
||||
{{ claim.rights_holder }}'s claim to {{ claim.work.title }} (https://{{ current_site.domain }}{% url 'work' claim.work_id %}) on Unglue.it has been released. email us (rights@gluejar.com) if you have any questions about this.
|
||||
{{ claim.rights_holder }}'s claim to {{ claim.work.title }} (https://{{ current_site.domain }}{% url 'work' claim.work_id %}) on Unglue.it has been released. email us (rights@ebookfoundation.org) if you have any questions about this.
|
||||
{% endifequal %}
|
||||
|
||||
The Unglue.it team
|
|
@ -17,7 +17,7 @@
|
|||
<br /><br />
|
||||
You are now free to start a campaign to sell or unglue your work. If you're logged in, you can <a href="{% url 'rightsholders' %}">open a campaign</a>. (You can also find this page by clicking on "Rights Holder Tools" at the bottom of any Unglue.it page.)
|
||||
<br /><br />
|
||||
To run a campaign, you'll need to set up campaign parameters. You'll also need to write a pitch. For Pledge-to-Unglue and Buy-to-Unglue campaigns, this will appear in the Description tab on your book's <a href="{% url 'work' claim.work_id %}">work page</a>. Think about who your book's audience is, and remind them why they'll love this book -- your pitch is not a catalog page! We encourage video, audio, and links to make your pitch come alive. For Thanks-for-Ungluing, your pitch will occur when the user clicks a Download button. You should emphasize how the ungluer's support enables you to keep doing what you do. Feel free to email us (rights@gluejar.com) if you need any help with this.
|
||||
To run a campaign, you'll need to set up campaign parameters. You'll also need to write a pitch. For Pledge-to-Unglue and Buy-to-Unglue campaigns, this will appear in the Description tab on your book's <a href="{% url 'work' claim.work_id %}">work page</a>. Think about who your book's audience is, and remind them why they'll love this book -- your pitch is not a catalog page! We encourage video, audio, and links to make your pitch come alive. For Thanks-for-Ungluing, your pitch will occur when the user clicks a Download button. You should emphasize how the ungluer's support enables you to keep doing what you do. Feel free to email us (rights@ebookfoundation.org) if you need any help with this.
|
||||
<br /><br />
|
||||
If you're running a Buy-to-Unglue or Thanks-for-Ungluing Campaign, now is the time to upload your digital files. For Buy-to-Unglue, you need to decide on revenue targets and pricing for individual and library licenses.
|
||||
<br /><br />
|
||||
|
@ -25,12 +25,11 @@ If you're running a Pledge Campaign, you need to decide on a funding target, and
|
|||
<br /><br />
|
||||
Finally, think about how you're going to publicize your campaign: social media, newsletters, media contacts, professional organizations, et cetera. Have a plan for how to reach out to these potential supporters before you launch your campaign. Your supporters' sense of connection with you and your book is key to your campaign's success. Again, email us if you'd like help.
|
||||
|
||||
We're thrilled to be working with you.
|
||||
{% endifequal %}
|
||||
{% ifequal claim.status 'pending' %}
|
||||
The claim for <a href="{% url 'work' claim.work_id %}">{{ claim.work.title }}</a> will be examined, and we'll email you. <a href="{% url 'feedback' %}?page={{request.build_absolute_uri|urlencode:""}}">Contact us</a> if you need any help.
|
||||
{% endifequal %}
|
||||
{% ifequal claim.status 'release' %}
|
||||
The claim for <a href="{% url 'work' claim.work_id %}">{{ claim.work.title }}</a> has been released. Contact us at rights@gluejar.com if you have questions.
|
||||
The claim for <a href="{% url 'work' claim.work_id %}">{{ claim.work.title }}</a> has been released. Contact us at rights@ebookfoundation.org if you have questions.
|
||||
{% endifequal %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,11 +1,20 @@
|
|||
Your Platform Services Agreement has been accepted and you're now an official Unglue.it rights holder.
|
||||
Your Rights Holder Agreement for {{ rights_holder.rights_holder_name }} has been received and Unglue.it staff is reviewing it.
|
||||
|
||||
Here's what to do next. Find your book(s) on Unglue.it. On the More... tab of the book page, you'll now see an option to claim the book. Do this. We'll follow up. Once we've approved your claim, you'll be able to run campaigns for the book.
|
||||
Here's the information we received - we'll use this to verify that you really exist and can be relied upon to fulfill your obligations. Once we've reviewed and approved the agreement, you'll receive by email a digitally signed copy for your reference.
|
||||
|
||||
If your book isn't listed in Google Books (which powers our search), you won't be able to find it at Unglue.it. That's okay. You can submit your books for inclusion in Google's search results: https://books.google.com/googlebooks/publishers.html . We can also create a custom page for you; just notify us.
|
||||
Rights Holder: {{ rights_holder.rights_holder_name }}
|
||||
Unglue.it Username for Rights Holder: {{ rights_holder.owner.username }}
|
||||
Business Address:
|
||||
{{ rights_holder.address }}
|
||||
Mailing Address:
|
||||
{{ rights_holder.mailing }}
|
||||
Tel: {{ rights_holder.telephone }}
|
||||
Email: {{ rights_holder.email }}
|
||||
Signer Name: {{ rights_holder.signer }}
|
||||
Signer Title: {{ rights_holder.signer_title }}
|
||||
Signature: {{ rights_holder.signature }}
|
||||
|
||||
You can also start thinking ahead about what you'd like your campaigns to look like and how you'd like to publicize them. Some good things to brainstorm: your campaign pitch; any photos or video you can include; compelling premiums you might be able to offer; what you want your target to be and how long you think your campaign should last; and how to share your campaign with your social networks (online and off) and media contacts.
|
||||
|
||||
Need help with any of this? We'd be delighted. Email us at rights@gluejar.com. We're thrilled to be working with you.
|
||||
Need help with any of this? Email us at rights@ebookfoundation.org.
|
||||
|
||||
The Unglue.it team
|
|
@ -1,4 +1,4 @@
|
|||
{% extends "notification/notice_template.html" %}
|
||||
{% block comments_textual %}
|
||||
You are now an approved rights holder on Unglue.it. For your next step, find your works in our database and claim them (under the More... tab). See your email for more details.
|
||||
The Unglue.it rights holder agreement for {{ rights_holder.rights_holder_name }} has been received. Unglue.it staff will use the information you've supplied to verify that you really exist and can be relied upon to fulfill your obligations. Once we've reviewed and approved the agreement, you'll receive by email a digitally signed copy for your reference.
|
||||
{% endblock %}
|
|
@ -1 +1 @@
|
|||
You're now a confirmed rights holder on Unglue.it.
|
||||
Your Unglue.it rights holder agreement has been received.
|
|
@ -1,4 +1,4 @@
|
|||
{% if pledged %}You pledged toward it{% else %}You put it on your list{% endif %}, and now the campaign for {{ campaign.work.title}} (https://{{current_site.domain}}{% url 'work' campaign.work_id %}) has succeeded.
|
||||
{% if pledged %}You supported it{% else %}You put it on your list{% endif %}, and now the campaign for {{ campaign.work.title}} (https://{{current_site.domain}}{% url 'work' campaign.work_id %}) has succeeded.
|
||||
{% ifequal campaign.type 1 %}
|
||||
You will notified when an Unglued ebook edition is available, within 90 days.
|
||||
{% if pledged %}
|
||||
|
|
|
@ -24,9 +24,9 @@ The Creative Commons licensing terms for {{ work.title }} allow you to redistrib
|
|||
{% endif %}
|
||||
|
||||
{% if work.last_campaign_status == 'SUCCESSFUL' %}
|
||||
If you have any problems with this unglued ebook, please don't hesitate to let us know at support@gluejar.com. And if you love being able to give this ebook for free to all of your friends, please consider supporting other ebooks for ungluing.
|
||||
If you have any problems with this unglued ebook, please don't hesitate to let us know at unglueit@ebookfoundation.org. And if you love being able to give this ebook for free to all of your friends, please consider supporting other ebooks for ungluing.
|
||||
{% else %}
|
||||
If you have any problems with these ebook files, please don't hesitate to let us know at support@gluejar.com. For example, if the file isn't what it says it is, or if the licensing or copyright status is misrepresented, we want to know as soon as possble.
|
||||
If you have any problems with these ebook files, please don't hesitate to let us know at unglueit@ebookfoundation.org. For example, if the file isn't what it says it is, or if the licensing or copyright status is misrepresented, we want to know as soon as possble.
|
||||
{% endif %}
|
||||
|
||||
Thanks,
|
||||
|
|
|
@ -2,9 +2,7 @@ Alas. The campaign to unglue {{ campaign.work.title }} (https://{{current_site.
|
|||
|
||||
If you pledged toward this work, your pledge will expire shortly and your credit card will not be charged, nor will you receive any premiums.
|
||||
|
||||
Still want to give {{ campaign.work.title }} to the world? Don't despair. Keep it on your wishlist and tell everyone why you love this book. The rights holder, {{ campaign.rightsholder }}, may run a campaign with different terms in the future. With your help, we may yet be able to unglue {{ campaign.work.title }}.
|
||||
|
||||
There are also other books with active campaigns that need your help: https://unglue.it/campaigns/ending .
|
||||
If you donated in support of this work, your donation will be used to support other campaigns that qualify for charitable support.
|
||||
|
||||
Thank you for your support.
|
||||
|
||||
|
|
|
@ -10,11 +10,7 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block comments_textual %}
|
||||
If you pledged toward this work, your pledge will expire shortly and your credit card will not be charged, nor will you receive any premiums.
|
||||
|
||||
Still want to give {{ campaign.work.title }} to the world? Don't despair. Keep it on your faves and tell everyone why you love this book. The rights holder, {{ campaign.rightsholder }}, may run a campaign with different terms in the future. With your help, we may yet be able to unglue {{ campaign.work.title }}.
|
||||
|
||||
There are also <a href="https://unglue.it/campaigns/ending">other books with active campaigns</a> that need your help.
|
||||
If you pledged toward this work, your pledge will expire shortly and your credit card will not be charged, nor will you receive any premiums. If you donated in support of this work, your donation will be used to support other campaigns that qualify for charitable support.
|
||||
|
||||
Thank you for your support.
|
||||
{% endblock %}
|
|
@ -25,10 +25,6 @@
|
|||
<style type="text/css">
|
||||
.header-text{height:36px;line-height:36px;display:block;text-decoration:none;font-weight:bold;letter-spacing:-0.05em}
|
||||
.panelborders{border-width:1px 0;border-style:solid none;border-color:#fff}
|
||||
.roundedspan{border:1px solid #d4d4d4;-moz-border-radius:7px;-webkit-border-radius:7px;border-radius:7px;padding:1px;color:#fff;margin:0 8px 0 0;display:inline-block}
|
||||
.roundedspan>span{padding:7px 7px;min-width:15px;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;text-align:center;display:inline-block}
|
||||
.roundedspan>span .hovertext{display:none}
|
||||
.roundedspan>span:hover .hovertext{display:inline}
|
||||
.mediaborder{padding:5px;border:solid 5px #edf3f4}
|
||||
.actionbuttons{width:auto;height:36px;line-height:36px;background:#8dc63f;-moz-border-radius:32px;-webkit-border-radius:32px;border-radius:32px;color:white;cursor:pointer;font-size:13px;font-weight:bold;padding:0 15px;border:0;margin:5px 0}
|
||||
.header-text{height:36px;line-height:36px;display:block;text-decoration:none;font-weight:bold;letter-spacing:-0.05em}
|
||||
|
@ -52,15 +48,12 @@
|
|||
.border{-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;border:solid 2px #d6dde0;margin:5px auto;padding-right:5px;padding-left:5px}
|
||||
.btn_support.kindle{height:40px}
|
||||
.btn_support.kindle a{width:auto;font-size:15px}
|
||||
.preview{border:solid 3px #e35351;-moz-border-radius:7px;-webkit-border-radius:7px;border-radius:7px;clear:both;padding:5px 10px;font-size:13px;width:90%}
|
||||
.preview a{color:#8dc63f}
|
||||
.launch_top{border:solid 3px #e35351;-moz-border-radius:7px;-webkit-border-radius:7px;border-radius:7px;clear:both;padding:5px 10px;font-size:13px;width:90%;border-color:#8dc63f;margin:10px auto 0 auto;font-size:15px;line-height:22.5px}
|
||||
.launch_top a{color:#8dc63f}
|
||||
.launch_top.pale{border-color:#d6dde0;font-size:13px}
|
||||
.launch_top.alert{border-color:#e35351;font-size:13px}
|
||||
.preview_content{border:solid 3px #e35351;-moz-border-radius:7px;-webkit-border-radius:7px;border-radius:7px;clear:both;padding:5px 10px;font-size:13px;width:90%;width:80%;margin:10px auto}
|
||||
.preview_content a{color:#8dc63f}
|
||||
.utilityheaders{text-transform:uppercase;color:#3d4e53;font-size:15px;display:block}
|
||||
html,body{height:100%}
|
||||
body{padding:0 0 20px 0;margin:0;font-size:13px;line-height:16.900000000000002px;font-family:"Lucida Grande","Lucida Sans Unicode","Lucida Sans",Arial,Helvetica,sans-serif;color:#3d4e53}
|
||||
a{font-weight:bold;font-size:inherit;text-decoration:none;cursor:pointer;color:#6994a3}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue