Completed
Push — add/e2e-mailchimp-block-test ( e217db...6066d0 )
by Yaroslav
98:30 queued 85:55
created

Jetpack_Subscriptions::init()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 0
dl 0
loc 9
rs 9.9666
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
97
	/**
98
	 * Jetpack_Subscriptions::xmlrpc_methods()
99
	 *
100
	 * Register subscriptions methods with the Jetpack XML-RPC server.
101
	 * @param array $methods
102
	 */
103
	function xmlrpc_methods( $methods ) {
104
		return array_merge(
105
			$methods,
106
			array(
107
				'jetpack.subscriptions.subscribe' => array( $this, 'subscribe' ),
108
			)
109
		);
110
	}
111
112
	/*
113
	 * Disable Subscribe on Single Post
114
	 * Register post meta
115
	 */
116
	function subscription_post_page_metabox() {
117
		if (
118
			/**
119
			 * Filter whether or not to show the per-post subscription option.
120
			 *
121
			 * @module subscriptions
122
			 *
123
			 * @since 3.7.0
124
			 *
125
			 * @param bool true = show checkbox option on all new posts | false = hide the option.
126
			 */
127
			 ! apply_filters( 'jetpack_allow_per_post_subscriptions', false ) )
128
		{
129
			return;
130
		}
131
132
		if ( has_filter( 'jetpack_subscriptions_exclude_these_categories' ) || has_filter( 'jetpack_subscriptions_include_only_these_categories' ) ) {
133
			return;
134
		}
135
136
		global $post;
137
		$disable_subscribe_value = get_post_meta( $post->ID, '_jetpack_dont_email_post_to_subs', true );
138
		// only show checkbox if post hasn't been published and is a 'post' post type.
139
		if ( get_post_status( $post->ID ) !== 'publish' && get_post_type( $post->ID ) == 'post' ) :
140
			// Nonce it
141
			wp_nonce_field( 'disable_subscribe', 'disable_subscribe_nonce' );
142
			?>
143
			<div class="misc-pub-section">
144
				<label for="_jetpack_dont_email_post_to_subs"><?php _e( 'Jetpack Subscriptions:', 'jetpack' ); ?></label><br>
145
				<input type="checkbox" name="_jetpack_dont_email_post_to_subs" id="jetpack-per-post-subscribe" value="1" <?php checked( $disable_subscribe_value, 1, true ); ?> />
146
				<?php _e( 'Don&#8217;t send this to subscribers', 'jetpack' ); ?>
147
			</div>
148
		<?php endif;
149
	}
150
151
	/**
152
	 * Checks whether or not the post should be emailed to subscribers
153
	 *
154
	 * It checks for the following things in order:
155
	 * - Usage of filter jetpack_subscriptions_exclude_these_categories
156
	 * - Usage of filter jetpack_subscriptions_include_only_these_categories
157
	 * - Existence of the per-post checkbox option
158
	 *
159
	 * Only one of these can be used at any given time.
160
	 *
161
	 * @param $new_status string - the "new" post status of the transition when saved
162
	 * @param $old_status string - the "old" post status of the transition when saved
163
	 * @param $post obj - The post object
164
	 */
165
	function maybe_send_subscription_email( $new_status, $old_status, $post ) {
166
167
		if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
168
			return;
169
		}
170
171
		// Make sure that the checkbox is preseved
172
		if ( ! empty( $_POST['disable_subscribe_nonce'] ) && wp_verify_nonce( $_POST['disable_subscribe_nonce'], 'disable_subscribe' ) ) {
173
			$set_checkbox = isset( $_POST['_jetpack_dont_email_post_to_subs'] ) ? 1 : 0;
174
			update_post_meta( $post->ID, '_jetpack_dont_email_post_to_subs', $set_checkbox );
175
		}
176
	}
177
178
	function update_published_message( $messages ) {
179
		global $post;
180
		if ( ! $this->should_email_post_to_subscribers( $post ) ) {
181
			return $messages;
182
		}
183
184
		$view_post_link_html = sprintf( ' <a href="%1$s">%2$s</a>',
185
			esc_url( get_permalink( $post ) ),
186
			__( 'View post', 'jetpack' )
187
		);
188
189
		$messages['post'][6] = sprintf(
190
			/* translators: Message shown after a post is published */
191
			esc_html__( 'Post published and sending emails to subscribers.', 'jetpack' )
192
			) . $view_post_link_html;
193
		return $messages;
194
	}
