Test Failed
Push — ci ( 50c141...9054a2 )
by litefeel
03:24
created

Writing_On_GitHub_Database   F

Complexity

Total Complexity 67

Size/Duplication

Total Lines 538
Duplicated Lines 10.78 %

Coupling/Cohesion

Dependencies 2

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 67
cbo 2
dl 58
loc 538
ccs 0
cts 258
cp 0
rs 3.0612
c 0
b 0
f 0

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Writing_On_GitHub_Database often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Writing_On_GitHub_Database, and based on these observations, apply Extract Interface, too.

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
     * @param  bool $force
46
     *
47
     * @return Writing_On_GitHub_Post[]|WP_Error
48
     */
49
	public function fetch_all_supported( $force = false ) {
50
		$args  = array(
51
			'post_type'   => $this->get_whitelisted_post_types(),
52
			'post_status' => $this->get_whitelisted_post_statuses(),
53
			'nopaging'    => true,
0 ignored issues
show
introduced by
Disabling pagination is prohibited in VIP context, do not set nopaging to true ever.
Loading history...
54
			'fields'      => 'ids',
55
		);
56
57
		$query = new WP_Query( apply_filters( 'wogh_pre_fetch_all_supported', $args ) );
58
59
		$post_ids = $query->get_posts();
60
61
		if ( ! $post_ids ) {
62
			return new WP_Error(
63
				'no_results',
64
				__( 'Querying for supported posts returned no results.', 'writing-on-github' )
65
			);
66
		}
67
68
        /* @var Writing_On_GitHub_Post[] $results */
69
		$results = array();
70
		foreach ( $post_ids as $post_id ) {
71
			// Do not export posts that have already been exported
72
			if ( $force || ! get_post_meta( $post_id, '_wogh_sha', true ) ||
73
				 ! get_post_meta( $post_id, '_wogh_github_path', true ) ) {
74
75
				$results[] = new Writing_On_GitHub_Post( $post_id, $this->app->api() );
76
			}
77
		}
78
79
		return $results;
80
	}
81
82
	/**
83
	 * Queries a post and returns it if it's supported.
84
	 *
85
	 * @param int $post_id Post ID to fetch.
86
	 *
87
	 * @return WP_Error|Writing_On_GitHub_Post
88
	 */
89
	public function fetch_by_id( $post_id ) {
90
		$post = new Writing_On_GitHub_Post( $post_id, $this->app->api() );
91
92
		if ( ! $this->is_post_supported( $post ) ) {
93
			return new WP_Error(
94
				'unsupported_post',
95
				sprintf(
96
					__(
97
						'Post ID %s is not supported by WOGH. See wiki to find out how to add support.',
98
						'writing-on-github'
99
					),
100
					$post_id
101
				)
102
			);
103
		}
104
105
		return $post;
106
	}
107
108
	/**
109
	 * Saves an array of Post objects to the database
110
	 * and associates their author as well as their latest
111
	 *
112
	 * @param Writing_On_GitHub_Post[] $posts Array of Posts to save.
113
	 *
114
	 * @return string|WP_Error
115
	 */
116
	public function save_posts( array $posts ) {
117
118
		/**
119
		 * Whether an error has occurred.
120
		 *
121
		 * @var WP_Error|false $error
122
		 */
123
		$error = false;
124
125
		foreach ( $posts as $post ) {
126
			$args = apply_filters( 'wogh_pre_import_args', $this->post_args( $post ), $post );
127
128
			remove_filter( 'content_save_pre', 'wp_filter_post_kses' );
129
			$post_id = $post->is_new() ?
130
				wp_insert_post( $args, true ) :
131
				wp_update_post( $args, true );
132
			add_filter( 'content_save_pre', 'wp_filter_post_kses' );
133
134
			if ( is_wp_error( $post_id ) ) {
135
                /* @var WP_Error $post_id */
136
                $error = wogh_append_error( $error, $post_id );
137
138
				// Abort saving if updating the post fails.
139
				continue;
140
			}
141
142
			if ( $post->is_new() ) {
143
				$author = false;
144
				$meta = $post->get_meta();
145
				if ( ! empty( $meta ) && ! empty( $meta['author'] ) ) {
146
					$author = $meta['author'];
147
				}
148
				$user    = $this->fetch_commit_user( $author );
149
				$user_id = is_wp_error( $user ) ? 0 : $user->ID;
150
				$this->set_post_author( $post_id, $user_id );
151
			}
152
153
			$post->set_post( get_post( $post_id ) );
154
155
			$meta = apply_filters( 'wogh_pre_import_meta', $post->get_meta(), $post );
156
157
			// unset( $meta['tags'] );
158
			// unset( $meta['categories'] );
159
			// unset( $meta['author'] );
160
			// unset( $meta['post_date'] );
161
			// unset( $meta['post_excerpt'] );
162
			// unset( $meta['permalink'] );
163
			// unset( $meta['link'] );
164
165
			// foreach ( $meta as $key => $value ) {
166
			// 	update_post_meta( $post_id, $key, $value );
167
			// }
168
		}
169
170
		if ( $error ) {
171
			return $error;
172
		}
173
174
		return __( 'Successfully saved posts.', 'writing-on-github' );
175
	}
