commit
eacb801af9
6
admin.py
6
admin.py
|
@ -41,6 +41,8 @@ from regluit.core.lookups import (
|
|||
OwnerLookup,
|
||||
EditionLookup
|
||||
)
|
||||
from regluit.libraryauth.models import Library, Block, CardPattern, EmailPattern
|
||||
from regluit.libraryauth.admin import LibraryAdmin, BlockAdmin, CardPatternAdmin, EmailPatternAdmin
|
||||
|
||||
class RegluitAdmin(AdminSite):
|
||||
login_template = 'registration/login.html'
|
||||
|
@ -211,6 +213,10 @@ class MARCRecordAdmin(ModelAdmin):
|
|||
admin_site = RegluitAdmin("Admin")
|
||||
|
||||
admin_site.register(User, UserAdmin)
|
||||
admin_site.register(Library, LibraryAdmin)
|
||||
admin_site.register(Block, BlockAdmin)
|
||||
admin_site.register(CardPattern, CardPatternAdmin)
|
||||
admin_site.register(EmailPattern, EmailPatternAdmin)
|
||||
admin_site.register(models.Work, WorkAdmin)
|
||||
admin_site.register(models.Claim, ClaimAdmin)
|
||||
admin_site.register(models.RightsHolder, RightsHolderAdmin)
|
||||
|
|
|
@ -0,0 +1,328 @@
|
|||
# -*- 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 'Acq.refreshes'
|
||||
db.add_column('core_acq', 'refreshes',
|
||||
self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime(2013, 10, 17, 0, 0), auto_now_add=True, blank=True),
|
||||
keep_default=False)
|
||||
|
||||
# Adding field 'Acq.lib_acq'
|
||||
db.add_column('core_acq', 'lib_acq',
|
||||
self.gf('django.db.models.fields.related.ForeignKey')(related_name='loans', null=True, to=orm['core.Acq']),
|
||||
keep_default=False)
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Deleting field 'Acq.refreshes'
|
||||
db.delete_column('core_acq', 'refreshes')
|
||||
|
||||
# Deleting field 'Acq.lib_acq'
|
||||
db.delete_column('core_acq', 'lib_acq_id')
|
||||
|
||||
|
||||
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', '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'}),
|
||||
'refreshes': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2013, 10, 17, 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'}),
|
||||
'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', [], {'db_index': 'True'}),
|
||||
'description': ('ckeditor.fields.RichTextField', [], {'null': 'True'}),
|
||||
'details': ('ckeditor.fields.RichTextField', [], {'null': 'True', 'blank': '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'}),
|
||||
'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'}),
|
||||
'target': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '14', 'decimal_places': '2'}),
|
||||
'type': ('django.db.models.fields.PositiveSmallIntegerField', [], {'default': '1'}),
|
||||
'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(2013, 10, 17, 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': "'pending'", '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'},
|
||||
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': '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']"}),
|
||||
'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'},
|
||||
'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'}),
|
||||
'public_domain': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
|
||||
'publication_date': ('django.db.models.fields.CharField', [], {'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.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': '31'}),
|
||||
'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.marcrecord': {
|
||||
'Meta': {'object_name': 'MARCRecord'},
|
||||
'edition': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'MARCrecords'", 'null': 'True', 'to': "orm['core.Edition']"}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'link_target': ('django.db.models.fields.CharField', [], {'default': "'DIRECT'", 'max_length': '6'})
|
||||
},
|
||||
'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', [], {}),
|
||||
'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'}),
|
||||
'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': '1', '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', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'source': ('django.db.models.fields.CharField', [], {'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', 'blank': 'True'}),
|
||||
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'null': 'True', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'language': ('django.db.models.fields.CharField', [], {'default': "'en'", 'max_length': '2'}),
|
||||
'num_wishes': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}),
|
||||
'openlibrary_lookup': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
|
||||
'title': ('django.db.models.fields.CharField', [], {'max_length': '1000'})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['core']
|
176
core/models.py
176
core/models.py
|
@ -56,13 +56,16 @@ from regluit.core.parameters import (
|
|||
INDIVIDUAL,
|
||||
LIBRARY,
|
||||
BORROWED,
|
||||
TESTING
|
||||
TESTING,
|
||||
RESERVE,
|
||||
)
|
||||
|
||||
|
||||
from regluit.booxtream import BooXtream
|
||||
watermarker = BooXtream()
|
||||
|
||||
from regluit.libraryauth.models import Library
|
||||
|
||||
pm = PostMonkey(settings.MAILCHIMP_API_KEY)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
@ -256,9 +259,10 @@ class Acq(models.Model):
|
|||
"""
|
||||
Short for Acquisition, this is a made-up word to describe the thing you acquire when you buy or borrow an ebook
|
||||
"""
|
||||
CHOICES = ((INDIVIDUAL,'Individual license'),(LIBRARY,'Library License'),(BORROWED,'Borrowed from Library'), (TESTING,'Just for Testing'))
|
||||
CHOICES = ((INDIVIDUAL,'Individual license'),(LIBRARY,'Library License'),(BORROWED,'Borrowed from Library'), (TESTING,'Just for Testing'), (RESERVE,'On Reserve'),)
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
expires = models.DateTimeField(null=True)
|
||||
refreshes = models.DateTimeField(auto_now_add=True, default=now())
|
||||
work = models.ForeignKey("Work", related_name='acqs', null=False)
|
||||
user = models.ForeignKey(User, related_name='acqs')
|
||||
license = models.PositiveSmallIntegerField(null = False, default = INDIVIDUAL,
|
||||
|
@ -266,6 +270,9 @@ class Acq(models.Model):
|
|||
watermarked = models.ForeignKey("booxtream.Boox", null=True)
|
||||
nonce = models.CharField(max_length=32, null=True)
|
||||
|
||||
# when the acq is a loan, this points at the library's acq it's derived from
|
||||
lib_acq = models.ForeignKey("self", related_name="loans", null=True)
|
||||
|
||||
@property
|
||||
def expired(self):
|
||||
if self.expires is None:
|
||||
|
@ -274,13 +281,19 @@ class Acq(models.Model):
|
|||
return self.expires < datetime.now()
|
||||
|
||||
def get_mobi_url(self):
|
||||
if self.expired:
|
||||
return ''
|
||||
return self.get_watermarked().download_link_mobi
|
||||
|
||||
def get_epub_url(self):
|
||||
if self.expired:
|
||||
return ''
|
||||
return self.get_watermarked().download_link_epub
|
||||
|
||||
def get_watermarked(self):
|
||||
if self.watermarked == None or self.watermarked.expired:
|
||||
if self.on_reserve:
|
||||
self.borrow(self.user)
|
||||
params={
|
||||
'customeremailaddress': self.user.email,
|
||||
'customername': self.user.username,
|
||||
|
@ -302,13 +315,55 @@ class Acq(models.Model):
|
|||
|
||||
def _hash(self):
|
||||
return hashlib.md5('1c1a56974ef08edc%s:%s:%s'%(self.user.id,self.work.id,self.created)).hexdigest()
|
||||
|
||||
def expire_in(self, delta):
|
||||
self.expires = now()+delta
|
||||
self.save()
|
||||
if self.lib_acq:
|
||||
self.lib_acq.refreshes = now()+ (timedelta(days=14))
|
||||
self.lib_acq.save()
|
||||
|
||||
@property
|
||||
def on_reserve(self):
|
||||
return self.license==RESERVE
|
||||
|
||||
def borrow(self, user=None):
|
||||
if self.on_reserve:
|
||||
self.license=BORROWED
|
||||
self.expire_in(timedelta(days=14))
|
||||
self.user.wishlist.add_work( self.work, "borrow")
|
||||
return self
|
||||
elif self.borrowable and user:
|
||||
user.wishlist.add_work( self.work, "borrow")
|
||||
borrowed = Acq.objects.create(user=user,work=self.work,license= BORROWED, lib_acq=self)
|
||||
from regluit.core.tasks import watermark_acq
|
||||
watermark_acq.delay(borrowed)
|
||||
return borrowed
|
||||
|
||||
@property
|
||||
def borrowable(self):
|
||||
if self.license == RESERVE and not self.expired:
|
||||
return True
|
||||
if self.license == LIBRARY:
|
||||
return self.refreshes < datetime.now()
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
|
||||
def add_acq_nonce(sender, instance, created, **kwargs):
|
||||
if created:
|
||||
instance.nonce=instance._hash()
|
||||
instance.save()
|
||||
def set_expiration(sender, instance, created, **kwargs):
|
||||
if created:
|
||||
if instance.license == RESERVE:
|
||||
instance.expire_in(timedelta(hours=2))
|
||||
if instance.license == BORROWED:
|
||||
instance.expire_in(timedelta(days=14))
|
||||
|
||||
post_save.connect(add_acq_nonce,sender=Acq)
|
||||
post_save.connect(set_expiration,sender=Acq)
|
||||
|
||||
class Campaign(models.Model):
|
||||
LICENSE_CHOICES = settings.CCCHOICES
|
||||
|
@ -672,15 +727,26 @@ class Campaign(models.Model):
|
|||
return Premium.objects.none()
|
||||
return Premium.objects.filter(campaign=self).filter(type='CU').order_by('amount')
|
||||
|
||||
def active_offers(self):
|
||||
@property
|
||||
def library_offer(self):
|
||||
return self._offer(LIBRARY)
|
||||
|
||||
@property
|
||||
def individual_offer(self):
|
||||
return self._offer(INDIVIDUAL)
|
||||
|
||||
def _offer(self, license):
|
||||
if self.type is REWARDS:
|
||||
return Offer.objects.none()
|
||||
return Offer.objects.filter(work=self.work,active=True).order_by('price')
|
||||
return None
|
||||
try:
|
||||
return Offer.objects.get(work=self.work, active=True, license=license)
|
||||
except Offer.DoesNotExist:
|
||||
return None
|
||||
|
||||
@property
|
||||
def days_per_copy(self):
|
||||
if self.active_offers().count()>0:
|
||||
return Decimal(float(self.active_offers()[0].price) / self.dollar_per_day )
|
||||
if self.individual_offer:
|
||||
return Decimal(float(self.individual_offer.price) / self.dollar_per_day )
|
||||
else:
|
||||
return Decimal(0)
|
||||
|
||||
|
@ -1045,21 +1111,82 @@ class Work(models.Model):
|
|||
def create_offers(self):
|
||||
for choice in Offer.CHOICES:
|
||||
if not self.offers.filter(license=choice[0]):
|
||||
self.offers.create(license=choice[0])
|
||||
self.offers.create(license=choice[0],active=True,price=Decimal(10))
|
||||
return self.offers.all()
|
||||
|
||||
def purchased_by(self,user):
|
||||
if user==None or not user.is_authenticated():
|
||||
|
||||
def borrowable(self, user):
|
||||
if user.is_anonymous():
|
||||
return False
|
||||
acqs= self.acqs.filter(user=user)
|
||||
if acqs.count()==0:
|
||||
return False
|
||||
for acq in acqs:
|
||||
if acq.expires is None:
|
||||
return True
|
||||
if acq.expires > now():
|
||||
return True
|
||||
lib_user=(lib.user for lib in user.profile.libraries)
|
||||
lib_license=self.get_user_license(lib_user)
|
||||
if lib_license and lib_license.borrowable:
|
||||
return True
|
||||
return False
|
||||
|
||||
def in_library(self,user):
|
||||
if user.is_anonymous():
|
||||
return False
|
||||
lib_user=(lib.user for lib in user.profile.libraries)
|
||||
lib_license=self.get_user_license(lib_user)
|
||||
if lib_license and lib_license.acqs.count():
|
||||
return True
|
||||
return False
|
||||
|
||||
@property
|
||||
def lib_acqs(self):
|
||||
return self.acqs.filter(license=LIBRARY)
|
||||
|
||||
class user_license:
|
||||
acqs=Acq.objects.none()
|
||||
def __init__(self,acqs):
|
||||
self.acqs=acqs
|
||||
|
||||
@property
|
||||
def is_active(self):
|
||||
return self.acqs.filter(expires__isnull = True).count()>0 or self.acqs.filter(expires__gt= now()).count()>0
|
||||
|
||||
@property
|
||||
def borrowed(self):
|
||||
loans = self.acqs.filter(license=BORROWED,expires__gt= now())
|
||||
if loans.count()==0:
|
||||
return None
|
||||
else:
|
||||
return loans[0]
|
||||
|
||||
@property
|
||||
def purchased(self):
|
||||
purchases = self.acqs.filter(license=INDIVIDUAL)
|
||||
if purchases.count()==0:
|
||||
return None
|
||||
else:
|
||||
return purchases[0]
|
||||
|
||||
@property
|
||||
def lib_acqs(self):
|
||||
return self.acqs.filter(license=LIBRARY)
|
||||
|
||||
@property
|
||||
def next_acq(self):
|
||||
loans = self.acqs.filter(license=LIBRARY, refreshes__gt=now()).order_by('refreshes')
|
||||
if loans.count()==0:
|
||||
return None
|
||||
else:
|
||||
return loans[0]
|
||||
|
||||
@property
|
||||
def borrowable(self):
|
||||
return self.acqs.filter(license=LIBRARY, refreshes__lt=now()).count()>0
|
||||
|
||||
def get_user_license(self, user):
|
||||
if user==None:
|
||||
return None
|
||||
if isinstance(user, User):
|
||||
if user.is_anonymous():
|
||||
return None
|
||||
return self.user_license(self.acqs.filter(user=user))
|
||||
else:
|
||||
# assume it's several users
|
||||
return self.user_license(self.acqs.filter(user__in=user))
|
||||
|
||||
class Author(models.Model):
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
|
@ -1501,6 +1628,17 @@ class UserProfile(models.Model):
|
|||
for social in socials:
|
||||
auths[social.provider]=True
|
||||
return auths
|
||||
|
||||
@property
|
||||
def libraries(self):
|
||||
libs=[]
|
||||
for group in self.user.groups.all():
|
||||
try:
|
||||
libs.append(group.library)
|
||||
except Library.DoesNotExist:
|
||||
pass
|
||||
return libs
|
||||
|
||||
|
||||
class Press(models.Model):
|
||||
url = models.URLField()
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
(REWARDS, BUY2UNGLUE) = (1, 2)
|
||||
(INDIVIDUAL, LIBRARY, BORROWED) = (1, 2, 3)
|
||||
(INDIVIDUAL, LIBRARY, BORROWED, RESERVE) = (1, 2, 3, 4)
|
||||
TESTING = 0
|
||||
|
|
|
@ -31,7 +31,8 @@ regluit imports
|
|||
"""
|
||||
from regluit.payment.signals import transaction_charged, transaction_failed, pledge_modified, pledge_created
|
||||
from regluit.utils.localdatetime import now
|
||||
from regluit.core.parameters import REWARDS, BUY2UNGLUE
|
||||
from regluit.core.parameters import REWARDS, BUY2UNGLUE, LIBRARY, RESERVE
|
||||
from regluit.libraryauth.models import Library
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -184,7 +185,12 @@ def handle_transaction_charged(sender,transaction=None, **kwargs):
|
|||
else:
|
||||
# provision the book
|
||||
Acq = get_model('core', 'Acq')
|
||||
new_acq = Acq.objects.create(user=transaction.user,work=transaction.campaign.work,license= transaction.offer.license)
|
||||
if transaction.offer.license == LIBRARY:
|
||||
library = Library.objects.get(id=transaction.extra['library_id'])
|
||||
new_acq = Acq.objects.create(user=library.user,work=transaction.campaign.work,license= LIBRARY)
|
||||
reserve_acq = Acq.objects.create(user=transaction.user,work=transaction.campaign.work,license= RESERVE, lib_acq = new_acq)
|
||||
else:
|
||||
new_acq = Acq.objects.create(user=transaction.user,work=transaction.campaign.work,license= transaction.offer.license)
|
||||
transaction.campaign.update_left()
|
||||
notification.send([transaction.user], "purchase_complete", {'transaction':transaction}, True)
|
||||
from regluit.core.tasks import watermark_acq
|
||||
|
|
|
@ -57,8 +57,8 @@ from regluit.core.models import (
|
|||
EbookFile,
|
||||
Acq,
|
||||
)
|
||||
|
||||
from regluit.core.parameters import TESTING
|
||||
from regluit.libraryauth.models import Library
|
||||
from regluit.core.parameters import TESTING, LIBRARY, RESERVE
|
||||
from regluit.frontend.views import safe_get_work
|
||||
from regluit.payment.models import Transaction
|
||||
from regluit.payment.parameters import PAYMENT_TYPE_AUTHORIZATION
|
||||
|
@ -864,5 +864,27 @@ class EbookFileTests(TestCase):
|
|||
self.assertRegexpMatches(url,'download.booxtream.com/')
|
||||
print url
|
||||
|
||||
from .signals import handle_transaction_charged
|
||||
class LibTests(TestCase):
|
||||
|
||||
class transaction:
|
||||
pass
|
||||
|
||||
def test_purchase(self):
|
||||
w = Work.objects.create(title="Work 1")
|
||||
e = Edition.objects.create(title=w.title,work=w)
|
||||
u = User.objects.create_user('test', 'test@example.org', 'testpass')
|
||||
lu = User.objects.create_user('library', 'testu@example.org', 'testpass')
|
||||
lib = Library.objects.create(user=lu)
|
||||
c = Campaign.objects.create(work=w, type = parameters.BUY2UNGLUE, cc_date_initial= datetime(2020,1,1),target=1000, deadline=datetime(2020,1,1))
|
||||
|
||||
new_acq = Acq.objects.create(user=lib.user,work=c.work,license= LIBRARY)
|
||||
self.assertTrue(new_acq.borrowable)
|
||||
reserve_acq = Acq.objects.create(user=u,work=c.work,license= RESERVE, lib_acq = new_acq)
|
||||
self.assertTrue(reserve_acq.borrowable)
|
||||
self.assertFalse(new_acq.borrowable)
|
||||
|
||||
self.assertTrue(reserve_acq.expires< now()+timedelta(hours=3))
|
||||
reserve_acq.borrow()
|
||||
self.assertTrue(reserve_acq.expires> now()+timedelta(hours=3))
|
||||
|
||||
|
|
|
@ -41,3 +41,11 @@ def campaign_list_users(campaign_list, how_many):
|
|||
else :
|
||||
user_list = users[0: how_many]
|
||||
return user_list
|
||||
|
||||
def library_users(library, how_many):
|
||||
count= library.group.user_set.all().count()
|
||||
if count <= how_many :
|
||||
user_list = library.group.user_set.all().order_by('-last_login')[0: count]
|
||||
else :
|
||||
user_list = library.group.user_set.all().order_by('-last_login')[0: how_many]
|
||||
return user_list
|
||||
|
|
|
@ -13,7 +13,6 @@ from django import forms
|
|||
from django.conf import settings
|
||||
from django.conf.global_settings import LANGUAGES
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.auth.forms import AuthenticationForm
|
||||
from django.core.validators import validate_email
|
||||
from django.db import models
|
||||
from django.forms.widgets import RadioSelect
|
||||
|
@ -48,6 +47,8 @@ from regluit.core.models import (
|
|||
FACEBOOK,
|
||||
GRAVATAR
|
||||
)
|
||||
from regluit.libraryauth.models import Library
|
||||
from regluit.core.parameters import LIBRARY
|
||||
from regluit.core.lookups import (
|
||||
OwnerLookup,
|
||||
WorkLookup,
|
||||
|
@ -365,6 +366,8 @@ class OfferForm(forms.ModelForm):
|
|||
model = Offer
|
||||
widgets = {
|
||||
'work': forms.HiddenInput,
|
||||
'license': forms.HiddenInput,
|
||||
'active': forms.HiddenInput,
|
||||
}
|
||||
|
||||
date_selector=range(date.today().year, settings.MAX_CC_DATE.year+1)
|
||||
|
@ -488,6 +491,9 @@ class CampaignPurchaseForm(forms.Form):
|
|||
anonymous = forms.BooleanField(required=False, label=_("Make this purchase anonymous, please"))
|
||||
offer_id = forms.IntegerField(required=False)
|
||||
offer=None
|
||||
library_id = forms.IntegerField(required=False)
|
||||
library = None
|
||||
|
||||
def clean_offer_id(self):
|
||||
offer_id = self.cleaned_data['offer_id']
|
||||
try:
|
||||
|
@ -495,13 +501,30 @@ class CampaignPurchaseForm(forms.Form):
|
|||
except Offer.DoesNotExist:
|
||||
raise forms.ValidationError(_("Sorry, that offer is not valid."))
|
||||
|
||||
def clean_library_id(self):
|
||||
library_id = self.cleaned_data['library_id']
|
||||
if library_id:
|
||||
try:
|
||||
self.library = Library.objects.get(id=library_id)
|
||||
except Library.DoesNotExist:
|
||||
raise forms.ValidationError(_("Sorry, that Library is not valid."))
|
||||
|
||||
def clean(self):
|
||||
if self.offer.license == LIBRARY:
|
||||
if not self.library:
|
||||
raise forms.ValidationError(_("No library specified." ))
|
||||
return self.cleaned_data
|
||||
|
||||
def amount(self):
|
||||
return self.offer.price if self.offer else None
|
||||
|
||||
@property
|
||||
def trans_extra(self):
|
||||
return PledgeExtra( anonymous=self.cleaned_data['anonymous'],
|
||||
pe = PledgeExtra( anonymous=self.cleaned_data['anonymous'],
|
||||
offer = self.offer )
|
||||
if self.library:
|
||||
pe.extra['library_id']=self.library.id
|
||||
return pe
|
||||
|
||||
class CampaignPledgeForm(forms.Form):
|
||||
preapproval_amount = forms.DecimalField(
|
||||
|
@ -622,14 +645,6 @@ class FeedbackForm(forms.Form):
|
|||
|
||||
return cleaned_data
|
||||
|
||||
class AuthForm(AuthenticationForm):
|
||||
def __init__(self, request=None, *args, **kwargs):
|
||||
if request and request.method == 'GET':
|
||||
saved_un= request.COOKIES.get('un', None)
|
||||
super(AuthForm, self).__init__(initial={"username":saved_un},*args, **kwargs)
|
||||
else:
|
||||
super(AuthForm, self).__init__(*args, **kwargs)
|
||||
|
||||
class MsgForm(forms.Form):
|
||||
msg = forms.CharField(widget=forms.Textarea(), error_messages={'required': 'Please specify a message.'})
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
{% load humanize %}
|
||||
{% load purchased %}
|
||||
{% load lib_acqs %}
|
||||
{% with work.first_ebook as first_ebook %}
|
||||
{% with work.last_campaign.supporters as supporters %}
|
||||
{% with work.cover_image_thumbnail as thumbnail %}
|
||||
|
@ -10,7 +11,7 @@
|
|||
{% with work.last_campaign.deadline as deadline %}
|
||||
{% with work.id as workid %}
|
||||
{% with request.user.wishlist.works.all as wishlist %}
|
||||
{% purchased %}
|
||||
{% purchased %}{% lib_acqs %}
|
||||
<div class="thewholebook listview tabs {% if first_ebook or status == 'SUCCESSFUL' %}tabs-1{% else %}{% if status == 'ACTIVE' %}tabs-2{% else %}tabs-3{% endif %}{% endif %}">
|
||||
<div class="listview book-list">
|
||||
<div class="listview panelback side2">
|
||||
|
@ -18,7 +19,7 @@
|
|||
<div class="greenpanel2">
|
||||
{% if last_campaign %}
|
||||
{% comment %}top section: campaign info + optional action button. Varies by campaign status.{% endcomment %}
|
||||
{% if status == 'SUCCESSFUL' %}
|
||||
{% if status == 'SUCCESSFUL' or license_is_active or borrowable %}
|
||||
<div class="greenpanel_top">
|
||||
{% comment %}bibliographic data{% endcomment %}
|
||||
<div class="white_text">
|
||||
|
@ -29,21 +30,41 @@
|
|||
<div class="moreinfo">
|
||||
<a href="{% if workid %}{% url work workid %}{% else %}{% url googlebooks googlebooks_id %}{% endif %}" target="_top">More Info</a>
|
||||
</div>
|
||||
{% if purchased %}
|
||||
<div class="unglued_white">
|
||||
<b>Purchased!</b>
|
||||
</div>
|
||||
{% else %}{% if borrowed %}
|
||||
<b>Borrowed! </b>
|
||||
<p><b>until</b> {{ borrowed.expires|date:"M d, Y" }}</p>
|
||||
{% else %}{% if borrowable %}
|
||||
<div class="unglued_white">
|
||||
<b>Library has it!</b>
|
||||
<p><b>{{ lib_acqs.count }}</b>{% ifequal lib_acqs.count 1 %} copy{% else %} copies{% endifequal %}</p>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="unglued_white">
|
||||
<b>UNGLUED!</b>
|
||||
<p><b>On:</b> {{ deadline|date:"M d, Y" }}</p>
|
||||
<p><b>Raised:</b> {{ work.last_campaign.current_total|floatformat:0|intcomma }}</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}{% endif %}{% endif %}
|
||||
</div>
|
||||
<div class="add_button">
|
||||
{% include "book_panel_addbutton.html" %}
|
||||
</div>
|
||||
<div class="white_text bottom_button" >
|
||||
{% if first_ebook %}
|
||||
{% if purchased %}
|
||||
<a href="{% url download workid %}" class="hijax"><span class="read_itbutton button_text"><span>Read it Now</span></span></a>
|
||||
{% else %}{% if borrowed %}
|
||||
<a href="{% url download workid %}" class="hijax"><span class="read_itbutton button_text"><span>Read it Now</span></span></a>
|
||||
{% else %}{% if borrowable %}
|
||||
<a href="{% url borrow workid %}?library={{library}}" class="hijax"><span class="read_itbutton button_text"><span>Borrow It</span></span></a>
|
||||
{% else %}{% if first_ebook %}
|
||||
<a href="{% url download workid %}" class="hijax"><span class="read_itbutton button_text"><span>Read it Now</span></span></a>
|
||||
{% else %}
|
||||
<a href="{% url work workid %}"><span class="read_itbutton button_text"><span>Coming Soon</span></span></a>
|
||||
{% endif %}
|
||||
{% endif %}{% endif %}{% endif %}{% endif %}
|
||||
</div>
|
||||
{% else %}{% if status == 'ACTIVE' %}
|
||||
<div class="greenpanel_top">
|
||||
|
@ -58,16 +79,24 @@
|
|||
</div>
|
||||
|
||||
<div class="unglued_white">
|
||||
<b>UNGLUE IT!</b>
|
||||
{% ifequal work.last_campaign.type 1 %}
|
||||
<p><b>${{ work.last_campaign.current_total|floatformat:0|intcomma }}</b> raised</p>
|
||||
<p><b>${{ work.last_campaign.target|floatformat:0|intcomma }}</b> needed</p>
|
||||
<p>by {{ deadline|naturalday:"M d, Y" }}</p>
|
||||
{% if in_library %}
|
||||
{% if borrowable %}
|
||||
<p>Available in your library now!</p>
|
||||
{% else %}
|
||||
<p>Available in your library on<br />{{ next_acq.refreshes|date:"M j, Y" }}</p>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<p><b>${{ work.last_campaign.left|floatformat:0|intcomma }}</b> needed</p>
|
||||
<p>now ungluing on </p>
|
||||
<p>{{ work.last_campaign.cc_date|naturalday:"M d, Y" }}</p>
|
||||
{% endifequal %}
|
||||
<b>UNGLUE IT!</b>
|
||||
{% ifequal work.last_campaign.type 1 %}
|
||||
<p><b>${{ work.last_campaign.current_total|floatformat:0|intcomma }}</b> raised</p>
|
||||
<p><b>${{ work.last_campaign.target|floatformat:0|intcomma }}</b> needed</p>
|
||||
<p>by {{ deadline|naturalday:"M d, Y" }}</p>
|
||||
{% else %}
|
||||
<p><b>${{ work.last_campaign.left|floatformat:0|intcomma }}</b> needed</p>
|
||||
<p>will unglue on </p>
|
||||
<p>{{ work.last_campaign.cc_date|naturalday:"M d, Y" }}</p>
|
||||
{% endifequal %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% if request.user.id in supporters %}
|
||||
|
@ -80,10 +109,10 @@
|
|||
</div>
|
||||
<div class="white_text bottom_button" >
|
||||
{% ifequal 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>Pledge</span></span></a>
|
||||
{% else %}
|
||||
{% if purchased %}
|
||||
<a href="{% url download workid %}"><span class="read_itbutton pledge button_text"><span>Download</span></span></a>
|
||||
{% if in_library %}
|
||||
<a href="{% url purchase work_id=workid %}"><span class="read_itbutton pledge button_text"><span>Reserve It</span></span></a>
|
||||
{% else %}
|
||||
<a href="{% url purchase work_id=workid %}"><span class="read_itbutton pledge button_text"><span>Purchase</span></span></a>
|
||||
{% endif %}
|
||||
|
@ -166,50 +195,37 @@
|
|||
</div>
|
||||
|
||||
{% comment %}same logic as above{% endcomment %}
|
||||
{% if request.user.is_anonymous %}
|
||||
{% if 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>
|
||||
</div>
|
||||
{% else %}{% if show_purchase %}
|
||||
<div class="listview panelfront side1 add-wishlist">
|
||||
<span class="booklist_pledge"><a href="{% url purchase work_id=workid %}" class="fakeinput">Purchase</a></span>
|
||||
</div>
|
||||
{% else %}{% if request.user.is_anonymous %}
|
||||
<div class="listview panelfront side1 create-account">
|
||||
<span title="{% if workid %}{% url work workid %}{% else %}{% url googlebooks googlebooks_id %}{% endif %}">Login to Add</span>
|
||||
</div>
|
||||
{% else %}{% if request.user.id in supporters %}
|
||||
<div class="listview panelfront side1 on-wishlist">
|
||||
<span>Pledged!</span>
|
||||
</div>
|
||||
{% else %}{% ifequal supporter request.user %}
|
||||
{% comment %} used only on your own supporter page. {% endcomment %}
|
||||
{% ifequal status "ACTIVE" %}
|
||||
<div class="listview panelfront side1 pledge">
|
||||
{% ifequal work.last_campaign.type 1 %}
|
||||
<span class="booklist_pledge"><a href="{% url pledge work_id=workid %}" class="fakeinput">Pledge</a></span>
|
||||
{% else %}
|
||||
{% if purchased %}
|
||||
<span class="booklist_pledge"><a href="{% url download workid %}" class="fakeinput">Download</a></span>
|
||||
{% else %}
|
||||
<span class="booklist_pledge"><a href="{% url purchase work_id=workid %}" class="fakeinput">Purchase</a></span>
|
||||
{% endif %}
|
||||
|
||||
{% endifequal %}
|
||||
</div>
|
||||
{% else %}
|
||||
{% else %}{% if work in wishlist %}
|
||||
{% ifequal supporter request.user %}
|
||||
{% comment %} used only on your own supporter page. {% endcomment %}
|
||||
<div class="listview panelfront side1 remove-wishlist">
|
||||
<span id="l{{ workid }}">Un-list</span>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="listview panelfront side1 on-wishlist">
|
||||
{% if purchased %}
|
||||
<span>Purchased!</span>
|
||||
{% else %}{% if borrowed %}
|
||||
<span>Borrowed! ...until</span>
|
||||
{% else %}{% if request.user.id in supporters %}
|
||||
<span>Pledged!</span>
|
||||
{% else %}
|
||||
<span>On My List!</span>
|
||||
{% endif %}{% endif %}{% endif %}
|
||||
</div>
|
||||
{% endifequal %}
|
||||
{% else %}{% ifequal status "ACTIVE" %}
|
||||
<div class="listview panelfront side1 add-wishlist">
|
||||
{% ifequal work.last_campaign.type 1 %}
|
||||
<span class="booklist_pledge"><a href="{% url pledge work_id=workid %}" class="fakeinput">Pledge</a></span>
|
||||
{% else %}
|
||||
{% if purchased %}
|
||||
<span class="booklist_pledge"><a href="{% url download workid %}" class="fakeinput">Download</a></span>
|
||||
{% else %}
|
||||
<span class="booklist_pledge"><a href="{% url purchase work_id=workid %}" class="fakeinput">Purchase</a></span>
|
||||
{% endif %}
|
||||
{% endifequal %}
|
||||
</div>
|
||||
{% else %}{% if work in wishlist %}
|
||||
<div class="listview panelfront side1 on-wishlist">
|
||||
<span>On My List!</span>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="listview panelfront side1 add-wishlist">
|
||||
{% if on_search_page %}
|
||||
|
@ -218,17 +234,23 @@
|
|||
<span class="work_id" id="l{{ workid }}">Add to My List</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}{% endifequal %}{% endifequal %}{% endif %}{% endif %}
|
||||
{% endif %}{% endif %}{% endif %}{% endif %}
|
||||
|
||||
<div class="listview panelfront side1 booklist-status">
|
||||
{% ifequal status "ACTIVE" %}
|
||||
{% ifequal work.last_campaign.type 1 %}
|
||||
<span class="booklist-status-text"><b>${{ work.last_campaign.current_total|floatformat:0|intcomma }}</b>/<b>${{ work.last_campaign.target|floatformat:0|intcomma }}</b></span>
|
||||
{% if in_library %}
|
||||
{% if borrowed %}
|
||||
<span class="booklist-status-text">{{ borrowed.expires|date:"M j, Y" }}</span>
|
||||
{% else %}
|
||||
<span class="booklist-status-text" style="line-height:19px">available<br />{{ next_acq.refreshes|date:"M j, Y" }}</span>
|
||||
{% endif %}
|
||||
{% else %}{% ifequal work.last_campaign.type 1 %}
|
||||
<span class="booklist-status-text"><b>${{ work.last_campaign.current_total|floatformat:0|intcomma }}</b>/<b>${{ work.last_campaign.target|floatformat:0|intcomma }}</b></span>
|
||||
{% else %}
|
||||
<span class="booklist-status-text"><b>${{ work.last_campaign.left|floatformat:0|intcomma }}</b> to go</span>
|
||||
{% endifequal %}
|
||||
<span class="booklist-status-text"><b>${{ work.last_campaign.left|floatformat:0|intcomma }}</b> to go</span>
|
||||
{% endifequal %}{% endif %}
|
||||
{% else %}{% ifequal status "INITIALIZED" %}
|
||||
<span class="booklist-status-label">Status: </span><span class="booklist-status-text">Coming soon!</span>
|
||||
<span class="booklist-status-label">Status: </span><span class="booklist-status-text">Coming soon!</span>
|
||||
{% else %}{% ifequal status "SUCCESSFUL" %}
|
||||
{% if not first_ebook %}
|
||||
<span class="booklist-status-text">Ebook coming soon</span>
|
||||
|
@ -243,22 +265,29 @@
|
|||
For status icons, we should display...
|
||||
If there is an ebook: options to get it
|
||||
If no ebook but there is an active or successful campaign: progress toward goal
|
||||
If B2U, read, borrow, reserve, purchase
|
||||
Otherwise: number of wishes
|
||||
{% endcomment %}
|
||||
{% if first_ebook %}
|
||||
<a href="{% url download workid %}" class="hijax"><div class="read_itbutton"><span>Read it Now</span></div></a>
|
||||
{% if purchased or borrowed or first_ebook %}
|
||||
<a href="{% url download workid %}" class="hijax" title="Download this work"><div class="read_itbutton"><span>Read it Now</span></div></a>
|
||||
{% else %}{% if borrowable %}
|
||||
<a href="{% url borrow workid %}?library={{library}}" class="hijax" title="Borrow this work"><div class="read_itbutton"><span>Borrow It</span></div></a>
|
||||
{% else %}{% if in_library %}
|
||||
<a href="{% url purchase work_id=workid %}" title="Reserve or buy this work"><div class="read_itbutton"><span>Reserve It</span></div></a>
|
||||
{% else %}{% if status == 'ACTIVE' or status == 'SUCCESSFUL' %}
|
||||
{% if not library or not in_library %}
|
||||
<div class="booklist-status-img">
|
||||
<img src="/static/images/images/icon-book-37by25-{{ work.percent_unglued }}.png" title="book list status" alt="book list status" />
|
||||
</div>
|
||||
<div class="booklist-status-label panel">{{ work.percent_of_goal }}%</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% if work.num_wishes %}
|
||||
<a href="{% if workid %}{% url work workid %}{% else %}{% url googlebooks googlebooks_id %}{% endif %}?tab=3" class="nobold"><span class="rounded"><span class="grey"><span class="panelnope">Listed by </span>{{ work.num_wishes }}</span></span></a>
|
||||
{% else %}
|
||||
<a href="{% if workid %}{% url work workid %}{% else %}{% url googlebooks googlebooks_id %}{% endif %}?tab=3" class="nobold"><span class="rounded"><span class="grey"><span class="panelnope">Listed by </span>0</span></span></a>
|
||||
{% endif %}
|
||||
{% endif %}{% endif %}
|
||||
{% endif %}{% endif %}{% endif %}{% endif %}
|
||||
|
||||
</div>
|
||||
<div class="listview panelfront side1 ebooks">
|
||||
|
|
|
@ -23,9 +23,7 @@
|
|||
<div id="user-block1">
|
||||
<div id="block-intro-text"><span class="special-user-name">Latest Comments</span></div>
|
||||
</div>
|
||||
<div class="user-block2"><span class="user-short-info">With your help we're raising money to give these books to the world.</span>
|
||||
</div>
|
||||
<div class="user-block3">
|
||||
<div class="user-block24"><span class="user-short-info">Ungluers have a lot to say about their books.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -99,7 +99,7 @@ $j(document).ready(function() {
|
|||
</div>
|
||||
{% endif %}
|
||||
{% if acq %}
|
||||
<h3>Download your ebook!</h3>
|
||||
<h3>Download your ebook{% if acq.lib_acq %}{% if acq.on_reserve %}, on reserve for you at{% else %}, on loan to you at{% endif %} {{ acq.lib_acq.user.library }}{% endif %}</h3>
|
||||
<div class="ebook_download">
|
||||
<a href="{{ formats.epub }}"><img src="/static/images/epub32.png" height="32" alt="epub" title="epub" /></a>
|
||||
<a href="{{ formats.epub }}">EPUB</a> (for iBooks, Nook, Kobo)
|
||||
|
|
|
@ -10,6 +10,9 @@
|
|||
<ul class="menu level2">
|
||||
{% if not request.user.is_anonymous %}
|
||||
<li class="first"><a href="/"><span>My Books</span></a></li>
|
||||
{% for library in request.user.profile.libraries %}
|
||||
<li><a href="{% url library library %}"><span>{{ library }}</span></a></li>
|
||||
{% endfor %}
|
||||
<li>
|
||||
{% else %}
|
||||
<li class="first">
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
<li class="parent">
|
||||
<span class="faq">Do eBooks I buy through unglue.it have DRM?</span>
|
||||
<span class="menu level2 answer">
|
||||
NO. However, each purchased book you download from unglue.it is personalized with your unglue.it username and your personal license certificate. You should not make copies of personal ebooks for your friends; if you do so, your license may be revoked and the rightsholders may get mad at you. To share ebooks, you can purchase Unglue.it library licenses!
|
||||
NO. However, each purchased book you download from unglue.it is personalized with your unglue.it username and your personal license certificate. You should not make copies of personal ebooks for your friends; if you do so, your license may be revoked and the rightsholders may get mad at you. To share ebooks, check out Unglue.it library licenses!
|
||||
</span>
|
||||
</li>
|
||||
|
||||
|
@ -44,6 +44,19 @@
|
|||
If you're unhappy with an Unglue.it ebook in any way, use the feedback tab at the right of this page to ask for a refund. But be sure to tell us why- that's our best way of learning how to make Ungue.it better!
|
||||
</span>
|
||||
</li>
|
||||
|
||||
<li class="parent">
|
||||
<span class="faq">What is a Library License?</span>
|
||||
<span class="menu level2 answer">
|
||||
If your library participates in Unglue.it, you can pay for a library license instead of purchasing an individual license. The ebook is yours for the first loan period, but the license belongs to your library so other library users can use it after your loan period is up. Obviously, it costs more because more people benefit, but you get the benefit of being part of a community sharing library assets.
|
||||
</span>
|
||||
</li>
|
||||
<li class="parent">
|
||||
<span class="faq">Why can't I buy a Library License?</span>
|
||||
<span class="menu level2 answer">
|
||||
You need to join a library participating in Unglue.it. If your library is not on <a href="{% url library_list %}">this list</a>, contact your librarian and have them check out our <a href="{% url libraries %}">libraries FAQ</a>.
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
{% extends "basedocumentation.html" %}
|
||||
{% block title %}Join {{ library }}{% endblock %}
|
||||
{% block doccontent %}
|
||||
<h2> Select your library </h2>
|
||||
|
||||
<h2> Other Account Management Tools </h2>
|
||||
<ul>
|
||||
<li>Want to <a href="{% url auth_password_change %}">change your password</a>?</li>
|
||||
<li>... or <a href="{% url notification_notice_settings %}">manage your contact preferences</a>?</li>
|
||||
<li>... or <a href="{% url email_change %}">change your email address</a>?</li>
|
||||
<li>... or <a href="{% url regluit.frontend.views.edit_user %}">change your username</a>?</li>
|
||||
<li>... or <a href="{% url marc_config %}">change your MARC record preferences</a>?</li>
|
||||
<li>... or <a href="{% url kindle_config %}">add or change a Send-to-Kindle email</a>?</li>
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
||||
|
|
@ -0,0 +1,269 @@
|
|||
{% extends "base.html" %}
|
||||
{% load endless %}
|
||||
{% load truncatechars %}
|
||||
{% block title %} — {{ library.user.username }}{% 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" />
|
||||
{% endblock %}
|
||||
{% block extra_js %}
|
||||
<script type="text/javascript" src="/static/js/wishlist.js"></script>
|
||||
<script type="text/javascript" src="{{ jquery_ui_home }}"></script>
|
||||
<script type="text/javascript" src="/static/js/greenpanel.js"></script>
|
||||
<script type="text/javascript" src="/static/js/import_books.js"></script>
|
||||
<script type="text/javascript" src="/static/js/counter.js"></script>
|
||||
|
||||
{% if works %}
|
||||
<!-- when the user's wishlist is empty, views.py gives us works for a slideshow -->
|
||||
<script src="/static/js/slides.min.jquery.js"></script>
|
||||
<script src="/static/js/slideshow.js"></script>
|
||||
|
||||
<!-- toggle to panelview instead of listview default so slideshow will look right -->
|
||||
<script type="text/javascript">
|
||||
var $j = jQuery.noConflict();
|
||||
$j(document).ready(function($) {
|
||||
$('.listview').addClass("panelview").removeClass("listview");
|
||||
});
|
||||
</script>
|
||||
{% else %}
|
||||
<!-- we only need these when there's stuff on the user's wishlist -->
|
||||
<script type="text/javascript" src="/static/js/toggle.js"></script>
|
||||
<script type="text/javascript" src="/static/js/tabs.js"></script>
|
||||
{% endif %}
|
||||
|
||||
<!-- highlight LT/GR add functions when people click on import divs -->
|
||||
<script type="text/javascript">
|
||||
var $j = jQuery.noConflict();
|
||||
function highlightTarget(targetdiv) {
|
||||
var target = $j(targetdiv);
|
||||
target.css({"background": "#8dc63f"}).animate(
|
||||
{backgroundColor: "white"}, 1500
|
||||
);
|
||||
};
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% block extra_head %}
|
||||
<link rel="alternate" type="application/atom+xml" title="feed for books from {{ supporter }}'s ungluing list" href="feed" />
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block topsection %}
|
||||
<div id="locationhash">{{ activetab }}</div>
|
||||
{% ifequal supporter request.user %}
|
||||
<div class="launch_top pale">You are logged in as the administrator of {{ library.user.username }}
|
||||
</div>
|
||||
{% endifequal %}
|
||||
|
||||
<div id="js-topsection">
|
||||
<div class="js-main">
|
||||
<div class="js-topnews">
|
||||
<div class="js-topnews1">
|
||||
<div class="js-topnews2">
|
||||
<div class="js-topnews3">
|
||||
<div class="user-block" itemscope itemtype="http://schema.org/Person">
|
||||
<div id="user-block1">
|
||||
<div class="block-inner">
|
||||
<img class="user-avatar" src="{{ library.user.profile.avatar_url }}" height="50" width="50" alt="Avatar for {{ supporter }}" title="Avatar" />
|
||||
<span class="user-name">
|
||||
<a href="#"><span itemprop="name">{{ library.user.username }}</span></a>
|
||||
</span>
|
||||
</div>
|
||||
<span class="user-badges">
|
||||
{% if library.user.profile.badges.all %}
|
||||
{% for badge in library.user.profile.badges.all %}
|
||||
<img src="{{ badge.path }}" alt="{{ badge.description }}" title="{{ badge.description }}" width="26" height="26" class="{{ badge.name }}" />
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{% if library.group in request.user.groups.all %}
|
||||
<div class="user-block23">
|
||||
<i>{{ library }} is {{ request.user }}'s Library!</i>
|
||||
</div>
|
||||
<div class="user-block4">
|
||||
<div class="social">
|
||||
{% if library.user.profile.home_url %}
|
||||
<a href="{{ library.user.profile.home_url }}" class="nounderline">
|
||||
<img src="/static/images/supporter_icons/home_square.png" alt="{{ supporter }}'s homepage" title="{{ supporter }}'s Homepage" />
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if library.user.profile.facebook_id %}
|
||||
<a href="http://www.facebook.com/profile.php?id={{library.user.profile.facebook_id}}" class="nounderline">
|
||||
<img src="/static/images/supporter_icons/facebook_square.png" alt="{{ supporter }}'s Facebook" title="{{ supporter }}'s Facebook" />
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if library.user.profile.twitter_id %}
|
||||
<a href="https://twitter.com/#!/{{ library.user.profile.twitter_id }}" class="nounderline">
|
||||
<img src="/static/images/supporter_icons/twitter_square.png" alt="{{ supporter }}'s Twitter" title="{{ supporter }}'s Twitter" />
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if library.user.profile.goodreads_user_link %}
|
||||
<a href="{{library.user.profile.goodreads_user_link}}" class="nounderline">
|
||||
<img src="/static/images/supporter_icons/goodreads_square.png" alt="{{ supporter }}'s profile on GoodReads" title="{{ supporter }}'s page on GoodReads" />
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if library.user.profile.librarything_id %}
|
||||
<a href="http://www.librarything.com/profile/{{ library.user.profile.librarything_id }}" class="nounderline">
|
||||
<img src="/static/images/supporter_icons/librarything_square.png" alt="{{ supporter }}'s profile on LibraryThing" title="{{ supporter }}'s page on LibraryThing" />
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="user-block24">
|
||||
{% include library.join_template %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div id="main-container">
|
||||
<div class="js-main">
|
||||
<div id="js-leftcol">
|
||||
{% include "explore.html" %}
|
||||
</div>
|
||||
|
||||
<div id="js-maincol-fr">
|
||||
<div class="js-maincol-inner">
|
||||
<div id="content-block">
|
||||
|
||||
{% if not works %}
|
||||
|
||||
{% comment %}
|
||||
if we're in empty-wishlist, slideshow mode, suppress tab area
|
||||
{% endcomment %}
|
||||
|
||||
<div class="content-block-heading wantto" id="tabs">
|
||||
<ul class="tabs">
|
||||
<li class="tabs1"><a href="#">Unglued</a></li>
|
||||
<li class="tabs2"><a href="#">Available</a></li>
|
||||
</ul>
|
||||
|
||||
<ul class="book-list-view">
|
||||
<li>View As:</li>
|
||||
<li class="view-list">
|
||||
<a href="#" id="toggle-list">
|
||||
<img src="/static/images/booklist/view-list.png" alt="view list" title="view list" />
|
||||
</a>
|
||||
</li>
|
||||
<li class="view-list">
|
||||
<a href="#" id="toggle-panel">
|
||||
<img src="/static/images/booklist/view-icon.png" alt="view icon" title="view icon" />
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if wishlist %}
|
||||
<div id="content-block-content">
|
||||
{% ifequal wishlist.works.all.count 0 %}
|
||||
{% ifequal request.user supporter %}
|
||||
<div class="empty-wishlist">
|
||||
|
||||
<div><h2 style="padding-left:35px;">Add a book to your list to get started.</h2><br /><br /></div>
|
||||
|
||||
<div id="js-slide">
|
||||
<div class="js-main">
|
||||
<div class="jsmodule">
|
||||
{% include "slideshow.html" %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="js-maincontainer-bot-block">
|
||||
<div id="js-search">
|
||||
<label>What book would you give to the world? </label>
|
||||
<form action="{% url search %}" method="get">
|
||||
<input type="text" id="watermark" onfocus="imgfocus()" onblur="imgblur(0)" size="25" class="inputbox" name="q" value="{{ q }}">
|
||||
<input type="submit" class="greenbutton" value="Search">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br /><br /><hr />We'd also love to hear your <a href="/feedback">feedback</a>.
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="empty-wishlist">
|
||||
It looks like {{ library.user.username }} is just getting started, and hasn't added books just yet.<br /><br />
|
||||
{% endifequal %}
|
||||
{% else %}
|
||||
|
||||
{% if request.user.is_anonymous %}
|
||||
<div class="tabs-1 anon_about">
|
||||
{% if works_unglued %}
|
||||
{{ supporter }} wants you to know about these free books. <a href="/about/unglued/" class="hijax">Find out why.</a>
|
||||
{% else %}
|
||||
{{ supporter }} isn't promoting any free books yet. <a href="/about/unglued_empty/" class="hijax">Find out how you can.</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="tabs-2 anon_about">
|
||||
{% if works_active %}
|
||||
{{ supporter }} is helping to make these books free. <a href="/about/active/" class="hijax">Find out how.</a>
|
||||
{% else %}
|
||||
{{ supporter }} isn't ungluing books at the moment. <a href="/about/active_empty" class="hijax">Find out how you can.</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="tabs-3 anon_about">
|
||||
{% if works_wished %}
|
||||
{{ supporter }} wants these books to be free. <a href="/about/wishlist/" class="hijax">Find out how to help.</a>
|
||||
{% else %}
|
||||
{{ supporter }} hasn't decided which books to give the world yet. <a href="/about/wishlist_empty/" class="hijax">Learn more.</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% lazy_paginate 20 works_unglued using "works_unglued" %}
|
||||
{% for work in works_unglued %}
|
||||
<div class="{% cycle 'row1' 'row2' %}">
|
||||
{% with work.googlebooks_id as googlebooks_id %}
|
||||
{% include "book_panel.html" %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div class="pagination content-block-heading tabs-1">
|
||||
{% get_pages %}
|
||||
{% for page in pages %}
|
||||
<a href="{{ page.path }}#1" class="endless_page_link">{{ page.number }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{% lazy_paginate 20 works_active using "works_active" %}
|
||||
{% for work in works_active %}
|
||||
<div class="{% cycle 'row1' 'row2' %}">
|
||||
{% with work.googlebooks_id as googlebooks_id %}
|
||||
{% include "book_panel.html" %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div class="pagination content-block-heading tabs-2">
|
||||
{% get_pages %}
|
||||
{% for page in pages %}
|
||||
<a href="{{ page.path }}#2" class="endless_page_link">{{ page.number }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
|
||||
{% endifequal %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
|
@ -0,0 +1,72 @@
|
|||
{% extends "base.html" %}
|
||||
{% 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/liblist.css" />
|
||||
{% endblock %}
|
||||
{% block extra_head %}
|
||||
<script type="text/javascript" src="{{ jquery_ui_home }}"></script>
|
||||
{% endblock %}
|
||||
{% block topsection %}
|
||||
|
||||
<div id="js-topsection">
|
||||
<div class="js-main">
|
||||
<div class="js-topnews">
|
||||
<div class="js-topnews1">
|
||||
<div class="js-topnews2">
|
||||
<div class="js-topnews3">
|
||||
<div class="user-block">
|
||||
<div id="user-block1">
|
||||
<div id="block-intro-text"><span class="special-user-name">Unglue.it Libraries</span></div>
|
||||
</div>
|
||||
<div class="user-block24"><span class="user-short-info">These libraries are participating in Unglue.it. If one of these is your library, you should be able to join, borrow and donate books. If your library is not on this list, contact your librarian and have them check out our <a href="{% url libraries %}">libraries FAQ</a>.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div id="main-container">
|
||||
<div class="js-main">
|
||||
<div id="js-leftcol">
|
||||
{% include "explore.html" %}
|
||||
</div>
|
||||
|
||||
<div id="js-maincol-fr">
|
||||
<div class="js-maincol-inner">
|
||||
<div id="content-block">
|
||||
<div id="content-block-content">
|
||||
<div style="height:46px;"></div>
|
||||
{% for library in libraries %}
|
||||
<div class="items {% cycle 'row1' 'row2' %}{% if library.group in request.user.groups.all %} joined{% endif %}">
|
||||
<div class="avatar">
|
||||
<a href="{% url library library %}">
|
||||
<img class="user-avatar" src="{{ library.user.profile.avatar_url }}" height="50" width="50" alt="Avatar for {{ library }}" title="{{ library }}" />
|
||||
</a>
|
||||
</div>
|
||||
<div class="nonavatar">
|
||||
<div class="libname"><a href="{% url library library %}">{{ library }}</a> {{ library.user.tagline }}</div>
|
||||
<div class="libstat">{% if library.group in request.user.groups.all %}<em>joined</em>{% else %} {% endif %}</div>
|
||||
<div class="libstat">{{ library.library_users.count }} Unglue.it users</div>
|
||||
<div class="libstat">{{ library.user.acqs.count }} Unglue.it holdings</div>
|
||||
<div class="libstat">{{ library.user.wishlist.works.count }} books on list</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% empty %}
|
||||
No libraries yet.
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -114,29 +114,34 @@ Please fix the following before launching your campaign:
|
|||
<div class="tabs-1">
|
||||
<h3>Edit the editions (if needed)</h3>
|
||||
{% if campaign.rh.can_sell %}
|
||||
<p> If you want to sell ebooks as part of a buy to unglue campaign, you'll need to upload the ebook files that you want to sell. </p>
|
||||
<p> If you want to sell ebooks as part of a buy to unglue campaign, you'll also need to upload an EPUB file for the ebook you want to sell. </p>
|
||||
{% endif %}
|
||||
<ul>
|
||||
{% for edition in campaign.work.editions.all %}
|
||||
<li> <a href="{% url new_edition edition.work.id edition.id %}"> Edit </a><i>{{ edition }}</i>
|
||||
{% if campaign.rh.can_sell %}You can also <a href="{% url edition_uploads edition.id %}"> load files</a> for this edition.
|
||||
<li>Edition: <i>{{ edition }}</i>
|
||||
<ul>
|
||||
{% for ebook_file in edition.ebook_files.all %}
|
||||
<li>{{ebook_file.file}} created {{ebook_file.created}} </li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<li><a href="{% url new_edition edition.work.id edition.id %}"> Edit </a> the edition</li>
|
||||
{% if campaign.rh.can_sell %}
|
||||
<li>You can also <a href="{% url edition_uploads edition.id %}"> Load a file</a> for this edition.</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% if campaign.work.ebookfiles.0 %}
|
||||
<p> <b>An Ebook file has been loaded.</b> </p>
|
||||
<p>Active file: {{campaign.work.ebookfiles.0.file}} created {{campaign.work.ebookfiles.0.created}} </p>
|
||||
<p>Edition: <i>{{ campaign.work.ebookfiles.0.edition }}</i> </p>
|
||||
{% endif %}
|
||||
<form action="#" method="POST">
|
||||
{% csrf_token %}
|
||||
{{ form.media }}
|
||||
<h3>Select the main edition</h3>
|
||||
<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>
|
||||
|
||||
|
||||
<div class="std_form">
|
||||
{{ form.edition.errors }}{{ form.edition }}
|
||||
</div>
|
||||
{% if campaign.edition %}
|
||||
<p>If the details of the edition you've chosen aren't accurate, you can <a href="{% url rh_edition work.id campaign.edition.id %}">edit the edition</a>.</p>
|
||||
{% else %}
|
||||
|
@ -146,8 +151,9 @@ Please fix the following before launching your campaign:
|
|||
{% ifnotequal campaign_status 'ACTIVE' %}
|
||||
<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="http://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>
|
||||
<div class="std_form">
|
||||
{{ form.license.errors }}{{ form.license }}
|
||||
|
||||
</div>
|
||||
{% ifequal 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>
|
||||
|
@ -157,9 +163,9 @@ Please fix the following before launching your campaign:
|
|||
<p>Once you launch the campaign, you'll be able to decrease your target, but not increase it.</p>
|
||||
|
||||
<p>Please email us if you want to talk about pricing strategy.</p>
|
||||
|
||||
<div class="std_form">
|
||||
{{ form.target.errors }}${{ form.target }}
|
||||
|
||||
</div>
|
||||
<h3>Ending date</h3>
|
||||
<p> This is the ending date of your campaign. Once you launch the campaign, you won't be able to change it.</p>
|
||||
|
||||
|
@ -176,9 +182,9 @@ Please fix the following before launching your campaign:
|
|||
<p>Once you launch the campaign, you'll be able to decrease your target, but not increase it.</p>
|
||||
|
||||
<p>Please email us if you want to talk about pricing strategy.</p>
|
||||
|
||||
<div class="std_form">
|
||||
{{ form.target.errors }}${{ form.target }}
|
||||
|
||||
</div>
|
||||
<h3>Initial Ungluing Date</h3>
|
||||
<p>When you launch a Buy-To-Unglue campaign, you will specify a date in the future at which your book will become Creative Commons Licensed. eBooks sold via unglue.it will include a notice of this license. With every sale, the effective date of this license will advance a bit toward the present. </p>
|
||||
|
||||
|
@ -191,23 +197,30 @@ Please fix the following before launching your campaign:
|
|||
<h3>License being offered</h3>
|
||||
<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>
|
||||
<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="http://creativecommons.org/licenses">Creative Commons: About the Licenses</a>.</p></p>
|
||||
<div class="std_form">
|
||||
{{ form.license.errors }}<span>{{ form.license }}</span>
|
||||
</div>
|
||||
<h3>Target Price</h3>
|
||||
<p>The current target price for your campaign is <b>${{ campaign.target|intcomma }}</b>. Since your campaign is active, you may lower, but not raise, this target.</p>
|
||||
<div class="std_form">
|
||||
${{ form.target.errors }}{{ form.target }}
|
||||
|
||||
</div>
|
||||
{% ifequal 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>
|
||||
{% else %}
|
||||
<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>
|
||||
<div class="std_form">
|
||||
{{ form.cc_date_initial.errors }}{{ form.cc_date_initial }}
|
||||
<h3>Ending date</h3>
|
||||
<p>Your Buy-to-Unglue Campaign will run until <b>{{ campaign.deadline }}</b>. Contact unglue.it staff to extend it.</p>
|
||||
</div>
|
||||
<div class="std_form">
|
||||
<!--{{ form.deadline.errors }}-->{{ form.deadline }}
|
||||
</div>
|
||||
{% endifequal %}
|
||||
{% endifnotequal %}
|
||||
|
||||
|
@ -279,14 +292,14 @@ Please fix the following before launching your campaign:
|
|||
|
||||
<h3>e-mail contact addresses</h3>
|
||||
<p>Enter the email address where notifications about this campaign should be sent. If your campaign succeeds, this email needs to work if you want to get paid! This address will not be exposed on the website.</p>
|
||||
<p>{{ form.paypal_receiver.errors }}{{ form.paypal_receiver }}</p>
|
||||
<p class="std_form">{{ form.paypal_receiver.errors }}{{ form.paypal_receiver }}</p>
|
||||
<p>(Optional, but highly recommended). Enter an email address where ungluers with questions about the book or the campaign can contact you or someone involved. This address will not be exposed on the website.</p>
|
||||
<p>{{ form.email.errors }}{{ form.email }}</p>
|
||||
<p class="std_form">{{ form.email.errors }}{{ form.email }}</p>
|
||||
|
||||
{% if work.publishers %}
|
||||
<h3>Publisher</h3>
|
||||
<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>{{ form.publisher.errors }}{{ form.publisher }}</p>
|
||||
<p class="std_form">{{ form.publisher.errors }}{{ form.publisher }}</p>
|
||||
{% endif %}
|
||||
{% ifequal 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 />
|
||||
|
@ -363,17 +376,21 @@ Please fix the following before launching your campaign:
|
|||
{% endifequal %}
|
||||
{% ifequal campaign.type 2 %}
|
||||
<h3> Offers to sell </h3>
|
||||
<p> Make sure that ebook files for this work <a class="tabs1">have been loaded</a>!
|
||||
{% if not campaign.work.ebookfiles.0 %}
|
||||
<p> <b>An EPUB file for this work <a class="tabs1">needs to be loaded</a>!</b></p>
|
||||
{% endif %}
|
||||
<p> Enter a per/copy price for each license type. You may change these prices after the campaign has begun.</p>
|
||||
<div class="jsmod-content">
|
||||
<p>
|
||||
{% for offer in offers %}
|
||||
<form action="#" method="POST">
|
||||
<form action="#" method="POST"><div>
|
||||
{% csrf_token %}
|
||||
{{ offer.offer_form.as_p }}
|
||||
<br />
|
||||
<input type="submit" name="change_offer" value="Change Offer" />
|
||||
</form>
|
||||
|
||||
<span class="std_form">{{ offer.get_license_display }}: ${{ offer.offer_form.price }}{{ offer.offer_form.price.errors }}
|
||||
{{ offer.offer_form.active }}
|
||||
{{ offer.offer_form.license }}
|
||||
{{ offer.offer_form.work }}</span>
|
||||
<input type="submit" name="change_offer" value="Change Price" />
|
||||
</div></form>
|
||||
<p />
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endifequal %}
|
||||
|
@ -422,10 +439,14 @@ Please fix the following before launching your campaign:
|
|||
<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>
|
||||
{% else %}
|
||||
{% comment %}This might be a good place to put a sales report. {% endcomment %}
|
||||
{% endifequal %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
{% load humanize %}Your Unglue.it credit card transaction has completed and your credit card has been charged ${{ transaction.amount|intcomma }}. If you have not already done so, download your ebook at
|
||||
https://{{ current_site.domain }}{% url download transaction.campaign.work.id %}
|
||||
{% load humanize %}Your Unglue.it credit card transaction has completed and your credit card has been charged ${{ transaction.amount|intcomma }}. {% ifequal transaction.offer.license 2 %}If you have not already done so, download your ebook at
|
||||
https://{{ current_site.domain }}{% url download transaction.campaign.work.id %}{% endifequal %}
|
||||
|
||||
Thanks to you and other ungluers, {{ transaction.campaign.work.title }} will be eventually be released to the world in an unglued ebook edition. Thanks to your purchase, the ungluing date advanced {{ transaction.offer.days_per_copy|floatformat }} days to {{ transaction.campaign.cc_date }}.
|
||||
|
||||
{% ifequal transaction.offer.license 1 %}
|
||||
This ebook is licensed to you personally, and your personal license has been embedded in the ebook file. You may download as many times as you need to, but you can't make copies for the use of others until the ungluing date. You can make that date come sooner by encouraging your friends to buy a copy.
|
||||
{% else %}
|
||||
This ebook is licensed to your library and its license has been embedded in the ebook file. If you'd like to be the first to use it, please get your copy now at
|
||||
https://{{ current_site.domain }}{% url borrow transaction.campaign.work.id %}
|
||||
After an hour, the ebook will be available to all of your library's users on a one-user-per two weeks basis until the ungluing date, when it will be free to all. You can make that date come sooner by encouraging your friends to buy a copy.{% endifequal %}
|
||||
|
||||
For more information about the book, visit the visit the book's unglue.it page at
|
||||
https://{{ current_site.domain }}{% url work transaction.campaign.work.id %}
|
||||
|
|
|
@ -6,12 +6,21 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block comments_graphical %}
|
||||
{% ifequal transaction.offer.license 1 %}
|
||||
Your Unglue.it credit card transaction has completed and your credit card has been charged ${{ transaction.amount|intcomma }}. If you have not already done so, download your ebook at <a href="{% url download transaction.campaign.work.id %}">the book's download page.</a>
|
||||
{% else %}
|
||||
Your Unglue.it credit card transaction has completed and your credit card has been charged ${{ transaction.amount|intcomma }}.
|
||||
{% endifequal %}
|
||||
{% endblock %}
|
||||
|
||||
{% block comments_textual %}
|
||||
<p>Thanks to you and other ungluers, {{ transaction.campaign.work.title }} will be eventually be released to the world in an unglued ebook edition. Thanks to your purchase, the ungluing date advanced {{ transaction.offer.days_per_copy|floatformat }} days to {{ transaction.campaign.cc_date }}.</p>
|
||||
|
||||
{% ifequal transaction.offer.license 1 %}
|
||||
<p>This ebook is licensed to you personally, and your personal license has been embedded in the ebook file. You may download as many times as you need to, but you can't make copies for the use of others until the ungluing date. You can make that date come sooner by encouraging your friends to buy a copy.</p>
|
||||
{% else %}
|
||||
<p>This ebook is licensed to your library and its license has been embedded in the ebook file. If you'd like to be the first to use it, please <a href="{% url borrow transaction.campaign.work.id %}">get your copy now</a>. After an hour, the ebook will be available to all of your library's users on a one-user-per two weeks basis until the ungluing date, when it will be free to all. You can make that date come sooner by encouraging your friends to buy a copy.</p>
|
||||
{% endifequal %}
|
||||
<p>For more information about the book, visit the visit the <a href="{% url work transaction.campaign.work.id %}">book's unglue.it page</a>.
|
||||
</p>
|
||||
<p>Thank you again for your support.
|
||||
|
|
|
@ -1 +1 @@
|
|||
You have purchased {{transaction.campaign.work.title}}.
|
||||
You have purchased {{transaction.campaign.work.title}}{% ifequal transaction.offer.license 2 %} for your library{% endifequal %}.
|
|
@ -1,5 +1,6 @@
|
|||
{% extends "basepledge.html" %}
|
||||
{% load humanize %}
|
||||
{% load lib_acqs %}
|
||||
|
||||
{% block title %}Pledge{% endblock %}
|
||||
|
||||
|
@ -64,31 +65,67 @@
|
|||
</div>
|
||||
</div>
|
||||
{% ifequal work.last_campaign.status "ACTIVE" %}
|
||||
{% lib_acqs %}
|
||||
<div class="jsmodule rounded clearfix">
|
||||
<div class="jsmod-content">
|
||||
|
||||
<form class="pledgeform" method="POST" action="#">
|
||||
{% if next_acq %}
|
||||
<div class="pledge_amount">This ebook can be reserved from your library.</div>
|
||||
<div class="bigger" style="margin:20px">Available starting {{ next_acq.refreshes|date:"M j, Y"}} at {{ next_acq.user }}.</div>
|
||||
<div><a href="{% url reserve work.id%}?library={{ next_acq.user }}" class="fakeinput">Reserve Now</a> </div>
|
||||
<div style="height:30px;"></div>
|
||||
<div class="bigger" style="height:30px;clear:both;margin:20px">If you'd rather not wait, consider one of the purchase options.</div>
|
||||
{% endif %}
|
||||
{% csrf_token %}
|
||||
{{ form.non_field_errors }}
|
||||
|
||||
{% if work.offers.all|length > 1 %}
|
||||
<div class="pledge_amount premium_level">Choose your license type:</div>
|
||||
<div class="pledge_amount premium_level">Purchase Options: Individual or Library?</div>
|
||||
|
||||
<div style="height:10px;"></div>
|
||||
|
||||
<ul class="support menu" id="premiums_list">
|
||||
{% for offer in work.last_campaign.active_offers %}
|
||||
<li class="{% if forloop.first %}first{% else %}{% if forloop.last %}last{% endif %}{% endif %}">
|
||||
<label for="offer_{{offer.id}}">
|
||||
<input type="radio" name="offer_id" id="offer_{{offer.id}}" value="{{offer.id}}" {% ifequal request.REQUEST.offer_id offer.id|stringformat:"s" %}checked="checked"{% else %} {% ifequal offer_id offer.id %}checked="checked"{% endifequal %}{% endifequal %} />
|
||||
<span class="menu-item-price">
|
||||
${{ offer.price|intcomma }}
|
||||
</span>
|
||||
<span class="menu-item-desc">
|
||||
{{ offer.get_license_display }}
|
||||
</span>
|
||||
</label></li>
|
||||
{% endfor %}
|
||||
{% with work.last_campaign.individual_offer as offer %}
|
||||
<li class="first">
|
||||
{% if user_license.purchased %}
|
||||
<span class="menu-item-desc">Individual license already purchased!</span>
|
||||
{% else %}
|
||||
<label for="offer_{{offer.id}}">
|
||||
<input type="radio" name="offer_id" id="offer_{{offer.id}}" value="{{offer.id}}" {% ifequal request.REQUEST.offer_id offer.id|stringformat:"s" %}checked="checked"{% else %} {% ifequal offer_id offer.id %}checked="checked"{% endifequal %}{% endifequal %} />
|
||||
<span class="menu-item-price">
|
||||
${{ offer.price|intcomma }}
|
||||
</span>
|
||||
<span class="menu-item-desc">
|
||||
{{ offer.get_license_display }}
|
||||
</span>
|
||||
</label>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endwith %}
|
||||
{% with work.last_campaign.library_offer as offer %}
|
||||
<li class="last">
|
||||
{% if request.user.profile.libraries %}
|
||||
<label for="offer_{{offer.id}}">
|
||||
<input type="radio" name="offer_id" id="offer_{{offer.id}}" value="{{offer.id}}" {% ifequal request.REQUEST.offer_id offer.id|stringformat:"s" %}checked="checked"{% else %} {% ifequal offer_id offer.id %}checked="checked"{% endifequal %}{% endifequal %} />
|
||||
<span class="menu-item-price">
|
||||
${{ offer.price|intcomma }}
|
||||
</span>
|
||||
<span class="menu-item-desc">
|
||||
{{ offer.get_license_display }} for
|
||||
<select name="library_id" class="std_form">
|
||||
{% for library in request.user.profile.libraries %}
|
||||
<option value="{{library.id}}">{{ library }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</span>
|
||||
</label>
|
||||
{% else %}
|
||||
<a href="{% url library_list %}"><span class="menu-item-desc">
|
||||
Join a Library to share and borrow unglue.it ebooks
|
||||
</span></a>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endwith %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<div style="height:10px;"></div>
|
||||
|
|
|
@ -70,25 +70,13 @@ function highlightTarget(targetdiv) {
|
|||
<link rel="alternate" type="application/atom+xml" title="feed for books from {{ supporter }}'s ungluing list" href="feed" />
|
||||
{% endblock %}
|
||||
|
||||
{% comment %}
|
||||
To do:
|
||||
create topsection file for inclusion in multiple contexts, if needed
|
||||
Goodreads
|
||||
be sure words display correctlydja
|
||||
do I need both add-wishlist and remove-wishlist classes? do they differ?
|
||||
better alignment on I am ungluing & badges
|
||||
|
||||
make sure backed/backing/wishlist is the order we want the badges to be in
|
||||
test code with other campaign statuses -- random_campaigns needs to set a variety of statuses!
|
||||
why is there a status in regluit.payment.models.Transaction? does it duplicate the status in regluit.core.models.Campaign?
|
||||
|
||||
|
||||
there's no tab for seeing ALL my books, only the filters! huh.
|
||||
|
||||
{% endcomment %}
|
||||
|
||||
{% block topsection %}
|
||||
<div id="locationhash">{{ activetab }}</div>
|
||||
{% if supporter.library %}
|
||||
<div class="launch_top pale">{{ supporter.library }} is a Library participating in Unglue.it. <a href="{% url join_library supporter.username %}">Click here</a> to use {{ supporter.library }}'s books.
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div id="js-topsection">
|
||||
<div class="js-main">
|
||||
<div class="js-topnews">
|
||||
|
@ -207,9 +195,9 @@ there's no tab for seeing ALL my books, only the filters! huh.
|
|||
</div>
|
||||
<div class="check-list" id="connectgr">
|
||||
{% if user.profile.goodreads_user_id %}
|
||||
<a href="{{goodreads_auth_url}}">Update your GoodReads connection</a> <br /> or disconnect GoodReads: {{ profile_form.clear_goodreads }}
|
||||
<a href="{% url goodreads_auth %}">Update your GoodReads connection</a> <br /> or disconnect GoodReads: {{ profile_form.clear_goodreads }}
|
||||
{% else %}
|
||||
<a href="{{goodreads_auth_url}}">Connect your GoodReads account</a> to Unglue.it
|
||||
<a href="{% url goodreads_auth %}">Connect your GoodReads account</a> to Unglue.it
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="check-list" id="connectlt">
|
||||
|
@ -220,7 +208,7 @@ there's no tab for seeing ALL my books, only the filters! huh.
|
|||
</form>
|
||||
<div class="block block3">
|
||||
<h3 class="title">Import your books</h3>
|
||||
{% if goodreads_id %}
|
||||
{% if request.user.profile.goodreads_user_id %}
|
||||
<form id="load_shelf_form" method="post" action="#">
|
||||
{% csrf_token %}
|
||||
<div class="fieldWrapper">
|
||||
|
@ -232,7 +220,7 @@ there's no tab for seeing ALL my books, only the filters! huh.
|
|||
{% else %}
|
||||
<div id="loadgr" onclick="highlightTarget('#connectgr'); return false;"><div>Connect your GoodReads account to import from GoodReads.</div></div>
|
||||
{% endif %}
|
||||
{% if librarything_id %}
|
||||
{% if request.user.profile.librarything_id %}
|
||||
<form id="librarything_load" method="post" action="#">
|
||||
{% csrf_token %}
|
||||
<div id="loadlt"><input type="submit" id="librarything_input" value="Add your LibraryThing library" /></div>
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
{% extends "base.html" %}
|
||||
{% load comments %}
|
||||
{% load humanize %}
|
||||
{% load purchased %}
|
||||
{% load lib_acqs %}
|
||||
{% block title %}—
|
||||
{% if work.first_ebook %}
|
||||
{{ work.title }} is a Free eBook
|
||||
|
@ -11,7 +13,6 @@
|
|||
|
||||
{% block extra_css %}
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/campaign2.css" />
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
|
@ -58,7 +59,9 @@
|
|||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% block content %}
|
||||
{% purchased %}
|
||||
{% lib_acqs %}
|
||||
{% with work.last_campaign_status as status %}
|
||||
{% with work.id as work_id %}
|
||||
<div id="main-container">
|
||||
|
@ -123,6 +126,15 @@
|
|||
<span>{{ work.last_campaign.supporters_count }}</span> ungluers
|
||||
{% endif %}
|
||||
</div>
|
||||
{% ifequal work.last_campaign.type 2 %}
|
||||
<div class="campaign-status-info">
|
||||
{% if work.lib_acqs.count == 1 %}
|
||||
<span>1</span> copy in a library
|
||||
{% else %}
|
||||
<span>{{ work.lib_acqs.count }}</span> in libraries
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endifequal %}
|
||||
<div class="campaign-status-info">
|
||||
{% ifequal work.last_campaign.type 1 %}
|
||||
<span>{{ work.last_campaign.countdown }}</span> to go
|
||||
|
@ -458,13 +470,15 @@
|
|||
<div class="btn_support"><form action="{% url pledge work_id %}" method="get"><input type="submit" value="Pledge" /></form></div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% if purchased %}
|
||||
{% if license_is_active %}
|
||||
<div class="btn_support">
|
||||
<a href="{% url download_purchased work_id %}" class="hijax"><span>Download</span></a>
|
||||
</div>
|
||||
{% else %}{% if borrowable %}
|
||||
<div class="btn_support"><form action="{% url borrow work_id %}" method="get"><input type="submit" value="Borrow" /></form></div>
|
||||
{% else %}
|
||||
<div class="btn_support"><form action="{% url purchase work_id %}" method="get"><input type="submit" value="Purchase" /></form></div>
|
||||
{% endif %}
|
||||
<div class="btn_support"><form action="{% url purchase work_id %}" method="get"><input type="submit" value="{% if next_acq %}Reserve{% else %}Purchase{% endif %}" /></form></div>
|
||||
{% endif %}{% endif %}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% if work.first_ebook %}
|
||||
|
@ -600,23 +614,57 @@
|
|||
</ul>
|
||||
</div>
|
||||
{% else %}
|
||||
{% if purchased %}
|
||||
<h3 class="jsmod-title">
|
||||
<span class="on-wishlist">Purchased!</span>
|
||||
</h3>
|
||||
{% else %}
|
||||
<a href="{% url purchase work_id %}"><h3 class="jsmod-title"><span>Purchase Options</span></h3></a>
|
||||
{% if lib_licenses.available %}
|
||||
<h3 class="jsmod-title"><span>Borrow</span></h3>
|
||||
<div class="jsmod-content">
|
||||
<ul class="support menu">
|
||||
{% for lib_license in lib_licenses.all %}
|
||||
<li class="{% if forloop.first %}first{% else %}{% if forloop.last %}last{% endif %}{% endif %}">
|
||||
<a href="???">Borrow!</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
<h3 class="jsmod-title"><span>Buy for Yourself</span></h3>
|
||||
<div class="jsmod-content">
|
||||
<ul class="support menu">
|
||||
{% for offer in work.last_campaign.active_offers %}
|
||||
<li class="{% if forloop.first %}first{% else %}{% if forloop.last %}last{% endif %}{% endif %}">
|
||||
<a href="{% url purchase work_id %}?offer_id={{offer.id}}">
|
||||
<span class="menu-item-price">${{ offer.price|floatformat:0|intcomma }}</span>
|
||||
<span class="menu-item-desc">{{ offer.get_license_display }}</span>
|
||||
</a></li>
|
||||
{% endfor %}
|
||||
{% if purchased %}
|
||||
<li class="first no_link">
|
||||
<span class="menu-item-desc">Purchased!</span>
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="first">
|
||||
<a href="{% url purchase work_id %}?offer_id={{work.last_campaign.individual_offer.id}}">
|
||||
<span class="menu-item-price">${{ work.last_campaign.individual_offer.price|floatformat:0|intcomma }}</span>
|
||||
<span class="menu-item-desc">{{ work.last_campaign.individual_offer.get_license_display }}</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
{% if borrowed %}
|
||||
<h3 class="jsmod-title">
|
||||
<span class="on-wishlist">Borrowed!</span>
|
||||
</h3>
|
||||
{% else %}
|
||||
<h3 class="jsmod-title"><span>Buy for a Library</span></h3>
|
||||
<div class="jsmod-content">
|
||||
<ul class="support menu">
|
||||
<li class="first last">
|
||||
{% if request.user.profile.libraries %}
|
||||
<a href="{% url purchase work_id %}?offer_id={{work.last_campaign.library_offer.id}}">
|
||||
<span class="menu-item-price">${{ work.last_campaign.library_offer.price|floatformat:0|intcomma }}</span>
|
||||
<span class="menu-item-desc">{{ work.last_campaign.library_offer.get_license_display }}</span>
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="{% url library_list %}"><span class="menu-item-desc">
|
||||
Join a Library to share and borrow unglue.it ebooks
|
||||
</span></a>
|
||||
{% endif %}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endifequal %}
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
from django import template
|
||||
from regluit.utils.localdatetime import now
|
||||
from regluit.parameters import *
|
||||
|
||||
register = template.Library()
|
||||
|
||||
@register.simple_tag(takes_context=True)
|
||||
def bookpanel(context):
|
||||
work = context['work']
|
||||
library = context.get('library',None)
|
||||
user = context['request'].user
|
||||
campaign = context.get('last_campaign', None)
|
||||
|
||||
# compute a boolean that's true if bookpanel should show a "pledge" button...
|
||||
# campaign is ACTIVE, type 1 - PLEDGE
|
||||
# user has not pledged or user is anonymous
|
||||
|
||||
show_pledge = False
|
||||
if campaign and campaign.type==PLEDGE:
|
||||
if user.is_anonymous() or not user.id in context.get('supporters', []):
|
||||
show_pledge = True
|
||||
context['show_pledge'] = show_pledge
|
||||
|
||||
# compute a boolean that's true if bookpanel should show a "purchase" button...
|
||||
# campaign is ACTIVE, type 2 - BUY2UNGLUE
|
||||
# user has not purchased or user is anonymous
|
||||
# user has not borrowed or user is anonymous
|
||||
# work not available in users library
|
||||
# not on the library page
|
||||
|
||||
show_purchase = False
|
||||
if campaign and campaign.type==BUY2UNGLUE:
|
||||
if user.is_anonymous() or not context.get('license_is_active', False):
|
||||
if not context.get('borrowable', False):
|
||||
if not library:
|
||||
show_purchase = True
|
||||
context['show_purchase'] = show_purchase
|
||||
return ''
|
|
@ -0,0 +1,27 @@
|
|||
from regluit.utils.localdatetime import now
|
||||
from django import template
|
||||
register = template.Library()
|
||||
|
||||
@register.simple_tag(takes_context=True)
|
||||
def lib_acqs(context):
|
||||
work = context['work']
|
||||
library = context.get('library',False)
|
||||
if library:
|
||||
lib_user = library.user
|
||||
else:
|
||||
user = context['request'].user
|
||||
if user.is_anonymous():
|
||||
return ''
|
||||
else:
|
||||
lib_user = (lib.user for lib in user.profile.libraries)
|
||||
try:
|
||||
user_license = work.get_user_license(lib_user)
|
||||
except AttributeError:
|
||||
user_license = None
|
||||
if user_license:
|
||||
context['lib_acqs'] = user_license.lib_acqs
|
||||
context['next_acq'] = user_license.next_acq
|
||||
else:
|
||||
context['lib_acqs'] = None
|
||||
|
||||
return ''
|
|
@ -7,8 +7,23 @@ register = template.Library()
|
|||
def purchased(context):
|
||||
work = context['work']
|
||||
user = context['request'].user
|
||||
try:
|
||||
context['purchased'] = work.purchased_by(user)
|
||||
except:
|
||||
context['purchased'] = False
|
||||
if user.is_anonymous():
|
||||
return ''
|
||||
try:
|
||||
user_license = work.get_user_license(user)
|
||||
context['borrowable'] = work.borrowable(user)
|
||||
context['in_library'] = work.in_library(user)
|
||||
except AttributeError:
|
||||
user_license = None
|
||||
context['borrowable'] = None
|
||||
context['in_library'] = None
|
||||
|
||||
if user_license:
|
||||
context['purchased'] = user_license.purchased
|
||||
context['borrowed'] = user_license.borrowed
|
||||
context['license_is_active'] = user_license.is_active
|
||||
else:
|
||||
context['purchased'] = None
|
||||
context['borrowed'] = None
|
||||
context['license_is_active'] = False
|
||||
return ''
|
||||
|
|
|
@ -48,8 +48,8 @@ urlpatterns = patterns(
|
|||
url(r"^next/$", "next", name="next"),
|
||||
url(r"^supporter/(?P<supporter_username>[^/]+)/$", "supporter", {'template_name': 'supporter.html'}, name="supporter"),
|
||||
url(r"^supporter/(?P<userlist>[^/]+)/marc/$", "marc", name="user_marc"),
|
||||
url(r"^library/(?P<library_name>[^/]+)/$", "library", name="library"),
|
||||
url(r"^accounts/manage/$", login_required(ManageAccount.as_view()), name="manage_account"),
|
||||
url(r'^accounts/superlogin/$', 'superlogin', name='superlogin'),
|
||||
url(r"^search/$", "search", name="search"),
|
||||
url(r"^privacy/$", TemplateView.as_view(template_name="privacy.html"),
|
||||
name="privacy"),
|
||||
|
@ -87,6 +87,8 @@ urlpatterns = patterns(
|
|||
url(r"^work/(?P<work_id>\d+)/lockss/$", "lockss", name="lockss"),
|
||||
url(r"^lockss/(?P<year>\d+)/$", "lockss_manifest", name="lockss_manifest"),
|
||||
url(r"^work/(?P<work_id>\d+)/download/$", "download", name="download"),
|
||||
url(r"^work/(?P<work_id>\d+)/borrow/$", "borrow", name="borrow"),
|
||||
url(r"^work/(?P<work_id>\d+)/reserve/$", login_required(PurchaseView.as_view(),login_url='/accounts/login/purchase/'), name="reserve"),
|
||||
url(r"^work/(?P<work_id>\d+)/merge/$", login_required(MergeView.as_view()), name="merge"),
|
||||
url(r"^work/(?P<work_id>\d+)/split/$", "split_work", name="split"),
|
||||
url(r"^work/\d+/acks/images/(?P<file_name>[\w\.]*)$", "static_redirect_view",{'dir': 'images'}),
|
||||
|
|
|
@ -107,7 +107,6 @@ from regluit.frontend.forms import (
|
|||
WorkForm,
|
||||
OtherWorkForm,
|
||||
MsgForm,
|
||||
AuthForm,
|
||||
PressForm,
|
||||
KindleEmailForm,
|
||||
MARCUngluifyForm,
|
||||
|
@ -136,6 +135,8 @@ from regluit.payment.parameters import (
|
|||
|
||||
from regluit.utils.localdatetime import now, date_today
|
||||
from regluit.booxtream.exceptions import BooXtreamError
|
||||
from regluit.libraryauth.views import Authenticator
|
||||
from regluit.libraryauth.models import Library
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -296,18 +297,6 @@ def stub(request):
|
|||
def acks(request, work):
|
||||
return render(request,'front_matter.html', {'campaign': work.last_campaign()})
|
||||
|
||||
def superlogin(request, **kwargs):
|
||||
extra_context = None
|
||||
if request.method == 'POST' and request.user.is_anonymous():
|
||||
username=request.POST.get("username", "")
|
||||
try:
|
||||
user=models.User.objects.get(username=username)
|
||||
extra_context={"socials":user.profile.social_auths}
|
||||
except:
|
||||
pass
|
||||
if request.GET.has_key("add"):
|
||||
request.session["add_wishlist"]=request.GET["add"]
|
||||
return login(request, extra_context=extra_context, authentication_form=AuthForm, **kwargs)
|
||||
|
||||
@login_required
|
||||
def social_auth_reset_password(request):
|
||||
|
@ -424,7 +413,6 @@ def work(request, work_id, action='display'):
|
|||
'claimstatus': claimstatus,
|
||||
'rights_holder_name': rights_holder_name,
|
||||
'cover_width': cover_width_number,
|
||||
'purchased': work.purchased_by(request.user),
|
||||
})
|
||||
|
||||
def edition_uploads(request, edition_id):
|
||||
|
@ -1104,6 +1092,7 @@ class PurchaseView(PledgeView):
|
|||
'tid': self.transaction.id if self.transaction else None,
|
||||
'cover_width': cover_width(self.work),
|
||||
'offer_id':self.offer_id,
|
||||
'user_license': self.work.get_user_license(self.request.user),
|
||||
})
|
||||
|
||||
return context
|
||||
|
@ -1135,7 +1124,7 @@ class PurchaseView(PledgeView):
|
|||
def get_preapproval_amount(self):
|
||||
self.offer_id = self.request.REQUEST.get('offer_id', None)
|
||||
if not self.offer_id:
|
||||
self.offer_id = self.work.last_campaign().active_offers()[0].id
|
||||
self.offer_id = self.work.last_campaign().individual_offer.id
|
||||
preapproval_amount = None
|
||||
if self.offer_id != None:
|
||||
try:
|
||||
|
@ -1766,7 +1755,7 @@ def campaign_admin(request):
|
|||
|
||||
return render(request, "campaign_admin.html", context)
|
||||
|
||||
def supporter(request, supporter_username, template_name):
|
||||
def supporter(request, supporter_username, template_name, extra_context={}):
|
||||
supporter = get_object_or_404(User, username=supporter_username)
|
||||
wishlist = supporter.wishlist
|
||||
works = []
|
||||
|
@ -1846,22 +1835,10 @@ def supporter(request, supporter_username, template_name):
|
|||
else:
|
||||
profile_form= ProfileForm(instance=profile_obj)
|
||||
|
||||
if request.user.profile.goodreads_user_id is not None:
|
||||
goodreads_id = request.user.profile.goodreads_user_id
|
||||
else:
|
||||
goodreads_id = None
|
||||
|
||||
if request.user.profile.librarything_id is not None:
|
||||
librarything_id = request.user.profile.librarything_id
|
||||
else:
|
||||
librarything_id = None
|
||||
else:
|
||||
profile_form = ''
|
||||
goodreads_id = None
|
||||
librarything_id = None
|
||||
|
||||
process_kindle_email(request)
|
||||
|
||||
context = {
|
||||
"supporter": supporter,
|
||||
"wishlist": wishlist,
|
||||
|
@ -1875,14 +1852,27 @@ def supporter(request, supporter_username, template_name):
|
|||
"wished": wished,
|
||||
"profile_form": profile_form,
|
||||
"ungluers": userlists.other_users(supporter, 5 ),
|
||||
"goodreads_auth_url": reverse('goodreads_auth'),
|
||||
"goodreads_id": goodreads_id,
|
||||
"librarything_id": librarything_id,
|
||||
"activetab": activetab
|
||||
"activetab": activetab,
|
||||
}
|
||||
|
||||
context.update(extra_context)
|
||||
return render(request, template_name, context)
|
||||
|
||||
def library(request,library_name):
|
||||
context={}
|
||||
try:
|
||||
# determine if the supporter is a library
|
||||
authenticator = Authenticator(request,library_name)
|
||||
context['authenticator'] = authenticator
|
||||
context['library'] = library = authenticator.library
|
||||
except Library.DoesNotExist:
|
||||
raise Http404
|
||||
context['works_active']= models.Work.objects.filter(acqs__user=library.user,acqs__license=LIBRARY).distinct()
|
||||
context['activetab'] = "#2"
|
||||
context['ungluers'] = userlists.library_users(library, 5 )
|
||||
return supporter(request,library_name,template_name='libraryauth/library.html', extra_context=context)
|
||||
|
||||
|
||||
|
||||
def edit_user(request):
|
||||
if not request.user.is_authenticated():
|
||||
return HttpResponseRedirect(reverse('superlogin'))
|
||||
|
@ -2633,6 +2623,33 @@ def download(request, work_id):
|
|||
})
|
||||
|
||||
return render(request, "download.html", context)
|
||||
|
||||
@login_required
|
||||
def borrow(request, work_id):
|
||||
work = safe_get_work(work_id)
|
||||
library = request.GET.get('library', '')
|
||||
try:
|
||||
libuser = User.objects.get(username = library)
|
||||
except User.DoesNotExist:
|
||||
libuser = None
|
||||
if libuser:
|
||||
acqs= models.Acq.objects.filter(user = libuser, license = LIBRARY, refreshes__lt = now())
|
||||
if not libuser or acqs.count()==0:
|
||||
acq=None
|
||||
for other_library in request.user.profile.libraries:
|
||||
if other_library.user!=libuser:
|
||||
acqs= models.Acq.objects.filter(user = other_library.user, license = LIBRARY, refreshes__lt = now())
|
||||
if acqs.count()>0:
|
||||
acq=acqs[0]
|
||||
continue
|
||||
else:
|
||||
acq=acqs[0]
|
||||
if acq:
|
||||
borrowed = acq.borrow(request.user)
|
||||
return download(request, work_id)
|
||||
else:
|
||||
# shouldn't happen
|
||||
return work(request, work_id)
|
||||
|
||||
def download_ebook(request, ebook_id):
|
||||
ebook = get_object_or_404(models.Ebook,id=ebook_id)
|
||||
|
@ -2647,6 +2664,8 @@ def download_purchased(request, work_id):
|
|||
|
||||
def download_acq(request, nonce, format):
|
||||
acq = get_object_or_404(models.Acq,nonce=nonce)
|
||||
if acq.on_reserve:
|
||||
acq.borrow()
|
||||
if format == 'epub':
|
||||
return HttpResponseRedirect( acq.get_epub_url() )
|
||||
else:
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
from . import models
|
||||
|
||||
from selectable.forms import AutoCompleteSelectWidget,AutoCompleteSelectField
|
||||
from selectable.base import ModelLookup
|
||||
from selectable.registry import registry
|
||||
|
||||
from django import forms
|
||||
from django.contrib.admin import ModelAdmin
|
||||
from django.contrib.auth.models import User, Group
|
||||
|
||||
class UserLookup(ModelLookup):
|
||||
model = User
|
||||
search_fields = ('username__icontains',)
|
||||
|
||||
registry.register(UserLookup)
|
||||
|
||||
class LibraryAdminForm(forms.ModelForm):
|
||||
user = AutoCompleteSelectField(
|
||||
UserLookup,
|
||||
widget=AutoCompleteSelectWidget(UserLookup),
|
||||
required=True,
|
||||
)
|
||||
class Meta(object):
|
||||
model = models.Library
|
||||
widgets= {'group':forms.HiddenInput}
|
||||
exclude = ('group', )
|
||||
|
||||
|
||||
class LibraryAdmin(ModelAdmin):
|
||||
list_display = ('user', )
|
||||
form = LibraryAdminForm
|
||||
search_fields = ['user__username']
|
||||
|
||||
class BlockAdmin(ModelAdmin):
|
||||
list_display = ('library', 'lower', 'upper',)
|
||||
search_fields = ('library__user__username', 'lower', 'upper',)
|
||||
|
||||
class CardPatternAdmin(ModelAdmin):
|
||||
list_display = ('library', 'pattern', 'checksum',)
|
||||
search_fields = ('library__user__username', )
|
||||
|
||||
class EmailPatternAdmin(ModelAdmin):
|
||||
list_display = ('library', 'pattern', )
|
||||
search_fields = ('library__user__username',)
|
|
@ -0,0 +1,100 @@
|
|||
'''
|
||||
to make a backend named <backend> you need to...
|
||||
1. define a function <backend>_authenticate(request, library)
|
||||
returns true if can request.user can be authenticated to the library, and attaches a credential property to the library object
|
||||
returns fals if otherwise.
|
||||
2. define a class <backend>_authenticator
|
||||
with a process((self, authenticator, success_url, deny_url) method which is expected to return a response
|
||||
3. make a libraryauth/<backend>_join.html template (authenticator will be in its context) to insert a link or form for a user to join the library
|
||||
4. if you need to show the user a form, define a model form class <backend>_form with init method __init__(self, request, library, *args, **kwargs)
|
||||
and model LibraryUser
|
||||
5. add new auth choice to Library.backend choices and the admin as desired
|
||||
|
||||
'''
|
||||
import logging
|
||||
from django.db.models import Q
|
||||
from django import forms
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.shortcuts import render
|
||||
|
||||
from .models import Block, IP, LibraryUser
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def ip_authenticate(request, library):
|
||||
try:
|
||||
ip = IP(request.META['REMOTE_ADDR'])
|
||||
blocks = Block.objects.filter(Q(lower=ip) | Q(lower__lte=ip, upper__gte=ip))
|
||||
for block in blocks:
|
||||
if block.library==library:
|
||||
logger.info('%s authenticated for %s from %s'%(request.user, library, ip))
|
||||
library.credential=ip
|
||||
return True
|
||||
return False
|
||||
except KeyError:
|
||||
return False
|
||||
|
||||
class ip_authenticator():
|
||||
def process(authenticator, success_url, deny_url):
|
||||
return HttpResponseRedirect(deny_url)
|
||||
|
||||
def cardnum_authenticate(request, library):
|
||||
return False
|
||||
|
||||
class cardnum_authenticator():
|
||||
def process(self, authenticator, success_url, deny_url):
|
||||
if authenticator.form and authenticator.request.method=='POST' and authenticator.form.is_valid():
|
||||
library = authenticator.form.cleaned_data['library']
|
||||
library.credential = authenticator.form.cleaned_data['credential']
|
||||
logger.info('%s authenticated for %s from %s'%(authenticator.request.user, authenticator.library, authenticator.form.cleaned_data.get('number')))
|
||||
library.add_user(authenticator.form.cleaned_data['user'])
|
||||
return HttpResponseRedirect(success_url)
|
||||
else:
|
||||
return render(authenticator.request, 'libraryauth/library.html', {
|
||||
'library':authenticator.library,
|
||||
'authenticator':authenticator,
|
||||
})
|
||||
|
||||
class cardnum_form(forms.ModelForm):
|
||||
credential = forms.RegexField(
|
||||
label="Enter Your Library Card Number",
|
||||
max_length=20,
|
||||
regex=r'^\d+$',
|
||||
required = True,
|
||||
help_text = "(digits only)",
|
||||
error_messages = {'invalid': "digits only!",}
|
||||
)
|
||||
def __init__(self, request, library, *args, **kwargs):
|
||||
if request.method=="POST":
|
||||
data=request.POST
|
||||
super(cardnum_form, self).__init__(data=data)
|
||||
else:
|
||||
initial={'user':request.user, 'library':library}
|
||||
super(cardnum_form, self).__init__(initial=initial)
|
||||
|
||||
def clean(self):
|
||||
library = self.cleaned_data.get('library', None)
|
||||
credential = self.cleaned_data.get('credential', '')
|
||||
for card_pattern in library.card_patterns.all():
|
||||
if card_pattern.is_valid(credential):
|
||||
return self.cleaned_data
|
||||
raise forms.ValidationError("the library card number must be VALID.")
|
||||
|
||||
class Meta:
|
||||
model = LibraryUser
|
||||
widgets = { 'library': forms.HiddenInput, 'user': forms.HiddenInput }
|
||||
|
||||
def email_authenticate(request, library):
|
||||
if request.user.is_anonymous():
|
||||
return False
|
||||
email = request.user.email
|
||||
for email_pattern in library.email_patterns.all():
|
||||
if email_pattern.is_valid(email):
|
||||
logger.info('%s authenticated for %s from %s'%(request.user, library, email))
|
||||
library.credential=email
|
||||
return True
|
||||
return False
|
||||
|
||||
class email_authenticator():
|
||||
def process(authenticator, success_url, deny_url):
|
||||
return HttpResponseRedirect(deny_url)
|
|
@ -0,0 +1,10 @@
|
|||
from django.contrib.auth.forms import AuthenticationForm
|
||||
|
||||
class AuthForm(AuthenticationForm):
|
||||
def __init__(self, request=None, *args, **kwargs):
|
||||
if request and request.method == 'GET':
|
||||
saved_un= request.COOKIES.get('un', None)
|
||||
super(AuthForm, self).__init__(initial={"username":saved_un},*args, **kwargs)
|
||||
else:
|
||||
super(AuthForm, self).__init__(*args, **kwargs)
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
# -*- 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 model 'Library'
|
||||
db.create_table('libraryauth_library', (
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('user', self.gf('django.db.models.fields.related.OneToOneField')(related_name='library', unique=True, to=orm['auth.User'])),
|
||||
('group', self.gf('django.db.models.fields.related.OneToOneField')(related_name='library', unique=True, null=True, to=orm['auth.Group'])),
|
||||
('backend', self.gf('django.db.models.fields.CharField')(default='ip', max_length=10)),
|
||||
))
|
||||
db.send_create_signal('libraryauth', ['Library'])
|
||||
|
||||
# Adding model 'Block'
|
||||
db.create_table('libraryauth_block', (
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('library', self.gf('django.db.models.fields.related.ForeignKey')(related_name='blocks', to=orm['libraryauth.Library'])),
|
||||
('lower', self.gf('regluit.libraryauth.models.IPAddressModelField')(unique=True, db_index=True)),
|
||||
('upper', self.gf('regluit.libraryauth.models.IPAddressModelField')(db_index=True, null=True, blank=True)),
|
||||
))
|
||||
db.send_create_signal('libraryauth', ['Block'])
|
||||
|
||||
# Adding model 'CardPattern'
|
||||
db.create_table('libraryauth_cardpattern', (
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('library', self.gf('django.db.models.fields.related.ForeignKey')(related_name='card_patterns', to=orm['libraryauth.Library'])),
|
||||
('pattern', self.gf('django.db.models.fields.CharField')(max_length=20)),
|
||||
('checksum', self.gf('django.db.models.fields.BooleanField')(default=True)),
|
||||
))
|
||||
db.send_create_signal('libraryauth', ['CardPattern'])
|
||||
|
||||
# Adding model 'LibraryUser'
|
||||
db.create_table('libraryauth_libraryuser', (
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('library', self.gf('django.db.models.fields.related.ForeignKey')(related_name='library_users', to=orm['libraryauth.Library'])),
|
||||
('user', self.gf('django.db.models.fields.related.ForeignKey')(related_name='user_libraries', to=orm['auth.User'])),
|
||||
('credential', self.gf('django.db.models.fields.CharField')(max_length=30, null=True)),
|
||||
('date_modified', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, blank=True)),
|
||||
))
|
||||
db.send_create_signal('libraryauth', ['LibraryUser'])
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Deleting model 'Library'
|
||||
db.delete_table('libraryauth_library')
|
||||
|
||||
# Deleting model 'Block'
|
||||
db.delete_table('libraryauth_block')
|
||||
|
||||
# Deleting model 'CardPattern'
|
||||
db.delete_table('libraryauth_cardpattern')
|
||||
|
||||
# Deleting model 'LibraryUser'
|
||||
db.delete_table('libraryauth_libraryuser')
|
||||
|
||||
|
||||
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'})
|
||||
},
|
||||
'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'})
|
||||
},
|
||||
'libraryauth.block': {
|
||||
'Meta': {'ordering': "['lower']", 'object_name': 'Block'},
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'library': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'blocks'", 'to': "orm['libraryauth.Library']"}),
|
||||
'lower': ('regluit.libraryauth.models.IPAddressModelField', [], {'unique': 'True', 'db_index': 'True'}),
|
||||
'upper': ('regluit.libraryauth.models.IPAddressModelField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'})
|
||||
},
|
||||
'libraryauth.cardpattern': {
|
||||
'Meta': {'object_name': 'CardPattern'},
|
||||
'checksum': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'library': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'card_patterns'", 'to': "orm['libraryauth.Library']"}),
|
||||
'pattern': ('django.db.models.fields.CharField', [], {'max_length': '20'})
|
||||
},
|
||||
'libraryauth.library': {
|
||||
'Meta': {'object_name': 'Library'},
|
||||
'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'}),
|
||||
'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'library'", 'unique': 'True', 'to': "orm['auth.User']"})
|
||||
},
|
||||
'libraryauth.libraryuser': {
|
||||
'Meta': {'object_name': 'LibraryUser'},
|
||||
'credential': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
|
||||
'date_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'library': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'library_users'", 'to': "orm['libraryauth.Library']"}),
|
||||
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_libraries'", 'to': "orm['auth.User']"})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['libraryauth']
|
|
@ -0,0 +1,99 @@
|
|||
# -*- 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 model 'EmailPattern'
|
||||
db.create_table('libraryauth_emailpattern', (
|
||||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
|
||||
('library', self.gf('django.db.models.fields.related.ForeignKey')(related_name='email_patterns', to=orm['libraryauth.Library'])),
|
||||
('pattern', self.gf('django.db.models.fields.CharField')(max_length=20)),
|
||||
))
|
||||
db.send_create_signal('libraryauth', ['EmailPattern'])
|
||||
|
||||
|
||||
def backwards(self, orm):
|
||||
# Deleting model 'EmailPattern'
|
||||
db.delete_table('libraryauth_emailpattern')
|
||||
|
||||
|
||||
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'})
|
||||
},
|
||||
'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'})
|
||||
},
|
||||
'libraryauth.block': {
|
||||
'Meta': {'ordering': "['lower']", 'object_name': 'Block'},
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'library': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'blocks'", 'to': "orm['libraryauth.Library']"}),
|
||||
'lower': ('regluit.libraryauth.models.IPAddressModelField', [], {'unique': 'True', 'db_index': 'True'}),
|
||||
'upper': ('regluit.libraryauth.models.IPAddressModelField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'})
|
||||
},
|
||||
'libraryauth.cardpattern': {
|
||||
'Meta': {'object_name': 'CardPattern'},
|
||||
'checksum': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'library': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'card_patterns'", 'to': "orm['libraryauth.Library']"}),
|
||||
'pattern': ('django.db.models.fields.CharField', [], {'max_length': '20'})
|
||||
},
|
||||
'libraryauth.emailpattern': {
|
||||
'Meta': {'object_name': 'EmailPattern'},
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'library': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'email_patterns'", 'to': "orm['libraryauth.Library']"}),
|
||||
'pattern': ('django.db.models.fields.CharField', [], {'max_length': '20'})
|
||||
},
|
||||
'libraryauth.library': {
|
||||
'Meta': {'object_name': 'Library'},
|
||||
'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'}),
|
||||
'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'library'", 'unique': 'True', 'to': "orm['auth.User']"})
|
||||
},
|
||||
'libraryauth.libraryuser': {
|
||||
'Meta': {'object_name': 'LibraryUser'},
|
||||
'credential': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True'}),
|
||||
'date_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
|
||||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
|
||||
'library': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'library_users'", 'to': "orm['libraryauth.Library']"}),
|
||||
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'user_libraries'", 'to': "orm['auth.User']"})
|
||||
}
|
||||
}
|
||||
|
||||
complete_apps = ['libraryauth']
|
|
@ -0,0 +1,285 @@
|
|||
# IP address part of this of this copied from https://github.com/benliles/django-ipauth/blob/master/ipauth/models.py
|
||||
|
||||
import re
|
||||
from django.contrib.auth.models import User, Group
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core import validators
|
||||
from django.db import models
|
||||
from django.db.models import Q
|
||||
from django.db.models.signals import post_save
|
||||
from django.forms import IPAddressField as BaseIPAddressField
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
class Library(models.Model):
|
||||
'''
|
||||
name and other things derive from the User
|
||||
'''
|
||||
user = models.OneToOneField(User, related_name='library')
|
||||
group = models.OneToOneField(Group, related_name='library', null = True)
|
||||
backend = models.CharField(max_length=10, choices=(
|
||||
('ip','IP authentication'),
|
||||
('cardnum', 'Library Card Number check'),
|
||||
('email', 'e-mail pattern check'),
|
||||
),default='ip')
|
||||
credential = None
|
||||
|
||||
def __unicode__(self):
|
||||
return self.user.username
|
||||
|
||||
def add_user(self, user):
|
||||
user.groups.add(self.group)
|
||||
(library_user, created) = LibraryUser.objects.get_or_create(library=self, user=user)
|
||||
library_user.credential=self.credential
|
||||
library_user.save()
|
||||
|
||||
def has_user(self, user):
|
||||
return self.group in user.groups.all() or user == self.user
|
||||
|
||||
@property
|
||||
def join_template(self):
|
||||
return 'libraryauth/' + self.backend + '_join.html'
|
||||
|
||||
def add_group(sender, created, instance, **kwargs):
|
||||
if created:
|
||||
num=''
|
||||
while created:
|
||||
(group,created)=Group.objects.get_or_create(name=instance.user.username + num)
|
||||
# make sure not using a group twice!
|
||||
if created:
|
||||
created = False
|
||||
else:
|
||||
num += '+'
|
||||
try:
|
||||
group.library
|
||||
created = True
|
||||
except Library.DoesNotExist:
|
||||
pass
|
||||
instance.group=group
|
||||
instance.save()
|
||||
|
||||
post_save.connect(add_group, sender=Library)
|
||||
|
||||
def ip_to_long(value):
|
||||
validators.validate_ipv4_address(value)
|
||||
|
||||
lower_validator = validators.MinValueValidator(0)
|
||||
upper_validator = validators.MinValueValidator(255)
|
||||
|
||||
value = value.split('.')
|
||||
output = 0
|
||||
|
||||
for i in range(0, 4):
|
||||
validators.validate_integer(value[i])
|
||||
lower_validator(value[i])
|
||||
upper_validator(value[i])
|
||||
output += int(value[i]) * (256**(3-i))
|
||||
|
||||
return output
|
||||
|
||||
def long_to_ip(value):
|
||||
validators.validate_integer(value)
|
||||
value = int(value)
|
||||
|
||||
validators.MinValueValidator(0)(value)
|
||||
validators.MaxValueValidator(4294967295)(value)
|
||||
|
||||
return '%d.%d.%d.%d' % (value >> 24, value >> 16 & 255,
|
||||
value >> 8 & 255, value & 255)
|
||||
|
||||
class IP(object):
|
||||
def __init__(self, value):
|
||||
self.int = value
|
||||
|
||||
def _set_int(self, value):
|
||||
if isinstance(value, IP):
|
||||
self._int = IP.int
|
||||
|
||||
try:
|
||||
self._int = int(value)
|
||||
except ValueError:
|
||||
self._int = ip_to_long(value)
|
||||
except (TypeError, ValidationError):
|
||||
self._int = None
|
||||
|
||||
def _get_int(self):
|
||||
return self._int
|
||||
|
||||
int = property(_get_int, _set_int)
|
||||
|
||||
def _get_str(self):
|
||||
if self.int!=None:
|
||||
return long_to_ip(self.int)
|
||||
return ''
|
||||
|
||||
string = property(_get_str, _set_int)
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, IP):
|
||||
try:
|
||||
other = IP(other)
|
||||
except:
|
||||
return False
|
||||
|
||||
return self.int == other.int
|
||||
|
||||
def __cmp__(self, other):
|
||||
if not isinstance(other, IP):
|
||||
other = IP(other)
|
||||
|
||||
if self.int and other.int:
|
||||
return self.int.__cmp__(other.int)
|
||||
|
||||
raise ValueError('Invalid arguments')
|
||||
|
||||
def __unicode__(self):
|
||||
return self.string
|
||||
|
||||
def __str__(self):
|
||||
return self.string
|
||||
|
||||
class IPAddressFormField(BaseIPAddressField):
|
||||
default_validators = []
|
||||
|
||||
def prepare_value(self, value):
|
||||
if isinstance(value, IP):
|
||||
return value.string
|
||||
|
||||
try:
|
||||
return IP(value).string
|
||||
except:
|
||||
pass
|
||||
|
||||
return value
|
||||
|
||||
def to_python(self, value):
|
||||
if value==0:
|
||||
return IP(0)
|
||||
if value in validators.EMPTY_VALUES:
|
||||
return None
|
||||
|
||||
try:
|
||||
return IP(value)
|
||||
except ValidationError:
|
||||
raise ValidationError(self.default_error_messages['invalid'],
|
||||
code='invalid')
|
||||
|
||||
class IPAddressModelField(models.IPAddressField):
|
||||
__metaclass__ = models.SubfieldBase
|
||||
empty_strings_allowed = False
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
models.Field.__init__(self, *args, **kwargs)
|
||||
|
||||
def get_internal_type(self):
|
||||
return "PositiveIntegerField"
|
||||
|
||||
def get_prep_value(self, value):
|
||||
if not value:
|
||||
return value
|
||||
|
||||
if isinstance(value, IP):
|
||||
return value.int
|
||||
|
||||
def to_python(self, value):
|
||||
if isinstance(value, IP):
|
||||
return value
|
||||
try:
|
||||
return IP(value)
|
||||
except ValidationError:
|
||||
return None
|
||||
|
||||
def formfield(self, **kwargs):
|
||||
defaults = {'form_class': IPAddressFormField}
|
||||
defaults.update(kwargs)
|
||||
return super(models.IPAddressField, self).formfield(**defaults)
|
||||
|
||||
class Block(models.Model):
|
||||
library = models.ForeignKey(Library, related_name='blocks')
|
||||
lower = IPAddressModelField(db_index=True, unique=True)
|
||||
upper = IPAddressModelField(db_index=True, blank=True, null=True)
|
||||
|
||||
def clean(self):
|
||||
if self.upper and self.upper.int:
|
||||
try:
|
||||
if self.lower > self.upper:
|
||||
raise ValidationError('Lower end of the Block must be less '
|
||||
'than or equal to the upper end')
|
||||
except ValueError, e:
|
||||
pass
|
||||
|
||||
others = Block.objects.exclude(pk=self.pk)
|
||||
query = Q(lower__lte=self.lower, upper__gte=self.lower) | \
|
||||
Q(lower=self.lower)
|
||||
if self.upper and self.upper.int:
|
||||
textual = u'%s-%s' % (self.lower, self.upper)
|
||||
query = query | Q(lower__range=(self.lower, self.upper)) | \
|
||||
Q(lower__lte=self.upper, upper__gte=self.upper)
|
||||
else:
|
||||
textual = str(self.lower)
|
||||
|
||||
query = others.filter(query)
|
||||
|
||||
if query.exists():
|
||||
values = query.distinct().values_list('library__user__username', flat=True)
|
||||
raise ValidationError('%s overlaps a block in in use by: %s' % (textual,
|
||||
', '.join(list(frozenset(values))[:5])))
|
||||
|
||||
|
||||
def __unicode__(self):
|
||||
if self.upper and self.upper.int:
|
||||
return u'%s %s-%s' % (self.library, self.lower, self.upper)
|
||||
return u'%s %s' % (self.library, self.lower)
|
||||
|
||||
class Meta:
|
||||
ordering = ['lower',]
|
||||
|
||||
|
||||
from south.modelsinspector import add_introspection_rules
|
||||
escaped_package= __name__.replace('.', '\.')
|
||||
add_introspection_rules([], ['^' + escaped_package + '\.IPAddressModelField'])
|
||||
|
||||
# from http://en.wikipedia.org/wiki/Luhn_algorithm#Implementation_of_standard_Mod_10
|
||||
def luhn_checksum(card_number):
|
||||
def digits_of(n):
|
||||
return [int(d) for d in str(n)]
|
||||
digits = digits_of(card_number)
|
||||
odd_digits = digits[-1::-2]
|
||||
even_digits = digits[-2::-2]
|
||||
checksum = 0
|
||||
checksum += sum(odd_digits)
|
||||
for d in even_digits:
|
||||
checksum += sum(digits_of(d*2))
|
||||
return checksum % 10
|
||||
|
||||
class CardPattern(models.Model):
|
||||
library = models.ForeignKey(Library, related_name='card_patterns')
|
||||
# match pattern ^\d+#+$
|
||||
pattern = models.CharField(max_length=20)
|
||||
checksum = models.BooleanField(default=True)
|
||||
|
||||
def is_valid(self, card_number):
|
||||
match_pattern='^' + self.pattern.replace('#','\d',20) + '$'
|
||||
if re.match(match_pattern,card_number) is None:
|
||||
return False
|
||||
if self.checksum:
|
||||
return luhn_checksum(card_number) == 0
|
||||
else:
|
||||
return True
|
||||
|
||||
class LibraryUser(models.Model):
|
||||
library = models.ForeignKey(Library, related_name='library_users')
|
||||
user = models.ForeignKey(User, related_name='user_libraries')
|
||||
credential = models.CharField(max_length=30, null=True)
|
||||
date_modified = models.DateTimeField(auto_now=True)
|
||||
|
||||
class EmailPattern(models.Model):
|
||||
library = models.ForeignKey(Library, related_name='email_patterns')
|
||||
# email endswith string
|
||||
pattern = models.CharField(max_length=20)
|
||||
|
||||
def is_valid(self, email):
|
||||
if email.lower().endswith(self.pattern.lower()):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<div class="p_form">To use {{ library }}...{{authenticator.form.non_field_errors}}<br />
|
||||
<form action="{% url join_library library %}#" method="POST" class="std_form " id="join_form">
|
||||
{% csrf_token %}
|
||||
{{ authenticator.form.credential.label_tag }}: {{ authenticator.form.credential }}
|
||||
{% if authenticator.form.credential.errors %}
|
||||
{{ authenticator.form.credential.errors }}
|
||||
{% else %}
|
||||
{{ authenticator.form.credential.help_text }}
|
||||
{% endif %}<br />
|
||||
<input type="submit" name="join" value="Make this my Library">
|
||||
{{ authenticator.form.library }}
|
||||
{{ authenticator.form.user }}
|
||||
</form>
|
||||
</div>
|
|
@ -0,0 +1 @@
|
|||
Denied authentication for {{ request.user }} at {{ request.META.REMOTE_ADDR }}
|
|
@ -0,0 +1,6 @@
|
|||
<br />
|
||||
{% if authenticator.allowed %}
|
||||
<a href="{% url join_library authenticator.library %}?next={% url join_library authenticator.library %}" class="fakeinput">Make this my Library</a>
|
||||
{% else %}
|
||||
Based on your account's email address, you can't join {{ authenticator.library }}. You can <a href="{% url email_change %}"> change your email address</a> if you need to.
|
||||
{% endif %}
|
|
@ -0,0 +1,6 @@
|
|||
<br />
|
||||
{% if authenticator.allowed %}
|
||||
<a href="{% url join_library authenticator.library %}?next={% url join_library authenticator.library %}" class="fakeinput">Make this my Library</a>
|
||||
{% else %}
|
||||
You can't join {{ authenticator.library }} at your current location.
|
||||
{% endif %}
|
|
@ -0,0 +1 @@
|
|||
{{library}}
|
|
@ -0,0 +1,2 @@
|
|||
{% extends "registration/from_pledge.html" %}
|
||||
{% block login_pitch %}<h3>Before you can join {{library}} on unglue.it, please login or make an unglue.it account. </h3>{% endblock %}
|
|
@ -0,0 +1,15 @@
|
|||
from django.conf.urls.defaults import *
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.views.generic.simple import direct_to_template
|
||||
from . import views, models
|
||||
|
||||
urlpatterns = patterns(
|
||||
"",
|
||||
url(r"^libraryauth/(?P<library>[^/]+)/join/$", views.join_library, name="join_library"),
|
||||
url(r"^libraryauth/(?P<library>[^/]+)/deny/$", direct_to_template, {'template':'libraryauth/denied.html'}, name="bad_library"),
|
||||
url(r"^libraryauth/list/$", direct_to_template, {
|
||||
'template':'libraryauth/list.html',
|
||||
'extra_context':{'libraries':models.Library.objects.order_by('user__username')}
|
||||
}, name="library_list"),
|
||||
url(r'^accounts/superlogin/$', views.superlogin, name='superlogin'),
|
||||
)
|
|
@ -0,0 +1,69 @@
|
|||
import logging
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.contrib.auth.views import login
|
||||
from django.http import HttpResponseRedirect
|
||||
from . import backends
|
||||
|
||||
from .models import Library
|
||||
from .forms import AuthForm
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def join_library(request, library):
|
||||
library=get_object_or_404(Library, user__username=library)
|
||||
return Authenticator(request,library).process(
|
||||
reverse('library',args=[str(library)]),
|
||||
reverse('bad_library',args=[str(library)]),
|
||||
)
|
||||
|
||||
def superlogin(request, extra_context=None, **kwargs):
|
||||
if request.method == 'POST' and request.user.is_anonymous():
|
||||
username=request.POST.get("username", "")
|
||||
try:
|
||||
user=models.User.objects.get(username=username)
|
||||
extra_context={"socials":user.profile.social_auths}
|
||||
except:
|
||||
pass
|
||||
if request.GET.has_key("add"):
|
||||
request.session["add_wishlist"]=request.GET["add"]
|
||||
return login(request, extra_context=extra_context, authentication_form=AuthForm, **kwargs)
|
||||
|
||||
class Authenticator:
|
||||
request=None
|
||||
library=None
|
||||
|
||||
def __init__(self, request, library, *args, **kwargs):
|
||||
self.request = request
|
||||
if isinstance(library , basestring):
|
||||
self.library = Library.objects.get(user__username=library)
|
||||
elif isinstance(library , Library):
|
||||
self.library=library
|
||||
else:
|
||||
raise Exception
|
||||
try:
|
||||
form_class = getattr(backends, self.library.backend + '_form')
|
||||
self.form = form_class(request, library, *args, **kwargs)
|
||||
except AttributeError:
|
||||
self.form = None
|
||||
|
||||
def process(self, success_url, deny_url):
|
||||
logger.info('authenticator for %s at %s.'%(self.request.user, self.library))
|
||||
if self.library.has_user(self.request.user):
|
||||
return HttpResponseRedirect(success_url)
|
||||
backend_test= getattr(backends, self.library.backend + '_authenticate')
|
||||
if backend_test(self.request, self.library):
|
||||
if self.request.user.is_authenticated():
|
||||
self.library.add_user(self.request.user)
|
||||
return HttpResponseRedirect(success_url)
|
||||
else:
|
||||
return superlogin(self.request, extra_context={'library':self.library}, template_name='libraryauth/library_login.html')
|
||||
|
||||
else:
|
||||
backend_authenticator= getattr(backends, self.library.backend + '_authenticator')
|
||||
return backend_authenticator().process(self, success_url, deny_url)
|
||||
|
||||
def allowed(self):
|
||||
backend_test= getattr(backends, self.library.backend + '_authenticate')
|
||||
return backend_test(self.request, self.library)
|
|
@ -150,7 +150,7 @@ INSTALLED_APPS = (
|
|||
'django.contrib.admin',
|
||||
'regluit.booxtream',
|
||||
'regluit.pyepub',
|
||||
|
||||
'regluit.libraryauth',
|
||||
)
|
||||
|
||||
# A sample logging configuration. The only tangible logging
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1 @@
|
|||
.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}.errors{-moz-border-radius:16px;-webkit-border-radius:16px;border-radius:16px;border:solid #e35351 3px;clear:both;width:90%;height:auto;line-height:16px;padding:7px 0;font-weight:bold;font-size:13px;text-align:center}.errors li{list-style:none;border:0}.items{clear:both;padding:5px;margin:0 5px 8px 0;width:95%}.items.row1{background:#f6f9f9}.items.row2{background:#fff}.items div{float:left}.items div img{margin:0 5px}.items .image img{height:100px}.items:after{content:".";display:block;height:0;clear:both;visibility:hidden}.items .nonavatar{width:620px;padding-top:5px}.items .nonavatar span{padding-right:5px}.items .nonavatar div.libname{width:100%}.items .nonavatar div.libname a{font-size:15px}.items .nonavatar div.libstat{width:25%;display:block}.items .avatar{float:left;margin:0 auto;padding-top:5px}.joined{border:3px #b8dde0 solid;margin-top:3px;margin-bottom:5px;padding-left:2px}.joined em{font-color:#b8dde0;font-style:italic}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -364,7 +364,9 @@ ul.support li {
|
|||
border-bottom:1px solid @blue-grey;
|
||||
padding:10px 5px 10px 10px;
|
||||
background:url("@{image-base}icons/pledgearrow.png") 98% center no-repeat;
|
||||
|
||||
&.no_link{
|
||||
background:none;
|
||||
}
|
||||
&.last {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
@ -396,6 +398,11 @@ ul.support li {
|
|||
color: #fff;
|
||||
text-decoration: none;
|
||||
}
|
||||
&.no_link{
|
||||
background:#fff;
|
||||
color:@call-to-action
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -285,11 +285,6 @@ div.pledge-container {
|
|||
font-weight: bold;
|
||||
}
|
||||
|
||||
.std_form, .std_form input, .std_form select {
|
||||
line-height: 30px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.call-to-action {
|
||||
color: @call-to-action;
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
@import "variables.less";
|
||||
|
||||
.items {
|
||||
clear: both;
|
||||
padding: 5px;
|
||||
margin: 0 5px 8px 0;
|
||||
//min-height: 105px;
|
||||
width: 95%;
|
||||
|
||||
&.row1 {
|
||||
background: #f6f9f9;
|
||||
}
|
||||
|
||||
&.row2 {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
div {
|
||||
float: left;
|
||||
|
||||
img {
|
||||
margin: 0 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.image img {
|
||||
height: 100px;
|
||||
}
|
||||
|
||||
// so div will stretch to height of content
|
||||
&:after {
|
||||
content: ".";
|
||||
display: block;
|
||||
height: 0;
|
||||
clear: both;
|
||||
visibility: hidden;
|
||||
}
|
||||
.nonavatar {
|
||||
width: 620px;
|
||||
padding-top: 5px;
|
||||
span {
|
||||
padding-right: 5px;
|
||||
}
|
||||
div.libname{
|
||||
width: 100%;
|
||||
a{
|
||||
font-size: @font-size-larger;
|
||||
}
|
||||
}
|
||||
div.libstat {
|
||||
width:25%;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.avatar {
|
||||
float: left;
|
||||
margin: 0 auto;
|
||||
padding-top: 5px;
|
||||
}
|
||||
}
|
||||
.joined {
|
||||
border: 3px #B8DDE0 solid;
|
||||
margin-top: 3px;
|
||||
margin-bottom: 5px;
|
||||
padding-left: 2px;
|
||||
em {
|
||||
font-color:#B8DDE0;
|
||||
font-style:italic;
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
.preview_campaign {
|
||||
float: right;
|
||||
margin-right: 10px;
|
||||
margin-right: 40px;
|
||||
}
|
||||
|
||||
input[name="launch"] {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
width: auto;
|
||||
}
|
||||
|
||||
input[type="submit"] {
|
||||
input[type="submit"], a.fakeinput {
|
||||
float: right;
|
||||
font-size: @font-size-header;
|
||||
margin: 10px 0 10px;
|
||||
|
@ -294,4 +294,4 @@ span.level2.menu.answer {
|
|||
float: left;
|
||||
width: 48%;
|
||||
padding: 1%;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -261,14 +261,13 @@ ul.menu{
|
|||
.p_form .errorlist {
|
||||
.one-border-radius(16px);
|
||||
border: none;
|
||||
color:@call-to-action;
|
||||
color:@alert;
|
||||
clear: none;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
line-height: 16px;
|
||||
padding: 0;
|
||||
font-weight: normal;
|
||||
font-size: 13px;
|
||||
text-align: left;
|
||||
display: inline;
|
||||
li {
|
||||
|
@ -980,4 +979,9 @@ li.checked {
|
|||
|
||||
.yes_js {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.std_form, .std_form input, .std_form select {
|
||||
line-height: 30px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
|
|
@ -87,6 +87,20 @@
|
|||
float:left;
|
||||
width:25%;
|
||||
}
|
||||
.user-block23 {
|
||||
color:@medium-blue;
|
||||
font-size: @font-size-larger;
|
||||
line-height:normal;
|
||||
float:left;
|
||||
width:50%;
|
||||
}
|
||||
.user-block24 {
|
||||
color:@medium-blue;
|
||||
font-size: @font-size-larger;
|
||||
line-height:normal;
|
||||
float:left;
|
||||
width:75%;
|
||||
}
|
||||
|
||||
.user-block3,
|
||||
.user-block4 {
|
||||
|
|
4
urls.py
4
urls.py
|
@ -4,7 +4,8 @@ from django.conf.urls.defaults import *
|
|||
from django.views.generic.simple import direct_to_template
|
||||
|
||||
from frontend.forms import ProfileForm
|
||||
from frontend.views import superlogin, social_auth_reset_password
|
||||
from frontend.views import social_auth_reset_password
|
||||
from libraryauth.views import superlogin
|
||||
from regluit.admin import admin_site
|
||||
from regluit.core.sitemaps import WorkSitemap, PublisherSitemap
|
||||
|
||||
|
@ -40,6 +41,7 @@ urlpatterns = patterns('',
|
|||
(r'^api/', include('regluit.api.urls')),
|
||||
(r'', include('regluit.frontend.urls')),
|
||||
(r'', include('regluit.payment.urls')),
|
||||
(r'', include('regluit.libraryauth.urls')),
|
||||
(r'^selectable/', include('selectable.urls')),
|
||||
url(r'^admin/', include(admin_site.urls)),
|
||||
(r'^comments/', include('django.contrib.comments.urls')),
|
||||
|
|
Loading…
Reference in New Issue