195
196
	public function should_email_post_to_subscribers( $post ) {
197
		$should_email = true;
198
		if ( get_post_meta( $post->ID, '_jetpack_dont_email_post_to_subs', true ) ) {
199
			return false;
200
		}
201
202
		// Only posts are currently supported
203
		if ( $post->post_type !== 'post' ) {
204
			return false;
205
		}
206
207
		// Private posts are not sent to subscribers.
208
		if ( 'private' === $post->post_status ) {
209
			return false;
210
		}
211
212
		/**
213
		 * Array of categories that will never trigger subscription emails.
214
		 *
215
		 * Will not send subscription emails from any post from within these categories.
216
		 *
217
		 * @module subscriptions
218
		 *
219
		 * @since 3.7.0
220
		 *
221
		 * @param array $args Array of category slugs or ID's.
222
		 */
223
		$excluded_categories = apply_filters( 'jetpack_subscriptions_exclude_these_categories', array() );
224
225
		// Never email posts from these categories
226
		if ( ! empty( $excluded_categories ) && in_category( $excluded_categories, $post->ID ) ) {
227
			$should_email = false;
228
		}
229
230
		/**
231
		 * ONLY send subscription emails for these categories
232
		 *
233
		 * Will ONLY send subscription emails to these categories.
234
		 *
235
		 * @module subscriptions
236
		 *
237
		 * @since 3.7.0
238
		 *
239
		 * @param array $args Array of category slugs or ID's.
240
		 */
241
		$only_these_categories = apply_filters( 'jetpack_subscriptions_exclude_all_categories_except', array() );
242
243
		// Only emails posts from these categories
244
		if ( ! empty( $only_these_categories ) && ! in_category( $only_these_categories, $post->ID ) ) {
245
			$should_email = false;
246
		}
247
248
		return $should_email;
249
	}
250
251
	function set_post_flags( $flags, $post ) {
252
		$flags['send_subscription'] = $this->should_email_post_to_subscribers( $post );
253
		return $flags;
254
	}
255
256
	/**
257
	 * Jetpack_Subscriptions::configure()
258
	 *
259
	 * Jetpack Subscriptions configuration screen.
260
	 */
261
	function configure() {
262
		// Create the section
263
		add_settings_section(
264
			'jetpack_subscriptions',
265
			__( 'Jetpack Subscriptions Settings', 'jetpack' ),
266
			array( $this, 'subscriptions_settings_section' ),
267
			'discussion'
268
		);
269
270
		/** Subscribe to Posts ***************************************************/
271
272
		add_settings_field(
273
			'jetpack_subscriptions_post_subscribe',
274
			__( 'Follow Blog', 'jetpack' ),
275
			array( $this, 'subscription_post_subscribe_setting' ),
276
			'discussion',
277
			'jetpack_subscriptions'
278
		);
279
280
		register_setting(
281
			'discussion',
282
			'stb_enabled'
283
		);
284
285
		/** Subscribe to Comments ******************************************************/
286
287
		add_settings_field(
288
			'jetpack_subscriptions_comment_subscribe',
289
			__( 'Follow Comments', 'jetpack' ),
290
			array( $this, 'subscription_comment_subscribe_setting' ),
291
			'discussion',
292
			'jetpack_subscriptions'
293
		);
294
295
		register_setting(
296
			'discussion',
297
			'stc_enabled'
298
		);
299
300
		/** Subscription Messaging Options ******************************************************/
301
302
		register_setting(
303
			'reading',
304
			'subscription_options',
305
			array( $this, 'validate_settings' )
306
		);
307
308
		add_settings_section(
309
			'email_settings',
310
			__( 'Follower Settings', 'jetpack' ),
311
			array( $this, 'reading_section' ),
312
			'reading'
313
		);
314
315
		add_settings_field(
316
			'invitation',
317
			__( 'Blog follow email text', 'jetpack' ),
318
			array( $this, 'setting_invitation' ),
319
			'reading',
320
			'email_settings'
321
		);
322
323
		add_settings_field(
324
			'comment-follow',
325
			__( 'Comment follow email text', 'jetpack' ),
326
			array( $this, 'setting_comment_follow' ),
327
			'reading',
328
			'email_settings'
329
		);
330
	}