176
177
	protected function post_args( $post ) {
178
		$args = $post->get_args();
179
		$meta = $post->get_meta();
180
181
		// prevent backslash loss
182
		$args['post_content'] = addslashes($args['post_content']);
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
183
184
		// update tags
185
		if ( ! empty( $meta['tags'] ) {
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 '{'
Loading history...
186
		    $args['tags_input'] = $meta['tags'];
187
		}
188
189
		// update categories
190
		if ( ! empty( $meta['categories'] ) {
191
		    $categories = $meta['categories'];
192
		    if ( ! is_array( $categories ) ) {
193
		        $categories = array( $categories );
194
		    }
195
		    $terms = get_terms( array(
196
		        'taxonomy' => 'category',
197
		        'fields' => 'id=>name',
198
		        'hide_empty' => 0,
199
		        'name' => $categories
0 ignored issues
show
introduced by
Each line in an array declaration must end in a comma
Loading history...
200
		        )
201
		    );
202
		    $map = array();
203
		    foreach ( $categories as $name ) {
204
		        $map[$name] = 1;
0 ignored issues
show
introduced by
Array keys should be surrounded by spaces unless they contain a string or an integer.
Loading history...
205
		    }
206
207
		    $ids = array();
208
		    if ( ! empty( $terms ) ) {
209
		        foreach ( $terms as $id => $name ) {
210
		            $ids[] = $id;
211
		            unset( $map[$name] );
0 ignored issues
show
introduced by
Array keys should be surrounded by spaces unless they contain a string or an integer.
Loading history...
212
		        }
213
		    }
214
215
		    // create new terms
216
		    if ( ! empty( $map ) ) {
217
		        foreach ( $map as $name => $value ) {
218
		            $term = wp_insert_term( $name, 'category', array( 'parent' => 0 ) );
219
		            // array('term_id' => $term_id, 'term_taxonomy_id' => $tt_id);
220
		            $ids[] = $term['term_id'];
221
		        }
222
		    }
223
224
		    $args['post_category'] = $ids;
225
		}
226
227
		return $args;
228
	}
229
230
    private function get_post_id_by_filename( $filename, $pattern  ) {
231
        preg_match( $pattern , $filename, $matches );
232
        $title = $matches[4];
233
234
        $query = new WP_Query( array(
235
            'name'     => $title,
236
            'posts_per_page' => 1,
237
            'post_type' => $this->get_whitelisted_post_types(),
238
            'fields'         => 'ids',
239
        ) );
240
241
        $post_id = $query->get_posts();
242
        $post_id = array_pop( $post_id );
243
        return $post_id;
244
    }
245
246
	/**
247
	 * Deletes a post from the database based on its GitHub path.
248
	 *
249
	 * @param string $path Path of Post to delete.
250
	 *
251
	 * @return string|WP_Error
252
	 */
253
	public function delete_post_by_path( $path ) {
254
		$query = new WP_Query( array(
255
			'meta_key'       => '_wogh_github_path',
0 ignored issues
show
introduced by
Detected usage of meta_key, possible slow query.
Loading history...
256
			'meta_value'     => $path,
0 ignored issues
show
introduced by
Detected usage of meta_value, possible slow query.
Loading history...
257
			'meta_compare'   => '=',
258
			'posts_per_page' => 1,
259
			'fields'         => 'ids',
260
		) );
261
262
		$post_id = $query->get_posts();
263
		$post_id = array_pop( $post_id );
264
265
		if ( ! $post_id ) {
266
			$parts     = explode( '/', $path );
267
			$filename  = array_pop( $parts );
268
			$directory = $parts ? array_shift( $parts ) : '';
269
270
			if ( false !== strpos( $directory, 'post' ) ) {
271
                $post_id = get_post_id_by_filename( $filename, '/([0-9]{4})-([0-9]{2})-([0-9]{2})-(.*)\.md/' );
272
			}
273
274
			if ( ! $post_id ) {
275
                $post_id = get_post_id_by_filename( $filename, '/(.*)\.md/' );
276
			}
277
		}
278
279
		if ( ! $post_id ) {
280
			return new WP_Error(
281
				'path_not_found',
282
				sprintf(
283
					__( 'Post not found for path %s.', 'writing-on-github' ),
284
					$path
285
				)
286
			);
287
		}
288
289
		$result = wp_delete_post( $post_id );
290
291
		// If deleting fails...
292
		if ( false === $result ) {
293
			$post = get_post( $post_id );
294
295
			// ...and the post both exists and isn't in the trash...
296
			if ( $post && 'trash' !== $post->post_status ) {
297
				// ... then something went wrong.
298
				return new WP_Error(
299
					'db_error',
300
					sprintf(
301
						__( 'Failed to delete post ID %d.', 'writing-on-github' ),
302
						$post_id
303
					)
304
				);
305
			}
306
		}
307
308
		return sprintf(
309
			__( 'Successfully deleted post ID %d.', 'writing-on-github' ),
310
			$post_id
311
		);
312
	}
313
314
	public function delete_post( $post_id ) {
315
		$result = wp_delete_post( $post_id );
316
317
		// If deleting fails...
318
		if ( false === $result ) {
319
			$post = get_post( $post_id );
320
321
			// ...and the post both exists and isn't in the trash...
322
			if ( $post && 'trash' !== $post->post_status ) {
323
				// ... then something went wrong.
324
				return new WP_Error(
325
					'db_error',
326
					sprintf(
327
						__( 'Failed to delete post ID %d.', 'writing-on-github' ),
328
						$post_id
329
					)
330
				);
331
			}
332
		}
333
334
		return sprintf(
335
			__( 'Successfully deleted post ID %d.', 'writing-on-github' ),
336
			$post_id
337
		);
338
	}
339
340
	/**
341
	 * Returns the list of post type permitted.
342
	 *
343
	 * @return array
344
	 */
345
	protected function get_whitelisted_post_types() {
346
		return apply_filters( 'wogh_whitelisted_post_types', $this->whitelisted_post_types );
347
	}
348
349
	/**
350
	 * Returns the list of post status permitted.
351
	 *
352
	 * @return array
353
	 */
354
	protected function get_whitelisted_post_statuses() {
355
		return apply_filters( 'wogh_whitelisted_post_statuses', $this->whitelisted_post_statuses );
356
	}
357
358
	/**
359
	 * Formats a whitelist array for a query.
360
	 *
361
	 * @param array $whitelist Whitelisted posts to format into query.
362
	 *
363
	 * @return string Whitelist formatted for query
364
	 */
365
	protected function format_for_query( $whitelist ) {
366
		foreach ( $whitelist as $key => $value ) {
367
			$whitelist[ $key ] = "'$value'";
368
		}
369
370
		return implode( ', ', $whitelist );
371
	}
372
373
	/**
374
	 * Verifies that both the post's status & type
375
	 * are currently whitelisted
376
	 *
377
	 * @param  Writing_On_GitHub_Post $post Post to verify.
378
	 *
379
	 * @return boolean                          True if supported, false if not.
380
	 */
381
	protected function is_post_supported( Writing_On_GitHub_Post $post ) {
382
		if ( wp_is_post_revision( $post->id ) ) {
383
			return false;
384
		}
385
386
		// We need to allow trashed posts to be queried, but they are not whitelisted for export.
387
		if ( ! in_array( $post->status(), $this->get_whitelisted_post_statuses() ) && 'trash' !== $post->status() ) {
388
			return false;
389
		}
390
391
		if ( ! in_array( $post->type(), $this->get_whitelisted_post_types() ) ) {
392
			return false;
393
		}
394
395
		if ( $post->has_password() ) {
396
			return false;
397
		}
398
399
		return apply_filters( 'wogh_is_post_supported', true, $post );
400
	}
401
402
	/**
403
	 * Retrieves the commit user for a provided display name
404
	 *
405
	 * Searches for a user with provided display name or returns
406
	 * the default user saved in the database.
407
	 *
408
	 * @param string $display_name User display name to search for.
409
	 *
410
	 * @return WP_Error|WP_User
411
	 */
412
	protected function fetch_commit_user( $display_name ) {
413
		// If we can't find a user and a default hasn't been set,
414
		// we're just going to set the revision author to 0.
415
		$user = false;
416
417
		if ( ! empty( $display_name ) ) {
418
			$search_string = esc_attr( $display_name );
419
			$query = new WP_User_Query( array(
420
			    'search'         => "{$search_string}",
421
			    'search_columns' => array(
422
			        'display_name',
423
			        'user_nicename',
424
			        'user_login',
425
			    )
426
			) );
427
			$users = $query->get_results();
428
			$user = empty($users) ? false : $users[0];
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
429
		}
430
431
		if ( ! $user ) {
432
			// Use the default user.
433
			$user = get_user_by( 'id', (int) get_option( 'wogh_default_user' ) );
434
		}
435
436
		if ( ! $user ) {
437
			return new WP_Error(
438
				'user_not_found',
439
				sprintf(
440
					__( 'Commit user not found for email %s', 'writing-on-github' ),
441
					$email
442
				)
443
			);
444
		}
445
446
		return $user;
447
	}
448
449
	// /**
450
	//  * Sets the author latest revision
451
	//  * of the provided post ID to the provided user.
452
	//  *
453
	//  * @param int $post_id Post ID to update revision author.
454
	//  * @param int $user_id User ID for revision author.
455
	//  *
456
	//  * @return string|WP_Error
457
	//  */
458
	// protected function set_revision_author( $post_id, $user_id ) {
459
	// 	$revision = wp_get_post_revisions( $post_id );
460
461
	// 	if ( ! $revision ) {
462
	// 		$new_revision = wp_save_post_revision( $post_id );
463
464
	// 		if ( ! $new_revision || is_wp_error( $new_revision ) ) {
465
	// 			return new WP_Error( 'db_error', 'There was a problem saving a new revision.' );
466
	// 		}
467
468
	// 		// `wp_save_post_revision` returns the ID, whereas `get_post_revision` returns the whole object
469
	// 		// in order to be consistent, let's make sure we have the whole object before continuing.
470
	// 		$revision = get_post( $new_revision );
471
472
	// 		if ( ! $revision ) {
473
	// 			return new WP_Error( 'db_error', 'There was a problem retrieving the newly recreated revision.' );
474
	// 		}
475
	// 	} else {
476
	// 		$revision = array_shift( $revision );
477
	// 	}
478
479
	// 	return $this->set_post_author( $revision->ID, $user_id );
480
	// }
481
482
	/**
483
	 * Updates the user ID for the provided post ID.
484
	 *
485
	 * Bypassing triggering any hooks, including creating new revisions.
486
	 *
487
	 * @param int $post_id Post ID to update.
488
	 * @param int $user_id User ID to update to.
489
	 *
490
	 * @return string|WP_Error
491
	 */
492
	protected function set_post_author( $post_id, $user_id ) {
493
		global $wpdb;
494
495
		$result = $wpdb->update(
0 ignored issues
show
introduced by
Usage of a direct database call is discouraged.
Loading history...
496
			$wpdb->posts,
497
			array(
498
				'post_author' => (int) $user_id,
499
			),
500
			array(
501
				'ID' => (int) $post_id,
502
			),
503
			array( '%d' ),
504
			array( '%d' )
505
		);
506
507
		if ( false === $result ) {
508
			return new WP_Error( 'db_error', $wpdb->last_error );
509
		}
510
511
		if ( 0 === $result ) {
512
			return sprintf(
513
				__( 'No change for post ID %d.', 'writing-on-github' ),
514
				$post_id
515
			);
516
		}
517
518
		clean_post_cache( $post_id );
519
520
		return sprintf(
521
			__( 'Successfully updated post ID %d.', 'writing-on-github' ),
522
			$post_id
523
		);
524
	}
525
526
	// *
527
	//  * Update the provided post's blob sha.
528
	//  *
529
	//  * @param Writing_On_GitHub_Post $post Post to update.
530
	//  * @param string                     $sha Sha to update to.
531
	//  *
532
	//  * @return bool|int
533
534
	// public function set_post_sha( $post, $sha ) {
535
	// 	return update_post_meta( $post->id, '_wogh_sha', $sha );
536
	// }
537
}
538