Compare commits

...

3 Commits

Author SHA1 Message Date
warrenchen
ed70c742df Refine media assets and pagination display 2026-06-29 17:44:40 +09:00
warrenchen
320bdda242 Merge branch 'main' into develop 2026-06-29 15:20:53 +09:00
warrenchen
35362b48c1 SES issue: Comment out email sending logic in contact form submission 2026-06-29 13:22:26 +09:00
28 changed files with 155 additions and 73 deletions

View File

@ -1,5 +1,5 @@
.newsletter-page {
background: #0e1b42 url("../img/newsletter_bg.png") center center / cover no-repeat fixed;
background: #0e1b42 url("../img/newsletter_bg.webp") center center / cover no-repeat fixed;
}
.newsletter-page .site-main > .site-container {
@ -170,4 +170,4 @@
.newsletter-back-link__text {
font-size: 14px;
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 922 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 841 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

View File

@ -143,46 +143,46 @@ def contact_form_submit(request):
except Exception as exc:
logger.warning("contact form admin notification email failed: %s", exc)
if submission.email:
values_text = {
"name": submission.name,
"email": submission.email,
"contact": submission.contact,
"category": submission.get_category_display(),
"message": submission.message,
"source_page": submission.source_page or "",
}
values_html = {
"name": escape(submission.name),
"email": escape(submission.email),
"contact": escape(submission.contact),
"category": escape(submission.get_category_display()),
"message": escape(submission.message).replace("\n", "<br>"),
"source_page": escape(submission.source_page or ""),
}
user_subject = _render_contact_template(
notification_settings.contact_form_user_subject_template,
values_text,
)
user_text = _render_contact_template(
notification_settings.contact_form_user_text_template,
values_text,
)
user_html = _render_contact_template(
notification_settings.contact_form_user_html_template,
values_html,
)
try:
send_contact_user_email(
to_email=submission.email,
subject=user_subject,
text_body=user_text,
html_body=user_html,
notification_config=notification_settings,
smtp_config=smtp_settings,
)
except Exception as exc:
logger.warning("contact form user copy email failed: %s", exc)
# if submission.email:
# values_text = {
# "name": submission.name,
# "email": submission.email,
# "contact": submission.contact,
# "category": submission.get_category_display(),
# "message": submission.message,
# "source_page": submission.source_page or "",
# }
# values_html = {
# "name": escape(submission.name),
# "email": escape(submission.email),
# "contact": escape(submission.contact),
# "category": escape(submission.get_category_display()),
# "message": escape(submission.message).replace("\n", "<br>"),
# "source_page": escape(submission.source_page or ""),
# }
# user_subject = _render_contact_template(
# notification_settings.contact_form_user_subject_template,
# values_text,
# )
# user_text = _render_contact_template(
# notification_settings.contact_form_user_text_template,
# values_text,
# )
# user_html = _render_contact_template(
# notification_settings.contact_form_user_html_template,
# values_html,
# )
# try:
# send_contact_user_email(
# to_email=submission.email,
# subject=user_subject,
# text_body=user_text,
# html_body=user_html,
# notification_config=notification_settings,
# smtp_config=smtp_settings,
# )
# except Exception as exc:
# logger.warning("contact form user copy email failed: %s", exc)
if request.headers.get("x-requested-with") == "XMLHttpRequest":
return JsonResponse({"success": True})

View File

@ -1,3 +1,5 @@
import os
from django.contrib.syndication.views import Feed
from django.utils.feedgenerator import Rss201rev2Feed
from django.utils.html import strip_tags
@ -5,6 +7,20 @@ from django.utils.html import strip_tags
from .models import ArticlePage
def _get_env_int(name, default):
value = os.environ.get(name)
if value is None:
return default
try:
parsed = int(value)
except ValueError:
return default
return parsed if parsed > 0 else default
ARTICLE_FEED_SIZE = _get_env_int("ARTICLE_FEED_SIZE", 50)
class LatestArticlesFeed(Feed):
feed_type = Rss201rev2Feed
title = "DeBuT AI 最新文章"
@ -12,7 +28,7 @@ class LatestArticlesFeed(Feed):
description = "DeBuT AI 最新文章 RSS feed"
def items(self):
return ArticlePage.objects.live().order_by("-date", "-id")[:20]
return ArticlePage.objects.live().order_by("-date", "-id")[:ARTICLE_FEED_SIZE]
def item_title(self, item):
return item.title

View File

@ -141,23 +141,27 @@
justify-content: center;
align-items: center;
gap: 8px;
margin: 40px 0 20px;
font-size: 14px;
margin: 0;
font-size: 12px;
font-weight: 400;
line-height: 15px;
letter-spacing: 0.3em;
}
.page-size-label {
color: #0e1b4266;
color: #0e1b42;
}
.page-size-option {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 40px;
height: 32px;
min-width: 37px;
height: 24px;
border: 1px solid #0e1b42;
border-radius: 4px;
color: #0e1b42;
font-weight: 600;
letter-spacing: 0.05em;
}
.page-size-option.is-current {

View File

@ -11,6 +11,21 @@
line-height: 60px;
}
.category-toolbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 24px;
}
.category-toolbar .category-title {
flex: 0 0 auto;
}
.category-toolbar .page-size-selector {
margin: 0 0 0 auto;
}
.subcategory-title {
display: flex;
align-items: center;
@ -38,6 +53,16 @@
}
@media (max-width: 767px) {
.category-toolbar {
align-items: flex-start;
flex-direction: column;
gap: 12px;
}
.category-toolbar .page-size-selector {
margin-left: 0;
}
.subcategory-title::after {
display: none;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 247 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 212 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 KiB

View File

@ -27,8 +27,11 @@
</nav>
{% endif %}
<div class="site-hero-band full-bleed">
<div class="site-container">
<div class="site-container category-toolbar">
<div class="block-title category-title"><span>{{ self.title}}</span></div>
{% if not page.has_subcategories %}
{% include "home/includes/page_size_selector.html" with category=category_sections.0 %}
{% endif %}
</div>
</div>
{% if page.has_subcategories %}

View File

@ -5,8 +5,9 @@
{% endblock %}
{% block content %}
<div class="site-hero-band full-bleed">
<div class="site-container">
<div class="site-container category-toolbar">
<div class="block-title category-title"><span>#{{ tag.name }}</span></div>
{% include "home/includes/page_size_selector.html" with category=category_sections.0 %}
</div>
</div>

View File

@ -6,19 +6,6 @@
<div class="page-article-list">
{% include "home/includes/article_list.html" with items=category.items show_hero=show_hero empty_message=empty_message %}
{% if category.pagination.page_size_options %}
<div class="page-size-selector" aria-label="每頁文章數">
<span class="page-size-label">每頁</span>
{% for option in category.pagination.page_size_options %}
{% if option.is_current %}
<span class="page-size-option is-current">{{ option.value }}</span>
{% else %}
<a class="page-size-option" href="{{ option.url }}">{{ option.value }}</a>
{% endif %}
{% endfor %}
</div>
{% endif %}
{% if category.items.paginator.num_pages > 1 %}
<div class="pagination">
{% if category.items.has_previous %}

View File

@ -0,0 +1,14 @@
{% if category.pagination.page_size_options %}
<div class="page-size-selector" aria-label="每頁文章數">
<span class="page-size-label">每頁顯示</span>
{% for option in category.pagination.page_size_options %}
{% if option.is_current %}
<span class="page-size-option is-current">{{ option.value }}</span>
{% else %}
<span class="page-size-option">
<a href="{{ option.url }}">{{ option.value }}</a>
</span>
{% endif %}
{% endfor %}
</div>
{% endif %}

View File

@ -9,8 +9,8 @@ register = template.Library()
@register.simple_tag
def random_default_cover():
choices = (
"img/default_cover_1.png",
"img/default_cover_2.png",
"img/default_cover_3.png",
"img/default_cover_1.jpg",
"img/default_cover_2.jpg",
"img/default_cover_3.jpg",
)
return static(random.choice(choices))

View File

@ -1,7 +1,19 @@
from django.conf import settings
from django.templatetags.static import static
def ga4(request):
return {
"ga4_measurement_id": getattr(settings, "GA4_MEASUREMENT_ID", ""),
}
def default_og_image(request):
return {
"default_og_image": {
"url": request.build_absolute_uri(static("img/og_default.jpg")),
"width": 528,
"height": 337,
"alt": "DeBuT AI",
},
}

View File

@ -177,6 +177,7 @@ TEMPLATES = [
"wagtail.contrib.settings.context_processors.settings",
"home.context_processors.navigation_pages",
"mysite.context_processors.ga4",
"mysite.context_processors.default_og_image",
],
},
},

