Completed
Push — add/changelog-77 ( fc32a5...198265 )
by Jeremy
26:28 queued 19:02
created

Jetpack_Contact_Info_Widget::update_goodmap()   B

Complexity

Conditions 8
Paths 3

Size

Total Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 8
nc 3
nop 2
dl 0
loc 26
rs 8.4444
c 0
b 0
f 0
1
<?php
2
3
use Automattic\Jetpack\Assets;
4
5
if ( ! class_exists( 'Jetpack_Contact_Info_Widget' ) ) {
6
7
	//register Contact_Info_Widget widget
8
	function jetpack_contact_info_widget_init() {
9
		register_widget( 'Jetpack_Contact_Info_Widget' );
10
	}
11
12
	add_action( 'widgets_init', 'jetpack_contact_info_widget_init' );
13
14
	/**
15
	 * Makes a custom Widget for displaying Restaurant Location/Map, Hours, and Contact Info available.
16
	 *
17
	 * @package WordPress
18
	 */
19
	class Jetpack_Contact_Info_Widget extends WP_Widget {
20
21
		/**
22
		 * Constructor
23
		 */
24
		function __construct() {
25
			global $pagenow;
26
27
			$widget_ops = array(
28
				'classname'                   => 'widget_contact_info',
29
				'description'                 => __( 'Display a map with your location, hours, and contact information.', 'jetpack' ),
30
				'customize_selective_refresh' => true,
31
			);
32
			parent::__construct(
33
				'widget_contact_info',
34
				/** This filter is documented in modules/widgets/facebook-likebox.php */
35
				apply_filters( 'jetpack_widget_name', __( 'Contact Info & Map', 'jetpack' ) ),
36
				$widget_ops
37
			);
38
			$this->alt_option_name = 'widget_contact_info';
39
40 View Code Duplication
			if ( is_customize_preview() ) {
41
				add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
42
			} elseif ( 'widgets.php' === $pagenow ) {
43
				add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
44
			}
45
46
			add_action( 'wp_ajax_customize-contact-info-api-key', array( $this, 'ajax_check_api_key' ) );
47
		}
48
49
		/**
50
		 * Enqueue scripts and styles.
51
		 */
52
		public function enqueue_scripts() {
53
			wp_enqueue_style(
54
				'contact-info-map-css',
55
				plugins_url( 'contact-info/contact-info-map.css', __FILE__ ),
56
				array(),
57
				JETPACK__VERSION
58
			);
59
		}
60
61
62
		/**
63
		 * Return an associative array of default values
64
		 *
65
		 * These values are used in new widgets.
66
		 *
67
		 * @return array Array of default values for the Widget's options
68
		 */
69
		public function defaults() {
70
			return array(
71
				'title'   => __( 'Hours & Info', 'jetpack' ),
72
				'address' => __( "3999 Mission Boulevard,\nSan Diego CA 92109", 'jetpack' ),
73
				'phone'   => _x( '1-202-555-1212', 'Example of a phone number', 'jetpack' ),
74
				'hours'   => __( "Lunch: 11am - 2pm \nDinner: M-Th 5pm - 11pm, Fri-Sat:5pm - 1am", 'jetpack' ),
75
				'email'   => null,
76
				'showmap' => 0,
77
				'apikey'  => null,
78
				'goodmap' => null,
79
			);
80
		}
81
82
		/**
83
		 * Outputs the HTML for this widget.
84
		 *
85
		 * @param array $args     An array of standard parameters for widgets in this theme
86
		 * @param array $instance An array of settings for this widget instance
87
		 *
88
		 * @return void Echoes it's output
89
		 **/
90
		function widget( $args, $instance ) {
91
			$instance = wp_parse_args( $instance, $this->defaults() );
92
93
			echo $args['before_widget'];
94
95
			if ( '' != $instance['title'] ) {
96
				echo $args['before_title'] . $instance['title'] . $args['after_title'];
97
			}
98
99
			/**
100
			 * Fires at the beginning of the Contact Info widget, after the title.
101
			 *
102
			 * @module widgets
103
			 *
104
			 * @since 3.9.2
105
			 */
106
			do_action( 'jetpack_contact_info_widget_start' );
107
108
			echo '<div itemscope itemtype="http://schema.org/LocalBusiness">';
109
110
			if ( '' != $instance['address'] ) {
111
112
				$showmap = $instance['showmap'];
113
				$goodmap = isset( $instance['goodmap'] ) ? $instance['goodmap'] : $this->has_good_map( $instance );
114
115
				if ( $showmap && true === $goodmap ) {
116
					/**
117
					 * Set a Google Maps API Key.
118
					 *
119
					 * @since 4.1.0
120
					 *
121
					 * @param string $api_key Google Maps API Key
122
					 */
123
					$api_key = apply_filters( 'jetpack_google_maps_api_key', $instance['apikey'] );
124
					echo $this->build_map( $instance['address'], $api_key );
125
				} elseif ( $showmap && is_customize_preview() && true !== $goodmap ) {
126
					printf(
127
						'<span class="contact-map-api-error" style="display: block;">%s</span>',
128
						esc_html( $instance['goodmap'] )
129
					);
130
				}
131
132
				$map_link = $this->build_map_link( $instance['address'] );
133
134
				echo '<div class="confit-address" itemscope itemtype="http://schema.org/PostalAddress" itemprop="address"><a href="' . esc_url( $map_link ) . '" target="_blank">' . str_replace( "\n", '<br/>', esc_html( $instance['address'] ) ) . '</a></div>';
135
			}
136
137
			if ( '' != $instance['phone'] ) {
138
				if ( wp_is_mobile() ) {
139
					echo '<div class="confit-phone"><span itemprop="telephone"><a href="' . esc_url( 'tel:' . $instance['phone'] ) . '">' . esc_html( $instance['phone'] ) . '</a></span></div>';
140
				} else {
141
					echo '<div class="confit-phone"><span itemprop="telephone">' . esc_html( $instance['phone'] ) . '</span></div>';
142
				}
143
			}
144
145
			if ( is_email( trim( $instance['email'] ) ) ) {
146
				printf(
147
					'<div class="confit-email"><a href="mailto:%1$s">%1$s</a></div>',
148
					esc_html( $instance['email'] )
149
				);
150
			}
151
152
			if ( '' != $instance['hours'] ) {
153
				echo '<div class="confit-hours" itemprop="openingHours">' . str_replace( "\n", '<br/>', esc_html( $instance['hours'] ) ) . '</div>';
154
			}
155
156
			echo '</div>';
157
158
			/**
159
			 * Fires at the end of Contact Info widget.
160
			 *
161
			 * @module widgets
162
			 *
163
			 * @since 3.9.2
164
			 */
165
			do_action( 'jetpack_contact_info_widget_end' );
166
167
			echo $args['after_widget'];
168
169
			/** This action is documented in modules/widgets/gravatar-profile.php */
170
			do_action( 'jetpack_stats_extra', 'widget_view', 'contact_info' );
171
		}
172
173
174
		/**
175
		 * Deals with the settings when they are saved by the admin. Here is
176
		 * where any validation should be dealt with.
177
		 *
178
		 * @param array $new_instance New configuration values
179
		 * @param array $old_instance Old configuration values
180
		 *
181
		 * @return array
182
		 */
183
		function update( $new_instance, $old_instance ) {
184
185
			$instance            = array();
186
			$instance['title']   = wp_kses( $new_instance['title'], array() );
187
			$instance['address'] = wp_kses( $new_instance['address'], array() );
188
			$instance['phone']   = wp_kses( $new_instance['phone'], array() );
189
			$instance['email']   = wp_kses( $new_instance['email'], array() );
190
			$instance['hours']   = wp_kses( $new_instance['hours'], array() );
191
			$instance['apikey']  = wp_kses( isset( $new_instance['apikey'] ) ? $new_instance['apikey'] : $old_instance['apikey'], array() );
192
193
			if ( ! isset( $new_instance['showmap'] ) ) {
194
				$instance['showmap'] = 0;
195
			} else {
196
				$instance['showmap'] = intval( $new_instance['showmap'] );
197
			}
198
199
			$instance['goodmap'] = $this->update_goodmap( $old_instance, $instance );
200
201
			return $instance;
202
		}
203
204
205
		/**
206
		 * Displays the form for this widget on the Widgets page of the WP Admin area.
207
		 *
208
		 * @param array $instance Instance configuration.
209
		 *
210
		 * @return void
211
		 */
212
		function form( $instance ) {
213
			$instance = wp_parse_args( $instance, $this->defaults() );
214
			/** This filter is documented in modules/widgets/contact-info.php */
215
			$apikey = apply_filters( 'jetpack_google_maps_api_key', $instance['apikey'] );
216
217
			wp_enqueue_script(
218
				'contact-info-admin',
219
				Assets::get_file_url_for_environment(
220
					'_inc/build/widgets/contact-info/contact-info-admin.min.js',
221
					'modules/widgets/contact-info/contact-info-admin.js'
222
				),
223
				array( 'jquery' ),
224
				20160727
225
			);
226
227
			if ( is_customize_preview() ) {
228
				$customize_contact_info_api_key_nonce = wp_create_nonce( 'customize_contact_info_api_key' );
229
				wp_localize_script(
230
					'contact-info-admin',
231
					'contact_info_api_key_ajax_obj',
232
					array( 'nonce' => $customize_contact_info_api_key_nonce )
233
				);
234
			}
235
236
			?>
237
			<p>
238
				<label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php esc_html_e( 'Title:', 'jetpack' ); ?></label>
239
				<input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" type="text" value="<?php echo esc_attr( $instance['title'] ); ?>" />
240
			</p>
241
242
			<p>
243
				<label for="<?php echo esc_attr( $this->get_field_id( 'address' ) ); ?>"><?php esc_html_e( 'Address:', 'jetpack' ); ?></label>
244
				<textarea class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'address' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'address' ) ); ?>"><?php echo esc_textarea( $instance['address'] ); ?></textarea>
245
246
				<input class="jp-contact-info-showmap" id="<?php echo esc_attr( $this->get_field_id( 'showmap' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'showmap' ) ); ?>" value="1" type="checkbox" <?php checked( $instance['showmap'], 1 ); ?> />
247
				<label for="<?php echo esc_attr( $this->get_field_id( 'showmap' ) ); ?>"><?php esc_html_e( 'Show map', 'jetpack' ); ?></label>
248
			</p>
249
250
			<p class="jp-contact-info-admin-map" style="<?php echo $instance['showmap'] ? '' : 'display: none;'; ?>">
251
				<label for="<?php echo esc_attr( $this->get_field_id( 'apikey' ) ); ?>">
252
					<?php _e( 'Google Maps API Key', 'jetpack' ); ?>
253
					<input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'apikey' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'apikey' ) ); ?>" type="text" value="<?php echo esc_attr( $apikey ); ?>" />
