self::ACTION, 's' => isset( $filters['search'] ) ? $filters['search'] : '', 'status' => isset( $filters['status'] ) ? $filters['status'] : '', 'batch' => isset( $filters['batch'] ) ? $filters['batch'] : '', ), 'strlen' ); return wp_nonce_url( add_query_arg( $args, admin_url( 'admin-post.php' ) ), self::ACTION ); } /** * Sends the CSV file. */ public function handle() { if ( ! current_user_can( NovoVerify_Config::CAPABILITY ) ) { wp_die( esc_html__( 'You do not have permission to access this page.', 'novoverify' ), 403 ); } check_admin_referer( self::ACTION ); // phpcs:disable WordPress.Security.NonceVerification.Recommended -- Nonce checked above. $filters = array( 'search' => isset( $_REQUEST['s'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['s'] ) ) : '', 'status' => isset( $_REQUEST['status'] ) ? sanitize_key( wp_unslash( $_REQUEST['status'] ) ) : '', 'batch' => isset( $_REQUEST['batch'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['batch'] ) ) : '', ); // phpcs:enable WordPress.Security.NonceVerification.Recommended $filename = 'novoverify-codes-' . gmdate( 'Y-m-d' ) . '.csv'; nocache_headers(); header( 'Content-Type: text/csv; charset=utf-8' ); header( 'Content-Disposition: attachment; filename="' . $filename . '"' ); header( 'X-Content-Type-Options: nosniff' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen -- Streaming to the response body, not the file system. $output = fopen( 'php://output', 'w' ); // UTF-8 byte order mark so spreadsheet apps detect the encoding. // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fwrite -- Streaming to the response body. fwrite( $output, "\xEF\xBB\xBF" ); // Column names are machine-readable keys so the file can be imported again. fputcsv( $output, array( 'code', 'product_name', 'batch', 'status', 'check_count', 'first_checked_at', 'last_checked_at', 'created_at' ), ',', '"', '' ); $last_id = 0; do { $rows = NovoVerify_DB::get_codes_after( $last_id, self::CHUNK_SIZE, $filters ); $rows_count = count( $rows ); foreach ( $rows as $row ) { $last_id = (int) $row->id; fputcsv( $output, array( '' !== $row->display_code ? $row->display_code : $row->code, NovoVerify_Csv::escape_cell( $row->product_name ), NovoVerify_Csv::escape_cell( $row->batch ), $row->status, (int) $row->check_count, self::local_time( $row->first_checked_at ), self::local_time( $row->last_checked_at ), self::local_time( $row->created_at ), ), ',', '"', '' ); } flush(); } while ( self::CHUNK_SIZE === $rows_count ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose -- See fopen() above. fclose( $output ); exit; } /** * Converts a UTC date to the site's timezone in a sortable format. * * @param string|null $utc UTC date. * @return string */ private static function local_time( $utc ) { return NovoVerify_Codes::format_datetime( $utc, 'Y-m-d H:i:s' ); } }