GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Pull Request — master (#31)
by
unknown
01:41
created

hm-post-repeat.php ➔ admin_table_row_post_states()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 19
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 9
nc 6
nop 2
dl 0
loc 19
rs 9.2
c 0
b 0
f 0
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__ . '\save_post_repeating_status', 10 );
40
add_action( 'save_post',                   __NAMESPACE__ . '\create_next_repeat_post', 11 );
41
add_action( 'admin_enqueue_scripts',       __NAMESPACE__ . '\enqueue_scripts' );
42
add_filter( 'display_post_states',         __NAMESPACE__ . '\admin_table_row_post_states', 10, 2 );
43
44
// Add repeat type table view to admin screen.
45
add_filter( 'views_edit-post',             __NAMESPACE__ . '\admin_table_views_links' );
46
add_filter( 'pre_get_posts',               __NAMESPACE__ . '\admin_table_repeat_type_posts_query' );
47
48
/**
49
 * Enqueue the scripts and styles that are needed by this plugin.
50
 */
51
function enqueue_scripts( $hook ) {
52
53
	// Ensure we only load them on the edit post and add new post admin screens
54
	if ( ! in_array( $hook, array( 'post.php', 'post-new.php' ) ) ) {
55
		return;
56
	}
57
58
	$plugin_data = get_plugin_data( __FILE__ );
59
	$plugin_dir_url = plugin_dir_url( __FILE__ );
60
61
	wp_enqueue_script( 'hm-post-repeat', $plugin_dir_url . 'hm-post-repeat.js', 'jquery', $plugin_data['Version'], true );
62
	wp_enqueue_style( 'hm-post-repeat', $plugin_dir_url . 'hm-post-repeat.css', array(), $plugin_data['Version'] );
63
64
}
65
66
/**
67
 * Output the Post Repeat UI that is shown in the Publish post meta box.
68
 *
69
 * The UI varies depending on whether the post is the original repeating post
70
 * or itself a repeat.
71
 */
72
function publish_box_ui() {
73
74
	if ( ! in_array( get_post_type(), repeating_post_types() ) ) {
75
		return;
76
	} ?>
77
78
	<div class="misc-pub-section misc-pub-hm-post-repeat">
79
80
		<span class="dashicons dashicons-controls-repeat"></span>
81
82
		<?php esc_html_e( 'Repeat:', 'hm-post-repeat' ); ?>
83
84
		<?php if ( is_repeat_post( get_the_id() ) ) : ?>
85
86
			<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>
87
88
		<?php else : ?>
89
90
			<?php $repeating_schedule = get_repeating_schedule( get_the_id() ); ?>
91
			<?php $is_repeating_post = is_repeating_post( get_the_id() ) && isset( $repeating_schedule ); ?>
92
93
			<strong><?php echo ! $is_repeating_post ? esc_html__( 'No', 'hm-post-repeat' ) : esc_html( $repeating_schedule['display'] ); ?></strong>
94
95
			<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>
96
97
			<span class="hide-if-js" id="hm-post-repeat">
98
99
				<select name="hm-post-repeat">
100
					<option<?php selected( ! $is_repeating_post ); ?> value="no"><?php esc_html_e( 'No', 'hm-post-repeat' ); ?></option>
101
					<?php foreach ( get_repeating_schedules() as $schedule_slug => $schedule ) : ?>
102
						<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>
103
					<?php endforeach; ?>
104
				</select>
105
106
				<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>
107
108
			</span>
109
110
		<?php endif; ?>
111
112
	</div>
113
114
<?php }
115
116
/**
117
 * Add some custom post states to cover repeat and repeating posts.
118
 *
119
 * By default post states are displayed on the Edit Post screen in bold after the post title.
120
 *
121
 * @param array   $post_states The original array of post states.
122
 * @param WP_Post $post        The post object to get / return the states.
123
 * @return array The array of post states with ours added.
124
 */
125
function admin_table_row_post_states( $post_states, $post ) {
126
127
	if ( is_repeating_post( $post->ID ) ) {
128
129
		// If the schedule has been removed since publishing, let the user know.
130
		if ( get_repeating_schedule( $post->ID ) ) {
131
			$post_states['hm-post-repeat'] = __( 'Repeating', 'hm-post-repeat' );
132
		} else {
133
			$post_states['hm-post-repeat'] = __( 'Invalid Repeating Schedule', 'hm-post-repeat' );
134
		}
135
136
	}
137
138
	if ( is_repeat_post( $post->ID ) ) {
139
		$post_states['hm-post-repeat'] = __( 'Repeat', 'hm-post-repeat' );
140
	}
141
142
	return $post_states;
143
}
144
145
/**
146
 * Save the repeating status to post meta.
147
 *
148
 * Hooked into `save_post`. When saving a post that has been set to repeat we save a post meta entry.
149
 *
150
 * @param int    $post_id             The ID of the post.
151
 * @param string $post_repeat_setting Used to manually set the repeating schedule from tests.
152
 */
