Skip to content

Commit 2060145

Browse files
authored
Merge pull request #112 from a8cteam51/feature/jp-related-posts-query-loop
Added Jetpack related post query block variation
2 parents 7d3635b + dab6894 commit 2060145

6 files changed

Lines changed: 21414 additions & 0 deletions

File tree

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## 0.1.0 (2025-11-12)
2+
3+
### Initial release
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<?php
2+
/**
3+
* Plugin Autoupdate Filter Self Update class.
4+
* Sets up autoupdates for this GitHub-hosted plugin.
5+
*
6+
* @package wpcomsp
7+
*/
8+
9+
if ( ! defined( 'ABSPATH' ) ) {
10+
exit; // Exit if accessed directly.
11+
}
12+
13+
/**
14+
* Class WPCOMSP_Blocks_Self_Update
15+
* Sets up autoupdates for this GitHub-hosted plugin.
16+
*/
17+
class WPCOMSP_Blocks_Self_Update {
18+
19+
public static $instance;
20+
21+
/**
22+
* Get instance of this class.
23+
*
24+
* @return WPCOMSP_Blocks_Self_Update
25+
*/
26+
public static function get_instance() {
27+
if ( ! self::$instance ) {
28+
self::$instance = new self();
29+
}
30+
31+
return self::$instance;
32+
}
33+
34+
/**
35+
* Initialize WordPress hooks
36+
*
37+
* @return void
38+
*/
39+
public function hooks() {
40+
add_filter( 'update_plugins_opsoasis.wpspecialprojects.com', array( $this, 'self_update' ), 10, 3 );
41+
}
42+
43+
/**
44+
* Check for updates to this plugin
45+
*
46+
* @param array $update Array of update data.
47+
* @param array $plugin_data Array of plugin data.
48+
* @param string $plugin_file Path to plugin file.
49+
*
50+
* @return array|boolean Array of update data or false if no update available.
51+
*/
52+
public function self_update( $update, array $plugin_data, string $plugin_file ) {
53+
// Already completed update check elsewhere.
54+
if ( ! empty( $update ) ) {
55+
return $update;
56+
}
57+
58+
$plugin_filename_parts = explode( '/', $plugin_file );
59+
60+
// Ask opsoasis.wpspecialprojects.com if there's an update.
61+
$response = wp_remote_get(
62+
'https://opsoasis.wpspecialprojects.com/wp-json/opsoasis-blocks-version-manager/v1/update-check',
63+
array(
64+
'body' => array(
65+
'plugin' => $plugin_filename_parts[0],
66+
'version' => $plugin_data['Version'],
67+
),
68+
)
69+
);
70+
71+
$response_code = wp_remote_retrieve_response_code( $response );
72+
73+
// Bail if this plugin wasn't found on opsoasis.wpspecialprojects.com.
74+
if ( '' === $response_code || 404 === $response_code || 202 === $response_code ) {
75+
return $update;
76+
}
77+
78+
$updated_version = wp_remote_retrieve_body( $response );
79+
$updated_array = json_decode( $updated_version, true );
80+
81+
// Validate the response structure
82+
if ( ! is_array( $updated_array ) || ! isset( $updated_array['slug'], $updated_array['version'], $updated_array['package_url'] ) ) {
83+
return $update;
84+
}
85+
86+
return array(
87+
'slug' => $updated_array['slug'],
88+
'version' => $updated_array['version'],
89+
'url' => $updated_array['package_url'],
90+
'package' => $updated_array['package_url'],
91+
);
92+
}
93+
}
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
<?php
2+
/**
3+
* Plugin Name: Jetpack Related Posts Query Loop
4+
* Description: Adds a query loop variation to display related posts from Jetpack.
5+
* Version: 0.1.0
6+
* Author: WordPress Special Projects Team
7+
* Author URI: https://wpspecialprojects.wordpress.com/
8+
* Update URI: https://opsoasis.wpspecialprojects.com/jp-related-posts-query-loop/
9+
* License: GPL-2.0-or-later
10+
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
11+
* Text Domain: a8csp-jprpql
12+
* Requires at least: 6.6
13+
* Tested up to: 6.8.3
14+
* Requires PHP: 7.4
15+
*
16+
* @package a8csp
17+
*/
18+
19+
if ( ! defined( 'ABSPATH' ) ) {
20+
exit; // Exit if accessed directly.
21+
}
22+
23+
// If no other WPCOMSP Block Plugin added the self update class, add it.
24+
if ( ! class_exists( 'WPCOMSP_Blocks_Self_Update' ) ) {
25+
require __DIR__ . '/classes/class-wpcomsp-blocks-self-update.php';
26+
27+
WPCOMSP_Blocks_Self_Update::get_instance()->hooks();
28+
}
29+
30+
/**
31+
* Setup auto-updates for this plugin from our monorepo.
32+
* Done in an anonymous function for simplicity in making this a drop-in snippet.
33+
*
34+
* @param array $blocks Array of plugin files.
35+
*
36+
* @return array
37+
*/
38+
add_filter(
39+
'wpcomsp_installed_blocks',
40+
function ( $blocks ) {
41+
// Add the plugin slug here to enable autoupdates.
42+
$blocks[] = 'jp-related-posts-query-loop';
43+
44+
return $blocks;
45+
}
46+
);
47+
48+
/**
49+
* Add block variations.
50+
*
51+
* @param array<mixed> $variations Array of registered variations for a block type.
52+
* @param WP_Block_Type $block_type The full block type object.
53+
*
54+
* @return array<mixed>
55+
*
56+
* @SuppressWarnings(ExcessiveMethodLength)
57+
*/
58+
function a8csp_jrpql_register_related_query_block_variation( $variations, $block_type ) {
59+
60+
if ( 'core/query' === $block_type->name ) {
61+
$variations[] = array(
62+
'icon' => 'star-filled',
63+
'name' => 'jp-related-posts-query-loop',
64+
'title' => esc_html__( 'Related Posts Query', 'a8csp-jprpql' ),
65+
'description' => esc_html__( 'Related posts query block.', 'a8csp-jprpql' ),
66+
'keywords' => array(
67+
/* translators: search keyword for block variation */
68+
esc_html__( 'query', 'a8csp-jprpql' ),
69+
/* translators: search keyword for block variation */
70+
esc_html__( 'related', 'a8csp-jprpql' ),
71+
),
72+
'attributes' => array(
73+
'align' => 'wide',
74+
'query' => array(
75+
'query_type' => 'related-posts',
76+
'perPage' => 4,
77+
'inherit' => false,
78+
'postType' => 'post',
79+
),
80+
'namespace' => 'a8csp-jrpql/related-posts',
81+
),
82+
'allowedControls' => array( 'postCount', 'postType' ),
83+
'isActive' => array( 'namespace' ),
84+
'isDefault' => false,
85+
);
86+
}
87+
88+
return $variations;
89+
}
90+
add_filter( 'get_block_type_variations', 'a8csp_jrpql_register_related_query_block_variation', 10, 2 );
91+
92+
/**
93+
* Add hooks for query blocks to store post ids and related previous post ids for blocks
94+
* with the query > related_previous_posts attribute.
95+
*
96+
* @param string|null $block_content The pre-rendered content. Default null.
97+
* @param array<string,mixed> $block An associative array of the block being rendered. See WP_Block_Parser_Block.
98+
*
99+
* @return string|null
100+
*/
101+
function a8csp_jrpql_pre_render_block_query_block_related( $block_content, $block ) {
102+
103+
if ( 'core/query' !== $block['blockName'] ) {
104+
return $block_content;
105+
}
106+
107+
// if flag is set -> related previous posts
108+
if ( isset( $block['attrs']['namespace'] ) && 'a8csp-jrpql/related-posts' === $block['attrs']['namespace'] ) {
109+
add_filter( 'query_loop_block_query_vars', 'a8csp_jrpql_query_loop_block_query_vars_related' );
110+
}
111+
112+
return $block_content;
113+
}
114+
add_filter( 'pre_render_block', 'a8csp_jrpql_pre_render_block_query_block_related', 10, 2 );
115+
116+
/**
117+
* Update query args for frontend query block.
118+
*
119+
* @param array<string,mixed> $query_args Array containing parameters for `WP_Query` as parsed by the block context.
120+
*
121+
* @return array<string,mixed>
122+
*/
123+
function a8csp_jrpql_query_loop_block_query_vars_related( $query_args ) {
124+
125+
// remove filter so it doesn't run for other query loops
126+
remove_filter( 'query_loop_block_query_vars', 'a8csp_jrpql_query_loop_block_query_vars_related' );
127+
128+
return a8csp_jrpql_get_related_posts_args( $query_args );
129+
}
130+
131+
/**
132+
* Get related posts.
133+
*
134+
* @param array<string, mixed> $query_args Query arguments.
135+
*
136+
* @return array<string, mixed> An array containing the posts and the source of the data.
137+
*/
138+
function a8csp_jrpql_get_related_posts_args( array $query_args ): array {
139+
140+
$post_id = get_the_ID();
141+
142+
if ( ! empty( $post_id ) ) {
143+
return $query_args;
144+
}
145+
146+
$posts_per_page = $query_args['posts_per_page'] ?? 4;
147+
$posts_type = $query_args['posts_type'] ?? 'post';
148+
$post_ids = array();
149+
150+
if ( class_exists( 'Jetpack_RelatedPosts' ) && class_exists( 'Jetpack_RelatedPosts_Raw' ) ) {
151+
/**
152+
* Get related posts instance.
153+
*
154+
* @var Jetpack_RelatedPosts_Raw $posts
155+
*/
156+
$posts = Jetpack_RelatedPosts::init_raw();
157+
$posts = $posts->set_query_name( 'a8csp_jrpql_related_posts' )->get_for_post_id( $post_id, array( 'size' => $posts_per_page, 'post_type' => $posts_type ) );
158+
159+
$post_ids = wp_list_pluck( $posts, 'id' );
160+
$post_ids = array_filter(
161+
$post_ids,
162+
function ( $post_id ) {
163+
return (int) $post_id > 0;
164+
}
165+
);
166+
}
167+
168+
// fallback to get random posts
169+
if ( count( $post_ids ) === 0 ) {
170+
$posts = new WP_Query(
171+
array(
172+
'posts_per_page' => $posts_per_page * 3,
173+
'fields' => 'ids',
174+
'post_type' => $posts_type,
175+
'post__not_in' => array( $post_id ),
176+
)
177+
);
178+
179+
$post_ids = $posts->posts;
180+
shuffle( $post_ids );
181+
}
182+
183+
// Only query for the posts we need.
184+
$post_ids = array_slice( $post_ids, 0, $posts_per_page );
185+
186+
$query_args['offset'] = 0;
187+
$query_args['post__in'] = $post_ids;
188+
$query_args['orderby'] = 'post__in';
189+
190+
return $query_args;
191+
}

0 commit comments

Comments
 (0)