diff --git a/spa/ajax.py b/spa/ajax.py index 653d5d9..afac010 100644 --- a/spa/ajax.py +++ b/spa/ajax.py @@ -3,6 +3,7 @@ import logging from django.conf.urls import url from django.contrib.auth.decorators import login_required +from django.contrib.auth.models import User from django.db.models import get_model from django.http import HttpResponse from annoying.decorators import render_to @@ -13,7 +14,7 @@ from django.views.decorators.csrf import csrf_exempt from core.utils import live from dss import localsettings, settings from spa import social -from spa.models import UserProfile, MixFavourite, Release +from spa.models import UserProfile, MixFavourite, Release, UserFollows from spa.models.Mix import Mix from spa.models.Comment import Comment from spa.models.MixLike import MixLike @@ -43,6 +44,7 @@ class AjaxHandler(object): url(r'^live_now_playing/$', 'spa.ajax.live_now_playing'), url(r'^like/$', 'spa.ajax.like', name='ajax_mix_like'), url(r'^favourite/$', 'spa.ajax.favourite', name='ajax_mix_favourite'), + url(r'^toggle_follow/$', 'spa.ajax.toggle_follow', name='ajax_toggle_follow'), url(r'^facebook_post_likes_allowed/$', 'spa.ajax.facebook_post_likes_allowed', name='ajax_facebook_post_likes_allowed'), url(r'^upload_image/(?P\d+)/$', 'spa.ajax.upload_image', name='ajax_upload_image'), @@ -170,6 +172,25 @@ def like(request): return HttpResponse(response) +@login_required() +def toggle_follow(request): + if request.is_ajax() and request.method == 'POST' and 'userId' in request.POST: + follower = request.user + following = User.objects.get(pk=request.POST['userId']) + if follower is not None and following is not None: + try: + user_follow = UserFollows.objects.get(follower=follower, following=following) + user_follow.delete() + response = _get_json('Unfollowed') + except UserFollows.DoesNotExist: + user_follow = UserFollows(follower=follower.get_profile(), following=following.get_profile()) + user_follow.save() + response = _get_json('Followed') + else: + response = _get_json('Invalid') + + return HttpResponse(response) + @login_required() def favourite(request): if request.is_ajax(): diff --git a/spa/api/v1/UserResource.py b/spa/api/v1/UserResource.py index 699e255..201fe1a 100644 --- a/spa/api/v1/UserResource.py +++ b/spa/api/v1/UserResource.py @@ -2,9 +2,10 @@ from django.contrib.auth.models import User from tastypie import fields from tastypie.authentication import Authentication from tastypie.authorization import DjangoAuthorization +from django.conf.urls import url + from spa.api.v1.BackboneCompatibleResource import BackboneCompatibleResource from spa.models import UserProfile -from django.conf.urls import url class UserProfileResource(BackboneCompatibleResource): @@ -31,8 +32,10 @@ class UserProfileResource(BackboneCompatibleResource): 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 + 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'] @@ -66,12 +69,19 @@ class UserProfileResource(BackboneCompatibleResource): self._hydrateBitmapOption(bundle.obj.activity_sharing, UserProfile.ACTIVITY_SHARE_COMMENTS) bundle.data['activity_sharing_networks_facebook'] = \ - self._hydrateBitmapOption(bundle.obj.activity_sharing_networks, UserProfile.ACTIVITY_SHARE_NETWORK_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) + self._hydrateBitmapOption(bundle.obj.activity_sharing_networks, + UserProfile.ACTIVITY_SHARE_NETWORK_TWITTER) + bundle.data['mix_count'] = 4 + bundle.data['follower_count'] = 4 + bundle.data['following_count'] = 8 + bundle.data['following'] = bundle.obj.is_follower(bundle.request.user) return bundle + class UserResource(BackboneCompatibleResource): profile = fields.ToOneField(UserProfileResource, attribute='userprofile', related_name='user', full=True) @@ -84,8 +94,10 @@ class UserResource(BackboneCompatibleResource): def prepend_urls(self): return [ - url(r"^(?P%s)/(?P\d+)/$" % self._meta.resource_name, self.wrap_view('dispatch_detail'), name="api_dispatch_detail"), - url(r"^(?P%s)/(?P[\w\d_.-]+)/$" % self._meta.resource_name, self.wrap_view('dispatch_detail'), name="api_dispatch_detail"), + url(r"^(?P%s)/(?P\d+)/$" % self._meta.resource_name, self.wrap_view('dispatch_detail'), + name="api_dispatch_detail"), + url(r"^(?P%s)/(?P[\w\d_.-]+)/$" % self._meta.resource_name, + self.wrap_view('dispatch_detail'), name="api_dispatch_detail"), ] def dehydrate(self, bundle): diff --git a/spa/migrations/0007_auto__chg_field_mix_title__del_field_userfollows_user_to__del_field_us.py b/spa/migrations/0007_auto__chg_field_mix_title__del_field_userfollows_user_to__del_field_us.py new file mode 100644 index 0000000..16ea526 --- /dev/null +++ b/spa/migrations/0007_auto__chg_field_mix_title__del_field_userfollows_user_to__del_field_us.py @@ -0,0 +1,251 @@ +# -*- 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 'Mix.title' + db.alter_column(u'spa_mix', 'title', self.gf('django.db.models.fields.CharField')(max_length=150)) + # Deleting field 'UserFollows.user_to' + db.delete_column(u'spa_userfollows', 'user_to_id') + + # Deleting field 'UserFollows.user_from' + db.delete_column(u'spa_userfollows', 'user_from_id') + + # Adding field 'UserFollows.follower' + db.add_column(u'spa_userfollows', 'follower', + self.gf('django.db.models.fields.related.OneToOneField')(default=-1, related_name='followers', unique=True, to=orm['spa.UserProfile']), + keep_default=False) + + # Adding field 'UserFollows.following' + db.add_column(u'spa_userfollows', 'following', + self.gf('django.db.models.fields.related.OneToOneField')(default=-1, related_name='following', unique=True, to=orm['spa.UserProfile']), + keep_default=False) + + + def backwards(self, orm): + + # Changing field 'Mix.title' + db.alter_column(u'spa_mix', 'title', self.gf('django.db.models.fields.CharField')(max_length=50)) + # Adding field 'UserFollows.user_to' + db.add_column(u'spa_userfollows', 'user_to', + self.gf('django.db.models.fields.related.OneToOneField')(default=-1, related_name='following', unique=True, to=orm['spa.UserProfile']), + keep_default=False) + + # Adding field 'UserFollows.user_from' + db.add_column(u'spa_userfollows', 'user_from', + self.gf('django.db.models.fields.related.OneToOneField')(default=-1, related_name='followers', unique=True, to=orm['spa.UserProfile']), + keep_default=False) + + # Deleting field 'UserFollows.follower' + db.delete_column(u'spa_userfollows', 'follower_id') + + # Deleting field 'UserFollows.following' + db.delete_column(u'spa_userfollows', 'following_id') + + + 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, 30, 0, 0)'}), + 'event_date': ('django.db.models.fields.DateField', [], {'default': 'datetime.datetime(2013, 4, 30, 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, 30, 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': '150'}), + '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, 30, 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, 30, 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'}, + 'follower': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'followers'", 'unique': 'True', 'to': "orm['spa.UserProfile']"}), + 'following': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'following'", 'unique': 'True', 'to': "orm['spa.UserProfile']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + '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'] \ No newline at end of file diff --git a/spa/migrations/0008_auto__del_field_userfollows_following__del_field_userfollows_follower.py b/spa/migrations/0008_auto__del_field_userfollows_following__del_field_userfollows_follower.py new file mode 100644 index 0000000..b45db89 --- /dev/null +++ b/spa/migrations/0008_auto__del_field_userfollows_following__del_field_userfollows_follower.py @@ -0,0 +1,251 @@ +# -*- 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): + # Deleting field 'UserFollows.following' + db.delete_column(u'spa_userfollows', 'following_id') + + # Deleting field 'UserFollows.follower' + db.delete_column(u'spa_userfollows', 'follower_id') + + # Adding M2M table for field follower on 'UserFollows' + db.create_table(u'spa_userfollows_follower', ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('userfollows', models.ForeignKey(orm['spa.userfollows'], null=False)), + ('userprofile', models.ForeignKey(orm['spa.userprofile'], null=False)) + )) + db.create_unique(u'spa_userfollows_follower', ['userfollows_id', 'userprofile_id']) + + # Adding M2M table for field following on 'UserFollows' + db.create_table(u'spa_userfollows_following', ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('userfollows', models.ForeignKey(orm['spa.userfollows'], null=False)), + ('userprofile', models.ForeignKey(orm['spa.userprofile'], null=False)) + )) + db.create_unique(u'spa_userfollows_following', ['userfollows_id', 'userprofile_id']) + + + def backwards(self, orm): + # Adding field 'UserFollows.following' + db.add_column(u'spa_userfollows', 'following', + self.gf('django.db.models.fields.related.OneToOneField')(default=-1, related_name='following', unique=True, to=orm['spa.UserProfile']), + keep_default=False) + + # Adding field 'UserFollows.follower' + db.add_column(u'spa_userfollows', 'follower', + self.gf('django.db.models.fields.related.OneToOneField')(default=-1, related_name='followers', unique=True, to=orm['spa.UserProfile']), + keep_default=False) + + # Removing M2M table for field follower on 'UserFollows' + db.delete_table('spa_userfollows_follower') + + # Removing M2M table for field following on 'UserFollows' + db.delete_table('spa_userfollows_following') + + + 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, 30, 0, 0)'}), + 'event_date': ('django.db.models.fields.DateField', [], {'default': 'datetime.datetime(2013, 4, 30, 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, 30, 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': '150'}), + '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, 30, 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, 30, 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'}, + 'follower': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'followers'", 'symmetrical': 'False', 'to': "orm['spa.UserProfile']"}), + 'following': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'following'", 'symmetrical': 'False', 'to': "orm['spa.UserProfile']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}) + }, + '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'] \ No newline at end of file diff --git a/spa/models/UserFollows.py b/spa/models/UserFollows.py index dbecddf..a37156f 100644 --- a/spa/models/UserFollows.py +++ b/spa/models/UserFollows.py @@ -5,5 +5,5 @@ from spa.models._BaseModel import _BaseModel class UserFollows(_BaseModel): - user_from = models.OneToOneField(UserProfile, related_name='followers') - user_to = models.OneToOneField(UserProfile, related_name='following') + follower = models.ManyToManyField(UserProfile, related_name='followers') + following = models.ManyToManyField(UserProfile, related_name='following') diff --git a/spa/models/UserProfile.py b/spa/models/UserProfile.py index e041b63..4ff3f7c 100644 --- a/spa/models/UserProfile.py +++ b/spa/models/UserProfile.py @@ -1,3 +1,4 @@ +from logging import log import urlparse from django.contrib.auth.models import User @@ -80,6 +81,17 @@ class UserProfile(_BaseModel): except Exception, e: self.logger.error("Unable to create profile slug: %s", e.message) + def is_follower(self, user): + try: + if user in self.followers.objects: + return True + else: + return False + except Exception, ex: + log.debug(ex.message) + + return False + def get_absolute_url(self): if self.slug is None or len(self.slug) == 0: self.__create_slug() diff --git a/static/css/deepsouthsounds.css b/static/css/deepsouthsounds.css index a9836b5..21621c1 100644 --- a/static/css/deepsouthsounds.css +++ b/static/css/deepsouthsounds.css @@ -406,3 +406,35 @@ div.event-content td { height: 120px; width: 180px; } + +.btn.loading { + background-image: -webkit-gradient(linear, 0 0, 100% 100%, + color-stop(.25, rgba(0, 0, 0, .10)), + color-stop(.25, transparent), + color-stop(.5, transparent), + color-stop(.5, rgba(0, 0, 0, .10)), + color-stop(.75, rgba(0, 0, 0, .10)), + color-stop(.75, transparent), + to(transparent)); + background-image: + -moz-linear-gradient(-45deg, + rgba(0, 0, 0, .10) 25%, + transparent 25%, + transparent 50%, rgba(0, 0, 0, .10) 50%, + rgba(0, 0, 0, .10) 75%, + transparent 75%, transparent + ); + background-size: 50px 50px; + -moz-background-size: 50px 50px; + -webkit-background-size: 50px 50px; + -webkit-animation: animate-stripes 2s linear infinite; +} + +@-webkit-keyframes animate-stripes { + from { + background-position: 0 0; + } + to { + background-position: -50px 0; + } +} \ No newline at end of file diff --git a/static/js/app/site.js b/static/js/app/site.js index 005e33e..f39515d 100644 --- a/static/js/app/site.js +++ b/static/js/app/site.js @@ -15,14 +15,18 @@ $(document).ready(function () { Backbone.history.navigate("/"); } }); -$(document).on({ - ajaxStart: function () { - $('#ajax-request').show(); - }, - ajaxStop: function () { - $('#ajax-request').hide(); - } -}); + +if (com.podnoms.settings.isDebug) { + $(document).on({ + ajaxStart: function () { + $('#ajax-request').show(); + }, + ajaxStop: function () { + $('#ajax-request').hide(); + } + }); +} + $(document).ajaxSend(function (event, xhr, settings) { function getCookie(name) { var cookieValue = null; diff --git a/static/js/app/views/sidebaruser.js b/static/js/app/views/sidebaruser.js index fbb95eb..23b8561 100644 --- a/static/js/app/views/sidebaruser.js +++ b/static/js/app/views/sidebaruser.js @@ -9,11 +9,48 @@ window.SidebarViewUser = Backbone.View.extend({ events: { + "click #follow-button": "toggleFollow" }, render: function () { ich.addTemplate('sidebaruser', this.template()); - var rendered = ich.sidebaruser(this.model.toJSON()); - $(this.el).html(rendered); + this.model.formatDate = function (date) { + return 'Yaaaaaaa....'; + }; + $(this.el).html(ich.sidebaruser(this.model.toJSON())); + + this._renderFollowButton(); return this; + }, + _renderFollowButton: function () { + if (this.model.get('profile').following) { + $('#follow-button', this.el).addClass("btn-warning disabled"); + $('#follow-button', this.el).removeClass("btn-success"); + $('#follow-button', this.el).text("Unfollow"); + $('#follow-icon', this.el).addClass("icon-eye-close"); + $('#follow-icon', this.el).removeClass("icon-eye-open"); + } else { + $('#follow-button', this.el).removeClass("btn-warning disabled"); + $('#follow-button', this.el).addClass("btn-success"); + $('#follow-button', this.el).text("Follow"); + $('#follow-icon', this.el).removeClass("icon-eye-close"); + $('#follow-icon', this.el).addClass("icon-eye-open"); + } + }, + toggleFollow: function () { + var model = this.model; + $('#follow-button', this.el).addClass("loading"); + $.post( + "/ajax/toggle_follow/", + { userId: this.model.get("id") }, + function (data) { + var result = $.parseJSON(data); + if (result.value == 'Followed') + model.set('profile.following', true); + else + model.set('profile.following', true); + this._renderFollowButton(); + $('#follow-button', this.el).removeClass("loading"); + } + ); } }); diff --git a/templates/javascript/settings.js b/templates/javascript/settings.js index 3db64ed..dc35151 100644 --- a/templates/javascript/settings.js +++ b/templates/javascript/settings.js @@ -10,6 +10,7 @@ com.podnoms.settings = { streamInfoUrl: 'http://{{ LIVE_STREAM_INFO_URL }}', volume: '{{ DEFAULT_AUDIO_VOLUME }}', smDebugMode: '{{ SM_DEBUG_MODE }}', + isDebug: '{{ IS_DEBUG }}', drawTimelineOnMix: false, currentUser: {{ CURRENT_USER_ID }}, /** simple helper to take an api JSON object and initialise a player item */ diff --git a/templates/views/SidebarViewUser.html b/templates/views/SidebarViewUser.html index 2c8292c..b623dc6 100644 --- a/templates/views/SidebarViewUser.html +++ b/templates/views/SidebarViewUser.html @@ -1,7 +1,30 @@ {% verbatim %} -
-
{{ profile.display_name }}
- -

{{ profile.description }}

-
+
+ +
    +
  • {{ profile.display_name }}
  • +
  • +

    + +

    +
  • +
+ + +

{{ profile.description }}

+
+
Last Seen
+
{{last_login}}
+
Mixes Uploaded
+
{{ profile.mix_count }}
+
Followers
+
{{ profile.follower_count }}
+
Following
+
{{ profile.following_count }}
+
+
{% endverbatim %} diff --git a/templates/views/_MixItemInsert.html b/templates/views/_MixItemInsert.html index 04cc006..c8313b0 100644 --- a/templates/views/_MixItemInsert.html +++ b/templates/views/_MixItemInsert.html @@ -2,7 +2,7 @@
- mix-logo + mix-logo