Skip to content

Commit

Permalink
attempt to implement collection modals
Browse files Browse the repository at this point in the history
  • Loading branch information
quimmrc committed Jan 16, 2025
1 parent 52543c4 commit 5f137eb
Show file tree
Hide file tree
Showing 9 changed files with 245 additions and 19 deletions.
94 changes: 94 additions & 0 deletions fscollections/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
#
# Freesound is (c) MUSIC TECHNOLOGY GROUP, UNIVERSITAT POMPEU FABRA
#
# Freesound is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# Freesound is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Authors:
# See AUTHORS file.
#

from django import forms
from fscollections.models import Collection

#this class was aimed to perform similarly to BookmarkSound, however, at first the method to add a sound to a collection
#will be opening the search engine in a modal, looking for a sound in there and adding it to the actual collection page
#this can be found in edit pack -> add sounds
#class CollectSoundForm(forms.ModelForm):

class CollectionSoundForm(forms.Form):
#list of existing collections
#add sound to collection from sound page
collection = forms.ChoiceField(
label=False,
choices=[],
required=True)

new_collection_name = forms.ChoiceField(
label = False,
help_text=None,
required = False)

use_last_collection = forms.BooleanField(widget=forms.HiddenInput(), required=False, initial=False)
user_collections = None

NO_COLLECTION_CHOICE_VALUE = '-1'
NEW_COLLECTION_CHOICE_VALUE = '0'

def __init__(self, *args, **kwargs):
self.user_collections = kwargs.pop('user_collections', False)
self.user_saving_sound = kwargs.pop('user_saving_sound', False)
self.sound_id = kwargs.pop('sound_id', False)
super().__init__(*args, **kwargs)
self.fields['collection'].choices = [(self.NO_COLLECTION_CHOICE_VALUE, '--- No collection ---'),#in this case this goes to bookmarks collection (might have to be created)
(self.NEW_COLLECTION_CHOICE_VALUE, 'Create a new collection...')] + \
([(collection.id, collection.name) for collection in self.user_collections]
if self.user_collections else[])

self.fields['new_collection_name'].widget.attrs['placeholder'] = "Fill in the name for the new collection"
self.fields['category'].widget.attrs = {
'data-grey-items': f'{self.NO_CATEGORY_CHOICE_VALUE},{self.NEW_CATEGORY_CHOICE_VALUE}'}
#i don't fully understand what this last line of code does

def save(self, *args, **kwargs):
collection_to_use = None

if not self.cleaned_data['use_last_collection']:
if self.cleaned_data['collection'] == self.NO_COLLECTION_CHOICE_VALUE:
pass
elif self.cleaned_data['collection'] == self.NEW_COLLECTION_CHOICE_VALUE:
if self.cleaned_data['new_collection_name'] != "":
collection = \
Collection(author=self.user_saving_bookmark, name=self.cleaned_data['new_collection_name'])
collection.save()
collection_to_use = collection
else:
collection_to_use = Collection.objects.get(id=self.cleaned_data['collection'])
else: #en aquest cas - SÍ estem fent servir l'última coleccio, NO estem creant una nova coleccio, NO estem agafant una coleccio existent i
# per tant ens trobem en un cas de NO COLLECTION CHOICE VALUE (no s'ha triat cap coleccio)
# si no es tria cap coleccio: l'usuari té alguna colecció? NO -> creem BookmarksCollection pels seus sons privats
# SI -> per defecte es posa a BookmarksCollection
try:
last_user_collection = \
Collection.objects.filter(user=self.user_saving_bookmark).order_by('-created')[0]
# If user has a previous bookmark, use the same category (or use none if no category used in last
# bookmark)
collection_to_use = last_user_collection
except IndexError:
# This is first bookmark of the user
pass

# If collection already exists, don't save it and return the existing one
collection, _ = Collection.objects.get_or_create(
name = collection_to_use.name, author=self.user_saving_bookmark)
return collection
8 changes: 6 additions & 2 deletions fscollections/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,17 @@
class Collection(models.Model):

author = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_length=128, default="") #add restrictions
sounds = models.ManyToManyField(Sound, related_name="collections") #NOTE: before next migration pluralize sound(s) - check consequences in views
name = models.CharField(max_length=128, default="BookmarkCollection") #add restrictions
sounds = models.ManyToManyField(Sound, related_name="collections")
created = models.DateTimeField(db_index=True, auto_now_add=True)
description = models.TextField(max_length=500, default="")
#NOTE: before next migration add a num_sounds attribute
#bookmarks = True or False depending on it being the BookmarkCollection for a user (the one created for the first sound without collection assigned)
#contributors = delicate stuff
#subcolletion_path = sth with tagsn and routing folders for downloads
#follow relation for users and collections (intersted but not owner nor contributor)
#sooner or later you'll need to start using forms for adding sounds to collections xd


def __str__(self):
return f"{self.name}"
Expand Down
5 changes: 4 additions & 1 deletion fscollections/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,8 @@