153
function save_post_repeating_status( $post_id = null, $post_repeat_setting = null ) {
154
155
	if ( is_null( $post_repeat_setting ) ) {
156
		$post_repeat_setting = isset( $_POST['hm-post-repeat'] ) ? sanitize_text_field( $_POST['hm-post-repeat'] ) : '';
157
	}
158
159
	if ( ! in_array( get_post_type( $post_id ), repeating_post_types() ) || empty( $post_repeat_setting ) ) {
160
		return;
161
	}
162
163
	if ( 'no' === $post_repeat_setting ) {
164
		delete_post_meta( $post_id, 'hm-post-repeat' );
165
	}
166
167
	// Make sure we have a valid schedule.
168
	elseif ( in_array( $post_repeat_setting, array_keys( get_repeating_schedules() ) ) ) {
169
		update_post_meta( $post_id, 'hm-post-repeat', $post_repeat_setting );
170
	}
171
172
}
173
174
175
/**
176
 * Create the next repeat post when the last one is published.
177
 *
178
 * When a repeat post (or the original) is published we copy and schedule a new post
179
 * to publish on the correct interval. That way the next repeat post is always ready to go.
180
 * This is hooked into publish_post so that the repeat post is only created when the original
181
 * is published.
182
 *
183
 * @param int $post_id The ID of the post.
184
 */
185
function create_next_repeat_post( $post_id ) {
186
187
	if ( ! in_array( get_post_type( $post_id ), repeating_post_types() ) ) {
188
		return false;
189
	}
190
191
	if ( 'publish' !== get_post_status( $post_id ) ) {
192
		return false;
193
	}
194
195
	$original_post_id = get_repeating_post( $post_id );
196
197
	// Bail if we're not publishing a repeat(ing) post
198
	if ( ! $original_post_id ) {
199
		return false;
200
	}
201
202
	$original_post = get_post( $original_post_id, ARRAY_A );
203
204
	// If there is already a repeat post scheduled don't create another one
205
	if ( get_next_scheduled_repeat_post( $original_post['ID'] ) ) {
206
		return false;
207
	}
208
209
	// Bail if the saved schedule doesn't exist
210
	$repeating_schedule = get_repeating_schedule( $original_post['ID'] );
211
212
	if ( ! $repeating_schedule ) {
213
		return false;
214
	}
215
216
	// Bail if the original post isn't already published
217
	if ( 'publish' !== $original_post['post_status'] ) {
218
		return false;
219
	}
220
221
	$next_post = $original_post;
222
223
	// Create the repeat post as a copy of the original, but ignore some fields
224
	unset( $next_post['ID'] );
225
	unset( $next_post['guid'] );
226
	unset( $next_post['post_date_gmt'] );
227
	unset( $next_post['post_modified'] );
228
	unset( $next_post['post_modified_gmt'] );
229
230
	// We set the post_parent to the original post_id, so they're related
231
	$next_post['post_parent'] = $original_post['ID'];
232
233
	// Set the next post to publish in the future
234
	$next_post['post_status'] = 'future';
235
236
	// Use the date of the current post being saved as the base
237
	$next_post['post_date'] = date( 'Y-m-d H:i:s', strtotime( get_post_field( 'post_date', $post_id ) . ' + ' . $repeating_schedule['interval'] ) );
238
239
	// Make sure the next post will be in the future
240
	if ( strtotime( $next_post['post_date'] ) <= time() ) {
241
		return false;
242
	}
243
244
	/**
245
	 * Use this filter to modify the scheduled post before it gets stored.
246
	 *
247
	 * @param array $next_post          The post data about to be saved.
248
	 * @param array $repeating_schedule Repeating schedule info.
249
	 * @param array $original_post      The original repeating post.
250
	 */
251
	$next_post = apply_filters( 'hm_post_repeat_edit_repeat_post', $next_post, $repeating_schedule, $original_post );
252
253
	// All checks done, get that post scheduled!
254
	$next_post_id = wp_insert_post( wp_slash( $next_post ), true );
255
256
	if ( is_wp_error( $next_post_id ) ) {
257
		return false;
258
	}
259
260
	// Mirror any post_meta
261
	$post_meta = get_post_meta( $original_post['ID'] );
262
263
	if ( $post_meta  ) {
264
265
		// Ignore some internal meta fields
266
		unset( $post_meta['_edit_lock'] );
267
		unset( $post_meta['_edit_last'] );
268
269
		// Don't copy the post repeat meta as only the original post should have that
270
		unset( $post_meta['hm-post-repeat'] );
271
272
		foreach ( $post_meta as $key => $values ) {
273
			foreach ( $values as $value ) {
274
				add_post_meta( $next_post_id, $key, maybe_unserialize( $value ) );
275
			}
276
		}
277
	}
278
279
	// Mirror any term relationships
280
	$taxonomies = get_object_taxonomies( $original_post['post_type'] );
281
282
	foreach ( $taxonomies as $taxonomy ) {
283
		wp_set_object_terms( $next_post_id, wp_list_pluck( wp_get_object_terms( $original_post['ID'], $taxonomy ), 'slug' ), $taxonomy );
284
	}
285
286
	return $next_post_id;
287
288
}
289
290
/**
291
 * The post types the feature is enabled on
292
 *
293
 * By default only posts have the feature enabled but others can be added with the `hm_post_repeat_post_types` filter.
294
 *
295
 * @return array An array of post types
296
 */
