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:27
created

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

Complexity

Conditions 4
Paths 6

Size

Total Lines 20
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 9
nc 6
nop 2
dl 0
loc 20
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 links to admin screen.
45
add_action( 'init', function() {
46
	foreach ( repeating_post_types() as $post_type ) {
47
		add_filter( "views_edit-{$post_type}", __NAMESPACE__ . '\admin_table_views_links' );
48
	}
49
} );
50
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
 * Save the repeating status to post meta.
152
 *
153
 * Hooked into `save_post`. When saving a post that has been set to repeat we save a post meta entry.
154
 *
155
 * @param int    $post_id             The ID of the post.
156
 * @param string $post_repeat_setting Used to manually set the repeating schedule from tests.
157
 */
158
function save_post_repeating_status( $post_id = null, $post_repeat_setting = null ) {
159
160
	if ( is_null( $post_repeat_setting ) ) {
161
		$post_repeat_setting = isset( $_POST['hm-post-repeat'] ) ? sanitize_text_field( $_POST['hm-post-repeat'] ) : '';
162
	}
163
164
	if ( ! in_array( get_post_type( $post_id ), repeating_post_types() ) || empty( $post_repeat_setting ) ) {
165
		return;
166
	}
167
168
	if ( 'no' === $post_repeat_setting ) {
169
		delete_post_meta( $post_id, 'hm-post-repeat' );
170
	}
171
172
	// Make sure we have a valid schedule.
173
	elseif ( in_array( $post_repeat_setting, array_keys( get_repeating_schedules() ) ) ) {
174
		update_post_meta( $post_id, 'hm-post-repeat', $post_repeat_setting );
175
	}
176
177
}
178
179
180
/**
181
 * Create the next repeat post when the last one is published.
182
 *
183
 * When a repeat post (or the original) is published we copy and schedule a new post
184
 * to publish on the correct interval. That way the next repeat post is always ready to go.
185
 * This is hooked into publish_post so that the repeat post is only created when the original
186
 * is published.
187
 *
188
 * @param int $post_id The ID of the post.
189
 */
