Writing_On_GitHub_Database   C
last analyzed

Complexity

Total Complexity 57

Size/Duplication

Total Lines 496
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 3
Bugs 2 Features 0
Metric Value
eloc 189
c 3
b 2
f 0
dl 0
loc 496
ccs 0
cts 229
cp 0
rs 5.04
wmc 57

14 Methods

Rating   Name   Duplication   Size   Complexity  
A get_whitelisted_post_types() 0 2 1
A get_whitelisted_post_statuses() 0 2 1
A __construct() 0 2 1
A is_post_supported() 0 19 6
A get_post_id_by_filename() 0 14 1
B post_args() 0 51 9
B delete_post_by_path() 0 58 9
A fetch_all_supported() 0 31 6
A format_for_query() 0 6 2
A delete_post() 0 23 4
A fetch_by_id() 0 17 2
A fetch_commit_user() 0 35 5
A set_post_author() 0 31 3
B save_post() 0 43 7

How to fix   Complexity   

Complex Class

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,
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
     * Save an post to database
110
     * and associates their author as well as their latest
111
     *
112
     * @param  Writing_On_GitHub_Post $post [description]
113
     * @return WP_Error|true
114
     */
115
    public function save_post( Writing_On_GitHub_Post $post ) {
116
        $args = apply_filters( 'wogh_pre_import_args', $this->post_args( $post ), $post );
117
118
        remove_filter( 'content_save_pre', 'wp_filter_post_kses' );
119
        $post_id = $post->is_new() ?
120
            wp_insert_post( $args, true ) :
121
            wp_update_post( $args, true );
122
        add_filter( 'content_save_pre', 'wp_filter_post_kses' );
123
124
        if ( is_wp_error( $post_id ) ) {
125
            /* @var WP_Error $post_id */
126
            return $post_id;
127
        }
128
129
        if ( $post->is_new() ) {
130
            $author = false;
131
            $meta = $post->get_meta();
132
            if ( ! empty( $meta ) && ! empty( $meta['author'] ) ) {
133
                $author = $meta['author'];
134
            }
135
            $user    = $this->fetch_commit_user( $author );
136
            $user_id = is_wp_error( $user ) ? 0 : $user->ID;
0 ignored issues
show
Bug introduced by
The property ID does not seem to exist on WP_Error.
Loading history...
137
            $this->set_post_author( $post_id, $user_id );
0 ignored issues
show
Bug introduced by
It seems like $post_id can also be of type WP_Error; however, parameter $post_id of Writing_On_GitHub_Database::set_post_author() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

137
            $this->set_post_author( /** @scrutinizer ignore-type */ $post_id, $user_id );
Loading history...
138
        }
139
140
        $post->set_post( get_post( $post_id ) );
0 ignored issues
show
Bug introduced by
It seems like $post_id can also be of type WP_Error; however, parameter $post of get_post() does only seem to accept WP_Post|integer|null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

140
        $post->set_post( get_post( /** @scrutinizer ignore-type */ $post_id ) );
Loading history...
141
142
        $meta = apply_filters( 'wogh_pre_import_meta', $post->get_meta(), $post );
143
144
        update_post_meta( $post_id, '_wogh_sha', $meta['_wogh_sha'] );
0 ignored issues
show
Bug introduced by
It seems like $post_id can also be of type WP_Error; however, parameter $post_id of update_post_meta() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

144
        update_post_meta( /** @scrutinizer ignore-type */ $post_id, '_wogh_sha', $meta['_wogh_sha'] );
Loading history...
145
146
        // unset( $meta['tags'] );
147
        // unset( $meta['categories'] );
148
        // unset( $meta['author'] );
149
        // unset( $meta['post_date'] );
150
        // unset( $meta['post_excerpt'] );
151
        // unset( $meta['permalink'] );
152
        // unset( $meta['link'] );
153
154
        // foreach ( $meta as $key => $value ) {
155
        //  update_post_meta( $post_id, $key, $value );
156
        // }
157
        return true;
158
    }
159
160
    protected function post_args( $post ) {
161
        $args = $post->get_args();
162
        $meta = $post->get_meta();
163
164
        // prevent backslash loss
165
        $args['post_content'] = addslashes( $args['post_content'] );
166
167
        // update tags
168
        if ( ! empty( $meta['tags'] ) ) {
169
            $args['tags_input'] = $meta['tags'];
170
        }
171
172
        // update categories
173
        if ( ! empty( $meta['categories'] ) ) {
174
            $categories = $meta['categories'];
175
            if ( ! is_array( $categories ) ) {
176
                $categories = array( $categories );
177
            }
178
            $terms = get_terms( array(
179
                'taxonomy' => 'category',
180
                'fields' => 'id=>name',
181
                'hide_empty' => 0,
182
                'name' => $categories
183
                )
184
            );
185
            $map = array();
186
            foreach ( $categories as $name ) {
187
                $map[$name] = 1;
188
            }
189
190
            $ids = array();
191
            if ( ! empty( $terms ) ) {
192
                foreach ( $terms as $id => $name ) {
193
                    $ids[] = $id;
194
                    unset( $map[$name] );
195
                }
196
            }
197
198
            // create new terms
199
            if ( ! empty( $map ) ) {
200
                foreach ( $map as $name => $value ) {
201
                    $term = wp_insert_term( $name, 'category', array( 'parent' => 0 ) );
202
                    // array('term_id' => $term_id, 'term_taxonomy_id' => $tt_id);
203
                    $ids[] = $term['term_id'];
204
                }
205
            }
206
207
            $args['post_category'] = $ids;
208
        }
209
210
        return $args;
211
    }
212
213
    private function get_post_id_by_filename( $filename, $pattern  ) {
214
        preg_match( $pattern , $filename, $matches );
215
        $title = $matches[4];
216
217
        $query = new WP_Query( array(
218
            'name'     => $title,
219
            'posts_per_page' => 1,
220
            'post_type' => $this->get_whitelisted_post_types(),
221
            'fields'         => 'ids',
222
        ) );
223
224
        $post_id = $query->get_posts();
225
        $post_id = array_pop( $post_id );
226
        return $post_id;
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
                $post_id = get_post_id_by_filename( $filename, '/([0-9]{4})-([0-9]{2})-([0-9]{2})-(.*)\.md/' );
0 ignored issues
show
Bug introduced by
The function get_post_id_by_filename was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

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