sanitize_text_field(wp_unslash($_POST['widget'] ?? '')), 'dashboard' => sanitize_text_field(wp_unslash($_POST['dashboard'] ?? '')), 'action' => sanitize_text_field(wp_unslash($_POST['action'] ?? '')), ]; // validate if (empty($input['widget'])) { self::fail('No widget specified.'); } // execute $data = $input + [ 'html' => Render::renderSingleWidgetMetabox($input['widget']), 'scripts' => Render::renderScripts(), ]; // new widget will be auto-stored by "meta-box-order" ajax (secondary request) // success self::success($data); } /** * Handler of "tekod-adwman-del-widget" ajax action. */ public static function handleDelWidget(): void { self::checkNonce(); // get params $input = [ 'widget' => sanitize_text_field(wp_unslash($_POST['widget'] ?? '')), 'dashboard' => sanitize_text_field(wp_unslash($_POST['dashboard'] ?? '')), 'action' => sanitize_text_field(wp_unslash($_POST['action'] ?? '')), ]; // validate if (empty($input['dashboard'])) { self::fail('No dashboard specified.', $input); } if (empty($input['widget'])) { self::fail('No widget specified.', $input); } // execute $success = Store::removeWidgetFromDashboard($input['dashboard'], $input['widget']); // response $data = $input; if ($success) { self::success($data); } else { self::fail('Unable to remove widget.', $data); } } /** * Handler of "tekod-adwman-add-dash" ajax action. */ public static function handleAddDashboard(): void { self::checkNonce(); // get params $input = [ 'name' => sanitize_text_field(wp_unslash($_POST['name'] ?? '')), 'action' => sanitize_text_field(wp_unslash($_POST['action'] ?? '')), ]; // validate if (empty($input['name'])) { self::fail('Name is required.'); } // execute Store::saveDashboard($input['name'], []); $data = [ 'action' => $input['action'], 'name' => $input['name'], 'widgets' => [], ]; // success self::success($data); } /** * Handler of "tekod-adwman-del-dash" ajax action. */ public static function handleDeleteDashboard(): void { self::checkNonce(); // get params $input = [ 'name' => sanitize_text_field(wp_unslash($_POST['name'] ?? '')), 'action' => sanitize_text_field(wp_unslash($_POST['action'] ?? '')), ]; // validate if (empty($input['name'])) { self::fail('Name is required.'); } // execute $data = [ 'action' => $input['action'], 'name' => $input['name'], 'deleted' => Store::deleteDashboard($input['name']), ]; // success self::success($data); } /** * Handler of "tekod-adwman-get-dash" ajax action. */ public static function handleGetDashboard(): void { self::checkNonce(); // get params $input = [ 'name' => sanitize_text_field(wp_unslash($_POST['name'] ?? '')), 'action' => sanitize_text_field(wp_unslash($_POST['action'] ?? '')), ]; // validate if (empty($input['name'])) { self::fail('Name is required.'); } // execute Widgets::getAllWidgets(); $dashStruct = Store::getDashboardByName($input['name']); $columns = Render::renderColumns($dashStruct); $scripts = Render::renderScripts(); $data = [ 'action' => $input['action'], 'name' => $input['name'], 'columns' => $columns, 'ids' => array_filter(explode(',', implode(',', $dashStruct))), 'scripts' => $scripts, ]; // success self::success($data); } /** * Handler of "tekod-adwman-rename-dash" ajax action. */ public static function handleRenameDashboard(): void { self::checkNonce(); // get params $input = [ 'dashboard' => sanitize_text_field(wp_unslash($_POST['dashboard'] ?? '')), 'name' => sanitize_text_field(wp_unslash($_POST['name'] ?? '')), 'action' => sanitize_text_field(wp_unslash($_POST['action'] ?? '')), ]; // validate if (empty($input['dashboard'])) { self::fail('No dashboard specified.', $input); } if (empty($input['name'])) { self::fail('Name is required.'); } // execute if (!Store::renameDashboard($input['dashboard'], $input['name'])) { self::fail('Specified dashboard not found.', $input); } // success self::success($input); } /** * Handler of "wp_ajax_tekod-adwman-view-mode" ajax action. */ public static function handleViewMode(): void { self::checkNonce(); // get params $input = [ 'mode' => sanitize_text_field(wp_unslash($_POST['mode'] ?? '')), 'action' => sanitize_text_field(wp_unslash($_POST['action'] ?? '')), ]; // validate if (empty($input['mode'])) { self::fail('No mode specified.', $input); } // execute $config = Store::getConfig(); $config['view'] = $input['mode']; Store::saveConfig($config); // success self::success($input); } /** * Handler of "wp_ajax_tekod-adwman-load-deferred" ajax action. */ public static function handleLoadDeferred(): void { self::checkNonce(); // get params $input = [ 'widget' => sanitize_text_field(wp_unslash($_POST['widget'] ?? '')), 'dashboard' => sanitize_text_field(wp_unslash($_POST['dashboard'] ?? '')), 'action' => sanitize_text_field(wp_unslash($_POST['action'] ?? '')), ]; // validate if (empty($input['widget'])) { self::fail('No widget specified.', $input); } if (empty($input['dashboard'])) { self::fail('No dashboard specified.', $input); } // execute $data = $input + [ 'html' => Render::renderSingleWidgetContent($input['widget'], $input['dashboard']), ]; // success self::success($data); } /** * Handler of "meta-box-order" ajax action. */ public static function handleWpMetaBoxOrder(): void { // beginning of this handler is copy of native wp_ajax_meta_box_order() function // sanitization issue: // i had to add sanitization of fetching from superglobal variables because it will not pass plugin approval process, // even though the original function in the WordPress core does not include it. check_ajax_referer('meta-box-order'); $order = isset($_POST['order']) ? array_map('sanitize_text_field', (array) wp_unslash($_POST['order'] ?? [])) : false; $page_columns = sanitize_text_field(wp_unslash($_POST['page_columns'] ?? 'auto')); if ('auto' !== $page_columns) { $page_columns = (int) $page_columns; } $page = sanitize_text_field(wp_unslash($_POST['page'] ?? '')); if (sanitize_key($page) !== $page) { wp_die(0); } $user = wp_get_current_user(); if (!$user) { wp_die(-1); } // custom storing mechanism self::handleWpMetaBoxOrderCustomized($order, $page_columns, $user, $page); // success wp_send_json_success(); } /** * Custom storing order of meta boxes. * * @param mixed $order * @param mixed $page_columns * @param \WP_User $user * @param string $page */ protected static function handleWpMetaBoxOrderCustomized(mixed $order, mixed $page_columns, \WP_User $user, string $page): void { $selectedDashboard = sanitize_text_field(wp_unslash($_POST['tekod_adwman_dashboard_name'] ?? '')); // nonce is checked in caller function Store::saveDashboard($selectedDashboard, $order); // send default dashboard to native storage if ($selectedDashboard === Store::getListOfDashboards()[0]) { if ($order) { update_user_meta($user->ID, "meta-box-order_$page", $order); } if ($page_columns) { update_user_meta($user->ID, "screen_layout_$page", $page_columns); } } // deregister native handler remove_action('wp_ajax_meta-box-order', 'wp_ajax_meta_box_order', 1); } /** * Terminate execution if nonce is not valid. */ protected static function checkNonce(): void { $nonce = sanitize_text_field(wp_unslash($_POST['nonce'] ?? '')); //$action = sanitize_text_field(wp_unslash($_POST['action'] ?? '')); if (wp_verify_nonce($nonce, 'tekod-adwman-nonce') === false) { self::fail('Invalid nonce.'); } } /** * Terminate current request with success. * * @param array $data */ protected static function success(array $data): void { // send example data $response = [ 'success' => true, 'data' => $data, ]; wp_send_json($response, 200); } /** * Terminate current request with failure. * * @param string $message * @param array $data * @param int $status */ protected static function fail(string $message, array $data = [], int $status = 200): void { $response = [ 'success' => false, 'data' => $data, 'error' => $message, ]; wp_send_json($response, $status); } }