Update content categorization and handle hierarchical documentation
Enhancements were implemented to support hierarchical documentation. Local CMS now respects parent ID and order attributes of content items, and content can be categories as 'blog' or 'documentation'. Changes were also made to the wordpress integration supporting these new categorizations. Introduced working with nested documentation pages.
This commit is contained in:
@@ -41,4 +41,23 @@ You will be asked to set up the Wordpress instance when you visit `http://localh
|
||||
|
||||
## Note for Wordpress REST API
|
||||
|
||||
To make the REST API in your Wordpress instance work, please change the permalink structure to `/%post%/` from the Wordpress admin panel.
|
||||
To make the REST API in your Wordpress instance work, please change the permalink structure to `/%post%/` from the Wordpress admin panel.
|
||||
|
||||
## Blog
|
||||
|
||||
To include Blog Posts from Wordpress - please create a **post** with category named `blog` and add posts to it.
|
||||
|
||||
## Documentation
|
||||
|
||||
To include Documentation from Wordpress - please create a **page** with category named `documentation` and add posts to it.
|
||||
|
||||
This involves enabling categories for pages. To do this, add the following code to your theme's `functions.php` file:
|
||||
|
||||
```php
|
||||
function add_categories_to_pages() {
|
||||
register_taxonomy_for_object_type('category', 'page');
|
||||
}
|
||||
add_action('init', 'add_categories_to_pages');
|
||||
```
|
||||
|
||||
Please refer to `wp-content/themes/twentytwentyfour/functions.php` for an example of a theme that includes this code.
|
||||
@@ -21,10 +21,28 @@ services:
|
||||
ports:
|
||||
- 8080:80
|
||||
restart: always
|
||||
working_dir: /var/www/html
|
||||
volumes:
|
||||
- ./wp-content:/var/www/html/wp-content
|
||||
environment:
|
||||
- WORDPRESS_DB_HOST=db
|
||||
- WORDPRESS_DB_USER=wordpress
|
||||
- WORDPRESS_DB_PASSWORD=wordpress
|
||||
- WORDPRESS_DB_NAME=wordpress
|
||||
- WORDPRESS_DEBUG=1
|
||||
- WORDPRESS_CONFIG_EXTRA = |
|
||||
define('FS_METHOD', 'direct');
|
||||
/** disable wp core auto update */
|
||||
define('WP_AUTO_UPDATE_CORE', false);
|
||||
|
||||
/** local environment settings */
|
||||
define('WP_CACHE', false);
|
||||
define('ENVIRONMENT', 'local');
|
||||
|
||||
/** force site home url */
|
||||
if(!defined('WP_HOME')) {
|
||||
define('WP_HOME', 'http://localhost');
|
||||
define('WP_SITEURL', WP_HOME);
|
||||
}
|
||||
volumes:
|
||||
db_data:
|
||||
@@ -20,93 +20,83 @@ export class WordpressClient implements CmsClient {
|
||||
this.apiUrl = apiUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves content items from a CMS based on the provided options.
|
||||
*
|
||||
* @param {Cms.GetContentItemsOptions} options - The options to customize the retrieval of content items.
|
||||
*/
|
||||
async getContentItems(options?: Cms.GetContentItemsOptions) {
|
||||
let endpoint: string;
|
||||
|
||||
switch (options?.type) {
|
||||
case 'post':
|
||||
endpoint = '/wp-json/wp/v2/posts?_embed';
|
||||
break;
|
||||
|
||||
case 'page':
|
||||
endpoint = '/wp-json/wp/v2/pages?_embed';
|
||||
break;
|
||||
|
||||
default:
|
||||
endpoint = '/wp-json/wp/v2/posts?_embed';
|
||||
}
|
||||
|
||||
const url = new URL(this.apiUrl + endpoint);
|
||||
const queryParams = new URLSearchParams({
|
||||
_embed: 'true',
|
||||
});
|
||||
|
||||
if (options?.limit) {
|
||||
url.searchParams.append('per_page', options.limit.toString());
|
||||
queryParams.append('per_page', options.limit.toString());
|
||||
}
|
||||
|
||||
if (options?.offset) {
|
||||
url.searchParams.append('offset', options.offset.toString());
|
||||
queryParams.append('offset', options.offset.toString());
|
||||
}
|
||||
|
||||
if (options?.categories) {
|
||||
url.searchParams.append('categories', options.categories.join(','));
|
||||
const ids = await this.getCategories({
|
||||
slugs: options.categories,
|
||||
}).then((categories) => categories.map((category) => category.id));
|
||||
|
||||
if (ids.length) {
|
||||
queryParams.append('categories', ids.join(','));
|
||||
} else {
|
||||
console.warn(
|
||||
'No categories found for the provided slugs',
|
||||
options.categories,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (options?.tags) {
|
||||
url.searchParams.append('tags', options.tags.join(','));
|
||||
const ids = await this.getCategories({
|
||||
slugs: options.tags,
|
||||
}).then((tags) => tags.map((tag) => tag.id));
|
||||
|
||||
if (ids.length) {
|
||||
queryParams.append('tags', ids.join(','));
|
||||
} else {
|
||||
console.warn('No tags found for the provided slugs', options.tags);
|
||||
}
|
||||
}
|
||||
|
||||
const response = await fetch(url.toString());
|
||||
const data = (await response.json()) as WP_REST_API_Post[];
|
||||
if (options?.parentIds && options.parentIds.length > 0) {
|
||||
queryParams.append('parent', options.parentIds.join(','));
|
||||
}
|
||||
|
||||
return Promise.all(
|
||||
data.map(async (item) => {
|
||||
// Fetch author, categories, and tags as before...
|
||||
const endpoints = [
|
||||
`/wp-json/wp/v2/pages?${queryParams.toString()}`,
|
||||
`/wp-json/wp/v2/posts?${queryParams.toString()}`,
|
||||
];
|
||||
|
||||
const urls = endpoints.map((endpoint) => `${this.apiUrl}${endpoint}`);
|
||||
|
||||
const responses = await Promise.all(
|
||||
urls.map((url) => fetch(url).then((value) => value.json())),
|
||||
).then((values) => values.flat().filter(Boolean));
|
||||
|
||||
return await Promise.all(
|
||||
responses.map(async (item: WP_REST_API_Post) => {
|
||||
let parentId: string | undefined;
|
||||
|
||||
if (!item) {
|
||||
throw new Error('Failed to fetch content items');
|
||||
}
|
||||
|
||||
if (item.parent) {
|
||||
parentId = item.parent.toString();
|
||||
}
|
||||
|
||||
let children: Cms.ContentItem[] = [];
|
||||
|
||||
const embeddedChildren = (
|
||||
item._embedded ? item._embedded['wp:children'] ?? [] : []
|
||||
) as WP_REST_API_Post[];
|
||||
|
||||
if (options?.depth && options.depth > 0) {
|
||||
children = await Promise.all(
|
||||
embeddedChildren.map(async (child) => {
|
||||
const childAuthor = await this.getAuthor(child.author);
|
||||
|
||||
const childCategories = await this.getCategoriesByIds(
|
||||
child.categories ?? [],
|
||||
);
|
||||
|
||||
const childTags = await this.getTagsByIds(child.tags ?? []);
|
||||
|
||||
return {
|
||||
id: child.id.toString(),
|
||||
title: child.title.rendered,
|
||||
type: child.type as Cms.ContentType,
|
||||
image: this.getFeaturedMedia(child),
|
||||
description: child.excerpt.rendered,
|
||||
url: child.link,
|
||||
content: child.content.rendered,
|
||||
slug: child.slug,
|
||||
publishedAt: new Date(child.date),
|
||||
author: childAuthor?.name,
|
||||
categories: childCategories,
|
||||
tags: childTags,
|
||||
parentId: child.parent?.toString(),
|
||||
};
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
const author = await this.getAuthor(item.author);
|
||||
const categories = await this.getCategoriesByIds(item.categories ?? []);
|
||||
const tags = await this.getTagsByIds(item.tags ?? []);
|
||||
const image = item.featured_media ? this.getFeaturedMedia(item) : '';
|
||||
const order = item.menu_order ?? 0;
|
||||
|
||||
return {
|
||||
id: item.id.toString(),
|
||||
@@ -120,19 +110,34 @@ export class WordpressClient implements CmsClient {
|
||||
author: author?.name,
|
||||
categories: categories,
|
||||
tags: tags,
|
||||
type: item.type as Cms.ContentType,
|
||||
parentId,
|
||||
children,
|
||||
order,
|
||||
children: [],
|
||||
};
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
async getContentItemById(slug: string, type: 'posts' | 'pages' = 'posts') {
|
||||
const url = `${this.apiUrl}/wp-json/wp/v2/${type}?slug=${slug}&_embed`;
|
||||
const response = await fetch(url);
|
||||
const data = (await response.json()) as WP_REST_API_Post[];
|
||||
const item = data[0];
|
||||
async getContentItemById(slug: string) {
|
||||
const searchParams = new URLSearchParams({
|
||||
_embed: 'true',
|
||||
slug,
|
||||
});
|
||||
|
||||
const endpoints = [
|
||||
`/wp-json/wp/v2/pages?${searchParams.toString()}`,
|
||||
`/wp-json/wp/v2/posts?${searchParams.toString()}`,
|
||||
];
|
||||
|
||||
const promises = endpoints.map((endpoint) =>
|
||||
fetch(this.apiUrl + endpoint).then((res) => res.json()),
|
||||
);
|
||||
|
||||
const responses = await Promise.all(promises).then((values) =>
|
||||
values.filter(Boolean),
|
||||
);
|
||||
|
||||
const item = responses[0][0] as WP_REST_API_Post;
|
||||
|
||||
if (!item) {
|
||||
return;
|
||||
@@ -146,9 +151,9 @@ export class WordpressClient implements CmsClient {
|
||||
return {
|
||||
id: item.id.toString(),
|
||||
image,
|
||||
order: item.menu_order ?? 0,
|
||||
url: item.link,
|
||||
description: item.excerpt.rendered,
|
||||
type: item.type as Cms.ContentType,
|
||||
children: [],
|
||||
title: item.title.rendered,
|
||||
content: item.content.rendered,
|
||||
@@ -157,6 +162,7 @@ export class WordpressClient implements CmsClient {
|
||||
author: author?.name,
|
||||
categories,
|
||||
tags,
|
||||
parentId: item.parent?.toString(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -207,10 +213,22 @@ export class WordpressClient implements CmsClient {
|
||||
queryParams.append('offset', options.offset.toString());
|
||||
}
|
||||
|
||||
if (options?.slugs) {
|
||||
const slugs = options.slugs.join(',');
|
||||
|
||||
queryParams.append('slug', slugs);
|
||||
}
|
||||
|
||||
const response = await fetch(
|
||||
`${this.apiUrl}/wp-json/wp/v2/categories?${queryParams.toString()}`,
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
console.error('Failed to fetch categories', await response.json());
|
||||
|
||||
throw new Error('Failed to fetch categories');
|
||||
}
|
||||
|
||||
const data = (await response.json()) as WP_REST_API_Category[];
|
||||
|
||||
return data.map((item) => ({
|
||||
@@ -231,10 +249,21 @@ export class WordpressClient implements CmsClient {
|
||||
queryParams.append('offset', options.offset.toString());
|
||||
}
|
||||
|
||||
if (options?.slugs) {
|
||||
const slugs = options.slugs.join(',');
|
||||
queryParams.append('slug', slugs);
|
||||
}
|
||||
|
||||
const response = await fetch(
|
||||
`${this.apiUrl}/wp-json/wp/v2/tags?${queryParams.toString()}`,
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
console.error('Failed to fetch tags', await response.json());
|
||||
|
||||
throw new Error('Failed to fetch tags');
|
||||
}
|
||||
|
||||
const data = (await response.json()) as WP_REST_API_Tag[];
|
||||
|
||||
return data.map((item) => ({
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<?php
|
||||
|
||||
<footer>
|
||||
</footer>
|
||||
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
function register_category_and_tag_with_pages(){
|
||||
/*add categories and tags to pages*/
|
||||
register_taxonomy_for_object_type('category', 'page');
|
||||
register_taxonomy_for_object_type('post_tag', 'page');
|
||||
}
|
||||
add_action( 'init', 'register_category_and_tag_with_pages');
|
||||
|
||||
function register_pre_get_category_and_tag_with_pages( $query ) {
|
||||
|
||||
if ( is_admin() || ! $query->is_main_query() ) {
|
||||
return;
|
||||
}
|
||||
/*view categories and tags archive pages */
|
||||
if($query->is_category && $query->is_main_query()){
|
||||
$query->set('post_type', array( 'post', 'page'));
|
||||
}
|
||||
if($query->is_tag && $query->is_main_query()){
|
||||
$query->set('post_type', array( 'post', 'page'));
|
||||
}
|
||||
}
|
||||
add_action( 'pre_get_posts', 'register_pre_get_category_and_tag_with_pages');
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html <?php language_attributes(); ?>>
|
||||
<head>
|
||||
<meta charset="<?php bloginfo( 'charset' ); ?>">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<?php wp_head(); ?>
|
||||
</head>
|
||||
|
||||
<body <?php body_class(); ?>>
|
||||
|
||||
<div class="wrap">
|
||||
<div id="primary" class="content-area">
|
||||
<main id="main" class="site-main" role="main">
|
||||
|
||||
<?php
|
||||
if (have_posts()) :
|
||||
/* Start the Loop */
|
||||
while (have_posts()) : the_post();
|
||||
?>
|
||||
<div>
|
||||
<a href="<?php the_permalink(); ?>"><h3><?php the_title(); ?></h3></a>
|
||||
</div>
|
||||
<?php
|
||||
endwhile;
|
||||
/* End the Loop */
|
||||
else :
|
||||
// Nothing
|
||||
endif;
|
||||
?>
|
||||
|
||||
</main><!-- #main -->
|
||||
</div><!-- #primary -->
|
||||
</div><!-- .wrap -->
|
||||
|
||||
<?php get_footer(); ?>
|
||||
<?php wp_footer(); ?>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user