190
function create_next_repeat_post( $post_id ) {
191
192
	if ( ! in_array( get_post_type( $post_id ), repeating_post_types() ) ) {
193
		return false;
194
	}
195
196
	if ( 'publish' !== get_post_status( $post_id ) ) {
197
		return false;
198
	}
199
200
	$original_post_id = get_repeating_post( $post_id );
201
202
	// Bail if we're not publishing a repeat(ing) post
203
	if ( ! $original_post_id ) {
204
		return false;
205
	}
206
207
	$original_post = get_post( $original_post_id, ARRAY_A );
208
209
	// If there is already a repeat post scheduled don't create another one
210
	if ( get_next_scheduled_repeat_post( $original_post['ID'] ) ) {
211
		return false;
212
	}
213
214
	// Bail if the saved schedule doesn't exist
215
	$repeating_schedule = get_repeating_schedule( $original_post['ID'] );
216
217
	if ( ! $repeating_schedule ) {
218
		return false;
219
	}
220
221
	// Bail if the original post isn't already published
222
	if ( 'publish' !== $original_post['post_status'] ) {
223
		return false;
224
	}
225
226
	$next_post = $original_post;
227
228
	// Create the repeat post as a copy of the original, but ignore some fields
229
	unset( $next_post['ID'] );
230
	unset( $next_post['guid'] );
231
	unset( $next_post['post_date_gmt'] );
232
	unset( $next_post['post_modified'] );
233
	unset( $next_post['post_modified_gmt'] );
234
235
	// We set the post_parent to the original post_id, so they're related
236
	$next_post['post_parent'] = $original_post['ID'];
237
238
	// Set the next post to publish in the future
239
	$next_post['post_status'] = 'future';
240
241
	// Use the date of the current post being saved as the base
242
	$next_post['post_date'] = date( 'Y-m-d H:i:s', strtotime( get_post_field( 'post_date', $post_id ) . ' + ' . $repeating_schedule['interval'] ) );
243
244
	// Make sure the next post will be in the future
245
	if ( strtotime( $next_post['post_date'] ) <= time() ) {
246
		return false;
247
	}
248
249
	/**
250
	 * Use this filter to modify the scheduled post before it gets stored.
251
	 *
252
	 * @param array $next_post          The post data about to be saved.
253
	 * @param array $repeating_schedule Repeating schedule info.
254
	 * @param array $original_post      The original repeating post.
255
	 */
256
	$next_post = apply_filters( 'hm_post_repeat_edit_repeat_post', $next_post, $repeating_schedule, $original_post );
257
258
	// All checks done, get that post scheduled!
259
	$next_post_id = wp_insert_post( wp_slash( $next_post ), true );
260
261
	if ( is_wp_error( $next_post_id ) ) {
262
		return false;
263
	}
264
265
	// Mirror any post_meta
266
	$post_meta = get_post_meta( $original_post['ID'] );
267
268
	if ( $post_meta  ) {
269
270
		// Ignore some internal meta fields
271
		unset( $post_meta['_edit_lock'] );
272
		unset( $post_meta['_edit_last'] );
273
274
		// Don't copy the post repeat meta as only the original post should have that
275
		unset( $post_meta['hm-post-repeat'] );
276
277
		foreach ( $post_meta as $key => $values ) {
278
			foreach ( $values as $value ) {
279
				add_post_meta( $next_post_id, $key, maybe_unserialize( $value ) );
280
			}
281
		}
282
	}
283
284
	// Mirror any term relationships
285
	$taxonomies = get_object_taxonomies( $original_post['post_type'] );
286
287
	foreach ( $taxonomies as $taxonomy ) {
288
		wp_set_object_terms( $next_post_id, wp_list_pluck( wp_get_object_terms( $original_post['ID'], $taxonomy ), 'slug' ), $taxonomy );
289
	}
290
291
	return $next_post_id;
292
293
}
294
295
/**
296
 * The post types the feature is enabled on
297
 *
298
 * By default only posts have the feature enabled but others can be added with the `hm_post_repeat_post_types` filter.
299
 *
300
 * @return array An array of post types
301
 */
302
function repeating_post_types() {
303
304
	/**
305
	 * Enable support for additional post types.
306
	 *
307
	 * @param string[] $post_types Post type slugs.
308
	 */
309
	return apply_filters( 'hm_post_repeat_post_types', array( 'post' ) );
310
311
}
312
313
/**
314
 * All available repeat schedules.
315
 *
316
 * @return array An array of all available repeat schedules
317
 */
318
function get_repeating_schedules() {
319
320
	/**
321
	 * Enable support for additional schedules.
322
	 *
323
	 * @param array[] $schedules Schedule array items.
324
	 */
325
	$schedules = apply_filters( 'hm_post_repeat_schedules', array(
326
		'daily'   => array( 'interval' => '1 day',   'display' => __( 'Daily',   'hm-post-repeat' ) ),
327
		'weekly'  => array( 'interval' => '1 week',  'display' => __( 'Weekly',  'hm-post-repeat' ) ),
328
		'monthly' => array( 'interval' => '1 month', 'display' => __( 'Monthly', 'hm-post-repeat' ) ),
329
	) );
330
331
	foreach ( $schedules as $slug => &$schedule ) {
332
		$schedule['slug'] = $slug;
333
	}
334
335
	return $schedules;
336
337
}
338
339
/**
340
 * Get the repeating schedule of the given post_id.
341
 *
342
 * @param int $post_id The id of the post you want to check.
343
 * @return array|null The schedule to repeat by, or null if invalid.
344
 */