View File

@ -321,6 +321,21 @@ a {
color: #0e1b42;
}
.template-darkbackground .page-size-label {
color: #ffffff;
}
.template-darkbackground .page-size-option {
border-color: #ffffffb3;
color: #ffffffb3;
}
.template-darkbackground .page-size-option.is-current {
background-color: #ffffffb3;
background-clip: padding-box;
color: #0e1b42;
}
.template-darkbackground .site-hero-band .news-title {
height: 60px;
}

View File

@ -16,11 +16,12 @@
{% if page.search_description %}
<meta name="description" content="{{ page.search_description }}" />
{% endif %}
{% if og_image %}
<meta property="og:image" content="{{ og_image.url }}" />
<meta property="og:image:width" content="{{ og_image.width }}" />
<meta property="og:image:height" content="{{ og_image.height }}" />
<meta property="og:image:alt" content="{{ og_image.alt }}" />
{% firstof og_image default_og_image as resolved_og_image %}
{% if resolved_og_image %}
<meta property="og:image" content="{{ resolved_og_image.url }}" />
<meta property="og:image:width" content="{{ resolved_og_image.width }}" />
<meta property="og:image:height" content="{{ resolved_og_image.height }}" />
<meta property="og:image:alt" content="{{ resolved_og_image.alt }}" />
{% endif %}
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="alternate" type="application/rss+xml" title="DeBuT AI 最新文章 RSS" href="{% url 'article_feed' %}">

View File

@ -14,8 +14,11 @@
{% block content %}
{% if search_query %}
<div class="site-hero-band full-bleed">
<div class="site-container">
<div class="site-container category-toolbar">
<div class="block-title category-title"><span>{{ search_query }}</span></div>
{% if results_count %}
{% include "home/includes/page_size_selector.html" with category=category_sections.0 %}
{% endif %}
</div>
</div>
{% endif %}