Completed
Push — add/gutenblocks ( 631f0a...ff720e )
by Rastislav
104:58 queued 97:08
created

subscriptions.php ➔ jetpack_do_subscription_form()   B

Complexity

Conditions 6
Paths 8

Size

Total Lines 26
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 18
nc 8
nop 1
dl 0
loc 26
rs 8.439
c 0
b 0
f 0
1
<?php
2
/**
3
 * Module Name: Subscriptions
4
 * Module Description: Allow users to subscribe to your posts and comments and receive notifications via email
5
 * Jumpstart Description: Give visitors two easy subscription options — while commenting, or via a separate email subscription widget you can display.
6
 * Sort Order: 9
7
 * Recommendation Order: 8
8
 * First Introduced: 1.2
9
 * Requires Connection: Yes
10
 * Auto Activate: Yes
11
 * Module Tags: Social
12
 * Feature: Engagement, Jumpstart
13
 * Additional Search Queries: subscriptions, subscription, email, follow, followers, subscribers, signup
14
 */
15
16
add_action( 'jetpack_modules_loaded', 'jetpack_subscriptions_load' );
17
18
function jetpack_subscriptions_load() {
19
	Jetpack::enable_module_configurable( __FILE__ );
20
	Jetpack::module_configuration_load( __FILE__, 'jetpack_subscriptions_configuration_load' );
21
}
22
23
function jetpack_subscriptions_configuration_load() {
24
	wp_safe_redirect( admin_url( 'options-discussion.php#jetpack-subscriptions-settings' ) );
25
	exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The function jetpack_subscriptions_configuration_load() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
26
}
27
28
class Jetpack_Subscriptions {
29
	public $jetpack = false;
30
31
	public static $hash;
32
33
	/**
34
	 * Singleton
35
	 * @static
36
	 */
37
	static function init() {
38
		static $instance = false;
39
40
		if ( !$instance ) {
41
			$instance = new Jetpack_Subscriptions;
42
		}
43
44
		return $instance;
45
	}
46
47
	function __construct() {
48
		$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...
49
50
		// Don't use COOKIEHASH as it could be shared across installs && is non-unique in multisite.
51
		// @see: https://twitter.com/nacin/status/378246957451333632
52
		self::$hash = md5( get_option( 'siteurl' ) );
53
54
		add_filter( 'jetpack_xmlrpc_methods', array( $this, 'xmlrpc_methods' ) );
55
56
		// @todo remove sync from subscriptions and move elsewhere...
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
57
58
		// Add Configuration Page
59
		add_action( 'admin_init', array( $this, 'configure' ) );
60
61
		// Set up the subscription widget.
62
		add_action( 'widgets_init', array( $this, 'widget_init' ) );
63
64
		// Catch subscription widget submits
65
		if ( isset( $_REQUEST['jetpack_subscriptions_widget'] ) )
66
			add_action( 'template_redirect', array( $this, 'widget_submit' ) );
67
68
		// Set up the comment subscription checkboxes
69
		add_action( 'comment_form', array( $this, 'comment_subscribe_init' ) );
70
71
		// Catch comment posts and check for subscriptions.
72
		add_action( 'comment_post', array( $this, 'comment_subscribe_submit' ), 50, 2 );
73
74
		// Adds post meta checkbox in the post submit metabox
75
		add_action( 'post_submitbox_misc_actions', array( $this, 'subscription_post_page_metabox' ) );
76
77
		add_action( 'transition_post_status', array( $this, 'maybe_send_subscription_email' ), 10, 3 );
78
79
		add_filter( 'jetpack_published_post_flags', array( $this, 'set_post_flags' ), 10, 2 );
80
81
		// Gutenberg!
82
		add_action( 'init', array( __CLASS__, 'register_block_type' ) );
83
		add_action( 'enqueue_block_editor_assets', array( __CLASS__, 'enqueue_block_editor_assets' ) );
84
85
		// Add REST API route to get the total number of subscribers.
86
		add_action( 'rest_api_init', array( $this, 'rest_api_get_subscriber_count' ) );
87
	}
88
89
	function rest_api_get_subscriber_count() {
90
		register_rest_route( 'jetpack', '/get_subscriber_count', array(
91
			'methods' => 'GET',
92
			'callback' => array( 'Jetpack_Subscriptions_Widget', 'fetch_subscriber_count' ),
93
		) );
94
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
	public function should_email_post_to_subscribers( $post ) {
179
		$should_email = true;
180
		if ( get_post_meta( $post->ID, '_jetpack_dont_email_post_to_subs', true ) ) {
181
			return false;
182
		}
183
184
		// Only posts are currently supported
185
		if ( $post->post_type !== 'post' ) {
186
			return false;
187
		}
188
189
		/**
190
		 * Array of categories that will never trigger subscription emails.
191
		 *
192
		 * Will not send subscription emails from any post from within these categories.
193
		 *
194
		 * @module subscriptions
195
		 *
196
		 * @since 3.7.0
197
		 *
198
		 * @param array $args Array of category slugs or ID's.
199
		 */
200
		$excluded_categories = apply_filters( 'jetpack_subscriptions_exclude_these_categories', array() );
201
202
		// Never email posts from these categories
203
		if ( ! empty( $excluded_categories ) && in_category( $excluded_categories, $post->ID ) ) {
204
			$should_email = false;
205
		}
206
207
		/**
208
		 * ONLY send subscription emails for these categories
209
		 *
210
		 * Will ONLY send subscription emails to these categories.
211
		 *
212
		 * @module subscriptions
213
		 *
214
		 * @since 3.7.0
215
		 *
216
		 * @param array $args Array of category slugs or ID's.
217
		 */
218
		$only_these_categories = apply_filters( 'jetpack_subscriptions_exclude_all_categories_except', array() );
219
220
		// Only emails posts from these categories
221
		if ( ! empty( $only_these_categories ) && ! in_category( $only_these_categories, $post->ID ) ) {
222
			$should_email = false;
223
		}
224
225
		return $should_email;
226
	}
227
228
	function set_post_flags( $flags, $post ) {
229
		$flags['send_subscription'] = $this->should_email_post_to_subscribers( $post );
230
		return $flags;
231
	}
232
233
	/**
234
	 * Jetpack_Subscriptions::configure()
235
	 *
236
	 * Jetpack Subscriptions configuration screen.
237
	 */
238
	function configure() {
239
		// Create the section
240
		add_settings_section(
241
			'jetpack_subscriptions',
242
			__( 'Jetpack Subscriptions Settings', 'jetpack' ),
243
			array( $this, 'subscriptions_settings_section' ),
244
			'discussion'
245
		);
246
247
		/** Subscribe to Posts ***************************************************/
248
249
		add_settings_field(
250
			'jetpack_subscriptions_post_subscribe',
251
			__( 'Follow Blog', 'jetpack' ),
252
			array( $this, 'subscription_post_subscribe_setting' ),
253
			'discussion',
254
			'jetpack_subscriptions'
255
		);
256
257
		register_setting(
258
			'discussion',
259
			'stb_enabled'
260
		);
261
262
		/** Subscribe to Comments ******************************************************/
263
264
		add_settings_field(
265
			'jetpack_subscriptions_comment_subscribe',
266
			__( 'Follow Comments', 'jetpack' ),
267
			array( $this, 'subscription_comment_subscribe_setting' ),
268
			'discussion',
269
			'jetpack_subscriptions'
270
		);
271
272
		register_setting(
273
			'discussion',
274
			'stc_enabled'
275
		);
276
277
		/** Subscription Messaging Options ******************************************************/
278
279
		register_setting(
280
			'reading',
281
			'subscription_options',
282
			array( $this, 'validate_settings' )
283
		);
284
285
		add_settings_section(
286
			'email_settings',
287
			__( 'Follower Settings', 'jetpack' ),
288
			array( $this, 'reading_section' ),
289
			'reading'
290
		);
291
292
		add_settings_field(
293
			'invitation',
294
			__( 'Blog follow email text', 'jetpack' ),
295
			array( $this, 'setting_invitation' ),
296
			'reading',
297
			'email_settings'
298
		);
299
300
		add_settings_field(
301
			'comment-follow',
302
			__( 'Comment follow email text', 'jetpack' ),
303
			array( $this, 'setting_comment_follow' ),
304
			'reading',
305
			'email_settings'
306
		);
307
	}
308
309
	/**
310
	 * Discussions setting section blurb
311
	 *
312
	 */
313
	function subscriptions_settings_section() {
314
	?>
315
316
		<p id="jetpack-subscriptions-settings"><?php _e( 'Change whether your visitors can subscribe to your posts or comments or both.', 'jetpack' ); ?></p>
317
318
	<?php
319
	}
320
321
	/**
322
	 * Post Subscriptions Toggle
323
	 *
324
	 */
325 View Code Duplication
	function subscription_post_subscribe_setting() {
326
327
		$stb_enabled = get_option( 'stb_enabled', 1 ); ?>
328
329
		<p class="description">
330
			<input type="checkbox" name="stb_enabled" id="jetpack-post-subscribe" value="1" <?php checked( $stb_enabled, 1 ); ?> />
331
			<?php _e( "Show a <em>'follow blog'</em> option in the comment form", 'jetpack' ); ?>
332
		</p>
333
	<?php
334
	}
335
336
	/**
337
	 * Comments Subscriptions Toggle
338
	 *
339
	 */
340 View Code Duplication
	function subscription_comment_subscribe_setting() {
341
342
		$stc_enabled = get_option( 'stc_enabled', 1 ); ?>
343
344
		<p class="description">
345
			<input type="checkbox" name="stc_enabled" id="jetpack-comment-subscribe" value="1" <?php checked( $stc_enabled, 1 ); ?> />
346
			<?php _e( "Show a <em>'follow comments'</em> option in the comment form", 'jetpack' ); ?>
347
		</p>
348
349
	<?php
350
	}
351
352
	function validate_settings( $settings ) {
353
		global $allowedposttags;
354
355
		$default = $this->get_default_settings();
356
357
		// Blog Follow
358
		$settings['invitation'] = trim( wp_kses( $settings['invitation'], $allowedposttags ) );
359
		if ( empty( $settings['invitation'] ) )
360
			$settings['invitation'] = $default['invitation'];
361
362
		// Comments Follow (single post)
363
		$settings['comment_follow'] = trim( wp_kses( $settings['comment_follow'], $allowedposttags ) );
364
		if ( empty( $settings['comment_follow'] ) )
365
			$settings['comment_follow'] = $default['comment_follow'];
366
367
		return $settings;
368
	}
369
370
	public function reading_section() {
371
		echo '<p id="follower-settings">';
372
		_e( 'These settings change emails sent from your blog to followers.', 'jetpack' );
373
		echo '</p>';
374
	}
375
376
	public function setting_invitation() {
377
		$settings = $this->get_settings();
378
		echo '<textarea name="subscription_options[invitation]" class="large-text" cols="50" rows="5">' . esc_textarea( $settings['invitation'] ) . '</textarea>';
379
		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>';
380
	}
381
382
	public function setting_comment_follow() {
383
		$settings = $this->get_settings();
384
		echo '<textarea name="subscription_options[comment_follow]" class="large-text" cols="50" rows="5">' . esc_textarea( $settings['comment_follow'] ) . '</textarea>';
385
		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>';
386
	}
387
388
	function get_default_settings() {
389
		return array(
390
			'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' ),
391
			'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' )
392
		);
393
	}
