# Calorie Calculator — developer hooks

Use these WordPress actions/filters and front-end events to extend Calorie Calculator (Pro and Free) from a theme or custom plugin **without editing plugin files**.

Prefix: `calcal_` (PHP) and `calcal:` (JavaScript).

> Calculation (BMR / TDEE) runs in the browser. PHP hooks cannot change the maths mid-calc; use JS events for that moment, or alter markup/email/PDF/leads on the server.

---

## Quick start (PHP)

Place this in a custom plugin or your child theme’s `functions.php`:

```php
add_action( 'calcal_lead_created', function ( $post_id, $meta, $form_data ) {
	// e.g. push to your CRM, Slack, or custom table.
	error_log( 'CalCal lead #' . (int) $post_id . ' email=' . ( $meta['email'] ?? '' ) );
}, 10, 3 );

add_filter( 'calcal_calculator_html', function ( $html, $atts ) {
	return '<div class="my-brand-wrap">' . $html . '</div>';
}, 10, 2 );
```

---

## Calculator shortcode / embed

Fired for `[[calorie_calculator]]` (and Block / Elementor output that uses the same shortcode path).

> When this guide is published as a WordPress page, use **double brackets** (`[[calorie_calculator]]`) so WordPress shows the shortcode as text instead of rendering the calculator. Readers see a single `[calorie_calculator]`.

| Hook | Type | Arguments | Notes |
|------|------|-----------|--------|
| `calcal_calculator_atts` | filter | `$atts`, `$user_atts`, `$content` | After defaults / site settings are resolved, before enqueue + render. |
| `calcal_before_calculator` | action | `$atts` | Before template output. |
| `calcal_calculator_html` | filter | `$html`, `$atts` | After template render; return markup. |
| `calcal_after_calculator` | action | `$atts`, `$html` | After HTML is filtered (shortcode still returns `$html`). |

**Pro only — table shortcodes**

| Hook | Type | Arguments |
|------|------|-----------|
| `calcal_foods_table_atts` | filter | `$atts`, `$user_atts`, `$content` |
| `calcal_foods_table_html` | filter | `$html`, `$atts` |
| `calcal_exercises_table_atts` | filter | `$atts`, `$user_atts`, `$content` |
| `calcal_exercises_table_html` | filter | `$html`, `$atts` |

Example — force metric units on every embed:

```php
add_filter( 'calcal_calculator_atts', function ( $atts ) {
	$atts['unit'] = 'metric';
	return $atts;
} );
```

---

## Leads / usage logs

When a visitor submits the calculator and logging is enabled, a private `calculator_log` post is stored.

| Hook | Type | Arguments | Notes |
|------|------|-----------|--------|
| `calcal_lead_meta` | filter | `$meta`, `$form_data` | Sanitised meta before `wp_insert_post`. Return an array. |
| `calcal_lead_created` | action | `$post_id`, `$meta`, `$form_data` | After a successful log insert. Best place for custom CRM sync. |

`$meta` typically includes: `first_name`, `last_name`, `email`, `age`, `gender`, `height`, `weight`, `activity_factor`, `bmr` (TDEE), `basal_bmr`, `bmi`, `bmi_category`, `unit`, and on Pro also `macros`, `body_fat`, `formula_used`, `user_id` when logged in.

---

## Webhooks

| Hook | Type | Arguments | Notes |
|------|------|-----------|--------|
| `calcal_webhook_payload` | filter | `$body_data`, `$payload` | Full JSON body (includes `event`, `site`, `timestamp` + lead fields) before `wp_remote_post`. |

```php
add_filter( 'calcal_webhook_payload', function ( $body, $payload ) {
	$body['source'] = 'calorie-calculator';
	return $body;
}, 10, 2 );
```

---

## Integrations (Pro)

ConvertKit / HubSpot dispatch (after log create, when email is present):

| Hook | Type | Arguments |
|------|------|-----------|
| `calcal_integrations_lead` | filter | `$lead`, `$meta`, `$post_id` |
| `calcal_before_integrations` | action | `$lead`, `$meta`, `$post_id` |
| `calcal_after_integrations` | action | `$lead`, `$meta`, `$post_id` |

`$lead` keys: `email`, `first_name`, `last_name`, `consent`.

---

## Result email (Pro; Free has subject/body filters only)

| Hook | Type | Arguments | Notes |
|------|------|-----------|--------|
| `calcal_email_context` | filter | `$context`, `$recipient`, `$person`, `$calc`, `$result_html` | Token map (`{user_name}`, `{result}`, …). Pro. |
| `calcal_email_subject` | filter | `$subject`, `$context`, `$plugin` | |
| `calcal_email_body` | filter | `$body`, `$context`, `$plugin` | HTML body before `wp_mail`. |

```php
add_filter( 'calcal_email_subject', function ( $subject ) {
	return 'Your calorie plan from ' . get_bloginfo( 'name' );
} );
```

---

## PDF download (Pro)

| Hook | Type | Arguments |
|------|------|-----------|
| `calcal_pdf_filename` | filter | `$filename`, `$person_data`, `$calculator_data` |
| `calcal_pdf_person_data` | filter | `$person_data`, `$calculator_data` |
| `calcal_pdf_message` | filter | `$message`, `$person_data`, `$calculator_data` |
| `calcal_pdf_generated` | action | `$file_url`, `$file_path`, `$person_data`, `$calculator_data` |

Filenames must end in `.pdf` (unsafe values fall back to a safe default).

---

## Front-end JavaScript events

Events fire on `document` as both:

- jQuery: `jQuery( document ).on( 'calcal:…', function ( e, detail ) { … } );`
- Native: `document.addEventListener( 'calcal:…', function ( e ) { console.log( e.detail ); } );`

| Event | When | `detail` (typical) |
|-------|------|---------------------|
| `calcal:result_shown` | Results HTML displayed (or email-gated placeholder) | `shown`, `gated`, `formData`, `resultHtml` (if shown), `form`, `resultEl` |
| `calcal:lead_submitted` | AJAX log request succeeded | `unit` (`us` \| `metric`), `formData`, `form` |
| `calcal:form_cleared` | Clear / reset | `form` |

Legacy (still fired): `calcalpro_form_cleared` (jQuery only; second argument is the jQuery form).

```js
document.addEventListener( 'calcal:result_shown', function ( e ) {
	if ( ! e.detail || ! e.detail.shown ) {
		return;
	}
	// e.g. analytics, GTM dataLayer push
	window.dataLayer = window.dataLayer || [];
	window.dataLayer.push( { event: 'calcal_result', unit: e.detail.formData && e.detail.formData.unit } );
} );
```

---

## Stability

- Prefer the `calcal_*` / `calcal:` names above for new code.
- Do not rely on Freemius SDK hooks (`ccp_fs_loaded`, `$fs->…`) for product features.
- Hook signatures may gain extra trailing arguments in later versions; always declare the arity you need (`add_action( …, 10, 3 )`).

---

## Support

Product documentation: [Calorie Calculator Pro documentations](https://wp.zubaer.com/calorie-calculator-pro-documentations/)

Contact: [wp.zubaer.com/contact](https://wp.zubaer.com/contact)