345
function get_repeating_schedule( $post_id ) {
346
347
	if ( ! is_repeating_post( $post_id ) ) {
348
		return;
349
	}
350
351
	$repeating_schedule = get_post_meta( $post_id, 'hm-post-repeat', true );
352
	$schedules = get_repeating_schedules();
353
354
	// Backwards compatibility with 0.3 when we only supported weekly
355
	if ( '1' === $repeating_schedule ) {
356
		$repeating_schedule = 'weekly';
357
	}
358
359
	if ( array_key_exists( $repeating_schedule, $schedules ) ) {
360
		return $schedules[ $repeating_schedule ];
361
	}
362
363
}
364
365
/**
366
 * Check whether a given post_id is a repeating post.
367
 *
368
 * A repeating post is defined as the original post that was set to repeat.
369
 *
370
 * @param int $post_id The id of the post you want to check.
371
 * @return bool Whether the passed post_id is a repeating post or not.
372
 */
373
function is_repeating_post( $post_id ) {
374
375
	// We check $_POST data so that this function works inside a `save_post` hook when the post_meta hasn't yet been saved
376
	if ( isset( $_POST['hm-post-repeat'] ) && isset( $_POST['ID'] ) && $_POST['ID'] === $post_id ) {
377
		return true;
378
	}
379
380
	if ( get_post_meta( $post_id, 'hm-post-repeat', true ) ) {
381
		return true;
382
	}
383
384
	return false;
385
386
}
387
388
/**
389
 * Check whether a given post_id is a repeat post.
390
 *
391
 * A repeat post is defined as any post which is a repeat of the original repeating post.
392
 *
393
 * @param int $post_id The id of the post you want to check.
394
 * @return bool Whether the passed post_id is a repeat post or not.
395
 */
396
function is_repeat_post( $post_id ) {
397
398
	$post_parent = get_post_field( 'post_parent', $post_id );
399
400
	if ( $post_parent && get_post_meta( $post_parent, 'hm-post-repeat', true ) ) {
401
		return true;
402
	}
403
404
	return false;
405
406
}
407
408
/**
409
 * Get the next scheduled repeat post
410
 *
411
 * @param int $post_id The id of a repeat or repeating post
412
 * @return Int|Bool Return the ID of the next repeat post_id or false if it can't find one
413
 */
414
function get_next_scheduled_repeat_post( $post_id ) {
415
416
	$post = get_post( get_repeating_post( $post_id ) );
417
418
	$repeat_posts = get_posts( array( 'post_status' => 'future', 'post_parent' => $post->ID ) );
419
420
	if ( isset( $repeat_posts[0] ) ) {
421
	 	return $repeat_posts[0];
422
	}
423
424
	return false;
425
426
}
427
428
/**
429
 * Get the next scheduled repeat post
430
 *
431
 * @param int $post_id The id of a repeat or repeating post
432
 * @return Int|Bool Return the original repeating post_id or false if it can't find it
433
 */
434
function get_repeating_post( $post_id ) {
435
436
	$original_post_id = false;
437
438
	// Are we publishing a repeat post
439
	if ( is_repeat_post( $post_id ) ) {
440
		$original_post_id = get_post( $post_id )->post_parent;
441
	}
442
443
	// Or the original
444
	elseif ( is_repeating_post( $post_id ) ) {
445
		$original_post_id = $post_id;
446
	}
447
448
	return $original_post_id;
449
450
}
451
452
/**
453
 * Adds admin table view link per available repeat type.
454
 * So that only all posts of specific repeat type are displayed.
455
 *
456
 * Added at the end of link list for All | Mine | Published | Scheduled | Drafts
457
 *
458
 * @param array $views An array of available list table views.
459
 *
460
 * @return array Available list of table views with custom added views per repeat type.
461
 */
