From e8ed3fcbf15cb91eff0f38be7f9ddcf0cd441610 Mon Sep 17 00:00:00 2001 From: Eric Andrew Lewis Date: Thu, 11 Feb 2016 19:07:30 +0000 Subject: [PATCH] Menus: Allow larger menus to be created in the Edit Menu screen. In the Edit Menu screen, each menu item creates 11 form input elements. In menus with more than 71 menu items, often items after the 71st weren't saved. This was because PHP's runtime configuration `max_input_vars` default value is 1000. Large menus exceed this, so PHP didn't populate the `$_POST` superglobal for the latter menu items. The entire form is now JSON-encoded into a single input which populates `$_POST` manually on form submission. This was attempted previously in [36506] which was reverted in [36507]. Some form fields were not being slurped into the form's JSON representation, and it did not scale for a site with many posts. This approach fixes those problems. Props ocean90, afercia. See #14134. git-svn-id: https://develop.svn.wordpress.org/trunk@36510 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-admin/js/nav-menu.js | 12 ++++++++++++ src/wp-admin/nav-menus.php | 21 +++++++++++++++++++++ src/wp-includes/script-loader.php | 2 +- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/wp-admin/js/nav-menu.js b/src/wp-admin/js/nav-menu.js index 264dc574fc..de11878da5 100644 --- a/src/wp-admin/js/nav-menu.js +++ b/src/wp-admin/js/nav-menu.js @@ -43,6 +43,7 @@ var wpNavMenu; this.attachQuickSearchListeners(); this.attachThemeLocationsListeners(); + this.attachMenuSaveSubmitListeners(); this.attachTabsPanelListeners(); @@ -834,6 +835,17 @@ var wpNavMenu; }); }, + attachMenuSaveSubmitListeners : function() { + /* + * When a navigation menu is saved, store a JSON representation of all form data + * in a single input to avoid PHP `max_input_vars` limitations. See #14134. + */ + $('#update-nav-menu').submit(function() { + var navMenuData = $('#update-nav-menu').serializeArray(); + $('[name="nav-menu-data"]').val( JSON.stringify( navMenuData ) ); + }); + }, + attachThemeLocationsListeners : function() { var loc = $('#nav-menu-theme-locations'), params = {}; params.action = 'menu-locations-save'; diff --git a/src/wp-admin/nav-menus.php b/src/wp-admin/nav-menus.php index 8f17e03031..7a6a18c8ad 100644 --- a/src/wp-admin/nav-menus.php +++ b/src/wp-admin/nav-menus.php @@ -49,6 +49,26 @@ $num_locations = count( array_keys( $locations ) ); // Allowed actions: add, update, delete $action = isset( $_REQUEST['action'] ) ? $_REQUEST['action'] : 'edit'; +/* + * If a JSON blob of navigation menu data is found, expand it and inject it + * into `$_POST` to avoid PHP `max_input_vars` limitations. See #14134. + */ +if ( isset( $_POST['nav-menu-data'] ) ) { + $data = json_decode( stripslashes( $_POST['nav-menu-data'] ) ); + if ( ! is_null( $data ) && $data ) { + foreach ( $data as $post_input_data ) { + // For input names that are arrays (e.g. `menu-item-db-id[3]`), derive the array path keys via regex. + if ( preg_match( '#(.*)(?:\[(\d+)\])#', $post_input_data->name, $matches ) ) { + if ( empty( $_POST[$matches[1]] ) ) { + $_POST[$matches[1]] = array(); + } + $_POST[$matches[1]][(int)$matches[2]] = $post_input_data->value; + } else { + $_POST[$post_input_data->name] = $post_input_data->value; + } + } + } +} switch ( $action ) { case 'add-menu-item': check_admin_referer( 'add-menu_item', 'menu-settings-column-nonce' ); @@ -731,6 +751,7 @@ require_once( ABSPATH . 'wp-admin/admin-header.php' );