From b97fed53404bc60916fbfd540c727d30ed25b1e5 Mon Sep 17 00:00:00 2001 From: Warren Chen Date: Thu, 16 Oct 2025 12:02:57 +0900 Subject: [PATCH 01/18] Implement footer navigation and social media --- .gitignore | 2 +- innovedus_cms/base/__init__.py | 0 innovedus_cms/base/admin.py | 3 + innovedus_cms/base/apps.py | 6 ++ innovedus_cms/base/migrations/0001_initial.py | 55 ++++++++++ ..._navigationsettings_github_url_and_more.py | 36 +++++++ .../migrations/0003_socialmediasettings.py | 24 +++++ ...ter_navigationsettings_options_and_more.py | 44 ++++++++ innovedus_cms/base/migrations/__init__.py | 0 innovedus_cms/base/models.py | 100 ++++++++++++++++++ .../templates/base/includes/footer_text.html | 5 + innovedus_cms/base/templatetags/__init__.py | 0 .../base/templatetags/navigation_tags.py | 18 ++++ innovedus_cms/base/tests.py | 3 + innovedus_cms/base/views.py | 3 + innovedus_cms/manage.py | 4 + innovedus_cms/mysite/settings/base.py | 3 + innovedus_cms/mysite/templates/base.html | 2 + .../mysite/templates/includes/footer.html | 32 ++++++ 19 files changed, 339 insertions(+), 1 deletion(-) create mode 100644 innovedus_cms/base/__init__.py create mode 100644 innovedus_cms/base/admin.py create mode 100644 innovedus_cms/base/apps.py create mode 100644 innovedus_cms/base/migrations/0001_initial.py create mode 100644 innovedus_cms/base/migrations/0002_remove_navigationsettings_github_url_and_more.py create mode 100644 innovedus_cms/base/migrations/0003_socialmediasettings.py create mode 100644 innovedus_cms/base/migrations/0004_alter_navigationsettings_options_and_more.py create mode 100644 innovedus_cms/base/migrations/__init__.py create mode 100644 innovedus_cms/base/models.py create mode 100644 innovedus_cms/base/templates/base/includes/footer_text.html create mode 100644 innovedus_cms/base/templatetags/__init__.py create mode 100644 innovedus_cms/base/templatetags/navigation_tags.py create mode 100644 innovedus_cms/base/tests.py create mode 100644 innovedus_cms/base/views.py create mode 100644 innovedus_cms/mysite/templates/includes/footer.html diff --git a/.gitignore b/.gitignore index 61cdb80..431a750 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ .env __pycache__ -*.pyc \ No newline at end of file +*.pyc \ No newline at end of file diff --git a/innovedus_cms/base/__init__.py b/innovedus_cms/base/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/innovedus_cms/base/admin.py b/innovedus_cms/base/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/innovedus_cms/base/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/innovedus_cms/base/apps.py b/innovedus_cms/base/apps.py new file mode 100644 index 0000000..05011e8 --- /dev/null +++ b/innovedus_cms/base/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class BaseConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'base' diff --git a/innovedus_cms/base/migrations/0001_initial.py b/innovedus_cms/base/migrations/0001_initial.py new file mode 100644 index 0000000..a81d612 --- /dev/null +++ b/innovedus_cms/base/migrations/0001_initial.py @@ -0,0 +1,55 @@ +# Generated by Django 5.2.7 on 2025-10-15 03:30 + +import django.db.models.deletion +import uuid +import wagtail.fields +import wagtail.models.preview +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('wagtailcore', '0095_groupsitepermission'), + ] + + operations = [ + migrations.CreateModel( + name='NavigationSettings', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('linkedin_url', models.URLField(blank=True, verbose_name='LinkedIn URL')), + ('github_url', models.URLField(blank=True, verbose_name='GitHub URL')), + ('mastodon_url', models.URLField(blank=True, verbose_name='Mastodon URL')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='FooterText', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('translation_key', models.UUIDField(default=uuid.uuid4, editable=False)), + ('live', models.BooleanField(default=True, editable=False, verbose_name='live')), + ('has_unpublished_changes', models.BooleanField(default=False, editable=False, verbose_name='has unpublished changes')), + ('first_published_at', models.DateTimeField(blank=True, db_index=True, null=True, verbose_name='first published at')), + ('last_published_at', models.DateTimeField(editable=False, null=True, verbose_name='last published at')), + ('go_live_at', models.DateTimeField(blank=True, null=True, verbose_name='go live date/time')), + ('expire_at', models.DateTimeField(blank=True, null=True, verbose_name='expiry date/time')), + ('expired', models.BooleanField(default=False, editable=False, verbose_name='expired')), + ('body', wagtail.fields.RichTextField()), + ('latest_revision', models.ForeignKey(blank=True, editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailcore.revision', verbose_name='latest revision')), + ('live_revision', models.ForeignKey(blank=True, editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailcore.revision', verbose_name='live revision')), + ('locale', models.ForeignKey(editable=False, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='wagtailcore.locale', verbose_name='locale')), + ], + options={ + 'verbose_name_plural': 'Footer Text', + 'abstract': False, + 'unique_together': {('translation_key', 'locale')}, + }, + bases=(wagtail.models.preview.PreviewableMixin, models.Model), + ), + ] diff --git a/innovedus_cms/base/migrations/0002_remove_navigationsettings_github_url_and_more.py b/innovedus_cms/base/migrations/0002_remove_navigationsettings_github_url_and_more.py new file mode 100644 index 0000000..5c2df52 --- /dev/null +++ b/innovedus_cms/base/migrations/0002_remove_navigationsettings_github_url_and_more.py @@ -0,0 +1,36 @@ +# Generated by Django 5.2.7 on 2025-10-15 06:17 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('base', '0001_initial'), + ] + + operations = [ + migrations.RemoveField( + model_name='navigationsettings', + name='github_url', + ), + migrations.RemoveField( + model_name='navigationsettings', + name='mastodon_url', + ), + migrations.AddField( + model_name='navigationsettings', + name='facebook_url', + field=models.URLField(blank=True, verbose_name='Facebook URL'), + ), + migrations.AddField( + model_name='navigationsettings', + name='instagram_url', + field=models.URLField(blank=True, verbose_name='Instagram URL'), + ), + migrations.AddField( + model_name='navigationsettings', + name='thread_url', + field=models.URLField(blank=True, verbose_name='Thread URL'), + ), + ] diff --git a/innovedus_cms/base/migrations/0003_socialmediasettings.py b/innovedus_cms/base/migrations/0003_socialmediasettings.py new file mode 100644 index 0000000..ea4f694 --- /dev/null +++ b/innovedus_cms/base/migrations/0003_socialmediasettings.py @@ -0,0 +1,24 @@ +# Generated by Django 5.2.7 on 2025-10-15 06:48 + +import wagtail.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('base', '0002_remove_navigationsettings_github_url_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='SocialMediaSettings', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('links', wagtail.fields.StreamField([('link', 2)], block_lookup={0: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('twitter', 'Twitter'), ('instagram', 'Instagram'), ('linkedin', 'LinkedIn'), ('youtube', 'YouTube')]}), 1: ('wagtail.blocks.URLBlock', (), {}), 2: ('wagtail.blocks.StructBlock', [[('platform', 0), ('url', 1)]], {})})), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/innovedus_cms/base/migrations/0004_alter_navigationsettings_options_and_more.py b/innovedus_cms/base/migrations/0004_alter_navigationsettings_options_and_more.py new file mode 100644 index 0000000..8bc5815 --- /dev/null +++ b/innovedus_cms/base/migrations/0004_alter_navigationsettings_options_and_more.py @@ -0,0 +1,44 @@ +# Generated by Django 5.2.7 on 2025-10-15 08:49 + +import wagtail.fields +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('base', '0003_socialmediasettings'), + ] + + operations = [ + migrations.AlterModelOptions( + name='navigationsettings', + options={'verbose_name': 'Footer Navigation'}, + ), + migrations.RemoveField( + model_name='navigationsettings', + name='facebook_url', + ), + migrations.RemoveField( + model_name='navigationsettings', + name='instagram_url', + ), + migrations.RemoveField( + model_name='navigationsettings', + name='linkedin_url', + ), + migrations.RemoveField( + model_name='navigationsettings', + name='thread_url', + ), + migrations.AddField( + model_name='navigationsettings', + name='footer_links', + field=wagtail.fields.StreamField([('section', 5)], blank=True, block_lookup={0: ('wagtail.blocks.CharBlock', (), {'required': False}), 1: ('wagtail.blocks.CharBlock', (), {}), 2: ('wagtail.blocks.URLBlock', (), {}), 3: ('wagtail.blocks.StructBlock', [[('label', 1), ('url', 2)]], {}), 4: ('wagtail.blocks.ListBlock', (3,), {}), 5: ('wagtail.blocks.StructBlock', [[('title', 0), ('links', 4)]], {})}, null=True), + ), + migrations.AlterField( + model_name='socialmediasettings', + name='links', + field=wagtail.fields.StreamField([('link', 2)], block_lookup={0: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('facebook', 'Facebook'), ('twitter', 'Twitter'), ('instagram', 'Instagram'), ('thread', 'Thread'), ('linkedin', 'LinkedIn'), ('youtube', 'YouTube')]}), 1: ('wagtail.blocks.URLBlock', (), {}), 2: ('wagtail.blocks.StructBlock', [[('platform', 0), ('url', 1)]], {})}), + ), + ] diff --git a/innovedus_cms/base/migrations/__init__.py b/innovedus_cms/base/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/innovedus_cms/base/models.py b/innovedus_cms/base/models.py new file mode 100644 index 0000000..47e1f04 --- /dev/null +++ b/innovedus_cms/base/models.py @@ -0,0 +1,100 @@ +from django.db import models +from wagtail.admin.panels import ( + FieldPanel, + MultiFieldPanel, + + # import PublishingPanel: + PublishingPanel, +) + +# import RichTextField: +from wagtail.fields import RichTextField + +# import DraftStateMixin, PreviewableMixin, RevisionMixin, TranslatableMixin: +from wagtail.models import ( + DraftStateMixin, + PreviewableMixin, + RevisionMixin, + TranslatableMixin, +) + +from wagtail.contrib.settings.models import ( + BaseGenericSetting, + register_setting, +) + +from wagtail.snippets.models import register_snippet +from wagtail.fields import StreamField +from wagtail import blocks + +@register_setting +class NavigationSettings(BaseGenericSetting): + footer_links = StreamField([ + ("section", blocks.StructBlock([ + ("title", blocks.CharBlock(required=False)), + ("links", blocks.ListBlock(blocks.StructBlock([ + ("label", blocks.CharBlock()), + ("url", blocks.URLBlock()) + ]))), + ])) + ], use_json_field=True, blank=True, null=True) + + panels = [ + FieldPanel("footer_links"), + ] + + class Meta: + verbose_name = "Footer Navigation" + +class SocialLinkBlock(blocks.StructBlock): + SOCIAL_MEDIA_CHOICES = [ + ("facebook", "Facebook"), + ("twitter", "Twitter"), + ("instagram", "Instagram"), + ("thread", "Thread"), + ("linkedin", "LinkedIn"), + ("youtube", "YouTube"), + ] + + platform = blocks.ChoiceBlock(choices=SOCIAL_MEDIA_CHOICES) + url = blocks.URLBlock() + + class Meta: + icon = "link" + label = "Social Link" + +@register_setting +class SocialMediaSettings(BaseGenericSetting): + links = StreamField([ + ("link", SocialLinkBlock()), + ], use_json_field=True) + + panels = [FieldPanel("links")] + +@register_snippet +class FooterText( + DraftStateMixin, + RevisionMixin, + PreviewableMixin, + TranslatableMixin, + models.Model, +): + + body = RichTextField() + + panels = [ + FieldPanel("body"), + PublishingPanel(), + ] + + def __str__(self): + return "Footer text" + + def get_preview_template(self, request, mode_name): + return "base.html" + + def get_preview_context(self, request, mode_name): + return {"footer_text": self.body} + + class Meta(TranslatableMixin.Meta): + verbose_name_plural = "Footer Text" \ No newline at end of file diff --git a/innovedus_cms/base/templates/base/includes/footer_text.html b/innovedus_cms/base/templates/base/includes/footer_text.html new file mode 100644 index 0000000..2dec019 --- /dev/null +++ b/innovedus_cms/base/templates/base/includes/footer_text.html @@ -0,0 +1,5 @@ +{% load wagtailcore_tags %} + +
+ {{ footer_text|richtext }} +
\ No newline at end of file diff --git a/innovedus_cms/base/templatetags/__init__.py b/innovedus_cms/base/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/innovedus_cms/base/templatetags/navigation_tags.py b/innovedus_cms/base/templatetags/navigation_tags.py new file mode 100644 index 0000000..384484e --- /dev/null +++ b/innovedus_cms/base/templatetags/navigation_tags.py @@ -0,0 +1,18 @@ +from django import template + +from base.models import FooterText + +register = template.Library() + + +@register.inclusion_tag("base/includes/footer_text.html", takes_context=True) +def get_footer_text(context): + footer_text = context.get("footer_text", "") + + if not footer_text: + instance = FooterText.objects.filter(live=True).first() + footer_text = instance.body if instance else "" + + return { + "footer_text": footer_text, + } \ No newline at end of file diff --git a/innovedus_cms/base/tests.py b/innovedus_cms/base/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/innovedus_cms/base/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/innovedus_cms/base/views.py b/innovedus_cms/base/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/innovedus_cms/base/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/innovedus_cms/manage.py b/innovedus_cms/manage.py index 71901b0..64f57ad 100644 --- a/innovedus_cms/manage.py +++ b/innovedus_cms/manage.py @@ -3,6 +3,10 @@ import os import sys +# from dotenv import load_dotenv + +# env_file = os.environ.get("ENV_FILE", "../.env") +# load_dotenv(env_file) def main(): """Run administrative tasks.""" diff --git a/innovedus_cms/mysite/settings/base.py b/innovedus_cms/mysite/settings/base.py index 7fa2bc7..08ad2d1 100644 --- a/innovedus_cms/mysite/settings/base.py +++ b/innovedus_cms/mysite/settings/base.py @@ -28,6 +28,7 @@ INSTALLED_APPS = [ "search", "wagtail.contrib.forms", "wagtail.contrib.redirects", + "wagtail.contrib.settings", "wagtail.embeds", "wagtail.sites", "wagtail.users", @@ -46,6 +47,7 @@ INSTALLED_APPS = [ "django.contrib.sessions", "django.contrib.messages", "django.contrib.staticfiles", + "base", ] MIDDLEWARE = [ @@ -74,6 +76,7 @@ TEMPLATES = [ "django.template.context_processors.request", "django.contrib.auth.context_processors.auth", "django.contrib.messages.context_processors.messages", + "wagtail.contrib.settings.context_processors.settings", ], }, }, diff --git a/innovedus_cms/mysite/templates/base.html b/innovedus_cms/mysite/templates/base.html index 79e2a17..530596f 100644 --- a/innovedus_cms/mysite/templates/base.html +++ b/innovedus_cms/mysite/templates/base.html @@ -36,6 +36,8 @@ {% block content %}{% endblock %} + {% include "includes/footer.html" %} + {# Global javascript #} diff --git a/innovedus_cms/mysite/templates/includes/footer.html b/innovedus_cms/mysite/templates/includes/footer.html new file mode 100644 index 0000000..ee5cc23 --- /dev/null +++ b/innovedus_cms/mysite/templates/includes/footer.html @@ -0,0 +1,32 @@ +{% load navigation_tags %} + + \ No newline at end of file -- 2.47.2 From 0874255859e0f10e32a8e107fd09c4441938fbfc Mon Sep 17 00:00:00 2001 From: Warren Chen Date: Thu, 16 Oct 2025 15:29:55 +0900 Subject: [PATCH 02/18] Add header --- .../base/migrations/0005_headersettring.py | 28 ++++++++++++++++ ...6_rename_headersettring_headersettrings.py | 18 ++++++++++ ...7_rename_headersettrings_headersettings.py | 18 ++++++++++ innovedus_cms/base/models.py | 31 ++++++++++++++++++ innovedus_cms/manage.py | 6 ++-- .../images/DeButAI.2e16d0ba.fill-200x60.jpg | Bin 0 -> 3132 bytes .../images/DeButAI.2e16d0ba.fill-60x60.jpg | Bin 0 -> 1986 bytes .../media/images/DeButAI.max-165x165.jpg | Bin 0 -> 4796 bytes .../media/original_images/DeButAI.jpg | Bin 0 -> 91774 bytes innovedus_cms/mysite/templates/base.html | 2 ++ .../mysite/templates/includes/header.html | 30 +++++++++++++++++ 11 files changed, 130 insertions(+), 3 deletions(-) create mode 100644 innovedus_cms/base/migrations/0005_headersettring.py create mode 100644 innovedus_cms/base/migrations/0006_rename_headersettring_headersettrings.py create mode 100644 innovedus_cms/base/migrations/0007_rename_headersettrings_headersettings.py create mode 100644 innovedus_cms/media/images/DeButAI.2e16d0ba.fill-200x60.jpg create mode 100644 innovedus_cms/media/images/DeButAI.2e16d0ba.fill-60x60.jpg create mode 100644 innovedus_cms/media/images/DeButAI.max-165x165.jpg create mode 100644 innovedus_cms/media/original_images/DeButAI.jpg create mode 100644 innovedus_cms/mysite/templates/includes/header.html diff --git a/innovedus_cms/base/migrations/0005_headersettring.py b/innovedus_cms/base/migrations/0005_headersettring.py new file mode 100644 index 0000000..8d138e0 --- /dev/null +++ b/innovedus_cms/base/migrations/0005_headersettring.py @@ -0,0 +1,28 @@ +# Generated by Django 5.2.7 on 2025-10-16 04:59 + +import django.db.models.deletion +import wagtail.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('base', '0004_alter_navigationsettings_options_and_more'), + ('wagtailimages', '0027_image_description'), + ] + + operations = [ + migrations.CreateModel( + name='HeaderSettring', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('site_name', models.CharField(blank=True, max_length=255)), + ('extra_links', wagtail.fields.StreamField([('link', 2)], blank=True, block_lookup={0: ('wagtail.blocks.CharBlock', (), {}), 1: ('wagtail.blocks.URLBlock', (), {}), 2: ('wagtail.blocks.StructBlock', [[('label', 0), ('url', 1)]], {})}, null=True)), + ('logo', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.image')), + ], + options={ + 'verbose_name': 'Header Settings', + }, + ), + ] diff --git a/innovedus_cms/base/migrations/0006_rename_headersettring_headersettrings.py b/innovedus_cms/base/migrations/0006_rename_headersettring_headersettrings.py new file mode 100644 index 0000000..f5fc5b2 --- /dev/null +++ b/innovedus_cms/base/migrations/0006_rename_headersettring_headersettrings.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.7 on 2025-10-16 05:11 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('base', '0005_headersettring'), + ('wagtailimages', '0027_image_description'), + ] + + operations = [ + migrations.RenameModel( + old_name='HeaderSettring', + new_name='HeaderSettrings', + ), + ] diff --git a/innovedus_cms/base/migrations/0007_rename_headersettrings_headersettings.py b/innovedus_cms/base/migrations/0007_rename_headersettrings_headersettings.py new file mode 100644 index 0000000..1a1f312 --- /dev/null +++ b/innovedus_cms/base/migrations/0007_rename_headersettrings_headersettings.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.7 on 2025-10-16 05:12 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('base', '0006_rename_headersettring_headersettrings'), + ('wagtailimages', '0027_image_description'), + ] + + operations = [ + migrations.RenameModel( + old_name='HeaderSettrings', + new_name='HeaderSettings', + ), + ] diff --git a/innovedus_cms/base/models.py b/innovedus_cms/base/models.py index 47e1f04..99355ee 100644 --- a/innovedus_cms/base/models.py +++ b/innovedus_cms/base/models.py @@ -27,6 +27,37 @@ from wagtail.snippets.models import register_snippet from wagtail.fields import StreamField from wagtail import blocks +@register_setting +class HeaderSettings(BaseGenericSetting): + logo = models.ForeignKey( + "wagtailimages.Image", + null=True, + blank=True, + on_delete=models.SET_NULL, + related_name="+", + ) + site_name = models.CharField(max_length=255, blank=True) + extra_links = StreamField([ + ("link", blocks.StructBlock([ + ("label", blocks.CharBlock()), + ("url", blocks.URLBlock()) + ])) + ], use_json_field=True, blank=True, null=True) + + panels = [ + MultiFieldPanel( + [ + FieldPanel("logo"), + FieldPanel("site_name"), + FieldPanel("extra_links"), + ], + heading="Header Settings", + ), + ] + + class Meta: + verbose_name = "Header Settings" + @register_setting class NavigationSettings(BaseGenericSetting): footer_links = StreamField([ diff --git a/innovedus_cms/manage.py b/innovedus_cms/manage.py index 64f57ad..336e757 100644 --- a/innovedus_cms/manage.py +++ b/innovedus_cms/manage.py @@ -3,10 +3,10 @@ import os import sys -# from dotenv import load_dotenv +from dotenv import load_dotenv -# env_file = os.environ.get("ENV_FILE", "../.env") -# load_dotenv(env_file) +env_file = os.environ.get("ENV_FILE", "../.env") +load_dotenv(env_file) def main(): """Run administrative tasks.""" diff --git a/innovedus_cms/media/images/DeButAI.2e16d0ba.fill-200x60.jpg b/innovedus_cms/media/images/DeButAI.2e16d0ba.fill-200x60.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5d8d7a5170a11505e53209c7b83ec256920eaea5 GIT binary patch literal 3132 zcmb7Gc|4Ts7k}P)%^3S^maH?D$acrriwrYasmIdA^_LocH^jbDlHjyh}q%&j4koEAm#4ZDhf500R;b0^&e{?H?1# za?F z5zRZ~EAJl}%|V3r<>6Z3r|MY}pP5StJL`v(6D z`~3_5ItwuY=vX9JKKNTWf$_+B05BL-z|Xd%-qTMSxnVHvr`c}WK-gdK@>PyA1+c8*bDZPl@+#~s}N{_!aA`R=*T zOG`_S0Ry060RaIRjr^d|XpEpJRuB;h5fLF#yaa)Om%!tRQu5M7lB^^iFGG=$RZvh; zQX)z#t12t1$}1`<@<|}%Q9+ELI2J3eNWzm8|8H5k4)9oD26|yg8KCeGjE9zbfE~5alYq0+AIRfKiYDdT9iR!Vo~=V5B;s{-gJhlq=>h4BjtJm^WAy51TH@NL^ZB2B+)Puw}*PTAK z#RY=&WfS6M4D;qCnZoSi+g5p<{EJ7$iR>?KtlmG6f9D=L>V3)BP-T0Z);*I0PS*g3ouQy3tL`hEEiQVrcEO%!bMQaO#gWSVD zQ$CTRqi8z|5?0+kS`s$*Ej0aOp1im3d3TMH*cfLqvO+r!W(d~) z(Y*nCR9+hT%VRyc>NOZ^g?$vU0eXgszlQ`);++pP5pG10!P z5VznJ9G>dj8=vsl;eAP}voV3|66tAPKTg+IOt(1E+M*NS=1#oeJz!1=-Y9q^Z;${A{*^W4mQest!4ar>Fp*5NTuv8KHWtvQ z_3pxtxfdK9r>b&0ND_yfts zLM?y$n-Ra~%{CQ{m_s&KGp&2CuTd}fY*6sJ>O6ARdK}_93}dh&Lcd}`K`;=&6DZ0! zqMj2wibPhSTRJ!L(5m_dRxZ&=**RsBRGMSVq4KV=UsZvzKz&VHyXSOT_I8cAxSr9A z=2;RA;pB|CDBYp?rcv5Q8*Hp6wNC2#^s$-15TR4$U&|d`HeOa}H1*kXE5@*=OXF+H zX}Olp9|oiMiHSpt^oHoE$m_()rX)m6fAB z?2Rb1kmBdp3d&pCQ(C=-FY3E74SI%OZRO?{pV(`1I^#%|L=y(9GkoJ31)nCj+JK^T zn)V>C_N7q<)2&kb^`SsXtxtvKJB5AQGvYnpPBGNYyfRB)Z1I~tWOK;Nfqo-`)i2zX zOMdn$dNMu8bF|QQLm?L^u%0$l_hwj~{(+`Ax+J^4xOv2?~Y$FvkY%+320#s zHcp9uSANn{aXZt)`pxRNZLUv*2hJ+`z8w&H6z~L)&T0#^!OY8`K7!vxXa_^eaNL4Yp`k%^VHeM5;*+#%6~a9u*Js zbhB!Xd}+%KY$=6LEB~%_GKH!=R2L<>zcggkEHA$=_Tf>rA}MVXgKgq=mIgJR=ex9s zvnHvfDGn34i-it9TLuI}*nbs2gjBvB&eAC=DZA_)n@n%yQM(++=KgLO7L;$mtXLu6 znE7_Qk*b^rHFEpSv8mg*3qg8CLQ%{+J#)gZ8$5BDf&K^l=6X~5XV;OfvK1^_D)| zd5|c=*tpK*@W`Y>A~35p_Q@Y!}eX_Dv27~NxFDo)_fLz04lB|Uy}XZ zOn4YymU|=-m&!)zXzg2Wkm@*O6u~gO(tsg6py*jTCE?gnWI9rYbB>K&{}dqZ16N}f zO1qjl#Cs%A^o3kBDtyLKr<9|;D-tg4#ImY5PjiF<%RGix5t$*p4JrEO9@Xi)>H8~9 z_a}k1_oN)8G;Vk2wJ>^dj-%H7N?FDk#F{Wag)GzlXQf z2)0SQpb5Qtf{-e0EZkPZGQv}n9FlESb-PMzxCZDskxJ^I{)S$>3J@6%dS0}DM=0k81MPLOF-Ok^Mm6A21$4-s3gvD zs>v>}w7u4qQmEjX1^bpVqu1H6krmyO`hPZwTcddas^Asy?``h7_8bmwd3H zuyQBE^4(<7^u&XJ@{^3U+gKa*#x>Pa1MyufzjbM$T$T8yTyAkY8+4O)5HsrRABX5U z7Sm=Zoa+ue&?R^&3BAl z#)p6il+Bg_1OWhnKVa-dx@JqIF(IMBve`lY!yDIA;!`y!0YGY+RvkKPrXVtEp@7~C zFu(?;z#P~q<2C8>Fj+W&p;2aO;(D2w1oX7z)O;Q(o3Rs&O1;k zEFL}gpU5;prHO|-2Keye)74s7EE8g8mNuQj9EjO!SQNz06ka-vB@~Vu##C)=b$F;0 zo=Jo_cIh`b?i>8~UGNCNGhwq7_!?T(N_ZatQmGrDG#FkV2p{SVvA!|7w!9nw&wc=~ zt)pvkWdNMr1OTUdbWH@$u?henY_2z@oXw(BKz%d$)hT-sqTl zFv^G{VS?dw8VdKuA;U4U0|iGh8gA?cObkK-gXTn@Ha=g)ijIh^oZ5onQ957Uo4!*| zyk?b_XEy)dGe5O#nOzR^rT^3WvGN()%a%z5At4K3ZUF&P0TjVUl4CRk#es!@uoSvU z;1vv56oR84cPiT5Q!X}D4xUJg;VlY$zr`j!@p%7lqRj8s+g2=oJ^o~Sw3^tuFd{Xp zo)cR8M_t(IpT|5%spkX;vw~GO#-vBQ2wgT|LcDTidUq1PC^7d)>n*NXvS-b%K_6I$ zEeYodR`N|CBuWSz+=ohl%J?7_hr*UMdO7)Ny(tEd1StA121<~S;X6b@DGG19WqWHK zTW@G9_CEnhF0fq$rD1gJYr_ask^~l(4!HX~wv|BWtIQzW@}_u5W`_RVyonF`X?8Gb zQ}ThCq|1=$zFgsK#s?ptx!qZ*n*1ty#l(KGVs4#R6%rR=1cHI)v9B96e3e2c$r|k4 z)F$*c6b2ma<<+~2r~EnH&>kFNdbmchUEfi072D7`YqEG={;ajxXYB$mi>Jx`wq3SN zxL#v=;aczv?@uey;siauuSr<@B;NK}%L8++b+k!j+lEE0Pwmgun{BBWxRzEK?3gXy z+j*<+OK;1Es2mxWn_amxMsvoZTJQM1ukP{lEvroE7V@ASb54ZU->9xgVgKGZt|6i0 zcFFvF+nP0HJyzuPrKh)Dt5U4 zc3tkxyY8X4^7|X!tS&1lskpMPWQrmby&UP6^!of*M~x+3Su<6y^lLiMS=iNh3`re$ z9#-S0ZTD9uGR)fQo>r@yb{p=?m-E)Tue#*Iwn* z=9fek%)usNZfX3|7+2Suf~s4&uKiDFN3!?0Rkz6Iq;EX#@nUMpGwq|Upn^@p5NY5J zyZhyv$IG42fuI?OQ`ZKhf)!P)gpR_uT!#nG2CZLsAG7jmmN=0CVGQQI%|99VHX8cDCb=T%yaKMAdHOrX6WUcI!${38L$exfjBD*1!ZIG>OS?gn8(qzk0mXeU{ z*=27dB!#4?_&%yneP6HN_n+UH*UWv-J?FXad(S=R-t+8^@4f_3x?~+P0D%Ai1pENI zuOZ_)>gqNIMyJR+CpCX5I^7)HDNqyuxO@2e7-^Ak=4UK$46gwgzyTltRzSecf#RiS zOf~^Pq1gFT9DIG;_V8cX9NQZm0EQ1Z+j*SJP6mz9RgV^@` ze_{Lo!oRsd69Dc9E_M(6E542aARhpzt4jlWKKRxD$>itSAY5I4`#kdiKzSViV2OYG z?9&0DE*1cICja({fOc$G0O0-;2S1;Szs3V3z%~f@3dw@{!6N9o==B*fOgunq0KvJ}2N7tiwa$Mx)Hl`+VAp_DLT;*`q_oEHsX~?r9+-jjjj}UY{e=HyBiGrteAR z-agx6X>l~f4|^>P)Wc}L1E-^oXe?OqE?i}<)6l>4+1`;>Ql+-ox%c=!nX7T9x4PnY zBKo(M*=xjHD(nUTfPVIS<$0AdvpPtpmXW&L#qzlsFIjv>i8e|k)FfgZnzo!0F zZu7Byd&K`!7(ibgKmkxF6oA1Q;JaztBpJV9O&^dBSW zRR|pVU6|(?CA0YH@u$a>u4{~He;y+vIM>Rvk55c%R0xGyXJ0(JeIZ+uo3a_y)^d04 zrrX(G(`qdRm$7oY+B-`nfp@gtPE7Z9#x7zj{f-Xq)A(F_x#&3eRnakx#PXy?$I!l# zF9FfD$9KezS9%%+zCS9z5NBl+`Sw%dg8wc+cuNybJx^gHl;mA%qV58^hq#aWkH6|o zl7Chy#kKXBlPz1%okq&4f8wO%s3NPZJ%2wb&lq#NY~{#}9pB3z`D4qySIVd&E>G_j zO*)w&Of-IMAs_V!*6q8lSM`=j2JR`8?!COSFYrmnk*6q^_fd!UXA~;D)|7fPOeN!J zh;X}Nlu%E{1L9{&nH!Aai$TyCL=fobxy1+$fxaiA2(Z` z`OICQAV}r-&Nolfm-*1}X0d`4v-h7CS3X@%dmCWl9F=X;h%*!TQ=b43#0co zA3_I({fQ3<6pg_f;)JBtWMqj3K0mh{Of*%<_spmLb9F12#CtCj3feyl`&V7IX7sX_ zxT3Npcwv>ce!i9OQBj&(`wWxH_?s3@a#>sr_T8c>S-1E8xu~(H8=6D1X1PnMAgjn8 zD+Edprs8h_!wrqc2*KEK(jY6z(BLO6bdPrH;H1XkFVYgZ#^}B!k2c50Q4Kc|IIfY8 zmsGV2+Q~<|Mn|7Pc_^={h+f$`e0uINur^{o!^m&?G{_tb4X4}tDhheot)~_hc^}_o zAw94)&de_(DzWopG{l?}Ux$Lz(LezZ7!(GhM=&CO;SP8}ntiK_jEK=HMm{u}6c4$P2G?n)47JOXjn< z^?qm!KR63=^W}#g>FxY?@!7{;;#|hd-Vj|8^N{ksppnqdn`x}t zJrpT^r5CzOE($$6%)0>g($RTu#MDswdeA!tVtC@QRC3CqoU?& zh)G5f0ivUiC9VpeSGQ~6nr8AW*(ecdPRCl83JN|LX5%+Dwl?Y~TFO*rKa$GQ7)}qJk4o;FXvw1P)u+6 zD8vUNiOJK3jjziIKk*x}ey&~S(Hbny?-gJ)tSxC#3wT#ftgEt!yM}KYCHv_52QYiz zu)G7^)Gnj3h!=mXrjh0S1=^Y!XXcNA3pe6xU_!i=nx1!u94 znfWiMuT1d;#&O?zrR$PmdGY0hO6{!)2XZ%4cwj|Ju^5EbQ`eXus@ht#ss5;VbNS;c zc_PJ$F)vJ~eqLbVg??+am@cy9dg-$FZO-51ZFWQWhSb{=etaw z%~2bK9_pU6lQ{lTnc6o@mO7R`?PYpZP|%OZL%koakz*FM5p?RDY)=<&C-;*H~HFj0loor)tN%C%eSvt zz1c45eV45kvY|aDvr@aT4dDyPtrNJTRc;vO+gu*X^2I25{Ur7sY7UdMD7$>2^&d-e z#Ds=3Vd2wKPeyNm<-=K+_H1VhI+xk+WenK|cThoVXwV~`#6L1^@8gUooppWT#~BnY zF1H@yMvL?>c%k$l0=*#4Z`-$E(;htXPeWGJxv@Tg8XGj4SgI;Lr9OKIUL<_ZKC|P> zwwLC3#)Fuap*#X1pi3mY%XtIOXDHPC-6sf7=4nl<$vBcWniJ`o% zdMc)&;voRRZ%CyRXLO0(=-8&@^0SIt$7Vtb7qVHgoHWA(_rO}ap((Rc#|JaUvxTCv z5g#(!*ZK0k(YSP|A3~aTuA3yiGf`_4UVq`-gD=N%w*MnQ!$);J-35~UM2a$ZF0f9U zz}08GNZf>aq&0hfYn*4;6+#(p=f{|D+0&Y7*4x>9_uIxu!3C4JJpUG3Pq`GzI z{LtpUn_aItzjgS)^aH~;&K+`*u?tN*#E2nDi7W~F>g?wMAB~ORKcZ!~O*Td4k#d%{ zCi<8oItU3N);AiJ2^>bV?AvhVE4Iw{OHx`yw{N~%3Ac)iiX*BU-^|!dVRRGqKpg$j z{KP>rO6okTX(IA%#!a0PH7Juj)iAzGXGBVDZP1)!c;{wFsRlI}oiEN|Az+)cA$smi zdVHisMC#WY_BNby45J23jyzMHE9_f@%PoeS23VABcg9pas^pl1QsWDCM!Gm|N*l(_u8#EX(malh0Uf8#7ZCXBN(5=7hWS&MS zzdG3R=e$7epyh^dY;a$T+Qe<0L=VXff5W^lxwA+=ZAF9yshqfbQUHxHR^C@{-OIg$v(iyCE78|tWKJo{>;s?FbTz_TfoMy>X&FpCrohNSDNRN(N zTJbdx4bOI;&l*|Gq)Dn@RBp>k)D}nIWf#C!WwSFA52eVxRq1%m%O~+X|5S>=j^pWC zUmRk5e2&d4Oz?T611=_2J2rqMjye&(-wZ1=Dr^%M7FkF1k6#7L{XUbdu6ku8nSUwW zL)lzQJ$1*Yx1?Z+MLCKH-{KN<4wBY8kN-x}M_aoHuQ7dTA3`K+O|16^@qg!&sl6hK zFTBb2zDW3C^!rxW5%@+uK!m*fes$IhLDDjceg2`fP3vAH(cuVvX)M4?Yl zP*xlnn#;q|`#M>d`ag$$AP>#K#h!)PXhfK6l|IZ$;9%B6^>?X z_1!7;-I_HgLV3?LB}gR-<=H(IS)p5OkGk!u{eWwrdR2%^)ixNie?TPCNI!Ms1L~S> zHz_|$(}R2)+SwT;Vx{xX!l z{>G615`zwY>IYF)e1KH&<^3zU{#BKbQi`u9GXVbx?M+7ZQC~*9iovd`acP)F^hEex z+ZRE=z>L^gLVKz>Rhpd-tw@fNyPwMHp2KipI1 z;+@KaH93C{#I+m0bF>PqNhqL{2FIt+9~sR(lpiW?S4_1Ym)DEBon(umcf$LTo24Q irp@vm-(1`cJQgY4h#}Fk8M}D9ABjXiQ0-COo%j!MtA>mK literal 0 HcmV?d00001 diff --git a/innovedus_cms/media/original_images/DeButAI.jpg b/innovedus_cms/media/original_images/DeButAI.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8d27988a58883cc12ea99ac37e6aad13148f03d8 GIT binary patch literal 91774 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 O(O5#mv*YYyI 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 X13C=oFrdSL4g)$2{2yZge%JLo)6{Qj literal 0 HcmV?d00001 diff --git a/innovedus_cms/mysite/templates/base.html b/innovedus_cms/mysite/templates/base.html index 530596f..5378b0d 100644 --- a/innovedus_cms/mysite/templates/base.html +++ b/innovedus_cms/mysite/templates/base.html @@ -34,6 +34,8 @@ {% wagtailuserbar %} + {% include "includes/header.html" %} + {% block content %}{% endblock %} {% include "includes/footer.html" %} diff --git a/innovedus_cms/mysite/templates/includes/header.html b/innovedus_cms/mysite/templates/includes/header.html new file mode 100644 index 0000000..560fcc7 --- /dev/null +++ b/innovedus_cms/mysite/templates/includes/header.html @@ -0,0 +1,30 @@ +{% load wagtailsettings_tags wagtailimages_tags %} +{% get_settings use_default_site=True as settings %} + +

