PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 14.2.2
Jetpack – WP Security, Backup, Speed, & Growth v14.2.2
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 2 years ago milestone-widget.css 4 years ago milestone.js 1 year ago milestone.php 5 years ago style-admin.css 4 years ago
class-milestone-widget.php
754 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 */
636 public function form( $instance ) {
637 $instance = $this->sanitize_instance( $instance );
638
639 $units = array(
640 'automatic' => _x( 'Automatic', 'Milestone widget: mode in which the date unit is determined automatically', 'jetpack' ),
641 'years' => _x( 'Years', 'Milestone widget: mode in which the date unit is set to years', 'jetpack' ),
642 'months' => _x( 'Months', 'Milestone widget: mode in which the date unit is set to months', 'jetpack' ),
643 'days' => _x( 'Days', 'Milestone widget: mode in which the date unit is set to days', 'jetpack' ),
644 'hours' => _x( 'Hours', 'Milestone widget: mode in which the date unit is set to hours', 'jetpack' ),
645 );
646 ?>
647 <div class="milestone-widget">
648 <p>
649 <label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php esc_html_e( 'Title', 'jetpack' ); ?></label>
650 <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'] ); ?>" />
651 </p>
652
653 <p>
654 <label for="<?php echo esc_attr( $this->get_field_id( 'event' ) ); ?>"><?php esc_html_e( 'Description', 'jetpack' ); ?></label>
655 <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'] ); ?>" />
656 </p>
657
658 <fieldset class="jp-ms-data-time">
659 <legend><?php esc_html_e( 'Date', 'jetpack' ); ?></legend>
660
661 <label for="<?php echo esc_attr( $this->get_field_id( 'month' ) ); ?>" class="assistive-text"><?php esc_html_e( 'Month', 'jetpack' ); ?></label>
662 <select id="<?php echo esc_attr( $this->get_field_id( 'month' ) ); ?>" class="month" name="<?php echo esc_attr( $this->get_field_name( 'month' ) ); ?>">
663 <?php
664 global $wp_locale;
665 for ( $i = 1; $i < 13; $i++ ) {
666 $monthnum = zeroise( $i, 2 );
667 printf(
668 '<option value="%s" %s>%s-%s</option>',
669 esc_attr( $monthnum ),
670 selected( $i, $instance['month'], false ),
671 esc_attr( $monthnum ),
672 esc_attr( $wp_locale->get_month_abbrev( $wp_locale->get_month( $i ) ) )
673 );
674 }
675 ?>
676 </select>
677
678 <label for="<?php echo esc_attr( $this->get_field_id( 'day' ) ); ?>" class="assistive-text"><?php esc_html_e( 'Day', 'jetpack' ); ?></label>
679 <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'] ); ?>">,
680
681 <label for="<?php echo esc_attr( $this->get_field_id( 'year' ) ); ?>" class="assistive-text"><?php esc_html_e( 'Year', 'jetpack' ); ?></label>
682 <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'] ); ?>">
683 </fieldset>
684
685 <fieldset class="jp-ms-data-time">
686 <legend><?php esc_html_e( 'Time', 'jetpack' ); ?></legend>
687
688 <label for="<?php echo esc_attr( $this->get_field_id( 'hour' ) ); ?>" class="assistive-text"><?php esc_html_e( 'Hour', 'jetpack' ); ?></label>
689 <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'] ); ?>">
690
691 <label for="<?php echo esc_attr( $this->get_field_id( 'min' ) ); ?>" class="assistive-text"><?php esc_html_e( 'Minutes', 'jetpack' ); ?></label>
692
693 <span class="time-separator">:</span>
694
695 <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'] ); ?>">
696 </fieldset>
697
698 <fieldset class="jp-ms-data-unit">
699 <legend><?php esc_html_e( 'Time Unit', 'jetpack' ); ?></legend>
700
701 <label for="<?php echo esc_attr( $this->get_field_id( 'unit' ) ); ?>" class="assistive-text">
702 <?php esc_html_e( 'Time Unit', 'jetpack' ); ?>
703 </label>
704
705 <select id="<?php echo esc_attr( $this->get_field_id( 'unit' ) ); ?>" class="unit" name="<?php echo esc_attr( $this->get_field_name( 'unit' ) ); ?>">
706 <?php
707 foreach ( $units as $key => $unit ) {
708 printf(
709 '<option value="%s" %s>%s</option>',
710 esc_attr( $key ),
711 selected( $key, $instance['unit'], false ),
712 esc_html( $unit )
713 );
714 }
715 ?>
716 </select>
717 </fieldset>
718
719 <ul class="milestone-type">
720 <li>
721 <label>
722 <input
723 <?php checked( $instance['type'], 'until' ); ?>
724 name="<?php echo esc_attr( $this->get_field_name( 'type' ) ); ?>"
725 type="radio"
726 value="until"
727 />
728 <?php esc_html_e( 'Until your milestone', 'jetpack' ); ?>
729 </label>
730 </li>
731
732 <li>
733 <label>
734 <input
735 <?php checked( $instance['type'], 'since' ); ?>
736 name="<?php echo esc_attr( $this->get_field_name( 'type' ) ); ?>"
737 type="radio"
738 value="since"
739 />
740 <?php esc_html_e( 'Since your milestone', 'jetpack' ); ?>
741 </label>
742 </li>
743 </ul>
744
745 <p class="milestone-message-wrapper">
746 <label for="<?php echo esc_attr( $this->get_field_id( 'message' ) ); ?>"><?php esc_html_e( 'Milestone Reached Message', 'jetpack' ); ?></label>
747 <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>
748 </p>
749 </div>
750
751 <?php
752 }
753 }
754