Milo Subscriptions, meet Breakdance: restrict content by subscription

This one started in our support inbox.

A customer was building client sites in Breakdance and wrote in with a simple question: “Using WooCommerce Subscriptions, we’re able to set conditions in Breakdance based on ‘User Subscriptions’ to display or hide content. Are we able to do this with your plugin somehow?”

Out of the box, the answer was “not yet.” But it turned out to be an easy gap to close, so we wrote a snippet for them. This post is that snippet, cleaned up and explained, so anyone can do the same today.

Once it’s in place, two new conditions show up in Breakdance:

They sit right alongside Breakdance’s built-in conditions, like logged-in status or user role. No shortcodes, no custom fields, just pick an element and gate it.

The snippet, explained bit by bit

Save the three blocks below, stacked in order, as a single file in wp-content/plugins/ (for example milo-breakdance-conditions.php) and activate it like any plugin. You can also paste the body into your theme’s functions.php or a Code Snippets plugin.

It’s inert unless both Breakdance and Milo Subscriptions are active, so it’s safe to leave it installed.

Block 1: the header, the hook, and a safety check

<?php
/**
 * Plugin Name: Milo Subscriptions: Breakdance Conditions
 * Description: Adds "User Subscription" display conditions to Breakdance, powered by Milo Subscriptions.
 * Version:     1.0.0
 */

defined( 'ABSPATH' ) || exit;

add_action(
	'breakdance_register_template_types_and_conditions',
	'milo_bd_register_conditions'
);

