Completed
Push — dna/fix-wpcom-fatal ( a948da...ae1bff )
by
unknown
07:16
created

social_notifications_subscribe_field()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 13
rs 9.8333
c 0
b 0
f 0
1
<?php
2
/**
3
 * Module Name: Subscriptions
4
 * Module Description: Let visitors subscribe to new posts and comments via email
5
 * Sort Order: 9
6
 * Recommendation Order: 8
7
 * First Introduced: 1.2
8
 * Requires Connection: Yes
9
 * Auto Activate: No
10
 * Module Tags: Social
11
 * Feature: Engagement
12
 * Additional Search Queries: subscriptions, subscription, email, follow, followers, subscribers, signup
13
 */
14
15
add_action( 'jetpack_modules_loaded', 'jetpack_subscriptions_load' );
16
17
function jetpack_subscriptions_load() {
18
	Jetpack::enable_module_configurable( __FILE__ );
19
}
20
21
/**
22
 * Cherry picks keys from `$_SERVER` array.
23
 *
24
 * @since 6.0.0
25
 *
26
 * @return array An array of server data.
27
 */
28
function jetpack_subscriptions_cherry_pick_server_data() {
29
	$data = array();
30
31
	foreach ( $_SERVER as $key => $value ) {
32
		if ( ! is_string( $value ) || 0 === strpos( $key, 'HTTP_COOKIE' ) ) {
33
			continue;
34
		}
35
36
		if ( 0 === strpos( $key, 'HTTP_' ) || in_array( $key, array( 'REMOTE_ADDR', 'REQUEST_URI', 'DOCUMENT_URI' ), true ) ) {
37
			$data[ $key ] = $value;
38
		}
39
	}
40
41
	return $data;
42
}
43
44
class Jetpack_Subscriptions {
45
	public $jetpack = false;
46
47
	public static $hash;
48
49
	/**
50
	 * Singleton
51
	 * @static
52
	 */
53
	static function init() {
54
		static $instance = false;
55
56
		if ( !$instance ) {
57
			$instance = new Jetpack_Subscriptions;
58
		}
59
60
		return $instance;
61
	}
62
63
	function __construct() {
64
		$this->jetpack = Jetpack::init();
0 ignored issues
show
Documentation Bug introduced by
It seems like \Jetpack::init() of type object<Jetpack> is incompatible with the declared type boolean of property $jetpack.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
65
66
		// Don't use COOKIEHASH as it could be shared across installs && is non-unique in multisite.
67
		// @see: https://twitter.com/nacin/status/378246957451333632
68
		self::$hash = md5( get_option( 'siteurl' ) );
69
70
		add_filter( 'jetpack_xmlrpc_methods', array( $this, 'xmlrpc_methods' ) );
71
72
		// @todo remove sync from subscriptions and move elsewhere...
73
74
		// Add Configuration Page
75
		add_action( 'admin_init', array( $this, 'configure' ) );
76
77
		// Catch subscription widget submits
78
		if ( isset( $_REQUEST['jetpack_subscriptions_widget'] ) )
79
			add_action( 'template_redirect', array( $this, 'widget_submit' ) );
80
81
		// Set up the comment subscription checkboxes
82
		add_filter( 'comment_form_submit_field', array( $this, 'comment_subscribe_init' ), 10, 2 );
83
84
		// Catch comment posts and check for subscriptions.
85
		add_action( 'comment_post', array( $this, 'comment_subscribe_submit' ), 50, 2 );
86
87
		// Adds post meta checkbox in the post submit metabox
88
		add_action( 'post_submitbox_misc_actions', array( $this, 'subscription_post_page_metabox' ) );
89
90
		add_action( 'transition_post_status', array( $this, 'maybe_send_subscription_email' ), 10, 3 );
91
92
		add_filter( 'jetpack_published_post_flags', array( $this, 'set_post_flags' ), 10, 2 );
93
94
		add_filter( 'post_updated_messages', array( $this, 'update_published_message' ), 18, 1 );
95
96
		// Set and delete "social_notifications_subscribe" option during activation / deactivation
97
		add_action( 'jetpack_activate_module_subscriptions',   array( $this, 'set_social_notifications_subscribe' ) );
98
		add_action( 'jetpack_deactivate_module_subscriptions', array( $this, 'delete_social_notifications_subscribe' ) );
99
	}
100
101
	/**
102
	 * Jetpack_Subscriptions::xmlrpc_methods()
103
	 *
104
	 * Register subscriptions methods with the Jetpack XML-RPC server.
105
	 * @param array $methods
106
	 */
107
	function xmlrpc_methods( $methods ) {
108
		return array_merge(
109
			$methods,
110
			array(
111
				'jetpack.subscriptions.subscribe' => array( $this, 'subscribe' ),
112
			)
113
		);
114
	}
115
116
	/*
117
	 * Disable Subscribe on Single Post
118
	 * Register post meta
119
	 */
120
	function subscription_post_page_metabox() {
121
		if (
122
			/**
123
			 * Filter whether or not to show the per-post subscription option.
124
			 *
125
			 * @module subscriptions
126
			 *
127
			 * @since 3.7.0
128
			 *
129
			 * @param bool true = show checkbox option on all new posts | false = hide the option.
130
			 */
131
			 ! apply_filters( 'jetpack_allow_per_post_subscriptions', false ) )
132
		{
133
			return;
134
		}
135
136
		if ( has_filter( 'jetpack_subscriptions_exclude_these_categories' ) || has_filter( 'jetpack_subscriptions_include_only_these_categories' ) ) {
137
			return;
138
		}
139
140
		global $post;
141
		$disable_subscribe_value = get_post_meta( $post->ID, '_jetpack_dont_email_post_to_subs', true );
142
		// only show checkbox if post hasn't been published and is a 'post' post type.
143
		if ( get_post_status( $post->ID ) !== 'publish' && get_post_type( $post->ID ) == 'post' ) :
144
			// Nonce it
145
			wp_nonce_field( 'disable_subscribe', 'disable_subscribe_nonce' );
146
			?>
147
			<div class="misc-pub-section">
148
				<label for="_jetpack_dont_email_post_to_subs"><?php _e( 'Jetpack Subscriptions:', 'jetpack' ); ?></label><br>
149
				<input type="checkbox" name="_jetpack_dont_email_post_to_subs" id="jetpack-per-post-subscribe" value="1" <?php checked( $disable_subscribe_value, 1, true ); ?> />
150
				<?php _e( 'Don&#8217;t send this to subscribers', 'jetpack' ); ?>
151
			</div>
152
		<?php endif;
153
	}
154
155
	/**
156
	 * Checks whether or not the post should be emailed to subscribers
157
	 *
158
	 * It checks for the following things in order:
159
	 * - Usage of filter jetpack_subscriptions_exclude_these_categories
160
	 * - Usage of filter jetpack_subscriptions_include_only_these_categories
161
	 * - Existence of the per-post checkbox option
162
	 *
163
	 * Only one of these can be used at any given time.
164
	 *
165
	 * @param $new_status string - the "new" post status of the transition when saved
166
	 * @param $old_status string - the "old" post status of the transition when saved
167
	 * @param $post obj - The post object
168
	 */
169
	function maybe_send_subscription_email( $new_status, $old_status, $post ) {
170
171
		if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
172
			return;
173
		}
174
175
		// Make sure that the checkbox is preseved
176
		if ( ! empty( $_POST['disable_subscribe_nonce'] ) && wp_verify_nonce( $_POST['disable_subscribe_nonce'], 'disable_subscribe' ) ) {
177
			$set_checkbox = isset( $_POST['_jetpack_dont_email_post_to_subs'] ) ? 1 : 0;
178
			update_post_meta( $post->ID, '_jetpack_dont_email_post_to_subs', $set_checkbox );
179
		}
180
	}
