Merge branch 'master' into google_books_button

pull/1/head
Raymond Yee 2015-03-18 11:20:28 -07:00
commit eb034013eb
10 changed files with 433 additions and 21 deletions

View File

@ -0,0 +1,351 @@
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding field 'Ebook.filesize'
db.add_column('core_ebook', 'filesize',
self.gf('django.db.models.fields.PositiveIntegerField')(null=True),
keep_default=False)
def backwards(self, orm):
# Deleting field 'Ebook.filesize'
db.delete_column('core_ebook', 'filesize')
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'booxtream.boox': {
'Meta': {'object_name': 'Boox'},
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'download_link_epub': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True'}),
'download_link_mobi': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True'}),
'downloads_remaining': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '0'}),
'expirydays': ('django.db.models.fields.PositiveSmallIntegerField', [], {}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'referenceid': ('django.db.models.fields.CharField', [], {'max_length': '32'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'core.acq': {
'Meta': {'object_name': 'Acq'},
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}),
'expires': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'lib_acq': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'loans'", 'null': 'True', 'to': "orm['core.Acq']"}),
'license': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '1'}),
'nonce': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True'}),
'refreshed': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'refreshes': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 3, 5, 0, 0)', 'auto_now_add': 'True', 'blank': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'acqs'", 'to': "orm['auth.User']"}),
'watermarked': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['booxtream.Boox']", 'null': 'True'}),
'work': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'acqs'", 'to': "orm['core.Work']"})
},
'core.author': {
'Meta': {'object_name': 'Author'},
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'editions': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'authors'", 'symmetrical': 'False', 'to': "orm['core.Edition']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '500'})
},
'core.badge': {
'Meta': {'object_name': 'Badge'},
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '72', 'blank': 'True'})
},
'core.campaign': {
'Meta': {'object_name': 'Campaign'},
'activated': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'db_index': 'True'}),
'amazon_receiver': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
'cc_date_initial': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'deadline': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'db_index': 'True'}),
'description': ('ckeditor.fields.RichTextField', [], {'null': 'True'}),
'details': ('ckeditor.fields.RichTextField', [], {'null': 'True', 'blank': 'True'}),
'do_watermark': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'dollar_per_day': ('django.db.models.fields.FloatField', [], {'null': 'True'}),
'edition': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'campaigns'", 'null': 'True', 'to': "orm['core.Edition']"}),
'email': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'left': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '14', 'decimal_places': '2', 'db_index': 'True'}),
'license': ('django.db.models.fields.CharField', [], {'default': "'CC BY-NC-ND'", 'max_length': '255'}),
'managers': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'campaigns'", 'symmetrical': 'False', 'to': "orm['auth.User']"}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '500', 'null': 'True'}),
'paypal_receiver': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
'publisher': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'campaigns'", 'null': 'True', 'to': "orm['core.Publisher']"}),
'status': ('django.db.models.fields.CharField', [], {'default': "'INITIALIZED'", 'max_length': '15', 'null': 'True', 'db_index': 'True'}),
'target': ('django.db.models.fields.DecimalField', [], {'default': '0.0', 'null': 'True', 'max_digits': '14', 'decimal_places': '2'}),
'type': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '1'}),
'use_add_ask': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'work': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'campaigns'", 'to': "orm['core.Work']"})
},
'core.campaignaction': {
'Meta': {'object_name': 'CampaignAction'},
'campaign': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'actions'", 'to': "orm['core.Campaign']"}),
'comment': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'type': ('django.db.models.fields.CharField', [], {'max_length': '15'})
},
'core.celerytask': {
'Meta': {'object_name': 'CeleryTask'},
'active': ('django.db.models.fields.NullBooleanField', [], {'default': 'True', 'null': 'True', 'blank': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2015, 3, 5, 0, 0)', 'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'null': 'True'}),
'function_args': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
'function_name': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'task_id': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tasks'", 'null': 'True', 'to': "orm['auth.User']"})
},
'core.claim': {
'Meta': {'object_name': 'Claim'},
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'rights_holder': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'claim'", 'to': "orm['core.RightsHolder']"}),
'status': ('django.db.models.fields.CharField', [], {'default': "'active'", 'max_length': '7'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'claim'", 'to': "orm['auth.User']"}),
'work': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'claim'", 'to': "orm['core.Work']"})
},
'core.ebook': {
'Meta': {'object_name': 'Ebook'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}),
'download_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'edition': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'ebooks'", 'to': "orm['core.Edition']"}),
'filesize': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
'format': ('django.db.models.fields.CharField', [], {'max_length': '25'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'provider': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'rights': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_index': 'True'}),
'url': ('django.db.models.fields.URLField', [], {'max_length': '1024'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'})
},
'core.ebookfile': {
'Meta': {'object_name': 'EbookFile'},
'asking': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'edition': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'ebook_files'", 'to': "orm['core.Edition']"}),
'file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
'format': ('django.db.models.fields.CharField', [], {'max_length': '25'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'core.edition': {
'Meta': {'object_name': 'Edition'},
'cover_image': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'publication_date': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50', 'null': 'True', 'blank': 'True'}),
'publisher_name': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'editions'", 'null': 'True', 'to': "orm['core.PublisherName']"}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '1000'}),
'unglued': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'work': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'editions'", 'null': 'True', 'to': "orm['core.Work']"})
},
'core.gift': {
'Meta': {'object_name': 'Gift'},
'acq': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'gifts'", 'to': "orm['core.Acq']"}),
'giver': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'gifts'", 'to': "orm['auth.User']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'message': ('django.db.models.fields.TextField', [], {'default': "''", 'max_length': '512'}),
'to': ('django.db.models.fields.CharField', [], {'max_length': '75', 'blank': 'True'}),
'used': ('django.db.models.fields.DateTimeField', [], {'null': 'True'})
},
'core.hold': {
'Meta': {'object_name': 'Hold'},
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'library': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'holds'", 'to': "orm['libraryauth.Library']"}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'holds'", 'to': "orm['auth.User']"}),
'work': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'holds'", 'to': "orm['core.Work']"})
},
'core.identifier': {
'Meta': {'unique_together': "(('type', 'value'),)", 'object_name': 'Identifier'},
'edition': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'identifiers'", 'null': 'True', 'to': "orm['core.Edition']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'type': ('django.db.models.fields.CharField', [], {'max_length': '4'}),
'value': ('django.db.models.fields.CharField', [], {'max_length': '250'}),
'work': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'identifiers'", 'to': "orm['core.Work']"})
},
'core.key': {
'Meta': {'object_name': 'Key'},
'encrypted_value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
},
'core.libpref': {
'Meta': {'object_name': 'Libpref'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'marc_link_target': ('django.db.models.fields.CharField', [], {'default': "'UNGLUE'", 'max_length': '6'}),
'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'libpref'", 'unique': 'True', 'to': "orm['auth.User']"})
},
'core.offer': {
'Meta': {'object_name': 'Offer'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'license': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '1'}),
'price': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '6', 'decimal_places': '2'}),
'work': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'offers'", 'to': "orm['core.Work']"})
},
'core.premium': {
'Meta': {'object_name': 'Premium'},
'amount': ('django.db.models.fields.DecimalField', [], {'max_digits': '10', 'decimal_places': '0'}),
'campaign': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'premiums'", 'null': 'True', 'to': "orm['core.Campaign']"}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'limit': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'type': ('django.db.models.fields.CharField', [], {'max_length': '2'})
},
'core.press': {
'Meta': {'object_name': 'Press'},
'date': ('django.db.models.fields.DateField', [], {'db_index': 'True'}),
'highlight': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'language': ('django.db.models.fields.CharField', [], {'max_length': '20', 'blank': 'True'}),
'note': ('django.db.models.fields.CharField', [], {'max_length': '140', 'blank': 'True'}),
'source': ('django.db.models.fields.CharField', [], {'max_length': '140'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '140'}),
'url': ('django.db.models.fields.URLField', [], {'max_length': '200'})
},
'core.publisher': {
'Meta': {'object_name': 'Publisher'},
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'logo_url': ('django.db.models.fields.URLField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'key_publisher'", 'to': "orm['core.PublisherName']"}),
'url': ('django.db.models.fields.URLField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'})
},
'core.publishername': {
'Meta': {'object_name': 'PublisherName'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'publisher': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'alternate_names'", 'null': 'True', 'to': "orm['core.Publisher']"})
},
'core.rightsholder': {
'Meta': {'object_name': 'RightsHolder'},
'can_sell': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'email': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'rights_holder'", 'to': "orm['auth.User']"}),
'rights_holder_name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'core.subject': {
'Meta': {'ordering': "['name']", 'object_name': 'Subject'},
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_visible': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '200'}),
'works': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'subjects'", 'symmetrical': 'False', 'to': "orm['core.Work']"})
},
'core.userprofile': {
'Meta': {'object_name': 'UserProfile'},
'avatar_source': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '4', 'null': 'True'}),
'badges': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'holders'", 'symmetrical': 'False', 'to': "orm['core.Badge']"}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'facebook_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True'}),
'goodreads_auth_secret': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'goodreads_auth_token': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'goodreads_user_id': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}),
'goodreads_user_link': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
'goodreads_user_name': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
'home_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'kindle_email': ('django.db.models.fields.EmailField', [], {'max_length': '254', 'blank': 'True'}),
'librarything_id': ('django.db.models.fields.CharField', [], {'max_length': '31', 'blank': 'True'}),
'pic_url': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}),
'tagline': ('django.db.models.fields.CharField', [], {'max_length': '140', 'blank': 'True'}),
'twitter_id': ('django.db.models.fields.CharField', [], {'max_length': '15', 'blank': 'True'}),
'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'profile'", 'unique': 'True', 'to': "orm['auth.User']"})
},
'core.waswork': {
'Meta': {'object_name': 'WasWork'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'moved': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}),
'was': ('django.db.models.fields.IntegerField', [], {'unique': 'True'}),
'work': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['core.Work']"})
},
'core.wishes': {
'Meta': {'object_name': 'Wishes', 'db_table': "'core_wishlist_works'"},
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'source': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '15', 'blank': 'True'}),
'wishlist': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['core.Wishlist']"}),
'work': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'wishes'", 'to': "orm['core.Work']"})
},
'core.wishlist': {
'Meta': {'object_name': 'Wishlist'},
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'wishlist'", 'unique': 'True', 'to': "orm['auth.User']"}),
'works': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'wishlists'", 'symmetrical': 'False', 'through': "orm['core.Wishes']", 'to': "orm['core.Work']"})
},
'core.work': {
'Meta': {'ordering': "['title']", 'object_name': 'Work'},
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'}),
'earliest_publication': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True'}),
'featured': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_free': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'language': ('django.db.models.fields.CharField', [], {'default': "'en'", 'max_length': '5', 'db_index': 'True'}),
'num_wishes': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}),
'openlibrary_lookup': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
'selected_edition': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'selected_works'", 'null': 'True', 'to': "orm['core.Edition']"}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '1000'})
},
'libraryauth.library': {
'Meta': {'object_name': 'Library'},
'approved': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'backend': ('django.db.models.fields.CharField', [], {'default': "'ip'", 'max_length': '10'}),
'group': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'library'", 'unique': 'True', 'null': 'True', 'to': "orm['auth.Group']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '80'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'libraries'", 'to': "orm['auth.User']"}),
'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'library'", 'unique': 'True', 'to': "orm['auth.User']"})
}
}
complete_apps = ['core']

