1<?php
2/**
3 * The classic widget administration screen, for use in widgets.php.
4 *
5 * @package WordPress
6 * @subpackage Administration
7 */
8
9// Don't load directly.
10if ( ! defined( 'ABSPATH' ) ) {
11	die( '-1' );
12}
13
14$widgets_access = get_user_setting( 'widgets_access' );
15if ( isset( $_GET['widgets-access'] ) ) {
16	check_admin_referer( 'widgets-access' );
17
18	$widgets_access = 'on' === $_GET['widgets-access'] ? 'on' : 'off';
19	set_user_setting( 'widgets_access', $widgets_access );
20}
21
22if ( 'on' === $widgets_access ) {
23	add_filter( 'admin_body_class', 'wp_widgets_access_body_class' );
24} else {
25	wp_enqueue_script( 'admin-widgets' );
26
27	if ( wp_is_mobile() ) {
28		wp_enqueue_script( 'jquery-touch-punch' );
29	}
30}
31
32/**
33 * Fires early before the Widgets administration screen loads,
34 * after scripts are enqueued.
35 *
36 * @since 2.2.0
37 */
38do_action( 'sidebar_admin_setup' );
39
40get_current_screen()->add_help_tab(
41	array(
42		'id'      => 'overview',
43		'title'   => __( 'Overview' ),
44		'content' =>
45				'<p>' . __( 'Widgets are independent sections of content that can be placed into any widgetized area provided by your theme (commonly called sidebars). To populate your sidebars/widget areas with individual widgets, drag and drop the title bars into the desired area. By default, only the first widget area is expanded. To populate additional widget areas, click on their title bars to expand them.' ) . '</p>
46	<p>' . __( 'The Available Widgets section contains all the widgets you can choose from. Once you drag a widget into a sidebar, it will open to allow you to configure its settings. When you are happy with the widget settings, click the Save button and the widget will go live on your site. If you click Delete, it will remove the widget.' ) . '</p>',
47	)
48);
49get_current_screen()->add_help_tab(
50	array(
51		'id'      => 'removing-reusing',
52		'title'   => __( 'Removing and Reusing' ),
53		'content' =>
54				'<p>' . __( 'If you want to remove the widget but save its setting for possible future use, just drag it into the Inactive Widgets area. You can add them back anytime from there. This is especially helpful when you switch to a theme with fewer or different widget areas.' ) . '</p>
55	<p>' . __( 'Widgets may be used multiple times. You can give each widget a title, to display on your site, but it&#8217;s not required.' ) . '</p>
56	<p>' . __( 'Enabling Accessibility Mode, via Screen Options, allows you to use Add and Edit buttons instead of using drag and drop.' ) . '</p>',
57	)
58);
59get_current_screen()->add_help_tab(
60	array(
61		'id'      => 'missing-widgets',
62		'title'   => __( 'Missing Widgets' ),
63		'content' =>
64				'<p>' . __( 'Many themes show some sidebar widgets by default until you edit your sidebars, but they are not automatically displayed in your sidebar management tool. After you make your first widget change, you can re-add the default widgets by adding them from the Available Widgets area.' ) . '</p>' .
65					'<p>' . __( 'When changing themes, there is often some variation in the number and setup of widget areas/sidebars and sometimes these conflicts make the transition a bit less smooth. If you changed themes and seem to be missing widgets, scroll down on this screen to the Inactive Widgets area, where all of your widgets and their settings will have been saved.' ) . '</p>',
66	)
67);
68
69get_current_screen()->set_help_sidebar(
70	'<p><strong>' . __( 'For more information:' ) . '</strong></p>' .
71	'<p>' . __( '<a href="https://wordpress.org/support/article/appearance-widgets-screen/">Documentation on Widgets</a>' ) . '</p>' .
72	'<p>' . __( '<a href="https://wordpress.org/support/">Support</a>' ) . '</p>'
73);
74
75// These are the widgets grouped by sidebar.
76$sidebars_widgets = wp_get_sidebars_widgets();
77
78if ( empty( $sidebars_widgets ) ) {
79	$sidebars_widgets = wp_get_widget_defaults();
80}
81
82foreach ( $sidebars_widgets as $sidebar_id => $widgets ) {
83	if ( 'wp_inactive_widgets' === $sidebar_id ) {
84		continue;
85	}
86
87	if ( ! is_registered_sidebar( $sidebar_id ) ) {
88		if ( ! empty( $widgets ) ) { // Register the inactive_widgets area as sidebar.
89			register_sidebar(
90				array(
91					'name'          => __( 'Inactive Sidebar (not used)' ),
92					'id'            => $sidebar_id,
93					'class'         => 'inactive-sidebar orphan-sidebar',
94					'description'   => __( 'This sidebar is no longer available and does not show anywhere on your site. Remove each of the widgets below to fully remove this inactive sidebar.' ),
95					'before_widget' => '',
96					'after_widget'  => '',
97					'before_title'  => '',
98					'after_title'   => '',
99				)
100			);
101		} else {
102			unset( $sidebars_widgets[ $sidebar_id ] );
103		}
104	}
105}
106
107// Register the inactive_widgets area as sidebar.
108register_sidebar(
109	array(
110		'name'          => __( 'Inactive Widgets' ),
111		'id'            => 'wp_inactive_widgets',
112		'class'         => 'inactive-sidebar',
113		'description'   => __( 'Drag widgets here to remove them from the sidebar but keep their settings.' ),
114		'before_widget' => '',
115		'after_widget'  => '',
116		'before_title'  => '',
117		'after_title'   => '',
118	)
119);
120
121retrieve_widgets();
122
123// We're saving a widget without JS.
124if ( isset( $_POST['savewidget'] ) || isset( $_POST['removewidget'] ) ) {
125	$widget_id = $_POST['widget-id'];
126	check_admin_referer( "save-delete-widget-$widget_id" );
127
128	$number = isset( $_POST['multi_number'] ) ? (int) $_POST['multi_number'] : '';
129	if ( $number ) {
130		foreach ( $_POST as $key => $val ) {
131			if ( is_array( $val ) && preg_match( '/__i__|%i%/', key( $val ) ) ) {
132				$_POST[ $key ] = array( $number => array_shift( $val ) );
133				break;
134			}
135		}
136	}
137
138	$sidebar_id = $_POST['sidebar'];
139	$position   = isset( $_POST[ $sidebar_id . '_position' ] ) ? (int) $_POST[ $sidebar_id . '_position' ] - 1 : 0;
140
141	$id_base = $_POST['id_base'];
142	$sidebar = isset( $sidebars_widgets[ $sidebar_id ] ) ? $sidebars_widgets[ $sidebar_id ] : array();
143
144	// Delete.
145	if ( isset( $_POST['removewidget'] ) && $_POST['removewidget'] ) {
146
147		if ( ! in_array( $widget_id, $sidebar, true ) ) {
148			wp_redirect( admin_url( 'widgets.php?error=0' ) );
149			exit;
150		}
151
152		$sidebar = array_diff( $sidebar, array( $widget_id ) );
153		$_POST   = array(
154			'sidebar'            => $sidebar_id,
155			'widget-' . $id_base => array(),
156			'the-widget-id'      => $widget_id,
157			'delete_widget'      => '1',
158		);
159
160		/**
161		 * Fires immediately after a widget has been marked for deletion.
162		 *
163		 * @since 4.4.0
164		 *
165		 * @param string $widget_id  ID of the widget marked for deletion.
166		 * @param string $sidebar_id ID of the sidebar the widget was deleted from.
167		 * @param string $id_base    ID base for the widget.
168		 */
169		do_action( 'delete_widget', $widget_id, $sidebar_id, $id_base );
170	}
171
172	$_POST['widget-id'] = $sidebar;
173
174	foreach ( (array) $wp_registered_widget_updates as $name => $control ) {
175		if ( $name !== $id_base || ! is_callable( $control['callback'] ) ) {
176			continue;
177		}
178
179		ob_start();
180			call_user_func_array( $control['callback'], $control['params'] );
181		ob_end_clean();
182
183		break;
184	}
185
186	$sidebars_widgets[ $sidebar_id ] = $sidebar;
187
188	// Remove old position.
189	if ( ! isset( $_POST['delete_widget'] ) ) {
190		foreach ( $sidebars_widgets as $key => $sb ) {
191			if ( is_array( $sb ) ) {
192				$sidebars_widgets[ $key ] = array_diff( $sb, array( $widget_id ) );
193			}
194		}
195		array_splice( $sidebars_widgets[ $sidebar_id ], $position, 0, $widget_id );
196	}
197
198	wp_set_sidebars_widgets( $sidebars_widgets );
199	wp_redirect( admin_url( 'widgets.php?message=0' ) );
200	exit;
201}
202
203// Remove inactive widgets without JS.
204if ( isset( $_POST['removeinactivewidgets'] ) ) {
205	check_admin_referer( 'remove-inactive-widgets', '_wpnonce_remove_inactive_widgets' );
206
207	if ( $_POST['removeinactivewidgets'] ) {
208		foreach ( $sidebars_widgets['wp_inactive_widgets'] as $key => $widget_id ) {
209			$pieces       = explode( '-', $widget_id );
210			$multi_number = array_pop( $pieces );
211			$id_base      = implode( '-', $pieces );
212			$widget       = get_option( 'widget_' . $id_base );
213			unset( $widget[ $multi_number ] );
214			update_option( 'widget_' . $id_base, $widget );
215			unset( $sidebars_widgets['wp_inactive_widgets'][ $key ] );
216		}
217
218		wp_set_sidebars_widgets( $sidebars_widgets );
219	}
220
221	wp_redirect( admin_url( 'widgets.php?message=0' ) );
222	exit;
223}
224
225// Output the widget form without JS.
226if ( isset( $_GET['editwidget'] ) && $_GET['editwidget'] ) {
227	$widget_id = $_GET['editwidget'];
228
229	if ( isset( $_GET['addnew'] ) ) {
230		// Default to the first sidebar.
231		$keys    = array_keys( $wp_registered_sidebars );
232		$sidebar = reset( $keys );
233
234		if ( isset( $_GET['base'] ) && isset( $_GET['num'] ) ) { // Multi-widget.
235			// Copy minimal info from an existing instance of this widget to a new instance.
236			foreach ( $wp_registered_widget_controls as $control ) {
237				if ( $_GET['base'] === $control['id_base'] ) {
238					$control_callback                                = $control['callback'];
239					$multi_number                                    = (int) $_GET['num'];
240					$control['params'][0]['number']                  = -1;
241					$control['id']                                   = $control['id_base'] . '-' . $multi_number;
242					$widget_id                                       = $control['id'];
243					$wp_registered_widget_controls[ $control['id'] ] = $control;
244					break;
245				}
246			}
247		}
248	}
249
250	if ( isset( $wp_registered_widget_controls[ $widget_id ] ) && ! isset( $control ) ) {
251		$control          = $wp_registered_widget_controls[ $widget_id ];
252		$control_callback = $control['callback'];
253	} elseif ( ! isset( $wp_registered_widget_controls[ $widget_id ] ) && isset( $wp_registered_widgets[ $widget_id ] ) ) {
254		$name = esc_html( strip_tags( $wp_registered_widgets[ $widget_id ]['name'] ) );
255	}
256
257	if ( ! isset( $name ) ) {
258		$name = esc_html( strip_tags( $control['name'] ) );
259	}
260
261	if ( ! isset( $sidebar ) ) {
262		$sidebar = isset( $_GET['sidebar'] ) ? $_GET['sidebar'] : 'wp_inactive_widgets';
263	}
264
265	if ( ! isset( $multi_number ) ) {
266		$multi_number = isset( $control['params'][0]['number'] ) ? $control['params'][0]['number'] : '';
267	}
268
269	$id_base = isset( $control['id_base'] ) ? $control['id_base'] : $control['id'];
270
271	// Show the widget form.
272	$width = ' style="width:' . max( $control['width'], 350 ) . 'px"';
273	$key   = isset( $_GET['key'] ) ? (int) $_GET['key'] : 0;
274
275	require_once ABSPATH . 'wp-admin/admin-header.php'; ?>
276	<div class="wrap">
277	<h1><?php echo esc_html( $title ); ?></h1>
278	<div class="editwidget"<?php echo $width; ?>>
279	<h2>
280	<?php
281	/* translators: %s: Widget name. */
282	printf( __( 'Widget %s' ), $name );
283	?>
284	</h2>
285
286	<form action="widgets.php" method="post">
287	<div class="widget-inside">
288	<?php
289	if ( is_callable( $control_callback ) ) {
290		call_user_func_array( $control_callback, $control['params'] );
291	} else {
292		echo '<p>' . __( 'There are no options for this widget.' ) . "</p>\n";
293	}
294	?>
295	</div>
296
297	<p class="describe"><?php _e( 'Select both the sidebar for this widget and the position of the widget in that sidebar.' ); ?></p>
298	<div class="widget-position">
299	<table class="widefat"><thead><tr><th><?php _e( 'Sidebar' ); ?></th><th><?php _e( 'Position' ); ?></th></tr></thead><tbody>
300	<?php
301	foreach ( $wp_registered_sidebars as $sbname => $sbvalue ) {
302		echo "\t\t<tr><td><label><input type='radio' name='sidebar' value='" . esc_attr( $sbname ) . "'" . checked( $sbname, $sidebar, false ) . " /> $sbvalue[name]</label></td><td>";
303		if ( 'wp_inactive_widgets' === $sbname || 'orphaned_widgets' === substr( $sbname, 0, 16 ) ) {
304			echo '&nbsp;';
305		} else {
306			if ( ! isset( $sidebars_widgets[ $sbname ] ) || ! is_array( $sidebars_widgets[ $sbname ] ) ) {
307				$j                           = 1;
308				$sidebars_widgets[ $sbname ] = array();
309			} else {
310				$j = count( $sidebars_widgets[ $sbname ] );
311				if ( isset( $_GET['addnew'] ) || ! in_array( $widget_id, $sidebars_widgets[ $sbname ], true ) ) {
312					$j++;
313				}
314			}
315			$selected = '';
316			echo "\t\t<select name='{$sbname}_position'>\n";
317			echo "\t\t<option value=''>" . __( '&mdash; Select &mdash;' ) . "</option>\n";
318			for ( $i = 1; $i <= $j; $i++ ) {
319				if ( in_array( $widget_id, $sidebars_widgets[ $sbname ], true ) ) {
320					$selected = selected( $i, $key + 1, false );
321				}
322				echo "\t\t<option value='$i'$selected> $i </option>\n";
323			}
324			echo "\t\t</select>\n";
325		}
326		echo "</td></tr>\n";
327	}
328	?>
329	</tbody></table>
330	</div>
331
332	<div class="widget-control-actions">
333		<div class="alignleft">
334			<?php if ( ! isset( $_GET['addnew'] ) ) : ?>
335				<input type="submit" name="removewidget" id="removewidget" class="button-link button-link-delete widget-control-remove" value="<?php _e( 'Delete' ); ?>" />
336				<span class="widget-control-close-wrapper">
337					| <a href="widgets.php" class="button-link widget-control-close"><?php _e( 'Cancel' ); ?></a>
338				</span>
339			<?php else : ?>
340				<a href="widgets.php" class="button-link widget-control-close"><?php _e( 'Cancel' ); ?></a>
341			<?php endif; ?>
342		</div>
343		<div class="alignright">
344			<?php submit_button( __( 'Save Widget' ), 'primary alignright', 'savewidget', false ); ?>
345			<input type="hidden" name="widget-id" class="widget-id" value="<?php echo esc_attr( $widget_id ); ?>" />
346			<input type="hidden" name="id_base" class="id_base" value="<?php echo esc_attr( $id_base ); ?>" />
347			<input type="hidden" name="multi_number" class="multi_number" value="<?php echo esc_attr( $multi_number ); ?>" />
348			<?php wp_nonce_field( "save-delete-widget-$widget_id" ); ?>
349		</div>
350		<br class="clear" />
351	</div>
352
353	</form>
354	</div>
355	</div>
356	<?php
357	require_once ABSPATH . 'wp-admin/admin-footer.php';
358	exit;
359}
360
361$messages = array(
362	__( 'Changes saved.' ),
363);
364
365$errors = array(
366	__( 'Error while saving.' ),
367	__( 'Error in displaying the widget settings form.' ),
368);
369
370require_once ABSPATH . 'wp-admin/admin-header.php';
371?>
372
373<div class="wrap">
374<h1 class="wp-heading-inline">
375<?php
376echo esc_html( $title );
377?>
378</h1>
379
380<?php
381if ( current_user_can( 'customize' ) ) {
382	printf(
383		' <a class="page-title-action hide-if-no-customize" href="%1$s">%2$s</a>',
384		esc_url(
385			add_query_arg(
386				array(
387					array( 'autofocus' => array( 'panel' => 'widgets' ) ),
388					'return' => urlencode( remove_query_arg( wp_removable_query_args(), wp_unslash( $_SERVER['REQUEST_URI'] ) ) ),
389				),
390				admin_url( 'customize.php' )
391			)
392		),
393		__( 'Manage with Live Preview' )
394	);
395}
396
397$nonce = wp_create_nonce( 'widgets-access' );
398?>
399<div class="widget-access-link">
400	<a id="access-on" href="widgets.php?widgets-access=on&_wpnonce=<?php echo urlencode( $nonce ); ?>"><?php _e( 'Enable accessibility mode' ); ?></a><a id="access-off" href="widgets.php?widgets-access=off&_wpnonce=<?php echo urlencode( $nonce ); ?>"><?php _e( 'Disable accessibility mode' ); ?></a>
401</div>
402
403<hr class="wp-header-end">
404
405<?php if ( isset( $_GET['message'] ) && isset( $messages[ $_GET['message'] ] ) ) { ?>
406<div id="message" class="updated notice is-dismissible"><p><?php echo $messages[ $_GET['message'] ]; ?></p></div>
407<?php } ?>
408<?php if ( isset( $_GET['error'] ) && isset( $errors[ $_GET['error'] ] ) ) { ?>
409<div id="message" class="error"><p><?php echo $errors[ $_GET['error'] ]; ?></p></div>
410<?php } ?>
411
412<?php
413/**
414 * Fires before the Widgets administration page content loads.
415 *
416 * @since 3.0.0
417 */
418do_action( 'widgets_admin_page' );
419?>
420
421<div class="widget-liquid-left">
422<div id="widgets-left">
423	<div id="available-widgets" class="widgets-holder-wrap">
424		<div class="sidebar-name">
425			<button type="button" class="handlediv hide-if-no-js" aria-expanded="true">
426				<span class="screen-reader-text"><?php _e( 'Available Widgets' ); ?></span>
427				<span class="toggle-indicator" aria-hidden="true"></span>
428			</button>
429			<h2><?php _e( 'Available Widgets' ); ?> <span id="removing-widget"><?php _ex( 'Deactivate', 'removing-widget' ); ?> <span></span></span></h2>
430		</div>
431		<div class="widget-holder">
432			<div class="sidebar-description">
433				<p class="description"><?php _e( 'To activate a widget drag it to a sidebar or click on it. To deactivate a widget and delete its settings, drag it back.' ); ?></p>
434			</div>
435			<div id="widget-list">
436				<?php wp_list_widgets(); ?>
437			</div>
438			<br class='clear' />
439		</div>
440		<br class="clear" />
441	</div>
442
443<?php
444
445$theme_sidebars = array();
446foreach ( $wp_registered_sidebars as $sidebar => $registered_sidebar ) {
447	if ( false !== strpos( $registered_sidebar['class'], 'inactive-sidebar' ) || 'orphaned_widgets' === substr( $sidebar, 0, 16 ) ) {
448		$wrap_class = 'widgets-holder-wrap';
449		if ( ! empty( $registered_sidebar['class'] ) ) {
450			$wrap_class .= ' ' . $registered_sidebar['class'];
451		}
452
453		$is_inactive_widgets = 'wp_inactive_widgets' === $registered_sidebar['id'];
454		?>
455		<div class="<?php echo esc_attr( $wrap_class ); ?>">
456			<div class="widget-holder inactive">
457				<?php wp_list_widget_controls( $registered_sidebar['id'], $registered_sidebar['name'] ); ?>
458
459				<?php if ( $is_inactive_widgets ) { ?>
460				<div class="remove-inactive-widgets">
461					<form action="" method="post">
462						<p>
463							<?php
464							$attributes = array( 'id' => 'inactive-widgets-control-remove' );
465
466							if ( empty( $sidebars_widgets['wp_inactive_widgets'] ) ) {
467								$attributes['disabled'] = '';
468							}
469
470							submit_button( __( 'Clear Inactive Widgets' ), 'delete', 'removeinactivewidgets', false, $attributes );
471							?>
472							<span class="spinner"></span>
473						</p>
474						<?php wp_nonce_field( 'remove-inactive-widgets', '_wpnonce_remove_inactive_widgets' ); ?>
475					</form>
476				</div>
477				<?php } ?>
478			</div>
479			<?php if ( $is_inactive_widgets ) { ?>
480			<p class="description"><?php _e( 'This will clear all items from the inactive widgets list. You will not be able to restore any customizations.' ); ?></p>
481			<?php } ?>
482		</div>
483		<?php
484
485	} else {
486		$theme_sidebars[ $sidebar ] = $registered_sidebar;
487	}
488}
489
490?>
491</div>
492</div>
493<?php
494
495$i                    = 0;
496$split                = 0;
497$single_sidebar_class = '';
498$sidebars_count       = count( $theme_sidebars );
499
500if ( $sidebars_count > 1 ) {
501	$split = (int) ceil( $sidebars_count / 2 );
502} else {
503	$single_sidebar_class = ' single-sidebar';
504}
505
506?>
507<div class="widget-liquid-right">
508<div id="widgets-right" class="wp-clearfix<?php echo $single_sidebar_class; ?>">
509<div class="sidebars-column-1">
510<?php
511
512foreach ( $theme_sidebars as $sidebar => $registered_sidebar ) {
513	$wrap_class = 'widgets-holder-wrap';
514	if ( ! empty( $registered_sidebar['class'] ) ) {
515		$wrap_class .= ' sidebar-' . $registered_sidebar['class'];
516	}
517
518	if ( $i > 0 ) {
519		$wrap_class .= ' closed';
520	}
521
522	if ( $split && $i === $split ) {
523		?>
524		</div><div class="sidebars-column-2">
525		<?php
526	}
527
528	?>
529	<div class="<?php echo esc_attr( $wrap_class ); ?>">
530		<?php
531		// Show the control forms for each of the widgets in this sidebar.
532		wp_list_widget_controls( $sidebar, $registered_sidebar['name'] );
533		?>
534	</div>
535	<?php
536
537	$i++;
538}
539
540?>
541</div>
542</div>
543</div>
544<form method="post">
545<?php wp_nonce_field( 'save-sidebar-widgets', '_wpnonce_widgets', false ); ?>
546</form>
547<br class="clear" />
548</div>
549
550<div class="widgets-chooser">
551	<ul class="widgets-chooser-sidebars"></ul>
552	<div class="widgets-chooser-actions">
553		<button class="button widgets-chooser-cancel"><?php _e( 'Cancel' ); ?></button>
554		<button class="button button-primary widgets-chooser-add"><?php _e( 'Add Widget' ); ?></button>
555	</div>
556</div>
557
558<?php
559
560/**
561 * Fires after the available widgets and sidebars have loaded, before the admin footer.
562 *
563 * @since 2.2.0
564 */
565do_action( 'sidebar_admin_page' );
566require_once ABSPATH . 'wp-admin/admin-footer.php';
567