urlpatterns = [
path('',views.collections, name='collections'),
path('<int:collection_id>/', views.collections, name='collections')
path('<int:collection_id>/', views.collections, name='collections'),
path('<int:collection_id>/<int:sound_id>/delete/', views.delete_sound_from_collection, name='delete-sound-from-collection'),
path('<int:sound_id>/add/', views.add_sound_to_collection, name='add-sound-to-collection'),
path('get_form_for_collection_sound/<int:sound_id>/', views.get_form_for_collecting_sound, name="collection-add-form-for-sound")
]
84 changes: 71 additions & 13 deletions fscollections/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,43 +25,101 @@
from django.urls import reverse

from fscollections.models import Collection
from fscollections.forms import CollectionSoundForm
from sounds.models import Sound
from sounds.views import add_sounds_modal_helper

@login_required
def collections(request, collection_id=None):
user = request.user
user_collections = Collection.objects.filter(author=user).order_by('-created') #first user collection should be on top - this would affect the if block
is_owner = False
#if no collection id is provided for this URL, render the oldest collection in the webpage
#be careful when loading this url without having any collection for a user
#rn the relation user-collection only exists when you own the collection

if not collection_id:
collection = user_collections.last()
else:
collection = get_object_or_404(Collection, id=collection_id)


if user == collection.author:
is_owner = True

tvars = {'collection': collection,
'collections_for_user': user_collections}
'collections_for_user': user_collections,
'is_owner': is_owner}

return render(request, 'collections/collections.html', tvars)

#upon creating a new user, create alongside a personal PRIVATE collection to store sounds
#NOTE: tbd - when a user wants to save a sound without having any collection, create a personal bookmarks collection

def add_sound_to_collection(request, sound_id, collection_id):
def add_sound_to_collection(request, sound_id, collection_id=None):
sound = get_object_or_404(Sound, id=sound_id)
collection = get_object_or_404(Collection, id=collection_id, author=request.user)
if collection_id is None:
collection = Collection.objects.filter(author=request.user).order_by("created")[0]
else:
collection = get_object_or_404(Collection, id=collection_id, author=request.user)

if sound.moderation_state=='OK':
collection.sounds.add(sound)
collection.save()
return True
return HttpResponseRedirect(reverse("collections", args=[collection.id]))
else:
return "sound not moderated or not collection owner"


def delete_sound_from_collection(request, sound_id, collection_id):
def delete_sound_from_collection(request, collection_id, sound_id):
#this should work as in Packs - select several sounds and remove them all at once from the collection
#by now it works as in Bookmarks in terms of UI
sound = get_object_or_404(Sound, id=sound_id)
collection = get_object_or_404(Collection, id=collection_id, author=request.user)
collection.sounds.remove(sound)
collection.save()
return HttpResponseRedirect(reverse("collections", args=[collection.id]))

if sound in collection.sound.all():
collection.sounds.remove(sound)
collection.save()
return True
else:
return "this sound is not in the collection"
@login_required
def get_form_for_collecting_sound(request, sound_id):
user = request.user
sound = Sound.objects.get(id=sound_id)

try:
last_collection = \
Collection.objects.filter(author=request.user).order_by('-created')[0]
# If user has a previous bookmark, use the same category by default (or use none if no category used in last
# bookmark)
except IndexError:
last_collection = None

user_collections = Collection.objects.filter(author=user).order_by('-created')
form = CollectionSoundForm(initial={'collection': last_collection.id if last_collection else CollectionSoundForm.NO_COLLECTION_CHOICE_VALUE},
prefix=sound.id,
user_collections=user_collections)

collections_already_containing_sound = Collection.objects.filter(author=user, collection__sounds=sound).distinct()
tvars = {'user': user,
'sound': sound,
'last_collection': last_collection,
'collections': user_collections,
'form': form,
'collections_with_sound': collections_already_containing_sound}
print("NICE CHECKPOINT")
print(tvars)

return render(request, 'modal_collect_sound.html', tvars)


#NOTE: there should be two methods to add a sound into a collection
#1: adding from the sound.html page through a "bookmark-like" button and opening a Collections modal
#2: from the collection.html page through a search-engine modal as done in Packs
"""
@login_required
def add_sounds_modal_for_collection(request, collection_id):
collection = get_object_or_404(Collection, id=collection_id)
tvars = add_sounds_modal_helper(request, username=collection.author.username)
tvars.update({
'modal_title': 'Add sounds to Collection',
'help_text': 'Collections are great!',
})
return render(request, 'sounds/modal_add_sounds.html', tvars)
"""
2 changes: 1 addition & 1 deletion sounds/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ class PackEditForm(ModelForm):
required=False)
description = HtmlCleaningCharField(widget=forms.Textarea(attrs={'cols': 80, 'rows': 10}),
help_text=HtmlCleaningCharField.make_help_text(), required=False)

