Custom Actions
Create your own form actions by extending the Action base class. Custom actions appear in the form editor's Actions dropdown and can be configured per form.
Action Base Class
Every action extends Core_Forms\Actions\Action and implements two methods:
namespace Core_Forms\Actions;
use Core_Forms\Form;
use Core_Forms\Submission;
abstract class Action {
public $type = ''; // Unique identifier
public $label = ''; // Display name in admin
abstract function page_settings( $settings, $index );
abstract function process( array $settings, Submission $submission, Form $form );
}
Registration
The hook() method registers three WordPress hooks:
cf_available_form_actions-- Adds the action to the dropdowncf_output_form_action_{type}_settings-- Renders the settings UIcf_process_form_action_{type}-- Processes the action on submission
$action = new MyAction();
$action->hook();
Complete Example: CRM Integration
<?php
namespace My_Plugin\Actions;
use Core_Forms\Actions\Action;
use Core_Forms\Form;
use Core_Forms\Submission;
class CrmAction extends Action {
public $type = 'my_crm';
public $label = 'Send to CRM';
public function __construct() {
$this->label = __( 'Send to CRM', 'my-plugin' );
}
/**
* Render the action settings in the form editor.
*
* @param array $settings Current saved settings.
* @param string|int $index Action index (for field names).
*/
public function page_settings( $settings, $index ) {
$defaults = [
'api_url' => '',
'api_key' => '',
'list_id' => '',
'email_field'=> 'email',
'name_field' => 'name',
];
$settings = array_merge( $defaults, $settings );
?>
<span class="cf-action-summary">
<?php printf( 'CRM: %s', esc_html( $settings['api_url'] ?: 'Not configured' ) ); ?>
</span>
<input type="hidden"
name="form[settings][actions][<?php echo $index; ?>][type]"
value="<?php echo $this->type; ?>" />
<table class="form-table">
<tr>
<th><label><?php _e( 'API URL', 'my-plugin' ); ?></label></th>
<td>
<input name="form[settings][actions][<?php echo $index; ?>][api_url]"
value="<?php echo esc_attr( $settings['api_url'] ); ?>"
type="url" class="regular-text" required />
</td>
</tr>
<tr>
<th><label><?php _e( 'API Key', 'my-plugin' ); ?></label></th>
<td>
<input name="form[settings][actions][<?php echo $index; ?>][api_key]"
value="<?php echo esc_attr( $settings['api_key'] ); ?>"
type="text" class="regular-text" required />
</td>
</tr>
<tr>
<th><label><?php _e( 'List ID', 'my-plugin' ); ?></label></th>
<td>
<input name="form[settings][actions][<?php echo $index; ?>][list_id]"
value="<?php echo esc_attr( $settings['list_id'] ); ?>"
type="text" class="regular-text" />
</td>
</tr>
<tr>
<th><label><?php _e( 'Email Field', 'my-plugin' ); ?></label></th>
<td>
<input name="form[settings][actions][<?php echo $index; ?>][email_field]"
value="<?php echo esc_attr( $settings['email_field'] ); ?>"
type="text" class="regular-text" placeholder="email" />
</td>
</tr>
<tr>
<th><label><?php _e( 'Name Field', 'my-plugin' ); ?></label></th>
<td>
<input name="form[settings][actions][<?php echo $index; ?>][name_field]"
value="<?php echo esc_attr( $settings['name_field'] ); ?>"
type="text" class="regular-text" placeholder="name" />
</td>
</tr>
</table>
<?php
}
/**
* Process the action when the form is submitted.
*
* @param array $settings Action settings.
* @param Submission $submission The submission.
* @param Form $form The form.
*/
public function process( array $settings, Submission $submission, Form $form ) {
if ( empty( $settings['api_url'] ) || empty( $settings['api_key'] ) ) {
return false;
}
$data = $submission->data;
$body = [
'email' => $data[ $settings['email_field'] ] ?? '',
'name' => $data[ $settings['name_field'] ] ?? '',
'list_id' => $settings['list_id'],
'source' => home_url(),
'form' => $form->title,
];
$response = wp_remote_post( $settings['api_url'], [
'headers' => [
'Authorization' => 'Bearer ' . $settings['api_key'],
'Content-Type' => 'application/json',
],
'body' => wp_json_encode( $body ),
'timeout' => 15,
] );
if ( is_wp_error( $response ) ) {
throw new \Exception( $response->get_error_message() );
}
$code = wp_remote_retrieve_response_code( $response );
if ( $code >= 400 ) {
throw new \Exception( "CRM API returned HTTP {$code}" );
}
return true;
}
}
Registering the Action
Register your action in your plugin's initialization:
add_action( 'init', function() {
if ( class_exists( 'Core_Forms\Actions\Action' ) ) {
$crm = new \My_Plugin\Actions\CrmAction();
$crm->hook();
}
} );
Settings Field Names
Action settings fields must follow this naming convention:
name="form[settings][actions][{$index}][{setting_key}]"
The $index parameter passed to page_settings() is the action's position in the actions array. Always include a hidden type field:
<input type="hidden"
name="form[settings][actions][<?php echo $index; ?>][type]"
value="my_crm" />
Error Handling
Throw exceptions in process() to signal failure. When running in the background queue, the ActionQueue catches exceptions and handles retry logic:
public function process( array $settings, Submission $submission, Form $form ) {
// Throwing triggers retry with exponential backoff
throw new \Exception( 'API connection failed' );
}
Testing Actions
Use the REST API test endpoint to run an action with sample data:
POST /wp-json/cf/v1/actions/test
{
"action_type": "my_crm",
"form_id": 123,
"settings": {
"api_url": "https://api.example.com/contacts",
"api_key": "test-key",
"email_field": "email"
},
"test_data": {
"name": "Test User",
"email": "test@example.com"
}
}