394
395
	function get_settings() {
396
		return wp_parse_args( (array) get_option( 'subscription_options', array() ), $this->get_default_settings() );
397
	}
398
399
	/**
400
	 * Jetpack_Subscriptions::subscribe()
401
	 *
402
	 * Send a synchronous XML-RPC subscribe to blog posts or subscribe to post comments request.
403
	 *
404
	 * @param string $email
405
	 * @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...
406
	 * @param bool   $async    (optional) Should the subscription be performed asynchronously?  Defaults to true.
407
	 *
408
	 * @return true|Jetpack_Error true on success
409
	 *	invalid_email   : not a valid email address
410
	 *	invalid_post_id : not a valid post ID
411
	 *	unknown_post_id : unknown post
412
	 *	not_subscribed  : strange error.  Jetpack servers at WordPress.com could subscribe the email.
413
	 *	disabled        : Site owner has disabled subscriptions.
414
	 *	active          : Already subscribed.
415
	 *	unknown         : strange error.  Jetpack servers at WordPress.com returned something malformed.
416
	 *	unknown_status  : strange error.  Jetpack servers at WordPress.com returned something I didn't understand.
417
	 */
418
	function subscribe( $email, $post_ids = 0, $async = true, $extra_data = array() ) {
419
		if ( !is_email( $email ) ) {
420
			return new Jetpack_Error( 'invalid_email' );
421
		}
422
423
		if ( !$async ) {
424
			Jetpack::load_xml_rpc_client();
425
			$xml = new Jetpack_IXR_ClientMulticall();
426
		}
427
428
		foreach ( (array) $post_ids as $post_id ) {
429
			$post_id = (int) $post_id;
430
			if ( $post_id < 0 ) {
431
				return new Jetpack_Error( 'invalid_post_id' );
432
			} else if ( $post_id && !$post = get_post( $post_id ) ) {
433
				return new Jetpack_Error( 'unknown_post_id' );
434
			}
435
436
			if ( $async ) {
437
				Jetpack::xmlrpc_async_call( 'jetpack.subscribeToSite', $email, $post_id, serialize( $extra_data ) );
438
			} else {
439
				$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...
440
			}
441
		}
442
443
		if ( $async ) {
444
			return;
445
		}
446
447
		// Call
448
		$xml->query();
449
450
		if ( $xml->isError() ) {
451
			return $xml->get_jetpack_error();
452
		}
453
454
		$responses = $xml->getResponse();
455
456
		$r = array();
457
		foreach ( (array) $responses as $response ) {
458
			if ( isset( $response['faultCode'] ) || isset( $response['faultString'] ) ) {
459
				$r[] = $xml->get_jetpack_error( $response['faultCode'], $response['faultString'] );
460
				continue;
461
			}
462
463
			if ( !is_array( $response[0] ) || empty( $response[0]['status'] ) ) {
464
				$r[] = new Jetpack_Error( 'unknown' );
465
				continue;
466
			}
467
468
			switch ( $response[0]['status'] ) {
469
			case 'error' :
470
				$r[] = new Jetpack_Error( 'not_subscribed' );
471
				continue 2;
472
			case 'disabled' :
473
				$r[] = new Jetpack_Error( 'disabled' );
474
				continue 2;
475
			case 'active' :
476
				$r[] = new Jetpack_Error( 'active' );
477
				continue 2;
478
			case 'pending' :
479
				$r[] = true;
480
				continue 2;
481
			default :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a DEFAULT statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in the default statement.

switch ($expr) {
    default : //wrong
        doSomething();
        break;
}

switch ($expr) {
    default: //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
482
				$r[] = new Jetpack_Error( 'unknown_status', (string) $response[0]['status'] );
483
				continue 2;
484
			}
485
		}
486
487
		return $r;
488
	}
489
490
	/**
491
	 * Jetpack_Subscriptions::widget_init()
492
	 *
493
	 * Initialize and register the Jetpack Subscriptions widget.
494
	 */
495
	function widget_init() {
496
		register_widget( 'Jetpack_Subscriptions_Widget' );
497
	}
498
499
	/**
500
	 * Jetpack_Subscriptions::widget_submit()
501
	 *
502
	 * When a user submits their email via the blog subscription widget, check the details and call the subsribe() method.
503
	 */
504
	function widget_submit() {
505
		// Check the nonce.
506
		if ( is_user_logged_in() ) {
507
			check_admin_referer( 'blogsub_subscribe_' . get_current_blog_id() );
508
		}
509
510
		if ( empty( $_REQUEST['email'] ) )
511
			return false;
512
513
		$redirect_fragment = false;
514
		if ( isset( $_REQUEST['redirect_fragment'] ) ) {
515
			$redirect_fragment = preg_replace( '/[^a-z0-9_-]/i', '', $_REQUEST['redirect_fragment'] );
516
		}
517
		if ( !$redirect_fragment ) {
518
			$redirect_fragment = 'subscribe-blog';
519
		}
520
521
		$subscribe = Jetpack_Subscriptions::subscribe(
522
												$_REQUEST['email'],
523
												0,
524
												false,
525
												array(
526
													'source'         => 'widget',
527
													'widget-in-use'  => is_active_widget( false, false, 'blog_subscription', true ) ? 'yes' : 'no',
528
													'comment_status' => '',
529
													'server_data'    => $_SERVER,
530
												)
531
		);
532
533
		if ( is_wp_error( $subscribe ) ) {
534
			$error = $subscribe->get_error_code();
535
		} else {
536
			$error = false;
537
			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...
538
				if ( is_wp_error( $response ) ) {
539
					$error = $response->get_error_code();
540
					break;
541
				}
542
			}
543
		}
544
545
		switch ( $error ) {
546
			case false:
547
				$result = 'success';
548
				break;
549
			case 'invalid_email':
550
				$result = $error;
551
				break;
552
			case 'blocked_email':
553
				$result = 'opted_out';
554
				break;
555
			case 'active':
556
			case 'pending':
557
				$result = 'already';
558
				break;
559
			default:
560
				$result = 'error';
561
				break;
562
		}
563
564
		$redirect = add_query_arg( 'subscribe', $result );
565
566
		/**
567
		 * Fires on each subscription form submission.
568
		 *
569
		 * @module subscriptions
570
		 *
571
		 * @since 3.7.0
572
		 *
573
		 * @param string $result Result of form submission: success, invalid_email, already, error.
574
		 */
575
		do_action( 'jetpack_subscriptions_form_submission', $result );
576
577
		wp_safe_redirect( "$redirect#$redirect_fragment" );
578
		exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method widget_submit() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
579
	}
