/** * Handle menu uploads and downloads. * This is a callback for the 'admin_menu_editor_header' action. * * @param string $action * @return void */ function menu_editor_header($action = '') { $wp_menu_editor = $this->wp_menu_editor; //Handle menu download requests if ($action == 'download_menu') { $export = $this->get_exported_menu(); if (empty($export['menu']) || empty($export['filename'])) { die("Exported data not found"); } //Force file download header("Content-Description: File Transfer"); header('Content-Disposition: attachment; filename="' . $export['filename'] . '"'); header("Content-Type: application/force-download"); header("Content-Transfer-Encoding: binary"); header("Content-Length: " . strlen($export['menu'])); /* The three lines below basically make the download non-cacheable */ header("Cache-control: private"); header("Pragma: private"); header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); echo $export['menu']; die; //Handle menu uploads } elseif ($action == 'upload_menu') { header('Content-Type: text/html'); if (empty($_FILES['menu'])) { echo $wp_menu_editor->json_encode(array('error' => "No file specified")); die; } $file_data = $_FILES['menu']; if (filesize($file_data['tmp_name']) > $this->export_settings['max_file_size']) { $this->output_for_jquery_form($wp_menu_editor->json_encode(array('error' => "File too big"))); die; } //Check for general upload errors. if ($file_data['error'] != UPLOAD_ERR_OK) { switch ($file_data['error']) { case UPLOAD_ERR_INI_SIZE: $message = sprintf('The uploaded file exceeds the upload_max_filesize directive in php.ini. Limit: %s', strval(ini_get('upload_max_filesize'))); break; case UPLOAD_ERR_FORM_SIZE: $message = "The uploaded file exceeds the internal file size limit. Please contact the developer."; break; case UPLOAD_ERR_PARTIAL: $message = "The file was only partially uploaded"; break; case UPLOAD_ERR_NO_FILE: $message = "No file was uploaded"; break; case UPLOAD_ERR_NO_TMP_DIR: $message = "Missing a temporary folder"; break; case UPLOAD_ERR_CANT_WRITE: $message = "Failed to write file to disk"; break; case UPLOAD_ERR_EXTENSION: $message = "File upload stopped by a PHP extension"; break; default: $message = 'Unknown upload error #' . $file_data['error']; break; } $this->output_for_jquery_form($wp_menu_editor->json_encode(array('error' => $message))); die; } $file_contents = file_get_contents($file_data['tmp_name']); //Check if this file could plausibly contain an exported menu if (strpos($file_contents, $this->export_settings['old_format_string']) !== false) { //This is an exported menu in the old format. $data = $wp_menu_editor->json_decode($file_contents, true); if (!(isset($data['menu']) && is_array($data['menu']))) { $this->output_for_jquery_form($wp_menu_editor->json_encode(array('error' => "Unknown or corrupted file format"))); die; } try { $menu = ameMenu::load_array($data['menu'], false, true); } catch (InvalidMenuException $ex) { $this->output_for_jquery_form($wp_menu_editor->json_encode(array('error' => $ex->getMessage()))); die; } } else { if (strpos($file_contents, ameMenu::format_name) !== false) { //This is an export file in the new format. try { $menu = ameMenu::load_json($file_contents, false, true); } catch (InvalidMenuException $ex) { $this->output_for_jquery_form($wp_menu_editor->json_encode(array('error' => $ex->getMessage()))); die; } } else { //This is an unknown file. $this->output_for_jquery_form($wp_menu_editor->json_encode(array('error' => "Unknown file format"))); die; } } //Merge the imported menu with the current one. $menu['tree'] = $wp_menu_editor->menu_merge($menu['tree']); //Everything looks okay, send back the menu data $this->output_for_jquery_form(ameMenu::to_json($menu)); die; } }
private function display_editor_ui() { //Prepare a bunch of parameters for the editor. $editor_data = array('message' => isset($this->get['message']) ? intval($this->get['message']) : null, 'images_url' => plugins_url('images', $this->plugin_file), 'hide_advanced_settings' => $this->options['hide_advanced_settings'], 'show_extra_icons' => $this->options['show_extra_icons'], 'settings_page_url' => $this->get_settings_page_url(), 'show_deprecated_hide_button' => $this->options['show_deprecated_hide_button'], 'dashicons_available' => wp_style_is('dashicons', 'done')); //Build a tree struct. for the default menu $default_tree = ameMenu::wp2tree($this->default_wp_menu, $this->default_wp_submenu); $default_menu = ameMenu::load_array($default_tree); //Is there a custom menu? if (!empty($this->merged_custom_menu)) { $custom_menu = $this->merged_custom_menu; } else { //Start out with the default menu if there is no user-created one $custom_menu = $default_menu; } //The editor doesn't use the color CSS. Including it would just make the page bigger and waste bandwidth. unset($custom_menu['color_css']); unset($custom_menu['color_css_modified']); //Encode both menus as JSON $editor_data['default_menu_js'] = ameMenu::to_json($default_menu); $editor_data['custom_menu_js'] = ameMenu::to_json($custom_menu); //Create a list of all known capabilities and roles. Used for the drop-down list on the access field. $all_capabilities = ameRoleUtils::get_all_capabilities(); //"level_X" capabilities are deprecated so we don't want people using them. //This would look better with array_filter() and an anonymous function as a callback. for ($level = 0; $level <= 10; $level++) { $cap = 'level_' . $level; if (isset($all_capabilities[$cap])) { unset($all_capabilities[$cap]); } } $all_capabilities = array_keys($all_capabilities); natcasesort($all_capabilities); //Multi-site installs also get the virtual "Super Admin" cap, but only the Super Admin sees it. if (is_multisite() && !isset($all_capabilities['super_admin']) && is_super_admin()) { array_unshift($all_capabilities, 'super_admin'); } $editor_data['all_capabilities'] = $all_capabilities; //Create a list of all roles, too. $all_roles = ameRoleUtils::get_role_names(); asort($all_roles); $editor_data['all_roles'] = $all_roles; //Include hint visibility settings $editor_data['show_hints'] = $this->get_hint_visibility(); require dirname(__FILE__) . '/editor-page.php'; }
} } elseif ($second[$key] !== $value) { $difference[$key] = $value; } } return $difference; } add_action('admin_init', function () { global $wp_menu_editor; $menu = $wp_menu_editor->load_custom_menu(); if (empty($menu)) { return; } $compressed = ameMenu::compress($menu); $loaded = ameMenu::load_json(ameMenu::to_json($compressed)); $expected = ameMenu::load_json(ameMenu::to_json($menu)); $diff1 = ame_test_array_diff_assoc_recursive($expected, $loaded); $diff2 = ame_test_array_diff_assoc_recursive($loaded, $expected); if (!empty($diff1) || !empty($diff2)) { header('X-AME-Test-Failed: compression', true, 500); echo "<h1>Test failed: Compression causes data loss!</h1>"; echo "<p>Loading compressed and uncompressed versions of the same menu configuration produced different results.</p>"; echo '<p>Keys that are missing or different in the compressed configuration (expected vs compressed):</p>'; echo '<pre>'; echo htmlentities(print_r($diff1, true)); echo '</pre>'; echo '<h2>Reverse diff (compressed vs expected):</h2>'; echo '<pre>'; echo htmlentities(print_r($diff2, true)); echo '</pre>'; exit;
/** * Handle menu uploads and downloads. * This is a callback for the 'admin_menu_editor_header' action. * * @param string $action * @return void */ function menu_editor_header($action = '') { $wp_menu_editor = $this->wp_menu_editor; //Handle menu download requests if ($action == 'download_menu') { $export = $this->get_exported_menu(); if (empty($export['menu']) || empty($export['filename'])) { die("Exported data not found"); } //Force file download header("Content-Description: File Transfer"); header('Content-Disposition: attachment; filename="' . $export['filename'] . '"'); header("Content-Type: application/force-download"); header("Content-Transfer-Encoding: binary"); header("Content-Length: " . strlen($export['menu'])); /* The three lines below basically make the download non-cacheable */ header("Cache-control: private"); header("Pragma: private"); header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); echo $export['menu']; die; //Handle menu uploads } elseif ($action == 'upload_menu') { header('Content-Type: text/html'); if (empty($_FILES['menu'])) { echo $wp_menu_editor->json_encode(array('error' => "No file specified")); die; } $file_data = $_FILES['menu']; if (filesize($file_data['tmp_name']) > $this->export_settings['max_file_size']) { echo '<textarea cols="50" rows="5">', $wp_menu_editor->json_encode(array('error' => "File too big")), '</textarea>'; die; } $file_contents = file_get_contents($file_data['tmp_name']); //Check if this file could plausibly contain an exported menu if (strpos($file_contents, $this->export_settings['old_format_string']) !== false) { //This is an exported menu in the old format. $data = $wp_menu_editor->json_decode($file_contents, true); if (!(isset($data['menu']) && is_array($data['menu']))) { echo '<textarea cols="50" rows="5">', $wp_menu_editor->json_encode(array('error' => "Unknown or corrupted file format")), '</textarea>'; die; } try { $menu = ameMenu::load_array($data['menu']); } catch (InvalidMenuException $ex) { echo '<textarea cols="50" rows="5">', $wp_menu_editor->json_encode(array('error' => $ex->getMessage())), '</textarea>'; die; } } else { if (strpos($file_contents, ameMenu::format_name) !== false) { //This is an export file in the new format. try { $menu = ameMenu::load_json($file_contents); } catch (InvalidMenuException $ex) { echo '<textarea cols="50" rows="5">', $wp_menu_editor->json_encode(array('error' => $ex->getMessage())), '</textarea>'; die; } } else { //This is an unknown file. echo '<textarea cols="50" rows="5">', $wp_menu_editor->json_encode(array('error' => "Unknown file format")), '</textarea>'; die; } } //Merge the imported menu with the current one. $menu['tree'] = $wp_menu_editor->menu_merge($menu['tree']); //Everything looks okay, send back the menu data die('<textarea>' . ameMenu::to_json($menu) . '</textarea>'); } }
/** * Export the admin menu configuration as JSON. * * ## OPTIONS * * [<file>] * : Export file name. * * [--output] * : Dump the export data to the console instead of saving it as a file. * * [--file=<file>] * : An alternative way to specify the export file name. * * @param array $args * @param array $assoc_args */ public function export($args, $assoc_args) { if (empty($assoc_args['file']) && !empty($args[0])) { $assoc_args['file'] = $args[0]; } if (empty($assoc_args['file']) && empty($assoc_args['output'])) { WP_CLI::error('You must specify either a file name or the "--output" parameter.'); return; } $menuEditor = $this->getMenuEditor(); $customMenu = $menuEditor->load_custom_menu(); if (empty($customMenu)) { WP_CLI::error('Nothing to export. This site is using the default admin menu.'); return; } $json = ameMenu::to_json(ameMenu::compress($customMenu)); if (!empty($assoc_args['output'])) { WP_CLI::line($json); } else { $fileName = $assoc_args['file']; $bytesWritten = file_put_contents($fileName, $json); if ($bytesWritten > 0) { WP_CLI::success(sprintf('Export completed. %d bytes written.', $bytesWritten)); } else { WP_CLI::error('Failed to write the file.'); } } }
private function display_editor_ui() { //Prepare a bunch of parameters for the editor. $editor_data = array('message' => isset($this->get['message']) ? intval($this->get['message']) : null, 'images_url' => $this->plugin_dir_url . '/images', 'hide_advanced_settings' => $this->options['hide_advanced_settings']); //Build a tree struct. for the default menu $default_tree = ameMenu::wp2tree($this->default_wp_menu, $this->default_wp_submenu); $default_menu = ameMenu::load_array($default_tree); //Is there a custom menu? if (!empty($this->merged_custom_menu)) { $custom_menu = $this->merged_custom_menu; } else { //Start out with the default menu if there is no user-created one $custom_menu = $default_menu; } //Encode both menus as JSON $editor_data['default_menu_js'] = ameMenu::to_json($default_menu); $editor_data['custom_menu_js'] = ameMenu::to_json($custom_menu); //Create a list of all known capabilities and roles. Used for the drop-down list on the access field. $all_capabilities = ameRoleUtils::get_all_capabilities(); //"level_X" capabilities are deprecated so we don't want people using them. //This would look better with array_filter() and an anonymous function as a callback. for ($level = 0; $level <= 10; $level++) { $cap = 'level_' . $level; if (isset($all_capabilities[$cap])) { unset($all_capabilities[$cap]); } } $all_capabilities = array_keys($all_capabilities); natcasesort($all_capabilities); $editor_data['all_capabilities'] = $all_capabilities; //Create a list of all roles, too. $all_roles = ameRoleUtils::get_role_names(); //Multi-site installs also get the virtual "Super Admin" role if (is_multisite() && !isset($all_roles['super_admin'])) { $all_roles['super_admin'] = 'Super Admin'; } asort($all_roles); $editor_data['all_roles'] = $all_roles; //Include hint visibility settings $editor_data['show_hints'] = $this->get_hint_visibility(); require dirname(__FILE__) . '/editor-page.php'; }