function milo_bd_register_conditions() {

	if (
		! function_exists( '\Breakdance\Themeless\registerCondition' )
		|| ! class_exists( '\Milo\Subscriptions\Manager' )
	) {
		return;
	}

What’s happening here:

Note we don’t close the function yet. The two conditions go inside it.

Block 2: the “User Subscription Status” condition

	// Condition 1 — User Subscription Status
	\Breakdance\Themeless\registerCondition( array(
		'supports'         => array( 'element_display', 'templating' ),
		'availableForType' => array( 'ALL' ),
		'slug'             => 'milo-user-subscription-status',
		'label'            => 'User Subscription Status',
		'category'         => 'Milo Subscriptions',
		// Breakdance flags third-party conditions as Pro-only by default, which
		// disables them on the free version. Opting out makes them work on both.
		'proOnly'          => false,
		'operands'         => array( 'is', 'is not' ),
		'valueInputType'   => 'dropdown',
		'values'           => function () {
			return array( array(
				'label' => 'Subscription',
				'items' => array(
					array( 'text' => 'Any active subscription', 'value' => 'any-active' ),
					array( 'text' => 'Active',                  'value' => 'active' ),
					array( 'text' => 'On hold',                 'value' => 'on-hold' ),
					array( 'text' => 'Pending',                 'value' => 'pending' ),
					array( 'text' => 'Pending cancellation',    'value' => 'pending-cancel' ),
					array( 'text' => 'Cancelled',               'value' => 'cancelled' ),
					array( 'text' => 'Expired',                 'value' => 'expired' ),
				),
			) );
		},
		'callback'         => function ( $operand, $value ) {
			$user_id = get_current_user_id();
			$has = false;
			if ( $user_id ) {
				// "Any active subscription" = Active OR Pending-cancellation
				// (still in a paid period). A specific status matches only itself.
				$statuses = ( 'any-active' === $value )
					? array( 'active', 'pending-cancel' )
					: array( (string) $value );
				$has = ! empty( \Milo\Subscriptions\Manager::get_user_subscriptions( $user_id, $statuses ) );
			}
			return ( 'is not' === $operand ) ? ! $has : $has;
		},
	) );

registerCondition takes one big array. Read it top to bottom and it’s mostly self-describing:

The interesting part is callback. This is the function that decides true or false when the page renders:

  1. Grab the current user. If nobody’s logged in, the answer is just false.
  2. Work out which statuses count as a match. If the user picked “Any active subscription,” we treat both active and pending-cancel as a pass, because someone who cancelled but is still inside a paid period still has access. Otherwise, we match only the exact status they chose.
  3. Ask Milo for the user’s subscriptions in those statuses. If the result isn’t empty, they have one, so $has is true.
  4. Finally, flip the result if the user chose “is not.”

That Manager::get_user_subscriptions() call is the entire bridge. It’s Milo’s public method, and it does the heavy lifting so the snippet doesn’t have to.

Block 3: the “Subscribed to Product” condition

	// Condition 2 — Subscribed to Product
	\Breakdance\Themeless\registerCondition( array(
		'supports'         => array( 'element_display', 'templating' ),
		'availableForType' => array( 'ALL' ),
		'slug'             => 'milo-subscribed-to-product',
		'label'            => 'Subscribed to Product',
		'category'         => 'Milo Subscriptions',
		// Same opt-out as above, so this condition works on free Breakdance too.
		'proOnly'          => false,
		'operands'         => array( 'is one of', 'is none of', 'is all of' ),
		'values'           => function () {
			$products = wc_get_products( array(
				'type'   => array( 'subscription', 'variable-subscription' ),
				'status' => 'publish',
				'limit'  => -1,
			) );
			return array( array(
				'label' => 'Subscription products',
				'items' => array_map( function ( $p ) {
					// Value MUST be a string. Breakdance validates condition
					// values as strings and rejects the payload otherwise.
					return array( 'text' => $p->get_name(), 'value' => (string) $p->get_id() );
				}, $products ),
			) );
		},
		'callback'         => function ( $operand, $value ) {
			$selected = array_map( 'intval', (array) $value );
			$owned    = array();
			$user_id  = get_current_user_id();
			if ( $user_id ) {
				$subs = \Milo\Subscriptions\Manager::get_user_subscriptions( $user_id, array( 'active', 'pending-cancel' ) );
				foreach ( $subs as $sub ) {
					foreach ( $sub->get_items() as $item ) {
						$owned[] = (int) $item->get_product_id();
						if ( $item->get_variation_id() ) {
							$owned[] = (int) $item->get_variation_id();
						}
					}
				}
			}
			$matches = array_map( function ( $id ) use ( $owned ) {
				return in_array( $id, $owned, true );
			}, $selected );

			if ( 'is one of'  === $operand ) { return in_array( true, $matches, true ); }
			if ( 'is none of' === $operand ) { return ! in_array( true, $matches, true ); }
			if ( 'is all of'  === $operand ) { return ! empty( $matches ) && ! in_array( false, $matches, true ); }
			return false;
		},
	) );
}

Same shape as the first condition, with two differences worth pointing out:

The final } closes the milo_bd_register_conditions function we opened back in Block 1. Stack the three blocks in order and that’s the complete plugin.


Using it in Breakdance

  1. Edit any page in Breakdance.
  2. Click the element you want to gate, say a “Members Only” section.
  3. In the right-hand panel, open Settings → Conditions.
  4. Click Add Condition and open the category dropdown. You’ll see a new Milo Subscriptions group.
  5. Pick User Subscription Status → is → Any active subscription.

Now that section only renders for customers with a live subscription. Everyone else never even receives the content. It’s removed server-side, not just hidden with CSS.

Want to gate by a specific plan instead? Use Subscribed to Product → is one of → [your product]. That’s the one for “Gold tier” content, course modules, or download areas tied to a particular subscription.

Bonus: gate a whole template

Because the conditions also support templating, you can apply them to an entire Breakdance Template, not just individual elements. Build a “Members Area” template, set its condition to User Subscription Status is Active, and the whole layout becomes subscriber-only.


A couple of things to know


Which builder should we do next?

This snippet came from one support request, and it turned into something every Breakdance user can use.

With that being said, which page builder do you reach for, and which one would you most like to see Milo support next? If you’re using something that isn’t Breakdance, Elementor, or Bricks, tell us. We’d like to know what to prioritise.