580
581
	/**
582
	 * Jetpack_Subscriptions::comment_subscribe_init()
583
	 *
584
	 * Set up and add the comment subscription checkbox to the comment form.
585
	 */
586
	function comment_subscribe_init() {
587
		global $post;
588
589
		$comments_checked = '';
590
		$blog_checked     = '';
591
592
		// Check for a comment / blog submission and set a cookie to retain the setting and check the boxes.
593
		if ( isset( $_COOKIE[ 'jetpack_comments_subscribe_' . self::$hash . '_' . $post->ID ] ) ) {
594
			$comments_checked = ' checked="checked"';
595
		}
596
597
		if ( isset( $_COOKIE[ 'jetpack_blog_subscribe_' . self::$hash ] ) ) {
598
			$blog_checked = ' checked="checked"';
599
		}
600
601
		// Some themes call this function, don't show the checkbox again
602
		remove_action( 'comment_form', 'subscription_comment_form' );
603
604
		// Check if Mark Jaquith's Subscribe to Comments plugin is active - if so, suppress Jetpack checkbox
605
606
		$str = '';
607
608
		if ( FALSE === has_filter( 'comment_form', 'show_subscription_checkbox' ) && 1 == get_option( 'stc_enabled', 1 ) && empty( $post->post_password ) && 'post' == get_post_type() ) {
609
			// Subscribe to comments checkbox
610
			$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 . ' /> ';
611
			$comment_sub_text = __( 'Notify me of follow-up comments by email.', 'jetpack' );
612
			$str .=	'<label class="subscribe-label" id="subscribe-label" for="subscribe_comments">' . esc_html(
613
				/**
614
				 * Filter the Subscribe to comments text appearing below the comment form.
615
				 *
616
				 * @module subscriptions
617
				 *
618
				 * @since 3.4.0
619
				 *
620
				 * @param string $comment_sub_text Subscribe to comments text.
621
				 */
622
				apply_filters( 'jetpack_subscribe_comment_label', $comment_sub_text )
623
			) . '</label>';
624
			$str .= '</p>';
625
		}
626
627
		if ( 1 == get_option( 'stb_enabled', 1 ) ) {
628
			// Subscribe to blog checkbox
629
			$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 . ' /> ';
630
			$blog_sub_text = __( 'Notify me of new posts by email.', 'jetpack' );
631
			$str .=	'<label class="subscribe-label" id="subscribe-blog-label" for="subscribe_blog">' . esc_html(
632
				/**
633
				 * Filter the Subscribe to blog text appearing below the comment form.
634
				 *
635
				 * @module subscriptions
636
				 *
637
				 * @since 3.4.0
638
				 *
639
				 * @param string $comment_sub_text Subscribe to blog text.
640
				 */
641
				apply_filters( 'jetpack_subscribe_blog_label', $blog_sub_text )
642
			) . '</label>';
643
			$str .= '</p>';
644
		}
645
646
		/**
647
		 * Filter the output of the subscription options appearing below the comment form.
648
		 *
649
		 * @module subscriptions
650
		 *
651
		 * @since 1.2.0
652
		 *
653
		 * @param string $str Comment Subscription form HTML output.
654
		 */
655
		echo apply_filters( 'jetpack_comment_subscription_form', $str );
656
	}
