235 lines
6.7 KiB
PHP
235 lines
6.7 KiB
PHP
<?php
|
|||
|
|
/**
|
||
|
|
* Eight fields on the menu item screen.
|
||
|
|
*
|
||
|
|
* WordPress core has offered `wp_nav_menu_item_custom_fields` since 5.4, so
|
||
|
|
* this needs no plugin and no custom admin page — the fields appear inside the
|
||
|
|
* item WordPress already draws, and save with it.
|
||
|
|
*
|
||
|
|
* Eight against UberMenu's 149. That is a real reduction in control and it is
|
||
|
|
* deliberate: what went away is per-item colour, per-item background image and
|
||
|
|
* per-item height, which is exactly the set that made the old menu a picture
|
||
|
|
* instead of a design. Colour comes from tokens, the ground comes from the
|
||
|
|
* parchment border-image, and height comes from the content.
|
||
|
|
*
|
||
|
|
* @package TLIG
|
||
|
|
*/
|
||
|
|
|
||
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
||
|
|
exit;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* The field definitions, in the order they are drawn.
|
||
|
|
*
|
||
|
|
* @return array<string,array<string,mixed>>
|
||
|
|
*/
|
||
|
|
function tlig_nav_fields(): array {
|
||
|
|
return [
|
||
|
|
'panel' => [
|
||
|
|
'type' => 'checkbox',
|
||
|
|
'label' => __( 'Opens a mega panel', 'tlig' ),
|
||
|
|
'help' => __( 'Top-level items only. The panel is as tall as its content.', 'tlig' ),
|
||
|
|
],
|
||
|
|
'heading' => [
|
||
|
|
'type' => 'checkbox',
|
||
|
|
'label' => __( 'Heading, not a link', 'tlig' ),
|
||
|
|
],
|
||
|
|
'span' => [
|
||
|
|
'type' => 'select',
|
||
|
|
'label' => __( 'Column width', 'tlig' ),
|
||
|
|
'options' => [
|
||
|
|
'' => __( 'Automatic', 'tlig' ),
|
||
|
|
'3' => __( 'Quarter', 'tlig' ),
|
||
|
|
'4' => __( 'Third', 'tlig' ),
|
||
|
|
'6' => __( 'Half', 'tlig' ),
|
||
|
|
'8' => __( 'Two thirds', 'tlig' ),
|
||
|
|
'12' => __( 'Full width', 'tlig' ),
|
||
|
|
],
|
||
|
|
],
|
||
|
|
'layout' => [
|
||
|
|
'type' => 'select',
|
||
|
|
'label' => __( 'Image position', 'tlig' ),
|
||
|
|
'options' => [
|
||
|
|
'' => __( 'No image', 'tlig' ),
|
||
|
|
'image_left' => __( 'Beside the label', 'tlig' ),
|
||
|
|
'image_below' => __( 'Above the label', 'tlig' ),
|
||
|
|
],
|
||
|
|
],
|
||
|
|
'image' => [
|
||
|
|
'type' => 'number',
|
||
|
|
'label' => __( 'Image (attachment ID)', 'tlig' ),
|
||
|
|
],
|
||
|
|
'icon' => [
|
||
|
|
'type' => 'select',
|
||
|
|
'label' => __( 'Icon', 'tlig' ),
|
||
|
|
'options' => [
|
||
|
|
'' => __( 'None', 'tlig' ),
|
||
|
|
'search' => __( 'Search', 'tlig' ),
|
||
|
|
'book' => __( 'Book', 'tlig' ),
|
||
|
|
'headphones' => __( 'Headphones', 'tlig' ),
|
||
|
|
],
|
||
|
|
],
|
||
|
|
'feed' => [
|
||
|
|
'type' => 'number',
|
||
|
|
'label' => __( 'List children of page ID', 'tlig' ),
|
||
|
|
'help' => __( 'Generated at render time, so new pages appear by themselves.', 'tlig' ),
|
||
|
|
],
|
||
|
|
'block' => [
|
||
|
|
'type' => 'textarea',
|
||
|
|
'label' => __( 'HTML block', 'tlig' ),
|
||
|
|
'help' => __( 'An escape hatch. Filtered through wp_kses_post, so it cannot carry a stylesheet.', 'tlig' ),
|
||
|
|
],
|
||
|
|
];
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Draws the fields inside the menu item.
|
||
|
|
*
|
||
|
|
* @param int $item_id Menu item post ID.
|
||
|
|
*/
|
||
|
|
function tlig_nav_item_fields( $item_id ): void {
|
||
|
|
$nav = tlig_nav_meta( (int) $item_id );
|
||
|
|
|
||
|
|
wp_nonce_field( 'tlig_nav_' . $item_id, 'tlig_nav_nonce_' . $item_id );
|
||
|
|
|
||
|
|
echo '<fieldset class="field-tlig-nav description-wide" style="margin:10px 0;padding:8px 10px;border:1px solid #dcdcde;">';
|
||
|
|
echo '<legend style="font-weight:600;padding:0 4px;">' . esc_html__( 'TLIG navigation', 'tlig' ) . '</legend>';
|
||
|
|
|
||
|
|
foreach ( tlig_nav_fields() as $key => $field ) {
|
||
|
|
$name = sprintf( 'tlig_nav[%d][%s]', $item_id, $key );
|
||
|
|
$id = sprintf( 'tlig-nav-%d-%s', $item_id, $key );
|
||
|
|
$value = $nav[ $key ] ?? '';
|
||
|
|
|
||
|
|
// `feed` is stored as an array so the walker can grow it later; the
|
||
|
|
// screen only ever asks for the parent, which is the part an editor
|
||
|
|
// would change.
|
||
|
|
if ( 'feed' === $key && is_array( $value ) ) {
|
||
|
|
$value = $value['parent'] ?? '';
|
||
|
|
}
|
||
|
|
|
||
|
|
echo '<p class="description description-wide" style="margin:6px 0;">';
|
||
|
|
|
||
|
|
switch ( $field['type'] ) {
|
||
|
|
case 'checkbox':
|
||
|
|
printf(
|
||
|
|
'<label for="%1$s"><input type="checkbox" id="%1$s" name="%2$s" value="1"%3$s> %4$s</label>',
|
||
|
|
esc_attr( $id ),
|
||
|
|
esc_attr( $name ),
|
||
|
|
checked( ! empty( $value ), true, false ),
|
||
|
|
esc_html( $field['label'] )
|
||
|
|
);
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 'select':
|
||
|
|
printf( '<label for="%s">%s<br>', esc_attr( $id ), esc_html( $field['label'] ) );
|
||
|
|
printf( '<select id="%s" name="%s" class="widefat">', esc_attr( $id ), esc_attr( $name ) );
|
||
|
|
foreach ( $field['options'] as $opt => $text ) {
|
||
|
|
printf(
|
||
|
|
'<option value="%s"%s>%s</option>',
|
||
|
|
esc_attr( $opt ),
|
||
|
|
selected( (string) $value, (string) $opt, false ),
|
||
|
|
esc_html( $text )
|
||
|
|
);
|
||
|
|
}
|
||
|
|
echo '</select></label>';
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 'textarea':
|
||
|
|
printf(
|
||
|
|
'<label for="%1$s">%2$s<br><textarea id="%1$s" name="%3$s" rows="3" class="widefat">%4$s</textarea></label>',
|
||
|
|
esc_attr( $id ),
|
||
|
|
esc_html( $field['label'] ),
|
||
|
|
esc_attr( $name ),
|
||
|
|
esc_textarea( (string) $value )
|
||
|
|
);
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
printf(
|
||
|
|
'<label for="%1$s">%2$s<br><input type="number" id="%1$s" name="%3$s" value="%4$s" class="widefat"></label>',
|
||
|
|
esc_attr( $id ),
|
||
|
|
esc_html( $field['label'] ),
|
||
|
|
esc_attr( $name ),
|
||
|
|
esc_attr( (string) $value )
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
if ( ! empty( $field['help'] ) ) {
|
||
|
|
printf( '<span class="description" style="display:block;color:#646970;">%s</span>', esc_html( $field['help'] ) );
|
||
|
|
}
|
||
|
|
|
||
|
|
echo '</p>';
|
||
|
|
}
|
||
|
|
|
||
|
|
echo '</fieldset>';
|
||
|
|
}
|
||
|
|
add_action( 'wp_nav_menu_item_custom_fields', 'tlig_nav_item_fields', 10, 1 );
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Saves them.
|
||
|
|
*
|
||
|
|
* Stored as one array under `_tlig_nav` rather than eight meta rows: the whole
|
||
|
|
* point is that an item's configuration is small enough to read at a glance.
|
||
|
|
* An empty configuration deletes the row instead of writing an empty array, so
|
||
|
|
* the 214 items that need no settings carry no meta at all.
|
||
|
|
*
|
||
|
|
* @param int $menu_id Menu term ID.
|
||
|
|
* @param int $item_id Menu item post ID.
|
||
|
|
*/
|
||
|
|
function tlig_nav_item_save( $menu_id, $item_id ): void {
|
||
|
|
$nonce = $_POST[ 'tlig_nav_nonce_' . $item_id ] ?? '';
|
||
|
|
|
||
|
|
if ( ! wp_verify_nonce( sanitize_key( $nonce ), 'tlig_nav_' . $item_id ) ) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
$raw = $_POST['tlig_nav'][ $item_id ] ?? [];
|
||
|
|
if ( ! is_array( $raw ) ) {
|
||
|
|
$raw = [];
|
||
|
|
}
|
||
|
|
|
||
|
|
$nav = [];
|
||
|
|
|
||
|
|
foreach ( tlig_nav_fields() as $key => $field ) {
|
||
|
|
$value = $raw[ $key ] ?? '';
|
||
|
|
|
||
|
|
switch ( $field['type'] ) {
|
||
|
|
case 'checkbox':
|
||
|
|
if ( ! empty( $value ) ) {
|
||
|
|
$nav[ $key ] = 1;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 'select':
|
||
|
|
$value = (string) $value;
|
||
|
|
if ( '' !== $value && isset( $field['options'][ $value ] ) ) {
|
||
|
|
$nav[ $key ] = 'span' === $key ? (int) $value : $value;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 'textarea':
|
||
|
|
$value = wp_kses_post( (string) $value );
|
||
|
|
if ( '' !== trim( $value ) ) {
|
||
|
|
$nav[ $key ] = $value;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
$value = (int) $value;
|
||
|
|
if ( $value > 0 ) {
|
||
|
|
$nav[ $key ] = 'feed' === $key
|
||
|
|
? [ 'parent' => $value, 'count' => -1, 'orderby' => 'title' ]
|
||
|
|
: $value;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if ( $nav ) {
|
||
|
|
update_post_meta( $item_id, '_tlig_nav', $nav );
|
||
|
|
} else {
|
||
|
|
delete_post_meta( $item_id, '_tlig_nav' );
|
||
|
|
}
|
||
|
|
}
|
||
|
|
add_action( 'wp_update_nav_menu_item', 'tlig_nav_item_save', 10, 2 );
|