Completed
Push — fix/jitm-hash-cache ( 1ccec9...0297bc )
by
unknown
265:45 queued 255:58
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
19
	 */
20
	static function init() {
21
		if ( is_null( self::$instance ) ) {
22
			self::$instance = new Jetpack_JITM;
23
		}
24
25
		return self::$instance;
26
	}
27
28
	/**
29
	 * Jetpack_JITM constructor.
30
	 */
31
	private function __construct() {
32
		if ( ! Jetpack::is_active() ) {
33
			return;
34
		}
35
		add_action( 'current_screen', array( $this, 'prepare_jitms' ) );
36
	}
37
38
	/**
39
	 * Get's the Jetpack emblem
40
	 *
41
	 * @return string The Jetpack emblem
42
	 */
43
	function get_emblem()
44
	{
45
		return '<div class="jp-emblem">' . Jetpack::get_jp_emblem() . '</div>';
46
	}
47
48
	/**
49
	 * Prepare actions according to screen and post type.
50
	 *
51
	 * @since 3.8.2
52
	 *
53
	 * @uses Jetpack_Autoupdate::get_possible_failures()
54
	 *
55
	 * @param object $screen
56
	 */
57
	function prepare_jitms( $screen ) {
58
		add_action( 'admin_enqueue_scripts', array( $this, 'jitm_enqueue_files' ) );
59
		add_action( 'admin_notices', array( $this, 'ajax_message' ) );
60
		add_action( 'edit_form_top', array( $this, 'ajax_message' ) );
61
	}
62
63
	/**
64
	 * A special filter for WooCommerce, to set a message based on local state.
65
	 *
66
	 * @param $message string The current message
67
	 *
68
	 * @return array The new message
69
	 */
70
	static function jitm_woocommerce_services_msg( $content ) {
71
		if ( ! function_exists( 'wc_get_base_location' ) ) {
72
			return $content;
73
		}
74
75
		$base_location = wc_get_base_location();
76
77
		switch ( $base_location['country'] ) {
78
			case 'US':
79
				$content->message = esc_html__( 'New free service: Show USPS shipping rates on your store! Added bonus: print shipping labels without leaving WooCommerce.', 'jetpack' );
80
				break;
81
			case 'CA':
82
				 $content->message = esc_html__( 'New free service: Show Canada Post shipping rates on your store!', 'jetpack' );
83
				break;
84
			default:
85
				$content->message = '';
86
		}
87
88
		return $content;
89
	}
90
91
	/**
92
	 * A special filter for WooCommerce Call To Action button
93
	 *
94
	 * @param $CTA string The existing CTA
95
	 *
96
	 * @return string The new CTA
97
	 */
98
	static function jitm_jetpack_woo_services_install( $CTA ) {
99
		return wp_nonce_url( add_query_arg( array(
100
			'wc-services-action' => 'install'
101
		), admin_url( 'admin.php?page=wc-settings' ) ), 'wc-services-install' );
102
	}
103
104
	/**
105
	 * A special filter for WooCommerce Call To Action button
106
	 *
107
	 * @param $CTA string The existing CTA
108
	 *
109
	 * @return string The new CTA
110
	 */
111
	static function jitm_jetpack_woo_services_activate( $CTA ) {
112
		return wp_nonce_url( add_query_arg( array(
113
			'wc-services-action' => 'activate'
114
		), admin_url( 'admin.php?page=wc-settings' ) ), 'wc-services-install' );
115
	}
116
117
	/**
118
	 * Injects the dom to show a JITM inside of
119
	 */
120
	function ajax_message() {
121
		$message_path = $this->get_message_path();
122
		$query_string = _http_build_query( $_GET, '', ',' );
123
124
		?>
125
		<div class="jetpack-jitm-message"
126
		     data-nonce="<?php echo wp_create_nonce( 'wp_rest' ) ?>"
127
		     data-message-path="<?php echo esc_attr( $message_path ) ?>"
128
		     data-query="<?php echo urlencode_deep( $query_string ) ?>"
129
		></div>
130
		<?php
131
	}
132
133
	/**
134
	 * Get's the current message path for display of a JITM
135
	 *
136
	 * @return string The message path
137
	 */
138
	function get_message_path() {
139
		$screen = get_current_screen();
140
141
		return 'wp:' . $screen->id . ':' . current_filter();
142
	}
143
144
	/**
145
	* Function to enqueue jitm css and js
146
	*/
