Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@

All notable changes to `WP Curate` will be documented in this file.

## 3.0.0
## Unreleased

- Enhancement: Enable pinning scheduled posts via `wp_curate_include_future_posts` filter.

## 3.0.0 - 2025-10-31

- Support for previews of Query block and Patterns.
- Pattern/Variation picker when inserting a Query block.
Expand Down
32 changes: 29 additions & 3 deletions blocks/post/edit.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import { useState } from 'react';
import classnames from 'classnames';
import type { WP_REST_API_Post as WpRestApiPost } from 'wp-types'; // eslint-disable-line camelcase

// @ts-expect-error BlockContextProvider not available in types yet.
import { InnerBlocks, useBlockProps, BlockContextProvider } from '@wordpress/block-editor';
import { PostPicker } from '@alleyinteractive/block-editor-tools';
import { PostPicker, usePostById } from '@alleyinteractive/block-editor-tools';
import { dispatch, select, useSelect } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
import { Button } from '@wordpress/components';
import { Button, Notice } from '@wordpress/components';
import { useCallback } from '@wordpress/element';

import type { Block } from '../../types/block';
import NoRender from './norender';
import SearchFilters from '../../components/SearchFilters';
import recursivelyFindBlocksByName from '../../services/recursivelyFindBlocksByName';
import { postTypeWithFuture } from '../../services/utils';