331
332
	/**
333
	 * Discussions setting section blurb
334
	 *
335
	 */
336
	function subscriptions_settings_section() {
337
	?>
338
		<p id="jetpack-subscriptions-settings"><?php _e( 'Change whether your visitors can subscribe to your posts or comments or both.', 'jetpack' ); ?></p>
339
340
	<?php
341
	}
342
343
	/**
344
	 * Post Subscriptions Toggle
345
	 *
346
	 */
347 View Code Duplication
	function subscription_post_subscribe_setting() {
348
349
		$stb_enabled = get_option( 'stb_enabled', 1 ); ?>
350
351
		<p class="description">
352
			<input type="checkbox" name="stb_enabled" id="jetpack-post-subscribe" value="1" <?php checked( $stb_enabled, 1 ); ?> />
353
			<?php _e( "Show a <em>'follow blog'</em> option in the comment form", 'jetpack' ); ?>
354
		</p>
355
	<?php
356
	}
357
358
	/**
359
	 * Comments Subscriptions Toggle
360
	 *
361
	 */
362 View Code Duplication
	function subscription_comment_subscribe_setting() {
363
364
		$stc_enabled = get_option( 'stc_enabled', 1 ); ?>
365
366
		<p class="description">
367
			<input type="checkbox" name="stc_enabled" id="jetpack-comment-subscribe" value="1" <?php checked( $stc_enabled, 1 ); ?> />
368
			<?php _e( "Show a <em>'follow comments'</em> option in the comment form", 'jetpack' ); ?>
369
		</p>
370
371
	<?php
372
	}
373
374
	function validate_settings( $settings ) {
375
		global $allowedposttags;
376
377
		$default = $this->get_default_settings();
378
379
		// Blog Follow
380
		$settings['invitation'] = trim( wp_kses( $settings['invitation'], $allowedposttags ) );
381
		if ( empty( $settings['invitation'] ) )
382
			$settings['invitation'] = $default['invitation'];
383
384
		// Comments Follow (single post)
385
		$settings['comment_follow'] = trim( wp_kses( $settings['comment_follow'], $allowedposttags ) );
386
		if ( empty( $settings['comment_follow'] ) )
387
			$settings['comment_follow'] = $default['comment_follow'];
388
389
		return $settings;
390
	}
391
392
	public function reading_section() {
393
		echo '<p id="follower-settings">';
394
		_e( 'These settings change emails sent from your blog to followers.', 'jetpack' );
395
		echo '</p>';
396
	}
397
398
	public function setting_invitation() {
399
		$settings = $this->get_settings();
400
		echo '<textarea name="subscription_options[invitation]" class="large-text" cols="50" rows="5">' . esc_textarea( $settings['invitation'] ) . '</textarea>';
401
		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>';
402
	}
403
404
	public function setting_comment_follow() {
405
		$settings = $this->get_settings();
406
		echo '<textarea name="subscription_options[comment_follow]" class="large-text" cols="50" rows="5">' . esc_textarea( $settings['comment_follow'] ) . '</textarea>';
407
		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>';
408
	}
409
410
	function get_default_settings() {
411
		return array(
412
			'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' ),
413
			'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' )
414
		);
415
	}
416
417
	function get_settings() {
418
		return wp_parse_args( (array) get_option( 'subscription_options', array() ), $this->get_default_settings() );
419
	}
420
421
	/**
422
	 * Jetpack_Subscriptions::subscribe()
423
	 *
424
	 * Send a synchronous XML-RPC subscribe to blog posts or subscribe to post comments request.
425
	 *
426
	 * @param string $email
427
	 * @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...
428
	 * @param bool   $async    (optional) Should the subscription be performed asynchronously?  Defaults to true.
429
	 *
430
	 * @return true|Jetpack_Error true on success
431
	 *	invalid_email   : not a valid email address
432
	 *	invalid_post_id : not a valid post ID
433
	 *	unknown_post_id : unknown post
434
	 *	not_subscribed  : strange error.  Jetpack servers at WordPress.com could subscribe the email.
435
	 *	disabled        : Site owner has disabled subscriptions.
436
	 *	active          : Already subscribed.
437
	 *	unknown         : strange error.  Jetpack servers at WordPress.com returned something malformed.
438
	 *	unknown_status  : strange error.  Jetpack servers at WordPress.com returned something I didn't understand.
439
	 */
