Skip to content

Commit

Permalink
Merge branch 'develop' into feature/wp-phpcs-3
Browse files Browse the repository at this point in the history
  • Loading branch information
nicholasio committed Feb 29, 2024
2 parents ae4315f + 5396b23 commit 564dccd
Show file tree
Hide file tree
Showing 52 changed files with 3,885 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
"@headstartwp/next": patch
---

Fix invalid structured data ld+json
Hotfix preview alternative headers
21 changes: 20 additions & 1 deletion docs/documentation/01-Getting Started/headless-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,4 +210,23 @@ This option control how redirects are handled. There are 2 supported methods of

## debug

You can enable log debugging for both requests and redirects. `debug.requests` will enable logging all API requests made by the framework and `debug.redirects` will log all attempts to detect and fetch a redirect from WordPress.
You can enable log debugging for both requests and redirects. `debug.requests` will enable logging all API requests made by the framework and `debug.redirects` will log all attempts to detect and fetch a redirect from WordPress.

## preview

### alternativeAuthorizationHeader

Tells HeadstartWP to use an alternative header (`X-HeadstartWP-Authorization`) instead of the default `Authorization` header for making authenticated preview requests.

Make sure you have HeadstartWP plugin >= 1.0.1, `@headstartwp/core` >= 1.3.1 and `@headstartwp/next`>= 1.3.1 to use this setting.

```js
module.exports = {
// other configs.
// ...

preview: {
alternativeAuthorizationHeader: true
}
}
```
163 changes: 163 additions & 0 deletions docs/documentation/02 - Data Fetching/useSearchNative.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
---
slug: /data-fetching/usesearch-native
sidebar_position: 5
---

# The useSearchNative hook

