Completed
Push — master ( 48f45b...559f84 )
by Armando
01:19
created

WP2D_Post::post()   C

Complexity

Conditions 10
Paths 14

Size

Total Lines 70
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 70
rs 5.9999
c 0
b 0
f 0
cc 10
eloc 33
nc 14
nop 2

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
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 19 and the first side effect is on line 10.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
/**
3
 * A diaspora* flavoured WP Post class.
4
 *
5
 * @package WP_To_Diaspora\Post
6
 * @since   1.5.0
7
 */
8
9
// Exit if accessed directly.
10
defined( 'ABSPATH' ) || exit;
11
12
use League\HTMLToMarkdown\HtmlConverter;
13
14
/**
15
 * Custom diaspora* post class to manage all post related things.
16
 *
17
 * @since 1.5.0
18
 */
19
class WP2D_Post {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
20
21
	/**
22
	 * The original post object.
23
	 *
24
	 * @var WP_Post
25
	 * @since 1.5.0
26
	 */
27
	public $post;
28
29
	/**
30
	 * The original post ID.
31
	 *
32
	 * @var int
33
	 * @since 1.5.0
34
	 */
35
	public $ID;
36
37
	/**
38
	 * If this post should be shared on diaspora*.
39
	 *
40
	 * @var bool
41
	 * @since 1.5.0
42
	 */
43
	public $post_to_diaspora;
44
45
	/**
46
	 * If a link back to the original post should be added.
47
	 *
48
	 * @var bool
49
	 * @since 1.5.0
50
	 */
51
	public $fullentrylink;
52
53
	/**
54
	 * What content gets posted.
55
	 *
56
	 * @var string
57
	 * @since 1.5.0
58
	 */
59
	public $display;
60
61
	/**
62
	 * The types of tags to post. (global,custom,post)
63
	 *
64
	 * @var array
65
	 * @since 1.5.0
66
	 */
67
	public $tags_to_post;
68
69
	/**
70
	 * The post's custom tags.
71
	 *
72
	 * @var array
73
	 * @since 1.5.0
74
	 */
75
	public $custom_tags;
76
77
	/**
78
	 * Aspects this post gets posted to.
79
	 *
80
	 * @var array
81
	 * @since 1.5.0
82
	 */
83
	public $aspects;
84
85
	/**
86
	 * Services this post gets posted to.
87
	 *
88
	 * @var array
89
	 * @since 1.5.0
90
	 */
91
	public $services;
92
93
	/**
94
	 * The post's history of diaspora* posts.
95
	 *
96
	 * @var array
97
	 * @since 1.5.0
98
	 */
99
	public $post_history;
100
101
	/**
102
	 * If the post actions have all been set up already.
103
	 *
104
	 * @var boolean
105
	 * @since 1.5.0
106
	 */
107
	private static $_is_set_up = false;
108
109
	/**
110
	 * Setup all the necessary WP callbacks.
111
	 *
112
	 * @since 1.5.0
113
	 */
114
	public static function setup() {
115
		if ( self::$_is_set_up ) {
116
			return;
117
		}
118
119
		$instance = new WP2D_Post( null );
120
121
		// Notices when a post has been shared or if it has failed.
122
		add_action( 'admin_notices', [ $instance, 'admin_notices' ] );
123
		add_action( 'admin_init', [ $instance, 'ignore_post_error' ] );
124
125
		// Handle diaspora* posting when saving the post.
126
		add_action( 'save_post', [ $instance, 'post' ], 20, 2 );
127
		add_action( 'save_post', [ $instance, 'save_meta_box_data' ], 10 );
128
129
		// Add meta boxes.
130
		add_action( 'add_meta_boxes', [ $instance, 'add_meta_boxes' ] );
131
132
		self::$_is_set_up = true;
133
	}
134
135
	/**
136
	 * Constructor.
137
	 *
138
	 * @since 1.5.0
139
	 *
140
	 * @param int|WP_Post $post Post ID or the post itself.
141
	 */
142
	public function __construct( $post ) {
143
		$this->_assign_wp_post( $post );
144
	}
145
146
	/**
147
	 * Assign the original WP_Post object and all the custom meta data.
148
	 *
149
	 * @since 1.5.0
150
	 *
151
	 * @param int|WP_Post $post Post ID or the post itself.
152
	 */
153
	private function _assign_wp_post( $post ) {
154
		if ( $this->post = get_post( $post ) ) {
155
			$this->ID = $this->post->ID;
156
157
			$options = WP2D_Options::instance();
158
159
			// Assign all meta values, expanding non-existent ones with the defaults.
160
			$meta_current = get_post_meta( $this->ID, '_wp_to_diaspora', true );
161
			$meta         = wp_parse_args(
162
				$meta_current,
163
				$options->get_options()
164
			);
165
			if ( $meta ) {
166
				foreach ( $meta as $key => $value ) {
167
					$this->$key = $value;
168
				}
169
			}
170
171
			// If no WP2D meta data has been saved yet, this post shouldn't be published.
172
			// This can happen if existing posts (before WP2D) get updated externally, not through the post edit screen.
173
			// Check DiasPHPora/wp-to-diaspora#91 for reference.
174
			// Also, when we have a post scheduled for publishing, don't touch it.
175
			// This is important when modifying scheduled posts using Quick Edit.
176
			if ( ! $meta_current && ! in_array( $this->post->post_status, [ 'auto-draft', 'future' ], true ) ) {
177
				$this->post_to_diaspora = false;
178
			}
179
180
			$this->post_history = get_post_meta( $this->ID, '_wp_to_diaspora_post_history', true );
181
		}
182
	}
183
184
	/**
185
	 * Post to diaspora* when saving a post.
186
	 *
187
	 * @since 1.5.0
188
	 *
189
	 * @todo  Maybe somebody wants to share a password protected post to a closed aspect.
190
	 *
191
	 * @param integer $post_id ID of the post being saved.
192
	 * @param WP_Post $post    Post object being saved.
193
	 *
194
	 * @return bool If the post was posted successfully.
195
	 */
196
	public function post( $post_id, $post ) {
197
		$this->_assign_wp_post( $post );
198
199
		$options = WP2D_Options::instance();
200
201
		// Is this post type enabled for posting?
202
		if ( ! in_array( $post->post_type, $options->get_option( 'enabled_post_types' ), true ) ) {
203
			return false;
204
		}
205
206
		// Make sure we're posting to diaspora* and the post isn't password protected.
207
		if ( ! ( $this->post_to_diaspora && 'publish' === $post->post_status && '' === $post->post_password ) ) {
208
			return false;
209
		}
210
211
		$status_message = $this->_get_title_link();
212
213
		// Post the full post text, just the excerpt, or nothing at all?
214
		if ( 'full' === $this->display ) {
215
			$status_message .= $this->_get_full_content();
216
		} elseif ( 'excerpt' === $this->display ) {
217
			$status_message .= $this->_get_excerpt_content();
218
		}
219
220
		// Add the tags assigned to the post.
221
		$status_message .= $this->_get_tags_to_add();
222
223
		// Add the original entry link to the post?
224
		$status_message .= $this->_get_posted_at_link();
225
226
		$status_converter = new HtmlConverter( [ 'strip_tags' => true ] );
227
		$status_message   = $status_converter->convert( $status_message );
228
229
		// Set up the connection to diaspora*.
230
		$api = WP2D_Helpers::api_quick_connect();
231
		if ( empty( $status_message ) ) {
232
			return false;
233
		}
234
235
		if ( $api->has_last_error() ) {
236
			// Save the post error as post meta data, so we can display it to the user.
237
			update_post_meta( $post_id, '_wp_to_diaspora_post_error', $api->get_last_error() );
238
239
			return false;
240
		}
241
242
		// Add services to share to via diaspora*.
243
		$extra_data = [
244
			'services' => $this->services,
245
		];
246
247
		// Try to post to diaspora*.
248
		$response = $api->post( $status_message, $this->aspects, $extra_data );
249
		if ( ! $response ) {
250
			return false;
251
		}
252
253
		// Save certain diaspora* post data as meta data for future reference.
254
		$this->_save_to_history( (object) $response );
255
256
		// If there is still a previous post error around, remove it.
257
		delete_post_meta( $post_id, '_wp_to_diaspora_post_error' );
258
259
		// Unset post_to_diaspora meta field to prevent mistakenly republishing to diaspora*.
260
		$meta                     = get_post_meta( $post_id, '_wp_to_diaspora', true );
261
		$meta['post_to_diaspora'] = false;
262
		update_post_meta( $post_id, '_wp_to_diaspora', $meta );
263
264
		return true;
265
	}
266
267
	/**
268
	 * Get the title of the post linking to the post itself.
269
	 *
270
	 * @since 1.5.0
271
	 *
272
	 * @return string Post title as a link.
273
	 */
274
	private function _get_title_link() {
275
		$title      = esc_html( $this->post->post_title );
276
		$permalink  = get_permalink( $this->ID );
277
		$title_link = sprintf( '<strong><a href="%2$s" title="%2$s">%1$s</a></strong>', $title, $permalink );
278
279
		/**
280
		 * Filter the title link at the top of the post.
281
		 *
282
		 * @since 1.5.4.1
283
		 *
284
		 * @param string    $default   The whole HTML of the title link to be outputted.
285
		 * @param WP2D_Post $wp2d_post This object, to allow total customisation of the title.
286
		 */
287
		return apply_filters( 'wp2d_title_filter', "<p>{$title_link}</p>", $this );
288
	}
289
290
	/**
291
	 * Get the full post content with only default filters applied.
292
	 *
293
	 * @since 1.5.0
294
	 *
295
	 * @return string The full post content.
296
	 */
297
	private function _get_full_content() {
298
		// Only allow certain shortcodes.
299
		global $shortcode_tags;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
300
		$shortcode_tags_bkp = [];
301
302
		foreach ( $shortcode_tags as $shortcode_tag => $shortcode_function ) {
303
			if ( ! in_array( $shortcode_tag, apply_filters( 'wp2d_shortcodes_filter', [ 'wp_caption', 'caption', 'gallery' ] ), true ) ) {
304
				$shortcode_tags_bkp[ $shortcode_tag ] = $shortcode_function;
305
				unset( $shortcode_tags[ $shortcode_tag ] );
306
			}
307
		}
308
309
		// Disable all filters and then enable only defaults. This prevents additional filters from being posted to diaspora*.
310
		remove_all_filters( 'the_content' );
311
312
		/** @var array $content_filters */
313
		$content_filters = apply_filters( 'wp2d_content_filters_filter', [ 'do_shortcode', 'wptexturize', 'convert_smilies', 'convert_chars', 'wpautop', 'shortcode_unautop', 'prepend_attachment', [ $this, 'embed_remove' ] ] );
314
		foreach ( $content_filters as $filter ) {
315
			add_filter( 'the_content', $filter );
316
		}
317
318
		// Extract URLs from [embed] shortcodes.
319
		add_filter( 'embed_oembed_html', [ $this, 'embed_url' ], 10, 2 );
320
321
		// Add the pretty caption after the images.
322
		add_filter( 'img_caption_shortcode', [ $this, 'custom_img_caption' ], 10, 3 );
323
324
		// Overwrite the native shortcode handler to add pretty captions.
325
		// http://wordpress.stackexchange.com/a/74675/54456 for explanation.
326
		add_shortcode( 'gallery', [ $this, 'custom_gallery_shortcode' ] );
327
328
		$post_content = apply_filters( 'the_content', $this->post->post_content );
329
330
		// Put the removed shortcode tags back again.
331
		$shortcode_tags += $shortcode_tags_bkp;
332
333
		/**
334
		 * Filter the full content of the post.
335
		 *
336
		 * @since 2.1.0
337
		 *
338
		 * @param string    $default   The whole HTML of the post to be outputted.
339
		 * @param WP2D_Post $wp2d_post This object, to allow total customisation of the post.
340
		 */
341
		return apply_filters( 'wp2d_post_filter', $post_content, $this );
342
	}
343
344
	/**
345
	 * Get the post's excerpt in a nice format.
346
	 *
347
	 * @since 1.5.0
348
	 *
349
	 * @return string Post's excerpt.
350
	 */
351
	private function _get_excerpt_content() {
352
		// Look for the excerpt in the following order:
353
		// 1. Custom post excerpt.
354
		// 2. Text up to the <!--more--> tag.
355
		// 3. Manually trimmed content.
356
		$content = $this->post->post_content;
357
		$excerpt = $this->post->post_excerpt;
358
		if ( '' === $excerpt ) {
359
			if ( $more_pos = strpos( $content, '<!--more' ) ) {
360
				$excerpt = substr( $content, 0, $more_pos );
361
			} else {
362
				$excerpt = wp_trim_words( $content, 42, '[...]' );
363
			}
364
		}
365
366
		/**
367
		 * Filter the excerpt of the post.
368
		 *
369
		 * @since 2.1.0
370
		 *
371
		 * @param string    $default   The whole HTML of the excerpt to be outputted.
372
		 * @param WP2D_Post $wp2d_post This object, to allow total customisation of the excerpt.
373
		 */
374
		return apply_filters( 'wp2d_excerpt_filter', "<p>{$excerpt}</p>", $this );
375
	}
376
377
	/**
378
	 * Get a string of tags that have been added to the post.
379
	 *
380
	 * @since 1.5.0
381
	 *
382
	 * @return string Tags added to the post.
383
	 */
384
	private function _get_tags_to_add() {
385
		$options       = WP2D_Options::instance();
386
		$tags_to_post  = $this->tags_to_post;
387
		$tags_to_add   = '';
388
		$diaspora_tags = [];
389
390
		// Add any diaspora* tags?
391
		if ( ! empty( $tags_to_post ) ) {
392
			// The diaspora* tags to add to the post.
393
			$diaspora_tags_tmp = [];
394
395
			// Add global tags?
396
			$global_tags = $options->get_option( 'global_tags' );
397
			if ( is_array( $global_tags ) && in_array( 'global', $tags_to_post, true ) ) {
398
				$diaspora_tags_tmp += array_flip( $global_tags );
399
			}
400
401
			// Add custom tags?
402
			if ( is_array( $this->custom_tags ) && in_array( 'custom', $tags_to_post, true ) ) {
403
				$diaspora_tags_tmp += array_flip( $this->custom_tags );
404
			}
405
406
			// Add post tags?
407
			$post_tags = wp_get_post_tags( $this->ID, [ 'fields' => 'slugs' ] );
408
			if ( is_array( $post_tags ) && in_array( 'post', $tags_to_post, true ) ) {
409
				$diaspora_tags_tmp += array_flip( $post_tags );
410
			}
411
412
			// Get an array of cleaned up tags.
413
			// NOTE: Validate method needs a variable, as it's passed by reference!
414
			$diaspora_tags_tmp = array_keys( $diaspora_tags_tmp );
415
			$options->validate_tags( $diaspora_tags_tmp );
416
417
			// Get all the tags and list them all nicely in a row.
418
			foreach ( $diaspora_tags_tmp as $tag ) {
0 ignored issues
show
Bug introduced by
The expression $diaspora_tags_tmp of type array|string is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
419
				$diaspora_tags[] = '#' . $tag;
420
			}
421
422
			// Add all the found tags.
423
			if ( ! empty( $diaspora_tags ) ) {
424
				$tags_to_add = implode( ' ', $diaspora_tags ) . '<br/>';
425
			}
426
		}
427
428
		/**
429
		 * Filter the tags of the post.
430
		 *
431
		 * @since 2.1.0
432
		 *
433
		 * @param string    $default   The whole string of tags to be outputted.
434
		 * @param array     $tags      All tags that are assigned to this post.
435
		 * @param WP2D_Post $wp2d_post This object, to allow total customisation of the tags output.
436
		 */
437
		return apply_filters( 'wp2d_tags_filter', $tags_to_add, $diaspora_tags, $this );
438
	}
439
440
	/**
441
	 * Get the link to the original post.
442
	 *
443
	 * @since 1.5.0
444
	 *
445
	 * @return string Original post link.
446
	 */
447
	private function _get_posted_at_link() {
448
		if ( $this->fullentrylink ) {
449
			$prefix         = esc_html__( 'Originally posted at:', 'wp-to-diaspora' );
450
			$permalink      = get_permalink( $this->ID );
451
			$title          = esc_html__( 'Permalink', 'wp-to-diaspora' );
452
			$posted_at_link = sprintf( '%1$s <a href="%2$s" title="%3$s">%2$s</a>', $prefix, $permalink, $title );
453
454
			/**
455
			 * Filter the "Originally posted at" link at the bottom of the post.
456
			 *
457
			 * @since 1.5.4.1
458
			 *
459
			 * @param string    $default   The whole HTML of the text and link to be outputted.
460
			 * @param WP2D_Post $wp2d_post This object, to allow total customisation of the title.
461
			 * @param string    $prefix    The "Originally posted at:" prefix before the link.
462
			 */
463
			return apply_filters( 'wp2d_posted_at_link_filter', "<p>{$posted_at_link}</p>", $this, $prefix );
464
		}
465
466
		return '';
467
	}
468
469
	/**
470
	 * Save the details of the new diaspora* post to this post's history.
471
	 *
472
	 * @since 1.5.0
473
	 *
474
	 * @param object $response Response from the API containing the diaspora* post details.
475
	 */
476
	private function _save_to_history( $response ) {
477
		// Make sure the post history is an array.
478
		if ( empty( $this->post_history ) ) {
479
			$this->post_history = [];
480
		}
481
482
		// Add a new entry to the history.
483
		$this->post_history[] = [
484
			'id'         => $response->id,
485
			'guid'       => $response->guid,
486
			'created_at' => $this->post->post_modified,
487
			'aspects'    => $this->aspects,
488
			'nsfw'       => $response->nsfw,
489
			'post_url'   => $response->permalink,
490
		];
491
492
		update_post_meta( $this->ID, '_wp_to_diaspora_post_history', $this->post_history );
493
	}
494
495
	/**
496
	 * Return URL from [embed] shortcode instead of generated iframe.
497
	 *
498
	 * @since 1.5.0
499
	 * @see   WP_Embed::shortcode()
500
	 *
501
	 * @param mixed  $html The cached HTML result, stored in post meta.
502
	 * @param string $url  The attempted embed URL.
503
	 *
504
	 * @return string URL of the embed.
505
	 */
506
	public function embed_url( $html, $url ) {
507
		return $url;
508
	}
509
510
	/**
511
	 * Removes '[embed]' and '[/embed]' left by embed_url.
512
	 *
513
	 * @since 1.5.0
514
	 * @todo  It would be great to fix it using only one filter.
515
	 *       It's happening because embed filter is being removed by remove_all_filters('the_content') on WP2D_Post::post().
516
	 *
517
	 * @param string $content Content of the post.
518
	 *
519
	 * @return string The content with the embed tags removed.
520
	 */
521
	public function embed_remove( $content ) {
522
		return str_replace( [ '[embed]', '[/embed]' ], [ '<p>', '</p>' ], $content );
523
	}
524
525
	/**
526
	 * Prettify the image caption.
527
	 *
528
	 * @since 1.5.3
529
	 *
530
	 * @param string $caption Caption to be prettified.
531
	 *
532
	 * @return string Prettified image caption.
533
	 */
534
	public function get_img_caption( $caption ) {
535
		$caption = trim( $caption );
536
		if ( '' === $caption ) {
537
			return '';
538
		}
539
540
		/**
541
		 * Filter the image caption to be displayed after images with captions.
542
		 *
543
		 * @since 1.5.3
544
		 *
545
		 * @param string $default The whole HTML of the caption.
546
		 * @param string $caption The caption text.
547
		 */
548
		return apply_filters( 'wp2d_image_caption', "<blockquote>{$caption}</blockquote>", $caption );
549
	}
550
551
	/**
552
	 * Filter the default caption shortcode output.
553
	 *
554
	 * @since 1.5.3
555
	 * @see   img_caption_shortcode()
556
	 *
557
	 * @param string $empty   The caption output. Default empty.
558
	 * @param array  $attr    Attributes of the caption shortcode.
559
	 * @param string $content The image element, possibly wrapped in a hyperlink.
560
	 *
561
	 * @return string The caption shortcode output.
562
	 */
563
	public function custom_img_caption( $empty, $attr, $content ) {
564
		$content = do_shortcode( $content );
565
566
		// If a caption attribute is defined, we'll add it after the image.
567
		if ( isset( $attr['caption'] ) && '' !== $attr['caption'] ) {
568
			$content .= "\n" . $this->get_img_caption( $attr['caption'] );
569
		}
570
571
		return $content;
572
	}
573
574
	/**
575
	 * Create a custom gallery caption output.
576
	 *
577
	 * @since 1.5.3
578
	 *
579
	 * @param   array $attr Gallery attributes.
580
	 *
581
	 * @return  string
582
	 */
583
	public function custom_gallery_shortcode( $attr ) {
584
		// Default value in WordPress.
585
		$captiontag = current_theme_supports( 'html5', 'gallery' ) ? 'figcaption' : 'dd';
586
587
		// User value.
588
		if ( isset( $attr['captiontag'] ) ) {
589
			$captiontag = $attr['captiontag'];
590
		}
591
592
		// Let WordPress create the regular gallery.
593
		$gallery = gallery_shortcode( $attr );
594
595
		// Change the content of the captions.
596
		$gallery = preg_replace_callback(
597
			'~(<' . $captiontag . '.*>)(.*)(</' . $captiontag . '>)~mUus',
598
			[ $this, 'custom_gallery_regex_callback' ],
599
			$gallery
600
		);
601
602
		return $gallery;
603
	}
604
605
	/**
606
	 * Change the result of the regex match from custom_gallery_shortcode.
607
	 *
608
	 * @param array $m Regex matches.
609
	 *
610
	 * @return string Prettified gallery image caption.
611
	 */
612
	public function custom_gallery_regex_callback( $m ) {
613
		return $this->get_img_caption( $m[2] );
614
	}
615
616
	/*
617
	 * META BOX
618
	 */
619
620
	/**
621
	 * Adds a meta box to the main column on the enabled Post Types' edit screens.
622
	 *
623
	 * @since 1.5.0
624
	 */
625
	public function add_meta_boxes() {
626
		$options = WP2D_Options::instance();
627
		foreach ( $options->get_option( 'enabled_post_types' ) as $post_type ) {
628
			add_meta_box(
629
				'wp_to_diaspora_meta_box',
630
				'WP to diaspora*',
631
				[ $this, 'meta_box_render' ],
632
				$post_type,
633
				'side',
634
				'high'
635
			);
636
		}
637
	}
638
639
	/**
640
	 * Prints the meta box content.
641
	 *
642
	 * @since 1.5.0
643
	 *
644
	 * @param WP_Post $post The object for the current post.
645
	 */
646
	public function meta_box_render( $post ) {
647
		$this->_assign_wp_post( $post );
648
649
		// Add an nonce field so we can check for it later.
650
		wp_nonce_field( 'wp_to_diaspora_meta_box', 'wp_to_diaspora_meta_box_nonce' );
651
652
		// Get the default values to use, but give priority to the meta data already set.
653
		$options = WP2D_Options::instance();
654
655
		// Make sure we have some value for post meta fields.
656
		$this->custom_tags = $this->custom_tags ?: [];
657
658
		// If this post is already published, don't post again to diaspora* by default.
659
		$this->post_to_diaspora = ( $this->post_to_diaspora && 'publish' !== get_post_status( $this->ID ) );
660
		$this->aspects          = $this->aspects ?: [];
661
		$this->services         = $this->services ?: [];
662
663
		// Have we already posted on diaspora*?
664
		if ( is_array( $this->post_history ) ) {
665
			$latest_post = end( $this->post_history );
666
			?>
667
			<p><a href="<?php echo esc_attr( $latest_post['post_url'] ); ?>" target="_blank"><?php esc_html_e( 'Already posted to diaspora*.', 'wp-to-diaspora' ); ?></a></p>
668
			<?php
669
		}
670
		?>
671
672
		<p><?php $options->post_to_diaspora_render( $this->post_to_diaspora ); ?></p>
673
		<p><?php $options->fullentrylink_render( $this->fullentrylink ); ?></p>
674
		<p><?php $options->display_render( $this->display ); ?></p>
675
		<p><?php $options->tags_to_post_render( $this->tags_to_post ); ?></p>
676
		<p><?php $options->custom_tags_render( $this->custom_tags ); ?></p>
677
		<p><?php $options->aspects_services_render( [ 'aspects', $this->aspects ] ); ?></p>
678
		<p><?php $options->aspects_services_render( [ 'services', $this->services ] ); ?></p>
679
680
		<?php
681
	}
682
683
	/**
684
	 * When the post is saved, save our meta data.
685
	 *
686
	 * @since 1.5.0
687
	 *
688
	 * @param integer $post_id The ID of the post being saved.
689
	 */
690
	public function save_meta_box_data( $post_id ) {
0 ignored issues
show
Coding Style introduced by
save_meta_box_data uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
691
		/*
692
		 * We need to verify this came from our screen and with proper authorization,
693
		 * because the save_post action can be triggered at other times.
694
		 */
695
		if ( ! $this->_is_safe_to_save() ) {
696
			return;
697
		}
698
699
		/* OK, it's safe for us to save the data now. */
700
701
		// Meta data to save.
702
		$meta_to_save = $_POST['wp_to_diaspora_settings'];
703
		$options      = WP2D_Options::instance();
704
705
		// Checkboxes.
706
		$options->validate_checkboxes( [ 'post_to_diaspora', 'fullentrylink' ], $meta_to_save );
707
708
		// Single Selects.
709
		$options->validate_single_selects( 'display', $meta_to_save );
710
711
		// Multiple Selects.
712
		$options->validate_multi_selects( 'tags_to_post', $meta_to_save );
713
714
		// Save custom tags as array.
715
		$options->validate_tags( $meta_to_save['custom_tags'] );
716
717
		// Clean up the list of aspects. If the list is empty, only use the 'Public' aspect.
718
		$options->validate_aspects_services( $meta_to_save['aspects'], [ 'public' ] );
719
720
		// Clean up the list of services.
721
		$options->validate_aspects_services( $meta_to_save['services'] );
722
723
		// Update the meta data for this post.
724
		update_post_meta( $post_id, '_wp_to_diaspora', $meta_to_save );
725
	}
726
727
	/**
728
	 * Perform all checks to see if we are allowed to save the meta data.
729
	 *
730
	 * @since 1.5.0
731
	 *
732
	 * @return bool If the verification checks have passed.
733
	 */
734
	private function _is_safe_to_save() {
0 ignored issues
show
Coding Style introduced by
_is_safe_to_save uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
735
		// Verify that our nonce is set and  valid.
736
		if ( ! ( isset( $_POST['wp_to_diaspora_meta_box_nonce'] ) && wp_verify_nonce( $_POST['wp_to_diaspora_meta_box_nonce'], 'wp_to_diaspora_meta_box' ) ) ) {
737
			return false;
738
		}
739
740
		// If this is an autosave, our form has not been submitted, so we don't want to do anything.
741
		if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
742
			return false;
743
		}
744
745
		// Check the user's permissions.
746
		$permission = ( isset( $_POST['post_type'] ) && 'page' === $_POST['post_type'] ) ? 'edit_pages' : 'edit_posts';
747
		if ( ! current_user_can( $permission, $this->ID ) ) {
748
			return false;
749
		}
750
751
		// Make real sure that we have some meta data to save.
752
		if ( ! isset( $_POST['wp_to_diaspora_settings'] ) ) {
753
			return false;
754
		}
755
756
		return true;
757
	}