440
	function subscribe( $email, $post_ids = 0, $async = true, $extra_data = array() ) {
441
		if ( !is_email( $email ) ) {
442
			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...
443
		}
444
445
		if ( !$async ) {
446
			Jetpack::load_xml_rpc_client();
447
			$xml = new Jetpack_IXR_ClientMulticall();
448
		}
449
450
		foreach ( (array) $post_ids as $post_id ) {
451
			$post_id = (int) $post_id;
452
			if ( $post_id < 0 ) {
453
				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...
454
			} else if ( $post_id && !$post = get_post( $post_id ) ) {
455
				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...
456
			}
457
458
			if ( $async ) {
459
				Jetpack::xmlrpc_async_call( 'jetpack.subscribeToSite', $email, $post_id, serialize( $extra_data ) );
460
			} else {
461
				$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...
462
			}
463
		}
464
465
		if ( $async ) {
466
			return;
467
		}
468
469
		// Call
470
		$xml->query();
471
472
		if ( $xml->isError() ) {
473
			return $xml->get_jetpack_error();
474
		}
475
476
		$responses = $xml->getResponse();
477
478
		$r = array();
479
		foreach ( (array) $responses as $response ) {
480
			if ( isset( $response['faultCode'] ) || isset( $response['faultString'] ) ) {
481
				$r[] = $xml->get_jetpack_error( $response['faultCode'], $response['faultString'] );
482
				continue;
483
			}
484
485
			if ( !is_array( $response[0] ) || empty( $response[0]['status'] ) ) {
486
				$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...
487
				continue;
488
			}
489
490
			switch ( $response[0]['status'] ) {
491
			case 'error' :
492
				$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...
493
				continue 2;
494
			case 'disabled' :
495
				$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...
496
				continue 2;
497
			case 'active' :
498
				$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...
499
				continue 2;
500
			case 'pending' :
501
				$r[] = true;
502
				continue 2;
503
			default :
504
				$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...
505
				continue 2;
506
			}
507
		}
508
509
		return $r;
510
	}
511
512
	/**
513
	 * Jetpack_Subscriptions::widget_submit()
514
	 *
515
	 * When a user submits their email via the blog subscription widget, check the details and call the subsribe() method.
516
	 */
517
	function widget_submit() {
518
		// Check the nonce.
519
		if ( is_user_logged_in() ) {
520
			check_admin_referer( 'blogsub_subscribe_' . get_current_blog_id() );
521
		}
522
523
		if ( empty( $_REQUEST['email'] ) )
524
			return false;
525
526
		$redirect_fragment = false;
527
		if ( isset( $_REQUEST['redirect_fragment'] ) ) {
528
			$redirect_fragment = preg_replace( '/[^a-z0-9_-]/i', '', $_REQUEST['redirect_fragment'] );
529
		}
530
		if ( !$redirect_fragment ) {
531
			$redirect_fragment = 'subscribe-blog';
532
		}
533
534
		$subscribe = Jetpack_Subscriptions::subscribe(
535
												$_REQUEST['email'],
536
												0,
537
												false,
538
												array(
539
													'source'         => 'widget',
540
													'widget-in-use'  => is_active_widget( false, false, 'blog_subscription', true ) ? 'yes' : 'no',
541
													'comment_status' => '',
542
													'server_data'    => jetpack_subscriptions_cherry_pick_server_data(),
543
												)
544
		);
545
546
		if ( is_wp_error( $subscribe ) ) {
547
			$error = $subscribe->get_error_code();
0 ignored issues
show
Bug introduced by
The method get_error_code() does not seem to exist on object<Jetpack_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...
548
		} else {
549
			$error = false;
550
			foreach ( $subscribe as $response ) {
0 ignored issues
show
Bug introduced by
The expression $subscribe of type object<Jetpack_Error>|null|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...
551
				if ( is_wp_error( $response ) ) {
552
					$error = $response->get_error_code();
553
					break;
554
				}
555
			}
556
		}
557
558
		switch ( $error ) {
559
			case false:
560
				$result = 'success';
561
				break;
562
			case 'invalid_email':
563
				$result = $error;
564
				break;
565
			case 'blocked_email':
566
				$result = 'opted_out';
567
				break;
568
			case 'active':
569
			case 'pending':
570
				$result = 'already';
571
				break;
572
			default:
573
				$result = 'error';
574
				break;
575
		}
576
577
		$redirect = add_query_arg( 'subscribe', $result );
578
579
		/**
580
		 * Fires on each subscription form submission.
581
		 *
582
		 * @module subscriptions
583
		 *
584
		 * @since 3.7.0
585
		 *
586
		 * @param string $result Result of form submission: success, invalid_email, already, error.
587
		 */
588
		do_action( 'jetpack_subscriptions_form_submission', $result );
589
590
		wp_safe_redirect( "$redirect#$redirect_fragment" );
591
		exit;
592
	}
