Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
145 commits
Select commit Hold shift + click to select a range
30b8547
Add Gated content form action
truongwp Apr 16, 2026
56ccb14
Create gated_tokens db table
truongwp Apr 16, 2026
347fa2d
Add gated token helper
truongwp Apr 16, 2026
b84c0b4
Add code to trigger the gated content action
truongwp Apr 16, 2026
709c312
Add code to obtain gated content token
truongwp Apr 16, 2026
60852cd
Add gated content shortcode
truongwp Apr 16, 2026
e0e06c1
Handle password protected posts
truongwp Apr 16, 2026
b0a3830
Fix open access for password protected pages not working
truongwp Apr 17, 2026
69ebb2f
Add show=url to shortcode
truongwp Apr 19, 2026
f12e90c
Support private pages
truongwp Apr 19, 2026
3d338a6
Use frm_clone_icon
truongwp Apr 21, 2026
9b550ac
Necessary code to add more gated content type
truongwp Apr 24, 2026
32eeae2
Add unit tests
truongwp Apr 25, 2026
5b5b5b8
Add support for running gated content in payment success
truongwp Apr 25, 2026
334d5f9
Update unit tests
truongwp Apr 25, 2026
6f0f023
Merge master into branch, keeping db_version 106 and built JS
truongwp Apr 27, 2026
20314c4
Fix JS error when sorting fields and PHP error when duplicating form
truongwp Apr 27, 2026
b95e703
Use transient to temporarily store the gated content token
truongwp Apr 27, 2026
27dab71
Fix PHPStan
truongwp Apr 27, 2026
e7b8988
Fix PHPStan
truongwp Apr 27, 2026
2c012f7
Remove md5()
truongwp Apr 27, 2026
d4c35aa
Fix deepsource error
truongwp Apr 27, 2026
ccc67e3
Fix Psalm
truongwp Apr 27, 2026
2a8d369
Fix PHPStan
truongwp Apr 27, 2026
877e9e6
Revert deepsource config file
truongwp Apr 27, 2026
872adcc
Change apply_filters() params
truongwp Apr 28, 2026
ae4744a
Fix empty redirect URL
truongwp Apr 28, 2026
cf17c3b
Revoke old token before updating
truongwp Apr 28, 2026
0d917fc
Update unit tests
truongwp Apr 28, 2026
18c8086
Schedule delete expired gated content tokens
truongwp Apr 28, 2026
0bb1615
Update obtain_token() method
truongwp Apr 28, 2026
d8e35dc
Reduce extra DB query
truongwp Apr 28, 2026
a559c55
Update data-item-count
truongwp Apr 28, 2026
d9910b0
Remove deprecated function
truongwp Apr 28, 2026
91d6480
Improve code
truongwp Apr 28, 2026
1e6b04a
Improve code
truongwp Apr 28, 2026
dcdc4ab
Move some features to Pro
truongwp Apr 28, 2026
29a1e45
Remove unnecessary since
truongwp Apr 28, 2026
b9603d7
Improve code
truongwp Apr 28, 2026
432fb4f
Add back the static variable for tokens
truongwp Apr 28, 2026
ede69c0
Add cache
truongwp Apr 28, 2026
418bc99
Improve code
truongwp Apr 28, 2026
010c8b0
Remove unused code
truongwp Apr 28, 2026
bb0487d
Show extra settings in Gated content action
truongwp Apr 28, 2026
6a7eb60
Improve code
truongwp Apr 29, 2026
2848b6f
Change icon and color of gated content action
truongwp May 7, 2026
753612e
Change gated content item column layout
truongwp May 7, 2026
21242ec
Improve styling of gated content action
truongwp May 7, 2026
bbe8e5b
Fix the file dropdown in Gated content not live updated
truongwp May 11, 2026
6cd6c74
Gated content: Remove unnecessary code
truongwp May 11, 2026
43bbf89
Gated content: Improve code
truongwp May 12, 2026
e374054
Gated content: improve the cookie format to reduce DB calls
truongwp May 13, 2026
ca7f657
Gated content: Refactor code
truongwp May 17, 2026
0ba0afc
Gated content: Add some filters
truongwp May 18, 2026
5cf9fd0
Gated content: update unit tests
truongwp May 18, 2026
e3d33cd
Gated content: do not show publish pages in gated item select
truongwp May 18, 2026
c45c1cc
Gated content: Init autocomplete after adding row
truongwp May 18, 2026
db257d3
Gated content: Fix autocomplete JS
truongwp May 18, 2026
b963933
Gated content: Fix private page not open
truongwp May 18, 2026
e428393
Gated content: Improve code
truongwp May 18, 2026
436f767
Gated content: add cache to action_contains_item()
truongwp May 18, 2026
5b90719
Gated content: remove update event
truongwp May 18, 2026
0de960e
Merge branch 'master' into rock-gated-content
truongwp May 18, 2026
79f9a3e
Gated content: Move update entry code to Pro
truongwp May 18, 2026
9bc7edf
Gated content: move expired_time shortcode code to Pro
truongwp May 19, 2026
5ce30a2
Gated content: simplify the user id check
truongwp May 19, 2026
7cc2798
Gated content: support post and page by default
truongwp May 19, 2026
c50aa62
Gated content: break down long method
truongwp May 19, 2026
eeb003b
Fix PHPStan
truongwp May 19, 2026
aefcbd6
Fix deepsource JS
truongwp May 19, 2026
d4812a6
Fix PHPStan
truongwp May 19, 2026
f7c62d3
Fix deepsource JS
truongwp May 19, 2026
7420ce8
Gated content: improve code
truongwp May 19, 2026
f6f1b1f
Gated content: remove unused code
truongwp May 19, 2026
6fd7e06
Gated content: Create shortcode controller
truongwp May 19, 2026
5cd1f21
Gated content: improve code
truongwp May 19, 2026
130ee65
Gated content: improve code
truongwp May 19, 2026
7fcc076
Gated content: remove unrelated code
truongwp May 19, 2026
1d015d5
Gated content: improve JS code
truongwp May 19, 2026
f964005
Gated content: fix type and PHPStan
truongwp May 19, 2026
8f17d13
Gated content: Fix Rector and PHP CS Fixer
truongwp May 19, 2026
f8b738c
Gated content: Fix unit tests
truongwp May 19, 2026
d17fca3
Fix eslint
truongwp May 19, 2026
7002d7a
Fix unit tests
truongwp May 19, 2026
fdf8cb2
Fix PHPCS
truongwp May 19, 2026
66d2f25
Fix PHPCS
truongwp May 19, 2026
cf67061
Gated content: rename filter
truongwp May 20, 2026
eff7f69
Gated content: add FrmgatedItem class
truongwp May 20, 2026
ba4531d
Gated content: update unit tests
truongwp May 20, 2026
819a01e
Gated content: fix PHPCS
truongwp May 20, 2026
b980518
Gated content: Fix mago
truongwp May 20, 2026
45e88fc
Gated content: Fix PHPStan
truongwp May 20, 2026
c8b525f
Gated content: update FrmGatedItem construct
truongwp May 20, 2026
207f0e5
Gated content: improve code
truongwp May 20, 2026
d611cde
Gated content: Fix PHPCS and PHPStan
truongwp May 20, 2026
0227fb1
Gated content: Fix PHPCS and PHPStan
truongwp May 20, 2026
07c2ca5
Gated content: fix PHPCSFixer
truongwp May 20, 2026
aeb7e04
Gated content: update hook param
truongwp May 20, 2026
0ee2321
Gated content: change hook
truongwp May 20, 2026
ddb131c
Gated content: support string item ID
truongwp May 20, 2026
e0ca044
Gated content: Fix the private not work
truongwp May 20, 2026
74ce8d9
Gated content: Add support for view
truongwp May 20, 2026
4a68b9b
Gated content: make each post type an option
truongwp May 21, 2026
a2dc00d
Add gated content shortcodes to the shortcodes box
truongwp May 25, 2026
a27c9aa
Merge origin/master into rock-gated-content
truongwp May 25, 2026
3b627c3
Fix PHPCS and PHPCSFixer
truongwp May 25, 2026
2520f2b
Fix PHPStan
truongwp May 25, 2026
57aaa77
Fix PHPCS
truongwp May 25, 2026
0292812
Gated content: update code to support Views
truongwp Jun 1, 2026
00dc257
Merge branch 'master' into rock-gated-content
truongwp Jun 1, 2026
66ea7c3
Gated content: Fix PHPCS
truongwp Jun 1, 2026
7eae149
Gated content: Fix PHPCS
truongwp Jun 1, 2026
c1af7da
Gated content: Remove Upload file type (v2)
truongwp Jun 4, 2026
f211380
Merge branch 'master' into rock-gated-content
truongwp Jun 4, 2026
e638fc5
Fix PHPCS
truongwp Jun 4, 2026
2979f16
Fix ESlint
truongwp Jun 4, 2026
005a41d
Use array_to_html_params() helper in view file
truongwp Jun 30, 2026
3301764
Use %i for table name in wpdb query
truongwp Jun 30, 2026
d3d99b3
Add missing param in doc comment
truongwp Jun 30, 2026
cecf632
Rename variables
truongwp Jun 30, 2026
1edd47e
Rename hook and params
truongwp Jun 30, 2026
e90f6d1
Move inline CSS into separate file
truongwp Jun 30, 2026
22cc881
Remove redundant parentheses
truongwp Jun 30, 2026
0e65854
Restore .htaccess
truongwp Jun 30, 2026
2bd0db9
Move gated content action JS into separate file
truongwp Jun 30, 2026
c315a73
Merge branch 'master' into rock-gated-content
truongwp Jun 30, 2026
6dc0cd5
Gated content: Use pretty permalink for private post
truongwp Jul 1, 2026
76f3667
Fix PHP CS Fixer and PHPStan
truongwp Jul 1, 2026
cdd8856
Fix PHPCS
truongwp Jul 1, 2026
3a239e5
Add description for gated content action
Crabcyborg Jul 3, 2026
2b694af
Gated content: add action to the lite actions
truongwp Jul 9, 2026
1e4eb91
Gated content: change width of shortcode columns
truongwp Jul 9, 2026
353e3dc
Gated content: change width of gated item columns
truongwp Jul 9, 2026
72f782a
Gated content: change color of delete icon
truongwp Jul 9, 2026
eae299d
Gated content: Add animation to copy icon
truongwp Jul 9, 2026
d47483e
Gated content: clean the recent commit
truongwp Jul 9, 2026
801f00e
Merge branch 'master' into rock-gated-content
truongwp Jul 9, 2026
943d5a1
Fix stylelint
truongwp Jul 9, 2026
c48997f
Gated content: update copy icon color
truongwp Jul 9, 2026
ae20223
Gated content: use div for shortcodes list
truongwp Jul 9, 2026
6478116
Gated content: improve the position of copy icon
truongwp Jul 9, 2026
0b65fda
Remove unused CSS
truongwp Jul 9, 2026
fa85fcc
Gated content: remove access_code from URL for public posts
truongwp Jul 14, 2026
9429842
Merge branch 'master' into rock-gated-content
Crabcyborg Jul 14, 2026
9ce7358
Run phpcbf
Crabcyborg Jul 14, 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
4 changes: 3 additions & 1 deletion classes/controllers/FrmFormActionsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public static function register_actions() {
$action_classes = array(
'on_submit' => 'FrmOnSubmitAction',
'email' => 'FrmEmailAction',
'gated_content' => 'FrmGatedContentAction',
'wppost' => 'FrmDefPostAction',
'register' => 'FrmDefRegAction',
'stripe' => 'FrmStripeLiteAction',
Expand Down Expand Up @@ -134,6 +135,7 @@ private static function apply_default_action_descriptions() {
'api' => __( 'System integration', 'formidable' ),
'googlespreadsheet' => __( 'Spreadsheet sync', 'formidable' ),
'convertkit' => __( 'Broadcast publishing', 'formidable' ),
'gated_content' => __( 'Access control', 'formidable' ),
);

foreach ( self::$registered_actions->actions as $action ) {
Expand Down Expand Up @@ -1126,7 +1128,7 @@ function ( $options ) {
* @return string[]
*/
public static function get_lite_actions() {
return apply_filters( 'frm_lite_form_actions', array( 'on_submit', 'email', 'payment', 'stripe', 'square', 'paypal' ) );
return apply_filters( 'frm_lite_form_actions', array( 'on_submit', 'email', 'payment', 'stripe', 'square', 'paypal', 'gated_content' ) );
}

/**
Expand Down
8 changes: 5 additions & 3 deletions classes/controllers/FrmFormsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -1754,7 +1754,7 @@ public static function mb_tags_box( $form_id, $class = '', $template_path = 'def
$col = 'one';
$settings_tab = FrmAppHelper::is_admin_page( 'formidable' );
$cond_shortcodes = apply_filters( 'frm_conditional_shortcodes', array() );
$entry_shortcodes = self::get_shortcode_helpers( $settings_tab );
$entry_shortcodes = self::get_shortcode_helpers( $settings_tab, $form_id );
$advanced_helpers = self::advanced_helpers( compact( 'fields', 'form_id' ) );

if ( 'default' === $template_path || ! file_exists( $template_path ) ) {
Expand Down Expand Up @@ -1863,10 +1863,11 @@ private static function user_shortcodes() {
* @since 2.0.6
*
* @param bool $settings_tab
* @param int $form_id
*
* @return array
*/
private static function get_shortcode_helpers( $settings_tab ) {
private static function get_shortcode_helpers( $settings_tab, $form_id = 0 ) {
$entry_shortcodes = array(
'id' => __( 'Entry ID', 'formidable' ),
'key' => __( 'Entry Key', 'formidable' ),
Expand Down Expand Up @@ -1894,8 +1895,9 @@ private static function get_shortcode_helpers( $settings_tab ) {
*
* @param array $entry_shortcodes
* @param bool $settings_tab
Comment thread
truongwp marked this conversation as resolved.
* @param int $form_id
*/
return apply_filters( 'frm_helper_shortcodes', $entry_shortcodes, $settings_tab );
return apply_filters( 'frm_helper_shortcodes', $entry_shortcodes, $settings_tab, $form_id );

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function apply_filters invoked with 4 parameters, 2 required


The function call is not valid, which will result in a fatal runtime error.

}

/**
Expand Down
334 changes: 334 additions & 0 deletions classes/controllers/FrmGatedContentController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,334 @@
<?php
/**
* Gated Content Controller
*
* @package Formidable
*
* @since x.x
*/

if ( ! defined( 'ABSPATH' ) ) {
die( 'You are not allowed to call this page directly.' );
}

class FrmGatedContentController {

/**
* Post ID unlocked for the current request by maybe_unlock_post().
*
* Stored here so filter_password_required() can reference it without a
* closure (closures are forbidden as action/filter callbacks).
*
* @var int
*/
private static $unlocked_post_id = 0;

/**
* Allow private posts into the main query when a valid gated-content token is present.
*
* Hooked on 'pre_get_posts', which fires before WP_Query runs its DB query.
* Private posts are excluded at the query level by WordPress, so the 'wp'
* hook is too late — get_queried_object_id() returns 0 for a 404'd private post.
*
* Token validation is intentionally deferred to maybe_unlock_post() (the 'wp'
* hook): by that time get_queried_object_id() is reliable and we can validate
* the token against the exact post that was resolved. If no valid token exists
* for the private post, maybe_unlock_post() forces a 404.
*
* @param WP_Query $query Current query object.
*
* @return void
*/
public static function maybe_include_private_posts( $query ) {
if ( ! $query->is_main_query() || is_admin() ) {
return;
}

// Only widen singular requests — archives/lists must never expose private posts.
if ( ! $query->is_singular ) {
return;
}

$statuses = $query->get( 'post_status' );

if ( ! is_array( $statuses ) ) {
$statuses = $statuses ? array( $statuses ) : array( 'publish' );
}

// Already includes private — nothing to widen.
if ( in_array( 'private', $statuses, true ) ) {
return;
}

$queried_post = self::get_queried_post( $query );

if ( ! $queried_post ) {
return;
}

$post_item = FrmGatedItem::make(
array(
'type' => $queried_post->post_type,
'id' => $queried_post->ID,
)
);

if ( ! FrmGatedTokenHelper::get_valid_token( $post_item ) ) {
return;
}

$statuses[] = 'private';
$query->set( 'post_status', $statuses );
}

/**
* Resolve the requested WP_Post from the query vars at pre_get_posts time.
*
* Get_queried_object_id() is not available at pre_get_posts because the query
* has not run yet. For numeric-ID URLs the post comes from get_post(); for
* pretty-permalink slugs, get_page_by_path() resolves the slug including
* private posts (it queries all statuses except trash/auto-draft).
*
* Post types searched are derived from the enabled item type configs that have
* a 'post_type' key, so add-ons only need to register their item type once.
*
* @param WP_Query $query Main query object.
*
* @return WP_Post|null Resolved post, or null if it cannot be determined.
*/
private static function get_queried_post( $query ) {
$post_id = (int) $query->get( 'p' );

if ( ! $post_id ) {
$post_id = (int) $query->get( 'page_id' );
}

if ( $post_id ) {
$post = get_post( $post_id );
return $post ? $post : null;
}

$slug = $query->get( 'pagename' );

if ( ! $slug ) {
$slug = $query->get( 'name' );
}

if ( ! $slug ) {
return null;
}

$post_types = array();

foreach ( FrmGatedContentAction::get_types() as $type_key => $type_config ) {
if ( empty( $type_config['disabled'] ) && post_type_exists( $type_key ) ) {
$post_types[] = $type_key;
}
}

$post = get_page_by_path( $slug, OBJECT, $post_types );
return $post instanceof WP_Post ? $post : null;
}

/**
* Attempt to unlock a gated post (password-protected or private) using a token.
*
* Hooked on 'wp' so get_queried_object_id() is available. Private posts are
* already in the query by this point (via maybe_include_private_posts), so
* only password-protected posts need the post_password_required filter.
*
* Resolution order:
* 1. URL query parameter access_code (raw token → hashed via get_valid_token).
* 2. Any frm_gc_* cookie whose hash validates against the current post.
*
* @return void
*/
public static function maybe_unlock_post() {
$post_id = get_queried_object_id();

if ( ! $post_id ) {
return;
}

$post = get_post( $post_id );

if ( ! $post ) {
return;
}

$is_password_protected = '' !== $post->post_password;
$is_restricted_private = 'private' === $post->post_status && ! current_user_can( 'read_private_posts', $post_id );
$access_code_from_url = FrmAppHelper::simple_get( 'access_code' );

// Nothing to unlock — post is publicly accessible.
if ( ! $is_password_protected && ! $is_restricted_private ) {
if ( $access_code_from_url && wp_safe_redirect( remove_query_arg( 'access_code' ) ) ) {
exit;
}

return;
Comment thread
truongwp marked this conversation as resolved.
}

$post_item = FrmGatedItem::make(
array(
'type' => $post->post_type,
'id' => $post_id,
)
);
$valid_token = FrmGatedTokenHelper::get_valid_token( $post_item );

if ( $valid_token ) {
// Password-protected posts need an explicit filter; private posts are
// already accessible because maybe_include_private_posts widened the query.
if ( $is_password_protected ) {
self::$unlocked_post_id = $post_id;
add_filter( 'post_password_required', 'FrmGatedContentController::filter_password_required', 10, 2 );
}

// Strip the raw token from the URL to prevent leakage via browser history,
// server logs, and Referer headers. The cookie set above grants access on
// the redirected request without the query parameter.
if ( $access_code_from_url && wp_safe_redirect( remove_query_arg( 'access_code' ) ) ) {
exit;
}

return;
}

// No valid token — force a 404 to prevent private posts from being exposed.
if ( $is_restricted_private ) {
self::force_404();
Comment thread
Crabcyborg marked this conversation as resolved.
}
}

/**
* Force the current request to a 404 response.
*
* Used when a private post was widened into the main query by
* maybe_include_private_posts() but no valid token was found.
*
* @return void
*/
private static function force_404() {
global $wp_query;
$wp_query->set_404();
status_header( 404 );
nocache_headers();
}

/**
* Filter callback: return false for the single post unlocked by maybe_unlock_post().
*
* Fires on the 'post_password_required' filter. Only overrides the result for
* the specific post ID stored in self::$unlocked_post_id — all other posts are
* passed through unchanged.
*
* @param bool $required Whether the password is required.
* @param WP_Post $post Post being checked.
*
* @return bool
*/
public static function filter_password_required( $required, $post ) {
return $post->ID === self::$unlocked_post_id ? false : $required;
}

/**
* Delete all gated tokens linked to a gated content action when it is permanently deleted.
*
* Fires on 'before_delete_post'. Only acts on frm_form_actions posts whose
* post_excerpt identifies them as gated_content actions.
*
* @param int $post_id Post ID being deleted.
* @param WP_Post $post Post object being deleted.
*
* @return void
*/
/**
* Clear the action-item membership cache when a gated content action is updated.
*
* Fires on 'save_post_frm_form_actions'. Only acts on updates (not creates)
* because the item list cannot change during initial creation.
*
* @param int $post_id Post ID of the saved action.
* @param WP_Post $post Saved post object.
* @param bool $update True when updating an existing post, false on create.
*
* @return void
*/
public static function on_action_updated( $post_id, $post, $update ) {
if ( ! $update || FrmGatedContentAction::$slug !== $post->post_excerpt ) {
return;
}
FrmGatedTokenHelper::delete_action_item_cache( $post_id );
}

/**
* Clean up when a gated content action post is permanently deleted.
*
* Hooked to `before_delete_post`. Clears the action-item transient cache
* (while the post is still readable) then removes all associated tokens.
*
* @param int $post_id Post ID of the action being deleted.
* @param WP_Post $post The action post object.
*
* @return void
*/
public static function on_action_deleted( int $post_id, WP_Post $post ) {
if ( 'frm_form_actions' !== $post->post_type || FrmGatedContentAction::$slug !== $post->post_excerpt ) {
return;
}
// Clear action-item cache first — the action post still exists at this
// point (before_delete_post) so its settings are still readable.
FrmGatedTokenHelper::delete_action_item_cache( $post_id );
FrmGatedTokenHelper::delete_by_action( $post_id );
}

/**
* Generate a gated content token when a form action fires.
*
* @param WP_Post $action Form action post object (post_excerpt = 'gated_content').
* @param object $entry Submitted form entry object.
* @param object $form Form object.
* @param string $event Trigger event ('create', 'payment-success', 'user_registration', …).
*
* @return void
*/
public static function trigger( $action, $entry, $form, $event ) {
FrmGatedTokenHelper::generate( $action, $entry, $event );
Comment on lines +296 to +297

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Tighten trigger() action type before calling token generation.

FrmGatedTokenHelper::generate() expects a WP_Post, but trigger() forwards any object. This is currently tripping static analysis and can break when non-WP_Post payloads are dispatched.

Suggested fix
-	 * `@param` object $action Form action post object (post_excerpt = 'gated_content').
+	 * `@param` WP_Post $action Form action post object (post_excerpt = 'gated_content').
@@
 	public static function trigger( $action, $entry, $form, $event ) {
+		if ( ! $action instanceof WP_Post ) {
+			return;
+		}
 		FrmGatedTokenHelper::generate( $action, $entry, $event );
 	}
🧰 Tools
🪛 GitHub Check: PHPStan

[failure] 240-240:
Parameter #1 $action of static method FrmGatedTokenHelper::generate() expects WP_Post, object given.

🪛 PHPMD (2.15.0)

[warning] 239-239: Avoid unused parameters such as '$form'. (undefined)

(UnusedFormalParameter)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@classes/controllers/FrmGatedContentController.php` around lines 239 - 240,
The trigger() method currently passes whatever $action is directly to
FrmGatedTokenHelper::generate(), but generate() expects a WP_Post; update
trigger() to validate and tighten the type of $action before calling
FrmGatedTokenHelper::generate(): check that $action is an instance of WP_Post
(using instanceof) and only call FrmGatedTokenHelper::generate($action, $entry,
$event) when that check passes (otherwise return early or bail out and
optionally log/debug). Ensure you reference the trigger() method and
FrmGatedTokenHelper::generate() when making the change so the validation is
colocated with the call.

}

/**
* Add [frm_gated_content id="…"] entries to the Advanced tab shortcode helpers box.
*
* One entry per gated content action attached to the current form. The left
* column shows the action name (post_title) and the right column shows the
* ready-to-paste shortcode.
*
* Hooked to `frm_helper_shortcodes` with 3 accepted args.
*
* @since x.x
*
* @param array $shortcodes Existing shortcode helpers array (shortcode => label).
* @param string $settings_tab Active settings tab slug.
* @param int $form_id Current form ID.
*
* @return array
*/
public static function add_shortcode_helper( $shortcodes, $settings_tab, $form_id ) {
if ( ! $form_id ) {
return $shortcodes;
}

$actions = FrmFormAction::get_action_for_form( $form_id, FrmGatedContentAction::$slug, array( 'post_status' => 'publish' ) );

if ( ! $actions ) {
return $shortcodes;
}

foreach ( $actions as $action ) {
$shortcodes[ 'frm_gated_content id="' . $action->ID . '"' ] = $action->post_title;
}

return $shortcodes;
}
}
Loading
Loading