758
759
	/**
760
	 * Add admin notices when a post gets displayed.
761
	 *
762
	 * @since 1.5.0
763
	 *
764
	 * @todo  Ignore post error with AJAX.
765
	 */
766
	public function admin_notices() {
0 ignored issues
show
Coding Style introduced by
admin_notices uses the super-global variable $_GET which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
767
		global $post, $pagenow;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
768
		if ( ! $post || 'post.php' !== $pagenow ) {
769
			return;
770
		}
771
772
		if ( ( $error = get_post_meta( $post->ID, '_wp_to_diaspora_post_error', true ) ) && is_wp_error( $error ) ) {
773
			// Are we adding a help tab link to this notice?
774
			$help_link = WP2D_Contextual_Help::get_help_tab_quick_link( $error );
775
776
			// This notice will only be shown if posting to diaspora* has failed.
777
			printf( '<div class="error notice is-dismissible"><p>%1$s %2$s %3$s <a href="%4$s">%5$s</a></p></div>',
778
				esc_html__( 'Failed to post to diaspora*.', 'wp-to-diaspora' ),
779
				esc_html__( $error->get_error_message() ),
780
				$help_link,
781
				esc_url( add_query_arg( 'wp2d_ignore_post_error', '' ) ),
782
				esc_html__( 'Ignore', 'wp-to-diaspora' )
783
			);
784
		} elseif ( ( $diaspora_post_history = get_post_meta( $post->ID, '_wp_to_diaspora_post_history', true ) ) && is_array( $diaspora_post_history ) ) {
785
			// Get the latest post from the history.
786
			$latest_post = end( $diaspora_post_history );
787
788
			// Only show if this post is showing a message and the post is a fresh share.
789
			if ( isset( $_GET['message'] ) && $post->post_modified === $latest_post['created_at'] ) {
790
				printf( '<div class="updated notice is-dismissible"><p>%1$s <a href="%2$s" target="_blank">%3$s</a></p></div>',
791
					esc_html__( 'Successfully posted to diaspora*.', 'wp-to-diaspora' ),
792
					esc_url( $latest_post['post_url'] ),
793
					esc_html__( 'View Post' )
794
				);
795
			}
796
		}
797
	}
798
799
	/**
800
	 * Delete the error post meta data if it gets ignored.
801
	 *
802
	 * @since 1.5.0
803
	 */
804
	public function ignore_post_error() {
0 ignored issues
show
Coding Style introduced by
ignore_post_error uses the super-global variable $_GET which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
805
		// If "Ignore" link has been clicked, delete the post error meta data.
806
		if ( isset( $_GET['wp2d_ignore_post_error'], $_GET['post'] ) ) {
807
			delete_post_meta( $_GET['post'], '_wp_to_diaspora_post_error' );
808
		}
809
	}
810
}
811