> The [useSearchNative](/api/modules/headstartwp_next#usesearchnative) hook is the Next.js binding for the [useFetchSearchNative](/api/namespaces/headstartwp_core.react#usefetchsearchnative).
The `useSearchNative` hook is the implementation of core [Search Results](https://developer.wordpress.org/rest-api/reference/search-results/) endpoint.

:::caution
This hook was introduced in `@headstartwp/[email protected]`, `@headstartwp/[email protected]` and requires the the HeadstartWP WordPress plugin >= 1.1.0
:::caution

The headstartwp WordPress plugin does additional customizations to ensure the Search Results endpoints return all the embeddable data associated with search results.

## Basic Usage

Assuming a `src/pages/search/[[...path]].js` route with the following content.

:::info
This example is using the optional catch-all route `[[..path]].js` because we want the `/search` route to be handled by the same file and fetch the latest posts.
:::info

```js title="src/pages/search/[[...path]].js"
import { useSearchNative } from '@headstartwp/next';

const ArchivePage = () => {
const { loading, error, data } = useSearchNative({ per_page: 10 });

if (loading) {
return 'Loading...';
}

if (error) {
return 'error...';
}

if (data.pageInfo.totalItems === 0) {
return 'Nothing found';
}

return (
<>
<h1>Search Results</h1>
<ul>
{data.searchResults.map((item) => (
<li key={item.id}>
<Link href={item.url}>
{item.id} - {item.title}
</Link>
</li>
))}
</ul>
</>
);
};
```

The route will automatically render the latest 10 results if no search term is provided. The following paths are automatically handled:

- /search/search-term
- /search/search-term/page/2
- /search

## Searching from multiple post types

You can specify any of the supported parameters described in the [Search Results](https://developer.wordpress.org/rest-api/reference/search-results/#arguments) endpoint documentation.

```js title="src/pages/search/[[...path]].js"
import { useSearchNative } from '@headstartwp/next';

const ArchivePage = () => {
const { loading, error, data } = useSearchNative({
per_page: 10,
type: 'post',
subtype: ['post', 'page']
});

if (loading) {
return 'Loading...';
}

if (error) {
return 'error...';
}

if (data.pageInfo.totalItems === 0) {
return 'Nothing found';
}

return (
<>
<h1>Search Results</h1>
<ul>
{data.searchResults.map((item) => (
<li key={item.id}>
<Link href={item.url}>
{item.id} - {item.title}
</Link>
</li>
))}
</ul>
</>
);
};
```

## Searching for terms

You can also search for terms:

```js title="src/pages/terms/search/[[...path]].js"
import { useSearch } from '@headstartwp/next';

const ArchivePage = () => {
const { loading, error, data } = useSearchNative({
per_page: 10,
type: 'term',
subtype: ['category', 'category']
});

if (loading) {
return 'Loading...';
}

if (error) {
return 'error...';
}

if (data.pageInfo.totalItems === 0) {
return 'Nothing found';
}

return (
<>
<h1>Search Results</h1>
<ul>
{data.searchResults.map((item) => (
<li key={item.id}>
<Link href={item.url}>
{item.id} - {item.title}
</Link>
</li>
))}
</ul>
</>
);
};
```

## Accessing embeddable data
By default, the Search Results endpoints only return the object of the associated search results but do not return embeddable data of the search results entities themselves. For instance, when searching for posts, even if you pass the `_embed` parameter, WordPress won't return the associated term objects, author objects etc.

HeadstartWP plugin extends the core endpoint so that it returns these embedded objects to avoid the need for additional queries. Check the [PostSearchEntity](/api/interfaces/headstartwp_core.PostSearchEntity/) and [TermSearcheEntity](/api/interfaces/headstartwp_core.TermSearchEntity/).

## QueriedObject

The `useNativeSearch` hook also exposes a `queriedObject`.

The queried object for this hook is an object of type [SearchEntity](/api/interfaces/headstartwp_core.SearchEntity/).

25 changes: 21 additions & 4 deletions docs/documentation/06-WordPress Integration/previews.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ slug: /wordpress-integration/previews

# Previews

The preview feature requires the 10up's headless WordPress plugin installed. The preview functionality is built on top of [Next.js preview API](https://nextjs.org/docs/advanced-features/preview-mode). It uses a short-lived JWT token generated on the WordPress side that can only be used for previewing, this means it is not necessary to set up a hardcoded secret between WP and Next.js.
The preview feature requires the HeadstartWP plugin installed. The preview functionality is built on top of [Next.js preview API](https://nextjs.org/docs/advanced-features/preview-mode). It uses a short-lived JWT token generated on the WordPress side that can only be used for previewing, this means it is not necessary to set up a hardcoded secret between WP and Next.js.

For previews to work, make sure the frontend URL is entered in WP settings as per instructions in [Installing WordPress Plugin](/learn/getting-started/installing-wordpress-plugin).

The logic for generating the JWT token and redirecting to the preview endpoint can be seen [here](https://github.com/10up/headstartwp/blob/develop/wp/headless-wp/includes/classes/Preview/preview.php).
The logic for generating the JWT token and redirecting it to the preview endpoint can be seen [here](https://github.com/10up/headstartwp/blob/develop/wp/headless-wp/includes/classes/Preview/preview.php).
```php
$token = PreviewToken::generate(
[
Expand Down Expand Up @@ -42,7 +42,7 @@ Below is a summary of the preview workflow.

## Usage

The Next.js project **must** expose a `api/preview` endpoint that uses the [previewHandler](/api/modules/headstartwp_next/#previewhandler).
The Next.js project **must** expose an `api/preview` endpoint that uses the [previewHandler](/api/modules/headstartwp_next/#previewhandler).

```javascript
//src/pages/api/preview.js
Expand Down Expand Up @@ -152,4 +152,21 @@ The JWT token expires after 5 min by default, after this period, open another pr

**I'm unable to preview a custom post type**

Make sure you defined the right `single` property when registering the custom post type. See [headless config docs](/learn/getting-started/headless-config/#customposttypes). The `single` property must match the route prefix for the custom post type.
Make sure you defined the right `single` property when registering the custom post type. See [headless config docs](/learn/getting-started/headless-config/#customposttypes). The `single` property must match the route prefix for the custom post type.

**I have a custom authentication using the Authorization header, how can I use the preview functionality?**

Make sure you have HeadstartWP plugin >= 1.0.1, `@headstartwp/core` >= 1.3.1 and `@headstartwp/next`>= 1.3.1. Then in your `headstartwp.config.js` add the following config:

```js
module.exports = {
// other configs.
// ...

preview: {
alternativeAuthorizationHeader: true
}
}
```

This will tell HeadstartWP to use an alternative header (`X-HeadstartWP-Authorization`) instead of the default `Authorization` header.
12 changes: 12 additions & 0 deletions packages/core/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# @headstartwp/core

## 1.3.1

### Patch Changes

- 0bd8e415: Add ability to preview using an alternative authorization header

## 1.3.0

### Minor Changes

- 8452279a: Implement WordPress native search endpoint

## 1.2.0

### Minor Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@headstartwp/core",
"version": "1.2.0",
"version": "1.3.1",
"description": "`@headstartwp/core` is the core package that houses framework-agnostic components and utilities for building headless sites with WordPress.",
"homepage": "https://github.com/10up/headstartwp/blob/develop/packages/core/README.md",
"license": "MIT",
Expand Down
46 changes: 42 additions & 4 deletions packages/core/src/data/strategies/AbstractFetchStrategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,20 @@ export interface FetchOptions {
*/
bearerToken?: string;

/**
* The preview token to use for the request.
*
* These are tokens issued by the HeadstartWP plugin and is used to authenticate previews
*/
previewToken?: string;

/**
* Flag to enable using the alternative authorization header.
*
* This can be useful if you have separate authentication on your project.
*/
alternativePreviewAuthorizationHeader?: boolean;

/**
* Whether to burst cache by appending a timestamp to the query
*/
Expand Down Expand Up @@ -224,6 +238,21 @@ export abstract class AbstractFetchStrategy<E, Params extends EndpointParams, R
};
}

getPreviewHeaderName(options: Partial<FetchOptions> = {}) {
return options.alternativePreviewAuthorizationHeader
? 'X-HeadstartWP-Authorization'
: 'Authorization';
}

getPreviewAuthHeader(options: Partial<FetchOptions> = {}) {
let previewAuthHeader = '';
if (options.previewToken) {
previewAuthHeader = `Bearer ${options.previewToken}`;
}

return previewAuthHeader;
}

getAuthHeader(options: Partial<FetchOptions> = {}) {
let bearerAuthHeader = '';
if (options.bearerToken) {
Expand Down Expand Up @@ -266,13 +295,22 @@ export abstract class AbstractFetchStrategy<E, Params extends EndpointParams, R
const { burstCache = false } = options;

const args = {};

const headers: Record<string, string> = {};
const authHeader = this.getAuthHeader(options);

if (authHeader) {
headers.Authorization = authHeader;
}

const previewAuthHeader = this.getPreviewAuthHeader(options);

if (options.previewToken) {
headers[this.getPreviewHeaderName(options)] = previewAuthHeader;
}

if (Object.keys(headers).length > 0) {
// @ts-expect-error
args.headers = {
Authorization: authHeader,
};
args.headers = headers;
}

const result = await apiGet(`${this.baseURL}${url}`, args, burstCache);
Expand Down
Loading

0 comments on commit 564dccd

Please sign in to comment.