254
					<br />
255
					<small><?php printf( wp_kses( __( 'Google now requires an API key to use their maps on your site. <a href="%s">See our documentation</a> for instructions on acquiring a key.', 'jetpack' ), array( 'a' => array( 'href' => true ) ) ), 'https://jetpack.com/support/extra-sidebar-widgets/contact-info-widget/' ); ?></small>
256
				</label>
257
			</p>
258
259
			<p class="jp-contact-info-admin-map jp-contact-info-embed-map" style="<?php echo $instance['showmap'] ? '' : 'display: none;'; ?>">
260
				<?php
261
				if ( ! is_customize_preview() && true === $instance['goodmap'] ) {
262
					echo $this->build_map( $instance['address'], $apikey ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
263
				} elseif ( true !== $instance['goodmap'] && ! empty( $instance['goodmap'] ) ) {
264
					printf(
265
						'<span class="notice notice-warning" style="display: block;">%s</span>',
266
						esc_html( $instance['goodmap'] )
267
					);
268
				}
269
				?>
270
			</p>
271
272
			<p>
273
				<label for="<?php echo esc_attr( $this->get_field_id( 'phone' ) ); ?>"><?php esc_html_e( 'Phone:', 'jetpack' ); ?></label>
274
				<input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'phone' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'phone' ) ); ?>" type="text" value="<?php echo esc_attr( $instance['phone'] ); ?>" />
