PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 7.1.4
Jetpack – WP Security, Backup, Speed, & Growth v7.1.4
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 All 500 releases
jetpack / _inc / lib / widgets.php
widgets.php
777 lines 25.8 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 jetpack_require_lib( 'tracks/client' );
561 jetpack_tracks_record_event( wp_get_current_user(), 'wpcom_widgets_activate_widget', array(
562 'widget' => $id_base,
563 'settings' => json_encode( $settings ),
564 ) );
565 }
566
567 return self::get_widget_by_id( $widget_id );
568 }
569
570 /**
571 * Activate an array of new widgets. Like calling `activate_widget` multiple times.
572 *
573 * @param array $widgets An array of widget arrays. Each sub-array must be of the format required by `activate_widget`.
574 *
575 * @return array|WP_Error The newly added widgets in the form returned by `get_all_widgets`.
576 */
577 public static function activate_widgets( $widgets ) {
578 if ( ! is_array( $widgets ) ) {
579 return new WP_Error( 'invalid_data', 'Invalid widgets', 400 );
580 }
581
582 $added_widgets = array();
583
584 foreach( $widgets as $widget ) {
585 $added_widgets[] = self::activate_widget( $widget['id_base'], $widget['sidebar'], $widget['position'], $widget['settings'] );
586 }
587
588 return $added_widgets;
589 }
590
591 /**
592 * Return the last instance key (integer) of an existing widget matching
593 * `$id_base`. So if you pass in `text`, and there is a widget with the id
594 * `text-2`, this function will return `2`.
595 *
596 * @param string $id_base The id_base of a type of widget. (eg: 'rss')
597 *
598 * @return integer The last instance key of that type of widget.
599 */
600 public static function get_last_widget_instance_key_with_id_base( $id_base ) {
601 $similar_widgets = self::get_widgets_with_id_base( $id_base );
602
603 if ( ! empty( $similar_widgets ) ) {
604 // If the last widget with the same name is `text-3`, we want `text-4`
605 usort( $similar_widgets, __CLASS__ . '::sort_widgets' );
606
607 $last_widget = array_pop( $similar_widgets );
608 $last_val = intval( self::get_widget_instance_key( $last_widget['id'] ) );
609
610 return $last_val;
611 }
612
613 return 0;
614 }
615
616 /**
617 * Method used to sort widgets
618 *
619 * @since 5.4
620 *
621 * @param array $a
622 * @param array $b
623 *
624 * @return int
625 */
626 public static function sort_widgets( $a, $b ) {
627 $a_val = intval( self::get_widget_instance_key( $a['id'] ) );
628 $b_val = intval( self::get_widget_instance_key( $b['id'] ) );
629 if ( $a_val > $b_val ) {
630 return 1;
631 }
632 if ( $a_val < $b_val ) {
633 return -1;
634 }
635 return 0;
636 }
637
638 /**
639 * Retrieve a given widget object instance by ID base (eg. 'text' or 'archives').
640 *
641 * @param string $id_base The id_base of a type of widget.
642 *
643 * @return WP_Widget|false The found widget object or false if the id_base was not found.
644 */
645 public static function get_registered_widget_object( $id_base ) {
646 if ( ! $id_base ) {
647 return false;
648 }
649
650 // Get all of the registered widgets.
651 global $wp_widget_factory;
652 if ( ! isset( $wp_widget_factory ) ) {
653 return false;
654 }
655
656 $registered_widgets = $wp_widget_factory->widgets;
657 if ( empty( $registered_widgets ) ) {
658 return false;
659 }
660
661 foreach ( array_values( $registered_widgets ) as $registered_widget_object ) {
662 if ( $registered_widget_object->id_base === $id_base ) {
663 return $registered_widget_object;
664 }
665 }
666 return false;
667 }
668
669 /**
670 * Validate a given widget ID base (eg. 'text' or 'archives').
671 *
672 * @param string $id_base The id_base of a type of widget.
673 *
674 * @return boolean True if the widget is of a known type.
675 */
676 public static function validate_id_base( $id_base ) {
677 return ( false !== self::get_registered_widget_object( $id_base ) );
678 }
679
680 /**
681 * Insert a new widget in a given sidebar.
682 *
683 * @param string $widget_id ID of the widget.
684 * @param array $widget_options Content of the widget.
685 * @param string $sidebar ID of the sidebar to which the widget will be added.
686 *
687 * @return WP_Error|true True when data has been saved correctly, error otherwise.
688 */
689 static function insert_widget_in_sidebar( $widget_id, $widget_options, $sidebar ) {
690 // Retrieve sidebars, widgets and their instances
691 $sidebars_widgets = get_option( 'sidebars_widgets', array() );
692 $widget_instances = get_option( 'widget_' . $widget_id, array() );
693
694 // Retrieve the key of the next widget instance
695 $numeric_keys = array_filter( array_keys( $widget_instances ), 'is_int' );
696 $next_key = $numeric_keys ? max( $numeric_keys ) + 1 : 2;
697
698 // Add this widget to the sidebar
699 if ( ! isset( $sidebars_widgets[ $sidebar ] ) ) {
700 $sidebars_widgets[ $sidebar ] = array();
701 }
702 $sidebars_widgets[ $sidebar ][] = $widget_id . '-' . $next_key;
703
704 // Add the new widget instance
705 $widget_instances[ $next_key ] = $widget_options;
706
707 // Store updated sidebars, widgets and their instances
708 if (
709 ! ( update_option( 'sidebars_widgets', $sidebars_widgets ) )
710 || ( ! ( update_option( 'widget_' . $widget_id, $widget_instances ) ) )
711 ) {
712 return new WP_Error( 'widget_update_failed', 'Failed to update widget or sidebar.', 400 );
713 };
714
715 return true;
716 }
717
718 /**
719 * Update the content of an existing widget in a given sidebar.
720 *
721 * @param string $widget_id ID of the widget.
722 * @param array $widget_options New content for the update.
723 * @param string $sidebar ID of the sidebar to which the widget will be added.
724 *
725 * @return WP_Error|true True when data has been updated correctly, error otherwise.
726 */
727 static function update_widget_in_sidebar( $widget_id, $widget_options, $sidebar ) {
728 // Retrieve sidebars, widgets and their instances
729 $sidebars_widgets = get_option( 'sidebars_widgets', array() );
730 $widget_instances = get_option( 'widget_' . $widget_id, array() );
731
732 // Retrieve index of first widget instance in that sidebar
733 $widget_key = false;
734 foreach ( $sidebars_widgets[ $sidebar ] as $widget ) {
735 if ( strpos( $widget, $widget_id ) !== false ) {
736 $widget_key = absint( str_replace( $widget_id . '-', '', $widget ) );
737 break;
738 }
739 }
740
741 // There is no widget instance
742 if ( ! $widget_key ) {
743 return new WP_Error( 'invalid_data', 'No such widget.', 400 );
744 }
745
746 // Update the widget instance and option if the data has changed
747 if ( $widget_instances[ $widget_key ]['title'] !== $widget_options['title']
748 || $widget_instances[ $widget_key ]['address'] !== $widget_options['address']
749 ) {
750
751 $widget_instances[ $widget_key ] = array_merge( $widget_instances[ $widget_key ], $widget_options );
752
753 // Store updated widget instances and return Error when not successful
754 if ( ! ( update_option( 'widget_' . $widget_id, $widget_instances ) ) ) {
755 return new WP_Error( 'widget_update_failed', 'Failed to update widget.', 400 );
756 };
757 };
758 return true;
759 }
760
761 /**
762 * Retrieve the first active sidebar.
763 *
764 * @return string|WP_Error First active sidebar, error if none exists.
765 */
766 static function get_first_sidebar() {
767 $active_sidebars = get_option( 'sidebars_widgets', array() );
768 unset( $active_sidebars[ 'wp_inactive_widgets' ], $active_sidebars[ 'array_version' ] );
769
770 if ( empty( $active_sidebars ) ) {
771 return false;
772 }
773 $active_sidebars_keys = array_keys( $active_sidebars );
774 return array_shift( $active_sidebars_keys );
775 }
776 }
777