Skip to content

Developer reference

Catalog exposes five filters so add-ons and theme code can override its decisions per product or per visitor. Every storefront decision the plugin makes passes through one of them, which is how the planned Catalog Pro layers scheduled windows and per-role rules on top of the free plugin without forking it. Add these to your theme’s functions.php or a small site plugin.

The filters only run when the master switch (enabled) is on; with catalog mode off, none of them fire.

Whether catalog mode applies to the current visitor at all. Runs on every price and add-to-cart decision. The passed value is the free plugin’s role-rule result; return a boolean to override it. This is the right hook for time- or context-based rules.

// Only run catalog mode outside business hours.
add_filter( 'catalog/applies', function ( bool $applies ): bool {
$hour = (int) current_time( 'G' );
return ( $hour < 9 || $hour >= 17 ) ? $applies : false;
} );

Whether the price should be hidden for the product being rendered. Receives the free settings’ value and the WC_Product, so you can decide per product.

// Keep prices visible for one product, ID 4521, even under catalog mode.
add_filter( 'catalog/hide_price', function ( bool $hide, WC_Product $product ): bool {
return 4521 === $product->get_id() ? false : $hide;
}, 10, 2 );

Whether add-to-cart should be hidden — and the product made non-purchasable — for the given product. Same signature as catalog/hide_price. Use it for per-product or per-category exceptions, which the free settings screen doesn’t offer.

// Run catalog mode only on the "wholesale" product category.
add_filter( 'catalog/hide_add_to_cart', function ( bool $hide, WC_Product $product ): bool {
return has_term( 'wholesale', 'product_cat', $product->get_id() ) ? $hide : false;
}, 10, 2 );

Note: because hiding add-to-cart also sets the product non-purchasable, returning true here blocks direct cart URLs and the Store API for that product, not just the button.

The text shown in place of a hidden price. Receives the configured notice and the WC_Product. Return per-product copy, or an empty string to show nothing.

// Show stock-aware copy in place of the price.
add_filter( 'catalog/price_notice', function ( string $notice, WC_Product $product ): string {
return $product->is_in_stock() ? 'Call for pricing' : 'Currently unavailable';
}, 10, 2 );

The string is escaped (esc_html) before output, so return plain text, not HTML.

HTML rendered in place of add-to-cart when it’s hidden. Defaults to an empty string (nothing is shown), so this is the hook to add a “Request a quote” or “Enquire” button — the free settings screen has no button field. Receives the empty default, the WC_Product, and a $context of single or loop.

// Put an enquiry button where add-to-cart was, on single product pages only.
add_filter( 'catalog/add_to_cart_replacement', function ( string $html, WC_Product $product, string $context ): string {
if ( 'single' !== $context ) {
return $html;
}
$url = add_query_arg( 'product', $product->get_id(), home_url( '/enquiry/' ) );
return sprintf(
'<a class="button" href="%s">%s</a>',
esc_url( $url ),
esc_html__( 'Request a quote', 'your-textdomain' )
);
}, 10, 3 );

The returned HTML is printed as-is, so escape it yourself — Catalog trusts this filter’s output. On the single product page it replaces the add-to-cart form (at priority 30 of woocommerce_single_product_summary); in loops it replaces the add-to-cart link.

catalog/booted fires after the plugin boots and is an internal boot signal for bundled add-ons to extend the DI container — it is not a stable extension point for theme code and is intentionally undocumented for general use.

Key (in catalog_settings)TypeDefault
enabledbooltrue
hide_pricebooltrue
hide_add_to_cartbooltrue
price_noticestring''
role_modestringeveryone
role_liststring[][]

There are no custom tables. A catalog_db_version option tracks the schema version; both options are removed on uninstall.