PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3
Jetpack – WP Security, Backup, Speed, & Growth v16.3
16.3 16.3-beta 16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 All 508 releases
← All changes | modules/widgets/milestone/class-milestone-widget.php +758 -0 16.2-beta → 16.3 View file →
@@ -1,0 +1,758 @@
1 +<?php
2 +/**
3 + * Milestone Countdown Widget
4 + *
5 + * @package automattic/jetpack
6 + */
7 +
8 +use Automattic\Jetpack\Assets;
9 +
10 +if ( ! defined( 'ABSPATH' ) ) {
11 + exit( 0 );
12 +}
13 +
14 +/**
15 + * Class Milestone_Widget
16 + */
17 +class Milestone_Widget extends WP_Widget {
18 + /**
19 + * Holding array for widget configuration and localization.
20 + *
21 + * @var array
22 + */
23 + private static $config_js = array();
24 +
25 + /**
26 + * Available time units sorted in descending order.
27 + *
28 + * @var Array
29 + */
30 + protected $available_units = array(
31 + 'years',
32 + 'months',
33 + 'days',
34 + 'hours',
35 + 'minutes',
36 + 'seconds',
37 + );
38 +
39 + /**
40 + * Milestone_Widget constructor.
41 + */
42 + public function __construct() {
43 + $widget = array(
44 + 'classname' => 'milestone-widget',
45 + 'description' => __( 'Display a countdown to a certain date.', 'jetpack' ),
46 + );
47 +
48 + parent::__construct(
49 + 'Milestone_Widget',
50 + /** This filter is documented in modules/widgets/facebook-likebox.php */
51 + apply_filters( 'jetpack_widget_name', __( 'Milestone', 'jetpack' ) ),
52 + $widget
53 + );
54 +
55 + add_action( 'wp_enqueue_scripts', array( __CLASS__, 'enqueue_template' ) );
56 + add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_admin' ) );
57 + add_action( 'wp_footer', array( $this, 'localize_script' ) );
58 +
59 + if ( is_active_widget( false, false, $this->id_base, true ) || is_active_widget( false, false, 'monster', true ) || is_customize_preview() ) {
60 + add_action( 'wp_head', array( __CLASS__, 'styles_template' ) );
61 + }
62 + }
63 +
64 + /**
65 + * Enqueue admin assets.
66 + *
67 + * @param string $hook_suffix Hook suffix provided by WordPress.
68 + */
69 + public static function enqueue_admin( $hook_suffix ) {
70 + if ( 'widgets.php' === $hook_suffix ) {
71 + wp_enqueue_style( 'milestone-admin', plugin_dir_url( __FILE__ ) . 'style-admin.css', array(), '20201113' );
72 + wp_enqueue_script(
73 + 'milestone-admin-js',
74 + Assets::get_file_url_for_environment(
75 + '_inc/build/widgets/milestone/admin.min.js',
76 + 'modules/widgets/milestone/admin.js'
77 + ),
78 + array( 'jquery' ),
79 + '20201113',
80 + true
81 + );
82 + }
83 + }
84 +
85 + /**
86 + * Enqueue the frontend JS.
87 + */
88 + public static function enqueue_template() {
89 + if ( class_exists( 'Jetpack_AMP_Support' ) && Jetpack_AMP_Support::is_amp_request() ) {
90 + return;
91 + }
92 +
93 + wp_enqueue_script(
94 + 'milestone',
95 + Assets::get_file_url_for_environment(
96 + '_inc/build/widgets/milestone/milestone.min.js',
97 + 'modules/widgets/milestone/milestone.js'
98 + ),
99 + array(),
100 + '20201113',
101 + true
102 + );
103 + }
104 +
105 + /**
106 + * Output the frontend styling.
107 + */
108 + public static function styles_template() {
109 + global $themecolors;
110 + $colors = wp_parse_args(
111 + $themecolors,
112 + array(
113 + 'bg' => 'ffffff',
114 + 'border' => 'cccccc',
115 + 'text' => '333333',
116 + )
117 + );
118 + ?>
119 +<style>
120 +.milestone-widget {
121 + --milestone-text-color: <?php echo self::sanitize_color_hex( $colors['text'] ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>;
122 + --milestone-bg-color: <?php echo self::sanitize_color_hex( $colors['bg'] ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>;
123 + --milestone-border-color:<?php echo self::sanitize_color_hex( $colors['border'] ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>;
124 +}
125 +</style>
126 + <?php
127 + }
128 +
129 + /**
130 + * Ensure that a string representing a color in hexadecimal
131 + * notation is safe for use in css and database saves.
132 + *
133 + * @param string $hex Hexademical code to sanitize.
134 + * @param string $prefix Prefix for the hex code.
135 + *
136 + * @return string Color in hexadecimal notation on success - the string "transparent" otherwise.
137 + */
138 + public static function sanitize_color_hex( $hex, $prefix = '#' ) {
139 + $hex = trim( $hex );
140 +
141 + /* Strip recognized prefixes. */
142 + if ( str_starts_with( $hex, '#' ) ) {
143 + $hex = substr( $hex, 1 );
144 + } elseif ( str_starts_with( $hex, '%23' ) ) {
145 + $hex = substr( $hex, 3 );
146 + }
147 +
148 + if ( 0 !== preg_match( '/^[0-9a-fA-F]{6}$/', $hex ) ) {
149 + return $prefix . $hex;
150 + }
151 +
152 + return 'transparent';
153 + }
154 +
155 + /**
156 + * Localize Front-end Script.
157 + *
158 + * Print the javascript configuration array only if the
159 + * current template has an instance of the widget that
160 + * is still counting down. In all other cases, this
161 + * function will dequeue milestone.js.
162 + *
163 + * Hooks into the "wp_footer" action.
164 + */
165 + public function localize_script() {
166 + if ( class_exists( 'Jetpack_AMP_Support' ) && Jetpack_AMP_Support::is_amp_request() ) {
167 + return;
168 + }
169 +
170 + if ( empty( self::$config_js['instances'] ) ) {
171 + wp_dequeue_script( 'milestone' );
172 + return;
173 + }
174 + self::$config_js['api_root'] = esc_url_raw( rest_url() );
175 + wp_localize_script( 'milestone', 'MilestoneConfig', self::$config_js );
176 + }
177 +
178 + /**
179 + * Return an associative array of default values
180 + *
181 + * These values are used in new widgets.
182 + *
183 + * @return array Array of default values for the Widget's options.
184 + */
185 + public function defaults() {
186 + $now = current_datetime();
187 + $now_timestamp = $now->getTimestamp();
188 +
189 + return array(
190 + 'title' => '',
191 + 'event' => __( 'The Big Day', 'jetpack' ),
192 + 'unit' => 'automatic',
193 + 'type' => 'until',
194 + 'message' => __( 'The big day is here.', 'jetpack' ),
195 + 'day' => gmdate( 'd', $now_timestamp ),
196 + 'month' => gmdate( 'm', $now_timestamp ),
197 + 'year' => gmdate( 'Y', $now_timestamp ),
198 + 'hour' => 0,
199 + 'min' => 0,
200 + );
201 + }
202 +
203 + /**
204 + * Widget
205 + *
206 + * @param array $args Widget args.
207 + * @param array $instance Widget instance.
208 + */
209 + public function widget( $args, $instance ) {
210 + $instance = wp_parse_args( $instance, $this->defaults() );
211 +
212 + $this->enqueue_scripts();
213 +
214 + echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
215 +
216 + /** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */
217 + $title = apply_filters( 'widget_title', $instance['title'] );
218 + if ( ! empty( $title ) ) {
219 + echo $args['before_title'] . $title . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
220 + }
221 +
222 + $widget_id = ! empty( $args['widget_id'] ) ? $args['widget_id'] : 'milestone_widget';
223 + $data = $this->get_widget_data( $instance );
224 + $config = array(
225 + 'id' => $widget_id,
226 + 'message' => $data['message'],
227 + 'refresh' => $data['refresh'],
228 + );
229 +
230 + /*
231 + * Sidebars may be configured to not expose the `widget_id`. Example: `twentytwenty` footer areas.
232 + *
233 + * We need our own unique identifier.
234 + */
235 + $config['content_id'] = $widget_id . '-content';
236 +
237 + self::$config_js['instances'][] = $config;
238 +
239 + printf( '<div id="%s" class="milestone-content">', esc_html( $config['content_id'] ) );
240 +
241 + echo '<div class="milestone-header">';
242 + echo '<strong class="event">' . esc_html( $instance['event'] ) . '</strong>';
243 + echo '<span class="date">' . esc_html( date_i18n( get_option( 'date_format' ), $data['milestone'] ) ) . '</span>';
244 + echo '</div>';
245 +
246 + echo wp_kses_post( $data['message'] );
247 +
248 + echo '</div><!--milestone-content-->';
249 +
250 + echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
251 +
252 + /** This action is documented in modules/widgets/gravatar-profile.php */
253 + do_action( 'jetpack_stats_extra', 'widget_view', 'milestone' );
254 + }
255 +
256 + /**
257 + * Enqueue widget styles
258 + */
259 + public function enqueue_scripts() {
260 + wp_enqueue_style(
261 + 'milestone-widget',
262 + plugins_url( 'milestone-widget.css', __FILE__ ),
263 + array(),
264 + JETPACK__VERSION
265 + );
266 + }
267 +
268 + /**
269 + * Getter for the widget data.
270 + *
271 + * @param array $instance Widget instance.
272 + *
273 + * @return array
274 + */
275 + public function get_widget_data( $instance ) {
276 + $data = array();
277 +
278 + $instance = $this->sanitize_instance( $instance );
279 +
280 + $milestone = mktime( $instance['hour'], $instance['min'], 0, $instance['month'], $instance['day'], $instance['year'] );
281 + $now = (int) current_time( 'timestamp' ); // phpcs:ignore WordPress.DateTime.CurrentTimeTimestamp.Requested
282 + $type = $instance['type'];
283 +
284 + if ( 'since' === $type ) {
285 + $diff = (int) floor( $now - $milestone );
286 + } else {
287 + $diff = (int) floor( $milestone - $now );
288 + }
289 +
290 + $data['diff'] = $diff;
291 + $data['unit'] = $this->get_unit( $diff, $instance['unit'] );
292 +
293 + // Setting the refresh counter to equal the number of seconds it takes to flip a unit.
294 + $refresh_intervals = array(
295 + 0, // should be YEAR_IN_SECONDS, but doing setTimeout for a year doesn't seem to be logical.
296 + 0, // same goes for MONTH_IN_SECONDS.
297 + DAY_IN_SECONDS,
298 + HOUR_IN_SECONDS,
299 + MINUTE_IN_SECONDS,
300 + 1,
301 + );
302 +
303 + $data['refresh'] = $refresh_intervals[ array_search( $data['unit'], $this->available_units, true ) ];
304 + $data['milestone'] = $milestone;
305 +
306 + if ( ( 1 > $diff ) && ( 'until' === $type ) ) {
307 + $data['message'] = '<div class="milestone-message">' . $instance['message'] . '</div>';
308 + $data['refresh'] = 0; // No need to refresh, the milestone has been reached.
309 + } else {
310 + $interval_text = $this->get_interval_in_units( $diff, $data['unit'] );
311 + $interval = (int) $interval_text;
312 +
313 + if ( 'since' === $type ) {
314 +
315 + switch ( $data['unit'] ) {
316 + case 'years':
317 + $data['message'] = sprintf(
318 + /* translators: %s is the number of year(s). */
319 + _n(
320 + '<span class="difference">%s</span> <span class="label">year ago.</span>',
321 + '<span class="difference">%s</span> <span class="label">years ago.</span>',
322 + $interval,
323 + 'jetpack'
324 + ),
325 + $interval_text
326 + );
327 + break;
328 + case 'months':
329 + $data['message'] = sprintf(
330 + /* translators: %s is the number of month(s). */
331 + _n(
332 + '<span class="difference">%s</span> <span class="label">month ago.</span>',
333 + '<span class="difference">%s</span> <span class="label">months ago.</span>',
334 + $interval,
335 + 'jetpack'
336 + ),
337 + $interval_text
338 + );
339 + break;
340 + case 'days':
341 + $data['message'] = sprintf(
342 + /* translators: %s is the number of days(s). */
343 + _n(
344 + '<span class="difference">%s</span> <span class="label">day ago.</span>',
345 + '<span class="difference">%s</span> <span class="label">days ago.</span>',
346 + $interval,
347 + 'jetpack'
348 + ),
349 + $interval_text
350 + );
351 + break;
352 + case 'hours':
353 + $data['message'] = sprintf(
354 + /* translators: %s is the number of hours(s). */
355 + _n(
356 + '<span class="difference">%s</span> <span class="label">hour ago.</span>',
357 + '<span class="difference">%s</span> <span class="label">hours ago.</span>',
358 + $interval,
359 + 'jetpack'
360 + ),
361 + $interval_text
362 + );
363 + break;
364 + case 'minutes':
365 + $data['message'] = sprintf(
366 + /* translators: %s is the number of minutes(s). */
367 + _n(
368 + '<span class="difference">%s</span> <span class="label">minute ago.</span>',
369 + '<span class="difference">%s</span> <span class="label">minutes ago.</span>',
370 + $interval,
371 + 'jetpack'
372 + ),
373 + $interval_text
374 + );
375 + break;
376 + case 'seconds':
377 + $data['message'] = sprintf(
378 + /* translators: %s is the number of second(s). */
379 + _n(
380 + '<span class="difference">%s</span> <span class="label">second ago.</span>',
381 + '<span class="difference">%s</span> <span class="label">seconds ago.</span>',
382 + $interval,
383 + 'jetpack'
384 + ),
385 + $interval_text
386 + );
387 + break;
388 + }
389 + } else {
390 + switch ( $this->get_unit( $diff, $instance['unit'] ) ) {
391 + case 'years':
392 + $data['message'] = sprintf(
393 + /* translators: %s is the number of year(s). */
394 + _n(
395 + '<span class="difference">%s</span> <span class="label">year to go.</span>',
396 + '<span class="difference">%s</span> <span class="label">years to go.</span>',
397 + $interval,
398 + 'jetpack'
399 + ),
400 + $interval_text
401 + );
402 + break;
403 + case 'months':
404 + $data['message'] = sprintf(
405 + /* translators: %s is the number of month(s). */
406 + _n(
407 + '<span class="difference">%s</span> <span class="label">month to go.</span>',
408 + '<span class="difference">%s</span> <span class="label">months to go.</span>',
409 + $interval,
410 + 'jetpack'
411 + ),
412 + $interval_text
413 + );
414 + break;
415 + case 'days':
416 + $data['message'] = sprintf(
417 + /* translators: %s is the number of days(s). */
418 + _n(
419 + '<span class="difference">%s</span> <span class="label">day to go.</span>',
420 + '<span class="difference">%s</span> <span class="label">days to go.</span>',
421 + $interval,
422 + 'jetpack'
423 + ),
424 + $interval_text
425 + );
426 + break;
427 + case 'hours':
428 + $data['message'] = sprintf(
429 + /* translators: %s is the number of hour(s). */
430 + _n(
431 + '<span class="difference">%s</span> <span class="label">hour to go.</span>',
432 + '<span class="difference">%s</span> <span class="label">hours to go.</span>',
433 + $interval,
434 + 'jetpack'
435 + ),
436 + $interval_text
437 + );
438 + break;
439 + case 'minutes':
440 + $data['message'] = sprintf(
441 + /* translators: %s is the number of minute(s). */
442 + _n(
443 + '<span class="difference">%s</span> <span class="label">minute to go.</span>',
444 + '<span class="difference">%s</span> <span class="label">minutes to go.</span>',
445 + $interval,
446 + 'jetpack'
447 + ),
448 + $interval_text
449 + );
450 + break;
451 + case 'seconds':
452 + $data['message'] = sprintf(
453 + /* translators: %s is the number of second(s). */
454 + _n(
455 + '<span class="difference">%s</span> <span class="label">second to go.</span>',
456 + '<span class="difference">%s</span> <span class="label">seconds to go.</span>',
457 + $interval,
458 + 'jetpack'
459 + ),
460 + $interval_text
461 + );
462 + break;
463 + }
464 + }
465 + $data['message'] = '<div class="milestone-countdown">' . $data['message'] . '</div>';
466 + }
467 +
468 + return $data;
469 + }
470 +
471 + /**
472 + * Return the largest possible time unit that the difference will be displayed in.
473 + *
474 + * @param Integer $seconds the interval in seconds.
475 + * @param String $maximum_unit the maximum unit that will be used. Optional.
476 + * @return String $calculated_unit
477 + */
478 + protected function get_unit( $seconds, $maximum_unit = 'automatic' ) {
479 + $unit = '';
480 +
481 + if ( $seconds >= YEAR_IN_SECONDS * 2 ) {
482 + // more than 2 years - show in years, one decimal point.
483 + $unit = 'years';
484 +
485 + } elseif ( $seconds >= YEAR_IN_SECONDS ) {
486 + if ( 'years' === $maximum_unit ) {
487 + $unit = 'years';
488 + } else {
489 + // automatic mode - showing months even if it's between one and two years.
490 + $unit = 'months';
491 + }
492 + } elseif ( $seconds >= MONTH_IN_SECONDS * 3 ) {
493 + // fewer than 2 years - show in months.
494 + $unit = 'months';
495 +
496 + } elseif ( $seconds >= MONTH_IN_SECONDS ) {
497 + if ( 'months' === $maximum_unit ) {
498 + $unit = 'months';
499 + } else {
500 + // automatic mode - showing days even if it's between one and three months.
501 + $unit = 'days';
502 + }
503 + } elseif ( $seconds >= DAY_IN_SECONDS - 1 ) {
504 + // fewer than a month - show in days.
505 + $unit = 'days';
506 +
507 + } elseif ( $seconds >= HOUR_IN_SECONDS - 1 ) {
508 + // less than 1 day - show in hours.
509 + $unit = 'hours';
510 +
511 + } elseif ( $seconds >= MINUTE_IN_SECONDS - 1 ) {
512 + // less than 1 hour - show in minutes.
513 + $unit = 'minutes';
514 +
515 + } else {
516 + // less than 1 minute - show in seconds.
517 + $unit = 'seconds';
518 + }
519 +
520 + $maximum_unit_index = array_search( $maximum_unit, $this->available_units, true );
521 + $unit_index = array_search( $unit, $this->available_units, true );
522 +
523 + if (
524 + false === $maximum_unit_index // the maximum unit parameter is automatic.
525 + || $unit_index > $maximum_unit_index // there is not enough seconds for even one maximum time unit.
526 + ) {
527 + return $unit;
528 + }
529 + return $maximum_unit;
530 + }
531 +
532 + /**
533 + * Returns a time difference value in specified units.
534 + *
535 + * @param int $seconds Number of seconds.
536 + * @param string $units Unit.
537 + * @return int $time_in_units.
538 + */
539 + protected function get_interval_in_units( $seconds, $units ) {
540 + switch ( $units ) {
541 + case 'years':
542 + $years = $seconds / YEAR_IN_SECONDS;
543 + $decimals = abs( round( $years, 1 ) - round( $years ) ) > 0 ? 1 : 0;
544 + return number_format_i18n( $years, $decimals );
545 + case 'months':
546 + return (int) ( $seconds / 60 / 60 / 24 / 30 );
547 + case 'days':
548 + return (int) ( $seconds / 60 / 60 / 24 + 1 );
549 + case 'hours':
550 + return (int) ( $seconds / 60 / 60 );
551 + case 'minutes':
552 + return (int) ( $seconds / 60 + 1 );
553 + default:
554 + return $seconds;
555 + }
556 + }
557 +
558 + /**
559 + * Update widget.
560 + *
561 + * @param array $new_instance New instance of the widget being saved.
562 + * @param array $old_instance Previous instance being saved over.
563 + *
564 + * @return array
565 + */
566 + public function update( $new_instance, $old_instance ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
567 + return $this->sanitize_instance( $new_instance );
568 + }
569 +
570 + /**
571 + * Make sure that a number is within a certain range.
572 + * If the number is too small it will become the possible lowest value.
573 + * If the number is too large it will become the possible highest value.
574 + *
575 + * @param int $n The number to check.
576 + * @param int $floor The lowest possible value.
577 + * @param int $ceil The highest possible value.
578 + */
579 + public function sanitize_range( $n, $floor, $ceil ) {
580 + $n = (int) $n;
581 + if ( $n < $floor ) {
582 + $n = $floor;
583 + } elseif ( $n > $ceil ) {
584 + $n = $ceil;
585 + }
586 + return $n;
587 + }
588 +
589 + /**
590 + * Sanitize an instance of this widget.
591 + *
592 + * Date ranges match the documentation for mktime in the php manual.
593 + *
594 + * @see https://php.net/manual/en/function.mktime.php#refsect1-function.mktime-parameters
595 + *
596 + * @uses Milestone_Widget::sanitize_range().
597 + *
598 + * @param array $dirty Unsantized data for the widget.
599 + *
600 + * @return array Santized data.
601 + */
602 + public function sanitize_instance( $dirty ) {
603 + $dirty = wp_parse_args(
604 + $dirty,
605 + $this->defaults()
606 + );
607 +
608 + $allowed_tags = array(
609 + 'a' => array(
610 + 'title' => array(),
611 + 'href' => array(),
612 + 'target' => array(),
613 + ),
614 + 'em' => array( 'title' => array() ),
615 + 'strong' => array( 'title' => array() ),
616 + );
617 +
618 + $clean = array(
619 + 'title' => trim( wp_strip_all_tags( stripslashes( $dirty['title'] ) ) ),
620 + 'event' => trim( wp_strip_all_tags( stripslashes( $dirty['event'] ) ) ),
621 + 'unit' => $dirty['unit'],
622 + 'type' => $dirty['type'],
623 + 'message' => wp_kses( $dirty['message'], $allowed_tags ),
624 + 'year' => $this->sanitize_range( $dirty['year'], 1901, 2037 ),
625 + 'month' => $this->sanitize_range( $dirty['month'], 1, 12 ),
626 + 'hour' => $this->sanitize_range( $dirty['hour'], 0, 23 ),
627 + 'min' => zeroise( $this->sanitize_range( $dirty['min'], 0, 59 ), 2 ),
628 + );
629 +
630 + $clean['day'] = $this->sanitize_range( $dirty['day'], 1, gmdate( 't', mktime( 0, 0, 0, $clean['month'], 1, $clean['year'] ) ) );
631 +
632 + return $clean;
633 + }
634 +
635 + /**
636 + * Form
637 + *
638 + * @param array $instance Widget instance.
639 + * @return string|void
640 + */
641 + public function form( $instance ) {
642 + $instance = $this->sanitize_instance( $instance );
643 +
644 + $units = array(
645 + 'automatic' => _x( 'Automatic', 'Milestone widget: mode in which the date unit is determined automatically', 'jetpack' ),
646 + 'years' => _x( 'Years', 'Milestone widget: mode in which the date unit is set to years', 'jetpack' ),
647 + 'months' => _x( 'Months', 'Milestone widget: mode in which the date unit is set to months', 'jetpack' ),
648 + 'days' => _x( 'Days', 'Milestone widget: mode in which the date unit is set to days', 'jetpack' ),
649 + 'hours' => _x( 'Hours', 'Milestone widget: mode in which the date unit is set to hours', 'jetpack' ),
650 + );
651 + ?>
652 + <div class="milestone-widget">
653 + <p>
654 + <label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php esc_html_e( 'Title', 'jetpack' ); ?></label>
655 + <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'] ); ?>" />
656 + </p>
657 +
658 + <p>
659 + <label for="<?php echo esc_attr( $this->get_field_id( 'event' ) ); ?>"><?php esc_html_e( 'Description', 'jetpack' ); ?></label>
660 + <input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'event' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'event' ) ); ?>" type="text" value="<?php echo esc_attr( $instance['event'] ); ?>" />
661 + </p>
662 +
663 + <fieldset class="jp-ms-data-time">
664 + <legend><?php esc_html_e( 'Date', 'jetpack' ); ?></legend>
665 +
666 + <label for="<?php echo esc_attr( $this->get_field_id( 'month' ) ); ?>" class="assistive-text"><?php esc_html_e( 'Month', 'jetpack' ); ?></label>
667 + <select id="<?php echo esc_attr( $this->get_field_id( 'month' ) ); ?>" class="month" name="<?php echo esc_attr( $this->get_field_name( 'month' ) ); ?>">
668 + <?php
669 + global $wp_locale;
670 + for ( $i = 1; $i < 13; $i++ ) {
671 + $monthnum = zeroise( $i, 2 );
672 + printf(
673 + '<option value="%s" %s>%s-%s</option>',
674 + esc_attr( $monthnum ),
675 + selected( $i, $instance['month'], false ),
676 + esc_attr( $monthnum ),
677 + esc_attr( $wp_locale->get_month_abbrev( $wp_locale->get_month( $i ) ) )
678 + );
679 + }
680 + ?>
681 + </select>
682 +
683 + <label for="<?php echo esc_attr( $this->get_field_id( 'day' ) ); ?>" class="assistive-text"><?php esc_html_e( 'Day', 'jetpack' ); ?></label>
684 + <input id="<?php echo esc_attr( $this->get_field_id( 'day' ) ); ?>" class="day" name="<?php echo esc_attr( $this->get_field_name( 'day' ) ); ?>" type="text" value="<?php echo esc_attr( $instance['day'] ); ?>">,
685 +
686 + <label for="<?php echo esc_attr( $this->get_field_id( 'year' ) ); ?>" class="assistive-text"><?php esc_html_e( 'Year', 'jetpack' ); ?></label>
687 + <input id="<?php echo esc_attr( $this->get_field_id( 'year' ) ); ?>" class="year" name="<?php echo esc_attr( $this->get_field_name( 'year' ) ); ?>" type="text" value="<?php echo esc_attr( $instance['year'] ); ?>">
688 + </fieldset>
689 +
690 + <fieldset class="jp-ms-data-time">
691 + <legend><?php esc_html_e( 'Time', 'jetpack' ); ?></legend>
692 +
693 + <label for="<?php echo esc_attr( $this->get_field_id( 'hour' ) ); ?>" class="assistive-text"><?php esc_html_e( 'Hour', 'jetpack' ); ?></label>
694 + <input id="<?php echo esc_attr( $this->get_field_id( 'hour' ) ); ?>" class="hour" name="<?php echo esc_attr( $this->get_field_name( 'hour' ) ); ?>" type="text" value="<?php echo esc_attr( $instance['hour'] ); ?>">
695 +
696 + <label for="<?php echo esc_attr( $this->get_field_id( 'min' ) ); ?>" class="assistive-text"><?php esc_html_e( 'Minutes', 'jetpack' ); ?></label>
697 +
698 + <span class="time-separator">:</span>
699 +
700 + <input id="<?php echo esc_attr( $this->get_field_id( 'min' ) ); ?>" class="minutes" name="<?php echo esc_attr( $this->get_field_name( 'min' ) ); ?>" type="text" value="<?php echo esc_attr( $instance['min'] ); ?>">
701 + </fieldset>
702 +
703 + <fieldset class="jp-ms-data-unit">
704 + <legend><?php esc_html_e( 'Time Unit', 'jetpack' ); ?></legend>
705 +
706 + <label for="<?php echo esc_attr( $this->get_field_id( 'unit' ) ); ?>" class="assistive-text">
707 + <?php esc_html_e( 'Time Unit', 'jetpack' ); ?>
708 + </label>
709 +
710 + <select id="<?php echo esc_attr( $this->get_field_id( 'unit' ) ); ?>" class="unit" name="<?php echo esc_attr( $this->get_field_name( 'unit' ) ); ?>">
711 + <?php
712 + foreach ( $units as $key => $unit ) {
713 + printf(
714 + '<option value="%s" %s>%s</option>',
715 + esc_attr( $key ),
716 + selected( $key, $instance['unit'], false ),
717 + esc_html( $unit )
718 + );
719 + }
720 + ?>
721 + </select>
722 + </fieldset>
723 +
724 + <ul class="milestone-type">
725 + <li>
726 + <label>
727 + <input
728 + <?php checked( $instance['type'], 'until' ); ?>
729 + name="<?php echo esc_attr( $this->get_field_name( 'type' ) ); ?>"
730 + type="radio"
731 + value="until"
732 + />
733 + <?php esc_html_e( 'Until your milestone', 'jetpack' ); ?>
734 + </label>
735 + </li>
736 +
737 + <li>
738 + <label>
739 + <input
740 + <?php checked( $instance['type'], 'since' ); ?>
741 + name="<?php echo esc_attr( $this->get_field_name( 'type' ) ); ?>"
742 + type="radio"
743 + value="since"
744 + />
745 + <?php esc_html_e( 'Since your milestone', 'jetpack' ); ?>
746 + </label>
747 + </li>
748 + </ul>
749 +
750 + <p class="milestone-message-wrapper">
751 + <label for="<?php echo esc_attr( $this->get_field_id( 'message' ) ); ?>"><?php esc_html_e( 'Milestone Reached Message', 'jetpack' ); ?></label>
752 + <textarea id="<?php echo esc_attr( $this->get_field_id( 'message' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'message' ) ); ?>" class="widefat" rows="3"><?php echo esc_textarea( $instance['message'] ); ?></textarea>
753 + </p>
754 + </div>
755 +
756 + <?php
757 + }
758 +}