Completed
Push — fix/jitm-dismissals ( 86a339 )
by
unknown
09:49
created

class.jetpack-jitm.php (1 issue)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/**
4
 * Jetpack just in time messaging through out the admin
5
 *
6
 * @since 3.7.0
7
 */
8
class Jetpack_JITM {
9
10
	/**
11
	 * @var Jetpack_JITM
12
	 **/
13
	private static $instance = null;
14
15
	/**
16
	 * Initializes the class, or returns the singleton
17
	 *
18
	 * @return Jetpack_JITM | false
19
	 */
20
	static function init() {
21
		/**
22
		 * Filter to turn off all just in time messages
23
		 *
24
		 * @since 3.7.0
25
		 * @since 5.4.0 Correct docblock to reflect default arg value
26
		 *
27
		 * @param bool false Whether to show just in time messages.
28
		 */
29
		if ( ! apply_filters( 'jetpack_just_in_time_msgs', false ) ) {
30
			return false;
31
		}
32
33
		if ( is_null( self::$instance ) ) {
34
			self::$instance = new Jetpack_JITM;
35
		}
36
37
		return self::$instance;
38
	}
39
40
	/**
41
	 * Jetpack_JITM constructor.
42
	 */
43
	private function __construct() {
44
		if ( ! Jetpack::is_active() || Jetpack::is_development_mode() ) {
45
			return;
46
		}
47
		add_action( 'current_screen', array( $this, 'prepare_jitms' ) );
48
	}
49
50
	/**
51
	 * Get's the Jetpack emblem
52
	 *
53
	 * @return string The Jetpack emblem
54
	 */
55
	function get_emblem() {
56
		return '<div class="jp-emblem">' . Jetpack::get_jp_emblem() . '</div>';
57
	}
58
59
	/**
60
	 * Prepare actions according to screen and post type.
61
	 *
62
	 * @since 3.8.2
63
	 *
64
	 * @uses Jetpack_Autoupdate::get_possible_failures()
65
	 *
66
	 * @param object $screen
67
	 */
68
	function prepare_jitms( $screen ) {
69
		if ( ! in_array( $screen->id, array(
70
			'toplevel_page_jetpack',
71
			'jetpack_page_stats',
72
			'jetpack_page_akismet-key-config',
73
			'admin_page_jetpack_modules'
74
		) ) ) {
75
			add_action( 'admin_enqueue_scripts', array( $this, 'jitm_enqueue_files' ) );
76
			add_action( 'admin_notices', array( $this, 'ajax_message' ) );
77
			add_action( 'edit_form_top', array( $this, 'ajax_message' ) );
78
		}
79
	}
80
81
	/**
82
	 * A special filter for WooCommerce, to set a message based on local state.
83
	 *
84
	 * @param $message string The current message
85
	 *
86
	 * @return array The new message
87
	 */
88
	static function jitm_woocommerce_services_msg( $content ) {
89
		if ( ! function_exists( 'wc_get_base_location' ) ) {
90
			return $content;
91
		}
92
93
		$base_location = wc_get_base_location();
94
95
		switch ( $base_location['country'] ) {
96
			case 'US':
97
				$content->message = esc_html__( 'New free service: Show USPS shipping rates on your store! Added bonus: print shipping labels without leaving WooCommerce.', 'jetpack' );
98
				break;
99
			case 'CA':
100
				$content->message = esc_html__( 'New free service: Show Canada Post shipping rates on your store!', 'jetpack' );
101
				break;
102
			default:
103
				$content->message = '';
104
		}
105
106
		return $content;
107
	}
108
109
	/**
110
	 * A special filter for WooCommerce Call To Action button
111
	 *
112
	 * @param $CTA string The existing CTA
113
	 *
114
	 * @return string The new CTA
115
	 */
116
	static function jitm_jetpack_woo_services_install( $CTA ) {
117
		return wp_nonce_url( add_query_arg( array(
118
			'wc-services-action' => 'install'
119
		), admin_url( 'admin.php?page=wc-settings' ) ), 'wc-services-install' );
120
	}
121
122
	/**
123
	 * A special filter for WooCommerce Call To Action button
124
	 *
125
	 * @param $CTA string The existing CTA
126
	 *
127
	 * @return string The new CTA
128
	 */
129
	static function jitm_jetpack_woo_services_activate( $CTA ) {
130
		return wp_nonce_url( add_query_arg( array(
131
			'wc-services-action' => 'activate'
132
		), admin_url( 'admin.php?page=wc-settings' ) ), 'wc-services-install' );
133
	}
134
135
	/**
136
	 * Injects the dom to show a JITM inside of
137
	 */
138
	function ajax_message() {
139
		$message_path = $this->get_message_path();
140
		$query_string = _http_build_query( $_GET, '', ',' );
141
142
		?>
143
		<div class="jetpack-jitm-message"
144
		     data-nonce="<?php echo wp_create_nonce( 'wp_rest' ) ?>"
145
		     data-message-path="<?php echo esc_attr( $message_path ) ?>"
146
		     data-query="<?php echo urlencode_deep( $query_string ) ?>"
147
		></div>
148
		<?php
149
	}
150
151
	/**
152
	 * Get's the current message path for display of a JITM
153
	 *
154
	 * @return string The message path
155
	 */
156
	function get_message_path() {
157
		$screen = get_current_screen();
158
159
		return 'wp:' . $screen->id . ':' . current_filter();
160
	}
161
162
	/**
163
	 * Function to enqueue jitm css and js
164
	 */
165
	function jitm_enqueue_files() {
166
		$min = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
167
		wp_register_style(
168
			'jetpack-jitm-css',
169
			plugins_url( "css/jetpack-admin-jitm{$min}.css", JETPACK__PLUGIN_FILE ),
170
			false,
171
			JETPACK__VERSION .
172
			'-201243242'
173
		);
174
		wp_style_add_data( 'jetpack-jitm-css', 'rtl', 'replace' );
175
		wp_style_add_data( 'jetpack-jitm-css', 'suffix', $min );
176
		wp_enqueue_style( 'jetpack-jitm-css' );
177
178
		wp_enqueue_script( 'jetpack-jitm-new', plugins_url( '_inc/jetpack-jitm.js', JETPACK__PLUGIN_FILE ), array( 'jquery' ), JETPACK__VERSION, true );
179
		wp_localize_script( 'jetpack-jitm-new', 'jitm_config', array(
180
			'api_root' => esc_url_raw( rest_url() ),
181
		) );
182
	}
183
184
	/**
185
	 * Dismisses a JITM feature class so that it will no longer be shown
186
	 *
187
	 * @param $id string The id of the JITM that was dismissed
188
	 * @param $feature_class string The feature class of the JITM that was dismissed
189
	 *
190
	 * @return bool Always true
191
	 */
192
	function dismiss( $id, $feature_class ) {
193
		JetpackTracking::record_user_event( 'jitm_dismiss_client', array(
194
			'jitm_id' => $id,
195
			'feature_class' => $feature_class,
196
		) );
197
198
199
		$hide_jitm = Jetpack_Options::get_option( 'hide_jitm' );
200
		if ( ! is_array( $hide_jitm ) ) {
201
			$hide_jitm = array();
202
		}
203
204
		if ( isset( $hide_jitm[ $feature_class ] ) ) {
205
			if ( ! is_array( $hide_jitm[ $feature_class ] ) ) {
206
				$hide_jitm[ $feature_class ] = array( 'last_dismissal' => 0, 'number' => 0 );
207
			}
208
		} else {
209
			$hide_jitm[ $feature_class ] = array( 'last_dismissal' => 0, 'number' => 0 );
210
		}
211
212
		$number = $hide_jitm[ $feature_class ]['number'];
213
214
		$hide_jitm[ $feature_class ] = array( 'last_dismissal' => time(), 'number' => $number + 1 );
215
216
		Jetpack_Options::update_option( 'hide_jitm', $hide_jitm );
217
218
		return true;
219
	}
220
221
	/**
222
	 * Asks the wpcom API for the current message to display keyed on query string and message path
223
	 *
224
	 * @param $message_path string The message path to ask for
225
	 * @param $query string The query string originally from the front end
226
	 *
227
	 * @return array The JITM's to show, or an empty array if there is nothing to show
228
	 */
229
	function get_messages( $message_path, $query ) {
230
		// custom filters go here
231
		add_filter( 'jitm_woocommerce_services_msg', array( 'Jetpack_JITM', 'jitm_woocommerce_services_msg' ) );
232
		add_filter( 'jitm_jetpack_woo_services_install', array( 'Jetpack_JITM', 'jitm_jetpack_woo_services_install' ) );
233
		add_filter( 'jitm_jetpack_woo_services_activate', array(
234
			'Jetpack_JITM',
235
			'jitm_jetpack_woo_services_activate'
236
		) );
237
238
		$user = wp_get_current_user();
239
240
		// unauthenticated or invalid requests just bail
241
		if ( ! $user ) {
242
			return array();
243
		}
244
245
		require_once( JETPACK__PLUGIN_DIR . 'class.jetpack-client.php' );
246
247
		$site_id = Jetpack_Options::get_option( 'id' );
248
249
		// build our jitm request
250
		$path = add_query_arg( array(
251
			'external_user_id' => urlencode_deep( $user->ID ),
252
			'query_string'     => urlencode_deep( $query ),
253
		), sprintf( '/sites/%d/jitm/%s', $site_id, $message_path ) );
254
255
		// attempt to get from cache
256
		$envelopes = get_transient( 'jetpack_jitm_' . substr( md5( $path ), 0, 31 ) );
257
258
		// if something is in the cache and it was put in the cache after the last sync we care about, use it
259
		$use_cache = false;
260
261
		/** This filter is documented in class.jetpack.php */
262
		if ( apply_filters( 'jetpack_just_in_time_msg_cache', false ) ) {
263
			$use_cache = true;
264
		}
265
266
		if ( $use_cache ) {
267
			$last_sync  = (int) get_transient( 'jetpack_last_plugin_sync' );
268
			$from_cache = $envelopes && $last_sync > 0 && $last_sync < $envelopes['last_response_time'];
269
		} else {
270
			$from_cache = false;
271
		}
272
273
		// otherwise, ask again
274
		if ( ! $from_cache ) {
275
			$wpcom_response = Jetpack_Client::wpcom_json_api_request_as_blog(
276
				$path,
277
				'2',
278
				array(
279
					'user_id'    => $user->ID,
280
					'user_roles' => implode( ',', $user->roles ),
281
				),
282
				null,
283
				'wpcom'
284
			);
285
286
			// silently fail...might be helpful to track it?
287
			if ( is_wp_error( $wpcom_response ) ) {
288
				return array();
289
			}
290
291
			$envelopes = json_decode( $wpcom_response['body'] );
292
293
			if ( ! is_array( $envelopes ) ) {
294
				return array();
295
			}
296
297
			$expiration = isset( $envelopes[0] ) ? $envelopes[0]->ttl : 300;
298
299
			// do not cache if expiration is 0 or we're not using the cache
300
			if ( 0 != $expiration && $use_cache ) {
301
				$envelopes['last_response_time'] = time();
302
303
				set_transient( 'jetpack_jitm_' . substr( md5( $path ), 0, 31 ), $envelopes, $expiration );
304
			}
305
		}
306
307
		$hidden_jitms = Jetpack_Options::get_option( 'hide_jitm' );
308
		unset( $envelopes['last_response_time'] );
309
310
		foreach ( $envelopes as $idx => &$envelope ) {
311
312
			$dismissed_feature = isset( $hidden_jitms[ $envelope->feature_class ] ) && is_array( $hidden_jitms[ $envelope->feature_class ] ) ? $hidden_jitms[ $envelope->feature_class ] : null;
313
314
			// if the this feature class has been dismissed and the request has not expired from the cache, skip it as it's been dismissed
315
			if ( is_array( $dismissed_feature ) && $from_cache && time() - $dismissed_feature['last_dismissal'] < $expiration + 60 ) {
0 ignored issues
show
The variable $expiration 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...
316
				unset( $envelopes[ $idx ] );
317
				continue;
318
			}
319
320
			JetpackTracking::record_user_event( 'jitm_view_client', array(
321
				'jitm_id' => $envelope->id,
322
			) );
323
324
			$normalized_site_url      = Jetpack::build_raw_urls( get_home_url() );
325
			$envelope->url            = 'https://jetpack.com/redirect/?source=jitm-' . $envelope->id . '&site=' . $normalized_site_url . '&u=' . $user->ID;
326
			$envelope->jitm_stats_url = Jetpack::build_stats_url( array( 'x_jetpack-jitm' => $envelope->id ) );
327
328
			if ( $envelope->CTA->hook ) {
329
				$envelope->url = apply_filters( 'jitm_' . $envelope->CTA->hook, $envelope->url );
330
				unset( $envelope->CTA->hook );
331
			}
332
333
			if ( isset( $envelope->content->hook ) ) {
334
				$envelope->content = apply_filters( 'jitm_' . $envelope->content->hook, $envelope->content );
335
				unset( $envelope->content->hook );
336
			}
337
338
			// no point in showing an empty message
339
			if ( empty( $envelope->content->message ) ) {
340
				return array();
341
			}
342
343
			switch ( $envelope->content->icon ) {
344
				case 'jetpack':
345
					$envelope->content->icon = '<div class="jp-emblem">' . Jetpack::get_jp_emblem() . '</div>';
346
					break;
347
				case 'woocommerce':
348
					$envelope->content->icon = '<div class="jp-emblem"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Layer_1" x="0px" y="0px" viewBox="0 0 168 100" xml:space="preserve" enable-background="new 0 0 168 100" width="50" height="30"><style type="text/css">
349
					.st0{clip-path:url(#SVGID_2_);enable-background:new    ;}
350
					.st1{clip-path:url(#SVGID_4_);}
351
					.st2{clip-path:url(#SVGID_6_);}
352
					.st3{clip-path:url(#SVGID_8_);fill:#8F567F;}
353
					.st4{clip-path:url(#SVGID_10_);fill:#FFFFFE;}
354
					.st5{clip-path:url(#SVGID_12_);fill:#FFFFFE;}
355
					.st6{clip-path:url(#SVGID_14_);fill:#FFFFFE;}
356
				</style><g><defs><polygon id="SVGID_1_" points="83.8 100 0 100 0 0.3 83.8 0.3 167.6 0.3 167.6 100 "/></defs><clipPath id="SVGID_2_"><use xlink:href="#SVGID_1_" overflow="visible"/></clipPath><g class="st0"><g><defs><rect id="SVGID_3_" width="168" height="100"/></defs><clipPath id="SVGID_4_"><use xlink:href="#SVGID_3_" overflow="visible"/></clipPath><g class="st1"><defs><path id="SVGID_5_" d="M15.6 0.3H152c8.6 0 15.6 7 15.6 15.6v52c0 8.6-7 15.6-15.6 15.6h-48.9l6.7 16.4L80.2 83.6H15.6C7 83.6 0 76.6 0 67.9v-52C0 7.3 7 0.3 15.6 0.3"/></defs><clipPath id="SVGID_6_"><use xlink:href="#SVGID_5_" overflow="visible"/></clipPath><g class="st2"><defs><rect id="SVGID_7_" width="168" height="100"/></defs><clipPath id="SVGID_8_"><use xlink:href="#SVGID_7_" overflow="visible"/></clipPath><rect x="-10" y="-9.7" class="st3" width="187.6" height="119.7"/></g></g></g></g></g><g><defs><path id="SVGID_9_" d="M8.4 14.5c1-1.3 2.4-2 4.3-2.1 3.5-0.2 5.5 1.4 6 4.9 2.1 14.3 4.4 26.4 6.9 36.4l15-28.6c1.4-2.6 3.1-3.9 5.2-4.1 3-0.2 4.9 1.7 5.6 5.7 1.7 9.1 3.9 16.9 6.5 23.4 1.8-17.4 4.8-30 9-37.7 1-1.9 2.5-2.9 4.5-3 1.6-0.1 3 0.3 4.3 1.4 1.3 1 2 2.3 2.1 3.9 0.1 1.2-0.1 2.3-0.7 3.3 -2.7 5-4.9 13.2-6.6 24.7 -1.7 11.1-2.3 19.8-1.9 26.1 0.1 1.7-0.1 3.2-0.8 4.5 -0.8 1.5-2 2.4-3.7 2.5 -1.8 0.1-3.6-0.7-5.4-2.5C52.4 66.7 47.4 57 43.7 44.1c-4.4 8.8-7.7 15.3-9.9 19.7 -4 7.7-7.5 11.7-10.3 11.9 -1.9 0.1-3.5-1.4-4.8-4.7 -3.5-9-7.3-26.3-11.3-52C7.1 17.3 7.5 15.8 8.4 14.5"/></defs><clipPath id="SVGID_10_"><use xlink:href="#SVGID_9_" overflow="visible"/></clipPath><rect x="-2.7" y="-0.6" class="st4" width="90.6" height="86.4"/></g><g><defs><path id="SVGID_11_" d="M155.6 25.2c-2.5-4.3-6.1-6.9-11-7.9 -1.3-0.3-2.5-0.4-3.7-0.4 -6.6 0-11.9 3.4-16.1 10.2 -3.6 5.8-5.3 12.3-5.3 19.3 0 5.3 1.1 9.8 3.3 13.6 2.5 4.3 6.1 6.9 11 7.9 1.3 0.3 2.5 0.4 3.7 0.4 6.6 0 12-3.4 16.1-10.2 3.6-5.9 5.3-12.4 5.3-19.4C159 33.4 157.9 28.9 155.6 25.2zM147 44.2c-0.9 4.5-2.7 7.9-5.2 10.1 -2 1.8-3.9 2.5-5.5 2.2 -1.7-0.3-3-1.8-4-4.4 -0.8-2.1-1.2-4.2-1.2-6.2 0-1.7 0.2-3.4 0.5-5 0.6-2.8 1.8-5.5 3.6-8.1 2.3-3.3 4.7-4.8 7.1-4.2 1.7 0.3 3 1.8 4 4.4 0.8 2.1 1.2 4.2 1.2 6.2C147.5 40.9 147.3 42.6 147 44.2z"/></defs><clipPath id="SVGID_12_"><use xlink:href="#SVGID_11_" overflow="visible"/></clipPath><rect x="109.6" y="6.9" class="st5" width="59.4" height="71.4"/></g><g><defs><path id="SVGID_13_" d="M112.7 25.2c-2.5-4.3-6.1-6.9-11-7.9 -1.3-0.3-2.5-0.4-3.7-0.4 -6.6 0-11.9 3.4-16.1 10.2 -3.5 5.8-5.3 12.3-5.3 19.3 0 5.3 1.1 9.8 3.3 13.6 2.5 4.3 6.1 6.9 11 7.9 1.3 0.3 2.5 0.4 3.7 0.4 6.6 0 12-3.4 16.1-10.2 3.5-5.9 5.3-12.4 5.3-19.4C116 33.4 114.9 28.9 112.7 25.2zM104.1 44.2c-0.9 4.5-2.7 7.9-5.2 10.1 -2 1.8-3.9 2.5-5.5 2.2 -1.7-0.3-3-1.8-4-4.4 -0.8-2.1-1.2-4.2-1.2-6.2 0-1.7 0.2-3.4 0.5-5 0.6-2.8 1.8-5.5 3.6-8.1 2.3-3.3 4.7-4.8 7.1-4.2 1.7 0.3 3 1.8 4 4.4 0.8 2.1 1.2 4.2 1.2 6.2C104.6 40.9 104.4 42.6 104.1 44.2z"/></defs><clipPath id="SVGID_14_"><use xlink:href="#SVGID_13_" overflow="visible"/></clipPath><rect x="66.7" y="6.9" class="st6" width="59.4" height="71.4"/></g></svg></div>';
357
					break;
358
				default:
359
					$envelope->content->icon = '';
360
					break;
361
			}
362
363
			$jetpack = Jetpack::init();
364
			$jetpack->stat( 'jitm', $envelope->id . '-viewed-' . JETPACK__VERSION );
365
			$jetpack->do_stats( 'server_side' );
366
		}
367
368
		return $envelopes;
369
	}
370
}
371
372
add_action( 'init', array( 'Jetpack_JITM', 'init' ) );
373