Skip to main content

For developers

Everything below is stable, public surface — the filter contract a theme implements, the helpers a child theme or another extension can call, and the option keys behind the settings page.

The theme contract

The extension detects whether the active theme is WooCommerce-aware and wires its settings to one of two sets of filters. The detection happens at after_setup_theme (priority 99), after the theme has had its say.

A WooCommerce-aware theme

If the theme declares WooCommerce support itself, the extension steps aside and feeds these filters instead — the theme owns the markup and decides what to do with the values:

FilterDefaultPurpose
unysonplus_woocommerce_loop_columns3Shop grid columns
unysonplus_woocommerce_products_per_page12Products per page
unysonplus_woocommerce_sidebarnoneShop sidebar (none | left | right)
unysonplus_woocommerce_thumbnail_columns4Gallery thumbnail columns
unysonplus_woocommerce_related_count3Related-products count

A theme can also override them directly, which wins over the saved setting:

// Force a 4-column shop grid regardless of what is saved in the settings.
add_filter( 'unysonplus_woocommerce_loop_columns', function () {
return 4;
} );

Any other theme

When no theme integration is present, the same values are applied through WooCommerce's own filters — loop_shop_columns, loop_shop_per_page, woocommerce_product_thumbnails_columns and woocommerce_output_related_products_args — plus add_theme_support( 'woocommerce' ) and the gallery features, and a small baseline stylesheet.

You do not choose between the two paths; the extension picks the right one.

Helper functions

All of these are defined in the extension's helpers.php, are safe to call whether or not WooCommerce is active, and are guarded with function_exists() so they can be safely called from a theme that might run without the extension.

upwc_wc_catalog_mode()

if ( function_exists( 'upwc_wc_catalog_mode' ) && upwc_wc_catalog_mode() ) {
// The store is a lookbook — do not print a price or a cart button.
}

True when Catalog Mode is on, whether or not Disable Purchasing is on with it.

Catalog Mode works by unhooking WooCommerce's price / add-to-cart templates. Anything that renders a price or cart button without going through those hooks — a custom card, a widget, a theme partial — must check this helper, or it will keep selling from a shop that is meant to be a catalog.

upwc_wc_catalog_locked()

if ( function_exists( 'upwc_wc_catalog_locked' ) && upwc_wc_catalog_locked() ) {
return; // Nothing can be bought — this element has nothing to do.
}

True when Catalog Mode and Disable Purchasing are both on. Use it to hide anything that only makes sense in a shop that sells: a cart link, a mini-cart, a checkout button. The extension's own shop-only elements gate on exactly this.

upwc_wc_enquiry_html( $product )

echo upwc_wc_enquiry_html( $product ); // '' when the enquiry button is off or unconfigured

Returns the enquiry button markup for one product, or an empty string. Already escaped. Use it if you are rendering your own product card and want the same button the archives get.

Wishlist

upwc_wishlist_enabled(); // bool — feature on (and not catalog-locked)
upwc_wishlist_ids(); // int[] — this visitor's list, newest first
upwc_wishlist_has( $product_id ); // bool
upwc_wishlist_toggle( $product_id ); // [ 'ids' => int[], 'active' => bool ]
upwc_wishlist_save( $ids ); // int[] as stored
upwc_wishlist_button_html( $id ); // string — the heart, or '' when off

Signed-in visitors are stored in the upwc_wishlist user meta; guests in the upwc_wishlist cookie. upwc_wishlist_ids() reads whichever applies, so you rarely need to know which.

add_action( 'upwc_wishlist_saved', function ( $ids ) {
// Fires on every add, remove and login-merge.
} );

// Cap the list (default 200).
add_filter( 'upwc_wishlist_max_items', fn() => 50 );

Compare

upwc_compare_enabled(); // bool
upwc_compare_ids(); // int[]
upwc_compare_max(); // int (2–6)
upwc_compare_button_html( $id ); // string
upwc_compare_table_html( $ids ); // string — the side-by-side table

Back in stock

upwc_bis_enabled(); // bool
upwc_bis_emails( $product_id ); // string[] — who is waiting
upwc_bis_subscribe( $product_id, $email ); // bool
// Rewrite the restock email.
add_filter( 'upwc_bis_notification', function ( $mail, $product, $emails ) {
$mail['message'] = my_template( $product );
$mail['headers'] = [ 'Content-Type: text/html; charset=UTF-8' ];
return $mail;
}, 10, 3 );

// Cap sign-ups per product (default 2000).
add_filter( 'upwc_bis_max_subscribers', fn() => 500 );

Swatches

upwc_swatches_enabled(); // bool
upwc_swatches_on_cards(); // bool
upwc_swatches_card_html( $product ); // string — the card swatches
upwc_swatch_term_visual( $term ); // [ 'type' => color|image|label, 'value' => … ]
// Where a term's colour / image is read from — add your own plugin's keys.
add_filter( 'upwc_swatch_color_meta_keys', fn( $keys ) => [ ...$keys, 'my_swatch_hex' ] );
add_filter( 'upwc_swatch_image_meta_keys', fn( $keys ) => [ ...$keys, 'my_swatch_img' ] );

// Above this many options an attribute keeps its dropdown (default 15).
add_filter( 'upwc_swatches_max_options', fn() => 24 );

