PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-a.1
Jetpack – WP Security, Backup, Speed, & Growth v16.3-a.1
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 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 All 503 releases
jetpack / modules / widgets / milestone / class-milestone-widget.php

class-milestone-widget.php in Jetpack – WP Security, Backup, Speed, & Growth 16.3-a.1, at modules/widgets/milestone/class-milestone-widget.php

759 lines 23.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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 }
759