181
182
	function update_published_message( $messages ) {
183
		global $post;
184
		if ( ! $this->should_email_post_to_subscribers( $post ) ) {
185
			return $messages;
186
		}
187
188
		$view_post_link_html = sprintf( ' <a href="%1$s">%2$s</a>',
189
			esc_url( get_permalink( $post ) ),
190
			__( 'View post', 'jetpack' )
191
		);
192
193
		$messages['post'][6] = sprintf(
194
			/* translators: Message shown after a post is published */
195
			esc_html__( 'Post published and sending emails to subscribers.', 'jetpack' )
196
			) . $view_post_link_html;
197
		return $messages;
198
	}
199
200
	public function should_email_post_to_subscribers( $post ) {
201
		$should_email = true;
202
		if ( get_post_meta( $post->ID, '_jetpack_dont_email_post_to_subs', true ) ) {
203
			return false;
204
		}
205
206
		// Only posts are currently supported
207
		if ( $post->post_type !== 'post' ) {
208
			return false;
209
		}
210
211
		// Private posts are not sent to subscribers.
212
		if ( 'private' === $post->post_status ) {
213
			return false;
214
		}
215
216
		/**
217
		 * Array of categories that will never trigger subscription emails.
218
		 *
219
		 * Will not send subscription emails from any post from within these categories.
220
		 *
221
		 * @module subscriptions
222
		 *
223
		 * @since 3.7.0
224
		 *
225
		 * @param array $args Array of category slugs or ID's.
226
		 */
227
		$excluded_categories = apply_filters( 'jetpack_subscriptions_exclude_these_categories', array() );
228
229
		// Never email posts from these categories
230
		if ( ! empty( $excluded_categories ) && in_category( $excluded_categories, $post->ID ) ) {
231
			$should_email = false;
232
		}
233
234
		/**
235
		 * ONLY send subscription emails for these categories
236
		 *
237
		 * Will ONLY send subscription emails to these categories.
238
		 *
239
		 * @module subscriptions
240
		 *
241
		 * @since 3.7.0
242
		 *
243
		 * @param array $args Array of category slugs or ID's.
244
		 */
245
		$only_these_categories = apply_filters( 'jetpack_subscriptions_exclude_all_categories_except', array() );
246
247
		// Only emails posts from these categories
248
		if ( ! empty( $only_these_categories ) && ! in_category( $only_these_categories, $post->ID ) ) {
249
			$should_email = false;
250
		}
251
252
		return $should_email;
253
	}