275
			</p>
276
277
			<p>
278
				<label for="<?php echo esc_attr( $this->get_field_id( 'email' ) ); ?>"><?php esc_html_e( 'Email Address:', 'jetpack' ); ?></label>
279
				<input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'email' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'email' ) ); ?>" type="text" value="<?php echo esc_attr( $instance['email'] ); ?>" />
280
			</p>
281
282
			<p>
283
				<label for="<?php echo esc_attr( $this->get_field_id( 'hours' ) ); ?>"><?php esc_html_e( 'Hours:', 'jetpack' ); ?></label>
284
				<textarea class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'hours' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'hours' ) ); ?>"><?php echo esc_textarea( $instance['hours'] ); ?></textarea>
285
			</p>
286
287
			<?php
288
		}
289
290
291
		/**
292
		 * Generate a Google Maps link for the supplied address.
293
		 *
294
		 * @param string $address Address to link to.
295
		 *
296
		 * @return string
297
		 */
298
		function build_map_link( $address ) {
299
			// Google map urls have lots of available params but zoom (z) and query (q) are enough.
300
			return 'https://maps.google.com/maps?z=16&q=' . $this->urlencode_address( $address );
301
		}
302
303
304
		/**
305
		 * Builds map display HTML code from the supplied address.
306
		 *
307
		 * @param string $address Address.
308
		 * @param string $api_key API Key.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $api_key not be string|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...
309
		 *
310
		 * @return string HTML of the map.
311
		 */
