import uuid from django.conf import settings from django.db import models class BlockType(models.TextChoices): RICHTEXT = "richtext", "Rich Text" IMAGE = "image", "Image" VIDEO = "video", "Video" EMBED = "embed", "Embed" DOWNLOAD = "download", "Download" class ScanStatus(models.TextChoices): PENDING = "pending", "Pending" CLEAN = "clean", "Clean" INFECTED = "infected", "Infected" ERROR = "error", "Error" class MediaAsset(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) uploaded_by = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="media_assets" ) org_id = models.UUIDField(db_index=True) file_name = models.CharField(max_length=255) file_path = models.CharField(max_length=1000) file_size = models.PositiveBigIntegerField() # bytes mime_type = models.CharField(max_length=100, blank=True) extension = models.CharField(max_length=20, blank=True) scan_status = models.CharField( max_length=20, choices=ScanStatus.choices, default=ScanStatus.PENDING ) scan_result = models.TextField(blank=True) alt_text = models.CharField(max_length=500, blank=True) # WCAG 2.1 AA created_at = models.DateTimeField(auto_now_add=True) class Meta: db_table = "cms_media_asset" ordering = ["-created_at"] def __str__(self): return self.file_name class ContentBlock(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) page = models.ForeignKey( "courses.Page", on_delete=models.CASCADE, related_name="content_blocks" ) order = models.PositiveIntegerField(default=0) block_type = models.CharField(max_length=20, choices=BlockType.choices) # Richtext: HTML body string body = models.TextField(blank=True) # Image/video/download: reference to MediaAsset media_asset = models.ForeignKey( MediaAsset, null=True, blank=True, on_delete=models.SET_NULL, related_name="blocks" ) # Embed: external URL embed_url = models.URLField(max_length=1000, blank=True) # WCAG 2.1 AA: alt text required for image blocks alt_text = models.CharField(max_length=500, blank=True) # Shared metadata caption = models.CharField(max_length=500, blank=True) extra = models.JSONField(default=dict, blank=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: db_table = "cms_content_block" ordering = ["order"] constraints = [ models.UniqueConstraint(fields=["page", "order"], name="unique_page_block_order"), ] def __str__(self): return f"{self.page_id} / {self.block_type} block {self.order}" class CourseTheme(models.Model): """Per-course visual theme configuration.""" id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) course = models.OneToOneField( "courses.Course", on_delete=models.CASCADE, related_name="theme" ) primary_color = models.CharField(max_length=7, default="#000000") # hex secondary_color = models.CharField(max_length=7, default="#ffffff") logo = models.ForeignKey( MediaAsset, null=True, blank=True, on_delete=models.SET_NULL, related_name="course_logos" ) extra = models.JSONField(default=dict, blank=True) updated_at = models.DateTimeField(auto_now=True) class Meta: db_table = "cms_course_theme"