skip to Main Content

Adding an Action to Uncanny Automator

Custom Triggers & Actions
1. Creating an Uncanny Automator integration
2. Adding a Trigger to Uncanny Automator
3. Adding an Action to Uncanny Automator
4. Adding a Condition to Uncanny Automator Pro
5. Adding a Settings Page for an App Integration
6. Uncanny Automator terminology

Now let’s add an Action to our integration. This action will simply send emails to a desired address. We will have to add several options, so it can accept the email address, text and other parameters:

The action file and class


You can view the complete code of this file here, or you can download the whole sample plugin from our GitHub repository.


Let’s be organized and create the actions folder inside our integration. Then create the send-email-sample.php file in it. In that file, define the new Send_Email_Sample class that extends the \Uncanny_Automator\Recipe\Action class:

<?php

class Send_Email_Sample extends \Uncanny_Automator\Recipe\Action {

	protected function setup_action() {

		// Define the Actions's info
		$this->set_integration( 'SAMPLE_INTEGRATION' );
		$this->set_action_code( 'SEND_EMAIL_SAMPLE' );
		$this->set_action_meta( 'EMAIL_TO' );

		// Define the Action's sentence
		$this->set_sentence( sprintf( esc_attr__( 'Send an email to {{email address:%1$s}} from Sample Integration', 'automator-sample' ), $this->get_action_meta() ) );
		$this->set_readable_sentence( esc_attr__( 'Send an {{email}} from Sample Integration', 'automator-sample' ) );
		
	}
}

In the setup_action method, define the integration’s code using the set_integration function. The code can be any existing integration code. In our case it would be SAMPLE_INTEGRATION as we defined earlier in the sample-integration.php file.

The set_action_code method defines the unique name of this particular action. You have to come up with one that is unique to this integration. It’s okay to have same action codes in different integrations though.

The set_action_meta value would be the unique ID for the email address filed. Later it will be used to retrieve the entered email address and also to form the action’s sentence.

The next two methods define the Action’s sentence. If we want our EMAIL_TO field value to show up in the middle of the sentence, we need to add it in the value we pass to the set_sentence method. This time we will use the $this->get_action_meta() which will output the meta. Again we do a bit of string processing to keep the sentence translation strings clean from dynamic variables.

The string from the set_readable_sentence will be used when the action is listed in dropdowns.

Instantiating the Action class

Now let’s get back to uncanny-automator-sample.php file, and add the following line at the end of the sample_integration_load_files function:

require_once 'actions/send-email-sample.php';
new Send_Email_Sample();

Note the we can pass any number of dependencies to the class and they will be available in $this->dependencies array.

Adding Action options to the recipe UI

Your action may have no options at all and do just one thing, but if you need to provide users with a way to multiply the use-cases of your action, you can add custom fields to the UI.

For this action, let’s add several email and text fields:

Override the options method that returns an array of arrays. Each array represents a field.

public function options() {

		return array(
			Automator()->helpers->recipe->field->text(
				array(
					'option_code' => 'EMAIL_FROM',
					'label'       => 'From',
					'description' => 'Sample description',
					'placeholder' => 'Enter from email',
					'input_type'  => 'email',
					'default'     => 'john@doe.com',
				)
			),
			Automator()->helpers->recipe->field->text(
				array(
					'option_code' => 'EMAIL_TO',
					'label'       => 'To',
					'input_type'  => 'email',
				)
			),
			Automator()->helpers->recipe->field->text(
				array(
					'option_code' => 'EMAIL_SUBJECT',
					'label'       => 'Subject',
					'input_type'  => 'text',
				)
			),
			Automator()->helpers->recipe->field->text(
				array(
					'option_code' => 'EMAIL_BODY',
					'label'       => 'Body',
					'input_type'  => 'textarea',
				)
			),
		);
	}