def clean_pack_sounds(self):
pack_sounds = re.sub("[^0-9,]", "", self.cleaned_data['pack_sounds'])
pack_sounds = re.sub(",+", ",", pack_sounds)
Expand Down
16 changes: 14 additions & 2 deletions templates/collections/collections.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,35 @@
<h4>Your collections</h4>
<div class="v-spacing-top-3">
<ul class="list-style-type-none">
{% if collections_for_user %}
{% for col in collections_for_user %}
<li><a href="{% url "collections" col.id %}" {% if collection.id == col.id %}style="font-weight:bold"{% endif %}>{{col.name}}</a>
<span class=text-light-grey> · {{col.sounds.count}} sound{{col.sounds.count|pluralize}}</span></li>
{% endfor %}
{% else %}
<span class=text-light-grey> You don't have any collection yet... </span>
{% endif %}
</ul>
</div>
</div>
<div class="col-lg-8">
<h3>{{collection.name}}</h3>
<h3>{{collection.name}} </h3>
by <a href="{% url "account" collection.author.id %}">{{collection.author.username}}</a>
<div>Description: {% if collection.description %}{{collection.description}} {% endif %}</div>

<div class = "row">
{% if collection.sounds.count > 0 %}
{% for sound in collection.sounds.all %}
<div class="col-6 col-md-4">
{% display_sound_small sound %}
{% if is_owner %}
<div class="right v-spacing-4 v-spacing-top-negative-2"><a class="cursor-pointer bw-link--grey" data-toggle="confirmation-modal" data-modal-confirmation-title="Are you sure you want to remove '{{sound.name}}' from '{{collection.name}}'?" data-modal-confirmation-url="{% url "delete-sound-from-collection" collection.id sound.id %}?next={{request.path}}&page={{current_page}}" title="Remove from collection" aria-label="Remove from collection">{% bw_icon 'trash' %} Remove sound</a></div>
{% endif %}
</div>
{% endfor %}
{% else %} There aren't any sounds in this collection yet {% endif %}
{% else %}
There aren't any sounds in this collection yet
{% endif %}
</div>
</div>
</div>
Expand Down
47 changes: 47 additions & 0 deletions templates/collections/modal_collect_sound.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{% extends "molecules/modal_base.html" %}
{% load static %}
{% load bw_templatetags %}

{% block id %}collectSoundModal{% endblock %}
{% block extra-class %}{% if request.user.is_authenticated %}modal-width-60{% endif %}{% endblock %}
{% block aria-label %}Collect sound modal{% endblock %}

<div class="col-12">
{% if not request.user.is_authenticated %}
<div class="text-center">
<h4 class="v-spacing-5">Can't collect sound</h4>
</div>
<div class="v-spacing-4 text-center">
<div class="v-spacing-4">To collect sounds, you need to be logged in with your Freesound account.</div>
<button class="btn-primary" data-dismiss="modal">Ok</button>
</div>
{% elif not sound_is_moderated_and_processed_ok %}
<div class="text-center">
<h4 class="v-spacing-5">Can't collect sound</h4>
</div>
<div class="v-spacing-4 text-center">
<div class="v-spacing-4">This sound can't be collected because it has not yet been processed or moderated.</div>
<button class="btn-primary" data-dismiss="modal">Ok</button>
</div>
{% else %}
<div class="center">
<h4 class="v-spacing-5">Collect Sound</h4>
</div>
<div class="v-spacing-4">
{% if collections%}
<div class="v-spacing-4">
{% bw_icon 'bookmark-filled' %}This sound is already in your collections under
{% for col in collections_with_sound %}
<span class="text-black">{{col.name}}</span>{% if not forloop.last%},{% endif %}
{% endfor %}
</div>
{% endif %}
<form>
{{ form }}
<button class = "btn-primary v-spacing-top-2">Collect Sound</button>
</form>
</div>
{% endif %}
</div>
{% endblock%}

3 changes: 3 additions & 0 deletions templates/molecules/navbar_user_section.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
<li class="bw-nav__action dropdown-item">
<a class="bw-link--black" href="{% url 'accounts-manage-sounds' 'published' %}">Manage sounds</a>
</li>
<li class="bw-nav__action dropdown-item">
<a class="bw-link--black" href="{% url 'collections' %}">Your collections</a>
</li>
<li class="bw-nav__action dropdown-item">
<a class="bw-link--black" href="{% url 'bookmarks' %}">Bookmarks</a>
</li>
Expand Down
5 changes: 5 additions & 0 deletions templates/sounds/sound.html
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ <h1><a class="bw-link--black" href="{% url 'sound' sound.user.username sound.id
<button title="Bookmark this sound" aria-label="Bookmark this sound" class="btn-neutral" data-toggle="bookmark-modal" data-modal-url="{% url 'bookmarks-add-form-for-sound' sound.id %}" data-add-bookmark-url="{% url 'add-bookmark' sound.id %}">
{% bw_icon 'bookmark' %}
</button>
<span class="h-spacing-left-1"></span>
<button title="Collect this sound" aria-label="Collect this sound" class="btn-neutral" data-toggle="bookmark-modal" data-modal-url="{% url 'collection-add-form-for-sound' sound.id %}" >
{% bw_icon 'bookmark' %}
</button>
<!--<a class = "btn-neutral" href="{% url 'add-sound-to-collection' sound.id %}">{% bw_icon 'plus' %}</a>-->
</div>
</div>
</div>
Expand Down

0 comments on commit 5f137eb

Please sign in to comment.