GFAddOn

Introduction

The GFAddOn class provides basic functionality for developers when creating new add-ons for Gravity Forms. It facilitates the following:

  • Initialization
  • Enforcing Gravity Forms minimum version requirement
  • Creating settings pages (add-on and form settings)
  • Permissions and integration with the Members plugin
  • Conditional script enqueuing including no-conflict mode integration
  • Adding custom meta fields to the entry object
  • Creating a results page
  • Integration with the Gravity Forms Locking API
  • Standardization of UI elements and styles
  • Automatic clean uninstall

Getting Started

These are the first steps you’ll need to take to create an add-on using the Add-On Framework:

  • Create two PHP files. The first file handles registering your add-on. The second file will contain your add-on’s functionality. See A Simple Add-On in the Add-On Framework article for an example.
  • In the second PHP file you would include the Add-On Framework files by calling the following:
GFForms::include_addon_framework();
  • Inherit the Add-On Framework by creating a new class which extends GFAddOn:
class GFSimpleAddOn extends GFAddOn {}
protected $_version = '1.0';
protected $_min_gravityforms_version = '1.9';
protected $_slug = 'simpleaddon';
protected $_path = 'simpleaddon/simpleaddon.php';
protected $_full_path = __FILE__;
protected $_title = 'Gravity Forms Simple Add-On';
protected $_short_title = 'Simple Add-On';
  • Add support for getting an instance of the add-on.

    When Gravity Forms is loading it initializes the add-ons; it does this by looping through each registered add-on and calling its get_instance function. Adding a get_instance function also helps other developers integrate with your add-on.
/**
 * @var object|null $_instance If available, contains an instance of this class.
 */
private static $_instance = null;

/**
 * Returns an instance of this class, and stores it in the $_instance property.
 *
 * @return object $_instance An instance of this class.
 */
public static function get_instance() {
      if ( self::$_instance == null ) {
            self::$_instance = new self();
      }
      return self::$_instance;
}

The Class Variables

  • $_version string

    The version of this add-on.

  • $_min_gravityforms_version string

    The version of Gravity Forms required for this add-on, failing to meet this version prevents the add-on from loading.

  • $_min_compatible_gravityforms_version string

    The minimum version of Gravity Forms that is compatible with all the add-on features, failing to meet this version will still allow the add-on to load and work, but some features will not work as expected or will be disabled.

  • $_slug string

    A short, lowercase, URL-safe unique identifier for the add-on. This will be used in option keys, filter, actions, URLs, and text-domain localization. The maximum size allowed for the slug is 33 characters.

  • $_path string

    Relative path to the plugin from the plugins folder. Example “gravityforms/gravityforms.php”

  • $_full_path string

    The physical path to the main plugin file. Set this to __FILE__

  • $_title string

    The complete title of the Add-On.

  • $_short_title string

    The short title of the Add-On to be used in limited spaces.

Activating Features

The Add-On Framework contains many features that can be activated by overriding functions in the base class (GFAddOn). To override a function, add a function with the same name (and arguments) as the function in the base class.

A good example of this is the plugin page. If you take a look at the code in GFAddOn you’ll see an empty function called plugin_page() that looks like this:

/**
 * Override this function to create a custom plugin page
 */
protected function plugin_page() {}

This function in GFAddOn does nothing; it’s just a placeholder with documentation explaining how to use it. To activate the plugin page and add a menu link for your add-on to the Forms menu, add the following function to the add-on:

/**
 * Creates a custom page for this add-on.
 */
public function plugin_page() {
	echo 'This page appears in the Forms menu';
}

Now the function in the base class has been overridden, the feature has been activated, and the link to the page will appear in the Forms menu.

There are many features like this in the Add-On Framework. Please read through the documentation on this page for further details of the features and how to activate them.

Requiring Dependencies

If your Gravity Forms add-on has any requirements that might not be present everywhere, it’s a good idea to define those dependencies using the minimum_requirements() class method. Within this class method, you will be able to define things such as Gravity Forms/WordPress/PHP versions, other Gravity Forms add-ons, PHP modules, and even other plugins that your Gravity Forms add-on might require.

