This commit is contained in:
2026-07-02 15:54:39 -06:00
commit 9883323161
17470 changed files with 4470592 additions and 0 deletions
@@ -0,0 +1,115 @@
<?php
/**
* Service provider for async (background) processors.
*
* @package Gravity_Forms\Gravity_Forms
*/
namespace Gravity_Forms\Gravity_Forms\Async;
use Gravity_Forms\Gravity_Forms\GF_Service_Container;
use Gravity_Forms\Gravity_Forms\GF_Service_Provider;
use Gravity_Forms\Gravity_Forms\Telemetry\GF_Telemetry_Processor;
use Gravity_Forms\Gravity_Forms\Bulk_Actions\GF_Entry_Bulk_Action_Processor;
use Gravity_Forms\Gravity_Forms\Bulk_Actions\Endpoints\GF_Bulk_Action_Endpoint_Start;
use Gravity_Forms\Gravity_Forms\Bulk_Actions\Endpoints\GF_Bulk_Action_Endpoint_Status;
use Gravity_Forms\Gravity_Forms\Bulk_Actions\Endpoints\GF_Bulk_Action_Endpoint_Cancel;
use GFForms;
use GF_Background_Upgrader;
use GF_Feed_Processor;
if ( ! class_exists( 'GFForms' ) ) {
die();
}
/**
* Class GF_Background_Processing_Service_Provider
*
* @since 2.6.9
*/
class GF_Background_Process_Service_Provider extends GF_Service_Provider {
const UPGRADER = 'upgrade_processor';
const FEEDS = 'feeds_processor';
const NOTIFICATIONS = 'notifications_processor';
const TELEMETRY = 'telemetry_processor';
const BULK_ACTION = 'bulk_action_processor';
const BULK_ACTION_ENDPOINT_START = 'bulk_action_endpoint_start';
const BULK_ACTION_ENDPOINT_STATUS = 'bulk_action_endpoint_status';
const BULK_ACTION_ENDPOINT_CANCEL = 'bulk_action_endpoint_cancel';
/**
* The names and classes of the async (background) processors.
*
* @since 2.6.9
*
* @var string[]
*/
protected $processors = array(
self::UPGRADER => GF_Background_Upgrader::class,
self::FEEDS => GF_Feed_Processor::class,
self::NOTIFICATIONS => GF_Notifications_Processor::class,
self::TELEMETRY => GF_Telemetry_Processor::class,
self::BULK_ACTION => GF_Entry_Bulk_Action_Processor::class,
);
/**
* Initializing the processors and adding them to the container as services.
*
* @since 2.6.9
*
* @param GF_Service_Container $container
*/
public function register( GF_Service_Container $container ) {
GFForms::init_background_upgrader();
require_once GF_PLUGIN_DIR_PATH . 'includes/addon/class-gf-feed-processor.php';
require_once GF_PLUGIN_DIR_PATH . 'includes/async/class-gf-notifications-processor.php';
require_once GF_PLUGIN_DIR_PATH . 'includes/telemetry/class-gf-telemetry-processor.php';
require_once GF_PLUGIN_DIR_PATH . 'includes/bulk-actions/class-gf-entry-bulk-action-processor.php';
require_once GF_PLUGIN_DIR_PATH . 'includes/bulk-actions/endpoints/class-gf-bulk-action-endpoint-start.php';
require_once GF_PLUGIN_DIR_PATH . 'includes/bulk-actions/endpoints/class-gf-bulk-action-endpoint-status.php';
require_once GF_PLUGIN_DIR_PATH . 'includes/bulk-actions/endpoints/class-gf-bulk-action-endpoint-cancel.php';
foreach ( $this->processors as $name => $class ) {
$container->add( $name, function () use ( $name, $class ) {
if ( $name === self::UPGRADER ) {
return GFForms::$background_upgrader;
}
$callback = array( $class, 'get_instance' );
if ( is_callable( $callback ) ) {
return call_user_func( $callback );
}
return new $class();
} );
}
$container->add( self::BULK_ACTION_ENDPOINT_START, function() {
return new GF_Bulk_Action_Endpoint_Start();
} );
$container->add( self::BULK_ACTION_ENDPOINT_STATUS, function() {
return new GF_Bulk_Action_Endpoint_Status();
} );
$container->add( self::BULK_ACTION_ENDPOINT_CANCEL, function() {
return new GF_Bulk_Action_Endpoint_Cancel();
} );
}
public function init( GF_Service_Container $container ) {
add_action( 'wp_ajax_' . GF_Bulk_Action_Endpoint_Start::ACTION_NAME, function() use ( $container ) {
$container->get( self::BULK_ACTION_ENDPOINT_START )->handle();
} );
add_action( 'wp_ajax_' . GF_Bulk_Action_Endpoint_Status::ACTION_NAME, function() use ( $container ) {
$container->get( self::BULK_ACTION_ENDPOINT_STATUS )->handle();
} );
add_action( 'wp_ajax_' . GF_Bulk_Action_Endpoint_Cancel::ACTION_NAME, function() use ( $container ) {
$container->get( self::BULK_ACTION_ENDPOINT_CANCEL )->handle();
} );
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,160 @@
<?php
namespace Gravity_Forms\Gravity_Forms\Async;
use GFCommon;
use GFAPI;
if ( ! class_exists( 'GFForms' ) ) {
die();
}
if ( ! class_exists( 'Gravity_Forms\Gravity_Forms\Async\GF_Background_Process' ) ) {
require_once GF_PLUGIN_DIR_PATH . 'includes/async/class-gf-background-process.php';
}
/**
* GF_Notifications_Processor Class.
*
* @since 2.6.9
*/
class GF_Notifications_Processor extends GF_Background_Process {
/**
* The action name.
*
* @since 2.6.9
*
* @var string
*/
protected $action = 'gf_notifications_processor';
/**
* Indicates if the task uses an array that supports the attempts key.
*
* @since 2.9.9
*
* @var bool
*/
protected $supports_attempts = true;
/**
* Processes the task.
*
* @since 2.6.9
*
* @param array $item The task arguments.
*
* @return bool
*/
protected function task( $item ) {
$key = isset( $item['notification'] ) ? 'notification' : 'notifications';
$notification_or_ids = rgar( $item, $key );
if ( empty( $notification_or_ids ) || ! is_array( $notification_or_ids ) ) {
return false;
}
if ( ! empty( $item['entry'] ) ) {
$entry = $item['entry'];
} else {
$entry = GFAPI::get_entry( rgar( $item, 'entry_id' ) );
if ( is_wp_error( $entry ) ) {
GFCommon::log_debug( __METHOD__ . sprintf( '(): Aborting; Entry #%d not found.', rgar( $item, 'entry_id' ) ) );
return false;
}
}
$form = GFAPI::get_form( rgar( $item, 'form_id' ) );
if ( empty( $form ) ) {
GFCommon::log_debug( __METHOD__ . sprintf( '(): Aborting; Form #%d not found.', rgar( $item, 'form_id' ) ) );
return false;
}
$form = $this->filter_form( $form, $entry );
$event = rgar( $item, 'event', 'form_submission' );
$data = rgar( $item, 'data' );
if ( ! is_array( $data ) ) {
$data = array();
}
if ( $key === 'notification' ) {
$notifications = array( rgar( $notification_or_ids, 'id', 'custom' ) );
} else {
$notifications = $notification_or_ids;
}
/**
* Allows custom actions to be performed before notifications are sent asynchronously.
*
* @since 2.7.1
*
* @param string $event The event being processed.
* @param array $notifications An array containing the IDs of the notifications being processed.
* @param array $form The form being processed.
* @param array $entry The entry being processed.
* @param array $data An array of data which can be used in the notifications via the generic {object:property} merge tag. Defaults to empty array.
*/
do_action( 'gform_pre_process_async_notifications', $event, $notifications, $form, $entry, $data );
if ( $key === 'notification' ) {
GFCommon::send_notification( $notification_or_ids, $form, $entry, $data );
} else {
GFCommon::send_notifications( $notifications, $form, $entry, true, $event, $data );
}
/**
* Allows custom actions to be performed after notifications are sent asynchronously.
*
* @since 2.7.1
*
* @param string $event The event being processed.
* @param array $notifications An array containing the IDs of the notifications being processed.
* @param array $form The form being processed.
* @param array $entry The entry being processed.
* @param array $data An array of data which can be used in the notifications via the generic {object:property} merge tag. Defaults to empty array.
*/
do_action( 'gform_post_process_async_notifications', $event, $notifications, $form, $entry, $data );
return false;
}
/**
* Determines if async (background) processing of notifications is enabled.
*
* @since 2.7.1
* @since 2.10.0 Background notifications enabled by default for new installs.
*
* @param array $notifications An array containing the IDs of the notifications to be sent.
* @param array $form The form being processed.
* @param array $entry The entry being processed.
* @param string $event The event being processed.
* @param array $data An array of data which can be used in the notifications via the generic {object:property} merge tag. Defaults to empty array.
*
* @return bool
*/
public function is_enabled( $notifications, $form, $entry, $event = 'form_submission', $data = array() ) {
$form_id = absint( rgar( $form, 'id' ) );
$is_enabled = (bool) get_option( 'gform_enable_async_notifications' );
/**
* Allows async (background) processing of notifications to be enabled or disabled.
*
* @since 2.6.9
* @since 2.10.0 Updated to use the gform_enable_async_notifications option value as the default.
*
* @param bool $is_enabled Is async (background) processing of notifications enabled? Defaults to the value of the gform_enable_async_notifications option.
* @param string $event The event the notifications are to be sent for.
* @param array $notifications An array containing the IDs of the notifications to be sent.
* @param array $form The form currently being processed.
* @param array $entry The entry currently being processed.
* @param array $data An array of data which can be used in the notifications via the generic {object:property} merge tag. Defaults to empty array.
*/
return gf_apply_filters( array(
'gform_is_asynchronous_notifications_enabled',
$form_id,
), $is_enabled, $event, $notifications, $form, $entry, $data );
}
}
@@ -0,0 +1,216 @@
<?php
/**
* WP Async Request
*
* @package WP-Background-Processing
*/
namespace Gravity_Forms\Gravity_Forms\Async;
/**
* Abstract WP_Async_Request class.
*
* @since 2.2
* @since 2.9.8 Namespaced.
*
* @abstract
*/
abstract class WP_Async_Request {
/**
* Prefix
*
* (default value: 'wp')
*
* @var string
* @access protected
*/
protected $prefix = 'wp';
/**
* Action
*
* (default value: 'async_request')
*
* @var string
* @access protected
*/
protected $action = 'async_request';
/**
* Identifier
*
* @var mixed
* @access protected
*/
protected $identifier;
/**
* Data
*
* (default value: array())
*
* @var array
* @access protected
*/
protected $data = array();
/**
* Initiate new async request
*/
public function __construct() {
$this->identifier = $this->prefix . '_' . $this->action;
add_action( 'wp_ajax_' . $this->identifier, array( $this, 'maybe_handle' ) ); // @phpstan-ignore-line
add_action( 'wp_ajax_nopriv_' . $this->identifier, array( $this, 'maybe_handle' ) ); // @phpstan-ignore-line
}
/**
* Set data used during the request
*
* @param array $data Data.
*
* @return $this
*/
public function data( $data ) {
$this->data = $data;
return $this;
}
/**
* Dispatch the async request
*
* @return array|\WP_Error
*/
public function dispatch() {
$url = add_query_arg( $this->get_query_args(), $this->get_query_url() );
$args = $this->get_post_args();
return wp_remote_post( esc_url_raw( $url ), $args );
}
/**
* Get query args
*
* @return array
*/
protected function get_query_args() {
if ( property_exists( $this, 'query_args' ) ) {
return $this->query_args;
}
$args = array(
'action' => $this->identifier,
'nonce' => wp_create_nonce( $this->identifier ),
);
/**
* Filters the query arguments used during an async request.
*
* @since 2.9.7
*
* @param array $args
*/
return apply_filters( $this->identifier . '_query_args', $args );
}
/**
* Get query URL
*
* @return string
*/
protected function get_query_url() {
if ( property_exists( $this, 'query_url' ) ) {
return $this->query_url;
}
$url = admin_url( 'admin-ajax.php' );
/**
* Filters the query URL used during an async request.
*
* @since 2.9.7
*
* @param string $url
*/
return apply_filters( $this->identifier . '_query_url', $url );
}
/**
* Get post args
*
* @return array
*/
protected function get_post_args() {
if ( property_exists( $this, 'post_args' ) ) {
return $this->post_args;
}
$args = array(
'timeout' => 5,
'blocking' => false,
'body' => $this->data,
'cookies' => $_COOKIE, // Passing cookies ensures request is performed as initiating user.
'sslverify' => apply_filters( 'https_local_ssl_verify', false ), // Local requests, fine to pass false.
);
/**
* Filters the post arguments used during an async request.
*
* @since 2.9.7
*
* @param array $args
*/
return apply_filters( $this->identifier . '_post_args', $args );
}
/**
* Maybe handle
*
* Check for correct nonce and pass to handler.
*
* @return void|mixed
*/
public function maybe_handle() {
// Don't lock up other requests while processing
session_write_close();
check_ajax_referer( $this->identifier, 'nonce' );
$this->handle();
return $this->maybe_wp_die();
}
/**
* Should the process exit with wp_die?
*
* @since 2.9.7
*
* @param mixed $return What to return if filter says don't die, default is null.
*
* @return void|mixed
*/
protected function maybe_wp_die( $return = null ) {
/**
* Should wp_die be used?
*
* @return bool
*/
if ( apply_filters( $this->identifier . '_wp_die', true ) ) {
wp_die();
}
return $return;
}
/**
* Handle
*
* Override this method to perform any actions required
* during the async request.
*/
abstract protected function handle();
}