657
658
	/**
659
	 * Jetpack_Subscriptions::comment_subscribe_init()
660
	 *
661
	 * When a user checks the comment subscribe box and submits a comment, subscribe them to the comment thread.
662
	 */
663
	function comment_subscribe_submit( $comment_id, $approved ) {
664
		if ( 'spam' === $approved ) {
665
			return;
666
		}
667
668
		$comment = get_comment( $comment_id );
669
670
		// Set cookies for this post/comment
671
		$this->set_cookies( isset( $_REQUEST['subscribe_comments'] ), $comment->comment_post_ID, isset( $_REQUEST['subscribe_blog'] ) );
672
673
		if ( !isset( $_REQUEST['subscribe_comments'] ) && !isset( $_REQUEST['subscribe_blog'] ) )
674
			return;
675
676
		$post_ids = array();
677
678
		if ( isset( $_REQUEST['subscribe_comments'] ) )
679
			$post_ids[] = $comment->comment_post_ID;
680
681
		if ( isset( $_REQUEST['subscribe_blog'] ) )
682
			$post_ids[] = 0;
683
684
		Jetpack_Subscriptions::subscribe(
685
									$comment->comment_author_email,
686
									$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...
687
									true,
688
									array(
689
										'source'         => 'comment-form',
690
										'widget-in-use'  => is_active_widget( false, false, 'blog_subscription', true ) ? 'yes' : 'no',
691
										'comment_status' => $approved,
692
										'server_data'    => $_SERVER,
693
									)
694
		);
695
	}
696
697
	/**
698
	 * Jetpack_Subscriptions::set_cookies()
699
	 *
700
	 * Set a cookie to save state on the comment and post subscription checkboxes.
701
	 *
702
	 * @param bool $subscribe_to_post Whether the user chose to subscribe to subsequent comments on this post.
703
	 * @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...
704
	 * @param bool $subscribe_to_blog Whether the user chose to subscribe to all new posts on the blog.
705
	 */