254
255
	function set_post_flags( $flags, $post ) {
256
		$flags['send_subscription'] = $this->should_email_post_to_subscribers( $post );
257
		return $flags;
258
	}
259
260
	/**
261
	 * Jetpack_Subscriptions::configure()
262
	 *
263
	 * Jetpack Subscriptions configuration screen.
264
	 */
265
	function configure() {
266
		// Create the section
267
		add_settings_section(
268
			'jetpack_subscriptions',
269
			__( 'Jetpack Subscriptions Settings', 'jetpack' ),
270
			array( $this, 'subscriptions_settings_section' ),
271
			'discussion'
272
		);
273
274
		/** Subscribe to Posts ***************************************************/
275
276
		add_settings_field(
277
			'jetpack_subscriptions_post_subscribe',
278
			__( 'Follow Blog', 'jetpack' ),
279
			array( $this, 'subscription_post_subscribe_setting' ),
280
			'discussion',
281
			'jetpack_subscriptions'
282
		);
283
284
		register_setting(
285
			'discussion',
286
			'stb_enabled'
287
		);
288
289
		/** Subscribe to Comments ******************************************************/
290
291
		add_settings_field(
292
			'jetpack_subscriptions_comment_subscribe',
293
			__( 'Follow Comments', 'jetpack' ),
294
			array( $this, 'subscription_comment_subscribe_setting' ),
295
			'discussion',
296
			'jetpack_subscriptions'
297
		);
298
299
		register_setting(
300
			'discussion',
301
			'stc_enabled'
302
		);
303
304
		/** Email me whenever: Someone follows my blog ***************************************************/
305
		/* @since 8.1 */
306
307
		add_settings_section(
308
			'notifications_section',
309
			__( 'Someone follows my blog', 'jetpack' ),
310
			array( $this, 'social_notifications_subscribe_section' ),
311
			'discussion'
312
		);
313
314
		add_settings_field(
315
			'jetpack_subscriptions_social_notifications_subscribe',
316
			__( 'Email me whenever', 'jetpack' ),
317
			array( $this, 'social_notifications_subscribe_field' ),
318
			'discussion',
319
			'notifications_section'
320
		);
321
322
		register_setting(
323
			'discussion',
324
			'social_notifications_subscribe',
325
			array( $this, 'social_notifications_subscribe_validate' )
326
		);
327
328
		/** Subscription Messaging Options ******************************************************/
329
330
		register_setting(
331
			'reading',
332
			'subscription_options',
333
			array( $this, 'validate_settings' )
334
		);
335
336
		add_settings_section(
337
			'email_settings',
338
			__( 'Follower Settings', 'jetpack' ),
339
			array( $this, 'reading_section' ),
340
			'reading'
341
		);
342
343
		add_settings_field(
344
			'invitation',
345
			__( 'Blog follow email text', 'jetpack' ),
346
			array( $this, 'setting_invitation' ),
347
			'reading',
348
			'email_settings'
349
		);
350
351
		add_settings_field(
352
			'comment-follow',
353
			__( 'Comment follow email text', 'jetpack' ),
354
			array( $this, 'setting_comment_follow' ),
355
			'reading',
356
			'email_settings'
357
		);
358
	}