View File

@ -9,6 +9,7 @@ import random
import urllib
import urllib2
from urlparse import urlparse
import unicodedata
from ckeditor.fields import RichTextField
from datetime import timedelta, datetime
@ -1186,6 +1187,24 @@ class Work(models.Model):
elif self.authors().count()>2:
return "%s et al." % self.authors()[0].name
return ''
def kindle_safe_title(self):
"""
Removes accents, keeps letters and numbers, replaces non-Latin characters with "#", and replaces punctuation with "_"
"""
safe = u''
nkfd_form = unicodedata.normalize('NFKD', self.title) #unaccent accented letters
for c in nkfd_form:
ccat = unicodedata.category(c)
#print ccat
if ccat.startswith('L') or ccat.startswith('N'): # only letters and numbers
if ord(c) > 127:
safe = safe + '#' #a non latin script letter or number
else:
safe = safe + c
elif not unicodedata.combining(c): #not accents (combining forms)
safe = safe + '_' #punctuation
return safe
def last_campaign(self):
# stash away the last campaign to prevent repeated lookups
@ -1826,6 +1845,8 @@ class EbookFile(models.Model):
except:
return False
send_to_kindle_limit=7492232
class Ebook(models.Model):
FORMAT_CHOICES = settings.FORMATS
RIGHTS_CHOICES = cc.CHOICES
@ -1835,12 +1856,19 @@ class Ebook(models.Model):
provider = models.CharField(max_length=255)
download_count = models.IntegerField(default=0)
active = models.BooleanField(default=True)
filesize = models.PositiveIntegerField(null=True)
# use 'PD-US', 'CC BY', 'CC BY-NC-SA', 'CC BY-NC-ND', 'CC BY-NC', 'CC BY-ND', 'CC BY-SA', 'CC0'
rights = models.CharField(max_length=255, null=True, choices = RIGHTS_CHOICES, db_index=True)
edition = models.ForeignKey('Edition', related_name='ebooks')
user = models.ForeignKey(User, null=True)
def kindle_sendable(self):
if not self.filesize or self.filesize < send_to_kindle_limit:
return True
else:
return False
def set_provider(self):
self.provider=Ebook.infer_provider(self.url)
return self.provider

