Completed
Branch ci (4cd40d)
by litefeel
03:54 queued 01:39
created

Writing_On_GitHub_Database::save_posts()   C

Complexity

Conditions 11
Paths 50

Size

Total Lines 64
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
eloc 36
nc 50
nop 1
dl 0
loc 64
rs 6.0563
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Database interface.
4
 * @package Writing_On_GitHub
5
 */
6
7
/**
8
 * Class Writing_On_GitHub_Database
9
 */
10
class Writing_On_GitHub_Database {
11
12
	/**
13
	 * Application container.
14
	 *
15
	 * @var Writing_On_GitHub
16
	 */
17
	protected $app;
18
19
	/**
20
	 * Currently whitelisted post types.
21
	 *
22
	 * @var array
23
	 */
24
	protected $whitelisted_post_types = array( 'post', 'page' );
25
26
	/**
27
	 * Currently whitelisted post statuses.
28
	 *
29
	 * @var array
30
	 */
31
	protected $whitelisted_post_statuses = array( 'publish' );
32
33
	/**
34
	 * Instantiates a new Database object.
35
	 *
36
	 * @param Writing_On_GitHub $app Application container.
37
	 */
38
	public function __construct( Writing_On_GitHub $app ) {
39
		$this->app = $app;
40
	}
41
42
	/**
43
	 * Queries the database for all of the supported posts.
44
	 *
45
	 * @return Writing_On_GitHub_Post[]|WP_Error
46
	 */
47
	public function fetch_all_supported(bool $force = false, bool aaa cc) {
0 ignored issues
show
Bug introduced by
This code did not parse for me. Apparently, there is an error somewhere around this line:

Syntax error, unexpected T_STRING, expecting T_VARIABLE
Loading history...
48
		$args  = array(
49
			'post_type'   => $this->get_whitelisted_post_types(),
50
			'post_status' => $this->get_whitelisted_post_statuses(),
51
			'nopaging'    => true,
52
			'fields'      => 'ids',
53
		);
54
55
		$query = new WP_Query( apply_filters( 'wogh_pre_fetch_all_supported', $args ) );
56
57
		$post_ids = $query->get_posts();
58
59
		if ( ! $post_ids ) {
60
			return new WP_Error(
61
				'no_results',
62
				__( 'Querying for supported posts returned no results.', 'writing-on-github' )
63
			);
64
		}
65
66
		$results = array();
67
		foreach ( $post_ids as $post_id ) {
68
			// Do not export posts that have already been exported
69
			if ( ! get_post_meta( $post_id, '_wogh_sha', true ) ||
70
				 ! get_post_meta( $post_id, '_wogh_github_path', true) ) {
71
72
				$results[] = new Writing_On_GitHub_Post( $post_id, $this->app->api() );
73
			}
74
		}
75
76
		return $results;
77
	}
78
79
	/**
80
	 * Queries a post and returns it if it's supported.
81
	 *
82
	 * @param int $post_id Post ID to fetch.
83
	 *
84
	 * @return WP_Error|Writing_On_GitHub_Post
85
	 */
86
	public function fetch_by_id( $post_id ) {
87
		$post = new Writing_On_GitHub_Post( $post_id, $this->app->api() );
88
89
		if ( ! $this->is_post_supported( $post ) ) {
90
			return new WP_Error(
91
				'unsupported_post',
92
				sprintf(
93
					__(
94
						'Post ID %s is not supported by WOGH. See wiki to find out how to add support.',
95
						'writing-on-github'
96
					),
97
					$post_id
98
				)
99
			);
100
		}
101
102
		return $post;
103
	}
104
105
	/**
106
	 * Saves an array of Post objects to the database
107
	 * and associates their author as well as their latest
108
	 *
109
	 * @param Writing_On_GitHub_Post[] $posts Array of Posts to save.
110
	 * @param string                       $email Author email.
111
	 *
112
	 * @return string|WP_Error
113
	 */
114
	public function save_posts( array $posts ) {
115
116
		/**
117
		 * Whether an error has occurred.
118
		 *
119
		 * @var WP_Error|false $error
120
		 */
121
		$error = false;
122
123
		foreach ( $posts as $post ) {
124
			$args = apply_filters( 'wogh_pre_import_args', $this->post_args( $post ), $post );
125
126
			remove_filter('content_save_pre', 'wp_filter_post_kses');
127
			$post_id = $post->is_new() ?
128
				wp_insert_post( $args, true ) :
129
				wp_update_post( $args, true );
130
			add_filter('content_save_pre', 'wp_filter_post_kses');
131
132
			if ( is_wp_error( $post_id ) ) {
133
				if ( ! $error ) {
134
					$error = $post_id;
135
				} else {
136
					$error->add( $post_id->get_error_code(), $post_id->get_error_message() );
137
				}
138
139
				// Abort saving if updating the post fails.
140
				continue;
141
			}
142
143
			// $this->set_revision_author( $post_id, $user_id );
0 ignored issues
show
Unused Code Comprehensibility introduced by
62% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
144
145
			if ( $post->is_new() ) {
146
				$author = false;
147
				$meta = $post->get_meta();
148
				if ( ! empty( $meta ) && ! empty( $meta['author'] ) ) {
149
					$author = $meta['author'];
150
				}
151
				$user    = $this->fetch_commit_user( $author );
152
				$user_id = ! is_wp_error( $user ) ? $user->ID : 0;
153
				$this->set_post_author( $post_id, $user_id );
154
			}
155
156
			$post->set_post( get_post( $post_id ) );
157
158
			$meta = apply_filters( 'wogh_pre_import_meta', $post->get_meta(), $post );
159
160
			unset( $meta['tags'] );
161
			unset( $meta['categories'] );
162
			unset( $meta['author'] );
163
			unset( $meta['post_date'] );
164
			unset( $meta['post_excerpt'] );
165
			unset( $meta['permalink'] );
166
167
			foreach ( $meta as $key => $value ) {
168
				update_post_meta( $post_id, $key, $value );
169
			}
170
		}
171
172
		if ( $error ) {
173
			return $error;
174
		}
175
176
		return __( 'Successfully saved posts.', 'writing-on-github' );
177
	}
178
179
	protected function post_args( $post ) {
180
		$args = $post->get_args();
181
		$meta = $post->get_meta();
182
183
		// update tags
184
		if ( isset( $meta['tags'] ) && $meta['tags'] ) {
185
			$args['tags_input'] = $meta['tags'];
186
		}
187
188
		// update categories
189
		if ( isset( $meta['categories'] ) && $meta['categories'] ) {
190
			$categories = $meta['categories'];
191
			if (!is_array($categories)) {
192
				$categories = array($categories);
193
			}
194
			$terms = get_terms(array(
195
				'taxonomy' => 'category',
196
				'fields' => 'id=>name',
197
				'hide_empty' => 0,
198
				'name' => $categories
199
				)
200
			);
201
			$map = array();
202
			foreach ($categories as $name) {
203
				$map[$name] = 1;
204
			}
205
206
			$ids = array();
207
			if (!empty($terms)) {
208
				foreach ($terms as $id => $name) {
209
					$ids[] = $id;
210
					unset($map[$name]);
211
				}
212
			}
213
214
			// create new terms
215
			if (!empty($map)) {
216
				foreach ($map as $name => $value) {
217
					$term = wp_insert_term($name, 'category', array('parent' => 0));
218
					// array('term_id' => $term_id, 'term_taxonomy_id' => $tt_id);
0 ignored issues
show
Unused Code Comprehensibility introduced by
65% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
219
					$ids[] = $term['term_id'];
220
				}
221
			}
222
223
			$args['post_category'] = $ids;
224
		}
225
226
		return $args;
227
	}
228
229
	/**
230
	 * Deletes a post from the database based on its GitHub path.
231
	 *
232
	 * @param string $path Path of Post to delete.
233
	 *
234
	 * @return string|WP_Error
235
	 */
236
	public function delete_post_by_path( $path ) {
237
		$query = new WP_Query( array(
238
			'meta_key'       => '_wogh_github_path',
239
			'meta_value'     => $path,
240
			'meta_compare'   => '=',
241
			'posts_per_page' => 1,
242
			'fields'         => 'ids',
243
		) );
244
245
		$post_id = $query->get_posts();
246
		$post_id = array_pop( $post_id );
247
248
		if ( ! $post_id ) {
249
			$parts     = explode( '/', $path );
250
			$filename  = array_pop( $parts );
251
			$directory = $parts ? array_shift( $parts ) : '';
252
253
			if ( false !== strpos( $directory, 'post' ) ) {
254
				preg_match( '/([0-9]{4})-([0-9]{2})-([0-9]{2})-(.*)\.md/', $filename, $matches );
255
				$title = $matches[4];
256
257
				$query = new WP_Query( array(
258
					'name'     => $title,
259
					'posts_per_page' => 1,
260
					'post_type' => $this->get_whitelisted_post_types(),
261
					'fields'         => 'ids',
262
				) );
263
264
				$post_id = $query->get_posts();
265
				$post_id = array_pop( $post_id );
266
			}
267
268
			if ( ! $post_id ) {
269
				preg_match( '/(.*)\.md/', $filename, $matches );
270
				$title = $matches[1];
271
272
				$query = new WP_Query( array(
273
					'name'     => $title,
274
					'posts_per_page' => 1,
275
					'post_type' => $this->get_whitelisted_post_types(),
276
					'fields'         => 'ids',
277
				) );
278
279
				$post_id = $query->get_posts();
280
				$post_id = array_pop( $post_id );
281
			}
282
		}
283
284
		if ( ! $post_id ) {
285
			return new WP_Error(
286
				'path_not_found',
287
				sprintf(
288
					__( 'Post not found for path %s.', 'writing-on-github' ),
289
					$path
290
				)
291
			);
292
		}
293
294
		$result = wp_delete_post( $post_id );
295
296
		// If deleting fails...
297
		if ( false === $result ) {
298
			$post = get_post( $post_id );
299
300
			// ...and the post both exists and isn't in the trash...
301
			if ( $post && 'trash' !== $post->post_status ) {
302
				// ... then something went wrong.
303
				return new WP_Error(
304
					'db_error',
305
					sprintf(
306
						__( 'Failed to delete post ID %d.', 'writing-on-github' ),
307
						$post_id
308
					)
309
				);
310
			}
311
		}
312
313
		return sprintf(
314
			__( 'Successfully deleted post ID %d.', 'writing-on-github' ),
315
			$post_id
316
		);
317
	}
318
319
	public function delete_post( $post_id ) {
320
		$result = wp_delete_post( $post_id );
321
322
		// If deleting fails...
323
		if ( false === $result ) {
324
			$post = get_post( $post_id );
325
326
			// ...and the post both exists and isn't in the trash...
327
			if ( $post && 'trash' !== $post->post_status ) {
328
				// ... then something went wrong.
329
				return new WP_Error(
330
					'db_error',
331
					sprintf(
332
						__( 'Failed to delete post ID %d.', 'writing-on-github' ),
333
						$post_id
334
					)
335
				);
336
			}
337
		}
338
339
		return sprintf(
340
			__( 'Successfully deleted post ID %d.', 'writing-on-github' ),
341
			$post_id
342
		);
343
	}
344
345
	/**
346
	 * Returns the list of post type permitted.
347
	 *
348
	 * @return array
349
	 */
350
	protected function get_whitelisted_post_types() {
351
		return apply_filters( 'wogh_whitelisted_post_types', $this->whitelisted_post_types );
352
	}
353
354
	/**
355
	 * Returns the list of post status permitted.
356
	 *
357
	 * @return array
358
	 */
359
	protected function get_whitelisted_post_statuses() {
360
		return apply_filters( 'wogh_whitelisted_post_statuses', $this->whitelisted_post_statuses );
361
	}
362
363
	/**
364
	 * Formats a whitelist array for a query.
365
	 *
366
	 * @param array $whitelist Whitelisted posts to format into query.
367
	 *
368
	 * @return string Whitelist formatted for query
369
	 */
370
	protected function format_for_query( $whitelist ) {
371
		foreach ( $whitelist as $key => $value ) {
372
			$whitelist[ $key ] = "'$value'";
373
		}
374
375
		return implode( ', ', $whitelist );
376
	}
377
378
	/**
379
	 * Verifies that both the post's status & type
380
	 * are currently whitelisted
381
	 *
382
	 * @param  Writing_On_GitHub_Post $post Post to verify.
383
	 *
384
	 * @return boolean                          True if supported, false if not.
385
	 */
386
	protected function is_post_supported( Writing_On_GitHub_Post $post ) {
387
		if ( wp_is_post_revision( $post->id ) ) {
388
			return false;
389
		}
390
391
		// We need to allow trashed posts to be queried, but they are not whitelisted for export.
392
		if ( ! in_array( $post->status(), $this->get_whitelisted_post_statuses() ) && 'trash' !== $post->status() ) {
393
			return false;
394
		}
395
396
		if ( ! in_array( $post->type(), $this->get_whitelisted_post_types() ) ) {
397
			return false;
398
		}
399
400
		if ( $post->has_password() ) {
401
			return false;
402
		}
403
404
		return apply_filters( 'wogh_is_post_supported', true, $post );
405
	}
406
407
	/**
408
	 * Retrieves the commit user for a provided display name
409
	 *
410
	 * Searches for a user with provided display name or returns
411
	 * the default user saved in the database.
412
	 *
413
	 * @param string $display_name User display name to search for.
414
	 *
415
	 * @return WP_Error|WP_User
416
	 */
417
	protected function fetch_commit_user( $display_name ) {
418
		// If we can't find a user and a default hasn't been set,
419
		// we're just going to set the revision author to 0.
420
		$user = false;
421
422
		if ( ! empty( $display_name ) ) {
423
			$search_string = esc_attr( $display_name );
424
			$query = new WP_User_Query( array(
425
				'search'         => "{$search_string}",
426
				'search_columns' => array(
427
					'display_name',
428
					'user_nicename',
429
					'user_login',
430
				)
431
			) );
432
			$users = $query->get_results();
433
			$user = empty($users) ? false : $users[0];
434
		}
435
436
		if ( ! $user ) {
437
			// Use the default user.
438
			$user = get_user_by( 'id', (int) get_option( 'wogh_default_user' ) );
439
		}
440
441
		if ( ! $user ) {
442
			return new WP_Error(
443
				'user_not_found',
444
				sprintf(
445
					__( 'Commit user not found for email %s', 'writing-on-github' ),
446
					$email
447
				)
448
			);
449
		}
450
451
		return $user;
452
	}
453
454
	/**
455
	 * Sets the author latest revision
456
	 * of the provided post ID to the provided user.
457
	 *
458
	 * @param int $post_id Post ID to update revision author.
459
	 * @param int $user_id User ID for revision author.
460
	 *
461
	 * @return string|WP_Error
462
	 */
463
	protected function set_revision_author( $post_id, $user_id ) {
464
		$revision = wp_get_post_revisions( $post_id );
465
466
		if ( ! $revision ) {
467
			$new_revision = wp_save_post_revision( $post_id );
468
469
			if ( ! $new_revision || is_wp_error( $new_revision ) ) {
470
				return new WP_Error( 'db_error', 'There was a problem saving a new revision.' );
471
			}
472
473
			// `wp_save_post_revision` returns the ID, whereas `get_post_revision` returns the whole object
474
			// in order to be consistent, let's make sure we have the whole object before continuing.
475
			$revision = get_post( $new_revision );
476
477
			if ( ! $revision ) {
478
				return new WP_Error( 'db_error', 'There was a problem retrieving the newly recreated revision.' );
479
			}
480
		} else {
481
			$revision = array_shift( $revision );
482
		}
483
484
		return $this->set_post_author( $revision->ID, $user_id );
485
	}
486
487
	/**
488
	 * Updates the user ID for the provided post ID.
489
	 *
490
	 * Bypassing triggering any hooks, including creating new revisions.
491
	 *
492
	 * @param int $post_id Post ID to update.
493
	 * @param int $user_id User ID to update to.
494
	 *
495
	 * @return string|WP_Error
496
	 */
497
	protected function set_post_author( $post_id, $user_id ) {
498
		global $wpdb;
499
500
		$result = $wpdb->update(
501
			$wpdb->posts,
502
			array(
503
				'post_author' => (int) $user_id,
504
			),
505
			array(
506
				'ID' => (int) $post_id,
507
			),
508
			array( '%d' ),
509
			array( '%d' )
510
		);
511
512
		if ( false === $result ) {
513
			return new WP_Error( 'db_error', $wpdb->last_error );
514
		}
515
516
		if ( 0 === $result ) {
517
			return sprintf(
518
				__( 'No change for post ID %d.', 'writing-on-github' ),
519
				$post_id
520
			);
521
		}
522
523
		clean_post_cache( $post_id );
524
525
		return sprintf(
526
			__( 'Successfully updated post ID %d.', 'writing-on-github' ),
527
			$post_id
528
		);
529
	}
530
531
	/**
532
	 * Update the provided post's blob sha.
533
	 *
534
	 * @param Writing_On_GitHub_Post $post Post to update.
535
	 * @param string                     $sha Sha to update to.
536
	 *
537
	 * @return bool|int
538
	 */
539
	public function set_post_sha( $post, $sha ) {
540
		return update_post_meta( $post->id, '_wogh_sha', $sha );
541
	}
542
}
543