Skip to content

Commit 66e2b74

Browse files
committed
First pass at auto-distribution.
1 parent 962a432 commit 66e2b74

2 files changed

Lines changed: 304 additions & 0 deletions

File tree

includes/auto-distribute.php

Lines changed: 302 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,302 @@
1+
<?php
2+
3+
namespace Distributor\AutoDistribute;
4+
5+
function setup() {
6+
$n = function( $function ) {
7+
return __NAMESPACE__ . "\\$function";
8+
};
9+
10+
/**
11+
* Filter whether auto-distribution is enabled.
12+
*
13+
* To enable auto-distribution, you can use the code:
14+
* ```php
15+
* add_filter( 'dt_auto_distribution_enabled', '__return_true' );
16+
* ```
17+
*
18+
* Enabling auto-distribution will automatically distribute posts upon publication
19+
* to all network and external connections that the post had not already been distributed
20+
* to. These posts will be distributed as published posts, not drafts.
21+
*
22+
* @since x.x.x.
23+
* @hook dt_auto_distribution_enabled
24+
*
25+
* @param {bool} $enabled Whether the auto-distribution feature is enabled. Default false.
26+
*
27+
* @return {bool} Whether the auto-distribution feature is enabled.
28+
*/
29+
if ( ! apply_filters( 'dt_auto_distribution_enabled', false ) ) {
30+
return;
31+
}
32+
33+
add_action( 'publish_post', $n( 'schedule_post_for_auto_distribution' ), 10 );
34+
add_action( 'dt_auto_distribute', $n( 'distribute_post_to_all_connections' ), 10, 2 );
35+
}
36+
37+
/**
38+
* Schedule a post for auto distribution.
39+
*
40+
* Create a scheduled event to distribute the post to all connections,
41+
* including both internal and external connections.
42+
*
43+
* @param int|WP_Post $post Post ID or WP_Post object.
44+
* @param int $user_id User ID of the post author. Defaults to current user.
45+
*/
46+
function schedule_post_for_auto_distribution( $post = 0, $user_id = 0 ) {
47+
$post = get_post( $post );
48+
49+
if ( ! $post ) {
50+
return;
51+
}
52+
53+
if ( get_post_meta( $post->ID, 'dt_original_post_url', true ) ) {
54+
// Avoid re-distributing posts that are themselves distributed
55+
return;
56+
}
57+
58+
$user_id = $user_id ?? get_current_user_id();
59+
60+
if ( ! wp_next_scheduled( 'dt_auto_distribute', [ $post->ID, $user_id ] ) ) {
61+
wp_schedule_single_event( time(), 'dt_auto_distribute', [ $post->ID, $user_id ] );
62+
}
63+
}
64+
65+
/**
66+
* Distribute a post to all connections.
67+
*
68+
* This function retrieves all connections for the given post and user,
69+
* and distributes the post to each connection that is not already syndicated.
70+
*
71+
* @param int|WP_Post $post Post ID or WP_Post object.
72+
* @param int $user_id User ID of the post author.
73+
*/
74+
function distribute_post_to_all_connections( $post = 0, $user_id = 0 ) {
75+
$post = get_post( $post );
76+
77+
if ( ! $post || ! $user_id ) {
78+
return;
79+
}
80+
81+
$connections = get_connections( $post->ID, $user_id );
82+
if ( empty( $connections ) ) {
83+
return;
84+
}
85+
86+
foreach( $connections as $connection ) {
87+
if ( $connection['syndicated'] ) {
88+
continue;
89+
}
90+
91+
$connection_id = $connection['id'];
92+
$connection_type = $connection['type'];
93+
94+
distribute_post( $post->ID, $user_id, $connection_id, $connection_type );
95+
}
96+
}
97+
98+
/**
99+
* Distribute a post to a specific connection.
100+
*
101+
* This function pushes the post to the specified connection and updates
102+
* the connection map in the post meta.
103+
*
104+
* @param int $post_id Post ID.
105+
* @param int $user_id User ID of the post author.
106+
* @param int $connection_id Connection ID.
107+
* @param string $connection_type Type of connection ('external' or 'internal').
108+
*/
109+
function distribute_post( $post_id = 0, $user_id = 0, $connection_id = 0, $connection_type = '' ) {
110+
$post = get_post( $post_id );
111+
112+
if ( ! $post || ! $user_id || ! $connection_id || ! $connection_type ) {
113+
return;
114+
}
115+
116+
if ( 'external' === $connection_type ) {
117+
$connection = \Distributor\ExternalConnection::instantiate( $connection_id );
118+
} elseif ( 'internal' === $connection_type ) {
119+
$connection = new \Distributor\InternalConnections\NetworkSiteConnection( get_site( $connection_id ) );
120+
} else {
121+
return; // Invalid connection type
122+
}
123+
124+
$post_id = $post->ID;
125+
$connection_map = get_post_meta( intval( $post_id ), 'dt_connection_map', true );
126+
if ( ! is_array( $connection_map ) ) {
127+
$connection_map = [];
128+
}
129+
if ( empty( $connection_map[ 'internal' ] ) ) {
130+
$connection_map[ 'internal' ] = [];
131+
}
132+
if ( empty( $connection_map[ 'external' ] ) ) {
133+
$connection_map[ 'external' ] = [];
134+
}
135+
$remote_post = $connection->push( $post_id );
136+
if ( ! is_wp_error( $remote_post ) && ! empty( $remote_post['id'] ) ) {
137+
// Store the connection mapping
138+
$connection_map[ $connection_type ][ $connection_id ] = [
139+
'post_id' => (int) $remote_post['id'],
140+
'time' => time(),
141+
];
142+
143+
// Update the post meta with the new connection map
144+
update_post_meta( intval( $post_id ), 'dt_connection_map', $connection_map );
145+
}
146+
}
147+
148+
/**
149+
* Get all connections
150+
*
151+
* @param integer $post_id Post ID.
152+
* @param integer $user_id User ID.
153+
* @return array
154+
*/
155+
function get_connections( $post_id = 0, $user_id = 0 ) {
156+
$external = get_external_connections( $post_id, $user_id );
157+
$internal = get_internal_connections( $post_id, $user_id );
158+
159+
return array_merge( $external, $internal );
160+
}
161+
162+
/**
163+
* Get all external connections
164+
*
165+
* @param integer $post_id Post ID.
166+
* @param integer $user_id User ID.
167+
* @return array
168+
*/
169+
function get_external_connections( $post_id = 0, $user_id = 0 ) {
170+
$connection_objects = [];
171+
$connections = new \WP_Query(
172+
[
173+
'post_type' => 'dt_ext_connection',
174+
'fields' => 'ids',
175+
'no_found_rows' => true,
176+
'posts_per_page' => 550,
177+
]
178+
);
179+
180+
// Get our current connection mapping
181+
$connection_map = (array) get_post_meta( $post_id, 'dt_connection_map', true );
182+
if ( empty( $connection_map['external'] ) ) {
183+
$connection_map['external'] = [];
184+
}
185+
186+
// Make sure we have a proper user set up
187+
if ( ! $user_id ) {
188+
$user_id = get_current_user_id();
189+
}
190+
wp_set_current_user( $user_id );
191+
192+
foreach ( $connections->posts as $connection_id ) {
193+
$connection_type = get_post_meta( $connection_id, 'dt_external_connection_type', true );
194+
195+
if ( empty( \Distributor\Connections::factory()->get_registered()[ $connection_type ] ) ) {
196+
continue;
197+
}
198+
199+
$connection_status = get_post_meta( $connection_id, 'dt_external_connections', true );
200+
$allowed_roles = get_post_meta( $connection_id, 'dt_external_connection_allowed_roles', true );
201+
202+
if ( empty( $allowed_roles ) ) {
203+
$allowed_roles = array( 'administrator', 'editor' );
204+
}
205+
206+
// Make sure we have a proper connection status
207+
if ( empty( $connection_status ) || empty( $connection_status['can_get'] ) ) {
208+
continue;
209+
}
210+
211+
// Make sure we have no connection errors
212+
if ( ! empty( $connection_status['errors'] ) && ! empty( $connection_status['errors']['no_distributor'] ) ) {
213+
continue;
214+
}
215+
216+
// Make sure this post type is supported
217+
if ( ! in_array( get_post_type( $post_id ), $connection_status['can_post'], true ) ) {
218+
continue;
219+
}
220+
221+
// If not admin lets make sure the current user can push to this connection
222+
if ( ! current_user_can( apply_filters( 'dt_push_capabilities', 'manage_options' ) ) ) {
223+
$current_user_roles = (array) wp_get_current_user()->roles;
224+
225+
if ( count( array_intersect( $current_user_roles, $allowed_roles ) ) < 1 ) {
226+
continue;
227+
}
228+
}
229+
230+
$connection = \Distributor\ExternalConnection::instantiate( $connection_id );
231+
232+
if ( ! is_wp_error( $connection ) ) {
233+
$connection_objects[ 'external' . $connection->id ] = [
234+
'type' => 'external',
235+
'id' => $connection->id,
236+
'url' => $connection->base_url,
237+
'name' => $connection->name,
238+
'syndicated' => ( ! empty( $connection_map['external'][ (int) $connection_id ] ) ) ? true : false,
239+
];
240+
}
241+
}
242+
243+
return $connection_objects;
244+
}
245+
246+
/**
247+
* Get all internal connections
248+
*
249+
* @param integer $post_id Post ID.
250+
* @param integer $user_id User ID.
251+
* @return array
252+
*/
253+
function get_internal_connections( $post_id = 0, $user_id = 0 ) {
254+
$connection_objects = [];
255+
256+
if ( ! is_multisite() || empty( \Distributor\Connections::factory()->get_registered()['networkblog'] ) ) {
257+
return $connection_objects;
258+
}
259+
260+
// Get our current connection mapping
261+
$connection_map = (array) get_post_meta( $post_id, 'dt_connection_map', true );
262+
if ( empty( $connection_map['internal'] ) ) {
263+
$connection_map['internal'] = [];
264+
}
265+
266+
// Make sure we have a proper user set up
267+
if ( ! $user_id ) {
268+
$user_id = get_current_user_id();
269+
}
270+
wp_set_current_user( $user_id );
271+
272+
$sites = \Distributor\InternalConnections\NetworkSiteConnection::build_available_authorized_sites( $user_id, 'push' );
273+
274+
foreach ( $sites as $site_array ) {
275+
if ( ! in_array( get_post_type( $post_id ), $site_array['post_types'], true ) ) {
276+
continue;
277+
}
278+
279+
$connection = new \Distributor\InternalConnections\NetworkSiteConnection( $site_array['site'] );
280+
281+
$syndicated = false;
282+
if ( ! empty( $connection_map['internal'][ (int) $connection->site->blog_id ] ) ) {
283+
switch_to_blog( $connection->site->blog_id );
284+
$syndicated = get_permalink( $connection_map['internal'][ (int) $connection->site->blog_id ]['post_id'] );
285+
restore_current_blog();
286+
287+
if ( empty( $syndicated ) ) {
288+
$syndicated = true; // In case it was deleted
289+
}
290+
}
291+
292+
$connection_objects[ 'internal' . $connection->site->blog_id ] = [
293+
'type' => 'internal',
294+
'id' => $connection->site->blog_id,
295+
'url' => untrailingslashit( preg_replace( '#(https?:\/\/|www\.)#i', '', get_site_url( $connection->site->blog_id ) ) ),
296+
'name' => $connection->site->blogname,
297+
'syndicated' => $syndicated,
298+
];
299+
}
300+
301+
return $connection_objects;
302+
}

includes/bootstrap.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ function( $response ) {
8787
require_once __DIR__ . '/settings.php';
8888
require_once __DIR__ . '/template-tags.php';
8989
require_once __DIR__ . '/debug-info.php';
90+
require_once __DIR__ . '/auto-distribute.php';
9091

9192
// Include application passwords.
9293
add_action(
@@ -236,3 +237,4 @@ function() {
236237
\Distributor\DistributedPostUI\setup();
237238
\Distributor\Settings\setup();
238239
\Distributor\DebugInfo\setup();
240+
\Distributor\AutoDistribute\setup();

0 commit comments

Comments
 (0)