mirror of
https://github.com/facebook/docusaurus.git
synced 2025-04-29 10:17:55 +02:00
feat: adds blog archive route (#5428)
* [feature] adds blog archive route * Update plugin-content-blog.md * fix TS issues + minor refactors * remove useless css * add translation apis * add missing translations Co-authored-by: slorber <lorber.sebastien@gmail.com>
This commit is contained in:
parent
e5d9ff18a8
commit
cb8718a1e3
28 changed files with 177 additions and 4 deletions
|
@ -225,6 +225,8 @@ export default function pluginContentBlog(
|
|||
blogPostComponent,
|
||||
blogTagsListComponent,
|
||||
blogTagsPostsComponent,
|
||||
routeBasePath,
|
||||
archiveBasePath,
|
||||
} = options;
|
||||
|
||||
const {addRoute, createData} = actions;
|
||||
|
@ -243,6 +245,26 @@ export default function pluginContentBlog(
|
|||
? blogPosts
|
||||
: take(blogPosts, options.blogSidebarCount);
|
||||
|
||||
const archiveUrl = normalizeUrl([
|
||||
baseUrl,
|
||||
routeBasePath,
|
||||
archiveBasePath,
|
||||
]);
|
||||
|
||||
// creates a blog archive route
|
||||
const archiveProp = await createData(
|
||||
`${docuHash(archiveUrl)}.json`,
|
||||
JSON.stringify({blogPosts}, null, 2),
|
||||
);
|
||||
addRoute({
|
||||
path: archiveUrl,
|
||||
component: '@theme/BlogArchivePage',
|
||||
exact: true,
|
||||
modules: {
|
||||
archive: aliasedSource(archiveProp),
|
||||
},
|
||||
});
|
||||
|
||||
// This prop is useful to provide the blog list sidebar
|
||||
const sidebarProp = await createData(
|
||||
// Note that this created data path must be in sync with
|
||||
|
|
|
@ -70,10 +70,6 @@ declare module '@theme/BlogListPage' {
|
|||
import type {Content} from '@theme/BlogPostPage';
|
||||
import type {BlogSidebar} from '@theme/BlogSidebar';
|
||||
|
||||
export type Item = {
|
||||
readonly content: () => JSX.Element;
|
||||
};
|
||||
|
||||
export type Metadata = {
|
||||
readonly blogTitle: string;
|
||||
readonly blogDescription: string;
|
||||
|
@ -130,3 +126,17 @@ declare module '@theme/BlogTagsPostsPage' {
|
|||
const BlogTagsPostsPage: (props: Props) => JSX.Element;
|
||||
export default BlogTagsPostsPage;
|
||||
}
|
||||
|
||||
declare module '@theme/BlogArchivePage' {
|
||||
import type {Content} from '@theme/BlogPostPage';
|
||||
|
||||
export type ArchiveBlogPost = Content;
|
||||
|
||||
export type Props = {
|
||||
readonly archive: {
|
||||
blogPosts: readonly ArchiveBlogPost[];
|
||||
};
|
||||
};
|
||||
|
||||
export default function BlogArchivePage(props: Props): JSX.Element;
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ export const DEFAULT_OPTIONS: PluginOptions = {
|
|||
include: ['**/*.{md,mdx}'],
|
||||
exclude: GlobExcludeDefault,
|
||||
routeBasePath: 'blog',
|
||||
archiveBasePath: 'archive',
|
||||
path: 'blog',
|
||||
editLocalizedFiles: false,
|
||||
authorsMapPath: 'authors.yml',
|
||||
|
@ -43,6 +44,7 @@ export const DEFAULT_OPTIONS: PluginOptions = {
|
|||
|
||||
export const PluginOptionSchema = Joi.object<PluginOptions>({
|
||||
path: Joi.string().default(DEFAULT_OPTIONS.path),
|
||||
archiveBasePath: Joi.string().default(DEFAULT_OPTIONS.archiveBasePath),
|
||||
routeBasePath: Joi.string()
|
||||
// '' not allowed, see https://github.com/facebook/docusaurus/issues/3374
|
||||
// .allow('')
|
||||
|
|
|
@ -35,6 +35,7 @@ export interface PluginOptions extends RemarkAndRehypePluginOptions {
|
|||
id?: string;
|
||||
path: string;
|
||||
routeBasePath: string;
|
||||
archiveBasePath: string;
|
||||
include: string[];
|
||||
exclude: string[];
|
||||
postsPerPage: number | 'ALL';
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
"theme.SearchPage.inputPlaceholder": "اكتب ما تبحث عنه هنا",
|
||||
"theme.SearchPage.noResultsText": "لم يتم العثور على نتائج",
|
||||
"theme.TOCCollapsible.toggleButtonLabel": "محتويات هذه الصفحة",
|
||||
"theme.blog.archive.description": "Archive",
|
||||
"theme.blog.archive.title": "Archive",
|
||||
"theme.blog.paginator.navAriaLabel": "التنقل في صفحة قائمة المدونة",
|
||||
"theme.blog.paginator.newerEntries": "إدخالات أحدث",
|
||||
"theme.blog.paginator.olderEntries": "إدخالات أقدم",
|
||||
|
|
|
@ -43,6 +43,10 @@
|
|||
"theme.SearchPage.noResultsText___DESCRIPTION": "The paragraph for empty search result",
|
||||
"theme.TOCCollapsible.toggleButtonLabel": "On this page",
|
||||
"theme.TOCCollapsible.toggleButtonLabel___DESCRIPTION": "The label used by the button on the collapsible TOC component",
|
||||
"theme.blog.archive.description": "Archive",
|
||||
"theme.blog.archive.description___DESCRIPTION": "The page & hero description of the blog archive page",
|
||||
"theme.blog.archive.title": "Archive",
|
||||
"theme.blog.archive.title___DESCRIPTION": "The page & hero title of the blog archive page",
|
||||
"theme.blog.paginator.navAriaLabel": "Blog list page navigation",
|
||||
"theme.blog.paginator.navAriaLabel___DESCRIPTION": "The ARIA label for the blog pagination",
|
||||
"theme.blog.paginator.newerEntries": "Newer Entries",
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
"theme.SearchPage.inputPlaceholder": "আপনার অনুসন্ধান এখানে টাইপ করুন",
|
||||
"theme.SearchPage.noResultsText": "কোন ফলাফল পাওয়া যায়নি",
|
||||
"theme.TOCCollapsible.toggleButtonLabel": "এই পেজ এ রয়েছে",
|
||||
"theme.blog.archive.description": "Archive",
|
||||
"theme.blog.archive.title": "Archive",
|
||||
"theme.blog.paginator.navAriaLabel": "ব্লগ তালিকা পেজ নেভিগেশন",
|
||||
"theme.blog.paginator.newerEntries": "নতুন এন্ট্রি",
|
||||
"theme.blog.paginator.olderEntries": "পুরানো এন্ট্রি",
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
"theme.SearchPage.inputPlaceholder": "Zde napište hledaný text",
|
||||
"theme.SearchPage.noResultsText": "Nic nebylo nalezeno",
|
||||
"theme.TOCCollapsible.toggleButtonLabel": "Na této stránce",
|
||||
"theme.blog.archive.description": "Archive",
|
||||
"theme.blog.archive.title": "Archive",
|
||||
"theme.blog.paginator.navAriaLabel": "Stránkování článků na blogu",
|
||||
"theme.blog.paginator.newerEntries": "Novější záznamy",
|
||||
"theme.blog.paginator.olderEntries": "Starší záznamy",
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
"theme.SearchPage.inputPlaceholder": "Indtast din søgning her",
|
||||
"theme.SearchPage.noResultsText": "Ingen resultater fundet",
|
||||
"theme.TOCCollapsible.toggleButtonLabel": "On this page",
|
||||
"theme.blog.archive.description": "Archive",
|
||||
"theme.blog.archive.title": "Archive",
|
||||
"theme.blog.paginator.navAriaLabel": "Blogoversigt navigation",
|
||||
"theme.blog.paginator.newerEntries": "Nyere indslag",
|
||||
"theme.blog.paginator.olderEntries": "Tidligere indslag",
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
"theme.SearchPage.inputPlaceholder": "Geben Sie hier Ihre Suche ein",
|
||||
"theme.SearchPage.noResultsText": "Es wurden keine Ergebnisse gefunden",
|
||||
"theme.TOCCollapsible.toggleButtonLabel": "On this page",
|
||||
"theme.blog.archive.description": "Archive",
|
||||
"theme.blog.archive.title": "Archive",
|
||||
"theme.blog.paginator.navAriaLabel": "Navigation der Blog-Listenseite",
|
||||
"theme.blog.paginator.newerEntries": "Neuere Einträge",
|
||||
"theme.blog.paginator.olderEntries": "Ältere Einträge",
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
"theme.SearchPage.inputPlaceholder": "Escribe tu búsqueda aquí",
|
||||
"theme.SearchPage.noResultsText": "No se encontraron resultados",
|
||||
"theme.TOCCollapsible.toggleButtonLabel": "En esta página",
|
||||
"theme.blog.archive.description": "Archive",
|
||||
"theme.blog.archive.title": "Archive",
|
||||
"theme.blog.paginator.navAriaLabel": "Navegación por la página de la lista de blogs ",
|
||||
"theme.blog.paginator.newerEntries": "Entradas más recientes",
|
||||
"theme.blog.paginator.olderEntries": "Entradas más antiguas",
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
"theme.SearchPage.inputPlaceholder": "عبارت مورد نظر را اینجا بنویسید",
|
||||
"theme.SearchPage.noResultsText": "هیچ نتیجه ای پیدا نشد",
|
||||
"theme.TOCCollapsible.toggleButtonLabel": "مطالب این صفحه",
|
||||
"theme.blog.archive.description": "Archive",
|
||||
"theme.blog.archive.title": "Archive",
|
||||
"theme.blog.paginator.navAriaLabel": "کنترل لیست مطالب وبلاک",
|
||||
"theme.blog.paginator.newerEntries": "مطالب جدید تر",
|
||||
"theme.blog.paginator.olderEntries": "مطالب قدیمی تر",
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
"theme.SearchPage.inputPlaceholder": "I-type and inyong hinahanap dito",
|
||||
"theme.SearchPage.noResultsText": "Walang resultang nahanap",
|
||||
"theme.TOCCollapsible.toggleButtonLabel": "On this page",
|
||||
"theme.blog.archive.description": "Archive",
|
||||
"theme.blog.archive.title": "Archive",
|
||||
"theme.blog.paginator.navAriaLabel": "Nabegasyón para sa pahina na listahan ng blog",
|
||||
"theme.blog.paginator.newerEntries": "Mas bagong mga éntri",
|
||||
"theme.blog.paginator.olderEntries": "Mas lumang mga éntri",
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
"theme.SearchPage.inputPlaceholder": "Tapez votre recherche ici",
|
||||
"theme.SearchPage.noResultsText": "Aucun résultat trouvé",
|
||||
"theme.TOCCollapsible.toggleButtonLabel": "Sur cette page",
|
||||
"theme.blog.archive.description": "Archive",
|
||||
"theme.blog.archive.title": "Archive",
|
||||
"theme.blog.paginator.navAriaLabel": "Pagination de la liste des articles du blog",
|
||||
"theme.blog.paginator.newerEntries": "Nouvelles entrées",
|
||||
"theme.blog.paginator.olderEntries": "Anciennes entrées",
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
"theme.SearchPage.inputPlaceholder": "הקלד כאן לחיפוש",
|
||||
"theme.SearchPage.noResultsText": "לא נמצאו תוצאות",
|
||||
"theme.TOCCollapsible.toggleButtonLabel": "בעמוד זה",
|
||||
"theme.blog.archive.description": "Archive",
|
||||
"theme.blog.archive.title": "Archive",
|
||||
"theme.blog.paginator.navAriaLabel": "רשימת דפי הבלוג",
|
||||
"theme.blog.paginator.newerEntries": "הכי חדש",
|
||||
"theme.blog.paginator.olderEntries": "ישן יותר",
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
"theme.SearchPage.inputPlaceholder": "अपनी खोज यहाँ टाइप करें",
|
||||
"theme.SearchPage.noResultsText": "कोई परिणाम नहीं मिलें",
|
||||
"theme.TOCCollapsible.toggleButtonLabel": "इस पेज पर",
|
||||
"theme.blog.archive.description": "Archive",
|
||||
"theme.blog.archive.title": "Archive",
|
||||
"theme.blog.paginator.navAriaLabel": "ब्लॉग सूची पेज नेविगेशन",
|
||||
"theme.blog.paginator.newerEntries": "नए एंट्रीज़",
|
||||
"theme.blog.paginator.olderEntries": "पुराने एंट्रीज़",
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
"theme.SearchPage.inputPlaceholder": "ここに検索するキーワードを入力してください",
|
||||
"theme.SearchPage.noResultsText": "検索結果が見つかりませんでした",
|
||||
"theme.TOCCollapsible.toggleButtonLabel": "On this page",
|
||||
"theme.blog.archive.description": "Archive",
|
||||
"theme.blog.archive.title": "Archive",
|
||||
"theme.blog.paginator.navAriaLabel": "ブログ記事一覧のナビゲーション",
|
||||
"theme.blog.paginator.newerEntries": "新しい記事",
|
||||
"theme.blog.paginator.olderEntries": "過去の記事",
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
"theme.SearchPage.inputPlaceholder": "여기에 검색할 키워드를 입력하세요.",
|
||||
"theme.SearchPage.noResultsText": "검색 결과가 없습니다.",
|
||||
"theme.TOCCollapsible.toggleButtonLabel": "On this page",
|
||||
"theme.blog.archive.description": "Archive",
|
||||
"theme.blog.archive.title": "Archive",
|
||||
"theme.blog.paginator.navAriaLabel": "블로그 게시물 목록 탐색",
|
||||
"theme.blog.paginator.newerEntries": "이전 페이지",
|
||||
"theme.blog.paginator.olderEntries": "다음 페이지",
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
"theme.SearchPage.inputPlaceholder": "wpisz szukaną frazę tutaj…",
|
||||
"theme.SearchPage.noResultsText": "Nie znaleziono żadnych wyników",
|
||||
"theme.TOCCollapsible.toggleButtonLabel": "On this page",
|
||||
"theme.blog.archive.description": "Archive",
|
||||
"theme.blog.archive.title": "Archive",
|
||||
"theme.blog.paginator.navAriaLabel": "Nawigacja na stronie listy wpisów na blogu",
|
||||
"theme.blog.paginator.newerEntries": "Nowsze wpisy",
|
||||
"theme.blog.paginator.olderEntries": "Starsze wpisy",
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
"theme.SearchPage.inputPlaceholder": "Digite sua busca aqui",
|
||||
"theme.SearchPage.noResultsText": "Nenhum resultado foi encontrado",
|
||||
"theme.TOCCollapsible.toggleButtonLabel": "On this page",
|
||||
"theme.blog.archive.description": "Archive",
|
||||
"theme.blog.archive.title": "Archive",
|
||||
"theme.blog.paginator.navAriaLabel": "Navegação da página de listagem do blog",
|
||||
"theme.blog.paginator.newerEntries": "Conteúdo mais novo",
|
||||
"theme.blog.paginator.olderEntries": "Conteúdo mais antigo",
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
"theme.SearchPage.inputPlaceholder": "Escreva aqui a sua pesquisa",
|
||||
"theme.SearchPage.noResultsText": "Nenhum resultado encontrado",
|
||||
"theme.TOCCollapsible.toggleButtonLabel": "On this page",
|
||||
"theme.blog.archive.description": "Archive",
|
||||
"theme.blog.archive.title": "Archive",
|
||||
"theme.blog.paginator.navAriaLabel": "Navegação da página de listagem do blog",
|
||||
"theme.blog.paginator.newerEntries": "Publicações mais recentes",
|
||||
"theme.blog.paginator.olderEntries": "Publicações mais antigas",
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
"theme.SearchPage.inputPlaceholder": "Введите фразу для поиска",
|
||||
"theme.SearchPage.noResultsText": "По запросу ничего не найдено",
|
||||
"theme.TOCCollapsible.toggleButtonLabel": "Содержание этой страницы",
|
||||
"theme.blog.archive.description": "Archive",
|
||||
"theme.blog.archive.title": "Archive",
|
||||
"theme.blog.paginator.navAriaLabel": "Навигация по странице списка блогов",
|
||||
"theme.blog.paginator.newerEntries": "Следующие записи",
|
||||
"theme.blog.paginator.olderEntries": "Предыдущие записи",
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
"theme.SearchPage.inputPlaceholder": "Aramanızı buraya yazın",
|
||||
"theme.SearchPage.noResultsText": "Hiçbir sonuç bulunamadı",
|
||||
"theme.TOCCollapsible.toggleButtonLabel": "On this page",
|
||||
"theme.blog.archive.description": "Archive",
|
||||
"theme.blog.archive.title": "Archive",
|
||||
"theme.blog.paginator.navAriaLabel": "Blog gönderi sayfası navigasyonu",
|
||||
"theme.blog.paginator.newerEntries": "Yeni Girdiler",
|
||||
"theme.blog.paginator.olderEntries": "Eski Girdiler",
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
"theme.SearchPage.inputPlaceholder": "Nhập từ khóa cần tìm vào đây",
|
||||
"theme.SearchPage.noResultsText": "Không tìm thấy kết quả nào",
|
||||
"theme.TOCCollapsible.toggleButtonLabel": "On this page",
|
||||
"theme.blog.archive.description": "Archive",
|
||||
"theme.blog.archive.title": "Archive",
|
||||
"theme.blog.paginator.navAriaLabel": "Thanh điều hướng của trang danh sách bài viết",
|
||||
"theme.blog.paginator.newerEntries": "Các bài mới hơn",
|
||||
"theme.blog.paginator.olderEntries": "Các bài cũ hơn",
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
"theme.SearchPage.inputPlaceholder": "在此输入搜索字词",
|
||||
"theme.SearchPage.noResultsText": "未找到任何结果",
|
||||
"theme.TOCCollapsible.toggleButtonLabel": "本页总览",
|
||||
"theme.blog.archive.description": "Archive",
|
||||
"theme.blog.archive.title": "Archive",
|
||||
"theme.blog.paginator.navAriaLabel": "博文列表分页导航",
|
||||
"theme.blog.paginator.newerEntries": "较新的博文",
|
||||
"theme.blog.paginator.olderEntries": "较旧的博文",
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
"theme.SearchPage.inputPlaceholder": "在此輸入搜尋字詞",
|
||||
"theme.SearchPage.noResultsText": "未找到任何結果",
|
||||
"theme.TOCCollapsible.toggleButtonLabel": "本頁導覽",
|
||||
"theme.blog.archive.description": "Archive",
|
||||
"theme.blog.archive.title": "Archive",
|
||||
"theme.blog.paginator.navAriaLabel": "部落格文章列表分頁導覽",
|
||||
"theme.blog.paginator.newerEntries": "較新的文章",
|
||||
"theme.blog.paginator.olderEntries": "較舊的文章",
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import Layout from '@theme/Layout';
|
||||
import Link from '@docusaurus/Link';
|
||||
import type {ArchiveBlogPost, Props} from '@theme/BlogArchivePage';
|
||||
import {translate} from '@docusaurus/Translate';
|
||||
|
||||
type YearProp = {
|
||||
year: string;
|
||||
posts: ArchiveBlogPost[];
|
||||
};
|
||||
|
||||
function Year({year, posts}: YearProp) {
|
||||
return (
|
||||
<>
|
||||
<h3>{year}</h3>
|
||||
<ul>
|
||||
{posts.map((post) => (
|
||||
<li key={post.metadata.date}>
|
||||
<Link to={post.metadata.permalink}>
|
||||
{post.metadata.formattedDate} - {post.metadata.title}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function YearsSection({years}: {years: YearProp[]}) {
|
||||
return (
|
||||
<section className="margin-vert--lg">
|
||||
<div className="container">
|
||||
<div className="row">
|
||||
{years.map((_props, idx) => (
|
||||
<div key={idx} className="col col--4 margin-vert--lg">
|
||||
<Year {..._props} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function listPostsByYears(blogPosts: readonly ArchiveBlogPost[]): YearProp[] {
|
||||
const postsByYear: Map<string, ArchiveBlogPost[]> = blogPosts.reduceRight(
|
||||
(posts, post) => {
|
||||
const year = post.metadata.date.split('-')[0];
|
||||
const yearPosts = posts.get(year) || [];
|
||||
return posts.set(year, [post, ...yearPosts]);
|
||||
},
|
||||
new Map(),
|
||||
);
|
||||
|
||||
return Array.from(postsByYear, ([year, posts]) => ({
|
||||
year,
|
||||
posts,
|
||||
}));
|
||||
}
|
||||
|
||||
export default function BlogArchive({archive}: Props) {
|
||||
const title = translate({
|
||||
id: 'theme.blog.archive.title',
|
||||
message: 'Archive',
|
||||
description: 'The page & hero title of the blog archive page',
|
||||
});
|
||||
const description = translate({
|
||||
id: 'theme.blog.archive.description',
|
||||
message: 'Archive',
|
||||
description: 'The page & hero description of the blog archive page',
|
||||
});
|
||||
const years = listPostsByYears(archive.blogPosts);
|
||||
return (
|
||||
<Layout title={title} description={description}>
|
||||
<header className="hero hero--primary">
|
||||
<div className="container">
|
||||
<h1 className="hero__title">{title}</h1>
|
||||
<p className="hero__subtitle">{description}</p>
|
||||
</div>
|
||||
</header>
|
||||
<main>{years.length > 0 && <YearsSection years={years} />}</main>
|
||||
</Layout>
|
||||
);
|
||||
}
|
|
@ -36,6 +36,7 @@ Accepted fields:
|
|||
| `blogSidebarCount` | <code>number | 'ALL'</code> | `5` | Number of blog post elements to show in the blog sidebar. `'ALL'` to show all blog posts; `0` to disable |
|
||||
| `blogSidebarTitle` | `string` | `'Recent posts'` | Title of the blog sidebar. |
|
||||
| `routeBasePath` | `string` | `'blog'` | URL route for the blog section of your site. **DO NOT** include a trailing slash. Use `/` to put the blog at root path. |
|
||||
| `archiveBasePath` | `string` | `'/archive'` | URL route for the archive blog section of your site. It is prepended to the `routeBasePath`. **DO NOT** include a trailing slash. |
|
||||
| `include` | `string[]` | `['**/*.{md,mdx}']` | Matching files will be included and processed. |
|
||||
| `exclude` | `string[]` | _See example configuration_ | No route will be created for matching files. |
|
||||
| `postsPerPage` | <code>number | 'ALL'</code> | `10` | Number of posts to show per page in the listing page. Use `'ALL'` to display all posts on one listing page. |
|
||||
|
|
Loading…
Add table
Reference in a new issue