-- 2.47.2 From 3232de90d45b9af4a6d871ae0c9c36792a8fbc94 Mon Sep 17 00:00:00 2001 From: Warren Chen Date: Fri, 17 Oct 2025 17:47:21 +0900 Subject: [PATCH 03/18] Add article and category page models, templates, and default cover image --- .../0003_articlepage_categorypage.py | 39 ++++ .../0004_latestpage_recommandedpage.py | 35 ++++ ..._rename_recommandedpage_recommendedpage.py | 18 ++ ...age_cover_image_articlepage_recommended.py | 25 +++ innovedus_cms/home/models.py | 171 +++++++++++++++++- .../home/static/img/default_cover.jpg | Bin 0 -> 9256 bytes .../home/templates/home/article_page.html | 15 ++ .../home/templates/home/category_page.html | 9 + .../home/templates/home/home_page.html | 9 +- .../home/includes/category_block_list.html | 25 +++ .../home/includes/category_full_list.html | 39 ++++ .../original_images/DefaultArticleCover.jpg | Bin 0 -> 61513 bytes 12 files changed, 375 insertions(+), 10 deletions(-) create mode 100644 innovedus_cms/home/migrations/0003_articlepage_categorypage.py create mode 100644 innovedus_cms/home/migrations/0004_latestpage_recommandedpage.py create mode 100644 innovedus_cms/home/migrations/0005_rename_recommandedpage_recommendedpage.py create mode 100644 innovedus_cms/home/migrations/0006_articlepage_cover_image_articlepage_recommended.py create mode 100644 innovedus_cms/home/static/img/default_cover.jpg create mode 100644 innovedus_cms/home/templates/home/article_page.html create mode 100644 innovedus_cms/home/templates/home/category_page.html create mode 100644 innovedus_cms/home/templates/home/includes/category_block_list.html create mode 100644 innovedus_cms/home/templates/home/includes/category_full_list.html create mode 100644 innovedus_cms/media/original_images/DefaultArticleCover.jpg diff --git a/innovedus_cms/home/migrations/0003_articlepage_categorypage.py b/innovedus_cms/home/migrations/0003_articlepage_categorypage.py new file mode 100644 index 0000000..ed3cc8a --- /dev/null +++ b/innovedus_cms/home/migrations/0003_articlepage_categorypage.py @@ -0,0 +1,39 @@ +# Generated by Django 5.2.7 on 2025-10-17 04:15 + +import django.db.models.deletion +import wagtail.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0002_create_homepage'), + ('wagtailcore', '0095_groupsitepermission'), + ] + + operations = [ + migrations.CreateModel( + name='ArticlePage', + fields=[ + ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')), + ('date', models.DateField(verbose_name='Published date')), + ('intro', models.CharField(blank=True, max_length=250)), + ('body', wagtail.fields.StreamField([('heading', 0), ('paragraph', 1), ('image', 2), ('embed', 3)], block_lookup={0: ('wagtail.blocks.CharBlock', (), {'form_classname': 'full title'}), 1: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link']}), 2: ('wagtail.images.blocks.ImageChooserBlock', (), {}), 3: ('wagtail.embeds.blocks.EmbedBlock', (), {})})), + ], + options={ + 'abstract': False, + }, + bases=('wagtailcore.page',), + ), + migrations.CreateModel( + name='CategoryPage', + fields=[ + ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')), + ], + options={ + 'abstract': False, + }, + bases=('wagtailcore.page',), + ), + ] diff --git a/innovedus_cms/home/migrations/0004_latestpage_recommandedpage.py b/innovedus_cms/home/migrations/0004_latestpage_recommandedpage.py new file mode 100644 index 0000000..50bdb38 --- /dev/null +++ b/innovedus_cms/home/migrations/0004_latestpage_recommandedpage.py @@ -0,0 +1,35 @@ +# Generated by Django 5.2.7 on 2025-10-17 07:28 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0003_articlepage_categorypage'), + ('wagtailcore', '0095_groupsitepermission'), + ] + + operations = [ + migrations.CreateModel( + name='LatestPage', + fields=[ + ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')), + ], + options={ + 'abstract': False, + }, + bases=('wagtailcore.page',), + ), + migrations.CreateModel( + name='RecommandedPage', + fields=[ + ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')), + ], + options={ + 'abstract': False, + }, + bases=('wagtailcore.page',), + ), + ] diff --git a/innovedus_cms/home/migrations/0005_rename_recommandedpage_recommendedpage.py b/innovedus_cms/home/migrations/0005_rename_recommandedpage_recommendedpage.py new file mode 100644 index 0000000..738ab54 --- /dev/null +++ b/innovedus_cms/home/migrations/0005_rename_recommandedpage_recommendedpage.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.7 on 2025-10-17 07:36 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0004_latestpage_recommandedpage'), + ('wagtailcore', '0095_groupsitepermission'), + ] + + operations = [ + migrations.RenameModel( + old_name='RecommandedPage', + new_name='RecommendedPage', + ), + ] diff --git a/innovedus_cms/home/migrations/0006_articlepage_cover_image_articlepage_recommended.py b/innovedus_cms/home/migrations/0006_articlepage_cover_image_articlepage_recommended.py new file mode 100644 index 0000000..ab23371 --- /dev/null +++ b/innovedus_cms/home/migrations/0006_articlepage_cover_image_articlepage_recommended.py @@ -0,0 +1,25 @@ +# Generated by Django 5.2.7 on 2025-10-17 07:50 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0005_rename_recommandedpage_recommendedpage'), + ('wagtailimages', '0027_image_description'), + ] + + operations = [ + migrations.AddField( + model_name='articlepage', + name='cover_image', + field=models.ForeignKey(blank=True, help_text='文章列表與分享用的首圖', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.image'), + ), + migrations.AddField( + model_name='articlepage', + name='recommended', + field=models.BooleanField(default=False, help_text='在推薦清單顯示'), + ), + ] diff --git a/innovedus_cms/home/models.py b/innovedus_cms/home/models.py index 5076f57..b7d3a3a 100644 --- a/innovedus_cms/home/models.py +++ b/innovedus_cms/home/models.py @@ -1,7 +1,174 @@ from django.db import models from wagtail.models import Page +from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger +BLOCK_SIZE = 5 +PAGE_SIZE = 10 -class HomePage(Page): - pass +class CategoryMixin: + def build_category_blocks(self, request=None): + blocks = [] + subcategories = self.get_children().type(CategoryPage).live() + if subcategories.exists(): + for category in subcategories : + blocks.append({ + "title": category.title, + "items": ArticlePage.objects.child_of(category).live().order_by("-first_published_at")[:BLOCK_SIZE], + "url": category.url, + }) + else: + paginator = Paginator(ArticlePage.objects.child_of(self).live().order_by("-first_published_at"), PAGE_SIZE) + page_number = request.GET.get("page") if request else None + + try: + page_obj = paginator.page(page_number) + except PageNotAnInteger: + page_obj = paginator.page(1) + except EmptyPage: + page_obj = paginator.page(paginator.num_pages) + + blocks.append({ + "title": self.title, + "items": page_obj, + "url": self.url, + }) + return blocks + + def get_latest_articles(self, request=None): + latestPage = LatestPage.objects.first() + if not request: + return { + "title": latestPage.title, + "items": ArticlePage.objects.live().order_by("-first_published_at")[:BLOCK_SIZE], + "url": latestPage.url, + } + else: + paginator = Paginator(ArticlePage.objects.live().order_by("-first_published_at"), PAGE_SIZE) + page_number = request.GET.get("page") + + try: + page_obj = paginator.page(page_number) + except PageNotAnInteger: + page_obj = paginator.page(1) + except EmptyPage: + page_obj = paginator.page(paginator.num_pages) + return { + "title": self.title, + "items": page_obj, + "url": self.url, + } + + def get_recommended_articles(self, request=None): + recommendedPage = RecommendedPage.objects.first() + if not request: + return { + "title": recommendedPage.title, + "items": ArticlePage.objects.filter(recommended=True).live()[:BLOCK_SIZE], + "url": recommendedPage.url, + } + else: + paginator = Paginator(ArticlePage.objects.filter(recommended=True).live(), PAGE_SIZE) + page_number = request.GET.get("page") + + try: + page_obj = paginator.page(page_number) + except PageNotAnInteger: + page_obj = paginator.page(1) + except EmptyPage: + page_obj = paginator.page(paginator.num_pages) + return { + "title": self.title, + "items": page_obj, + "url": self.url, + } + return blocks + +class HomePage(Page, CategoryMixin): + def get_context(self, request): + context = super().get_context(request) + + category_blocks = [ + self.get_latest_articles(), + self.get_recommended_articles(), + ] + + # 找出第一層 CategoryPage(HomePage 直屬子項) + categories = CategoryPage.objects.child_of(self).live().in_menu() + + # 若第一層沒有分類,就嘗試抓所有 descendant CategoryPage + if not categories.exists(): + categories = CategoryPage.objects.descendant_of(self).live().in_menu() + + for category in categories: + subcategories = category.get_children().type(CategoryPage).live() + category_blocks.append({ + "title": category.title, + "type": "category", + "items": subcategories or ArticlePage.objects.child_of(category).live()[:BLOCK_SIZE], + "url": category.url, + }) + + context["category_blocks"] = category_blocks + return context + +class LatestPage(Page, CategoryMixin): + template = "home/category_page.html" + def get_context(self, request): + context = super().get_context(request) + context["category_blocks"] = [ + self.get_latest_articles(request) + ] + return context + +class RecommendedPage(Page, CategoryMixin): + template = "home/category_page.html" + def get_context(self, request): + context = super().get_context(request) + context["category_blocks"] = [ + self.get_recommended_articles(request) + ] + return context + +class CategoryPage(Page, CategoryMixin): + @property + def has_subcategories(self): + return self.get_children().type(CategoryPage).live().exists() + + def get_context(self, request): + context = super().get_context(request) + context["category_blocks"] = self.build_category_blocks(request) + return context + +# from wagtail.fields import RichTextField +from wagtail.admin.panels import FieldPanel +from wagtail import blocks +from wagtail.embeds.blocks import EmbedBlock +from wagtail.images.blocks import ImageChooserBlock +from wagtail.fields import StreamField + +class ArticlePage(Page): + cover_image = models.ForeignKey( + "wagtailimages.Image", + null=True, blank=True, + on_delete=models.SET_NULL, + related_name="+", + help_text="文章列表與分享用的首圖" + ) + date = models.DateField("Published date") + intro = models.CharField(max_length=250, blank=True) + body = StreamField([ + ("heading", blocks.CharBlock(form_classname="full title")), + ("paragraph", blocks.RichTextBlock(features=["bold", "italic", "link"])), + ("image", ImageChooserBlock()), + ("embed", EmbedBlock()), + ], use_json_field=True) + recommended = models.BooleanField(default=False, help_text="在推薦清單顯示") + + content_panels = Page.content_panels + [ + FieldPanel("recommended"), + FieldPanel("cover_image"), + FieldPanel("date"), + FieldPanel("intro"), + FieldPanel("body"), + ] diff --git a/innovedus_cms/home/static/img/default_cover.jpg b/innovedus_cms/home/static/img/default_cover.jpg new file mode 100644 index 0000000000000000000000000000000000000000..61cb38d51dd309f0fbdc09f81fe705ba5c2d8249 GIT binary patch literal 9256 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 literal 0 HcmV?d00001 diff --git a/innovedus_cms/home/templates/home/article_page.html b/innovedus_cms/home/templates/home/article_page.html new file mode 100644 index 0000000..788c1c0 --- /dev/null +++ b/innovedus_cms/home/templates/home/article_page.html @@ -0,0 +1,15 @@ +{% extends "base.html" %} +{% load wagtailcore_tags wagtailimages_tags %} + +{% block content %} +
+ {% image page.cover_image original as cover %} + {{ page.title }} +

{{ page.title }}

+

{{ page.date }}

