$type, 'message' => $message, ), 60 ); wp_safe_redirect( $this->tools_url() ); exit; } /** * Handle the maintenance/import actions. * * @return void */ public function handle() { $this->guard(); $tool = isset( $_POST['at_tool'] ) ? sanitize_key( wp_unslash( $_POST['at_tool'] ) ) : ''; switch ( $tool ) { case 'starter': $result = Demo::import_starter(); if ( is_wp_error( $result ) ) { $this->redirect_back( 'error', $result->get_error_message() ); } $this->redirect_back( 'success', sprintf( /* translators: 1: testimonials count, 2: groups count, 3: images count. */ __( 'Starter demo imported: %1$d testimonials, %2$d groups, %3$d images.', 'advanced-testimonial' ), $result['testimonials'], $result['groups'], $result['images'] ) ); break; case 'import': $this->do_import(); break; case 'reset': delete_option( Settings::OPTION ); $this->redirect_back( 'success', __( 'Settings reset to defaults.', 'advanced-testimonial' ) ); break; case 'clear': Query::bust_cache(); delete_transient( DemoLibrary::TRANSIENT ); $this->redirect_back( 'success', __( 'Caches cleared — testimonial queries and the demo library list.', 'advanced-testimonial' ) ); break; case 'debug': $on = ! get_option( self::DEBUG_OPTION ); update_option( self::DEBUG_OPTION, $on ? 1 : 0, false ); $this->redirect_back( 'success', $on ? __( 'Debug mode enabled (query cache bypassed).', 'advanced-testimonial' ) : __( 'Debug mode disabled.', 'advanced-testimonial' ) ); break; default: $this->redirect_back( 'error', __( 'Unknown action.', 'advanced-testimonial' ) ); } } /** * Handle an uploaded demo file import. * * @return void */ private function do_import() { if ( empty( $_FILES['at_import_file']['tmp_name'] ) || ! is_uploaded_file( $_FILES['at_import_file']['tmp_name'] ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- verifying a PHP upload before reading it. $this->redirect_back( 'error', __( 'Please choose a .json file to import.', 'advanced-testimonial' ) ); } $tmp = sanitize_text_field( wp_unslash( $_FILES['at_import_file']['tmp_name'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput $name = isset( $_FILES['at_import_file']['name'] ) ? sanitize_file_name( wp_unslash( $_FILES['at_import_file']['name'] ) ) : ''; if ( 'json' !== strtolower( pathinfo( $name, PATHINFO_EXTENSION ) ) ) { $this->redirect_back( 'error', __( 'The file must be a .json export.', 'advanced-testimonial' ) ); } $data = wp_json_file_decode( $tmp, array( 'associative' => true ) ); if ( ! is_array( $data ) || ( empty( $data['testimonials'] ) && empty( $data['groups'] ) ) ) { $this->redirect_back( 'error', __( 'That file is not a valid Advanced Testimonial export.', 'advanced-testimonial' ) ); } $strategy = isset( $_POST['at_strategy'] ) ? sanitize_key( wp_unslash( $_POST['at_strategy'] ) ) : 'append'; $with_settings = ! empty( $_POST['at_with_settings'] ); if ( ! $with_settings ) { unset( $data['settings'] ); } $result = Demo::import( $data, $strategy ); $this->redirect_back( 'success', sprintf( /* translators: 1: testimonials, 2: groups, 3: images, 4: skipped. */ __( 'Import complete: %1$d testimonials, %2$d groups, %3$d images (%4$d skipped).', 'advanced-testimonial' ), $result['testimonials'], $result['groups'], $result['images'], $result['skipped'] ) ); } /** * Stream a JSON export as a download. * * @return void */ public function handle_export() { $this->guard(); $include = array( 'testimonials' => ! empty( $_POST['at_export_testimonials'] ), 'groups' => ! empty( $_POST['at_export_groups'] ), 'settings' => ! empty( $_POST['at_export_settings'] ), ); if ( ! $include['testimonials'] && ! $include['groups'] && ! $include['settings'] ) { $include['testimonials'] = true; $include['groups'] = true; } $data = Demo::export( $include ); $filename = 'advanced-testimonial-export-' . gmdate( 'Y-m-d' ) . '.json'; nocache_headers(); header( 'Content-Type: application/json; charset=utf-8' ); header( 'Content-Disposition: attachment; filename=' . $filename ); echo wp_json_encode( $data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ); exit; } /** * Output any one-time notice (called by the Settings page). * * @return void */ public static function maybe_notice() { $notice = get_transient( self::NOTICE_TRANSIENT ); if ( ! $notice ) { return; } delete_transient( self::NOTICE_TRANSIENT ); $class = ( 'error' === $notice['type'] ) ? 'notice-error' : ( 'warning' === $notice['type'] ? 'notice-warning' : 'notice-success' ); printf( '

%2$s

', esc_attr( $class ), esc_html( $notice['message'] ) ); } /** * Render the Tools tab panel (separate forms, not the settings form). * * @return void */ public static function render_panel() { $post_url = admin_url( 'admin-post.php' ); $debug_on = (bool) get_option( self::DEBUG_OPTION ); echo '
'; // Starter demo. echo '

' . esc_html__( 'Starter Demo', 'advanced-testimonial' ) . '

'; echo '

' . esc_html__( 'One-click sample content: 6 testimonials, 2 groups, ratings, companies and images. Existing testimonials are skipped.', 'advanced-testimonial' ) . '

'; echo '
'; wp_nonce_field( self::NONCE ); echo ''; echo ''; submit_button( __( 'Import Starter Demo', 'advanced-testimonial' ), 'primary', '', false ); echo '
'; // Import. echo '

' . esc_html__( 'Import', 'advanced-testimonial' ) . '

'; echo '
'; wp_nonce_field( self::NONCE ); echo ''; echo ''; echo '

'; echo '

' . esc_html__( 'Existing testimonials:', 'advanced-testimonial' ) . '

'; echo '


'; echo '
'; echo '

'; echo '

'; submit_button( __( 'Import File', 'advanced-testimonial' ), 'secondary', '', false ); echo '
'; // Export. echo '

' . esc_html__( 'Export', 'advanced-testimonial' ) . '

'; echo '
'; wp_nonce_field( self::NONCE ); echo ''; echo '


'; echo '
'; echo '

'; submit_button( __( 'Export (.json)', 'advanced-testimonial' ), 'secondary', '', false ); echo '
'; // Maintenance. echo '

' . esc_html__( 'Maintenance', 'advanced-testimonial' ) . '

'; echo '

'; self::action_button( $post_url, 'clear', __( 'Clear Cache', 'advanced-testimonial' ), 'secondary' ); self::action_button( $post_url, 'debug', $debug_on ? __( 'Disable Debug Mode', 'advanced-testimonial' ) : __( 'Enable Debug Mode', 'advanced-testimonial' ), 'secondary' ); self::action_button( $post_url, 'reset', __( 'Reset Settings', 'advanced-testimonial' ), 'delete', true ); echo '

'; echo '

' . ( $debug_on ? esc_html__( 'Debug mode is ON — query cache is bypassed.', 'advanced-testimonial' ) : esc_html__( 'Reset restores default settings. Testimonials are never deleted.', 'advanced-testimonial' ) ) . '

'; echo '
'; // System info. echo '

' . esc_html__( 'System Info', 'advanced-testimonial' ) . '

'; echo ''; echo '
'; echo '
'; } /** * Render a single inline action button (its own mini form). * * @param string $post_url admin-post.php URL. * @param string $tool Tool key. * @param string $label Button label. * @param string $type Button style. * @param bool $confirm Whether to confirm before submit. * @return void */ private static function action_button( $post_url, $tool, $label, $type, $confirm = false ) { echo '
'; wp_nonce_field( self::NONCE ); echo ''; echo ''; submit_button( $label, $type, '', false ); echo '
'; } /** * Build the System Info text. * * @return string */ private static function system_info() { $theme = wp_get_theme(); $counts = wp_count_posts( CPT::POST_TYPE ); $groups = wp_count_terms( array( 'taxonomy' => Taxonomy::TAXONOMY, 'hide_empty' => false, ) ); $lines = array( 'Plugin version : ' . ADVANCED_TESTIMONIAL_VERSION, 'WordPress : ' . get_bloginfo( 'version' ), 'PHP : ' . PHP_VERSION, 'Theme : ' . $theme->get( 'Name' ) . ' ' . $theme->get( 'Version' ), 'Memory limit : ' . WP_MEMORY_LIMIT, 'Testimonials : ' . ( isset( $counts->publish ) ? (int) $counts->publish : 0 ), 'Groups : ' . ( is_wp_error( $groups ) ? 0 : (int) $groups ), 'Debug mode : ' . ( get_option( self::DEBUG_OPTION ) ? 'on' : 'off' ), ); return implode( "\n", $lines ); } }