706
	function set_cookies( $subscribe_to_post = false, $post_id = null, $subscribe_to_blog = false ) {
707
		$post_id = intval( $post_id );
708
709
		/** This filter is already documented in core/wp-includes/comment-functions.php */
710
		$cookie_lifetime = apply_filters( 'comment_cookie_lifetime',       30000000 );
711
712
		/**
713
		 * Filter the Jetpack Comment cookie path.
714
		 *
715
		 * @module subscriptions
716
		 *
717
		 * @since 2.5.0
718
		 *
719
		 * @param string COOKIEPATH Cookie path.
720
		 */
721
		$cookie_path     = apply_filters( 'jetpack_comment_cookie_path',   COOKIEPATH );
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned correctly; expected 1 space but found 5 spaces

This check looks for improperly formatted assignments.

Every assignment must have exactly one space before and one space after the equals operator.

To illustrate:

$a = "a";
$ab = "ab";
$abc = "abc";

will have no issues, while

$a   = "a";
$ab  = "ab";
$abc = "abc";

will report issues in lines 1 and 2.

Loading history...
722
723
		/**
724
		 * Filter the Jetpack Comment cookie domain.
725
		 *
726
		 * @module subscriptions
727
		 *
728
		 * @since 2.5.0
729
		 *
730
		 * @param string COOKIE_DOMAIN Cookie domain.
731
		 */
732
		$cookie_domain   = apply_filters( 'jetpack_comment_cookie_domain', COOKIE_DOMAIN );
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned correctly; expected 1 space but found 3 spaces

This check looks for improperly formatted assignments.

Every assignment must have exactly one space before and one space after the equals operator.

To illustrate:

$a = "a";
$ab = "ab";
$abc = "abc";

will have no issues, while

$a   = "a";
$ab  = "ab";
$abc = "abc";

will report issues in lines 1 and 2.

Loading history...
733
734
		if ( $subscribe_to_post && $post_id >= 0 ) {
735
			setcookie( 'jetpack_comments_subscribe_' . self::$hash . '_' . $post_id, 1, time() + $cookie_lifetime, $cookie_path, $cookie_domain );
736
		} else {
737
			setcookie( 'jetpack_comments_subscribe_' . self::$hash . '_' . $post_id, '', time() - 3600, $cookie_path, $cookie_domain );
738
		}
739
740
		if ( $subscribe_to_blog ) {
741
			setcookie( 'jetpack_blog_subscribe_' . self::$hash, 1, time() + $cookie_lifetime, $cookie_path, $cookie_domain );
742
		} else {
743
			setcookie( 'jetpack_blog_subscribe_' . self::$hash, '', time() - 3600, $cookie_path, $cookie_domain );
744
		}
745
	}
746
747
	public static function register_block_type() {
748
		if ( ! function_exists( 'register_block_type' ) ) {
749
			return;
750
		}
751
		register_block_type( 'jetpack/subscription-form', array(
752
			'render_callback' => 'jetpack_do_subscription_form'
753
		) );
754
	}
755
756
	public static function enqueue_block_editor_assets() {
757
		wp_register_script(
758
			'jetpack-block-subscription-form',
759
			plugins_url( 'subscriptions/block.js', __FILE__ ),
760
			array( 'wp-blocks', 'wp-element' )
761
		);
762
		wp_enqueue_script( 'jetpack-block-subscription-form' );
763
		wp_localize_script( 'jetpack-block-subscription-form', 'jpSubBlockI18n', array(
764
			'Subscription Form' => __( 'Subscription Form', 'jetpack' ),
765
			'Subscribe to Blog via Email' => __( 'Subscribe to Blog via Email', 'jetpack' ),
766
			'Enter your email address to subscribe to this blog and receive notifications of new posts by email.'
767
				=> __( 'Enter your email address to subscribe to this blog and receive notifications of new posts by email.', 'jetpack' ),
768
			'Email Address' => __( 'Email Address', 'jetpack' ),
769
			'Subscribe' => __( 'Subscribe', 'jetpack' ),
770
			"Success! An email was just sent to confirm your subscription. Please find the email now and click 'Confirm Follow' to start subscribing."
771
				=> __( "Success! An email was just sent to confirm your subscription. Please find the email now and click 'Confirm Follow' to start subscribing.", 'jetpack' ),
772
			'Join %s other subscribers' => __( 'Join %s other subscribers', 'jetpack' ),
773
			'Widget title:' => __( 'Widget title:', 'jetpack' ),
774
			'Subscription Form settings' => __( 'Subscription Form settings', 'jetpack' ),
775
			'Optional text to display to your readers:' => __( 'Optional text to display to your readers:', 'jetpack' ),
776
			'Subscribe Placeholder:' => __( 'Subscribe Placeholder:', 'jetpack' ),
777
			'Subscribe Button:' => __( 'Subscribe Button:', 'jetpack' ),
778
			'Success Message Text:' => __( 'Success Message Text:', 'jetpack' ),
779
			'Show total number of subscribers?' => __( 'Show total number of subscribers?', 'jetpack' ),
780
		) );
781
782
		wp_register_style( 'jetpack-subscriptions', plugins_url( 'subscriptions/subscriptions.css', __FILE__ ) );
783
		wp_enqueue_style( 'jetpack-subscriptions' );
784
	}
785
}
786
787
Jetpack_Subscriptions::init();
788
789
/***
790
 * Blog Subscription Widget
791
 */