593
594
	/**
595
	 * Jetpack_Subscriptions::comment_subscribe_init()
596
	 *
597
	 * Set up and add the comment subscription checkbox to the comment form.
598
	 *
599
	 * @param string $submit_button HTML markup for the submit field.
600
	 * @param array  $args          Arguments passed to `comment_form()`.
601
	 */
602
	function comment_subscribe_init( $submit_button, $args ) {
603
		global $post;
604
605
		$comments_checked = '';
606
		$blog_checked     = '';
607
608
		// Check for a comment / blog submission and set a cookie to retain the setting and check the boxes.
609
		if ( isset( $_COOKIE[ 'jetpack_comments_subscribe_' . self::$hash . '_' . $post->ID ] ) ) {
610
			$comments_checked = ' checked="checked"';
611
		}
612
613
		if ( isset( $_COOKIE[ 'jetpack_blog_subscribe_' . self::$hash ] ) ) {
614
			$blog_checked = ' checked="checked"';
615
		}
616
617
		// Some themes call this function, don't show the checkbox again
618
		remove_action( 'comment_form', 'subscription_comment_form' );
619
620
		// Check if Mark Jaquith's Subscribe to Comments plugin is active - if so, suppress Jetpack checkbox
621
622
		$str = '';
623
624
		if ( FALSE === has_filter( 'comment_form', 'show_subscription_checkbox' ) && 1 == get_option( 'stc_enabled', 1 ) && empty( $post->post_password ) && 'post' == get_post_type() ) {
625
			// Subscribe to comments checkbox
626
			$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 . ' /> ';
627
			$comment_sub_text = __( 'Notify me of follow-up comments by email.', 'jetpack' );
628
			$str .=	'<label class="subscribe-label" id="subscribe-label" for="subscribe_comments">' . esc_html(
629
				/**
630
				 * Filter the Subscribe to comments text appearing below the comment form.
631
				 *
632
				 * @module subscriptions
633
				 *
634
				 * @since 3.4.0
635
				 *
636
				 * @param string $comment_sub_text Subscribe to comments text.
637
				 */
638
				apply_filters( 'jetpack_subscribe_comment_label', $comment_sub_text )
639
			) . '</label>';
640
			$str .= '</p>';
641
		}
642
643
		if ( 1 == get_option( 'stb_enabled', 1 ) ) {
644
			// Subscribe to blog checkbox
645
			$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 . ' /> ';
646
			$blog_sub_text = __( 'Notify me of new posts by email.', 'jetpack' );
647
			$str .=	'<label class="subscribe-label" id="subscribe-blog-label" for="subscribe_blog">' . esc_html(
648
				/**
649
				 * Filter the Subscribe to blog text appearing below the comment form.
650
				 *
651
				 * @module subscriptions
652
				 *
653
				 * @since 3.4.0
654
				 *
655
				 * @param string $comment_sub_text Subscribe to blog text.
656
				 */
657
				apply_filters( 'jetpack_subscribe_blog_label', $blog_sub_text )
658
			) . '</label>';
659
			$str .= '</p>';
660
		}
661
662
		/**
663
		 * Filter the output of the subscription options appearing below the comment form.
664
		 *
665
		 * @module subscriptions
666
		 *
667
		 * @since 1.2.0
668
		 *
669
		 * @param string $str Comment Subscription form HTML output.
670
		 */
671
		$str = apply_filters( 'jetpack_comment_subscription_form', $str );
672
673
		return $str . $submit_button;
674
	}
675
676
	/**
677
	 * Jetpack_Subscriptions::comment_subscribe_init()
678
	 *
679
	 * When a user checks the comment subscribe box and submits a comment, subscribe them to the comment thread.
680
	 */