View File

@ -15,7 +15,18 @@ Redirect permanent / https://just.unglue.it/
SSLEngine on
ServerName just.unglue.it:443
SSLProtocol All -SSLv2 -SSLv3
# generated using https://mozilla.github.io/server-side-tls/ssl-config-generator/
# intermediate mode
# 2015.03.04 (with Apache v 2.2.22 and OpenSSL 1.0.1 and HSTS enabled)
SSLProtocol all -SSLv2 -SSLv3
SSLCipherSuite ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA
SSLHonorCipherOrder on
# HSTS (mod_headers is required) (15768000 seconds = 6 months)
Header always add Strict-Transport-Security "max-age=15768000"
SSLCertificateFile /etc/ssl/certs/server.crt
SSLCertificateKeyFile /etc/ssl/private/server.key
SSLCertificateChainFile /etc/ssl/certs/STAR_unglue_it.ca-bundle

View File

@ -14,7 +14,15 @@ Redirect permanent / https://unglue.it/
ServerName unglue.it:443
SSLEngine on
SSLProtocol All -SSLv2 -SSLv3
# generated using https://mozilla.github.io/server-side-tls/ssl-config-generator/
# intermediate mode
# 2015.03.04 (with Apache v 2.2.22 and OpenSSL 1.0.1 and HSTS enabled)
SSLProtocol all -SSLv2 -SSLv3
SSLCipherSuite ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA
SSLHonorCipherOrder on
SSLCertificateFile /etc/ssl/certs/server.crt
SSLCertificateKeyFile /etc/ssl/private/server.key
SSLCertificateChainFile /etc/ssl/certs/STAR_unglue_it.ca-bundle

