diff --git a/requirements.txt b/requirements.txt index c117c80..3051422 100755 --- a/requirements.txt +++ b/requirements.txt @@ -22,6 +22,7 @@ git+git://github.com/etianen/django-require.git#django-require git+git://github.com/spookylukey/django-paypal.git#django-paypal git+git://github.com/llazzaro/django-scheduler.git#django-scheduler git+git://github.com/cyberdelia/django-pipeline.git#django-pipeline +git+git://github.com/disqus/django-bitfield.git#django-bitfield django-templated-email django-grappelli humanize diff --git a/spa/api/v1/ActivityResource.py b/spa/api/v1/ActivityResource.py index bf76003..0cc8dec 100755 --- a/spa/api/v1/ActivityResource.py +++ b/spa/api/v1/ActivityResource.py @@ -2,12 +2,12 @@ import humanize from tastypie.authentication import Authentication from tastypie.authorization import Authorization from tastypie.constants import ALL, ALL_WITH_RELATIONS -from spa.api.v1.BackboneCompatibleResource import BackboneCompatibleResource +from spa.api.v1.BaseResource import BaseResource from spa.models import UserProfile from spa.models.activity import Activity -class ActivityResource(BackboneCompatibleResource): +class ActivityResource(BaseResource): class Meta: queryset = Activity.objects.select_subclasses().order_by('-id') resource_name = 'activity' diff --git a/spa/api/v1/BackboneCompatibleResource.py b/spa/api/v1/BackboneCompatibleResource.py deleted file mode 100755 index 8c48c23..0000000 --- a/spa/api/v1/BackboneCompatibleResource.py +++ /dev/null @@ -1,15 +0,0 @@ -import logging -import datetime -import humanize -from tastypie.resources import ModelResource - - -class BackboneCompatibleResource(ModelResource): - logger = logging.getLogger(__name__) - pass - - def humanize_date(self, date): - if (timezone.now() - date) <= datetime.timedelta(days=1): - return humanize.naturaltime(date) - else: - return humanize.naturalday(date) \ No newline at end of file diff --git a/spa/api/v1/BaseResource.py b/spa/api/v1/BaseResource.py new file mode 100644 index 0000000..66a4f08 --- /dev/null +++ b/spa/api/v1/BaseResource.py @@ -0,0 +1,14 @@ +import logging +from tastypie.resources import ModelResource + + +class BaseResource(ModelResource): + logger = logging.getLogger(__name__) + pass + + def _remove_kwargs(self, *args, **kwargs): + for arg in args: + if arg in kwargs: + del kwargs['activity_sharing_networks_facebook'] + + return kwargs \ No newline at end of file diff --git a/spa/api/v1/ChatResource.py b/spa/api/v1/ChatResource.py index 0201349..97ad1b6 100755 --- a/spa/api/v1/ChatResource.py +++ b/spa/api/v1/ChatResource.py @@ -1,7 +1,7 @@ -from spa.api.v1.BackboneCompatibleResource import BackboneCompatibleResource +from spa.api.v1.BaseResource import BaseResource from spa.models.chatmessage import ChatMessage -class ChatResource(BackboneCompatibleResource): +class ChatResource(BaseResource): class Meta: queryset = ChatMessage.objects.all().order_by('-timestamp') resource_name = 'chat' diff --git a/spa/api/v1/CommentResource.py b/spa/api/v1/CommentResource.py index 63e45b5..8c1870f 100755 --- a/spa/api/v1/CommentResource.py +++ b/spa/api/v1/CommentResource.py @@ -3,13 +3,13 @@ from tastypie.authentication import Authentication from tastypie.authorization import Authorization from tastypie.exceptions import ImmediateHttpResponse from tastypie.http import HttpBadRequest, HttpMethodNotAllowed, HttpUnauthorized, HttpApplicationError, HttpNotImplemented -from spa.api.v1.BackboneCompatibleResource import BackboneCompatibleResource +from spa.api.v1.BaseResource import BaseResource from spa.models import Mix, UserProfile from spa.models.activity import ActivityComment from spa.models.comment import Comment -class CommentResource(BackboneCompatibleResource): +class CommentResource(BaseResource): mix = fields.ToOneField('spa.api.v1.MixResource.MixResource', 'mix') likes = fields.ToManyField('spa.api.v1.UserResource.UserResource', 'likes', related_name='favourites', diff --git a/spa/api/v1/EventResource.py b/spa/api/v1/EventResource.py index 002c11e..ad16d9e 100755 --- a/spa/api/v1/EventResource.py +++ b/spa/api/v1/EventResource.py @@ -1,7 +1,7 @@ from django.core.exceptions import ObjectDoesNotExist import humanize from tastypie.authorization import Authorization -from spa.api.v1.BackboneCompatibleResource import BackboneCompatibleResource +from spa.api.v1.BaseResource import BaseResource from spa.models.recurrence import Recurrence """ from spa.views.venue import Venue diff --git a/spa/api/v1/GenreResource.py b/spa/api/v1/GenreResource.py index d45fff0..ab7a76e 100644 --- a/spa/api/v1/GenreResource.py +++ b/spa/api/v1/GenreResource.py @@ -1,11 +1,11 @@ from tastypie.authentication import Authentication from tastypie.authorization import Authorization -from spa.api.v1.BackboneCompatibleResource import BackboneCompatibleResource +from spa.api.v1.BaseResource import BaseResource from spa.models import Genre -class GenreResource(BackboneCompatibleResource): +class GenreResource(BaseResource): class Meta: queryset = Genre.objects.all().order_by('description') resource_name = 'genres' diff --git a/spa/api/v1/MixResource.py b/spa/api/v1/MixResource.py index dc61984..e044d27 100755 --- a/spa/api/v1/MixResource.py +++ b/spa/api/v1/MixResource.py @@ -13,7 +13,7 @@ from tastypie.http import HttpGone, HttpUnauthorized from tastypie.utils import trailing_slash from dss import settings -from spa.api.v1.BackboneCompatibleResource import BackboneCompatibleResource +from spa.api.v1.BaseResource import BaseResource from spa.api.v1.CommentResource import CommentResource from spa.api.v1.ActivityResource import ActivityResource from spa.models.mix import Mix @@ -21,7 +21,7 @@ from spa.models.show import Show from spa.models.userprofile import UserProfile -class MixResource(BackboneCompatibleResource): +class MixResource(BaseResource): comments = fields.ToManyField('spa.api.v1.CommentResource.CommentResource', 'comments', null=True, full=True) favourites = fields.ToManyField('spa.api.v1.UserResource.UserResource', diff --git a/spa/api/v1/NotificationResource.py b/spa/api/v1/NotificationResource.py index 59e71e2..5562e42 100644 --- a/spa/api/v1/NotificationResource.py +++ b/spa/api/v1/NotificationResource.py @@ -1,11 +1,11 @@ from tastypie.authentication import SessionAuthentication from tastypie.authorization import DjangoAuthorization -from spa.api.v1.BackboneCompatibleResource import BackboneCompatibleResource +from spa.api.v1.BaseResource import BaseResource from spa.models.notification import Notification from spa.models.userprofile import UserProfile -class NotificationResource(BackboneCompatibleResource): +class NotificationResource(BaseResource): class Meta: queryset = Notification.objects.order_by('-id') resource_name = 'notification' diff --git a/spa/api/v1/PlaylistResource.py b/spa/api/v1/PlaylistResource.py index f1f95fb..2cbdf80 100644 --- a/spa/api/v1/PlaylistResource.py +++ b/spa/api/v1/PlaylistResource.py @@ -6,11 +6,11 @@ from tastypie import fields from tastypie.exceptions import ImmediateHttpResponse from tastypie.http import HttpUnauthorized from tastypie.utils import trailing_slash -from spa.api.v1.BackboneCompatibleResource import BackboneCompatibleResource +from spa.api.v1.BaseResource import BaseResource from spa.models import Playlist, Mix, UserProfile -class PlaylistResource(BackboneCompatibleResource): +class PlaylistResource(BaseResource): user = fields.ToOneField('spa.api.v1.UserResource.UserResource', 'user') mixes = fields.ManyToManyField('spa.api.v1.MixResource.MixResource', 'mixes', full=True, null=True) diff --git a/spa/api/v1/ReleaseAudioResource.py b/spa/api/v1/ReleaseAudioResource.py index 722151d..55f25ab 100755 --- a/spa/api/v1/ReleaseAudioResource.py +++ b/spa/api/v1/ReleaseAudioResource.py @@ -1,8 +1,8 @@ from tastypie import fields -from spa.api.v1.BackboneCompatibleResource import BackboneCompatibleResource +from spa.api.v1.BaseResource import BaseResource from spa.models.release import ReleaseAudio -class ReleaseAudioResource(BackboneCompatibleResource): +class ReleaseAudioResource(BaseResource): release = fields.ToOneField('spa.api.v1.ReleaseResource.ReleaseResource', 'release') class Meta: diff --git a/spa/api/v1/ReleaseResource.py b/spa/api/v1/ReleaseResource.py index 49fd50f..180f878 100755 --- a/spa/api/v1/ReleaseResource.py +++ b/spa/api/v1/ReleaseResource.py @@ -2,11 +2,11 @@ import datetime from tastypie import fields from tastypie.authorization import Authorization from tastypie.constants import ALL_WITH_RELATIONS -from spa.api.v1.BackboneCompatibleResource import BackboneCompatibleResource +from spa.api.v1.BaseResource import BaseResource from spa.models import Label from spa.models.release import Release from django.core.exceptions import ObjectDoesNotExist -class ReleaseResource(BackboneCompatibleResource): +class ReleaseResource(BaseResource): release_audio = fields.ToManyField('spa.api.v1.ReleaseAudioResource.ReleaseAudioResource', 'release_audio', 'release', null=True, blank=True) class Meta: queryset = Release.objects.all() diff --git a/spa/api/v1/ShowResource.py b/spa/api/v1/ShowResource.py index d87670c..f84a40f 100644 --- a/spa/api/v1/ShowResource.py +++ b/spa/api/v1/ShowResource.py @@ -3,7 +3,7 @@ from tastypie.authorization import Authorization from tastypie.exceptions import ImmediateHttpResponse from tastypie.http import HttpBadRequest -from spa.api.v1.BackboneCompatibleResource import BackboneCompatibleResource +from spa.api.v1.BaseResource import BaseResource from spa.models import Show from spa.models.show import ShowOverlapException @@ -11,7 +11,7 @@ from spa.models.show import ShowOverlapException DATE_FORMAT = '%d/%m/%Y %H:%M:%S' -class ShowResource(BackboneCompatibleResource): +class ShowResource(BaseResource): mix = fields.ToOneField('spa.api.v1.MixResource.MixResource', 'mix', null=False, full=False) diff --git a/spa/api/v1/UserResource.py b/spa/api/v1/UserResource.py index 6ba30c0..7152ae3 100755 --- a/spa/api/v1/UserResource.py +++ b/spa/api/v1/UserResource.py @@ -10,14 +10,14 @@ from tastypie.utils import trailing_slash from tastypie_msgpack import Serializer from dss import settings -from spa.api.v1.BackboneCompatibleResource import BackboneCompatibleResource +from spa.api.v1.BaseResource import BaseResource from spa.api.v1.PlaylistResource import PlaylistResource from spa.models.userprofile import UserProfile from spa.models.mix import Mix from core.tasks import update_geo_info_task -class UserResource(BackboneCompatibleResource): +class UserResource(BaseResource): following = fields.ToManyField(to='self', attribute='following', related_name='following', null=True) followers = fields.ToManyField(to='self', attribute='followers', related_name='followers', null=True) @@ -44,7 +44,6 @@ class UserResource(BackboneCompatibleResource): authorization = Authorization() authentication = Authentication() - @staticmethod def _hydrate_bitmap_opt(source, comparator): return True if (source & comparator) != 0 else False @@ -75,26 +74,11 @@ class UserResource(BackboneCompatibleResource): return super(UserResource, self).obj_create(bundle, **kwargs) 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'] - - ret = super(UserResource, self).obj_update(bundle, skip_errors, **kwargs) - - try: - update_geo_info_task.delay(ip_address=bundle.request.META['REMOTE_ADDR'], - profile_id=bundle.request.user.get_profile().id) - except: - pass - - return ret + update_geo_info_task.delay( + ip_address=bundle.request.META['REMOTE_ADDR'], + profile_id=bundle.request.user.get_profile().id + ) + return super(UserResource, self).obj_update(bundle, skip_errors, **kwargs) def _create_playlist(self, request): pass @@ -120,6 +104,13 @@ class UserResource(BackboneCompatibleResource): del bundle.data['activity_sharing_networks'] bundle.data['display_name'] = bundle.obj.get_nice_name() bundle.data['avatar_image'] = bundle.obj.get_avatar_image() + + bundle.data['email_notification_plays'] = bundle.obj.email_notifications.plays.is_set + bundle.data['email_notification_likes'] = bundle.obj.email_notifications.likes.is_set + bundle.data['email_notification_favourites'] = bundle.obj.email_notifications.favourites.is_set + bundle.data['email_notification_follows'] = bundle.obj.email_notifications.follows.is_set + bundle.data['email_notification_comments'] = bundle.obj.email_notifications.comments.is_set + if bundle.obj.user.id == bundle.request.user.id: bundle.data['email'] = bundle.obj.email bundle.data['first_name'] = bundle.obj.first_name @@ -130,6 +121,8 @@ class UserResource(BackboneCompatibleResource): self._hydrate_bitmap_opt(bundle.obj.activity_sharing, UserProfile.ACTIVITY_SHARE_FAVOURITES) bundle.data['activity_sharing_comments'] = \ self._hydrate_bitmap_opt(bundle.obj.activity_sharing, UserProfile.ACTIVITY_SHARE_COMMENTS) + bundle.data['activity_sharing_plays'] = \ + self._hydrate_bitmap_opt(bundle.obj.activity_sharing, UserProfile.ACTIVITY_SHARE_PLAYS) bundle.data['activity_sharing_networks_facebook'] = \ self._hydrate_bitmap_opt(bundle.obj.activity_sharing_networks, @@ -153,10 +146,12 @@ class UserResource(BackboneCompatibleResource): def hydrate(self, bundle): if 'activity_sharing_likes' in bundle.data: + plays = UserProfile.ACTIVITY_SHARE_PLAYS if bundle.data['activity_sharing_plays'] else 0 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_plays'] del bundle.data['activity_sharing_likes'] del bundle.data['activity_sharing_favourites'] del bundle.data['activity_sharing_comments'] diff --git a/spa/audio.py b/spa/audio.py index eceecff..74d180e 100755 --- a/spa/audio.py +++ b/spa/audio.py @@ -5,8 +5,10 @@ import urlparse from django.conf.urls import url import json -from django.http import Http404, HttpResponse, HttpResponseForbidden, HttpResponseNotFound +from django.http import Http404, HttpResponse, HttpResponseForbidden, HttpResponseNotFound, HttpResponseRedirect from django.core.servers.basehttp import FileWrapper +from django.shortcuts import redirect +from django.utils.encoding import smart_str from nginx_signing.signing import UriSigner from sendfile import sendfile @@ -37,18 +39,24 @@ def download(request, mix_id): mix = Mix.objects.get(pk=mix_id) if mix is not None: if mix.download_allowed: - audio_file = mix.get_absolute_path() - filename, extension = os.path.splitext(audio_file) - - if os.path.exists(audio_file): - return sendfile( - request, - audio_file, - attachment=True, - attachment_filename='Deep South Sounds - %s%s' % ( - mix.title, extension + if mix.archive_path in [None, '']: + audio_file = mix.get_absolute_path() + filename, extension = os.path.splitext(audio_file) + if os.path.exists(audio_file): + return sendfile( + request, + audio_file, + attachment=True, + attachment_filename='Deep South Sounds - %s%s' % ( + mix.title, extension + ) ) - ) + else: + response = HttpResponseRedirect(mix.archive_path) + response['Content-Disposition'] = 'attachment; filename=' + \ + smart_str('Deep South Sounds - %s%s' % (mix.title, mix.filetype)) + + return response else: return HttpResponse('Downloads not allowed for this mix', status=401) diff --git a/spa/models/userprofile.py b/spa/models/userprofile.py index 1f99a2e..4c0636a 100755 --- a/spa/models/userprofile.py +++ b/spa/models/userprofile.py @@ -1,5 +1,6 @@ import logging import urlparse +from bitfield.models import BitField from django.contrib.auth.models import User from django.core.exceptions import SuspiciousOperation @@ -37,6 +38,7 @@ class UserProfile(BaseModel): ACTIVITY_SHARE_LIKES = 1 ACTIVITY_SHARE_FAVOURITES = 2 ACTIVITY_SHARE_COMMENTS = 4 + ACTIVITY_SHARE_PLAYS = 8 ACTIVITY_SHARE_NETWORK_FACEBOOK = 1 ACTIVITY_SHARE_NETWORK_TWITTER = 2 @@ -51,6 +53,14 @@ class UserProfile(BaseModel): activity_sharing = models.IntegerField(default=0) activity_sharing_networks = models.IntegerField(default=0) + email_notifications = BitField(flags=( + ('plays', 'Plays'), + ('likes', 'Likes'), + ('favourites', 'Favourites'), + ('follows', 'Follows'), + ('comments', 'Comments'), + ), default=0) + following = models.ManyToManyField('self', null=True, blank=True, symmetrical=False, related_name='followers') #location properties diff --git a/static/js/dss/apps/user/views/userEditView.coffee b/static/js/dss/apps/user/views/userEditView.coffee index 8231caa..f90020e 100644 --- a/static/js/dss/apps/user/views/userEditView.coffee +++ b/static/js/dss/apps/user/views/userEditView.coffee @@ -50,17 +50,13 @@ error: (data, status, e) -> utils.showError e - @uploadImage - el: $('#avatar_image') - success: -> - utils.showMessage "Successfully updated yourself" - Backbone.history.navigate "/", - trigger: true - else toastr.info "Successfully updated yourself" + alert("What to do") + """ Backbone.history.navigate "/", trigger: true + """ true error: -> toastr.error "There was an error updating your info. Please try again later." diff --git a/static/js/dss/lib/social.coffee b/static/js/dss/lib/social.coffee index cc75926..18e73b3 100644 --- a/static/js/dss/lib/social.coffee +++ b/static/js/dss/lib/social.coffee @@ -1,11 +1,9 @@ @social = do -> postFacebookLike: (mixId) -> - #first off, find if the current user has allowed facebook likes $.getJSON "social/like/" + mixId + "/", (data) -> utils.showAlert "Posted your like to facebook, you can stop this in your settings page.", "Cheers feen" - generateEmbedCode: (model) -> console.log("Generating embed code"); utils.modal "/dlg/embed/" + model.get('slug') diff --git a/static/js/dss/templates/usereditview.jst b/static/js/dss/templates/usereditview.jst index fb0eb53..2adcde6 100644 --- a/static/js/dss/templates/usereditview.jst +++ b/static/js/dss/templates/usereditview.jst @@ -64,61 +64,123 @@
-
| Likes | -Favourites | -Comments | -
| - > - | -- > - | -- > - | -
| - > - | -- > - | -