diff --git a/innovedus_cms/home/feeds.py b/innovedus_cms/home/feeds.py
new file mode 100644
index 0000000..ff473c0
--- /dev/null
+++ b/innovedus_cms/home/feeds.py
@@ -0,0 +1,30 @@
+from django.contrib.syndication.views import Feed
+from django.utils.feedgenerator import Rss201rev2Feed
+from django.utils.html import strip_tags
+
+from .models import ArticlePage
+
+
+class LatestArticlesFeed(Feed):
+ feed_type = Rss201rev2Feed
+ title = "DeBuT AI 最新文章"
+ link = "/"
+ description = "DeBuT AI 最新文章 RSS feed"
+
+ def items(self):
+ return ArticlePage.objects.live().order_by("-date", "-id")[:20]
+
+ def item_title(self, item):
+ return item.title
+
+ def item_description(self, item):
+ return strip_tags(item.intro or item.search_description or "")
+
+ def item_link(self, item):
+ return item.url
+
+ def item_pubdate(self, item):
+ return item.date
+
+ def item_categories(self, item):
+ return list(item.tags.values_list("name", flat=True))
diff --git a/innovedus_cms/home/models.py b/innovedus_cms/home/models.py
index 96d7320..5dfda27 100644
--- a/innovedus_cms/home/models.py
+++ b/innovedus_cms/home/models.py
@@ -8,6 +8,7 @@ from modelcluster.contrib.taggit import ClusterTaggableManager
from modelcluster.fields import ParentalKey
from taggit.models import TaggedItemBase
from wagtail.search import index
+from .pagination import build_pagination_context, get_page_size
def _get_env_int(name, default):
value = os.environ.get(name)
@@ -62,7 +63,7 @@ class CategoryMixin:
ArticlePage.objects.child_of(self)
.live()
.order_by("-date", "-id"),
- PAGE_SIZE,
+ get_page_size(request, PAGE_SIZE),
)
page_number = request.GET.get("page") if request else None
@@ -78,7 +79,7 @@ class CategoryMixin:
"title": self.title,
"items": page_obj,
"url": self.url,
- "page_range": paginator.get_elided_page_range(page_obj.number),
+ "pagination": build_pagination_context(request, page_obj, paginator),
}
)
return blocks
@@ -98,7 +99,8 @@ class CategoryMixin:
else:
# Paginated view
paginator = Paginator(
- ArticlePage.objects.live().order_by("-date", "-id"), PAGE_SIZE
+ ArticlePage.objects.live().order_by("-date", "-id"),
+ get_page_size(request, PAGE_SIZE),
)
page_number = request.GET.get("page")
@@ -112,7 +114,7 @@ class CategoryMixin:
"title": self.title,
"items": page_obj,
"url": self.url,
- "page_range": paginator.get_elided_page_range(page_obj.number),
+ "pagination": build_pagination_context(request, page_obj, paginator),
}
def get_trending_articles(self, request=None, exclude_ids=None):
@@ -134,7 +136,7 @@ class CategoryMixin:
}
else:
# Paginated view
- paginator = Paginator(articles_qs, PAGE_SIZE)
+ paginator = Paginator(articles_qs, get_page_size(request, PAGE_SIZE))
page_number = request.GET.get("page")
try:
@@ -147,7 +149,7 @@ class CategoryMixin:
"title": self.title,
"items": page_obj,
"url": self.url,
- "page_range": paginator.get_elided_page_range(page_obj.number),
+ "pagination": build_pagination_context(request, page_obj, paginator),
}
@@ -310,6 +312,15 @@ class ArticlePage(Page, BreadcrumbMixin):
def get_context(self, request):
context = super().get_context(request)
+ if self.cover_image:
+ cover = self.cover_image.get_rendition("original")
+ context["og_image"] = {
+ "url": request.build_absolute_uri(cover.url),
+ "width": cover.width,
+ "height": cover.height,
+ "alt": self.title,
+ }
+
breadcrumbs, site_root = self.build_breadcrumbs()
# context["breadcrumbs"] = breadcrumbs
# context["breadcrumb_root"] = site_root
diff --git a/innovedus_cms/home/pagination.py b/innovedus_cms/home/pagination.py
new file mode 100644
index 0000000..cd7a7d9
--- /dev/null
+++ b/innovedus_cms/home/pagination.py
@@ -0,0 +1,56 @@
+PAGE_SIZE_OPTIONS = (10, 20, 30)
+
+
+def get_page_size(request, default):
+ try:
+ page_size = int(request.GET.get("page_size", default))
+ except (TypeError, ValueError):
+ return default
+
+ return page_size if page_size in PAGE_SIZE_OPTIONS else default
+
+
+def build_query_string(request, **updates):
+ query = request.GET.copy()
+
+ for key, value in updates.items():
+ if value is None:
+ query.pop(key, None)
+ else:
+ query[key] = value
+
+ return query.urlencode()
+
+
+def build_pagination_context(request, page_obj, paginator):
+ page_range = paginator.get_elided_page_range(page_obj.number)
+ page_size = paginator.per_page
+
+ return {
+ "page_size": page_size,
+ "page_size_options": [
+ {
+ "value": option,
+ "url": f"?{build_query_string(request, page_size=option, page=None)}",
+ "is_current": option == page_size,
+ }
+ for option in PAGE_SIZE_OPTIONS
+ ],
+ "pages": [
+ {
+ "number": page_num,
+ "url": f"?{build_query_string(request, page=page_num)}"
+ if page_num != "…"
+ else "",
+ "is_current": page_num == page_obj.number,
+ "is_ellipsis": page_num == "…",
+ }
+ for page_num in page_range
+ ],
+ "previous_url": f"?{build_query_string(request, page=page_obj.previous_page_number())}"
+ if page_obj.has_previous()
+ else "",
+ "next_url": f"?{build_query_string(request, page=page_obj.next_page_number())}"
+ if page_obj.has_next()
+ else "",
+ }
diff --git a/innovedus_cms/home/static/css/article_list.css b/innovedus_cms/home/static/css/article_list.css
index dae7827..4fb498d 100644
--- a/innovedus_cms/home/static/css/article_list.css
+++ b/innovedus_cms/home/static/css/article_list.css
@@ -136,6 +136,35 @@
color: #0e1b4266;
}
+.page-size-selector {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ gap: 8px;
+ margin: 40px 0 20px;
+ font-size: 14px;
+}
+
+.page-size-label {
+ color: #0e1b4266;
+}
+
+.page-size-option {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ min-width: 40px;
+ height: 32px;
+ border: 1px solid #0e1b42;
+ border-radius: 4px;
+ color: #0e1b42;
+}
+
+.page-size-option.is-current {
+ background: #0e1b42;
+ color: #ffffff;
+}
+
.pagination {
display: flex;
justify-content: center;
diff --git a/innovedus_cms/home/static/css/category.css b/innovedus_cms/home/static/css/category.css
index a748c31..222243b 100644
--- a/innovedus_cms/home/static/css/category.css
+++ b/innovedus_cms/home/static/css/category.css
@@ -3,6 +3,14 @@
color: #ffffff;
}
+.template-darkbackground .category-title {
+ height: 60px;
+}
+
+.template-darkbackground .category-title span {
+ line-height: 60px;
+}
+
.subcategory-title {
display: flex;
align-items: center;
diff --git a/innovedus_cms/home/templates/home/article_page.html b/innovedus_cms/home/templates/home/article_page.html
index 6979947..ed853a5 100644
--- a/innovedus_cms/home/templates/home/article_page.html
+++ b/innovedus_cms/home/templates/home/article_page.html
@@ -63,6 +63,13 @@