Probo Connect for WooCommerce

Building a Custom Uploader Type for Probo Connect

Every product that goes through Probo needs artwork — a PDF, an image, or sometimes nothing at all. Probo Connect ships with three built-in ways to handle this: the Probo Uploader (where customers upload their own files), Artwork via URL (where you specify a direct link), and No File (for products that don't need artwork).

How Uploader Types Work

Probo Connect uses an abstract class called Probo_Uploader_Type_Abstract that handles all the hook registration for you. When you extend it and instantiate your class, the following happens automatically:

  1. Product admin — Your type appears in the "File Uploader type" dropdown on the product edit screen.
  2. Add to cart — When a customer adds a product to their cart, Probo Connect fires probo_uploader_generate_{your_type} so you can create or prepare uploader data.
  3. Cart display — The filter probo_store_cart_render_uploader_{your_type} lets you render upload status or file info in the cart.
  4. Checkout — The action probo_save_order_item_meta_{your_type} lets you persist uploader data to the order.
  5. Order sync — The filter probo_order_uploader_type_{your_type} asks your type to return the file data that gets sent to the Probo API.
  6. Admin order view — The filter probo_admin_order_render_uploader_{your_type} lets you render file info in the WooCommerce order admin.

All of these hooks are registered in the constructor of the abstract class — keyed by whatever string your get_type() method returns. You don't need to register any hooks manually.

Product Type Support

Each uploader type declares which WooCommerce product types it supports:

  • normal — Standard WooCommerce products (simple, variable)
  • configurable — Probo Configurable products (the custom product type added by Probo Connect)

You set this via the $uploader_for property in your constructor.

What You'll Build

We'll create a plugin called "My Custom Probo Uploader" that adds a new uploader type: my_custom_file. This type lets store admins enter an external file URL and a description per product — a stripped-down but realistic example that touches every part of the lifecycle.

You can adapt this to pull files from wherever you need: a DAM, an API, a custom upload form — the pattern is the same.

Step 1: Create Your Plugin

Create a new folder in wp-content/plugins/ called my-custom-probo-uploader with a single main file:

wp-content/plugins/my-custom-probo-uploader/
    my-custom-probo-uploader.php

Here's the plugin bootstrap:

<?php
/**
  Plugin Name: My Custom Probo Uploader
  Description: Adds a custom uploader type to Probo Connect.
  Version: 1.0.0
  Requires Plugins: probo-connect
  Author: Your Name
 /

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

/**
  Initialize the custom uploader type after all plugins are loaded.
 
  We hook into 'plugins_loaded' to make sure Probo Connect's abstract class
  is available before we try to extend it.
 /
add_action( 'plugins_loaded', function () {

    // Safety check: Probo Connect must be active.
    if ( ! class_exists( 'Probo_Uploader_Type_Abstract' ) ) {
        return;
    }

    // Load and instantiate our custom uploader type.
    require_once DIR . '/class-my-custom-file-uploader.php';
    new My_Custom_File_Uploader();
} );

Two things to note here:

  1. Requires Plugins: probo-connect* — This WordPress 6.5+ header tells WordPress that your plugin depends on Probo Connect. WordPress will enforce the dependency in the admin.
  2. class_exists check — Even with the header, it's good practice to check at runtime. If someone deactivates Probo Connect, your plugin won't crash.

Step 2: Create the Uploader Type Class

Create class-my-custom-file-uploader.php in the same directory:

<?php
/**
  Custom file uploader type for Probo Connect.
 /

if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

class My_Custom_File_Uploader extends Probo_Uploader_Type_Abstract {

    /**
      Constructor.
     /
    public function __construct() {
        // This uploader works with standard (normal) WooCommerce products.
        // Use self::PROBO_UPLOADER_PRODUCT_CONFIGURABLE for configurable products,
        // or include both in the array for both types.
        $this->uploader_for = array( self::PROBO_UPLOADER_PRODUCT_DIRECT );

        // IMPORTANT: Always call the parent constructor.
        // This registers all the hooks automatically based on get_type().
        parent::__construct();
    }

    /**
      Unique key for this uploader type.
     
      This string is used in hook names, meta values, and the admin dropdown.
      Use lowercase with underscores. Must be unique across all uploader types.
     
      @return string
     /
    public function get_type(): string {
        return 'my_custom_file';
    }

    /*
      Human-readable label shown in the product admin dropdown.
     
      @return string
     /
    public function get_label(): string {
        return __( 'My Custom File', 'my-custom-probo-uploader' );
    }

    /**
      Return file data for the Probo API order payload.
     
      This is the most important method. When an order is synced to Probo,
      this method provides the file information for the order line item.
     
      @param array $files_object  Existing files data (usually empty).
      @param int   $product_id    The WooCommerce product ID.
      @param int   $item_id       The order item ID.
      @param array $uploader_data The uploader data stored on the order item.
      @return array File data for the Probo API.
     /
    public function probo_handle_order_uploader(
        array $files_object,
        int $product_id,
        int $item_id,
        array $uploader_data = array()
    ): array {
        $file_url = get_post_meta( $product_id, '_my_custom_file_url', true );

        if ( empty( $file_url ) ) {
            return array();
        }

        return array(
            'files' => array(
                array(
                    'uri' => $file_url,
                ),
            ),
        );
    }
}

That's it for the minimum viable uploader type. Activate your plugin, edit any simple WooCommerce product, go to the Probo Connect* tab, and you'll see "My Custom File" in the uploader type dropdown.

But right now it doesn't do much — there are no fields for the admin to fill in, and nothing shows in the order view. Let's fix that.

Step 3: Add Admin Product Fields

When an admin selects your uploader type, you probably want to show some configuration fields. Override two methods:

/**
  Declare which meta fields this uploader type uses.
 
  Fields listed here are automatically saved when the product is saved,
  thanks to Probo Connect's built-in save handler.
 
  @return array Array of post meta field names.
 /
public function get_admin_field_names(): array {
    return array(
        '_my_custom_file_url',
        '_my_custom_file_description',
    );
}

/*
  Render admin fields in the Probo Connect product tab.
 
  This fires when your uploader type is selected for the product.
  Use WooCommerce's built-in form helper functions.
 
  @param int $post_id The product post ID.
 */
public function probo_admin_product_render_uploader_fields( int $post_id ): void {
    ?>
    <hr />
    <div class="inline info woocommerce-message">
        <p class="help">
            <strong><?php esc_html_e( 'Custom File Settings', 'my-custom-probo-uploader' ); ?></strong><br/>
            <?php esc_html_e( 'Enter the URL of the print file for this product.', 'my-custom-probo-uploader' ); ?>
        </p>
    </div>
    <?php

    woocommerce_wp_text_input(
        array(
            'id'          => '_my_custom_file_url',
            'label'       => ( 'File URL', 'my-custom-probo-uploader' ),
            'description' => ( 'Direct URL to the print file (.pdf, .jpg, .png).', 'my-custom-probo-uploader' ),
            'desc_tip'    => true,
        )
    );

    woocommerce_wp_text_input(
        array(
            'id'          => '_my_custom_file_description',
            'label'       => ( 'File Description', 'my-custom-probo-uploader' ),
            'description' => ( 'Optional description for internal reference.', 'my-custom-probo-uploader' ),
            'desc_tip'    => true,
        )
    );
}

The fields you return from get_admin_field_names() are automatically saved when the admin saves the product — Probo Connect handles this through its product tab save handler.

Step 4: Show File Info in the Admin Order View

When a store admin views an order, it's helpful to see which file is attached. Override probo_admin_order_render_uploader():

/**
  Render file info in the WooCommerce admin order view.
 
  @param WC_Order_Item_Product $item       The order item.
  @param WC_Product            $product    The product.
  @param int                   $product_id The product ID.
  @return string HTML to display.
 */
public function probo_admin_order_render_uploader(
    WC_Order_Item_Product $item,
    WC_Product $product,
    int $product_id
): string {
    $file_url    = get_post_meta( $product_id, '_my_custom_file_url', true );
    $description = get_post_meta( $product_id, '_my_custom_file_description', true );

    if ( empty( $file_url ) ) {
        return '';
    }

    ob_start();
    ?>
    <div style="margin-top: 10px; padding: 10px; background: #f9f9f9; border: 1px solid #ddd;">
        <strong><?php esc_html_e( 'Print File:', 'my-custom-probo-uploader' ); ?></strong>
        <a href="<?php echo esc_url( $file_url ); ?>" target="_blank">
            <?php echo esc_html( basename( $file_url ) ); ?>
        </a>
        <?php if ( ! empty( $description ) ) : ?>
            <br /><em><?php echo esc_html( $description ); ?></em>
        <?php endif; ?>
    </div>
    <?php
    return ob_get_clean();
}

Step 5: Add Validation

Want to prevent a product from being added to the cart if no file URL is configured? Override validate_add_to_cart():

/**
  Validate that the product has a file URL before allowing add-to-cart.
 
  @param int $product_id The product ID.
  @return true|WP_Error True if valid, WP_Error to block with a message.
 */
public function validate_add_to_cart( int $product_id ) {
    $file_url = get_post_meta( $product_id, '_my_custom_file_url', true );

    if ( empty( $file_url ) ) {
        return new WP_Error(
            'missing_file',
            __( 'This product cannot be purchased because no print file has been configured.', 'my-custom-probo-uploader' )
        );
    }

    return true;
}

When validate_add_to_cart() returns a WP_Error, Probo Connect shows the error message as a WooCommerce notice and blocks the add-to-cart action.

Going Further

Here are some optional methods you can override for more advanced scenarios:

Cart rendering

Show file status or info in the WooCommerce cart:

public function probo_store_cart_render_uploader( array $cart_item, string $cart_item_key ) {
    $product_id = $cart_item['product_id'];
    $file_url   = get_post_meta( $product_id, '_my_custom_file_url', true );

    if ( ! empty( $file_url ) ) {
        return '<small>' . esc_html( basename( $file_url ) ) . '</small>';
    }
}

Cart item data processing

Enrich the cart item data when a product is added. Useful if your uploader type needs to store runtime data in the cart session:

public function process_cart_item_data( array $cart_item_data, int $product_id ): array {
    // Add any custom data that should travel with the cart item.
    $cart_item_data['_my_custom_file_url'] = get_post_meta( $product_id, '_my_custom_file_url', true );
    return $cart_item_data;
}

Order item meta saving

Persist custom data from the cart to the WooCommerce order when checkout completes:

public function save_order_item_meta( WC_Order_Item_Product $item, array $values ): void {
    if ( ! empty( $values['_my_custom_file_url'] ) ) {
        $item->add_meta_data( '_my_custom_file_url', $values['_my_custom_file_url'], true );
    }
}

Auto-order check

Control whether an order line item is eligible for automatic ordering. Return false to require manual review:

public function maybe_can_auto_order( WC_Order_Item_Product $item ): bool {
    // Only auto-order if we have a file URL.
    $product_id = $item->get_product_id();
    $file_url   = get_post_meta( $product_id, '_my_custom_file_url', true );
    return ! empty( $file_url );
}

Method Reference

All methods available on Probo_Uploader_Type_Abstract:

Method Required Description
get_type(): string Yes Unique key for your uploader type (e.g., my_custom_file). Used in hook names and stored as meta.
get_label(): string Yes Human-readable label shown in the product admin dropdown.
probo_handle_order_uploader(array, int, int, array): array Yes Returns the file data sent to the Probo API when an order is synced. Return a files array (direct URLs) or an uploaders array (Probo-hosted uploads).
get_admin_field_names(): array No Returns field names that should be auto-saved on the product. Default: empty array.
probo_admin_product_render_uploader_fields(int): void No Renders custom fields in the Probo Connect product tab. Default: no output.
probo_admin_product_save_uploader_fields(int): void No Custom save logic for admin fields. Default: no-op (fields from get_admin_field_names() are saved automatically).
probo_store_cart_render_uploader(array, string) No Renders uploader info in the cart. Default: no output.
probo_admin_order_render_uploader(WC_Order_Item_Product, WC_Product, int): string No Renders file info in the admin order view. Default: no output.
probo_uploader_generate(array, array) No Called when a product is added to the cart. Use this to create upload sessions or prepare data. Default: passes through cart item data.
validate_add_to_cart(int): true|WP_Error No Validates whether the product can be added to the cart. Return WP_Error to block with a message. Default: returns true.
process_cart_item_data(array, int): array No Enriches cart item data when a product is added. Default: passes through unmodified.
save_order_item_meta(WC_Order_Item_Product, array): void No Saves custom meta data to the order item during checkout. Default: no-op.
maybe_can_auto_order(WC_Order_Item_Product): bool No Controls whether the order item is eligible for auto-ordering. Default: returns true.
supports_product_type(string): bool No Checks if this uploader supports a given product type. Based on $uploader_for. Usually no need to override.

Constructor Properties

Property Type Description
$uploader_for array Product types this uploader supports. Use the class constants: self::PROBO_UPLOADER_PRODUCT_DIRECT ('normal') and self::PROBO_UPLOADER_PRODUCT_CONFIGURABLE ('configurable'). Set this before calling parent::__construct().