Various changes to user profile editing

This commit is contained in:
Fergal Moran
2013-04-29 11:25:19 +01:00
parent 3398f1c670
commit 9ff641c11d
31 changed files with 5344 additions and 943 deletions

2
.gitignore vendored
View File

@@ -4,6 +4,7 @@ tags
.tags_sorted_by_file
.idea
*.pyc
*.swp
media/*
build/*
_working/*
@@ -13,4 +14,3 @@ dss.conf
dss/debugsettings.py
mysql
test.py

View File

@@ -209,7 +209,7 @@ def upload_release_image(request, release_id):
release.save()
return HttpResponse(_get_json("Success"))
except Exception, ex:
logger.exception("Error uploading avatar")
logger.exception("Error uploading release image")
return HttpResponse(_get_json("Failed"))
@@ -223,17 +223,17 @@ def upload_image(request, mix_id):
mix.save()
return HttpResponse(_get_json("Success"))
except Exception, ex:
logger.exception("Error uploading avatar")
logger.exception("Error uploading image")
return HttpResponse(_get_json("Failed"))
@csrf_exempt
def upload_avatar_image(request):
try:
if 'Filedata' in request.FILES:
if 'avatar_image' in request.FILES:
profile = request.user.get_profile()
if profile:
profile.avatar_image = request.FILES['Filedata']
profile.avatar_image = request.FILES['avatar_image']
profile.save()
return HttpResponse(_get_json("Success"))
except Exception, ex:

View File

@@ -49,5 +49,7 @@ class ActivityResource(BackboneCompatibleResource):
return [i for i in data['objects'] if i is not None and i.obj.user is not None and i.obj.get_object_name is not None and i.obj.get_object_url is not None]
"""
"""
def dehydrate_date(self, bundle):
return self.humanize_date(bundle.obj.date)
"""

View File

@@ -22,8 +22,10 @@ class CommentResource(BackboneCompatibleResource):
bundle.data['user'] = {'pk': request.user.pk}
return super(CommentResource, self).obj_create(bundle, request, user=request.user)
"""
def dehydrate_date_created(self, bundle):
return self.humanize_date(bundle.obj.date_created)
"""
def dehydrate(self, bundle):
bundle.data['avatar_image'] = bundle.obj.user.get_profile().get_small_profile_image()

View File

@@ -1,71 +1,88 @@
from django.conf.urls import url
from django.contrib.auth.models import User
from tastypie import fields
from tastypie.authentication import Authentication
from tastypie.authorization import Authorization
from tastypie.constants import ALL, ALL_WITH_RELATIONS
from tastypie.authorization import DjangoAuthorization
from spa.api.v1.BackboneCompatibleResource import BackboneCompatibleResource
from spa.models import UserProfile
class UserResource(BackboneCompatibleResource):
class UserProfileResource(BackboneCompatibleResource):
class Meta:
queryset = UserProfile.objects.all()
excludes = []
authorization = Authorization()
authentication = Authentication()
resource_name = 'profile'
include_resource_uri = False
include_absolute_url = False
always_return_data = True
filtering = {
'user': ALL_WITH_RELATIONS,
'username': ALL,
'id': ALL,
}
authorization = DjangoAuthorization()
authentication = Authentication()
def prepend_urls(self):
return [
url(r"^(?P<resource_name>%s)/(?P<slug>[\w\d_.-]+)/$" % self._meta.resource_name, self.wrap_view('dispatch_detail'), name="api_dispatch_detail"),
]
def _hydrateBitmapOption(self, source, comparator):
return "checked" if (source & comparator) != 0 else ""
def authorized_read_list(self, object_list, bundle):
return object_list.filter(user_id=bundle.request.user.id)
def authorized_read_detail(self, object_list, bundle):
return object_list.only('description')
def hydrate(self, bundle):
if 'activity_sharing_likes' in bundle.data:
likes = UserProfile.ACTIVITY_SHARE_LIKES if bundle.data['activity_sharing_likes'] else 0
favourites = UserProfile.ACTIVITY_SHARE_FAVOURITES if bundle.data['activity_sharing_favourites'] else 0
comments = UserProfile.ACTIVITY_SHARE_COMMENTS if bundle.data['activity_sharing_comments'] else 0
bundle.data['activity_sharing'] = (likes | favourites | comments)
del bundle.data['activity_sharing_likes']
del bundle.data['activity_sharing_favourites']
del bundle.data['activity_sharing_comments']
if 'activity_sharing_networks_facebook' in bundle.data:
facebook = UserProfile.ACTIVITY_SHARE_NETWORK_FACEBOOK if bundle.data['activity_sharing_networks_facebook'] else 0
twitter = UserProfile.ACTIVITY_SHARE_NETWORK_TWITTER if bundle.data['activity_sharing_networks_twitter'] else 0
bundle.data['activity_sharing_networks'] = (facebook | twitter)
del bundle.data['activity_sharing_networks_facebook']
del bundle.data['activity_sharing_networks_twitter']
return bundle
def obj_update(self, bundle, skip_errors=False, **kwargs):
"""
This feels extremely hacky - but for some reason, deleting from the bundle
in hydrate is not preventing the fields from being serialized at the ORM
"""
if 'activity_sharing_networks_facebook' in kwargs: del kwargs['activity_sharing_networks_facebook']
if 'activity_sharing_networks_twitter' in kwargs: del kwargs['activity_sharing_networks_twitter']
if 'activity_sharing_likes' in kwargs: del kwargs['activity_sharing_likes']
if 'activity_sharing_favourites' in kwargs: del kwargs['activity_sharing_favourites']
if 'activity_sharing_comments' in kwargs: del kwargs['activity_sharing_comments']
return super(UserProfileResource, self).obj_update(bundle, skip_errors, **kwargs)
def dehydrate(self, bundle):
#set the "me" user only properties
del bundle.data['activity_sharing']
del bundle.data['activity_sharing_networks']
if bundle.obj.user.id == bundle.request.user.id:
bundle.data['email'] = bundle.obj.email
if bundle.obj.activity_sharing is not None:
bundle.data['activity_share_likes'] = \
(bundle.obj.activity_sharing & UserProfile.ACTIVITY_SHARE_LIKES) != 0
bundle.data['activity_share_favourites'] = \
(bundle.obj.activity_sharing & UserProfile.ACTIVITY_SHARE_FAVOURITES) != 0
bundle.data['activity_share_comments'] = \
(bundle.obj.activity_sharing & UserProfile.ACTIVITY_SHARE_COMMENTS) != 0
else:
bundle.data['activity_share_likes'] = 0
bundle.data['activity_share_favourites'] = 0
bundle.data['activity_share_comments'] = 0
bundle.data['activity_sharing_likes'] = \
self._hydrateBitmapOption(bundle.obj.activity_sharing, UserProfile.ACTIVITY_SHARE_LIKES)
bundle.data['activity_sharing_favourites'] = \
self._hydrateBitmapOption(bundle.obj.activity_sharing, UserProfile.ACTIVITY_SHARE_FAVOURITES)
bundle.data['activity_sharing_comments'] = \
self._hydrateBitmapOption(bundle.obj.activity_sharing, UserProfile.ACTIVITY_SHARE_COMMENTS)
if bundle.obj.activity_sharing_networks is not None:
bundle.data['activity_share_networks_facebook'] = \
(bundle.obj.activity_sharing_networks & UserProfile.ACTIVITY_SHARE_NETWORK_FACEBOOK) != 0
bundle.data['activity_share_networks_twitter'] = \
(bundle.obj.activity_sharing_networks & UserProfile.ACTIVITY_SHARE_NETWORK_TWITTER) != 0
else:
bundle.data['activity_share_networks_facebook'] = 0
bundle.data['activity_share_networks_facebook'] = 0
else:
del bundle.data['activity_sharing']
del bundle.data['activity_sharing_networks']
bundle.data['first_name'] = bundle.obj.first_name
bundle.data['last_name'] = bundle.obj.last_name
bundle.data['activity_sharing_networks_facebook'] = \
self._hydrateBitmapOption(bundle.obj.activity_sharing_networks, UserProfile.ACTIVITY_SHARE_NETWORK_FACEBOOK)
bundle.data['activity_sharing_networks_twitter'] = \
self._hydrateBitmapOption(bundle.obj.activity_sharing_networks, UserProfile.ACTIVITY_SHARE_NETWORK_TWITTER)
return bundle
def hydrate_slug(self, bundle):
if bundle.data['slug'] == '':
bundle.data['slug'] = None
return bundle
class UserResource(BackboneCompatibleResource):
profile = fields.ToOneField(UserProfileResource, attribute='userprofile', related_name='user', full=True)
class Meta:
queryset = User.objects.all()
resource_name = 'user'
excludes = ['is_active', 'is_staff', 'is_superuser', 'password']
authorization = DjangoAuthorization()
authentication = Authentication()
def dehydrate(self, bundle):
if bundle.obj.id != bundle.request.user.id:
del bundle.data['email']
del bundle.data['username']
return bundle

View File

@@ -1,10 +1,10 @@
from django.core.management.base import NoArgsCommand
from spa.models import Mix
class Command(NoArgsCommand):
def handle(self, *args, **options):
candidates = Mix.objects.filter(waveform_generated=False)
for mix in candidates:
print "Deleting: %s" % mix.title
mix.delete()
from django.core.management.base import NoArgsCommand
from spa.models import Mix
class Command(NoArgsCommand):
def handle(self, *args, **options):
candidates = Mix.objects.filter(waveform_generated=False)
for mix in candidates:
print "Deleting: %s" % mix.title
mix.delete()

View File

@@ -1,14 +1,14 @@
import os
import shutil
from django.core.management.base import NoArgsCommand
from spa.models import Mix
class Command(NoArgsCommand):
def handle(self, *args, **options):
candidates = Mix.objects.all()
for mix in candidates:
file = mix.get_absolute_path(prefix="expired/")
if os.path.exists(file):
new = mix.get_absolute_path()
shutil.move(file, new)
import os
import shutil
from django.core.management.base import NoArgsCommand
from spa.models import Mix
class Command(NoArgsCommand):
def handle(self, *args, **options):
candidates = Mix.objects.all()
for mix in candidates:
file = mix.get_absolute_path(prefix="expired/")
if os.path.exists(file):
new = mix.get_absolute_path()
shutil.move(file, new)

View File

@@ -1,39 +1,39 @@
"""
Tightens up response content by removed superflous line breaks and whitespace.
By Doug Van Horn
---- CHANGES ----
v1.1 - 31st May 2011
Cal Leeming [Simplicity Media Ltd]
Modified regex to strip leading/trailing white space from every line, not just those with blank \n.
---- TODO ----
* Ensure whitespace isn't stripped from within <pre> or <code> or <textarea> tags.
"""
import re
class StripWhitespaceMiddleware(object):
"""
Strips leading and trailing whitespace from response content.
"""
def __init__(self):
self.whitespace = re.compile('^\s*\n', re.MULTILINE)
# self.whitespace_lead = re.compile('^\s+', re.MULTILINE)
# self.whitespace_trail = re.compile('\s+$', re.MULTILINE)
def process_response(self, request, response):
if "text" in response['Content-Type']:
if hasattr(self, 'whitespace_lead'):
response.content = self.whitespace_lead.sub('', response.content)
if hasattr(self, 'whitespace_trail'):
response.content = self.whitespace_trail.sub('\n', response.content)
# Uncomment the next line to remove empty lines
if hasattr(self, 'whitespace'):
response.content = self.whitespace.sub('', response.content)
return response
else:
return response
"""
Tightens up response content by removed superflous line breaks and whitespace.
By Doug Van Horn
---- CHANGES ----
v1.1 - 31st May 2011
Cal Leeming [Simplicity Media Ltd]
Modified regex to strip leading/trailing white space from every line, not just those with blank \n.
---- TODO ----
* Ensure whitespace isn't stripped from within <pre> or <code> or <textarea> tags.
"""
import re
class StripWhitespaceMiddleware(object):
"""
Strips leading and trailing whitespace from response content.
"""
def __init__(self):
self.whitespace = re.compile('^\s*\n', re.MULTILINE)
# self.whitespace_lead = re.compile('^\s+', re.MULTILINE)
# self.whitespace_trail = re.compile('\s+$', re.MULTILINE)
def process_response(self, request, response):
if "text" in response['Content-Type']:
if hasattr(self, 'whitespace_lead'):
response.content = self.whitespace_lead.sub('', response.content)
if hasattr(self, 'whitespace_trail'):
response.content = self.whitespace_trail.sub('\n', response.content)
# Uncomment the next line to remove empty lines
if hasattr(self, 'whitespace'):
response.content = self.whitespace.sub('', response.content)
return response
else:
return response

View File

@@ -0,0 +1,219 @@
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Changing field 'UserProfile.user'
db.alter_column(u'spa_userprofile', 'user_id', self.gf('django.db.models.fields.related.OneToOneField')(unique=True, to=orm['auth.User']))
def backwards(self, orm):
# Changing field 'UserProfile.user'
db.alter_column(u'spa_userprofile', 'user_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], unique=True))
models = {
u'auth.group': {
'Meta': {'object_name': 'Group'},
u'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': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
u'auth.permission': {
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
u'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': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
u'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': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
u'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'}),
u'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'})
},
'spa._activity': {
'Meta': {'object_name': '_Activity'},
'date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'uid': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True'})
},
'spa._lookup': {
'Meta': {'object_name': '_Lookup'},
'description': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'spa.chatmessage': {
'Meta': {'object_name': 'ChatMessage'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'message': ('django.db.models.fields.TextField', [], {}),
'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'chat_messages'", 'null': 'True', 'to': "orm['spa.UserProfile']"})
},
'spa.comment': {
'Meta': {'object_name': 'Comment'},
'comment': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'mix': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'comments'", 'null': 'True', 'to': "orm['spa.Mix']"}),
'time_index': ('django.db.models.fields.IntegerField', [], {}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"})
},
'spa.event': {
'Meta': {'object_name': 'Event'},
'attendees': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'attendees'", 'symmetrical': 'False', 'to': u"orm['auth.User']"}),
'date_created': ('django.db.models.fields.DateField', [], {'default': 'datetime.datetime(2013, 4, 24, 0, 0)'}),
'event_date': ('django.db.models.fields.DateField', [], {'default': 'datetime.datetime(2013, 4, 24, 0, 0)'}),
'event_description': ('tinymce.models.HTMLField', [], {}),
'event_recurrence': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['spa.Recurrence']"}),
'event_time': ('django.db.models.fields.TimeField', [], {'default': 'datetime.datetime(2013, 4, 24, 0, 0)'}),
'event_title': ('django.db.models.fields.CharField', [], {'max_length': '250'}),
'event_venue': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['spa.Venue']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'spa.genre': {
'Meta': {'object_name': 'Genre'},
'description': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True'})
},
'spa.label': {
'Meta': {'object_name': 'Label'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'spa.mix': {
'Meta': {'object_name': 'Mix'},
'description': ('django.db.models.fields.TextField', [], {}),
'download_allowed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'download_url': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'duration': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'genres': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['spa.Genre']", 'symmetrical': 'False'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_featured': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'local_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100', 'blank': 'True'}),
'mix_image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'blank': 'True'}),
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
'stream_url': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '50'}),
'uid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '38', 'blank': 'True'}),
'upload_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2013, 4, 24, 0, 0)'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['spa.UserProfile']"}),
'waveform_generated': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
},
'spa.mixdownload': {
'Meta': {'object_name': 'MixDownload', '_ormbases': ['spa._Activity']},
u'_activity_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['spa._Activity']", 'unique': 'True', 'primary_key': 'True'}),
'mix': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'downloads'", 'to': "orm['spa.Mix']"})
},
'spa.mixfavourite': {
'Meta': {'object_name': 'MixFavourite', '_ormbases': ['spa._Activity']},
u'_activity_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['spa._Activity']", 'unique': 'True', 'primary_key': 'True'}),
'mix': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'favourites'", 'to': "orm['spa.Mix']"})
},
'spa.mixlike': {
'Meta': {'object_name': 'MixLike', '_ormbases': ['spa._Activity']},
u'_activity_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['spa._Activity']", 'unique': 'True', 'primary_key': 'True'}),
'mix': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'likes'", 'to': "orm['spa.Mix']"})
},
'spa.mixplay': {
'Meta': {'object_name': 'MixPlay', '_ormbases': ['spa._Activity']},
u'_activity_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['spa._Activity']", 'unique': 'True', 'primary_key': 'True'}),
'mix': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'plays'", 'to': "orm['spa.Mix']"})
},
'spa.purchaselink': {
'Meta': {'object_name': 'PurchaseLink'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'provider': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'track': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'purchase_link'", 'to': "orm['spa.Tracklist']"}),
'url': ('django.db.models.fields.URLField', [], {'max_length': '200'})
},
'spa.recurrence': {
'Meta': {'object_name': 'Recurrence', '_ormbases': ['spa._Lookup']},
u'_lookup_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['spa._Lookup']", 'unique': 'True', 'primary_key': 'True'})
},
'spa.release': {
'Meta': {'object_name': 'Release'},
'embed_code': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'release_artist': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'release_date': ('django.db.models.fields.DateField', [], {'default': 'datetime.datetime(2013, 4, 24, 0, 0)'}),
'release_description': ('django.db.models.fields.TextField', [], {}),
'release_image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'blank': 'True'}),
'release_label': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['spa.Label']"}),
'release_title': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['spa.UserProfile']"})
},
'spa.releaseaudio': {
'Meta': {'object_name': 'ReleaseAudio'},
'description': ('django.db.models.fields.TextField', [], {}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'local_file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
'release': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'release_audio'", 'null': 'True', 'to': "orm['spa.Release']"})
},
'spa.tracklist': {
'Meta': {'object_name': 'Tracklist'},
'artist': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'description': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'index': ('django.db.models.fields.SmallIntegerField', [], {}),
'label': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'mix': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'tracklist'", 'to': "orm['spa.Mix']"}),
'remixer': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'timeindex': ('django.db.models.fields.TimeField', [], {'null': 'True'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
},
'spa.userfollows': {
'Meta': {'object_name': 'UserFollows'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'user_from': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'followers'", 'unique': 'True', 'to': "orm['spa.UserProfile']"}),
'user_to': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'following'", 'unique': 'True', 'to': "orm['spa.UserProfile']"})
},
'spa.userprofile': {
'Meta': {'object_name': 'UserProfile'},
'activity_sharing': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'activity_sharing_networks': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'avatar_image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'blank': 'True'}),
'avatar_type': ('django.db.models.fields.CharField', [], {'default': "'social'", 'max_length': '15'}),
'description': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'blank': 'True'}),
'display_name': ('django.db.models.fields.CharField', [], {'max_length': '35', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'slug': ('django.db.models.fields.SlugField', [], {'default': 'None', 'max_length': '50', 'null': 'True', 'blank': 'True'}),
'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'userprofile'", 'unique': 'True', 'to': u"orm['auth.User']"})
},
'spa.venue': {
'Meta': {'object_name': 'Venue'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}),
'venue_address': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
'venue_image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'blank': 'True'}),
'venue_name': ('django.db.models.fields.CharField', [], {'max_length': '250'})
}
}
complete_apps = ['spa']

View File

@@ -30,7 +30,7 @@ class UserProfile(_BaseModel):
ACTIVITY_SHARE_NETWORK_TWITTER = 2
# This field is required.
user = models.ForeignKey(User, unique=True)
user = models.OneToOneField(User, unique=True, related_name='userprofile')
avatar_type = models.CharField(max_length=15, default='social')
avatar_image = models.ImageField(blank=True, upload_to=avatar_name)
display_name = models.CharField(blank=True, max_length=35)
@@ -43,19 +43,15 @@ class UserProfile(_BaseModel):
def __unicode__(self):
return "%s - %s" % (self.user.get_full_name(), self.slug)
def save(self, force_insert=False, force_update=False, using=None):
def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
"""
Save Photo after ensuring it is not blank. Resize as needed.
"""
if not force_insert and not self.id:
return
if self.slug is None or self.slug == '':
self.slug = unique_slugify(self, self.get_username())
print "Slugified: %s" % self.slug
return super(UserProfile, self).save(force_insert, force_update, using)
return super(UserProfile, self).save(force_insert, force_update, using, update_fields)
def get_username(self):
return self.user.username

View File

@@ -1 +1 @@
__author__ = 'fergalm'
__author__ = 'fergalm'

View File

@@ -7,6 +7,7 @@ from spa.models import UserProfile
__author__ = 'fergalm'
@not_minified_response
def get_template(request, template_name):
#Temporary hack here to create user profiles for zombie users
@@ -29,6 +30,9 @@ def get_template_ex(request, template_name):
def get_javascript(request, template_name):
localsettings.JS_SETTINGS.update({
'CURRENT_USER_ID': request.user.id or -1
})
return render_to_response(
'javascript/%s.js' % template_name,
localsettings.JS_SETTINGS,

View File

@@ -1,91 +1,91 @@
.bootstrap-timepicker.dropdown-menu {
border-radius: 4px 4px 4px 4px;
display: none;
left: 0;
margin-top: 1px;
padding: 4px;
top: 0;
}
.bootstrap-timepicker.dropdown-menu.open {
display: inline-block;
}
.bootstrap-timepicker.dropdown-menu:before {
border-bottom: 7px solid rgba(0, 0, 0, 0.2);
border-left: 7px solid transparent;
border-right: 7px solid transparent;
content: "";
left: 6px;
position: absolute;
top: -7px;
}
.bootstrap-timepicker.dropdown-menu:after {
border-bottom: 6px solid #FFFFFF;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
content: "";
left: 7px;
position: absolute;
top: -6px;
}
.bootstrap-timepicker.modal {
margin-left: -100px;
margin-top: 0;
top: 30%;
width: 200px;
}
.bootstrap-timepicker.modal .modal-content {
padding: 0;
}
.bootstrap-timepicker table {
margin: 0;
width: 100%;
}
.bootstrap-timepicker table td {
height: 30px;
margin: 0;
padding: 2px;
text-align: center;
width: 49%;
}
.bootstrap-timepicker table.show-meridian td, .bootstrap-timepicker table.show-seconds td {
width: 32%;
}
.bootstrap-timepicker table.show-seconds.show-meridian td {
width: 23.5%;
}
.bootstrap-timepicker table td.separator {
width: 2% !important;
}
.bootstrap-timepicker table td span {
width: 100%;
}
.bootstrap-timepicker table td a {
border: 1px solid transparent;
display: inline-block;
margin: 0;
outline: 0 none;
padding: 8px 0;
width: 90%;
}
.bootstrap-timepicker table td a:hover {
background-color: #EEEEEE;
border-color: #DDDDDD;
border-radius: 4px 4px 4px 4px;
}
.bootstrap-timepicker table td a i {
margin-top: 2px;
}
.bootstrap-timepicker table td input {
margin: 0;
text-align: center;
width: 25px;
}
.bootstrap-timepicker-component .add-on {
cursor: pointer;
}
.bootstrap-timepicker-component .add-on i {
display: block;
height: 16px;
width: 16px;
}
.bootstrap-timepicker.dropdown-menu {
border-radius: 4px 4px 4px 4px;
display: none;
left: 0;
margin-top: 1px;
padding: 4px;
top: 0;
}
.bootstrap-timepicker.dropdown-menu.open {
display: inline-block;
}
.bootstrap-timepicker.dropdown-menu:before {
border-bottom: 7px solid rgba(0, 0, 0, 0.2);
border-left: 7px solid transparent;
border-right: 7px solid transparent;
content: "";
left: 6px;
position: absolute;
top: -7px;
}
.bootstrap-timepicker.dropdown-menu:after {
border-bottom: 6px solid #FFFFFF;
border-left: 6px solid transparent;
border-right: 6px solid transparent;
content: "";
left: 7px;
position: absolute;
top: -6px;
}
.bootstrap-timepicker.modal {
margin-left: -100px;
margin-top: 0;
top: 30%;
width: 200px;
}
.bootstrap-timepicker.modal .modal-content {
padding: 0;
}
.bootstrap-timepicker table {
margin: 0;
width: 100%;
}
.bootstrap-timepicker table td {
height: 30px;
margin: 0;
padding: 2px;
text-align: center;
width: 49%;
}
.bootstrap-timepicker table.show-meridian td, .bootstrap-timepicker table.show-seconds td {
width: 32%;
}
.bootstrap-timepicker table.show-seconds.show-meridian td {
width: 23.5%;
}
.bootstrap-timepicker table td.separator {
width: 2% !important;
}
.bootstrap-timepicker table td span {
width: 100%;
}
.bootstrap-timepicker table td a {
border: 1px solid transparent;
display: inline-block;
margin: 0;
outline: 0 none;
padding: 8px 0;
width: 90%;
}
.bootstrap-timepicker table td a:hover {
background-color: #EEEEEE;
border-color: #DDDDDD;
border-radius: 4px 4px 4px 4px;
}
.bootstrap-timepicker table td a i {
margin-top: 2px;
}
.bootstrap-timepicker table td input {
margin: 0;
text-align: center;
width: 25px;
}
.bootstrap-timepicker-component .add-on {
cursor: pointer;
}
.bootstrap-timepicker-component .add-on i {
display: block;
height: 16px;
width: 16px;
}

View File

@@ -391,3 +391,14 @@ div.event-content td {
position: relative;
top: 1px;
}
.dss-option-table td{
text-align: center;
vertical-align: middle;
}
#div_avatar_image_upload{
width: 100%;
text-align: center;
}
#div_avatar_image{
display: inline-block;
}

View File

@@ -221,14 +221,16 @@ var AppRouter = Backbone.Router.extend({
alert("Connecting accounts");
},
userDetails: function () {
var user = new User();
var user = new User({
id: com.podnoms.settings.currentUser
});
$('#site-content-fill').html('');
user.fetch({
success: function () {
var content = new UserView({
model: user
}).el;
$('#content').html(content);
});
$('#content').html(content.render().el);
}
});
}

View File

@@ -1,19 +1,19 @@
/** @license
----------------------------------------------
Copyright (c) 2012, Fergal Moran. All rights reserved.
Code provided under the BSD License:
*/
var Activity = DSSModel.extend({
urlRoot:com.podnoms.settings.urlRoot + "activity/"
});
var ActivityCollection = TastypieCollection.extend({
model: Activity,
url:com.podnoms.settings.urlRoot + "activity/",
comparator: function (activity) {
return -activity.get("id");
}
/** @license
----------------------------------------------
Copyright (c) 2012, Fergal Moran. All rights reserved.
Code provided under the BSD License:
*/
var Activity = DSSModel.extend({
urlRoot:com.podnoms.settings.urlRoot + "activity/"
});
var ActivityCollection = TastypieCollection.extend({
model: Activity,
url:com.podnoms.settings.urlRoot + "activity/",
comparator: function (activity) {
return -activity.get("id");
}
});

View File

@@ -8,8 +8,17 @@
*/
var User = DSSModel.extend({
urlRoot:com.podnoms.settings.urlRoot + "user/",
isValid:function () {
isValid: function () {
this.errors = {};
return "";
},
avatarGravatar: function(){
return this.get('profile').avatar_type == 'gravatar';
},
avatarSocial: function(){
return this.get('profile').avatar_type == 'social';
},
avatarCustom: function(){
return this.get('profile').avatar_type == 'custom';
}
});

View File

@@ -235,7 +235,7 @@ window.MixCreateView = DSSEditableView.extend({
},
checkRedirect: function () {
if (this.state == 2) {
Backbone.history.navigate('/mix/' + this.model.get('id'), {trigger: true});
Backbone.history.navigate('/mix/' + this.model.get('slug'), {trigger: true});
}
},
initialize: function () {

View File

@@ -6,77 +6,81 @@
Code provided under the BSD License:
*/
window.UserView = DSSEditableView.extend({
events:{
"click #save-changes":"saveChanges",
"click input[type=radio]":"selectAvatar",
"change input":"changed",
"change textarea":"changed",
"change select":"changed",
"change .switch":"changeSwitch"
},
initialize:function () {
this.render();
},
render:function () {
var model = this.model;
$(this.el).html(this.template({"item":this.model.toJSON()}));
$('.switch', this.el).each(function (item) {
var val = model.get(this.id) ? "on" : "off";
$(this).attr('checked', val == "on");
$(this).iphoneSwitch(
val,
function (obj) {
},
function (obj) {
},
{
speed:250,
use_images:true,
track_bg_color:'#333',
sync_checkbox:true
}
);
});
$("#div_avatar_image", this.el).hide();
var avatarType = this.model.get('avatar_type');
if (!com.podnoms.utils.isEmpty(avatarType))
$('#' + this.model.get('avatar_type'), this.el).attr('checked', 'checked');
//console.clear();
UserView = DSSEditableView.extend({
events: {
"click #save-changes": "saveChanges",
"change input[type=radio]": "selectAvatar"
},
initialize: function () {
},
render: function () {
ich.addTemplate('user', this.template());
var renderedTemplate = ich.user(this.model.toJSON());
$(this.el).html(renderedTemplate);
$("#div_avatar_image_upload", this.el).hide();
var avatarType = this.model.get('profile').avatar_type;
if (!com.podnoms.utils.isEmpty(avatarType)){
$('#avatar_' + avatarType, this.el).attr('checked', true);
if (avatarType == 'custom') {
$("#div_avatar_image_upload", this.el).show();
$('#file_upload').uploadifive({
'uploadScript': 'ajax/upload_avatar_image/'
});
}
}
return this;
},
saveChanges:function () {
var model = this.model;
saveChanges: function () {
var data = Backbone.Syphon.serialize(this);
this.model.set(data);
var ref = this;
this._saveChanges({
success:function () {
com.podnoms.utils.showAlert("Success", "Successfully updated yourself");
Backbone.history.navigate('/', {trigger:true});
success: function () {
if (ref.model.get('profile').avatar_type == 'custom'){
$.ajaxFileUpload({
url: '/ajax/upload_avatar_image/',
secureuri: false,
fileElementId: 'avatar_image',
success: function (data, status) {
if (typeof(data.error) != 'undefined') {
if (data.error != '') {
alert(data.error);
} else {
alert(data.msg);
}
} else {
com.podnoms.utils.showAlert("Success", "Successfully updated yourself");
Backbone.history.navigate('/', {trigger:true});
}
},
error: function (data, status, e) {
alert(e);
}
});
}else{
com.podnoms.utils.showAlert("Success", "Successfully updated yourself");
Backbone.history.navigate('/', {trigger:true});
}
},
error:function () {
error: function () {
com.podnoms.utils.showError("Error", "There was an error updating your info. Please try again later.");
}
});
return false;
},
changeSwitch:function (evt) {
var bit = $(evt.currentTarget).data('bitflag');
var coalesce = $(evt.currentTarget).data('coalesce');
if ($(evt.currentTarget).attr('checked')) {
this.model.set(coalesce, this.model.get(coalesce) | bit);
} else {
this.model.set(coalesce, this.model.get(coalesce) & ~bit);
}
this.model.save();
},
selectAvatar:function (evt) {
selectAvatar: function (evt) {
var type = $(evt.currentTarget).val();
this.model.set('avatar_type', type);
if (type == 'custom') {
$("#div_avatar_image", this.el).show();
$("#div_avatar_image_upload", this.el).show();
$('#file_upload').uploadifive({
'uploadScript':'ajax/upload_avatar_image/'
'uploadScript': 'ajax/upload_avatar_image/'
});
}else{
$("#div_avatar_image_upload", this.el).hide();
}
}
});

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -56,7 +56,7 @@ window.DSSModel = window.TastypieModel.extend({
}
});
window.DSSEditableView = Backbone.View.extend({
window.DSSEditableView = Backbone.Marionette.ItemView.extend({
events: {
"change input": "changed",
"change textarea": "changed"
@@ -71,6 +71,9 @@ window.DSSEditableView = Backbone.View.extend({
}
},
changed: function (evt) {
//change handler for the form to update the model
//with the new values
return;
var changed = evt.currentTarget;
//$("#" + changed.id)
if (!com.podnoms.utils.isEmpty(changed.id)) {

View File

@@ -0,0 +1,471 @@
// Backbone.Syphon, v0.4.1
// Copyright (c)2012 Derick Bailey, Muted Solutions, LLC.
// Distributed under MIT license
// http://github.com/derickbailey/backbone.syphon
Backbone.Syphon = (function (Backbone, $, _) {
var Syphon = {};
// Ignore Element Types
// --------------------
// Tell Syphon to ignore all elements of these types. You can
// push new types to ignore directly in to this array.
Syphon.ignoredTypes = ["button", "submit", "reset", "fieldset"];
// Syphon
// ------
// Get a JSON object that represents
// all of the form inputs, in this view.
// Alternately, pass a form element directly
// in place of the view.
Syphon.serialize = function (view, options) {
var data = {};
// Build the configuration
var config = buildConfig(options);
// Get all of the elements to process
var elements = getInputElements(view, config);
// Process all of the elements
_.each(elements, function (el) {
var $el = $(el);
var type = getElementType($el);
// Get the key for the input
var keyExtractor = config.keyExtractors.get(type);
var key = keyExtractor($el);
// Get the value for the input
var inputReader = config.inputReaders.get(type);
var value = inputReader($el);
// Get the key assignment validator and make sure
// it's valid before assigning the value to the key
var validKeyAssignment = config.keyAssignmentValidators.get(type);
if (validKeyAssignment($el, key, value)) {
var keychain = config.keySplitter(key);
data = assignKeyValue(data, keychain, value);
}
});
// Done; send back the results.
return data;
};
// Use the given JSON object to populate
// all of the form inputs, in this view.
// Alternately, pass a form element directly
// in place of the view.
Syphon.deserialize = function (view, data, options) {
// Build the configuration
var config = buildConfig(options);
// Get all of the elements to process
var elements = getInputElements(view, config);
// Flatten the data structure that we are deserializing
var flattenedData = flattenData(config, data);
// Process all of the elements
_.each(elements, function (el) {
var $el = $(el);
var type = getElementType($el);
// Get the key for the input
var keyExtractor = config.keyExtractors.get(type);
var key = keyExtractor($el);
// Get the input writer and the value to write
var inputWriter = config.inputWriters.get(type);
var value = flattenedData[key];
// Write the value to the input
inputWriter($el, value);
});
};
// Helpers
// -------
// Retrieve all of the form inputs
// from the form
var getInputElements = function (view, config) {
var form = getForm(view);
var elements = form.elements;
elements = _.reject(elements, function (el) {
var reject;
var type = getElementType(el);
var extractor = config.keyExtractors.get(type);
var identifier = extractor($(el));
var foundInIgnored = _.include(config.ignoredTypes, type);
var foundInInclude = _.include(config.include, identifier);
var foundInExclude = _.include(config.exclude, identifier);
if (foundInInclude) {
reject = false;
} else {
if (config.include) {
reject = true;
} else {
reject = (foundInExclude || foundInIgnored);
}
}
return reject;
});
return elements;
};
// Determine what type of element this is. It
// will either return the `type` attribute of
// an `<input>` element, or the `tagName` of
// the element when the element is not an `<input>`.
var getElementType = function (el) {
var typeAttr;
var $el = $(el);
var tagName = $el[0].tagName;
var type = tagName;
if (tagName.toLowerCase() === "input") {
typeAttr = $el.attr("type");
if (typeAttr) {
type = typeAttr;
} else {
type = "text";
}
}
// Always return the type as lowercase
// so it can be matched to lowercase
// type registrations.
return type.toLowerCase();
};
// If a form element is given, just return it.
// Otherwise, get the form element from the view.
var getForm = function (viewOrForm) {
if (_.isUndefined(viewOrForm.$el) && viewOrForm.tagName.toLowerCase() === 'form') {
return viewOrForm;
} else {
return viewOrForm.$el.is("form") ? viewOrForm.el : viewOrForm.$("form")[0];
}
};
// Build a configuration object and initialize
// default values.
var buildConfig = function (options) {
var config = _.clone(options) || {};
config.ignoredTypes = _.clone(Syphon.ignoredTypes);
config.inputReaders = config.inputReaders || Syphon.InputReaders;
config.inputWriters = config.inputWriters || Syphon.InputWriters;
config.keyExtractors = config.keyExtractors || Syphon.KeyExtractors;
config.keySplitter = config.keySplitter || Syphon.KeySplitter;
config.keyJoiner = config.keyJoiner || Syphon.KeyJoiner;
config.keyAssignmentValidators = config.keyAssignmentValidators || Syphon.KeyAssignmentValidators;
return config;
};
// Assigns `value` to a parsed JSON key.
//
// The first parameter is the object which will be
// modified to store the key/value pair.
//
// The second parameter accepts an array of keys as a
// string with an option array containing a
// single string as the last option.
//
// The third parameter is the value to be assigned.
//
// Examples:
//
// `["foo", "bar", "baz"] => {foo: {bar: {baz: "value"}}}`
//
// `["foo", "bar", ["baz"]] => {foo: {bar: {baz: ["value"]}}}`
//
// When the final value is an array with a string, the key
// becomes an array, and values are pushed in to the array,
// allowing multiple fields with the same name to be
// assigned to the array.
var assignKeyValue = function (obj, keychain, value) {
if (!keychain) {
return obj;
}
var key = keychain.shift();
// build the current object we need to store data
if (!obj[key]) {
obj[key] = _.isArray(key) ? [] : {};
}
// if it's the last key in the chain, assign the value directly
if (keychain.length === 0) {
if (_.isArray(obj[key])) {
obj[key].push(value);
} else {
obj[key] = value;
}
}
// recursive parsing of the array, depth-first
if (keychain.length > 0) {
assignKeyValue(obj[key], keychain, value);
}
return obj;
};
// Flatten the data structure in to nested strings, using the
// provided `KeyJoiner` function.
//
// Example:
//
// This input:
//
// ```js
// {
// widget: "wombat",
// foo: {
// bar: "baz",
// baz: {
// quux: "qux"
// },
// quux: ["foo", "bar"]
// }
// }
// ```
//
// With a KeyJoiner that uses [ ] square brackets,
// should produce this output:
//
// ```js
// {
// "widget": "wombat",
// "foo[bar]": "baz",
// "foo[baz][quux]": "qux",
// "foo[quux]": ["foo", "bar"]
// }
// ```
var flattenData = function (config, data, parentKey) {
var flatData = {};
_.each(data, function (value, keyName) {
var hash = {};
// If there is a parent key, join it with
// the current, child key.
if (parentKey) {
keyName = config.keyJoiner(parentKey, keyName);
}
if (_.isArray(value)) {
keyName += "[]";
hash[keyName] = value;
} else if (_.isObject(value)) {
hash = flattenData(config, value, keyName);
} else {
hash[keyName] = value;
}
// Store the resulting key/value pairs in the
// final flattened data object
_.extend(flatData, hash);
});
return flatData;
};
return Syphon;
})(Backbone, jQuery, _);
// Type Registry
// -------------
// Type Registries allow you to register something to
// an input type, and retrieve either the item registered
// for a specific type or the default registration
Backbone.Syphon.TypeRegistry = function () {
this.registeredTypes = {};
};
// Borrow Backbone's `extend` keyword for our TypeRegistry
Backbone.Syphon.TypeRegistry.extend = Backbone.Model.extend;
_.extend(Backbone.Syphon.TypeRegistry.prototype, {
// Get the registered item by type. If nothing is
// found for the specified type, the default is
// returned.
get: function (type) {
var item = this.registeredTypes[type];
if (!item) {
item = this.registeredTypes["default"];
}
return item;
},
// Register a new item for a specified type
register: function (type, item) {
this.registeredTypes[type] = item;
},
// Register a default item to be used when no
// item for a specified type is found
registerDefault: function (item) {
this.registeredTypes["default"] = item;
},
// Remove an item from a given type registration
unregister: function (type) {
if (this.registeredTypes[type]) {
delete this.registeredTypes[type];
}
}
});
// Key Extractors
// --------------
// Key extractors produce the "key" in `{key: "value"}`
// pairs, when serializing.
Backbone.Syphon.KeyExtractorSet = Backbone.Syphon.TypeRegistry.extend();
// Built-in Key Extractors
Backbone.Syphon.KeyExtractors = new Backbone.Syphon.KeyExtractorSet();
// The default key extractor, which uses the
// input element's "id" attribute
Backbone.Syphon.KeyExtractors.registerDefault(function ($el) {
return $el.prop("name");
});
// Input Readers
// -------------
// Input Readers are used to extract the value from
// an input element, for the serialized object result
Backbone.Syphon.InputReaderSet = Backbone.Syphon.TypeRegistry.extend();
// Built-in Input Readers
Backbone.Syphon.InputReaders = new Backbone.Syphon.InputReaderSet();
// The default input reader, which uses an input
// element's "value"
Backbone.Syphon.InputReaders.registerDefault(function ($el) {
return $el.val();
});
// Checkbox reader, returning a boolean value for
// whether or not the checkbox is checked.
Backbone.Syphon.InputReaders.register("checkbox", function ($el) {
var checked = $el.prop("checked");
return checked;
});
// Input Writers
// -------------
// Input Writers are used to insert a value from an
// object into an input element.
Backbone.Syphon.InputWriterSet = Backbone.Syphon.TypeRegistry.extend();
// Built-in Input Writers
Backbone.Syphon.InputWriters = new Backbone.Syphon.InputWriterSet();
// The default input writer, which sets an input
// element's "value"
Backbone.Syphon.InputWriters.registerDefault(function ($el, value) {
$el.val(value);
});
// Checkbox writer, set whether or not the checkbox is checked
// depending on the boolean value.
Backbone.Syphon.InputWriters.register("checkbox", function ($el, value) {
$el.prop("checked", value);
});
// Radio button writer, set whether or not the radio button is
// checked. The button should only be checked if it's value
// equals the given value.
Backbone.Syphon.InputWriters.register("radio", function ($el, value) {
$el.prop("checked", $el.val() === value);
});
// Key Assignment Validators
// -------------------------
// Key Assignment Validators are used to determine whether or not a
// key should be assigned to a value, after the key and value have been
// extracted from the element. This is the last opportunity to prevent
// bad data from getting serialized to your object.
Backbone.Syphon.KeyAssignmentValidatorSet = Backbone.Syphon.TypeRegistry.extend();
// Build-in Key Assignment Validators
Backbone.Syphon.KeyAssignmentValidators = new Backbone.Syphon.KeyAssignmentValidatorSet();
// Everything is valid by default
Backbone.Syphon.KeyAssignmentValidators.registerDefault(function () {
return true;
});
// But only the "checked" radio button for a given
// radio button group is valid
Backbone.Syphon.KeyAssignmentValidators.register("radio", function ($el, key, value) {
return $el.prop("checked");
});
// Backbone.Syphon.KeySplitter
// ---------------------------
// This function is used to split DOM element keys in to an array
// of parts, which are then used to create a nested result structure.
// returning `["foo", "bar"]` results in `{foo: { bar: "value" }}`.
//
// Override this method to use a custom key splitter, such as:
// `<input name="foo.bar.baz">`, `return key.split(".")`
Backbone.Syphon.KeySplitter = function (key) {
var matches = key.match(/[^\[\]]+/g);
if (key.indexOf("[]") === key.length - 2) {
lastKey = matches.pop();
matches.push([lastKey]);
}
return matches;
}
// Backbone.Syphon.KeyJoiner
// -------------------------
// Take two segments of a key and join them together, to create the
// de-normalized key name, when deserializing a data structure back
// in to a form.
//
// Example:
//
// With this data strucutre `{foo: { bar: {baz: "value", quux: "another"} } }`,
// the key joiner will be called with these parameters, and assuming the
// join happens with "[ ]" square brackets, the specified output:
//
// `KeyJoiner("foo", "bar")` //=> "foo[bar]"
// `KeyJoiner("foo[bar]", "baz")` //=> "foo[bar][baz]"
// `KeyJoiner("foo[bar]", "quux")` //=> "foo[bar][quux]"
Backbone.Syphon.KeyJoiner = function (parentKey, childKey) {
return parentKey + "[" + childKey + "]";
}

File diff suppressed because one or more lines are too long

View File

@@ -26,7 +26,6 @@
</head>
<body>
{% include 'inc/analytics.html' %}
{% include 'inc/facebook_init.html' %}
{% include 'inc/ancient_browser.html' %}
<div id="header"></div>
<div class="container-fluid">
@@ -71,6 +70,8 @@
<script src="{{ STATIC_URL }}js/libs/ICanHaz.js"></script>
<script src="{{ STATIC_URL }}js/libs/select2.js"></script>
<script src="{{ STATIC_URL }}js/libs/backbone/backbone.js"></script>
<script src="{{ STATIC_URL }}js/libs/backbone/backbone.marionette.js"></script>
<script src="{{ STATIC_URL }}js/libs/backbone/backbone.syphon.js"></script>
<script src="{{ STATIC_URL }}js/libs/backbone/backbone-tastypie.js"></script>
<script src="{{ STATIC_URL }}js/libs/backbone/backbone.mine.js"></script>
<script src="{{ STATIC_URL }}js/libs/backbone/backbone.infiniscroll.js"></script>

View File

@@ -1,4 +1,4 @@
User-agent: *
Disallow:
Disallow: /media/
Disallow: /ajax/
User-agent: *
Disallow:
Disallow: /media/
Disallow: /ajax/

View File

@@ -1,22 +1,22 @@
<!doctype html>
<html lang="en">
<head prefix="og: http://ogp.me/ns#">
<meta charset="utf-8">
<title>Two structured audio properties</title>
<meta property="og:title" content='Brawther "back to basics"'>
<meta property="og:site_name" content="Deep South Sounds">
<meta property="og:type" content="audio.song">
<meta property="og:url" content="http://examples.opengraphprotocol.us/audio-array.html">
<meta property="og:image"
content="http://ext-test.deepsouthsounds.com:8000/media/mix-images/Deepsouthsounds/brawther.jpg">
<meta property="og:image:width" content="50">
<meta property="og:image:height" content="50">
<meta property="og:image:type" content="image/png">
<meta property="og:audio"
content="http://ext-test.deepsouthsounds.com:8000/media/mixes/Deepsouthsounds/Brawther - BackToBasics Podcast 01.mp3">
<meta property="og:audio:type" content="audio/mpeg">
</head>
<body>
<p>Audio property with type declaration as structured property.</p>
</body>
<!doctype html>
<html lang="en">
<head prefix="og: http://ogp.me/ns#">
<meta charset="utf-8">
<title>Two structured audio properties</title>
<meta property="og:title" content='Brawther "back to basics"'>
<meta property="og:site_name" content="Deep South Sounds">
<meta property="og:type" content="audio.song">
<meta property="og:url" content="http://examples.opengraphprotocol.us/audio-array.html">
<meta property="og:image"
content="http://ext-test.deepsouthsounds.com:8000/media/mix-images/Deepsouthsounds/brawther.jpg">
<meta property="og:image:width" content="50">
<meta property="og:image:height" content="50">
<meta property="og:image:type" content="image/png">
<meta property="og:audio"
content="http://ext-test.deepsouthsounds.com:8000/media/mixes/Deepsouthsounds/Brawther - BackToBasics Podcast 01.mp3">
<meta property="og:audio:type" content="audio/mpeg">
</head>
<body>
<p>Audio property with type declaration as structured property.</p>
</body>
</html>

View File

@@ -1,22 +1,22 @@
<!doctype html>
<html lang="en">
<head prefix="og: http://ogp.me/ns#">
<meta charset="utf-8">
<title>Two structured audio properties</title>
<meta property="og:title" content='Brawther "back to basics"'>
<meta property="og:site_name" content="Deep South Sounds">
<meta property="og:type" content="audio.song">
<meta property="og:url" content="http://ext-test.deepsouthsounds.com:8000/static/html/test1.html">
<meta property="og:image"
content="http://ext-test.deepsouthsounds.com:8000/media/mix-images/Deepsouthsounds/brawther.jpg">
<meta property="og:image:width" content="50">
<meta property="og:image:height" content="50">
<meta property="og:image:type" content="image/png">
<meta property="og:audio"
content="http://ext-test.deepsouthsounds.com:8000/media/mixes/Deepsouthsounds/Brawther - BackToBasics Podcast 01.mp3">
<meta property="og:audio:type" content="audio/mpeg">
</head>
<body>
<p>Audio property with type declaration as structured property.</p>
</body>
<!doctype html>
<html lang="en">
<head prefix="og: http://ogp.me/ns#">
<meta charset="utf-8">
<title>Two structured audio properties</title>
<meta property="og:title" content='Brawther "back to basics"'>
<meta property="og:site_name" content="Deep South Sounds">
<meta property="og:type" content="audio.song">
<meta property="og:url" content="http://ext-test.deepsouthsounds.com:8000/static/html/test1.html">
<meta property="og:image"
content="http://ext-test.deepsouthsounds.com:8000/media/mix-images/Deepsouthsounds/brawther.jpg">
<meta property="og:image:width" content="50">
<meta property="og:image:height" content="50">
<meta property="og:image:type" content="image/png">
<meta property="og:audio"
content="http://ext-test.deepsouthsounds.com:8000/media/mixes/Deepsouthsounds/Brawther - BackToBasics Podcast 01.mp3">
<meta property="og:audio:type" content="audio/mpeg">
</head>
<body>
<p>Audio property with type declaration as structured property.</p>
</body>
</html>

View File

@@ -1,15 +1,15 @@
<div class="activity-block">
<a href="">
<img class="image-avatar-small" src="<%= item.avatar_image %>" alt="">
</a>
<div class="activity-details">
<a class="activity-user" href="<%= item.user_url %>">
<%= item.user_name %>
</a>
<blockquote class="pull-right">
<p><%= item.activity %></p>
<small><%= item.date_created %></small>
</blockquote>
</div>
</div>
<div class="activity-block">
<a href="">
<img class="image-avatar-small" src="<%= item.avatar_image %>" alt="">
</a>
<div class="activity-details">
<a class="activity-user" href="<%= item.user_url %>">
<%= item.user_name %>
</a>
<blockquote class="pull-right">
<p><%= item.activity %></p>
<small><%= item.date_created %></small>
</blockquote>
</div>
</div>

View File

@@ -11,6 +11,7 @@ com.podnoms.settings = {
volume: '{{ DEFAULT_AUDIO_VOLUME }}',
smDebugMode: '{{ SM_DEBUG_MODE }}',
drawTimelineOnMix: false,
currentUser: {{ CURRENT_USER_ID }},
/** simple helper to take an api JSON object and initialise a player item */
setupPlayer: function (data, id) {
com.podnoms.player.setupPlayer({

View File

@@ -1,33 +1,34 @@
{% verbatim %}
<div id="title-area">
<p class="page-header">User details</p>
</div>
<div id="layout-area" class="hero-well">
<div class="row">
<form enctype="multipart/form-data" method="post" class="form-horizontal" id="id-new-user-form">
{% csrf_token %}
<input type="hidden" name="profile[id]" value="{{ profile.id }}"/>
<fieldset>
<div class="span6">
<div class="clearfix control-group" id="div_display_name">
<label class="control-label" for="display_name">Display name</label>
<div class="controls">
<input type="text" maxlength="75" name="email" class="textinput textInput" id="display_name"
value="<%= item.display_name %>">
<input type="text" maxlength="75" name="profile[display_name]" class="textinput textInput"
id="profile[display_name]"
value="{{ profile.display_name }}">
</div>
</div>
<div class="clearfix control-group" id="div_slug">
<label class="control-label" for="slug">Profile Slug</label>
<div class="controls">
<input type="text" maxlength="75" name="email" class="textinput textInput" id="slug"
value="<%= item.slug %>">
<input type="text" maxlength="75" name="profile[slug]" class="textinput textInput"
id="profile[slug]"
value="{{ profile.slug }}">
</div>
</div>
<div class="clearfix control-group" id="div_email">
<label class="control-label" for="email">E-mail address</label>
<div class="controls">
<input type="text" maxlength="75" name="email" class="textinput textInput" id="email"
value="{{ user.email }}">
value="{{ email }}">
</div>
</div>
<div class="clearfix control-group" id="div_first_name">
@@ -35,85 +36,108 @@
<div class="controls">
<input type="text" maxlength="30" name="first_name" class="textinput textInput"
id="first_name"
value="{{ user.first_name }}">
value="{{ first_name }}">
</div>
</div>
<div class="clearfix control-group" id="div_last_name">
<label class="control-label " for="last_name">Last name</label>
<div class="controls">
<input type="text" maxlength="30" name="last_name" class="textinput textInput"
id="last_name"
value="{{ user.last_name }}">
value="{{ last_name }}">
</div>
</div>
<div class="clearfix control-group" id="div_avatar_image_select">
<label class="control-label requiredField" for="avatar_image_select_0">Avatar Image<span
class="asteriskField">*</span></label>
<div class="controls">
<label class="radio">
Use gravatar image.
<input type="radio" value="gravatar" id="gravatar" name="avatar_type">
</label>
<label class="radio">
Use Twitter/Facebook image.
<input type="radio" value="social" id="social" name="avatar_type">
</label>
<label class="radio">
Use custom image (upload below).
<input type="radio" value="custom" id="custom" name="avatar_type">
</label>
<label class="radio">Use gravatar image.<input type="radio" id='avatar_gravatar' value='gravatar' name="profile[avatar_type]" /></label>
<label class="radio">Use Twitter/Facebook image.<input type="radio" id='avatar_social' value='social' name="profile[avatar_type]" /></label>
<label class="radio">Use custom image.<input type="radio" id='avatar_custom' value='custom' name="profile[avatar_type]" /></label>
</div>
</div>
<div class="clearfix control-group" id="div_avatar_image">
<div class="controls">
<form action="/ajax/file-upload">
{% csrf_token %}
<input id="file_upload" type="file" name="file_upload"/>
</form>
<div class="clearfix control-group" id="div_avatar_image_upload">
<label class="control-label requiredField" for="fileupload-new">Upload image<span
class="asteriskField">*</span></label>
<div class="fileupload fileupload-new pull-left" data-provides="fileupload"
id="div_avatar_image">
<div class="fileupload-new thumbnail" style="width: 200px; height: 150px;">
<img src="{{ profile[avatar_image] }}"/>
</div>
<div class="fileupload-preview fileupload-exists thumbnail"
style="max-width: 200px; max-height: 150px; line-height: 20px;">
</div>
<div>
<span class="btn btn-file">
<span class="fileupload-new">
Select image
</span>
<span class="fileupload-exists">
Change
</span>
<input id="avatar_image" type="file" size="45" name="avatar_image" class="input">
</span>
<a href="#" class="btn fileupload-exists" data-dismiss="fileupload">Remove</a>
</div>
</div>
</div>
</div>
<div class="span6">
<div class="row">
<textarea style="width: 92%" id="description" rows="5">{{ user.description }}</textarea>
<span class="help-block">Tell us a bit about yourself.</span>
<p>
<h5>Tell us a bit about yourself.</h5>
</p>
<textarea style="width: 100%" name="profile[description]" id="profile[description]" rows="5">{{profile.description}}</textarea>
</div>
<div class="row">
<h5>Share this activity</h5>
<ul class="list-horiz">
<li>
<h5>Likes</h5>
<input type="checkbox" name="switch" value="" id="activity_share_likes"
class="switch" data-coalesce="activity_sharing" data-bitflag="1">
</li>
<li>
<h5>Favourites</h5>
<input type="checkbox" name="switch" value="" id="activity_share_favourites"
class="switch" data-coalesce="activity_sharing" data-bitflag="2">
</li>
<li>
<h5>Comments</h5>
<input type="checkbox" name="switch" value="" id="activity_share_comments"
class="switch" data-coalesce="activity_sharing" data-bitflag="4">
</li>
</ul>
</div>
<div class="row">
<h5>On these networks</h5>
<ul class="list-horiz">
<li>
<h5>Facebook</h5>
<input type="checkbox" name="switch" value="" id="activity_share_networks_facebook"
class="switch" data-coalesce="activity_sharing_networks" data-bitflag="1">
</li>
<li>
<h5>Twitter</h5>
<input type="checkbox" name="switch" value="" id="activity_share_networks_twitter"
class="switch" data-coalesce="activity_sharing_networks" data-bitflag="2">
</li>
</ul>
<p>
<h5>What can we share about you?</h5>
</p>
<div class="span5 pull-left">
<h5>Share this activity</h5>
<table class="table dss-option-table table-condensed table-bordered">
<tbody>
<tr class="info">
<td>Likes</td>
<td>Favourites</td>
<td>Comments</td>
</tr>
<tr>
<td>
<input type="checkbox" name="profile[activity_sharing_likes]"
id="activity_sharing_likes" {{ profile.activity_sharing_likes }}>
</td>
<td>
<input type="checkbox" name="profile[activity_sharing_favourites]"
id="activity_sharing_favourites" {{ profile.activity_sharing_favourites }}>
</td>
<td>
<input type="checkbox" name="profile[activity_sharing_comments]"
id="activity_sharing_comments" {{ profile.activity_sharing_comments }}>
</td>
</tr>
</tbody>
</table>
</div>
<div class="span5 pull-right">
<h5>On these networks</h5>
<table class="table dss-option-table table-condensed table-bordered">
<tbody>
<tr class="info">
<td>Facebook</td>
<td>Twitter</td>
</tr>
<tr>
<td style="text-align: center; vertical-align: middle">
<input type="checkbox" name="profile[activity_sharing_networks_facebook]" {{ profile.activity_sharing_networks_facebook }}>
</td>
<td>
<input type="checkbox" name="profile[activity_sharing_networks_twitter]" {{ profile.activity_sharing_networks_twitter }}>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</fieldset>
@@ -124,4 +148,5 @@
<button id="save-changes" class="btn">Save changes</button>
</div>
</div>
</div>
</div>
{% endverbatim %}