Example:

array(
  // Require WordPress version 4.6.2 or higher.
  'wordpress' => array(
    'version' => '4.6.2'
  ),

  // Require PHP version 5.3 or higher.
  'php' => array(
    'version'    => '5.3',

    // Require specific PHP extensions.
    'extensions' => array(

      // Require cURL version 1.0 or higher.
      'curl' => array(
        'version' => '1.0'
      ),

      // Require any version of mbstring.
      'mbstring',
    ),

    // Require specific functions to be available.
    'functions' => array(
      'openssl_random_pseudo_bytes',
      'mcrypt_create_iv'
    ),
  ),

  // Require other add-ons to be present.
  'add-ons' => array(

    // Require any version of the Mailchimp add-on.
    'gravityformsmailchimp',

    // Require the Stripe add-on and ensure the name matches.
    'gravityformsstripe' => array(
      'name' => 'Gravity Forms Stripe Add-On'
    ),

    // Require the PayPal add-on version 5.0 or higher.
    'gravityformspaypal' => array(
      'version' => '5.0'
    ),
  ),

  // Required plugins.
  'plugins'   => array(

    // Require the REST API.
    'rest-api/plugin.php',

    // Require Jetpack and ensure the name matches.
    'jetpack/jetpack.php' => 'Jetpack by WordPress.com',
  ),

  // Any additional custom requirements via callbacks.
  array( $this, 'custom_requirement' ),
);

Initialization

The Add-On Framework provides the following functions to help with initialization and to help organize your code:

  • pre_init()
    Override this function to perform any tasks that need to be done before the WordPress action “init” fires

     

  • init()
    Override this function to perform tasks during WordPress initialization

  • init_admin()
    Override this function to perform tasks only in the admin back-end

  • init_frontend()
    Override this function to perform tasks only in the front-end

  • init_ajax()
    Override this function to perform tasks only while processing ajax requests

IMPORTANT: call the parent function first. e.g. parent::init();

Example:

GFForms::include_addon_framework();
class GFSimpleAddOn extends GFAddOn {

    // [snip: class variables]

    public function pre_init() {
        parent::pre_init();
        // add tasks or filters here that you want to perform during the class constructor - before WordPress has been completely initialized
    }

    public function init() {
        parent::init();
        // add tasks or filters here that you want to perform both in the backend and frontend and for ajax requests
    }

    public function init_admin() {
        parent::init_admin();
        // add tasks or filters here that you want to perform only in admin
    }

    public function init_frontend() {
        parent::init_frontend();
        // add tasks or filters here that you want to perform only in the front end
    }

    public function init_ajax() {
        parent::init_ajax();
        // add tasks or filters here that you want to perform only during ajax requests
    }
}

Settings API

The Add-On Framework includes a Settings API that can be used to create form and plugin settings pages. The API provides the mechanisms for rendering field UIs and saving values automatically. It supports standard field types like radio buttons, text boxes, and checkboxes and also custom field types.

See the Settings API for details about how to define sections of fields.

See the following articles for implementation examples of the various field types.

There are also a number of Helper Functions available for use when rendering custom settings.

Creating Form Settings

To create a tab on the Form Settings page, override the form_settings_fields() function returning an array with the configuration for the settings.

Complete example with all field types:

/**
 * Configures the settings which should be rendered on the Form Settings > Simple Add-On tab.
 *
 * @return array
 */
