Passed
Push — master ( 3a3f23...fdc038 )
by litefeel
02:23
created

Writing_On_GitHub_Database::save_post()   C

Complexity

Conditions 7
Paths 12

Size

Total Lines 44
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 56

Importance

Changes 0
Metric Value
cc 7
eloc 21
nc 12
nop 1
dl 0
loc 44
ccs 0
cts 23
cp 0
crap 56
rs 6.7272
c 0
b 0
f 0
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
     * 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
0 ignored issues
show
Documentation introduced by
Should the return type not be WP_Error|boolean?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
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;
137
            $this->set_post_author( $post_id, $user_id );
138
        }
139
140
        $post->set_post( get_post( $post_id ) );
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'] );
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
0 ignored issues
show
introduced by
Each line in an array declaration must end in a comma
Loading history...
183
                )
184
            );
185
            $map = array();
186
            foreach ( $categories as $name ) {
187
                $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...
188
            }
189
190
            $ids = array();
191
            if ( ! empty( $terms ) ) {
192
                foreach ( $terms as $id => $name ) {
193
                    $ids[] = $id;
194
                    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...
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',
0 ignored issues
show
introduced by
Detected usage of meta_key, possible slow query.
Loading history...
239
            'meta_value'     => $path,
0 ignored issues
show
introduced by
Detected usage of meta_value, possible slow query.
Loading history...
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/' );
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 View Code Duplication
        if ( false === $result ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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 View Code Duplication
        if ( false === $result ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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];
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...
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(
0 ignored issues
show
introduced by
Usage of a direct database call is discouraged.
Loading history...
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