PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 14.8.1
Jetpack – WP Security, Backup, Speed, & Growth v14.8.1
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 14.2.2 14.3.1 14.4.2 14.5.1 14.6.1 14.7.1 14.8.1 14.9.2 15.0.3 15.1.2 15.2.1 15.3.2 15.4.1 15.5.1 15.6.1 15.7.2 15.8.1 15.9.2 16.0.2 16.1.3 16.2-a.5 16.2-a.3 16.1.2 16.2-a.1 16.1.1 16.1 16.1-beta 16.1-beta.2 16.1-beta.3 16.1-a.5 16.1-a.3 16.0.1 16.1-a.1 16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / modules / widgets / milestone / class-milestone-widget.php
jetpack / modules / widgets / milestone Last commit date
admin.js 6 years ago class-milestone-widget.php 1 year ago milestone-widget.css 1 year ago milestone.js 1 year ago milestone.php 5 years ago style-admin.css 1 year ago
class-milestone-widget.php
755 lines
1 <?php
2 /**
3 * Milestone Countdown Widget
4 *
5 * @package automattic/jetpack
6 */
7
8 use Automattic\Jetpack\Assets;
9
10 /**
11 * Class Milestone_Widget
12 */
13 class Milestone_Widget extends WP_Widget {
14 /**
15 * Holding array for widget configuration and localization.
16 *
17 * @var array
18 */
19 private static $config_js = array();
20
21 /**
22 * Available time units sorted in descending order.
23 *
24 * @var Array
25 */
26 protected $available_units = array(
27 'years',
28 'months',
29 'days',
30 'hours',
31 'minutes',
32 'seconds',
33 );
34
35 /**
36 * Milestone_Widget constructor.
37 */
38 public function __construct() {
39 $widget = array(
40 'classname' => 'milestone-widget',
41 'description' => __( 'Display a countdown to a certain date.', 'jetpack' ),
42 );
43
44 parent::__construct(
45 'Milestone_Widget',
46 /** This filter is documented in modules/widgets/facebook-likebox.php */
47 apply_filters( 'jetpack_widget_name', __( 'Milestone', 'jetpack' ) ),
48 $widget
49 );
50
51 add_action( 'wp_enqueue_scripts', array( __CLASS__, 'enqueue_template' ) );
52 add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_admin' ) );
53 add_action( 'wp_footer', array( $this, 'localize_script' ) );
54
55 if ( is_active_widget( false, false, $this->id_base, true ) || is_active_widget( false, false, 'monster', true ) || is_customize_preview() ) {
56 add_action( 'wp_head', array( __CLASS__, 'styles_template' ) );
57 }
58 }
59
60 /**
61 * Enqueue admin assets.
62 *
63 * @param string $hook_suffix Hook suffix provided by WordPress.
64 */
65 public static function enqueue_admin( $hook_suffix ) {
66 if ( 'widgets.php' === $hook_suffix ) {
67 wp_enqueue_style( 'milestone-admin', plugin_dir_url( __FILE__ ) . 'style-admin.css', array(), '20201113' );
68 wp_enqueue_script(
69 'milestone-admin-js',
70 Assets::get_file_url_for_environment(
71 '_inc/build/widgets/milestone/admin.min.js',
72 'modules/widgets/milestone/admin.js'
73 ),
74 array( 'jquery' ),
75 '20201113',
76 true
77 );
78 }
79 }
80
81 /**
82 * Enqueue the frontend JS.
83 */
84 public static function enqueue_template() {
85 if ( class_exists( 'Jetpack_AMP_Support' ) && Jetpack_AMP_Support::is_amp_request() ) {
86 return;
87 }
88
89 wp_enqueue_script(
90 'milestone',
91 Assets::get_file_url_for_environment(
92 '_inc/build/widgets/milestone/milestone.min.js',
93 'modules/widgets/milestone/milestone.js'
94 ),
95 array(),
96 '20201113',
97 true
98 );
99 }
100
101 /**
102 * Output the frontend styling.
103 */
104 public static function styles_template() {
105 global $themecolors;
106 $colors = wp_parse_args(
107 $themecolors,
108 array(
109 'bg' => 'ffffff',
110 'border' => 'cccccc',
111 'text' => '333333',
112 )
113 );
114 ?>
115 <style>
116 .milestone-widget {
117 --milestone-text-color: <?php echo self::sanitize_color_hex( $colors['text'] ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>;
118 --milestone-bg-color: <?php echo self::sanitize_color_hex( $colors['bg'] ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>;
119 --milestone-border-color:<?php echo self::sanitize_color_hex( $colors['border'] ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>;
120 }
121 </style>
122 <?php
123 }
124
125 /**
126 * Ensure that a string representing a color in hexadecimal
127 * notation is safe for use in css and database saves.
128 *
129 * @param string $hex Hexademical code to sanitize.
130 * @param string $prefix Prefix for the hex code.
131 *
132 * @return string Color in hexadecimal notation on success - the string "transparent" otherwise.
133 */
134 public static function sanitize_color_hex( $hex, $prefix = '#' ) {
135 $hex = trim( $hex );
136
137 /* Strip recognized prefixes. */
138 if ( str_starts_with( $hex, '#' ) ) {
139 $hex = substr( $hex, 1 );
140 } elseif ( str_starts_with( $hex, '%23' ) ) {
141 $hex = substr( $hex, 3 );
142 }
143
144 if ( 0 !== preg_match( '/^[0-9a-fA-F]{6}$/', $hex ) ) {
145 return $prefix . $hex;
146 }
147
148 return 'transparent';
149 }
150
151 /**
152 * Localize Front-end Script.
153 *
154 * Print the javascript configuration array only if the
155 * current template has an instance of the widget that
156 * is still counting down. In all other cases, this
157 * function will dequeue milestone.js.
158 *
159 * Hooks into the "wp_footer" action.
160 */
161 public function localize_script() {
162 if ( class_exists( 'Jetpack_AMP_Support' ) && Jetpack_AMP_Support::is_amp_request() ) {
163 return;
164 }
165
166 if ( empty( self::$config_js['instances'] ) ) {
167 wp_dequeue_script( 'milestone' );
168 return;
169 }
170 self::$config_js['api_root'] = esc_url_raw( rest_url() );
171 wp_localize_script( 'milestone', 'MilestoneConfig', self::$config_js );
172 }
173
174 /**
175 * Return an associative array of default values
176 *
177 * These values are used in new widgets.
178 *
179 * @return array Array of default values for the Widget's options.
180 */
181 public function defaults() {
182 $now = current_datetime();
183 $now_timestamp = $now->getTimestamp();
184
185 return array(
186 'title' => '',
187 'event' => __( 'The Big Day', 'jetpack' ),
188 'unit' => 'automatic',
189 'type' => 'until',
190 'message' => __( 'The big day is here.', 'jetpack' ),
191 'day' => gmdate( 'd', $now_timestamp ),
192 'month' => gmdate( 'm', $now_timestamp ),
193 'year' => gmdate( 'Y', $now_timestamp ),
194 'hour' => 0,
195 'min' => 0,
196 );
197 }
198
199 /**
200 * Widget
201 *
202 * @param array $args Widget args.
203 * @param array $instance Widget instance.
204 */
205 public function widget( $args, $instance ) {
206 $instance = wp_parse_args( $instance, $this->defaults() );
207
208 $this->enqueue_scripts();
209
210 echo $args['before_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
211
212 /** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */
213 $title = apply_filters( 'widget_title', $instance['title'] );
214 if ( ! empty( $title ) ) {
215 echo $args['before_title'] . $title . $args['after_title']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
216 }
217
218 $widget_id = ! empty( $args['widget_id'] ) ? $args['widget_id'] : 'milestone_widget';
219 $data = $this->get_widget_data( $instance );
220 $config = array(
221 'id' => $widget_id,
222 'message' => $data['message'],
223 'refresh' => $data['refresh'],
224 );
225
226 /*
227 * Sidebars may be configured to not expose the `widget_id`. Example: `twentytwenty` footer areas.
228 *
229 * We need our own unique identifier.
230 */
231 $config['content_id'] = $widget_id . '-content';
232
233 self::$config_js['instances'][] = $config;
234
235 printf( '<div id="%s" class="milestone-content">', esc_html( $config['content_id'] ) );
236
237 echo '<div class="milestone-header">';
238 echo '<strong class="event">' . esc_html( $instance['event'] ) . '</strong>';
239 echo '<span class="date">' . esc_html( date_i18n( get_option( 'date_format' ), $data['milestone'] ) ) . '</span>';
240 echo '</div>';
241
242 echo wp_kses_post( $data['message'] );
243
244 echo '</div><!--milestone-content-->';
245
246 echo $args['after_widget']; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
247
248 /** This action is documented in modules/widgets/gravatar-profile.php */
249 do_action( 'jetpack_stats_extra', 'widget_view', 'milestone' );
250 }
251
252 /**
253 * Enqueue widget styles
254 */
255 public function enqueue_scripts() {
256 wp_enqueue_style(
257 'milestone-widget',
258 plugins_url( 'milestone-widget.css', __FILE__ ),
259 array(),
260 JETPACK__VERSION
261 );
262 }
263
264 /**
265 * Getter for the widget data.
266 *
267 * @param array $instance Widget instance.
268 *
269 * @return array
270 */
271 public function get_widget_data( $instance ) {
272 $data = array();
273
274 $instance = $this->sanitize_instance( $instance );
275
276 $milestone = mktime( $instance['hour'], $instance['min'], 0, $instance['month'], $instance['day'], $instance['year'] );
277 $now = (int) current_time( 'timestamp' ); // phpcs:ignore WordPress.DateTime.CurrentTimeTimestamp.Requested
278 $type = $instance['type'];
279
280 if ( 'since' === $type ) {
281 $diff = (int) floor( $now - $milestone );
282 } else {
283 $diff = (int) floor( $milestone - $now );
284 }
285
286 $data['diff'] = $diff;
287 $data['unit'] = $this->get_unit( $diff, $instance['unit'] );
288
289 // Setting the refresh counter to equal the number of seconds it takes to flip a unit.
290 $refresh_intervals = array(
291 0, // should be YEAR_IN_SECONDS, but doing setTimeout for a year doesn't seem to be logical.
292 0, // same goes for MONTH_IN_SECONDS.
293 DAY_IN_SECONDS,
294 HOUR_IN_SECONDS,
295 MINUTE_IN_SECONDS,
296 1,
297 );
298
299 $data['refresh'] = $refresh_intervals[ array_search( $data['unit'], $this->available_units, true ) ];
300 $data['milestone'] = $milestone;
301
302 if ( ( 1 > $diff ) && ( 'until' === $type ) ) {
303 $data['message'] = '<div class="milestone-message">' . $instance['message'] . '</div>';
304 $data['refresh'] = 0; // No need to refresh, the milestone has been reached.
305 } else {
306 $interval_text = $this->get_interval_in_units( $diff, $data['unit'] );
307 $interval = (int) $interval_text;
308
309 if ( 'since' === $type ) {
310
311 switch ( $data['unit'] ) {
312 case 'years':
313 $data['message'] = sprintf(
314 /* translators: %s is the number of year(s). */
315 _n(
316 '<span class="difference">%s</span> <span class="label">year ago.</span>',
317 '<span class="difference">%s</span> <span class="label">years ago.</span>',
318 $interval,
319 'jetpack'
320 ),
321 $interval_text
322 );
323 break;
324 case 'months':
325 $data['message'] = sprintf(
326 /* translators: %s is the number of month(s). */
327 _n(
328 '<span class="difference">%s</span> <span class="label">month ago.</span>',
329 '<span class="difference">%s</span> <span class="label">months ago.</span>',
330 $interval,
331 'jetpack'
332 ),
333 $interval_text
334 );
335 break;
336 case 'days':
337 $data['message'] = sprintf(
338 /* translators: %s is the number of days(s). */
339 _n(
340 '<span class="difference">%s</span> <span class="label">day ago.</span>',
341 '<span class="difference">%s</span> <span class="label">days ago.</span>',
342 $interval,
343 'jetpack'
344 ),
345 $interval_text
346 );
347 break;
348 case 'hours':
349 $data['message'] = sprintf(
350 /* translators: %s is the number of hours(s). */
351 _n(
352 '<span class="difference">%s</span> <span class="label">hour ago.</span>',
353 '<span class="difference">%s</span> <span class="label">hours ago.</span>',
354 $interval,
355 'jetpack'
356 ),
357 $interval_text
358 );
359 break;
360 case 'minutes':
361 $data['message'] = sprintf(
362 /* translators: %s is the number of minutes(s). */
363 _n(
364 '<span class="difference">%s</span> <span class="label">minute ago.</span>',
365 '<span class="difference">%s</span> <span class="label">minutes ago.</span>',
366 $interval,
367 'jetpack'
368 ),
369 $interval_text
370 );
371 break;
372 case 'seconds':
373 $data['message'] = sprintf(
374 /* translators: %s is the number of second(s). */
375 _n(
376 '<span class="difference">%s</span> <span class="label">second ago.</span>',
377 '<span class="difference">%s</span> <span class="label">seconds ago.</span>',
378 $interval,
379 'jetpack'
380 ),
381 $interval_text
382 );
383 break;
384 }
385 } else {
386 switch ( $this->get_unit( $diff, $instance['unit'] ) ) {
387 case 'years':
388 $data['message'] = sprintf(
389 /* translators: %s is the number of year(s). */
390 _n(
391 '<span class="difference">%s</span> <span class="label">year to go.</span>',
392 '<span class="difference">%s</span> <span class="label">years to go.</span>',
393 $interval,
394 'jetpack'
395 ),
396 $interval_text
397 );
398 break;
399 case 'months':
400 $data['message'] = sprintf(
401 /* translators: %s is the number of month(s). */
402 _n(
403 '<span class="difference">%s</span> <span class="label">month to go.</span>',
404 '<span class="difference">%s</span> <span class="label">months to go.</span>',
405 $interval,
406 'jetpack'
407 ),
408 $interval_text
409 );
410 break;
411 case 'days':
412 $data['message'] = sprintf(
413 /* translators: %s is the number of days(s). */
414 _n(
415 '<span class="difference">%s</span> <span class="label">day to go.</span>',
416 '<span class="difference">%s</span> <span class="label">days to go.</span>',
417 $interval,
418 'jetpack'
419 ),
420 $interval_text
421 );
422 break;
423 case 'hours':
424 $data['message'] = sprintf(
425 /* translators: %s is the number of hour(s). */
426 _n(
427 '<span class="difference">%s</span> <span class="label">hour to go.</span>',
428 '<span class="difference">%s</span> <span class="label">hours to go.</span>',
429 $interval,
430 'jetpack'
431 ),
432 $interval_text
433 );
434 break;
435 case 'minutes':
436 $data['message'] = sprintf(
437 /* translators: %s is the number of minute(s). */
438 _n(
439 '<span class="difference">%s</span> <span class="label">minute to go.</span>',
440 '<span class="difference">%s</span> <span class="label">minutes to go.</span>',
441 $interval,
442 'jetpack'
443 ),
444 $interval_text
445 );
446 break;
447 case 'seconds':
448 $data['message'] = sprintf(
449 /* translators: %s is the number of second(s). */
450 _n(
451 '<span class="difference">%s</span> <span class="label">second to go.</span>',
452 '<span class="difference">%s</span> <span class="label">seconds to go.</span>',
453 $interval,
454 'jetpack'
455 ),
456 $interval_text
457 );
458 break;
459 }
460 }
461 $data['message'] = '<div class="milestone-countdown">' . $data['message'] . '</div>';
462 }
463
464 return $data;
465 }
466
467 /**
468 * Return the largest possible time unit that the difference will be displayed in.
469 *
470 * @param Integer $seconds the interval in seconds.
471 * @param String $maximum_unit the maximum unit that will be used. Optional.
472 * @return String $calculated_unit
473 */
474 protected function get_unit( $seconds, $maximum_unit = 'automatic' ) {
475 $unit = '';
476
477 if ( $seconds >= YEAR_IN_SECONDS * 2 ) {
478 // more than 2 years - show in years, one decimal point.
479 $unit = 'years';
480
481 } elseif ( $seconds >= YEAR_IN_SECONDS ) {
482 if ( 'years' === $maximum_unit ) {
483 $unit = 'years';
484 } else {
485 // automatic mode - showing months even if it's between one and two years.
486 $unit = 'months';
487 }
488 } elseif ( $seconds >= MONTH_IN_SECONDS * 3 ) {
489 // fewer than 2 years - show in months.
490 $unit = 'months';
491
492 } elseif ( $seconds >= MONTH_IN_SECONDS ) {
493 if ( 'months' === $maximum_unit ) {
494 $unit = 'months';
495 } else {
496 // automatic mode - showing days even if it's between one and three months.
497 $unit = 'days';
498 }
499 } elseif ( $seconds >= DAY_IN_SECONDS - 1 ) {
500 // fewer than a month - show in days.
501 $unit = 'days';
502
503 } elseif ( $seconds >= HOUR_IN_SECONDS - 1 ) {
504 // less than 1 day - show in hours.
505 $unit = 'hours';
506
507 } elseif ( $seconds >= MINUTE_IN_SECONDS - 1 ) {
508 // less than 1 hour - show in minutes.
509 $unit = 'minutes';
510
511 } else {
512 // less than 1 minute - show in seconds.
513 $unit = 'seconds';
514 }
515
516 $maximum_unit_index = array_search( $maximum_unit, $this->available_units, true );
517 $unit_index = array_search( $unit, $this->available_units, true );
518
519 if (
520 false === $maximum_unit_index // the maximum unit parameter is automatic.
521 || $unit_index > $maximum_unit_index // there is not enough seconds for even one maximum time unit.
522 ) {
523 return $unit;
524 }
525 return $maximum_unit;
526 }
527
528 /**
529 * Returns a time difference value in specified units.
530 *
531 * @param int $seconds Number of seconds.
532 * @param string $units Unit.
533 * @return int $time_in_units.
534 */
535 protected function get_interval_in_units( $seconds, $units ) {
536 switch ( $units ) {
537 case 'years':
538 $years = $seconds / YEAR_IN_SECONDS;
539 $decimals = abs( round( $years, 1 ) - round( $years ) ) > 0 ? 1 : 0;
540 return number_format_i18n( $years, $decimals );
541 case 'months':
542 return (int) ( $seconds / 60 / 60 / 24 / 30 );
543 case 'days':
544 return (int) ( $seconds / 60 / 60 / 24 + 1 );
545 case 'hours':
546 return (int) ( $seconds / 60 / 60 );
547 case 'minutes':
548 return (int) ( $seconds / 60 + 1 );
549 default:
550 return $seconds;
551 }
552 }
553
554 /**
555 * Update widget.
556 *
557 * @param array $new_instance New instance of the widget being saved.
558 * @param array $old_instance Previous instance being saved over.
559 *
560 * @return array
561 */
562 public function update( $new_instance, $old_instance ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
563 return $this->sanitize_instance( $new_instance );
564 }
565
566 /**
567 * Make sure that a number is within a certain range.
568 * If the number is too small it will become the possible lowest value.
569 * If the number is too large it will become the possible highest value.
570 *
571 * @param int $n The number to check.
572 * @param int $floor The lowest possible value.
573 * @param int $ceil The highest possible value.
574 */
575 public function sanitize_range( $n, $floor, $ceil ) {
576 $n = (int) $n;
577 if ( $n < $floor ) {
578 $n = $floor;
579 } elseif ( $n > $ceil ) {
580 $n = $ceil;
581 }
582 return $n;
583 }
584
585 /**
586 * Sanitize an instance of this widget.
587 *
588 * Date ranges match the documentation for mktime in the php manual.
589 *
590 * @see https://php.net/manual/en/function.mktime.php#refsect1-function.mktime-parameters
591 *
592 * @uses Milestone_Widget::sanitize_range().
593 *
594 * @param array $dirty Unsantized data for the widget.
595 *
596 * @return array Santized data.
597 */
598 public function sanitize_instance( $dirty ) {
599 $dirty = wp_parse_args(
600 $dirty,
601 $this->defaults()
602 );
603
604 $allowed_tags = array(
605 'a' => array(
606 'title' => array(),
607 'href' => array(),
608 'target' => array(),
609 ),
610 'em' => array( 'title' => array() ),
611 'strong' => array( 'title' => array() ),
612 );
613
614 $clean = array(
615 'title' => trim( wp_strip_all_tags( stripslashes( $dirty['title'] ) ) ),
616 'event' => trim( wp_strip_all_tags( stripslashes( $dirty['event'] ) ) ),
617 'unit' => $dirty['unit'],
618 'type' => $dirty['type'],
619 'message' => wp_kses( $dirty['message'], $allowed_tags ),
620 'year' => $this->sanitize_range( $dirty['year'], 1901, 2037 ),
621 'month' => $this->sanitize_range( $dirty['month'], 1, 12 ),
622 'hour' => $this->sanitize_range( $dirty['hour'], 0, 23 ),
623 'min' => zeroise( $this->sanitize_range( $dirty['min'], 0, 59 ), 2 ),
624 );
625
626 $clean['day'] = $this->sanitize_range( $dirty['day'], 1, gmdate( 't', mktime( 0, 0, 0, $clean['month'], 1, $clean['year'] ) ) );
627
628 return $clean;
629 }
630
631 /**
632 * Form
633 *
634 * @param array $instance Widget instance.
635 * @return string|void
636 */
637 public function form( $instance ) {
638 $instance = $this->sanitize_instance( $instance );
639
640 $units = array(
641 'automatic' => _x( 'Automatic', 'Milestone widget: mode in which the date unit is determined automatically', 'jetpack' ),
642 'years' => _x( 'Years', 'Milestone widget: mode in which the date unit is set to years', 'jetpack' ),
643 'months' => _x( 'Months', 'Milestone widget: mode in which the date unit is set to months', 'jetpack' ),
644 'days' => _x( 'Days', 'Milestone widget: mode in which the date unit is set to days', 'jetpack' ),
645 'hours' => _x( 'Hours', 'Milestone widget: mode in which the date unit is set to hours', 'jetpack' ),
646 );
647 ?>
648 <div class="milestone-widget">
649 <p>
650 <label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php esc_html_e( 'Title', 'jetpack' ); ?></label>
651 <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'] ); ?>" />
652 </p>
653
654 <p>
655 <label for="<?php echo esc_attr( $this->get_field_id( 'event' ) ); ?>"><?php esc_html_e( 'Description', 'jetpack' ); ?></label>
656 <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'] ); ?>" />
657 </p>
658
659 <fieldset class="jp-ms-data-time">
660 <legend><?php esc_html_e( 'Date', 'jetpack' ); ?></legend>
661
662 <label for="<?php echo esc_attr( $this->get_field_id( 'month' ) ); ?>" class="assistive-text"><?php esc_html_e( 'Month', 'jetpack' ); ?></label>
663 <select id="<?php echo esc_attr( $this->get_field_id( 'month' ) ); ?>" class="month" name="<?php echo esc_attr( $this->get_field_name( 'month' ) ); ?>">
664 <?php
665 global $wp_locale;
666 for ( $i = 1; $i < 13; $i++ ) {
667 $monthnum = zeroise( $i, 2 );
668 printf(
669 '<option value="%s" %s>%s-%s</option>',
670 esc_attr( $monthnum ),
671 selected( $i, $instance['month'], false ),
672 esc_attr( $monthnum ),
673 esc_attr( $wp_locale->get_month_abbrev( $wp_locale->get_month( $i ) ) )
674 );
675 }
676 ?>
677 </select>
678
679 <label for="<?php echo esc_attr( $this->get_field_id( 'day' ) ); ?>" class="assistive-text"><?php esc_html_e( 'Day', 'jetpack' ); ?></label>
680 <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'] ); ?>">,
681
682 <label for="<?php echo esc_attr( $this->get_field_id( 'year' ) ); ?>" class="assistive-text"><?php esc_html_e( 'Year', 'jetpack' ); ?></label>
683 <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'] ); ?>">
684 </fieldset>
685
686 <fieldset class="jp-ms-data-time">
687 <legend><?php esc_html_e( 'Time', 'jetpack' ); ?></legend>
688
689 <label for="<?php echo esc_attr( $this->get_field_id( 'hour' ) ); ?>" class="assistive-text"><?php esc_html_e( 'Hour', 'jetpack' ); ?></label>
690 <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'] ); ?>">
691
692 <label for="<?php echo esc_attr( $this->get_field_id( 'min' ) ); ?>" class="assistive-text"><?php esc_html_e( 'Minutes', 'jetpack' ); ?></label>
693
694 <span class="time-separator">:</span>
695
696 <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'] ); ?>">
697 </fieldset>
698
699 <fieldset class="jp-ms-data-unit">
700 <legend><?php esc_html_e( 'Time Unit', 'jetpack' ); ?></legend>
701
702 <label for="<?php echo esc_attr( $this->get_field_id( 'unit' ) ); ?>" class="assistive-text">
703 <?php esc_html_e( 'Time Unit', 'jetpack' ); ?>
704 </label>
705
706 <select id="<?php echo esc_attr( $this->get_field_id( 'unit' ) ); ?>" class="unit" name="<?php echo esc_attr( $this->get_field_name( 'unit' ) ); ?>">
707 <?php
708 foreach ( $units as $key => $unit ) {
709 printf(
710 '<option value="%s" %s>%s</option>',
711 esc_attr( $key ),
712 selected( $key, $instance['unit'], false ),
713 esc_html( $unit )
714 );
715 }
716 ?>
717 </select>
718 </fieldset>
719
720 <ul class="milestone-type">
721 <li>
722 <label>
723 <input
724 <?php checked( $instance['type'], 'until' ); ?>
725 name="<?php echo esc_attr( $this->get_field_name( 'type' ) ); ?>"
726 type="radio"
727 value="until"
728 />
729 <?php esc_html_e( 'Until your milestone', 'jetpack' ); ?>
730 </label>
731 </li>
732
733 <li>
734 <label>
735 <input
736 <?php checked( $instance['type'], 'since' ); ?>
737 name="<?php echo esc_attr( $this->get_field_name( 'type' ) ); ?>"
738 type="radio"
739 value="since"
740 />
741 <?php esc_html_e( 'Since your milestone', 'jetpack' ); ?>
742 </label>
743 </li>
744 </ul>
745
746 <p class="milestone-message-wrapper">
747 <label for="<?php echo esc_attr( $this->get_field_id( 'message' ) ); ?>"><?php esc_html_e( 'Milestone Reached Message', 'jetpack' ); ?></label>
748 <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>
749 </p>
750 </div>
751
752 <?php
753 }
754 }
755