View File

@ -214,7 +214,7 @@ class EbookFileForm(forms.ModelForm):
class EbookForm(forms.ModelForm):
class Meta:
model = Ebook
exclude =( 'created', 'download_count', 'active')
exclude =( 'created', 'download_count', 'active', 'filesize')
widgets = {
'edition': forms.HiddenInput,
'user': forms.HiddenInput,

View File

@ -2,10 +2,10 @@
{% if edition.ebook_form %}
{% if show_ebook_form %}
<div id="add_ebook">
{% if alert %}<div class="yikes">{{alert}}</div>{% endif %}
{% if edition.ebooks.all.0 %}
<h2>eBooks for this Edition</h2>
{% if alert %}<div class="yikes">{{alert}}</div>{% endif %}
{% for ebook in edition.ebooks.all %}
<a href="{% url download_ebook ebook.id %}">{{ ebook.format }}</a> {{ebook.rights}} at {{ebook.provider}}. Downloaded {{ ebook.download_count }} times.<br />
{% endfor %}

View File

@ -179,7 +179,7 @@ function put_un_in_cookie2(){
<div class="jsmodule">
<h3 class="module-title">News</h3>
<div class="jsmod-content">
Blog: <a href="http://blog.unglue.it/2014/12/23/new-at-unglue-it-buy-as-a-gift/">New at Unglue.it: Buy as a Gift</a>
Blog: <a href="http://blog.unglue.it/2015/02/27/unglue-it-joins-gitenberg/">Unglue.it joins GITenberg</a>
</div>
</div>
<div class="jsmodule">

View File

@ -23,7 +23,9 @@
{% block ce_content %}
<h2>Kindle email change successful</h2>
<div id="content-main">
<p>Hooray! We can now send most unglued ebooks to you at {{ request.user.profile.kindle_email }}. Some ebooks are too big for us to send, though.</p>
<p>{% if ok_email %}Hooray! We can now send most unglued ebooks to you at {{ request.user.profile.kindle_email }}. Some ebooks are too big for us to send, though.
{% else %}<span class="yikes">{{ request.user.profile.kindle_email }} is probably not the right email for your Kindle; most Kindles use an @kindle.com email address. You can <a href="{% url kindle_config %}">change it</a>, but we'll try sending it anyway.</span> {% endif %}</p>
{% if work %}
<p>
We're now emailing you the ebook you wanted, <i><a href="{% url work work.id %}">{{ work.title }}</a></i>...

View File

@ -33,7 +33,8 @@
(If you'd like to change your Kindle email, you can do so below. You'll need to download the book again.)
</p>
{% else %}
<p>You already have a Kindle email on file with Unglue.it: {{ kindle_email }} .</p>
<p>You already have a Kindle email on file with Unglue.it: {{ kindle_email }} .
{% if not ok_email %}<span class="yikes">That's probably not the right email; most Kindles use an @kindle.com email address.</span> {% endif %}</p>
<p>You can change it below.</p>
<p>
If you emailed yourself an Unglue.it ebook and got a message from Amazon that the sender is not in your approved email list, add <b>notices@gluejar.com</b> to your <a href="http://www.amazon.com/myk#pdocSettings">Approved Personal Document Email List</a> under Personal Document Settings.
@ -43,7 +44,7 @@
<p>
Before your device or app can receive emails from Unglue.it, you'll have to add <b>notices@gluejar.com</b> to your <a href="http://www.amazon.com/myk#pdocSettings">Approved Personal Document Email List</a> under Personal Document Settings.
</p>
<p>Then, enter your Kindle email address below:</p>
<p>Then, enter your Kindle email address below (most Kindles use an @kindle.com email address.):</p>
{% endif %}
{% if work %}
<form method="post" action="{% url kindle_config_download work.id %}">{% csrf_token %}

View File

@ -2861,12 +2861,12 @@ class DownloadView(PurchaseView):
#send to kindle
try:
non_google_ebooks.filter(format='mobi')[0]
can_kindle = True
kindle_ebook = non_google_ebooks.filter(format='mobi')[0]
can_kindle = kindle_ebook.kindle_sendable()
except IndexError:
try:
non_google_ebooks.filter(format='pdf')[0]
can_kindle = True
kindle_ebook = non_google_ebooks.filter(format='pdf')[0]
can_kindle = kindle_ebook.kindle_sendable()
except IndexError:
can_kindle = False
# configure the xfer url
@ -2894,7 +2894,7 @@ class DownloadView(PurchaseView):
'iphone': 'iPhone' in agent,
'android': android,
'desktop': desktop,
'mac_ibooks': 'Mac OS X 10.9' in agent or 'Mac OS X 10_9' in agent,
'mac_ibooks': 'Mac OS X 10.9' in agent or 'Mac OS X 10_9' in agent or 'Mac OS X 10.10' in agent or 'Mac OS X 10_10' in agent,
'acq':acq,
'show_beg': self.show_beg,
'preapproval_amount': self.get_preapproval_amount(),
@ -3144,7 +3144,11 @@ def kindle_config(request, work_id=None):
template = "kindle_change_successful.html"
else:
form = KindleEmailForm()
return render(request, template, {'form': form, 'work': work})
return render(request, template, {
'form': form,
'work': work,
'ok_email': request.user.profile.kindle_email and ('kindle' in request.user.profile.kindle_email),
})
@require_POST
@csrf_exempt
@ -3179,7 +3183,10 @@ def send_to_kindle(request, work_id, javascript='0'):
if acq:
ebook_url = acq.get_mobi_url()
ebook_format = 'mobi'
title = acq.work.title
filesize = None
title = acq.work.kindle_safe_title()
ebook=None
else:
non_google_ebooks = work.ebooks().exclude(provider='Google Books')
try:
@ -3194,9 +3201,9 @@ def send_to_kindle(request, work_id, javascript='0'):
ebook.increment()
ebook_url = ebook.url
ebook_format = ebook.format
filesize = ebook.filesize
logger.info('ebook: {0}, user_ip: {1}'.format(work_id, request.META['REMOTE_ADDR']))
title = ebook.edition.title
title = title.replace(' ', '_')
title = ebook.edition.work.kindle_safe_title()
context['ebook_url']=ebook_url
context['ebook_format']=ebook_format
@ -3213,7 +3220,6 @@ def send_to_kindle(request, work_id, javascript='0'):
"""
TO FIX rigorously:
Amazon SES has a 10 MB size limit (http://aws.amazon.com/ses/faqs/#49) in messages sent
to determine whether the file will meet this limit, we probably need to compare the
size of the mime-encoded file to 10 MB. (and it's unclear exactly what the Amazon FAQ means precisely by
@ -3226,8 +3232,13 @@ def send_to_kindle(request, work_id, javascript='0'):
This won't perfectly measure size of email, but should be safe, and is much faster than doing the check after download.
"""
filehandle = urllib.urlopen(ebook_url)
filesize = int(filehandle.info().getheaders("Content-Length")[0])
if filesize > 7492232:
if not filesize:
filesize = int(filehandle.info().getheaders("Content-Length")[0])
if ebook:
ebook.filesize = filesize if filesize < 2147483647 else 2147483647 # largest safe positive integer
ebook.save()
if filesize > models.send_to_kindle_limit:
logger.info('ebook %s is too large to be emailed' % work.id)
return local_response(request, javascript, context, 0)