| 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 |
|