147
	function jitm_enqueue_files() {
148
		$min = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
149
		wp_register_style(
150
			'jetpack-jitm-css',
151
			plugins_url( "css/jetpack-admin-jitm{$min}.css", JETPACK__PLUGIN_FILE ),
152
			false,
153
			JETPACK__VERSION .
154
			'-201243242'
155
		);
156
		wp_style_add_data( 'jetpack-jitm-css', 'rtl', 'replace' );
157
		wp_style_add_data( 'jetpack-jitm-css', 'suffix', $min );
158
		wp_enqueue_style( 'jetpack-jitm-css' );
159
160
		wp_enqueue_script( 'jetpack-jitm-new', plugins_url( '_inc/jetpack-jitm.js', JETPACK__PLUGIN_FILE ), array( 'jquery' ), JETPACK__VERSION, true );
161
		wp_localize_script('jetpack-jitm-new', 'jitm_config', array(
162
				'api_root' => esc_url_raw( rest_url() ),
163
		) );
164
	}
165
166
	/**
167
	 * Dismisses a JITM feature class so that it will no longer be shown
168
	 *
169
	 * @param $id string The id of the JITM that was dismissed
170
	 * @param $feature_class string The feature class of the JITM that was dismissed
171
	 *
172
	 * @return bool Always true
173
	 */
174
	function dismiss( $id, $feature_class ) {
175
		// todo: track dismissal of id and feature class?
176
		$hide_jitm = Jetpack_Options::get_option( 'hide_jitm' );
177
		if ( ! is_array( $hide_jitm ) ) {
178
			$hide_jitm = array();
179
		}
180
181
		if ( isset( $hide_jitm[ $feature_class ] ) ) {
182
			if ( ! is_array( $hide_jitm[ $feature_class ] ) ) {
183
				$hide_jitm[ $feature_class ] = array( 'last_dismissal' => 0, 'number' => 0 );
184
			}
185
		} else {
186
			$hide_jitm[ $feature_class ] = array( 'last_dismissal' => 0, 'number' => 0 );
187
		}
188
189
		$number = $hide_jitm[ $feature_class ]['number'];
190
191
		$hide_jitm[ $feature_class ] = array( 'last_dismissal' => time(), 'number' => $number + 1 );
192
193
		Jetpack_Options::update_option( 'hide_jitm', $hide_jitm );
194
195
		return true;
196
	}
197
198
	/**
199
	 * Asks the wpcom API for the current message to display keyed on query string and message path
200
	 *
201
	 * @param $message_path string The message path to ask for
202
	 * @param $query string The query string originally from the front end
203
	 *
204
	 * @return array The JITM's to show, or an empty array if there is nothing to show
205
	 */
