PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 8.5
Jetpack – WP Security, Backup, Speed, & Growth v8.5
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 8.5, at _inc/lib/widgets.php

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