Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
5b9c840
Add normalized form submission context
danieliser Aug 7, 2026
ee5c2d0
Modernize form integration script syntax
danieliser Aug 7, 2026
0ded074
Merge branch 'develop' into feature/form-submission-context
github-actions[bot] Aug 8, 2026
79882a2
Merge branch 'develop' into feature/form-submission-context
github-actions[bot] Aug 8, 2026
8ccf7e9
Merge branch 'develop' into feature/form-submission-context
github-actions[bot] Aug 9, 2026
a9e0c4a
Merge branch 'develop' into feature/form-submission-context
github-actions[bot] Aug 11, 2026
1d0110c
Merge branch 'develop' into feature/form-submission-context
github-actions[bot] Aug 11, 2026
8d7b06c
Merge branch 'develop' into feature/form-submission-context
github-actions[bot] Aug 11, 2026
a301e7a
Merge branch 'develop' into feature/form-submission-context
github-actions[bot] Aug 11, 2026
3913aeb
Merge branch 'develop' into feature/form-submission-context
github-actions[bot] Aug 11, 2026
371ef6d
Merge branch 'develop' into feature/form-submission-context
github-actions[bot] Aug 11, 2026
d9fec2e
Merge branch 'develop' into feature/form-submission-context
github-actions[bot] Aug 11, 2026
2c25369
Merge branch 'develop' into feature/form-submission-context
github-actions[bot] Aug 11, 2026
820539e
Merge branch 'develop' into feature/form-submission-context
github-actions[bot] Aug 11, 2026
439be61
Merge branch 'develop' into feature/form-submission-context
github-actions[bot] Aug 12, 2026
72e4f19
Merge branch 'develop' into feature/form-submission-context
github-actions[bot] Aug 12, 2026
57ade47
Merge branch 'develop' into feature/form-submission-context
github-actions[bot] Aug 12, 2026
2033b65
Merge branch 'develop' into feature/form-submission-context
github-actions[bot] Aug 12, 2026
99fdd08
Merge branch 'develop' into feature/form-submission-context
github-actions[bot] Aug 12, 2026
350582d
Merge branch 'develop' into feature/form-submission-context
github-actions[bot] Aug 12, 2026
16126cb
Merge branch 'develop' into feature/form-submission-context
github-actions[bot] Aug 12, 2026
dded681
Merge branch 'develop' into feature/form-submission-context
github-actions[bot] Aug 13, 2026
22362d7
Merge branch 'develop' into feature/form-submission-context
github-actions[bot] Aug 13, 2026
bf81398
Merge branch 'develop' into feature/form-submission-context
github-actions[bot] Aug 13, 2026
aea610e
Merge branch 'develop' into feature/form-submission-context
github-actions[bot] Aug 13, 2026
df56d46
Merge branch 'develop' into feature/form-submission-context
github-actions[bot] Aug 13, 2026
eb713d0
Merge branch 'develop' into feature/form-submission-context
github-actions[bot] Aug 13, 2026
13886fd
Merge branch 'develop' into feature/form-submission-context
github-actions[bot] Aug 13, 2026
9da2f20
Merge branch 'develop' into feature/form-submission-context
github-actions[bot] Aug 13, 2026
2b46171
Merge branch 'develop' into feature/form-submission-context
github-actions[bot] Aug 13, 2026
57c84dd
Merge branch 'develop' into feature/form-submission-context
github-actions[bot] Aug 13, 2026
6fdaef1
Merge branch 'develop' into feature/form-submission-context
github-actions[bot] Aug 13, 2026
7d3f2db
Merge branch 'develop' into feature/form-submission-context
github-actions[bot] Aug 13, 2026
c2138a8
Merge branch 'develop' into feature/form-submission-context
github-actions[bot] Aug 13, 2026
8cd6629
Merge branch 'develop' into feature/form-submission-context
github-actions[bot] Aug 13, 2026
0d5a9ff
Merge branch 'develop' into feature/form-submission-context
github-actions[bot] Aug 13, 2026
e1489be
Merge branch 'develop' into feature/form-submission-context
github-actions[bot] Aug 13, 2026
bc2b7c1
Merge branch 'develop' into feature/form-submission-context
github-actions[bot] Aug 13, 2026
e5efa24
Merge branch 'develop' into feature/form-submission-context
github-actions[bot] Aug 14, 2026
aa4c186
Merge branch 'develop' into feature/form-submission-context
github-actions[bot] Aug 14, 2026
b1c1da3
Merge branch 'develop' into feature/form-submission-context
github-actions[bot] Aug 14, 2026
910dc27
Merge branch 'develop' into feature/form-submission-context
github-actions[bot] Aug 14, 2026
0079a93
Merge branch 'develop' into feature/form-submission-context
github-actions[bot] Aug 14, 2026
bb2b50e
Merge branch 'develop' into feature/form-submission-context
github-actions[bot] Aug 15, 2026
7d5cfe2
Merge branch 'develop' into feature/form-submission-context
github-actions[bot] Aug 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 43 additions & 21 deletions assets/js/src/site/plugins/pum-integrations.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,17 @@
// Ensure PUM exists globally
window.PUM = window.PUM || {};
window.PUM.integrations = window.PUM.integrations || {};
const PUM = window.PUM;
const pumVars = window.pum_vars;

