WP Manifestindependent plugin directory
manifest / developer / wp-xlsx-export

XLSX Export

Declare a dataset, get a nonce-protected, capability-checked, multi-sheet XLSX export in the WordPress admin

by Alex Mochulskyi · github.com/alexskybrain/wp-xlsx-export

★ 0stars
0forks

Install

No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:

wp plugin install https://github.com/alexskybrain/wp-xlsx-export/archive/refs/heads/main.zip

Every admin screen eventually gets the same request: "can we get this in Excel?" The usual answer is a CSV with the extension renamed, which Excel then mangles — leading zeros disappear, dates change meaning, and anything with a comma in it splits into two columns.

This plugin produces real .xlsx files, and makes adding a new export a matter of describing the data rather than writing another download handler.

Requirements

  • WordPress 6.4+
  • PHP 8.2+
  • PhpSpreadsheet, installed with Composer
composer install

Without it the plugin still loads, the export buttons explain what is missing, and nothing fatals.

Out of the box

Activating the plugin adds an Export users button to Users → All Users, producing a workbook with one sheet per role. That example is also the reference implementation: src/Datasets/UsersDataset.php is what a real dataset looks like.

Adding an export

Describe the data, and the nonce, the capability check, the HTTP headers and the workbook assembly are handled for you.

use XlsxExport\{Dataset, Sheet, Workbook};

final class OrdersDataset implements Dataset {
    public function key(): string        { return 'orders'; }
    public function label(): string      { return 'Export orders'; }
    public function capability(): string { return 'manage_woocommerce'; }

    public function accepted_args(): array {
        return ['status' => 'sanitize_key'];
    }

    public function build(array $args): Workbook {
        return (new Workbook('orders-' . gmdate('Y-m-d') . '.xlsx'))
            ->add(new Sheet('Orders', ['ID', 'Total'], $this->rows($args)));
    }

    private function rows(array $args): Generator {
        foreach (wc_get_orders(['status' => $args['status'] ?? 'any']) as $order) {
            yield [$order->get_id(), $order->get_total()];
        }
    }
}

add_action('xlsx_export_register_datasets', function ($registry) {
    $registry->add(new OrdersDataset());
});

Then place the button on any admin screen:

XlsxExport\ExportButton::render($registry->get('orders'), ['status' => 'completed']);

What the framework guarantees

The request is checked before it is read. A download must name a registered dataset, carry a nonce tied to that specific dataset, and come from a user who holds the capability that dataset declares. Exports routinely contain personal data, so the capability is per dataset rather than a blanket manage_options.

Query arguments are allow-listed. Only the arguments a dataset declares in accepted_args() are passed to build(), each through the sanitiser it named. An unexpected parameter never reaches a query.

Output buffers are discarded before the file is sent. A notice from an unrelated plugin, or a stray blank line after a closing PHP tag, otherwise ends up inside the archive and Excel offers to "repair" the file — which looks like data loss to whoever opened it.

Sheet titles are made valid and unique. Excel rejects \ / ? * [ ] : in a tab name and truncates at 31 characters. Two datasets whose names truncate to the same string is a realistic accident, so titles are deduplicated rather than silently overwriting each other.

Rows can be a generator. Nothing requires the full result set in memory; the bundled users export pages through WP_User_Query two hundred at a time.

Tests

php tests/workbook-test.php

Sheet naming and workbook assembly have no WordPress dependencies, so the fiddly parts — truncation, forbidden characters, deduplication, generator handling — are verified with plain PHP.

Architecture

src/
  Dataset.php           interface: key, label, capability, args, build
  Registry.php          the datasets this site can export
  Workbook.php          ordered sheets with unique, valid titles
  Sheet.php             title, headers, rows
  ExportController.php  guards the request, resolves the dataset
  XlsxWriter.php        PhpSpreadsheet, headers, streaming
  ExportButton.php      renders the link
  Datasets/             the bundled users example

License

GPL-2.0-or-later — see LICENSE.