359
360
	/**
361
	 * Discussions setting section blurb
362
	 *
363
	 */
364
	function subscriptions_settings_section() {
365
	?>
366
		<p id="jetpack-subscriptions-settings"><?php _e( 'Change whether your visitors can subscribe to your posts or comments or both.', 'jetpack' ); ?></p>
367
368
	<?php
369
	}
370
371
	/**
372
	 * Post Subscriptions Toggle
373
	 *
374
	 */
375 View Code Duplication
	function subscription_post_subscribe_setting() {
376
377
		$stb_enabled = get_option( 'stb_enabled', 1 ); ?>
378
379
		<p class="description">
380
			<input type="checkbox" name="stb_enabled" id="jetpack-post-subscribe" value="1" <?php checked( $stb_enabled, 1 ); ?> />
381
			<?php _e( "Show a <em>'follow blog'</em> option in the comment form", 'jetpack' ); ?>
382
		</p>
383
	<?php
384
	}
385
386
	/**
387
	 * Comments Subscriptions Toggle
388
	 *
389
	 */
390 View Code Duplication
	function subscription_comment_subscribe_setting() {
391
392
		$stc_enabled = get_option( 'stc_enabled', 1 ); ?>
393
394
		<p class="description">
395
			<input type="checkbox" name="stc_enabled" id="jetpack-comment-subscribe" value="1" <?php checked( $stc_enabled, 1 ); ?> />
396
			<?php _e( "Show a <em>'follow comments'</em> option in the comment form", 'jetpack' ); ?>
397
		</p>
398
399
	<?php
400
	}
401
402
	/**
403
	 * Someone follows my blog section
404
	 *
405
	 * @since 8.1
406
	 */
407
	public function social_notifications_subscribe_section() {
408
		// Atypical usage here. We emit jquery to move subscribe notification checkbox to be with the rest of the email notification settings
409
		?>
410
		<script type="text/javascript">
411
			jQuery( function( $ )  {
412
				var table = $( '#social_notifications_subscribe' ).parents( 'table:first' ),
413
					header = table.prevAll( 'h2:first' ),
414
					newParent = $( '#moderation_notify' ).parent( 'label' ).parent();
415
416
				if ( ! table.length || ! header.length || ! newParent.length ) {
417
					return;
418
				}
419
420
				newParent.append( '<br/>' ).append( table.end().parent( 'label' ).siblings().andSelf() );
421
				header.remove();
422
				table.remove();
423
			} );
424
		</script>
425
		<?php
426
	}
427
428
	/**
429
	 * Someone follows my blog Toggle
430
	 *
431
	 * @since 8.1
432
	 */
433
	public function social_notifications_subscribe_field() {
434
		$checked = intval( 'on' === get_option( 'social_notifications_subscribe', 'on' ) );
435
		?>
436
437
		<label>
438
			<input type="checkbox" name="social_notifications_subscribe" id="social_notifications_subscribe" value="1" <?php checked( $checked ); ?> />
439
			<?php
440
				/* translators: this is a label for a setting that starts with "Email me whenever" */
441
				esc_html_e( 'Someone follows my blog', 'jetpack' );
442
			?>
443
		</label>
444
		<?php
445
	}
446
447
	/**
448
	 * Validate "Someone follows my blog" option
449
	 *
450
	 * @since 8.1
451
	 *
452
	 * @param String $input the input string to be validated.
453
	 * @return string on|off
454
	 */
455
	public function social_notifications_subscribe_validate( $input ) {
456
		// If it's not set (was unchecked during form submission) or was set to off (during option update), return 'off'.
457
		if ( ! $input || 'off' === $input ) {
458
			return 'off';
459
		}
460
461
		// Otherwise we return 'on'.
462
		return 'on';
463
	}
464
465
	function validate_settings( $settings ) {
466
		global $allowedposttags;
467
468
		$default = $this->get_default_settings();
469
470
		// Blog Follow
471
		$settings['invitation'] = trim( wp_kses( $settings['invitation'], $allowedposttags ) );
472
		if ( empty( $settings['invitation'] ) )
473
			$settings['invitation'] = $default['invitation'];
474
475
		// Comments Follow (single post)
476
		$settings['comment_follow'] = trim( wp_kses( $settings['comment_follow'], $allowedposttags ) );
477
		if ( empty( $settings['comment_follow'] ) )
478
			$settings['comment_follow'] = $default['comment_follow'];
479
480
		return $settings;
481
	}
482
483
	public function reading_section() {
484
		echo '<p id="follower-settings">';
485
		_e( 'These settings change emails sent from your blog to followers.', 'jetpack' );
486
		echo '</p>';
487
	}
