PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 14.4
Jetpack – WP Security, Backup, Speed, & Growth v14.4
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 14.2.2 14.3.1 All 501 releases
jetpack / _inc / lib / widgets.php

widgets.php in Jetpack – WP Security, Backup, Speed, & Growth 14.4, at _inc/lib/widgets.php

788 lines 26.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2 /**
3 * Widgets and Sidebars Library
4 *
5 * Helper functions for manipulating widgets on a per-blog basis.
6 * Only helpful on `wp_loaded` or later (currently requires widgets to be registered and the theme context to already be loaded).
7 *
8 * Used by the REST API
9 *
10 * @package automattic/jetpack
11 */
12
13 /**
14 * Widgets and Sidebars Library
15 */
16 class Jetpack_Widgets {
17
18 /**
19 * Returns the `sidebars_widgets` option with the `array_version` element removed.
20 *
21 * @return array The current value of sidebars_widgets
22 */
23 public static function get_sidebars_widgets() {
24 $sidebars = get_option( 'sidebars_widgets', array() );
25 if ( isset( $sidebars['array_version'] ) ) {
26 unset( $sidebars['array_version'] );
27 }
28 return $sidebars;
29 }
30
31 /**
32 * Format widget data for output and for use by other widget functions.
33 *
34 * The output looks like:
35 *
36 * array(
37 * 'id' => 'text-3',
38 * 'sidebar' => 'sidebar-1',
39 * 'position' => '0',
40 * 'settings' => array(
41 * 'title' => 'hello world'
42 * )
43 * )
44 *
45 * @param string|integer $position The position of the widget in its sidebar.
46 * @param string $widget_id The widget's id (eg: 'text-3').
47 * @param string $sidebar The widget's sidebar id (eg: 'sidebar-1').
48 * @param array|null $settings The settings for the widget.
49 *
50 * @return array A normalized array representing this widget.
51 */
52 public static function format_widget( $position, $widget_id, $sidebar, $settings = null ) {
53 if ( ! $settings ) {
54 $all_settings = get_option( self::get_widget_option_name( $widget_id ) );
55 $instance = self::get_widget_instance_key( $widget_id );
56 $settings = $all_settings[ $instance ];
57 }
58 $widget = array();
59
60 $widget['id'] = $widget_id;
61 $widget['id_base'] = self::get_widget_id_base( $widget_id );
62 $widget['settings'] = $settings;
63 $widget['sidebar'] = $sidebar;
64 $widget['position'] = $position;
65
66 return $widget;
67 }
68
69 /**
70 * Return a widget's id_base from its id.
71 *
72 * @param string $widget_id The id of a widget. (eg: 'text-3').
73 *
74 * @return string The id_base of a widget (eg: 'text').
75 */
76 public static function get_widget_id_base( $widget_id ) {
77 // Grab what's before the hyphen.
78 return substr( $widget_id, 0, strrpos( $widget_id, '-' ) );
79 }
80
81 /**
82 * Determine a widget's option name (the WP option where the widget's settings
83 * are stored - generally `widget_` + the widget's id_base).
84 *
85 * @param string $widget_id The id of a widget. (eg: 'text-3').
86 *
87 * @return string The option name of the widget's settings. (eg: 'widget_text')
88 */
89 public static function get_widget_option_name( $widget_id ) {
90 return 'widget_' . self::get_widget_id_base( $widget_id );
91 }
92
93 /**
94 * Determine a widget instance key from its ID. (eg: 'text-3' becomes '3').
95 * Used to access the widget's settings.
96 *
97 * @param string $widget_id The id of a widget.
98 *
99 * @return integer The instance key of that widget.
100 */
101 public static function get_widget_instance_key( $widget_id ) {
102 // Grab all numbers from the end of the id.
103 preg_match( '/(\d+)$/', $widget_id, $matches );
104
105 return (int) $matches[0];
106 }
107
108 /**
109 * Return a widget by ID (formatted for output) or null if nothing is found.
110 *
111 * @param string $widget_id The id of a widget to look for.
112 *
113 * @return array|null The matching formatted widget (see format_widget).
114 */
115 public static function get_widget_by_id( $widget_id ) {
116 $found = null;
117 foreach ( self::get_all_widgets() as $widget ) {
118 if ( $widget['id'] === $widget_id ) {
119 $found = $widget;
120 }
121 }
122 return $found;
123 }
124
125 /**
126 * Return an array of all widgets (active and inactive) formatted for output.
127 *
128 * @return array An array of all widgets (see format_widget).
129 */
130 public static function get_all_widgets() {
131 $all_widgets = array();
132 $sidebars_widgets = self::get_all_sidebars();
133
134 foreach ( $sidebars_widgets as $sidebar => $widgets ) {
135 if ( ! is_array( $widgets ) ) {
136 continue;
137 }
138 foreach ( $widgets as $key => $widget_id ) {
139 array_push( $all_widgets, self::format_widget( $key, $widget_id, $sidebar ) );
140 }
141 }
142
143 return $all_widgets;
144 }
145
146 /**
147 * Return an array of all active widgets formatted for output.
148 *
149 * @return array An array of all active widgets (see format_widget).
150 */
151 public static function get_active_widgets() {
152 $active_widgets = array();
153 $all_widgets = self::get_all_widgets();
154 foreach ( $all_widgets as $widget ) {
155 if ( 'wp_inactive_widgets' === $widget['sidebar'] ) {
156 continue;
157 }
158 array_push( $active_widgets, $widget );
159 }
160 return $active_widgets;
161 }
162
163 /**
164 * Return an array of all widget IDs (active and inactive)
165 *
166 * @return array An array of all widget IDs.
167 */
168 public static function get_all_widget_ids() {
169 $all_widgets = array();
170 $sidebars_widgets = self::get_all_sidebars();
171 foreach ( array_values( $sidebars_widgets ) as $widgets ) {
172 if ( ! is_array( $widgets ) ) {
173 continue;
174 }
175 foreach ( array_values( $widgets ) as $widget_id ) {
176 array_push( $all_widgets, $widget_id );
177 }
178 }
179 return $all_widgets;
180 }
181
182 /**
183 * Return an array of widgets with a specific id_base (eg: `text`).
184 *
185 * @param string $id_base The id_base of a widget type.
186 *
187 * @return array All the formatted widgets matching that widget type (see format_widget).
188 */
189 public static function get_widgets_with_id_base( $id_base ) {
190 $matching_widgets = array();
191 foreach ( self::get_all_widgets() as $widget ) {
192 if ( self::get_widget_id_base( $widget['id'] ) === $id_base ) {
193 array_push( $matching_widgets, $widget );
194 }
195 }
196 return $matching_widgets;
197 }
198
199 /**
200 * Return the array of widget IDs in a sidebar or null if that sidebar does
201 * not exist. Will return an empty array for an existing empty sidebar.
202 *
203 * @param string $sidebar The id of a sidebar.
204 *
205 * @return array|null The array of widget IDs in the sidebar.
206 */
207 public static function get_widgets_in_sidebar( $sidebar ) {
208 $sidebars = self::get_all_sidebars();
209
210 if ( ! $sidebars || ! is_array( $sidebars ) ) {
211 return null;
212 }
213 if ( ! $sidebars[ $sidebar ] && array_key_exists( $sidebar, $sidebars ) ) {
214 return array();
215 }
216 return $sidebars[ $sidebar ];
217 }
218
219 /**
220 * Return an associative array of all registered sidebars for this theme,
221 * active and inactive, including the hidden disabled widgets sidebar (keyed
222 * by `wp_inactive_widgets`). Each sidebar is keyed by the ID of the sidebar
223 * and its value is an array of widget IDs for that sidebar.
224 *
225 * @return array An associative array of all sidebars and their widget IDs.
226 */
227 public static function get_all_sidebars() {
228 $sidebars_widgets = self::get_sidebars_widgets();
229
230 if ( ! is_array( $sidebars_widgets ) ) {
231 return array();
232 }
233 return $sidebars_widgets;
234 }
235
236 /**
237 * Return an associative array of all active sidebars for this theme, Each
238 * sidebar is keyed by the ID of the sidebar and its value is an array of
239 * widget IDs for that sidebar.
240 *
241 * @return array An associative array of all active sidebars and their widget IDs.
242 */
243 public static function get_active_sidebars() {
244 $sidebars = array();
245 foreach ( self::get_all_sidebars() as $sidebar => $widgets ) {
246 if ( 'wp_inactive_widgets' === $sidebar || ! isset( $widgets ) || ! is_array( $widgets ) ) {
247 continue;
248 }
249 $sidebars[ $sidebar ] = $widgets;
250 }
251 return $sidebars;
252 }
253
254 /**
255 * Activates a widget in a sidebar. Does not validate that the sidebar exists,
256 * so please do that first. Also does not save the widget's settings. Please
257 * do that with `set_widget_settings`.
258 *
259 * If position is not set, it will be set to the next available position.
260 *
261 * @param string $widget_id The newly-formed id of the widget to be added.
262 * @param string $sidebar The id of the sidebar where the widget will be added.
263 * @param string|integer $position (Optional) The position within the sidebar where the widget will be added.
264 *
265 * @return bool
266 */
267 public static function add_widget_to_sidebar( $widget_id, $sidebar, $position ) {
268 return self::move_widget_to_sidebar( array( 'id' => $widget_id ), $sidebar, $position );
269 }
270
271 /**
272 * Removes a widget from a sidebar. Does not validate that the sidebar exists
273 * or remove any settings from the widget, so please do that separately.
274 *
275 * @param array $widget The widget to be removed.
276 */
277 public static function remove_widget_from_sidebar( $widget ) {
278 $sidebars_widgets = self::get_sidebars_widgets();
279 // Remove the widget from its old location and reflow the positions of the remaining widgets.
280 array_splice( $sidebars_widgets[ $widget['sidebar'] ], $widget['position'], 1 );
281
282 update_option( 'sidebars_widgets', $sidebars_widgets );
283 }
284
285 /**
286 * Moves a widget to a sidebar. Does not validate that the sidebar exists,
287 * so please do that first. Also does not save the widget's settings. Please
288 * do that with `set_widget_settings`. The first argument should be a
289 * widget as returned by `format_widget` including `id`, `sidebar`, and
290 * `position`.
291 *
292 * If $position is not set, it will be set to the next available position.
293 *
294 * Can be used to add a new widget to a sidebar if
295 * $widget['sidebar'] === NULL
296 *
297 * Can be used to move a widget within a sidebar as well if
298 * $widget['sidebar'] === $sidebar.
299 *
300 * @param array $widget The widget to be moved (see format_widget).
301 * @param string $sidebar The sidebar where this widget will be moved.
302 * @param string|integer $position (Optional) The position where this widget will be moved in the sidebar.
303 *
304 * @return bool
305 */
306 public static function move_widget_to_sidebar( $widget, $sidebar, $position ) {
307 $sidebars_widgets = self::get_sidebars_widgets();
308
309 /*
310 * If a position is passed and the sidebar isn't empty,
311 * splice the widget into the sidebar,
312 * update the sidebar option, and return the result.
313 */
314 if ( isset( $widget['sidebar'] ) && isset( $widget['position'] ) ) {
315 array_splice( $sidebars_widgets[ $widget['sidebar'] ], $widget['position'], 1 );
316 }
317
318 // Sometimes an existing empty sidebar is NULL, so initialize it.
319 if ( array_key_exists( $sidebar, $sidebars_widgets ) && ! is_array( $sidebars_widgets[ $sidebar ] ) ) {
320 $sidebars_widgets[ $sidebar ] = array();
321 }
322
323 // If no position is passed, set one from items in sidebar.
324 if ( ! isset( $position ) ) {
325 $position = 0;
326 $last_position = self::get_last_position_in_sidebar( $sidebar );
327 if ( isset( $last_position ) && is_numeric( $last_position ) ) {
328 $position = $last_position + 1;
329 }
330 }
331
332 // Add the widget to the sidebar and reflow the positions of the other widgets.
333 if ( empty( $sidebars_widgets[ $sidebar ] ) ) {
334 $sidebars_widgets[ $sidebar ][] = $widget['id'];
335 } else {
336 array_splice( $sidebars_widgets[ $sidebar ], (int) $position, 0, $widget['id'] );
337 }
338
339 set_theme_mod(
340 'sidebars_widgets',
341 array(
342 'time' => time(),
343 'data' => $sidebars_widgets,
344 )
345 );
346 return update_option( 'sidebars_widgets', $sidebars_widgets );
347 }
348
349 /**
350 * Return an integer containing the largest position number in a sidebar or
351 * null if there are no widgets in that sidebar.
352 *
353 * @param string $sidebar The id of a sidebar.
354 *
355 * @return integer|null The last index position of a widget in that sidebar.
356 */
357 public static function get_last_position_in_sidebar( $sidebar ) {
358 $widgets = self::get_widgets_in_sidebar( $sidebar );
359 if ( ! $widgets ) {
360 return null;
361 }
362 $last_position = 0;
363 foreach ( $widgets as $widget_id ) {
364 $widget = self::get_widget_by_id( $widget_id );
365 if ( (int) $widget['position'] > (int) $last_position ) {
366 $last_position = (int) $widget['position'];
367 }
368 }
369 return $last_position;
370 }
371
372 /**
373 * Saves settings for a widget. Does not add that widget to a sidebar. Please
374 * do that with `move_widget_to_sidebar` first. Will merge the settings of
375 * any existing widget with the same `$widget_id`.
376 *
377 * @param string $widget_id The id of a widget.
378 * @param array $settings An associative array of settings to merge with any existing settings on this widget.
379 *
380 * @return boolean|WP_Error True if update was successful.
381 */
382 public static function set_widget_settings( $widget_id, $settings ) {
383 $widget_option_name = self::get_widget_option_name( $widget_id );
384 $widget_settings = get_option( $widget_option_name );
385 $instance_key = self::get_widget_instance_key( $widget_id );
386 $old_settings = $widget_settings[ $instance_key ];
387 $settings = self::sanitize_widget_settings( $widget_id, $settings, $old_settings );
388
389 if ( ! $settings ) {
390 return new WP_Error( 'invalid_data', 'Update failed.', 500 );
391 }
392 if ( is_array( $old_settings ) ) {
393 // array_filter prevents empty arguments from replacing existing ones.
394 $settings = wp_parse_args( array_filter( $settings ), $old_settings );
395 }
396
397 $widget_settings[ $instance_key ] = $settings;
398
399 return update_option( $widget_option_name, $widget_settings );
400 }
401
402 /**
403 * Sanitize an associative array for saving.
404 *
405 * @param string $widget_id The id of a widget.
406 * @param array $settings A widget settings array.
407 * @param array $old_settings The existing widget settings array.
408 *
409 * @return array|false The settings array sanitized by `WP_Widget::update` or false if sanitization failed.
410 */
411 private static function sanitize_widget_settings( $widget_id, $settings, $old_settings ) {
412 $widget = self::get_registered_widget_object( self::get_widget_id_base( $widget_id ) );
413
414 if ( ! $widget ) {
415 return false;
416 }
417 $new_settings = $widget->update( $settings, $old_settings );
418 if ( ! is_array( $new_settings ) ) {
419 return false;
420 }
421 return $new_settings;
422 }
423
424 /**
425 * Deletes settings for a widget. Does not remove that widget to a sidebar. Please
426 * do that with `remove_widget_from_sidebar` first.
427 *
428 * @param array $widget The widget which will have its settings removed (see format_widget).
429 */
430 public static function remove_widget_settings( $widget ) {
431 $widget_option_name = self::get_widget_option_name( $widget['id'] );
432 $widget_settings = get_option( $widget_option_name );
433 unset( $widget_settings[ self::get_widget_instance_key( $widget['id'] ) ] );
434 update_option( $widget_option_name, $widget_settings );
435 }
436
437 /**
438 * Update a widget's settings, sidebar, and position. Returns the (updated)
439 * formatted widget if successful or a WP_Error if it fails.
440 *
441 * @param string $widget_id The id of a widget to update.
442 * @param string $sidebar (Optional) A sidebar to which this widget will be moved.
443 * @param string|integer $position (Optional) A new position to which this widget will be moved within its new or existing sidebar.
444 * @param array|object|string $settings Settings to merge with the existing settings of the widget (will be passed through `decode_settings`).
445 *
446 * @return array|WP_Error The newly added widget as an associative array with all the above properties.
447 */
448 public static function update_widget( $widget_id, $sidebar, $position, $settings ) {
449 $settings = self::decode_settings( $settings );
450 if ( isset( $settings ) && ! is_array( $settings ) ) {
451 return new WP_Error( 'invalid_data', 'Invalid settings', 400 );
452 }
453 // Default to an empty array if nothing is specified.
454 if ( ! is_array( $settings ) ) {
455 $settings = array();
456 }
457 $widget = self::get_widget_by_id( $widget_id );
458 if ( ! $widget ) {
459 return new WP_Error( 'not_found', 'No widget found.', 400 );
460 }
461 if ( ! $sidebar ) {
462 $sidebar = $widget['sidebar'];
463 }
464 if ( ! isset( $position ) ) {
465 $position = $widget['position'];
466 }
467 if ( ! is_numeric( $position ) ) {
468 return new WP_Error( 'invalid_data', 'Invalid position', 400 );
469 }
470 $widgets_in_sidebar = self::get_widgets_in_sidebar( $sidebar );
471 if ( ! isset( $widgets_in_sidebar ) ) {
472 return new WP_Error( 'invalid_data', 'No such sidebar exists', 400 );
473 }
474 self::move_widget_to_sidebar( $widget, $sidebar, $position );
475 $widget_save_status = self::set_widget_settings( $widget_id, $settings );
476 if ( is_wp_error( $widget_save_status ) ) {
477 return $widget_save_status;
478 }
479 return self::get_widget_by_id( $widget_id );
480 }
481
482 /**
483 * Deletes a widget entirely including all its settings. Returns a WP_Error if
484 * the widget could not be found. Otherwise returns an empty array.
485 *
486 * @param string $widget_id The id of a widget to delete. (eg: 'text-2').
487 *
488 * @return array|WP_Error An empty array if successful.
489 */
490 public static function delete_widget( $widget_id ) {
491 $widget = self::get_widget_by_id( $widget_id );
492 if ( ! $widget ) {
493 return new WP_Error( 'not_found', 'No widget found.', 400 );
494 }
495 self::remove_widget_from_sidebar( $widget );
496 self::remove_widget_settings( $widget );
497 return array();
498 }
499
500 /**
501 * Return an array of settings. The input can be either an object, a JSON
502 * string, or an array.
503 *
504 * @param array|string|object $settings The settings of a widget as passed into the API.
505 *
506 * @return array Decoded associative array of settings.
507 */
508 public static function decode_settings( $settings ) {
509 // Treat as string in case JSON was passed.
510 if ( is_object( $settings ) && property_exists( $settings, 'scalar' ) ) {
511 $settings = $settings->scalar;
512 }
513 if ( is_object( $settings ) ) {
514 $settings = (array) $settings;
515 }
516 // Attempt to decode JSON string.
517 if ( is_string( $settings ) ) {
518 $settings = (array) json_decode( $settings );
519 }
520 return $settings;
521 }
522
523 /**
524 * Activate a new widget.
525 *
526 * @param string $id_base The id_base of the new widget (eg: 'text').
527 * @param string $sidebar The id of the sidebar where this widget will go. Dependent on theme. (eg: 'sidebar-1').
528 * @param string|integer $position (Optional) The position of the widget in the sidebar. Defaults to the last position.
529 * @param array|object|string $settings (Optional) An associative array of settings for this widget (will be passed through `decode_settings`). Varies by widget.
530 *
531 * @return array|WP_Error The newly added widget as an associative array with all the above properties except 'id_base' replaced with the generated 'id'.
532 */
533 public static function activate_widget( $id_base, $sidebar, $position, $settings ) {
534 if ( ! isset( $id_base ) || ! self::validate_id_base( $id_base ) ) {
535 return new WP_Error( 'invalid_data', 'Invalid ID base', 400 );
536 }
537
538 if ( ! isset( $sidebar ) ) {
539 return new WP_Error( 'invalid_data', 'No sidebar provided', 400 );
540 }
541
542 if ( isset( $position ) && ! is_numeric( $position ) ) {
543 return new WP_Error( 'invalid_data', 'Invalid position', 400 );
544 }
545
546 $settings = self::decode_settings( $settings );
547 if ( isset( $settings ) && ! is_array( $settings ) ) {
548 return new WP_Error( 'invalid_data', 'Invalid settings', 400 );
549 }
550
551 // Default to an empty array if nothing is specified.
552 if ( ! is_array( $settings ) ) {
553 $settings = array();
554 }
555
556 $widget_counter = 1 + self::get_last_widget_instance_key_with_id_base( $id_base );
557 $widget_id = $id_base . '-' . $widget_counter;
558 if ( 0 >= $widget_counter ) {
559 return new WP_Error( 'invalid_data', 'Error creating widget ID' . $widget_id, 500 );
560 }
561 if ( self::get_widget_by_id( $widget_id ) ) {
562 return new WP_Error( 'invalid_data', 'Widget ID already exists', 500 );
563 }
564
565 self::add_widget_to_sidebar( $widget_id, $sidebar, $position );
566 $widget_save_status = self::set_widget_settings( $widget_id, $settings );
567 if ( is_wp_error( $widget_save_status ) ) {
568 return $widget_save_status;
569 }
570
571 // Add a Tracks event for non-Headstart activity.
572 if ( ! defined( 'HEADSTART' ) ) {
573 $tracking = new Automattic\Jetpack\Tracking();
574 $tracking->tracks_record_event(
575 wp_get_current_user(),
576 'wpcom_widgets_activate_widget',
577 array(
578 'widget' => $id_base,
579 'settings' => wp_json_encode( $settings ),
580 )
581 );
582 }
583
584 return self::get_widget_by_id( $widget_id );
585 }
586
587 /**
588 * Activate an array of new widgets. Like calling `activate_widget` multiple times.
589 *
590 * @param array $widgets An array of widget arrays. Each sub-array must be of the format required by `activate_widget`.
591 *
592 * @return array|WP_Error The newly added widgets in the form returned by `get_all_widgets`.
593 */
594 public static function activate_widgets( $widgets ) {
595 if ( ! is_array( $widgets ) ) {
596 return new WP_Error( 'invalid_data', 'Invalid widgets', 400 );
597 }
598
599 $added_widgets = array();
600
601 foreach ( $widgets as $widget ) {
602 $added_widgets[] = self::activate_widget( $widget['id_base'], $widget['sidebar'], $widget['position'], $widget['settings'] );
603 }
604
605 return $added_widgets;
606 }
607
608 /**
609 * Return the last instance key (integer) of an existing widget matching
610 * `$id_base`. So if you pass in `text`, and there is a widget with the id
611 * `text-2`, this function will return `2`.
612 *
613 * @param string $id_base The id_base of a type of widget. (eg: 'rss').
614 *
615 * @return integer The last instance key of that type of widget.
616 */
617 public static function get_last_widget_instance_key_with_id_base( $id_base ) {
618 $similar_widgets = self::get_widgets_with_id_base( $id_base );
619
620 if ( ! empty( $similar_widgets ) ) {
621 // If the last widget with the same name is `text-3`, we want `text-4`.
622 usort( $similar_widgets, __CLASS__ . '::sort_widgets' );
623
624 $last_widget = array_pop( $similar_widgets );
625 $last_val = (int) self::get_widget_instance_key( $last_widget['id'] );
626
627 return $last_val;
628 }
629
630 return 0;
631 }
632
633 /**
634 * Method used to sort widgets
635 *
636 * @since 5.4
637 *
638 * @param array $a A normalized array representing a widget.
639 * @param array $b A normalized array representing a widget.
640 *
641 * @return int
642 */
643 public static function sort_widgets( $a, $b ) {
644 $a_val = (int) self::get_widget_instance_key( $a['id'] );
645 $b_val = (int) self::get_widget_instance_key( $b['id'] );
646 return $a_val <=> $b_val;
647 }
648
649 /**
650 * Retrieve a given widget object instance by ID base (eg. 'text' or 'archives').
651 *
652 * @param string $id_base The id_base of a type of widget.
653 *
654 * @return WP_Widget|false The found widget object or false if the id_base was not found.
655 */
656 public static function get_registered_widget_object( $id_base ) {
657 if ( ! $id_base ) {
658 return false;
659 }
660
661 // Get all of the registered widgets.
662 global $wp_widget_factory;
663 if ( ! isset( $wp_widget_factory ) ) {
664 return false;
665 }
666
667 $registered_widgets = $wp_widget_factory->widgets;
668 if ( empty( $registered_widgets ) ) {
669 return false;
670 }
671
672 foreach ( array_values( $registered_widgets ) as $registered_widget_object ) {
673 if ( $registered_widget_object->id_base === $id_base ) {
674 return $registered_widget_object;
675 }
676 }
677 return false;
678 }
679
680 /**
681 * Validate a given widget ID base (eg. 'text' or 'archives').
682 *
683 * @param string $id_base The id_base of a type of widget.
684 *
685 * @return boolean True if the widget is of a known type.
686 */
687 public static function validate_id_base( $id_base ) {
688 return ( false !== self::get_registered_widget_object( $id_base ) );
689 }
690
691 /**
692 * Insert a new widget in a given sidebar.
693 *
694 * @param string $widget_id ID of the widget.
695 * @param array $widget_options Content of the widget.
696 * @param string $sidebar ID of the sidebar to which the widget will be added.
697 *
698 * @return WP_Error|true True when data has been saved correctly, error otherwise.
699 */
700 public static function insert_widget_in_sidebar( $widget_id, $widget_options, $sidebar ) {
701 // Retrieve sidebars, widgets and their instances.
702 $sidebars_widgets = get_option( 'sidebars_widgets', array() );
703 $widget_instances = get_option( 'widget_' . $widget_id, array() );
704
705 // Retrieve the key of the next widget instance.
706 $numeric_keys = array_filter( array_keys( $widget_instances ), 'is_int' );
707 $next_key = $numeric_keys ? max( $numeric_keys ) + 1 : 2;
708
709 // Add this widget to the sidebar.
710 if ( ! isset( $sidebars_widgets[ $sidebar ] ) ) {
711 $sidebars_widgets[ $sidebar ] = array();
712 }
713 $sidebars_widgets[ $sidebar ][] = $widget_id . '-' . $next_key;
714
715 // Add the new widget instance.
716 $widget_instances[ $next_key ] = $widget_options;
717
718 // Store updated sidebars, widgets and their instances.
719 if (
720 ! ( update_option( 'sidebars_widgets', $sidebars_widgets ) )
721 || ( ! ( update_option( 'widget_' . $widget_id, $widget_instances ) ) )
722 ) {
723 return new WP_Error( 'widget_update_failed', 'Failed to update widget or sidebar.', 400 );
724 }
725
726 return true;
727 }
728
729 /**
730 * Update the content of an existing widget in a given sidebar.
731 *
732 * @param string $widget_id ID of the widget.
733 * @param array $widget_options New content for the update.
734 * @param string $sidebar ID of the sidebar to which the widget will be added.
735 *
736 * @return WP_Error|true True when data has been updated correctly, error otherwise.
737 */
738 public static function update_widget_in_sidebar( $widget_id, $widget_options, $sidebar ) {
739 // Retrieve sidebars, widgets and their instances.
740 $sidebars_widgets = get_option( 'sidebars_widgets', array() );
741 $widget_instances = get_option( 'widget_' . $widget_id, array() );
742
743 // Retrieve index of first widget instance in that sidebar.
744 $widget_key = false;
745 foreach ( $sidebars_widgets[ $sidebar ] as $widget ) {
746 if ( str_contains( $widget, $widget_id ) ) {
747 $widget_key = absint( str_replace( $widget_id . '-', '', $widget ) );
748 break;
749 }
750 }
751
752 // There is no widget instance.
753 if ( ! $widget_key ) {
754 return new WP_Error( 'invalid_data', 'No such widget.', 400 );
755 }
756
757 // Update the widget instance and option if the data has changed.
758 if ( $widget_instances[ $widget_key ]['title'] !== $widget_options['title']
759 || $widget_instances[ $widget_key ]['address'] !== $widget_options['address']
760 ) {
761
762 $widget_instances[ $widget_key ] = array_merge( $widget_instances[ $widget_key ], $widget_options );
763
764 // Store updated widget instances and return Error when not successful.
765 if ( ! ( update_option( 'widget_' . $widget_id, $widget_instances ) ) ) {
766 return new WP_Error( 'widget_update_failed', 'Failed to update widget.', 400 );
767 }
768 }
769 return true;
770 }
771
772 /**
773 * Retrieve the first active sidebar.
774 *
775 * @return string|WP_Error First active sidebar, error if none exists.
776 */
777 public static function get_first_sidebar() {
778 $active_sidebars = get_option( 'sidebars_widgets', array() );
779 unset( $active_sidebars['wp_inactive_widgets'], $active_sidebars['array_version'] );
780
781 if ( empty( $active_sidebars ) ) {
782 return false;
783 }
784 $active_sidebars_keys = array_keys( $active_sidebars );
785 return array_shift( $active_sidebars_keys );
786 }
787 }
788