fix: video pagination

This commit is contained in:
mohamad
2026-08-01 16:57:49 +03:30
parent 28e0f7f8fe
commit 472eeee2f3
17 changed files with 473 additions and 20 deletions
+20 -3
View File
@@ -8,19 +8,36 @@ from .models import SiteBranding, SiteContact
@admin.register(SiteBranding)
class SiteBrandingAdmin(admin.ModelAdmin):
fieldsets = (
("Icon", {"fields": ("icon", "icon_alt")}),
(
"Brand assets",
{
"fields": (
"icon",
"icon_alt",
"website_icon",
"hero_logo",
"hero_logo_alt",
),
"description": (
"Manage each placement independently. Removing an upload restores "
"the built-in Tecvico asset for that placement."
),
},
),
(
"Sizes",
{
"fields": ("navbar_icon_size", "footer_icon_size"),
"description": "Square dimensions in pixels for each placement.",
"description": (
"Set the logo height for each placement. Its width is calculated "
"automatically so the uploaded image is never cropped or stretched."
),
},
),
(
"Appearance",
{
"fields": (
"object_fit",
"show_border",
"border_width",
"border_color",
@@ -0,0 +1,34 @@
# Generated by Django 5.2.15 on 2026-08-01 13:13
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0003_alter_sitebranding_border_color'),
]
operations = [
migrations.AddField(
model_name='sitebranding',
name='hero_logo',
field=models.ImageField(blank=True, help_text='Large brand artwork shown in the homepage hero. Leave empty to use the default hero artwork.', null=True, upload_to='branding/hero/'),
),
migrations.AddField(
model_name='sitebranding',
name='hero_logo_alt',
field=models.CharField(blank=True, default='Tecvico showcase', help_text='Accessible description for the homepage hero logo.', max_length=200),
),
migrations.AddField(
model_name='sitebranding',
name='website_icon',
field=models.FileField(blank=True, help_text='Icon shown in browser tabs and bookmarks. Use a square ICO, PNG, SVG, JPG, or WebP file. Leave empty to use the default Tecvico icon.', null=True, upload_to='branding/icons/', validators=[django.core.validators.FileExtensionValidator(allowed_extensions=('ico', 'png', 'svg', 'jpg', 'jpeg', 'webp'))]),
),
migrations.AlterField(
model_name='sitebranding',
name='icon',
field=models.ImageField(blank=True, help_text='Brand logo shown in the site navigation and footer. Leave empty to use the default logo.', null=True, upload_to='branding/', verbose_name='Header and footer logo'),
),
]
@@ -0,0 +1,23 @@
# Generated by Django 5.2.15 on 2026-08-01 13:19
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('core', '0004_sitebranding_hero_logo_sitebranding_hero_logo_alt_and_more'),
]
operations = [
migrations.AlterField(
model_name='sitebranding',
name='footer_icon_size',
field=models.PositiveSmallIntegerField(default=34, help_text='Maximum height in pixels for the footer logo. Width scales automatically.'),
),
migrations.AlterField(
model_name='sitebranding',
name='navbar_icon_size',
field=models.PositiveSmallIntegerField(default=26, help_text='Maximum height in pixels for the header logo. Width scales automatically.'),
),
]
+36 -5
View File
@@ -1,3 +1,4 @@
from django.core.validators import FileExtensionValidator
from django.db import models
@@ -13,20 +14,50 @@ class SiteBranding(models.Model):
upload_to="branding/",
blank=True,
null=True,
help_text="Logo shown in the site header and footer. Leave empty to use the default static icon.",
verbose_name="Header and footer logo",
help_text="Brand logo shown in the site navigation and footer. Leave empty to use the default logo.",
)
icon_alt = models.CharField(
max_length=200,
blank=True,
help_text="Alt text for the brand icon (decorative icons can stay empty).",
)
website_icon = models.FileField(
upload_to="branding/icons/",
blank=True,
null=True,
validators=[
FileExtensionValidator(
allowed_extensions=("ico", "png", "svg", "jpg", "jpeg", "webp")
)
],
help_text=(
"Icon shown in browser tabs and bookmarks. Use a square ICO, PNG, SVG, "
"JPG, or WebP file. Leave empty to use the default Tecvico icon."
),
)
hero_logo = models.ImageField(
upload_to="branding/hero/",
blank=True,
null=True,
help_text=(
"Large brand artwork shown in the homepage hero. Leave empty to use "
"the default hero artwork."
),
)
hero_logo_alt = models.CharField(
max_length=200,
blank=True,
default="Tecvico showcase",
help_text="Accessible description for the homepage hero logo.",
)
navbar_icon_size = models.PositiveSmallIntegerField(
default=26,
help_text="Width and height in pixels for the header icon.",
help_text="Maximum height in pixels for the header logo. Width scales automatically.",
)
footer_icon_size = models.PositiveSmallIntegerField(
default=34,
help_text="Width and height in pixels for the footer icon.",
help_text="Maximum height in pixels for the footer logo. Width scales automatically.",
)
show_border = models.BooleanField(
default=False,
@@ -61,9 +92,9 @@ class SiteBranding(models.Model):
def icon_style(self, size_px):
parts = [
f"width:{size_px}px",
"width:auto",
f"height:{size_px}px",
f"object-fit:{self.object_fit}",
"object-fit:contain",
]
if self.show_border:
parts.append(f"border:{self.border_width}px solid {self.border_color}")
+27 -1
View File
@@ -1,4 +1,5 @@
from django.test import RequestFactory, TestCase
from django.urls import reverse
from apps.core.context_processors import site_branding
from apps.core.models import SiteBranding
@@ -17,7 +18,9 @@ class SiteBrandingModelTests(TestCase):
branding.border_width = 2
branding.navbar_icon_size = 30
style = branding.navbar_icon_style
self.assertIn("width:30px", style)
self.assertIn("width:auto", style)
self.assertIn("height:30px", style)
self.assertIn("object-fit:contain", style)
self.assertIn("border:2px solid #ffffff", style)
def test_icon_style_omits_border_when_disabled(self):
@@ -33,3 +36,26 @@ class SiteBrandingContextProcessorTests(TestCase):
ctx = site_branding(request)
self.assertIn("site_branding", ctx)
self.assertIsInstance(ctx["site_branding"], SiteBranding)
class SiteBrandingTemplateTests(TestCase):
def test_custom_brand_assets_are_rendered_independently(self):
branding = SiteBranding.load()
branding.icon = "branding/navigation-logo.png"
branding.website_icon = "branding/icons/site-icon.png"
branding.hero_logo = "branding/hero/hero-logo.png"
branding.hero_logo_alt = "Tecvico research platform"
branding.save()
response = self.client.get(reverse("pages:home"))
self.assertContains(response, 'src="/media/branding/navigation-logo.png"')
self.assertContains(response, 'href="/media/branding/icons/site-icon.png"')
self.assertContains(response, 'src="/media/branding/hero/hero-logo.png"')
self.assertContains(response, 'alt="Tecvico research platform"')
def test_default_assets_remain_when_custom_assets_are_empty(self):
response = self.client.get(reverse("pages:home"))
self.assertContains(response, "images/tecvico/brand-logo.svg")
self.assertContains(response, "images/tecvico/hero-image.svg")