added usersetting for generative banner

This commit is contained in:
nvrl committed 2026-02-18 17:31:48 +01:00
1 parent c779ed36d0
commit a822eb95c9
8 files changed
+168 -18

No files matched your search

@@ -0,0 +1,29 @@
# Generated by Django 6.0.2 on 2026-02-18 16:11
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0025_banner_schema'),
]
operations = [
migrations.AddField(
model_name='banner',
name='is_enabled',
field=models.BooleanField(default=True, help_text='Allows users to select this banner.'),
),
migrations.AddField(
model_name='user',
name='selected_banner',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='users', to='api.banner'),
),
migrations.AlterField(
model_name='banner',
name='is_active',
field=models.BooleanField(default=False, help_text='Designates this as the global default banner.'),
),
]
+8 -2
View File
@@ -32,6 +32,7 @@ class User(AbstractUser):
pending_email = models.EmailField(max_length=254, blank=True, null=True)
email_confirmation_code = models.CharField(max_length=6, blank=True, null=True)
following = models.ManyToManyField('self', symmetrical=False, related_name='followers', blank=True)
selected_banner = models.ForeignKey('Banner', on_delete=models.SET_NULL, null=True, blank=True, related_name='users')
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
@@ -195,7 +196,8 @@ class Banner(models.Model):
name = models.CharField(max_length=100)
slug = models.SlugField(unique=True)
description = models.TextField(blank=True)
is_active = models.BooleanField(default=False)
is_active = models.BooleanField(default=False, help_text="Designates this as the global default banner.")
is_enabled = models.BooleanField(default=True, help_text="Allows users to select this banner.")
settings = models.JSONField(default=dict, blank=True)
schema = models.JSONField(default=list, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
@@ -203,8 +205,12 @@ class Banner(models.Model):
def save(self, *args, **kwargs):
if self.is_active:
# Deactivate other banners
# Set all others to inactive
Banner.objects.filter(is_active=True).exclude(pk=self.pk).update(is_active=False)
elif not Banner.objects.filter(is_active=True).exclude(pk=self.pk).exists():
# If this was the only active banner, don't allow deactivating it
# unless there's another one to take its place.
self.is_active = True
super().save(*args, **kwargs)
def __str__(self):
+3 -2
View File
@@ -9,7 +9,7 @@ class CategorySerializer(serializers.ModelSerializer):
class BannerSerializer(serializers.ModelSerializer):
class Meta:
model = Banner
fields = ['id', 'name', 'slug', 'description', 'is_active', 'settings', 'schema', 'created_at', 'updated_at']
fields = ['id', 'name', 'slug', 'description', 'is_active', 'is_enabled', 'settings', 'schema', 'created_at', 'updated_at']
class ReplySerializer(serializers.ModelSerializer):
user = serializers.StringRelatedField(read_only=True)
@@ -87,10 +87,11 @@ class UserSerializer(serializers.ModelSerializer):
is_following = serializers.SerializerMethodField()
unread_notifications_count = serializers.SerializerMethodField()
is_superuser = serializers.BooleanField(read_only=True)
selected_banner_slug = serializers.CharField(source='selected_banner.slug', read_only=True, allow_null=True)
class Meta:
model = User
fields = ['id', 'username', 'email', 'theme', 'language', 'avatar', 'following_count', 'followers_count', 'is_following', 'unread_notifications_count', 'is_superuser']
fields = ['id', 'username', 'email', 'theme', 'language', 'avatar', 'following_count', 'followers_count', 'is_following', 'unread_notifications_count', 'is_superuser', 'selected_banner', 'selected_banner_slug']
def get_avatar(self, obj):
if obj.avatar:
+56 -2
View File
@@ -418,6 +418,7 @@ class UserViewSet(viewsets.ModelViewSet):
user = request.user
theme = request.data.get('theme')
language = request.data.get('language')
banner_id = request.data.get('selected_banner')
updated = False
if theme in dict(User.THEME_CHOICES):
@@ -426,10 +427,27 @@ class UserViewSet(viewsets.ModelViewSet):
if language in dict(User.LANGUAGE_CHOICES):
user.language = language
updated = True
if banner_id is not None:
if banner_id == "":
user.selected_banner = None
updated = True
else:
try:
banner = Banner.objects.get(pk=banner_id, is_enabled=True)
user.selected_banner = banner
updated = True
except Banner.DoesNotExist:
return Response({'error': 'Invalid or disabled banner'}, status=status.HTTP_400_BAD_REQUEST)
if updated:
user.save()
return Response({'status': 'preferences updated', 'theme': user.theme, 'language': user.language})
return Response({
'status': 'preferences updated',
'theme': user.theme,
'language': user.language,
'selected_banner': user.selected_banner.id if user.selected_banner else None
})
return Response({'error': 'No valid updates provided'}, status=status.HTTP_400_BAD_REQUEST)
@action(detail=False, methods=['post'])
@@ -691,6 +709,11 @@ class BannerViewSet(viewsets.ModelViewSet):
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
pagination_class = None
def get_queryset(self):
if self.request.user.is_authenticated and self.request.user.is_superuser:
return Banner.objects.all()
return Banner.objects.filter(is_enabled=True)
def get_permissions(self):
if self.action in ['list', 'retrieve', 'active']:
return [permissions.AllowAny()]
@@ -698,6 +721,27 @@ class BannerViewSet(viewsets.ModelViewSet):
@action(detail=False, methods=['get'])
def active(self, request):
# Determine which banner to show
# Priority:
# 1. Requested user's preference (if username provided in query)
# 2. Logged in user's preference
# 3. Global default
username = request.query_params.get('username')
user = None
if username:
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
pass
if not user and request.user.is_authenticated:
user = request.user
if user and user.selected_banner and user.selected_banner.is_enabled:
serializer = self.get_serializer(user.selected_banner)
return Response(serializer.data)
active_banner = Banner.objects.filter(is_active=True).first()
if active_banner:
serializer = self.get_serializer(active_banner)
@@ -708,8 +752,18 @@ class BannerViewSet(viewsets.ModelViewSet):
def activate(self, request, pk=None):
banner = self.get_object()
banner.is_active = True
banner.is_enabled = True # Active banner must be enabled
banner.save()
return Response({'status': 'banner activated'})
return Response({'status': 'banner set as global default'})
@action(detail=True, methods=['post'])
def toggle_enabled(self, request, pk=None):
banner = self.get_object()
if banner.is_active:
return Response({'error': 'Cannot disable the global default banner.'}, status=status.HTTP_400_BAD_REQUEST)
banner.is_enabled = not banner.is_enabled
banner.save()
return Response({'status': 'enabled' if banner.is_enabled else 'disabled'})
class AdminViewSet(viewsets.ViewSet):
permission_classes = [permissions.IsAdminUser]