792
793
class Jetpack_Subscriptions_Widget extends WP_Widget {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
794 View Code Duplication
	function __construct() {
795
		$widget_ops  = array(
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned correctly; expected 1 space but found 2 spaces

This check looks for improperly formatted assignments.

Every assignment must have exactly one space before and one space after the equals operator.

To illustrate:

$a = "a";
$ab = "ab";
$abc = "abc";

will have no issues, while

$a   = "a";
$ab  = "ab";
$abc = "abc";

will report issues in lines 1 and 2.

Loading history...
796
			'classname' => 'jetpack_subscription_widget',
797
			'description' => esc_html__( 'Add an email signup form to allow people to subscribe to your blog.', 'jetpack' ),
798
			'customize_selective_refresh' => true,
799
		);
800
801
		parent::__construct(
802
			'blog_subscription',
803
			/** This filter is documented in modules/widgets/facebook-likebox.php */
804
			apply_filters( 'jetpack_widget_name', __( 'Blog Subscriptions', 'jetpack' ) ),
805
			$widget_ops
806
		);
807
808
		if ( is_active_widget( false, false, $this->id_base ) || is_active_widget( false, false, 'monster' ) || is_customize_preview() ) {
809
			add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_style' ) );
810
		}
811
	}
812
813
	/**
814
	 * Enqueue the form's CSS.
815
	 *
816
	 * @since 4.5.0
817
	 */
818
	function enqueue_style() {
819
		wp_register_style( 'jetpack-subscriptions', plugins_url( 'subscriptions/subscriptions.css', __FILE__ ) );
820
		wp_enqueue_style( 'jetpack-subscriptions' );
821
	}
822
823
	function widget( $args, $instance ) {
824
		if (
825
			( ! defined( 'IS_WPCOM' ) || ! IS_WPCOM ) &&
826
			/** This filter is already documented in modules/contact-form/grunion-contact-form.php */
827
			false === apply_filters( 'jetpack_auto_fill_logged_in_user', false )
828
		) {
829
			$subscribe_email = '';
830
		} else {
831
			$current_user = wp_get_current_user();
832
			if ( ! empty( $current_user->user_email ) ) {
833
				$subscribe_email = esc_attr( $current_user->user_email );
834
			} else {
835
				$subscribe_email = '';
836
			}
837
		}
838
839
		/** This action is already documented in modules/widgets/gravatar-profile.php */
840
		do_action( 'jetpack_stats_extra', 'widget_view', 'jetpack_subscriptions' );
841
842
		$source                 = 'widget';
843
		$instance            	= wp_parse_args( (array) $instance, $this->defaults() );
844
		$subscribe_text      	= isset( $instance['subscribe_text'] )        ? stripslashes( $instance['subscribe_text'] )        : '';
845
		$subscribe_placeholder 	= isset( $instance['subscribe_placeholder'] ) ? stripslashes( $instance['subscribe_placeholder'] ) : '';
846
		$subscribe_button    	= isset( $instance['subscribe_button'] )      ? stripslashes( $instance['subscribe_button'] )      : '';
847
		$success_message    	= isset( $instance['success_message'] )       ? stripslashes( $instance['success_message'] )      : '';
848
		$widget_id              = esc_attr( !empty( $args['widget_id'] )      ? esc_attr( $args['widget_id'] ) : mt_rand( 450, 550 ) );
849
850
		$show_subscribers_total = (bool) $instance['show_subscribers_total'];
851
		$subscribers_total      = $this->fetch_subscriber_count(); // Only used for the shortcode [total-subscribers]
852
853
		// Give the input element a unique ID
854
		/**
855
		 * Filter the subscription form's ID prefix.
856
		 *
857
		 * @module subscriptions
858
		 *
859
		 * @since 2.7.0
860
		 *
861
		 * @param string subscribe-field Subscription form field prefix.
862
		 * @param int $widget_id Widget ID.
863
		 */
864
		$subscribe_field_id = apply_filters( 'subscribe_field_id', 'subscribe-field', $widget_id );
865
866
		// Display the subscription form
867
		echo $args['before_widget'];
868
869
		// Only show the title if there actually is a title
870 View Code Duplication
		if( ! empty( $instance['title'] ) ) {
871
			echo $args['before_title'] . esc_attr( $instance['title'] ) . $args['after_title'] . "\n";
872
		}
873
874
		$referer = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
875
876
		// Display any errors
877
		if ( isset( $_GET['subscribe'] ) ) :
878
			switch ( $_GET['subscribe'] ) :
879
				case 'invalid_email' : ?>
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
880
					<p class="error"><?php esc_html_e( 'The email you entered was invalid. Please check and try again.', 'jetpack' ); ?></p>
881
				<?php break;
0 ignored issues
show
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
882
				case 'opted_out' : ?>
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
883
					<p class="error"><?php printf( __( 'The email address has opted out of subscription emails. <br /> You can manage your preferences at <a href="%1$s" title="%2$s" target="_blank">subscribe.wordpress.com</a>', 'jetpack' ),
884
							'https://subscribe.wordpress.com/',
885
							__( 'Manage your email preferences.', 'jetpack' )
886
						); ?>
887
				<?php break;
0 ignored issues
show
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
888
				case 'already' : ?>
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
889
					<p class="error"><?php esc_html_e( 'You have already subscribed to this site. Please check your inbox.', 'jetpack' ); ?></p>
890
				<?php break;
0 ignored issues
show
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
891
				case 'success' : ?>
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
892
					<div class="success"><?php echo wpautop( str_replace( '[total-subscribers]', number_format_i18n( $subscribers_total['value'] ), $success_message ) ); ?></div>
893
					<?php break;
0 ignored issues
show
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
894
				default : ?>
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a DEFAULT statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in the default statement.

switch ($expr) {
    default : //wrong
        doSomething();
        break;
}

switch ($expr) {
    default: //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
Coding Style introduced by
The default body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a default statement must start on the line immediately following the statement.

switch ($expr) {
    default:
        doSomething(); //right
        break;
}


switch ($expr) {
    default:

        doSomething(); //wrong
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
895
					<p class="error"><?php esc_html_e( 'There was an error when subscribing. Please try again.', 'jetpack' ); ?></p>
896
				<?php break;
0 ignored issues
show
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
897
			endswitch;
898
		endif;
899
900
		// Display a subscribe form
901
		if ( isset( $_GET['subscribe'] ) && 'success' == $_GET['subscribe'] ) { ?>
902
			<?php
903
		} else { ?>
904
			<form action="#" method="post" accept-charset="utf-8" id="subscribe-blog-<?php echo $widget_id; ?>">
905
				<?php
906
				if ( ! isset ( $_GET['subscribe'] ) || 'success' != $_GET['subscribe'] ) {
907
					?><div id="subscribe-text"><?php echo wpautop( str_replace( '[total-subscribers]', number_format_i18n( $subscribers_total['value'] ), $subscribe_text ) ); ?></div><?php
908
				}
909
910
				if ( $show_subscribers_total && 0 < $subscribers_total['value'] ) {
911
					echo wpautop( sprintf( _n( 'Join %s other subscriber', 'Join %s other subscribers', $subscribers_total['value'], 'jetpack' ), number_format_i18n( $subscribers_total['value'] ) ) );
912
				}
913
				if ( ! isset ( $_GET['subscribe'] ) || 'success' != $_GET['subscribe'] ) { ?>
914
					<p id="subscribe-email">
915
						<label id="jetpack-subscribe-label" for="<?php echo esc_attr( $subscribe_field_id ) . '-' . esc_attr( $widget_id ); ?>">
916
							<?php echo !empty( $subscribe_placeholder ) ? esc_html( $subscribe_placeholder ) : esc_html__( 'Email Address:', 'jetpack' ); ?>
917
						</label>
918
						<input type="email" name="email" required="required" class="required" value="<?php echo esc_attr( $subscribe_email ); ?>" id="<?php echo esc_attr( $subscribe_field_id ) . '-' . esc_attr( $widget_id ); ?>" placeholder="<?php echo esc_attr( $subscribe_placeholder ); ?>" />
919
					</p>
920
921
					<p id="subscribe-submit">
922
						<input type="hidden" name="action" value="subscribe" />
923
						<input type="hidden" name="source" value="<?php echo esc_url( $referer ); ?>" />
924
						<input type="hidden" name="sub-type" value="<?php echo esc_attr( $source ); ?>" />
925
						<input type="hidden" name="redirect_fragment" value="<?php echo $widget_id; ?>" />
926
						<?php
927
							if ( is_user_logged_in() ) {
928
								wp_nonce_field( 'blogsub_subscribe_'. get_current_blog_id(), '_wpnonce', false );
929
							}
930
						?>
931
						<input type="submit" value="<?php echo esc_attr( $subscribe_button ); ?>" name="jetpack_subscriptions_widget" />
932
					</p>
933
				<?php }?>
934
			</form>
935
936
			<script>
937
			/*
938
			Custom functionality for safari and IE
939
			 */
940
			(function( d ) {
941
				// In case the placeholder functionality is available we remove labels
942
				if ( ( 'placeholder' in d.createElement( 'input' ) ) ) {
943
					var label = d.querySelector( 'label[for=subscribe-field-<?php echo $widget_id; ?>]' );
944
						label.style.clip 	 = 'rect(1px, 1px, 1px, 1px)';
945
						label.style.position = 'absolute';
946
						label.style.height   = '1px';
947
						label.style.width    = '1px';
948
						label.style.overflow = 'hidden';
949
				}
950
951
				// Make sure the email value is filled in before allowing submit
952
				var form = d.getElementById('subscribe-blog-<?php echo $widget_id; ?>'),
953
					input = d.getElementById('<?php echo esc_attr( $subscribe_field_id ) . '-' . esc_attr( $widget_id ); ?>'),
954
					handler = function( event ) {
955
						if ( '' === input.value ) {
956
							input.focus();
957
958
							if ( event.preventDefault ){
959
								event.preventDefault();
960
							}
961
962
							return false;
963
						}
964
					};
965
966
				if ( window.addEventListener ) {
967
					form.addEventListener( 'submit', handler, false );
968
				} else {
969
					form.attachEvent( 'onsubmit', handler );
970
				}
971
			})( document );
972
			</script>
973
		<?php } ?>
974
		<?php
975
976
		echo "\n" . $args['after_widget'];
977
	}
978
979
	function increment_subscriber_count( $current_subs_array = array() ) {
980
		$current_subs_array['value']++;
981
982
		set_transient( 'wpcom_subscribers_total', $current_subs_array, 3600 ); // try to cache the result for at least 1 hour
983
984
		return $current_subs_array;
985
	}
986
987
	public static function fetch_subscriber_count() {
988
		$subs_count = get_transient( 'wpcom_subscribers_total' );
989
990
		if ( FALSE === $subs_count || 'failed' == $subs_count['status'] ) {
991
			Jetpack:: load_xml_rpc_client();
0 ignored issues
show
Coding Style introduced by
Expected 0 spaces after double colon; 1 found

This check looks for references to static members where there are spaces between the name of the type and the actual member.

An example:

Certificate:: TRIPLEDES_CBC

will actually work, but is not very readable.

Loading history...
992
993
			$xml = new Jetpack_IXR_Client( array( 'user_id' => JETPACK_MASTER_USER, ) );
994
995
			$xml->query( 'jetpack.fetchSubscriberCount' );
996
997
			if ( $xml->isError() ) { // if we get an error from .com, set the status to failed so that we will try again next time the data is requested
998
				$subs_count = array(
999
					'status'  => 'failed',
1000
					'code'    => $xml->getErrorCode(),
1001
					'message' => $xml->getErrorMessage(),
1002
					'value'	  => ( isset( $subs_count['value'] ) ) ? $subs_count['value'] : 0,
1003
				);
1004
			} else {
1005
				$subs_count = array(
1006
					'status' => 'success',
1007
					'value'  => $xml->getResponse(),
1008
				);
1009
			}
1010
1011
			set_transient( 'wpcom_subscribers_total', $subs_count, 3600 ); // try to cache the result for at least 1 hour
1012
		}
1013
1014
		return $subs_count;
1015
	}
1016
1017
	function update( $new_instance, $old_instance ) {
1018
		$instance = $old_instance;
1019
1020
		$instance['title']					= wp_kses( stripslashes( $new_instance['title'] ), array() );
1021
		$instance['subscribe_text']			= wp_filter_post_kses( stripslashes( $new_instance['subscribe_text'] ) );
1022
		$instance['subscribe_placeholder']	= wp_kses( stripslashes( $new_instance['subscribe_placeholder'] ), array() );
1023
		$instance['subscribe_button']		= wp_kses( stripslashes( $new_instance['subscribe_button'] ), array() );
1024
		$instance['success_message']		= wp_kses( stripslashes( $new_instance['success_message'] ), array() );
1025
		$instance['show_subscribers_total']	= isset( $new_instance['show_subscribers_total'] ) && $new_instance['show_subscribers_total'];
1026
1027
		return $instance;
1028
	}
1029
1030
	public static function defaults() {
1031
		return array(
1032
			'title'               	 => esc_html__( 'Subscribe to Blog via Email', 'jetpack' ),
1033
			'subscribe_text'      	 => esc_html__( 'Enter your email address to subscribe to this blog and receive notifications of new posts by email.', 'jetpack' ),
1034
			'subscribe_placeholder'	 => esc_html__( 'Email Address', 'jetpack' ),
1035
			'subscribe_button'    	 => esc_html__( 'Subscribe', 'jetpack' ),
1036
			'success_message'    	 => esc_html__( "Success! An email was just sent to confirm your subscription. Please find the email now and click 'Confirm Follow' to start subscribing.", 'jetpack' ),
1037
			'show_subscribers_total' => true,
1038
		);
1039
	}
1040
1041
	function form( $instance ) {
1042
		$instance = wp_parse_args( (array) $instance, $this->defaults() );
1043
1044
		$title               	= stripslashes( $instance['title'] );
1045
		$subscribe_text      	= stripslashes( $instance['subscribe_text'] );
1046
		$subscribe_placeholder 	= stripslashes( $instance['subscribe_placeholder'] );
1047
		$subscribe_button    	= stripslashes( $instance['subscribe_button'] );
1048
		$success_message		= stripslashes( $instance['success_message']);
1049
		$show_subscribers_total = checked( $instance['show_subscribers_total'], true, false );
1050
1051
		$subs_fetch = $this->fetch_subscriber_count();
1052
1053
		if ( 'failed' == $subs_fetch['status'] ) {
1054
			printf( '<div class="error inline"><p>' . __( '%s: %s', 'jetpack' ) . '</p></div>', esc_html( $subs_fetch['code'] ), esc_html( $subs_fetch['message'] ) );
1055
		}
1056
		$subscribers_total = number_format_i18n( $subs_fetch['value'] );
1057
?>
1058
<p>
1059
	<label for="<?php echo $this->get_field_id( 'title' ); ?>">
1060
		<?php _e( 'Widget title:', 'jetpack' ); ?>
1061
		<input class="widefat" id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>" />
1062
	</label>
1063
</p>
1064
<p>
1065
	<label for="<?php echo $this->get_field_id( 'subscribe_text' ); ?>">
1066
		<?php _e( 'Optional text to display to your readers:', 'jetpack' ); ?>
1067
		<textarea class="widefat" id="<?php echo $this->get_field_id( 'subscribe_text' ); ?>" name="<?php echo $this->get_field_name( 'subscribe_text' ); ?>" rows="3"><?php echo esc_html( $subscribe_text ); ?></textarea>
1068
	</label>
1069
</p>
1070
<p>
1071
	<label for="<?php echo $this->get_field_id( 'subscribe_placeholder' ); ?>">
1072
		<?php esc_html_e( 'Subscribe Placeholder:', 'jetpack' ); ?>
1073
		<input class="widefat" id="<?php echo $this->get_field_id( 'subscribe_placeholder' ); ?>" name="<?php echo $this->get_field_name( 'subscribe_placeholder' ); ?>" type="text" value="<?php echo esc_attr( $subscribe_placeholder ); ?>" />
1074
	</label>
1075
</p>
1076
<p>
1077
	<label for="<?php echo $this->get_field_id( 'subscribe_button' ); ?>">
1078
		<?php _e( 'Subscribe Button:', 'jetpack' ); ?>
1079
		<input class="widefat" id="<?php echo $this->get_field_id( 'subscribe_button' ); ?>" name="<?php echo $this->get_field_name( 'subscribe_button' ); ?>" type="text" value="<?php echo esc_attr( $subscribe_button ); ?>" />
1080
	</label>
1081
</p>
1082
<p>
1083
	<label for="<?php echo $this->get_field_id( 'success_message' ); ?>">
1084
		<?php _e( 'Success Message Text:', 'jetpack' ); ?>
1085
		<textarea class="widefat" id="<?php echo $this->get_field_id( 'success_message' ); ?>" name="<?php echo $this->get_field_name( 'success_message' ); ?>" rows="5"><?php echo esc_html( $success_message ); ?></textarea>
1086
	</label>
1087
</p>
1088
<p>
1089
	<label for="<?php echo $this->get_field_id( 'show_subscribers_total' ); ?>">
1090
		<input type="checkbox" id="<?php echo $this->get_field_id( 'show_subscribers_total' ); ?>" name="<?php echo $this->get_field_name( 'show_subscribers_total' ); ?>" value="1"<?php echo $show_subscribers_total; ?> />
1091
		<?php echo esc_html( sprintf( _n( 'Show total number of subscribers? (%s subscriber)', 'Show total number of subscribers? (%s subscribers)', $subscribers_total, 'jetpack' ), $subscribers_total ) ); ?>
1092
	</label>
1093
</p>
1094
<?php
1095
	}
1096
}
1097
1098
add_shortcode( 'jetpack_subscription_form', 'jetpack_do_subscription_form' );
1099
add_shortcode( 'blog_subscription_form', 'jetpack_do_subscription_form' );
1100
1101
function jetpack_do_subscription_form( $instance ) {
1102
	if ( empty( $instance ) || ! is_array( $instance ) ) {
1103
		$instance = array();
1104
	}
1105
	$instance['show_subscribers_total'] = empty( $instance['show_subscribers_total'] ) ? false : true;
1106
1107
	if (
1108
		array_key_exists( 'subscribe_text', $instance ) &&
1109
		is_array( $instance['subscribe_text'] )
1110
	) {
1111
		$instance['subscribe_text'] = $instance['subscribe_text'][0];
1112
	}
1113
1114
	$instance = shortcode_atts(
1115
		Jetpack_Subscriptions_Widget::defaults(),
1116
		$instance,
1117
		'jetpack_subscription_form'
1118
	);
1119
	$args = array(
1120
		'before_widget' => sprintf( '<div class="%s">', 'jetpack_subscription_widget' ),
1121
	);
1122
	ob_start();
1123
	the_widget( 'Jetpack_Subscriptions_Widget', $instance, $args );
1124
	$output = ob_get_clean();
1125
	return $output;
1126
}
1127