+
{{ page.intro }}
+
+ {{ page.body }} +
+
+{% endblock %} \ No newline at end of file diff --git a/innovedus_cms/home/templates/home/category_page.html b/innovedus_cms/home/templates/home/category_page.html new file mode 100644 index 0000000..5ae1796 --- /dev/null +++ b/innovedus_cms/home/templates/home/category_page.html @@ -0,0 +1,9 @@ +{% extends "base.html" %} +{% load wagtailcore_tags %} +{% block content %} + {% if page.has_subcategories %} + {% include "home/includes/category_block_list.html" %} + {% else %} + {% include "home/includes/category_full_list.html" %} + {% endif %} +{% endblock %} diff --git a/innovedus_cms/home/templates/home/home_page.html b/innovedus_cms/home/templates/home/home_page.html index db9e9b0..a7d00e4 100644 --- a/innovedus_cms/home/templates/home/home_page.html +++ b/innovedus_cms/home/templates/home/home_page.html @@ -5,17 +5,10 @@ {% block extra_css %} -{% comment %} -Delete the line below if you're just getting started and want to remove the welcome screen! -{% endcomment %} - {% endblock extra_css %} {% block content %} -{% comment %} -Delete the line below if you're just getting started and want to remove the welcome screen! -{% endcomment %} -{% include 'home/welcome_page.html' %} +{% include "home/includes/category_block_list.html" with category_blocks=category_blocks %} {% endblock content %} diff --git a/innovedus_cms/home/templates/home/includes/category_block_list.html b/innovedus_cms/home/templates/home/includes/category_block_list.html new file mode 100644 index 0000000..7f1b955 --- /dev/null +++ b/innovedus_cms/home/templates/home/includes/category_block_list.html @@ -0,0 +1,25 @@ +{% load wagtailimages_tags %} + + +
+ {% for category in category_blocks %} +
+

{{ category.title }}

+ +
+ {% endfor %} +
diff --git a/innovedus_cms/home/templates/home/includes/category_full_list.html b/innovedus_cms/home/templates/home/includes/category_full_list.html new file mode 100644 index 0000000..296d914 --- /dev/null +++ b/innovedus_cms/home/templates/home/includes/category_full_list.html @@ -0,0 +1,39 @@ +{% load wagtailimages_tags %} + +
+ {% with category=category_blocks.0 %} +

{{ category.title }}

+ + + + {% if category.items.paginator.num_pages > 1 %} + + {% endif %} + + {% endwith %} +
diff --git a/innovedus_cms/media/original_images/DefaultArticleCover.jpg b/innovedus_cms/media/original_images/DefaultArticleCover.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2c1fea8f088fe5b9b3d8614eae170c742219a3db GIT binary patch literal 61513 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%0A@)PvvzE53O2}P-?e%xS zkU@vUe%d16%2XA#UL2SL)QD(mE7gxn%pYbj2L9RL6^q5qc{2)YGc>M0DVruD$YzYT z^4hkBQlgI*<;5u{tIWsuWC7wegghzrQIu4-8}dj;9S+>(Rv0#k)hqAvTV^LsoM%-@ z$XwS|6Dp>^)R51lD@OVM>ss5r{MP_h>ERKXS`{IC);E1xbRovxSb-WHvg-FNVVG2t$_bzTkxMv Date: Wed, 29 Oct 2025 15:59:20 +0900 Subject: [PATCH 04/18] Add banner image support to ArticlePage and enhance embed validation - Introduced `banner_image` field to ArticlePage model. - Updated article templates to display banner images. - Added ValidatingEmbedBlock for improved URL validation. - Refactored category block templates to use static template loading. - Enhanced header navigation to include submenu support. - Updated .gitignore to exclude media files. --- .gitignore | 3 +- innovedus_cms/home/blocks.py | 37 +++++ ...age_banner_image_alter_articlepage_body.py | 26 ++++ innovedus_cms/home/models.py | 143 +++++++++++------- .../home/templates/home/article_page.html | 8 +- .../templates/home/blocks/h2_heading.html | 1 + .../home/blocks/horizontal_rule.html | 1 + .../home/includes/category_block_list.html | 6 +- .../home/includes/category_full_list.html | 9 +- innovedus_cms/mysite/settings/base.py | 24 ++- innovedus_cms/mysite/templates/base.html | 2 + .../mysite/templates/includes/header.html | 23 ++- 12 files changed, 212 insertions(+), 71 deletions(-) create mode 100644 innovedus_cms/home/blocks.py create mode 100644 innovedus_cms/home/migrations/0007_articlepage_banner_image_alter_articlepage_body.py create mode 100644 innovedus_cms/home/templates/home/blocks/h2_heading.html create mode 100644 innovedus_cms/home/templates/home/blocks/horizontal_rule.html diff --git a/.gitignore b/.gitignore index 431a750..3d5de7c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .env __pycache__ -*.pyc \ No newline at end of file +*.pyc +media/ \ No newline at end of file diff --git a/innovedus_cms/home/blocks.py b/innovedus_cms/home/blocks.py new file mode 100644 index 0000000..9474647 --- /dev/null +++ b/innovedus_cms/home/blocks.py @@ -0,0 +1,37 @@ +from django.core.exceptions import ValidationError +from wagtail.embeds.blocks import EmbedBlock +from wagtail.embeds import embeds as wagtail_embeds +from wagtail import blocks + + +class ValidatingEmbedBlock(EmbedBlock): + """ + Embed block that validates the URL at clean-time by resolving + via Wagtail's embed system. Raises ValidationError if not embeddable. + """ + + def clean(self, value): + value = super().clean(value) + if value: + try: + # Attempt to resolve and cache embed; will raise on failure + wagtail_embeds.get_embed(value) + except Exception: + raise ValidationError( + "嵌入連結無法驗證,請確認為公開且可嵌入的 URL。" + ) + return value + + +class H2HeadingBlock(blocks.CharBlock): + class Meta: + template = "home/blocks/h2_heading.html" + icon = "title" + label = "Heading" + + +class HorizontalRuleBlock(blocks.StaticBlock): + class Meta: + template = "home/blocks/horizontal_rule.html" + icon = "horizontalrule" + label = "Separator" diff --git a/innovedus_cms/home/migrations/0007_articlepage_banner_image_alter_articlepage_body.py b/innovedus_cms/home/migrations/0007_articlepage_banner_image_alter_articlepage_body.py new file mode 100644 index 0000000..895da35 --- /dev/null +++ b/innovedus_cms/home/migrations/0007_articlepage_banner_image_alter_articlepage_body.py @@ -0,0 +1,26 @@ +# Generated by Django 5.2.7 on 2025-10-29 06:25 + +import django.db.models.deletion +import wagtail.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0006_articlepage_cover_image_articlepage_recommended'), + ('wagtailimages', '0027_image_description'), + ] + + operations = [ + migrations.AddField( + model_name='articlepage', + name='banner_image', + field=models.ForeignKey(blank=True, help_text='文章頁頂部橫幅圖片', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.image'), + ), + migrations.AlterField( + model_name='articlepage', + name='body', + field=wagtail.fields.StreamField([('heading', 0), ('paragraph', 1), ('image', 2), ('embed', 3), ('hr', 4), ('html', 5)], block_lookup={0: ('home.blocks.H2HeadingBlock', (), {'form_classname': 'full title'}), 1: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link', 'ol', 'ul']}), 2: ('wagtail.images.blocks.ImageChooserBlock', (), {}), 3: ('home.blocks.ValidatingEmbedBlock', (), {}), 4: ('home.blocks.HorizontalRuleBlock', (), {}), 5: ('wagtail.blocks.RawHTMLBlock', (), {'help_text': '僅限信任來源的 blockquote/iframe 原始碼'})}), + ), + ] diff --git a/innovedus_cms/home/models.py b/innovedus_cms/home/models.py index b7d3a3a..8ce9420 100644 --- a/innovedus_cms/home/models.py +++ b/innovedus_cms/home/models.py @@ -6,19 +6,29 @@ from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger BLOCK_SIZE = 5 PAGE_SIZE = 10 + class CategoryMixin: def build_category_blocks(self, request=None): blocks = [] - subcategories = self.get_children().type(CategoryPage).live() + subcategories = self.get_children().type(CategoryPage).live() if subcategories.exists(): - for category in subcategories : - blocks.append({ - "title": category.title, - "items": ArticlePage.objects.child_of(category).live().order_by("-first_published_at")[:BLOCK_SIZE], - "url": category.url, - }) + for category in subcategories: + blocks.append( + { + "title": category.title, + "items": ArticlePage.objects.child_of(category) + .live() + .order_by("-first_published_at")[:BLOCK_SIZE], + "url": category.url, + } + ) else: - paginator = Paginator(ArticlePage.objects.child_of(self).live().order_by("-first_published_at"), PAGE_SIZE) + paginator = Paginator( + ArticlePage.objects.child_of(self) + .live() + .order_by("-first_published_at"), + PAGE_SIZE, + ) page_number = request.GET.get("page") if request else None try: @@ -28,23 +38,29 @@ class CategoryMixin: except EmptyPage: page_obj = paginator.page(paginator.num_pages) - blocks.append({ - "title": self.title, - "items": page_obj, - "url": self.url, - }) + blocks.append( + { + "title": self.title, + "items": page_obj, + "url": self.url, + } + ) return blocks - + def get_latest_articles(self, request=None): - latestPage = LatestPage.objects.first() + latest_page = LatestPage.objects.first() if not request: return { - "title": latestPage.title, - "items": ArticlePage.objects.live().order_by("-first_published_at")[:BLOCK_SIZE], - "url": latestPage.url, + "title": latest_page.title if latest_page else "最新文章", + "items": ArticlePage.objects.live().order_by("-first_published_at")[ + :BLOCK_SIZE + ], + "url": latest_page.url if latest_page else "#", } else: - paginator = Paginator(ArticlePage.objects.live().order_by("-first_published_at"), PAGE_SIZE) + paginator = Paginator( + ArticlePage.objects.live().order_by("-first_published_at"), PAGE_SIZE + ) page_number = request.GET.get("page") try: @@ -60,12 +76,12 @@ class CategoryMixin: } def get_recommended_articles(self, request=None): - recommendedPage = RecommendedPage.objects.first() + recommended_page = RecommendedPage.objects.first() if not request: return { - "title": recommendedPage.title, + "title": recommended_page.title if recommended_page else "推薦文章", "items": ArticlePage.objects.filter(recommended=True).live()[:BLOCK_SIZE], - "url": recommendedPage.url, + "url": recommended_page.url if recommended_page else "#", } else: paginator = Paginator(ArticlePage.objects.filter(recommended=True).live(), PAGE_SIZE) @@ -82,54 +98,54 @@ class CategoryMixin: "items": page_obj, "url": self.url, } - return blocks + class HomePage(Page, CategoryMixin): def get_context(self, request): context = super().get_context(request) - category_blocks = [ - self.get_latest_articles(), - self.get_recommended_articles(), - ] + category_blocks = [self.get_latest_articles(), self.get_recommended_articles()] - # 找出第一層 CategoryPage(HomePage 直屬子項) + # 找出第一層 CategoryPage(HomePage 直屬子頁) categories = CategoryPage.objects.child_of(self).live().in_menu() - # 若第一層沒有分類,就嘗試抓所有 descendant CategoryPage + # 若第一層沒有,抓 descendant CategoryPage if not categories.exists(): categories = CategoryPage.objects.descendant_of(self).live().in_menu() - + for category in categories: subcategories = category.get_children().type(CategoryPage).live() - category_blocks.append({ - "title": category.title, - "type": "category", - "items": subcategories or ArticlePage.objects.child_of(category).live()[:BLOCK_SIZE], - "url": category.url, - }) + category_blocks.append( + { + "title": category.title, + "type": "category", + "items": ArticlePage.objects.child_of(category).live()[:BLOCK_SIZE], + "url": category.url, + } + ) context["category_blocks"] = category_blocks return context + class LatestPage(Page, CategoryMixin): template = "home/category_page.html" + def get_context(self, request): context = super().get_context(request) - context["category_blocks"] = [ - self.get_latest_articles(request) - ] + context["category_blocks"] = [self.get_latest_articles(request)] return context - + + class RecommendedPage(Page, CategoryMixin): template = "home/category_page.html" + def get_context(self, request): context = super().get_context(request) - context["category_blocks"] = [ - self.get_recommended_articles(request) - ] + context["category_blocks"] = [self.get_recommended_articles(request)] return context + class CategoryPage(Page, CategoryMixin): @property def has_subcategories(self): @@ -139,36 +155,53 @@ class CategoryPage(Page, CategoryMixin): context = super().get_context(request) context["category_blocks"] = self.build_category_blocks(request) return context - -# from wagtail.fields import RichTextField + + from wagtail.admin.panels import FieldPanel from wagtail import blocks -from wagtail.embeds.blocks import EmbedBlock from wagtail.images.blocks import ImageChooserBlock from wagtail.fields import StreamField +from .blocks import ValidatingEmbedBlock, H2HeadingBlock, HorizontalRuleBlock + class ArticlePage(Page): cover_image = models.ForeignKey( "wagtailimages.Image", - null=True, blank=True, + null=True, + blank=True, on_delete=models.SET_NULL, related_name="+", - help_text="文章列表與分享用的首圖" + help_text="列表封面圖", + ) + banner_image = models.ForeignKey( + "wagtailimages.Image", + null=True, + blank=True, + on_delete=models.SET_NULL, + related_name="+", + help_text="文章內文橫幅圖片", ) date = models.DateField("Published date") intro = models.CharField(max_length=250, blank=True) - body = StreamField([ - ("heading", blocks.CharBlock(form_classname="full title")), - ("paragraph", blocks.RichTextBlock(features=["bold", "italic", "link"])), - ("image", ImageChooserBlock()), - ("embed", EmbedBlock()), - ], use_json_field=True) - recommended = models.BooleanField(default=False, help_text="在推薦清單顯示") + body = StreamField( + [ + ("heading", H2HeadingBlock(form_classname="full title")), + ("paragraph", blocks.RichTextBlock(features=["bold", "italic", "link"])), + ("image", ImageChooserBlock()), + ("embed", ValidatingEmbedBlock()), + ("hr", HorizontalRuleBlock()), + ("html", blocks.RawHTMLBlock(help_text="僅限信任來源的 blockquote/iframe 原始碼")), + ], + use_json_field=True, + ) + recommended = models.BooleanField(default=False, help_text="在推薦區塊顯示") content_panels = Page.content_panels + [ FieldPanel("recommended"), FieldPanel("cover_image"), + FieldPanel("banner_image"), FieldPanel("date"), FieldPanel("intro"), FieldPanel("body"), ] + diff --git a/innovedus_cms/home/templates/home/article_page.html b/innovedus_cms/home/templates/home/article_page.html index 788c1c0..3a1307b 100644 --- a/innovedus_cms/home/templates/home/article_page.html +++ b/innovedus_cms/home/templates/home/article_page.html @@ -3,13 +3,15 @@ {% block content %}
- {% image page.cover_image original as cover %} - {{ page.title }}

{{ page.title }}

+ {% if page.banner_image %} + {% image page.banner_image original as banner %} + {{ page.title }} + {% endif %}

{{ page.date }}

