merge all the things

pull/1/head
Andromeda Yelton 2012-03-26 16:16:57 -04:00
commit 1a53a79752
28 changed files with 1238 additions and 213 deletions

View File

@ -1,8 +1,10 @@
from django.contrib.auth.models import User
from django.contrib.syndication.views import Feed
from django.shortcuts import get_object_or_404
from django.utils.feedgenerator import Atom1Feed
class SupporterWishlistFeed(Feed):
feed_type = Atom1Feed
def get_object(self, request, supporter):
return get_object_or_404(User, username=supporter)

View File

@ -60,7 +60,7 @@
"campaign": null,
"amount": 50,
"type": "00",
"description": "Your name and link of your choice under \"benefactors\"",
"description": "Your name and profile URL of your choice under \"benefactors\"",
"created": "2011-11-17 22:03:37"
}
},
@ -71,7 +71,7 @@
"campaign": null,
"amount": 100,
"type": "00",
"description": "Your name, link of your choice, and a brief message (140 characters max) under \"bibliophiles\"",
"description": "Your name, profile URL, and profile tagline under \"bibliophiles\"",
"created": "2011-11-17 22:03:37"
}
}

View File

@ -0,0 +1,222 @@
# encoding: 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 'Campaign.license'
db.add_column('core_campaign', 'license', self.gf('django.db.models.fields.CharField')(default='CC BY-NC-ND', max_length=255), keep_default=False)
# Changing field 'Ebook.url'
db.alter_column('core_ebook', 'url', self.gf('django.db.models.fields.URLField')(max_length=1024))
def backwards(self, orm):
# Deleting field 'Campaign.license'
db.delete_column('core_campaign', 'license')
# Changing field 'Ebook.url'
db.alter_column('core_ebook', 'url', self.gf('django.db.models.fields.CharField')(max_length=1024))
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'})
},
'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.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'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'deadline': ('django.db.models.fields.DateTimeField', [], {}),
'description': ('django.db.models.fields.TextField', [], {'null': 'True'}),
'details': ('django.db.models.fields.TextField', [], {'null': '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'}),
'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'}),
'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(2012, 3, 23, 12, 0, 53, 181728)', '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'}),
'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'}),
'url': ('django.db.models.fields.URLField', [], {'max_length': '1024'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'})
},
'core.edition': {
'Meta': {'object_name': 'Edition'},
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'null': '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'}),
'publisher': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '1000'}),
'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.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'}),
'type': ('django.db.models.fields.CharField', [], {'max_length': '2'})
},
'core.rightsholder': {
'Meta': {'object_name': 'RightsHolder'},
'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', 'blank': 'True'})
},
'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'},
'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'}),
'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'}),
'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'}),
'openlibrary_lookup': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '1000'})
}
}
complete_apps = ['core']

View File

@ -0,0 +1,217 @@
# encoding: 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 'Premium.limit'
db.add_column('core_premium', 'limit', self.gf('django.db.models.fields.IntegerField')(default=0), keep_default=False)
def backwards(self, orm):
# Deleting field 'Premium.limit'
db.delete_column('core_premium', 'limit')
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'})
},
'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.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'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'deadline': ('django.db.models.fields.DateTimeField', [], {}),
'description': ('django.db.models.fields.TextField', [], {'null': 'True'}),
'details': ('django.db.models.fields.TextField', [], {'null': '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'}),
'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'}),
'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(2012, 3, 26, 14, 33, 44, 667768)', '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'}),
'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'}),
'url': ('django.db.models.fields.URLField', [], {'max_length': '1024'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'})
},
'core.edition': {
'Meta': {'object_name': 'Edition'},
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'null': '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'}),
'publisher': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '1000'}),
'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.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.rightsholder': {
'Meta': {'object_name': 'RightsHolder'},
'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', 'blank': 'True'})
},
'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'},
'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'}),
'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'}),
'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'}),
'openlibrary_lookup': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '1000'})
}
}
complete_apps = ['core']

View File

@ -66,12 +66,13 @@ class RightsHolder(models.Model):
return self.rights_holder_name
class Premium(models.Model):
PREMIUM_TYPES = ((u'00', u'Default'),(u'CU', u'Custom'))
PREMIUM_TYPES = ((u'00', u'Default'),(u'CU', u'Custom'),(u'XX', u'Inactive'))
created = models.DateTimeField(auto_now_add=True)
type = models.CharField(max_length=2, choices=PREMIUM_TYPES)
campaign = models.ForeignKey("Campaign", related_name="premiums", null=True)
amount = models.DecimalField(max_digits=10, decimal_places=0, blank=False)
description = models.TextField(null=True, blank=False)
limit = models.IntegerField(default = 0)
class CampaignAction(models.Model):
timestamp = models.DateTimeField(auto_now_add=True)
@ -81,11 +82,20 @@ class CampaignAction(models.Model):
campaign = models.ForeignKey("Campaign", related_name="actions", null=False)
class Campaign(models.Model):
LICENSE_CHOICES = (('CC BY-NC-ND','CC BY-NC-ND'),
('CC BY-ND','CC BY-ND'),
('CC BY','CC BY'),
('CC BY-NC','CC BY-NC'),
( 'CC BY-NC-SA','CC BY-NC-SA'),
( 'CC BY-SA','CC BY-SA'),
( 'CC0','CC0'),
)
created = models.DateTimeField(auto_now_add=True)
name = models.CharField(max_length=500, null=True, blank=False)
description = models.TextField(null=True, blank=False)
details = models.TextField(null=True, blank=False)
target = models.DecimalField(max_digits=14, decimal_places=2, null=True, blank=False)
license = models.CharField(max_length=255, choices = LICENSE_CHOICES, default='CC BY-NC-ND')
left = models.DecimalField(max_digits=14, decimal_places=2, null=True, blank=False)
deadline = models.DateTimeField()
activated = models.DateTimeField(null=True)

View File