312
		function build_map( $address, $api_key = null ) {
313
			$this->enqueue_scripts();
314
			$src = add_query_arg( 'q', rawurlencode( $address ), 'https://www.google.com/maps/embed/v1/place' );
315
			if ( ! empty( $api_key ) ) {
316
				$src = add_query_arg( 'key', $api_key, $src );
317
			}
318
319
			$height = 216;
320
321
			$iframe_attributes = sprintf(
322
				' height="%d" frameborder="0" src="%s" class="contact-map"',
323
				esc_attr( $height ),
324
				esc_url( $src )
325
			);
326
327
			$iframe_html = sprintf( '<iframe width="600" %s></iframe>', $iframe_attributes );
328
329
			if (
330
				! class_exists( 'Jetpack_AMP_Support' )
331
				|| ! Jetpack_AMP_Support::is_amp_request()
332
			) {
333
				return $iframe_html;
334
			}
335
336
			$amp_iframe_html = sprintf( '<amp-iframe layout="fixed-height" width="auto" sandbox="allow-scripts allow-same-origin" %s>', $iframe_attributes );
337
338
			// Add placeholder to avoid AMP error: <amp-iframe> elements must be positioned outside the first 75% of the viewport or 600px from the top (whichever is smaller).
339
			$amp_iframe_html .= sprintf( '<span placeholder>%s</span>', esc_html__( 'Loading map&hellip;', 'jetpack' ) );
340
341
			// Add original iframe as fallback in case JavaScript is disabled.
342
			$amp_iframe_html .= sprintf( '<noscript>%s</noscript>', $iframe_html );
343
344
			$amp_iframe_html .= '</amp-iframe>';
345
			return $amp_iframe_html;
346
		}