public function form_settings_fields( $form ) {
	return array(
		array(
			'title'  => esc_html__( 'Simple Form Settings', 'simpleaddon' ),
			'fields' => array(
				array(
					'label'   => esc_html__( 'My checkbox', 'simpleaddon' ),
					'type'    => 'checkbox',
					'name'    => 'enabled',
					'tooltip' => esc_html__( 'This is the tooltip', 'simpleaddon' ),
					'choices' => array(
						array(
							'label' => esc_html__( 'Enabled', 'simpleaddon' ),
							'name'  => 'enabled',
						),
					),
				),
				array(
					'label'   => esc_html__( 'My checkboxes', 'simpleaddon' ),
					'type'    => 'checkbox',
					'name'    => 'checkboxgroup',
					'tooltip' => esc_html__( 'This is the tooltip', 'simpleaddon' ),
					'choices' => array(
						array(
							'label' => esc_html__( 'First Choice', 'simpleaddon' ),
							'name'  => 'first',
						),
						array(
							'label' => esc_html__( 'Second Choice', 'simpleaddon' ),
							'name'  => 'second',
						),
						array(
							'label' => esc_html__( 'Third Choice', 'simpleaddon' ),
							'name'  => 'third',
						),
					),
				),
				array(
					'label'   => esc_html__( 'My Radio Buttons', 'simpleaddon' ),
					'type'    => 'radio',
					'name'    => 'myradiogroup',
					'tooltip' => esc_html__( 'This is the tooltip', 'simpleaddon' ),
					'choices' => array(
						array(
							'label' => esc_html__( 'First Choice', 'simpleaddon' ),
						),
						array(
							'label' => esc_html__( 'Second Choice', 'simpleaddon' ),
						),
						array(
							'label' => esc_html__( 'Third Choice', 'simpleaddon' ),
						),
					),
				),
				array(
					'label'      => esc_html__( 'My Horizontal Radio Buttons', 'simpleaddon' ),
					'type'       => 'radio',
					'horizontal' => true,
					'name'       => 'myradiogrouph',
					'tooltip'    => esc_html__( 'This is the tooltip', 'simpleaddon' ),
					'choices'    => array(
						array(
							'label' => esc_html__( 'First Choice', 'simpleaddon' ),
						),
						array(
							'label' => esc_html__( 'Second Choice', 'simpleaddon' ),
						),
						array(
							'label' => esc_html__( 'Third Choice', 'simpleaddon' ),
						),
					),
				),
				array(
					'label'   => esc_html__( 'My Dropdown', 'simpleaddon' ),
					'type'    => 'select',
					'name'    => 'mydropdown',
					'tooltip' => esc_html__( 'This is the tooltip', 'simpleaddon' ),
					'choices' => array(
						array(
							'label' => esc_html__( 'First Choice', 'simpleaddon' ),
							'value' => 'first',
						),
						array(
							'label' => esc_html__( 'Second Choice', 'simpleaddon' ),
							'value' => 'second',
						),
						array(
							'label' => esc_html__( 'Third Choice', 'simpleaddon' ),
							'value' => 'third',
						),
					),
				),
				array(
					'label'             => esc_html__( 'My Text Box', 'simpleaddon' ),
					'type'              => 'text',
					'name'              => 'mytext',
					'tooltip'           => esc_html__( 'This is the tooltip', 'simpleaddon' ),
					'class'             => 'medium',
					'feedback_callback' => array( $this, 'is_valid_setting' ),
				),
				array(
					'label'   => esc_html__( 'My Text Area', 'simpleaddon' ),
					'type'    => 'textarea',
					'name'    => 'mytextarea',
					'tooltip' => esc_html__( 'This is the tooltip', 'simpleaddon' ),
					'class'   => 'medium merge-tag-support mt-position-right',
				),
				array(
					'label' => esc_html__( 'My Hidden Field', 'simpleaddon' ),
					'type'  => 'hidden',
					'name'  => 'myhidden',
				),
				array(
					'label' => esc_html__( 'My Custom Field', 'simpleaddon' ),
					'type'  => 'my_custom_field_type',
					'name'  => 'my_custom_field',
					'args'  => array(
						'text'     => array(
							'label'         => esc_html__( 'A textbox sub-field', 'simpleaddon' ),
							'name'          => 'subtext',
							'default_value' => 'change me',
						),
						'checkbox' => array(
							'label'   => esc_html__( 'A checkbox sub-field', 'simpleaddon' ),
							'name'    => 'my_custom_field_check',
							'choices' => array(
								array(
									'label'         => esc_html__( 'Activate', 'simpleaddon' ),
									'name'          => 'subcheck',
									'default_value' => true,
								),
							),
						),
					),
				),
			),
		),
	);
}

/**
 * Define the markup for the my_custom_field_type type field.
 *
 * @param array $field The field properties.
 */