681
	function comment_subscribe_submit( $comment_id, $approved ) {
682
		if ( 'spam' === $approved ) {
683
			return;
684
		}
685
686
		$comment = get_comment( $comment_id );
687
688
		// Set cookies for this post/comment
689
		$this->set_cookies( isset( $_REQUEST['subscribe_comments'] ), $comment->comment_post_ID, isset( $_REQUEST['subscribe_blog'] ) );
690
691
		if ( !isset( $_REQUEST['subscribe_comments'] ) && !isset( $_REQUEST['subscribe_blog'] ) )
692
			return;
693
694
		$post_ids = array();
695
696
		if ( isset( $_REQUEST['subscribe_comments'] ) )
697
			$post_ids[] = $comment->comment_post_ID;
698
699
		if ( isset( $_REQUEST['subscribe_blog'] ) )
700
			$post_ids[] = 0;
701
702
		$result = Jetpack_Subscriptions::subscribe(
703
									$comment->comment_author_email,
704
									$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...
705
									true,
706
									array(
707
										'source'         => 'comment-form',
708
										'widget-in-use'  => is_active_widget( false, false, 'blog_subscription', true ) ? 'yes' : 'no',
709
										'comment_status' => $approved,
710
										'server_data'    => jetpack_subscriptions_cherry_pick_server_data(),
711
									)
712
		);
713
714
		/**
715
		 * Fires on each comment subscription form submission.
716
		 *
717
		 * @module subscriptions
718
		 *
719
		 * @since 5.5.0
720
		 *
721
		 * @param NULL|WP_Error $result Result of form submission: NULL on success, WP_Error otherwise.
722
		 * @param Array $post_ids An array of post IDs that the user subscribed to, 0 means blog subscription.
723
		 */
724
		do_action( 'jetpack_subscriptions_comment_form_submission', $result, $post_ids );
725
	}
726
727
	/**
728
	 * Jetpack_Subscriptions::set_cookies()
729
	 *
730
	 * Set a cookie to save state on the comment and post subscription checkboxes.
731
	 *
732
	 * @param bool $subscribe_to_post Whether the user chose to subscribe to subsequent comments on this post.
733
	 * @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...
734
	 * @param bool $subscribe_to_blog Whether the user chose to subscribe to all new posts on the blog.
735
	 */
736
	function set_cookies( $subscribe_to_post = false, $post_id = null, $subscribe_to_blog = false ) {
737
		$post_id = intval( $post_id );
738
739
		/** This filter is already documented in core/wp-includes/comment-functions.php */
740
		$cookie_lifetime = apply_filters( 'comment_cookie_lifetime',       30000000 );
741
742
		/**
743
		 * Filter the Jetpack Comment cookie path.
744
		 *
745
		 * @module subscriptions
746
		 *
747
		 * @since 2.5.0
748
		 *
749
		 * @param string COOKIEPATH Cookie path.
750
		 */
751
		$cookie_path     = apply_filters( 'jetpack_comment_cookie_path',   COOKIEPATH );
752
753
		/**
754
		 * Filter the Jetpack Comment cookie domain.
755
		 *
756
		 * @module subscriptions
757
		 *
758
		 * @since 2.5.0
759
		 *
760
		 * @param string COOKIE_DOMAIN Cookie domain.
761
		 */
762
		$cookie_domain   = apply_filters( 'jetpack_comment_cookie_domain', COOKIE_DOMAIN );
763
764
		if ( $subscribe_to_post && $post_id >= 0 ) {
765
			setcookie( 'jetpack_comments_subscribe_' . self::$hash . '_' . $post_id, 1, time() + $cookie_lifetime, $cookie_path, $cookie_domain );
766
		} else {
767
			setcookie( 'jetpack_comments_subscribe_' . self::$hash . '_' . $post_id, '', time() - 3600, $cookie_path, $cookie_domain );
768
		}
769
770
		if ( $subscribe_to_blog ) {
771
			setcookie( 'jetpack_blog_subscribe_' . self::$hash, 1, time() + $cookie_lifetime, $cookie_path, $cookie_domain );
772
		} else {
773
			setcookie( 'jetpack_blog_subscribe_' . self::$hash, '', time() - 3600, $cookie_path, $cookie_domain );
774
		}
775
	}
776
777
}
778
779
Jetpack_Subscriptions::init();
780
781
include dirname( __FILE__ ) . '/subscriptions/views.php';
782