import type {
Term,
Expand Down Expand Up @@ -47,6 +50,7 @@ interface PostTypeOrTerm {
interface Window {
wpCurateQueryBlock: {
allowedPostTypes: PostTypeOrTerm[];
includeFuturePosts: boolean;
};
}

Expand All @@ -73,6 +77,7 @@ export default function Edit({
const {
wpCurateQueryBlock: {
allowedPostTypes = [],
includeFuturePosts,
} = {},
} = (window as any as Window);

Expand Down Expand Up @@ -282,6 +287,13 @@ export default function Edit({

const shouldShowFilter = displayTypes.length !== postTypes.length
|| Object.values(terms).some((termList) => Array.isArray(termList) && termList.length > 0);

const postObj = usePostById(
postId,
// @ts-ignore This function does work with this argument.
includeFuturePosts ? postTypeWithFuture : null,
) as WpRestApiPost | null;

return (
<div
{...useBlockProps(
Expand All @@ -296,6 +308,15 @@ export default function Edit({
},
)}
>
{typeof postObj === 'object' && postObj !== null && 'status' in postObj && postObj.status === 'future' ? (
<Notice
status="warning"
isDismissible={false}
>
{__('Scheduled', 'wp-curate')}
</Notice>
) : null}

<BlockContextProvider value={{ postId }}>
<InnerBlocks />
</BlockContextProvider>
Expand All @@ -314,6 +335,8 @@ export default function Edit({
onUpdate={updatePost}
onReset={resetPost}
value={selected ?? 0}
// @ts-ignore This function does work with this prop.
getPostType={includeFuturePosts ? postTypeWithFuture : null}
previewRender={(NoRender)}
className="wp-curate-post-block__post-picker"
selectText={__('Pin a Post', 'wp-curate')}
Expand All @@ -326,7 +349,10 @@ export default function Edit({
setFiltered={setFiltered}
/>
)}
params={params}
params={{
...params,
wp_curate_include_future: includeFuturePosts,
}}
/>
{
// If this post isn't already in the posts list, show a button to pin it.
Expand Down
4 changes: 4 additions & 0 deletions blocks/query/edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ interface Window {
allowedTaxonomies: PostTypeOrTerm[];
parselyAvailable: string,
maxPosts: string,
includeFuturePosts: boolean,
};
}

Expand Down Expand Up @@ -82,6 +83,7 @@ export default function Edit({
allowedTaxonomies = [],
parselyAvailable = 'false',
maxPosts = '10',
includeFuturePosts,
} = {},
} = (window as any as Window);

Expand Down Expand Up @@ -228,6 +230,7 @@ export default function Edit({
per_page: postsToInclude.length,
type: postTypeString,
include: postsToInclude,
status: includeFuturePosts ? ['publish', 'future'] : 'publish',
_locale: 'user',
context: 'edit',
},
Expand All @@ -244,6 +247,7 @@ export default function Edit({
manualPosts,
setAttributes,
postTypeString,
includeFuturePosts,
]);

// When numberOfPosts changes, update manualPosts array.
Expand Down
22 changes: 15 additions & 7 deletions blocks/query/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ function wp_curate_query_block_init(): void {
'wp-curate-query-editor-script',
'wpCurateQueryBlock',
[
'allowedPostTypes' => array_filter(
'allowedPostTypes' => array_filter(
array_map(
function ( $slug ) {
$post_type_object = get_post_type_object( $slug );
Expand All @@ -77,7 +77,7 @@ function ( $slug ) {
$allowed_post_types
)
),
'allowedTaxonomies' => array_filter(
'allowedTaxonomies' => array_filter(
array_map(
function ( $slug ) {
$taxonomy = get_taxonomy( $slug );
Expand All @@ -95,16 +95,16 @@ function ( $slug ) {
$allowed_taxonomies,
),
),
'parselyAvailable' => $parsely_available ? 'true' : 'false',
'maxPosts' => $max_posts,
'parselyAvailable' => $parsely_available ? 'true' : 'false',
'maxPosts' => $max_posts,
/**
* Filters the order by options shown in the sidebar of the query block.
*
* @param array<string, string> $options The order by options as value => label pairs.
*
* @since 2.6.4
*/
'rawOrderByOptions' => apply_filters( 'wp_curate_order_by_options', [
'rawOrderByOptions' => apply_filters( 'wp_curate_order_by_options', [
'date' => __( 'Date', 'wp-curate' ),
'title' => __( 'Title', 'wp-curate' ),
] ),
Expand All @@ -115,8 +115,16 @@ function ( $slug ) {
*
* @since 2.6.4
*/
'orderByMetaKeys' => apply_filters( 'wp_curate_order_by_meta_keys', [] ),
]
'orderByMetaKeys' => apply_filters( 'wp_curate_order_by_meta_keys', [] ),
/**
* Filters whether to allow scheduled posts to be selected in the post picker.
*
* @since 3.1.0
*
* @param bool $include_future_posts Whether to include scheduled posts.
*/
'includeFuturePosts' => apply_filters( 'wp_curate_include_future_posts', false ),
],
);
}
add_action( 'init', 'wp_curate_query_block_init', 900 );
Expand Down
5 changes: 4 additions & 1 deletion blocks/subquery/edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ interface Window {
allowedTaxonomies: PostTypeOrTerm[];
parselyAvailable: string,
maxPosts: number,
includeFuturePosts: boolean,
};
}

Expand Down Expand Up @@ -86,6 +87,7 @@ export default function Edit({
allowedTaxonomies = [],
parselyAvailable = 'false',
maxPosts = 10,
includeFuturePosts,
} = {},
} = (window as any as Window);

Expand Down Expand Up @@ -225,6 +227,7 @@ export default function Edit({
per_page: postsToInclude.length,
type: postTypeString,
include: postsToInclude,
status: includeFuturePosts ? ['publish', 'future'] : 'publish',
_locale: 'user',
context: 'edit',
},
Expand All @@ -248,7 +251,7 @@ export default function Edit({
};

updateValidPosts();
}, [isFirstPost, manualPosts, postTypeString, setAttributes]);
}, [includeFuturePosts, isFirstPost, manualPosts, postTypeString, setAttributes]);

/**
* Check if deduplication is needed when validPosts are available.
Expand Down
15 changes: 13 additions & 2 deletions components/QueryControls/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Fragment, useState } from 'react';
import { PostPicker, TermSelector, Checkboxes } from '@alleyinteractive/block-editor-tools';
import classnames from 'classnames';

import { PostPicker, TermSelector, Checkboxes } from '@alleyinteractive/block-editor-tools';
import {
PanelBody,
PanelRow,
Expand All @@ -15,7 +16,9 @@ import { createInterpolateElement } from '@wordpress/element';
import { __, sprintf } from '@wordpress/i18n';
import { useDispatch } from '@wordpress/data';
import { store as noticesStore } from '@wordpress/notices';

import SearchFilters from '../SearchFilters';
import { postTypeWithFuture } from '../../services/utils';

import type {
Option,
Expand All @@ -31,6 +34,7 @@ interface Window {
wpCurateQueryBlock: {
rawOrderByOptions: Record<string, string>;
orderByMetaKeys: string[];
includeFuturePosts: boolean;
};
}

Expand Down Expand Up @@ -100,6 +104,7 @@ export default function QueryControls({
date: __('Date', 'wp-curate'),
},
orderByMetaKeys = [],
includeFuturePosts,
} = {},
} = (window as any as Window);

Expand Down Expand Up @@ -204,6 +209,7 @@ export default function QueryControls({

const shouldShowFilter = displayTypes.length !== postTypes.length
|| Object.values(terms).some((termList) => Array.isArray(termList) && termList.length > 0);

return (
<>
<InspectorControls>
Expand Down Expand Up @@ -254,14 +260,19 @@ export default function QueryControls({
onUpdate={(id: number) => { setManualPost(id, index); }}
value={manualPosts[index] || 0}
className="manual-posts__picker"
// @ts-ignore This function does work with this prop.
getPostType={includeFuturePosts ? postTypeWithFuture : null}
filters={(
<SearchFilters
shouldShowFilter={shouldShowFilter}
filtered={filtered}
setFiltered={setFiltered}
/>
)}
params={params}
params={{
...params,
wp_curate_include_future: Number(includeFuturePosts),
}}
/>
</PanelRow>
))}
Expand Down
13 changes: 13 additions & 0 deletions services/deduplicate/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ import { store as blockEditorStore } from '@wordpress/block-editor';
import type { Block } from '../../types/block';
import recursivelyFindBlocksByName from '../recursivelyFindBlocksByName';

interface Window {
wpCurateQueryBlock: {
includeFuturePosts: boolean;
};
}

const usedIds = new Map();
const curatedIds = new Map();

Expand Down Expand Up @@ -83,6 +89,12 @@ export function mainDedupe() {
return;
}

const {
wpCurateQueryBlock: {
includeFuturePosts,
} = {},
} = (window as any as Window);

running = true;
// Clear the flag for another run.
redo = false;
Expand Down Expand Up @@ -202,6 +214,7 @@ export function mainDedupe() {
type: postTypeString,
include: templateIds.join(','),
orderby: 'include',
wp_curate_include_future: includeFuturePosts,
},
queryId: 0,
},
Expand Down
31 changes: 31 additions & 0 deletions services/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
* @link https://github.com/WordPress/gutenberg/blob/trunk/packages/block-library/src/query/utils.js
*/

import apiFetch from '@wordpress/api-fetch';
import { useSelect } from '@wordpress/data';
import { useMemo } from '@wordpress/element';
import { store as blockEditorStore } from '@wordpress/block-editor';
import {
cloneBlock,
store as blocksStore,
} from '@wordpress/blocks';
import { addQueryArgs } from '@wordpress/url';

import type { BlockInstance, BlockVariation } from '@wordpress/blocks';
import type { BlockPattern } from '../blocks/query/types';
Expand Down Expand Up @@ -170,3 +172,32 @@ export const usePatterns = (clientId: string, name: string): BlockPattern[] => u
},
[name, clientId],
);

/**
* Custom function for use with usePostById to get the post type that includes scheduled posts.
*
* @param {number} postId The post ID.
* @return {Promise<string|null>} The post type or null if not found.
*/
export const postTypeWithFuture = async (postId: number) => {
let type = null;

const path = addQueryArgs('/wp/v2/search', {
include: postId,
wp_curate_include_future: 1,
});

const results = await apiFetch({ path });

if (
Array.isArray(results)
&& results.length > 0
&& typeof results[0] === 'object'
&& results[0] !== null
&& 'subtype' in results[0]
) {
type = results[0].subtype;
}

return type;
};
Loading
Loading