'; echo ''; } /** * @param array|null $preview */ private static function render_upload( ?array $preview ): void { $has_preview = is_array( $preview ) && ! empty( $preview['rows'] ); ?> >, snapshot_hint: string, filename?: string, summary?: array} $preview */ private static function render_preview( array $preview ): void { $summary = is_array( $preview['summary'] ?? null ) ? $preview['summary'] : Importer::summarize( $preview ); ?> $languages * @param list $regions */ private static function render_filters( string $lang, string $region, string $search, array $languages, array $regions ): void { $url = admin_url( 'admin.php?page=' . self::PAGE_SLUG ); ?> > $keys * @param list $labels */ private static function render_table( array $keys, array $labels ): void { echo '

' . esc_html__( 'Queries', '4wp-seo-helper' ) . '

'; if ( empty( $keys ) ) { echo '

' . esc_html__( 'No trends imported yet.', '4wp-seo-helper' ) . '

'; return; } echo ''; } /** * @param list> $keys */ private static function render_stop_list( array $keys ): void { echo ''; echo '

' . esc_html__( 'Excluded queries stay in the database but leave the main table and inventory Trends column.', '4wp-seo-helper' ) . '

'; if ( empty( $keys ) ) { echo '

' . esc_html__( 'Nothing excluded yet.', '4wp-seo-helper' ) . '

'; return; } echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; foreach ( $keys as $row ) { echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; } echo ''; } private static function exclude_form( int $key_id, bool $exclude ): string { $label = $exclude ? __( 'Exclude this query', '4wp-seo-helper' ) : __( 'Restore this query', '4wp-seo-helper' ); ob_start(); ?> > $batches */ private static function render_batches( array $batches ): void { echo '

' . esc_html__( 'Imported files', '4wp-seo-helper' ) . '

'; if ( empty( $batches ) ) { return; } echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; foreach ( $batches as $batch ) { $id = (int) $batch['id']; echo ''; echo ''; echo ''; echo ''; echo ''; echo ''; } echo '
' . esc_html__( 'File', '4wp-seo-helper' ) . '' . esc_html__( 'Rows', '4wp-seo-helper' ) . '' . esc_html__( 'Date', '4wp-seo-helper' ) . '
' . esc_html( (string) $batch['filename'] ) . '' . esc_html( (string) (int) $batch['rows_count'] ) . '' . esc_html( (string) $batch['created_at'] ) . ''; echo '
'; wp_nonce_field( 'forwp_trends_delete', 'forwp_trends_delete_nonce' ); echo ''; echo ''; echo ''; echo '
'; echo '
'; } private static function format_region( string $region ): string { if ( 'WORLDWIDE' === strtoupper( $region ) ) { return 'WW'; } return $region; } private static function process_post(): void { $action = isset( $_POST['forwp_trends_action'] ) ? sanitize_key( wp_unslash( (string) $_POST['forwp_trends_action'] ) ) : ''; if ( '' === $action ) { return; } if ( 'preview' === $action ) { check_admin_referer( 'forwp_trends_upload', 'forwp_trends_upload_nonce' ); self::handle_preview(); return; } if ( 'import' === $action ) { check_admin_referer( 'forwp_trends_import', 'forwp_trends_import_nonce' ); self::handle_import(); return; } if ( 'exclude' === $action ) { check_admin_referer( 'forwp_trends_exclude', 'forwp_trends_exclude_nonce' ); $key_id = isset( $_POST['key_id'] ) ? (int) $_POST['key_id'] : 0; $excluded = ! empty( $_POST['excluded'] ); Repository::set_excluded( $key_id, $excluded ); self::set_notice( 'success', $excluded ? __( 'Query moved to stop phrases.', '4wp-seo-helper' ) : __( 'Query restored to the table.', '4wp-seo-helper' ) ); wp_safe_redirect( admin_url( 'admin.php?page=' . self::PAGE_SLUG ) ); exit; } if ( 'delete' === $action ) { check_admin_referer( 'forwp_trends_delete', 'forwp_trends_delete_nonce' ); $batch_id = isset( $_POST['batch_id'] ) ? (int) $_POST['batch_id'] : 0; Importer::delete_batch( $batch_id ); self::set_notice( 'success', __( 'Batch deleted.', '4wp-seo-helper' ) ); wp_safe_redirect( admin_url( 'admin.php?page=' . self::PAGE_SLUG ) ); exit; } } private static function handle_preview(): void { $file = $_FILES['forwp_trends_file'] ?? null; if ( ! is_array( $file ) || empty( $file['tmp_name'] ) ) { self::set_notice( 'error', __( 'Choose a JSON or CSV file.', '4wp-seo-helper' ) ); return; } $name = (string) ( $file['name'] ?? '' ); if ( ! preg_match( '/\.(json|csv)$/i', $name ) ) { self::set_notice( 'error', __( 'JSON or CSV only.', '4wp-seo-helper' ) ); return; } $raw = file_get_contents( (string) $file['tmp_name'] ); if ( ! is_string( $raw ) ) { self::set_notice( 'error', __( 'Could not read the file.', '4wp-seo-helper' ) ); return; } $parsed = Parser::parse_file( $raw, $name ); if ( is_wp_error( $parsed ) ) { self::set_notice( 'error', $parsed->get_error_message() ); return; } $parsed['filename'] = sanitize_file_name( $name ); $parsed['summary'] = Importer::summarize( $parsed ); set_transient( Importer::PREVIEW_TRANSIENT . get_current_user_id(), $parsed, 15 * MINUTE_IN_SECONDS ); } private static function handle_import(): void { $parsed = get_transient( Importer::PREVIEW_TRANSIENT . get_current_user_id() ); if ( ! is_array( $parsed ) || empty( $parsed['rows'] ) ) { self::set_notice( 'error', __( 'Parse a file first.', '4wp-seo-helper' ) ); return; } $result = Importer::import( $parsed, (string) ( $parsed['filename'] ?? 'trends.json' ) ); delete_transient( Importer::PREVIEW_TRANSIENT . get_current_user_id() ); self::set_notice( 'success', sprintf( /* translators: %d: snapshot rows imported */ __( 'Imported %d snapshots.', '4wp-seo-helper' ), (int) ( $result['summary']['snapshots'] ?? 0 ) ) ); wp_safe_redirect( admin_url( 'admin.php?page=' . self::PAGE_SLUG ) ); exit; } private static function set_notice( string $type, string $message ): void { set_transient( 'forwp_trends_notice_' . get_current_user_id(), [ 'type' => $type, 'message' => $message, ], MINUTE_IN_SECONDS ); } }