488
489
	public function setting_invitation() {
490
		$settings = $this->get_settings();
491
		echo '<textarea name="subscription_options[invitation]" class="large-text" cols="50" rows="5">' . esc_textarea( $settings['invitation'] ) . '</textarea>';
492
		echo '<p><span class="description">'.__( 'Introduction text sent when someone follows your blog. (Site and confirmation details will be automatically added for you.)', 'jetpack' ).'</span></p>';
493
	}
494
495
	public function setting_comment_follow() {
496
		$settings = $this->get_settings();
497
		echo '<textarea name="subscription_options[comment_follow]" class="large-text" cols="50" rows="5">' . esc_textarea( $settings['comment_follow'] ) . '</textarea>';
498
		echo '<p><span class="description">'.__( 'Introduction text sent when someone follows a post on your blog. (Site and confirmation details will be automatically added for you.)', 'jetpack' ).'</span></p>';
499
	}
500
501
	function get_default_settings() {
502
		return array(
503
			'invitation'     => __( "Howdy.\n\nYou recently followed this blog's posts. This means you will receive each new post by email.\n\nTo activate, click confirm below. If you believe this is an error, ignore this message and we'll never bother you again.", 'jetpack' ),
504
			'comment_follow' => __( "Howdy.\n\nYou recently followed one of my posts. This means you will receive an email when new comments are posted.\n\nTo activate, click confirm below. If you believe this is an error, ignore this message and we'll never bother you again.", 'jetpack' )
505
		);
506
	}
507
508
	function get_settings() {
509
		return wp_parse_args( (array) get_option( 'subscription_options', array() ), $this->get_default_settings() );
510
	}
511
512
	/**
513
	 * Jetpack_Subscriptions::subscribe()
514
	 *
515
	 * Send a synchronous XML-RPC subscribe to blog posts or subscribe to post comments request.
516
	 *
517
	 * @param string $email
518
	 * @param array  $post_ids (optional) defaults to 0 for blog posts only: array of post IDs to subscribe to blog's posts
0 ignored issues
show
Documentation introduced by
Should the type for parameter $post_ids not be integer?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
519
	 * @param bool   $async    (optional) Should the subscription be performed asynchronously?  Defaults to true.
520
	 *
521
	 * @return true|Jetpack_Error true on success
522
	 *	invalid_email   : not a valid email address
523
	 *	invalid_post_id : not a valid post ID
524
	 *	unknown_post_id : unknown post
525
	 *	not_subscribed  : strange error.  Jetpack servers at WordPress.com could subscribe the email.
526
	 *	disabled        : Site owner has disabled subscriptions.
527
	 *	active          : Already subscribed.
528
	 *	unknown         : strange error.  Jetpack servers at WordPress.com returned something malformed.
529
	 *	unknown_status  : strange error.  Jetpack servers at WordPress.com returned something I didn't understand.
530
	 */
531
	function subscribe( $email, $post_ids = 0, $async = true, $extra_data = array() ) {
532
		if ( !is_email( $email ) ) {
533
			return new Jetpack_Error( 'invalid_email' );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with 'invalid_email'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
534
		}
535
536
		if ( !$async ) {
537
			$xml = new Jetpack_IXR_ClientMulticall();
538
		}
539
540
		foreach ( (array) $post_ids as $post_id ) {
541
			$post_id = (int) $post_id;
542
			if ( $post_id < 0 ) {
543
				return new Jetpack_Error( 'invalid_post_id' );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with 'invalid_post_id'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
544
			} else if ( $post_id && !$post = get_post( $post_id ) ) {
545
				return new Jetpack_Error( 'unknown_post_id' );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with 'unknown_post_id'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
546
			}
547
548
			if ( $async ) {
549
				Jetpack::xmlrpc_async_call( 'jetpack.subscribeToSite', $email, $post_id, serialize( $extra_data ) );
550
			} else {
551
				$xml->addCall( 'jetpack.subscribeToSite', $email, $post_id, serialize( $extra_data ) );
0 ignored issues
show
Bug introduced by
The variable $xml does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
552
			}
553
		}
554
555
		if ( $async ) {
556
			return;
557
		}
558
559
		// Call
560
		$xml->query();
561
562
		if ( $xml->isError() ) {
563
			return $xml->get_jetpack_error();
564
		}
565
566
		$responses = $xml->getResponse();
567
568
		$r = array();
569
		foreach ( (array) $responses as $response ) {
570
			if ( isset( $response['faultCode'] ) || isset( $response['faultString'] ) ) {
571
				$r[] = $xml->get_jetpack_error( $response['faultCode'], $response['faultString'] );
572
				continue;
573
			}
574
575
			if ( !is_array( $response[0] ) || empty( $response[0]['status'] ) ) {
576
				$r[] = new Jetpack_Error( 'unknown' );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with 'unknown'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
577
				continue;
578
			}
579
580
			switch ( $response[0]['status'] ) {
581
			case 'error' :
582
				$r[] = new Jetpack_Error( 'not_subscribed' );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with 'not_subscribed'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
583
				continue 2;
584
			case 'disabled' :
585
				$r[] = new Jetpack_Error( 'disabled' );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with 'disabled'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
586
				continue 2;
587
			case 'active' :
588
				$r[] = new Jetpack_Error( 'active' );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with 'active'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
589
				continue 2;
590
			case 'pending' :
591
				$r[] = true;
592
				continue 2;
593
			default :
594
				$r[] = new Jetpack_Error( 'unknown_status', (string) $response[0]['status'] );
0 ignored issues
show
Unused Code introduced by
The call to Jetpack_Error::__construct() has too many arguments starting with 'unknown_status'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
595
				continue 2;
596
			}
597
		}
598
599
		return $r;
600
	}
