|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
/* |
|
4
|
|
|
Plugin Name: Repeatable Posts |
|
5
|
|
|
Description: Designate a post as repeatable and it'll be copied and re-published on your chosen interval. |
|
6
|
|
|
Author: Human Made Limited |
|
7
|
|
|
Author URI: http://hmn.md/ |
|
8
|
|
|
Version: 0.4 |
|
9
|
|
|
License: GPL-2.0+ |
|
10
|
|
|
License URI: http://www.gnu.org/licenses/gpl-2.0.txt |
|
11
|
|
|
Text Domain: hm-post-repeat |
|
12
|
|
|
Domain Path: /languages |
|
13
|
|
|
*/ |
|
14
|
|
|
|
|
15
|
|
|
/* |
|
16
|
|
|
Copyright Human Made Limited (email : [email protected]) |
|
17
|
|
|
|
|
18
|
|
|
This program is free software; you can redistribute it and/or modify |
|
19
|
|
|
it under the terms of the GNU General Public License as published by |
|
20
|
|
|
the Free Software Foundation; either version 2 of the License, or |
|
21
|
|
|
(at your option) any later version. |
|
22
|
|
|
|
|
23
|
|
|
This program is distributed in the hope that it will be useful, |
|
24
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
25
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
26
|
|
|
GNU General Public License for more details. |
|
27
|
|
|
|
|
28
|
|
|
You should have received a copy of the GNU General Public License |
|
29
|
|
|
along with this program; if not, write to the Free Software |
|
30
|
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
31
|
|
|
*/ |
|
32
|
|
|
|
|
33
|
|
|
namespace HM\Post_Repeat; |
|
34
|
|
|
|
|
35
|
|
|
/** |
|
36
|
|
|
* Setup the actions and filters required by this class. |
|
37
|
|
|
*/ |
|
38
|
|
|
add_action( 'post_submitbox_misc_actions', __NAMESPACE__ . '\publish_box_ui' ); |
|
39
|
|
|
add_action( 'save_post', __NAMESPACE__ . '\manage_repeat_post', 10 ); |
|
40
|
|
|
add_action( 'admin_enqueue_scripts', __NAMESPACE__ . '\enqueue_scripts' ); |
|
41
|
|
|
add_filter( 'display_post_states', __NAMESPACE__ . '\admin_table_row_post_states', 10, 2 ); |
|
42
|
|
|
|
|
43
|
|
|
// Add repeat type table view links to admin screen for each CPT that supports Repeatable Posts. |
|
44
|
|
|
add_action( 'init', function() { |
|
45
|
|
|
foreach ( repeating_post_types() as $post_type ) { |
|
46
|
|
|
add_filter( "views_edit-{$post_type}", __NAMESPACE__ . '\admin_table_views_links' ); |
|
47
|
|
|
} |
|
48
|
|
|
} ); |
|
49
|
|
|
|
|
50
|
|
|
// Display only Repeatable Posts in admin table view for registered view links. |
|
51
|
|
|
add_filter( 'pre_get_posts', __NAMESPACE__ . '\admin_table_repeat_type_posts_query' ); |
|
52
|
|
|
|
|
53
|
|
|
/** |
|
54
|
|
|
* Enqueue the scripts and styles that are needed by this plugin. |
|
55
|
|
|
*/ |
|
56
|
|
|
function enqueue_scripts( $hook ) { |
|
57
|
|
|
|
|
58
|
|
|
// Ensure we only load them on the edit post and add new post admin screens |
|
59
|
|
|
if ( ! in_array( $hook, array( 'post.php', 'post-new.php' ) ) ) { |
|
60
|
|
|
return; |
|
61
|
|
|
} |
|
62
|
|
|
|
|
63
|
|
|
$plugin_data = get_plugin_data( __FILE__ ); |
|
64
|
|
|
$plugin_dir_url = plugin_dir_url( __FILE__ ); |
|
65
|
|
|
|
|
66
|
|
|
wp_enqueue_script( 'hm-post-repeat', $plugin_dir_url . 'hm-post-repeat.js', 'jquery', $plugin_data['Version'], true ); |
|
67
|
|
|
wp_enqueue_style( 'hm-post-repeat', $plugin_dir_url . 'hm-post-repeat.css', array(), $plugin_data['Version'] ); |
|
68
|
|
|
|
|
69
|
|
|
} |
|
70
|
|
|
|
|
71
|
|
|
/** |
|
72
|
|
|
* Output the Post Repeat UI that is shown in the Publish post meta box. |
|
73
|
|
|
* |
|
74
|
|
|
* The UI varies depending on whether the post is the original repeating post |
|
75
|
|
|
* or itself a repeat. |
|
76
|
|
|
*/ |
|
77
|
|
|
function publish_box_ui() { |
|
78
|
|
|
|
|
79
|
|
|
if ( ! in_array( get_post_type(), repeating_post_types() ) ) { |
|
80
|
|
|
return; |
|
81
|
|
|
} ?> |
|
82
|
|
|
|
|
83
|
|
|
<div class="misc-pub-section misc-pub-hm-post-repeat"> |
|
84
|
|
|
|
|
85
|
|
|
<span class="dashicons dashicons-controls-repeat"></span> |
|
86
|
|
|
|
|
87
|
|
|
<?php esc_html_e( 'Repeat:', 'hm-post-repeat' ); ?> |
|
88
|
|
|
|
|
89
|
|
|
<?php if ( is_repeat_post( get_the_id() ) ) : ?> |
|
90
|
|
|
|
|
91
|
|
|
<strong><?php printf( esc_html__( 'Repeat of %s', 'hm-post-repeat' ), '<a href="' . esc_url( get_edit_post_link( get_post()->post_parent ) ) . '">' . esc_html( get_the_title( get_post_field( 'post_parent', get_the_id() ) ) ) . '</a>' ); ?></strong> |
|
92
|
|
|
|
|
93
|
|
|
<?php else : ?> |
|
94
|
|
|
|
|
95
|
|
|
<?php $repeating_schedule = get_repeating_schedule( get_the_id() ); ?> |
|
96
|
|
|
<?php $is_repeating_post = is_repeating_post( get_the_id() ) && isset( $repeating_schedule ); ?> |
|
97
|
|
|
|
|
98
|
|
|
<strong><?php echo ! $is_repeating_post ? esc_html__( 'No', 'hm-post-repeat' ) : esc_html( $repeating_schedule['display'] ); ?></strong> |
|
99
|
|
|
|
|
100
|
|
|
<a href="#hm-post-repeat" class="edit-hm-post-repeat hide-if-no-js"><span aria-hidden="true"><?php esc_html_e( 'Edit', 'hm-post-repeat' ); ?></span> <span class="screen-reader-text"><?php esc_html_e( 'Edit Repeat Settings', 'hm-post-repeat' ); ?></span></a> |
|
101
|
|
|
|
|
102
|
|
|
<span class="hide-if-js" id="hm-post-repeat"> |
|
103
|
|
|
|
|
104
|
|
|
<select name="hm-post-repeat"> |
|
105
|
|
|
<option<?php selected( ! $is_repeating_post ); ?> value="no"><?php esc_html_e( 'No', 'hm-post-repeat' ); ?></option> |
|
106
|
|
|
<?php foreach ( get_repeating_schedules() as $schedule_slug => $schedule ) : ?> |
|
107
|
|
|
<option<?php selected( $is_repeating_post && $schedule_slug === $repeating_schedule['slug'] ); ?> value="<?php echo esc_attr( $schedule_slug ); ?>"><?php echo esc_html( $schedule['display'] ); ?></option> |
|
108
|
|
|
<?php endforeach; ?> |
|
109
|
|
|
</select> |
|
110
|
|
|
|
|
111
|
|
|
<a href="#hm-post-repeat" class="save-post-hm-post-repeat hide-if-no-js button"><?php esc_html_e( 'OK', 'hm-post-repeat' ); ?></a> |
|
112
|
|
|
|
|
113
|
|
|
</span> |
|
114
|
|
|
|
|
115
|
|
|
<?php endif; ?> |
|
116
|
|
|
|
|
117
|
|
|
</div> |
|
118
|
|
|
|
|
119
|
|
|
<?php } |
|
120
|
|
|
|
|
121
|
|
|
/** |
|
122
|
|
|
* Add some custom post states to cover repeat and repeating posts. |
|
123
|
|
|
* |
|
124
|
|
|
* By default post states are displayed on the Edit Post screen in bold after the post title. |
|
125
|
|
|
* |
|
126
|
|
|
* @param array $post_states The original array of post states. |
|
127
|
|
|
* @param WP_Post $post The post object to get / return the states. |
|
128
|
|
|
* @return array The array of post states with ours added. |
|
129
|
|
|
*/ |
|
130
|
|
|
function admin_table_row_post_states( $post_states, $post ) { |
|
131
|
|
|
|
|
132
|
|
|
if ( is_repeating_post( $post->ID ) ) { |
|
133
|
|
|
|
|
134
|
|
|
// If the schedule has been removed since publishing, let the user know. |
|
135
|
|
|
if ( get_repeating_schedule( $post->ID ) ) { |
|
136
|
|
|
$post_states['hm-post-repeat'] = __( 'Repeating', 'hm-post-repeat' ); |
|
137
|
|
|
} else { |
|
138
|
|
|
$post_states['hm-post-repeat'] = __( 'Invalid Repeating Schedule', 'hm-post-repeat' ); |
|
139
|
|
|
} |
|
140
|
|
|
|
|
141
|
|
|
} |
|
142
|
|
|
|
|
143
|
|
|
if ( is_repeat_post( $post->ID ) ) { |
|
144
|
|
|
$post_states['hm-post-repeat'] = __( 'Repeat', 'hm-post-repeat' ); |
|
145
|
|
|
} |
|
146
|
|
|
|
|
147
|
|
|
return $post_states; |
|
148
|
|
|
} |
|
149
|
|
|
|
|
150
|
|
|
/** |
|
151
|
|
|
* Manage Repeating Post functionality: |
|
152
|
|
|
* |
|
153
|
|
|
* 1. Correctly mark/un-mark a Repeating post as such (i.e. save/delete the Repeating Schedule to/from post meta). |
|
154
|
|
|
* 2. Manage cleanup and creation of a corresponding Repeat post. |
|
155
|
|
|
* |
|
156
|
|
|
* @param int $post_id The ID of the (Repeating or Repeat) post being saved. |
|
157
|
|
|
* NB: Used to manually set the Repeating post ID from tests. |
|
158
|
|
|
* @param string $post_repeat_setting Repeating Schedule setting (daily, weekly, etc). |
|
159
|
|
|
* NB: Used to manually set the Repeating Schedule for a post from tests. |
|
160
|
|
|
*/ |
|
161
|
|
|
function manage_repeat_post( $post_id = null, $post_repeat_setting = null ) { |
|
162
|
|
|
|
|
163
|
|
|
// Not running a phpunit test - check if Repeating Schedule setting value is present when saving a post. |
|
164
|
|
|
if ( is_null( $post_repeat_setting ) ) { |
|
165
|
|
|
$post_repeat_setting = isset( $_POST['hm-post-repeat'] ) ? sanitize_text_field( $_POST['hm-post-repeat'] ) : ''; |
|
166
|
|
|
} |
|
167
|
|
|
|
|
168
|
|
|
/** |
|
169
|
|
|
* Stop - if: |
|
170
|
|
|
* 1. Post type doesn't support Repeating Posts feature. |
|
171
|
|
|
* 2. Not processing a Repeat post. |
|
172
|
|
|
* 3. Not processing a Repeating post (i.e. Repeating Schedule isn't specified). |
|
173
|
|
|
*/ |
|
174
|
|
|
if ( |
|
175
|
|
|
! in_array( get_post_type( $post_id ), repeating_post_types() ) || |
|
176
|
|
|
( ! is_repeat_post( $post_id ) && empty( $post_repeat_setting ) ) |
|
177
|
|
|
) { |
|
178
|
|
|
return; |
|
179
|
|
|
} |
|
180
|
|
|
|
|
181
|
|
|
// Repeating post - save the specified Repeating Schedule and manage a Repeat post. |
|
182
|
|
|
if ( $post_repeat_setting ) { |
|
183
|
|
|
|
|
184
|
|
|
// Repeating Schedule is removed - i.e. the post is no longer Repeating. |
|
185
|
|
|
if ( 'no' === $post_repeat_setting ) { |
|
186
|
|
|
|
|
187
|
|
|
// Remove post meta from the Repeating post. |
|
188
|
|
|
delete_post_meta( $post_id, 'hm-post-repeat' ); |
|
189
|
|
|
|
|
190
|
|
|
// Clean up the currently scheduled Repeat post if any. |
|
191
|
|
|
delete_next_scheduled_repeat_post( $post_id ); |
|
192
|
|
|
} |
|
193
|
|
|
// Repeating Schedule is specified and valid - save it to post's meta and manage a Repeat post. |
|
194
|
|
|
elseif ( in_array( $post_repeat_setting, array_keys( get_repeating_schedules() ) ) ) { |
|
195
|
|
|
|
|
196
|
|
|
/** |
|
197
|
|
|
* Clean up the currently scheduled Repeat post if any. This takes care of cases when: |
|
198
|
|
|
* 1. Repeating Schedule has changed. |
|
199
|
|
|
* 2. Content of the Repeating post is changed. |
|
200
|
|
|
* |
|
201
|
|
|
* In such cases, the next scheduled post should reflect these changes. |
|
202
|
|
|
*/ |
|
203
|
|
|
delete_next_scheduled_repeat_post( $post_id ); |
|
204
|
|
|
|
|
205
|
|
|
// Update a Repeating Schedule for the current post. |
|
206
|
|
|
update_post_meta( $post_id, 'hm-post-repeat', $post_repeat_setting ); |
|
207
|
|
|
|
|
208
|
|
|
// Schedule a Repeat post. |
|
209
|
|
|
create_next_repeat_post( $post_id ); |
|
210
|
|
|
} |
|
211
|
|
|
} |
|
212
|
|
|
|
|
213
|
|
|
// Repeat post - is published, schedule a next Repeat post. |
|
214
|
|
|
if ( is_repeat_post( $post_id ) && 'publish' === get_post_status( $post_id ) ) { |
|
215
|
|
|
|
|
216
|
|
|
create_next_repeat_post( $post_id ); |
|
217
|
|
|
} |
|
218
|
|
|
} |
|
219
|
|
|
|
|
220
|
|
|
/** |
|
221
|
|
|
* Create the next Repeat post when: |
|
222
|
|
|
* 1. a Repeating post is published or already published post is re-saved. |
|
223
|
|
|
* 2. a previously scheduled Repeat post is published. |
|
224
|
|
|
* |
|
225
|
|
|
* In such cases we copy the Repeating post and schedule a new Repeat post to publish on the correct interval. |
|
226
|
|
|
* This way the next Repeat post is always ready to go. |
|
227
|
|
|
* |
|
228
|
|
|
* @param int $post_id The ID of the (Repeating or Repeat) post being saved. |
|
229
|
|
|
* |
|
230
|
|
|
* @return int|\WP_Error A scheduled Repeat post ID on success, |
|
231
|
|
|
* WP_Error if a Repeat post could not be created. |
|
232
|
|
|
* Terminate function execution if conditions are not met. |
|
233
|
|
|
*/ |
|
234
|
|
|
function create_next_repeat_post( $post_id ) { |
|
235
|
|
|
|
|
236
|
|
|
// Stop - post type doesn't support Repeating Posts feature. |
|
237
|
|
|
if ( ! in_array( get_post_type( $post_id ), repeating_post_types() ) ) { |
|
238
|
|
|
return false; |
|
239
|
|
|
} |
|
240
|
|
|
|
|
241
|
|
|
// Stop - the current post isn't being published. |
|
242
|
|
|
if ( 'publish' !== get_post_status( $post_id ) ) { |
|
243
|
|
|
return false; |
|
244
|
|
|
} |
|
245
|
|
|
|
|
246
|
|
|
// Get the Repeating post ID for the current post being saved (which in turn can be a Repeating or Repeat post). |
|
247
|
|
|
$original_post_id = get_repeating_post( $post_id ); |
|
248
|
|
|
|
|
249
|
|
|
if ( ! $original_post_id ) { |
|
250
|
|
|
return false; |
|
251
|
|
|
} |
|
252
|
|
|
|
|
253
|
|
|
// Get the original Repeating post. |
|
254
|
|
|
$original_post = get_post( $original_post_id, ARRAY_A ); |
|
255
|
|
|
|
|
256
|
|
|
// Stop - the original Repeating post isn't already published. |
|
257
|
|
|
if ( 'publish' !== $original_post['post_status'] ) { |
|
258
|
|
|
return false; |
|
259
|
|
|
} |
|
260
|
|
|
|
|
261
|
|
|
// Stop - there is already a Repeat post scheduled don't create another one. |
|
262
|
|
|
if ( get_next_scheduled_repeat_post( $original_post['ID'] ) ) { |
|
263
|
|
|
return false; |
|
264
|
|
|
} |
|
265
|
|
|
|
|
266
|
|
|
// Get Repeating Schedule. |
|
267
|
|
|
$repeating_schedule = get_repeating_schedule( $original_post['ID'] ); |
|
268
|
|
|
|
|
269
|
|
|
if ( ! $repeating_schedule ) { |
|
270
|
|
|
return false; |
|
271
|
|
|
} |
|
272
|
|
|
|
|
273
|
|
|
$next_post = $original_post; |
|
274
|
|
|
|
|
275
|
|
|
// Create the Repeat post as a copy of the original, but ignore some fields. |
|
276
|
|
|
unset( $next_post['ID'] ); |
|
277
|
|
|
unset( $next_post['guid'] ); |
|
278
|
|
|
unset( $next_post['post_date_gmt'] ); |
|
279
|
|
|
unset( $next_post['post_modified'] ); |
|
280
|
|
|
unset( $next_post['post_modified_gmt'] ); |
|
281
|
|
|
|
|
282
|
|
|
// Set the post_parent to the original Repeating post_id, so they're related. |
|
283
|
|
|
$next_post['post_parent'] = $original_post['ID']; |
|
284
|
|
|
|
|
285
|
|
|
// Set the next Repeat post to publish in the future. |
|
286
|
|
|
$next_post['post_status'] = 'future'; |
|
287
|
|
|
|
|
288
|
|
|
// Use the date of the current post being saved as the base. |
|
289
|
|
|
$next_post['post_date'] = date( 'Y-m-d H:i:s', strtotime( get_post_field( 'post_date', $post_id ) . ' + ' . $repeating_schedule['interval'] ) ); |
|
290
|
|
|
|
|
291
|
|
|
// Make sure the next post will be in the future from the current time. |
|
292
|
|
|
if ( strtotime( $next_post['post_date'] ) <= time() ) { |
|
293
|
|
|
return false; |
|
294
|
|
|
} |
|
295
|
|
|
|
|
296
|
|
|
/** |
|
297
|
|
|
* Use this filter to modify the scheduled post before it gets stored. |
|
298
|
|
|
* |
|
299
|
|
|
* @param array $next_post The post data about to be saved. |
|
300
|
|
|
* @param array $repeating_schedule Repeating schedule info. |
|
301
|
|
|
* @param array $original_post The original repeating post. |
|
302
|
|
|
*/ |
|
303
|
|
|
$next_post = apply_filters( 'hm_post_repeat_edit_repeat_post', $next_post, $repeating_schedule, $original_post ); |
|
304
|
|
|
|
|
305
|
|
|
/** |
|
306
|
|
|
* Remove and then re-add our custom functionality hooked in into `save_post` filter. |
|
307
|
|
|
* This is due to inserting a Repeat post using `wp_insert_post()` which in turn |
|
308
|
|
|
* calls the `save_post` filters again. |
|
309
|
|
|
* |
|
310
|
|
|
* We want our custom functionality to run on the `save_post` filter only once. |
|
311
|
|
|
*/ |
|
312
|
|
|
remove_action( 'save_post', __NAMESPACE__ . '\\manage_repeat_post' ); |
|
313
|
|
|
|
|
314
|
|
|
// All checks done, get that post scheduled! |
|
315
|
|
|
$next_post_id = wp_insert_post( wp_slash( $next_post ), true ); |
|
316
|
|
|
|
|
317
|
|
|
add_action( 'save_post', __NAMESPACE__ . '\\manage_repeat_post' ); |
|
318
|
|
|
|
|
319
|
|
|
if ( is_wp_error( $next_post_id ) ) { |
|
320
|
|
|
return false; |
|
321
|
|
|
} |
|
322
|
|
|
|
|
323
|
|
|
// Mirror any post meta. |
|
324
|
|
|
$post_meta = get_post_meta( $original_post['ID'] ); |
|
325
|
|
|
|
|
326
|
|
|
if ( $post_meta ) { |
|
327
|
|
|
|
|
328
|
|
|
// Ignore some internal meta fields. |
|
329
|
|
|
unset( $post_meta['_edit_lock'] ); |
|
330
|
|
|
unset( $post_meta['_edit_last'] ); |
|
331
|
|
|
|
|
332
|
|
|
// Don't copy the post Repeating Schedule meta as only the original Repeating post should have that. |
|
333
|
|
|
unset( $post_meta['hm-post-repeat'] ); |
|
334
|
|
|
|
|
335
|
|
|
foreach ( $post_meta as $key => $values ) { |
|
336
|
|
|
foreach ( $values as $value ) { |
|
337
|
|
|
add_post_meta( $next_post_id, $key, maybe_unserialize( $value ) ); |
|
338
|
|
|
} |
|
339
|
|
|
} |
|
340
|
|
|
} |
|
341
|
|
|
|
|
342
|
|
|
// Mirror any term relationships. |
|
343
|
|
|
$taxonomies = get_object_taxonomies( $original_post['post_type'] ); |
|
344
|
|
|
|
|
345
|
|
|
foreach ( $taxonomies as $taxonomy ) { |
|
346
|
|
|
wp_set_object_terms( $next_post_id, wp_list_pluck( wp_get_object_terms( $original_post['ID'], $taxonomy ), 'slug' ), $taxonomy ); |
|
347
|
|
|
} |
|
348
|
|
|
|
|
349
|
|
|
return $next_post_id; |
|
350
|
|
|
} |
|
351
|
|
|
|
|
352
|
|
|
/** |
|
353
|
|
|
* The post types the feature is enabled on |
|
354
|
|
|
* |
|
355
|
|
|
* By default only posts have the feature enabled but others can be added with the `hm_post_repeat_post_types` filter. |
|
356
|
|
|
* |
|
357
|
|
|
* @return array An array of post types |
|
358
|
|
|
*/ |
|
359
|
|
|
function repeating_post_types() { |
|
360
|
|
|
|
|
361
|
|
|
/** |
|
362
|
|
|
* Enable support for additional post types. |
|
363
|
|
|
* |
|
364
|
|
|
* @param string[] $post_types Post type slugs. |
|
365
|
|
|
*/ |
|
366
|
|
|
return apply_filters( 'hm_post_repeat_post_types', array( 'post' ) ); |
|
367
|
|
|
|
|
368
|
|
|
} |
|
369
|
|
|
|
|
370
|
|
|
/** |
|
371
|
|
|
* All available repeat schedules. |
|
372
|
|
|
* |
|
373
|
|
|
* @return array An array of all available repeat schedules |
|
374
|
|
|
*/ |
|
375
|
|
|
function get_repeating_schedules() { |
|
376
|
|
|
|
|
377
|
|
|
/** |
|
378
|
|
|
* Enable support for additional schedules. |
|
379
|
|
|
* |
|
380
|
|
|
* @param array[] $schedules Schedule array items. |
|
381
|
|
|
*/ |
|
382
|
|
|
$schedules = apply_filters( 'hm_post_repeat_schedules', array( |
|
383
|
|
|
'daily' => array( 'interval' => '1 day', 'display' => __( 'Daily', 'hm-post-repeat' ) ), |
|
384
|
|
|
'weekly' => array( 'interval' => '1 week', 'display' => __( 'Weekly', 'hm-post-repeat' ) ), |
|
385
|
|
|
'monthly' => array( 'interval' => '1 month', 'display' => __( 'Monthly', 'hm-post-repeat' ) ), |
|
386
|
|
|
) ); |
|
387
|
|
|
|
|
388
|
|
|
foreach ( $schedules as $slug => &$schedule ) { |
|
389
|
|
|
$schedule['slug'] = $slug; |
|
390
|
|
|
} |
|
391
|
|
|
|
|
392
|
|
|
return $schedules; |
|
393
|
|
|
|
|
394
|
|
|
} |
|
395
|
|
|
|
|
396
|
|
|
/** |
|
397
|
|
|
* Get the repeating schedule of the given post_id. |
|
398
|
|
|
* |
|
399
|
|
|
* @param int $post_id The id of the post you want to check. |
|
400
|
|
|
* @return array|null The schedule to repeat by, or null if invalid. |
|
401
|
|
|
*/ |
|
402
|
|
|
function get_repeating_schedule( $post_id ) { |
|
403
|
|
|
|
|
404
|
|
|
if ( ! is_repeating_post( $post_id ) ) { |
|
405
|
|
|
return; |
|
406
|
|
|
} |
|
407
|
|
|
|
|
408
|
|
|
$repeating_schedule = get_post_meta( $post_id, 'hm-post-repeat', true ); |
|
409
|
|
|
$schedules = get_repeating_schedules(); |
|
410
|
|
|
|
|
411
|
|
|
// Backwards compatibility with 0.3 when we only supported weekly |
|
412
|
|
|
if ( '1' === $repeating_schedule ) { |
|
413
|
|
|
$repeating_schedule = 'weekly'; |
|
414
|
|
|
} |
|
415
|
|
|
|
|
416
|
|
|
if ( array_key_exists( $repeating_schedule, $schedules ) ) { |
|
417
|
|
|
return $schedules[ $repeating_schedule ]; |
|
418
|
|
|
} |
|
419
|
|
|
|
|
420
|
|
|
} |
|
421
|
|
|
|
|
422
|
|
|
/** |
|
423
|
|
|
* Check whether a given post_id is a repeating post. |
|
424
|
|
|
* |
|
425
|
|
|
* A repeating post is defined as the original post that was set to repeat. |
|
426
|
|
|
* |
|
427
|
|
|
* @param int $post_id The id of the post you want to check. |
|
428
|
|
|
* @return bool Whether the passed post_id is a repeating post or not. |
|
429
|
|
|
*/ |
|
430
|
|
|
function is_repeating_post( $post_id ) { |
|
431
|
|
|
|
|
432
|
|
|
// We check $_POST data so that this function works inside a `save_post` hook when the post_meta hasn't yet been saved |
|
433
|
|
|
if ( isset( $_POST['hm-post-repeat'] ) && isset( $_POST['ID'] ) && $_POST['ID'] === $post_id ) { |
|
434
|
|
|
return true; |
|
435
|
|
|
} |
|
436
|
|
|
|
|
437
|
|
|
if ( get_post_meta( $post_id, 'hm-post-repeat', true ) ) { |
|
438
|
|
|
return true; |
|
439
|
|
|
} |
|
440
|
|
|
|
|
441
|
|
|
return false; |
|
442
|
|
|
|
|
443
|
|
|
} |
|
444
|
|
|
|
|
445
|
|
|
/** |
|
446
|
|
|
* Check whether a given post_id is a repeat post. |
|
447
|
|
|
* |
|
448
|
|
|
* A repeat post is defined as any post which is a repeat of the original repeating post. |
|
449
|
|
|
* |
|
450
|
|
|
* @param int $post_id The id of the post you want to check. |
|
451
|
|
|
* @return bool Whether the passed post_id is a repeat post or not. |
|
452
|
|
|
*/ |
|
453
|
|
|
function is_repeat_post( $post_id ) { |
|
454
|
|
|
|
|
455
|
|
|
$post_parent = get_post_field( 'post_parent', $post_id ); |
|
456
|
|
|
|
|
457
|
|
|
if ( $post_parent && get_post_meta( $post_parent, 'hm-post-repeat', true ) ) { |
|
458
|
|
|
return true; |
|
459
|
|
|
} |
|
460
|
|
|
|
|
461
|
|
|
return false; |
|
462
|
|
|
} |
|
463
|
|
|
|
|
464
|
|
|
/** |
|
465
|
|
|
* Delete currently scheduled i.e. future Repeat posts for the specified post. |
|
466
|
|
|
* |
|
467
|
|
|
* @param $post_id The ID of a Repeat or Repeating post to delete scheduled posts for. |
|
468
|
|
|
*/ |
|
469
|
|
|
function delete_next_scheduled_repeat_post( $post_id ) { |
|
470
|
|
|
|
|
471
|
|
|
// TO DO: will it ever be more than 1 scheduled post? |
|
472
|
|
|
$next_repeat_post = get_next_scheduled_repeat_post( $post_id ); |
|
473
|
|
|
|
|
474
|
|
|
if ( $next_repeat_post ) { |
|
475
|
|
|
// TO DO: should we trash or permanently delete? I think permanently delete. |
|
476
|
|
|
wp_delete_post( $next_repeat_post->ID, true ); |
|
477
|
|
|
} |
|
478
|
|
|
} |
|
479
|
|
|
|
|
480
|
|
|
/** |
|
481
|
|
|
* Get the next scheduled repeat post. |
|
482
|
|
|
* |
|
483
|
|
|
* @param int $post_id The ID of a Repeat or Repeating post. |
|
484
|
|
|
* |
|
485
|
|
|
* @return int|bool Return the ID of the next repeat post_id or false if it can't find one. |
|
486
|
|
|
*/ |
|
487
|
|
|
function get_next_scheduled_repeat_post( $post_id ) { |
|
488
|
|
|
|
|
489
|
|
|
$post = get_post( get_repeating_post( $post_id ) ); |
|
490
|
|
|
|
|
491
|
|
|
$repeat_posts = get_posts( array( 'post_status' => 'future', 'post_parent' => $post->ID ) ); |
|
492
|
|
|
|
|
493
|
|
|
if ( isset( $repeat_posts[0] ) ) { |
|
494
|
|
|
return $repeat_posts[0]; |
|
495
|
|
|
} |
|
496
|
|
|
|
|
497
|
|
|
return false; |
|
498
|
|
|
} |
|
499
|
|
|
|
|
500
|
|
|
/** |
|
501
|
|
|
* Get the next scheduled repeat post. |
|
502
|
|
|
* |
|
503
|
|
|
* @param int $post_id The ID of a Repeat or Repeating post. |
|
504
|
|
|
* |
|
505
|
|
|
* @return int|bool Return the original repeating post_id or false if it can't find it. |
|
506
|
|
|
*/ |
|
507
|
|
|
function get_repeating_post( $post_id ) { |
|
508
|
|
|
|
|
509
|
|
|
$original_post_id = false; |
|
510
|
|
|
|
|
511
|
|
|
// Are we publishing a Repeat post. |
|
512
|
|
|
if ( is_repeat_post( $post_id ) ) { |
|
513
|
|
|
$original_post_id = get_post( $post_id )->post_parent; |
|
514
|
|
|
} |
|
515
|
|
|
|
|
516
|
|
|
// Or the original Repeating post. |
|
517
|
|
|
elseif ( is_repeating_post( $post_id ) ) { |
|
518
|
|
|
$original_post_id = $post_id; |
|
519
|
|
|
} |
|
520
|
|
|
|
|
521
|
|
|
return $original_post_id; |
|
522
|
|
|
} |
|
523
|
|
|
|
|
524
|
|
|
/** |
|
525
|
|
|
* Adds admin table view link per available repeat type. |
|
526
|
|
|
* So that only all posts of specific repeat type are displayed. |
|
527
|
|
|
* |
|
528
|
|
|
* Added at the end of link list for All | Mine | Published | Scheduled | Drafts |
|
529
|
|
|
* |
|
530
|
|
|
* @param array $views An array of available list table views. |
|
531
|
|
|
* |
|
532
|
|
|
* @return array Available list of table views with custom added views per repeat type. |
|
533
|
|
|
*/ |
|
534
|
|
|
function admin_table_views_links( $views ) { |
|
535
|
|
|
|
|
536
|
|
|
// Add status link for each repeat type. |
|
537
|
|
|
foreach ( get_available_repeat_types() as $repeat_type => $repeat_desc ) { |
|
538
|
|
|
|
|
539
|
|
|
$url_args = array( |
|
540
|
|
|
'post_type' => get_current_screen()->post_type, |
|
541
|
|
|
'hm-post-repeat' => $repeat_type, |
|
542
|
|
|
); |
|
543
|
|
|
|
|
544
|
|
|
// Custom WP_Query to get posts count of repeat type. |
|
545
|
|
|
$repeat_type_query = new \WP_Query( get_repeat_type_query_params( $repeat_type ) ); |
|
546
|
|
|
|
|
547
|
|
|
$link_label = sprintf( |
|
548
|
|
|
'%s <span class="count">(%s)</span>', |
|
549
|
|
|
esc_html( $repeat_desc ), |
|
550
|
|
|
esc_html( number_format_i18n( $repeat_type_query->post_count ) ) |
|
551
|
|
|
); |
|
552
|
|
|
|
|
553
|
|
|
// Add current class to the link to highlight it when it's selected. |
|
554
|
|
|
$class_html = ( get_repeat_type_url_param() === $repeat_type ) ? ' class="current"' : ''; |
|
555
|
|
|
|
|
556
|
|
|
$link_html = sprintf( |
|
557
|
|
|
'<a href="%s"%s>%s</a>', |
|
558
|
|
|
esc_url( add_query_arg( $url_args, 'edit.php' ) ), |
|
559
|
|
|
$class_html, // html - hardcoded, no need to escape. |
|
560
|
|
|
$link_label // html - escaped earlier in the code. |
|
561
|
|
|
); |
|
562
|
|
|
|
|
563
|
|
|
$views[ $repeat_type ] = $link_html; |
|
564
|
|
|
} |
|
565
|
|
|
|
|
566
|
|
|
return $views; |
|
567
|
|
|
} |
|
568
|
|
|
|
|
569
|
|
|
/** |
|
570
|
|
|
* Customizes main admin query to get posts of specified repeat type |
|
571
|
|
|
* to be displayed in the admin table. |
|
572
|
|
|
* |
|
573
|
|
|
* @param \WP_Query $wp_query Main admin query. |
|
574
|
|
|
* |
|
575
|
|
|
* @return mixed Main admin query with edited params to get posts of specified repeat type. |
|
576
|
|
|
*/ |
|
577
|
|
|
function admin_table_repeat_type_posts_query( $wp_query ) { |
|
578
|
|
|
|
|
579
|
|
|
// Stop - if not admin or not main query (there are secondary WP_Query for counting posts for view links, etc). |
|
580
|
|
|
if ( ! is_admin() || ! $wp_query->is_main_query() ) { |
|
581
|
|
|
return $wp_query; |
|
582
|
|
|
} |
|
583
|
|
|
|
|
584
|
|
|
// Get URL query param for repeat type and check it's valid. |
|
585
|
|
|
$repeat_type = get_repeat_type_url_param(); |
|
586
|
|
|
|
|
587
|
|
|
if ( ! $repeat_type || ! is_allowed_repeat_type( $repeat_type ) ) { |
|
588
|
|
|
return $wp_query; |
|
589
|
|
|
} |
|
590
|
|
|
|
|
591
|
|
|
// Add a table view link per each repeat type. |
|
592
|
|
|
foreach ( get_repeat_type_query_params( $repeat_type ) as $key => $value ) { |
|
593
|
|
|
$wp_query->set( $key, $value ); |
|
594
|
|
|
} |
|
595
|
|
|
|
|
596
|
|
|
return $wp_query; |
|
597
|
|
|
} |
|
598
|
|
|
|
|
599
|
|
|
/** |
|
600
|
|
|
* Returns array of custom WP_Query params to get posts of specified repeat type. |
|
601
|
|
|
* Works for all CPT that support Repeatable Posts. |
|
602
|
|
|
* |
|
603
|
|
|
* @param string $repeat_type Repeat type of posts to get. |
|
604
|
|
|
* |
|
605
|
|
|
* @return array Array of custom WP_Query params. |
|
606
|
|
|
*/ |
|
607
|
|
|
function get_repeat_type_query_params( $repeat_type ) { |
|
608
|
|
|
|
|
609
|
|
|
$query['post_type'] = get_current_screen()->post_type; |
|
|
|
|
|
|
610
|
|
|
|
|
611
|
|
|
// Construct custom query to get posts of specified repeat type. |
|
612
|
|
|
$query['meta_query'] = array( |
|
613
|
|
|
array( |
|
614
|
|
|
'key' => 'hm-post-repeat', |
|
615
|
|
|
'compare' => 'EXISTS', |
|
616
|
|
|
), |
|
617
|
|
|
); |
|
618
|
|
|
|
|
619
|
|
|
if ( $repeat_type === 'repeat' ) { |
|
620
|
|
|
$query['post_parent__not_in'] = array( 0 ); |
|
621
|
|
|
|
|
622
|
|
|
} elseif ( $repeat_type === 'repeating' ) { |
|
623
|
|
|
$query['post_parent__in'] = array( 0 ); |
|
624
|
|
|
} |
|
625
|
|
|
|
|
626
|
|
|
return $query; |
|
627
|
|
|
} |
|
628
|
|
|
|
|
629
|
|
|
/** |
|
630
|
|
|
* Get URL query param for the repeat type of posts being displayed |
|
631
|
|
|
* in the admin post table. |
|
632
|
|
|
* |
|
633
|
|
|
* @return string Sanitized string of repeat type being displayed. |
|
634
|
|
|
*/ |
|
635
|
|
|
function get_repeat_type_url_param() { |
|
636
|
|
|
return isset( $_GET['hm-post-repeat'] ) ? sanitize_text_field( $_GET['hm-post-repeat'] ) : ''; |
|
637
|
|
|
} |
|
638
|
|
|
|
|
639
|
|
|
/** |
|
640
|
|
|
* Return available repeat types, i.e. repeating or repeat. |
|
641
|
|
|
* |
|
642
|
|
|
* @return array Available repeat types. |
|
643
|
|
|
*/ |
|
644
|
|
|
function get_available_repeat_types() { |
|
645
|
|
|
return array( |
|
646
|
|
|
'repeating' => __( 'Repeating', 'hm-post-repeat' ), |
|
647
|
|
|
'repeat' => __( 'Repeat', 'hm-post-repeat' ), |
|
648
|
|
|
); |
|
649
|
|
|
} |
|
650
|
|
|
|
|
651
|
|
|
/** |
|
652
|
|
|
* Check if a repeat type is valid. |
|
653
|
|
|
* |
|
654
|
|
|
* @param string $repeat_type Repeat type to check. |
|
655
|
|
|
* |
|
656
|
|
|
* @return bool True if repeat type is valid, |
|
657
|
|
|
* False otherwise. |
|
658
|
|
|
*/ |
|
659
|
|
|
function is_allowed_repeat_type( $repeat_type ) { |
|
660
|
|
|
return in_array( $repeat_type, array_keys( get_available_repeat_types() ) ); |
|
661
|
|
|
} |
|
662
|
|
|
|
Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.
Let’s take a look at an example:
As you can see in this example, the array
$myArrayis initialized the first time when the foreach loop is entered. You can also see that the value of thebarkey is only written conditionally; thus, its value might result from a previous iteration.This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.