adding features for social network

This commit is contained in:
2026-04-18 13:31:20 +02:00
parent 1bc57a99ce
commit 043e866ac9
7 changed files with 177 additions and 13 deletions

View File

@@ -88,6 +88,8 @@ django-silk[formatting]
yt-dlp
python-magic
weasyprint #tvoření PDFek z html dokumentu + css styly
## -- MISCELLANEOUS --

View File

@@ -2,6 +2,7 @@ from django.db import models
from django.conf import settings
from django.core.exceptions import ValidationError
from django.utils import timezone
from vontor_cz.custom_fields import WebPImageField
class Chat(models.Model):
owner = models.ForeignKey(
@@ -11,6 +12,11 @@ class Chat(models.Model):
related_name='owned_chats'
)
name = models.CharField(max_length=255, blank=True)
icon = WebPImageField(upload_to='chat_icons/', null=True, blank=True)
banner = WebPImageField(upload_to='chat_banners/', null=True, blank=True)
members = models.ManyToManyField(
settings.AUTH_USER_MODEL,
related_name='chats',
@@ -23,6 +29,15 @@ class Chat(models.Model):
blank=True
)
hub = models.ForeignKey(
'pages.Hub',
on_delete=models.CASCADE,
null=True,
blank=True
)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
@@ -44,7 +59,8 @@ class Message(models.Model):
sender = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
on_delete=models.SET_NULL,
null=True,
related_name='sent_messages'
)
@@ -68,18 +84,10 @@ class Message(models.Model):
updated_at = models.DateTimeField(auto_now=True)
def clean(self):
# VALIDATION: Ensure sender is actually in the chat
# Note: We check self.id to avoid running this on creation if logic depends on M2M
# But generally, a sender must be a member.
if self.chat and self.sender:
if not self.chat.members.filter(id=self.sender.id).exists():
if self.chat_id and self.sender_id:
if not self.chat.members.filter(id=self.sender_id).exists():
raise ValidationError("Sender is not a member of this chat.")
def save(self, *args, **kwargs):
# Optional: Run validation before saving
# self.full_clean()
super().save(*args, **kwargs)
# --- HELPER METHODS FOR WEBSOCKETS / VIEWS ---
def edit_content(self, new_text):

View File

View File

@@ -0,0 +1,17 @@
# Social Feature Ideas
## Hub
- Hub visibility flag (`is_public`) — private/public hubs
- Hub owner transfer (current owner passes ownership to another member)
## Chat
- Chat type field (DM vs group) — enforce 2-member limit on DMs
- Message soft-delete (`is_deleted` flag) — preserve reply context when a message is removed
## Posts
- Post reactions/likes — equivalent of MessageReaction but for posts
- Post comments — a `Comment` model FK-ing to `Post`
- Upvote/downvote system for posts — reddit style voting with score and sorting by "hotness"
## Users
- User blocking — affects message/post visibility across all social features

View File

@@ -1,3 +1,50 @@
from django.db import models
from django.conf import settings
from vontor_cz.custom_fields import WebPImageField
# Create your models here.
class Hub(models.Model):
name = models.CharField(max_length=255, unique=True)
description = models.TextField(blank=True, null=True)
owner = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL,
null=True,
related_name='owned_hubs'
)
icon = WebPImageField(upload_to='hub_icons/', null=True, blank=True)
banner = WebPImageField(upload_to='hub_banners/', null=True, blank=True)
members = models.ManyToManyField(
settings.AUTH_USER_MODEL,
related_name='hubs',
blank=True
)
#TODO:
def __str__(self):
return self.name
class HubPermission(models.Model):
hub = models.ForeignKey(Hub, on_delete=models.CASCADE, related_name='moderators')
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
changing_name = models.BooleanField(default=False)
changing_description = models.BooleanField(default=False)
changing_icon = models.BooleanField(default=False)
changing_banner = models.BooleanField(default=False)
managing_members = models.BooleanField(default=False)
managing_posts = models.BooleanField(default=False)
managing_chats = models.BooleanField(default=False)
class Meta:
unique_together = ('hub', 'user')
def __str__(self):
return f"{self.user} moderates {self.hub}"

View File

@@ -1,3 +1,42 @@
from django.db import models
# Create your models here.
from django.conf import settings
import magic
mime = magic.Magic(mime=True)
class Post(models.Model):
content = models.TextField(blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
author = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name='posts'
)
hub = models.ForeignKey(
'pages.Hub',
on_delete=models.CASCADE,
null=True,
blank=True
)
def __str__(self):
return f"Post {self.id}"
class PostContent(models.Model):
post = models.ForeignKey(Post, related_name='contents', on_delete=models.CASCADE)
mime_type = models.CharField(max_length=100)
file = models.FileField(upload_to='post_contents/', null=True, blank=True) # For images/videos
alt_text = models.TextField(blank=True, null=True) # For text content
def __str__(self):
return f"Content for Post {self.post.id} - Type: {self.mime_type}"
def save(self, *args, **kwargs):
if self.file and not self.file._committed:
self.mime_type = mime.from_buffer(self.file.read(1024))
return super().save(*args, **kwargs)

View File

@@ -0,0 +1,51 @@
import os
from io import BytesIO
from PIL import Image
from django.db import models
from django.core.files.base import ContentFile
import logging
logger = logging.getLogger(__name__)
class WebPImageField(models.ImageField):
"""
A custom ImageField that converts uploaded images to WebP automatically.
Inherits from models.ImageField (description: "Image").
Accepts the same arguments:
verbose_name, name, upload_to, storage,
width_field, height_field, **kwargs
"""
def pre_save(self, model_instance, add):
file_obj = getattr(model_instance, self.attname)
if file_obj and not file_obj._committed:
self._convert_to_webp(file_obj)
return super().pre_save(model_instance, add)
def _convert_to_webp(self, file_obj):
try:
file_obj.open()
image = Image.open(file_obj)
if image.format == 'WEBP':
image.close()
return
if image.mode == 'P':
image = image.convert('RGBA')
elif image.mode not in ('RGBA', 'LA'):
image = image.convert('RGB')
image_io = BytesIO()
image.save(image_io, format='WEBP', quality=85, optimize=True)
image.close()
new_filename = os.path.splitext(file_obj.name)[0] + '.webp'
file_obj.save(new_filename, ContentFile(image_io.getvalue()), save=False)
except Exception as e:
logger.error(f"WebP conversion failed: {e}")