601
602
	/**
603
	 * Jetpack_Subscriptions::widget_submit()
604
	 *
605
	 * When a user submits their email via the blog subscription widget, check the details and call the subsribe() method.
606
	 */
607
	function widget_submit() {
608
		// Check the nonce.
609
		if ( is_user_logged_in() ) {
610
			check_admin_referer( 'blogsub_subscribe_' . get_current_blog_id() );
611
		}
612
613
		if ( empty( $_REQUEST['email'] ) )
614
			return false;
615
616
		$redirect_fragment = false;
617
		if ( isset( $_REQUEST['redirect_fragment'] ) ) {
618
			$redirect_fragment = preg_replace( '/[^a-z0-9_-]/i', '', $_REQUEST['redirect_fragment'] );
619
		}
620
		if ( !$redirect_fragment ) {
621
			$redirect_fragment = 'subscribe-blog';
622
		}
623
624
		$subscribe = Jetpack_Subscriptions::subscribe(
625
												$_REQUEST['email'],
626
												0,
627
												false,
628
												array(
629
													'source'         => 'widget',
630
													'widget-in-use'  => is_active_widget( false, false, 'blog_subscription', true ) ? 'yes' : 'no',
631
													'comment_status' => '',
632
													'server_data'    => jetpack_subscriptions_cherry_pick_server_data(),
633
												)
634
		);
635
636
		if ( is_wp_error( $subscribe ) ) {
637
			$error = $subscribe->get_error_code();
0 ignored issues
show
Bug introduced by
The method get_error_code() does not seem to exist on object<WP_Error>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
638
		} else {
639
			$error = false;
640
			foreach ( $subscribe as $response ) {
0 ignored issues
show
Bug introduced by
The expression $subscribe of type null|object<WP_Error>|array 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...
641
				if ( is_wp_error( $response ) ) {
642
					$error = $response->get_error_code();
643
					break;
644
				}
645
			}
646
		}
647
648
		switch ( $error ) {
649
			case false:
650
				$result = 'success';
651
				break;
652
			case 'invalid_email':
653
				$result = $error;
654
				break;
655
			case 'blocked_email':
656
				$result = 'opted_out';
657
				break;
658
			case 'active':
659
			case 'pending':
660
				$result = 'already';
661
				break;
662
			default:
663
				$result = 'error';
664
				break;
665
		}
666
667
		$redirect = add_query_arg( 'subscribe', $result );
668
669
		/**
670
		 * Fires on each subscription form submission.
671
		 *
672
		 * @module subscriptions
673
		 *
674
		 * @since 3.7.0
675
		 *
676
		 * @param string $result Result of form submission: success, invalid_email, already, error.
677
		 */
678
		do_action( 'jetpack_subscriptions_form_submission', $result );
679
680
		wp_safe_redirect( "$redirect#$redirect_fragment" );
681
		exit;
682
	}
683
684
	/**
685
	 * Jetpack_Subscriptions::comment_subscribe_init()
686
	 *
687
	 * Set up and add the comment subscription checkbox to the comment form.
688
	 *
689
	 * @param string $submit_button HTML markup for the submit field.
690
	 * @param array  $args          Arguments passed to `comment_form()`.
691
	 */