206
	static function get_messages( $message_path, $query ) {
207
		// custom filters go here
208
		add_filter( 'jitm_woocommerce_services_msg', array( 'Jetpack_JITM', 'jitm_woocommerce_services_msg' ) );
209
		add_filter( 'jitm_jetpack_woo_services_install', array( 'Jetpack_JITM', 'jitm_jetpack_woo_services_install' ) );
210
		add_filter( 'jitm_jetpack_woo_services_activate', array(
211
			'Jetpack_JITM',
212
			'jitm_jetpack_woo_services_activate'
213
		) );
214
215
		$user = wp_get_current_user();
216
217
		// unauthenticated or invalid requests just bail
218
		if ( ! $user ) {
219
			return array();
220
		}
221
222
		require_once( JETPACK__PLUGIN_DIR . 'class.jetpack-client.php' );
223
224
		$site_id = Jetpack_Options::get_option( 'id' );
225
226
		// build our jitm request
227
		$path = add_query_arg( array(
228
			'force'                => 'wpcom',
229
			'external_user_id'     => urlencode_deep( $user->ID ),
230
			'user_roles'           => urlencode_deep( implode( ',', $user->roles ) ),
231
			'query_string'         => urlencode_deep( $query ),
232
		), sprintf( '/sites/%d/jitm/%s', $site_id, $message_path ) );
233
234
		// attempt to get from cache
235
		$envelopes  = get_transient( sha1( 'jetpack_jitm_' . $path ) );
236
		$from_cache = false;
237
238
		// if something is in the cache and it was put in the cache after the last heartbeat or sync, use it
239
		$last_heartbeat = Jetpack_Options::get_option( 'last_heartbeat', time() );
240
		$last_sync      = Jetpack_Options::get_option( 'last_sync', time() );
241
		if ( $envelopes && $last_heartbeat < $envelopes['response_time'] || $last_sync < $envelopes['response_time'] ) {
242
			$from_cache = true;
243
		}
244
245
		// otherwise, ask again
246
		if ( ! $from_cache ) {
247
			$wpcom_response = Jetpack_Client::wpcom_json_api_request_as_blog(
248
				$path,
249
				'2',
250
				array(
251
					'user_id'    => $user->ID,
252
					'user_roles' => implode( ',', $user->roles ),
253
				)
254
			);
255
256
			// silently fail...might be helpful to track it?
257
			if ( is_wp_error( $wpcom_response ) ) {
258
				return array();
259
			}
260
261
			$envelopes = json_decode( $wpcom_response['body'] );
262
263
			if ( ! is_array( $envelopes ) ) {
264
				return array();
265
			}
266
267
			$expiration                 = isset( $envelopes[0] ) ? $envelopes[0]->ttl : 300;
268
			$envelopes['response_time'] = time();
269
270
			set_transient( sha1( 'jetpack_jitm_' . $path ), $envelopes, $expiration );
271
		}
272
273
		$hidden_jitms = Jetpack_Options::get_option( 'hide_jitm' );
274
		unset( $envelopes['response_time'] );
275
276
		foreach ( $envelopes as $idx => &$envelope ) {
277
278
			$dismissed_feature = isset( $hidden_jitms[ $envelope->feature_class ] ) && is_array( $hidden_jitms[ $envelope->feature_class ] ) ? $hidden_jitms[ $envelope->feature_class ] : null;
279
280
			// if the this feature class has been dismissed and the request has not expired from the cache, skip it as it's been dismissed
281
			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...
282
				unset( $envelopes[ $idx ] );
283
				continue;
284
			}
285
286
			$normalized_site_url      = Jetpack::build_raw_urls( get_home_url() );
287
			$envelope->url            = 'https://jetpack.com/redirect/?source=jitm-' . $envelope->id . '&site=' . $normalized_site_url;
288
			$envelope->jitm_stats_url = Jetpack::build_stats_url( array( 'x_jetpack-jitm' => $envelope->id ) );
289
290
			if ( $envelope->CTA->hook ) {
291
				$envelope->url = apply_filters( 'jitm_' . $envelope->CTA->hook, $envelope->url ) ;
292
				unset( $envelope->CTA->hook );
293
			}
294
295
			if ( isset( $envelope->content->hook ) ) {
296
				$envelope->content = apply_filters( 'jitm_' . $envelope->content->hook, $envelope->content );
297
				unset( $envelope->content->hook );
298
			}
299
300
			// no point in showing an empty message
301
			if ( empty( $envelope->content->message ) ) {
302
				return array();
303
			}
304
305
			switch ( $envelope->content->icon ) {
306
				case 'jetpack':
307
					$envelope->content->icon = '<div class="jp-emblem">' . Jetpack::get_jp_emblem() . '</div>';
308
					break;
309
				case 'woocommerce':
310
					$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">
311
					.st0{clip-path:url(#SVGID_2_);enable-background:new    ;}
312
					.st1{clip-path:url(#SVGID_4_);}
313
					.st2{clip-path:url(#SVGID_6_);}
314
					.st3{clip-path:url(#SVGID_8_);fill:#8F567F;}
315
					.st4{clip-path:url(#SVGID_10_);fill:#FFFFFE;}
316
					.st5{clip-path:url(#SVGID_12_);fill:#FFFFFE;}
317
					.st6{clip-path:url(#SVGID_14_);fill:#FFFFFE;}
318
				</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>';
319
					break;
320
				default:
321
					$envelope->content->icon = '';
322
					break;
323
			}
324
325
			$jetpack = Jetpack::init();
326
			$jetpack->stat( 'jitm', $envelope->id . '-viewed-' . JETPACK__VERSION );
327
			$jetpack->do_stats( 'server_side' );
328
		}
329
330
		return $envelopes;
331
	}
332
}
333
if (
334
	/**
335
	 * Filter to turn off all just in time messages
336
	 *
337
	 * @since 3.7.0
338
	 *
339
	 * @param bool true Whether to show just in time messages.
340
	 */
341
	apply_filters( 'jetpack_just_in_time_msgs', false )
342
) {
343
	Jetpack_JITM::init();
344
}
345