{{ page.intro }}
{{ page.body }}
-{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/innovedus_cms/home/templates/home/blocks/h2_heading.html b/innovedus_cms/home/templates/home/blocks/h2_heading.html new file mode 100644 index 0000000..1a01967 --- /dev/null +++ b/innovedus_cms/home/templates/home/blocks/h2_heading.html @@ -0,0 +1 @@ +

{{ value }}

\ No newline at end of file diff --git a/innovedus_cms/home/templates/home/blocks/horizontal_rule.html b/innovedus_cms/home/templates/home/blocks/horizontal_rule.html new file mode 100644 index 0000000..10afc6a --- /dev/null +++ b/innovedus_cms/home/templates/home/blocks/horizontal_rule.html @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/innovedus_cms/home/templates/home/includes/category_block_list.html b/innovedus_cms/home/templates/home/includes/category_block_list.html index 7f1b955..c5edd83 100644 --- a/innovedus_cms/home/templates/home/includes/category_block_list.html +++ b/innovedus_cms/home/templates/home/includes/category_block_list.html @@ -1,4 +1,4 @@ -{% load wagtailimages_tags %} +{% load wagtailimages_tags static %}
@@ -11,9 +11,9 @@ {% if article.cover_image %} {% image article.cover_image max-200x200 as cover %} - {{ article.title }} + {{ article.title }} {% else %} - {{ article.title }} + {{ article.title }} {% endif %} {{ article.title }} diff --git a/innovedus_cms/home/templates/home/includes/category_full_list.html b/innovedus_cms/home/templates/home/includes/category_full_list.html index 296d914..6538144 100644 --- a/innovedus_cms/home/templates/home/includes/category_full_list.html +++ b/innovedus_cms/home/templates/home/includes/category_full_list.html @@ -1,4 +1,4 @@ -{% load wagtailimages_tags %} +{% load wagtailimages_tags static %}
{% with category=category_blocks.0 %} @@ -13,7 +13,7 @@ {% image article.cover_image max-200x200 as cover %} {{ article.title }} {% else %} - {{ article.title }} + {{ article.title }} {% endif %} {{ article.title }} @@ -26,14 +26,15 @@ {% if category.items.paginator.num_pages > 1 %} {% endif %} {% endwith %}
+ diff --git a/innovedus_cms/mysite/settings/base.py b/innovedus_cms/mysite/settings/base.py index 08ad2d1..7e0622d 100644 --- a/innovedus_cms/mysite/settings/base.py +++ b/innovedus_cms/mysite/settings/base.py @@ -124,6 +124,28 @@ USE_I18N = True USE_TZ = True +# --- Wagtail embeds (Instagram/FB via Graph oEmbed) --- +# Reads a token from env and adds an extra oEmbed finder for IG/FB. +# Keeps the default oEmbed finder first for YouTube/Vimeo/etc. +WAGTAIL_EMBED_FINDERS = [ + {"class": "wagtail.embeds.finders.oembed"}, + { + "class": "wagtail.embeds.finders.oembed", + "options": { + "providers": [ + {"endpoint": "https://graph.facebook.com/v11.0/instagram_oembed", "urls": ["https://www.instagram.com/*"]}, + {"endpoint": "https://graph.facebook.com/v11.0/oembed_post", "urls": ["https://www.facebook.com/*"]}, + {"endpoint": "https://graph.facebook.com/v11.0/oembed_page", "urls": ["https://www.facebook.com/*"]}, + {"endpoint": "https://graph.facebook.com/v11.0/oembed_video", "urls": ["https://www.facebook.com/*"]}, + ], + "params": { + "access_token": os.environ.get("IG_OEMBED_ACCESS_TOKEN", ""), + "omitscript": True, + }, + }, + }, +] + from django.utils.translation import gettext_lazy as _ LANGUAGES = [ @@ -191,4 +213,4 @@ CSRF_TRUSTED_ORIGINS = [ 'https://innovedus-cms.fly.dev', ] -ALLOWED_HOSTS = ['innovedus-cms.fly.dev'] \ No newline at end of file +ALLOWED_HOSTS = ['innovedus-cms.fly.dev'] diff --git a/innovedus_cms/mysite/templates/base.html b/innovedus_cms/mysite/templates/base.html index 5378b0d..1cf7e78 100644 --- a/innovedus_cms/mysite/templates/base.html +++ b/innovedus_cms/mysite/templates/base.html @@ -42,6 +42,8 @@ {# Global javascript #} + {# Instagram embed script to render IG oEmbeds #} + {% block extra_js %} {# Override this in templates to add extra javascript #} diff --git a/innovedus_cms/mysite/templates/includes/header.html b/innovedus_cms/mysite/templates/includes/header.html index 560fcc7..51498b8 100644 --- a/innovedus_cms/mysite/templates/includes/header.html +++ b/innovedus_cms/mysite/templates/includes/header.html @@ -14,11 +14,26 @@
diff --git a/innovedus_cms/home/templates/home/hashtag_page.html b/innovedus_cms/home/templates/home/hashtag_page.html new file mode 100644 index 0000000..287f677 --- /dev/null +++ b/innovedus_cms/home/templates/home/hashtag_page.html @@ -0,0 +1,19 @@ +{% extends "base.html" %} +{% load wagtailcore_tags %} +{% block content %} + + + {% include "home/includes/page-article-list.html" %} +{% endblock %} diff --git a/innovedus_cms/home/views.py b/innovedus_cms/home/views.py new file mode 100644 index 0000000..a64bfce --- /dev/null +++ b/innovedus_cms/home/views.py @@ -0,0 +1,43 @@ +from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator +from django.shortcuts import get_object_or_404, render + +from taggit.models import Tag +from wagtail.models import Site + +from .models import ArticlePage, PAGE_SIZE + + +def hashtag_search(request, slug): + tag = get_object_or_404(Tag, slug=slug) + articles = ( + ArticlePage.objects.live() + .filter(tags__slug=slug) + .order_by("-date") + ) + + paginator = Paginator(articles, PAGE_SIZE) + page_number = request.GET.get("page") + + try: + page_obj = paginator.page(page_number) + except PageNotAnInteger: + page_obj = paginator.page(1) + except EmptyPage: + page_obj = paginator.page(paginator.num_pages) + + site = Site.find_for_request(request) + site_root = site.root_page if site else None + + context = { + "tag": tag, + "category_sections": [ + { + "title": f"#{tag.name}", + "items": page_obj, + "url": request.path, + } + ], + "site_root": site_root, + } + + return render(request, "home/hashtag_page.html", context) diff --git a/innovedus_cms/mysite/urls.py b/innovedus_cms/mysite/urls.py index c2e8a0c..b60d225 100644 --- a/innovedus_cms/mysite/urls.py +++ b/innovedus_cms/mysite/urls.py @@ -7,11 +7,14 @@ from wagtail import urls as wagtail_urls from wagtail.documents import urls as wagtaildocs_urls from search import views as search_views +from home import views as home_views urlpatterns = [ path("django-admin/", admin.site.urls), path("admin/", include(wagtailadmin_urls)), path("documents/", include(wagtaildocs_urls)), + # use so Unicode tag slugs (e.g. 台北美食) still resolve + path("tags//", home_views.hashtag_search, name="hashtag_search"), path("search/", search_views.search, name="search"), ] -- 2.47.2 From 653847df6a55fc03a0108d00bb930ebf81db9982 Mon Sep 17 00:00:00 2001 From: Warren Chen Date: Mon, 10 Nov 2025 16:42:15 +0900 Subject: [PATCH 09/18] Add search functionality to ArticlePage and enhance search templates - Implement search fields in ArticlePage model for indexing. - Update hashtag search view to include site root in context. - Enhance header with a search form for articles. - Modify search results template to improve user experience and display. --- innovedus_cms/home/models.py | 29 ++++++++++ innovedus_cms/home/views.py | 1 + .../mysite/templates/includes/header.html | 10 ++++ .../search/templates/search/search.html | 58 +++++++++---------- innovedus_cms/search/views.py | 58 ++++++++++--------- 5 files changed, 99 insertions(+), 57 deletions(-) diff --git a/innovedus_cms/home/models.py b/innovedus_cms/home/models.py index e833e0b..f062e57 100644 --- a/innovedus_cms/home/models.py +++ b/innovedus_cms/home/models.py @@ -7,6 +7,7 @@ from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from modelcluster.contrib.taggit import ClusterTaggableManager from modelcluster.fields import ParentalKey from taggit.models import TaggedItemBase +from wagtail.search import index def _get_env_int(name, default): value = os.environ.get(name) @@ -272,6 +273,12 @@ class ArticlePage(Page): trending = models.BooleanField("Trending", default=False, help_text="在熱門區塊顯示") tags = ClusterTaggableManager(through="home.ArticlePageTag", blank=True) + search_fields = Page.search_fields + [ + index.SearchField("intro", partial_match=True), + index.SearchField("body_search_text", partial_match=True), + index.SearchField("tag_names_search_text", partial_match=True), + ] + content_panels = Page.content_panels + [ FieldPanel("trending"), FieldPanel("cover_image"), @@ -299,3 +306,25 @@ class ArticlePage(Page): context["related_articles"] = related_articles return context + + @property + def body_search_text(self): + if not self.body: + return "" + + excluded_types = {"image", "embed", "hr", "html"} + chunks = [] + + for block in self.body: + if block.block_type in excluded_types: + continue + # Each block decides how to expose searchable text + block_content = block.block.get_searchable_content(block.value) + if block_content: + chunks.extend(block_content) + + return " ".join(text for text in chunks if isinstance(text, str)) + + @property + def tag_names_search_text(self): + return " ".join(self.tags.values_list("name", flat=True)) diff --git a/innovedus_cms/home/views.py b/innovedus_cms/home/views.py index a64bfce..1dbfe37 100644 --- a/innovedus_cms/home/views.py +++ b/innovedus_cms/home/views.py @@ -38,6 +38,7 @@ def hashtag_search(request, slug): } ], "site_root": site_root, + "page": site_root.specific if site_root else None, } return render(request, "home/hashtag_page.html", context) diff --git a/innovedus_cms/mysite/templates/includes/header.html b/innovedus_cms/mysite/templates/includes/header.html index c07be12..c2f028d 100644 --- a/innovedus_cms/mysite/templates/includes/header.html +++ b/innovedus_cms/mysite/templates/includes/header.html @@ -56,5 +56,15 @@ {% endif %} + + diff --git a/innovedus_cms/search/templates/search/search.html b/innovedus_cms/search/templates/search/search.html index 476427f..6d06863 100644 --- a/innovedus_cms/search/templates/search/search.html +++ b/innovedus_cms/search/templates/search/search.html @@ -1,38 +1,38 @@ {% extends "base.html" %} -{% load static wagtailcore_tags %} +{% load wagtailcore_tags %} {% block body_class %}template-searchresults{% endblock %} -{% block title %}Search{% endblock %} +{% block title %} + {% if search_query %}搜尋:{{ search_query }}{% else %}搜尋{% endif %} +{% endblock %} {% block content %} -

Search

- -
- - -
- -{% if search_results %} -
    - {% for result in search_results %} -
  • -

    {{ result }}

    - {% if result.search_description %} - {{ result.search_description }} +
+ +
  • 搜尋
  • + {% if search_query %} +
  • {{ search_query }}
  • + {% endif %} + + -{% if search_results.has_previous %} -Previous -{% endif %} - -{% if search_results.has_next %} -Next -{% endif %} -{% elif search_query %} -No results found -{% endif %} +
    + {% if search_query %} + {% if results_count %} + {% include "home/includes/page-article-list.html" %} + {% else %} +

    找不到與「{{ search_query }}」相關的文章。

    + {% endif %} + {% else %} +

    請輸入關鍵字後再進行搜尋。

    + {% endif %} +
    {% endblock %} diff --git a/innovedus_cms/search/views.py b/innovedus_cms/search/views.py index 678bb7e..e76fbf1 100644 --- a/innovedus_cms/search/views.py +++ b/innovedus_cms/search/views.py @@ -1,46 +1,48 @@ -from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator +from urllib.parse import urlencode + +from django.core.paginator import Paginator from django.template.response import TemplateResponse -from wagtail.models import Page +from wagtail.models import Site -# To enable logging of search queries for use with the "Promoted search results" module -# -# uncomment the following line and the lines indicated in the search function -# (after adding wagtail.contrib.search_promotions to INSTALLED_APPS): - -# from wagtail.contrib.search_promotions.models import Query +from home.models import ArticlePage, PAGE_SIZE def search(request): - search_query = request.GET.get("query", None) - page = request.GET.get("page", 1) + search_query = (request.GET.get("query") or "").strip() + page_number = request.GET.get("page", 1) + category_sections = [] + results_page = None + results_count = 0 - # Search if search_query: - search_results = Page.objects.live().search(search_query) + search_queryset = ArticlePage.objects.live().search(search_query) + paginator = Paginator(search_queryset, PAGE_SIZE) + results_page = paginator.get_page(page_number) + results_count = paginator.count - # To log this query for use with the "Promoted search results" module: + if results_count: + query_string = urlencode({"query": search_query}) + category_sections = [ + { + "title": f"搜尋:{search_query}", + "items": results_page, + "url": f"{request.path}?{query_string}", + } + ] - # query = Query.get(search_query) - # query.add_hit() - - else: - search_results = Page.objects.none() - - # Pagination - paginator = Paginator(search_results, 10) - try: - search_results = paginator.page(page) - except PageNotAnInteger: - search_results = paginator.page(1) - except EmptyPage: - search_results = paginator.page(paginator.num_pages) + site = Site.find_for_request(request) + site_root = site.root_page if site else None return TemplateResponse( request, "search/search.html", { "search_query": search_query, - "search_results": search_results, + "category_sections": category_sections, + "results_page": results_page, + "results_count": results_count, + "site_root": site_root, + "page": site_root.specific if site_root else None, }, ) -- 2.47.2 From 97ddd2dfd14063d10617c20a434322566fca7343 Mon Sep 17 00:00:00 2001 From: Warren Chen Date: Tue, 11 Nov 2025 13:49:10 +0900 Subject: [PATCH 10/18] Refactor search view to improve query handling and pagination logic --- innovedus_cms/search/views.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/innovedus_cms/search/views.py b/innovedus_cms/search/views.py index e76fbf1..d93687c 100644 --- a/innovedus_cms/search/views.py +++ b/innovedus_cms/search/views.py @@ -2,6 +2,7 @@ from urllib.parse import urlencode from django.core.paginator import Paginator from django.template.response import TemplateResponse +from django.db.models import Q from wagtail.models import Site @@ -16,12 +17,17 @@ def search(request): results_count = 0 if search_query: - search_queryset = ArticlePage.objects.live().search(search_query) - paginator = Paginator(search_queryset, PAGE_SIZE) - results_page = paginator.get_page(page_number) - results_count = paginator.count + primary_qs = ArticlePage.objects.live().search(search_query) + results_count = primary_qs.count() + + if not results_count: + fallback_filter = Q(intro__icontains=search_query) | Q(body__icontains=search_query) + primary_qs = ArticlePage.objects.live().filter(fallback_filter).order_by("-date") + results_count = primary_qs.count() if results_count: + paginator = Paginator(primary_qs, PAGE_SIZE) + results_page = paginator.get_page(page_number) query_string = urlencode({"query": search_query}) category_sections = [ { -- 2.47.2 From 2c21cca5a7190bdd728b0f8b7edb47b410b4baf5 Mon Sep 17 00:00:00 2001 From: Warren Chen Date: Wed, 26 Nov 2025 17:29:52 +0900 Subject: [PATCH 11/18] Clean up and re-generate migrations --- ..._navigationsettings_github_url_and_more.py | 36 -------- ...ter_navigationsettings_options_and_more.py | 59 +++++++++++++ .../migrations/0003_socialmediasettings.py | 24 ------ ...ter_navigationsettings_options_and_more.py | 44 ---------- .../base/migrations/0005_headersettring.py | 28 ------- ...6_rename_headersettring_headersettrings.py | 18 ---- ...7_rename_headersettrings_headersettings.py | 18 ---- .../0003_articlepage_categorypage.py | 39 --------- ...tpage_trendingpage_articlepage_and_more.py | 83 +++++++++++++++++++ .../0004_latestpage_recommandedpage.py | 35 -------- ..._rename_recommandedpage_recommendedpage.py | 18 ---- ...age_cover_image_articlepage_recommended.py | 25 ------ ...age_banner_image_alter_articlepage_body.py | 26 ------ ...alter_articlepage_banner_image_and_more.py | 36 -------- .../0009_articlepagetag_articlepage_tags.py | 59 ------------- .../0010_alter_articlepage_recommended.py | 20 ----- ...011_rename_recommendedpage_trendingpage.py | 15 ---- ...commended_articlepage_trending_and_more.py | 30 ------- .../migrations/0013_alter_articlepage_date.py | 46 ---------- 19 files changed, 142 insertions(+), 517 deletions(-) delete mode 100644 innovedus_cms/base/migrations/0002_remove_navigationsettings_github_url_and_more.py create mode 100644 innovedus_cms/base/migrations/0002_socialmediasettings_alter_navigationsettings_options_and_more.py delete mode 100644 innovedus_cms/base/migrations/0003_socialmediasettings.py delete mode 100644 innovedus_cms/base/migrations/0004_alter_navigationsettings_options_and_more.py delete mode 100644 innovedus_cms/base/migrations/0005_headersettring.py delete mode 100644 innovedus_cms/base/migrations/0006_rename_headersettring_headersettrings.py delete mode 100644 innovedus_cms/base/migrations/0007_rename_headersettrings_headersettings.py delete mode 100644 innovedus_cms/home/migrations/0003_articlepage_categorypage.py create mode 100644 innovedus_cms/home/migrations/0003_categorypage_latestpage_trendingpage_articlepage_and_more.py delete mode 100644 innovedus_cms/home/migrations/0004_latestpage_recommandedpage.py delete mode 100644 innovedus_cms/home/migrations/0005_rename_recommandedpage_recommendedpage.py delete mode 100644 innovedus_cms/home/migrations/0006_articlepage_cover_image_articlepage_recommended.py delete mode 100644 innovedus_cms/home/migrations/0007_articlepage_banner_image_alter_articlepage_body.py delete mode 100644 innovedus_cms/home/migrations/0008_alter_articlepage_banner_image_and_more.py delete mode 100644 innovedus_cms/home/migrations/0009_articlepagetag_articlepage_tags.py delete mode 100644 innovedus_cms/home/migrations/0010_alter_articlepage_recommended.py delete mode 100644 innovedus_cms/home/migrations/0011_rename_recommendedpage_trendingpage.py delete mode 100644 innovedus_cms/home/migrations/0012_rename_recommended_articlepage_trending_and_more.py delete mode 100644 innovedus_cms/home/migrations/0013_alter_articlepage_date.py diff --git a/innovedus_cms/base/migrations/0002_remove_navigationsettings_github_url_and_more.py b/innovedus_cms/base/migrations/0002_remove_navigationsettings_github_url_and_more.py deleted file mode 100644 index 5c2df52..0000000 --- a/innovedus_cms/base/migrations/0002_remove_navigationsettings_github_url_and_more.py +++ /dev/null @@ -1,36 +0,0 @@ -# Generated by Django 5.2.7 on 2025-10-15 06:17 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('base', '0001_initial'), - ] - - operations = [ - migrations.RemoveField( - model_name='navigationsettings', - name='github_url', - ), - migrations.RemoveField( - model_name='navigationsettings', - name='mastodon_url', - ), - migrations.AddField( - model_name='navigationsettings', - name='facebook_url', - field=models.URLField(blank=True, verbose_name='Facebook URL'), - ), - migrations.AddField( - model_name='navigationsettings', - name='instagram_url', - field=models.URLField(blank=True, verbose_name='Instagram URL'), - ), - migrations.AddField( - model_name='navigationsettings', - name='thread_url', - field=models.URLField(blank=True, verbose_name='Thread URL'), - ), - ] diff --git a/innovedus_cms/base/migrations/0002_socialmediasettings_alter_navigationsettings_options_and_more.py b/innovedus_cms/base/migrations/0002_socialmediasettings_alter_navigationsettings_options_and_more.py new file mode 100644 index 0000000..3aa1e59 --- /dev/null +++ b/innovedus_cms/base/migrations/0002_socialmediasettings_alter_navigationsettings_options_and_more.py @@ -0,0 +1,59 @@ +# Generated by Django 5.2.7 on 2025-11-26 08:11 + +import django.db.models.deletion +import wagtail.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('base', '0001_initial'), + ('wagtailimages', '0027_image_description'), + ] + + operations = [ + migrations.CreateModel( + name='SocialMediaSettings', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('links', wagtail.fields.StreamField([('link', 2)], block_lookup={0: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('facebook', 'Facebook'), ('twitter', 'Twitter'), ('instagram', 'Instagram'), ('thread', 'Thread'), ('linkedin', 'LinkedIn'), ('youtube', 'YouTube')]}), 1: ('wagtail.blocks.URLBlock', (), {}), 2: ('wagtail.blocks.StructBlock', [[('platform', 0), ('url', 1)]], {})})), + ], + options={ + 'abstract': False, + }, + ), + migrations.AlterModelOptions( + name='navigationsettings', + options={'verbose_name': 'Footer Navigation'}, + ), + migrations.RemoveField( + model_name='navigationsettings', + name='github_url', + ), + migrations.RemoveField( + model_name='navigationsettings', + name='linkedin_url', + ), + migrations.RemoveField( + model_name='navigationsettings', + name='mastodon_url', + ), + migrations.AddField( + model_name='navigationsettings', + name='footer_links', + field=wagtail.fields.StreamField([('section', 5)], blank=True, block_lookup={0: ('wagtail.blocks.CharBlock', (), {'required': False}), 1: ('wagtail.blocks.CharBlock', (), {}), 2: ('wagtail.blocks.URLBlock', (), {}), 3: ('wagtail.blocks.StructBlock', [[('label', 1), ('url', 2)]], {}), 4: ('wagtail.blocks.ListBlock', (3,), {}), 5: ('wagtail.blocks.StructBlock', [[('title', 0), ('links', 4)]], {})}, null=True), + ), + migrations.CreateModel( + name='HeaderSettings', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('site_name', models.CharField(blank=True, max_length=255)), + ('extra_links', wagtail.fields.StreamField([('link', 2)], blank=True, block_lookup={0: ('wagtail.blocks.CharBlock', (), {}), 1: ('wagtail.blocks.URLBlock', (), {}), 2: ('wagtail.blocks.StructBlock', [[('label', 0), ('url', 1)]], {})}, null=True)), + ('logo', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.image')), + ], + options={ + 'verbose_name': 'Header Settings', + }, + ), + ] diff --git a/innovedus_cms/base/migrations/0003_socialmediasettings.py b/innovedus_cms/base/migrations/0003_socialmediasettings.py deleted file mode 100644 index ea4f694..0000000 --- a/innovedus_cms/base/migrations/0003_socialmediasettings.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 5.2.7 on 2025-10-15 06:48 - -import wagtail.fields -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('base', '0002_remove_navigationsettings_github_url_and_more'), - ] - - operations = [ - migrations.CreateModel( - name='SocialMediaSettings', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('links', wagtail.fields.StreamField([('link', 2)], block_lookup={0: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('twitter', 'Twitter'), ('instagram', 'Instagram'), ('linkedin', 'LinkedIn'), ('youtube', 'YouTube')]}), 1: ('wagtail.blocks.URLBlock', (), {}), 2: ('wagtail.blocks.StructBlock', [[('platform', 0), ('url', 1)]], {})})), - ], - options={ - 'abstract': False, - }, - ), - ] diff --git a/innovedus_cms/base/migrations/0004_alter_navigationsettings_options_and_more.py b/innovedus_cms/base/migrations/0004_alter_navigationsettings_options_and_more.py deleted file mode 100644 index 8bc5815..0000000 --- a/innovedus_cms/base/migrations/0004_alter_navigationsettings_options_and_more.py +++ /dev/null @@ -1,44 +0,0 @@ -# Generated by Django 5.2.7 on 2025-10-15 08:49 - -import wagtail.fields -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('base', '0003_socialmediasettings'), - ] - - operations = [ - migrations.AlterModelOptions( - name='navigationsettings', - options={'verbose_name': 'Footer Navigation'}, - ), - migrations.RemoveField( - model_name='navigationsettings', - name='facebook_url', - ), - migrations.RemoveField( - model_name='navigationsettings', - name='instagram_url', - ), - migrations.RemoveField( - model_name='navigationsettings', - name='linkedin_url', - ), - migrations.RemoveField( - model_name='navigationsettings', - name='thread_url', - ), - migrations.AddField( - model_name='navigationsettings', - name='footer_links', - field=wagtail.fields.StreamField([('section', 5)], blank=True, block_lookup={0: ('wagtail.blocks.CharBlock', (), {'required': False}), 1: ('wagtail.blocks.CharBlock', (), {}), 2: ('wagtail.blocks.URLBlock', (), {}), 3: ('wagtail.blocks.StructBlock', [[('label', 1), ('url', 2)]], {}), 4: ('wagtail.blocks.ListBlock', (3,), {}), 5: ('wagtail.blocks.StructBlock', [[('title', 0), ('links', 4)]], {})}, null=True), - ), - migrations.AlterField( - model_name='socialmediasettings', - name='links', - field=wagtail.fields.StreamField([('link', 2)], block_lookup={0: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('facebook', 'Facebook'), ('twitter', 'Twitter'), ('instagram', 'Instagram'), ('thread', 'Thread'), ('linkedin', 'LinkedIn'), ('youtube', 'YouTube')]}), 1: ('wagtail.blocks.URLBlock', (), {}), 2: ('wagtail.blocks.StructBlock', [[('platform', 0), ('url', 1)]], {})}), - ), - ] diff --git a/innovedus_cms/base/migrations/0005_headersettring.py b/innovedus_cms/base/migrations/0005_headersettring.py deleted file mode 100644 index 8d138e0..0000000 --- a/innovedus_cms/base/migrations/0005_headersettring.py +++ /dev/null @@ -1,28 +0,0 @@ -# Generated by Django 5.2.7 on 2025-10-16 04:59 - -import django.db.models.deletion -import wagtail.fields -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('base', '0004_alter_navigationsettings_options_and_more'), - ('wagtailimages', '0027_image_description'), - ] - - operations = [ - migrations.CreateModel( - name='HeaderSettring', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('site_name', models.CharField(blank=True, max_length=255)), - ('extra_links', wagtail.fields.StreamField([('link', 2)], blank=True, block_lookup={0: ('wagtail.blocks.CharBlock', (), {}), 1: ('wagtail.blocks.URLBlock', (), {}), 2: ('wagtail.blocks.StructBlock', [[('label', 0), ('url', 1)]], {})}, null=True)), - ('logo', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.image')), - ], - options={ - 'verbose_name': 'Header Settings', - }, - ), - ] diff --git a/innovedus_cms/base/migrations/0006_rename_headersettring_headersettrings.py b/innovedus_cms/base/migrations/0006_rename_headersettring_headersettrings.py deleted file mode 100644 index f5fc5b2..0000000 --- a/innovedus_cms/base/migrations/0006_rename_headersettring_headersettrings.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.2.7 on 2025-10-16 05:11 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('base', '0005_headersettring'), - ('wagtailimages', '0027_image_description'), - ] - - operations = [ - migrations.RenameModel( - old_name='HeaderSettring', - new_name='HeaderSettrings', - ), - ] diff --git a/innovedus_cms/base/migrations/0007_rename_headersettrings_headersettings.py b/innovedus_cms/base/migrations/0007_rename_headersettrings_headersettings.py deleted file mode 100644 index 1a1f312..0000000 --- a/innovedus_cms/base/migrations/0007_rename_headersettrings_headersettings.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.2.7 on 2025-10-16 05:12 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('base', '0006_rename_headersettring_headersettrings'), - ('wagtailimages', '0027_image_description'), - ] - - operations = [ - migrations.RenameModel( - old_name='HeaderSettrings', - new_name='HeaderSettings', - ), - ] diff --git a/innovedus_cms/home/migrations/0003_articlepage_categorypage.py b/innovedus_cms/home/migrations/0003_articlepage_categorypage.py deleted file mode 100644 index ed3cc8a..0000000 --- a/innovedus_cms/home/migrations/0003_articlepage_categorypage.py +++ /dev/null @@ -1,39 +0,0 @@ -# Generated by Django 5.2.7 on 2025-10-17 04:15 - -import django.db.models.deletion -import wagtail.fields -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('home', '0002_create_homepage'), - ('wagtailcore', '0095_groupsitepermission'), - ] - - operations = [ - migrations.CreateModel( - name='ArticlePage', - fields=[ - ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')), - ('date', models.DateField(verbose_name='Published date')), - ('intro', models.CharField(blank=True, max_length=250)), - ('body', wagtail.fields.StreamField([('heading', 0), ('paragraph', 1), ('image', 2), ('embed', 3)], block_lookup={0: ('wagtail.blocks.CharBlock', (), {'form_classname': 'full title'}), 1: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link']}), 2: ('wagtail.images.blocks.ImageChooserBlock', (), {}), 3: ('wagtail.embeds.blocks.EmbedBlock', (), {})})), - ], - options={ - 'abstract': False, - }, - bases=('wagtailcore.page',), - ), - migrations.CreateModel( - name='CategoryPage', - fields=[ - ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')), - ], - options={ - 'abstract': False, - }, - bases=('wagtailcore.page',), - ), - ] diff --git a/innovedus_cms/home/migrations/0003_categorypage_latestpage_trendingpage_articlepage_and_more.py b/innovedus_cms/home/migrations/0003_categorypage_latestpage_trendingpage_articlepage_and_more.py new file mode 100644 index 0000000..571b554 --- /dev/null +++ b/innovedus_cms/home/migrations/0003_categorypage_latestpage_trendingpage_articlepage_and_more.py @@ -0,0 +1,83 @@ +# Generated by Django 5.2.7 on 2025-11-26 08:11 + +import django.db.models.deletion +import home.models +import modelcluster.contrib.taggit +import modelcluster.fields +import wagtail.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0002_create_homepage'), + ('taggit', '0006_rename_taggeditem_content_type_object_id_taggit_tagg_content_8fc721_idx'), + ('wagtailcore', '0095_groupsitepermission'), + ('wagtailimages', '0027_image_description'), + ] + + operations = [ + migrations.CreateModel( + name='CategoryPage', + fields=[ + ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')), + ], + options={ + 'abstract': False, + }, + bases=('wagtailcore.page', home.models.CategoryMixin), + ), + migrations.CreateModel( + name='LatestPage', + fields=[ + ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')), + ], + options={ + 'abstract': False, + }, + bases=('wagtailcore.page', home.models.CategoryMixin), + ), + migrations.CreateModel( + name='TrendingPage', + fields=[ + ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')), + ], + options={ + 'abstract': False, + }, + bases=('wagtailcore.page', home.models.CategoryMixin), + ), + migrations.CreateModel( + name='ArticlePage', + fields=[ + ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')), + ('date', models.DateTimeField(verbose_name='Published date')), + ('intro', models.CharField(blank=True, max_length=250)), + ('body', wagtail.fields.StreamField([('heading', 0), ('paragraph', 1), ('image', 2), ('embed', 3), ('hr', 4), ('html', 5)], block_lookup={0: ('home.blocks.H2HeadingBlock', (), {'form_classname': 'full title'}), 1: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link']}), 2: ('wagtail.images.blocks.ImageChooserBlock', (), {}), 3: ('home.blocks.ValidatingEmbedBlock', (), {}), 4: ('home.blocks.HorizontalRuleBlock', (), {}), 5: ('wagtail.blocks.RawHTMLBlock', (), {'help_text': '僅限信任來源的 blockquote/iframe 原始碼'})})), + ('trending', models.BooleanField(default=False, help_text='在熱門區塊顯示', verbose_name='Trending')), + ('banner_image', models.ForeignKey(blank=True, help_text='文章內文橫幅圖片', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.image')), + ('cover_image', models.ForeignKey(blank=True, help_text='列表封面圖', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.image')), + ], + options={ + 'abstract': False, + }, + bases=('wagtailcore.page',), + ), + migrations.CreateModel( + name='ArticlePageTag', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('content_object', modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='tagged_items', to='home.articlepage')), + ('tag', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(app_label)s_%(class)s_items', to='taggit.tag')), + ], + options={ + 'abstract': False, + }, + ), + migrations.AddField( + model_name='articlepage', + name='tags', + field=modelcluster.contrib.taggit.ClusterTaggableManager(blank=True, help_text='A comma-separated list of tags.', through='home.ArticlePageTag', to='taggit.Tag', verbose_name='Tags'), + ), + ] diff --git a/innovedus_cms/home/migrations/0004_latestpage_recommandedpage.py b/innovedus_cms/home/migrations/0004_latestpage_recommandedpage.py deleted file mode 100644 index 50bdb38..0000000 --- a/innovedus_cms/home/migrations/0004_latestpage_recommandedpage.py +++ /dev/null @@ -1,35 +0,0 @@ -# Generated by Django 5.2.7 on 2025-10-17 07:28 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('home', '0003_articlepage_categorypage'), - ('wagtailcore', '0095_groupsitepermission'), - ] - - operations = [ - migrations.CreateModel( - name='LatestPage', - fields=[ - ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')), - ], - options={ - 'abstract': False, - }, - bases=('wagtailcore.page',), - ), - migrations.CreateModel( - name='RecommandedPage', - fields=[ - ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')), - ], - options={ - 'abstract': False, - }, - bases=('wagtailcore.page',), - ), - ] diff --git a/innovedus_cms/home/migrations/0005_rename_recommandedpage_recommendedpage.py b/innovedus_cms/home/migrations/0005_rename_recommandedpage_recommendedpage.py deleted file mode 100644 index 738ab54..0000000 --- a/innovedus_cms/home/migrations/0005_rename_recommandedpage_recommendedpage.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.2.7 on 2025-10-17 07:36 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('home', '0004_latestpage_recommandedpage'), - ('wagtailcore', '0095_groupsitepermission'), - ] - - operations = [ - migrations.RenameModel( - old_name='RecommandedPage', - new_name='RecommendedPage', - ), - ] diff --git a/innovedus_cms/home/migrations/0006_articlepage_cover_image_articlepage_recommended.py b/innovedus_cms/home/migrations/0006_articlepage_cover_image_articlepage_recommended.py deleted file mode 100644 index ab23371..0000000 --- a/innovedus_cms/home/migrations/0006_articlepage_cover_image_articlepage_recommended.py +++ /dev/null @@ -1,25 +0,0 @@ -# Generated by Django 5.2.7 on 2025-10-17 07:50 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('home', '0005_rename_recommandedpage_recommendedpage'), - ('wagtailimages', '0027_image_description'), - ] - - operations = [ - migrations.AddField( - model_name='articlepage', - name='cover_image', - field=models.ForeignKey(blank=True, help_text='文章列表與分享用的首圖', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.image'), - ), - migrations.AddField( - model_name='articlepage', - name='recommended', - field=models.BooleanField(default=False, help_text='在推薦清單顯示'), - ), - ] diff --git a/innovedus_cms/home/migrations/0007_articlepage_banner_image_alter_articlepage_body.py b/innovedus_cms/home/migrations/0007_articlepage_banner_image_alter_articlepage_body.py deleted file mode 100644 index 895da35..0000000 --- a/innovedus_cms/home/migrations/0007_articlepage_banner_image_alter_articlepage_body.py +++ /dev/null @@ -1,26 +0,0 @@ -# Generated by Django 5.2.7 on 2025-10-29 06:25 - -import django.db.models.deletion -import wagtail.fields -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('home', '0006_articlepage_cover_image_articlepage_recommended'), - ('wagtailimages', '0027_image_description'), - ] - - operations = [ - migrations.AddField( - model_name='articlepage', - name='banner_image', - field=models.ForeignKey(blank=True, help_text='文章頁頂部橫幅圖片', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.image'), - ), - migrations.AlterField( - model_name='articlepage', - name='body', - field=wagtail.fields.StreamField([('heading', 0), ('paragraph', 1), ('image', 2), ('embed', 3), ('hr', 4), ('html', 5)], block_lookup={0: ('home.blocks.H2HeadingBlock', (), {'form_classname': 'full title'}), 1: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link', 'ol', 'ul']}), 2: ('wagtail.images.blocks.ImageChooserBlock', (), {}), 3: ('home.blocks.ValidatingEmbedBlock', (), {}), 4: ('home.blocks.HorizontalRuleBlock', (), {}), 5: ('wagtail.blocks.RawHTMLBlock', (), {'help_text': '僅限信任來源的 blockquote/iframe 原始碼'})}), - ), - ] diff --git a/innovedus_cms/home/migrations/0008_alter_articlepage_banner_image_and_more.py b/innovedus_cms/home/migrations/0008_alter_articlepage_banner_image_and_more.py deleted file mode 100644 index dc0048a..0000000 --- a/innovedus_cms/home/migrations/0008_alter_articlepage_banner_image_and_more.py +++ /dev/null @@ -1,36 +0,0 @@ -# Generated by Django 5.2.7 on 2025-10-29 09:33 - -import django.db.models.deletion -import wagtail.fields -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('home', '0007_articlepage_banner_image_alter_articlepage_body'), - ('wagtailimages', '0027_image_description'), - ] - - operations = [ - migrations.AlterField( - model_name='articlepage', - name='banner_image', - field=models.ForeignKey(blank=True, help_text='文章內文橫幅圖片', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.image'), - ), - migrations.AlterField( - model_name='articlepage', - name='body', - field=wagtail.fields.StreamField([('heading', 0), ('paragraph', 1), ('image', 2), ('embed', 3), ('hr', 4), ('html', 5)], block_lookup={0: ('home.blocks.H2HeadingBlock', (), {'form_classname': 'full title'}), 1: ('wagtail.blocks.RichTextBlock', (), {'features': ['bold', 'italic', 'link']}), 2: ('wagtail.images.blocks.ImageChooserBlock', (), {}), 3: ('home.blocks.ValidatingEmbedBlock', (), {}), 4: ('home.blocks.HorizontalRuleBlock', (), {}), 5: ('wagtail.blocks.RawHTMLBlock', (), {'help_text': '僅限信任來源的 blockquote/iframe 原始碼'})}), - ), - migrations.AlterField( - model_name='articlepage', - name='cover_image', - field=models.ForeignKey(blank=True, help_text='列表封面圖', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.image'), - ), - migrations.AlterField( - model_name='articlepage', - name='recommended', - field=models.BooleanField(default=False, help_text='在推薦區塊顯示'), - ), - ] diff --git a/innovedus_cms/home/migrations/0009_articlepagetag_articlepage_tags.py b/innovedus_cms/home/migrations/0009_articlepagetag_articlepage_tags.py deleted file mode 100644 index fa7e80b..0000000 --- a/innovedus_cms/home/migrations/0009_articlepagetag_articlepage_tags.py +++ /dev/null @@ -1,59 +0,0 @@ -import django.db.models.deletion -import modelcluster.contrib.taggit -import modelcluster.fields -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("home", "0008_alter_articlepage_banner_image_and_more"), - ("taggit", "0004_alter_taggeditem_content_type_alter_taggeditem_tag"), - ] - - operations = [ - migrations.CreateModel( - name="ArticlePageTag", - fields=[ - ( - "id", - models.AutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ( - "content_object", - modelcluster.fields.ParentalKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="tagged_items", - to="home.articlepage", - ), - ), - ( - "tag", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="home_articlepagetag_items", - to="taggit.tag", - ), - ), - ], - options={ - "abstract": False, - }, - ), - migrations.AddField( - model_name="articlepage", - name="tags", - field=modelcluster.contrib.taggit.ClusterTaggableManager( - blank=True, - help_text="A comma-separated list of tags.", - through="home.ArticlePageTag", - to="taggit.Tag", - verbose_name="Tags", - ), - ), - ] diff --git a/innovedus_cms/home/migrations/0010_alter_articlepage_recommended.py b/innovedus_cms/home/migrations/0010_alter_articlepage_recommended.py deleted file mode 100644 index 9a6c2a6..0000000 --- a/innovedus_cms/home/migrations/0010_alter_articlepage_recommended.py +++ /dev/null @@ -1,20 +0,0 @@ -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("home", "0009_articlepagetag_articlepage_tags"), - ] - - operations = [ - migrations.AlterField( - model_name="articlepage", - name="recommended", - field=models.BooleanField( - default=False, - help_text="在熱門區塊顯示", - verbose_name="Trending", - ), - ), - ] diff --git a/innovedus_cms/home/migrations/0011_rename_recommendedpage_trendingpage.py b/innovedus_cms/home/migrations/0011_rename_recommendedpage_trendingpage.py deleted file mode 100644 index 87b487f..0000000 --- a/innovedus_cms/home/migrations/0011_rename_recommendedpage_trendingpage.py +++ /dev/null @@ -1,15 +0,0 @@ -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ("home", "0010_alter_articlepage_recommended"), - ] - - operations = [ - migrations.RenameModel( - old_name="RecommendedPage", - new_name="TrendingPage", - ), - ] diff --git a/innovedus_cms/home/migrations/0012_rename_recommended_articlepage_trending_and_more.py b/innovedus_cms/home/migrations/0012_rename_recommended_articlepage_trending_and_more.py deleted file mode 100644 index 2a4b86e..0000000 --- a/innovedus_cms/home/migrations/0012_rename_recommended_articlepage_trending_and_more.py +++ /dev/null @@ -1,30 +0,0 @@ -# Generated by Django 5.2.7 on 2025-11-06 04:04 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('home', '0011_rename_recommendedpage_trendingpage'), - ('taggit', '0006_rename_taggeditem_content_type_object_id_taggit_tagg_content_8fc721_idx'), - ] - - operations = [ - migrations.RenameField( - model_name='articlepage', - old_name='recommended', - new_name='trending', - ), - migrations.AlterField( - model_name='articlepagetag', - name='id', - field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), - ), - migrations.AlterField( - model_name='articlepagetag', - name='tag', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(app_label)s_%(class)s_items', to='taggit.tag'), - ), - ] diff --git a/innovedus_cms/home/migrations/0013_alter_articlepage_date.py b/innovedus_cms/home/migrations/0013_alter_articlepage_date.py deleted file mode 100644 index cdb39a9..0000000 --- a/innovedus_cms/home/migrations/0013_alter_articlepage_date.py +++ /dev/null @@ -1,46 +0,0 @@ -# Generated by Django 5.2.7 on 2025-11-10 05:37 - -import datetime as dt - -from django.conf import settings -from django.db import migrations, models -from django.utils import timezone - - -def normalize_article_dates(apps, schema_editor): - ArticlePage = apps.get_model("home", "ArticlePage") - - for page in ArticlePage.objects.all(): - if page.date is None: - continue - - value = page.date - if isinstance(value, dt.datetime): - base = value - elif isinstance(value, dt.date): - base = dt.datetime.combine(value, dt.time.min) - else: - continue - - if settings.USE_TZ and timezone.is_naive(base): - base = timezone.make_aware(base, timezone.get_default_timezone()) - - normalized = base.replace(hour=0, minute=0, second=0, microsecond=0) - if normalized != page.date: - ArticlePage.objects.filter(pk=page.pk).update(date=normalized) - - -class Migration(migrations.Migration): - - dependencies = [ - ('home', '0012_rename_recommended_articlepage_trending_and_more'), - ] - - operations = [ - migrations.AlterField( - model_name='articlepage', - name='date', - field=models.DateTimeField(verbose_name='Published date'), - ), - migrations.RunPython(normalize_article_dates, migrations.RunPython.noop), - ] -- 2.47.2 From 21475448d20d5cfaa2dc503cd564bcbbe3275cfa Mon Sep 17 00:00:00 2001 From: Warren Chen Date: Fri, 28 Nov 2025 17:36:54 +0900 Subject: [PATCH 12/18] Ready to deploy via docker --- innovedus_cms/.dockerignore | 5 +++++ innovedus_cms/Dockerfile | 17 +++++++++++++---- innovedus_cms/entrypoint.sh | 8 ++++++++ innovedus_cms/mysite/settings/base.py | 22 ++++++++++++++++++---- 4 files changed, 44 insertions(+), 8 deletions(-) create mode 100644 innovedus_cms/entrypoint.sh diff --git a/innovedus_cms/.dockerignore b/innovedus_cms/.dockerignore index b7c39d7..63b9dde 100644 --- a/innovedus_cms/.dockerignore +++ b/innovedus_cms/.dockerignore @@ -1,3 +1,8 @@ fly.toml .git/ +.venv +__pycache__/ +*.pyc *.sqlite3 +media/ +*.log diff --git a/innovedus_cms/Dockerfile b/innovedus_cms/Dockerfile index 3c36327..755d6ea 100644 --- a/innovedus_cms/Dockerfile +++ b/innovedus_cms/Dockerfile @@ -2,20 +2,29 @@ ARG PYTHON_VERSION=3.13-slim FROM python:${PYTHON_VERSION} -ENV PYTHONDONTWRITEBYTECODE 1 -ENV PYTHONUNBUFFERED 1 - -RUN mkdir -p /code +ENV PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 \ + PIP_NO_CACHE_DIR=on \ + DJANGO_SETTINGS_MODULE=mysite.settings.production WORKDIR /code +# Create an unprivileged user to run the app +RUN adduser --disabled-password --gecos '' app + COPY requirements.txt /tmp/requirements.txt RUN set -ex && \ pip install --upgrade pip && \ pip install -r /tmp/requirements.txt && \ rm -rf /root/.cache/ + COPY . /code +COPY entrypoint.sh /entrypoint.sh + +RUN chmod +x /entrypoint.sh && chown -R app:app /code +USER app EXPOSE 8000 +ENTRYPOINT ["/entrypoint.sh"] CMD ["gunicorn","--bind",":8000","--workers","2","mysite.wsgi"] diff --git a/innovedus_cms/entrypoint.sh b/innovedus_cms/entrypoint.sh new file mode 100644 index 0000000..0e3d21d --- /dev/null +++ b/innovedus_cms/entrypoint.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -e + +# Run pending migrations and collect static assets before starting the app +python manage.py migrate --noinput +python manage.py collectstatic --noinput + +exec "$@" diff --git a/innovedus_cms/mysite/settings/base.py b/innovedus_cms/mysite/settings/base.py index a281a37..4995035 100644 --- a/innovedus_cms/mysite/settings/base.py +++ b/innovedus_cms/mysite/settings/base.py @@ -16,6 +16,15 @@ import os PROJECT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) BASE_DIR = os.path.dirname(PROJECT_DIR) +def env_list(name, default): + """ + Return a list from a comma-separated env var; fall back to provided default list. + """ + value = os.environ.get(name) + if value: + return [item.strip() for item in value.split(",") if item.strip()] + return default + # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/ @@ -191,6 +200,9 @@ STORAGES = { }, } +# Avoid overwriting user uploads when using S3 storage unless explicitly enabled via env +AWS_S3_FILE_OVERWRITE = os.environ.get("AWS_S3_FILE_OVERWRITE", "False").lower() == "true" + # Django sets a maximum of 1000 fields per form by default, but particularly complex page models # can exceed this limit within Wagtail's page editor. DATA_UPLOAD_MAX_NUMBER_FIELDS = 10_000 @@ -218,8 +230,10 @@ WAGTAILADMIN_BASE_URL = "http://example.com" # see https://docs.wagtail.org/en/stable/advanced_topics/deploying.html#user-uploaded-files WAGTAILDOCS_EXTENSIONS = ['csv', 'docx', 'key', 'odt', 'pdf', 'pptx', 'rtf', 'txt', 'xlsx', 'zip'] -CSRF_TRUSTED_ORIGINS = [ - 'https://innovedus-cms.fly.dev', -] +CSRF_TRUSTED_ORIGINS = env_list( + "CSRF_TRUSTED_ORIGINS" +) -ALLOWED_HOSTS = ['innovedus-cms.fly.dev'] +ALLOWED_HOSTS = env_list( + "ALLOWED_HOSTS" +) -- 2.47.2 From e62a316924365968a036a0b58268a874c2f37700 Mon Sep 17 00:00:00 2001 From: Warren Chen Date: Wed, 14 Jan 2026 14:27:08 +0900 Subject: [PATCH 13/18] Add new image assets and implement horizontal and block list functionality - Added multiple PNG images for various sections of the site. - Introduced JavaScript functionality for horizontal and block lists, allowing smooth scrolling and dynamic arrow visibility based on scroll position. - Created a new template for displaying news articles, including a hero section for the latest article and a list for additional articles. - Enhanced the news list with responsive design and fallback images for articles without cover images. --- ...socialmediasettings_links_bannersnippet.py | 39 +++ ..._logo_headersettings_logo_dark_and_more.py | 29 ++ innovedus_cms/base/models.py | 52 +++- .../base/includes/banner_snippets.html | 27 ++ .../templates/base/includes/home_banner.html | 20 ++ .../base/templatetags/banner_tags.py | 15 + .../migrations/0004_articlepage_not_news.py | 18 ++ innovedus_cms/home/models.py | 34 ++- innovedus_cms/home/static/css/block_list.css | 79 +++++ innovedus_cms/home/static/css/home.css | 74 +++++ .../home/static/css/horizontal_list.css | 112 +++++++ innovedus_cms/home/static/css/news_list.css | 182 +++++++++++ innovedus_cms/home/static/img/picfrm_b139.png | Bin 0 -> 926 bytes innovedus_cms/home/static/img/picfrm_b194.png | Bin 0 -> 1969 bytes innovedus_cms/home/static/img/picfrm_b300.png | Bin 0 -> 2436 bytes innovedus_cms/home/static/img/picfrm_b318.png | Bin 0 -> 2461 bytes innovedus_cms/home/static/img/picfrm_b426.png | Bin 0 -> 2720 bytes innovedus_cms/home/static/img/picfrm_b480.png | Bin 0 -> 2837 bytes innovedus_cms/home/static/img/picfrm_o139.png | Bin 0 -> 938 bytes innovedus_cms/home/static/img/picfrm_o194.png | Bin 0 -> 2031 bytes innovedus_cms/home/static/img/picfrm_o300.png | Bin 0 -> 2499 bytes innovedus_cms/home/static/img/picfrm_o318.png | Bin 0 -> 2518 bytes innovedus_cms/home/static/img/picfrm_o426.png | Bin 0 -> 2784 bytes innovedus_cms/home/static/img/picfrm_o480.png | Bin 0 -> 2894 bytes innovedus_cms/home/static/js/block_list.js | 45 +++ .../home/static/js/horizontal_list.js | 45 +++ .../home/templates/home/home_page.html | 45 ++- .../templates/home/includes/block_list.html | 38 +-- .../home/includes/category_session.html | 16 +- .../home/includes/horizontal_list.html | 47 +-- .../templates/home/includes/news_list.html | 82 +++++ innovedus_cms/mysite/static/css/mysite.css | 288 ++++++++++++++++++ .../mysite/templates/includes/footer.html | 93 ++++-- .../mysite/templates/includes/header.html | 88 ++++-- 34 files changed, 1346 insertions(+), 122 deletions(-) create mode 100644 innovedus_cms/base/migrations/0003_alter_socialmediasettings_links_bannersnippet.py create mode 100644 innovedus_cms/base/migrations/0004_remove_headersettings_logo_headersettings_logo_dark_and_more.py create mode 100644 innovedus_cms/base/templates/base/includes/banner_snippets.html create mode 100644 innovedus_cms/base/templates/base/includes/home_banner.html create mode 100644 innovedus_cms/base/templatetags/banner_tags.py create mode 100644 innovedus_cms/home/migrations/0004_articlepage_not_news.py create mode 100644 innovedus_cms/home/static/css/block_list.css create mode 100644 innovedus_cms/home/static/css/home.css create mode 100644 innovedus_cms/home/static/css/horizontal_list.css create mode 100644 innovedus_cms/home/static/css/news_list.css create mode 100644 innovedus_cms/home/static/img/picfrm_b139.png create mode 100644 innovedus_cms/home/static/img/picfrm_b194.png create mode 100644 innovedus_cms/home/static/img/picfrm_b300.png create mode 100644 innovedus_cms/home/static/img/picfrm_b318.png create mode 100644 innovedus_cms/home/static/img/picfrm_b426.png create mode 100644 innovedus_cms/home/static/img/picfrm_b480.png create mode 100644 innovedus_cms/home/static/img/picfrm_o139.png create mode 100644 innovedus_cms/home/static/img/picfrm_o194.png create mode 100644 innovedus_cms/home/static/img/picfrm_o300.png create mode 100644 innovedus_cms/home/static/img/picfrm_o318.png create mode 100644 innovedus_cms/home/static/img/picfrm_o426.png create mode 100644 innovedus_cms/home/static/img/picfrm_o480.png create mode 100644 innovedus_cms/home/static/js/block_list.js create mode 100644 innovedus_cms/home/static/js/horizontal_list.js create mode 100644 innovedus_cms/home/templates/home/includes/news_list.html diff --git a/innovedus_cms/base/migrations/0003_alter_socialmediasettings_links_bannersnippet.py b/innovedus_cms/base/migrations/0003_alter_socialmediasettings_links_bannersnippet.py new file mode 100644 index 0000000..9314ad7 --- /dev/null +++ b/innovedus_cms/base/migrations/0003_alter_socialmediasettings_links_bannersnippet.py @@ -0,0 +1,39 @@ +# Generated by Django 5.2.7 on 2026-01-09 05:52 + +import django.db.models.deletion +import wagtail.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('base', '0002_socialmediasettings_alter_navigationsettings_options_and_more'), + ('wagtailimages', '0027_image_description'), + ] + + operations = [ + migrations.AlterField( + model_name='socialmediasettings', + name='links', + field=wagtail.fields.StreamField([('link', 2)], block_lookup={0: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('facebook', 'Facebook'), ('twitter', 'Twitter'), ('instagram', 'Instagram'), ('threads', 'Threads'), ('linkedin', 'LinkedIn'), ('youtube', 'YouTube')]}), 1: ('wagtail.blocks.URLBlock', (), {}), 2: ('wagtail.blocks.StructBlock', [[('platform', 0), ('url', 1)]], {})}), + ), + migrations.CreateModel( + name='BannerSnippet', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('key', models.CharField(blank=True, help_text='識別用 key(例如 home / category)', max_length=50)), + ('title', models.CharField(blank=True, max_length=255)), + ('link_url', models.URLField(blank=True)), + ('link_text', models.CharField(blank=True, max_length=100)), + ('is_active', models.BooleanField(default=True)), + ('sort_order', models.PositiveIntegerField(default=0)), + ('image', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.image')), + ], + options={ + 'verbose_name': 'Banner', + 'verbose_name_plural': 'Banners', + 'ordering': ['sort_order', 'id'], + }, + ), + ] diff --git a/innovedus_cms/base/migrations/0004_remove_headersettings_logo_headersettings_logo_dark_and_more.py b/innovedus_cms/base/migrations/0004_remove_headersettings_logo_headersettings_logo_dark_and_more.py new file mode 100644 index 0000000..d64738e --- /dev/null +++ b/innovedus_cms/base/migrations/0004_remove_headersettings_logo_headersettings_logo_dark_and_more.py @@ -0,0 +1,29 @@ +# Generated by Django 5.2.7 on 2026-01-09 09:01 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('base', '0003_alter_socialmediasettings_links_bannersnippet'), + ('wagtailimages', '0027_image_description'), + ] + + operations = [ + migrations.RemoveField( + model_name='headersettings', + name='logo', + ), + migrations.AddField( + model_name='headersettings', + name='logo_dark', + field=models.ForeignKey(blank=True, help_text='淺色底用(深色 logo)', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.image'), + ), + migrations.AddField( + model_name='headersettings', + name='logo_light', + field=models.ForeignKey(blank=True, help_text='深色底用(亮色 logo)', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.image'), + ), + ] diff --git a/innovedus_cms/base/models.py b/innovedus_cms/base/models.py index 99355ee..0824424 100644 --- a/innovedus_cms/base/models.py +++ b/innovedus_cms/base/models.py @@ -29,12 +29,21 @@ from wagtail import blocks @register_setting class HeaderSettings(BaseGenericSetting): - logo = models.ForeignKey( + logo_light = models.ForeignKey( "wagtailimages.Image", null=True, blank=True, on_delete=models.SET_NULL, related_name="+", + help_text="深色底用(亮色 logo)", + ) + logo_dark = models.ForeignKey( + "wagtailimages.Image", + null=True, + blank=True, + on_delete=models.SET_NULL, + related_name="+", + help_text="淺色底用(深色 logo)", ) site_name = models.CharField(max_length=255, blank=True) extra_links = StreamField([ @@ -47,7 +56,8 @@ class HeaderSettings(BaseGenericSetting): panels = [ MultiFieldPanel( [ - FieldPanel("logo"), + FieldPanel("logo_light"), + FieldPanel("logo_dark"), FieldPanel("site_name"), FieldPanel("extra_links"), ], @@ -82,7 +92,7 @@ class SocialLinkBlock(blocks.StructBlock): ("facebook", "Facebook"), ("twitter", "Twitter"), ("instagram", "Instagram"), - ("thread", "Thread"), + ("threads", "Threads"), ("linkedin", "LinkedIn"), ("youtube", "YouTube"), ] @@ -102,6 +112,40 @@ class SocialMediaSettings(BaseGenericSetting): panels = [FieldPanel("links")] +@register_snippet +class BannerSnippet(models.Model): + key = models.CharField(max_length=50, blank=True, help_text="識別用 key(例如 home / category)") + title = models.CharField(max_length=255, blank=True) + image = models.ForeignKey( + "wagtailimages.Image", + null=True, + blank=False, + on_delete=models.SET_NULL, + related_name="+", + ) + link_url = models.URLField(blank=True) + link_text = models.CharField(max_length=100, blank=True) + is_active = models.BooleanField(default=True) + sort_order = models.PositiveIntegerField(default=0) + + panels = [ + FieldPanel("key"), + FieldPanel("title"), + FieldPanel("image"), + FieldPanel("link_url"), + FieldPanel("link_text"), + FieldPanel("is_active"), + FieldPanel("sort_order"), + ] + + class Meta: + ordering = ["sort_order", "id"] + verbose_name = "Banner" + verbose_name_plural = "Banners" + + def __str__(self): + return self.title or f"Banner {self.pk}" + @register_snippet class FooterText( DraftStateMixin, @@ -128,4 +172,4 @@ class FooterText( return {"footer_text": self.body} class Meta(TranslatableMixin.Meta): - verbose_name_plural = "Footer Text" \ No newline at end of file + verbose_name_plural = "Footer Text" diff --git a/innovedus_cms/base/templates/base/includes/banner_snippets.html b/innovedus_cms/base/templates/base/includes/banner_snippets.html new file mode 100644 index 0000000..52f6f39 --- /dev/null +++ b/innovedus_cms/base/templates/base/includes/banner_snippets.html @@ -0,0 +1,27 @@ +{% load wagtailimages_tags banner_tags %} + +{% get_banners banner_key as banners %} +{% if banners %} + +{% endif %} diff --git a/innovedus_cms/base/templates/base/includes/home_banner.html b/innovedus_cms/base/templates/base/includes/home_banner.html new file mode 100644 index 0000000..3490cd2 --- /dev/null +++ b/innovedus_cms/base/templates/base/includes/home_banner.html @@ -0,0 +1,20 @@ +{% load wagtailimages_tags banner_tags %} + +{% get_banners "home" first=True as banner %} +{% if banner %} + +{% endif %} diff --git a/innovedus_cms/base/templatetags/banner_tags.py b/innovedus_cms/base/templatetags/banner_tags.py new file mode 100644 index 0000000..3b39814 --- /dev/null +++ b/innovedus_cms/base/templatetags/banner_tags.py @@ -0,0 +1,15 @@ +from django import template + +from base.models import BannerSnippet + +register = template.Library() + + +@register.simple_tag +def get_banners(key=None, first=False): + banners = BannerSnippet.objects.filter(is_active=True, image__isnull=False) + if key: + banners = banners.filter(key=key) + if first: + return banners.first() + return banners diff --git a/innovedus_cms/home/migrations/0004_articlepage_not_news.py b/innovedus_cms/home/migrations/0004_articlepage_not_news.py new file mode 100644 index 0000000..5bf324e --- /dev/null +++ b/innovedus_cms/home/migrations/0004_articlepage_not_news.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.7 on 2026-01-09 05:52 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0003_categorypage_latestpage_trendingpage_articlepage_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='articlepage', + name='not_news', + field=models.BooleanField(default=False, help_text='不列入最新消息區塊', verbose_name='Not News'), + ), + ] diff --git a/innovedus_cms/home/models.py b/innovedus_cms/home/models.py index f062e57..1e5f6b0 100644 --- a/innovedus_cms/home/models.py +++ b/innovedus_cms/home/models.py @@ -18,8 +18,9 @@ def _get_env_int(name, default): except ValueError: return default +NEWS_SIZE = _get_env_int("HOMEPAGE_NEWS_SIZE", 8) # Default to 8 articles in news layouts BLOCK_SIZE = _get_env_int("HOMEPAGE_BLOCK_SIZE", 5) # Default to 5 articles in block layout -HORIZON_SIZE = _get_env_int("HOMEPAGE_HORIZON_SIZE", 8) # Default to 8 articles in horizon layout +HORIZON_SIZE = _get_env_int("HOMEPAGE_HORIZON_SIZE", 4) # Default to 8 articles in horizon layout PAGE_SIZE = _get_env_int("HOMEPAGE_PAGE_SIZE", 10) # Default to 10 articles per page for pagination # Mixin for Category-related functionality @@ -85,8 +86,8 @@ class CategoryMixin: # No request means no pagination (e.g., homepage) return { "title": latest_page.title, - "items": ArticlePage.objects.live().order_by("-date")[ - :BLOCK_SIZE + "items": ArticlePage.objects.filter(not_news=False).live().order_by("-date")[ + :NEWS_SIZE ], "url": latest_page.url, } @@ -154,21 +155,24 @@ class HomePage(Page, CategoryMixin): } latest_section = self.get_latest_articles().copy() - latest_section["layout"] = "block" + # latest_section["layout"] = "block" sections["top_section"].append(latest_section) + # NOT NEED TRENDING SECTION CURRENTLY + # ------------------------------------------------------------------ # Exclude latest articles from trending section - latest_items = latest_section.get("items", []) - if hasattr(latest_items, "values_list"): - latest_ids = list(latest_items.values_list("id", flat=True)) - else: - latest_ids = [item.id for item in latest_items] + # latest_items = latest_section.get("items", []) + # if hasattr(latest_items, "values_list"): + # latest_ids = list(latest_items.values_list("id", flat=True)) + # else: + # latest_ids = [item.id for item in latest_items] - trending_section = self.get_trending_articles( - exclude_ids=latest_ids - ).copy() - trending_section["layout"] = "horizon" - sections["top_section"].append(trending_section) + # trending_section = self.get_trending_articles( + # exclude_ids=latest_ids + # ).copy() + # trending_section["layout"] = "horizon" + # sections["top_section"].append(trending_section) + # ------------------------------------------------------------------ # Build category sections categories = CategoryPage.objects.child_of(self).live().in_menu() @@ -271,6 +275,7 @@ class ArticlePage(Page): use_json_field=True, ) trending = models.BooleanField("Trending", default=False, help_text="在熱門區塊顯示") + not_news = models.BooleanField("Not News", default=False, help_text="不列入最新消息區塊") tags = ClusterTaggableManager(through="home.ArticlePageTag", blank=True) search_fields = Page.search_fields + [ @@ -281,6 +286,7 @@ class ArticlePage(Page): content_panels = Page.content_panels + [ FieldPanel("trending"), + FieldPanel("not_news"), FieldPanel("cover_image"), FieldPanel("banner_image"), FieldPanel("date"), diff --git a/innovedus_cms/home/static/css/block_list.css b/innovedus_cms/home/static/css/block_list.css new file mode 100644 index 0000000..a29d2e2 --- /dev/null +++ b/innovedus_cms/home/static/css/block_list.css @@ -0,0 +1,79 @@ +.block-list-wrap { + position: relative; +} + +.block-list-arrow { + position: absolute; + top: 50%; + transform: translateY(-50%); + width: 36px; + height: 36px; + border: 0; + border-radius: 8px; + background: rgba(255, 255, 255, 0.85); + box-shadow: 0 8px 16px rgba(0, 0, 0, 0.18); + cursor: pointer; +} + +.block-list-arrow[data-dir="left"] { + left: 0; +} + +.block-list-arrow[data-dir="right"] { + right: 0; +} + +.block-list-arrow.is-hidden { + opacity: 0; + pointer-events: none; +} + +.block-list { + display: flex; + align-items: stretch; + gap: 20px; + overflow-x: auto; + scroll-snap-type: x mandatory; + list-style: none; + margin: 24px 0; + padding: 12px 48px; +} + +.block-list li { + flex: 0 0 200px; + overflow: hidden; + scroll-snap-align: start; +} + +.block-list li a { + display: flex; + flex-direction: column; + gap: 12px; + color: inherit; + text-decoration: none; + padding: 16px; +} + +.block-list li img { + width: 100%; + height: 200px; + /* object-fit: contain; */ + display: block; + /* background: #f5f5f5; */ + /* border-radius: 8px; */ +} + +.block-list li a:hover, +.block-list li a:focus { + transform: translateY(-2px); +} + +.block-list .empty { + flex: 1 0 auto; + text-align: center; + padding: 24px; + color: #666; + background: #fafafa; + border: 1px dashed #ddd; + border-radius: 12px; +} diff --git a/innovedus_cms/home/static/css/home.css b/innovedus_cms/home/static/css/home.css new file mode 100644 index 0000000..e92c9f7 --- /dev/null +++ b/innovedus_cms/home/static/css/home.css @@ -0,0 +1,74 @@ +.home-page { + max-width: 890px; + margin: 0 auto; + padding: 0 16px; +} + +.home-hero-band { + background-color: #0e1b42; + color: #ffffff; + padding-bottom: 86px; +} + +.home-hero-band a { + color: #ffffff; +} + +.template-homepage .site-header { + background-color: #0e1b42; + color: #ffffff; +} + +.template-homepage .header-inner { + max-width: 1028px; + margin: 0 auto; + /* padding: 0 16px; */ +} + +.template-homepage .main-menu-link { + color: #ffffff; +} + +.template-homepage .header-search .search-input { + /* border-color: #ffffff; */ + background-color: rgba(255, 255, 255, 0.4); +} + +.template-homepage .header-search .search-icon { + color: #ffffff; +} + +.template-homepage .header-search input[type="search"] { + color: #ffffff; +} + +.list-title { + align-items: center; + margin: 10px 0; +} + +.block-title { + display: inline-block; + width: 197px; + height: 87px; + vertical-align: middle; + font-size: 20px; + font-weight: 700; +} + +.block-title span { + padding-left: 21px; + line-height: 87px; +} + +.block-title-divider { + display: inline-flex; + width: 28px; + height: 1px; + transform: translate(-4px, -4px); +} + +.more-link { + text-decoration: none; + font-size: 16px; +} \ No newline at end of file diff --git a/innovedus_cms/home/static/css/horizontal_list.css b/innovedus_cms/home/static/css/horizontal_list.css new file mode 100644 index 0000000..b548f5e --- /dev/null +++ b/innovedus_cms/home/static/css/horizontal_list.css @@ -0,0 +1,112 @@ +.horizontal-list-wrap { + position: relative; +} + +.horizontal-list-arrow { + position: absolute; + top: 50%; + transform: translateY(-50%); + z-index: 2; + display: flex; + align-items: center; + justify-content: center; + width: 52px; + height: 52px; + border: 0; + border-radius: 8px; + background: rgba(255, 255, 255, 0.85); + box-shadow: 0 8px 16px rgba(0, 0, 0, 0.18); + cursor: pointer; +} + +.horizontal-list-arrow[data-dir="left"] { + left: 0; +} + +.horizontal-list-arrow[data-dir="right"] { + right: 0; +} + +.horizontal-list-arrow-icon { + width: 18px; + height: 34px; + display: block; +} + +.horizontal-list-arrow[data-dir="right"] .horizontal-list-arrow-icon { + transform: rotate(180deg); +} + +.horizontal-list-arrow.is-hidden { + opacity: 0; + pointer-events: none; +} + +.horizontal-list { + display: flex; + align-items: stretch; + gap: 24px; + overflow-x: auto; + scroll-snap-type: x mandatory; + list-style: none; + margin: 24px 0; + padding: 12px 48px; + scrollbar-width: none; + -ms-overflow-style: none; +} + +.horizontal-list::-webkit-scrollbar { + display: none; +} + +.horizontal-list li { + flex: 0 0 200px; + overflow: hidden; + scroll-snap-align: start; +} + +.horizontal-list li a { + display: flex; + flex-direction: column; + gap: 12px; + color: inherit; + text-decoration: none; +} + +.horizontal-list li img { + width: 100%; + height: 200px; + display: block; + margin: 0 3px; +} + +.horizontal-list li a:hover, +.horizontal-list li a:focus { + transform: translateY(-2px); +} + +.horizontal-list li a span { + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + font-size: 20px; +} + +.horizontal-list .article-title { + font-size: 20px; +} + +.horizontal-list .article-intro { + font-size: 16px; +} + +.horizontal-list .empty { + flex: 1 0 auto; + text-align: center; + padding: 24px; + color: #666; + background: #fafafa; + border: 1px dashed #ddd; + border-radius: 12px; +} diff --git a/innovedus_cms/home/static/css/news_list.css b/innovedus_cms/home/static/css/news_list.css new file mode 100644 index 0000000..798d250 --- /dev/null +++ b/innovedus_cms/home/static/css/news_list.css @@ -0,0 +1,182 @@ +.news-title, .more-news-title { + background-color: #ffffff; + color: #0e1b42; +} + +.news-hero { + display: grid; + grid-template-columns: 1fr 1fr; + grid-template-areas: + "header header" + "image meta" + "image intro" + "image body"; + gap: 16px; + align-items: start; +} + +.news-hero .news-hero-header { + grid-area: header; + display: flex; + align-items: center; + gap: 12px; +} + +.news-hero .list-title { + grid-area: title; + max-width: 310px; + flex: 0 1 310px; + display: block; +} + +.news-hero .fist-news-title { + grid-area: head; + max-width: 580px; + font-size: 40px; + font-weight: 400; + color: #eb9f13; + margin: 0; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.news-hero .first-news-image { + grid-area: image; + position: relative; + border-radius: 10px; + overflow: hidden; +} + +.news-hero .first-news-image::after { + content: ""; + position: absolute; + left: 0px; + right: 0px; + bottom: 0px; + height: 25px; + background: url("../img/picfrm_b480.png") no-repeat left bottom / cover; + background-size: 480px 25px; + pointer-events: none; +} + +.news-hero .first-news-image img { + display: block; + width:480px; + height:293px; + object-fit:cover; + border-radius: 10px; +} + +.news-hero .fist-news-date { + grid-area: meta; + color: rgba(255, 255, 255, 0.4); +} + +.news-hero .first-news-intro { + grid-area: intro; + font-size: 20px; +} + +.news-hero .first-news-body { + grid-area: body; + font: 12px; +} + +.news-list-items { + display: contents; +} + +.news-list-lower { + margin-top: 24px; + display: flex; + flex-wrap: wrap; + gap: 30px; +} + +.news-list-items a { + display: flex; + flex-direction: column; + gap: 12px; + width: 200px; + text-decoration: none; +} + +.news-list-thumb { + position: relative; + width: 194px; + height: 133px; + border-radius: 10px; + overflow: hidden; +} + +.news-list-thumb::after { + content: ""; + position: absolute; + left: 0px; + right: 0px; + bottom: 0px; + height: 25px; + background: url("../img/picfrm_o194.png") no-repeat left bottom / cover; + background-size: 194px 25px; + pointer-events: none; +} + +.news-list-thumb img { + display: block; + width: 100%; + height: 100%; + object-fit: cover; +} + +.news-list-items a span{ + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.news-list-items .article-title { + font-size: 20px; +} + +.news-list-items .article-date { + color: rgba(255, 255, 255, 0.4); +} + +.more-news { + width: 200px; + display: flex; + flex-direction: column; + gap: 16px; +} + +.more-news .article-title { + font-size: 20px; + display: -webkit-box; + -webkit-line-clamp: 1; + -webkit-box-orient: vertical; + overflow: hidden; +} + +@media (max-width: 768px) { + .news-hero { + grid-template-columns: 1fr; + grid-template-areas: + "title" + "image" + "head" + "meta" + "intro" + "body"; + } + + .news-hero .news-hero-header { + display: contents; + } + + .news-hero .list-title { + max-width: none; + } +} diff --git a/innovedus_cms/home/static/img/picfrm_b139.png b/innovedus_cms/home/static/img/picfrm_b139.png new file mode 100644 index 0000000000000000000000000000000000000000..b5d4f6c9dd49bfcc5f3ef29c38a7a362e8df13a4 GIT binary patch literal 926 zcmeAS@N?(olHy`uVBq!ia0y~yU=#zgjX2nVY&p-g~!C?N{=6TY2$k9@E^Je*Q-Besw#IFZ)fdO1G3`;Sk^u$zaabbZua0+}MzC zk%NV!g_~nqK@cmafPhXFP_%&Yppf4x1xBVOSAGEn0X>C?L!P244h|b$Ffy?mVrB8R zSlPtF!69br(BQD6A%RmpbOHn8!A0yqlf?ye9xmY$Qc#Ha!obM%kf}*^PKYBD3rn}0 zf`dYg!-kg0S{@BROPOhDsc8h;!%(1u7o28lHcLHt=_y~q8=$+3tTKaV|9aR8k-2}W zN45U#riNs72ZacQ{$Ed}yv>q1%R>7cmb2a}xzoc~m45&9>(74478Oh^&%{rh^}H1q zH~&taTrc1A^(&YdndXXTd@eMd!E$0;eLZ5?_p05yqPgBIsXBUH!J*;6k8_3p0@kgxkAo}HJ?SsKs^G9-F;nR@qnJ4P`%iu{AAG#r>9YL4FUh*Paqp&HUVMG! z$?VJd(drHl_6uvoJ8Q=H@L4yye0D_H@y%En#}sr`X%4 z=&-Rh)`un-)P0Dqb-VndQ0wVQ_UP5e>x&KS8Mkl9|KQKdYX-_2p00i_>zopr0Na~f A@&Et; literal 0 HcmV?d00001 diff --git a/innovedus_cms/home/static/img/picfrm_b194.png b/innovedus_cms/home/static/img/picfrm_b194.png new file mode 100644 index 0000000000000000000000000000000000000000..bada8a3917d06b2a24fd0670e27fa80bd4e547e3 GIT binary patch literal 1969 zcmXw4dpwhEAHR27YYwYL%d(OXnx4?iv9dYjET^%Ea(Gwd%$~=iFe;~H3aN#6$!V*N zipN>x5t75RQC^2a<*>B$lJU^%z1QdS{Bhma@4mjj>-+g$zw7(?U74;fwkSCjIRF4q z_IB3p002va;$uhz^rb$&atxYavF^5(0DoZL0`z13FRDGw*%`n?F%p2KhX8PC3N%!p z0RS>=7$5`9FzFf_{0D`lv1R^=0VydE`qf_q0NZi))@09k*q7n}_wiG^8Q#ZCgn`Ef zP!me|?|=w2Ez0Z%#)LctAJx_|h0S_9tmPH4#i)5y1NrWrv3r4+n@@9n&mM8SQ}VH3 zJ>Nd8_ECNFds_ThedK%j*CVY&dK*a4-|lOBJw1y4TbR>QP=F~5Z1N=sL>K-hqlAc* z?;`_nwWZ@oC#@efs~U;O4Sdl29|PG>tT1PgDG=lu1k$|~5|=x@jT1I`Ik*4%jJ~Y~ zz>uCU?6#~9gmFdlh{vHfld7!tEA9oE`bZK05;a`Ub)DDx)G}#tO*>*orzMRCdxNLU*bp|I(Ub;%qbhYSG z6LYw@fmmAN)(B75Hqd=0RjPOqIqYcAH~A@SE4_>mY;dL>*fYm*@LVN0Wq>`JZx}h z<6>u6-_m{}m3XA{MiZJr`bBo_5jME4hKrgj<79u(DNt(B<2Z}001#bum+?G{EUei2 zCU(8~=ufv7i4>G}Ds&lJ$Tbf3&lAL_3%bC@Qh8A7)WqgW_mfn^pr~_{jtn%C)P4?3 z!Xum|!~L|P?#6)A{p*E)yzk0=T(F^4zX+$iR7mWPIZYAi6qKqnlO9qs)h+I^MQG_W@0qjw-;u8oTcW|}yx zS1>9eJQ;bdLk@FQoEH0}KFNR_^3qLFX|J->CuWKbIKRHE2Y=@^vlh(sk}nsFN?1mw zGi}_xw)TYJc%S!&A(yGKZAkv1O#epARUXyAt`kbd-84xEb<>9uake4UTF2u%VbE!P zz*X-My|`rlqj0K$-&~BC6Ve%mn>2B-4MDj>wkNggfvb5d-8}4~UIE?7OWtR}?9-Q| z5Xo@uEpE4)sItz^6p{stL}ufx7H*6O)nNEot&LDXQ&rG1&$*66(w#g6I|YnAB`7#l z7|gDk+2xg*Xl&(BZKvRV&ZGxD-}O$s-TL<0hb+YKSl*`Jw7s-9K#3nZYReZ)(*}jO z@*mhS>!jm_ z^gG0>w-KJYIf@~qKGq?r&_1Pk?PA^g6b1Eo?!GKm0oUbixTv7h=<0M7lzZsjL`RK`CD@&O%zv~_vGSX;C7znV@}jkvvw*sw;?IvOzNe{ou9sz1 zXULB9d0SD%iCEvY1c&MR_;50st5?cFc(&y6N#>JX%c|P_3#?Dm* literal 0 HcmV?d00001 diff --git a/innovedus_cms/home/static/img/picfrm_b300.png b/innovedus_cms/home/static/img/picfrm_b300.png new file mode 100644 index 0000000000000000000000000000000000000000..d9d2f5aba78a85bf4a89186d829f4e42a0e2f40c GIT binary patch literal 2436 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 kQ~PU?Z?aIhF65C8xG literal 0 HcmV?d00001 diff --git a/innovedus_cms/home/static/img/picfrm_b426.png b/innovedus_cms/home/static/img/picfrm_b426.png new file mode 100644 index 0000000000000000000000000000000000000000..25675e1da8f1b382af61ea4700361c2fd854d0d5 GIT binary patch literal 2720 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 literal 0 HcmV?d00001 diff --git a/innovedus_cms/home/static/img/picfrm_o139.png b/innovedus_cms/home/static/img/picfrm_o139.png new file mode 100644 index 0000000000000000000000000000000000000000..916760fbb95d8b25bebd9e34ccc87f9bc1e1e1e6 GIT binary patch literal 938 zcmeAS@N?(olHy`uVBq!ia0y~yU=#zgjX2nVQR1$T)6dM?r z)-oJ)QdVeah-pZeI5|jwk%dF7ih+^k2@^~AoGDEl3Jx1CFmosE0*h|Za(p&+-Ya-RwIaN98D4pWRZ&`GN& znHH+8_XfI3BkX|FpE*fB=0~{-9&#uw5Z_?noyIHNJ#ShU#}pld-ny^ds}II@Jlv7O zxlWdmMT99#cHb`BOnLKY4)Lx@R~#4`AF(deyTEq3=;myZ^Nhgou!&l*u6|DSM6-xr zkF)ZQUHOy|%B2vnr6Km)@px_Zh`p7wmrpZ}n^r6&#v*Vd{=lldsE_B@H62|(E#X?9 z;cxM$!W=-=4#MYlXC9ReJNU@DJwH#qMzx3&sJvoD!_k6?n;#eb{qwEx^Dq6H>O$Ke zQ^J++r#S+n?Vb3B$htj$az6ja7xpgSu&!GFlbQaxQiU~w4hgdlY+1K|exCJ)(xjJu zf67c}9Fe$J_8Ta}z$pGlV2aXG`Hr9RsDvIg?hoI`;&Van4WSysowB; z4*UD^)~?rYc*N`v=AU|U++%s~P51lhjtq@PtTkSddgAIo=be&|Uvc+mXvXCC?+#CX z==lDD&e0DxEf#ld&e<_>{E27UuQtUjK<)2G8-H#7=xFVdQ I&MBb@0Am+cB>(^b literal 0 HcmV?d00001 diff --git a/innovedus_cms/home/static/img/picfrm_o194.png b/innovedus_cms/home/static/img/picfrm_o194.png new file mode 100644 index 0000000000000000000000000000000000000000..b2f646a7674c7fe1a8f380ab4043f76a2fe75ef3 GIT binary patch literal 2031 zcmXw4dpy(YAOCJ4?guDYrCQ*2tyML2h%xrj*=;Y^klpiH=2Ma@e)rR?pam9poa7AD_v_9=AH;A!a)i4(U<%nN0LG#L2zdxJw4nh2 z3Z*bW0a{`5y;As3FN|5L@Y4q5pg`2;;12+xc zHJg9!p*SLNQatKvq4?N{ezUkOweAZ#C!}6tV;ha}e?bQjIInOxdmnn@K{WoyJasp{ zpxStoEeJVkT}z)ao$Ms@AzbREq=m6poMnzxo+1oID2+nYp2n_rrnNn3&$*FZcy?r~ zTDk!MLs3<0YACsGGwM{D6SiRTMpf~CS^VQ1iQMO43}@rf{qNSxUL4MvXquM-5CddG3q zn%^lVCJxro)?B02%hD0N>$gJyTu3Z6WImlfWGeSqwi}IImAb@ho2(iDj7kG9Wr5c% zfi2DV^H&p9KIb_r5k&}XMIp{%pL2$-w)K;RbGni~h^jolUFwVuOlJJQ--^AIb zAf2wlAfPjYTl-V>FC8d!>C(4s4H9%WS^BS;C~(*slTd|gK~#su`&)B?b>a*WTw+@m zLc78{j>K;|{|^Cx`x=0B%7C!M4&}*x8{^@Zy?9}xNa>iH@}!luI-jGgo(VyyfPM{> zn3de6dI>dVK_b&J{JFXsUH6qWFC;!^AJl6E6U6hvE#(0= zgAJD>+LEUOlRt>WrYI-^;+Ggh9-)Gs)0^TrdTOws&wDE2~o&2*M;V%j0q0+A7ZtZa(? zqb4vi_&oV*cX>XMDR0viltYiRr3G4+6O>mL_WAm5vHxq8Q9)6XsKt898|jm2Xa(JR z7%Gi>B`i7r)KLogt)73G!lL56;~!*%KqNk$54`{Q4iIxM~$RR;4~=BDmpSysY?e!+I3ZV1xH z?1ox6p%IKkLoY58_IWL#^-aJZ^NUb8%xLoM4z33$x4rIlEO&H~6I~IZR~eRW+ju~; z9Kx-3>(Ei0akL>DO4$^t!iG{|Q9N~ZsMtJ!w$SQ(Q;qh?UzkwUefwcZBf4gJ+UCIe z!96HQ_myff$Uo;Chi_DADe?x00*j%S=k~rnKX5i=D(&U1FEe+elB1SlxmqJdDTP}? zeB+2kCDNhsUbhYeih%otk*M2sF#ImZcElptcwK3IOt#2yQ&|>lY&Pus(eOT&YddJ{ z@lYusoS@S;CtN2bT+14QUKc`WF=0G?;^V%h`ozYI6kd*~WNFPSRG33s$=6t9v$y?~ zIKHiMoqGR_)?X*|XgT mPkTE)S{~lR>JsmLx}sTxuM(Pezmorq0rFuFV!dq literal 0 HcmV?d00001 diff --git a/innovedus_cms/home/static/img/picfrm_o300.png b/innovedus_cms/home/static/img/picfrm_o300.png new file mode 100644 index 0000000000000000000000000000000000000000..045ef82f36bb4b5cc22ad897000783c9ae4abaa4 GIT binary patch literal 2499 zcmX|Dc|26z8^1FqX2#m6x58-b`>!G-*+OFrA(SPlDKrzAEQQR-8j(n{j4dHsS%zd8 zJ5iM7#ng;?i3ZVP3FW|xw*x$ysH;m)}{|BV54uHm6kt8f57shOJ?IRwKNvd`HuPe^pvK3#TD;PK6-evwu1 zN(2;Uf4-3IREQ`@H(K+Ix7livGYF2m)-Y$fo;uo}?x@kqmD3>t`0NG6K#4%pUnEX~p zWf21_j7wl%<><;t^Se8AMYtBWzHo%|GL5adVjVN!9Xoa`bYmE>ae9i3ZT(_&_dSR(=w7`$BOEG#EILRdC0R) z;?J^DC%6Z6W>p`hHhqXuSpsTu){>4PL>fJs6{XE_*<#A6i!}r}=NYN>PEq zRh|56MCCl-*@aUkA5PT8$Tr4X%XM*MZO9P8F+Lq~hQS+FH<#Ia`t~BL1p9KWbGTvT z*ieAdKjg;ml8zuUuXGw|GOO+Iu~cYd3J-|O=XE$)#zXXVsTI*i?8O7_ysY}RWB=b5 zm)ou=Cdt27nHWJv^UHK>N6rcqFe9Rs(24Lo6Wz+m;y-fMIzOy)_=^xI4}eg{8B$Ke zE;g7++cv2hzf*kDH<4XxSxxBGi5jx4kA^T5K#i@(yc|*~=0vud&xLol*3MrE_jmhQ zWFCyC=P8;@Ktus`!DBpqI9w2G^6#M>_u%HE571RzGrRJaniIMs^tn0>#{w$Oni>58 zex1ZtL0Q7Sh};0NjfK*J$|7-bj6gzubAnw?;_kN`F4R1@*>TW0$h2qDL(pA=&nRtUh2@RRpBQjrRk=-bpl2F z*ve#*9|ErdfyRO|e#1H789nM|0V@lk8-^k4oe52MW7OIop&wR)>G?71_#XcnD&#s` zl}5EdrjHczr!a?8X8SAb9;r39?{d-Iy7iuAUgA=mPDbx(>wFCr!$DJ!1)BHa^v?Ph zaUK2{5tA_rF1M=-Rvt9igyxj{tx4wCdtrWHGu|N})?!wm?5!#1vgiD(1lotnooGcX z)m>NXgko~&CKy(Cm7r(q`O*54+gvKtbufXR4!BknD;!NtCD>B8WpLh6h>oGbO=D9XTrgeO)3Beys#=dx!2eq8%0veRBw;IiCa)&6^E6O z(M)dr?6znNL3`hU0`ZU{L?Z6)JG^wsfxMr`eBh+#Mn$UL%ZVx3zIz%#^=U9>vA+ZPv5+7k^jv_!oEf)xukGo3wmqf(3EHLlKc0_^O9h?GB~2a z4P#3p9?w5KU$?KzkV#1SB~;iYFV}ZZTk@j#cjT#Uk@$DUvh!Pn6iAYARTqys-Czx$ zi0B;h&V$0kuB)%F4b}Zvu){Fz8?>qf%ZE}MqT=xd#l~$LZDU^CQ!@82M9p_=D}j;B zz|ZCsf=YjBl-rL2-^yQxxOmE$9S!MWxm8~Wo>s>#$k!Y6Hxxx4Z6%{Cjagl4=c5mz zKmd-A*9coR7kEb0cMMtV`yVrk{Vjiz%J?S}BW#+ur+r8*Ww>Gm zo4Lp5QY{_4b?2XwJ`Nm7I;A?RdJHG9ed~E6K9P(>RY-yNZ4Q7)@&$2q{EOGS8x?k2 zzgk1VHjY_c*O&DwW*l%zlM1@WF|?)UU2d403G5YwGV)m(qW{wnV^cEpVU6061=~v$ zf^UtEmSf(A^<4FrXjkoYMt>&8)Xe$qfch@x9*Gq5;H^_uA3to)h+1x*8x+181DzNc z)Y$1#MX_e$Vy9T_BfV)}XL{|(k(+CX;R@94ne_J z>y+ce=l3~m8rfyZ{KYNMGC1s|Ct=G^>xE-x;D|EG(l4;Ad_1CT@xr0ru#kbZ!S`79 z>a9aqrZ&Spz3!D()r5WBA4EcN@wj^0FND!jAoqyKe#_|z9S>9WYiYO}QNg}*YkAG! zqfl}h*Ii|KkK0PqbJXv{g`?}$zp{h8x4Z)Pb#3v@R$4zHhbM4wA5#>wd1eyJ6*|)ucfnkR0x~ zy(b6+ApvtVYzJ`dgYO6eZs2%N>@iUDkcI$wun+deop*Hw=>u~Z2%HlJf{ILlp#}^P zNP-FmNdR}S=p7aE?^g(kD)H|egccb_q@8gBfut}vdt0w$@B-i8bJ9_R729;dx_5we zHiVNv*=>r2>wa13)aliQ9R`1dvZRc0$%fdNs2S}#lY2d9t(tEBhI}WKR#(Sfju7Y~ z>HfnBE&CUoHj7TCAK%zMoo^EvR;U7jA@!)K3wLvc6!qGuLmRKgmr03#JbA8mntqX4 z*VeWDBdB)HU^QesJ+u%6fx+PhGwdX6G|Uu_36HkW^zSa+L)hf@7R?FVtBf`$TW+m)C?3}N|chA$;kOftvyYN5l z4+x-Qs15*B$?RSjPx?y*vC5J1^}M-8iVt4R3$aidEZ#;4Q&{YbZ&noyLM)5*dnk-= z1E|^bsia1%I9Tdc5ZHg0*+j>_07bKh%qQmZVPCgSQTCv_<)^d(l&}sx3~TQ5rI0tB z5X(k=5ADdY$1d#r(b}+YX9`4&k})1&2(v1V9!@7qJsthag`b#^hJj$KyM1JL0vNq> zff#(=jfXvxO!z+j?efrAY+iBLnH-3<-{8RxPI(!Np!+`fX{`sYDr$kT%-|~)R z0T|dgv;c@KerUh8o`FIsL3K35Z*nO`5j%)}ttq08nhva8J)*1erw>$Ne?#AQX0i!MgP061vp_5z!0096E=30 z(l5ECo2wt^<|-b!-OpqWSaoq&2dz(SiGox(po3xIujV_t?lNY$ zzYX+d|MzCuQV)V6tFuuhFA555E(u%bS$)q-LBt?WWe3NdicWpK>e%phs(hh_cVzb; z&B31w@0Qdr~?7DxAHG&CHaFYAD> z3#OWQ4i5hHllBF@%91wy%k#JGZ$ezdGDT2kT0r2+9^GmFJ@zev>fHptl!^&%8=aAY zt+f1IO5-LP75MzZXe>=6Aq@2}kSp%t(mbLbc&}3l!6W0>m}l4O8)iURNyT``<<9Lk z<$sE*0?X=WuuvJvOdi`k@YQ2k=VMilK4XZCS={HQX?iw}%{9pil+wj*(`3b-&xkx* zR^}m2VxpMm%IjLav-ii3EjhKDThbV0c6UTsiF(rs_78h`aWxr=swt*KH3&&<|LWuc zwOvfQ(vV8yUr4mrbQkgymF7Ka9^Ba{ZWDMYDV@*}_t00XR_J{cx(B> z>SLH=m>dsNOUjplwbqm>$Gt`{6SH!o5?#P8&yh!_l*qmAKBi-Mk$*4;qr(5@(sMQ_s@YiO}?#x z93V&J#!Zcw(;_(urB)Vh%#yz!8X2|*JMxI&&YyA7?3QBZH!p8YJvK=qHu|T$C06i% zIGYF9i3_1m#-+jQ1$a8LMvIZ9c6s!!Dixr|_LWZ@lb6Y20=;kKb$UEuw^y@%VICNv=)Oo1!?^|hps!r|p3e1Ug{Zun zv?Xr1hOPfRAjwfV_qC(@ZsaLST~QpV#>M%YPB4Ekw|}$tU5p5P zGPM;Tlq4lx!SVSfM>p22ji8!0$(>F~%ymhll&wP^JzGoh?)0?hNtw&@e%Zun6)PE2 zJ7B#g1M^Fu6^pzWt#lMhN?J(fZSc3gO#XPPkc@=j@kQ@w;Z+Vrco2PgzPaVn=bG3x zYqLWp`QFz6;h4Hmk7^c1$o}XtI5=sHE zYLBO${T97aAM~iLG~Km=tt^x2tfjD5qz#xCLSeektab9d`K6@#7v$&^)1?KzI^XQl z<@X7yM|ag#vbJ6BxAoUTx+l=c&fn_RB-!*8j&P&1izp%H533hoI3(wKXU$M>NVr8t zOW1VXOG69}dB;?*3P^TS+V1KZS6uF+`-5}g_=DAb*ZYZz78O^yEW+O*@RFBpA1vN! zhztA}IQ5j#bw{I#Be8O$9P3XzNRQi_!q(g`a;W1ZQ&azh9DTHs`~7Lgwl?#n$Ci9>LNmAAr%_h)Pla=Evu{2Y GkokY@b6Y_G literal 0 HcmV?d00001 diff --git a/innovedus_cms/home/static/img/picfrm_o426.png b/innovedus_cms/home/static/img/picfrm_o426.png new file mode 100644 index 0000000000000000000000000000000000000000..48ca2c7fe395486f25e279122030583b2081e4fe GIT binary patch literal 2784 zcmYjTc|25m8=n~?W2vSnBAKjn8KE?~mfO%^rqI|)xX_PLhGAlo3zmC91ng2|ec zY;z575|PN**DEi{l65Q%?>Wckem?IXXU^|;&hvYo@AvsW&vPb`VrINwNKyy}gY74i zh!!vypB1n^joJtNI>=|o12=e}g|Pvw?9=gYz=r5%M|L+gg((AT6bv5k0Yic&z$FP> zz;Ol~#s}Qt;4=o|-?w~L48DKYu+yO7rQD287)A-f?zz@D%IQVPR_~3R1wM_R!S9izXwk`~ zF+ue)lC|j2nf35wG2Y}-XTh$AwX72gLxUkuxT^&1(!Rp^qWG@em#>aAyTA23pFQtT z-jH3y<8Axri%PB7a&F&91xzsv74-K%jeYkHW{RT|!(tjYx0h9Y-`-&9jU>sEeae3q z1ndC6TL>|$t%B7A@EFC(IkJYQ`pX!uAvWPfc)m)*^(HminKp2$8AomZBEO7~Ze!NKEj zQ@D__sp*yh4A^27oK1qf7ei+=J%d_OM#KzqYF|gTbPR~>nW_P%YIw<$Ach;L+`Rln z&8>&M#@YDw>WgPhJheci?2kh z8F3qLLG;!6vCJmj#bim~kR5|2Pvti^k;+UmZ$ST=TlHuU6KVs<#b#UA0njn~68k7# zxTf%%uHM*5M$`AqE)@9KIS$6p5jk$N$4;0uy_pkv_suQVrO0>8F~h{0V6KpDfS-Fu zT%pKt->6U^mJSU|5zMqz%P}7F|>#F$C>G1go7BLid?}m42RNF0L_OPu-(W zU<3Tyj8pJX$`ttJ$zOJyT8-X#FCdh?TNY&0AZd`w!W4?N;&VQTs6iA?k=3U%?BgUQ za#S=~qkN)5270k6NTGCeX6u2-qlDVeE3#0voZs{+EYaMgDp$R|YA^(3F*GPK29wW} zcaFjKs!-Ltt9O5QbN%X?{}#`PmRP$Ot!V_l_XBKyN<(??!D6IKG4w@&hWZ4TK!&wd z4UJCv8j%uH=mo`wA+b!vxmJ-wz%jc!v^J`^?Q<(MbwbnlAPiB*edU4z|{G}WJ@9&3i@q0~$O++u@2 z9n%1-UI0$^*4@0@d)MT+Z|b_6$?J!v@Z*j%n~x-@m(OKG!uQ5FPeQkpAz3O?;E(fLgY9H%ks**sn{=`IZ_$DTaIV1(&TXQYFqgpZ~X&nS*)PId-Mr4865n=kf9{JB_l ztqN*i0!?6hSqrK{h5{h5rfYX#G`2wc#eQapcA)3@>$uR5^Lh@RQIes&N~ovD{RRDE zXZ`ao8^r+qiX#yY1MS-SVD%sUhow8|IkI-VDVH8K;(=l=@gStfNfe-mU)C`>02`%} zmf+zv|8&6HC#zbUTIrxsrzKOw(z%@DfV8qY^ZYDegQgSO#f`9q+5X_gup7UPaKGzTV0dhLto6r+z^p*}nADI>=~ zKO)9A@Y=8d0g@1HUa3p*<~f1gK<+=m(eXTY4!wJ33n&wFKG7X}l3H@k(-- zCi=vB&SJX-R2tiJP3N}POl9w>5s45`gF~NF$l^-}o<2bM;BSXtbmfA#|@0d@_ImP!kH9gRJ;NIfm}o3Vd9-ivooMLD7lY-FDF zdMj9%z~De#%^!B>=eT7Y`f?2MD_2AmAjrgES~=pRShKPmd`?PdpUiBCXpUp4#9Hr= zr8v7WqJ^5Pv_%AzI+4rnNKLZWpSt__o5;ZhTmTy^-C=(3JCqvpmrHo~d=9 zphSfgqD~q@T4lOFg9FL@u)kQh<*#d) zzfYe^Xxa&RJ2^C3Ig)LYT${5@(k`2}XB-2ayyUO#+-djBuP`50W)2TFGM3{1&&l{4 z&bls`wDzP3=xvbCEEdiwCjlw|!F@G1Q`>+Cpm4nY)e_!jP7VM5J zyMXx647O>8Ij50((8e1TJHQ-Fy?l#XQoK`mL`(SDvRBAh#g%mXP1+1NM+ppkly~;y z(v++rmeZ(NfN)Xq{>pEW;@wp>My;IFJJ6^{k)3s0nX?~E&y;B?vVOois$7!8S{BM2 zsxRU9`XLRm{!fM~ zI4Tr%#~V0EQI>)no;)rQ-gYZK+Vg~JvIh_S7GDZBOiy^Z1~is*{wMxdpX!@&9%L{7<2?7^xx+b5{bI~~`+56# z5XKKbt@l773BWE8tuTJnGl{ rS7QEk*+5y@YjeS)iQDPz)9O0{s+4lG8v4ck;J-_8$UCK-%v88ONN^vYjnGz5XK-)vV^i{shezB1~In5m2}BgjLI@3*|J|- z%W#YQJ_s3&tRwPsZIiK26MpBMx%ZEA&il^1@ArA0@AG}0_x;1#iYO|A6#)Q1)a=4J zTL2Jf2ge#{A@Ke1M*U&%g9x!D8UxjRGV|c%oTsDNO-oDQI5t`S90y8jYpib z?DVW4WXD?w#L9J=A@Nci04@t~#kt8XuSdGO&5n{@k49qVF7CaC?8miV6-2d%%%0YT z!5lBfzq#rV+aEf;JX9qw#PnkEF2Vr+lfwZ8Nfo-n@SZ`DJDV^1yABkaA#Ph-tk=Gk zdj<|C!<+!*nGfpoLpy-%GHr69)dX+w)LvC1-R(FAkdd#4F~HoA7M}sf>+$vK_!m38 zncdga?Xpkoj{A;={eG0SL!fT}0#Qrj2TAadk1sCX9Zz5|0G`KFfx$nZu+UdPEC&=L zAD#?d#<`CalPKi}zO~?+#36uq9~7#cQ6^;q2jQ!T8P)AMnp$k`nVDun)~lv1^F6JI z16nE`?eZr9$Wt6Uc0F+h-L{_Va_D;M<$4&uLIfRGnLN)Y6oOi_h(4XHq1a(7;ggjX z1`+Vco&e}q<&pS6LP(DN*H&dZwo^wX<;tDSvgJ{jK7t(r5oC92hd=R0z1>ZajE!lj zoIOyDOsc=_z-xAgz#Q>l3Rm+ltI0!5Ae~SFnw(E%0#?Fx(@pFBDR9WW>R?(P51?R! z9$b{I>eDf+=+m}!;o;5K()E?3p&Wb2fhpb>F)E`4bNQ&0;JwA0n22B(b&va1r6--j zu_`sr!!~ihT7VH)m`cMd%Np7XmuyNyXz1(4FMjsSWHY2l>X2s^khlu8iI#kAC}h+u zeS|a`y}Kc6_*G{h3sZ^yVwcw1YXmLW_z43HZ;iX~CDx*Z5qCFeW9oRCG;lwJ*-+NL zGjfx}O7o@%YaP>dCa5vw@{ihl27d?hf}~dSqiYp>OR4T~m|qCA$;vH4ORK5*xwMW? zH?)V_=jaS$d*K|1E8_sVUR0D4%-Pm?oDelX&olkWhwQz)6V>V|Im!r*Q_s44w&|0} zz6%h|Dk7lBkkAN6zzHwSzWhG6;~Asbx20tn`91Q8z_hj(tM_~3@M5z<#}m7cWN_us zWdd-ue=Xn>ylLupf_Il%rJf}AW~9p*?3jrXd9%%3)=+xTtyNWUK~88X`#)INokmHJ zh+QJUSF0P2YS8Vu!rhr!ULXHf%k9>9eUyo}bWub~Mz{gg3!~$5mhU@@@fCFe_h$&zz&Fb?QN4fGerxA@r}{_n zpE;E zwo_pCw~=?7VUi1vB8&1t{oe!YQbm($>KmSAitX~(gX}BB-@Rn9gM%Cc;v6V|}zf|h_5U%PVnxOHkgK0hQc+vOLQsFo-|5eTaJzH4l z?(OHiKihFK#8nV4x+J#h^B8VYR5RS++1Qy0Vk#@g$A)SAk44D~iV5{fbdgT6cUxD= zPlJNg&A_vMJ9(qhFB)G87eK8HvPkFFouDO=mEb(9O1L8;wb6IN+E^2BicCr@FDucp zb?L!t&(c=zefa6gu(Pb#Z-s3R*Y3e3V=Wt>@O~<+$U1bP;ij4*ob1zwk7OAv+~CIS zfx0UfcTatg-fSG0K5E}+pwuy%`L&_q3DgE1!{P_5>b@23e=vyXq10EULy<0=$Y@@& zIr#ii^zC;MWF}Z8Yj>B$10#og!;>*?mih!DB)77Jb|dGnH~_EuvvtyGmt*`+LL*p2 zZ|DzKc&SEdZ63kSE`mw9o<9@%YU|7A7`_f0F*@c*U345nxuhVcyfdO1F3+-j!sq_% zZC_?RQGCF#*G@upS#8(d)J_e(ID9$Z2C?k_T^cAvira-4P<0f~@YthM6@@VpMa zu5O3i53Jzc5QdDhXQpk;ml5}F`40i!LZGAS^!8a+;vAxO&QoG<$Qc3q*`k6m=AIe; z#9EIIP$OMIq*yjGUANqim=zN4!voY=C zHvg!UaPr7l4p#0G84PKS08bl(3a{2~`4TZefhV(AB>>u?+TyZ0-R_gNPX5BS1@g{x zj3qWr=OnC8@|!xw(K>$nkMQba?yjy;5$51=EY@m69jQi&J%~BZB?l~uf*8K?K`Rq& zLUqd}vu^w17)-ao*{*FAK_(jB1=BJvlsS*+P#pN>ZI-(_Jpf zcK81{8`EoU{Nk~qB~&(v2X+45c&YmQ%_bHLl)4}GGu+{hFAogA*5a2{)XAXGp?--a zusXtXwS_OEquBng~oaq$P|MrJJZp9P*|$ za{0`l$GzffN{-$%=zcgC)bqhcTZLEzz75$~;#vZ@_AEvR( AlK=n! literal 0 HcmV?d00001 diff --git a/innovedus_cms/home/static/js/block_list.js b/innovedus_cms/home/static/js/block_list.js new file mode 100644 index 0000000..edd7129 --- /dev/null +++ b/innovedus_cms/home/static/js/block_list.js @@ -0,0 +1,45 @@ +(function () { + function initBlockList(block) { + var list = block.querySelector('.block-list'); + var left = block.querySelector('[data-dir="left"]'); + var right = block.querySelector('[data-dir="right"]'); + + if (!list || !left || !right) { + return; + } + + function getScrollAmount() { + return Math.max(200, Math.floor(list.clientWidth * 0.8)); + } + + function updateArrows() { + var maxScroll = list.scrollWidth - list.clientWidth; + var hasOverflow = maxScroll > 1; + var atStart = list.scrollLeft <= 1; + var atEnd = list.scrollLeft >= maxScroll - 1; + + left.classList.toggle('is-hidden', !hasOverflow || atStart); + right.classList.toggle('is-hidden', !hasOverflow || atEnd); + } + + left.addEventListener('click', function () { + list.scrollBy({ left: -getScrollAmount(), behavior: 'smooth' }); + }); + + right.addEventListener('click', function () { + list.scrollBy({ left: getScrollAmount(), behavior: 'smooth' }); + }); + + list.addEventListener('scroll', function () { + window.requestAnimationFrame(updateArrows); + }); + + window.addEventListener('resize', updateArrows); + updateArrows(); + } + + document.addEventListener('DOMContentLoaded', function () { + var blocks = document.querySelectorAll('[data-block-list]'); + blocks.forEach(initBlockList); + }); +})(); diff --git a/innovedus_cms/home/static/js/horizontal_list.js b/innovedus_cms/home/static/js/horizontal_list.js new file mode 100644 index 0000000..3a52dfb --- /dev/null +++ b/innovedus_cms/home/static/js/horizontal_list.js @@ -0,0 +1,45 @@ +(function () { + function initHorizontalList(block) { + var list = block.querySelector('.horizontal-list'); + var left = block.querySelector('[data-dir="left"]'); + var right = block.querySelector('[data-dir="right"]'); + + if (!list || !left || !right) { + return; + } + + function getScrollAmount() { + return Math.max(200, Math.floor(list.clientWidth * 0.8)); + } + + function updateArrows() { + var maxScroll = list.scrollWidth - list.clientWidth; + var hasOverflow = maxScroll > 1; + var atStart = list.scrollLeft <= 1; + var atEnd = list.scrollLeft >= maxScroll - 1; + + left.classList.toggle('is-hidden', !hasOverflow || atStart); + right.classList.toggle('is-hidden', !hasOverflow || atEnd); + } + + left.addEventListener('click', function () { + list.scrollBy({ left: -getScrollAmount(), behavior: 'smooth' }); + }); + + right.addEventListener('click', function () { + list.scrollBy({ left: getScrollAmount(), behavior: 'smooth' }); + }); + + list.addEventListener('scroll', function () { + window.requestAnimationFrame(updateArrows); + }); + + window.addEventListener('resize', updateArrows); + updateArrows(); + } + + document.addEventListener('DOMContentLoaded', function () { + var blocks = document.querySelectorAll('[data-horizontal-list]'); + blocks.forEach(initHorizontalList); + }); +})(); diff --git a/innovedus_cms/home/templates/home/home_page.html b/innovedus_cms/home/templates/home/home_page.html index 718cf8d..6af0092 100644 --- a/innovedus_cms/home/templates/home/home_page.html +++ b/innovedus_cms/home/templates/home/home_page.html @@ -1,20 +1,33 @@ {% extends "base.html" %} +{% load static %} {% block body_class %}template-homepage{% endblock %} +{% block extra_css %} + + + + +{% endblock %} {% block content %} -{% with top_section=sections.top_section %} -

    - 最新文章 -

    - {% for section in top_section %} - {% if section.layout == "block" %} - {% include "home/includes/block_list.html" with items=section.items %} - {% elif section.layout == "horizon" %} - {% include "home/includes/horizontal_list.html" with items=section.items %} - {% endif %} - {% endfor %} -{% endwith %} +
    +
    +
    + {% include "base/includes/home_banner.html" %} + {% with top_section=sections.top_section %} + {% for section in top_section %} + {% include "home/includes/news_list.html" with section=section %} + {% endfor %} + {% endwith %} +
    +
    +
    - {% for section in sections.category_sections %} - {% include "home/includes/category_session.html" with section=section %} - {% endfor %} -{% endblock content %} \ No newline at end of file +
    + {% for section in sections.category_sections %} + {% include "home/includes/category_session.html" with section=section %} + {% endfor %} +
    +{% endblock content %} +{% block extra_js %} + + +{% endblock %} diff --git a/innovedus_cms/home/templates/home/includes/block_list.html b/innovedus_cms/home/templates/home/includes/block_list.html index 1bd06ed..932e07b 100644 --- a/innovedus_cms/home/templates/home/includes/block_list.html +++ b/innovedus_cms/home/templates/home/includes/block_list.html @@ -1,19 +1,23 @@ {% load wagtailimages_tags static %} - +
    + + + +
    diff --git a/innovedus_cms/home/templates/home/includes/category_session.html b/innovedus_cms/home/templates/home/includes/category_session.html index e12ec5d..177941d 100644 --- a/innovedus_cms/home/templates/home/includes/category_session.html +++ b/innovedus_cms/home/templates/home/includes/category_session.html @@ -1,13 +1,15 @@ {% load wagtailimages_tags static %}
    -

    - {% if section.url %} - {{ section.title }} - {% else %} - {{ section.title }} - {% endif %} -

    +
    +
    {{ section.title }}
    + + 查看全部 +
    {% if section.layout == "block" %} {% include "home/includes/block_list.html" with items=section.items %} {% elif section.layout == "horizon" %} diff --git a/innovedus_cms/home/templates/home/includes/horizontal_list.html b/innovedus_cms/home/templates/home/includes/horizontal_list.html index 0775831..8f6c7f6 100644 --- a/innovedus_cms/home/templates/home/includes/horizontal_list.html +++ b/innovedus_cms/home/templates/home/includes/horizontal_list.html @@ -1,19 +1,32 @@ {% load wagtailimages_tags static %} - +
    + + + +
    diff --git a/innovedus_cms/home/templates/home/includes/news_list.html b/innovedus_cms/home/templates/home/includes/news_list.html new file mode 100644 index 0000000..6479bb0 --- /dev/null +++ b/innovedus_cms/home/templates/home/includes/news_list.html @@ -0,0 +1,82 @@ +{% load wagtailimages_tags static %} + +
    +
    +
    +
    +
    本日頭條
    + + 查看全部 +
    + {% with first_article=section.items|first %} + {% if first_article %} +
    + {{ first_article.title }} +
    + {% endif %} + {% endwith %} +
    + {% with first_article=section.items|first %} + {% if first_article %} +
    + {{ first_article.date|date:"Y.m.d" }} +
    + + {% if first_article.intro %} +
    + {{ first_article.intro }} +
    + {% endif %} +
    + {{ first_article.body_search_text|truncatechars:120 }} +
    + {% else %} + 目前沒有文章 + {% endif %} + {% endwith %} +
    + + {% if section.items|length >= 2 %} +
    + + +
    +
    更多頭條
    + {% for article in section.items|slice:"4:8" %} + + {{ article.title }} + + {% endfor %} +
    +
    + {% endif %} +
    diff --git a/innovedus_cms/mysite/static/css/mysite.css b/innovedus_cms/mysite/static/css/mysite.css index e69de29..fde066a 100644 --- a/innovedus_cms/mysite/static/css/mysite.css +++ b/innovedus_cms/mysite/static/css/mysite.css @@ -0,0 +1,288 @@ +a { + text-decoration: none; +} + +.site-header { + position: relative; + z-index: 10; +} + +.header-inner { + display: flex; + align-items: center; + gap: 24px; + padding: 30px 105px; +} + +.logo { + display: inline-flex; + align-items: center; +} + +.logo--light { + display: none; +} + +.logo--dark { + display: inline-flex; +} + +.template-homepage .logo--light { + display: inline-flex; +} + +.template-homepage .logo--dark { + display: none; +} + +.main-nav { + flex: 1; +} + +.main-menu { + display: flex; + align-items: center; + gap: 24px; + list-style: none; + margin: 0; + padding: 0; +} + +.menu-item { + position: relative; +} + +.main-menu-link { + display: inline-block; + padding: 12px 4px; + font-variation-settings: normal; + color: #0e1b42; + font-family: "Inter:Regular", "Noto Sans JP:Regular", sans-serif; + word-break: break-word; + font-weight: 400; + font-style: normal; + font-size: 14px; + letter-spacing: 0px; + line-height: normal; + --letter-spacing: 0px; +} + +.submenu { + position: absolute; + top: 100%; + left: 50%; + transform: translateX(-50%); + margin-top: -2px; + min-width: 220px; + list-style: none; + padding: 14px 0; + background: #fff; + border-radius: 16px; + box-shadow: 0 12px 30px rgba(0, 0, 0, 0.18); + opacity: 0; + visibility: hidden; + pointer-events: none; + transition: opacity 160ms ease, transform 160ms ease; +} + +.menu-item:hover .submenu, +.menu-item:focus-within .submenu { + opacity: 1; + visibility: visible; + pointer-events: auto; + transform: translateX(-50%) translateY(2px); +} + +.submenu-item a { + display: block; + font-variation-settings: normal; + color: #0e1b42; + font-family: "Inter:Regular", "Noto Sans JP:Regular", sans-serif; + word-break: break-word; + font-weight: 400; + font-style: normal; + font-size: 14px; + letter-spacing: 0px; + line-height: normal; + --letter-spacing: 0px; + padding: 10px 18px; +} + +.submenu-item a:hover, +.submenu-item a:focus { + background: rgba(0, 0, 0, 0.05); +} + + +.header-search .search-input { + display: flex; + align-items: center; + border-style: solid; + /* border-color: #0e1b42; */ + border-width: 1px; +} + +.header-search .search-icon { + display: inline-flex; + align-items: center; + justify-content: center; + width: 28px; + height: 28px; + background: none; + border: 0; + padding: 0; + cursor: pointer; + color: #1b2140; /* icon 顏色 */ +} + +.header-search .search-icon svg { + width: 20px; + height: 20px; +} + +.header-search input[type="search"] { + border: 0; + background: transparent; + outline: none; + color: #fff; + width: 160px; /* 依需要調 */ +} + +@layer figreset { + :root { + font-family: var( --default-font-family,ui-sans-serif,system-ui,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol','Noto Color Emoji' ) + } + + html { + -webkit-text-size-adjust: 100%; + -webkit-tap-highlight-color: transparent; + -webkit-font-smoothing: antialiased; + width: 100% + } + + body { + margin: 0; + width: 100% + } + + body: has([data-page-overflowx='hidden']) { + overflow-x:hidden + } + + body: has([data-page-overflowx='auto']) { + overflow-x:auto + } +} + +footer { + background: #0e1b42; + color: #ffffff; + padding: 24px 0; + font-size: 14px; + display: flex; + justify-content: center; + gap: 48px; + align-items: flex-start; + text-align: left; +} + +footer .company-info { + max-width: 300px; + margin: 0; + padding: 0 16px; +} + +footer .copyright img { + margin: 10px 0; + width: 265px; + height: 37px; +} + +footer .copyright p { + font-size: 10px; + text-align: left; + margin: 0; +} + +.footer-socials { + display: flex; + /* justify-content: center; */ + gap: 12px; + margin: 16px 0; + flex-wrap: wrap; +} + +.footer-socials .icon { + width: 32px; + height: 32px; + --fill-0: #ffffff; +} + +.footer-socials .icon circle { + fill: #ffffff; + fill-opacity: 0.85; +} + +.footer-socials .icon .icon-cutout { + fill: #0e1b42; +} + +footer .footer-links { + padding: 0 16px; +} + +footer .footer-links li { + list-style-type: none; +} + +footer .footer-links a { + font-size: 14px; + color: #ffffff; +} + +footer .footer-divider { + align-self: stretch; + display: flex; + align-items: center; + padding: 0 8px; + width: 1px; + border-left: 1px solid #ffffff; +} + +footer .footer-sections { + display: flex; + gap: 32px; + flex-wrap: wrap; +} + +@media (max-width: 768px) { + footer { + flex-direction: column; + align-items: center; + text-align: center; + } + + footer .company-info, + footer .footer-links { + max-width: 100%; + } + + .footer-socials { + justify-content: center; + } + + footer .footer-sections { + justify-content: center; + } + + footer .footer-divider { + width: 100%; + padding: 12px 0; + border-left: 0; + border-top: 1px solid #ffffff; + } + + footer .copyright p { + text-align: center; + } +} diff --git a/innovedus_cms/mysite/templates/includes/footer.html b/innovedus_cms/mysite/templates/includes/footer.html index ee5cc23..e01dbe0 100644 --- a/innovedus_cms/mysite/templates/includes/footer.html +++ b/innovedus_cms/mysite/templates/includes/footer.html @@ -1,32 +1,73 @@ {% load navigation_tags %} \ No newline at end of file + + + + + + diff --git a/innovedus_cms/mysite/templates/includes/header.html b/innovedus_cms/mysite/templates/includes/header.html index c2f028d..febedd7 100644 --- a/innovedus_cms/mysite/templates/includes/header.html +++ b/innovedus_cms/mysite/templates/includes/header.html @@ -1,45 +1,83 @@ {% load wagtailsettings_tags wagtailimages_tags %} {% get_settings use_default_site=True as settings %} - -- 2.47.2 From 4e642872b98c02af83a98de4fdcb94a52a9ba930 Mon Sep 17 00:00:00 2001 From: Warren Chen Date: Wed, 21 Jan 2026 16:34:57 +0900 Subject: [PATCH 18/18] - Complete CSS for category page --- innovedus_cms/home/models.py | 14 +- innovedus_cms/home/static/css/block_list.css | 323 ++++++++++-- innovedus_cms/home/static/css/category.css | 70 +++ innovedus_cms/home/static/css/home.css | 44 +- .../home/static/css/horizontal_list.css | 6 +- innovedus_cms/home/static/css/news_list.css | 78 ++- .../home/templates/home/category_page.html | 10 +- .../home/templates/home/home_page.html | 2 +- .../templates/home/includes/block_list.html | 85 +++- .../home/includes/category_session.html | 2 +- .../templates/home/includes/news_list.html | 2 +- innovedus_cms/mysite/settings/base.py | 6 +- innovedus_cms/mysite/static/css/mysite.css | 469 ++++++++++-------- 13 files changed, 732 insertions(+), 379 deletions(-) create mode 100644 innovedus_cms/home/static/css/category.css diff --git a/innovedus_cms/home/models.py b/innovedus_cms/home/models.py index 606ea2a..ca613de 100644 --- a/innovedus_cms/home/models.py +++ b/innovedus_cms/home/models.py @@ -19,8 +19,8 @@ def _get_env_int(name, default): return default NEWS_SIZE = _get_env_int("HOMEPAGE_NEWS_SIZE", 8) # Default to 8 articles in news layouts -BLOCK_SIZE = _get_env_int("HOMEPAGE_BLOCK_SIZE", 5) # Default to 5 articles in block layout -HORIZON_SIZE = _get_env_int("HOMEPAGE_HORIZON_SIZE", 4) # Default to 8 articles in horizon layout +BLOCK_SIZE = _get_env_int("HOMEPAGE_BLOCK_SIZE", 7) # Default to 7 articles in block layout +HORIZON_SIZE = _get_env_int("HOMEPAGE_HORIZON_SIZE", 4) # Default to 4 articles in horizon layout PAGE_SIZE = _get_env_int("HOMEPAGE_PAGE_SIZE", 10) # Default to 10 articles per page for pagination # Mixin for Category-related functionality @@ -37,9 +37,9 @@ class CategoryMixin: "title": category.title, "items": ArticlePage.objects.child_of(category) .live() - .order_by("-date", "-id")[:HORIZON_SIZE], + .order_by("-date", "-id")[:BLOCK_SIZE], "url": category.url, - "layout": "horizon", + "layout": "block", } ) else: @@ -224,9 +224,9 @@ class CategoryPage(Page, CategoryMixin): def get_context(self, request): context = super().get_context(request) context["category_sections"] = self.build_category_blocks(request) - breadcrumbs, site_root = self.build_breadcrumbs() - context["breadcrumbs"] = breadcrumbs - context["breadcrumb_root"] = site_root + # breadcrumbs, site_root = self.build_breadcrumbs() + # context["breadcrumbs"] = breadcrumbs + # context["breadcrumb_root"] = site_root return context diff --git a/innovedus_cms/home/static/css/block_list.css b/innovedus_cms/home/static/css/block_list.css index a29d2e2..c1f2a64 100644 --- a/innovedus_cms/home/static/css/block_list.css +++ b/innovedus_cms/home/static/css/block_list.css @@ -2,78 +2,299 @@ position: relative; } -.block-list-arrow { +.block-first-article { + display: grid; + grid-template-columns: 480px 1fr; + grid-template-areas: + "image content"; + gap: 16px 40px; + align-items: start; +} + +.block-first-article .first-article-image { + grid-area: image; + position: relative; + border-radius: 10px; + overflow: hidden; + width: 480px; +} + +.block-first-article .first-article-image img{ + display: block; + width:480px; + height:293px; + object-fit:cover; + border-radius: 10px; +} + +.block-first-article .first-article-content { + grid-area: content; + display: flex; + flex-direction: column; + gap: 16px; + max-height: 293px; + overflow: hidden; + position: relative; +} + +.block-first-article .first-article-title { + font-size: 40px; + font-weight: 400; + color: #000000; + margin: 0; + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + overflow: hidden; + flex: 0 0 auto; +} + +.block-first-article .first-article-date { + color: #0e1b4266; + flex: 0 0 auto; +} + +.block-first-article .first-article-intro { + font-size: 20px; + flex: 0 1 auto; + min-height: 0; + overflow: hidden; +} + +.block-first-article .first-article-body { + flex: 1 2 auto; + min-height: 0; + overflow: hidden; + position: relative; +} + +.block-first-article .first-article-content::after { + content: ""; position: absolute; - top: 50%; - transform: translateY(-50%); - width: 36px; - height: 36px; - border: 0; - border-radius: 8px; - background: rgba(255, 255, 255, 0.85); - box-shadow: 0 8px 16px rgba(0, 0, 0, 0.18); - cursor: pointer; -} - -.block-list-arrow[data-dir="left"] { left: 0; -} - -.block-list-arrow[data-dir="right"] { right: 0; -} - -.block-list-arrow.is-hidden { - opacity: 0; + bottom: 0; + height: 100px; + background: linear-gradient(to bottom, rgba(255, 255, 255, 0), #ffffff); pointer-events: none; } -.block-list { - display: flex; - align-items: stretch; - gap: 20px; - overflow-x: auto; - scroll-snap-type: x mandatory; - list-style: none; +.block-list-lower { margin: 24px 0; - padding: 12px 48px; + display: flex; + flex-wrap: wrap; + align-items: stretch; + gap: 28px; } -.block-list li { - flex: 0 0 200px; - overflow: hidden; - scroll-snap-align: start; +.block-list-items { + display: contents; } -.block-list li a { +.block-list-items a { display: flex; flex-direction: column; gap: 12px; - color: inherit; + width: 200px; text-decoration: none; - padding: 16px; } -.block-list li img { - width: 100%; - height: 200px; - /* object-fit: contain; */ +.block-list-thumb { + position: relative; + width: 194px; + height: 133px; + border-radius: 10px; + overflow: hidden; +} + +.block-list-thumb img { display: block; - /* background: #f5f5f5; */ - /* border-radius: 8px; */ + width: 100%; + height: 100%; + object-fit: cover; } -.block-list li a:hover, -.block-list li a:focus { - transform: translateY(-2px); +.block-list-items a span{ + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + overflow: hidden; } -.block-list .empty { - flex: 1 0 auto; +.block-list-items .article-title { + font-size: 20px; +} + +.block-list-items .article-date { + color: #0e1b4266; +} + +.block-list-more-article { + max-width: 430px; + min-width: 200px; + flex: 1 1 0; + display: flex; + align-items: center; + justify-content: center; + align-self: stretch; + margin-left: auto; text-align: center; - padding: 24px; - color: #666; - background: #fafafa; - border: 1px dashed #ddd; - border-radius: 12px; +} + +.block-list-more-article a { + display: flex; + align-items: center; + gap: 12px; + justify-content: center; +} + +.block-list-more-article span { + font-size: 18px; + font-weight: 700; + color: #0e1b42; + text-decoration: underline; +} + +.block-list-more-article .right-arrow { + background: none; + width: 52px; + height: 52px; + border: none; + padding: 0; + cursor: pointer; + box-shadow: #0e1b4266 1px 2px 1px 1px; +} + +.block-list-more-article .right-arrow .right-arrow-icon { + width: 16px; + height: 32px; + transform: rotate(180deg); +} + +@media (min-width: 768px) and (max-width: 1023px) { + .block-first-article { + grid-template-columns: 318px 1fr; + } + + .block-first-article .first-article-image { + width: 318px; + } + + .block-first-article .first-article-image img{ + width:318px; + height:290px; + } + + .block-first-article .first-article-title { + font-size: 36px; + } + + .block-list-lower { + gap: 20px; + } + + .block-list-more-article { + max-width: 200px; + flex: 1 1 200px; + } +} + +@media (max-width: 767px) { + .block-first-article { + grid-template-columns: 1fr; + grid-template-areas: + "image" + "content"; + } +} + +@media (min-width: 575px) and (max-width: 767px) { + .block-first-article .first-article-image { + width: 426px; + } + + .block-first-article .first-article-image img{ + width:426px; + height:260px; + } + + .block-first-article .first-article-title { + font-size: 36px; + } + + .block-list-items a { + display: grid; + grid-template-columns: 194px 1fr; + column-gap: 16px; + row-gap: 8px; + width: 100%; + } + + .block-list-items a > div:nth-of-type(1) { + grid-row: 1 / span 2; + } + + .block-list-items a > div:nth-of-type(2), + .block-list-items a > div:nth-of-type(3) { + grid-column: 2; + min-width: 0; + } + + .block-list-more-article { + max-width: 100%; + } +} + +@media (max-width: 574px) { + .block-first-article .first-article-image { + width: 300px; + } + + .block-first-article .first-article-image img{ + width: 300px; + height: 220px; + } + + .block-first-article .first-article-title { + font-size: 24px; + } + + .block-first-article .first-article-intro { + font-size: 16px; + } + + .block-list-lower { + gap: 16px; + } + + .block-list-items a { + width: 142px; + } + + .block-list-thumb { + width: 139px; + height: 110px; + } + + .block-list-items .article-title { + font-size: 16px; + } + + .block-list-items .article-date { + font-size: 12px; + } + + .block-list-more-article span { + font-size: 16px; + } + + .block-list-more-article .right-arrow { + width: 40px; + height: 40px; + } + + .block-list-more-article .right-arrow .right-arrow-icon { + width: 12px; + height: 24px; + } } diff --git a/innovedus_cms/home/static/css/category.css b/innovedus_cms/home/static/css/category.css new file mode 100644 index 0000000..e48bd6b --- /dev/null +++ b/innovedus_cms/home/static/css/category.css @@ -0,0 +1,70 @@ +.block-title { + display: inline-block; + width: 197px; + height: 87px; + vertical-align: middle; + font-size: 20px; + font-weight: 700; +} + +.block-title span { + padding-left: 21px; + line-height: 87px; +} + +.category-title { + background-color: #00abf5; + color: #ffffff; +} + +.subcategory-title { + display: flex; + align-items: center; + margin: 24px 0; + color: #0e1b42; + font-size: 18px; + font-weight: 700; +} + +.subcategory-title::before, +.subcategory-title::after { + content: ""; + flex: 1 1 auto; + height: 1px; + background: #0e1b42; +} + +.subcategory-title span { + display: inline-block; + width: 190px; + height: 40px; + line-height: 40px; + text-align: center; + border: 1px solid #0e1b42; + background: #ffffff; +} + +@media (max-width: 767px) { + .subcategory-title::after { + display: none; + } +} + +@media (max-width: 574px) { + .block-title { + width: 139px; + height: 55px; + font-size: 16px; + } + + .block-title span { + padding-left: 14px; + line-height: 55px; + } + + .subcategory-title span { + width: 139px; + height: 55px; + line-height: 55px; + } +} diff --git a/innovedus_cms/home/static/css/home.css b/innovedus_cms/home/static/css/home.css index 3db3ac7..a982be3 100644 --- a/innovedus_cms/home/static/css/home.css +++ b/innovedus_cms/home/static/css/home.css @@ -8,41 +8,12 @@ color: #ffffff; } -.template-homepage .site-header { - background-color: #0e1b42; - color: #ffffff; -} - -.template-homepage .main-menu-link { - color: #ffffff; -} - -.template-homepage .header-search .search-input { - /* border-color: #ffffff; */ - background-color: rgba(255, 255, 255, 0.4); -} - -.template-homepage .header-search .search-icon { - color: #ffffff; -} - -.template-homepage .header-search input[type="search"] { - color: #ffffff; -} - .home-banner img { width: 100%; height: auto; display: block; } -@media (max-width: 767px) { - .home-banner { - width: 100vw; - margin-left: calc(50% - 50vw); - } -} - .list-title { align-items: center; margin: 10px 0; @@ -78,7 +49,7 @@ color: #00abf5; } -.section-b .list-title .category_title { +.section-b .list-title .category-title { background-color: #00abf5; color: #ffffff; } @@ -91,7 +62,7 @@ color: #f4a41c; } -.section-o .list-title .category_title { +.section-o .list-title .category-title { background-color: #f4a41c; color: #ffffff; } @@ -101,11 +72,18 @@ } @media (min-width: 768px) and (max-width: 1023px) { - .list-title .category_title { + .list-title .category-title { width: 142px; } } +@media (max-width: 767px) { + .home-banner { + width: 100vw; + margin-left: calc(50% - 50vw); + } +} + @media (max-width: 574px) { .block-title { width: 139px; @@ -127,4 +105,4 @@ .more-link { font-size: 14px; } -} \ No newline at end of file +} diff --git a/innovedus_cms/home/static/css/horizontal_list.css b/innovedus_cms/home/static/css/horizontal_list.css index 80c5ea5..27cff22 100644 --- a/innovedus_cms/home/static/css/horizontal_list.css +++ b/innovedus_cms/home/static/css/horizontal_list.css @@ -150,7 +150,7 @@ .horizontal-list-thumb { width: 139px; - height: 114px; + height: 110px; } .section-b .horizontal-list-thumb::after { @@ -222,10 +222,6 @@ flex: 0 0 142px; } - /* .horizontal-list .article-title { - width: 142px; - } */ - .horizontal-list .article-title { font-size: 16px; } diff --git a/innovedus_cms/home/static/css/news_list.css b/innovedus_cms/home/static/css/news_list.css index e93fd68..eeab0a1 100644 --- a/innovedus_cms/home/static/css/news_list.css +++ b/innovedus_cms/home/static/css/news_list.css @@ -188,7 +188,42 @@ overflow: hidden; } +@media (min-width: 768px) and (max-width: 1023px) { + .news-hero { + grid-template-columns: 318px 1fr; + } + + .news-hero .fist-news-title { + max-width: 320px; + } + + .news-hero .first-news-image { + width: 318px; + } + + .news-hero .first-news-image::after { + background-size: 318px 25px; + } + + .news-hero .first-news-image img { + width: 318px; + height: 290px; + } + + .news-hero .first-news-content { + height: 290px; + } + + .news-list-lower { + gap: 16px; + } +} + @media (min-width: 575px) and (max-width: 1023px) { + .news-hero .fist-news-title { + font-size: 36px; + } + .more-news { display: grid; grid-template-columns: 200px 1fr 1fr; @@ -314,43 +349,6 @@ } } -@media (min-width: 768px) and (max-width: 1023px) { - .news-hero { - grid-template-columns: 318px 1fr; - } - - .news-hero .fist-news-title { - max-width: 320px; - } - - .news-hero .first-news-image { - width: 318px; - } - - .news-hero .first-news-image::after { - background-size: 318px 25px; - } - - .news-hero .first-news-image img { - width: 318px; - height: 290px; - } - - .news-hero .first-news-content { - height: 290px; - } - - .news-list-lower { - gap: 16px; - } -} - -@media (min-width: 575px) and (max-width: 1023px) { - .news-hero .fist-news-title { - font-size: 36px; - } -} - @media (max-width: 574px) { .news-hero .first-news-image { width: 300px; @@ -368,9 +366,6 @@ height: 220px; } - /* .news-hero .first-news-content { - height: 220px; - } */ .news-hero .fist-news-title { font-size: 24px; @@ -388,9 +383,6 @@ width: 142px; } - /* .news-list-lower { - justify-content: center; - } */ .news-list-thumb { width: 139px; height: 110px; diff --git a/innovedus_cms/home/templates/home/category_page.html b/innovedus_cms/home/templates/home/category_page.html index 0dba871..aa1361b 100644 --- a/innovedus_cms/home/templates/home/category_page.html +++ b/innovedus_cms/home/templates/home/category_page.html @@ -1,6 +1,10 @@ {% extends "base.html" %} -{% load wagtailcore_tags %} +{% load wagtailcore_tags static %} +{% block extra_css %} + +{% endblock %} {% block content %} + {# Breadcrumbs navigation may not used anymore #} {% if breadcrumbs %} {% endif %} +
    {{ self.title}}
    {% if page.has_subcategories %} {% for section in category_sections %} - {% include "home/includes/category_session.html" with section=section %} +
    {{ section.title }}
    + {% include "home/includes/block_list.html" with items=section.items %} {% endfor %} {% else %} {% include "home/includes/page-article-list.html" with category=category_sections.0 %} diff --git a/innovedus_cms/home/templates/home/home_page.html b/innovedus_cms/home/templates/home/home_page.html index 8e8ba60..81dc3a1 100644 --- a/innovedus_cms/home/templates/home/home_page.html +++ b/innovedus_cms/home/templates/home/home_page.html @@ -1,6 +1,6 @@ {% extends "base.html" %} {% load static %} -{% block body_class %}template-homepage{% endblock %} +{% block body_class %}template-darkbackground{% endblock %} {% block extra_css %} diff --git a/innovedus_cms/home/templates/home/includes/block_list.html b/innovedus_cms/home/templates/home/includes/block_list.html index 932e07b..7c13c04 100644 --- a/innovedus_cms/home/templates/home/includes/block_list.html +++ b/innovedus_cms/home/templates/home/includes/block_list.html @@ -1,23 +1,74 @@ {% load wagtailimages_tags static %} +{% block extra_css %} + +{% endblock %}
    - - - +
    + + {% endif %} diff --git a/innovedus_cms/home/templates/home/includes/category_session.html b/innovedus_cms/home/templates/home/includes/category_session.html index b8ec3aa..f24be4a 100644 --- a/innovedus_cms/home/templates/home/includes/category_session.html +++ b/innovedus_cms/home/templates/home/includes/category_session.html @@ -2,7 +2,7 @@
    -
    {{ section.title }}
    +
    {{ section.title }}