692
	function comment_subscribe_init( $submit_button, $args ) {
693
		global $post;
694
695
		$comments_checked = '';
696
		$blog_checked     = '';
697
698
		// Check for a comment / blog submission and set a cookie to retain the setting and check the boxes.
699
		if ( isset( $_COOKIE[ 'jetpack_comments_subscribe_' . self::$hash . '_' . $post->ID ] ) ) {
700
			$comments_checked = ' checked="checked"';
701
		}
702
703
		if ( isset( $_COOKIE[ 'jetpack_blog_subscribe_' . self::$hash ] ) ) {
704
			$blog_checked = ' checked="checked"';
705
		}
706
707
		// Some themes call this function, don't show the checkbox again
708
		remove_action( 'comment_form', 'subscription_comment_form' );
709
710
		// Check if Mark Jaquith's Subscribe to Comments plugin is active - if so, suppress Jetpack checkbox
711
712
		$str = '';
713
714
		if ( FALSE === has_filter( 'comment_form', 'show_subscription_checkbox' ) && 1 == get_option( 'stc_enabled', 1 ) && empty( $post->post_password ) && 'post' == get_post_type() ) {
715
			// Subscribe to comments checkbox
716
			$str .= '<p class="comment-subscription-form"><input type="checkbox" name="subscribe_comments" id="subscribe_comments" value="subscribe" style="width: auto; -moz-appearance: checkbox; -webkit-appearance: checkbox;"' . $comments_checked . ' /> ';
717
			$comment_sub_text = __( 'Notify me of follow-up comments by email.', 'jetpack' );
718
			$str .=	'<label class="subscribe-label" id="subscribe-label" for="subscribe_comments">' . esc_html(
719
				/**
720
				 * Filter the Subscribe to comments text appearing below the comment form.
721
				 *
722
				 * @module subscriptions
723
				 *
724
				 * @since 3.4.0
725
				 *
726
				 * @param string $comment_sub_text Subscribe to comments text.
727
				 */
728
				apply_filters( 'jetpack_subscribe_comment_label', $comment_sub_text )
729
			) . '</label>';
730
			$str .= '</p>';
731
		}
732
733
		if ( 1 == get_option( 'stb_enabled', 1 ) ) {
734
			// Subscribe to blog checkbox
735
			$str .= '<p class="comment-subscription-form"><input type="checkbox" name="subscribe_blog" id="subscribe_blog" value="subscribe" style="width: auto; -moz-appearance: checkbox; -webkit-appearance: checkbox;"' . $blog_checked . ' /> ';
736
			$blog_sub_text = __( 'Notify me of new posts by email.', 'jetpack' );
737
			$str .=	'<label class="subscribe-label" id="subscribe-blog-label" for="subscribe_blog">' . esc_html(
738
				/**
739
				 * Filter the Subscribe to blog text appearing below the comment form.
740
				 *
741
				 * @module subscriptions
742
				 *
743
				 * @since 3.4.0
744
				 *
745
				 * @param string $comment_sub_text Subscribe to blog text.
746
				 */
747
				apply_filters( 'jetpack_subscribe_blog_label', $blog_sub_text )
748
			) . '</label>';
749
			$str .= '</p>';
750
		}
751
752
		/**
753
		 * Filter the output of the subscription options appearing below the comment form.
754
		 *
755
		 * @module subscriptions
756
		 *
757
		 * @since 1.2.0
758
		 *
759
		 * @param string $str Comment Subscription form HTML output.
760
		 */
761
		$str = apply_filters( 'jetpack_comment_subscription_form', $str );
762
763
		return $str . $submit_button;
764
	}
765
766
	/**
767
	 * Jetpack_Subscriptions::comment_subscribe_init()
768
	 *
769
	 * When a user checks the comment subscribe box and submits a comment, subscribe them to the comment thread.
770
	 */
