Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion classes/controllers/FrmHooksController.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public static function load_hooks() {
add_action( 'wp_scheduled_delete', 'FrmForm::scheduled_delete' );

// Clear embed posts transient when posts are updated.
add_action( 'wp_insert_post', 'FrmFormsListHelper::maybe_clear_embed_posts_transient', 10, 2 );
add_action( 'wp_insert_post', 'FrmFormsHelper::maybe_clear_embed_posts_transient', 10, 2 );

// Form Shortcodes.
add_shortcode( 'formidable', 'FrmFormsController::get_form_shortcode' );
Expand Down
2 changes: 1 addition & 1 deletion classes/helpers/FrmAppHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class FrmAppHelper {
*
* @var int
*/
public static $db_version = 106;
public static $db_version = 107;

/**
* Used by the API add-on.
Expand Down
49 changes: 49 additions & 0 deletions classes/helpers/FrmFormsHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,19 @@

class FrmFormsHelper {

/**
* The transient name that stores data for which posts a form is embedded in.
*
* It lives here rather than in FrmFormsListHelper so the invalidation callback below can run
* without autoloading that admin list table on a front end post insert.
*
* @since 6.32
* @since 6.35 Moved here from FrmFormsListHelper.
*
* @var string
*/
const EMBED_POSTS_TRANSIENT = 'frm_posts_contain_form';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Visibility should be explicitly set for `EMBED_POSTS_TRANSIENT` constant


Visibility (also know as Access Modifiers) can be used to define where it can be accessed. There are three access modifiers available in PHP:

  • public - The class members can be accessed from everywhere. This is default.
  • protected - The class members can be accessed within the class and by classes derived from that class.
  • private - The class members can only be accessed within the class.

The class members(properties, constants, or methods) declared without any explicit visibility keyword are by default considered as public. It is recommended to set visibility explicitly, which increases code readability. In addition, it gives the developer a mental model of where the class member would be accessible, which also leads to a better API design and makes sure that you are not making something public which isn't supposed to be.
Also, as per PSR-12: Extended Coding Style, visibility should be explicitly declared with all class properties, constants and methods.


/**
* Store and re-use field type data for the insert_opt_html function (to avoid multiple calls to FrmField::all_field_selection).
*
Expand Down Expand Up @@ -2205,4 +2218,40 @@ public static function get_form_name( $form, $length = 0 ) {

return FrmAppHelper::truncate( $form_name, $length );
}

/**
* Maybe clear the embed posts transient.
*
* @since 6.32
* @since 6.35 Moved here from FrmFormsListHelper.
*
* @param int $post_id Post ID.
* @param WP_Post $post Post object.
*
* @return void
*/
public static function maybe_clear_embed_posts_transient( $post_id, $post ) {
if ( str_contains( $post->post_content, '[formidable ' ) || str_contains( $post->post_content, '<!-- wp:formidable/simple-form ' ) ) {
// New post contains the form shortcode, so clear the embed posts transient.
delete_transient( self::EMBED_POSTS_TRANSIENT );
return;
}

$cached_posts = get_transient( self::EMBED_POSTS_TRANSIENT );

if ( ! is_array( $cached_posts ) ) {
return;
}

// If the new post data of a cached post doesn't contain the Formidable forms, clear the transient.
foreach ( $cached_posts as $posts ) {
foreach ( $posts as $post_data ) {
if ( intval( $post_data->ID ) === intval( $post_id ) ) {
// This post contains the form shortcode before updating, so clear the embed posts transient.
delete_transient( self::EMBED_POSTS_TRANSIENT );
return;
}
}
}
}
}
41 changes: 6 additions & 35 deletions classes/helpers/FrmFormsListHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,6 @@

class FrmFormsListHelper extends FrmListHelper {

/**
* The transient name that stores data for which posts a form is embedded in.
*
* @since 6.32
*
* @var string
*/
private static $embed_posts_transient_name = 'frm_posts_contain_form';

/**
* @var string
*/
Expand Down Expand Up @@ -612,7 +603,7 @@ function () use ( $posts ) {
* @return array
*/
private function get_posts_contain_form( $form ) {
$cached_posts = get_transient( self::$embed_posts_transient_name );
$cached_posts = get_transient( FrmFormsHelper::EMBED_POSTS_TRANSIENT );

if ( isset( $cached_posts[ $form->id ] ) && is_array( $cached_posts[ $form->id ] ) ) {
return $cached_posts[ $form->id ];
Expand Down Expand Up @@ -653,7 +644,7 @@ private function get_posts_contain_form( $form ) {
}

$cached_posts[ $form->id ] = $posts;
set_transient( self::$embed_posts_transient_name, $cached_posts, DAY_IN_SECONDS );
set_transient( FrmFormsHelper::EMBED_POSTS_TRANSIENT, $cached_posts, DAY_IN_SECONDS );

return $posts;
}
Expand Down Expand Up @@ -734,37 +725,17 @@ private function query_posts_contain_form( $form ) {
}

/**
* Maybe clear the embed posts transient.
* @deprecated 6.35 Moved to FrmFormsHelper so a front end post insert does not have to load this admin list table.
*
* @since 6.32
* @codeCoverageIgnore
*
* @param int $post_id Post ID.
* @param WP_Post $post Post object.
*
* @return void
*/
public static function maybe_clear_embed_posts_transient( $post_id, $post ) {
if ( str_contains( $post->post_content, '[formidable ' ) || str_contains( $post->post_content, '<!-- wp:formidable/simple-form ' ) ) {
// New post contains the form shortcode, so clear the embed posts transient.
delete_transient( self::$embed_posts_transient_name );
return;
}

$cached_posts = get_transient( self::$embed_posts_transient_name );

if ( ! is_array( $cached_posts ) ) {
return;
}

// If the new post data of a cached post doesn't contain the Formidable forms, clear the transient.
foreach ( $cached_posts as $posts ) {
foreach ( $posts as $post_data ) {
if ( intval( $post_data->ID ) === intval( $post_id ) ) {
// This post contains the form shortcode before updating, so clear the embed posts transient.
delete_transient( self::$embed_posts_transient_name );
return;
}
}
}
_deprecated_function( __METHOD__, '6.35', 'FrmFormsHelper::maybe_clear_embed_posts_transient' );
FrmFormsHelper::maybe_clear_embed_posts_transient( $post_id, $post );
}
}
28 changes: 28 additions & 0 deletions classes/models/FrmMigrate.php
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ private function create_tables() {
* @since 6.6
* @since 6.16.3 idx_form_id_is_draft was also added to frm_items.
* @since 6.17 idx_form_id_type was also added to frm_fields.
* @since 6.35 idx_form_id_created_at was added to frm_items, and idx_parent_form_id to frm_forms.
*
* @return void
*/
Expand Down Expand Up @@ -325,6 +326,33 @@ private function add_composite_indexes_for_entries() {
if ( ! self::index_exists( $table_name, $index_name ) ) {
$wpdb->query( "CREATE INDEX idx_form_id_type ON `{$wpdb->prefix}frm_fields` (form_id, type(30))" );
}

/**
* The entry list sorts one form's entries by created_at, without filtering on is_draft.
* KEY form_id alone cannot satisfy that ORDER BY, so the database read every matching row
* and sorted it: on a form with 50,000 entries the first page took about 100ms.
*
* The is_draft column is deliberately left out. Putting it between form_id and created_at
* leaves created_at unusable for the sort whenever is_draft is unconstrained, which is
* exactly how the entry list queries. Draft filtering is served by idx_form_id_is_draft.
*/
$table_name = "{$wpdb->prefix}frm_items";
$index_name = 'idx_form_id_created_at';

if ( ! self::index_exists( $table_name, $index_name ) ) {
$wpdb->query( "CREATE INDEX idx_form_id_created_at ON `{$wpdb->prefix}frm_items` (form_id, created_at)" );
}

/**
* Repeaters and embedded forms look their child forms up by parent_form_id, which had
* no index at all, so every lookup was a full scan of frm_forms.
*/
$table_name = "{$wpdb->prefix}frm_forms";
$index_name = 'idx_parent_form_id';

if ( ! self::index_exists( $table_name, $index_name ) ) {
$wpdb->query( "CREATE INDEX idx_parent_form_id ON `{$wpdb->prefix}frm_forms` (parent_form_id)" );
}
}

/**
Expand Down
8 changes: 7 additions & 1 deletion paypal/controllers/FrmPayPalLiteHooksController.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@ public static function load_hooks() {

add_filter( 'frm_payment_gateways', 'FrmPayPalLiteAppController::add_gateway' );

add_action( 'init', 'FrmPayPalLiteConnectHelper::check_for_redirects' );
// Only hook this up on the OAuth return request. Registering it unconditionally made
// WordPress autoload FrmPayPalLiteConnectHelper on every front end page view.
// Paired with FrmPayPalLiteConnectHelper::user_landed_on_the_oauth_return_url, which
// checks the same query argument.
if ( isset( $_GET['frm_paypal_api_return_oauth'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
add_action( 'init', 'FrmPayPalLiteConnectHelper::check_for_redirects' );
}

// Use 20 so this happens after the Stripe add-on.
add_filter( 'frm_pro_show_card_callback', 'FrmPayPalLiteActionsController::maybe_show_card', 20, 2 );
Expand Down
8 changes: 7 additions & 1 deletion square/controllers/FrmSquareLiteHooksController.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@ public static function load_hooks() {

add_filter( 'frm_payment_gateways', 'FrmSquareLiteAppController::add_gateway' );

add_action( 'init', 'FrmSquareLiteConnectHelper::check_for_redirects' );
// Only hook this up on the OAuth return request. Registering it unconditionally made
// WordPress autoload FrmSquareLiteConnectHelper on every front end page view.
// Paired with FrmSquareLiteConnectHelper::user_landed_on_the_oauth_return_url, which
// checks the same query argument.
if ( isset( $_GET['frm_square_api_return_oauth'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
add_action( 'init', 'FrmSquareLiteConnectHelper::check_for_redirects' );
}

// Use 20 so this happens after the Stripe add-on.
add_filter( 'frm_pro_show_card_callback', 'FrmSquareLiteActionsController::maybe_show_card', 20, 2 );
Expand Down
25 changes: 24 additions & 1 deletion stripe/controllers/FrmStrpLiteHooksController.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@ public static function load_hooks() {
// Actions.
add_action( 'frm_entry_form', 'FrmStrpLiteAuth::add_hidden_token_field' );
add_action( 'frm_enqueue_form_scripts', 'FrmStrpLiteActionsController::maybe_load_scripts' );
add_action( 'init', 'FrmStrpLiteConnectHelper::check_for_stripe_connect_webhooks' );

// Only hook this up on requests it can act on. Registering it unconditionally made
// WordPress autoload FrmStrpLiteConnectHelper on every front end page view.
if ( self::request_may_be_for_stripe_connect() ) {
add_action( 'init', 'FrmStrpLiteConnectHelper::check_for_stripe_connect_webhooks' );
}

// Filters.
add_filter( 'frm_saved_errors', 'FrmStrpLiteAppController::maybe_add_payment_error', 10, 2 );
Expand Down Expand Up @@ -52,6 +57,24 @@ function ( $form_col ) {
add_action( 'frm_form_classes', 'FrmStrpLiteLinkController::add_form_classes' );
}

/**
* Could this request be a Stripe Connect return trip or one of the Connect AJAX actions?
*
* Used to keep FrmStrpLiteConnectHelper out of ordinary page views. The helper still runs
* its own narrower checks, this only decides whether it is worth loading at all.
*
* @since 6.35
*
* @return bool
*/
private static function request_may_be_for_stripe_connect() {
// These two query arguments are also checked in FrmStrpLiteConnectHelper, by
// user_landed_on_the_return_url and user_landed_on_the_oauth_return_url.
// phpcs:disable WordPress.Security.NonceVerification.Recommended
return wp_doing_ajax() || isset( $_GET['frm_stripe_connect_return'] ) || isset( $_GET['frm_stripe_connect_return_oauth'] );
// phpcs:enable WordPress.Security.NonceVerification.Recommended
}

/**
* @return void
*/
Expand Down
9 changes: 6 additions & 3 deletions stripe/controllers/FrmTransLiteHooksController.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ class FrmTransLiteHooksController {
* @return void
*/
public static function load_hooks() {
add_action( 'frm_add_form_option_section', 'FrmSquareLiteActionsController::actions_js' );

// Exit early, let the Payments submodule handle everything.
if ( class_exists( 'FrmTransHooksController', false ) ) {
return;
Expand All @@ -19,7 +17,6 @@ public static function load_hooks() {
// Actions.
add_action( 'frm_payment_cron', 'FrmTransLiteAppController::run_payment_cron' );
add_filter( 'frm_registered_form_actions', 'FrmTransLiteActionsController::register_actions' );
add_action( 'frm_add_form_option_section', 'FrmTransLiteActionsController::actions_js' );
add_action( 'frm_trigger_payment_action', 'FrmTransLiteActionsController::trigger_action', 10, 3 );

// Filters.
Expand All @@ -38,6 +35,10 @@ public static function load_hooks() {
public static function load_admin_hooks() {
add_action( 'frm_disconnected_gateway', 'FrmTransLiteAppController::maybe_remove_payment_cron', 10, 2 );

// frm_add_form_option_section only fires while rendering the form settings page, so these
// belong here rather than in load_hooks.
add_action( 'frm_add_form_option_section', 'FrmSquareLiteActionsController::actions_js' );

if ( class_exists( 'FrmTransHooksController', false ) ) {
add_action( 'frm_pay_show_square_options', 'FrmTransLiteAppController::add_repeat_cadence_value' );

Expand Down Expand Up @@ -73,6 +74,8 @@ function () {
add_action( 'admin_print_styles', array( self::class, 'hide_collect_payment_action' ) );
}

add_action( 'frm_add_form_option_section', 'FrmTransLiteActionsController::actions_js' );

// Actions.
add_action( 'admin_menu', 'FrmTransLitePaymentsController::menu', 25 );
add_action( 'admin_head', 'FrmTransLiteListsController::add_list_hooks' );
Expand Down
Loading