public function settings_my_custom_field_type( $field ) {
	echo '<div>' . esc_html__( 'My custom field contains a few settings:', 'simpleaddon' ) . '</div>';

	// get the text field settings from the main field and then render the text field
	$text_field = $field['args']['text'];
	$this->settings_text( $text_field );

	// get the checkbox field settings from the main field and then render the checkbox field
	$checkbox_field = $field['args']['checkbox'];
	$this->settings_checkbox( $checkbox_field );
}

/**
 * The feedback callback for the 'mytextbox' setting on the plugin settings page and the 'mytext' setting on the form settings page.
 *
 * @param string $value The setting value.
 *
 * @return bool
 */
public function is_valid_setting( $value ) {
	return strlen( $value ) > 10;
}

The code above will create a tab on the Form Settings page for the Simple Add-On and look similar to the following:
Form Settings Fields Example

Accessing Form Settings

The form settings for the current add-on can be accessed easily by using the function get_form_settings($form). This returns an associative array with the settings for the current add-on for the given form.

Example:

$settings = $this->get_form_settings( $form );

Adding Custom Settings to the Main Form Settings Tab

To add a custom setting to the main form settings tab, please consult the documentation for the gform_form_settings hook. Here’s a quick example of how you’d implement it with the Add-On Framework:

GFForms::include_addon_framework();
class GFSimpleAddOn extends GFAddOn {

    // [snip: class variables]

    public function init() {
        parent::init();
        add_filter( 'gform_form_settings', array( $this, 'my_custom_form_setting' ), 10, 2 );
        add_filter( 'gform_pre_form_settings_save', array( $this, 'save_my_custom_form_setting' ) );
    }

    function my_custom_form_setting( $settings, $form ) {
        $settings['Form Basics']['my_custom_setting'] = '
    <tr>
        <th><label for="my_custom_setting">My Custom Label</label></th>
        <td><input value="' . rgar( $form, 'my_custom_setting' ) . '" name="my_custom_setting"></td>
    </tr>';

        return $settings;
    }

    function save_my_custom_form_setting( $form ) {
        $form['my_custom_setting'] = rgpost( 'my_custom_setting' );
        return $form;
    }
}

Creating Plugin Settings

Plugin settings pages appear in a tab in the Gravity Forms settings page. To create a settings page, override the plugin_settings_fields() function using the same array structure as described above for the form settings.

Example:

/**
 * Configures the settings which should be rendered on the add-on settings tab.
 *
 * @return array
 */
public function plugin_settings_fields() {
	return array(
		array(
			'title'  => esc_html__( 'Simple Add-On Settings', 'simpleaddon' ),
			'fields' => array(
				array(
					'name'              => 'mytextbox',
					'tooltip'           => esc_html__( 'This is the tooltip', 'simpleaddon' ),
					'label'             => esc_html__( 'This is the label', 'simpleaddon' ),
					'type'              => 'text',
					'class'             => 'small',
					'feedback_callback' => array( $this, 'is_valid_setting' ),
				)
			)
		)
	);
}
[/php]

Accessing Plugin Settings

The plugin settings for the current add-on can be accessed easily by using the following functions:

  • get_plugin_settings()
    returns all plugin settings in an associative array

     

  • get_plugin_setting( $setting_name )
    returns a single setting with the given setting name e.g. “my_setting_name”

Example:

$all_plugin_settings = $this->get_plugin_settings();
$text                = $this->get_plugin_setting( 'mytextbox');

Enqueuing Scripts and Styles

See the Including Scripts and Styles When Using the Add-on Framework article.

Results Page

To add a results page, override the get_results_page_config() function and return the configuration array with the following properties:

  • title string

    The title of the results page.

  • capabilities array

    An array of capabilities that can view the results page.

  • callbacks array

    An array of callback functions. Possible values: fields, markup, calculation

Example:

public function get_results_page_config() {
    return array(
        'title'        => 'Poll Results',
        'capabilities' => array( 'gravityforms_polls_results' ),
        'callbacks'    => array(
            'fields' => array( $this, 'results_fields' )
        )
    );
}