771
	function comment_subscribe_submit( $comment_id, $approved ) {
772
		if ( 'spam' === $approved ) {
773
			return;
774
		}
775
776
		$comment = get_comment( $comment_id );
777
778
		// Set cookies for this post/comment
779
		$this->set_cookies( isset( $_REQUEST['subscribe_comments'] ), $comment->comment_post_ID, isset( $_REQUEST['subscribe_blog'] ) );
780
781
		if ( !isset( $_REQUEST['subscribe_comments'] ) && !isset( $_REQUEST['subscribe_blog'] ) )
782
			return;
783
784
		$post_ids = array();
785
786
		if ( isset( $_REQUEST['subscribe_comments'] ) )
787
			$post_ids[] = $comment->comment_post_ID;
788
789
		if ( isset( $_REQUEST['subscribe_blog'] ) )
790
			$post_ids[] = 0;
791
792
		$result = Jetpack_Subscriptions::subscribe(
793
									$comment->comment_author_email,
794
									$post_ids,
0 ignored issues
show
Documentation introduced by
$post_ids is of type array, but the function expects a integer.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
795
									true,
796
									array(
797
										'source'         => 'comment-form',
798
										'widget-in-use'  => is_active_widget( false, false, 'blog_subscription', true ) ? 'yes' : 'no',
799
										'comment_status' => $approved,
800
										'server_data'    => jetpack_subscriptions_cherry_pick_server_data(),
801
									)
802
		);
803
804
		/**
805
		 * Fires on each comment subscription form submission.
806
		 *
807
		 * @module subscriptions
808
		 *
809
		 * @since 5.5.0
810
		 *
811
		 * @param NULL|WP_Error $result Result of form submission: NULL on success, WP_Error otherwise.
812
		 * @param Array $post_ids An array of post IDs that the user subscribed to, 0 means blog subscription.
813
		 */
814
		do_action( 'jetpack_subscriptions_comment_form_submission', $result, $post_ids );
815
	}
816
817
	/**
818
	 * Jetpack_Subscriptions::set_cookies()
819
	 *
820
	 * Set a cookie to save state on the comment and post subscription checkboxes.
821
	 *
822
	 * @param bool $subscribe_to_post Whether the user chose to subscribe to subsequent comments on this post.
823
	 * @param int $post_id If $subscribe_to_post is true, the post ID they've subscribed to.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $post_id not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
824
	 * @param bool $subscribe_to_blog Whether the user chose to subscribe to all new posts on the blog.
825
	 */
826
	function set_cookies( $subscribe_to_post = false, $post_id = null, $subscribe_to_blog = false ) {
827
		$post_id = intval( $post_id );
828
829
		/** This filter is already documented in core/wp-includes/comment-functions.php */
830
		$cookie_lifetime = apply_filters( 'comment_cookie_lifetime',       30000000 );
831
832
		/**
833
		 * Filter the Jetpack Comment cookie path.
834
		 *
835
		 * @module subscriptions
836
		 *
837
		 * @since 2.5.0
838
		 *
839
		 * @param string COOKIEPATH Cookie path.
840
		 */
841
		$cookie_path     = apply_filters( 'jetpack_comment_cookie_path',   COOKIEPATH );
842
843
		/**
844
		 * Filter the Jetpack Comment cookie domain.
845
		 *
846
		 * @module subscriptions
847
		 *
848
		 * @since 2.5.0
849
		 *
850
		 * @param string COOKIE_DOMAIN Cookie domain.
851
		 */
852
		$cookie_domain   = apply_filters( 'jetpack_comment_cookie_domain', COOKIE_DOMAIN );
853
854
		if ( $subscribe_to_post && $post_id >= 0 ) {
855
			setcookie( 'jetpack_comments_subscribe_' . self::$hash . '_' . $post_id, 1, time() + $cookie_lifetime, $cookie_path, $cookie_domain );
856
		} else {
857
			setcookie( 'jetpack_comments_subscribe_' . self::$hash . '_' . $post_id, '', time() - 3600, $cookie_path, $cookie_domain );
858
		}
859
860
		if ( $subscribe_to_blog ) {
861
			setcookie( 'jetpack_blog_subscribe_' . self::$hash, 1, time() + $cookie_lifetime, $cookie_path, $cookie_domain );
862
		} else {
863
			setcookie( 'jetpack_blog_subscribe_' . self::$hash, '', time() - 3600, $cookie_path, $cookie_domain );
864
		}
865
	}
866
867
	/**
868
	 * Set the social_notifications_subscribe option to `off` when the Subscriptions module is activated.
869
	 *
870
	 * @since 8.1
871
	 *
872
	 * @return null
873
	 */
874
	function set_social_notifications_subscribe() {
875
		update_option( 'social_notifications_subscribe', 'off' );
876
	}
877
878
	/**
879
	 * Delete the social_notifications_subscribe option that was set to `off` on the module activation.
880
	 *
881
	 * @since 8.1
882
	 *
883
	 * @return null
884
	 */
885
	function delete_social_notifications_subscribe() {
886
		delete_option( 'social_notifications_subscribe' );
887
	}
888
889
}
890
891
Jetpack_Subscriptions::init();
892
893
include dirname( __FILE__ ) . '/subscriptions/views.php';
894