297
function repeating_post_types() {
298
299
	/**
300
	 * Enable support for additional post types.
301
	 *
302
	 * @param string[] $post_types Post type slugs.
303
	 */
304
	return apply_filters( 'hm_post_repeat_post_types', array( 'post' ) );
305
306
}
307
308
/**
309
 * All available repeat schedules.
310
 *
311
 * @return array An array of all available repeat schedules
312
 */
313
function get_repeating_schedules() {
314
315
	/**
316
	 * Enable support for additional schedules.
317
	 *
318
	 * @param array[] $schedules Schedule array items.
319
	 */
320
	$schedules = apply_filters( 'hm_post_repeat_schedules', array(
321
		'daily'   => array( 'interval' => '1 day',   'display' => __( 'Daily',   'hm-post-repeat' ) ),
322
		'weekly'  => array( 'interval' => '1 week',  'display' => __( 'Weekly',  'hm-post-repeat' ) ),
323
		'monthly' => array( 'interval' => '1 month', 'display' => __( 'Monthly', 'hm-post-repeat' ) ),
324
	) );
325
326
	foreach ( $schedules as $slug => &$schedule ) {
327
		$schedule['slug'] = $slug;
328
	}
329
330
	return $schedules;
331
332
}
333
334
/**
335
 * Get the repeating schedule of the given post_id.
336
 *
337
 * @param int $post_id The id of the post you want to check.
338
 * @return array|null The schedule to repeat by, or null if invalid.
339
 */
340
function get_repeating_schedule( $post_id ) {
341
342
	if ( ! is_repeating_post( $post_id ) ) {
343
		return;
344
	}
345
346
	$repeating_schedule = get_post_meta( $post_id, 'hm-post-repeat', true );
347
	$schedules = get_repeating_schedules();
348
349
	// Backwards compatibility with 0.3 when we only supported weekly
350
	if ( '1' === $repeating_schedule ) {
351
		$repeating_schedule = 'weekly';
352
	}
353
354
	if ( array_key_exists( $repeating_schedule, $schedules ) ) {
355
		return $schedules[ $repeating_schedule ];
356
	}
357
358
}
359
360
/**
361
 * Check whether a given post_id is a repeating post.
362
 *
363
 * A repeating post is defined as the original post that was set to repeat.
364
 *
365
 * @param int $post_id The id of the post you want to check.
366
 * @return bool Whether the passed post_id is a repeating post or not.
367
 */
368
function is_repeating_post( $post_id ) {
369
370
	// We check $_POST data so that this function works inside a `save_post` hook when the post_meta hasn't yet been saved
371
	if ( isset( $_POST['hm-post-repeat'] ) && isset( $_POST['ID'] ) && $_POST['ID'] === $post_id ) {
372
		return true;
373
	}
374
375
	if ( get_post_meta( $post_id, 'hm-post-repeat', true ) ) {
376
		return true;
377
	}
378
379
	return false;
380
381
}
382
383
/**
384
 * Check whether a given post_id is a repeat post.
385
 *
386
 * A repeat post is defined as any post which is a repeat of the original repeating post.
387
 *
388
 * @param int $post_id The id of the post you want to check.
389
 * @return bool Whether the passed post_id is a repeat post or not.
390
 */
391
function is_repeat_post( $post_id ) {
392
393
	$post_parent = get_post_field( 'post_parent', $post_id );
394
395
	if ( $post_parent && get_post_meta( $post_parent, 'hm-post-repeat', true ) ) {
396
		return true;
397
	}
398
399
	return false;
400
401
}
402
403
/**
404
 * Get the next scheduled repeat post
405
 *
406
 * @param int $post_id The id of a repeat or repeating post
407
 * @return Int|Bool Return the ID of the next repeat post_id or false if it can't find one
408
 */
