mirror of
https://code.eliotberriot.com/funkwhale/funkwhale.git
synced 2025-10-04 17:49:16 +02:00
See #170: add a description field on tracks, albums, tracks
This commit is contained in:
parent
424b9f133a
commit
2bc71eecfd
38 changed files with 653 additions and 59 deletions
|
@ -26,3 +26,12 @@ class AttachmentFactory(NoUpdateOnCreate, factory.django.DjangoModelFactory):
|
|||
|
||||
class Meta:
|
||||
model = "common.Attachment"
|
||||
|
||||
|
||||
@registry.register
|
||||
class CommonFactory(NoUpdateOnCreate, factory.django.DjangoModelFactory):
|
||||
text = factory.Faker("paragraph")
|
||||
content_type = "text/plain"
|
||||
|
||||
class Meta:
|
||||
model = "common.Content"
|
||||
|
|
21
api/funkwhale_api/common/migrations/0006_content.py
Normal file
21
api/funkwhale_api/common/migrations/0006_content.py
Normal file
|
@ -0,0 +1,21 @@
|
|||
# Generated by Django 2.2.7 on 2020-01-13 10:14
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('common', '0005_auto_20191125_1421'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Content',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('text', models.CharField(blank=True, max_length=5000, null=True)),
|
||||
('content_type', models.CharField(max_length=100)),
|
||||
],
|
||||
),
|
||||
]
|
|
@ -24,6 +24,14 @@ from . import utils
|
|||
from . import validators
|
||||
|
||||
|
||||
CONTENT_TEXT_MAX_LENGTH = 5000
|
||||
CONTENT_TEXT_SUPPORTED_TYPES = [
|
||||
"text/html",
|
||||
"text/markdown",
|
||||
"text/plain",
|
||||
]
|
||||
|
||||
|
||||
@Field.register_lookup
|
||||
class NotEqual(Lookup):
|
||||
lookup_name = "ne"
|
||||
|
@ -273,6 +281,15 @@ class MutationAttachment(models.Model):
|
|||
unique_together = ("attachment", "mutation")
|
||||
|
||||
|
||||
class Content(models.Model):
|
||||
"""
|
||||
A text content that can be associated to other models, like a description, a summary, etc.
|
||||
"""
|
||||
|
||||
text = models.CharField(max_length=CONTENT_TEXT_MAX_LENGTH, blank=True, null=True)
|
||||
content_type = models.CharField(max_length=100)
|
||||
|
||||
|
||||
@receiver(models.signals.post_save, sender=Attachment)
|
||||
def warm_attachment_thumbnails(sender, instance, **kwargs):
|
||||
if not instance.file or not settings.CREATE_IMAGE_THUMBNAILS:
|
||||
|
@ -302,3 +319,18 @@ def trigger_mutation_post_init(sender, instance, created, **kwargs):
|
|||
except AttributeError:
|
||||
return
|
||||
handler(instance)
|
||||
|
||||
|
||||
CONTENT_FKS = {
|
||||
"music.Track": ["description"],
|
||||
"music.Album": ["description"],
|
||||
"music.Artist": ["description"],
|
||||
}
|
||||
|
||||
|
||||
@receiver(models.signals.post_delete, sender=None)
|
||||
def remove_attached_content(sender, instance, **kwargs):
|
||||
fk_fields = CONTENT_FKS.get(instance._meta.label, [])
|
||||
for field in fk_fields:
|
||||
if getattr(instance, "{}_id".format(field)):
|
||||
getattr(instance, field).delete()
|
||||
|
|
|
@ -86,7 +86,6 @@ class MutationSerializer(serializers.Serializer):
|
|||
|
||||
class UpdateMutationSerializer(serializers.ModelSerializer, MutationSerializer):
|
||||
serialized_relations = {}
|
||||
previous_state_handlers = {}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
# we force partial mode, because update mutations are partial
|
||||
|
@ -141,9 +140,12 @@ class UpdateMutationSerializer(serializers.ModelSerializer, MutationSerializer):
|
|||
obj,
|
||||
*list(validated_data.keys()),
|
||||
serialized_relations=self.serialized_relations,
|
||||
handlers=self.previous_state_handlers,
|
||||
handlers=self.get_previous_state_handlers(),
|
||||
)
|
||||
|
||||
def get_previous_state_handlers(self):
|
||||
return {}
|
||||
|
||||
|
||||
def get_update_previous_state(obj, *fields, serialized_relations={}, handlers={}):
|
||||
if not fields:
|
||||
|
|
|
@ -11,6 +11,7 @@ from django.utils.encoding import smart_text
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from . import models
|
||||
from . import utils
|
||||
|
||||
|
||||
class RelatedField(serializers.RelatedField):
|
||||
|
@ -308,3 +309,12 @@ class AttachmentSerializer(serializers.Serializer):
|
|||
return models.Attachment.objects.create(
|
||||
file=validated_data["file"], actor=validated_data["actor"]
|
||||
)
|
||||
|
||||
|
||||
class ContentSerializer(serializers.Serializer):
|
||||
text = serializers.CharField(max_length=models.CONTENT_TEXT_MAX_LENGTH)
|
||||
content_type = serializers.ChoiceField(choices=models.CONTENT_TEXT_SUPPORTED_TYPES,)
|
||||
html = serializers.SerializerMethodField()
|
||||
|
||||
def get_html(self, o):
|
||||
return utils.render_html(o.text, o.content_type)
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
from django.utils.deconstruct import deconstructible
|
||||
|
||||
import bleach.sanitizer
|
||||
import markdown
|
||||
import os
|
||||
import shutil
|
||||
import uuid
|
||||
|
@ -241,3 +243,65 @@ def join_queries_or(left, right):
|
|||
return left | right
|
||||
else:
|
||||
return right
|
||||
|
||||
|
||||
def render_markdown(text):
|
||||
return markdown.markdown(text, extensions=["nl2br"])
|
||||
|
||||
|
||||
HTMl_CLEANER = bleach.sanitizer.Cleaner(
|
||||
strip=True,
|
||||
tags=[
|
||||
"p",
|
||||
"a",
|
||||
"abbr",
|
||||
"acronym",
|
||||
"b",
|
||||
"blockquote",
|
||||
"code",
|
||||
"em",
|
||||
"i",
|
||||
"li",
|
||||
"ol",
|
||||
"strong",
|
||||
"ul",
|
||||
],
|
||||
)
|
||||
|
||||
HTML_LINKER = bleach.linkifier.Linker()
|
||||
|
||||
|
||||
def clean_html(html):
|
||||
return HTMl_CLEANER.clean(html)
|
||||
|
||||
|
||||
def render_html(text, content_type):
|
||||
rendered = render_markdown(text)
|
||||
if content_type == "text/html":
|
||||
rendered = text
|
||||
elif content_type == "text/markdown":
|
||||
rendered = render_markdown(text)
|
||||
else:
|
||||
rendered = render_markdown(text)
|
||||
rendered = HTML_LINKER.linkify(rendered)
|
||||
return clean_html(rendered).strip().replace("\n", "")
|
||||
|
||||
|
||||
@transaction.atomic
|
||||
def attach_content(obj, field, content_data):
|
||||
from . import models
|
||||
|
||||
existing = getattr(obj, "{}_id".format(field))
|
||||
|
||||
if existing:
|
||||
getattr(obj, field).delete()
|
||||
|
||||
if not content_data:
|
||||
return
|
||||
|
||||
content_obj = models.Content.objects.create(
|
||||
text=content_data["text"][: models.CONTENT_TEXT_MAX_LENGTH],
|
||||
content_type=content_data["content_type"],
|
||||
)
|
||||
setattr(obj, field, content_obj)
|
||||
obj.save(update_fields=[field])
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue