Extension Hooks

File: ./mod.freeform.php and ./mcp.freeform.php

File: ./mod.freeform.php

File: ./libraries/Freeform_Forms.php

File: ./libraries/Notifications.php

freeform_module_validate_begin

The freeform_module_validate_begin hook currently exists in both the control panel file and module file, due to create/edit entry abilities in the control panel.

  • Files:
    • mod.freeform.php
    • mcp.freeform.php
  • Method:
    • mod.freeform.php -> save_form()
    • mcp.freeform.php -> save_entry()
  • Possible Uses:
    • Allows developers to alter the $error array and add errors before any others are added.
  • Variables:
    • $errors: (array) - empty starting error array
    • $this: (object) - class object of function caller
  • Important Class Vars
    • $this->field_errors: (array) - array of errors for specific fields
    • $this->edit: (bool) - form is in edit mode
    • $this->multipage: (bool) - form is in multipage mode
    • $this->last_page: (bool) - form is posting the last multipage page
  • Return:
    • type: associative array
    • required: yes

Important note about multipage forms and form editing:

With Freeform Classic, it is possible to have multipage forms and edit forms on the front end.

Hooks fire on every page of the a multipage form as data is stored each time, so if you are wanting to run a hook that only does work on the last page, make sure you are checking for $obj->multipage == TRUE and $obj->last_page == TRUE.

The same goes for editing. If you do not want your extension to do work on edits, check $obj->edit == TRUE; before running your code.

Remember to return required return arrays (and check for extensions->last_call) either way!

Important differences from Freeform 3:

Most errors are now keyed in the error array in order to assist inline errors or json responses to ajax validation, therefore, if you add an error for a specific field, make sure you key the error and add it to $obj->field_errors or $errors depending on if its a field error or a general error:

public function freeform_module_validate_begin($errors, $obj)
{
	$obj->field_errors['my_field_name']	= lang('error_line');
	$errors['my_general_error']				= lang('error_line_2');
}

Also notice that fields are now in their own array in the object container. This is necessary for Freeform's new abilities for inline errors and better ajax validation.

Hook Code:

if (ee()->extensions->active_hook('freeform_module_validate_begin') === TRUE)
{
	$errors = ee()->extensions->call(
		'freeform_module_validate_begin',
		$errors,
		$this
	);

	if (ee()->extensions->end_script === TRUE) return;
}

Extension function example:

public function freeform_module_validate_begin($errors, $obj)
{
	//have other extensions already manipulated?
	if (ee()->extensions->last_call !== FALSE)
	{
		$errors = ee()->extensions->last_call;
	}

	//detect and add custom errors
	$obj->field_errors['my_field_name']	= lang('error_line');
	$errors['my_general_error']				= lang('error_line_2');

	//must return error array
	return $errors;
}

Detecting Class object:

Since the hook is called from two different classes, you might need to detect which one for your extension's purposes. This can be achieved two ways:

REQ Detection:

if (REQ == 'CP')
{
	//request is from control panel
}
else
{
	//request is from template
}

Class Detection:

public function freeform_module_validate_begin($errors, $obj)
{
	if (get_class($obj) == 'Freeform')
	{
		//request is from template
	}
	else
	{
		//request is from control panel
	}
}

freeform_module_validate_end

The freeform_module_validate_end hook currently exists in both the control panel file and module file, due to create/edit entry abilities in the control panel.

  • Files:
    • mod.freeform.php
    • mcp.freeform.php
  • Method:
    • mod.freeform.php -> save_form()
    • mcp.freeform.php -> save_entry()
  • Possible Uses:
    • Allows developers to alter the $error array and edit errors before the errors are sent to the user.
  • Variables:
    • $errors: (array) - all errors after validation has finished
    • $this: (object) - class object of function caller
  • Important Class Vars
    • $this->field_errors: (array) - array of errors for specific fields
    • $this->edit: (bool) - form is in edit mode
    • $this->multipage: (bool) - form is in multipage mode
    • $this->last_page: (bool) - form is posting the last multipage page
  • Return:
    • type: associative array
    • required: yes

Unlike freeform_module_validate_begin, this hook occurs right before errors are sent to the user and already has all errors added by Freeform Classic or the freeform_module_validate_begin hook included in the $errors var and $obj->field_errors array.

Important note about multipage forms and form editing:

With Freeform Classic, it is possible to have multipage forms and edit forms on the front end.

Hooks fire on every page of the a multipage form as data is stored each time, so if you are wanting to run a hook that only does work on the last page, make sure you are checking for $obj->multipage == TRUE and $obj->last_page == TRUE.

The same goes for editing. If you do not want your extension to do work on edits, check $obj->edit == TRUE; before running your code.

Remember to return required return arrays (and check for extensions->last_call) either way!

Important differences from Freeform 3:

Most errors are now keyed in the error array in order to assist inline errors or json responses to ajax validation, therefore, if you add an error for a specific field, make sure you key the error and add it to $obj->field_errors or $errors depending on if its a field error or a general error:

public function freeform_module_validate_end($errors, $obj)
{
	$obj->field_errors['my_field_name']	= lang('error_line');
	$errors['my_general_error']				= lang('error_line_2');
}

Also notice that fields are now in their own array in the object container. This is necessary for Freeform's new abilities for inline errors and better ajax validation.

Hook Code:

if (ee()->extensions->active_hook('freeform_module_validate_end') === TRUE)
{
	$errors = ee()->extensions->call(
		'freeform_module_validate_end',
		$errors,
		$this
	);

	if (ee()->extensions->end_script === TRUE) return;
}

Extension function example:

public function freeform_module_validate_end($errors, $obj)
{
	//have other extensions already manipulated?
	if (ee()->extensions->last_call !== FALSE)
	{
		$errors = ee()->extensions->last_call;
	}

	//detect and add custom errors
	$obj->field_errors['my_field_name']	= lang('error_line');
	$errors['my_general_error']				= lang('error_line_2');

	//must return error array
	return $errors;
}

Detecting Class object:

Since the hook is called from two different classes, you might need to detect which one for your extension's purposes. This can be achieved two ways:

REQ Detection:

if (REQ == 'CP')
{
	//request is from control panel
}
else
{
	//request is from template
}

Class Detection:

public function freeform_module_validate_end($errors, $obj)
{
	if (get_class($obj) == 'Freeform')
	{
		//request is from template
	}
	else
	{
		//request is from control panel
	}
}

freeform_module_insert_begin

The freeform_module_insert_begin hook currently exists in both the control panel file and module file, due to create/edit entry abilities in the control panel.

  • Files:
    • mod.freeform.php
    • mcp.freeform.php
  • Method:
    • mod.freeform.php -> save_form()
    • mcp.freeform.php -> save_entry()
  • Possible Uses:
    • Allows developers to alter the field data that will be inputted into the form table for a new entry
  • Variables:
    • $field_input_data: (array) - input field from the posted form (only field that are actually in the form, and not the entire $_POST array.)
    • $entry_id: (int) - id of entry (0 if not in edit mode)
    • $form_id: (int) - id of form (always > 0, other wise it's a bug you should report ;D)
    • $this: (object) - class object of function caller
  • Important Class Vars
    • $this->edit: (bool) - form is in edit mode
    • $this->multipage: (bool) - form is in multipage mode
    • $this->last_page: (bool) - form is posting the last multipage page
  • Return:
    • type: associative array
    • required: yes

This hook occurs before fields are inserted and is the ideal time to adjust any values that are going to be inserted into the form's respective entries table.

Important note about multipage forms and form editing:

With Freeform Classic, it is possible to have multipage forms and edit forms on the front end.

Hooks fire on every page of the a multipage form as data is stored each time, so if you are wanting to run a hook that only does work on the last page, make sure you are checking for $obj->multipage == TRUE and $obj->last_page == TRUE.

The same goes for editing. If you do not want your extension to do work on edits, check $obj->edit == TRUE; before running your code.

Remember to return required return arrays (and check for extensions->last_call) either way!

Important differences from Freeform 3:

Freeform 3 only included the first parameter $data. As Freeform Classic now has a table per entry and forms are more formal than just a name for a collection, $form_id and $entry_id are included as well.

For further manipulation, the class object is also included in the hook call.

Hook Code:

if (ee()->extensions->active_hook('freeform_module_insert_begin') === TRUE)
{
	$field_input_data = ee()->extensions->call(
		'freeform_module_insert_begin',
		$field_input_data,
		$entry_id,
		$form_id,
		$this
	);

	if (ee()->extensions->end_script === TRUE)
	{
		return;
	}
}

Extension function example:

public function freeform_module_insert_begin($inputs, $entry_id, $form_id, $obj)
{
	//have other extensions already manipulated?
	if (ee()->extensions->last_call !== FALSE)
	{
		$inputs = ee()->extensions->last_call;
	}

	//custom input data
	$inputs['my_field_name']	= 'adjusted value';


	//must return input array
	return $inputs;
}

Detecting Class object:

Since the hook is called from two different classes, you might need to detect which one for your extension's purposes. This can be achieved two ways:

REQ Detection:

if (REQ == 'CP')
{
	//request is from control panel
}
else
{
	//request is from template
}

Class Detection:

public function freeform_module_insert_begin($inputs, $entry_id, $form_id, $obj)
{
	if (get_class($obj) == 'Freeform')
	{
		//request is from template
	}
	else
	{
		//request is from control panel
	}
}

freeform_module_insert_end

The freeform_module_insert_end hook currently exists in both the control panel file and module file, due to create/edit entry abilities in the control panel.

  • Files:
    • mod.freeform.php
    • mcp.freeform.php
  • Method:
    • mod.freeform.php -> save_form()
    • mcp.freeform.php -> save_entry()
  • Possible Uses:
    • Allows developers to alter the field data that will be inputted into the form table for a new entry
  • Variables:
    • $field_input_data: (array) - input field from the posted form (only field that are actually in the form, and not the entire $_POST array.)
    • $entry_id: (int) - id of entry (always > 0 as this is post entry insert)
    • $form_id: (int) - id of form (always > 0, other wise it's a bug you should report ;D)
    • $this: (object) - class object of function caller
  • Important Class Vars
    • $this->edit: (bool) - form is in edit mode
    • $this->multipage: (bool) - form is in multipage mode
    • $this->last_page: (bool) - form is posting the last multipage page
  • Return:
    • required: no

This hook occurs after the entry in inserted and is the ideal time to use for integration into other services, or for add-ons that require a reference to the form and entry of a completed form.

Important note about multipage forms and form editing:

With Freeform Classic, it is possible to have multipage forms and edit forms on the front end.

Hooks fire on every page of the a multipage form as data is stored each time, so if you are wanting to run a hook that only does work on the last page, make sure you are checking for $obj->multipage == TRUE and $obj->last_page == TRUE.

The same goes for editing. If you do not want your extension to do work on edits, check $obj->edit == TRUE; before running your code.

Important differences from Freeform 3:

Freeform 3 only included the first two parameters $data and $entry_id. Notifications have their own hooks and library now and as such the old $msg param that contained notification data has been done away with and replaced with $form_id, as Freeform Classic now has a table per entry and forms are more formal than just a name for a collection.

For further manipulation, the class object is also included in the hook call.

Hook Code:

if (ee()->extensions->active_hook('freeform_module_insert_end') === TRUE)
{
	ee()->extensions->call(
		'freeform_module_insert_end',
		$field_input_data,
		$entry_id,
		$form_id,
		$this
	);

	if (ee()->extensions->end_script === TRUE)
	{
		return;
	}
}

Extension function example:

public function freeform_module_insert_end($inputs, $entry_id, $form_id, $obj)
{
	if ( ! $obj->edit AND
		 ( ! $obj->multipage OR
		 	($obj->multipage AND $obj->last_page)
		 )
	)
	{
		//do something with final data
	}
}

Detecting Class object:

Since the hook is called from two different classes, you might need to detect which one for your extension's purposes. This can be achieved two ways:

REQ Detection:

if (REQ == 'CP')
{
	//request is from control panel
}
else
{
	//request is from template
}

Class Detection:

public function freeform_module_insert_end($inputs, $entry_id, $form_id, $obj)
{
	if (get_class($obj) == 'Freeform')
	{
		//request is from template
	}
	else
	{
		//request is from control panel
	}
}

freeform_module_form_begin

  • Files:
    • mod.freeform.php
  • Method:
    • mod.freeform.php -> form()
      • also via ->edit(), ->composer(), and ->composer_edit(), which call ->form()
  • Possible Uses:
    • Allows developers to alter ee()->TMPL->tagdata before the form is built.
  • Variables:
    • $this: (object) - class object of function caller
  • Important Class Vars
    • $this->edit: (bool) - form is in edit mode
  • Return:
    • required: no

This hook occurs before the form is built and

Important note about multipage forms and form editing:

With Freeform Classic, it is possible to have multipage forms and edit forms on the front end.

At this point in the code, only $obj->edit is available, but more parsing must happen before $obj->multipage == TRUE and $obj->last_page are available.

Hook Code:

if (ee()->extensions->active_hook('freeform_module_form_begin') === TRUE)
{
	ee()->extensions->call(
		'freeform_module_form_begin',
		$this
	);

	if (ee()->extensions->end_script === TRUE) return;
}

Extension function example:

public function freeform_module_form_begin($obj)
{
	if ( ! $obj->edit)
	{
		//do something with non-edit forms
	}

	ee()->TMPL->tagdata .= 'No one expects the Spanish Inquisition!';
}

freeform_module_pre_form_parse

  • Files:
    • mod.freeform.php
  • Method:
    • mod.freeform.php -> form()
      • also via ->edit(), ->composer(), and ->composer_edit(), which call ->form()
  • Possible Uses:
    • Allows developers to alter variables and tagdata before they are sent to the template parser
  • Variables:
    • $tagdata: (string) - partially parsed template data
    • $this: (object) - class object of function caller
  • Important Class Vars
    • $this->edit: (bool) - form is in edit mode
    • $this->multipage: (bool) - form is in multipage mode
    • $this->last_page: (bool) - form is posting the last multipage page
    • $this->variables: (array) - output variables that will be used to parse the tagdata
  • Return:
    • type: string
    • required: yes

This hook occurs before the form is built and after variables are collected, thus this is a good place to alter any variables before the parser runs.

Important note about multipage forms and form editing:

With Freeform Classic, it is possible to have multipage forms and edit forms on the front end.

Hooks fire on every page of the a multipage form as data is stored each time, so if you are wanting to run a hook that only does work on the last page, make sure you are checking for $obj->multipage == TRUE and $obj->last_page == TRUE.

The same goes for editing. If you do not want your extension to do work on edits, check $obj->edit == TRUE; before running your code.

Remember to return required return strings (and check for extensions->last_call) either way!

Hook Code:

if (ee()->extensions->active_hook('freeform_module_pre_form_parse') === TRUE)
{
	$tagdata = ee()->extensions->call(
		'freeform_module_pre_form_parse',
		$tagdata,
		$this
	);

	if (ee()->extensions->end_script === TRUE) return;
}

Extension function example:

public function freeform_module_pre_form_parse($tagdata, $obj)
{

	//have other extensions already manipulated?
	if (ee()->extensions->last_call !== FALSE)
	{
		$tagdata = ee()->extensions->last_call;
	}

	if ( ! $obj->edit)
	{
		//do for non-edit forms
	}


	$tagdata .= "Right, enough of that! That's too silly!";

	//required
	return $tagdata;
}

freeform_module_form_end

  • Files:
    • mod.freeform.php
  • Method:
    • mod.freeform.php -> form()
      • also via ->edit(), ->composer(), and ->composer_edit(), which call ->form()
  • Possible Uses:
    • Allows developers to alter variables and tagdata before they are sent to the template parser
  • Variables:
    • $return: (string) - final output tagdata
    • $this: (object) - class object of function caller
  • Important Class Vars
    • $this->edit: (bool) - form is in edit mode
    • $this->multipage: (bool) - form is in multipage mode
    • $this->last_page: (bool) - form is posting the last multipage page
    • $this->variables: (array) - output variables that will be used to parse the tagdata
  • Return:
    • type: string
    • required: yes

This hook occurs right at the end of form processing and right before tagdata is returned to the template parser for final output.

Important note about multipage forms and form editing:

With Freeform Classic, it is possible to have multipage forms and edit forms on the front end.

Hooks fire on every page of the a multipage form as data is stored each time, so if you are wanting to run a hook that only does work on the last page, make sure you are checking for $obj->multipage == TRUE and $obj->last_page == TRUE.

The same goes for editing. If you do not want your extension to do work on edits, check $obj->edit == TRUE; before running your code.

Remember to return required return strings (and check for extensions->last_call) either way!

Hook Code:

if (ee()->extensions->active_hook('freeform_module_form_end') === TRUE)
{
	$return = ee()->extensions->call(
		'freeform_module_form_end',
		$return,
		$this
	);

	if (ee()->extensions->end_script === TRUE) return;
}

Extension function example:

public function freeform_module_form_end($return, $obj)
{

	//have other extensions already manipulated?
	if (ee()->extensions->last_call !== FALSE)
	{
		$return = ee()->extensions->last_call;
	}

	if ( ! $obj->edit)
	{
		//do for non-edit forms
	}


	$return .= 'Listen. Strange women lying in ponds distributing swords is no basis for a system of government!';

	//required
	return $return;
}

freeform_module_entry_delete

  • Files:
    • Forms.php
  • Method:
    • Forms.php -> delete_entries()
      • called by mcp.freeform.php -> delete_entries()
  • Possible Uses:
    • Allows developers to adjust other third party code when form entries are deleted.
  • Variables:
    • $form_id (int) - form_id of posted entry to notify
    • $entry_ids (array) - entry_id of posted entry to notify
    • $this: (object) - class object of function caller
  • Return:
    • required: no

This hook is called after the verification of entry_ids to be deleted. The entry_ids cannot be affected before deletion and this hook is just for changing data of third party items that relate to form entries.

Hook Code:

if (ee()->extensions->active_hook('freeform_module_entry_delete') === TRUE)
{
	ee()->extensions->call(
		'freeform_module_entry_delete',
		$form_id,
		$entry_ids,
		$this
	);

	if (ee()->extensions->end_script === TRUE) return;
}

Extension function example:

public function freeform_module_entry_delete($form_id, $entry_ids, $obj)
{
	$this->my_third_party_table_delete($form_id, $entry_ids);
}

Notification Hooks

  • freeform_recipient_email
  • freeform_module_admin_notification
  • freeform_module_user_notification

These hooks are all run in the same line in the notification library, but are run for different types of notifications. This may seem redundant, but it's for two reasons. Backward compatibility with Freeform 3 hooks and separation of notification types for those who need it. Had the need for Freeform 3 compatibility not arisen, a $type argument could instead have been used.

The odd ordering of hook arguments is also due to backward compatibly and is detailed more later.

  • Files:

    • Notifications.php
  • Method:

    • Notifications.php -> send_notification()
      • called by mod.freeform.php->save_form()
  • Possible Uses:

    • Allows developers to alter email addresses, subject, message and other varibles before notifications are sent to users.
  • Variables:

    • $this->fields (array) - List of fields by shortname -> Label
    • $entry_id (int) - entry_id of posted entry to notify
    • $this->variables (array) - array of variables that can be adjusted for output
    • $form_id (int) - form_id of posted entry to notify
    • $this: (object) - class object of function caller
  • Important Class Vars

    • $this->variables['message'] - (string) parsed message
    • $this->variables['subject'] - (string) parsed subject
    • $this->variables['entry_date' ] - (string) Entry Date of posted entry
    • $this->variables['attachments'] - (array) Uploaded files to be attached
    • $this->variables['attachment_count'] - (int) Count of Uploaded files to be attached
    • $this->variables['recipients'] - (array) emails of recipients
    • $this->variables['cc_recipients'] - (array) emails of cc recipients
    • $this->variables['bcc_recipients'] - (array) emails of bcc recipients
    • $this->variables['reply_to_email'] - (string) reply to email address
    • $this->variables['reply_to_name'] - (string) reply to name
    • $this->variables['from_name'] - (string) email sender name
    • $this->variables['from_email'] - (string) email sender email
    • $this->wordwrap - (boolean) word wrap?
    • $this->mailtype - (string) 'html' or 'text'
    • $this->field_inputs - (array) key => value array of field inputs (Alteration to this array does not affect output as template parsing is finished by the time these hooks are run.)
  • Return:

    • type: associative array
    • required: yes

This hook is fired after all parsing of tags is finished, so make any adjustments accordingingly.

Important differences from Freeform 3:

Freeform 3 only had hooks for freeform_module_admin_notification, and freeform_module_user_notification and the array was named $msg (now $this->variables). Freeform Classic uses a table per form, and thus includes the the $form_id with hooks.

Freeform Classic also introduces the new freeform_recipient_email hook which covers dynamic and user input recipients.

Hook Code:

The hook code is a little different for notifications as it runs for 3 different kinds.

$hook_name = 'freeform_recipient_email';

if ($notification_type == 'admin')
{
	$hook_name = 'freeform_module_admin_notification';
}
else if ($notification_type == 'user')
{
	$hook_name = 'freeform_module_user_notification';
}

if (ee()->extensions->active_hook($hook_name) === TRUE)
{
	$this->variables = ee()->extensions->call(
		$hook_name,
		$this->fields,
		$entry_id,
		$this->variables,
		$form_id,
		$this
	);

	if (ee()->extensions->end_script === TRUE) return;
}

Extension function example:

public function freeform_module_user_notification($fields, $e_id, $vars, $f_id, $obj)
{

	//have other extensions already manipulated?
	if (ee()->extensions->last_call !== FALSE)
	{
		$vars = ee()->extensions->last_call;
	}

	$nl = ($obj->mailtype == 'html') ? '<br/>' : "\n";

	$vars['message'] .= $nl . "This form has also been sent to our CRM software! Thanks!";

	//required
	return $vars;
}