public function results_fields( $form ) {
    return GFAPI::get_fields_by_type( $form, array( 'poll' ) );
}
[/php]

Entry Meta

See the Including and using Entry Meta with the Add-On Framework article.

Object Locking API

The Gravity Forms Object Locking API prevents conflicts from occurring by preventing concurrent editing by different users of forms, entries, form settings and plugin settings. This eliminates the problem of users overwriting the changes made be other users. When one user is editing an object, the second user will be offered the choice to request control over the object. The first user will then be offered the choice to accept or deny that request. If the first user fails to respond, the second user will be offered control.

The Locking API uses the WordPress Heartbeat API to send updates at regular intervals, usually every 15 seconds when the page has focus, and every 2 minutes when the page doesn’t have the focus.

The Add-On Framework provides developers access to the Object Locking API in 2 ways:

  1. Locking is automatically implemented for form settings and plugin settings – nothing additional is required for this to work.

     

  2. Locking for custom objects. For example, an add-on that manages employee information may have an “employee” object. The Add-On Framework will prevent concurrent editing of the employee profile.

Locking is activated by overriding the following functions:

  • get_locking_config()
    Return an array with the locking configuration to activate locking

     

  • get_locking_object_id()
    Return the object id

  • is_locking_edit_page()
    Return true if the current page is the edit page

  • is_locking_view_page()
    Return true if the current page is the view page

  • is_locking_list_page()
    Return true if the current page is the list page

public function get_locking_config(){
    $strings = array(
        "currently_locked"  => __('This contact is currently locked. Click on the "Request Control" button to let %s know you'd like to take over.', "gravityforms"),
        "currently_editing" => "%s is currently editing this contact",
        "taken_over"        => "%s has taken over and is currently editing this contact.",
        "lock_requested"    => __("%s has requested permission to take over control of this contact.", "gravityforms")
    );
    $contact_id = $this->get_object_id();
    $config = array(
        "object_type" => "contact",
        "capabilities" => array("gravityforms_contacts_edit_contacts"),
        "redirect_url" => admin_url(sprintf("admin.php?page=gf_contacts&view=contact&subview=profile&id=%d&edit=0", $contact_id)),
        "edit_url" => admin_url(sprintf("admin.php?page=gf_contacts&view=contact&subview=profile&id=%d&edit=1", $contact_id)),
        "strings" => $strings
        );
    return $config;
}
public function get_locking_object_id(){
    return rgget("id");
}
public function is_locking_edit_page(){
    $is_edit_page = rgget("page") == "gf_contacts" && rgget("view") == "contact" && rgget("subview") == "profile" && rgget("edit") == 1;
    return $is_edit_page;
}
public function is_locking_view_page(){
    $is_view_page = rgget("page") == "gf_contacts" && rgget("view") == "contact" && rgget("subview") == "profile" && rgget("edit") == 0;
    return $is_view_page;
}
public function is_locking_list_page(){
    $is_list_page = rgget("page") == "gf_contacts" && rgempty("view", $_GET);
    return $is_list_page;
}

Uninstalling

All Gravity Forms Add-Ons should provide a way to remove settings completely before the user deactivates and deletes the plugin. The Add-On Framework handles much of this for you so you don’t need to configure anything unless you’ve added settings or database tables of your own. The uninstall button will be on the plugin settings page. The following elements will be removed automatically when the user uninstalls the add-on.

  • Form settings
  • Plugin settings
  • Entry meta
  • Version information

If you need to perform additional tasks such as removing custom options or database tables then override the uninstall() function. This will be called after permissions have been checked and before removing all the settings.

Example:

public function uninstall() {
    // custom code to drop database tables and remove custom options
}

The uninstall section of the settings page can be modified or removed completely by overriding the render_uninstall() function.

Example:

public function render_uninstall() {
    // an empty function will remove the uninstall section on the settings page
}

Sample Add-On

A sample add-on is available which demonstrates the basic features of the Add-On Framework:

  • Plugin page
  • Form Settings
  • Plugin Settings
  • Scripts and Styles
https://github.com/rocketgenius/simpleaddon