409
function get_next_scheduled_repeat_post( $post_id ) {
410
411
	$post = get_post( get_repeating_post( $post_id ) );
412
413
	$repeat_posts = get_posts( array( 'post_status' => 'future', 'post_parent' => $post->ID ) );
414
415
	if ( isset( $repeat_posts[0] ) ) {
416
	 	return $repeat_posts[0];
417
	}
418
419
	return false;
420
421
}
422
423
/**
424
 * Get the next scheduled repeat post
425
 *
426
 * @param int $post_id The id of a repeat or repeating post
427
 * @return Int|Bool Return the original repeating post_id or false if it can't find it
428
 */
429
function get_repeating_post( $post_id ) {
430
431
	$original_post_id = false;
432
433
	// Are we publishing a repeat post
434
	if ( is_repeat_post( $post_id ) ) {
435
		$original_post_id = get_post( $post_id )->post_parent;
436
	}
437
438
	// Or the original
439
	elseif ( is_repeating_post( $post_id ) ) {
440
		$original_post_id = $post_id;
441
	}
442
443
	return $original_post_id;
444
445
}
446
447
/**
448
 * Adds admin table view link per available repeat type.
449
 * So that only all posts of specific repeat type are displayed.
450
 *
451
 * Added at the end of link list for All | Mine | Published | Scheduled | Drafts
452
 *
453
 * @param array $views An array of available list table views.
454
 *
455
 * @return array Available list of table views with custom added views per repeat type.
456
 */
457
function admin_table_views_links( $views ) {
458
459
	$post_type = get_current_screen()->post_type;
460
461
	// Add status link for each repeat type.
462
	foreach ( get_available_repeat_types() as $repeat_type => $repeat_desc ) {
463
464
		$query_args = array(
465
			'post_type'      => $post_type,
466
			'hm-post-repeat' => $repeat_type,
467
		);
468
469
		// TO DO: add logic for getting the number of the posts correctly.
470
		$link_label = sprintf(
471
			$repeat_desc . ' <span class="count">(%s)</span>',
472
			number_format_i18n( 13 )
473
		);
474
475
		// Add current class to the link to highlight it when it's selected.
476
		$class_html = ( $repeat_type === get_repeat_type_url_param() ) ? ' class="current"' : '';
477
478
		$link_html = sprintf(
479
			'<a href="%s"%s>%s</a>',
480
			esc_url( add_query_arg( $query_args, 'edit.php' ) ),
481
			$class_html,
482
			$link_label
483
		);
484
485
		$views[ $repeat_type ] = $link_html;
486
	}
487
488
	return $views;
489
}
490
491
/**
492
 * Customizes main admin query to get posts of specified repeat type
493
 * to be displayed in the admin table.
494
 *
495
 * @param \WP_Query $wp_query Main admin query.
496
 *
497
 * @return mixed Main admin query with edited params to get posts of specified repeat type.
498
 */
499
function admin_table_repeat_type_posts_query( $wp_query ) {
500
501
	if ( ! is_admin() ) {
502
		return $wp_query;
503
	}
504
505
	// Get URL query param for repeat type and check it's valid.
506
	$repeat_type = get_repeat_type_url_param();
507
508
	if ( ! $repeat_type || ! is_allowed_repeat_type( $repeat_type ) ) {
509
		return $wp_query;
510
	}
511
512
	// Construct custom query to get posts of specified repeat type.
513
	$wp_query->set( 'meta_query', array(
514
			array(
515
					'key'     => 'hm-post-repeat',
516
					'compare' => 'EXISTS',
517
			),
518
	) );
519
520
	if ( $repeat_type === 'repeat' ) {
521
		$wp_query->set( 'post_parent__not_in', array( 0 ) );
522
523
	} elseif ( $repeat_type === 'repeating' ) {
524
		$wp_query->set( 'post_parent', 0 );
525
	}
526
527
	return $wp_query;
528
}
529
530
/**
531
 * Get URL query param for the repeat type of posts being displayed
532
 * in the admin post table.
533
 *
534
 * @return string Sanitized string of repeat type being displayed.
535
 */
536
function get_repeat_type_url_param() {
537
	return isset( $_GET['hm-post-repeat'] ) ? sanitize_text_field( $_GET['hm-post-repeat'] ) : '';
538
}
539
540
/**
541
 * Return available repeat types, i.e. repeating or repeat.
542
 *
543
 * @return array Available repeat types.
544
 */
545
function get_available_repeat_types() {
546
	return array(
547
		'repeating' => __( 'Repeating', 'hm-post-repeat' ),
548
		'repeat'    => __( 'Repeat', 'hm-post-repeat' ),
549
	);
550
}
551
552
/**
553
 * Check if a repeat type is valid.
554
 *
555
 * @param string $repeat_type Repeat type to check.
556
 *
557
 * @return bool True if repeat type is valid,
558
 *              False otherwise.
559
 */
560
function is_allowed_repeat_type( $repeat_type ) {
561
	return in_array( $repeat_type, array_keys( get_available_repeat_types() ) );
562
}
563