347
348
		/**
349
		 * Encode an URL
350
		 *
351
		 * @param string $address The URL to encode
352
		 *
353
		 * @return string The encoded URL
354
		 */
355
		function urlencode_address( $address ) {
356
357
			$address = strtolower( $address );
358
			$address = preg_replace( '/\s+/', ' ', trim( $address ) ); // Get rid of any unwanted whitespace
359
			$address = str_ireplace( ' ', '+', $address ); // Use + not %20
360
			return urlencode( $address );
361
		}
362
363
		/**
364
		 * Returns the instance's updated 'goodmap' value.
365
		 *
366
		 * @param array $old_instance Old configuration values.
367
		 * @param array $instance Current configuration values.
368
		 *
369
		 * @return bool|string The instance's updated 'goodmap' value. The value is true if
370
		 * $instance can display a good map. If not, returns an error message.
371
		 */
372
		function update_goodmap( $old_instance, $instance ) {
373
			/*
374
			 * If we have no address or don't want to show a map,
375
			 * no need to check if the map is valid.
376
			 */
377
			if ( empty( $instance['address'] ) || 0 === $instance['showmap'] ) {
378
				return false;
379
			}
380
381
			/*
382
			 * If there have been any changes that may impact the map in the widget
383
			 * (adding an address, address changes, new API key, API key change)
384
			 * then we want to check whether our map can be displayed again.
385
			 */
386
			if (
387
				! isset( $instance['goodmap'] )
388
				|| ! isset( $old_instance['address'] )
389
				|| $this->urlencode_address( $old_instance['address'] ) !== $this->urlencode_address( $instance['address'] )
390
				|| ! isset( $old_instance['apikey'] )
391
				|| $old_instance['apikey'] !== $instance['apikey']
392
			) {
393
				return $this->has_good_map( $instance );
394
			} else {
395
				return $instance['goodmap'];
396
			}
397
		}
398
399
		/**
400
		 * Check if the instance has a valid Map location.
401
		 *
402
		 * @param array $instance Widget instance configuration.
403
		 *
404
		 * @return bool|string Whether or not there is a valid map. If not, return an error message.
405
		 */
406
		function has_good_map( $instance ) {
407
			/** This filter is documented in modules/widgets/contact-info.php */
408
			$api_key = apply_filters( 'jetpack_google_maps_api_key', $instance['apikey'] );
409
			if ( ! empty( $api_key ) ) {
410
				$path     = add_query_arg(
411
					array(
412
						'q'   => rawurlencode( $instance['address'] ),
413
						'key' => $api_key,
414
					),
415
					'https://www.google.com/maps/embed/v1/place'
416
				);
417
				$response = wp_remote_get( esc_url_raw( $path ) );
418
419
				if ( 200 === wp_remote_retrieve_response_code( $response ) ) {
420
					return true;
421
				} else {
422
					return wp_remote_retrieve_body( $response );
423
				}
424
			}
425
426
			return __( 'Please enter a valid Google API Key.', 'jetpack' );
427
		}
428
429
		/**
430
		 * Check the Google Maps API key after an Ajax call from the widget's admin form in
431
		 * the Customizer preview.
432
		 */
433
		function ajax_check_api_key() {
434
			if ( isset( $_POST['apikey'] ) ) {
435
				if ( check_ajax_referer( 'customize_contact_info_api_key' ) && current_user_can( 'customize' ) ) {
436
					$apikey = wp_kses( $_POST['apikey'], array() );
437
					$default_instance = $this->defaults();
438
					$default_instance['apikey'] = $apikey;
439
					wp_send_json( array( 'result' => esc_html( $this->has_good_map( $default_instance ) ) ) );
440
				}
441
			} else {
442
				wp_die();
443
			}
444
		}
445
446
	}
447
448
}
449