462
function admin_table_views_links( $views ) {
463
464
	// Add status link for each repeat type.
465
	foreach ( get_available_repeat_types() as $repeat_type => $repeat_desc ) {
466
467
		$url_args = array(
468
			'post_type'      => get_current_screen()->post_type,
469
			'hm-post-repeat' => $repeat_type,
470
		);
471
472
		// Custom WP_Query to get posts count of repeat type.
473
		$repeat_type_query = new \WP_Query( get_repeat_type_query_params( $repeat_type ) );
474
475
		$link_label = sprintf(
476
				'%s <span class="count">(%s)</span>',
477
			$repeat_desc,
478
			number_format_i18n( $repeat_type_query->post_count )
479
		);
480
481
		// Add current class to the link to highlight it when it's selected.
482
		$class_html = ( get_repeat_type_url_param() === $repeat_type ) ? ' class="current"' : '';
483
484
		$link_html = sprintf(
485
			'<a href="%s"%s>%s</a>',
486
			esc_url( add_query_arg( $url_args, 'edit.php' ) ),
487
			$class_html,
488
			$link_label
489
		);
490
491
		$views[ $repeat_type ] = $link_html;
492
	}
493
494
	return $views;
495
}
496
497
/**
498
 * Customizes main admin query to get posts of specified repeat type
499
 * to be displayed in the admin table.
500
 *
501
 * @param \WP_Query $wp_query Main admin query.
502
 *
503
 * @return mixed Main admin query with edited params to get posts of specified repeat type.
504
 */
505
function admin_table_repeat_type_posts_query( $wp_query ) {
506
507
	if ( ! is_admin() ) {
508
		return $wp_query;
509
	}
510
511
	// Get URL query param for repeat type and check it's valid.
512
	$repeat_type = get_repeat_type_url_param();
513
514
	if ( ! $repeat_type || ! is_allowed_repeat_type( $repeat_type ) ) {
515
		return $wp_query;
516
	}
517
518
	foreach ( get_repeat_type_query_params( $repeat_type ) as $key => $value ) {
519
		$wp_query->set( $key, $value );
520
	}
521
522
	return $wp_query;
523
}
524
525
/**
526
 * Returns array of custom WP_Query params, to get posts
527
 * of specified repeat type.
528
 *
529
 * @param string $repeat_type Repeat type of posts to get.
530
 *
531
 * @return array Array of custom WP_Query params.
532
 */
533
function get_repeat_type_query_params( $repeat_type ) {
534
535
	$query['post_type'] = get_current_screen()->post_type;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$query was never initialized. Although not strictly required by PHP, it is generally a good practice to add $query = array(); before regardless.

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:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key 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.

Loading history...
536
537
	// Construct custom query to get posts of specified repeat type.
538
	$query['meta_query'] = array(
539
			array(
540
					'key'     => 'hm-post-repeat',
541
					'compare' => 'EXISTS',
542
			),
543
	);
544
545
	if ( $repeat_type === 'repeat' ) {
546
		$query['post_parent__not_in'] = array( 0 );
547
548
	} elseif ( $repeat_type === 'repeating' ) {
549
		$query['post_parent__in'] = array( 0 );
550
	}
551
552
	return $query;
553
}
554
555
/**
556
 * Get URL query param for the repeat type of posts being displayed
557
 * in the admin post table.
558
 *
559
 * @return string Sanitized string of repeat type being displayed.
560
 */
561
function get_repeat_type_url_param() {
562
	return isset( $_GET['hm-post-repeat'] ) ? sanitize_text_field( $_GET['hm-post-repeat'] ) : '';
563
}
564
565
/**
566
 * Return available repeat types, i.e. repeating or repeat.
567
 *
568
 * @return array Available repeat types.
569
 */
570
function get_available_repeat_types() {
571
	return array(
572
		'repeating' => __( 'Repeating', 'hm-post-repeat' ),
573
		'repeat'    => __( 'Repeat', 'hm-post-repeat' ),
574
	);
575
}
576
577
/**
578
 * Check if a repeat type is valid.
579
 *
580
 * @param string $repeat_type Repeat type to check.
581
 *
582
 * @return bool True if repeat type is valid,
583
 *              False otherwise.
584
 */
585
function is_allowed_repeat_type( $repeat_type ) {
586
	return in_array( $repeat_type, array_keys( get_available_repeat_types() ) );
587
}
588