Here we defined the EMAIL_FROM, EMAIL_TO, EMAIL_SUBJECT, and EMAIL_BODY fields with the according lables. Those could be just arrays, but we used an Automator field helper that will take care of any missing defaults: Automator()->helpers->recipe->field->text()

Note that at least one field should use the action_meta as the option_code, otherwise the action sentence won’t work.

You can find the full list of supported fields and helper methods to quickly use them in the src\core\lib\helpers\class-automator-recipe-helpers-field.php file.

Defining a custom token

Sometimes actions need to pass custom data to subsequent actions. This can be done with Tokens. For example, we might want to pass the status of our email in such a token:

Let’s add it by extending the define_tokens() method:

	public function define_tokens() {
		return array(
			'STATUS' => array(
				'name' => __( 'Send status', 'automator-sample' ),
				'type' => 'text',
			),
		);
	}

This method should return an array of arrays. Just follow the format above. Multiple tokens may be added. We will also have to hydrate the tokens with the $this->hydrate_tokens() method later.

Processing the action

The last important method in the Action class is the actual action process. Here you have access to the $user_id. Please use this value instead of the get_current_user() function because sometimes triggers are fired by an admin editing a specific user. We want actions to be performed on that user, and not the currently logged-in user.

protected function process_action( $user_id, $action_data, $recipe_id, $args, $parsed ) {

		$action_meta = $action_data['meta'];

		// Get the field values
		$to = sanitize_email( Automator()->parse->text( $action_meta['EMAIL_TO'], $recipe_id, $user_id, $args ) );
		$from = sanitize_email( Automator()->parse->text( $action_meta['EMAIL_FROM'], $recipe_id, $user_id, $args ) );
		$subject = sanitize_text_field( Automator()->parse->text( $action_meta['EMAIL_SUBJECT'], $recipe_id, $user_id, $args ) );
		$body = wp_filter_post_kses( stripslashes( ( Automator()->parse->text( $action_meta['EMAIL_BODY'], $recipe_id, $user_id, $args ) ) ) );
		
		//Set email headers
		$headers = array( 
			'Content-Type: text/html; charset=utf-8',
			'From: ' . get_bloginfo('name') . ' <' . $from . '>',
			'Reply-To: ' . get_bloginfo('name') . ' <' . $from . '>',
		 );

		// Send the email. Returns true or false
		$status = wp_mail( $to, $subject, $body, $headers ); 

		// Convert true or false into string error
		$status_string = $status ? __( 'Email was sent', 'automator-sample' ) : __( 'Email was not sent', 'automator-sample' );

		// Populate the custom token value
		$this->hydrate_tokens( 
			array( 
				'STATUS' => $status_string 
				) 
		);

		// Handle errors
		if ( ! $status ) {

			$this->add_log_error( $status_string );

			return false; // Return false if error ocurred during the action completion
		}

		// Always return true if everything was okay
		return true;
	}

The first several lines just fetch all the field values from the $action_data array. All values that can have tokens should be passed through the parser that will replcae the token placeholders with actual values from that particular recipe run: Automator()->parse->text( $string )

Next we just build the email headers and send it via the native wp_mail() function.

The wp_mail() function returns true or false as the result. We will generate a $status_string from the result and pass it to the $this->hydrate_tokens( $tokens ) method. This will store the status and output it wherever the STATUS token is used.

Important: The process_action method should return one of three things:

  • true – if the action succeded
  • false – if the action failed for some reason
  • null or no return value – if the action didn’t fail but haven’t performed anything. For example, if it was supposed to add a user to a course, but the user was already added to it earlier.

When an action is failed, it should report some kind of error in the logs. To define an error, we can use the $this->add_log_error( $string ); method. You can add multiple errors this way if needed.

The process_action() method also supports Exceptions. You can throw an exception anywhere in it and the action will be marked as failed with the Exception message as the error string.

Congrats, this concludes the action creation tutorial.

Checkout other articles from this series:

Back To Top