function filterNull( x ) {
return x;
}

$.extend( window.PUM.integrations, {
init: function () {
if ( 'undefined' !== typeof pum_vars.form_submission ) {
var submission = pum_vars.form_submission;
init() {
if ( pumVars && 'undefined' !== typeof pumVars.form_submission ) {
const submission = pumVars.form_submission;

// Declare these are not AJAX submissions.
submission.ajax = false;
Expand All @@ -39,19 +41,25 @@
* @param {Object} form JavaScript DOM node or jQuery object for the form submitted
* @param {Object} args {
* @type {string} formProvider Such as gravityforms or ninjaforms
* @type {string|int} formId Usually an integer ID number such as 1
* @type {int} formInstanceId Not all form plugins support this.
* @type {string|number} formId Usually an integer ID number such as 1
* @type {number} formInstanceId Not all form plugins support this.
* @type {string|number} submissionId Optional provider submission or entry ID.
* @type {number} sourcePostId Optional post/page ID where the form was submitted.
* @type {Object} context Extension-owned submission context.
* }
*/
formSubmission: function ( form, args ) {
var $popup = PUM.getPopup( form );
formSubmission( form, args ) {
const $popup = PUM.getPopup( form );

args = $.extend(
{
popup: $popup,
formProvider: null,
formId: null,
formInstanceId: null,
submissionId: null,
sourcePostId: null,
context: {},
formKey: null,
ajax: true, // Allows detecting submissions that may have already been counted.
tracked: false,
Expand All @@ -73,6 +81,17 @@
// $popup.trigger('pumFormSuccess');
}

/**
* Filters normalized form submission arguments before success handlers run.
*
* Extensions can append context without coupling to individual providers.
*/
args = window.PUM.hooks.applyFilters(
'pum.integration.form.submissionArgs',
args,
form
);

/**
* This hook fires after any integrated form is submitted successfully.
*
Expand All @@ -83,10 +102,13 @@
* @param {Object} form JavaScript DOM node or jQuery object for the form submitted
* @param {Object} args {
* @type {string} formProvider Such as gravityforms or ninjaforms
* @type {string|int} formId Usually an integer ID number such as 1
* @type {int} formInstanceId Not all form plugins support this.
* @type {string|number} formId Usually an integer ID number such as 1
* @type {number} formInstanceId Not all form plugins support this.
* @type {string|number} submissionId Optional provider submission or entry ID.
* @type {number} sourcePostId Optional post/page ID where the form was submitted.
* @type {Object} context Extension-owned submission context.
* @type {string} formKey Concatenation of provider, ID & Instance ID.
* @type {int} popupId The ID of the popup the form was in.
* @type {number} popupId The ID of the popup the form was in.
* @type {Object} popup Usable jQuery object for the popup.
* }
*/
Expand All @@ -96,14 +118,14 @@
args
);
},
checkFormKeyMatches: function (
checkFormKeyMatches(
formIdentifier,
formInstanceId,
submittedFormArgs
) {
formInstanceId = '' === formInstanceId ? formInstanceId : false;
// Check if the submitted form matches trigger requirements.
var checks = [
const checks = [
// Any supported form.
formIdentifier === 'any',

Expand Down Expand Up @@ -138,28 +160,28 @@
* @since 1.9.0
*
* @param {boolean} matchFound A boolean determining whether a match was found.
* @param {Object} args {
* @param {Object} args {
* @type {string} formIdentifier gravityforms_any or ninjaforms_1
* @type {int} formInstanceId Not all form plugins support this.
* @type {number} formInstanceId Not all form plugins support this.
* @type {Object} submittedFormArgs{
* @type {string} formProvider Such as gravityforms or ninjaforms
* @type {string|int} formId Usually an integer ID number such as 1
* @type {int} formInstanceId Not all form plugins support this.
* @type {string|number} formId Usually an integer ID number such as 1
* @type {number} formInstanceId Not all form plugins support this.
* @type {string} formKey Concatenation of provider, ID & Instance ID.
* @type {int} popupId The ID of the popup the form was in.
* @type {number} popupId The ID of the popup the form was in.
* @type {Object} popup Usable jQuery object for the popup.
* }
* }
*
* @returns {boolean}
* @return {boolean}
*/
return window.PUM.hooks.applyFilters(
'pum.integration.checkFormKeyMatches',
matchFound,
{
formIdentifier: formIdentifier,
formInstanceId: formInstanceId,
submittedFormArgs: submittedFormArgs,
formIdentifier,
formInstanceId,
submittedFormArgs,
}
);
},
Expand Down
25 changes: 22 additions & 3 deletions classes/Integration/Form/FluentForms.php
Original file line number Diff line number Diff line change
Expand Up @@ -112,13 +112,32 @@ public function on_success( $submission_id, $form_data, $form ) {

pum_integrated_form_submission(
[
'popup_id' => $popup_id,
'form_provider' => $this->key,
'form_id' => $form_id,
'popup_id' => $popup_id,
'form_provider' => $this->key,
'form_id' => $form_id,
'submission_id' => $submission_id,
'source_post_id' => $this->get_source_post_id(),
]
);
}

/**
* Resolve the post that supplied the submitted form.
*
* @return int|null
*/
private function get_source_post_id() {
$referer = wp_get_raw_referer();

if ( ! $referer ) {
return null;
}

$post_id = url_to_postid( $referer );

return $post_id ? $post_id : null;
}

/**
* Get the popup ID for this form submission.
*
Expand Down
2 changes: 2 additions & 0 deletions classes/Integrations.php
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,9 @@ public static function pum_vars( $vars = [] ) {
'form_provider' => 'formProvider',
'form_id' => 'formId',
'form_instance_id' => 'formInstanceId',
'submission_id' => 'submissionId',
'popup_id' => 'popupId',
'source_post_id' => 'sourcePostId',
]
);
}
Expand Down
42 changes: 42 additions & 0 deletions docs/form-submission-context.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Normalized form submission context

Popup Maker form integrations report successful submissions through
`pum_integrated_form_submission()` in PHP and
`PUM.integrations.formSubmission()` in JavaScript. Extensions can attach
portable, provider-independent data to the `context` object without coupling
their behavior to a specific form plugin.

```php
pum_integrated_form_submission( [
'form_provider' => 'example',
'form_id' => 12,
'submission_id' => 'entry-456',
'source_post_id' => 78,
'context' => [
'my_extension' => [
'campaign_id' => 90,
],
],
] );
```

JavaScript integrations use camel-cased keys. The
`pum.integration.form.submissionArgs` filter runs after Popup Maker has built
the form key and resolved the popup, but before conversion and success handlers
run.

```js
PUM.hooks.addFilter(
'pum.integration.form.submissionArgs',
( args ) => ( {
...args,
context: {
...args.context,
myExtension: { campaignId: 90 },
},
} )
);
```

Context is descriptive, not proof of authorization. Consumers must validate
untrusted values before using them for privileged operations.
6 changes: 6 additions & 0 deletions includes/functions/developers.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ function pum_trigger_popup_form_success( $popup_id = null, $settings = [] ) {
* @type string $form_provider Key indicating which form provider this form belongs to.
* @type string|int $form_id Form ID, usually numeric, but can be hash based.
* @type int $form_instance_id Optional form instance ID.
* @type string|int $submission_id Optional provider submission or entry ID.
* @type int $popup_id Optional popup ID.
* @type int $source_post_id Optional post/page ID where the form was submitted.
* @type array $context Optional extension-owned submission context.
* @type bool $ajax If the submission was processed via AJAX. Generally gonna be false outside of JavaScript.
* @type bool $tracked Whether the submission has been handled by tracking code or not. Prevents duplicates.
* }
Expand All @@ -57,6 +60,9 @@ function pum_integrated_form_submission( $args = [] ) {
'form_provider' => null,
'form_id' => null,
'form_instance_id' => null,
'submission_id' => null,
'source_post_id' => null,
'context' => [],
'ajax' => false,
'tracked' => false,
]
Expand Down
91 changes: 91 additions & 0 deletions tests/php/tests/FormSubmissionContext_Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<?php
/**
* Normalized form submission context tests.
*
* @package Popup_Maker
*/

/**
* Test the normalized form submission context contract.
*/
class FormSubmissionContext_Test extends WP_UnitTestCase {
/** @var callable|null Context filter registered by a test. */
private $context_filter;

/** @var callable|null Submission action registered by a test. */
private $submission_action;

/**
* Reset shared integration state after each test.
*/
public function tearDown(): void {
PUM_Integrations::$form_submission = null;

if ( $this->context_filter ) {
remove_filter( 'pum_integrated_form_submission_args', $this->context_filter );
}
if ( $this->submission_action ) {
remove_action( 'pum_integrated_form_submission', $this->submission_action );
}

parent::tearDown();
}

/**
* New context fields receive backward-compatible defaults.
*/
public function test_context_fields_have_defaults() {
pum_integrated_form_submission(
[
'form_provider' => 'gravityforms',
'form_id' => 7,
]
);

$submission = PUM_Integrations::$form_submission;

$this->assertNull( $submission['submission_id'] );
$this->assertNull( $submission['source_post_id'] );
$this->assertSame( [], $submission['context'] );
}

/**
* Extension context survives filtering and normalized dispatch.
*/
public function test_extension_context_is_preserved() {
$received = null;

$this->context_filter = static function ( $args ) {
$args['context']['content_upgrade'] = [
'incentive_id' => 42,
];

return $args;
};
add_filter(
'pum_integrated_form_submission_args',
$this->context_filter
);

$this->submission_action = static function ( $args ) use ( &$received ) {
$received = $args;
};
add_action(
'pum_integrated_form_submission',
$this->submission_action
);

pum_integrated_form_submission(
[
'form_provider' => 'gravityforms',
'form_id' => 7,
'submission_id' => 'entry-99',
'source_post_id' => 123,
]
);

$this->assertSame( 'entry-99', $received['submission_id'] );
$this->assertSame( 123, $received['source_post_id'] );
$this->assertSame( 42, $received['context']['content_upgrade']['incentive_id'] );
}
}