@ -14,7 +14,7 @@ from selectable.forms import AutoCompleteSelectWidget,AutoCompleteSelectField
from regluit.core.models import UserProfile, RightsHolder, Claim, Campaign, Premium, Ebook
from regluit.core.lookups import OwnerLookup
from regluit.utils.localdatetime import date_today
from regluit.utils.localdatetime import now
import logging
@ -131,6 +131,18 @@ class OpenCampaignForm(forms.ModelForm):
fields = 'name', 'work', 'managers'
widgets = { 'work': forms.HiddenInput }
class CustomPremiumForm(forms.ModelForm):
class Meta:
model = Premium
fields = 'campaign', 'amount', 'description', 'type', 'limit'
widgets = {
'description': forms.Textarea(attrs={'cols': 80, 'rows': 2}),
'campaign': forms.HiddenInput,
'type': forms.HiddenInput(attrs={'value':'XX'}),
'limit': forms.TextInput(attrs={'value':'0'}),
}
class ManageCampaignForm(forms.ModelForm):
paypal_receiver = forms.EmailField(
label=_("email address to collect Paypal funds"),
@ -139,7 +151,7 @@ class ManageCampaignForm(forms.ModelForm):
target = forms.DecimalField( min_value= D('0.00') )
class Meta:
model = Campaign
fields = 'description', 'details', 'target', 'deadline', 'paypal_receiver'
fields = 'description', 'details', 'license', 'target', 'deadline', 'paypal_receiver'
widgets = {
'description': forms.Textarea(attrs={'cols': 80, 'rows': 20}),
'details': forms.Textarea(attrs={'cols': 80, 'rows': 20}),
@ -160,11 +172,19 @@ class ManageCampaignForm(forms.ModelForm):
if self.instance:
if self.instance.status == 'ACTIVE' and self.instance.deadline != new_deadline:
raise forms.ValidationError(_('The closing date for an ACTIVE campaign cannot be changed.'))
if new_deadline-date_today() > timedelta(days=int(settings.UNGLUEIT_LONGEST_DEADLINE)):
if new_deadline - now() > timedelta(days=int(settings.UNGLUEIT_LONGEST_DEADLINE)):
raise forms.ValidationError(_('The chosen closing date is more than %s days from now' % settings.UNGLUEIT_LONGEST_DEADLINE))
elif new_deadline-date_today() < timedelta(days=int(settings.UNGLUEIT_SHORTEST_DEADLINE)):
elif new_deadline - now() < timedelta(days=int(settings.UNGLUEIT_SHORTEST_DEADLINE)):
raise forms.ValidationError(_('The chosen closing date is less than %s days from now' % settings.UNGLUEIT_SHORTEST_DEADLINE))
return new_deadline
def clean_license(self):
new_license = self.cleaned_data['license']
if self.instance:
if self.instance.status == 'ACTIVE':
raise forms.ValidationError(_('The license for an ACTIVE campaign cannot be changed.'))
return new_license
class CampaignPledgeForm(forms.Form):
preapproval_amount = forms.DecimalField(
@ -183,6 +203,10 @@ class CampaignPledgeForm(forms.Form):
if data is None:
raise forms.ValidationError(_("Please enter a pledge amount."))
return data
# should we do validation on the premium_id here?
# can see whether it corresponds to a real premium -- do that here?
# can also figure out moreover whether it's one of the allowed premiums for that campaign....
def clean(self):
cleaned_data = self.cleaned_data
@ -193,6 +217,7 @@ class CampaignPledgeForm(forms.Form):
premium_amount = Premium.objects.get(id=premium_id).amount
logger.info("preapproval_amount: {0}, premium_id: {1}, premium_amount:{2}".format(preapproval_amount, premium_id, premium_amount))
if preapproval_amount < premium_amount:
logger.info("raising form validating error")
raise forms.ValidationError(_("Sorry, you must pledge at least $%s to select that premium." % (premium_amount)))
except Exception, e:
if isinstance(e, forms.ValidationError):

View File

@ -1,8 +1,9 @@
{% extends "basedocumentation.html" %}
{% block base_js %}{% endblock %}
{% block extra_extra_head %}
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.13/themes/ui-lightness/jquery-ui.css" type="text/css" media="screen">
{{ form.media.css }}
<script type="text/javascript" src="{{ jquery_home }}"></script>
<script type="text/javascript" src="{{ jquery_ui_home }}"></script>
{{ form.media.js }}
{% endblock %}
@ -21,7 +22,7 @@
{% if campaigns_with_active_transactions %}
<form method="post" action="#">
{% csrf_token %}
<ul>
<ul class="terms">
{% for campaign in campaigns_with_active_transactions %}
<li><input type="radio" name="active_campaign" value="{{campaign.id}}" />{{campaign.id}} | {{campaign.name}} </li>
{% endfor %}

View File

@ -1,3 +1,3 @@
<h3> Terms and Conditions for Claiming Works </h3>
<pre>[legal stuff]</pre>
<p>By claiming this work, you agree that your claim is governed by a Platform Services Agreement in effect between you (or an entity that you act as agent for) and Gluejar, Inc., the operator of the Unglue.it website, and by the <a href="{% url terms %}">unglue.it website terms and conditions</a>.</p>

View File

@ -243,7 +243,7 @@ If you want to find an interesting campaign and don't have a specific book in mi
<dt>Are premiums required?</dt>
<dd>Yes. All campaigns have a required set of premiums, as follows:<br /><br />
<ul>
<ul class="terms">
<li>$1 // The unglued ebook delivered to your inbox.</li>
<li>$25 // Your name under "supporters" in the acknowledgements section.</li>
<li>$50 // Your name and link of your choice under "benefactors"</li>

View File

@ -23,6 +23,7 @@
</script>
<script type="text/javascript" src="/static/js/definitions.js"></script>
<script type="text/javascript" src="/static/js/wishlist.js"></script>
<script type="text/javascript" src="/static/js/greenpanel.js"></script>
<script type="text/javascript" src="/static/js/highlight_signup.js"></script>
<script src="/static/js/slides.min.jquery.js"></script>

View File

@ -34,7 +34,7 @@ Please fix the following before launching your campaign:
<form action="#" method="POST">
{% csrf_token %}
<p>This will be displayed in the Campaign tab for your work. It's your main pitch to supporters. It should include:</p>
<ul>
<ul class="terms">
<li>A synopsis of the work.</li>
<li>Hyperlinks for the author(s), publisher making the offer, or for the work itself.</li>
<li>Anything especially appealing about the work or author: awards, embedded video (460px max), etc.</li>
@ -42,7 +42,7 @@ Please fix the following before launching your campaign:
{{ form.description }}{{ form.description.errors }}
<h3>Offer details</h2>
<p>This will be displayed on the Details tab for your work. It gives additional information for the highly curious. It should include:</p>
<ul>
<ul class="terms">
<li>Details about the edition being offered.</li>
<li>Hyperlinks for the author(s), publisher making the offer, or for the work itself.</li>
</uL>
@ -50,6 +50,9 @@ Please fix the following before launching your campaign:
<h3>Target Price</h2>
<p> This is the target price for your campaign. Once you launch the campaign, you won't be able to increase it.</p>
{{ form.target }}{{ form.target.errors }}
<h3>License being offered</h2>
<p> This is the license you are offering to use once the campaign succeeds. For more info on the licenses you can use, see <a href="http://creativecommons.org/">Creative Commons</a></p>
{{ form.license }}{{ form.license.errors }}
<h3>Ending date</h2>
<p> This is the ending date of your campaign. Once you launch the campaign, you won't be able to change it.
The ending date can't be more than six months away- that's a practical limit for credit card authorizations.</p>
@ -65,5 +68,37 @@ The ending date can't be more than six months away- that's a practical limit for
<input type="submit" name="launch" value="Launch Campaign" />
</form>
<h3>Premiums</h3>
<div class="jsmod-content">
<form action="#" method="POST">
{% csrf_token %}
<ul class="support menu">
{% for premium in premiums %}
<li class="{% if forloop.first %}first{% else %}{% if forloop.last %}last{% endif %}{% endif %}">
<a href="{% url pledge work_id=campaign.work.id %}?premium_id={{premium.id}}">
<span class="menu-item-price">${{ premium.amount }}</span>
<span class="menu-item-desc">{{ premium.description }}</span>
</a>
{% if premium.type %}<span class="custom-premium"> <br />Type: {{ premium.get_type_display }}</span>{% endif %}
{% ifnotequal premium.limit 0 %}<br />Limit: {{ premium.limit }}{% endifnotequal %}
{% ifequal premium.type 'CU' %}<br />Deactivate? <input type="checkbox" name="premium_id" value="{{ premium.id }}" />{% endifequal %}
</li>
{% endfor %}
</ul>
<input type="submit" name="inactivate" value="Inactivate Checked Premiums" />
</form>
</div>
<h4>Add a custom premium for this campaign</h4>
<form action="#" method="POST">
{% csrf_token %}
Pledge Amount: {{ premium_form.amount }}{{ premium_form.amount.errors }}<br />
Premium Description: {{ premium_form.description }}{{ premium_form.description.errors }}<br />
Number Available (0 if no limit): {{ premium_form.limit }}{{ premium_form.limit.errors }}<br />
{{ premium_form.campaign }}
{{ premium_form.type }}{{ premium_form.type.errors }}
<br />
<input type="submit" name="add_premium" value="Add Premium" />
</form>
{% endif %}
{% endblock %}

View File

@ -6,9 +6,9 @@
<dl>
<dt> How many ungluers have registered?</dt>
<dd>
<ul>
<ul class="terms">
<li>{{ users.today.count }} have registered today. {% if users.today.count %}They are
<ul>{% for user in users.today %}
<ul class="terms">{% for user in users.today %}
<li><a href="{% url supporter user.username %}">{{user.username}}</a></li>
{% endfor %}</ul>{% endif %}
<br />{{ wishlists.today.count }} of them have 1 or more items on a wishlist.
@ -26,7 +26,7 @@
<br />{{ wishlists.all.count }} of them have 1 or more items on a wishlist.
</li>
</ul>
<ul>
<ul class="terms">
<li> {{ users.gr.count }} ungluers are connected to GoodReads.</li>
<li> {{ users.lt.count }} ungluers are connected to LibraryThing.</li>
<li> {{ users.fb.count }} ungluers are connected to FaceBook.</li>
@ -38,9 +38,9 @@
<dl>
<dt> How many works have been added to Unglue.it?</dt>
<dd>
<ul>
<ul class="terms">
<li>{{ works.today.count }} have been added today. {% if works.today.count %}They are
<ul>{% for work in works.today %}
<ul class="terms">{% for work in works.today %}
<li><a href="{% url work work.id %}">{{work.title}}</a></li>
{% endfor %}</ul>{% endif %}
</li>
@ -56,7 +56,7 @@
</dd>
<dt> How often have the works been wished?</dt>
<dd>
<ul>
<ul class="terms">
<li>{{ works.wishedby50.count }} have been wished by more than 50 ungluers.
</li>
<li>{{ works.wishedby20.count }} have been wished by more than 20 ungluers.
@ -74,9 +74,9 @@
<dl>
<dt> How many ebooks have been added to Unglue.it?</dt>
<dd>
<ul>
<ul class="terms">
<li>{{ ebooks.today.count }} have been added today. {% if ebooks.today.count %}They are
<ul>{% for ebook in ebooks.today %}
<ul class="terms">{% for ebook in ebooks.today %}
<li><a href="{% url work ebook.edition.work.id %}">{{ebook.edition.work.title}}</a></li>
{% endfor %}</ul>{% endif %}
</li>

View File

@ -78,7 +78,7 @@
{% for premium in premiums %}
<label for="{{premium.id}}">
<li class="{% if forloop.first %}first{% else %}{% if forloop.last %}last{% endif %}{% endif %}">
<input type="radio" name="premium_id" value="{{premium.id}}" id="{{premium.id}}" {% ifequal request.REQUEST.premium_id premium.id|stringformat:"s" %}checked="checked"{% endifequal %}" />
<input type="radio" name="premium_id" value="{{premium.id}}" {% ifequal request.REQUEST.premium_id premium.id|stringformat:"s" %}checked="checked"{% endifequal %} />
<span class="menu-item-price">
${{ premium.amount }}
</span>

View File

@ -0,0 +1,99 @@
{% extends "basepledge.html" %}
{% block title %}Pledge (Modify){% endblock %}
{% block extra_extra_head %}
<link type="text/css" rel="stylesheet" href="/static/css/campaign.css" />
<link type="text/css" rel="stylesheet" href="/static/css/pledge.css" />
{% endblock %}
{% block doccontent %}
<div style="height:10px";></div>
<div class="book-detail">
<div class="book-detail-img">
<a href="#"><img src="{{ work.cover_image_thumbnail }}" alt="{{ work.title }}" title="{{ work.title }}" width="131" height="192" /></a>
</div>
<div class="book-detail-info">
<h2 class="book-name">{{ work.title }}</h2>
<h3 class="book-author">{{ work.author }}</h3>
<h3 class="book-year">{{ work.publication_date }}</h3>
<div class="find-book">
<label>Find it here</label>
<div class="find-link">
<a class="find-google" href="{{ work.googlebooks_url }}"><img src="/static/images/supporter_icons/googlebooks_square.png" title="Find on Google Books" alt="Find on Google Books" /></a>
<a rel="nofollow" class="find-openlibrary" href="{% url work_openlibrary work.id %}"><img src="/static/images/supporter_icons/openlibrary_square.png" title="Find on OpenLibrary" alt="Find on OpenLibrary"></a>
{% if not request.user.is_anonymous %}
{% if request.user.profile.goodreads_user_link %}
<a rel="nofollow" class="find-goodreads" href="{% url work_goodreads work.id %}"><img src="/static/images/supporter_icons/goodreads_square.png" title="Find on GoodReads" alt="Find on GoodReads"></a>
{% endif %}
{% if request.user.profile.librarything_id %}
<a rel="nofollow" class="find-librarything" href="{% url work_librarything work.id %}"><img src="/static/images/supporter_icons/librarything_square.png" title="Find on LibraryThing" alt="Find on LibraryThing" /></a>
{% endif %}
{% endif %}
</div>
</div>
<div class="pledged-info">
<div class="pledged-group">
{{ work.last_campaign.supporters.count }} Ungluers have pledged ${{ work.last_campaign.current_total }}
</div>
<div class="status">
<img src="/static/images/images/icon-book-37by25-{{ work.percent_unglued }}.png" title="book list status" alt="book list status" />
</div>
</div>
</div>
</div>
<div class="jsmodule rounded pledge">
<div class="jsmod-content">
${{ work.last_campaign.target }} needed by<br />
{{ work.last_campaign.deadline }}
</div>
</div>
<div class="jsmodule rounded">
<div class="jsmod-content">
<p>You modifying your current pledge.</p>
{% comment %}
Even there is a CampaignPledgeForm in frontend/forms.py , the "widget" for premium_id is implemented in HTML here for now.
{% endcomment %}
<form method="POST" action="{% url pledge_modify work_id=work.id %}">
{% csrf_token %}
{{ form.non_field_errors }}
{{ form.preapproval_amount.errors }}
<div class="pledge_amount">{{ form.preapproval_amount.label_tag }}: ${{ form.preapproval_amount }}</div>
{{ form.anonymous.errors }}
{% comment %}
not supported yet; don't display
{{ form.anonymous.label_tag }}: {{ form.anonymous }}
{% endcomment %}
<ul class="support menu">
{% for premium in premiums %}
<label for="{{premium.id}}">
<li class="{% if forloop.first %}first{% else %}{% if forloop.last %}last{% endif %}{% endif %}">
<input type="radio" name="premium_id" value="{{premium.id}}" {% ifequal form.premium_id.value premium.id %}checked="checked"{% endifequal %} />
<span class="menu-item-price">
${{ premium.amount }}
</span>
<span class="menu-item-desc">
{{ premium.description }}
</span>
</a></li></label>
{% endfor %}
</ul>
<input type="submit" value="Modify Pledge" id="pledgesubmit"/>
</form>
</div>
{% endblock %}

View File

@ -3,6 +3,7 @@
{% block extra_extra_head %}
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.13/themes/ui-lightness/jquery-ui.css" type="text/css" media="screen">
<link href="/static/css/dj.selectable.css" type="text/css" media="all" rel="stylesheet" />
<script type="text/javascript" src="{{ jquery_home }}"></script>
<script type="text/javascript" src="{{ jquery_ui_home }}"></script>
<script type="text/javascript" src="/static/js/jquery.dj.selectable.js"></script>
@ -118,13 +119,13 @@ Needs to be written. What would you find helpful in a social media toolkit? <a
<p>Campaigns have rewards as a way to thank supporters. unglue.it includes a standard set of rewards in all campaigns. You are encouraged to add additional sweeteners to motivate people to donate.</p>
<p>Here are the standard rewards:</p>
<ul>
<ul class="terms">
<li><em>Any level</em> &#8212; The unglued ebook delivered to your inbox</li>
<li><em>$25</em> &#8212; Your name under "supporters" in the acknowledgements section</li>
<li><em>$50</em> &#8212; Your name &amp; link of your choice under "benefactors"</li>
<li><em>$100</em> &#8212; Your name, link of your choice, &amp; a brief message (140 characters max) under "bibliophiles"</li>
<li><em>$25</em> &#8212; Your username under "supporters" in the acknowledgements section</li>
<li><em>$50</em> &#8212; Your name &amp; profile link under "benefactors"</li>
<li><em>$100</em> &#8212; Your name, profile link, &amp; profile tagline under "bibliophiles"</li>
</ul>
<h2>More Questions</h2>
<p> Check the FAQ to the left, or <a href="/feedback">Send us feedback.</a>
{% endblock %}

View File

@ -1,8 +1,9 @@
{% extends "basedocumentation.html" %}
{% block base_js %}{% endblock %}
{% block extra_extra_head %}
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.13/themes/ui-lightness/jquery-ui.css" type="text/css" media="screen">
{{ form.media.css }}
<script type="text/javascript" src="{{ jquery_home }}"></script>
<script type="text/javascript" src="{{ jquery_ui_home }}" ></script>
{{ form.media.js }}
{% endblock %}

View File

@ -1,35 +0,0 @@
{% extends "basedocumentation.html" %}
{% block title %}Campaign Setup{% endblock %}
{% block doccontent %}
<h2>Set up campaign</h2>
<div class="book-detail-info">
<h2 class="book-name">Title: {{ work.title }}</h2>
<h3 class="book-author">Authors: {{ work.author }}</h3>
<h3 class="book-year">Published: {{ work.publication_date }}</h3>
<h3 class="book-author">Language: {{ work.editions.all.0.language }}</h3>
</div>
<form action="#">
<h3>Description of the work to be offered</h3>
This should include:
<ul>
<li>A synopsis of the work.
<li>Hyperlinks for the author(s), publisher making the offer, or for the work itself.
</ul>
<textarea cols="80" rows="20" name="description">
</textarea>
<h3>Offer details</h3>
This should include:
<ul>
<li>Details about the edition being offered.
<li>Hyperlinks for the author(s), publisher making the offer, or for the work itself.
</uL>
<textarea cols="80" rows="20" name="details">
</textarea>
</ul>
</form>
{% endblock %}

View File

@ -60,6 +60,9 @@ function highlightTarget(targetdiv) {
};
</script>
{% endblock %}
{% block extra_head %}
<link rel="alternate" type="application/atom+xml" title="feed for books from {{ supporter }}'s ungluing wishlist" href="feed" />
{% endblock %}
{% comment %}
To do:
@ -259,7 +262,7 @@ there's no tab for seeing ALL my books, only the filters! huh.
<li class="tabs3"><a href="#">Wishlisted</a></li>
</ul>
<span id="rss"><a href="feed" title="RSS feed of {{ supporter }}'s latest wishbooks" ><img src="/static/images/feedicons-standard/feed-icon-14x14.png" alt="RSS feed of {{ supporter }}'s latest wishbooks" /><span>Subscribe</span></a></span>
{% if not works %}
{% comment %}

View File

@ -101,7 +101,7 @@ $j(document).ready(function(){
</div>
{% if status == 'ACTIVE' %}
{% if pledged %}
<div class="btn_support modify"><form action="/stub/modify_pledge" method="get"><input type="submit" value="Change Pledge"/></form></div>
<div class="btn_support modify"><form action="{% url pledge_modify work_id=work.id %}" method="get"><input type="submit" value="Change Pledge"/></form></div>
{% else %}
<div class="btn_support"><form action="{% url pledge work_id=work.id %}" method="get"><input type="submit" value="Support"/></form></div>
{% endif %}
@ -197,7 +197,7 @@ $j(document).ready(function(){
{{ claim.rights_holder.rights_holder_name }}
{% endif %}
{% endfor %}
, has agreed to release <i>{{work.title}}</i> to the world as a Creative Commons licensed ebook if ungluers can join together to raise ${{ work.last_campaign.target }} by {{ work.last_campaign.deadline }}.
, has agreed to release <i>{{work.title}}</i> to the world as a Creative Commons licensed ebook ({{ work.last_campaign.license }}) if ungluers can join together to raise ${{ work.last_campaign.target }} by {{ work.last_campaign.deadline }}.
You can help!</p>
{{ work.last_campaign.description|safe }}
{% else %}
@ -355,7 +355,6 @@ $j(document).ready(function(){
<a href="{% url pledge work_id=work.id %}?premium_id={{premium.id}}">
<span class="menu-item-price">${{ premium.amount }}</span>
<span class="menu-item-desc">{{ premium.description }}</span>
{% ifequal premium.type 'CU' %}<span class="custom-premium">exclusive!</span>{% endifequal %}
</a></li>
{% endfor %}
</ul>

View File

@ -7,7 +7,7 @@ from django.conf import settings
from regluit.core.feeds import SupporterWishlistFeed
from regluit.core.models import Campaign
from regluit.frontend.views import CampaignFormView, GoodreadsDisplayView, LibraryThingView, PledgeView, PledgeCompleteView, PledgeCancelView, FAQView
from regluit.frontend.views import CampaignFormView, GoodreadsDisplayView, LibraryThingView, PledgeView, PledgeCompleteView, PledgeModifyView, PledgeCancelView, FAQView
from regluit.frontend.views import CampaignListView, DonateView, WorkListView, UngluedListView, InfoPageView
urlpatterns = patterns(
@ -44,11 +44,10 @@ urlpatterns = patterns(
url(r"^work/(?P<work_id>\d+)/goodreads/$", "work_goodreads", name="work_goodreads"),
url(r"^work/(?P<work_id>\d+)/openlibrary/$", "work_openlibrary", name="work_openlibrary"),
url(r"^googlebooks/(?P<googlebooks_id>.+)/$", "googlebooks", name="googlebooks"),
#may want to deprecate the following
url(r"^setup/work/(?P<work_id>\d+)/$", "work", {'action':'setup_campaign'}, name="setup_campaign"),
url(r"^pledge/(?P<work_id>\d+)/$", login_required(PledgeView.as_view()), name="pledge"),
url(r"^pledge/cancel/$", login_required(PledgeCancelView.as_view()), name="pledge_cancel"),
url(r"^pledge/complete/$", login_required(PledgeCompleteView.as_view()), name="pledge_complete"),
url(r"^pledge/modify/(?P<work_id>\d+)$", login_required(PledgeModifyView.as_view()), name="pledge_modify"),
url(r"^subjects/$", "subjects", name="subjects"),
url(r"^librarything/$", LibraryThingView.as_view(), name="librarything"),
url(r"^librarything/load/$","librarything_load", name="librarything_load"),

View File

@ -45,11 +45,11 @@ from regluit.core.goodreads import GoodreadsClient
from regluit.frontend.forms import UserData, ProfileForm, CampaignPledgeForm, GoodreadsShelfLoadingForm
from regluit.frontend.forms import RightsHolderForm, UserClaimForm, LibraryThingForm, OpenCampaignForm
from regluit.frontend.forms import ManageCampaignForm, DonateForm, CampaignAdminForm, EmailShareForm, FeedbackForm
from regluit.frontend.forms import EbookForm
from regluit.frontend.forms import EbookForm, CustomPremiumForm
from regluit.payment.manager import PaymentManager
from regluit.payment.models import Transaction
from regluit.payment.parameters import TARGET_TYPE_CAMPAIGN, TARGET_TYPE_DONATION, PAYMENT_TYPE_AUTHORIZATION
from regluit.payment.paypal import Preapproval, IPN_PAY_STATUS_NONE, IPN_PAY_STATUS_ACTIVE, IPN_PAY_STATUS_INCOMPLETE, IPN_PAY_STATUS_COMPLETED, IPN_PAY_STATUS_CANCELED, IPN_TYPE_PREAPPROVAL
from regluit.payment.paypal import Preapproval, IPN_PAY_STATUS_NONE, IPN_PREAPPROVAL_STATUS_ACTIVE, IPN_PAY_STATUS_INCOMPLETE, IPN_PAY_STATUS_COMPLETED, IPN_PREAPPROVAL_STATUS_CANCELED, IPN_TYPE_PREAPPROVAL
from regluit.core import goodreads
from tastypie.models import ApiKey
from regluit.payment.models import Transaction
@ -67,32 +67,32 @@ def slideshow(max):
# on the preview site there are no active campaigns, so we should show most-wished books instead
worklist = models.Work.objects.order_by('-num_wishes')[:max]
else:
worklist = []
if max > count:
# add all the works with active campaigns
for campaign in ending:
worklist.append(campaign.work)
# then fill out the rest of the list with popular but inactive works
remainder = max - count
remainder_works = models.Work.objects.exclude(campaigns__status='ACTIVE').order_by('-num_wishes')[:remainder]
worklist.extend(remainder_works)
worklist = []
if max > count:
# add all the works with active campaigns
for campaign in ending:
worklist.append(campaign.work)
# then fill out the rest of the list with popular but inactive works
remainder = max - count
remainder_works = models.Work.objects.exclude(campaigns__status='ACTIVE').order_by('-num_wishes')[:remainder]
worklist.extend(remainder_works)
else:
# if the active campaign list has more works than we can fit
# in our slideshow, it's the only source we need to draw from
while j < max:
worklist.append(ending[j].work)
j +=1
# if the active campaign list has more works than we can fit
# in our slideshow, it's the only source we need to draw from
while j < max:
worklist.append(ending[j].work)
j +=1
return worklist
def next(request):
if request.COOKIES.has_key('next'):
response = HttpResponseRedirect(urllib.unquote(request.COOKIES['next']))
response.delete_cookie('next')
return response
else:
return HttpResponseRedirect('/')
if request.COOKIES.has_key('next'):
response = HttpResponseRedirect(urllib.unquote(request.COOKIES['next']))
response.delete_cookie('next')
return response
else:
return HttpResponseRedirect('/')
def home(request):
if request.user.is_authenticated():
@ -141,20 +141,22 @@ def work(request, work_id, action='display'):
pledged = campaign.transactions().filter(user=request.user, status="ACTIVE")
except:
pledged = None
try:
pubdate = work.publication_date[:4]
except IndexError:
pubdate = 'unknown'
if not request.user.is_anonymous():
claimform = UserClaimForm( request.user, data={'work':work.pk, 'user': request.user.id}, prefix = 'claim')
claimform = UserClaimForm( request.user, data={'claim-work':work.pk, 'claim-user': request.user.id}, prefix = 'claim')
for edition in editions:
#edition.ebook_form = EbookForm( data = {'user':request.user.id, 'edition':edition.pk })
edition.ebook_form = EbookForm( instance= models.Ebook(user = request.user, edition = edition, provider = 'x' ), prefix = 'ebook_%d'%edition.id)
else:
claimform = None
if campaign:
# pull up premiums explicitly tied to the campaign or generic premiums
q = Q(campaign=campaign) | Q(campaign__isnull=True)
premiums = models.Premium.objects.filter(q)
premiums = models.Premium.objects.filter(q).exclude(type='XX').order_by('amount')
else:
premiums = None
@ -162,23 +164,19 @@ def work(request, work_id, action='display'):
base_url = request.build_absolute_uri("/")[:-1]
#may want to deprecate the following
if action == 'setup_campaign':
return render(request, 'setup_campaign.html', {'work': work})
else:
return render(request, 'work.html', {
'work': work,
'premiums': premiums,
'ungluers': userlists.supporting_users(work, 5),
'claimform': claimform,
'wishers': wishers,
'base_url': base_url,
'editions': editions,
'pubdate': pubdate,
'pledged':pledged,
'activetab': activetab,
'alert':alert
})
return render(request, 'work.html', {
'work': work,
'premiums': premiums,
'ungluers': userlists.supporting_users(work, 5),
'claimform': claimform,
'wishers': wishers,
'base_url': base_url,
'editions': editions,
'pubdate': pubdate,
'pledged':pledged,
'activetab': activetab,
'alert':alert
})
def manage_campaign(request, id):
campaign = get_object_or_404(models.Campaign, id=id)
@ -188,22 +186,59 @@ def manage_campaign(request, id):
campaign.not_manager=True
return render(request, 'manage_campaign.html', {'campaign': campaign})
alerts = []
if request.method == 'POST':
form= ManageCampaignForm(instance=campaign, data=request.POST)
if form.is_valid():
form.save()
alerts.append(_('Campaign data has been saved'))
else:
alerts.append(_('Campaign data has NOT been saved'))
if 'launch' in request.POST.keys():
if campaign.launchable :
campaign.activate()
alerts.append(_('Campaign has been launched'))
if request.method == 'POST' :
if request.POST.has_key('add_premium') :
postcopy=request.POST.copy()
postcopy['type']='CU'
new_premium_form = CustomPremiumForm(data=postcopy)
if new_premium_form.is_valid():
new_premium_form.save()
alerts.append(_('New premium has been added'))
new_premium_form = CustomPremiumForm(data={'campaign': campaign})
else:
alerts.append(_('Campaign has NOT been launched'))
alerts.append(_('New premium has not been added'))
form = ManageCampaignForm(instance=campaign)
elif request.POST.has_key('save') or request.POST.has_key('launch') :
form= ManageCampaignForm(instance=campaign, data=request.POST)
if form.is_valid():
form.save()
alerts.append(_('Campaign data has been saved'))
else:
alerts.append(_('Campaign data has NOT been saved'))
if 'launch' in request.POST.keys():
if campaign.launchable :
campaign.activate()
alerts.append(_('Campaign has been launched'))
else:
alerts.append(_('Campaign has NOT been launched'))
new_premium_form = CustomPremiumForm(data={'campaign': campaign})
elif request.POST.has_key('inactivate') :
if request.POST.has_key('premium_id'):
premiums_to_stop = request.POST['premium_id']
for premium_to_stop in premiums_to_stop:
selected_premium = models.Premium.objects.get(id=premium_to_stop)
if selected_premium.type == 'CU':
selected_premium.type = 'XX'
selected_premium.save()
alerts.append(_('Premium %s has been inactivated'% premium_to_stop))
form = ManageCampaignForm(instance=campaign)
new_premium_form = CustomPremiumForm(data={'campaign': campaign})
else:
form= ManageCampaignForm(instance=campaign)
return render(request, 'manage_campaign.html', {'campaign': campaign, 'form':form, 'problems': campaign.problems, 'alerts': alerts})
form = ManageCampaignForm(instance=campaign)
new_premium_form = CustomPremiumForm(data={'campaign': campaign})
# pull up premiums explicitly tied to the campaign or generic premiums
q = Q(campaign=campaign) | Q(campaign__isnull=True)
premiums = models.Premium.objects.filter(q).order_by('amount')
return render(request, 'manage_campaign.html', {
'campaign': campaign,
'form':form,
'problems': campaign.problems,
'alerts': alerts,
'premiums' : premiums,
'premium_form' : new_premium_form,
})
def googlebooks(request, googlebooks_id):
try:
@ -330,7 +365,7 @@ class PledgeView(FormView):
embedded = False
def get(self, request, *args, **kwargs):
# change https://code.djangoproject.com/browser/django/tags/releases/1.3.1/django/views/generic/edit.py#L129
# change the default behavior from https://code.djangoproject.com/browser/django/tags/releases/1.3.1/django/views/generic/edit.py#L129
# don't automatically bind the data to the form on GET, only on POST
# compare with https://code.djangoproject.com/browser/django/tags/releases/1.3.1/django/views/generic/edit.py#L34
form_class = self.get_form_class()
@ -376,10 +411,19 @@ class PledgeView(FormView):
work_id = self.kwargs["work_id"]
preapproval_amount = form.cleaned_data["preapproval_amount"]
anonymous = form.cleaned_data["anonymous"]
# right now, if there is a non-zero pledge amount, go with that. otherwise, do the pre_approval
# right now, if there is a non-zero pledge amount, go with that. otherwise, do the pre_approval
campaign = models.Work.objects.get(id=int(work_id)).last_campaign()
premium_id = form.cleaned_data["premium_id"]
# confirm that the premium_id is a valid one for the campaign in question
try:
premium = models.Premium.objects.get(id=premium_id)
if not (premium.campaign is None or premium.campaign == campaign):
premium = None
except models.Premium.DoesNotExist, e:
premium = None
p = PaymentManager(embedded=self.embedded)
# PledgeView is wrapped in login_required -- so in theory, user should never be None -- but I'll keep this logic here for now.
@ -397,7 +441,7 @@ class PledgeView(FormView):
# set the expiry date based on the campaign deadline
expiry = campaign.deadline + timedelta( days=settings.PREAPPROVAL_PERIOD_AFTER_CAMPAIGN )
t, url = p.authorize('USD', TARGET_TYPE_CAMPAIGN, preapproval_amount, expiry=expiry, campaign=campaign, list=None, user=user,
return_url=return_url, cancel_url=cancel_url, anonymous=anonymous)
return_url=return_url, cancel_url=cancel_url, anonymous=anonymous, premium=premium)
else: # embedded view -- which we're not actively using right now.
# embedded view triggerws instant payment: send to the partnering RH
receiver_list = [{'email':settings.PAYPAL_NONPROFIT_PARTNER_EMAIL, 'amount':preapproval_amount}]
@ -406,7 +450,7 @@ class PledgeView(FormView):
cancel_url = None
t, url = p.pledge('USD', TARGET_TYPE_CAMPAIGN, receiver_list, campaign=campaign, list=None, user=user,
return_url=return_url, cancel_url=cancel_url, anonymous=anonymous)
return_url=return_url, cancel_url=cancel_url, anonymous=anonymous, premium=premium)
if url:
logger.info("PledgeView paypal: " + url)
@ -417,21 +461,133 @@ class PledgeView(FormView):
logger.info("PledgeView paypal: Error " + str(t.reference))
return HttpResponse(response)
class PledgeModifyView(FormView):
"""
A view to handle request to change an existing pledge
"""
template_name="pledge_modify.html"
form_class = CampaignPledgeForm
embedded = False
def get_context_data(self, **kwargs):
context = super(PledgeModifyView, self).get_context_data(**kwargs)
# the following should be true since PledgeModifyView.as_view is wrapped in login_required
assert self.request.user.is_authenticated()
user = self.request.user
work = get_object_or_404(models.Work, id=self.kwargs["work_id"])
try:
campaign = work.last_campaign()
premiums = campaign.effective_premiums()
# which combination of campaign and transaction status required?
# Campaign must be ACTIVE
assert campaign.status == 'ACTIVE'
transactions = campaign.transactions().filter(user=user, status=IPN_PREAPPROVAL_STATUS_ACTIVE)
assert transactions.count() == 1
transaction = transactions[0]
assert transaction.type == PAYMENT_TYPE_AUTHORIZATION and transaction.status == IPN_PREAPPROVAL_STATUS_ACTIVE
except Exception, e:
raise e
# what stuff do we need to pull out to populate form?
# preapproval_amount, premium_id (which we don't have stored yet)
if transaction.premium is not None:
premium_id = transaction.premium.id
else:
premium_id = None
# is there a Transaction for an ACTIVE campaign for this
# should make sure Transaction is modifiable.
preapproval_amount = transaction.amount
data = {'preapproval_amount':preapproval_amount, 'premium_id':premium_id}
# initialize form with the current state of the transaction if the current values empty
form = kwargs['form']
if not(form.is_bound):
form_class = self.get_form_class()
form = form_class(initial=data)
context.update({'work':work,'campaign':campaign, 'premiums':premiums, 'form':form, 'premium_id':premium_id, 'faqmenu': 'pledge'})
return context
def form_invalid(self, form):
logger.info("form.non_field_errors: {0}".format(form.non_field_errors()))
response = self.render_to_response(self.get_context_data(form=form))
return response
def form_valid(self, form):
# What are the situations we need to deal with?
# 2 main situations: if the new amount is less than max_amount, no need to go out to PayPal again
# if new amount is greater than max_amount...need to go out and get new approval.
# to start with, we can use the standard pledge_complete, pledge_cancel machinery
# might have to modify the pledge_complete, pledge_cancel because the messages are going to be
# different because we're modifying a pledge rather than a new one.
work_id = self.kwargs["work_id"]
preapproval_amount = form.cleaned_data["preapproval_amount"]
anonymous = form.cleaned_data["anonymous"]
assert self.request.user.is_authenticated()
user = self.request.user
# right now, if there is a non-zero pledge amount, go with that. otherwise, do the pre_approval
campaign = models.Work.objects.get(id=int(work_id)).last_campaign()
assert campaign.status == 'ACTIVE'
premium_id = form.cleaned_data["premium_id"]
# confirm that the premium_id is a valid one for the campaign in question
try:
premium = models.Premium.objects.get(id=premium_id)
if not (premium.campaign is None or premium.campaign == campaign):
premium = None
except models.Premium.DoesNotExist, e:
premium = None
transactions = campaign.transactions().filter(user=user, status=IPN_PREAPPROVAL_STATUS_ACTIVE)
assert transactions.count() == 1
transaction = transactions[0]
assert transaction.type == PAYMENT_TYPE_AUTHORIZATION and transaction.status == IPN_PREAPPROVAL_STATUS_ACTIVE
p = PaymentManager(embedded=self.embedded)
status, url = p.modify_transaction(transaction=transaction, amount=preapproval_amount, premium=premium)
logger.info("status: {0}, url:{1}".format(status, url))
if status and url is not None:
logger.info("PledgeModifyView paypal: " + url)
return HttpResponseRedirect(url)
elif status and url is None:
# let's use the pledge_complete template for now and maybe look into customizing it.
return HttpResponseRedirect("{0}?tid={1}".format(reverse('pledge_complete'), transaction.id))
else:
return HttpResponse("No modication made")
class PledgeCompleteView(TemplateView):
"""A callback for PayPal to tell unglue.it that a payment transaction has completed successfully.
Possible things to implement:
after pledging, supporter receives email including thanks, work pledged, amount, expiry date, any next steps they should expect; others?
study other confirmation emails for their contents
after pledging, supporters are returned to a thank-you screen
should have prominent "thank you" or "congratulations" message
should have prominent share options
should suggest other works for supporters to explore (on what basis?)
link to work page? or to page on which supporter entered the process? (if the latter, how does that work with widgets?)
should note that a confirmation email has been sent to $email from $sender
should briefly note next steps (e.g. if this campaign succeeds you will be emailed on date X)
after pledging, supporter receives email including thanks, work pledged, amount, expiry date, any next steps they should expect; others?
study other confirmation emails for their contents
after pledging, supporters are returned to a thank-you screen
should have prominent "thank you" or "congratulations" message
should have prominent share options
should suggest other works for supporters to explore (on what basis?)
link to work page? or to page on which supporter entered the process? (if the latter, how does that work with widgets?)
should note that a confirmation email has been sent to $email from $sender
should briefly note next steps (e.g. if this campaign succeeds you will be emailed on date X)
"""
template_name="pledge_complete.html"
@ -467,8 +623,12 @@ should briefly note next steps (e.g. if this campaign succeeds you will be email
try:
if user.id == transaction.user.id:
correct_user = True
else:
# should be 403 -- but let's try 404 for now -- 403 exception coming in Django 1.4
raise Http404
except Exception, e:
pass
raise Http404
# check that the user had not already approved the transaction
# do we need to first run PreapprovalDetails to check on the status
@ -612,15 +772,15 @@ def claim(request):
data = request.GET
else:
data = request.POST
form = UserClaimForm(request.user, data=data)
form = UserClaimForm(request.user, data=data, prefix='claim')
if form.is_valid():
# make sure we're not creating a duplicate claim
if not models.Claim.objects.filter(work=data['work'], rights_holder=data['rights_holder'], status='pending').count():
if not models.Claim.objects.filter(work=data['claim-work'], rights_holder=data['claim-rights_holder'], status='pending').count():
form.save()
return HttpResponseRedirect(reverse('work', kwargs={'work_id': data['work']}))
return HttpResponseRedirect(reverse('work', kwargs={'work_id': data['claim-work']}))
else:
work = models.Work.objects.get(id=data['work'])
rights_holder = models.RightsHolder.objects.get(id=data['rights_holder'])
work = models.Work.objects.get(id=data['claim-work'])
rights_holder = models.RightsHolder.objects.get(id=data['claim-rights_holder'])
context = {'form': form, 'work': work, 'rights_holder':rights_holder }
return render(request, "claim.html", context)
@ -700,7 +860,7 @@ def campaign_admin(request):
# pull out Campaigns with Transactions that are ACTIVE -- and hence can be executed
# Campaign.objects.filter(transaction__status='ACTIVE')
campaigns_with_active_transactions = models.Campaign.objects.filter(transaction__status=IPN_PAY_STATUS_ACTIVE)
campaigns_with_active_transactions = models.Campaign.objects.filter(transaction__status=IPN_PREAPPROVAL_STATUS_ACTIVE)
# pull out Campaigns with Transactions that are INCOMPLETE
@ -712,7 +872,7 @@ def campaign_admin(request):
# show Campaigns with Transactions that are CANCELED
campaigns_with_canceled_transactions = models.Campaign.objects.filter(transaction__status=IPN_PAY_STATUS_CANCELED)
campaigns_with_canceled_transactions = models.Campaign.objects.filter(transaction__status=IPN_PREAPPROVAL_STATUS_CANCELED)
return (campaigns_with_active_transactions, campaigns_with_incomplete_transactions, campaigns_with_completed_transactions,
campaigns_with_canceled_transactions)

View File

@ -4,7 +4,7 @@ from django.contrib.auth.models import User
from django.core.urlresolvers import reverse
from regluit.payment.parameters import *
from regluit.payment.paypal import Pay, Execute, IPN, IPN_TYPE_PAYMENT, IPN_TYPE_PREAPPROVAL, IPN_TYPE_ADJUSTMENT, IPN_PAY_STATUS_ACTIVE, IPN_PAY_STATUS_INCOMPLETE, IPN_PAY_STATUS_NONE
from regluit.payment.paypal import Pay, Execute, IPN, IPN_TYPE_PAYMENT, IPN_TYPE_PREAPPROVAL, IPN_TYPE_ADJUSTMENT, IPN_PREAPPROVAL_STATUS_ACTIVE, IPN_PAY_STATUS_INCOMPLETE, IPN_PAY_STATUS_NONE
from regluit.payment.paypal import Preapproval, IPN_PAY_STATUS_COMPLETED, CancelPreapproval, PaymentDetails, PreapprovalDetails, IPN_SENDER_STATUS_COMPLETED, IPN_TXN_STATUS_COMPLETED
from regluit.payment.paypal import RefundPayment
import uuid
@ -173,11 +173,12 @@ class PaymentManager( object ):
for t in transactions:
if t.date_payment is None:
preapproval_status = self.update_preapproval(t)
preapproval_status = self.update_preapproval(t)
logger.info("transaction: {0}, preapproval_status: {1}".format(t, preapproval_status))
if not set(['status', 'currency', 'amount', 'approved']).isdisjoint(set(preapproval_status.keys())):
status["preapprovals"].append(preapproval_status)
else:
payment_status = self.update_payment(t)
payment_status = self.update_payment(t)
if not set(["status", "receivers"]).isdisjoint(payment_status.keys()):
status["payments"].append(payment_status)
@ -305,7 +306,7 @@ class PaymentManager( object ):
if authorized:
# return only ACTIVE transactions with approved=True
authorized_list = transaction_list.filter(type=PAYMENT_TYPE_AUTHORIZATION,
status=IPN_PAY_STATUS_ACTIVE,
status=IPN_PREAPPROVAL_STATUS_ACTIVE,
approved=True)
else:
authorized_list = []
@ -419,7 +420,7 @@ class PaymentManager( object ):
'''
# only allow active transactions to go through again, if there is an error, intervention is needed
transactions = Transaction.objects.filter(campaign=campaign, status=IPN_PAY_STATUS_ACTIVE)
transactions = Transaction.objects.filter(campaign=campaign, status=IPN_PREAPPROVAL_STATUS_ACTIVE)
for t in transactions:
@ -428,6 +429,11 @@ class PaymentManager( object ):
self.execute_transaction(t, receiver_list)
# TO DO: update campaign status
# Should this be done first before executing the transactions?
# How does the success/failure of transactions affect states of campaigns
return transactions
def finish_campaign(self, campaign):
@ -450,6 +456,9 @@ class PaymentManager( object ):
for t in transactions:
result = self.finish_transaction(t)
# TO DO: update campaign status
return transactions
def cancel_campaign(self, campaign):
@ -463,11 +472,13 @@ class PaymentManager( object ):
'''
transactions = Transaction.objects.filter(campaign=campaign, status=IPN_PAY_STATUS_ACTIVE)
transactions = Transaction.objects.filter(campaign=campaign, status=IPN_PREAPPROVAL_STATUS_ACTIVE)
for t in transactions:
result = self.cancel_transaction(t)
# TO DO: update campaign status
return transactions
@ -607,7 +618,7 @@ class PaymentManager( object ):
logger.info("Cancel Transaction " + str(transaction.id) + " Failed with error: " + p.error_string())
return False
def authorize(self, currency, target, amount, expiry=None, campaign=None, list=None, user=None, return_url=None, cancel_url=None, anonymous=False):
def authorize(self, currency, target, amount, expiry=None, campaign=None, list=None, user=None, return_url=None, cancel_url=None, anonymous=False, premium=None):
'''
authorize
@ -619,6 +630,10 @@ class PaymentManager( object ):
campaign: optional campaign object(to be set with TARGET_TYPE_CAMPAIGN)
list: optional list object(to be set with TARGET_TYPE_LIST)
user: optional user object
return_url: url to redirect supporter to after a successful PayPal transaction
cancel_url: url to send supporter to if support hits cancel while in middle of PayPal transaction
anonymous: whether this pledge is anonymous
premium: the premium selected by the supporter for this transaction
return value: a tuple of the new transaction object and a re-direct url. If the process fails,
the redirect url will be None
@ -635,7 +650,8 @@ class PaymentManager( object ):
campaign=campaign,
list=list,
user=user,
anonymous=anonymous
anonymous=anonymous,
premium=premium
)
# we might want to not allow for a return_url or cancel_url to be passed in but calculated
@ -679,7 +695,7 @@ class PaymentManager( object ):
logger.info("Authorize Error: " + p.error_string())
return t, None
def modify_transaction(self, transaction, amount=None, expiry=None, return_url=None, cancel_url=None):
def modify_transaction(self, transaction, amount, expiry=None, anonymous=None, premium=None, return_url=None, cancel_url=None):
'''
modify
@ -687,35 +703,34 @@ class PaymentManager( object ):
amount: the new amount
expiry: the new expiration date, or if none the current expiration date will be used
anonymous: new anonymous value; if None, then keep old value
premium: new premium selected; if None, then keep old value
return_url: the return URL after the preapproval(if needed)
cancel_url: the cancel url after the preapproval(if needed)
return value: True if successful, false otherwise. An optional second parameter for the forward URL if a new authorhization is needed
'''
if not amount:
logger.info("Error, no amount speicified")
return False
return value: True if successful, False otherwise. An optional second parameter for the forward URL if a new authorhization is needed
'''
# Can only modify the amount of a preapproval for now
if transaction.type != PAYMENT_TYPE_AUTHORIZATION:
# Can only modify the amount of a preapproval for now
logger.info("Error, attempt to modify an invalid transaction type")
return False, None
if transaction.status != IPN_PAY_STATUS_ACTIVE:
# Can only modify an active, pending transaction. If it is completed, we need to do a refund. If it is incomplete,
# then an IPN may be pending and we cannot touch it
# Can only modify an active, pending transaction. If it is completed, we need to do a refund. If it is incomplete,
# then an IPN may be pending and we cannot touch it
if transaction.status != IPN_PREAPPROVAL_STATUS_ACTIVE:
logger.info("Error, attempt to modify a transaction that is not active")
return False, None
if not expiry:
# Use the old expiration date
# if any of expiry, anonymous, or premium is None, use the existing value
if expiry is None:
expiry = transaction.date_expired
if anonymous is None:
anonymous = transaction.anonymous
if premium is None:
premium = transaction.premium
if amount > transaction.max_amount or expiry != transaction.date_expired:
# Increase or expiuration change, cancel and start again
self.cancel_transaction(transaction)
# Start a new authorization for the new amount
@ -728,27 +743,30 @@ class PaymentManager( object ):
transaction.user,
return_url,
cancel_url,
transaction.anonymous)
transaction.anonymous,
premium)
if t and url:
# Need to re-direct to approve the transaction
logger.info("New authorization needed, redirectiont to url %s" % url)
logger.info("New authorization needed, redirection to url %s" % url)
self.cancel_transaction(transaction)
return True, url
else:
# No amount change necessary
# a problem in authorize
logger.info("Error, unable to start a new authorization")
return False, None
elif amount <= transaction.max_amount:
# Change the amount but leave the preapproval alone
# Update transaction but leave the preapproval alone
transaction.amount = amount
transaction.anonymous = anonymous
transaction.premium = premium
transaction.save()
logger.info("Updated amount of transaction to %f" % amount)
return True, None
else:
# No changes
logger.info("Error, no modifications requested")
# this shouldn't happen
return False, None
@ -794,7 +812,7 @@ class PaymentManager( object ):
logger.info("Refund Transaction " + str(transaction.id) + " Failed with error: " + p.error_string())
return False
def pledge(self, currency, target, receiver_list, campaign=None, list=None, user=None, return_url=None, cancel_url=None, anonymous=False):
def pledge(self, currency, target, receiver_list, campaign=None, list=None, user=None, return_url=None, cancel_url=None, anonymous=False, premium=None):
'''
pledge
@ -812,6 +830,10 @@ class PaymentManager( object ):
campaign: optional campaign object(to be set with TARGET_TYPE_CAMPAIGN)
list: optional list object(to be set with TARGET_TYPE_LIST)
user: optional user object
return_url: url to redirect supporter to after a successful PayPal transaction
cancel_url: url to send supporter to if support hits cancel while in middle of PayPal transaction
anonymous: whether this pledge is anonymous
premium: the premium selected by the supporter for this transaction
return value: a tuple of the new transaction object and a re-direct url. If the process fails,
the redirect url will be None
@ -834,7 +856,8 @@ class PaymentManager( object ):
list=list,
user=user,
date_payment=now(),
anonymous=anonymous
anonymous=anonymous,
premium=premium
)
t.create_receivers(receiver_list)

View File

@ -0,0 +1,160 @@
# encoding: 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 'Transaction.premium'
db.add_column('payment_transaction', 'premium', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['core.Premium'], null=True), keep_default=False)
def backwards(self, orm):
# Deleting field 'Transaction.premium'
db.delete_column('payment_transaction', 'premium_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'})
},
'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.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'}),
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'deadline': ('django.db.models.fields.DateTimeField', [], {}),
'description': ('django.db.models.fields.TextField', [], {'null': 'True'}),
'details': ('django.db.models.fields.TextField', [], {'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'left': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '14', 'decimal_places': '2'}),
'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'}),
'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'}),
'work': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'campaigns'", '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'}),
'type': ('django.db.models.fields.CharField', [], {'max_length': '2'})
},
'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'}),
'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'}),
'openlibrary_lookup': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '1000'})
},
'payment.paymentresponse': {
'Meta': {'object_name': 'PaymentResponse'},
'api': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
'correlation_id': ('django.db.models.fields.CharField', [], {'max_length': '512', 'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'info': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True'}),
'timestamp': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
'transaction': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['payment.Transaction']"})
},
'payment.receiver': {
'Meta': {'object_name': 'Receiver'},
'amount': ('django.db.models.fields.DecimalField', [], {'default': "'0.00'", 'max_digits': '14', 'decimal_places': '2'}),
'currency': ('django.db.models.fields.CharField', [], {'max_length': '10'}),
'email': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'primary': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'reason': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
'status': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
'transaction': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['payment.Transaction']"}),
'txn_id': ('django.db.models.fields.CharField', [], {'max_length': '64'})
},
'payment.transaction': {
'Meta': {'object_name': 'Transaction'},
'amount': ('django.db.models.fields.DecimalField', [], {'default': "'0.00'", 'max_digits': '14', 'decimal_places': '2'}),
'anonymous': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'approved': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
'campaign': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['core.Campaign']", 'null': 'True'}),
'currency': ('django.db.models.fields.CharField', [], {'default': "'USD'", 'max_length': '10', 'null': 'True'}),
'date_authorized': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'date_executed': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
'date_expired': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
'date_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'date_payment': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
'error': ('django.db.models.fields.CharField', [], {'max_length': '256', 'null': 'True'}),
'execution': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'list': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['core.Wishlist']", 'null': 'True'}),
'max_amount': ('django.db.models.fields.DecimalField', [], {'default': "'0.00'", 'max_digits': '14', 'decimal_places': '2'}),
'pay_key': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
'preapproval_key': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}),
'premium': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['core.Premium']", 'null': 'True'}),
'reason': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}),
'receipt': ('django.db.models.fields.CharField', [], {'max_length': '256', 'null': 'True'}),
'secret': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}),
'status': ('django.db.models.fields.CharField', [], {'default': "'NONE'", 'max_length': '32'}),
'target': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'type': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'})
}
}
complete_apps = ['payment']

View File

@ -1,6 +1,6 @@
from django.db import models
from django.contrib.auth.models import User
from regluit.core.models import Campaign, Wishlist
from regluit.core.models import Campaign, Wishlist, Premium
from regluit.payment.parameters import *
from decimal import Decimal
import uuid
@ -16,7 +16,7 @@ class Transaction(models.Model):
#execution: e.g. EXECUTE_TYPE_CHAINED_INSTANT, EXECUTE_TYPE_CHAINED_DELAYED, EXECUTE_TYPE_PARALLEL
execution = models.IntegerField(default=EXECUTE_TYPE_NONE, null=False)
# status: constants defined in paypal.py (e.g., IPN_PAY_STATUS_ACTIVE, IPN_PAY_STATUS_CREATED)
# status: constants defined in paypal.py (e.g., IPN_PREAPPROVAL_STATUS_ACTIVE, IPN_PAY_STATUS_CREATED)
status = models.CharField(max_length=32, default='NONE', null=False)
# amount & currency -- amount of money and its currency involved for transaction
@ -59,9 +59,10 @@ class Transaction(models.Model):
date_authorized = models.DateTimeField(null=True)
date_expired = models.DateTimeField(null=True)
# associated User and Campaign for this Transaction
# associated User, Campaign, and Premium for this Transaction
user = models.ForeignKey(User, null=True)
campaign = models.ForeignKey(Campaign, null=True)
premium = models.ForeignKey(Premium, null=True)
# list: makes allowance for pledging against a Wishlist: not currently in use
list = models.ForeignKey(Wishlist, null=True)

View File

@ -60,15 +60,15 @@ IPN_PAY_STATUS_REVERSALERROR = 'REVERSALERROR'
IPN_PAY_STATUS_PROCESSING = 'PROCESSING'
IPN_PAY_STATUS_PENDING = 'PENDING'
# particular to preapprovals -- may want to rename these constants to IPN_PREAPPROVAL_STATUS_*
# particular to preapprovals
# https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_APPreapprovalDetails
#ACTIVE - The preapproval is active
#CANCELED - The preapproval was explicitly canceled by the sender or by PayPal
#DEACTIVED - The preapproval is not active; you can be reactivate it by resetting the personal identification number (PIN) or by contacting PayPal
IPN_PAY_STATUS_ACTIVE = "ACTIVE"
IPN_PAY_STATUS_CANCELED = "CANCELED"
IPN_PAY_STATUS_DEACTIVED = "DEACTIVED"
IPN_PREAPPROVAL_STATUS_ACTIVE = "ACTIVE"
IPN_PREAPPROVAL_STATUS_CANCELED = "CANCELED"
IPN_PREAPPROVAL_STATUS_DEACTIVED = "DEACTIVED"
# https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_APIPN
#COMPLETED - The sender's transaction has completed

View File

@ -60,7 +60,8 @@ def loginSandbox(selenium):
except:
traceback.print_exc()
def paySandbox(test, selenium, url, authorize=False):
def paySandbox(test, selenium, url, authorize=False, already_at_url=False, sleep_time=20):
if authorize:
print "AUTHORIZE SANDBOX"
@ -69,42 +70,45 @@ def paySandbox(test, selenium, url, authorize=False):
try:
# We need this sleep to make sure the JS engine is finished from the sandbox loging page
time.sleep(20)
time.sleep(sleep_time)
selenium.get(url)
print "Opened URL %s" % url
if not already_at_url:
selenium.get(url)
print "Opened URL %s" % url
try:
# Button is only visible if the login box is NOT open
# If the login box is open, the email/pw fiels are already accessible
login_element = WebDriverWait(selenium, 30).until(lambda d : d.find_element_by_id("loadLogin"))
login_element = WebDriverWait(selenium, 10).until(lambda d : d.find_element_by_id("loadLogin"))
login_element.click()
# This sleep is needed for js to slide the buyer login into view. The elements are always in the DOM
# so selenium can find them, but we need them in view to interact
time.sleep(20)
time.sleep(sleep_time)
except:
print "Ready for Login"
email_element = WebDriverWait(selenium, 60).until(lambda d : d.find_element_by_id("login_email"))
email_element.click()
email_element.clear()
email_element.send_keys(settings.PAYPAL_BUYER_LOGIN)
password_element = WebDriverWait(selenium, 60).until(lambda d : d.find_element_by_id("login_password"))
password_element.click()
password_element.clear()
password_element.send_keys(settings.PAYPAL_BUYER_PASSWORD)
submit_button = WebDriverWait(selenium, 60).until(lambda d : d.find_element_by_id("submitLogin"))
submit_button.click()
# This sleep makes sure js has time to animate out the next page
time.sleep(20)
time.sleep(sleep_time)
final_submit = WebDriverWait(selenium, 60).until(lambda d : d.find_element_by_id("submit.x"))
final_submit.click()
# This makes sure the processing of the final submit is complete
time.sleep(20)
time.sleep(sleep_time)
# Don't wait too long for this, it isn't really needed. By the time JS has gotten around to
# displaying this element, the redirect has usually occured
@ -261,7 +265,7 @@ class AuthorizeTest(TestCase):
t = Transaction.objects.get(id=t.id)
self.assertEqual(t.status, IPN_PAY_STATUS_ACTIVE)
self.assertEqual(t.status, IPN_PREAPPROVAL_STATUS_ACTIVE)
def tearDown(self):
self.selenium.quit()

View File

@ -1,7 +1,7 @@
from regluit.core import models
from regluit.payment.models import Transaction, PaymentResponse, Receiver
from regluit.payment.manager import PaymentManager
from regluit.payment.paypal import IPN_PAY_STATUS_ACTIVE, IPN_PAY_STATUS_INCOMPLETE, IPN_PAY_STATUS_COMPLETED
from regluit.payment.paypal import IPN_PREAPPROVAL_STATUS_ACTIVE, IPN_PAY_STATUS_INCOMPLETE, IPN_PAY_STATUS_COMPLETED
from django.conf import settings
@ -108,7 +108,7 @@ pm = PaymentManager()
def campaign_display():
campaigns_with_active_transactions = models.Campaign.objects.filter(transaction__status=IPN_PAY_STATUS_ACTIVE)
campaigns_with_active_transactions = models.Campaign.objects.filter(transaction__status=IPN_PREAPPROVAL_STATUS_ACTIVE)
campaigns_with_incomplete_transactions = models.Campaign.objects.filter(transaction__status=IPN_PAY_STATUS_INCOMPLETE)
campaigns_with_completed_transactions = models.Campaign.objects.filter(transaction__status=IPN_PAY_STATUS_COMPLETED)
@ -117,7 +117,7 @@ def campaign_display():
print "campaigns with completed transactions", campaigns_with_completed_transactions
def campaigns_active():
return models.Campaign.objects.filter(transaction__status=IPN_PAY_STATUS_ACTIVE)
return models.Campaign.objects.filter(transaction__status=IPN_PREAPPROVAL_STATUS_ACTIVE)
def campaigns_incomplete():
return models.Campaign.objects.filter(transaction__status=IPN_PAY_STATUS_INCOMPLETE)
@ -151,30 +151,36 @@ def recipient_status(clist):
# res = [pm.finish_campaign(c) for c in campaigns_incomplete()]
def support_campaign():
def support_campaign(do_local=True):
"""
programatically fire up selenium to make a Pledge
do_local should be True only if you are running support_campaign on db tied to LIVE_SERVER_TEST_URL
"""
import django
django.db.transaction.enter_transaction_management()
UNGLUE_IT_URL = settings.LIVE_SERVER_TEST_URL
# unglue.it login
USER = settings.UNGLUEIT_TEST_USER
PASSWORD = settings.UNGLUEIT_TEST_PASSWORD
# PayPal developer sandbox
from regluit.payment.tests import loginSandbox
from regluit.payment.tests import loginSandbox, paySandbox
setup_selenium()
# we can experiment with different webdrivers
# sel = webdriver.Firefox()
sel = webdriver.Firefox()
# Chrome
sel = webdriver.Chrome(executable_path='/Users/raymondyee/C/src/Gluejar/regluit/test/chromedriver')
#sel = webdriver.Chrome(executable_path='/Users/raymondyee/C/src/Gluejar/regluit/test/chromedriver')
# HTMLUNIT with JS -- not successful
#sel = webdriver.Remote("http://localhost:4444/wd/hub", webdriver.DesiredCapabilities.HTMLUNITWITHJS)
time.sleep(5)
time.sleep(10)
# find a campaign to pledge to
loginSandbox(sel)
@ -203,10 +209,11 @@ def support_campaign():
# pull up one of the campaigns to pledge to
# div.book-list div.title a
# for now, take the first book and click on the link to get to the work page
sel.find_elements_by_css_selector("div.book-list div.title a")[0].click()
work_links = WebDriverWait(sel,10).until(lambda d: d.find_elements_by_css_selector("div.book-list div.title a"))
work_links[0].click()
time.sleep(1)
sel.find_element_by_css_selector("input[value*='Support']").click()
support_button = WebDriverWait(sel,10).until(lambda d: d.find_element_by_css_selector("input[value*='Support']"))
support_button.click()
# just click Pledge without filling out amount -- should have the form validation spot the error
pledge_button = WebDriverWait(sel,20).until(lambda d: d.find_element_by_css_selector("input[value*='Pledge']"))
@ -221,12 +228,102 @@ def support_campaign():
# enter a $10 pledge
preapproval_amount_input = WebDriverWait(sel,20).until(lambda d: d.find_element_by_css_selector("input#id_preapproval_amount"))
preapproval_amount_input.send_keys("10")
# fill out a premium -- the first one for now
premium_button = WebDriverWait(sel,20).until(lambda d: d.find_element_by_css_selector('input[type="radio"][value="1"]'))
premium_button.click()
pledge_button = WebDriverWait(sel,20).until(lambda d: d.find_element_by_css_selector("input[value*='Pledge']"))
pledge_button.click()
# grab the URL where sel is now?
print "Now trying to pay PayPal", sel.current_url
paySandbox(None, sel, sel.current_url, authorize=True, already_at_url=True, sleep_time=5)
# should be back on a pledge complete page
print sel.current_url, re.search(r"/pledge/complete",sel.current_url)
time.sleep(2)
django.db.transaction.commit()
# time out to simulate an IPN -- update all the transactions
if do_local:
django.db.transaction.enter_transaction_management()
pm = PaymentManager()
print pm.checkStatus()
transaction0 = Transaction.objects.all()[0]
print "transaction amount:{0}, transaction premium:{1}".format(transaction0.amount, transaction0.premium.id)
django.db.transaction.commit()
django.db.transaction.enter_transaction_management()
# I have no idea what the a[href*="/work/"] is not displayed....so that's why I'm going up one element.
work_url = WebDriverWait(sel,20).until(lambda d: d.find_element_by_css_selector('p > a[href*="/work/"]'))
work_url.click()
# change_pledge
change_pledge_button = WebDriverWait(sel,20).until(lambda d: d.find_element_by_css_selector("input[value*='Change Pledge']"))
change_pledge_button.click()
# enter a new pledge, which is less than the previous amount and therefore doesn't require a new PayPal transaction
preapproval_amount_input = WebDriverWait(sel,20).until(lambda d: d.find_element_by_css_selector("input#id_preapproval_amount"))
preapproval_amount_input.clear() # get rid of existing pledge
preapproval_amount_input.send_keys("5")
pledge_button = WebDriverWait(sel,20).until(lambda d: d.find_element_by_css_selector("input[value*='Modify Pledge']"))
pledge_button.click()
# return to the Work page again
work_url = WebDriverWait(sel,20).until(lambda d: d.find_element_by_css_selector('p > a[href*="/work/"]'))
work_url.click()
change_pledge_button = WebDriverWait(sel,20).until(lambda d: d.find_element_by_css_selector("input[value*='Change Pledge']"))
change_pledge_button.click()
# enter a new pledge, which is more than the previous amount and therefore requires a new PayPal transaction
preapproval_amount_input = WebDriverWait(sel,20).until(lambda d: d.find_element_by_css_selector("input#id_preapproval_amount"))
preapproval_amount_input.clear() # get rid of existing pledge
preapproval_amount_input.send_keys("25")
pledge_button = WebDriverWait(sel,20).until(lambda d: d.find_element_by_css_selector("input[value*='Modify Pledge']"))
pledge_button.click()
paySandbox(None, sel, sel.current_url, authorize=True, already_at_url=True, sleep_time=5)
# wait a bit to allow PayPal sandbox to be update the status of the Transaction
time.sleep(10)
# Why is the status of the new transaction not being updated?
django.db.transaction.commit()
# force a db lookup -- see whether there are 1 or 2 transactions
if do_local:
transactions = list(Transaction.objects.all())
print "number of transactions", Transaction.objects.count()
print "transactions before pm.checkStatus"
print [(t.id, t.type, t.preapproval_key, t.status, t.premium, t.amount) for t in Transaction.objects.all()]
print "checkStatus:", pm.checkStatus(transactions=transactions)
yield sel
#sel.quit()
def berkeley_search():
sel = webdriver.Firefox()
sel.get("http://berkeley.edu")
search = WebDriverWait(sel,5).until(lambda d: d.find_element_by_css_selector('input[id="search_text"]'))
search.send_keys("quantum computing")
return sel
# wait for a bit and then highlight the text and fill it out with "Bach" instead
# I was looking at using XPath natively in Firefox....
# https://developer.mozilla.org/en/Introduction_to_using_XPath_in_JavaScript#Within_an_HTML_Document
# document.evaluate('//input[@id="search_text"]', document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null ).snapshotItem(0);
def suites():
testcases = [GoogleWebDriverTest]
suites = unittest.TestSuite([unittest.TestLoader().loadTestsFromTestCase(testcase) for testcase in testcases])

View File

@ -10,9 +10,9 @@ urlpatterns = patterns('',
(r'^accounts/', include('registration.backends.default.urls')),
(r'^socialauth/', include('social_auth.urls')),
url(r"^accounts/login/welcome/$", direct_to_template,
{'template': 'registration/welcome.html',
'extra_context': {'suppress_search_box': True,}
}),
{'template': 'registration/welcome.html',
'extra_context': {'suppress_search_box': True,}
}),
(r'^api/', include('regluit.api.urls')),
(r'', include('regluit.frontend.urls')),
(r'', include('regluit.payment.urls')),