Sticky add-to-cart and size guide

upwc_sticky_atc_enabled(); // bool
upwc_size_guide_enabled(); // bool
upwc_size_guide_content( $product_id ); // string — product's own, else the store default

The per-product size guide is the _upwc_size_guide post meta; back-in-stock sign-ups are _upwc_bis_emails.

upwc_wc_truthy( $value )

Normalizes a stored switch value ('yes', '1', true, …) to a boolean. Settings are stored as strings, so compare with this rather than == true.

Reading a setting

Every value on the settings page is stored in the extension's settings option:

$columns = fw_get_db_ext_settings_option( 'woocommerce', 'shop_columns' );
$mode = upwc_wc_truthy( fw_get_db_ext_settings_option( 'woocommerce', 'catalog_mode' ) );

Option keys

KeyTypeDefault
shop_columnsselect 263
products_per_pagetext (number)12
shop_sidebarnone | left | rightnone
gallery_thumbnail_columnsselect 264
related_counttext (number)3
gallery_zoomyes | noyes
gallery_lightboxyes | noyes
gallery_slideryes | noyes
catalog_modeyes | nono
catalog_lock_purchasingyes | nono
catalog_closed_noticetextarea(empty)
catalog_enquiryyes | nono
catalog_enquiry_labeltextRequest a Quote
catalog_enquiry_urltext(empty)
sale_badge_styletext | percenttext
sticky_atcyes | nono
sticky_atc_positionbottom | topbottom
sticky_atc_imageyes | noyes
wishlistyes | nono
wishlist_pagetext (URL)(empty)
compareyes | nono
compare_pagetext (URL)(empty)
compare_maxtext (number)4
back_in_stockyes | nono
back_in_stock_labeltextEmail me when this is back
back_in_stock_subjecttext{product} is back in stock
swatchesyes | nono
swatches_cardsyes | nono
swatches_shapecircle | squarecircle
size_guideyes | nono
size_guide_labeltextSize guide
size_guide_contentwp-editor(empty)
ajax_add_to_cartyes | noyes
show_breadcrumbyes | noyes

Reacting to a save

add_action( 'fw_extension_settings_form_saved:woocommerce', function ( $before ) {
// $before = the values as they were prior to this save.
// Flush a cache, regenerate a stylesheet, …
} );

This fires whether the settings were saved from the Unyson+ → WooCommerce page or from the Extensions manager, so a listener does not need to know which screen was used.

What Disable Purchasing hooks

Useful to know if you are debugging why something will not add to the cart. When Catalog Mode and Disable Purchasing are both on, the extension registers:

HookValue
woocommerce_is_purchasablefalse (priority 99)
woocommerce_variation_is_purchasablefalse (priority 99)
woocommerce_get_price_html'' (priority 99)
woocommerce_loop_add_to_cart_link'' (priority 99)
woocommerce_add_to_cart_validationfalse (priority 99)
wp_loaded @5removes WC_Form_Handler::add_to_cart_action and scrubs the add-to-cart request parameter
template_redirectredirects Cart / Checkout to the shop, or renders the closed-shop message

These are registered during after_setup_theme rather than on wp, because WooCommerce's add-to-cart form handler runs on wp_loaded — waiting for the query would be too late to stop it.

To exempt something, run at a later priority than 99, or turn the setting off and implement your own policy.

AJAX endpoints

All of them are admin-ajax.php actions, nonce upwc_wc_storefront (localized to the front end as upwcStorefront.nonce), available to signed-in visitors and guests alike.

ActionBodyReturns
upwc_wc_wishlist_toggleproduct_idids, active, count
upwc_wc_wishlist_getids, count
upwc_wc_compare_toggleproduct_idids, items, active, full, max, message
upwc_wc_compare_getids, items, max
upwc_wc_compare_clearempty ids / items
upwc_wc_bis_subscribeproduct_id, emailmessage

All are rate-limited (fw_rate_limit_ajax); the back-in-stock one hardest, since it writes an address someone else will be emailed at.

The repaint event

Wishlist and compare controls are rendered "off" and switched on in the browser, so anything that injects product cards after load must say so:

document.dispatchEvent( new CustomEvent( 'upwc:products:updated' ) );

Load More, Quick View and AJAX filtering already do. Dispatch it from your own code if you inject cards another way.

The settings page

The settings page is FW_Woocommerce_Settings_Page and registers at admin.php?page=fw-woocommerce-settings, under the fw-extensions parent menu, on admin_menu priority 20. It only registers when the WooCommerce plugin is active.

It renders the extension's own settings-options.php schema through fw()->backend->render_options() and saves on the page's load- hook, merging over stored values rather than writing wholesale. The Extensions-manager card's Settings link is filtered (fw_ext_manager_settings_url) to point here, so there is a single settings screen rather than two that can disagree.

Element rendering

The product-card markup for [wc_products], [wc_product] and the Load More / Quick View AJAX endpoints is shared in includes/products-render.php, so the initial render, the appended pages and the modal produce identical output. The mini-cart markup is shared between the wc_mini_cart shortcode and the header/footer element by upwc_render_mini_cart() in includes/mini-cart-render.php.

If you are adding an element that prints a price or a cart button, gate it on upwc_wc_catalog_mode() / upwc_wc_catalog_locked() — that is the whole reason those helpers are public.