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,857 @@
<?php
namespace Gravity_Forms\Gravity_SMTP\Connectors;
use Gravity_Forms\Gravity_SMTP\Data_Store\Data_Store_Router;
use Gravity_Forms\Gravity_SMTP\Logging\Debug\Debug_Logger;
use Gravity_Forms\Gravity_SMTP\Logging\Log\Logger;
use Gravity_Forms\Gravity_SMTP\Data_Store\Data_Store;
use Gravity_Forms\Gravity_SMTP\Models\Event_Model;
use Gravity_Forms\Gravity_SMTP\Utils\Header_Parser;
use Gravity_Forms\Gravity_SMTP\Utils\Recipient_Parser;
/**
* Connector_Base
*
* The base class for any connector registered to the system. Handles defining sending logic,
* settings fields for the connector, and data handling.
*
* @since 1.0
*/
abstract class Connector_Base {
const SETTING_ENABLED = 'enabled';
const SETTING_ACTIVATED = 'activated';
const SETTING_CONFIGURED = 'configured';
const SETTING_FROM_EMAIL = 'from_email';
const SETTING_FROM_NAME = 'from_name';
const SETTING_FORCE_FROM_EMAIL = 'force_from_email';
const SETTING_FORCE_FROM_NAME = 'force_from_name';
const SETTING_REPLY_TO_EMAIL = 'reply_to_email';
const SETTING_FORCE_REPLY_TO_EMAIL = 'force_reply_to_email';
const SETTING_IS_PRIMARY = 'is_primary';
const SETTING_IS_BACKUP = 'is_backup';
const OBFUSCATED_STRING = '****************';
protected static $configured = null;
/**
* @var string
*/
protected $name;
/**
* @var string
*/
protected $logo;
/**
* @var string
*/
protected $full_logo;
/**
* @var string
*/
protected $title;
/**
* @var string
*/
protected $description;
/**
* @var bool
*/
protected $disabled;
/**
* @var \PHPMailer
*/
protected $php_mailer;
/**
* @var Data_Store_Router $data_store
*/
protected $data_store;
/**
* @var Logger $logger
*/
protected $logger;
/**
* @var Event_Model $emails
*/
protected $emails;
/**
* @var array
*/
protected $events;
/**
* @var array
*/
protected $atts;
/**
* @var Header_Parser
*/
protected $header_parser;
/**
* @var Recipient_Parser
*/
protected $recipient_parser;
/**
* @var Debug_Logger
*/
protected $debug_logger;
/**
* @var int
*/
protected $email;
/**
* If populated, these fields will be obfuscated when they are displayed. Useful for API keys, etc.
*
* @var array
*/
protected $sensitive_fields = array();
/**
* Calls to wp_mail() will be routed to this method if this connector is enabled. Parameters
* are a match for wp_mail().
*
* @since 1.0
*
* @return mixed
*/
abstract public function send();
/**
* Define the settings fields for this connector, matching the field types and props to those in
* the React components.
*
* @since 1.0
*
* @return array
*/
abstract public function settings_fields();
/**
* Define the data that should be saved and loaded for this connector when dealing with its
* functionality. Typically this will be the values being modified via the Settings Fields, but
* can include other data as well.
*
* @since 1.0
*
* @return array
*/
abstract public function connector_data();
/**
* Get the description for this connector. Override in each connector to translate.
*
* @return string
*/
public function get_description() {
return $this->description;
}
/**
* Define any i18n strings needed for this connector. Defaults to noop.
*
* @since 1.0
*
* @return array
*/
public function connector_i18n() {
return array();
}
/**
* A map to handle migrating existing settings to this connector. Should
* return an array of arrays containing the following values:
*
* - original_key: The key for the setting in the existing add-on.
*
* - new_key: The new key to map the value to in our system.
*
* - sub_key: The optional sub-key to search for in a multidimensional array of
* options. Arrays can be navigated using '/', e.g. 'key/subkey/subsubkey'.
*
* - transform: An optional callback to apply to the value before saving.
*
* @since 1.0
*
* @return array
*/
public function migration_map() {
return array();
}
/**
* Constructor.
*
* @since 1.0
*
* @param $php_mailer
* @param $data_store
* @param $logger
* @param $events
* @param $header_parser
* @param $recipient_parser
* @param $debug_logger
*
* @return void
*/
public function __construct( $php_mailer, $data_store, $logger, $events, $header_parser, $recipient_parser, $debug_logger ) {
$this->php_mailer = $php_mailer;
$this->data_store = $data_store;
$this->logger = $logger;
$this->events = $events;
$this->header_parser = $header_parser;
$this->recipient_parser = $recipient_parser;
$this->debug_logger = $debug_logger;
}
public function handle_suppressed_email( $email, $source ) {
$atts = $this->get_atts();
$this->set_email_log_data( $atts['subject'], $atts['message'], $email, $atts['from'], $atts['headers'], $atts['attachments'], $source, array() );
$this->events->update( array( 'status' => 'suppressed' ), $this->email );
$this->logger->log( $this->email, 'failed', 'Recipient email address ' . $email . ' is suppressed.' );
$this->debug_logger->log_error( $this->wrap_debug_with_details( __FUNCTION__, $this->email, 'Recipient email address ' . $email . ' is suppressed.' ) );
}
/**
* Initialize the connector and map attributes as necessary.
*
* @since 1.0
*
* @param $to
* @param $subject
* @param $message
* @param $headers
* @param $attachments
*
* @return void
*/
public function init( $to, $subject, $message, $headers = '', $attachments = array(), $source = '' ) {
$service_name = $this->name === 'phpmail' ? 'wp_mail' : $this->name;
$this->email = $this->events->create(
$service_name,
'pending',
'',
'',
'',
'',
array(),
);
// Set to blank values to avoid warnings.
$from = '';
$from_name = '';
/**
* Filters the wp_mail() arguments.
*
* @since 2.2.0
*
* @param array $args A compacted array of wp_mail() arguments, including the "to" email,
* subject, message, headers, and attachments values.
*/
$atts = apply_filters( 'wp_mail', compact( 'to', 'subject', 'message', 'headers', 'attachments', 'from', 'from_name', 'source' ) );
$atts['to'] = $this->recipient_parser->parse( $atts['to'] );
$parsed_headers = $this->get_parsed_headers( $atts['headers'] );
if ( isset( $parsed_headers['from'] ) ) {
$from_data = $this->get_email_from_header( 'From', $parsed_headers['from'] );
$atts['from'] = $from_data->recipients()[0]->email();
$atts['from_name'] = $from_data->recipients()[0]->name();
} else {
$atts['from'] = '';
$atts['from_name'] = '';
}
$this->atts = $atts;
do_action( 'gravitysmtp_after_connector_init', $this->email, $this );
}
/**
* Get all of the attributes for this connector.
*
* @since 1.5.0
*
* @return array
*/
public function get_atts() {
return $this->atts;
}
protected function set_email_log_data( $subject, $message, $to, $from, $headers, $attachments, $source, $params = array() ) {
$params = $this->strip_sensitive_log_data( $params );
$this->events->update(
array(
'subject' => $subject,
'message' => $message,
'extra' => serialize(
array(
'to' => $to,
'from' => $from,
'headers' => array(),
'attachments' => $attachments,
'source' => $source,
'params' => $params,
)
)
),
$this->email
);
}
private function strip_sensitive_log_data( $params ) {
unset( $params['body'] );
unset( $params['headers'] );
return $params;
}
/**
* Get an attribute, passing it through necessary filters.
*
* @since 1.0
*
* @param $att_name
* @param $default
*
* @return mixed|null
*/
public function get_att( $att_name, $default = '' ) {
$value = isset( $this->atts[ $att_name ] ) ? $this->atts[ $att_name ] : $default;
$wp_mail_filters = array( 'from', 'from_name', 'content_type', 'charset' );
if ( in_array( $att_name, $wp_mail_filters ) ) {
$value = apply_filters( 'wp_mail_' . $att_name, $value );
}
return apply_filters( 'gravitysmtp_email_attribute_' . $att_name, $value );
}
/**
* Set an attribute.
*
* @since 1.5.0
*
* @param $att_name
* @param $value
*
* @return void
*/
public function set_att( $att_name, $value ) {
$this->atts[ $att_name ] = $value;
}
/**
* Get the From email.
*
* @since 1.0
*
* @param bool $return_array Wether to return an array containing the individual parts of the from address ( 'email', 'name' and 'from') or just the From string.
*
* @return string | array
*/
protected function get_from( $return_array = false ) {
$force_from_email = $this->get_setting( self::SETTING_FORCE_FROM_EMAIL, false );
$force_from_name = $this->get_setting( self::SETTING_FORCE_FROM_NAME, false );
if ( empty( $force_from_email ) && empty( $force_from_name ) ) {
$from = $this->get_att( 'from', '' );
if ( empty( $from ) ) {
$from = $this->get_setting( self::SETTING_FROM_EMAIL, '' );
}
$from_name = $this->get_att( 'from_name', '' );
if ( empty( $from_name ) ) {
$from_name = $this->get_setting( self::SETTING_FROM_NAME, '' );
}
} else {
$from = ! empty( $force_from_email )
? $this->get_setting( self::SETTING_FROM_EMAIL, '' )
: $this->get_att( 'from', '' );
$from_name = ! empty( $force_from_name )
? $this->get_setting( self::SETTING_FROM_NAME, '' )
: $this->get_att( 'from_name', '' );
}
// From was not passed; use admin email
if ( empty( $from ) ) {
$from = get_option( 'admin_email' );
}
// RFC 5322: quote display names that contain specials so parsers
// don't misinterpret them (e.g. parentheses treated as comments).
if ( ! empty( $from_name ) && preg_match( '/[()<>\[\]:;@\\\\",.]/', $from_name ) ) {
$from_name_quoted = '"' . str_replace( array( '\\', '"' ), array( '\\\\', '\\"' ), $from_name ) . '"';
} else {
$from_name_quoted = $from_name;
}
$from_str = ! empty( $from_name ) ? $from_name_quoted . ' <' . $from . '>' : $from;
if ( $return_array ) {
$return = array(
'email' => $from,
'from' => $from_str,
);
if ( ! empty( $from_name ) ) {
$return['name'] = $from_name;
}
return $return;
}
return $from_str;
}
/**
* Get the Reply-To email.
*
* @since 1.0
*
* @param bool $return_as_array Wether to return an array containing the individual parts of the reply-to address or just the Reply-To string.
*
* @return string | array
*/
public function get_reply_to( $return_as_array = false ) {
$force_reply_to_setting = $this->get_setting( self::SETTING_FORCE_REPLY_TO_EMAIL, false );
$default_reply_to_setting = $this->get_setting( self::SETTING_REPLY_TO_EMAIL, '' );
$parsed_headers = $this->get_parsed_headers( $this->atts['headers'] );
$forcing_reply_to = $force_reply_to_setting && ! empty( $default_reply_to_setting );
$defaulting_reply_to = ( ! isset( $parsed_headers['reply-to'] ) || empty( $parsed_headers['reply-to'] ) ) && ! empty( $default_reply_to_setting );
// If we're forcing the reply-to email or the reply-to header is empty, use the default reply-to email.
if ( $forcing_reply_to || $defaulting_reply_to ) {
return $return_as_array ? array( array( 'email' => $default_reply_to_setting ) ) : $default_reply_to_setting;
}
if ( ! isset( $parsed_headers['reply-to'] ) || empty( $parsed_headers['reply-to'] ) ) {
return $return_as_array ? array() : '';
}
$email_data = $this->get_email_from_header( 'Reply-To', $parsed_headers['reply-to'] );
return $return_as_array ? $email_data->as_array() : $email_data->as_string();
}
/**
* Get the sensitive fields array for this connector
*
* @since 1.9.0
*
* @return array
*/
public function get_sensitive_fields() {
return $this->sensitive_fields;
}
public function get_request_params() {
return array();
}
/**
* Get the default From settings fields.
*
* @since 1.0
*
* @return array[] Returns an array of settings fields.
*/
protected function get_from_settings_fields() {
return array(
array(
'component' => 'Input',
'props' => array(
'labelAttributes' => array(
'label' => esc_html__( 'Default From Email', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'medium',
),
'name' => self::SETTING_FROM_EMAIL,
'spacing' => 6,
'size' => 'size-l',
'value' => $this->get_setting( self::SETTING_FROM_EMAIL, '' ),
),
),
array(
'component' => 'Toggle',
'props' => array(
'initialChecked' => (bool) $this->get_setting( self::SETTING_FORCE_FROM_EMAIL, false ),
'labelAttributes' => array(
'label' => esc_html__( 'Force From Email', 'gravitysmtp' ),
),
'helpTextAttributes' => array(
'content' => esc_html__( 'If Force Email is enabled, the Default From Email address will override other plugin settings for all outgoing emails.', 'gravitysmtp' ),
'size' => 'text-xs',
'spacing' => array( 2, 0, 0, 0 ),
'weight' => 'regular',
),
'helpTextWidth' => 'full',
'labelPosition' => 'left',
'name' => self::SETTING_FORCE_FROM_EMAIL,
'size' => 'size-m',
'spacing' => 6,
'width' => 'full',
),
),
array(
'component' => 'Input',
'props' => array(
'labelAttributes' => array(
'label' => esc_html__( 'Default From Name', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'medium',
),
'name' => self::SETTING_FROM_NAME,
'size' => 'size-l',
'spacing' => 6,
'value' => $this->get_setting( self::SETTING_FROM_NAME, '' ),
),
),
array(
'component' => 'Toggle',
'props' => array(
'initialChecked' => (bool) $this->get_setting( self::SETTING_FORCE_FROM_NAME, false ),
'labelAttributes' => array(
'label' => esc_html__( 'Force From Name', 'gravitysmtp' ),
),
'helpTextAttributes' => array(
'content' => esc_html__( 'If Force Name is enabled, the Default From Name will override other plugin settings for all outgoing emails.', 'gravitysmtp' ),
'size' => 'text-xs',
'spacing' => array( 2, 0, 0, 0 ),
'weight' => 'regular',
),
'helpTextWidth' => 'full',
'labelPosition' => 'left',
'name' => self::SETTING_FORCE_FROM_NAME,
'size' => 'size-m',
'spacing' => 6,
'width' => 'full',
),
),
);
}
/**
* Get the default Reply-To settings fields.
*
* @since 1.0
*
* @return array[] Returns an array of settings fields.
*/
protected function get_reply_to_settings_fields() {
return array(
array(
'component' => 'Input',
'props' => array(
'labelAttributes' => array(
'label' => esc_html__( 'Default Reply-To Email', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'medium',
),
'name' => self::SETTING_REPLY_TO_EMAIL,
'spacing' => 6,
'size' => 'size-l',
'value' => $this->get_setting( self::SETTING_REPLY_TO_EMAIL, '' ),
),
),
array(
'component' => 'Toggle',
'props' => array(
'initialChecked' => (bool) $this->get_setting( self::SETTING_FORCE_REPLY_TO_EMAIL, false ),
'labelAttributes' => array(
'label' => esc_html__( 'Force Reply-To Email', 'gravitysmtp' ),
),
'helpTextAttributes' => array(
'content' => esc_html__( 'If Force Reply-To Email is enabled, the Default Reply-To Email address will override other plugin settings for all outgoing emails.', 'gravitysmtp' ),
'size' => 'text-xs',
'spacing' => array( 2, 0, 0, 0 ),
'weight' => 'regular',
),
'helpTextWidth' => 'full',
'labelPosition' => 'left',
'name' => self::SETTING_FORCE_REPLY_TO_EMAIL,
'size' => 'size-m',
'spacing' => 6,
'width' => 'full',
),
),
);
}
/**
* Retrieve any additional message headers that may have been added, whether through filters or
* custom code.
*
* @since 1.0
*
* @return array
*/
public function get_filtered_message_headers() {
$headers = $this->get_parsed_headers( $this->get_att( 'headers', array() ) );
foreach( $this->header_parser->standard_headers as $header ) {
unset( $headers[ $header ] );
}
return array_unique( $headers );
}
/**
* Get parsed/normalized headers for use in PHP Mailer.
*
* @since 1.0
*
* @param $headers
*
* @return array
*/
protected function get_parsed_headers( $headers ) {
$parsed_headers = $this->header_parser->parse( $headers );
if ( ! isset( $parsed_headers['content-type'] ) ) {
$parsed_headers['content-type'] = 'text/plain';
}
return $parsed_headers;
}
protected function get_header_from_string( $string ) {
return $this->header_parser->get_header_from_string( $string );
}
protected function get_formatted_cc( $values ) {
return $this->header_parser->get_formatted_cc( $values );
}
/**
* Get the email address info from the Header strings.
*
* @sicne 1.0
*
* @param $header_name
* @param $header_string
*
* @return array|array[]
*/
protected function get_email_from_header( $header_name, $header_string ) {
return $this->header_parser->get_email_from_header( $header_name, $header_string );
}
/**
* Helper method for retrieving plugin settings (i.e., settings for the plugin globally
* and not specific to this connector).
*
* @since 1.0
*
* @param $setting_name
* @param $default
*
* @return mixed|null
*/
protected function get_plugin_setting( $setting_name, $default = null ) {
return $this->data_store->get_plugin_setting( $setting_name, $default );
}
/**
* Helper method to retrieve a saved setting specifically for this connector.
*
* @since 1.0
*
* @param $setting_name
* @param $default
*
* @return mixed
*/
protected function get_setting( $setting_name, $default = null ) {
if ( $setting_name === self::SETTING_IS_BACKUP || $setting_name === self::SETTING_IS_PRIMARY ) {
$const_setting = $this->check_for_connector_status_flag( $setting_name );
if ( ! empty( $const_setting ) ) {
return $const_setting === $this->name;
}
}
return $this->data_store->get_setting( $this->name, $setting_name, $default );
}
protected function setting_should_be_obfuscated( $setting_name ) {
if ( ! in_array( $setting_name, $this->sensitive_fields ) ) {
return false;
}
return true;
}
protected function get_locked_settings() {
$return = array();
$defined_constants = array_filter( get_defined_constants(), function( $constant ) {
return strpos( $constant, 'GRAVITYSMTP_' ) !== false;
}, ARRAY_FILTER_USE_KEY );
foreach( $defined_constants as $constant => $constant_value ) {
$setting_name = strtolower( str_replace( 'GRAVITYSMTP_', '', $constant ) );
$return[] = $setting_name;
}
return $return;
}
private function check_for_connector_status_flag( $setting_name ) {
$const_check = $setting_name === self::SETTING_IS_PRIMARY ? 'GRAVITYSMTP_INTEGRATION_PRIMARY' : 'GRAVITYSMTP_INTEGRATION_BACKUP';
if ( defined( $const_check ) ) {
return constant( $const_check );
}
return false;
}
/**
* Get the mapped data for this connector to use in a Config.
*
* @since 1.0
*
* @return array
*/
public function get_data() {
$fields = $this->settings_fields();
$data = $this->get_merged_data();
if ( ! empty( $fields['fields'] ) ) {
foreach( $fields['fields'] as $idx => $field_data ) {
if ( ! array_key_exists( 'value', $field_data['props'] ) ) {
continue;
}
$name = $field_data['props']['name'];
if ( ! $this->setting_should_be_obfuscated( $name ) ) {
continue;
}
if ( empty( $fields['fields'][ $idx ]['props']['value'] ) ) {
continue;
}
$fields['fields'][$idx]['props']['value'] = self::OBFUSCATED_STRING;
}
}
foreach( $data as $key => $value ) {
if ( ! $this->setting_should_be_obfuscated( $key ) ) {
continue;
}
if ( empty( $value ) ) {
continue;
}
$data[ $key ] = self::OBFUSCATED_STRING;
}
return array(
'fields' => $fields,
'data' => $data,
'i18n' => $this->connector_i18n(),
'name' => $this->name,
'logo' => $this->logo,
'full_logo' => $this->full_logo,
'title' => $this->title,
'description' => $this->get_description(),
);
}
/**
* Get the unique data for this connector, merged with the default/common data for all
* connectors in the system.
*
* @since 1.0
*
* @return array
*/
protected function get_merged_data() {
// @todo - we might want to refactor this to use the Cache class in the future.
$configured_key = sprintf( 'gsmtp_connector_configured_%s', $this->name );
$cached = get_transient( $configured_key );
if ( $cached === false ) {
$is_configured = $this->is_configured();
$configured = ( ! is_wp_error( $is_configured ) && $is_configured !== false );
set_transient( $configured_key, array( 'configured' => $configured ), DAY_IN_SECONDS );
} else {
$configured = $cached['configured'];
}
$defaults = array(
self::SETTING_ACTIVATED => $this->get_setting( self::SETTING_ACTIVATED, true ),
self::SETTING_CONFIGURED => $configured,
self::SETTING_ENABLED => $this->get_setting( self::SETTING_ENABLED, false ),
self::SETTING_IS_PRIMARY => $this->get_setting( self::SETTING_IS_PRIMARY, false ),
self::SETTING_IS_BACKUP => $this->get_setting( self::SETTING_IS_BACKUP, false ),
);
return array_merge( $this->connector_data(), $defaults );
}
/**
* Whether this connector has been configured by the user. Defaults to checking for stored
* settings values, but can be overridden for other logic.
*
* @since 1.0
*
* @return bool
*/
public function is_configured() {
$configured = $this->get_setting( self::SETTING_CONFIGURED, null );
if ( ! is_null( $configured ) ) {
return $configured;
}
return false;
}
/**
* Whether test mode setting is enabled for the plugin.
*
* @since 1.0
*
* @return bool
*/
protected function is_test_mode() {
$test_mode = $this->get_plugin_setting( 'test_mode', 'false' );
if ( empty( $test_mode ) ) {
$test_mode = false;
} else {
$test_mode = $test_mode !== 'false';
}
return $test_mode;
}
protected function wrap_debug_with_details( $function, $email, $message ) {
return sprintf( '%s(): [EMAIL ID %s] - %s', $function, $email, $message );
}
}
@@ -0,0 +1,55 @@
<?php
namespace Gravity_Forms\Gravity_SMTP\Connectors;
class Connector_Factory {
protected $php_mailer;
protected $data_store;
protected $logger;
protected $emails;
protected $header_parser;
protected $recipient_parser;
protected $debug_logger;
public function __construct( $php_mailer, $data_store, $logger, $emails, $header_parser, $recipient_parser, $debug_logger ) {
$this->php_mailer = $php_mailer;
$this->data_store = $data_store;
$this->logger = $logger;
$this->emails = $emails;
$this->header_parser = $header_parser;
$this->recipient_parser = $recipient_parser;
$this->debug_logger = $debug_logger;
}
public function create( $type ) {
if ( $type === 'amazon-ses' ) {
$type = 'Amazon_SES';
}
if ( $type === 'mailersend' ) {
$type = 'MailerSend';
}
if ( $type === 'elastic_email' ) {
$type = 'Elastic_Email';
}
if( $type === 'SMTP2GO' ) {
$type = 'smtp2go';
}
if ( $type === 'smtpcom' ) {
$type = 'SMTPCom';
}
$classname = sprintf( '%s\Types\Connector_%s', __NAMESPACE__, ucfirst( $type ) );
if ( ! class_exists( $classname ) ) {
throw new \InvalidArgumentException( 'Connector type for type ' . $type . ' with class ' . $classname . ' does not exist.' );
}
return new $classname( $this->php_mailer, $this->data_store, $this->logger, $this->emails, $this->header_parser, $this->recipient_parser, $this->debug_logger );
}
}
@@ -0,0 +1,645 @@
<?php
namespace Gravity_Forms\Gravity_SMTP\Connectors;
use Gravity_Forms\Gravity_SMTP\Apps\Config\Tools_Config;
use Gravity_Forms\Gravity_SMTP\Connectors\Config\Connector_Config;
use Gravity_Forms\Gravity_SMTP\Connectors\Config\Connector_Endpoints_Config;
use Gravity_Forms\Gravity_SMTP\Connectors\Endpoints\Check_Background_Tasks_Endpoint;
use Gravity_Forms\Gravity_SMTP\Connectors\Endpoints\Cleanup_Data_Endpoint;
use Gravity_Forms\Gravity_SMTP\Connectors\Endpoints\Get_Connector_Emails;
use Gravity_Forms\Gravity_SMTP\Connectors\Endpoints\Get_Single_Email_Data_Endpoint;
use Gravity_Forms\Gravity_SMTP\Connectors\Endpoints\Save_Connector_Settings_Endpoint;
use Gravity_Forms\Gravity_SMTP\Connectors\Endpoints\Save_Plugin_Settings_Endpoint;
use Gravity_Forms\Gravity_SMTP\Connectors\Endpoints\Send_Test_Endpoint;
use Gravity_Forms\Gravity_SMTP\Connectors\Oauth\Google_Oauth_Handler;
use Gravity_Forms\Gravity_SMTP\Connectors\Oauth\Microsoft_Oauth_Handler;
use Gravity_Forms\Gravity_SMTP\Connectors\Oauth\Zoho_Oauth_Handler;
use Gravity_Forms\Gravity_SMTP\Connectors\Types\Connector_Amazon;
use Gravity_Forms\Gravity_SMTP\Connectors\Types\Connector_Brevo;
use Gravity_Forms\Gravity_SMTP\Connectors\Types\Connector_Cloudflare;
use Gravity_Forms\Gravity_SMTP\Connectors\Types\Connector_Elastic_Email;
use Gravity_Forms\Gravity_SMTP\Connectors\Types\Connector_Emailit;
use Gravity_Forms\Gravity_SMTP\Connectors\Types\Connector_Generic;
use Gravity_Forms\Gravity_SMTP\Connectors\Types\Connector_Google;
use Gravity_Forms\Gravity_SMTP\Connectors\Types\Connector_Mailchimp;
use Gravity_Forms\Gravity_SMTP\Connectors\Types\Connector_Mailersend;
use Gravity_Forms\Gravity_SMTP\Connectors\Types\Connector_Mailgun;
use Gravity_Forms\Gravity_SMTP\Connectors\Types\Connector_Mailjet;
use Gravity_Forms\Gravity_SMTP\Connectors\Types\Connector_Mailtrap;
use Gravity_Forms\Gravity_SMTP\Connectors\Types\Connector_Microsoft;
use Gravity_Forms\Gravity_SMTP\Connectors\Types\Connector_PHPMail;
use Gravity_Forms\Gravity_SMTP\Connectors\Types\Connector_Postmark;
use Gravity_Forms\Gravity_SMTP\Connectors\Types\Connector_Resend;
use Gravity_Forms\Gravity_SMTP\Connectors\Types\Connector_Sendgrid;
use Gravity_Forms\Gravity_SMTP\Connectors\Types\Connector_SMTP2GO;
use Gravity_Forms\Gravity_SMTP\Connectors\Types\Connector_Sparkpost;
use Gravity_Forms\Gravity_SMTP\Connectors\Types\Connector_SMTPCom;
use Gravity_Forms\Gravity_SMTP\Connectors\Types\Connector_Zoho;
use Gravity_Forms\Gravity_SMTP\Data_Store\Const_Data_Store;
use Gravity_Forms\Gravity_SMTP\Data_Store\Data_Store_Router;
use Gravity_Forms\Gravity_SMTP\Data_Store\Opts_Data_Store;
use Gravity_Forms\Gravity_SMTP\Data_Store\Plugin_Opts_Data_Store;
use Gravity_Forms\Gravity_SMTP\Logging\Debug\Debug_Logger;
use Gravity_Forms\Gravity_SMTP\Logging\Log\Logger;
use Gravity_Forms\Gravity_SMTP\Logging\Logging_Service_Provider;
use Gravity_Forms\Gravity_SMTP\Models\Debug_Log_Model;
use Gravity_Forms\Gravity_SMTP\Models\Event_Model;
use Gravity_Forms\Gravity_SMTP\Models\Hydrators\Hydrator_Factory;
use Gravity_Forms\Gravity_SMTP\Models\Log_Details_Model;
use Gravity_Forms\Gravity_SMTP\Models\Notifications_Model;
use Gravity_Forms\Gravity_SMTP\Users\Roles;
use Gravity_Forms\Gravity_SMTP\Utils\Booliesh;
use Gravity_Forms\Gravity_Tools\Logging\DB_Logging_Provider;
use Gravity_Forms\Gravity_Tools\Providers\Config_Collection_Service_Provider;
use Gravity_Forms\Gravity_Tools\Providers\Config_Service_Provider;
use Gravity_Forms\Gravity_Tools\Updates\Updates_Service_Provider;
use Gravity_Forms\Gravity_Tools\Utils\Utils_Service_Provider;
class Connector_Service_Provider extends Config_Service_Provider {
const CONNECTOR_FACTORY = 'connector_factory';
const PHPMAILER = 'phpmailer';
const DATA_STORE_CONST = 'data_store_const';
const DATA_STORE_OPTS = 'data_store_opts';
const DATA_STORE_PLUGIN_OPTS = 'data_store_plugin_opts';
const DATA_STORE_ROUTER = 'data_store_router';
const EVENT_MODEL = 'event_model';
const LOG_DETAILS_MODEL = 'log_details_model';
const NOTIFICATIONS_MODEL = 'notifications_model';
const HYDRATOR_FACTORY = 'hydrator_factory';
const NAME_MAP = 'name_map';
const REGISTERED_CONNECTORS = 'registered_connectors';
const CONNECTOR_DATA_MAP = 'connector_data_map';
const OAUTH_DATA_HANDLER = 'oauth_data_handler';
const GOOGLE_OAUTH_HANDLER = 'google_oauth_handler';
const MICROSOFT_OAUTH_HANDLER = 'microsoft_oauth_handler';
const ZOHO_OAUTH_HANDLER = 'zoho_oauth_handler';
const SEND_TEST_ENDPOINT = 'send_test_endpoint';
const CLEANUP_DATA_ENDPOINT = 'cleanup_data_endpoint';
const SAVE_CONNECTOR_SETTINGS_ENDPOINT = 'save_connector_settings_endpoint';
const SAVE_PLUGIN_SETTINGS_ENDPOINT = 'save_plugin_settings_endpoint';
const GET_SINGLE_EMAIL_DATA_ENDPOINT = 'get_single_email_data_endpoint';
const CHECK_BACKGROUND_TASKS_ENDPOINT = 'check_background_tasks_endpoint';
const GET_CONNECTOR_EMAILS_ENDPOINT = 'get_connector_emails_endpoint';
const CONNECTOR_ENDPOINTS_CONFIG = 'connector_endpoints_config';
const CONNECTOR_AMAZON_SES = 'Amazon';
const CONNECTOR_BREVO = 'Brevo';
const CONNECTOR_CLOUDFLARE = 'Cloudflare';
const CONNECTOR_ELASTIC_EMAIL = 'Elastic_Email';
const CONNECTOR_EMAILIT = 'Emailit';
const CONNECTOR_GENERIC = 'Generic';
const CONNECTOR_GOOGLE = 'Google';
const CONNECTOR_MAILCHIMP = 'Mailchimp';
const CONNECTOR_MAILERSEND = 'MailerSend';
const CONNECTOR_MAILGUN = 'Mailgun';
const CONNECTOR_MAILJET = 'Mailjet';
const CONNECTOR_MAILTRAP = 'Mailtrap';
const CONNECTOR_MICROSOFT = 'Microsoft';
const CONNECTOR_PHPMAIL = 'Phpmail';
const CONNECTOR_POSTMARK = 'Postmark';
const CONNECTOR_RESEND = 'Resend';
const CONNECTOR_SENDGRID = 'Sendgrid';
const CONNECTOR_SMTPCOM = 'SMTPCom';
const CONNECTOR_SMTP2GO = 'SMTP2GO';
const CONNECTOR_SPARKPOST = 'Sparkpost';
const CONNECTOR_ZOHO = 'Zoho';
protected $connectors = array(
self::CONNECTOR_AMAZON_SES => Connector_Amazon::class,
self::CONNECTOR_BREVO => Connector_Brevo::class,
self::CONNECTOR_CLOUDFLARE => Connector_Cloudflare::class,
self::CONNECTOR_ELASTIC_EMAIL => Connector_Elastic_Email::class,
self::CONNECTOR_EMAILIT => Connector_Emailit::class,
self::CONNECTOR_GENERIC => Connector_Generic::class,
self::CONNECTOR_GOOGLE => Connector_Google::class,
self::CONNECTOR_MAILCHIMP => Connector_Mailchimp::class,
self::CONNECTOR_MAILERSEND => Connector_Mailersend::class,
self::CONNECTOR_MAILGUN => Connector_Mailgun::class,
self::CONNECTOR_MAILJET => Connector_Mailjet::class,
self::CONNECTOR_MAILTRAP => Connector_Mailtrap::class,
self::CONNECTOR_MICROSOFT => Connector_Microsoft::class,
self::CONNECTOR_PHPMAIL => Connector_PHPMail::class,
self::CONNECTOR_POSTMARK => Connector_Postmark::class,
self::CONNECTOR_RESEND => Connector_Resend::class,
self::CONNECTOR_SENDGRID => Connector_Sendgrid::class,
self::CONNECTOR_SMTPCOM => Connector_SMTPCom::class,
self::CONNECTOR_SMTP2GO => Connector_SMTP2GO::class,
self::CONNECTOR_SPARKPOST => Connector_Sparkpost::class,
self::CONNECTOR_ZOHO => Connector_Zoho::class,
);
protected $configs = array(
self::CONNECTOR_ENDPOINTS_CONFIG => Connector_Endpoints_Config::class,
);
public function register( \Gravity_Forms\Gravity_Tools\Service_Container $container ) {
parent::register( $container );
$self = $this;
$this->container->add( self::PHPMAILER, function () {
global $phpmailer;
// (Re)create it, if it's gone missing.
if ( ! ( $phpmailer ) ) {
if ( file_exists( ABSPATH . WPINC . '/PHPMailer/PHPMailer.php' ) ) {
require_once ABSPATH . WPINC . '/PHPMailer/PHPMailer.php';
require_once ABSPATH . WPINC . '/PHPMailer/SMTP.php';
require_once ABSPATH . WPINC . '/PHPMailer/Exception.php';
$phpmailer = new \PHPMailer\PHPMailer\PHPMailer( true );
} elseif ( file_exists( ABSPATH . WPINC . '/class-phpmailer.php' ) ) {
require_once ABSPATH . WPINC . '/class-phpmailer.php';
require_once ABSPATH . WPINC . '/class-smtp.php';
$phpmailer = new PHPMailer( true );
}
$phpmailer::$validator = static function ( $email ) {
return (bool) is_email( $email );
};
}
return $phpmailer;
} );
$this->container->add( self::HYDRATOR_FACTORY, function () {
return new Hydrator_Factory();
} );
$this->container->add( self::DATA_STORE_CONST, function () {
return new Const_Data_Store();
} );
$this->container->add( self::DATA_STORE_OPTS, function () {
return new Opts_Data_Store();
} );
$this->container->add( self::DATA_STORE_PLUGIN_OPTS, function () {
return new Plugin_Opts_Data_Store();
} );
$this->container->add( self::EVENT_MODEL, function () use ( $container ) {
return new Event_Model( $container->get( self::HYDRATOR_FACTORY ), $container->get( self::DATA_STORE_ROUTER ), $container->get( Utils_Service_Provider::RECIPIENT_PARSER ), $container->get( Utils_Service_Provider::FILTER_PARSER ) );
} );
$this->container->add( self::LOG_DETAILS_MODEL, function () use ( $container ) {
return new Log_Details_Model( $container->get( self::DATA_STORE_PLUGIN_OPTS ) );
} );
$this->container->add( self::NOTIFICATIONS_MODEL, function () use ( $container ) {
return new Notifications_Model();
} );
$this->container->add( Logging_Service_Provider::LOGGER, function () use ( $container ) {
return new Logger( $container->get( self::LOG_DETAILS_MODEL ) );
} );
$this->container->add( Logging_Service_Provider::DEBUG_LOG_MODEL, function () use ( $container ) {
return new Debug_Log_Model();
} );
$this->container->add( Logging_Service_Provider::DB_LOGGING_PROVIDER, function () use ( $container ) {
return new DB_Logging_Provider( $container->get( Logging_Service_Provider::DEBUG_LOG_MODEL ) );
} );
$this->container->add( Logging_Service_Provider::DEBUG_LOGGER, function () use ( $container ) {
$data = $container->get( self::DATA_STORE_ROUTER );
$log_level = $data->get_plugin_setting( Tools_Config::SETTING_DEBUG_LOG_LEVEL, DB_Logging_Provider::DEBUG );
return new Debug_Logger( $container->get( Logging_Service_Provider::DB_LOGGING_PROVIDER ), $log_level );
} );
$this->container->add( self::DATA_STORE_ROUTER, function () use ( $container ) {
return new Data_Store_Router( $container->get( self::DATA_STORE_CONST ), $container->get( self::DATA_STORE_OPTS ), $container->get( self::DATA_STORE_PLUGIN_OPTS ) );
} );
$this->container->add( self::CONNECTOR_FACTORY, function () use ( $container ) {
return new Connector_Factory(
$container->get( self::PHPMAILER ),
$container->get( self::DATA_STORE_ROUTER ),
$container->get( Logging_Service_Provider::LOGGER ),
$container->get( self::EVENT_MODEL ),
$container->get( Utils_Service_Provider::HEADER_PARSER ),
$container->get( Utils_Service_Provider::RECIPIENT_PARSER ),
$container->get( Logging_Service_Provider::DEBUG_LOGGER )
);
} );
$this->container->add( self::SAVE_CONNECTOR_SETTINGS_ENDPOINT, function () use ( $container ) {
return new Save_Connector_Settings_Endpoint( $container->get( self::DATA_STORE_OPTS ), $container->get( self::DATA_STORE_PLUGIN_OPTS ), $container->get( self::CONNECTOR_FACTORY ) );
} );
$this->container->add( self::CLEANUP_DATA_ENDPOINT, function () use ( $container ) {
return new Cleanup_Data_Endpoint( $container->get( self::DATA_STORE_PLUGIN_OPTS ) );
} );
$this->container->add( self::SAVE_PLUGIN_SETTINGS_ENDPOINT, function () use ( $container ) {
return new Save_Plugin_Settings_Endpoint( $container->get( self::DATA_STORE_PLUGIN_OPTS ), $container->get( Updates_Service_Provider::LICENSE_API_CONNECTOR ) );
} );
$this->container->add( self::GET_SINGLE_EMAIL_DATA_ENDPOINT, function () use ( $container ) {
return new Get_Single_Email_Data_Endpoint( $container->get( self::LOG_DETAILS_MODEL ), $container->get( self::EVENT_MODEL ) );
} );
$this->container->add( self::CHECK_BACKGROUND_TASKS_ENDPOINT, function () {
return new Check_Background_Tasks_Endpoint();
} );
$this->container->add( self::SEND_TEST_ENDPOINT, function () use ( $container ) {
return new Send_Test_Endpoint( $container->get( self::CONNECTOR_FACTORY ), $container->get( self::DATA_STORE_ROUTER ), $container->get( self::EVENT_MODEL ), $container->get( self::LOG_DETAILS_MODEL ), $container->get( self::GET_SINGLE_EMAIL_DATA_ENDPOINT ) );
} );
$this->container->add( self::GET_CONNECTOR_EMAILS_ENDPOINT, function () use ( $container ) {
return new Get_Connector_Emails( $container->get( self::NOTIFICATIONS_MODEL ) );
} );
$this->container->add( self::OAUTH_DATA_HANDLER, function () use ( $container ) {
return new Oauth_Data_Handler( $container->get( self::DATA_STORE_ROUTER ), $container->get( self::DATA_STORE_OPTS ) );
} );
$this->container->add( self::GOOGLE_OAUTH_HANDLER, function () use ( $container ) {
return new Google_Oauth_Handler( $container->get( self::OAUTH_DATA_HANDLER ) );
} );
$this->container->add( self::MICROSOFT_OAUTH_HANDLER, function () use ( $container ) {
return new Microsoft_Oauth_Handler( $container->get( self::OAUTH_DATA_HANDLER ) );
} );
$this->container->add( self::ZOHO_OAUTH_HANDLER, function () use ( $container ) {
return new Zoho_Oauth_Handler( $container->get( self::OAUTH_DATA_HANDLER ) );
} );
$this->container->add( self::REGISTERED_CONNECTORS, function () use ( $self ) {
return $self->connectors;
} );
$this->register_connector_data();
}
public function init( \Gravity_Forms\Gravity_Tools\Service_Container $container ) {
add_action( 'init', function () use ( $container ) {
$page = filter_input( INPUT_GET, 'page' );
if ( $page !== 'gravitysmtp-settings' ) {
return;
}
$connectors = $container->get( self::REGISTERED_CONNECTORS );
foreach ( $connectors as $service => $class ) {
$configured_key = sprintf( 'gsmtp_connector_configured_%s', strtolower( $service ) );
delete_transient( $configured_key );
}
}, 11 );
// @todo - replace this with some AJAX action via JS
add_action( 'admin_post_smtp_disconnect_google', function () use ( $container ) {
check_admin_referer( 'gsmtp_disconnect_google' );
// Bail if current user does not have the permissions to be here.
if ( ! current_user_can( Roles::EDIT_INTEGRATIONS ) ) {
return;
}
$configured_key = sprintf( 'gsmtp_connector_configured_%s', 'google' );
delete_transient( $configured_key );
/**
* @var Opts_Data_Store $data
*/
$data = $container->get( self::DATA_STORE_OPTS );
/**
* @var Data_Store_Router
*/
$data_router = $container->get( self::DATA_STORE_ROUTER );
/**
* @var Plugin_Opts_Data_Store
*/
$plugin_data_store = $container->get( self::DATA_STORE_PLUGIN_OPTS );
$data->delete_all( 'google' );
$connector_values = $data_router->get_plugin_setting( Save_Connector_Settings_Endpoint::SETTING_PRIMARY_CONNECTOR, array() );
if ( ! is_array( $connector_values ) ) {
$connector_values = array();
}
$connector_values['google'] = 'false';
$plugin_data_store->save( Save_Connector_Settings_Endpoint::SETTING_PRIMARY_CONNECTOR, $connector_values );
$connector_values = $data_router->get_plugin_setting( Save_Connector_Settings_Endpoint::SETTING_BACKUP_CONNECTOR, array() );
if ( ! is_array( $connector_values ) ) {
$connector_values = array();
}
$connector_values['google'] = 'false';
$plugin_data_store->save( Save_Connector_Settings_Endpoint::SETTING_BACKUP_CONNECTOR, $connector_values );
$connector_values = $data_router->get_plugin_setting( Save_Connector_Settings_Endpoint::SETTING_ENABLED_CONNECTOR, array() );
if ( ! is_array( $connector_values ) ) {
$connector_values = array();
}
$connector_values['google'] = 'false';
$plugin_data_store->save( Save_Connector_Settings_Endpoint::SETTING_ENABLED_CONNECTOR, $connector_values );
/**
* @var Google_Oauth_Handler $oauth_handler
*/
$oauth_handler = $container->get( self::GOOGLE_OAUTH_HANDLER );
$return_url = urldecode( $oauth_handler->get_return_url( false ) );
wp_safe_redirect( $return_url );
} );
add_action( 'admin_post_smtp_disconnect_microsoft', function () use ( $container ) {
check_admin_referer( 'gsmtp_disconnect_microsoft' );
// Bail if current user does not have the permissions to be here.
if ( ! current_user_can( Roles::EDIT_INTEGRATIONS ) ) {
return;
}
$configured_key = sprintf( 'gsmtp_connector_configured_%s', 'microsoft' );
delete_transient( $configured_key );
/**
* @var Opts_Data_Store $data
*/
$data = $container->get( self::DATA_STORE_OPTS );
/**
* @var Data_Store_Router
*/
$data_router = $container->get( self::DATA_STORE_ROUTER );
/**
* @var Plugin_Opts_Data_Store
*/
$plugin_data_store = $container->get( self::DATA_STORE_PLUGIN_OPTS );
$data->delete_all( 'microsoft' );
$connector_values = $data_router->get_plugin_setting( Save_Connector_Settings_Endpoint::SETTING_PRIMARY_CONNECTOR, array() );
if ( ! is_array( $connector_values ) ) {
$connector_values = array();
}
$connector_values['microsoft'] = 'false';
$plugin_data_store->save( Save_Connector_Settings_Endpoint::SETTING_PRIMARY_CONNECTOR, $connector_values );
$connector_values = $data_router->get_plugin_setting( Save_Connector_Settings_Endpoint::SETTING_BACKUP_CONNECTOR, array() );
if ( ! is_array( $connector_values ) ) {
$connector_values = array();
}
$connector_values['microsoft'] = 'false';
$plugin_data_store->save( Save_Connector_Settings_Endpoint::SETTING_BACKUP_CONNECTOR, $connector_values );
$connector_values = $data_router->get_plugin_setting( Save_Connector_Settings_Endpoint::SETTING_ENABLED_CONNECTOR, array() );
if ( ! is_array( $connector_values ) ) {
$connector_values = array();
}
$connector_values['microsoft'] = 'false';
$plugin_data_store->save( Save_Connector_Settings_Endpoint::SETTING_ENABLED_CONNECTOR, $connector_values );
/**
* @var Microsoft_Oauth_Handler $oauth_handler
*/
$oauth_handler = $container->get( self::MICROSOFT_OAUTH_HANDLER );
$return_url = urldecode( $oauth_handler->get_return_url( 'settings' ) );
wp_safe_redirect( $return_url );
} );
add_action( 'admin_post_smtp_disconnect_zoho', function () use ( $container ) {
check_admin_referer( 'gsmtp_disconnect_zoho' );
// Bail if current user does not have the permissions to be here.
if ( ! current_user_can( Roles::EDIT_INTEGRATIONS ) ) {
return;
}
$configured_key = sprintf( 'gsmtp_connector_configured_%s', 'zoho' );
delete_transient( $configured_key );
/**
* @var Opts_Data_Store $data
*/
$data = $container->get( self::DATA_STORE_OPTS );
/**
* @var Data_Store_Router
*/
$data_router = $container->get( self::DATA_STORE_ROUTER );
/**
* @var Plugin_Opts_Data_Store
*/
$plugin_data_store = $container->get( self::DATA_STORE_PLUGIN_OPTS );
$data->delete_all( 'zoho' );
$connector_values = $data_router->get_plugin_setting( Save_Connector_Settings_Endpoint::SETTING_PRIMARY_CONNECTOR, array() );
if ( ! is_array( $connector_values ) ) {
$connector_values = array();
}
$connector_values['zoho'] = 'false';
$plugin_data_store->save( Save_Connector_Settings_Endpoint::SETTING_PRIMARY_CONNECTOR, $connector_values );
$connector_values = $data_router->get_plugin_setting( Save_Connector_Settings_Endpoint::SETTING_BACKUP_CONNECTOR, array() );
if ( ! is_array( $connector_values ) ) {
$connector_values = array();
}
$connector_values['zoho'] = 'false';
$plugin_data_store->save( Save_Connector_Settings_Endpoint::SETTING_BACKUP_CONNECTOR, $connector_values );
$connector_values = $data_router->get_plugin_setting( Save_Connector_Settings_Endpoint::SETTING_ENABLED_CONNECTOR, array() );
if ( ! is_array( $connector_values ) ) {
$connector_values = array();
}
$connector_values['zoho'] = 'false';
$plugin_data_store->save( Save_Connector_Settings_Endpoint::SETTING_ENABLED_CONNECTOR, $connector_values );
/**
* @var Zoho_Oauth_Handler $oauth_handler
*/
$oauth_handler = $container->get( self::ZOHO_OAUTH_HANDLER );
$return_url = urldecode( $oauth_handler->get_return_url( false ) );
wp_safe_redirect( $return_url );
} );
add_action( 'wp_ajax_' . Cleanup_Data_Endpoint::ACTION_NAME, function () use ( $container ) {
$container->get( self::CLEANUP_DATA_ENDPOINT )->handle();
} );
add_action( 'wp_ajax_' . Send_Test_Endpoint::ACTION_NAME, function () use ( $container ) {
$container->get( self::SEND_TEST_ENDPOINT )->handle();
} );
add_action( 'wp_ajax_' . Save_Connector_Settings_Endpoint::ACTION_NAME, function () use ( $container ) {
$container->get( self::SAVE_CONNECTOR_SETTINGS_ENDPOINT )->handle();
} );
add_action( 'wp_ajax_' . Save_Plugin_Settings_Endpoint::ACTION_NAME, function () use ( $container ) {
$container->get( self::SAVE_PLUGIN_SETTINGS_ENDPOINT )->handle();
} );
add_action( 'wp_ajax_' . Get_Single_Email_Data_Endpoint::ACTION_NAME, function () use ( $container ) {
$container->get( self::GET_SINGLE_EMAIL_DATA_ENDPOINT )->handle();
} );
add_action( 'wp_ajax_' . Check_Background_Tasks_Endpoint::ACTION_NAME, function () use ( $container ) {
$container->get( self::CHECK_BACKGROUND_TASKS_ENDPOINT )->handle();
} );
add_action( 'wp_ajax_' . Get_Connector_Emails::ACTION_NAME, function () use ( $container ) {
$container->get( self::GET_CONNECTOR_EMAILS_ENDPOINT )->handle();
} );
add_filter( 'gform_localized_script_data_gravitysmtp_admin_config', function ( $data ) {
if (
empty( $data['components']['settings']['data']['integrations'] ) &&
empty( $data['components']['setup_wizard']['data']['integrations'] ) &&
empty( $data['components']['tools']['data']['integrations'] )
) {
return $data;
}
$order = array(
'amazon-ses',
'brevo',
'cloudflare',
'elastic_email',
'emailit',
'generic',
'google-gmail',
'mailchimp',
'mailersend',
'mailgun',
'mailjet',
'mailtrap',
'microsoft',
'phpmail',
'postmark',
'sendgrid',
'smtpcom',
'smtp2go',
'sparkpost',
'zoho-mail',
);
// todo: setup wizard data should only be injected if should display is true for the app: includes/apps/setup-wizard/config/class-setup-wizard-config.php:18
foreach ( array( 'settings', 'setup_wizard', 'tools' ) as $app ) {
if ( empty( $data['components'][ $app ]['data']['integrations'] ) ) {
continue;
}
$integrations = $data['components'][ $app ]['data']['integrations'];
usort( $integrations, function ( $a, $b ) use ( $order ) {
$a_pos = array_search( $a['id'], $order );
$b_pos = array_search( $b['id'], $order );
if ( $a_pos === $b_pos ) {
return 0;
}
return $a_pos < $b_pos ? - 1 : 1;
} );
$data['components'][ $app ]['data']['integrations'] = $integrations;
}
return $data;
} );
}
private function register_connector_data() {
$is_ajax = defined( 'DOING_AJAX' ) && DOING_AJAX;
$page = filter_input( INPUT_GET, 'page' );
if ( ! $is_ajax && ! is_string( $page ) ) {
return;
}
if ( ! empty( $page ) ) {
$page = htmlspecialchars( $page );
}
if ( is_null( $page ) ) {
$page = '';
}
$plugin_data_store = $this->container->get( self::DATA_STORE_PLUGIN_OPTS );
$should_display = Booliesh::get( $plugin_data_store->get( Save_Plugin_Settings_Endpoint::PARAM_SETUP_WIZARD_SHOULD_DISPLAY, 'config', 'true' ) );
$should_register = $should_display ? strpos( $page, 'gravitysmtp-' ) !== false : in_array( $page, array(
'gravitysmtp-activity-log',
'gravitysmtp-dashboard',
'gravitysmtp-settings',
'gravitysmtp-suppression',
'gravitysmtp-tools',
) );
if ( $is_ajax ) {
$action = filter_input( INPUT_POST, 'action' );
if ( $action === 'migrate_settings' || $action === 'get_dashboard_data' ) {
$should_register = true;
}
}
if ( empty( $should_register ) ) {
return;
}
$connectors = apply_filters( 'gravitysmtp_connector_types', $this->connectors );
$config_collection = $this->container->get( Config_Collection_Service_Provider::CONFIG_COLLECTION );
$parser = $this->container->get( Config_Collection_Service_Provider::DATA_PARSER );
/**
* @var Connector_Factory $factory
*/
$factory = $this->container->get( self::CONNECTOR_FACTORY );
$name_map = array();
$data_map = array();
foreach ( $connectors as $connector_name => $connector ) {
$instance = $factory->create( $connector_name );
$config = new Connector_Config( $parser );
$connector_data = $instance->get_data();
$config->set_data( $connector_data );
$config_collection->add_config( $config );
$name_map[ $connector_data['name'] ] = $connector_data['title'];
$data_map[ $connector_data['name'] ] = $connector_data;
}
$this->container->add( self::NAME_MAP, $name_map );
$this->container->add( self::CONNECTOR_DATA_MAP, $data_map );
}
}
@@ -0,0 +1,35 @@
<?php
namespace Gravity_Forms\Gravity_SMTP\Connectors;
use Gravity_Forms\Gravity_SMTP\Data_Store\Data_Store_Router;
use Gravity_Forms\Gravity_SMTP\Data_Store\Opts_Data_Store;
use Gravity_Forms\Gravity_SMTP\Data_Store\Plugin_Opts_Data_Store;
use Gravity_Forms\Gravity_Tools\Data\Oauth_Data_Handler as Oauth_Data_Handler_Base;
class Oauth_Data_Handler implements Oauth_Data_Handler_Base {
/**
* @var Data_Store_Router $data
*/
protected $data;
/**
* @var Opts_Data_Store
*/
protected $opts_store;
public function __construct( $data_router, $opts_store ) {
$this->data = $data_router;
$this->opts_store = $opts_store;
}
public function get( $key, $connector = 'config' ) {
return $this->data->get_setting( $connector, $key );
}
public function save( $key, $value, $connector = 'config' ) {
return $this->opts_store->save( $key, $value, $connector );
}
}
@@ -0,0 +1,93 @@
<?php
namespace Gravity_Forms\Gravity_SMTP\Connectors\Config;
use Gravity_Forms\Gravity_SMTP\Apps\App_Service_Provider;
use Gravity_Forms\Gravity_SMTP\Gravity_SMTP;
use Gravity_Forms\Gravity_Tools\Config;
class Connector_Config extends Config {
protected $script_to_localize = 'gravitysmtp_scripts_admin';
protected $name = 'gravitysmtp_admin_config';
protected $overwrite = false;
protected $fields;
protected $logo;
protected $full_logo;
protected $title;
protected $description;
protected $short_name;
protected $data;
protected $i18n;
public function set_data( $data ) {
$this->fields = $data['fields'];
$this->short_name = $data['name'];
$this->logo = $data['logo'];
$this->full_logo = $data['full_logo'];
$this->title = $data['title'];
$this->description = $data['description'];
$this->data = $data['data'];
$this->i18n = $data['i18n'];
}
public function should_enqueue() {
return is_admin();
}
/**
* Config data.
*
* @return array[]
*/
public function data() {
$connector_data = array(
'title' => $this->title,
'description' => $this->description,
'id' => $this->short_name,
'logo' => $this->logo,
'full_logo' => $this->full_logo,
'settings' => $this->fields,
'data' => $this->data,
'i18n' => $this->i18n,
);
$components = array(
'settings' => array(
'data' => array(
'integrations' => array(
$connector_data,
),
),
),
'tools' => array(
'data' => array(
'integrations' => array(
$connector_data,
),
),
),
);
if ( $this->should_enqueue_setup_wizard() ) {
$components['setup_wizard'] = array(
'data' => array(
'integrations' => array(
$connector_data,
),
),
);
}
return array(
'components' => $components,
);
}
private function should_enqueue_setup_wizard() {
$should_enqueue = Gravity_SMTP::container()->get( App_Service_Provider::SHOULD_ENQUEUE_SETUP_WIZARD );
return is_callable( $should_enqueue ) ? $should_enqueue() : $should_enqueue;
}
}
@@ -0,0 +1,81 @@
<?php
namespace Gravity_Forms\Gravity_SMTP\Connectors\Config;
use Gravity_Forms\Gravity_SMTP\Connectors\Endpoints\Get_Single_Email_Data_Endpoint;
use Gravity_Forms\Gravity_SMTP\Connectors\Endpoints\Cleanup_Data_Endpoint;
use Gravity_Forms\Gravity_SMTP\Connectors\Endpoints\Migrate_Settings_Endpoint;
use Gravity_Forms\Gravity_SMTP\Connectors\Endpoints\Save_Connector_Settings_Endpoint;
use Gravity_Forms\Gravity_SMTP\Connectors\Endpoints\Save_Plugin_Settings_Endpoint;
use Gravity_Forms\Gravity_SMTP\Connectors\Endpoints\Send_Test_Endpoint;
use Gravity_Forms\Gravity_Tools\Config;
class Connector_Endpoints_Config extends Config {
protected $script_to_localize = 'gravitysmtp_scripts_admin';
protected $name = 'gravitysmtp_admin_config';
public function should_enqueue() {
return is_admin();
}
public function data() {
return array(
'common' => array(
'endpoints' => array(
Send_Test_Endpoint::ACTION_NAME => array(
'action' => array(
'value' => Send_Test_Endpoint::ACTION_NAME,
'default' => 'mock_endpoint',
),
'nonce' => array(
'value' => wp_create_nonce( Send_Test_Endpoint::ACTION_NAME ),
'default' => 'nonce',
),
),
Save_Connector_Settings_Endpoint::ACTION_NAME => array(
'action' => array(
'value' => Save_Connector_Settings_Endpoint::ACTION_NAME,
'default' => 'mock_endpoint',
),
'nonce' => array(
'value' => wp_create_nonce( Save_Connector_Settings_Endpoint::ACTION_NAME ),
'default' => 'nonce',
),
),
Save_Plugin_Settings_Endpoint::ACTION_NAME => array(
'action' => array(
'value' => Save_Plugin_Settings_Endpoint::ACTION_NAME,
'default' => 'mock_endpoint',
),
'nonce' => array(
'value' => wp_create_nonce( Save_Plugin_Settings_Endpoint::ACTION_NAME ),
'default' => 'nonce',
),
),
Get_Single_Email_Data_Endpoint::ACTION_NAME => array(
'action' => array(
'value' => Get_Single_Email_Data_Endpoint::ACTION_NAME,
'default' => 'mock_endpoint',
),
'nonce' => array(
'value' => wp_create_nonce( Get_Single_Email_Data_Endpoint::ACTION_NAME ),
'default' => 'nonce',
),
),
Cleanup_Data_Endpoint::ACTION_NAME => array(
'action' => array(
'value' => Cleanup_Data_Endpoint::ACTION_NAME,
'default' => 'mock_endpoint',
),
'nonce' => array(
'value' => wp_create_nonce( Cleanup_Data_Endpoint::ACTION_NAME ),
'default' => 'nonce',
),
),
)
),
);
}
}
@@ -0,0 +1,24 @@
<?php
namespace Gravity_Forms\Gravity_SMTP\Connectors\Endpoints;
use Gravity_Forms\Gravity_Tools\Endpoints\Endpoint;
class Check_Background_Tasks_Endpoint extends Endpoint {
const ACTION_NAME = 'gravitysmtp_check_background_tasks';
protected $minimum_cap = 'manage_options';
protected function get_nonce_name() {
return self::ACTION_NAME;
}
public function handle() {
if ( $this->validate() ) {
echo 'ok';
}
die();
}
}
@@ -0,0 +1,70 @@
<?php
namespace Gravity_Forms\Gravity_SMTP\Connectors\Endpoints;
use Gravity_Forms\Gravity_Tools\Endpoints\Endpoint;
use Gravity_Forms\Gravity_SMTP\Users\Roles;
class Cleanup_Data_Endpoint extends Endpoint {
const PARAM_TARGET = 'target';
const ACTION_NAME = 'cleanup_data';
protected $minimum_cap = Roles::EDIT_INTEGRATIONS;
/**
* @var Plugin_Opts_Data_Store;
*/
protected $data_store;
public function __construct( $data_store ) {
$this->data_store = $data_store;
}
protected function get_nonce_name() {
return self::ACTION_NAME;
}
private function reset_setup_wizard_data() {
$this->data_store->save( Save_Plugin_Settings_Endpoint::PARAM_SETUP_WIZARD_SHOULD_DISPLAY, 'true' );
$this->data_store->save( Save_Plugin_Settings_Endpoint::PARAM_LICENSE_KEY, '' );
update_option( 'gravitysmtp_generic', '' );
update_option( 'gravitysmtp_mailgun', '' );
update_option( 'gravitysmtp_postmark', '' );
update_option( 'gravitysmtp_sendgrid', '' );
wp_send_json_success( __( 'Setup wizard data reset.', 'gravitysmtp' ) );
}
public function handle() {
if ( ! $this->validate() ) {
wp_send_json_error( __( 'Missing required parameters.', 'gravitysmtp' ), 400 );
}
$target = filter_input( INPUT_POST, self::PARAM_TARGET );
$target = htmlspecialchars( $target );
// handle data clean up or resets here
switch( $target ) {
case 'setup_wizard':
$this->reset_setup_wizard_data();
break;
default:
break;
}
}
protected function validate() {
if ( ! parent::validate() ) {
return false;
}
if ( empty( $_REQUEST[ self::PARAM_TARGET ] ) ) {
return false;
}
return true;
}
}
@@ -0,0 +1,65 @@
<?php
namespace Gravity_Forms\Gravity_SMTP\Connectors\Endpoints;
use Gravity_Forms\Gravity_Tools\Endpoints\Endpoint;
use Gravity_Forms\Gravity_SMTP\Models\Notifications_Model;
use Gravity_Forms\Gravity_SMTP\Users\Roles;
class Get_Connector_Emails extends Endpoint {
const PARAM_CONNECTOR_NAME = 'connector_name';
const ACTION_NAME = 'get_connector_emails';
/**
* @var Notifications_Model
*/
protected $notifications;
protected $minimum_cap = Roles::VIEW_NOTIFICATIONS_SETTINGS;
protected $required_params = array(
self::PARAM_CONNECTOR_NAME,
);
public function __construct( $notifications ) {
$this->notifications = $notifications;
}
protected function get_nonce_name() {
return self::ACTION_NAME;
}
public function handle() {
if ( ! $this->validate() ) {
wp_send_json_error( __( 'Missing required parameters.', 'gravitysmtp' ), 400 );
}
$connector = rgpost( self::PARAM_CONNECTOR_NAME );
$notifications_for_connector = $this->notifications->by_service( $connector );
$emails = array();
if ( empty( $notifications_for_connector ) ) {
wp_send_json_success( $emails );
}
$notifications_for_connector = array_map( function ( $row ) use ( $connector ) {
$notifications = rgar( $row, 'notifications' );
$notifications = json_decode( $notifications, true );
$filtered = array_filter( $notifications, function ( $notification ) use ( $connector ) {
return rgar( $notification, 'service' ) == $connector && is_email( rgar( $notification, 'from' ) );
} );
return array_values( wp_list_pluck( $filtered, 'from' ) );
}, $notifications_for_connector );
foreach ( $notifications_for_connector as $found_emails ) {
$emails = array_merge( $emails, $found_emails );
}
wp_send_json_success( $emails );
}
}
@@ -0,0 +1,119 @@
<?php
namespace Gravity_Forms\Gravity_SMTP\Connectors\Endpoints;
use Gravity_Forms\Gravity_SMTP\Logging\Debug\Debug_Logger;
use Gravity_Forms\Gravity_SMTP\Models\Event_Model;
use Gravity_Forms\Gravity_SMTP\Models\Log_Details_Model;
use Gravity_Forms\Gravity_SMTP\Users\Roles;
use Gravity_Forms\Gravity_Tools\Endpoints\Endpoint;
class Get_Single_Email_Data_Endpoint extends Endpoint {
const PARAM_EVENT_ID = 'event_id';
const ACTION_NAME = 'get_single_email';
protected $minimum_cap = Roles::VIEW_EMAIL_LOG_DETAILS;
/**
* @var Log_Details_Model
*/
protected $logs;
/**
* @var Event_Model
*/
protected $emails;
protected $required_params = array(
self::PARAM_EVENT_ID,
);
public function __construct( $logs, $email ) {
$this->logs = $logs;
$this->emails = $email;
}
protected function get_nonce_name() {
return self::ACTION_NAME;
}
public function handle() {
if ( ! $this->validate() ) {
wp_send_json_error( __( 'Missing required parameters.', 'gravitysmtp' ), 400 );
}
$email = filter_input( INPUT_POST, self::PARAM_EVENT_ID, FILTER_SANITIZE_NUMBER_INT );
$data = $this->data( $email );
if ( ! empty( $data ) ) {
wp_send_json_success( $data );
}
Debug_Logger::log_message(
sprintf(
/* translators: %d: event ID */
__( 'Error retrieving data for event ID: %d.', 'gravitysmtp' ),
$email
),
'error'
);
wp_send_json_error(
/* translators: %d: email ID */
sprintf( __( 'Could not send get data for event ID: %d.', 'gravitysmtp' ), $email ),
500
);
}
private function get_i18n() {
return array(
'error_alert_title' => esc_html__( 'Error Saving', 'gravitysmtp' ),
'error_alert_generic_message' => esc_html__( 'Could not save; please check your logs.', 'gravitysmtp' ),
'error_alert_close_text' => esc_html__( 'Close', 'gravitysmtp' ),
'log_detail' => array(
'top_heading' => esc_html__( 'View Email', 'gravitysmtp' ),
'top_content' => esc_html__( 'Detailed log information for this email.', 'gravitysmtp' ),
'action_button_view_email_label' => esc_html__( 'View Email', 'gravitysmtp' ),
'action_button_resend_label' => esc_html__( 'Resend', 'gravitysmtp' ),
'action_button_print_label' => esc_html__( 'Print', 'gravitysmtp' ),
'action_button_export_label' => esc_html__( 'Export', 'gravitysmtp' ),
'action_button_delete_label' => esc_html__( 'Delete Log Entry', 'gravitysmtp' ),
'main_box_heading' => esc_html__( 'Email Log', 'gravitysmtp' ),
'main_box_created_label' => esc_html__( 'Created', 'gravitysmtp' ),
'main_box_from_label' => esc_html__( 'From', 'gravitysmtp' ),
'main_box_to_label' => esc_html__( 'To', 'gravitysmtp' ),
'main_box_subject_label' => esc_html__( 'Subject', 'gravitysmtp' ),
'secondary_box_heading' => esc_html__( 'Technical Information', 'gravitysmtp' ),
'sidebar_heading' => esc_html__( 'Status', 'gravitysmtp' ),
'sidebar_status_label' => esc_html__( 'Status', 'gravitysmtp' ),
'sidebar_date_service_label' => esc_html__( 'Service:', 'gravitysmtp' ),
'sidebar_has_attachment_label' => esc_html__( 'Has attachment:', 'gravitysmtp' ),
'sidebar_log_is_label' => esc_html__( 'Log ID:', 'gravitysmtp' ),
'sidebar_source_label' => esc_html__( 'Source:', 'gravitysmtp' ),
'sidebar_attachments_heading' => esc_html__( 'Attachments', 'gravitysmtp' ),
),
);
}
public function get_log_details( $id ) {
return $this->logs->full_details( $id );
}
public function data( $email_id ) {
$details = $this->get_log_details( $email_id );
if ( empty( $details ) ) {
return array();
}
return array(
'log_detail' => $details,
'i18n' => $this->get_i18n(),
'endpoints' => array(),
);
}
}
@@ -0,0 +1,131 @@
<?php
namespace Gravity_Forms\Gravity_SMTP\Connectors\Endpoints;
use Gravity_Forms\Gravity_SMTP\Connectors\Connector_Base;
use Gravity_Forms\Gravity_SMTP\Connectors\Connector_Factory;
use Gravity_Forms\Gravity_SMTP\Logging\Debug\Debug_Logger;
use Gravity_Forms\Gravity_SMTP\Data_Store\Plugin_Opts_Data_Store;
use Gravity_Forms\Gravity_SMTP\Users\Roles;
use Gravity_Forms\Gravity_Tools\Endpoints\Endpoint;
class Save_Connector_Settings_Endpoint extends Endpoint {
const PARAM_SETTINGS = 'settings';
const PARAM_CONNECTOR_TYPE = 'connector_type';
const PARAM_NO_VALIDATE = 'no_validate';
const SETTING_ENABLED_CONNECTOR = 'enabled_connector';
const SETTING_PRIMARY_CONNECTOR = 'primary_connector';
const SETTING_BACKUP_CONNECTOR = 'backup_connector';
const ACTION_NAME = 'save_connector_settings';
protected $minimum_cap = Roles::EDIT_INTEGRATIONS;
/**
* @var Connector_Factory $connector_factory
*/
protected $connector_factory;
/**
* @var Opts_Data_Store;
*/
protected $data_store;
/**
* @var Plugin_Opts_Data_Store
*/
protected $plugin_data_store;
protected $required_params = array(
self::PARAM_SETTINGS,
self::PARAM_CONNECTOR_TYPE,
);
public function __construct( $data_store, $plugin_data_store, $connector_factory ) {
$this->data_store = $data_store;
$this->plugin_data_store = $plugin_data_store;
$this->connector_factory = $connector_factory;
}
protected function get_nonce_name() {
return self::ACTION_NAME;
}
public function handle() {
if ( ! $this->validate() ) {
wp_send_json_error( __( 'Missing required parameters.', 'gravitysmtp' ), 400 );
}
$settings = filter_input( INPUT_POST, self::PARAM_SETTINGS, FILTER_DEFAULT, FILTER_REQUIRE_ARRAY );
$type = filter_input( INPUT_POST, self::PARAM_CONNECTOR_TYPE );
$no_validate = filter_has_var( INPUT_POST, self::PARAM_NO_VALIDATE );
$type = htmlspecialchars( $type );
$configured_key = sprintf( 'gsmtp_connector_configured_%s', $type );
$this->data_store->save_all( $settings, $type );
delete_transient( $configured_key );
// Only continue if this connector needs to be validated/enabled in some way.
if (
! isset( $settings[ Connector_Base::SETTING_ENABLED ] ) &&
! isset( $settings[ Connector_Base::SETTING_IS_PRIMARY ] ) &&
! isset( $settings[ Connector_Base::SETTING_IS_BACKUP ] )
) {
wp_send_json_success( $settings );
}
if ( isset( $settings[ Connector_Base::SETTING_ENABLED ] ) ) {
$this->save_connector_status( $type, self::SETTING_ENABLED_CONNECTOR, $settings[ Connector_Base::SETTING_ENABLED ] );
}
if ( isset( $settings[ Connector_Base::SETTING_IS_PRIMARY ] ) ) {
$this->save_connector_status( $type, self::SETTING_PRIMARY_CONNECTOR, $settings[ Connector_Base::SETTING_IS_PRIMARY ] );
}
if ( isset( $settings[ Connector_Base::SETTING_IS_BACKUP ] ) ) {
$this->save_connector_status( $type, self::SETTING_BACKUP_CONNECTOR, $settings[ Connector_Base::SETTING_IS_BACKUP ] );
}
/**
* @var Connector_Base $connector
*/
$connector = $this->connector_factory->create( $type );
$valid = $no_validate ? true : $connector->is_configured();
if ( is_wp_error( $valid ) ) {
$error_message = $valid->get_error_message();
Debug_Logger::log_message( sprintf(
/* translators: %1$s is the connector, eg: SendGrid, %2$s is the error message */
__( 'Error saving settings for %1$s: %2$s', 'gravitysmtp' ),
$type,
$error_message
), 'error' );
wp_send_json_error( $error_message, 500 );
}
wp_send_json_success( $settings );
}
/**
* Save a connector's status (enabled, primary, backup) in the settings array.
*
* @param $type
* @param $status_type
* @param $enabled
*
* @return void
*/
protected function save_connector_status( $type, $status_type, $enabled ) {
$connector_values = $this->plugin_data_store->get( $status_type, array() );
if ( ! is_array( $connector_values ) ) {
$connector_values = array();
}
$connector_values[ $type ] = $enabled;
$this->plugin_data_store->save( $status_type, $connector_values );
}
}
@@ -0,0 +1,144 @@
<?php
namespace Gravity_Forms\Gravity_SMTP\Connectors\Endpoints;
use Gravity_Forms\Gravity_SMTP\Users\Roles;
use Gravity_Forms\Gravity_Tools\Endpoints\Endpoint;
use Gravity_Forms\Gravity_Tools\License\License_Statuses;
class Save_Plugin_Settings_Endpoint extends Endpoint {
const PARAM_SETTINGS = 'settings';
const PARAM_SETTING_KEY = 'key';
const PARAM_SETTING_VALUE = 'value';
const PARAM_LICENSE_KEY = 'license_key';
const PARAM_TEST_MODE = 'test_mode';
const PARAM_EVENT_LOG_ENABLED = 'event_log_enabled';
const PARAM_SAVE_EMAIL_BODY_ENABLED = 'save_email_body_enabled';
const PARAM_SAVE_ATTACHMENTS_ENABLED = 'save_attachments_enabled';
const PARAM_EVENT_LOG_RETENTION = 'event_log_retention';
const PARAM_DEBUG_LOG_ENABLED = 'debug_log_enabled';
const PARAM_DEBUG_LOG_RETENTION = 'debug_log_retention';
const PARAM_USAGE_ANALYTICS = 'usage_analytics';
const PARAM_PER_PAGE = 'activity_log_per_page';
const PARAM_MAX_EVENT_RECORDS = 'max_event_records';
const PARAM_NOTIFY_WHEN_EMAIL_SENDING_FAILS_ENABLED = 'notify_when_email_sending_fails_enabled';
const PARAM_SLACK_ALERTS_ENABLED = 'slack_alerts_enabled';
const PARAM_TWILIO_ALERTS_ENABLED = 'twilio_alerts_enabled';
const PARAM_NOTIFICATIONS_EMAIL_DIGEST_ENABLED = 'notifications_email_digest_enabled';
const PARAM_NOTIFICATIONS_EMAIL_DIGEST_FREQUENCY = 'notifications_email_digest_frequency';
const PARAM_SETUP_WIZARD_SHOULD_DISPLAY = 'setup_wizard_should_display';
const ACTION_NAME = 'save_plugin_settings';
protected $minimum_cap = Roles::EDIT_GENERAL_SETTINGS;
/**
* @var Plugin_Opts_Data_Store;
*/
protected $data_store;
/**
* @var License_API_Connector;
*/
protected $api_connector;
protected $required_params = array();
public function __construct( $data_store, $api_connector ) {
$this->data_store = $data_store;
$this->api_connector = $api_connector;
}
protected function get_nonce_name() {
return self::ACTION_NAME;
}
public function handle() {
if ( ! $this->validate() ) {
wp_send_json_error( __( 'Request must contain either an array of values to update, or a key and value to update individually.', 'gravitysmtp' ), 400 );
}
$settings = filter_input( INPUT_POST, self::PARAM_SETTINGS, FILTER_DEFAULT, FILTER_REQUIRE_ARRAY );
if ( ! empty( $settings ) ) {
$this->handle_bulk_settings( $settings );
} else {
$this->handle_individual_setting();
}
}
protected function handle_bulk_settings( $settings ) {
$this->data_store->save_all( $settings );
$data = $settings;
if ( isset( $settings[ self::PARAM_LICENSE_KEY ] ) ) {
$data = array_merge( $data, $this->handle_license_key( $settings[ self::PARAM_LICENSE_KEY ] ) );
}
wp_send_json_success( $data );
}
protected function handle_individual_setting() {
$key = htmlspecialchars( filter_input( INPUT_POST, self::PARAM_SETTING_KEY ) );
$value = isset( $_POST[ self::PARAM_SETTING_VALUE ] ) ? $_POST[ self::PARAM_SETTING_VALUE ] : null;
if ( is_array( $value ) ) {
$value = filter_input( INPUT_POST, self::PARAM_SETTING_VALUE, FILTER_DEFAULT, FILTER_REQUIRE_ARRAY );
} else {
$value = htmlspecialchars( $value );
}
switch ( $key ) {
case self::PARAM_LICENSE_KEY:
$data = $this->handle_license_key_setting( $key, $value );
break;
default:
$this->data_store->save( $key, $value );
$data = array( $key => $value );
break;
}
do_action( 'gravitysmtp_save_plugin_setting', $key, $value );
wp_send_json_success( $data );
}
protected function handle_license_key_setting( $key, $value ) {
$this->data_store->save( $key, $value );
$data = array( $key => $value );
return array_merge( $data, $this->handle_license_key( $value ) );
}
protected function handle_license_key( $license_key ) {
$key_is_empty = empty( $license_key );
if ( $key_is_empty ) {
return array( 'license_is_valid' => null );
}
$license_info = $this->api_connector->check_license( $license_key );
return array( 'license_is_valid' => License_Statuses::VALID_KEY === $license_info->get_status() );
}
protected function validate() {
if ( ! parent::validate() ) {
return false;
}
if (
! isset( $_REQUEST[ self::PARAM_SETTINGS ] ) &&
( ! isset( $_REQUEST[ self::PARAM_SETTING_KEY ] ) || ! isset( $_REQUEST[ self::PARAM_SETTING_VALUE ] ) )
) {
return false;
}
return true;
}
}
@@ -0,0 +1,258 @@
<?php
namespace Gravity_Forms\Gravity_SMTP\Connectors\Endpoints;
use Gravity_Forms\Gravity_SMTP\Connectors\Connector_Factory;
use Gravity_Forms\Gravity_SMTP\Data_Store\Plugin_Opts_Data_Store;
use Gravity_Forms\Gravity_SMTP\Logging\Debug\Debug_Logger;
use Gravity_Forms\Gravity_SMTP\Models\Event_Model;
use Gravity_Forms\Gravity_SMTP\Models\Log_Details_Model;
use Gravity_Forms\Gravity_SMTP\Users\Roles;
use Gravity_Forms\Gravity_Tools\Endpoints\Endpoint;
class Send_Test_Endpoint extends Endpoint {
const PARAM_EMAIL = 'email';
const PARAM_CONNECTOR_TYPE = 'connector_type';
const PARAM_AS_HTML = 'as_html';
const PARAM_FROM_EMAIL = 'from_email';
const ACTION_NAME = 'send_test';
protected $minimum_cap = Roles::VIEW_TOOLS_SENDATEST;
public $last_email_id = 0;
/**
* @var Connector_Factory $connector_factory
*/
protected $connector_factory;
/**
* @var Plugin_Opts_Data_Store
*/
protected $plugin_data;
/**
* @var Event_Model
*/
protected $emails;
/**
* @var Log_Details_Model
*/
protected $logs;
/**
* @var Get_Single_Email_Data_Endpoint
*/
protected $email_endpoint;
protected $required_params = array(
self::PARAM_EMAIL,
self::PARAM_CONNECTOR_TYPE,
self::PARAM_AS_HTML,
self::PARAM_FROM_EMAIL,
);
public function __construct( $connector_factory, $plugin_data_store, $emails_model, $log_model, $email_endpoint ) {
$this->connector_factory = $connector_factory;
$this->plugin_data = $plugin_data_store;
$this->emails = $emails_model;
$this->logs = $log_model;
$this->email_endpoint = $email_endpoint;
}
protected function get_nonce_name() {
return self::ACTION_NAME;
}
protected function get_test_email_markup( $as_html ) {
if ( empty( $as_html ) ) {
return esc_html__( 'Test Successful', 'gravitysmtp' ) . "\r\n\r\n" .
esc_html__( 'Congratulations! Gravity SMTP is sending emails correctly!', 'gravitysmtp' ) . "\r\n" .
esc_html__( 'Gravity SMTP is taking care of sending your emails, so now you can focus on the content of your emails and leave the technical details to us.', 'gravitysmtp' );
}
$image_base_url = \Gravity_Forms\Gravity_SMTP\Gravity_SMTP::get_base_url() . '/assets/images/email-templates/';
return '<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Email Template</title>
<style>
body {
margin: 0;
padding: 0;
background: #fff;
font-family: inter, -apple-system, blinkmacsystemfont, \'Segoe UI\', roboto, oxygen-sans, ubuntu, cantarell, \'Helvetica Neue\', sans-serif;
}
img {
border: 0 none;
height: auto;
line-height: 100%;
outline: none;
text-decoration: none;
}
a img {
border: 0 none;
}
table, td {
border-collapse: collapse;
}
#bodyTable {
height: 100% !important;
margin: 0;
padding: 0;
width: 100% !important;
}
.wrapper {
max-width: 680px;
margin: 0 auto;
}
.content {
padding: 20px 20px 200px;
}
@media only screen and (max-width: 480px) {
.content {
padding: 20px 20px 120px;
}
}
</style>
</head>
<body>
<table id="bodyTable" role="presentation" width="100%" align="center"
style="background: url(\'' . $image_base_url . 'gravitysmtp-arrow-bg.png\') no-repeat top right / 514px 647px #fff; margin: 0;">
<tr>
<td>
<table class="wrapper" role="presentation">
<!-- Header with Logo -->
<tr>
<td style="padding: 70px 20px 32px; text-align: center;">
<img src="' . $image_base_url . 'gravitysmtp-email-logo.png" alt="' . esc_html__( 'Logo', 'gravitysmtp' ) . '"
style="display: block; margin: 0 auto; max-width: 200px">
</td>
</tr>
<!-- Content Area -->
<tr>
<td class="content">
<img src="' . $image_base_url . 'send-test/gravitysmtp-success.png" alt="' . esc_html__( 'Mail Icon', 'gravitysmtp' ) . '"
style="display: block; margin: 0 auto; max-width: 308px">
<h1 style="color: #242748; text-align: center; font-family: inter, -apple-system, blinkmacsystemfont, \'Segoe UI\', roboto, oxygen-sans, ubuntu, cantarell, \'Helvetica Neue\', sans-serif; font-size: 30px; font-style: normal; font-weight: 600; line-height: 30px; padding: 20px 0 32px; margin: 0;">' . esc_html__( 'Test Successful', 'gravitysmtp' ) . '</h1>
<div
style="border-radius: 3px; border: 1px solid #d5d7e9; box-shadow: 0px 2px 2px 0px rgba(58, 58, 87, 0.06);">
<p style="margin: 0; padding: 12px 24px; background: #f6f9fc; font-family: inter, -apple-system, blinkmacsystemfont, \'Segoe UI\', roboto, oxygen-sans, ubuntu, cantarell, \'Helvetica Neue\', sans-serif; border-bottom: 1px solid #d5d7e9; color: #242748; font-size: 14px; font-style: normal; font-weight: 500; line-height: 18px;">' . esc_html__( 'Congratulations! Gravity SMTP is sending emails correctly!', 'gravitysmtp' ) . '</p>
<p style="color: #5b5e80; background: #fff; margin: 0; padding: 16px 24px; font-family: inter, -apple-system, blinkmacsystemfont, \'Segoe UI\', roboto, oxygen-sans, ubuntu, cantarell, \'Helvetica Neue\', sans-serif; font-size: 14px; font-style: normal; font-weight: 400; line-height: 20px">' . esc_html__( 'Gravity SMTP is taking care of sending your emails, so now you can focus on the content of your emails and leave the technical details to us.', 'gravitysmtp' ) . '</p>
</div>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>';
}
public function handle() {
if ( ! $this->validate() ) {
wp_send_json_error( __( 'Missing required parameters.', 'gravitysmtp' ), 400 );
}
$this->override_error_handling();
$self = $this;
add_action( 'gravitysmtp_after_mail_created', function( $created_id ) use ( $self ) {
$self->last_email_id = $created_id;
}, 10, 1 );
$email = filter_input( INPUT_POST, self::PARAM_EMAIL, FILTER_SANITIZE_EMAIL );
$connector = filter_input( INPUT_POST, self::PARAM_CONNECTOR_TYPE );
$as_html = filter_input( INPUT_POST, self::PARAM_AS_HTML );
$from_email = filter_input( INPUT_POST, self::PARAM_FROM_EMAIL, FILTER_SANITIZE_EMAIL );
$connector = htmlspecialchars( $connector );
$as_html = htmlspecialchars( $as_html ) !== 'false';
$from_email = $from_email ? $from_email : get_option( 'admin_email' );
$content_type = $as_html ? 'text/html' : 'text/plain';
$headers = array(
'content-type' => 'Content-type: ' . $content_type,
'from' => 'From: ' . $from_email,
);
add_filter( 'gravitysmtp_connector_for_sending', function( $current_connector, $email_args ) use ( $connector ) {
return array( 'force' => true, 'connector' => $connector );
}, 8, 2 );
$success = wp_mail( array( 'email' => $email ), __( 'Test Email from Gravity SMTP', 'gravitysmtp' ), $this->get_test_email_markup( $as_html ), $headers, array() );
if ( $success === true ) {
wp_send_json_success( array( 'email' => $email ) );
}
$full_log = $this->get_full_log_data( $this->last_email_id );
$issues = array();
$log_copy = '';
if ( isset( $full_log['technical_information'] ) && is_array( $full_log['technical_information']['log'] ) ) {
array_push( $issues, end( $full_log['technical_information']['log'] ) );
$log_copy = implode( "\r\n", $full_log['technical_information']['log'] );
}
$reasons = array();
$steps = array();
if ( empty( $issues ) ) {
$reasons = array(
__( 'Incorrect plugin settings, such as invalid SMTP credentials or expired API key.', 'gravitysmtp' ),
__( 'The SMTP server blocking the incoming connection.', 'gravitysmtp' ),
__( 'Your web host rejecting the connection.', 'gravitysmtp' ),
);
$steps = array(
__( 'Triple check the plugin settings and ensure they are accurate, especially if you copy-pasted the values.', 'gravitysmtp' ),
__( 'Contact your web hosting provider to verify if your server allows outside connections and if any firewall or security policies are in place that could interfere.', 'gravitysmtp' ),
__( 'Consider using one of the other available integration types.', 'gravitysmtp' ),
);
}
$error_data = array(
'error_message' => __( 'There was a problem sending the test email.', 'gravitysmtp' ),
'issues' => $issues,
'full_log' => $full_log,
'log_copy' => $log_copy,
'possible_reasons' => $reasons,
'recommended_steps' => $steps,
);
Debug_Logger::log_message(
sprintf(
__( 'Send a test error: %1$s', 'gravitysmtp' ),
json_encode( $error_data )
),
'error'
);
wp_send_json_error( $error_data, 500 );
}
private function override_error_handling() {
ini_set( 'display_errors', 0 );
unset( $GLOBALS['wp_locale'] );
}
private function get_full_log_data( $email_id ) {
return $this->email_endpoint->get_log_details( $email_id );
}
}
@@ -0,0 +1,209 @@
<?php
namespace Gravity_Forms\Gravity_SMTP\Connectors\Oauth;
use Gravity_Forms\Gravity_SMTP\Connectors\Types\Connector_Google;
use Gravity_Forms\Gravity_SMTP\Connectors\Types\Connector_Microsoft;
use Gravity_Forms\Gravity_SMTP\Connectors\Endpoints\Save_Plugin_Settings_Endpoint;
use Gravity_Forms\Gravity_Tools\API\Oauth_Handler as Oauth_Handler_Base;
use Gravity_Forms\Gravity_Tools\Utils\Utils_Service_Provider;
class Google_Oauth_Handler extends Oauth_Handler_Base {
protected $supports_refresh_token = true;
protected $response_payload_name = 'code';
protected $namespace = 'google';
public function handle_response() {
if ( ! $this->is_response() ) {
return;
}
$url = 'https://oauth2.googleapis.com/token';
// Require code and state, and valid mode; otherwise redirect back.
if ( ! isset( $_GET['code'] ) ) { //phpcs:ignore
return;
}
$code = FILTER_INPUT( INPUT_GET, 'code', FILTER_DEFAULT );
$body = array(
'client_id' => $this->data->get( Connector_Google::SETTING_CLIENT_ID, $this->namespace ),
'client_secret' => $this->data->get( Connector_Google::SETTING_CLIENT_SECRET, $this->namespace ),
'grant_type' => 'authorization_code',
'code' => $code,
'redirect_uri' => urldecode( $this->get_return_url( 'settings', false ) ),
);
$request = wp_remote_post( $url, array( 'body' => $body ) );
if ( (int) wp_remote_retrieve_response_code( $request ) !== 200 ) { //phpcs:ignore
return;
}
$response = wp_remote_retrieve_body( $request );
$response = json_decode( $response, true );
if ( isset( $response[ $this->payload_access_token_name ] ) ) {
$this->store_access_token( $response[ $this->payload_access_token_name ] );
}
if ( isset( $response[ $this->payload_refresh_token_name ] ) ) {
$this->store_refresh_token( $response[ $this->payload_refresh_token_name ] );
}
}
protected function refresh_expired_token() {
$refresh_token = $this->get_refresh_token();
if ( empty( $refresh_token ) ) {
return new \WP_Error( __( 'Token is invalid or expired.', 'gravitysmtp' ) );
}
$refresh_url = $this->get_refresh_url();
$body = array(
'refresh_token' => $refresh_token,
'client_id' => $this->data->get( Connector_Google::SETTING_CLIENT_ID, $this->namespace ),
'client_secret' => $this->data->get( Connector_Google::SETTING_CLIENT_SECRET, $this->namespace ),
'grant_type' => 'refresh_token',
);
$response = wp_remote_post( $refresh_url, array( 'body' => $body ) );
$code = wp_remote_retrieve_response_code( $response );
if ( (int) $code !== 200 ) {
return new \WP_Error( __( 'Token is invalid or expired.', 'gravitysmtp' ) );
}
$response_body = json_decode( wp_remote_retrieve_body( $response ), true );
if ( empty( $response_body['access_token'] ) ) {
return new \WP_Error( __( 'Token is invalid or expired.', 'gravitysmtp' ) );
}
$new_token = $response_body['access_token'];
$this->store_access_token( $new_token );
return $new_token;
}
protected function is_response() {
$page = filter_input( INPUT_GET, 'page' );
$tab = filter_input( INPUT_GET, 'tab' );
$integration = filter_input( INPUT_GET, 'integration' );
$wizard_screen = filter_input( INPUT_GET, 'setup-wizard-page', FILTER_SANITIZE_NUMBER_INT );
$payload = filter_input( INPUT_GET, $this->response_payload_name );
// Valid screens are a specific integration tab, or the setup wizard page.
$is_valid_screen = $page === 'gravitysmtp-settings' &&
(
( $tab === 'integrations' && $integration === $this->namespace ) ||
( ! empty( $wizard_screen ) )
);
return ! empty( $payload ) && $is_valid_screen;
}
public function get_return_url( $context = 'settings' ) {
$base = admin_url( 'admin.php' );
$args = array(
'page' => 'gravitysmtp-settings',
);
if ( $context === 'settings' ) {
$args['integration'] = $this->namespace;
$args['tab'] = 'integrations';
}
if ( $context === 'wizard' ) {
$args['tab'] = 'integrations';
$args['setup-wizard-page'] = 4;
}
return urlencode( add_query_arg( $args, $base ) );
}
public function get_refresh_url() {
return esc_url('https://oauth2.googleapis.com/token' );
}
public function get_oauth_url( $context = 'settings' ) {
$state = array(
'url' => admin_url( 'admin.php' ),
'page' => 'gravitysmtp-settings',
'nonce' => wp_create_nonce( 'gravitysmtp' ),
);
if ( $context === 'settings' ) {
$state['tab'] = 'integrations';
$state['integration'] = $this->namespace;
}
$auth_url = add_query_arg(
array(
'redirect_to' => $this->get_return_url( $context ),
'state' => base64_encode(
json_encode(
$state
)
),
'license' => $this->data->get( Save_Plugin_Settings_Endpoint::PARAM_LICENSE_KEY ),
),
trailingslashit( GRAVITY_API_URL ) . 'auth/gmail'
);
return esc_url( $auth_url );
}
protected function is_valid_token( $token ) {
static $is_valid;
if ( ! is_null( $is_valid ) ) {
return $is_valid;
}
if ( empty( $token ) ) {
return false;
}
$check_url = 'https://gmail.googleapis.com/gmail/v1/users/me/profile';
$headers = array(
'Authorization' => 'Bearer ' . $token,
);
$response = wp_remote_get( $check_url, array( 'headers' => $headers ) );
$code = wp_remote_retrieve_response_code( $response );
$is_valid = (int) $code === 200;
return $is_valid;
}
public function get_connection_details() {
$token = $this->data->get( 'access_token', $this->namespace );
$check_url = 'https://gmail.googleapis.com/gmail/v1/users/me/profile';
$headers = array(
'Authorization' => 'Bearer ' . $token,
);
$response = wp_remote_get( $check_url, array( 'headers' => $headers ) );
$code = wp_remote_retrieve_response_code( $response );
if ( (int) $code !== 200 ) {
return array(
'email' => __( 'Unable to retrieve associated email.', 'gravitysmtp' ),
);
}
$body = json_decode( wp_remote_retrieve_body( $response ), true );
return array(
'email' => $body['emailAddress'],
);
}
}
@@ -0,0 +1,235 @@
<?php
namespace Gravity_Forms\Gravity_SMTP\Connectors\Oauth;
use Gravity_Forms\Gravity_SMTP\Connectors\Types\Connector_Google;
use Gravity_Forms\Gravity_SMTP\Connectors\Types\Connector_Microsoft;
use Gravity_Forms\Gravity_SMTP\Connectors\Endpoints\Save_Plugin_Settings_Endpoint;
use Gravity_Forms\Gravity_Tools\API\Oauth_Handler as Oauth_Handler_Base;
use Gravity_Forms\Gravity_Tools\Utils\Utils_Service_Provider;
class Microsoft_Oauth_Handler extends Oauth_Handler_Base {
protected $supports_refresh_token = true;
protected $namespace = 'microsoft';
protected $response_payload_name = 'code';
public function handle_response() {
if ( ! $this->is_response() ) {
return;
}
$url = 'https://login.microsoftonline.com/common/oauth2/v2.0/token';
$state = $this->get_state();
// Require code and state, and valid mode; otherwise redirect back.
if ( ! isset( $_GET['code'] ) || empty( $state ) ) { //phpcs:ignore
return;
}
$code = FILTER_INPUT( INPUT_GET, 'code', FILTER_DEFAULT );
$body = array(
'client_id' => $this->data->get( Connector_Microsoft::SETTING_CLIENT_ID, $this->namespace ),
'grant_type' => 'authorization_code',
'scope' => $this->get_scope(),
'code' => $code,
'redirect_uri' => $this->get_return_url( 'settings', false ),
'client_secret' => $this->data->get( Connector_Microsoft::SETTING_CLIENT_SECRET, $this->namespace )
);
$request = wp_remote_post( $url, array( 'body' => $body ) );
if ( (int) wp_remote_retrieve_response_code( $request ) !== 200 ) { //phpcs:ignore
return;
}
$response = wp_remote_retrieve_body( $request );
$response = json_decode( $response, true );
if ( isset( $response[ $this->payload_access_token_name ] ) ) {
$this->store_access_token( $response[ $this->payload_access_token_name ] );
}
if ( isset( $response[ $this->payload_refresh_token_name ] ) ) {
$this->store_refresh_token( $response[ $this->payload_refresh_token_name ] );
}
}
public function get_scope() {
return 'email Mail.Send User.Read profile openid offline_access';
}
protected function refresh_expired_token() {
$refresh_token = $this->get_refresh_token();
if ( empty( $refresh_token ) ) {
return new \WP_Error( __( 'Token is invalid or expired.', 'gravitysmtp' ) );
}
$refresh_url = $this->get_refresh_url();
$body = array(
'refresh_token' => $refresh_token,
'client_id' => $this->data->get( Connector_Microsoft::SETTING_CLIENT_ID, $this->namespace ),
'client_secret' => $this->data->get( Connector_Microsoft::SETTING_CLIENT_SECRET, $this->namespace ),
'grant_type' => 'refresh_token',
'scope' => $this->get_scope(),
);
$response = wp_remote_post( $refresh_url, array( 'body' => $body ) );
$code = wp_remote_retrieve_response_code( $response );
if ( (int) $code !== 200 ) {
return new \WP_Error( __( 'Token is invalid or expired.', 'gravitysmtp' ) );
}
$response_body = json_decode( wp_remote_retrieve_body( $response ), true );
if ( empty( $response_body['access_token'] ) ) {
return new \WP_Error( __( 'Token is invalid or expired.', 'gravitysmtp' ) );
}
$new_token = $response_body['access_token'];
$refresh_token = $response_body['refresh_token'];
$this->store_access_token( $new_token );
$this->store_refresh_token( $refresh_token );
return $new_token;
}
protected function is_response() {
$page = filter_input( INPUT_GET, 'page' );
$tab = filter_input( INPUT_GET, 'tab' );
$integration = filter_input( INPUT_GET, 'integration' );
$wizard_screen = filter_input( INPUT_GET, 'setup-wizard-page', FILTER_SANITIZE_NUMBER_INT );
$payload = filter_input( INPUT_GET, $this->response_payload_name );
// Valid screens are a specific integration tab, or the setup wizard page.
$is_valid_screen = $page === 'gravitysmtp-settings' &&
(
( $tab === 'integrations' && $integration === $this->namespace ) ||
( ! empty( $wizard_screen ) )
);
return ! empty( $payload ) && $is_valid_screen;
}
public function get_return_url( $context = 'settings', $encode = true ) {
$base = admin_url( 'admin.php' );
if ( $context === 'copy' ) {
return $base;
}
$args = array(
'page' => 'gravitysmtp-settings',
);
if ( $context === 'settings' ) {
$args['integration'] = $this->namespace;
$args['tab'] = 'integrations';
}
if ( $context === 'wizard' ) {
$args['tab'] = 'integrations';
$args['setup-wizard-page'] = 4;
}
$value = add_query_arg( $args, $base );
if ( ! $encode ) {
return $value;
}
return urlencode( $value );
}
public function get_refresh_url() {
return esc_url( 'https://login.microsoftonline.com/common/oauth2/v2.0/token' );
}
private function get_state( $context = 'settings' ) {
$state = array(
'url' => admin_url( 'admin.php' ),
'page' => 'gravitysmtp-settings',
'nonce' => wp_create_nonce( 'gravitysmtp' ),
);
if ( $context === 'settings' ) {
$state['tab'] = 'integrations';
$state['integration'] = $this->namespace;
}
return $state;
}
public function get_oauth_url( $context = 'settings' ) {
$state = $this->get_state( $context );
$auth_url = add_query_arg(
array(
'redirect_to' => $this->get_return_url( $context ),
'state' => base64_encode(
json_encode(
$state
)
),
'license' => $this->data->get( Save_Plugin_Settings_Endpoint::PARAM_LICENSE_KEY ),
),
trailingslashit( GRAVITY_API_URL ) . 'auth/microsoft'
);
return esc_url( $auth_url );
}
protected function is_valid_token( $token ) {
static $is_valid;
if ( ! is_null( $is_valid ) ) {
return $is_valid;
}
if ( empty( $token ) ) {
return false;
}
$check_url = 'https://graph.microsoft.com/v1.0/me';
$headers = array(
'Authorization' => 'Bearer ' . $token,
);
$response = wp_remote_get( $check_url, array( 'headers' => $headers ) );
$code = wp_remote_retrieve_response_code( $response );
$is_valid = (int) $code === 200;
return $is_valid;
}
public function get_connection_details() {
$token = $this->data->get( 'access_token', $this->namespace );
$check_url = 'https://graph.microsoft.com/v1.0/me';
$headers = array(
'Authorization' => 'Bearer ' . $token,
);
$response = wp_remote_get( $check_url, array( 'headers' => $headers ) );
$code = wp_remote_retrieve_response_code( $response );
if ( (int) $code !== 200 ) {
return array(
'email' => __( 'Unable to retrieve associated email.', 'gravitysmtp' ),
);
}
$body = json_decode( wp_remote_retrieve_body( $response ), true );
return array(
'email' => $body['mail'],
);
}
}
@@ -0,0 +1,237 @@
<?php
namespace Gravity_Forms\Gravity_SMTP\Connectors\Oauth;
use Gravity_Forms\Gravity_SMTP\Connectors\Types\Connector_Google;
use Gravity_Forms\Gravity_SMTP\Connectors\Endpoints\Save_Plugin_Settings_Endpoint;
use Gravity_Forms\Gravity_SMTP\Connectors\Types\Connector_Zoho;
use Gravity_Forms\Gravity_SMTP\Enums\Zoho_Datacenters_Enum;
use Gravity_Forms\Gravity_Tools\API\Oauth_Handler as Oauth_Handler_Base;
use Gravity_Forms\Gravity_Tools\Utils\Utils_Service_Provider;
class Zoho_Oauth_Handler extends Oauth_Handler_Base {
protected $supports_refresh_token = true;
protected $namespace = 'zoho';
protected $response_payload_name = 'code';
public function get_connection_details() {
return array(
'account_id' => $this->data->get( Connector_Zoho::SETTING_ACCOUNT_ID, $this->namespace )
);
}
public function handle_response() {
if ( ! $this->is_response() ) {
return;
}
$url = 'https://accounts.zoho.com/oauth/v2/token';
$state = $this->get_state();
// Require code and state, and valid mode; otherwise redirect back.
if ( ! isset( $_GET['code'] ) || empty( $state ) ) { //phpcs:ignore
return;
}
$code = FILTER_INPUT( INPUT_GET, 'code', FILTER_DEFAULT );
$args = array(
'grant_type' => 'authorization_code',
'redirect_uri' => $this->get_return_url( 'settings', true ),
);
$body = array(
'client_id' => $this->data->get( Connector_Zoho::SETTING_CLIENT_ID, $this->namespace ),
'client_secret' => $this->data->get( Connector_Zoho::SETTING_CLIENT_SECRET, $this->namespace ),
'code' => $code,
);
$url = add_query_arg( $args, $url );
$request = wp_remote_post( $url, array( 'body' => $body ) );
if ( (int) wp_remote_retrieve_response_code( $request ) !== 200 ) { //phpcs:ignore
return;
}
$response = wp_remote_retrieve_body( $request );
$response = json_decode( $response, true );
if ( isset( $response[ $this->payload_access_token_name ] ) ) {
$this->store_access_token( $response[ $this->payload_access_token_name ] );
}
if ( isset( $response[ $this->payload_refresh_token_name ] ) ) {
$this->store_refresh_token( $response[ $this->payload_refresh_token_name ] );
}
// Add Account ID
$accounts_url = $this->get_api_url( 'api/accounts' );
$headers = array(
'Authorization' => 'Zoho-oauthtoken ' . $response[ $this->payload_access_token_name ],
);
$request = wp_remote_get( $accounts_url, array( 'headers' => $headers ) );
$response = wp_remote_retrieve_body( $request );
$data = json_decode( $response, true );
if ( ! isset( $data['data'][0]['accountId'] ) ) {
return;
}
$this->data->save( Connector_Zoho::SETTING_ACCOUNT_ID, $data['data'][0]['accountId'], $this->namespace );
}
public function get_scope() {
return 'ZohoMail.messages.CREATE,ZohoMail.accounts.READ';
}
protected function refresh_expired_token() {
$refresh_token = $this->get_refresh_token();
if ( empty( $refresh_token ) ) {
return new \WP_Error( __( 'Token is invalid or expired.', 'gravitysmtp' ) );
}
$refresh_url = $this->get_refresh_url();
$args = array(
'refresh_token' => $refresh_token,
'client_id' => $this->data->get( Connector_Zoho::SETTING_CLIENT_ID, $this->namespace ),
'client_secret' => $this->data->get( Connector_Zoho::SETTING_CLIENT_SECRET, $this->namespace ),
'grant_type' => 'refresh_token',
'redirect_uri' => $this->get_return_url( 'settings', true ),
);
$url = add_query_arg( $args, $refresh_url );
$request = wp_remote_post( $url, array( 'body' => array() ) );
$code = wp_remote_retrieve_response_code( $request );
if ( (int) $code !== 200 ) {
return new \WP_Error( __( 'Token is invalid or expired.', 'gravitysmtp' ) );
}
$response_body = json_decode( wp_remote_retrieve_body( $request ), true );
if ( empty( $response_body['access_token'] ) ) {
return new \WP_Error( __( 'Token is invalid or expired.', 'gravitysmtp' ) );
}
$new_token = $response_body['access_token'];
$this->store_access_token( $new_token );
return $new_token;
}
protected function is_response() {
$page = filter_input( INPUT_GET, 'page' );
$tab = filter_input( INPUT_GET, 'tab' );
$integration = filter_input( INPUT_GET, 'integration' );
$wizard_screen = filter_input( INPUT_GET, 'setup-wizard-page', FILTER_SANITIZE_NUMBER_INT );
$payload = filter_input( INPUT_GET, $this->response_payload_name );
// Valid screens are a specific integration tab, or the setup wizard page.
$is_valid_screen = $page === 'gravitysmtp-settings' &&
(
( $tab === 'integrations' && $integration === $this->namespace ) ||
( ! empty( $wizard_screen ) )
);
return ! empty( $payload ) && $is_valid_screen;
}
public function get_return_url( $context = 'settings', $encode = true ) {
$base = admin_url( 'admin.php' );
if ( $context === 'copy' ) {
return $base;
}
$args = array(
'page' => 'gravitysmtp-settings',
);
if ( $context === 'settings' ) {
$args['integration'] = $this->namespace;
$args['tab'] = 'integrations';
}
if ( $context === 'wizard' ) {
$args['tab'] = 'integrations';
$args['setup-wizard-page'] = 4;
}
$value = add_query_arg( $args, $base );
if ( ! $encode ) {
return $value;
}
return urlencode( $value );
}
public function get_refresh_url() {
return esc_url( 'https://accounts.zoho.com/oauth/v2/token' );
}
private function get_state( $context = 'settings' ) {
$state = array(
'url' => admin_url( 'admin.php' ),
'page' => 'gravitysmtp-settings',
'nonce' => wp_create_nonce( 'gravitysmtp' ),
);
if ( $context === 'settings' ) {
$state['tab'] = 'integrations';
$state['integration'] = $this->namespace;
}
return $state;
}
public function get_oauth_url( $context = 'settings' ) {
return '';
}
protected function is_valid_token( $token ) {
static $is_valid;
if ( ! is_null( $is_valid ) ) {
return $is_valid;
}
if ( empty( $token ) ) {
return false;
}
$check_url = $this->get_api_url( 'api/accounts' );
$headers = array(
'Authorization' => 'Zoho-oauthtoken ' . $token,
'Content-type' => 'application/json',
'Accept' => 'application/json',
);
$response = wp_remote_get( $check_url, array( 'headers' => $headers ) );
$code = wp_remote_retrieve_response_code( $response );
$is_valid = (int) $code === 200;
return $is_valid;
}
private function get_api_url( $endpoint ) {
$data_center_location = $this->data->get( Connector_Zoho::SETTING_DATA_CENTER_REGION, $this->namespace );
if ( empty( $data_center_location ) ) {
$data_center_location = 'us';
}
$base = Zoho_Datacenters_Enum::url_for_datacenter( $data_center_location );
return trailingslashit( $base ) . $endpoint;
}
}
@@ -0,0 +1,432 @@
<?php
namespace Gravity_Forms\Gravity_SMTP\Connectors\Types;
use Exception;
use Gravity_Forms\Gravity_SMTP\Connectors\Connector_Base;
use Gravity_Forms\Gravity_SMTP\Feature_Flags\Feature_Flag_Manager;
use Gravity_Forms\Gravity_SMTP\Gravity_SMTP;
use Gravity_Forms\Gravity_SMTP\Utils\AWS_Signature_Handler;
use Gravity_Forms\Gravity_Tools\Utils\Utils_Service_Provider;
/**
* Connector for Amazon SES
*
* @since 1.0
*/
class Connector_Amazon extends Connector_Base {
const SETTING_CLIENT_ID = 'access_key_id';
const SETTING_CLIENT_SECRET = 'secret_access_key';
const SETTING_REGION = 'region';
const REGION_US_EAST_N_VIRGINIA = 'us-east-1';
const REGION_US_EAST_OHIO = 'us-east-2';
const REGION_US_WEST_N_CALIFORNIA = 'us-west-1';
const REGION_US_WEST_OREGON = 'us-west-2';
const REGION_AFRICA_CAPE_TOWN = 'af-south-1';
const REGION_ASIA_PACIFIC_HONG_KONG = 'ap-east-1';
const REGION_ASIA_PACIFIC_JAKARTA = 'ap-southeast-3';
const REGION_ASIA_PACIFIC_MUMBAI = 'ap-south-1';
const REGION_ASIA_PACIFIC_OSAKA = 'ap-northeast-3';
const REGION_ASIA_PACIFIC_SEOUL = 'ap-northeast-2';
const REGION_ASIA_PACIFIC_SINGAPORE = 'ap-southeast-1';
const REGION_ASIA_PACIFIC_SYDNEY = 'ap-southeast-2';
const REGION_ASIA_PACIFIC_TOKYO = 'ap-northeast-1';
const REGION_CANADA_CENTRAL = 'ca-central-1';
const REGION_EUROPE_FRANKFURT = 'eu-central-1';
const REGION_EUROPE_IRELAND = 'eu-west-1';
const REGION_EUROPE_LONDON = 'eu-west-2';
const REGION_EUROPE_MILAN = 'eu-south-1';
const REGION_EUROPE_PARIS = 'eu-west-3';
const REGION_EUROPE_STOCKHOLM = 'eu-north-1';
const REGION_MIDDLE_EAST_BAHRAIN = 'me-south-1';
const REGION_SOUTH_AMERICA_SAO_PAULO = 'sa-east-1';
const API_ENDPOINT = '/v2/email/outbound-emails';
const ISO8601_BASIC = 'Ymd\THis\Z';
protected $name = 'amazon';
protected $title = 'Amazon SES';
protected $disabled = false;
protected $logo = 'AmazonAWS';
protected $full_logo = 'AmazonAWSFull';
public function get_description() {
return __( 'Amazon SES offers a reliable and cost-effective service for sending and receiving emails using your own domain. It leverages Amazons robust infrastructure, making it a powerful option for managing your email communication.', 'gravitysmtp' );
}
protected $sensitive_fields = array(
self::SETTING_CLIENT_ID,
self::SETTING_CLIENT_SECRET,
);
/**
* Sending logic.
*
* @since 1.0
*
* @return bool
*/
public function send() {
$to = $this->get_att( 'to', '' );
$subject = $this->get_att( 'subject', '' );
$message = $this->get_att( 'message', '' );
$headers = $this->get_parsed_headers( $this->get_att( 'headers', array() ) );
$attachments = $this->get_att( 'attachments', array() );
$from = $this->get_from( true );
$reply_to = $this->get_reply_to( true );
$source = $this->get_att( 'source' );
$params = $this->get_request_params();
$email = $this->email;
$this->reset_phpmailer();
if ( ! empty( $headers['content-type'] ) ) {
$headers['content-type'] = $this->get_att( 'content_type', $headers['content-type'] );
}
$this->set_email_log_data( $subject, $message, $to, empty( $from['name'] ) ? $from['email'] : sprintf( '%s <%s>', $from['name'], $from['email'] ), $headers, $attachments, $source, $params );
$this->logger->log( $email, 'started', __( 'Starting email send for Amazon SES connector.', 'gravitysmtp' ) );
$this->php_mailer->CharSet = 'UTF-8';
$this->php_mailer->setFrom( $from['email'], empty( $from['name'] ) ? '' : $from['name'] );
foreach ( $to->recipients() as $recipient ) {
if ( ! empty( $recipient->name() ) ) {
$this->php_mailer->addAddress( $recipient->email(), $recipient->name() );
} else {
$this->php_mailer->addAddress( $recipient->email() );
}
}
$this->php_mailer->Subject = $subject;
$this->php_mailer->Body = $message;
if ( ! empty( $headers['cc'] ) ) {
foreach ( $headers['cc']->recipients() as $recipient ) {
if ( ! empty( $recipient->name() ) ) {
$this->php_mailer->addCC( $recipient->email(), $recipient->name() );
} else {
$this->php_mailer->addCC( $recipient->email() );
}
}
}
if ( ! empty( $headers['bcc'] ) ) {
foreach ( $headers['bcc']->recipients() as $recipient ) {
if ( ! empty( $recipient->name() ) ) {
$this->php_mailer->addBCC( $recipient->email(), $recipient->name() );
} else {
$this->php_mailer->addBCC( $recipient->email() );
}
}
}
if ( ! empty( $attachments ) ) {
foreach ( $attachments as $attachment ) {
$this->php_mailer->addAttachment( $attachment );
}
}
if ( ! empty( $reply_to ) ) {
foreach ( $reply_to as $address ) {
if ( isset( $address['name'] ) ) {
$this->php_mailer->addReplyTo( $address['email'], $address['name'] );
} else {
$this->php_mailer->addReplyTo( $address['email'] );
}
}
}
if ( ! empty( $headers['content-type'] ) && strpos( $headers['content-type'], 'text/html' ) !== false ) {
$this->php_mailer->isHTML( true );
} else {
$this->php_mailer->isHTML( false );
$this->php_mailer->ContentType = 'text/plain';
}
$additional_headers = $this->get_filtered_message_headers();
if ( ! empty( $additional_headers ) ) {
foreach ( $additional_headers as $key => $value ) {
$value = str_replace( sprintf( '%s:', $key ), '', $value );
$this->php_mailer->addCustomHeader( $key, trim( $value ) );
}
}
if ( $this->is_test_mode() ) {
$this->events->update( array( 'status' => 'sandboxed' ), $email );
$this->logger->log( $email, 'sandboxed', __( 'Email sandboxed.', 'gravitysmtp' ) );
return true;
}
try {
$debug_atts = compact( 'to', 'from', 'subject', 'headers', 'source', 'attachments', 'reply_to' );
$this->debug_logger->log_debug( $this->wrap_debug_with_details( __FUNCTION__, $email, 'Attempting send with the following attributes: ' . json_encode( $debug_atts ) ) );
$raw = $this->get_raw_message();
/**
* @var AWS_Signature_Handler $signature_handler
*/
$signature_handler = Gravity_SMTP::$container->get( Utils_Service_Provider::AWS_SIGNATURE_HANDLER );
$body = array(
'Action' => 'SendRawEmail',
'Version' => '2010-12-01',
'RawMessage' => array(
'Data' => $raw,
),
);
$request_data = $signature_handler->get_request_data( $body, $this->get_setting( self::SETTING_CLIENT_ID, '' ), $this->get_setting( self::SETTING_CLIENT_SECRET, '' ), $this->get_setting( self::SETTING_REGION, self::REGION_US_EAST_N_VIRGINIA ) );
$response = wp_remote_post( $request_data['url'], array( 'headers' => $request_data['headers'], 'body' => $request_data['body'] ) );
$code = wp_remote_retrieve_response_code( $response );
$is_success = (int) $code === 200;
if ( ! $is_success ) {
$this->log_failure( $email, wp_remote_retrieve_body( $response ) );
return $email;
}
$this->events->update( array( 'status' => 'sent' ), $email );
$this->logger->log( $email, 'sent', __( 'Email successfully sent.', 'gravitysmtp' ) );
return true;
} catch ( Exception $e ) {
$this->log_failure( $email, $e->getMessage() );
$this->debug_logger->log_fatal( $this->wrap_debug_with_details( __FUNCTION__, $email, 'Failed to send: ' . $e->getMessage() ) );
return $email;
}
}
/**
* Logs an email send failure.
*
* @since 1.4.0
*
* @param string $email the email that failed
* @param string $error_message the error message
*/
private function log_failure( $email, $error_message ) {
$this->events->update( array( 'status' => 'failed' ), $email );
$this->logger->log( $email, 'failed', $error_message );
}
private function get_raw_message() {
$this->php_mailer->preSend();
$raw = $this->php_mailer->getSentMIMEMessage();
return base64_encode( $raw );
}
/**
* Reset the PHPMailer instance to prevent carryover from previous send.
*
* @since 1.0
*
* @return void
*/
private function reset_phpmailer() {
$this->php_mailer->clearCustomHeaders();
$this->php_mailer->clearAddresses();
$this->php_mailer->clearBCCs();
$this->php_mailer->clearCCs();
$this->php_mailer->clearAllRecipients();
$this->php_mailer->clearReplyTos();
$this->php_mailer->clearAttachments();
}
/**
* Connector data.
*
* @since 1.0
*
* @return array
*/
public function connector_data() {
return array(
self::SETTING_CLIENT_ID => $this->get_setting( self::SETTING_CLIENT_ID, '' ),
self::SETTING_CLIENT_SECRET => $this->get_setting( self::SETTING_CLIENT_SECRET, '' ),
self::SETTING_REGION => $this->get_setting( self::SETTING_REGION, self::REGION_US_EAST_N_VIRGINIA ),
self::SETTING_FROM_EMAIL => $this->get_setting( self::SETTING_FROM_EMAIL, '' ),
self::SETTING_FORCE_FROM_EMAIL => $this->get_setting( self::SETTING_FORCE_FROM_EMAIL, false ),
self::SETTING_FROM_NAME => $this->get_setting( self::SETTING_FROM_NAME, '' ),
self::SETTING_FORCE_FROM_NAME => $this->get_setting( self::SETTING_FORCE_FROM_NAME, false ),
self::SETTING_REPLY_TO_EMAIL => $this->get_setting( self::SETTING_REPLY_TO_EMAIL, '' ),
self::SETTING_FORCE_REPLY_TO_EMAIL => $this->get_setting( self::SETTING_FORCE_REPLY_TO_EMAIL, false ),
);
}
/**
* Settings fields.
*
* @since 1.0
*
* @return array
*/
public function settings_fields() {
return array(
'title' => esc_html__( 'Amazon SES Settings', 'gravitysmtp' ),
'description' => '',
'fields' => array_merge(
array(
array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'Configuration', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => array( 4, 0, 4, 0 ),
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
),
array(
'component' => 'LinkedHelpTextInput',
'external' => true,
'handle_change' => true,
'links' => array(
array(
'key' => 'link',
'props' => array(
'href' => 'https://aws.amazon.com/console/',
'size' => 'text-xs',
'target' => '_blank',
),
),
),
'props' => array(
'helpTextAttributes' => array(
'content' => esc_html__( 'Log in to your {{link}}AWS Console{{link}}, go to the IAM dashboard, and create a new access key for an IAM user with ses:SendEmail and ses:SendRawEmail permissions.', 'gravitysmtp' ),
'size' => 'text-xs',
'weight' => 'regular',
),
'labelAttributes' => array(
'label' => esc_html__( 'Access Key ID', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'medium',
),
'name' => self::SETTING_CLIENT_ID,
'spacing' => 4,
'size' => 'size-l',
'value' => $this->get_setting( self::SETTING_CLIENT_ID, '' ),
),
),
array(
'component' => 'Input',
'props' => array(
'labelAttributes' => array(
'label' => esc_html__( 'Secret Access Key', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'medium',
),
'name' => self::SETTING_CLIENT_SECRET,
'size' => 'size-l',
'spacing' => 6,
'value' => $this->get_setting( self::SETTING_CLIENT_SECRET, '' ),
),
),
array(
'component' => 'Select',
'props' => array(
'labelAttributes' => array(
'label' => esc_html__( 'Region', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'medium',
),
'name' => self::SETTING_REGION,
'size' => 'size-l',
'spacing' => 6,
'initialValue' => $this->get_setting( self::SETTING_REGION, self::REGION_US_EAST_N_VIRGINIA ),
'options' => $this->get_region_setting_options(),
),
),
array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'General Settings', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => 4,
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
),
),
$this->get_from_settings_fields(),
$this->get_reply_to_settings_fields(),
),
);
}
protected function get_region_setting_options() {
$region_options = array(
__( 'US East (N. Virginia)', 'gravitysmtp' ) => self::REGION_US_EAST_N_VIRGINIA,
__( 'US East (Ohio)', 'gravitysmtp' ) => self::REGION_US_EAST_OHIO,
__( 'US West (N. California)', 'gravitysmtp' ) => self::REGION_US_WEST_N_CALIFORNIA,
__( 'US West (Oregon)', 'gravitysmtp' ) => self::REGION_US_WEST_OREGON,
__( 'Africa (Cape Town)', 'gravitysmtp' ) => self::REGION_AFRICA_CAPE_TOWN,
__( 'Asia Pacific (Hong Kong)', 'gravitysmtp' ) => self::REGION_ASIA_PACIFIC_HONG_KONG,
__( 'Asia Pacific (Jakarta)', 'gravitysmtp' ) => self::REGION_ASIA_PACIFIC_JAKARTA,
__( 'Asia Pacific (Mumbai)', 'gravitysmtp' ) => self::REGION_ASIA_PACIFIC_MUMBAI,
__( 'Asia Pacific (Osaka)', 'gravitysmtp' ) => self::REGION_ASIA_PACIFIC_OSAKA,
__( 'Asia Pacific (Seoul)', 'gravitysmtp' ) => self::REGION_ASIA_PACIFIC_SEOUL,
__( 'Asia Pacific (Singapore)', 'gravitysmtp' ) => self::REGION_ASIA_PACIFIC_SINGAPORE,
__( 'Asia Pacific (Sydney)', 'gravitysmtp' ) => self::REGION_ASIA_PACIFIC_SYDNEY,
__( 'Asia Pacific (Tokyo)', 'gravitysmtp' ) => self::REGION_ASIA_PACIFIC_TOKYO,
__( 'Canada (Central)', 'gravitysmtp' ) => self::REGION_CANADA_CENTRAL,
__( 'Europe (Frankfurt)', 'gravitysmtp' ) => self::REGION_EUROPE_FRANKFURT,
__( 'Europe (Ireland)', 'gravitysmtp' ) => self::REGION_EUROPE_IRELAND,
__( 'Europe (London)', 'gravitysmtp' ) => self::REGION_EUROPE_LONDON,
__( 'Europe (Milan)', 'gravitysmtp' ) => self::REGION_EUROPE_MILAN,
__( 'Europe (Paris)', 'gravitysmtp' ) => self::REGION_EUROPE_PARIS,
__( 'Europe (Stockholm)', 'gravitysmtp' ) => self::REGION_EUROPE_STOCKHOLM,
__( 'Middle East (Bahrain)', 'gravitysmtp' ) => self::REGION_MIDDLE_EAST_BAHRAIN,
__( 'South America (São Paulo)', 'gravitysmtp' ) => self::REGION_SOUTH_AMERICA_SAO_PAULO,
);
$settings = array();
foreach ( $region_options as $name => $slug ) {
$settings[] = array(
'label' => $name,
'value' => $slug,
);
}
return $settings;
}
/**
* Get the unique data for this connector, merged with the default/common data for all
* connectors in the system.
*
* @since 1.0
*
* @return array
*/
protected function get_merged_data() {
$data = parent::get_merged_data();
$data['disabled'] = ! Feature_Flag_Manager::is_enabled( 'amazon_ses_integration' );
return $data;
}
public function is_configured() {
if ( ! $this->get_setting( self::SETTING_CLIENT_ID, '' ) || ! $this->get_setting( self::SETTING_CLIENT_SECRET, '' ) ) {
return false;
}
return true;
}
}
@@ -0,0 +1,363 @@
<?php
namespace Gravity_Forms\Gravity_SMTP\Connectors\Types;
use Gravity_Forms\Gravity_SMTP\Connectors\Connector_Base;
/**
* Connector for Brevo
*
* @since 1.0
*/
class Connector_Brevo extends Connector_Base {
const SETTING_API_KEY = 'api_key';
protected $name = 'brevo';
protected $title = 'Brevo';
protected $description = '';
protected $logo = 'Brevo';
protected $full_logo = 'BrevoFull';
protected $url = 'https://api.brevo.com/v3/smtp/email';
protected $sensitive_fields = array(
self::SETTING_API_KEY,
);
public function get_description() {
return esc_html__( 'Confidently send transactional emails with Brevo, formerly Sendinblue. With an impressive free plan, Brevo allows you to send up to 300 transactional emails a day! And for those who need to send more, simply pay for what you send. For more information on how to get started with Brevo, read our documentation.', 'gravitysmtp' );
}
/**
* Sends email via Brevo.
*
* @since 1.0
*
* @return int Returns the email ID.
*/
public function send() {
try {
$atts = $this->get_send_atts();
$source = $this->get_att( 'source' );
$params = $this->get_request_params();
$email = $this->email;
$this->set_email_log_data( $atts['subject'], $atts['message'], $atts['to'], $atts['from']['from'], $atts['headers'], $atts['attachments'], $source, $params );
$this->logger->log( $email, 'started', __( 'Starting email send for Brevo connector.', 'gravitysmtp' ) );
if ( $this->is_test_mode() ) {
$this->events->update( array( 'status' => 'sandboxed' ), $email );
$this->logger->log( $email, 'sandboxed', __( 'Email sandboxed.', 'gravitysmtp' ) );
return true;
}
$response = wp_safe_remote_post( $this->url, $params );
$is_success = in_array( (int) wp_remote_retrieve_response_code( $response ), array( 201, 202 ) );
if ( ! $is_success ) {
$this->log_failure( $email, wp_remote_retrieve_body( $response ) );
return $email;
}
$this->events->update( array( 'status' => 'sent' ), $email );
$this->logger->log( $email, 'sent', __( 'Email successfully sent.', 'gravitysmtp' ) );
return true;
} catch ( \Exception $e ) {
$this->log_failure( $email, $e->getMessage() );
return $email;
}
}
/**
* Get the request parameters for sending email through connector.
*
* @since 1.0
*
* @return array
*/
public function get_request_params() {
$atts = $this->get_send_atts();
$api_key = $this->get_setting( self::SETTING_API_KEY );
$body = array(
'sender' => array(
'name' => isset( $atts['from']['name'] ) ? $atts['from']['name'] : '',
'email' => $atts['from']['email'],
),
'to' => $atts['to']->as_array(),
'subject' => $atts['subject'],
);
$body['sender'] = array_filter( $body['sender'] );
// Setting content
$is_html = ! empty( $atts['headers']['content-type'] ) && strpos( $atts['headers']['content-type'], 'text/html' ) !== false;
if ( $is_html ) {
$body['htmlContent'] = $atts['message'];
} else {
$body['textContent'] = $atts['message'];
}
// Setting cc
if ( ! empty( $atts['headers']['cc'] ) ) {
$body['cc'] = $atts['headers']['cc']->as_array();
}
// Setting bcc
if ( ! empty( $atts['headers']['bcc'] ) ) {
$body['bcc'] = $atts['headers']['bcc']->as_array();
}
// Setting reply to
if ( ! empty( $atts['reply_to'] ) ) {
if ( isset( $atts['reply_to']['email'] ) ) {
$reply_to = $atts['reply_to'];
} else {
$reply_to = $atts['reply_to'][0];
}
$body['replyTo'] = array_filter( array(
'name' => isset( $reply_to['name'] ) ? $reply_to['name'] : '',
'email' => $reply_to['email'],
) );
}
// Setting attachments
if ( ! empty( $atts['attachments'] ) ) {
$body['attachment'] = $this->get_attachments( $atts['attachments'] );
}
$additional_headers = $this->get_filtered_message_headers();
if ( ! empty( $additional_headers ) ) {
$body['headers'] = $additional_headers;
}
return array(
'body' => json_encode( $body ),
'headers' => $this->get_request_headers( $api_key ),
);
}
/**
* Get the attributes for sending email.
*
* @since 1.0
*
* @return array
*/
protected function get_send_atts() {
$headers = $this->get_parsed_headers( $this->get_att( 'headers', array() ) );
if ( ! empty( $headers['content-type'] ) ) {
$headers['content-type'] = $this->get_att( 'content_type', $headers['content-type'] );
}
return array(
'to' => $this->get_att( 'to', '' ),
'subject' => $this->get_att( 'subject', '' ),
'message' => $this->get_att( 'message', '' ),
'headers' => $headers,
'attachments' => $this->get_att( 'attachments', array() ),
'from' => $this->get_from( true ),
'reply_to' => $this->get_reply_to( true ),
);
}
/**
* Gets a list of attachments, and returns them in a format that can be used by the API.
*
* @param array $attachments The list of attachments.
*
* @since 1.0
*
* @return array Returns an array of attachments. Each item with a 'content' and 'name' property.
*/
protected function get_attachments( $attachments ) {
$data = array();
foreach ( $attachments as $custom_name => $attachment ) {
try {
if ( is_file( $attachment ) && is_readable( $attachment ) ) {
$fileName = is_numeric( $custom_name ) ? basename( $attachment ) : $custom_name;
$content = base64_encode( file_get_contents( $attachment ) );
$data[] = array(
'name' => $fileName,
'content' => $content,
);
}
} catch ( \Exception $e ) {
continue;
}
}
return $data;
}
/**
* Gets the headers to be used in the API request.
*
* @since 1.0
*
* @return array Returns the header array to be passed to Brevo's API.
*/
protected function get_request_headers( $api_key ) {
return array(
'content-type' => 'application/json',
'accept' => 'application/json',
'api-key' => $api_key,
);
}
/**
* Logs an email send failure.
*
* @since 1.0
*
* @param string $email The email that failed.
* @param string $error_message The error message.
*/
private function log_failure( $email, $error_message ) {
$this->events->update( array( 'status' => 'failed' ), $email );
$this->logger->log( $email, 'failed', $error_message );
}
/**
* Connector data.
*
* @since 1.0
*
* @return array
*/
public function connector_data() {
return array(
self::SETTING_API_KEY => $this->get_setting( self::SETTING_API_KEY, '' ),
self::SETTING_FROM_EMAIL => $this->get_setting( self::SETTING_FROM_EMAIL, '' ),
self::SETTING_FORCE_FROM_EMAIL => $this->get_setting( self::SETTING_FORCE_FROM_EMAIL, false ),
self::SETTING_FROM_NAME => $this->get_setting( self::SETTING_FROM_NAME, '' ),
self::SETTING_FORCE_FROM_NAME => $this->get_setting( self::SETTING_FORCE_FROM_NAME, false ),
self::SETTING_REPLY_TO_EMAIL => $this->get_setting( self::SETTING_REPLY_TO_EMAIL, '' ),
self::SETTING_FORCE_REPLY_TO_EMAIL => $this->get_setting( self::SETTING_FORCE_REPLY_TO_EMAIL, false ),
);
}
/**
* Settings fields.
*
* @since 1.0
*
* @return array
*/
public function settings_fields() {
return array(
'title' => esc_html__( 'Brevo Settings', 'gravitysmtp' ),
'description' => '',
'fields' => array_merge(
array(
array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'Configuration', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => array( 4, 0, 4, 0 ),
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
),
array(
'component' => 'Input',
'props' => array(
'labelAttributes' => array(
'label' => esc_html__( 'API Key', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'medium',
),
'helpTextAttributes' => array(
'asHtml' => true,
/* translators: 1: opening anchor tag, 2: closing anchor tag */
'content' => sprintf( __( 'To generate an API key from Brevo, log in to your Brevo dashboard and navigate to the API section. %1$sCreate a new API key%2$s and ensure your %3$sAuthorized IPs settings%2$s are configured correctly.', 'gravitysmtp' ), '<a class="gform-link gform-typography--size-text-xs" href="https://app.brevo.com/settings/keys/api" target="_blank" rel="noopener noreferrer">', '</a>', '<a class="gform-link gform-typography--size-text-xs" href="https://help.brevo.com/hc/en-us/articles/5740111683858-Authorize-IP-addresses-for-API-calls-to-improve-security" target="_blank" rel="noopener noreferrer">' ),
'size' => 'text-xs',
'weight' => 'regular',
),
'name' => self::SETTING_API_KEY,
'size' => 'size-l',
'spacing' => 6,
'value' => $this->get_setting( self::SETTING_API_KEY, '' ),
),
),
array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'General Settings', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => 4,
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
),
),
$this->get_from_settings_fields(),
$this->get_reply_to_settings_fields(),
),
);
}
/**
* Determine if the API credentials are configured correctly.
*
* @since 1.0
*
* @return bool|\WP_Error Returns true if configured, or a WP_Error object if not.
*/
public function is_configured() {
$valid_api = $this->verify_api_key();
if ( is_wp_error( $valid_api ) ) {
self::$configured = $valid_api;
return $valid_api;
}
self::$configured = true;
return true;
}
/**
* Verify the API key with the API.
*
* @since 1.0
*
* @return true|\WP_Error
*/
private function verify_api_key() {
$api_key = $this->get_setting( self::SETTING_API_KEY );
$url = 'https://api.brevo.com/v3/smtp/templates?limit=1';
if ( empty( $api_key ) ) {
return new \WP_Error( 'missing_api_key', __( 'No API Key provided.', 'gravitysmtp' ) );
}
$response = wp_remote_get(
$url,
array(
'headers' => $this->get_request_headers( $api_key ),
)
);
if ( wp_remote_retrieve_response_code( $response ) != '200' ) {
return new \WP_Error( 'invalid_api_key', __( 'Invalid API Key provided.', 'gravitysmtp' ) );
}
return true;
}
}
@@ -0,0 +1,863 @@
<?php
namespace Gravity_Forms\Gravity_SMTP\Connectors\Types;
use Exception;
use Gravity_Forms\Gravity_SMTP\Connectors\Connector_Base;
use Gravity_Forms\Gravity_SMTP\Feature_Flags\Feature_Flag_Manager;
use Gravity_Forms\Gravity_SMTP\Models\Event_Model;
use WP_Error;
/**
* Connector for Cloudflare Email Service.
*
* @since 2.2.0
*/
class Connector_Cloudflare extends Connector_Base {
const SETTING_ACCOUNT_ID = 'account_id';
const SETTING_API_TOKEN = 'api_token';
protected $name = 'cloudflare';
protected $title = 'Cloudflare';
protected $description = '';
protected $logo = 'Cloudflare';
protected $full_logo = 'CloudflareFull';
protected $url = 'https://api.cloudflare.com/client/v4/accounts/%s/email/sending/send';
protected $sensitive_fields = array(
self::SETTING_API_TOKEN,
);
/**
* Get the connector description.
*
* @since 2.2.0
*
* @return string
*/
public function get_description() {
return esc_html__( 'Send transactional email through Cloudflare Email Service using a domain managed on Cloudflare DNS. Requires a paid Cloudflare plan.', 'gravitysmtp' );
}
/**
* Send the email through Cloudflare.
*
* @since 2.2.0
*
* @return bool|int
*/
public function send() {
$email = $this->email;
$atts = $this->get_send_atts();
$source = $this->get_att( 'source' );
/** @var Event_Model $events */
$events = $this->events;
try {
$request_body = $this->get_request_body( $atts );
if ( is_wp_error( $request_body ) ) {
return $this->log_failure( $email, $request_body->get_error_message() );
}
$params = $this->get_request_params_for_body( $request_body );
$this->set_email_log_data( $atts['subject'], $atts['message'], $atts['to'], $atts['from']['from'], $atts['headers'], $atts['attachments'], $source, $params );
$this->logger->log( $email, 'started', __( 'Starting email send for Cloudflare connector.', 'gravitysmtp' ) );
$this->debug_logger->log_debug(
$this->wrap_debug_with_details(
__FUNCTION__,
$email,
sprintf(
'Starting Cloudflare send with %1$d recipient(s) and payload size %2$d bytes.',
$this->get_recipient_count( $atts ),
strlen( $params['body'] )
)
)
);
if ( $this->is_test_mode() ) {
$events->update( array( 'status' => 'sandboxed' ), $email );
$this->logger->log( $email, 'sandboxed', __( 'Email sandboxed.', 'gravitysmtp' ) );
$this->debug_logger->log_debug( $this->wrap_debug_with_details( __FUNCTION__, $email, 'Test mode is enabled, sandboxing email.' ) );
return true;
}
$response = wp_safe_remote_post( $this->get_send_url(), $params );
if ( is_wp_error( $response ) ) {
return $this->log_failure( $email, $response->get_error_message() );
}
$response_code = (int) wp_remote_retrieve_response_code( $response );
$response_body = wp_remote_retrieve_body( $response );
$decoded_body = json_decode( $response_body, true );
$this->debug_logger->log_debug(
$this->wrap_debug_with_details(
__FUNCTION__,
$email,
sprintf( 'Cloudflare response (%1$d): %2$s', $response_code, $response_body )
)
);
if ( ! $this->is_successful_response( $response_code, $decoded_body ) ) {
return $this->log_failure( $email, $this->get_api_error_message( $response_code, $decoded_body, $response_body ) );
}
$events->update( array( 'status' => 'sent' ), $email );
$this->logger->log( $email, 'sent', __( 'Email successfully sent.', 'gravitysmtp' ) );
$this->debug_logger->log_debug( $this->wrap_debug_with_details( __FUNCTION__, $email, 'Email successfully sent.' ) );
return true;
} catch ( Exception $e ) {
return $this->log_failure( $email, $e->getMessage() );
}
}
/**
* Get the request parameters for sending email through Cloudflare.
*
* @since 2.2.0
*
* @return array
*/
public function get_request_params() {
$request_body = $this->get_request_body( $this->get_send_atts() );
if ( is_wp_error( $request_body ) ) {
return array();
}
return $this->get_request_params_for_body( $request_body );
}
/**
* Get the request parameters for a prepared Cloudflare body.
*
* @since 2.2.0
*
* @param array $request_body The request body.
*
* @return array
*/
protected function get_request_params_for_body( $request_body ) {
return array(
'body' => wp_json_encode( $request_body ),
'headers' => $this->get_request_headers( trim( (string) $this->get_setting( self::SETTING_API_TOKEN, '' ) ) ),
);
}
/**
* Get the attributes for sending email.
*
* @since 2.2.0
*
* @return array
*/
protected function get_send_atts() {
$headers = $this->get_parsed_headers( $this->get_att( 'headers', array() ) );
if ( ! empty( $headers['content-type'] ) ) {
$headers['content-type'] = $this->get_att( 'content_type', $headers['content-type'] );
}
return array(
'to' => $this->get_att( 'to', '' ),
'subject' => $this->get_att( 'subject', '' ),
'message' => $this->get_att( 'message', '' ),
'headers' => $headers,
'attachments' => $this->get_att( 'attachments', array() ),
'from' => $this->get_from( true ),
'reply_to' => $this->get_reply_to( true ),
);
}
/**
* Build the Cloudflare request body.
*
* @since 2.2.0
*
* @param array $atts The send attributes.
*
* @return array|WP_Error
*/
protected function get_request_body( $atts ) {
$account_id = trim( (string) $this->get_setting( self::SETTING_ACCOUNT_ID, '' ) );
$api_token = trim( (string) $this->get_setting( self::SETTING_API_TOKEN, '' ) );
if ( empty( $account_id ) ) {
return new WP_Error( 'missing_account_id', __( 'No Account ID provided.', 'gravitysmtp' ) );
}
if ( empty( $api_token ) ) {
return new WP_Error( 'missing_api_token', __( 'No API Token provided.', 'gravitysmtp' ) );
}
$request_body = array(
'from' => $this->format_address( $atts['from'] ),
'to' => $this->format_recipients( $atts['to'] ),
'subject' => $atts['subject'],
);
$is_html = ! empty( $atts['headers']['content-type'] ) && strpos( $atts['headers']['content-type'], 'text/html' ) !== false;
if ( $is_html ) {
$request_body['html'] = $atts['message'];
$request_body['text'] = $this->get_text_body( $atts['message'] );
} else {
$request_body['text'] = $atts['message'];
}
if ( empty( $request_body['html'] ) && empty( $request_body['text'] ) ) {
return new WP_Error( 'missing_email_body', __( 'Cloudflare requires either an HTML or plain text email body.', 'gravitysmtp' ) );
}
if ( ! empty( $atts['headers']['cc'] ) ) {
$request_body['cc'] = $this->format_recipients( $atts['headers']['cc'] );
}
if ( ! empty( $atts['headers']['bcc'] ) ) {
$request_body['bcc'] = $this->format_recipients( $atts['headers']['bcc'] );
}
if ( ! empty( $atts['reply_to'] ) ) {
$reply_to = isset( $atts['reply_to'][0] ) ? $atts['reply_to'][0] : $atts['reply_to'];
if ( ! empty( $reply_to['email'] ) ) {
$request_body['reply_to'] = $this->format_address( $reply_to );
}
}
$additional_headers = $this->get_filtered_message_headers();
if ( ! empty( $additional_headers ) ) {
$request_body['headers'] = array_map( 'strval', $additional_headers );
}
if ( ! empty( $atts['attachments'] ) ) {
$attachments = $this->get_attachments( $atts['attachments'] );
if ( ! empty( $attachments ) ) {
$request_body['attachments'] = $attachments;
}
}
return $request_body;
}
/**
* Format a recipient collection for Cloudflare.
*
* @since 2.2.0
*
* @param object $recipients The parsed recipient collection.
*
* @return array
*/
protected function format_recipients( $recipients ) {
$formatted = array();
foreach ( $recipients->as_array() as $recipient ) {
$formatted[] = $this->format_address( $recipient );
}
return $formatted;
}
/**
* Format an address array for Cloudflare.
*
* @since 2.2.0
*
* @param array $address The address data.
*
* @return array|string
*/
protected function format_address( $address ) {
if ( ! empty( $address['name'] ) ) {
$name = $address['name'];
// RFC 5322: quote display names containing specials so they aren't
// mis-parsed (e.g. parentheses treated as comments by Cloudflare).
if ( preg_match( '/[()<>\[\]:;@\\\\",.]/', $name ) ) {
$name = '"' . str_replace( array( '\\', '"' ), array( '\\\\', '\\"' ), $name ) . '"';
}
return sprintf( '%s <%s>', $name, $address['email'] );
}
return $address['email'];
}
/**
* Convert HTML content into a text fallback.
*
* @since 2.2.0
*
* @param string $message The message body.
*
* @return string
*/
protected function get_text_body( $message ) {
$text_body = wp_strip_all_tags( $message );
return preg_replace( "/([\r\n]{2,}|[\n]{2,}|[\r]{2,}|[\r\t]{2,}|[\n\t]{2,})/", "\n", $text_body );
}
/**
* Get attachments formatted for Cloudflare.
*
* @since 2.2.0
*
* @param array $attachments The attachments array.
*
* @return array
*/
protected function get_attachments( $attachments ) {
$data = array();
foreach ( $attachments as $custom_name => $attachment ) {
$file = false;
try {
if ( is_file( $attachment ) && is_readable( $attachment ) ) {
$file_name = is_numeric( $custom_name ) ? basename( $attachment ) : $custom_name;
$content_id = wp_hash( $attachment );
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- Local attachments must be base64 encoded for API connectors.
$file = file_get_contents( $attachment );
$mime_type = mime_content_type( $attachment );
$file_type = str_replace( ';', '', trim( $mime_type ) );
}
} catch ( Exception $e ) {
$file = false;
}
if ( $file === false ) {
continue;
}
$data[] = array(
// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode -- Local attachments must be base64 encoded for Cloudflare's API.
'content' => base64_encode( $file ),
'disposition' => 'attachment',
'filename' => $file_name,
'type' => $file_type,
'content_id' => $content_id,
);
}
return $data;
}
/**
* Determine if the Cloudflare response is successful.
*
* @since 2.2.0
*
* @param int $response_code The HTTP response code.
* @param array|null $decoded_body The decoded response body.
*
* @return bool
*/
protected function is_successful_response( $response_code, $decoded_body ) {
if ( $response_code >= 300 ) {
return false;
}
if ( ! is_array( $decoded_body ) ) {
return false;
}
return ! empty( $decoded_body['success'] );
}
/**
* Build an actionable API error message.
*
* @since 2.2.0
*
* @param int $response_code The HTTP response code.
* @param array|null $decoded_body The decoded response body.
* @param string $response_body The raw response body.
*
* @return string
*/
protected function get_api_error_message( $response_code, $decoded_body, $response_body ) {
$parts = array();
if ( 429 === $response_code ) {
$parts[] = __( 'Cloudflare rate limit exceeded.', 'gravitysmtp' );
} elseif ( $response_code >= 300 ) {
$parts[] = sprintf(
/* translators: %d: HTTP response code. */
__( 'Cloudflare API request failed with status code %d.', 'gravitysmtp' ),
$response_code
);
} else {
$parts[] = __( 'Cloudflare API reported an unsuccessful response.', 'gravitysmtp' );
}
if ( is_array( $decoded_body ) ) {
$error_parts = array();
if ( ! empty( $decoded_body['errors'] ) && is_array( $decoded_body['errors'] ) ) {
foreach ( $decoded_body['errors'] as $error ) {
$code = isset( $error['code'] ) ? $error['code'] : '';
$message = isset( $error['message'] ) ? $error['message'] : '';
if ( empty( $message ) ) {
continue;
}
$error_parts[] = empty( $code ) ? $message : sprintf( '[%1$s] %2$s', $code, $message );
}
}
if ( ! empty( $decoded_body['messages'] ) && is_array( $decoded_body['messages'] ) ) {
foreach ( $decoded_body['messages'] as $message ) {
$message_text = isset( $message['message'] ) ? $message['message'] : '';
if ( empty( $message_text ) ) {
continue;
}
$error_parts[] = $message_text;
}
}
if ( ! empty( $error_parts ) ) {
$parts[] = implode( ' ', $error_parts );
}
}
if ( empty( $decoded_body ) && ! empty( $response_body ) ) {
$parts[] = $response_body;
}
return implode( ' ', array_filter( $parts ) );
}
/**
* Get the send URL.
*
* @since 2.2.0
*
* @return string
*/
protected function get_send_url() {
return sprintf( $this->url, trim( (string) $this->get_setting( self::SETTING_ACCOUNT_ID, '' ) ) );
}
/**
* Get the recipient count for the message.
*
* @since 2.2.0
*
* @param array $atts The send attributes.
*
* @return int
*/
protected function get_recipient_count( $atts ) {
$count = count( $atts['to']->as_array() );
if ( ! empty( $atts['headers']['cc'] ) ) {
$count += count( $atts['headers']['cc']->as_array() );
}
if ( ! empty( $atts['headers']['bcc'] ) ) {
$count += count( $atts['headers']['bcc']->as_array() );
}
return $count;
}
/**
* Get the headers to be used in the API request.
*
* @since 2.2.0
*
* @param string $api_token The Cloudflare API token.
*
* @return array
*/
protected function get_request_headers( $api_token ) {
return array(
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . $api_token,
);
}
/**
* Log an email send failure.
*
* @since 2.2.0
*
* @param int $email The failed email event ID.
* @param string $error_message The error message.
*
* @return int
*/
protected function log_failure( $email, $error_message ) {
/** @var Event_Model $events */
$events = $this->events;
$events->update( array( 'status' => 'failed' ), $email );
$this->logger->log( $email, 'failed', $error_message );
$this->debug_logger->log_error( $this->wrap_debug_with_details( __FUNCTION__, $email, $error_message ) );
return $email;
}
/**
* Connector data.
*
* @since 2.2.0
*
* @return array
*/
public function connector_data() {
return array(
self::SETTING_ACCOUNT_ID => $this->get_setting( self::SETTING_ACCOUNT_ID, '' ),
self::SETTING_API_TOKEN => $this->get_setting( self::SETTING_API_TOKEN, '' ),
self::SETTING_FROM_EMAIL => $this->get_setting( self::SETTING_FROM_EMAIL, '' ),
self::SETTING_FORCE_FROM_EMAIL => $this->get_setting( self::SETTING_FORCE_FROM_EMAIL, false ),
self::SETTING_FROM_NAME => $this->get_setting( self::SETTING_FROM_NAME, '' ),
self::SETTING_FORCE_FROM_NAME => $this->get_setting( self::SETTING_FORCE_FROM_NAME, false ),
self::SETTING_REPLY_TO_EMAIL => $this->get_setting( self::SETTING_REPLY_TO_EMAIL, '' ),
self::SETTING_FORCE_REPLY_TO_EMAIL => $this->get_setting( self::SETTING_FORCE_REPLY_TO_EMAIL, false ),
);
}
/**
* Get the unique data for this connector, merged with the default/common data for all
* connectors in the system.
*
* @since 2.2.0
*
* @return array
*/
protected function get_merged_data() {
$data = parent::get_merged_data();
$data['disabled'] = ! Feature_Flag_Manager::is_enabled( 'cloudflare_integration' );
return $data;
}
/**
* Settings fields.
*
* @since 2.2.0
*
* @return array
*/
public function settings_fields() {
return array(
'title' => esc_html__( 'Cloudflare Settings', 'gravitysmtp' ),
'description' => '',
'fields' => array_merge(
array(
array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'Configuration', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => array( 4, 0, 4, 0 ),
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
),
array(
'component' => 'Input',
'props' => array(
'labelAttributes' => array(
'label' => esc_html__( 'Account ID', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'medium',
),
'helpTextAttributes' => array(
'asHtml' => true,
/* translators: 1: opening anchor tag, 2: closing anchor tag */
'content' => sprintf( __( 'Copy the Account ID from your Cloudflare dashboard. Your Default From Email must use a domain managed on Cloudflare DNS and onboarded in Cloudflare Email Service for Email Sending. Cloudflare adds the required sending records on the cf-bounce subdomain plus your domain DMARC record. See Cloudflare\'s %1$sdomain configuration docs%2$s for setup details.', 'gravitysmtp' ), '<a class="gform-link gform-typography--size-text-xs" href="https://developers.cloudflare.com/email-service/configuration/domains/" target="_blank" rel="noopener noreferrer">', '</a>' ),
'size' => 'text-xs',
'weight' => 'regular',
),
'name' => self::SETTING_ACCOUNT_ID,
'size' => 'size-l',
'spacing' => 6,
'value' => $this->get_setting( self::SETTING_ACCOUNT_ID, '' ),
),
),
array(
'component' => 'Input',
'props' => array(
'labelAttributes' => array(
'label' => esc_html__( 'API Token', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'medium',
),
'helpTextAttributes' => array(
'asHtml' => true,
/* translators: 1: opening anchor tag, 2: closing anchor tag */
'content' => sprintf( __( 'Create a Cloudflare API token with access to Cloudflare Email Service for the target account. For full setup verification, also grant Zone Read and Email Sending Read permissions. Cloudflare Email Service currently requires a paid Cloudflare plan. See Cloudflare\'s %1$semail sending docs%2$s for setup guidance.', 'gravitysmtp' ), '<a class="gform-link gform-typography--size-text-xs" href="https://developers.cloudflare.com/email-service/api/send-emails/rest-api/" target="_blank" rel="noopener noreferrer">', '</a>' ),
'size' => 'text-xs',
'weight' => 'regular',
),
'name' => self::SETTING_API_TOKEN,
'size' => 'size-l',
'spacing' => 6,
'value' => $this->get_setting( self::SETTING_API_TOKEN, '' ),
),
),
array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'General Settings', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => 4,
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
),
array(
'component' => 'Text',
'props' => array(
'content' => esc_html__( 'Set your Default From Email to a mailbox on a domain managed on Cloudflare DNS and onboarded in Cloudflare Email Service for Email Sending.', 'gravitysmtp' ),
'size' => 'text-xs',
'spacing' => 6,
'weight' => 'regular',
),
),
),
$this->get_from_settings_fields(),
$this->get_reply_to_settings_fields(),
),
);
}
/**
* Determine if the Cloudflare connector is configured.
*
* Validates credentials are present, then checks the Cloudflare API to
* confirm the From Email domain is an active zone on this account with
* Email Sending enabled.
*
* @since 2.2.0
*
* @return bool|WP_Error
*/
public function is_configured() {
$account_id = trim( (string) $this->get_setting( self::SETTING_ACCOUNT_ID, '' ) );
$api_token = trim( (string) $this->get_setting( self::SETTING_API_TOKEN, '' ) );
$from_email = trim( (string) $this->get_setting( self::SETTING_FROM_EMAIL, '' ) );
if ( empty( $account_id ) ) {
return new WP_Error( 'missing_account_id', __( 'No Account ID provided.', 'gravitysmtp' ) );
}
if ( empty( $api_token ) ) {
return new WP_Error( 'missing_api_token', __( 'No API Token provided.', 'gravitysmtp' ) );
}
if ( empty( $from_email ) ) {
return new WP_Error( 'missing_from_email', __( 'No Default From Email provided. Cloudflare requires a From Email on a domain managed in your Cloudflare account.', 'gravitysmtp' ) );
}
if ( ! is_email( $from_email ) ) {
return new WP_Error( 'invalid_from_email', __( 'The Default From Email is not a valid email address.', 'gravitysmtp' ) );
}
$from_domain = $this->get_email_domain( $from_email );
if ( empty( $from_domain ) ) {
return new WP_Error( 'invalid_from_domain', __( 'Could not determine a domain from the Default From Email.', 'gravitysmtp' ) );
}
$zone_id = $this->get_zone_id_for_domain( $from_domain, $account_id, $api_token );
if ( is_wp_error( $zone_id ) ) {
return $zone_id;
}
$subdomains = $this->get_sending_subdomains( $zone_id, $api_token );
if ( is_wp_error( $subdomains ) ) {
return new WP_Error(
'sending_status_check_failed',
sprintf(
/* translators: %s: sending domain. */
__( 'Could not verify Email Sending status for %s. Make sure your API token has Email Sending read permissions.', 'gravitysmtp' ),
$from_domain
)
);
}
if ( empty( $subdomains ) ) {
return new WP_Error(
'no_sending_subdomains',
sprintf(
/* translators: %s: sending domain. */
__( 'No sending subdomains are configured for %s. Onboard your domain in Cloudflare Email Service for Email Sending.', 'gravitysmtp' ),
$from_domain
)
);
}
$has_enabled = false;
foreach ( $subdomains as $subdomain ) {
if ( ! empty( $subdomain['enabled'] ) ) {
$has_enabled = true;
break;
}
}
if ( ! $has_enabled ) {
return new WP_Error(
'sending_not_enabled',
sprintf(
/* translators: %s: sending domain. */
__( 'Email Sending is not yet enabled for %s. Complete the domain onboarding in Cloudflare Email Service so that the required DNS records are verified.', 'gravitysmtp' ),
$from_domain
)
);
}
return true;
}
/**
* Get the domain portion of an email address.
*
* @since 2.2.0
*
* @param string $email The email address.
*
* @return string
*/
protected function get_email_domain( $email ) {
$at_position = strrpos( $email, '@' );
if ( $at_position === false ) {
return '';
}
return strtolower( substr( $email, $at_position + 1 ) );
}
/**
* Resolve the Cloudflare zone ID for a domain on this account.
*
* @since 2.2.0
*
* @param string $domain The sending domain.
* @param string $account_id The Cloudflare account ID.
* @param string $api_token The Cloudflare API token.
*
* @return string|WP_Error The zone ID, or WP_Error on failure.
*/
protected function get_zone_id_for_domain( $domain, $account_id, $api_token ) {
$url = add_query_arg(
array(
'name' => $domain,
'account.id' => $account_id,
),
'https://api.cloudflare.com/client/v4/zones'
);
$response = $this->cloudflare_api_get( $url, $api_token );
if ( is_wp_error( $response ) ) {
return $response;
}
if ( empty( $response['result'] ) || ! is_array( $response['result'] ) ) {
return new WP_Error( 'zone_not_found', __( 'No matching zone found on this Cloudflare account.', 'gravitysmtp' ) );
}
$zone = $response['result'][0];
if ( empty( $zone['id'] ) ) {
return new WP_Error( 'zone_not_found', __( 'No matching zone found on this Cloudflare account.', 'gravitysmtp' ) );
}
if ( isset( $zone['status'] ) && 'active' !== $zone['status'] ) {
return new WP_Error(
'zone_not_active',
sprintf(
/* translators: %s: zone status. */
__( 'Zone exists but is not active (status: %s).', 'gravitysmtp' ),
$zone['status']
)
);
}
return $zone['id'];
}
/**
* Get the sending subdomains for a zone.
*
* @since 2.2.0
*
* @param string $zone_id The Cloudflare zone ID.
* @param string $api_token The Cloudflare API token.
*
* @return array|WP_Error The subdomains array, or WP_Error on failure.
*/
protected function get_sending_subdomains( $zone_id, $api_token ) {
$url = sprintf( 'https://api.cloudflare.com/client/v4/zones/%s/email/sending/subdomains', $zone_id );
$response = $this->cloudflare_api_get( $url, $api_token );
if ( is_wp_error( $response ) ) {
return $response;
}
if ( ! isset( $response['result'] ) || ! is_array( $response['result'] ) ) {
return array();
}
return $response['result'];
}
/**
* Make a GET request to the Cloudflare API v4.
*
* @since 2.2.0
*
* @param string $url The full API URL.
* @param string $api_token The Cloudflare API token.
*
* @return array|WP_Error The decoded response body, or WP_Error on failure.
*/
protected function cloudflare_api_get( $url, $api_token ) {
$response = wp_safe_remote_get(
$url,
array(
'headers' => $this->get_request_headers( $api_token ),
'timeout' => 15,
)
);
if ( is_wp_error( $response ) ) {
return $response;
}
$response_code = (int) wp_remote_retrieve_response_code( $response );
$decoded_body = json_decode( wp_remote_retrieve_body( $response ), true );
if ( ! $this->is_successful_response( $response_code, $decoded_body ) ) {
return new WP_Error(
'cloudflare_api_error',
$this->get_api_error_message( $response_code, $decoded_body, wp_remote_retrieve_body( $response ) )
);
}
return $decoded_body;
}
}
@@ -0,0 +1,387 @@
<?php
namespace Gravity_Forms\Gravity_SMTP\Connectors\Types;
use Gravity_Forms\Gravity_SMTP\Connectors\Connector_Base;
use Gravity_Forms\Gravity_SMTP\Feature_Flags\Feature_Flag_Manager;
/**
* Connector for Elastic Email
*
* @since 1.0
*/
class Connector_Elastic_Email extends Connector_Base {
const SETTING_API_KEY = 'api_key';
protected $name = 'elastic_email';
protected $title = 'Elastic Email';
protected $logo = 'ElasticEmail';
protected $full_logo = 'ElasticEmailFull';
protected $url = 'https://api.elasticemail.com/v4';
protected $sensitive_fields = array(
self::SETTING_API_KEY,
);
public function get_description() {
return esc_html__( 'Elastic Email is a high-performance email platform offering both marketing and transactional email solutions. With a free plan for up to 100 daily emails and affordable paid options, Elastic Email provides detailed analytics and automation tools.', 'gravitysmtp' );
}
/**
* Sends email via Elastic Email.
*
* @since 1.0
*
* @return int Returns the email ID.
*/
public function send() {
try {
$atts = $this->get_send_atts();
$source = $this->get_att( 'source' );
$params = $this->get_request_params();
$email = $this->email;
$this->set_email_log_data( $atts['subject'], $atts['message'], $atts['to'], $atts['from']['from'], $atts['headers'], $atts['attachments'], $source, $params );
$this->logger->log( $email, 'started', __( 'Starting email send for Elastic Email connector.', 'gravitysmtp' ) );
if ( $this->is_test_mode() ) {
$this->events->update( array( 'status' => 'sandboxed' ), $email );
$this->logger->log( $email, 'sandboxed', __( 'Email sandboxed.', 'gravitysmtp' ) );
return true;
}
$response = wp_safe_remote_post( $this->url . '/emails/transactional', $params );
$is_success = in_array( (int) wp_remote_retrieve_response_code( $response ), array( 200, 201, 202 ) );
if ( ! $is_success ) {
$this->log_failure( $email, wp_remote_retrieve_body( $response ) );
return $email;
}
$this->events->update( array( 'status' => 'sent' ), $email );
$this->logger->log( $email, 'sent', __( 'Email successfully sent.', 'gravitysmtp' ) );
return true;
} catch ( \Exception $e ) {
$this->log_failure( $email, $e->getMessage() );
return $email;
}
}
/**
* Get the request parameters for sending email through connector.
*
* @since 1.0
*
* @return array
*/
public function get_request_params() {
$atts = $this->get_send_atts();
$api_key = $this->get_setting( self::SETTING_API_KEY );
$is_html = ! empty( $atts['headers']['content-type'] ) && strpos( $atts['headers']['content-type'], 'text/html' ) !== false;
$from_email = $atts['from']['email'];
if ( ! empty( $atts['from']['name'] ) ) {
$from_email = sprintf( '%s <%s>', $atts['from']['name'], $atts['from']['email'] );
}
$body = array(
'Recipients' => array(
'To' => array(),
),
'Content' => array(
'From' => $from_email,
'Subject' => $atts['subject'],
'Body' => array(
array(
'Charset' => 'utf-8',
'Content' => $atts['message'],
'ContentType' => $is_html ? 'HTML' : 'PlainText',
),
),
),
);
foreach ( $atts['to']->as_array() as $to_value ) {
$body['Recipients']['To'][] = $to_value['email'];
}
// Setting cc
if ( ! empty( $atts['headers']['cc'] ) ) {
$body['Recipients']['CC'] = array();
foreach ( $atts['headers']['cc']->as_array() as $cc_value ) {
$body['Recipients']['CC'][] = $cc_value['email'];
}
}
// Setting bcc
if ( ! empty( $atts['headers']['bcc'] ) ) {
$body['Recipients']['BCC'] = array();
foreach ( $atts['headers']['bcc']->as_array() as $bcc_value ) {
$body['Recipients']['BCC'][] = $bcc_value['email'];
}
}
// Setting reply to
if ( ! empty( $atts['reply_to'] ) ) {
if ( isset( $atts['reply_to']['email'] ) ) {
$reply_to = $atts['reply_to'];
} else {
$reply_to = $atts['reply_to'][0];
}
$body['Content']['ReplyTo'] = $reply_to['email'];
}
// Setting attachments
if ( ! empty( $atts['attachments'] ) ) {
$body['Content']['Attachments'] = $this->get_attachments( $atts['attachments'] );
}
return array(
'body' => json_encode( $body ),
'headers' => $this->get_request_headers( $api_key ),
);
}
/**
* Get the attributes for sending email.
*
* @since 1.0
*
* @return array
*/
protected function get_send_atts() {
$headers = $this->get_parsed_headers( $this->get_att( 'headers', array() ) );
if ( ! empty( $headers['content-type'] ) ) {
$headers['content-type'] = $this->get_att( 'content_type', $headers['content-type'] );
}
return array(
'to' => $this->get_att( 'to', '' ),
'subject' => $this->get_att( 'subject', '' ),
'message' => $this->get_att( 'message', '' ),
'headers' => $headers,
'attachments' => $this->get_att( 'attachments', array() ),
'from' => $this->get_from( true ),
'reply_to' => $this->get_reply_to( true ),
);
}
/**
* Gets a list of attachments, and returns them in a format that can be used by the API.
*
* @param array $attachments The list of attachments.
*
* @since 1.0
*
* @return array Returns an array of attachments. Each item with a 'content' and 'name' property.
*/
protected function get_attachments( $attachments ) {
$data = array();
foreach ( $attachments as $custom_name => $attachment ) {
try {
if ( is_file( $attachment ) && is_readable( $attachment ) ) {
$fileName = is_numeric( $custom_name ) ? basename( $attachment ) : $custom_name;
$content = base64_encode( file_get_contents( $attachment ) );
$data[] = array(
'Name' => $fileName,
'BinaryContent' => $content,
'ContentType' => mime_content_type( $attachment ),
'Size' => filesize( $attachment ),
);
}
} catch ( \Exception $e ) {
continue;
}
}
return $data;
}
/**
* Gets the headers to be used in the API request.
*
* @since 1.0
*
* @return array Returns the header array to be passed to Elastic Email's API.
*/
protected function get_request_headers( $api_key ) {
return array(
'content-type' => 'application/json',
'accept' => 'application/json',
'X-ElasticEmail-ApiKey' => $api_key,
);
}
/**
* Logs an email send failure.
*
* @since 1.0
*
* @param string $email The email that failed.
* @param string $error_message The error message.
*/
private function log_failure( $email, $error_message ) {
$this->events->update( array( 'status' => 'failed' ), $email );
$this->logger->log( $email, 'failed', $error_message );
}
/**
* Connector data.
*
* @since 1.0
*
* @return array
*/
public function connector_data() {
return array(
self::SETTING_API_KEY => $this->get_setting( self::SETTING_API_KEY, '' ),
self::SETTING_FROM_EMAIL => $this->get_setting( self::SETTING_FROM_EMAIL, '' ),
self::SETTING_FORCE_FROM_EMAIL => $this->get_setting( self::SETTING_FORCE_FROM_EMAIL, false ),
self::SETTING_FROM_NAME => $this->get_setting( self::SETTING_FROM_NAME, '' ),
self::SETTING_FORCE_FROM_NAME => $this->get_setting( self::SETTING_FORCE_FROM_NAME, false ),
self::SETTING_REPLY_TO_EMAIL => $this->get_setting( self::SETTING_REPLY_TO_EMAIL, '' ),
self::SETTING_FORCE_REPLY_TO_EMAIL => $this->get_setting( self::SETTING_FORCE_REPLY_TO_EMAIL, false ),
);
}
/**
* Settings fields.
*
* @since 1.0
*
* @return array
*/
public function settings_fields() {
return array(
'title' => esc_html__( 'Elastic Email Settings', 'gravitysmtp' ),
'description' => '',
'fields' => array_merge(
array(
array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'Configuration', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => array( 4, 0, 4, 0 ),
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
),
array(
'component' => 'Input',
'props' => array(
'labelAttributes' => array(
'label' => esc_html__( 'API Key', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'medium',
),
'helpTextAttributes' => array(
'asHtml' => true,
/* translators: 1: opening anchor tag, 2: closing anchor tag */
'content' => sprintf( __( 'To generate an API key from Elastic Email, log in to your Elastic Email dashboard and navigate to the API section. %1$sCreate a new API key%2$s and ensure your %3$sdomain settings%2$s are configured correctly.', 'gravitysmtp' ), '<a class="gform-link gform-typography--size-text-xs" href="https://app.elasticemail.com/api/settings/create-api" target="_blank" rel="noopener noreferrer">', '</a>', '<a class="gform-link gform-typography--size-text-xs" href="https://app.elasticemail.com/api/settings/domains" target="_blank" rel="noopener noreferrer">' ),
'size' => 'text-xs',
'weight' => 'regular',
),
'name' => self::SETTING_API_KEY,
'size' => 'size-l',
'spacing' => 6,
'value' => $this->get_setting( self::SETTING_API_KEY, '' ),
),
),
array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'General Settings', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => 4,
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
),
),
$this->get_from_settings_fields(),
$this->get_reply_to_settings_fields(),
),
);
}
/**
* Determine if the API credentials are configured correctly.
*
* @since 1.0
*
* @return bool|\WP_Error Returns true if configured, or a WP_Error object if not.
*/
public function is_configured() {
$valid_api = $this->verify_api_key();
if ( is_wp_error( $valid_api ) ) {
self::$configured = $valid_api;
return $valid_api;
}
self::$configured = true;
return true;
}
/**
* Verify the API key with the API.
*
* @since 1.0
*
* @return true|\WP_Error
*/
private function verify_api_key() {
$api_key = $this->get_setting( self::SETTING_API_KEY );
$url = $this->url . '/statistics?from=' . date( 'Y-m-d h:i:s', time() );
if ( empty( $api_key ) ) {
return new \WP_Error( 'missing_api_key', __( 'No API Key provided.', 'gravitysmtp' ) );
}
$headers = $this->get_request_headers( $api_key );
$response = wp_remote_get(
$url,
array(
'headers' => $headers,
)
);
if ( wp_remote_retrieve_response_code( $response ) != '200' ) {
return new \WP_Error( 'invalid_api_key', __( 'Invalid API Key provided.', 'gravitysmtp' ) );
}
return true;
}
/**
* Get the unique data for this connector, merged with the default/common data for all
* connectors in the system.
*
* @since 1.0
*
* @return array
*/
protected function get_merged_data() {
$data = parent::get_merged_data();
$data['disabled'] = ! Feature_Flag_Manager::is_enabled( 'elasticemail_integration' );
return $data;
}
}
@@ -0,0 +1,409 @@
<?php
namespace Gravity_Forms\Gravity_SMTP\Connectors\Types;
use Exception;
use Gravity_Forms\Gravity_SMTP\Connectors\Connector_Base;
use Gravity_Forms\Gravity_SMTP\Feature_Flags\Feature_Flag_Manager;
use WP_Error;
/**
* Connector for Emailit
*
* @since 1.0
*/
class Connector_Emailit extends Connector_Base {
const SETTING_API_KEY = 'api_key';
protected $name = 'emailit';
protected $title = 'Emailit';
protected $logo = 'Emailit';
protected $full_logo = 'EmailitFull';
protected $url = 'https://api.emailit.com/v2';
protected $sensitive_fields = array(
self::SETTING_API_KEY,
);
/**
* Get the connector description.
*
* @since 1.0
*
* @return string The connector description.
*/
public function get_description() {
return esc_html__( 'Simple and no-subscription based API for sending transactional and marketing emails.', 'gravitysmtp' );
}
/**
* Sends email via Emailit.
*
* @since 1.0
*
* @return true|int True on success, int (email ID) on failure to trigger retry.
*/
public function send() {
try {
$atts = $this->get_send_atts();
$source = $this->get_att( 'source' );
$params = $this->get_request_params();
$email = $this->email;
$this->set_email_log_data( $atts['subject'], $atts['message'], $atts['to'], $atts['from']['from'], $atts['headers'], $atts['attachments'], $source, $params );
$this->logger->log( $email, 'started', __( 'Starting email send for Emailit connector.', 'gravitysmtp' ) );
if ( $this->is_test_mode() ) {
$this->events->update( array( 'status' => 'sandboxed' ), $email );
$this->logger->log( $email, 'sandboxed', __( 'Email sandboxed.', 'gravitysmtp' ) );
return true;
}
$response = wp_safe_remote_post( $this->url . '/emails', $params );
$is_success = in_array( (int) wp_remote_retrieve_response_code( $response ), array( 200, 201, 202 ) );
if ( ! $is_success ) {
$this->log_failure( $email, wp_remote_retrieve_body( $response ) );
return $email;
}
$this->events->update( array( 'status' => 'sent' ), $email );
$this->logger->log( $email, 'sent', __( 'Email successfully sent.', 'gravitysmtp' ) );
return true;
} catch ( Exception $e ) {
$this->log_failure( $email, $e->getMessage() );
return $email;
}
}
/**
* Get the request parameters for sending email through connector.
*
* @since 1.0
*
* @return array
*/
public function get_request_params() {
$atts = $this->get_send_atts();
$api_key = $this->get_setting( self::SETTING_API_KEY );
$message = array(
'to' => array(),
'from' => ! empty( $atts['from']['name'] ) ? sprintf( '%s <%s>', $atts['from']['name'], $atts['from']['email'] ) : $atts['from']['email'],
'subject' => $atts['subject'],
);
$to_value = array();
foreach ( $atts['to']->as_array() as $recipient ) {
if ( ! empty( $recipient['name'] ) ) {
$to_value[] = sprintf( '%s <%s>', $recipient['name'], $recipient['email'] );
} else {
$to_value[] = $recipient['email'];
}
}
$message['to'] = $to_value;
// Setting content
$is_html = ! empty( $atts['headers']['content-type'] ) && strpos( $atts['headers']['content-type'], 'text/html' ) !== false;
if ( $is_html ) {
$message['html'] = $atts['message'];
// Strip tags from plaintext
$text_body = wp_strip_all_tags( $atts['message'] );
// Remove leftover double-linebreaks from plaintext.
$text_body = preg_replace( "/([\r\n]{2,}|[\n]{2,}|[\r]{2,}|[\r\t]{2,}|[\n\t]{2,})/", "\n", $text_body );
$message['text'] = $text_body;
} else {
$message['text'] = $atts['message'];
}
// Setting cc
if ( ! empty( $atts['headers']['cc'] ) ) {
$cc_headers = array();
foreach ( $atts['headers']['cc']->as_array() as $cc_value ) {
$cc_headers[] = ! empty( $cc_value['name'] ) ? sprintf( '%s <%s>', $cc_value['name'], $cc_value['email'] ) : $cc_value['email'];
}
$message['cc'] = $cc_headers;
}
// Setting Bcc
if ( ! empty( $atts['headers']['bcc'] ) ) {
$bcc_headers = array();
foreach ( $atts['headers']['bcc']->as_array() as $bcc_value ) {
$bcc_headers[] = ! empty( $bcc_value['name'] ) ? sprintf( '%s <%s>', $bcc_value['name'], $bcc_value['email'] ) : $bcc_value['email'];
}
$message['bcc'] = $bcc_headers;
}
// Setting reply to
if ( ! empty( $atts['reply_to'] ) ) {
if ( isset( $atts['reply_to']['email'] ) ) {
$reply_to = $atts['reply_to'];
} else {
$reply_to = $atts['reply_to'][0];
}
$message['reply_to'] = $reply_to['email'];
}
// Setting attachments
if ( ! empty( $atts['attachments'] ) ) {
$message['attachments'] = $this->get_attachments( $atts['attachments'] );
}
return array(
'body' => json_encode( $message ),
'headers' => $this->get_request_headers( $api_key ),
);
}
/**
* Get the attributes for sending email.
*
* @since 1.0
*
* @return array
*/
protected function get_send_atts() {
$headers = $this->get_parsed_headers( $this->get_att( 'headers', array() ) );
if ( ! empty( $headers['content-type'] ) ) {
$headers['content-type'] = $this->get_att( 'content_type', $headers['content-type'] );
}
return array(
'to' => $this->get_att( 'to', '' ),
'subject' => $this->get_att( 'subject', '' ),
'message' => $this->get_att( 'message', '' ),
'headers' => $headers,
'attachments' => $this->get_att( 'attachments', array() ),
'from' => $this->get_from( true ),
'reply_to' => $this->get_reply_to( true ),
);
}
/**
* Gets a list of attachments, and returns them in a format that can be used by the API.
*
* @param array $attachments the list of attachments
*
* @since 1.0
*
* @return array Returns an array of attachments. Each item with a 'content' and 'name' property.
*/
protected function get_attachments( $attachments ) {
$data = array();
foreach ( $attachments as $custom_name => $attachment ) {
try {
if ( is_file( $attachment ) && is_readable( $attachment ) ) {
$fileName = is_numeric( $custom_name ) ? basename( $attachment ) : $custom_name;
$content = base64_encode( file_get_contents( $attachment ) );
$data[] = array(
'filename' => $fileName,
'content' => $content,
'content_type' => mime_content_type( $attachment ),
);
}
} catch ( Exception $e ) {
continue;
}
}
return $data;
}
/**
* Gets the headers to be used in the API request.
*
* @since 1.0
*
* @param string $api_key The API key for authorization.
*
* @return array returns the header array to be passed to Emailit's API
*/
protected function get_request_headers( $api_key ) {
return array(
'content-type' => 'application/json',
'Authorization' => 'Bearer ' . $api_key,
);
}
/**
* Logs an email send failure.
*
* @since 1.0
*
* @param string $email the email that failed
* @param string $error_message the error message
*
* @return void
*/
private function log_failure( $email, $error_message ) {
$this->events->update( array( 'status' => 'failed' ), $email );
$this->logger->log( $email, 'failed', $error_message );
}
/**
* Connector data.
*
* @since 1.0
*
* @return array
*/
public function connector_data() {
return array(
self::SETTING_API_KEY => $this->get_setting( self::SETTING_API_KEY, '' ),
self::SETTING_FROM_EMAIL => $this->get_setting( self::SETTING_FROM_EMAIL, '' ),
self::SETTING_FORCE_FROM_EMAIL => $this->get_setting( self::SETTING_FORCE_FROM_EMAIL, false ),
self::SETTING_FROM_NAME => $this->get_setting( self::SETTING_FROM_NAME, '' ),
self::SETTING_FORCE_FROM_NAME => $this->get_setting( self::SETTING_FORCE_FROM_NAME, false ),
self::SETTING_REPLY_TO_EMAIL => $this->get_setting( self::SETTING_REPLY_TO_EMAIL, '' ),
self::SETTING_FORCE_REPLY_TO_EMAIL => $this->get_setting( self::SETTING_FORCE_REPLY_TO_EMAIL, false ),
);
}
/**
* Settings fields.
*
* @since 1.0
*
* @return array
*/
public function settings_fields() {
return array(
'title' => esc_html__( 'Emailit Settings', 'gravitysmtp' ),
'description' => '',
'fields' => array_merge(
array(
array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'Configuration', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => array( 4, 0, 4, 0 ),
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
),
array(
'component' => 'Input',
'props' => array(
'labelAttributes' => array(
'label' => esc_html__( 'API Key', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'medium',
),
'helpTextAttributes' => array(
'asHtml' => true,
/* translators: 1: opening anchor tag, 2: closing anchor tag */
'content' => __( 'To generate an API key from Emailit, log in to your Emailit dashboard and navigate to the Credentials section and then generate your API key.', 'gravitysmtp' ),
'size' => 'text-xs',
'weight' => 'regular',
),
'name' => self::SETTING_API_KEY,
'size' => 'size-l',
'spacing' => 6,
'value' => $this->get_setting( self::SETTING_API_KEY, '' ),
),
),
array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'General Settings', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => 4,
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
),
),
$this->get_from_settings_fields(),
$this->get_reply_to_settings_fields(),
),
);
}
/**
* Determine if the API credentials are configured correctly.
*
* @since 1.0
*
* @return bool|WP_Error returns true if configured, or a WP_Error object if not
*/
public function is_configured() {
$valid_api = $this->verify_api_key();
if ( is_wp_error( $valid_api ) ) {
self::$configured = $valid_api;
return $valid_api;
}
self::$configured = true;
return true;
}
/**
* Verify the API key with the API.
*
* @since 1.0
*
* @return true|WP_Error
*/
private function verify_api_key() {
$api_key = $this->get_setting( self::SETTING_API_KEY, '' );
$url = $this->url . '/domains';
if ( empty( $api_key ) ) {
return new WP_Error( 'missing_api_key', __( 'No API Key provided.', 'gravitysmtp' ) );
}
$response = wp_remote_get(
$url,
array(
'headers' => $this->get_request_headers( $api_key ),
)
);
if ( wp_remote_retrieve_response_code( $response ) != '200' ) {
return new WP_Error( 'invalid_api_key', __( 'Invalid API Key provided.', 'gravitysmtp' ) );
}
return true;
}
/**
* Get the unique data for this connector, merged with the default/common data for all
* connectors in the system.
*
* @since 1.0
*
* @return array
*/
protected function get_merged_data() {
$data = parent::get_merged_data();
$data['disabled'] = ! Feature_Flag_Manager::is_enabled( 'emailit_integration' );
return $data;
}
}
@@ -0,0 +1,596 @@
<?php
namespace Gravity_Forms\Gravity_SMTP\Connectors\Types;
use Gravity_Forms\Gravity_SMTP\Connectors\Connector_Base;
use Gravity_Forms\Gravity_SMTP\Utils\Recipient_Collection;
/**
* Connector for Generic/Custom SMTP integration.
*
* @since 1.0
*/
class Connector_Generic extends Connector_Base {
const SETTING_HOST = 'host';
const SETTING_PORT = 'port';
const SETTING_AUTH = 'auth';
const SETTING_USERNAME = 'username';
const SETTING_PASSWORD = 'password';
const SETTING_ENCRYPTION_TYPE = 'encryption_type';
const SETTING_AUTO_TLS = 'auto_tls';
const SETTING_USE_RETURN_PATH = 'use_return_path';
protected $name = 'generic';
protected $title = 'Custom SMTP';
protected $disabled = false;
protected $description = '';
protected $logo = 'CustomSMTP';
protected $full_logo = 'CustomSMTPFull';
public function get_description() {
return __( "Use our Custom SMTP feature to easily connect to any SMTP server. If you don't want to use one of Gravity SMTP's built-in integrations, with Custom SMTP you can sync with a huge array of services that can reliably send your site's emails. For more information on how to get started with Custom SMTP, read our documentation.", 'gravitysmtp' );
}
protected $sensitive_fields = array(
self::SETTING_PASSWORD,
);
/**
* Sending logic.
*
* @since 1.0
*
* @return bool
*/
public function send() {
try {
/**
* @var Recipient_Collection $to
*/
$to = $this->get_att( 'to', '' );
$subject = $this->get_att( 'subject', '' );
$message = $this->get_att( 'message', '' );
$headers = $this->get_parsed_headers( $this->get_att( 'headers', array() ) );
$attachments = $this->get_att( 'attachments', array() );
$from = $this->get_from( true );
$reply_to = $this->get_reply_to( true );
$source = $this->get_att( 'source' );
$params = array( 'body' => array( __( 'Body is not stored for Custom SMTP events.', 'gravitysmtp' ) ), 'headers' => $headers );
$email = $this->email;
if ( ! empty( $headers['content-type'] ) ) {
$headers['content-type'] = $this->get_att( 'content_type', $headers['content-type'] );
}
$this->set_email_log_data( $subject, $message, $to, empty( $from['name'] ) ? $from['email'] : sprintf( '%s <%s>', $from['name'], $from['email'] ), $headers, $attachments, $source, $params );
$this->debug_logger->log_debug( $this->wrap_debug_with_details( __FUNCTION__, $email, 'Starting email send with Custom SMTP connector.' ) );
$this->reset_phpmailer();
$this->configure_phpmailer();
$this->php_mailer->setFrom( $from['email'], empty( $from['name'] ) ? '' : $from['name'] );
$this->debug_logger->log_debug( $this->wrap_debug_with_details( __FUNCTION__, $email, sprintf( 'Using From Name: %s, From Email: %s', empty( $from['name'] ) ? '' : $from['name'], $from['email'] ) ) );
foreach( $to->recipients() as $recipient ) {
if ( ! empty( $recipient->name() ) ) {
$this->php_mailer->addAddress( $recipient->email(), $recipient->name() );
} else {
$this->php_mailer->addAddress( $recipient->email() );
}
}
$this->debug_logger->log_debug( $this->wrap_debug_with_details( __FUNCTION__, $email, 'Email recipients: ' . json_encode( $to->as_array() ) ) );
$this->php_mailer->Subject = $subject;
$this->debug_logger->log_debug( $this->wrap_debug_with_details( __FUNCTION__, $email, 'Email subject: ' . $subject ) );
$this->php_mailer->Body = $message;
$this->debug_logger->log_debug( $this->wrap_debug_with_details( __FUNCTION__, $email, 'Email message: ' . esc_html( $message ) ) );
if ( ! empty( $headers['cc'] ) ) {
$this->debug_logger->log_debug( $this->wrap_debug_with_details( __FUNCTION__, $email, 'Email CC: ' . json_encode( $headers['cc']->as_array() ) ) );
foreach ( $headers['cc']->recipients() as $recipient ) {
if ( ! empty( $recipient->name() ) ) {
$this->php_mailer->addCC( $recipient->email(), $recipient->name() );
} else {
$this->php_mailer->addCC( $recipient->email() );
}
}
}
if ( ! empty( $headers['bcc'] ) ) {
$this->debug_logger->log_debug( $this->wrap_debug_with_details( __FUNCTION__, $email, 'Email BCC: ' . json_encode( $headers['bcc']->as_array() ) ) );
foreach ( $headers['bcc']->recipients() as $recipient ) {
if ( ! empty( $recipient->name() ) ) {
$this->php_mailer->addBCC( $recipient->email(), $recipient->name() );
} else {
$this->php_mailer->addBCC( $recipient->email() );
}
}
}
if ( ! empty( $attachments ) ) {
$this->debug_logger->log_debug( $this->wrap_debug_with_details( __FUNCTION__, $email, 'Email attachments: ' . json_encode( $attachments ) ) );
foreach ( $attachments as $attachment ) {
$this->php_mailer->addAttachment( $attachment );
}
}
if ( ! empty( $reply_to ) ) {
$this->debug_logger->log_debug( $this->wrap_debug_with_details( __FUNCTION__, $email, 'Email reply_to: ' . json_encode( $reply_to ) ) );
foreach( $reply_to as $address ) {
if ( isset( $address['name'] ) ) {
$this->php_mailer->addReplyTo( $address['email'], $address['name'] );
} else {
$this->php_mailer->addReplyTo( $address['email'] );
}
}
}
if ( ! empty( $headers['content-type'] ) && strpos( $headers['content-type'], 'text/html' ) !== false ) {
$this->debug_logger->log_debug( $this->wrap_debug_with_details( __FUNCTION__, $email, 'Setting content type to text/html' ) );
$this->php_mailer->isHTML( true );
} else {
$this->debug_logger->log_debug( $this->wrap_debug_with_details( __FUNCTION__, $email, 'Setting content type to text/plain' ) );
$this->php_mailer->isHTML( false );
$this->php_mailer->ContentType = 'text/plain';
}
$additional_headers = $this->get_filtered_message_headers();
if ( ! empty( $additional_headers ) ) {
$this->debug_logger->log_debug( $this->wrap_debug_with_details( __FUNCTION__, $email, 'Additional email headers: ' . json_encode( $additional_headers ) ) );
foreach ( $additional_headers as $key => $value ) {
$this->php_mailer->addCustomHeader( $key, $value );
}
}
if ( (bool) $this->get_setting( self::SETTING_USE_RETURN_PATH, false ) ) {
$this->php_mailer->Sender = $this->php_mailer->From;
}
$this->logger->log( $email, 'pre_send', array(
self::SETTING_AUTH => $this->php_mailer->SMTPAuth,
'secure' => $this->php_mailer->SMTPSecure,
self::SETTING_HOST => $this->php_mailer->Host,
self::SETTING_PORT => $this->php_mailer->Port,
) );
$this->debug_logger->log_debug( $this->wrap_debug_with_details( __FUNCTION__, $email, 'SMTP Connection Details: ' . json_encode( array(
self::SETTING_AUTH => $this->php_mailer->SMTPAuth,
'secure' => $this->php_mailer->SMTPSecure,
self::SETTING_HOST => $this->php_mailer->Host,
self::SETTING_PORT => $this->php_mailer->Port,
) ) ) );
if ( $this->is_test_mode() ) {
$this->events->update( array( 'status' => 'sandboxed' ), $email );
$this->debug_logger->log_debug( $this->wrap_debug_with_details( __FUNCTION__, $email, 'Test mode is enabled, sandboxing email.' ) );
return true;
}
/**
* Fires after PHPMailer is initialized.
*
* @since 2.2.0
*
* @param PHPMailer $phpmailer The PHPMailer instance (passed by reference).
*/
do_action_ref_array( 'phpmailer_init', array( &$this->php_mailer ) );
$this->php_mailer->send();
$this->events->update( array( 'status' => 'sent' ), $email );
$this->debug_logger->log_debug( $this->wrap_debug_with_details( __FUNCTION__, $email, 'Email successfully sent.' ) );
return true;
} catch ( \Exception $e ) {
$this->events->update( array( 'status' => 'failed' ), $email );
$this->logger->log( $email, 'failed', $this->php_mailer->ErrorInfo );
$this->debug_logger->log_error( $this->wrap_debug_with_details( __FUNCTION__, $email, 'Email failed to send. Details: ' . $this->php_mailer->ErrorInfo ) );
return $email;
}
}
/**
* Get the request parameters for sending email through connector.
*
* @since 1.0
*
* @return array
*/
public function get_request_params() {
return array();
}
/**
* Connector data.
*
* @since 1.0
*
* @return array
*/
public function connector_data() {
return array(
self::SETTING_HOST => $this->get_setting( self::SETTING_HOST, '' ),
self::SETTING_PORT => $this->get_setting( self::SETTING_PORT, '' ),
self::SETTING_AUTH => $this->get_setting( self::SETTING_AUTH, false ),
self::SETTING_USERNAME => $this->get_setting( self::SETTING_USERNAME, '' ),
self::SETTING_PASSWORD => $this->get_setting( self::SETTING_PASSWORD, '' ),
self::SETTING_FROM_EMAIL => $this->get_setting( self::SETTING_FROM_EMAIL, '' ),
self::SETTING_FORCE_FROM_EMAIL => $this->get_setting( self::SETTING_FORCE_FROM_EMAIL, false ),
self::SETTING_FROM_NAME => $this->get_setting( self::SETTING_FROM_NAME, '' ),
self::SETTING_FORCE_FROM_NAME => $this->get_setting( self::SETTING_FORCE_FROM_NAME, false ),
self::SETTING_REPLY_TO_EMAIL => $this->get_setting( self::SETTING_REPLY_TO_EMAIL, '' ),
self::SETTING_FORCE_REPLY_TO_EMAIL => $this->get_setting( self::SETTING_FORCE_REPLY_TO_EMAIL, false ),
self::SETTING_ENCRYPTION_TYPE => $this->get_setting( self::SETTING_ENCRYPTION_TYPE, 'tls' ),
self::SETTING_AUTO_TLS => (bool) $this->get_setting( self::SETTING_AUTO_TLS, false ),
self::SETTING_USE_RETURN_PATH => (bool) $this->get_setting( self::SETTING_USE_RETURN_PATH, false ),
);
}
/**
* Settings fields.
*
* @since 1.0
*
* @return array
*/
public function settings_fields() {
$encryption_type = $this->get_setting( self::SETTING_ENCRYPTION_TYPE, 'tls' );
return array(
'title' => esc_html__( 'Custom SMTP Settings', 'gravitysmtp' ),
'description' => '',
'fields' => array_merge(
array(
array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'Configuration', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => [ 4, 0, 4, 0 ],
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
),
// array(
// 'component' => 'Toggle',
// 'props' => array(
// 'initialChecked' => (bool) $this->get_plugin_setting( 'primary' ) === $this->name,
// 'labelAttributes' => array(
// 'label' => esc_html__( 'If enabled, Custom SMTP will be the default SMTP mailer.', 'gravitysmtp' ),
// ),
// 'labelPosition' => 'left',
// 'name' => 'default-mailer',
// 'size' => 'size-m',
// 'spacing' => 5,
// 'width' => 'full',
// ),
// ),
array(
'component' => 'Input',
'props' => array(
'helpTextAttributes' => array(
'content' => esc_html__( 'The URL (such as smtp.mailprovider.com) or IP address of your SMTP host.', 'gravitysmtp' ),
'spacing' => [ 2, 0, 0, 0 ],
),
'labelAttributes' => array(
'label' => esc_html__( 'SMTP Hostname', 'gravitysmtp' ),
),
'name' => self::SETTING_HOST,
'size' => 'size-l',
'spacing' => 6,
'value' => $this->get_setting( self::SETTING_HOST, '' ),
),
),
array(
'component' => 'Input',
'props' => array(
'helpTextAttributes' => array(
'content' => esc_html__( 'Port 465 is usually used with SSL. Ports 25 and 587 are usually used with TLS.', 'gravitysmtp' ),
'spacing' => [ 2, 0, 0, 0 ],
),
'labelAttributes' => array(
'label' => esc_html__( 'SMTP Port', 'gravitysmtp' ),
),
'name' => self::SETTING_PORT,
'size' => 'size-l',
'spacing' => 6,
'value' => $this->get_setting( self::SETTING_PORT, '' ),
),
),
array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'General Settings', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => 4,
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
),
array(
'component' => 'Toggle',
'props' => array(
'helpTextAttributes' => array(
'content' => esc_html__( 'If Return Path is enabled this adds the return path to the email header which indicates where non-deliverable notifications should be sent. Bounce messages may be lost if not enabled.', 'gravitysmtp' ),
'size' => 'text-xs',
'weight' => 'regular',
'spacing' => [ 2, 0, 0, 0 ],
),
'helpTextWidth' => 'full',
'initialChecked' => (bool) $this->get_setting( self::SETTING_USE_RETURN_PATH, false ),
'labelAttributes' => array(
'label' => esc_html__( 'Return Path', 'gravitysmtp' ),
),
'labelPosition' => 'left',
'name' => self::SETTING_USE_RETURN_PATH,
'size' => 'size-m',
'spacing' => 5,
'width' => 'full',
),
),
array(
'component' => 'Toggle',
'props' => array(
'helpTextAttributes' => array(
'content' => esc_html__( 'SMTP servers usually use TLS if available. However, on some servers, you may need to disable it to prevent issues.', 'gravitysmtp' ),
'size' => 'text-xs',
'weight' => 'regular',
'spacing' => [ 2, 0, 0, 0 ],
),
'helpTextWidth' => 'full',
'initialChecked' => (bool) $this->get_setting( self::SETTING_AUTO_TLS, false ),
'labelAttributes' => array(
'label' => esc_html__( 'Auto TLS', 'gravitysmtp' ),
),
'labelPosition' => 'left',
'name' => self::SETTING_AUTO_TLS,
'size' => 'size-m',
'spacing' => 5,
'width' => 'full',
),
),
array(
'component' => 'Box',
'props' => array(
'display' => 'block',
'spacing' => 6,
),
'fields' => array(
array(
'component' => 'Label',
'props' => array(
'label' => __( 'Encryption', 'gravitysmtp' ),
//'htmlFor' => self::SETTING_ENCRYPTION_TYPE,
'weight' => 'medium',
'size' => 'text-sm',
'spacing' => 2,
),
),
array(
'component' => 'InputGroup',
'props' => array(
'customAttributes' => array(
'style' => array(
'display' => 'flex',
),
),
'id' => self::SETTING_ENCRYPTION_TYPE . '_group',
'initialValue' => $encryption_type,
'inputType' => 'radio',
'spacing' => 2,
'data' => array(
array(
'id' => self::SETTING_ENCRYPTION_TYPE . '_tls',
'name' => self::SETTING_ENCRYPTION_TYPE,
'value' => 'tls',
'size' => 'size-md',
'spacing' => array( 0, 4, 0, 0 ),
'labelAttributes' => array(
'label' => __( 'TLS', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'regular',
),
),
array(
'id' => self::SETTING_ENCRYPTION_TYPE . '_ssl',
'name' => self::SETTING_ENCRYPTION_TYPE,
'value' => 'ssl',
'size' => 'size-md',
'spacing' => array( 0, 4, 0, 0 ),
'labelAttributes' => array(
'label' => __( 'SSL', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'regular',
),
),
array(
'id' => self::SETTING_ENCRYPTION_TYPE . '_none',
'name' => self::SETTING_ENCRYPTION_TYPE,
'value' => 'none',
'size' => 'size-md',
'spacing' => array( 0, 4, 0, 0 ),
'labelAttributes' => array(
'label' => __( 'None', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'regular',
),
),
),
),
),
array(
'component' => 'Text',
'props' => array(
'size' => 'text-xs',
'weight' => 'regular',
'content' => esc_html__( 'In most cases, TLS is the preferred encryption method.', 'gravitysmtp' ),
),
)
),
),
array(
'component' => 'Toggle',
'props' => array(
'helpTextAttributes' => array(
'content' => esc_html__( 'Enable authentication if your SMTP server requires a username and password. This option should be enabled in most cases.', 'gravitysmtp' ),
'size' => 'text-xs',
'weight' => 'regular',
'spacing' => [ 2, 0, 0, 0 ],
),
'helpTextWidth' => 'full',
'initialChecked' => (bool) $this->get_setting( self::SETTING_AUTH, false ),
'labelAttributes' => array(
'label' => esc_html__( 'Authentication', 'gravitysmtp' ),
),
'labelPosition' => 'left',
'name' => self::SETTING_AUTH,
'size' => 'size-m',
'spacing' => 5,
'width' => 'full',
),
),
array(
'component' => 'Input',
'props' => array(
'helpTextAttributes' => array(
'content' => esc_html__( 'The username for logging into your mail server.', 'gravitysmtp' ),
'size' => 'text-xs',
'weight' => 'regular',
),
'labelAttributes' => array(
'label' => esc_html__( 'Authentication Username', 'gravitysmtp' ),
),
'name' => self::SETTING_USERNAME,
'size' => 'size-l',
'spacing' => 6,
'value' => $this->get_setting( self::SETTING_USERNAME, '' ),
),
),
array(
'component' => 'Input',
'props' => array(
'helpTextAttributes' => array(
'content' => esc_html__( 'The password for accessing your mail server. It will be stored securely in the database.', 'gravitysmtp' ),
'size' => 'text-xs',
'weight' => 'regular',
),
'customAttributes' => array(
'style' => array(
'display' => 'block',
'width' => '100%',
),
),
'labelAttributes' => array(
'label' => esc_html__( 'Authentication Password', 'gravitysmtp' ),
),
'name' => self::SETTING_PASSWORD,
'size' => 'size-l',
'spacing' => 6,
'type' => 'password',
'value' => $this->get_setting( self::SETTING_PASSWORD, '' ),
),
),
),
$this->get_from_settings_fields(),
$this->get_reply_to_settings_fields(),
),
);
}
/**
* Determine if the SMTP credentials are configured correctly.
*
* @since 1.0
*
* @return bool|\WP_Error
*/
public function is_configured() {
// Protect against other plugins modifying PHPMailer.
if ( ! class_exists( 'PHPMailer\PHPMailer\SMTP' ) ) {
$error = new \WP_Error( 'invalid_configuration', __( 'PHPMailer is not configured on this system.', 'gravitysmtp' ) );
return $error;
}
$this->configure_phpmailer();
try {
// @todo - this can be adjusted if we find it's causing correct-but-slow configurations to fail.
$this->php_mailer->Timeout = 10;
$this->php_mailer->smtpConnect();
} catch ( \Exception $e ) {
$error = new \WP_Error( 'invalid_configuration', $e->getMessage() );
self::$configured = $error;
return $error;
}
self::$configured = true;
return true;
}
/**
* Reset the PHPMailer instance to prevent carryover from previous send.
*
* @since 1.0
*
* @return void
*/
private function reset_phpmailer() {
$this->php_mailer->clearCustomHeaders();
$this->php_mailer->clearAddresses();
$this->php_mailer->clearBCCs();
$this->php_mailer->clearCCs();
$this->php_mailer->clearAllRecipients();
$this->php_mailer->clearReplyTos();
$this->php_mailer->clearAttachments();
}
/**
* Configure the PHPMailer instance.
*
* @since 1.0
*
* @return void
*/
private function configure_phpmailer() {
$this->php_mailer->isSMTP();
$this->php_mailer->CharSet = \PHPMailer\PHPMailer\PHPMailer::CHARSET_UTF8;
$this->php_mailer->Host = $this->get_setting( self::SETTING_HOST, '' );
$this->php_mailer->Port = $this->get_setting( self::SETTING_PORT, '' );
if ( (bool) $this->get_setting( self::SETTING_AUTH ) ) {
$this->php_mailer->SMTPAuth = true;
$this->php_mailer->Username = $this->get_setting( self::SETTING_USERNAME, '' );
$this->php_mailer->Password = $this->get_setting( self::SETTING_PASSWORD, '' );
}
$this->php_mailer->SMTPSecure = $this->get_setting( self::SETTING_ENCRYPTION_TYPE, 'tls' );
$this->php_mailer->SMTPAutoTLS = (bool) $this->get_setting( self::SETTING_AUTO_TLS, false );
}
}
@@ -0,0 +1,642 @@
<?php
namespace Gravity_Forms\Gravity_SMTP\Connectors\Types;
use Google\Client;
use Google\Service\Gmail;
use Gravity_Forms\Gravity_SMTP\Connectors\Connector_Base;
use Gravity_Forms\Gravity_SMTP\Connectors\Connector_Service_Provider;
use Gravity_Forms\Gravity_SMTP\Connectors\Oauth\Google_Oauth_Handler;
use Gravity_Forms\Gravity_SMTP\Connectors\Oauth_Handler;
use Gravity_Forms\Gravity_SMTP\Gravity_SMTP;
use Gravity_Forms\Gravity_SMTP\Utils\Booliesh;
/**
* Connector for Google / Gmail
*
* @since 1.0
*/
class Connector_Google extends Connector_Base {
const SETTING_ACCESS_TOKEN = 'access_token';
const SETTING_CLIENT_ID = 'client_id';
const SETTING_CLIENT_SECRET = 'client_secret';
const SETTING_USE_RETURN_PATH = 'use_return_path';
const VALUE_REDIRECT_URI = 'redirect_uri';
protected $name = 'google';
protected $title = 'Google';
protected $disabled = true;
protected $description = '';
protected $logo = 'Google';
protected $full_logo = 'GoogleFull';
protected $oauth_handler;
public function init( $to, $subject, $message, $headers = '', $attachments = array(), $source = '' ) {
parent::init( $to, $subject, $message, $headers, $attachments, $source );
$this->oauth_handler = \Gravity_Forms\Gravity_SMTP\Gravity_SMTP::container()->get( Connector_Service_Provider::GOOGLE_OAUTH_HANDLER );
}
public function get_description() {
return esc_html__( 'Integrate your website with Gmail or a Google Workspace account, helping to improve email deliverability and prevent your carefully crafted content from ending up in spam folders. Be sure to check the email sending limits for Gmail and Google Workspace. For more information on how to get started with Gmail / Google Workspace, read our documentation.', 'gravitysmtp' );
}
protected $sensitive_fields = array(
self::SETTING_ACCESS_TOKEN,
self::SETTING_CLIENT_ID,
self::SETTING_CLIENT_SECRET,
);
/**
* Sending logic.
*
* @since 1.0
*
* @return bool
*/
public function send() {
$to = $this->get_att( 'to', '' );
$subject = $this->get_att( 'subject', '' );
$message = $this->get_att( 'message', '' );
$headers = $this->get_parsed_headers( $this->get_att( 'headers', array() ) );
$attachments = $this->get_att( 'attachments', array() );
$from = $this->get_from( true );
$reply_to = $this->get_reply_to( true );
$source = $this->get_att( 'source' );
$params = $this->get_request_params();
$email = $this->email;
$this->reset_phpmailer();
if ( ! empty( $headers['content-type'] ) ) {
$headers['content-type'] = $this->get_att( 'content_type', $headers['content-type'] );
}
$this->set_email_log_data( $subject, $message, $to, empty( $from['name'] ) ? $from['email'] : sprintf( '%s <%s>', $from['name'], $from['email'] ), $headers, $attachments, $source, $params );
$this->logger->log( $email, 'started', __( 'Starting email send for Google connector.', 'gravitysmtp' ) );
$this->php_mailer->setFrom( $from['email'], empty( $from['name'] ) ? '' : $from['name'] );
foreach( $to->recipients() as $recipient ) {
if ( ! empty( $recipient->name() ) ) {
$this->php_mailer->addAddress( $recipient->email(), $recipient->name() );
} else {
$this->php_mailer->addAddress( $recipient->email() );
}
}
$this->php_mailer->Subject = $subject;
$this->php_mailer->Body = $message;
if ( ! empty( $headers['cc'] ) ) {
foreach ( $headers['cc']->recipients() as $recipient ) {
if ( ! empty( $recipient->name() ) ) {
$this->php_mailer->addCC( $recipient->email(), $recipient->name() );
} else {
$this->php_mailer->addCC( $recipient->email() );
}
}
}
if ( ! empty( $headers['bcc'] ) ) {
foreach ( $headers['bcc']->recipients() as $recipient ) {
if ( ! empty( $recipient->name() ) ) {
$this->php_mailer->addBCC( $recipient->email(), $recipient->name() );
} else {
$this->php_mailer->addBCC( $recipient->email() );
}
}
}
if ( ! empty( $attachments ) ) {
foreach ( $attachments as $attachment ) {
$this->php_mailer->addAttachment( $attachment );
}
}
if ( ! empty( $reply_to ) ) {
foreach( $reply_to as $address ) {
if ( isset( $address['name'] ) ) {
$this->php_mailer->addReplyTo( $address['email'], $address['name'] );
} else {
$this->php_mailer->addReplyTo( $address['email'] );
}
}
}
if ( ! empty( $headers['content-type'] ) && strpos( $headers['content-type'], 'text/html' ) !== false ) {
$this->php_mailer->isHTML( true );
} else {
$this->php_mailer->isHTML( false );
$this->php_mailer->ContentType = 'text/plain';
}
$this->php_mailer->CharSet = 'UTF-8';
$additional_headers = $this->get_filtered_message_headers();
if ( ! empty( $additional_headers ) ) {
foreach ( $additional_headers as $key => $value ) {
$this->php_mailer->addCustomHeader( $key, $value );
}
}
if ( (bool) $this->get_setting( self::SETTING_USE_RETURN_PATH, false ) ) {
$this->php_mailer->Sender = $this->php_mailer->From;
}
if ( $this->is_test_mode() ) {
$this->events->update( array( 'status' => 'sandboxed' ), $email );
$this->logger->log( $email, 'sandboxed', __( 'Email sandboxed.', 'gravitysmtp' ) );
return true;
}
try {
$debug_atts = compact( 'to', 'from', 'subject', 'headers', 'source', 'attachments', 'reply_to' );
$this->debug_logger->log_debug( $this->wrap_debug_with_details( __FUNCTION__, $email, 'Attempting send with the following attributes: ' . json_encode( $debug_atts ) ) );
$raw = $this->get_raw_message();
$body = array(
'raw' => $raw,
);
$url = 'https://gmail.googleapis.com/gmail/v1/users/me/messages/send';
/**
* @var Google_Oauth_Handler $oauth_handler
*/
$oauth_handler = Gravity_SMTP::container()->get( Connector_Service_Provider::GOOGLE_OAUTH_HANDLER );
$token = $oauth_handler->get_access_token();
if ( is_wp_error( $token ) ) {
throw new \Exception( $token->get_error_message() );
}
$headers = array(
'Authorization' => 'Bearer ' . $token,
'Content-Type' => 'application/json',
);
$args = array(
'body' => json_encode( $body ),
'headers' => $headers,
);
$response = wp_remote_post( $url, $args );
$response_body = wp_remote_retrieve_body( $response );
$response_code = wp_remote_retrieve_response_code( $response );
if ( (int) $response_code !== 200 ) {
$this->log_failure( $email, $response_body );
return $email;
}
$this->events->update( array( 'status' => 'sent' ), $email );
$this->logger->log( $email, 'sent', __( 'Email successfully sent.', 'gravitysmtp' ) );
return true;
} catch ( \Exception $e ) {
$this->log_failure( $email, $e->getMessage() );
$this->debug_logger->log_fatal( $this->wrap_debug_with_details( __FUNCTION__, $email, 'Failed to send: ' . $e->getMessage() ) );
return $email;
}
}
private function log_failure( $email, $message ) {
$this->events->update( array( 'status' => 'failed' ), $email );
$this->logger->log( $email, 'failed', $message );
}
private function get_raw_message() {
$this->php_mailer->preSend();
$raw = $this->php_mailer->getSentMIMEMessage();
return str_replace(
[ '+', '/', '=' ],
[ '-', '_', '' ],
base64_encode( $raw )
);
}
/**
* Connector data.
*
* @since 1.0
*
* @return array
*/
public function connector_data() {
return array(
self::SETTING_CLIENT_ID => $this->get_setting( self::SETTING_CLIENT_ID, '' ),
self::SETTING_CLIENT_SECRET => $this->get_setting( self::SETTING_CLIENT_SECRET, '' ),
self::SETTING_ACCESS_TOKEN => $this->get_setting( self::SETTING_ACCESS_TOKEN, '' ),
self::SETTING_FROM_EMAIL => $this->get_setting( self::SETTING_FROM_EMAIL, '' ),
self::SETTING_FORCE_FROM_EMAIL => $this->get_setting( self::SETTING_FORCE_FROM_EMAIL, false ),
self::SETTING_FROM_NAME => $this->get_setting( self::SETTING_FROM_NAME, '' ),
self::SETTING_FORCE_FROM_NAME => $this->get_setting( self::SETTING_FORCE_FROM_NAME, false ),
self::SETTING_REPLY_TO_EMAIL => $this->get_setting( self::SETTING_REPLY_TO_EMAIL, '' ),
self::SETTING_FORCE_REPLY_TO_EMAIL => $this->get_setting( self::SETTING_FORCE_REPLY_TO_EMAIL, false ),
self::SETTING_USE_RETURN_PATH => (bool) $this->get_setting( self::SETTING_USE_RETURN_PATH, false ),
'oauth_url' => 'https://accounts.google.com/o/oauth2/v2/auth',
'oauth_params' => '&' . $this->get_oauth_params(),
);
}
protected function get_oauth_params() {
/**
* @var Google_Oauth_Handler $oauth_handler
*/
$oauth_handler = Gravity_SMTP::container()->get( Connector_Service_Provider::GOOGLE_OAUTH_HANDLER );
$params = array(
'response_type' => 'code',
'redirect_uri' => urldecode( $oauth_handler->get_return_url() ),
'scope' => 'https://www.googleapis.com/auth/gmail.send https://www.googleapis.com/auth/gmail.readonly',
'include_granted_scopes' => 'true',
'state' => 1,
'access_type' => 'offline',
'prompt' => 'consent',
);
return http_build_query( $params );
}
/**
* Settings fields.
*
* @since 1.0
*
* @return array
*/
public function settings_fields() {
/**
* @var Oauth_Handler $oauth_handler
*/
$oauth_handler = Gravity_SMTP::container()->get( Connector_Service_Provider::GOOGLE_OAUTH_HANDLER );
$oauth_handler->handle_response( $this->name );
$token = $oauth_handler->get_access_token( $this->name );
$has_token = $token && ! is_wp_error( $token );
$settings = array(
'title' => esc_html__( 'Google / Gmail Settings', 'gravitysmtp' ),
'hide_save' => ! $has_token,
'fields' => array(),
);
if ( ! $has_token ) {
$settings['fields'][] = array(
'component' => 'Alert',
'props' => array(
'id' => 'google-connection-notice',
'customIconPrefix' => 'gravity-admin-icon',
'theme' => 'cosmos',
'type' => 'notice',
'spacing' => 3,
),
'fields' => array(
array(
'component' => 'Text',
'props' => array(
'content' => esc_html__( 'Before proceeding, make sure to save your settings with the Client ID and Client Secret.', 'gravitysmtp' ),
'customClasses' => array( 'gform--display-block', 'gravitysmtp-integration__notice-message' ),
'size' => 'text-sm',
'spacing' => 4,
'tagName' => 'span',
),
),
array(
'component' => 'Link',
'props' => array(
'content' => esc_html__( 'Read our Google / Gmail documentation', 'gravitysmtp' ),
'customClasses' => array(
'gform-link--theme-cosmos',
'gravitysmtp-integration__notice-link',
'gform-button',
'gform-button--size-height-m',
'gform-button--white',
'gform-button--width-auto'
),
'href' => 'https://docs.gravitysmtp.com/category/integrations/google/',
'target' => '_blank',
),
),
),
);
} else {
$settings['fields'][] = array(
'component' => 'Alert',
'props' => array(
'id' => 'google-alias-notice',
'customIconPrefix' => 'gravity-admin-icon',
'theme' => 'cosmos',
'type' => 'info',
'spacing' => 3,
),
'fields' => array(
array(
'component' => 'LinkedText',
'external' => true,
'links' => array(
array(
'key' => 'link',
'props' => array(
'href' => 'https://docs.gravitysmtp.com/how-to-send-emails-from-an-alias-google-gmail/',
'size' => 'text-sm',
'target' => '_blank',
),
),
),
'props' => array(
'customClasses' => array( 'gform--display-block' ),
'content' => esc_html__( 'Important: To use alias email addresses with Gravity SMTP, ensure your primary Google account is authenticated, then add and verify your alias in your Google account settings. For detailed instructions, please refer to our {{link}}documentation article{{link}}.', 'gravitysmtp' ),
'weight' => 'medium',
'size' => 'text-sm',
'tagName' => 'span',
),
),
),
);
}
if ( isset( $_GET['code'] ) && ! $has_token ) {
$settings['fields'][] = array(
'component' => 'Alert',
'props' => array(
'id' => 'google-connection-error',
'customIconPrefix' => 'gravity-admin-icon',
'theme' => 'cosmos',
'type' => 'error',
'spacing' => 3,
),
'fields' => array(
array(
'component' => 'Text',
'props' => array(
'content' => esc_html__( 'Error Connecting to Google. Check your credentials and try again.', 'gravitysmtp' ),
'weight' => 'medium',
'size' => 'text-sm',
'spacing' => 2,
'tagName' => 'span',
),
),
),
);
}
$settings['fields'][] = array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'Configuration', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => [ 4, 0, 3, 0 ],
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
);
if ( ! $has_token ) {
$settings['fields'][] = array(
'component' => 'LinkedHelpTextInput',
'external' => true,
'handle_change' => true,
'links' => array(
array(
'key' => 'link',
'props' => array(
'href' => 'https://console.cloud.google.com/',
'size' => 'text-xs',
'target' => '_blank',
),
),
),
'props' => array(
'helpTextAttributes' => array(
'content' => esc_html__( 'To obtain a Client ID from Google / Gmail, log in to your {{link}}Google Cloud Console{{link}} and generate the Client ID.', 'gravitysmtp' ),
'size' => 'text-xs',
'weight' => 'regular',
),
'labelAttributes' => array(
'label' => esc_html__( 'Client ID', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'medium',
),
'name' => self::SETTING_CLIENT_ID,
'spacing' => 6,
'size' => 'size-l',
'value' => $this->get_setting( self::SETTING_CLIENT_ID, '' ),
),
);
$settings['fields'][] = array(
'component' => 'LinkedHelpTextInput',
'external' => true,
'handle_change' => true,
'links' => array(
array(
'key' => 'link',
'props' => array(
'href' => 'https://console.cloud.google.com/',
'size' => 'text-xs',
'target' => '_blank',
),
),
),
'props' => array(
'helpTextAttributes' => array(
'content' => esc_html__( 'To obtain a Client Secret from Google / Gmail, log in to your {{link}}Google Cloud Console{{link}} and generate the Client ID.', 'gravitysmtp' ),
'size' => 'text-xs',
'weight' => 'regular',
),
'labelAttributes' => array(
'label' => esc_html__( 'Client Secret', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'medium',
),
'name' => self::SETTING_CLIENT_SECRET,
'spacing' => 6,
'size' => 'size-l',
'value' => $this->get_setting( self::SETTING_CLIENT_SECRET, '' ),
),
);
$settings['fields'][] = array(
'component' => 'CopyInput',
'external' => true,
'props' => array(
'actionButtonAttributes' => array(
'customAttributes' => array(
'type' => 'button',
),
'icon' => 'copy',
'iconPrefix' => 'gravity-admin-icon',
'label' => esc_html__( 'Copy', 'gravitysmtp' ),
),
'labelAttributes' => array(
'label' => esc_html__( 'Authorized redirect URI', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'medium',
),
'name' => self::VALUE_REDIRECT_URI,
'spacing' => 6,
'size' => 'size-l',
'customAttributes' => array(
'readOnly' => true,
),
'value' => urldecode( $oauth_handler->get_return_url( 'settings' ) ),
'helpTextAttributes' => array(
'content' => __( 'Copy this URL into the "Authorized redirect URIs" field of your Google web application.', 'gravitysmtp' ),
'size' => 'text-xs',
'weight' => 'regular',
),
),
);
$settings['fields'][] = array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'Authorization', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => [ 4, 0, 3, 0 ],
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
);
$settings['fields'][] = array(
'component' => 'BrandedButton',
'external' => true,
'props' => array(
'label' => esc_html__( 'Sign in with Google', 'gravitysmtp' ),
'spacing' => 4,
'Svg' => 'GoogleAltLogo',
'type' => 'color',
),
);
} else {
$settings['fields'][] = array(
'component' => 'Box',
'props' => array(
'customClasses' => array( 'gravitysmtp-google-integration__connected-message' ),
'display' => 'flex',
'spacing' => 3,
),
'fields' => array(
array(
'component' => 'Icon',
'props' => array(
'customClasses' => array( 'gravitysmtp-google-integration__checkmark', 'gform-icon--preset-active', 'gform-icon-preset--status-correct', 'gform-alert__icon' ),
'icon' => 'checkmark-simple',
'iconPrefix' => 'gravity-admin-icon',
),
),
array(
'component' => 'Text',
'props' => array(
'asHtml' => true,
'content' => sprintf(
'%s <span class="gform-text gform-text--color-port gform-typography--size-text-sm gform-typography--weight-medium">%s</span>',
esc_html__( 'Connected with email account:', 'gravitysmtp' ),
esc_html( $oauth_handler->get_connection_details()['email'] )
),
'size' => 'text-sm',
'tagName' => 'span',
),
),
),
);
$disconnect_url = add_query_arg( '_wpnonce', wp_create_nonce( 'gsmtp_disconnect_google' ), admin_url( 'admin-post.php?action=smtp_disconnect_google' ) );
$settings['fields'][] = array(
'component' => 'Text',
'props' => array(
'content' => sprintf( '<a href="%s" class="%s"><span class="gravity-admin-icon gravity-admin-icon--x-circle gform-button__icon"></span>%s</a>', $disconnect_url, 'gform-link gform-link--theme-cosmos gform-button gform-button--size-height-m gform-button--white gform-button--width-auto gform-button--active-type-loader gform-button--loader-after gform-button--icon-leading', __( 'Disconnect from Google', 'gravitysmtp' ) ),
'asHtml' => true,
'spacing' => 6,
),
);
$settings['fields'][] = array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'Configuration', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => [ 4, 0, 4, 0 ],
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
);
$settings['fields'][] = array(
'component' => 'Toggle',
'props' => array(
'helpTextAttributes' => array(
'content' => esc_html__( 'If Return Path is enabled this adds the return path to the email header which indicates where non-deliverable notifications should be sent. Bounce messages may be lost if not enabled.', 'gravitysmtp' ),
'size' => 'text-xs',
'weight' => 'regular',
'spacing' => [ 2, 0, 0, 0 ],
),
'helpTextWidth' => 'full',
'initialChecked' => (bool) $this->get_setting( self::SETTING_USE_RETURN_PATH, false ),
'labelAttributes' => array(
'label' => esc_html__( 'Return Path', 'gravitysmtp' ),
),
'labelPosition' => 'left',
'name' => self::SETTING_USE_RETURN_PATH,
'size' => 'size-m',
'spacing' => 5,
'width' => 'full',
),
);
$settings['fields'] = array_merge( $settings['fields'], $this->get_from_settings_fields(), $this->get_reply_to_settings_fields() );
}
return $settings;
}
public function is_configured() {
if ( Booliesh::get( $this->get_setting( 'access_token', false ) ) ) {
/**
* @var Google_Oauth_Handler $oauth_handler
*/
$oauth_handler = Gravity_SMTP::container()->get( Connector_Service_Provider::GOOGLE_OAUTH_HANDLER );
$token = $oauth_handler->get_access_token();
return $token;
}
return false;
}
/**
* Reset the PHPMailer instance to prevent carryover from previous send.
*
* @since 1.0
*
* @return void
*/
private function reset_phpmailer() {
$this->php_mailer->clearCustomHeaders();
$this->php_mailer->clearAddresses();
$this->php_mailer->clearBCCs();
$this->php_mailer->clearCCs();
$this->php_mailer->clearAllRecipients();
$this->php_mailer->clearReplyTos();
$this->php_mailer->clearAttachments();
}
}
@@ -0,0 +1,440 @@
<?php
namespace Gravity_Forms\Gravity_SMTP\Connectors\Types;
use Gravity_Forms\Gravity_SMTP\Connectors\Connector_Base;
use Gravity_Forms\Gravity_SMTP\Feature_Flags\Feature_Flag_Manager;
/**
* Connector for Mailchimp
*
* @since 1.4.2
*/
class Connector_Mailchimp extends Connector_Base {
const SETTING_API_KEY = 'api_key';
const SETTING_USE_RETURN_PATH = 'use_return_path';
protected $name = 'mailchimp';
protected $title = 'Mailchimp';
protected $logo = 'Mailchimp';
protected $full_logo = 'MailchimpFull';
protected $url = 'https://mandrillapp.com/api/1.0/';
public function get_description() {
return esc_html__( 'Reach inboxes when it matters most. Send email notifications to your contacts with MailChimp Transactional Email.', 'gravitysmtp' );
}
protected $sensitive_fields = array(
self::SETTING_API_KEY,
);
protected function get_merged_data() {
$data = parent::get_merged_data();
$data['disabled'] = ! Feature_Flag_Manager::is_enabled( 'mailchimp_integration' );
return $data;
}
/**
* Sending logic.
*
* @since 1.4.2
*
* @return bool
*/
public function send() {
try {
$atts = $this->get_send_atts();
$source = $this->get_att( 'source' );
$from = $this->get_from( true );
$params = $this->get_request_params();
$email = $this->email;
$this->set_email_log_data( $atts['subject'], $atts['message'], $atts['to'], $atts['from']['from'], $atts['headers'], $atts['attachments'], $source, $params );
$this->logger->log( $email, 'started', __( 'Starting email send for Mailchimp connector.', 'gravitysmtp' ) );
$this->debug_logger->log_debug( $this->wrap_debug_with_details( __FUNCTION__, $email, 'Starting email send with Mailchimp connector and the following params: ' . json_encode( $params) ) );
$this->debug_logger->log_debug( $this->wrap_debug_with_details( __FUNCTION__, $email, sprintf( 'Using From Name: %s, From Email: %s', $from['name'], $from['email'] ) ) );
if ( $this->is_test_mode() ) {
$this->events->update( array( 'status' => 'sandboxed' ), $email );
$this->logger->log( $email, 'sandboxed', __( 'Email sandboxed.', 'gravitysmtp' ) );
$this->debug_logger->log_debug( $this->wrap_debug_with_details( __FUNCTION__, $email, 'Test mode is enabled, sandboxing email.' ) );
return true;
}
$url = $this->url . 'messages/send';
$response = wp_safe_remote_post( $url, $params );
$body = wp_remote_retrieve_body( $response );
$decoded = json_decode( $body, true );
$this->debug_logger->log_debug( $this->wrap_debug_with_details( __FUNCTION__, $email, 'Received response of: ' . $body ) );
$is_success = (int) wp_remote_retrieve_response_code( $response ) === 200 && $decoded[0]['status'] !== 'rejected';
if ( ! $is_success ) {
$this->log_failure( $email, $body );
$this->debug_logger->log_error( $this->wrap_debug_with_details( __FUNCTION__, $email, 'Email failed to send. Details: ' . $body ) );
return $email;
}
$this->events->update( array( 'status' => 'sent' ), $email );
$this->logger->log( $email, 'sent', __( 'Email successfully sent.', 'gravitysmtp' ) );
$this->debug_logger->log_debug( $this->wrap_debug_with_details( __FUNCTION__, $email, 'Email successfully sent.' ) );
return true;
} catch ( \Exception $e ) {
$this->log_failure( $email, $e->getMessage() );
$this->debug_logger->log_error( $this->wrap_debug_with_details( __FUNCTION__, $email, 'Email failed to send. Details: ' . $e->getMessage() ) );
return $email;
}
}
/**
* Logs an email send failure.
*
* @since 1.4.2
*
* @param string $email The email that failed.
* @param string $error_message The error message.
*/
private function log_failure( $email, $error_message ) {
$this->events->update( array( 'status' => 'failed' ), $email );
$this->logger->log( $email, 'failed', $error_message );
}
/**
* Get the request parameters for sending email through connector.
*
* @since 1.4.2
*
* @return array
*/
public function get_request_params() {
$atts = $this->get_send_atts();
$api_key = $this->get_setting( self::SETTING_API_KEY );
$body = array(
'key' => $api_key,
'message' => array(
'subject' => $atts['subject'],
'from_email' => $atts['from']['email'],
'headers' => array(),
),
);
if ( ! empty( $atts['from']['name'] ) ) {
$body['message']['from_name'] = $atts['from']['name'];
}
if ( ! empty( $atts['to'] ) ) {
$body['message']['to'] = $atts['to']->as_array();
}
// Setting content
$is_html = ! empty( $atts['headers']['content-type'] ) && strpos( $atts['headers']['content-type'], 'text/html' ) !== false;
if ( $is_html ) {
$body['message']['html'] = $atts['message'];
$body['message']['text'] = wp_strip_all_tags( $atts['message'] );
} else {
$body['message']['text'] = $atts['message'];
}
// Setting reply-to
if ( ! empty( $atts['headers']['reply-to'] ) ) {
$address = str_replace( 'Reply-To: ', '', $atts['headers']['reply-to'] );
$body['message']['headers']['reply-to'] = $address;
}
// Setting cc
if ( ! empty( $atts['headers']['cc'] ) ) {
foreach ( $atts['headers']['cc']->as_array() as $recipient ) {
if ( isset( $recipient['email'] ) ) {
$body['message']['to'][] = array(
'type' => 'cc',
'email' => $recipient['email'],
);
}
}
}
// Setting bcc
if ( ! empty( $atts['headers']['bcc'] ) ) {
foreach ( $atts['headers']['bcc']->as_array() as $recipient ) {
if ( isset( $recipient['email'] ) ) {
$body['message']['to'][] = array(
'type' => 'bcc',
'email' => $recipient['email'],
);
}
}
}
if ( (bool) $this->get_setting( self::SETTING_USE_RETURN_PATH, false ) ) {
$body['message']['return_path_domain'] = $atts['from']['email'];
}
// Setting attachments
if ( ! empty( $atts['attachments'] ) ) {
$body['message']['attachments'] = $this->get_attachments( $atts['attachments'] );
}
return array(
'body' => json_encode( $body ),
'headers' => array(
'Content-Type' => 'application/json',
),
);
}
/**
* Gets a list of attachments, and returns them in a format that can be used by the API.
*
* @param array $attachments The list of attachments.
*
* @since 1.4.2
*
* @return array Returns an array of attachments. Each item with a 'content' and 'name' property.
*/
protected function get_attachments( $attachments ) {
$data = array();
foreach ( $attachments as $custom_name => $attachment ) {
try {
if ( is_file( $attachment ) && is_readable( $attachment ) ) {
$file_name = is_numeric( $custom_name ) ? basename( $attachment ) : $custom_name;
$content = base64_encode( file_get_contents( $attachment ) );
$data[] = array(
'name' => $file_name,
'content' => $content,
'type' => mime_content_type( $attachment ),
);
}
} catch ( \Exception $e ) {
continue;
}
}
return $data;
}
/**
* Get the attributes for sending email.
*
* @since 1.4.2
*
* @return array
*/
protected function get_send_atts() {
$headers = $this->get_parsed_headers( $this->get_att( 'headers', array() ) );
if ( ! empty( $headers['content-type'] ) ) {
$headers['content-type'] = $this->get_att( 'content_type', $headers['content-type'] );
}
return array(
'to' => $this->get_att( 'to', '' ),
'subject' => $this->get_att( 'subject', '' ),
'message' => $this->get_att( 'message', '' ),
'headers' => $headers,
'attachments' => $this->get_att( 'attachments', array() ),
'from' => $this->get_from( true ),
'reply_to' => $this->get_reply_to( true ),
);
}
/**
* Connector data.
*
* @since 1.4.2
*
* @return array
*/
public function connector_data() {
return array(
self::SETTING_API_KEY => $this->get_setting( self::SETTING_API_KEY, '' ),
self::SETTING_FROM_EMAIL => $this->get_setting( self::SETTING_FROM_EMAIL, '' ),
self::SETTING_FORCE_FROM_EMAIL => $this->get_setting( self::SETTING_FORCE_FROM_EMAIL, false ),
self::SETTING_FROM_NAME => $this->get_setting( self::SETTING_FROM_NAME, '' ),
self::SETTING_FORCE_FROM_NAME => $this->get_setting( self::SETTING_FORCE_FROM_NAME, false ),
self::SETTING_REPLY_TO_EMAIL => $this->get_setting( self::SETTING_REPLY_TO_EMAIL, '' ),
self::SETTING_FORCE_REPLY_TO_EMAIL => $this->get_setting( self::SETTING_FORCE_REPLY_TO_EMAIL, false ),
self::SETTING_USE_RETURN_PATH => (bool) $this->get_setting( self::SETTING_USE_RETURN_PATH, false ),
);
}
/**
* Settings fields.
*
* @since 1.4.2
*
* @return array
*/
public function settings_fields() {
return array(
'title' => esc_html__( 'Mailchimp Settings', 'gravitysmtp' ),
'description' => '',
'fields' => array_merge(
array(
array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'Configuration', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => array( 4, 0, 4, 0 ),
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
),
array(
'component' => 'Input',
'props' => array(
'labelAttributes' => array(
'label' => esc_html__( 'API Key', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'medium',
),
'helpTextAttributes' => array(
'asHtml' => true,
/* translators: 1: opening anchor tag, 2: closing anchor tag */
'content' => sprintf( __( 'To generate an API key from Mailchimp, navigate to the %ssettings of your Mailchimp Transactional account%s and look for the API Keys section.', 'gravitysmtp' ), '<a class="gform-link gform-typography--size-text-xs" href="https://mandrillapp.com/settings" target="_blank" rel="noopener noreferrer">', '</a>' ),
'size' => 'text-xs',
'weight' => 'regular',
),
'name' => self::SETTING_API_KEY,
'size' => 'size-l',
'spacing' => 6,
'value' => $this->get_setting( self::SETTING_API_KEY, '' ),
),
),
),
array(
array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'General Settings', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => 4,
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
),
array(
'component' => 'Toggle',
'props' => array(
'helpTextAttributes' => array(
'content' => esc_html__( 'If Return Path is enabled this adds the return path to the email header which indicates where non-deliverable notifications should be sent. Bounce messages may be lost if not enabled.', 'gravitysmtp' ),
'size' => 'text-xs',
'weight' => 'regular',
'spacing' => [ 2, 0, 0, 0 ],
),
'helpTextWidth' => 'full',
'initialChecked' => (bool) $this->get_setting( self::SETTING_USE_RETURN_PATH, false ),
'labelAttributes' => array(
'label' => esc_html__( 'Return Path', 'gravitysmtp' ),
),
'labelPosition' => 'left',
'name' => self::SETTING_USE_RETURN_PATH,
'size' => 'size-m',
'spacing' => 5,
'width' => 'full',
),
)
),
$this->get_from_settings_fields(),
$this->get_reply_to_settings_fields(),
),
);
}
public function is_configured() {
static $configured;
if ( ! is_null( $configured ) ) {
return $configured;
}
if ( empty( $this->get_setting( self::SETTING_API_KEY, '' ) ) ) {
return false;
}
$base_url = $this->url . 'users/ping';
$body = json_encode( array(
'key' => $this->get_setting( self::SETTING_API_KEY, '' ),
) );
$params = array(
'body' => $body,
'headers' => array( 'Content-Type' => 'application/json' ),
);
$request = wp_remote_post( $base_url, $params );
$code = wp_remote_retrieve_response_code( $request );
if ( $code === 200 ) {
$configured = true;
return true;
}
$error = new \WP_Error( 'authentication_error', __( 'Could not authenticate with Mailchimp.', 'gravitysmtp' ) );
$configured = $error;
return $error;
}
protected function get_authenticated_domains() {
$base_url = $this->url . 'senders/domains';
$body = json_encode( array(
'key' => $this->get_setting( self::SETTING_API_KEY, '' ),
) );
$params = array(
'body' => $body,
'headers' => array( 'Content-Type' => 'application/json' ),
);
$request = wp_remote_post( $base_url, $params );
$code = wp_remote_retrieve_response_code( $request );
$no_results = array(
__( 'No authenticated Domains found in your account. Sending will not be possible until you add a verified domain to your Mailchimp Account.', 'gravitysmtp' ),
);
if ( $code !== 200 ) {
return $no_results;
}
$body = wp_remote_retrieve_body( $request );
$domains = json_decode( $body, true );
$results = array();
if ( empty( $domains ) ) {
return $no_results;
}
foreach ( $domains as $domain ) {
if ( empty( $domain['verified_at'] ) ) {
continue;
}
$results[] = $domain['domain'];
}
if ( empty( $results ) ) {
return $no_results;
}
return $results;
}
}
@@ -0,0 +1,396 @@
<?php
namespace Gravity_Forms\Gravity_SMTP\Connectors\Types;
use Gravity_Forms\Gravity_SMTP\Connectors\Connector_Base;
use Gravity_Forms\Gravity_SMTP\Feature_Flags\Feature_Flag_Manager;
/**
* Connector for MailerSend
*
* @since 1.0
*/
class Connector_MailerSend extends Connector_Base {
const SETTING_API_KEY = 'api_key';
protected $name = 'mailersend';
protected $title = 'MailerSend';
protected $disabled = true;
protected $logo = 'MailerSend';
protected $full_logo = 'MailerSendFull';
protected $url = 'https://api.mailersend.com/v1';
protected $sensitive_fields = array(
self::SETTING_API_KEY,
);
public function get_description() {
return esc_html__( 'Deliver transactional emails fast with MailerSend, a developer-friendly platform built for performance and scalability. Easily send notifications, receipts, and more with advanced analytics, templates, and robust API support.', 'gravitysmtp' );
}
/**
* Sends email via MailerSend.
*
* @since 1.0
*
* @return int Returns the email ID.
*/
public function send() {
try {
$atts = $this->get_send_atts();
$source = $this->get_att( 'source' );
$params = $this->get_request_params();
$email = $this->email;
$this->set_email_log_data( $atts['subject'], $atts['message'], $atts['to'], $atts['from']['from'], $atts['headers'], $atts['attachments'], $source, $params );
$this->logger->log( $email, 'started', __( 'Starting email send for MailerSend connector.', 'gravitysmtp' ) );
if ( $this->is_test_mode() ) {
$this->events->update( array( 'status' => 'sandboxed' ), $email );
$this->logger->log( $email, 'sandboxed', __( 'Email sandboxed.', 'gravitysmtp' ) );
return true;
}
$response = wp_safe_remote_post( $this->url . '/email', $params );
$is_success = in_array( (int) wp_remote_retrieve_response_code( $response ), array( 200, 201, 202 ) );
if ( ! $is_success ) {
$this->log_failure( $email, wp_remote_retrieve_body( $response ) );
return $email;
}
$this->events->update( array( 'status' => 'sent' ), $email );
$this->logger->log( $email, 'sent', __( 'Email successfully sent.', 'gravitysmtp' ) );
return true;
} catch ( \Exception $e ) {
$this->log_failure( $email, $e->getMessage() );
return $email;
}
}
/**
* Get the request parameters for sending email through connector.
*
* @since 1.0
*
* @return array
*/
public function get_request_params() {
$atts = $this->get_send_atts();
$api_key = $this->get_setting( self::SETTING_API_KEY );
$body = array(
'subject' => $atts['subject'],
'from' => array(
'email' => $atts['from']['email'],
'name' => $atts['from']['name'],
),
'to' => array(),
);
// Setting to
foreach ( $atts['to']->as_array() as $to ) {
$to_value = array(
'email' => $to['email'],
'name' => ! empty( $to['name'] ) ? $to['name'] : null,
);
$body['to'][] = array_filter( $to_value );
}
// Setting content
$is_html = ! empty( $atts['headers']['content-type'] ) && strpos( $atts['headers']['content-type'], 'text/html' ) !== false;
if ( $is_html ) {
$body['html'] = $atts['message'];
} else {
$body['text'] = $atts['message'];
}
// Setting cc
if ( ! empty( $atts['headers']['cc'] ) ) {
$body['cc'] = array();
foreach ( $atts['headers']['cc']->as_array() as $cc_value ) {
$values = array(
'email' => $cc_value['email'],
'name' => ! empty( $cc_value['name'] ) ? $cc_value['name'] : null,
);
$body['cc'][] = array_filter( $values );
}
}
// Setting bcc
if ( ! empty( $atts['headers']['bcc'] ) ) {
$body['bcc'] = array();
foreach ( $atts['headers']['bcc']->as_array() as $bcc_value ) {
$values = array(
'email' => $bcc_value['email'],
'name' => ! empty( $bcc_value['name'] ) ? $bcc_value['name'] : null,
);
$body['bcc'][] = array_filter( $values );
}
}
// Setting reply to
if ( ! empty( $atts['reply_to'] ) ) {
if ( isset( $atts['reply_to']['email'] ) ) {
$reply_to = $atts['reply_to'];
} else {
$reply_to = $atts['reply_to'][0];
}
$body['reply_to'] = array(
'email' => $reply_to['email'],
);
}
// Setting attachments
if ( ! empty( $atts['attachments'] ) ) {
$body['attachments'] = $this->get_attachments( $atts['attachments'] );
}
return array(
'body' => json_encode( $body ),
'headers' => $this->get_request_headers( $api_key ),
);
}
/**
* Get the attributes for sending email.
*
* @since 1.0
*
* @return array
*/
protected function get_send_atts() {
$headers = $this->get_parsed_headers( $this->get_att( 'headers', array() ) );
if ( ! empty( $headers['content-type'] ) ) {
$headers['content-type'] = $this->get_att( 'content_type', $headers['content-type'] );
}
return array(
'to' => $this->get_att( 'to', '' ),
'subject' => $this->get_att( 'subject', '' ),
'message' => $this->get_att( 'message', '' ),
'headers' => $headers,
'attachments' => $this->get_att( 'attachments', array() ),
'from' => $this->get_from( true ),
'reply_to' => $this->get_reply_to( true ),
);
}
/**
* Gets a list of attachments, and returns them in a format that can be used by the API.
*
* @param array $attachments The list of attachments.
*
* @since 1.0
*
* @return array Returns an array of attachments. Each item with a 'content' and 'name' property.
*/
protected function get_attachments( $attachments ) {
$data = array();
foreach ( $attachments as $custom_name => $attachment ) {
try {
if ( is_file( $attachment ) && is_readable( $attachment ) ) {
$fileName = is_numeric( $custom_name ) ? basename( $attachment ) : $custom_name;
$content = base64_encode( file_get_contents( $attachment ) );
$data[] = array(
'filename' => $fileName,
'content' => $content,
'disposition' => 'attachment',
);
}
} catch ( \Exception $e ) {
continue;
}
}
return $data;
}
/**
* Gets the headers to be used in the API request.
*
* @since 1.0
*
* @return array Returns the header array to be passed to MailerSend's API.
*/
protected function get_request_headers( $api_key ) {
return array(
'content-type' => 'application/json',
'accept' => 'application/json',
'Authorization' => 'Bearer ' . $api_key,
);
}
/**
* Logs an email send failure.
*
* @since 1.0
*
* @param string $email The email that failed.
* @param string $error_message The error message.
*/
private function log_failure( $email, $error_message ) {
$this->events->update( array( 'status' => 'failed' ), $email );
$this->logger->log( $email, 'failed', $error_message );
}
/**
* Connector data.
*
* @since 1.0
*
* @return array
*/
public function connector_data() {
return array(
self::SETTING_API_KEY => $this->get_setting( self::SETTING_API_KEY, '' ),
self::SETTING_FROM_EMAIL => $this->get_setting( self::SETTING_FROM_EMAIL, '' ),
self::SETTING_FORCE_FROM_EMAIL => $this->get_setting( self::SETTING_FORCE_FROM_EMAIL, false ),
self::SETTING_FROM_NAME => $this->get_setting( self::SETTING_FROM_NAME, '' ),
self::SETTING_FORCE_FROM_NAME => $this->get_setting( self::SETTING_FORCE_FROM_NAME, false ),
self::SETTING_REPLY_TO_EMAIL => $this->get_setting( self::SETTING_REPLY_TO_EMAIL, '' ),
self::SETTING_FORCE_REPLY_TO_EMAIL => $this->get_setting( self::SETTING_FORCE_REPLY_TO_EMAIL, false ),
);
}
/**
* Settings fields.
*
* @since 1.0
*
* @return array
*/
public function settings_fields() {
return array(
'title' => esc_html__( 'MailerSend Settings', 'gravitysmtp' ),
'description' => '',
'fields' => array_merge(
array(
array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'Configuration', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => array( 4, 0, 4, 0 ),
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
),
array(
'component' => 'Input',
'props' => array(
'labelAttributes' => array(
'label' => esc_html__( 'API Key', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'medium',
),
'helpTextAttributes' => array(
'asHtml' => true,
/* translators: 1: opening anchor tag, 2: closing anchor tag */
'content' => sprintf( __( 'To generate an API key from MailerSend, log in to your MailerSend dashboard and navigate to the Domain section. %1$sCreate a new domain%2$s and then %3$sgenerate your API token%2$s.', 'gravitysmtp' ), '<a class="gform-link gform-typography--size-text-xs" href="https://app.mailersend.com/domains" target="_blank" rel="noopener noreferrer">', '</a>', '<a class="gform-link gform-typography--size-text-xs" href="https://www.mailersend.com/help/managing-api-tokens" target="_blank" rel="noopener noreferrer">' ),
'size' => 'text-xs',
'weight' => 'regular',
),
'name' => self::SETTING_API_KEY,
'size' => 'size-l',
'spacing' => 6,
'value' => $this->get_setting( self::SETTING_API_KEY, '' ),
),
),
array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'General Settings', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => 4,
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
),
),
$this->get_from_settings_fields(),
$this->get_reply_to_settings_fields(),
),
);
}
/**
* Determine if the API credentials are configured correctly.
*
* @since 1.0
*
* @return bool|\WP_Error Returns true if configured, or a WP_Error object if not.
*/
public function is_configured() {
$valid_api = $this->verify_api_key();
if ( is_wp_error( $valid_api ) ) {
self::$configured = $valid_api;
return $valid_api;
}
self::$configured = true;
return true;
}
/**
* Verify the API key with the API.
*
* @since 1.0
*
* @return true|\WP_Error
*/
private function verify_api_key() {
$api_key = $this->get_setting( self::SETTING_API_KEY );
$url = $this->url . '/api-quota';
if ( empty( $api_key ) ) {
return new \WP_Error( 'missing_api_key', __( 'No API Key provided.', 'gravitysmtp' ) );
}
$response = wp_remote_get(
$url,
array(
'headers' => $this->get_request_headers( $api_key ),
)
);
if ( wp_remote_retrieve_response_code( $response ) != '200' ) {
return new \WP_Error( 'invalid_api_key', __( 'Invalid API Key provided.', 'gravitysmtp' ) );
}
return true;
}
/**
* Get the unique data for this connector, merged with the default/common data for all
* connectors in the system.
*
* @since 1.0
*
* @return array
*/
protected function get_merged_data() {
$data = parent::get_merged_data();
$data['disabled'] = ! Feature_Flag_Manager::is_enabled( 'mailersend_integration' );
return $data;
}
}
@@ -0,0 +1,603 @@
<?php
namespace Gravity_Forms\Gravity_SMTP\Connectors\Types;
use Gravity_Forms\Gravity_SMTP\Connectors\Connector_Base;
/**
* Connector for Mailgun
*
* @since 1.0
*/
class Connector_Mailgun extends Connector_Base {
const SETTING_API_KEY = 'api_key';
const SETTING_REGION = 'region';
const SETTING_DOMAIN = 'domain';
const SETTING_USE_RETURN_PATH = 'use_return_path';
const OPTION_REGION_US = 'us';
const OPTION_REGION_EU = 'eu';
const API_URL_US = 'https://api.mailgun.net/v3/';
const API_URL_EU = 'https://api.eu.mailgun.net/v3/';
protected $name = 'mailgun';
protected $title = 'Mailgun';
protected $disabled = true;
protected $description = '';
protected $logo = 'Mailgun';
protected $full_logo = 'MailgunFull';
public function get_description() {
return esc_html__( 'Mailgun is a transactional email service that provides industry-leading reliability, compliance, and speed. Offering a 30-day trial, Mailguns premium service starts at $35 a month, which allows you to send up to 50,000 emails. For more information on how to get started with Mailgun, read our documentation.', 'gravitysmtp' );
}
protected $sensitive_fields = array(
self::SETTING_API_KEY,
);
/**
* Sending logic.
*
* @since 1.0
*
* @return bool
*/
public function send() {
try {
$atts = $this->get_send_atts();
$source = $this->get_att( 'source' );
$params = $this->get_request_params();
$email = $this->email;
$this->set_email_log_data( $atts['subject'], $atts['message'], $atts['to'], $atts['from'], $atts['headers'], $atts['attachments'], $source, $params );
$this->logger->log( $email, 'started', __( 'Starting email send for Mailgun connector.', 'gravitysmtp' ) );
if ( $this->is_test_mode() ) {
$this->events->update( array( 'status' => 'sandboxed' ), $email );
$this->logger->log( $email, 'sandboxed', __( 'Email sandboxed.', 'gravitysmtp' ) );
return true;
}
$response = wp_safe_remote_post( $this->get_api_url(), $params );
if ( is_wp_error( $response ) ) {
$this->events->update( array( 'status' => 'failed' ), $email );
$this->logger->log( $email, 'failed', $response->get_error_message() );
return $email;
}
$response_body = wp_remote_retrieve_body( $response );
$response_code = wp_remote_retrieve_response_code( $response );
if ( (int) $response_code !== 200 ) {
$this->events->update( array( 'status' => 'failed' ), $email );
$this->logger->log( $email, 'failed', $response_body );
return $email;
}
$this->events->update( array( 'status' => 'sent' ), $email );
$this->logger->log( $email, 'sent', __( 'Email successfully sent.', 'gravitysmtp' ) );
return true;
} catch ( \Exception $e ) {
$this->events->update( array( 'status' => 'failed' ), $email );
$this->logger->log( $email, 'failed', $e->getMessage() );
return false;
}
}
/**
* Get the request parameters for sending email through connector.
*
* @since 1.0
*
* @return array
*/
public function get_request_params() {
$atts = $this->get_send_atts();
$api_key = $this->get_setting( self::SETTING_API_KEY );
if ( ! empty( $atts['headers']['content-type'] ) && strpos( $atts['headers']['content-type'], 'text/html' ) !== false ) {
$content_type = 'text/html';
} else {
$content_type = 'text/plain';
}
$content_type = $this->get_att( 'content_type', $content_type );
$body = [
'from' => $atts['from'],
'subject' => $atts['subject'],
'h:Content-Type' => $content_type
];
if ( ! empty( $atts['reply_to'] ) ) {
$body['h:Reply-To'] = $atts['reply_to'];
}
unset( $atts['headers']['reply-to'] );
if ( $content_type === 'text/html' ) {
$body['html'] = $atts['message'];
} else {
$body['text'] = $atts['message'];
}
$cc = isset( $atts['headers']['cc'] ) ? $atts['headers']['cc']->as_string( true ) : '';
$bcc = isset( $atts['headers']['bcc'] ) ? $atts['headers']['bcc']->as_string( true ) : '';
$recipients = array(
'to' => $atts['to']->as_string( true ),
'cc' => $cc,
'bcc' => $bcc,
);
$body = array_merge( $body, array_filter( $recipients ) );
foreach ( $this->get_filtered_message_headers() as $key => $value ) {
$header_key = sprintf( 'h:%s', $key );
if ( isset( $body[ $header_key ] ) ) {
continue;
}
$body[ $header_key ] = $value;
}
if ( (bool) $this->get_setting( self::SETTING_USE_RETURN_PATH, false ) ) {
$body['sender'] = $atts['from'];
}
$params = [
'body' => $body,
'headers' => $this->get_request_headers( $api_key )
];
if ( ! empty( $atts['attachments'] ) ) {
$params = $this->get_attachments( $params, $atts['attachments'] );
}
return $params;
}
/**
* Get the attributes for sending email.
*
* @since 1.0
*
* @return array
*/
protected function get_send_atts() {
return array(
'to' => $this->get_att( 'to', array() ),
'subject' => $this->get_att( 'subject', '' ),
'message' => $this->get_att( 'message', '' ),
'headers' => $this->get_parsed_headers( $this->get_att( 'headers', array() ) ),
'attachments' => $this->get_att( 'attachments', array() ),
'from' => $this->get_from(),
'reply_to' => $this->get_reply_to(),
);
}
/**
* Get the correct API URL based on region, and include the domain for requests.
*
* @since 1.0
*
* @return string
*/
protected function get_api_url( $endpoint = 'messages', $include_domain = true ) {
$region = $this->get_setting( self::SETTING_REGION, self::OPTION_REGION_US );
$url = $region === self::OPTION_REGION_US ? self::API_URL_US : self::API_URL_EU;
if ( $include_domain ) {
$url .= $this->get_setting( self::SETTING_DOMAIN, '' ) . '/';
}
$url .= $endpoint;
return sanitize_text_field( $url );
}
/**
* Get attachments for this email.
*
* @since 1.0
*
* @param $params
* @param $attachments
*
* @return array
*/
protected function get_attachments( $params, $attachments ) {
$data = array();
$payload = '';
foreach ( $attachments as $custom_name => $attachment ) {
$file = false;
try {
if ( is_file( $attachment ) && is_readable( $attachment ) ) {
$fileName = is_numeric( $custom_name ) ? basename( $attachment ) : $custom_name;
$file = file_get_contents( $attachment );
}
} catch ( \Exception $e ) {
$file = false;
}
if ( $file === false ) {
continue;
}
$data[] = [
'content' => $file,
'name' => $fileName,
];
}
if ( ! empty( $data ) ) {
$boundary = hash( 'sha256', uniqid( '', true ) );
foreach ( $params['body'] as $key => $value ) {
if ( is_array( $value ) ) {
foreach ( $value as $child_key => $child_value ) {
$payload .= '--' . $boundary;
$payload .= "\r\n";
$payload .= 'Content-Disposition: form-data; name="' . $key . "\"\r\n\r\n";
$payload .= $child_value;
$payload .= "\r\n";
}
} else {
$payload .= '--' . $boundary;
$payload .= "\r\n";
$payload .= 'Content-Disposition: form-data; name="' . $key . '"' . "\r\n\r\n";
$payload .= $value;
$payload .= "\r\n";
}
}
foreach ( $data as $key => $attachment ) {
$payload .= '--' . $boundary;
$payload .= "\r\n";
$payload .= 'Content-Disposition: form-data; name="attachment[' . $key . ']"; filename="' . $attachment['name'] . '"' . "\r\n\r\n";
$payload .= $attachment['content'];
$payload .= "\r\n";
}
$payload .= '--' . $boundary . '--';
$params['body'] = $payload;
$params['headers']['Content-Type'] = 'multipart/form-data; boundary=' . $boundary;
$this->attributes['headers']['content-type'] = 'multipart/form-data';
}
return $params;
}
/**
* Get the common headers for making the API request (with API keys).
*
* @since 1.0
*
* @return string[]
*/
protected function get_request_headers( $api_key ) {
return array(
'Authorization' => 'Basic ' . base64_encode( 'api:' . $api_key )
);
}
/**
* Connector data.
*
* @since 1.0
*
* @return array
*/
public function connector_data() {
return array(
self::SETTING_API_KEY => $this->get_setting( self::SETTING_API_KEY, '' ),
self::SETTING_FROM_EMAIL => $this->get_setting( self::SETTING_FROM_EMAIL, '' ),
self::SETTING_FORCE_FROM_EMAIL => $this->get_setting( self::SETTING_FORCE_FROM_EMAIL, false ),
self::SETTING_FROM_NAME => $this->get_setting( self::SETTING_FROM_NAME, '' ),
self::SETTING_FORCE_FROM_NAME => $this->get_setting( self::SETTING_FORCE_FROM_NAME, false ),
self::SETTING_REPLY_TO_EMAIL => $this->get_setting( self::SETTING_REPLY_TO_EMAIL, '' ),
self::SETTING_FORCE_REPLY_TO_EMAIL => $this->get_setting( self::SETTING_FORCE_REPLY_TO_EMAIL, false ),
self::SETTING_REGION => $this->get_setting( self::SETTING_REGION, self::OPTION_REGION_US ),
self::SETTING_DOMAIN => $this->get_setting( self::SETTING_DOMAIN, '' ),
self::SETTING_USE_RETURN_PATH => (bool) $this->get_setting( self::SETTING_USE_RETURN_PATH, false ),
);
}
/**
* Settings fields.
*
* @since 1.0
*
* @return array
*/
public function settings_fields() {
return array(
'title' => esc_html__( 'Mailgun Settings', 'gravitysmtp' ),
'description' => '',
'fields' => array_merge(
array(
array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'Configuration', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => [ 4, 0, 4, 0 ],
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
),
array(
'component' => 'Input',
'props' => array(
'labelAttributes' => array(
'label' => esc_html__( 'Mailgun API Key', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'medium',
),
'helpTextAttributes' => array(
'asHtml' => true,
/* translators: 1: opening anchor tag, 2: closing anchor tag */
'content' => sprintf( __( 'To obtain a %1$sMailgun API Key%2$s, please navigate to the \'Mailgun API Keys\' and generate a key.', 'gravitysmtp' ), '<a class="gform-link gform-typography--size-text-xs" href="https://app.mailgun.com/settings/api_security" target="_blank" rel="noopener noreferrer">', '</a>' ),
'size' => 'text-xs',
'weight' => 'regular',
),
'name' => self::SETTING_API_KEY,
'size' => 'size-l',
'spacing' => 6,
'value' => $this->get_setting( self::SETTING_API_KEY, '' ),
),
),
array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'General Settings', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => 4,
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
),
array(
'component' => 'Toggle',
'props' => array(
'helpTextAttributes' => array(
'content' => esc_html__( 'If Return Path is enabled this adds the return path to the email header which indicates where non-deliverable notifications should be sent. Bounce messages may be lost if not enabled.', 'gravitysmtp' ),
'size' => 'text-xs',
'weight' => 'regular',
'spacing' => [ 2, 0, 0, 0 ],
),
'helpTextWidth' => 'full',
'initialChecked' => (bool) $this->get_setting( self::SETTING_USE_RETURN_PATH, false ),
'labelAttributes' => array(
'label' => esc_html__( 'Return Path', 'gravitysmtp' ),
),
'labelPosition' => 'left',
'name' => self::SETTING_USE_RETURN_PATH,
'size' => 'size-m',
'spacing' => 5,
'width' => 'full',
),
),
array(
'component' => 'Box',
'props' => array(
'display' => 'block',
'spacing' => 6,
),
'fields' => array(
array(
'component' => 'Label',
'props' => array(
'label' => __( 'Region', 'gravitysmtp' ),
'weight' => 'medium',
'size' => 'text-sm',
'spacing' => 2,
),
),
array(
'component' => 'InputGroup',
'props' => array(
'customAttributes' => array(
'style' => array(
'display' => 'flex',
),
),
'id' => self::SETTING_REGION . '_group',
'initialValue' => $this->get_setting( self::SETTING_REGION, self::OPTION_REGION_US ),
'inputType' => 'radio',
'spacing' => 2,
'data' => array(
array(
'id' => self::SETTING_REGION . '_' . self::OPTION_REGION_US,
'name' => self::SETTING_REGION,
'value' => self::OPTION_REGION_US,
'size' => 'size-md',
'spacing' => array( 0, 4, 0, 0 ),
'labelAttributes' => array(
'label' => __( 'US', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'regular',
),
),
array(
'id' => self::SETTING_REGION . '_' . self::OPTION_REGION_EU,
'name' => self::SETTING_REGION,
'value' => self::OPTION_REGION_EU,
'size' => 'size-md',
'spacing' => array( 0, 4, 0, 0 ),
'labelAttributes' => array(
'label' => __( 'EU', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'regular',
),
),
),
),
),
array(
'component' => 'Text',
'props' => array(
'asHtml' => true,
/* translators: 1: opening anchor tag, 2: closing anchor tag */
'content' => sprintf( __( 'Choose your message sending endpoint. If subject to EU regulations, consider the EU region. For more information, visit %1$sMailgun.com%2$s.', 'gravitysmtp' ), '<a class="gform-link gform-typography--size-text-xs" href="https://www.mailgun.com/regions" target="_blank" rel="noopener noreferrer">', '</a>' ),
'size' => 'text-xs',
'weight' => 'regular',
),
),
),
),
),
$this->get_from_settings_fields(),
$this->get_reply_to_settings_fields(),
array(
array(
'component' => 'Input',
'props' => array(
'labelAttributes' => array(
'label' => esc_html__( 'Sending Domain', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'medium',
),
'helpTextAttributes' => array(
'asHtml' => true,
/* translators: 1: opening anchor tag, 2: closing anchor tag */
'content' => sprintf( __( 'Verify your Mailgun domain name. %1$sView domains%2$s.', 'gravitysmtp' ), '<a class="gform-link gform-typography--size-text-xs" href="https://login.mailgun.com/login" target="_blank" rel="noopener noreferrer">', '</a>' ),
'size' => 'text-xs',
'weight' => 'regular',
),
'name' => self::SETTING_DOMAIN,
'size' => 'size-l',
'spacing' => 6,
'value' => $this->get_setting( self::SETTING_DOMAIN, '' ),
),
),
),
),
);
}
public function migration_map() {
return array(
array(
'original_key' => 'gravityformsaddon_gravityformsmailgun_settings',
'sub_key' => 'apiKey',
'new_key' => self::SETTING_API_KEY,
),
array(
'original_key' => 'gravityformsaddon_gravityformsmailgun_settings',
'sub_key' => 'region',
'new_key' => self::SETTING_REGION,
)
);
}
/**
* Verify the API key with the API.
*
* @since 1.0
*
* @return true|\WP_Error
*/
private function verify_api_key() {
$api_key = $this->get_setting( self::SETTING_API_KEY );
$url = $this->get_api_url( 'messages', true );
if ( empty( $api_key ) ) {
return new \WP_Error( 'missing_api_key', __( 'No API Key provided.', 'gravitysmtp' ) );
}
$data = array(
'headers' => $this->get_request_headers( $api_key ),
'body' => array(
"from" => "string",
"to" => "string",
"subject" => "string",
"html" => "string",
),
);
$request = wp_remote_post( $url, $data );
$code = wp_remote_retrieve_response_code( $request );
if ( (int) $code === 401 ) {
return new \WP_Error( 'invalid_api_key', __( 'Invalid API Key provided.', 'gravitysmtp' ) );
}
return true;
}
/**
* Verify the sending domain with the API.
*
* @since 1.0
*
* @return true|\WP_Error
*/
private function verify_domain() {
$api_key = $this->get_setting( self::SETTING_API_KEY );
$url = $this->get_api_url( 'domains/' . $this->get_setting( self::SETTING_DOMAIN ), false );
$data = array(
'headers' => $this->get_request_headers( $api_key ),
);
$result = wp_remote_get( $url, $data );
if ( wp_remote_retrieve_response_code( $result ) == '404' ) {
return new \WP_Error( 'invalid_domain', __( 'Invalid sending domain provided.', 'gravitysmtp' ) );
}
return true;
}
/**
* Determine if the API credentials are configured correctly.
*
* @since 1.0
*
* @return bool|\WP_Error
*/
public function is_configured() {
$valid_api = $this->verify_api_key();
if ( is_wp_error( $valid_api ) ) {
self::$configured = $valid_api;
return $valid_api;
}
$valid_domain = $this->verify_domain();
if ( is_wp_error( $valid_domain ) ) {
self::$configured = $valid_domain;
return $valid_domain;
}
self::$configured = true;
return true;
}
}
@@ -0,0 +1,435 @@
<?php
namespace Gravity_Forms\Gravity_SMTP\Connectors\Types;
use Exception;
use Gravity_Forms\Gravity_SMTP\Connectors\Connector_Base;
use Gravity_Forms\Gravity_SMTP\Feature_Flags\Feature_Flag_Manager;
use WP_Error;
/**
* Connector for Mailjet
*
* @since 1.0
*/
class Connector_Mailjet extends Connector_Base {
const SETTING_API_KEY = 'api_key';
const SETTING_API_SECRET = 'api_secret';
protected $name = 'mailjet';
protected $title = 'Mailjet';
protected $disabled = true;
protected $logo = 'Mailjet';
protected $full_logo = 'MailjetFull';
protected $url = 'https://api.mailjet.com/v3.1';
protected $sensitive_fields = array(
self::SETTING_API_KEY,
self::SETTING_API_SECRET,
);
public function get_description() {
return esc_html__( 'Mailjet is a powerful transactional email system built for developers but designed so non-tech teams can contribute without coding.', 'gravitysmtp' );
}
/**
* Sends email via Mailjet.
*
* @since 1.0
*
* @return int returns the email ID
*/
public function send() {
try {
$atts = $this->get_send_atts();
$source = $this->get_att( 'source' );
$params = $this->get_request_params();
$email = $this->email;
$this->set_email_log_data( $atts['subject'], $atts['message'], $atts['to'], $atts['from']['from'], $atts['headers'], $atts['attachments'], $source, $params );
$this->logger->log( $email, 'started', __( 'Starting email send for Mailjet connector.', 'gravitysmtp' ) );
if ( $this->is_test_mode() ) {
$this->events->update( array( 'status' => 'sandboxed' ), $email );
$this->logger->log( $email, 'sandboxed', __( 'Email sandboxed.', 'gravitysmtp' ) );
return true;
}
$response = wp_safe_remote_post( $this->url . '/send', $params );
$is_success = in_array( (int) wp_remote_retrieve_response_code( $response ), array( 200, 201, 202 ) );
if ( ! $is_success ) {
$this->log_failure( $email, wp_remote_retrieve_body( $response ) );
return $email;
}
$this->events->update( array( 'status' => 'sent' ), $email );
$this->logger->log( $email, 'sent', __( 'Email successfully sent.', 'gravitysmtp' ) );
return true;
} catch ( Exception $e ) {
$this->log_failure( $email, $e->getMessage() );
return $email;
}
}
/**
* Get the request parameters for sending email through connector.
*
* @since 1.0
*
* @return array
*/
public function get_request_params() {
$atts = $this->get_send_atts();
$api_key = $this->get_setting( self::SETTING_API_KEY );
$api_secret = $this->get_setting( self::SETTING_API_SECRET );
$message = array(
'To' => array(),
'From' => array(
'Email' => $atts['from']['email'],
'Name' => $atts['from']['name'],
),
'Subject' => $atts['subject'],
);
foreach ( $atts['to']->as_array() as $recipient ) {
$to_value = array(
'Email' => $recipient['email'],
);
if ( ! empty( $recipient['name'] ) ) {
$to_value['Name'] = $recipient['name'];
}
$message['To'][] = $to_value;
}
// Setting content
$is_html = ! empty( $atts['headers']['content-type'] ) && strpos( $atts['headers']['content-type'], 'text/html' ) !== false;
if ( $is_html ) {
$message['HTMLPart'] = $atts['message'];
// Strip tags from plaintext
$text_body = wp_strip_all_tags( $atts['message'] );
// Remove leftover double-linebreaks from plaintext.
$text_body = preg_replace( "/([\r\n]{2,}|[\n]{2,}|[\r]{2,}|[\r\t]{2,}|[\n\t]{2,})/", "\n", $text_body );
$message['TextPart'] = $text_body;
} else {
$message['TextPart'] = $atts['message'];
}
// Setting cc
if ( ! empty( $atts['headers']['cc'] ) ) {
$message['Cc'] = array();
foreach ( $atts['headers']['cc']->as_array() as $cc_value ) {
$values = array(
'Email' => $cc_value['email'],
'Name' => ! empty( $cc_value['name'] ) ? $cc_value['name'] : null,
);
$message['Cc'][] = array_filter( $values );
}
}
// Setting Bcc
if ( ! empty( $atts['headers']['bcc'] ) ) {
$message['Bcc'] = array();
foreach ( $atts['headers']['bcc']->as_array() as $bcc_value ) {
$values = array(
'Email' => $bcc_value['email'],
'Name' => ! empty( $bcc_value['name'] ) ? $bcc_value['name'] : null,
);
$message['Bcc'][] = array_filter( $values );
}
}
// Setting reply to
if ( ! empty( $atts['reply_to'] ) ) {
if ( isset( $atts['reply_to']['email'] ) ) {
$reply_to = $atts['reply_to'];
} else {
$reply_to = $atts['reply_to'][0];
}
$message['Headers'] = array();
$message['Headers']['Reply-To'] = $reply_to['email'];
}
// Setting attachments
if ( ! empty( $atts['attachments'] ) ) {
$message['attachments'] = $this->get_attachments( $atts['attachments'] );
}
$body = array(
'Messages' => array(
$message,
),
);
return array(
'body' => json_encode( $body ),
'headers' => $this->get_request_headers( $api_key, $api_secret ),
);
}
/**
* Get the attributes for sending email.
*
* @since 1.0
*
* @return array
*/
protected function get_send_atts() {
$headers = $this->get_parsed_headers( $this->get_att( 'headers', array() ) );
if ( ! empty( $headers['content-type'] ) ) {
$headers['content-type'] = $this->get_att( 'content_type', $headers['content-type'] );
}
return array(
'to' => $this->get_att( 'to', '' ),
'subject' => $this->get_att( 'subject', '' ),
'message' => $this->get_att( 'message', '' ),
'headers' => $headers,
'attachments' => $this->get_att( 'attachments', array() ),
'from' => $this->get_from( true ),
'reply_to' => $this->get_reply_to( true ),
);
}
/**
* Gets a list of attachments, and returns them in a format that can be used by the API.
*
* @param array $attachments the list of attachments
*
* @since 1.0
*
* @return array Returns an array of attachments. Each item with a 'content' and 'name' property.
*/
protected function get_attachments( $attachments ) {
$data = array();
foreach ( $attachments as $custom_name => $attachment ) {
try {
if ( is_file( $attachment ) && is_readable( $attachment ) ) {
$fileName = is_numeric( $custom_name ) ? basename( $attachment ) : $custom_name;
$content = base64_encode( file_get_contents( $attachment ) );
$data[] = array(
'Filename' => $fileName,
'Base64Content' => $content,
'ContentType' => mime_content_type( $attachment ),
);
}
} catch ( Exception $e ) {
continue;
}
}
return $data;
}
/**
* Gets the headers to be used in the API request.
*
* @since 1.0
*
* @return array returns the header array to be passed to Mailjet's API
*/
protected function get_request_headers( $api_key, $api_secret ) {
return array(
'content-type' => 'application/json',
'accept' => 'application/json',
'Authorization' => 'Basic ' . base64_encode( $api_key . ':' . $api_secret ),
);
}
/**
* Logs an email send failure.
*
* @since 1.0
*
* @param string $email the email that failed
* @param string $error_message the error message
*/
private function log_failure( $email, $error_message ) {
$this->events->update( array( 'status' => 'failed' ), $email );
$this->logger->log( $email, 'failed', $error_message );
}
/**
* Connector data.
*
* @since 1.0
*
* @return array
*/
public function connector_data() {
return array(
self::SETTING_API_KEY => $this->get_setting( self::SETTING_API_KEY, '' ),
self::SETTING_API_SECRET => $this->get_setting( self::SETTING_API_SECRET, '' ),
self::SETTING_FROM_EMAIL => $this->get_setting( self::SETTING_FROM_EMAIL, '' ),
self::SETTING_FORCE_FROM_EMAIL => $this->get_setting( self::SETTING_FORCE_FROM_EMAIL, false ),
self::SETTING_FROM_NAME => $this->get_setting( self::SETTING_FROM_NAME, '' ),
self::SETTING_FORCE_FROM_NAME => $this->get_setting( self::SETTING_FORCE_FROM_NAME, false ),
self::SETTING_REPLY_TO_EMAIL => $this->get_setting( self::SETTING_REPLY_TO_EMAIL, '' ),
self::SETTING_FORCE_REPLY_TO_EMAIL => $this->get_setting( self::SETTING_FORCE_REPLY_TO_EMAIL, false ),
);
}
/**
* Settings fields.
*
* @since 1.0
*
* @return array
*/
public function settings_fields() {
return array(
'title' => esc_html__( 'Mailjet Settings', 'gravitysmtp' ),
'description' => '',
'fields' => array_merge(
array(
array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'Configuration', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => array( 4, 0, 4, 0 ),
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
),
array(
'component' => 'Input',
'props' => array(
'labelAttributes' => array(
'label' => esc_html__( 'API Key', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'medium',
),
'helpTextAttributes' => array(
'asHtml' => true,
/* translators: 1: opening anchor tag, 2: closing anchor tag */
'content' => sprintf( __( 'To generate an API key from Mailjet, log in to your Mailjet dashboard and navigate to the API section and then %1$sgenerate your API key%2$s.', 'gravitysmtp' ), '<a class="gform-link gform-typography--size-text-xs" href="https://app.mailjet.com/account/apikeys" target="_blank" rel="noopener noreferrer">', '</a>' ),
'size' => 'text-xs',
'weight' => 'regular',
),
'name' => self::SETTING_API_KEY,
'size' => 'size-l',
'spacing' => 6,
'value' => $this->get_setting( self::SETTING_API_KEY, '' ),
),
),
array(
'component' => 'Input',
'props' => array(
'labelAttributes' => array(
'label' => esc_html__( 'API Secret Key', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'medium',
),
'name' => self::SETTING_API_SECRET,
'size' => 'size-l',
'spacing' => 6,
'value' => $this->get_setting( self::SETTING_API_SECRET, '' ),
),
),
array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'General Settings', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => 4,
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
),
),
$this->get_from_settings_fields(),
$this->get_reply_to_settings_fields(),
),
);
}
/**
* Determine if the API credentials are configured correctly.
*
* @since 1.0
*
* @return bool|WP_Error returns true if configured, or a WP_Error object if not
*/
public function is_configured() {
$valid_api = $this->verify_api_key();
if ( is_wp_error( $valid_api ) ) {
self::$configured = $valid_api;
return $valid_api;
}
self::$configured = true;
return true;
}
/**
* Verify the API key with the API.
*
* @since 1.0
*
* @return true|WP_Error
*/
private function verify_api_key() {
$api_key = $this->get_setting( self::SETTING_API_KEY, '' );
$api_secret = $this->get_setting( self::SETTING_API_SECRET, '' );
$url = 'https://api.mailjet.com/v3/REST/geostatistics';
if ( empty( $api_key ) || empty( $api_secret ) ) {
return new WP_Error( 'missing_api_key', __( 'No API Key or Secret provided.', 'gravitysmtp' ) );
}
$response = wp_remote_get(
$url,
array(
'headers' => $this->get_request_headers( $api_key, $api_secret ),
)
);
if ( wp_remote_retrieve_response_code( $response ) != '200' ) {
return new WP_Error( 'invalid_api_key', __( 'Invalid API Key provided.', 'gravitysmtp' ) );
}
return true;
}
/**
* Get the unique data for this connector, merged with the default/common data for all
* connectors in the system.
*
* @since 1.0
*
* @return array
*/
protected function get_merged_data() {
$data = parent::get_merged_data();
$data['disabled'] = ! Feature_Flag_Manager::is_enabled( 'mailjet_integration' );
return $data;
}
}
@@ -0,0 +1,545 @@
<?php
namespace Gravity_Forms\Gravity_SMTP\Connectors\Types;
use Exception;
use Gravity_Forms\Gravity_SMTP\Connectors\Connector_Base;
use Gravity_Forms\Gravity_SMTP\Feature_Flags\Feature_Flag_Manager;
use WP_Error;
/**
* Connector for Mailtrap
*
* @since 1.9.5
*/
class Connector_Mailtrap extends Connector_Base {
const SETTING_API_KEY = 'api_key';
const SETTING_SANDBOX_ENABLED = 'sandbox_enabled';
const SETTING_INBOX_ID = 'inbox_id';
protected $name = 'mailtrap';
protected $title = 'Mailtrap';
protected $disabled = true;
protected $logo = 'Mailtrap';
protected $full_logo = 'MailtrapFull';
protected $sensitive_fields = array(
self::SETTING_API_KEY,
);
/**
* Get the description for this connector.
*
* @since 1.9.5
*
* @return string
*/
public function get_description() {
return esc_html__( 'Mailtrap is an email delivery platform for businesses and individuals to test, send, and control email infrastructure in one place.', 'gravitysmtp' );
}
/**
* Sends email via Mailtrap.
*
* @since 1.9.5
*
* @return bool|int Returns true on success, or the email ID on failure.
*/
public function send() {
try {
$atts = $this->get_send_atts();
$source = $this->get_att( 'source' );
$params = $this->get_request_params();
$email = $this->email;
$this->set_email_log_data( $atts['subject'], $atts['message'], $atts['to'], $atts['from']['from'], $atts['headers'], $atts['attachments'], $source, $params );
$this->logger->log( $email, 'started', __( 'Starting email send for Mailtrap connector.', 'gravitysmtp' ) );
if ( $this->is_test_mode() ) {
$this->events->update( array( 'status' => 'sandboxed' ), $email );
$this->logger->log( $email, 'sandboxed', __( 'Email sandboxed.', 'gravitysmtp' ) );
return true;
}
$url = $this->get_send_url();
$response = wp_safe_remote_post( $url, $params );
$response_code = (int) wp_remote_retrieve_response_code( $response );
$is_success = in_array( $response_code, array( 200, 201, 202 ) );
if ( ! $is_success ) {
$body = wp_remote_retrieve_body( $response );
$error_message = $this->get_api_error_message( $response_code, $body );
$this->log_failure( $email, $error_message );
return $email;
}
$this->events->update( array( 'status' => 'sent' ), $email );
$this->logger->log( $email, 'sent', __( 'Email successfully sent.', 'gravitysmtp' ) );
return true;
} catch ( Exception $e ) {
$this->log_failure( $email, $e->getMessage() );
return $email;
}
}
/**
* Get the URL to send email to, switching between transactional and sandbox endpoints.
*
* @since 1.9.5
*
* @return string
*/
private function get_send_url() {
$sandbox_enabled = (bool) $this->get_setting( self::SETTING_SANDBOX_ENABLED, false );
if ( $sandbox_enabled ) {
$inbox_id = absint( $this->get_setting( self::SETTING_INBOX_ID, '' ) );
// Sandbox endpoint requires the inbox ID in the URL path.
return sprintf( 'https://sandbox.api.mailtrap.io/api/send/%d', $inbox_id );
}
return 'https://send.api.mailtrap.io/api/send';
}
/**
* Get the request parameters for sending email through connector.
*
* @since 1.9.5
*
* @return array
*/
public function get_request_params() {
$atts = $this->get_send_atts();
$api_key = $this->get_setting( self::SETTING_API_KEY );
$body = array(
'from' => array(
'email' => $atts['from']['email'],
'name' => isset( $atts['from']['name'] ) ? $atts['from']['name'] : '',
),
'subject' => $atts['subject'],
);
// Build to recipients as array of objects.
$to_value = array();
foreach ( $atts['to']->as_array() as $recipient ) {
$to_entry = array( 'email' => $recipient['email'] );
if ( ! empty( $recipient['name'] ) ) {
$to_entry['name'] = $recipient['name'];
}
$to_value[] = $to_entry;
}
$body['to'] = $to_value;
// Set content — Mailtrap accepts both html and text fields.
$is_html = ! empty( $atts['headers']['content-type'] ) && strpos( $atts['headers']['content-type'], 'text/html' ) !== false;
if ( $is_html ) {
$body['html'] = $atts['message'];
// Strip tags for plain text fallback.
$text_body = wp_strip_all_tags( $atts['message'] );
// Remove leftover double-linebreaks from plaintext.
$text_body = preg_replace( "/([\r\n]{2,}|[\n]{2,}|[\r]{2,}|[\r\t]{2,}|[\n\t]{2,})/", "\n", $text_body );
$body['text'] = $text_body;
} else {
$body['text'] = $atts['message'];
}
// CC recipients.
if ( ! empty( $atts['headers']['cc'] ) ) {
$cc_value = array();
foreach ( $atts['headers']['cc']->as_array() as $cc_recipient ) {
$cc_entry = array( 'email' => $cc_recipient['email'] );
if ( ! empty( $cc_recipient['name'] ) ) {
$cc_entry['name'] = $cc_recipient['name'];
}
$cc_value[] = $cc_entry;
}
$body['cc'] = $cc_value;
}
// BCC recipients.
if ( ! empty( $atts['headers']['bcc'] ) ) {
$bcc_value = array();
foreach ( $atts['headers']['bcc']->as_array() as $bcc_recipient ) {
$bcc_entry = array( 'email' => $bcc_recipient['email'] );
if ( ! empty( $bcc_recipient['name'] ) ) {
$bcc_entry['name'] = $bcc_recipient['name'];
}
$bcc_value[] = $bcc_entry;
}
$body['bcc'] = $bcc_value;
}
// Reply-to.
if ( ! empty( $atts['reply_to'] ) ) {
if ( isset( $atts['reply_to']['email'] ) ) {
$reply_to = $atts['reply_to'];
} else {
$reply_to = $atts['reply_to'][0];
}
$reply_to_entry = array( 'email' => $reply_to['email'] );
if ( ! empty( $reply_to['name'] ) ) {
$reply_to_entry['name'] = $reply_to['name'];
}
$body['reply_to'] = $reply_to_entry;
}
// Attachments.
if ( ! empty( $atts['attachments'] ) ) {
$body['attachments'] = $this->get_attachments( $atts['attachments'] );
}
return array(
'body' => json_encode( $body ),
'headers' => $this->get_request_headers( $api_key ),
);
}
/**
* Get the attributes for sending email.
*
* @since 1.9.5
*
* @return array
*/
protected function get_send_atts() {
$headers = $this->get_parsed_headers( $this->get_att( 'headers', array() ) );
if ( ! empty( $headers['content-type'] ) ) {
$headers['content-type'] = $this->get_att( 'content_type', $headers['content-type'] );
}
return array(
'to' => $this->get_att( 'to', '' ),
'subject' => $this->get_att( 'subject', '' ),
'message' => $this->get_att( 'message', '' ),
'headers' => $headers,
'attachments' => $this->get_att( 'attachments', array() ),
'from' => $this->get_from( true ),
'reply_to' => $this->get_reply_to( true ),
);
}
/**
* Gets a list of attachments formatted for the Mailtrap API.
*
* @since 1.9.5
*
* @param array $attachments The list of attachments.
*
* @return array Returns an array of attachments with filename, content, type, and disposition.
*/
protected function get_attachments( $attachments ) {
$data = array();
foreach ( $attachments as $custom_name => $attachment ) {
try {
if ( is_file( $attachment ) && is_readable( $attachment ) ) {
$file_name = is_numeric( $custom_name ) ? basename( $attachment ) : $custom_name;
$content = base64_encode( file_get_contents( $attachment ) );
$mime_type = mime_content_type( $attachment );
$data[] = array(
'filename' => $file_name,
'content' => $content,
'type' => $mime_type,
'disposition' => 'attachment',
);
}
} catch ( Exception $e ) {
continue;
}
}
return $data;
}
/**
* Gets the headers to be used in the API request.
*
* @since 1.9.5
*
* @param string $api_key The Mailtrap API token.
*
* @return array Returns the header array to be passed to Mailtrap's API.
*/
protected function get_request_headers( $api_key ) {
return array(
'Content-Type' => 'application/json',
'Api-Token' => $api_key,
);
}
/**
* Logs an email send failure.
*
* @since 1.9.5
*
* @param string $email The email log ID.
* @param string $error_message The error message.
*/
private function log_failure( $email, $error_message ) {
$this->events->update( array( 'status' => 'failed' ), $email );
$this->logger->log( $email, 'failed', $error_message );
}
/**
* Parses an API error response into a human-readable message.
*
* @since 1.9.5
*
* @param int $response_code The HTTP response code.
* @param string $body The raw response body.
*
* @return string The error message.
*/
private function get_api_error_message( $response_code, $body ) {
// Rate limit exceeded — surface a clear message so admins know to wait.
if ( $response_code === 429 ) {
return __( 'Mailtrap rate limit exceeded. Please wait before sending again.', 'gravitysmtp' );
}
$decoded = json_decode( $body, true );
if ( ! empty( $decoded['errors'] ) && is_array( $decoded['errors'] ) ) {
return implode( ' ', $decoded['errors'] );
}
return $body;
}
/**
* Connector data.
*
* @since 1.9.5
*
* @return array
*/
public function connector_data() {
return array(
self::SETTING_API_KEY => $this->get_setting( self::SETTING_API_KEY, '' ),
self::SETTING_SANDBOX_ENABLED => $this->get_setting( self::SETTING_SANDBOX_ENABLED, false ),
self::SETTING_INBOX_ID => $this->get_setting( self::SETTING_INBOX_ID, '' ),
self::SETTING_FROM_EMAIL => $this->get_setting( self::SETTING_FROM_EMAIL, '' ),
self::SETTING_FORCE_FROM_EMAIL => $this->get_setting( self::SETTING_FORCE_FROM_EMAIL, false ),
self::SETTING_FROM_NAME => $this->get_setting( self::SETTING_FROM_NAME, '' ),
self::SETTING_FORCE_FROM_NAME => $this->get_setting( self::SETTING_FORCE_FROM_NAME, false ),
self::SETTING_REPLY_TO_EMAIL => $this->get_setting( self::SETTING_REPLY_TO_EMAIL, '' ),
self::SETTING_FORCE_REPLY_TO_EMAIL => $this->get_setting( self::SETTING_FORCE_REPLY_TO_EMAIL, false ),
);
}
/**
* Settings fields.
*
* @since 1.9.5
*
* @return array
*/
public function settings_fields() {
$sandbox_enabled = (bool) $this->get_setting( self::SETTING_SANDBOX_ENABLED, false );
$fields = array(
'title' => esc_html__( 'Mailtrap Settings', 'gravitysmtp' ),
'description' => '',
'fields' => array_merge(
array(
array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'Configuration', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => array( 4, 0, 4, 0 ),
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
),
array(
'component' => 'Input',
'props' => array(
'labelAttributes' => array(
'label' => esc_html__( 'API Token', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'medium',
),
'helpTextAttributes' => array(
'asHtml' => true,
/* translators: 1: opening anchor tag, 2: closing anchor tag */
'content' => sprintf( __( 'To obtain an API token from Mailtrap, log in to your %1$sMailtrap dashboard%2$s and navigate to API Tokens under your account settings.', 'gravitysmtp' ), '<a class="gform-link gform-typography--size-text-xs" href="https://mailtrap.io/api-tokens" target="_blank" rel="noopener noreferrer">', '</a>' ),
'size' => 'text-xs',
'weight' => 'regular',
),
'name' => self::SETTING_API_KEY,
'size' => 'size-l',
'spacing' => 6,
'value' => $this->get_setting( self::SETTING_API_KEY, '' ),
),
),
array(
'component' => 'Toggle',
'props' => array(
'helpTextAttributes' => array(
'content' => esc_html__( 'When enabled, emails are sent to your Mailtrap sandbox inbox instead of real recipients. Requires a Sandbox Inbox ID.', 'gravitysmtp' ),
'size' => 'text-xs',
'weight' => 'regular',
),
'initialChecked' => $sandbox_enabled,
'labelAttributes' => array(
'label' => esc_html__( 'Enable Sandbox Mode', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'medium',
),
'labelPosition' => 'left',
'name' => self::SETTING_SANDBOX_ENABLED,
'size' => 'size-m',
'spacing' => 6,
'width' => 'full',
),
),
array(
'component' => 'Input',
'props' => array(
'labelAttributes' => array(
'label' => esc_html__( 'Sandbox Inbox ID', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'medium',
),
'helpTextAttributes' => array(
'asHtml' => true,
/* translators: 1: opening anchor tag, 2: closing anchor tag */
'content' => sprintf( __( 'Enter the numeric Inbox ID from your %1$sMailtrap sandbox%2$s. Required when Sandbox Mode is enabled.', 'gravitysmtp' ), '<a class="gform-link gform-typography--size-text-xs" href="https://mailtrap.io/sandboxes" target="_blank" rel="noopener noreferrer">', '</a>' ),
'size' => 'text-xs',
'weight' => 'regular',
),
'name' => self::SETTING_INBOX_ID,
'size' => 'size-l',
'spacing' => 6,
'value' => $this->get_setting( self::SETTING_INBOX_ID, '' ),
),
),
array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'General Settings', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => 4,
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
),
),
$this->get_from_settings_fields(),
$this->get_reply_to_settings_fields(),
),
);
return $fields;
}
/**
* Determine if the API credentials are configured correctly.
*
* @since 1.9.5
*
* @return bool|WP_Error Returns true if configured, or a WP_Error object if not.
*/
public function is_configured() {
$sandbox_enabled = (bool) $this->get_setting( self::SETTING_SANDBOX_ENABLED, false );
// When sandbox mode is on, inbox_id is required for sending to work.
if ( $sandbox_enabled ) {
$inbox_id = $this->get_setting( self::SETTING_INBOX_ID, '' );
if ( empty( $inbox_id ) ) {
$error = new WP_Error( 'missing_inbox_id', __( 'Sandbox Inbox ID is required when Sandbox Mode is enabled.', 'gravitysmtp' ) );
self::$configured = $error;
return $error;
}
}
$valid_api = $this->verify_api_key();
if ( is_wp_error( $valid_api ) ) {
self::$configured = $valid_api;
return $valid_api;
}
self::$configured = true;
return true;
}
/**
* Verify the API token with the Mailtrap accounts endpoint.
*
* @since 1.9.5
*
* @return true|WP_Error
*/
private function verify_api_key() {
$api_key = $this->get_setting( self::SETTING_API_KEY, '' );
if ( empty( $api_key ) ) {
return new WP_Error( 'missing_api_key', __( 'No API Token provided.', 'gravitysmtp' ) );
}
$response = wp_remote_get(
'https://mailtrap.io/api/accounts',
array(
'headers' => $this->get_request_headers( $api_key ),
)
);
if ( wp_remote_retrieve_response_code( $response ) != '200' ) {
return new WP_Error( 'invalid_api_key', __( 'Invalid API Token provided.', 'gravitysmtp' ) );
}
return true;
}
/**
* Get the unique data for this connector, merged with the default/common data for all
* connectors in the system.
*
* @since 1.9.5
*
* @return array
*/
protected function get_merged_data() {
$data = parent::get_merged_data();
$data['disabled'] = ! Feature_Flag_Manager::is_enabled( 'mailtrap_integration' );
return $data;
}
}
@@ -0,0 +1,634 @@
<?php
namespace Gravity_Forms\Gravity_SMTP\Connectors\Types;
use Gravity_Forms\Gravity_SMTP\Connectors\Connector_Base;
use Gravity_Forms\Gravity_SMTP\Connectors\Connector_Service_Provider;
use Gravity_Forms\Gravity_SMTP\Connectors\Oauth\Google_Oauth_Handler;
use Gravity_Forms\Gravity_SMTP\Connectors\Oauth\Microsoft_Oauth_Handler;
use Gravity_Forms\Gravity_SMTP\Gravity_SMTP;
use Gravity_Forms\Gravity_SMTP\Utils\Booliesh;
/**
* Connector for 365 / Outlook
*
* @since 1.0
*/
class Connector_Microsoft extends Connector_Base {
const SETTING_ACCESS_TOKEN = 'access_token';
const SETTING_CLIENT_ID = 'client_id';
const SETTING_CLIENT_SECRET = 'client_secret';
const SETTING_USE_RETURN_PATH = 'use_return_path';
const VALUE_REDIRECT_URI = 'redirect_uri';
const VALUE_REDIRECT_URI_FULL = 'redirect_uri_full';
protected $name = 'microsoft';
protected $title = 'Microsoft';
protected $disabled = true;
protected $logo = 'Microsoft';
protected $full_logo = 'MicrosoftFull';
public function get_description() {
return __( "Deliver emails with confidence using Microsoft 365 / Outlook. Connect to Microsofts API to securely authenticate and send any emails or form notifications from your website.", 'gravitysmtp' );
}
protected $sensitive_fields = array(
self::SETTING_ACCESS_TOKEN,
self::SETTING_CLIENT_SECRET,
);
/**
* Sending logic.
*
* @since 1.0
*
* @return bool
*/
public function send() {
$to = $this->get_att( 'to', '' );
$subject = $this->get_att( 'subject', '' );
$message = $this->get_att( 'message', '' );
$headers = $this->get_parsed_headers( $this->get_att( 'headers', array() ) );
$attachments = $this->get_att( 'attachments', array() );
$from = $this->get_from( true );
$reply_to = $this->get_reply_to( true );
$source = $this->get_att( 'source' );
$params = $this->get_request_params();
$email = $this->email;
$this->reset_phpmailer();
if ( ! empty( $headers['content-type'] ) ) {
$headers['content-type'] = $this->get_att( 'content_type', $headers['content-type'] );
}
$this->set_email_log_data( $subject, $message, $to, empty( $from['name'] ) ? $from['email'] : sprintf( '%s <%s>', $from['name'], $from['email'] ), $headers, $attachments, $source, $params );
$this->logger->log( $email, 'started', __( 'Starting email send for Microsoft connector.', 'gravitysmtp' ) );
$this->php_mailer->setFrom( $from['email'], isset( $from['name'] ) ? $from['name'] : '' );
foreach ( $to->recipients() as $recipient ) {
if ( ! empty( $recipient->name() ) ) {
$this->php_mailer->addAddress( $recipient->email(), $recipient->name() );
} else {
$this->php_mailer->addAddress( $recipient->email() );
}
}
$this->php_mailer->Subject = $subject;
$this->php_mailer->Body = $message;
if ( ! empty( $headers['cc'] ) ) {
foreach ( $headers['cc']->recipients() as $recipient ) {
if ( ! empty( $recipient->name() ) ) {
$this->php_mailer->addCC( $recipient->email(), $recipient->name() );
} else {
$this->php_mailer->addCC( $recipient->email() );
}
}
}
if ( ! empty( $headers['bcc'] ) ) {
foreach ( $headers['bcc']->recipients() as $recipient ) {
if ( ! empty( $recipient->name() ) ) {
$this->php_mailer->addBCC( $recipient->email(), $recipient->name() );
} else {
$this->php_mailer->addBCC( $recipient->email() );
}
}
}
if ( ! empty( $attachments ) ) {
foreach ( $attachments as $attachment ) {
$this->php_mailer->addAttachment( $attachment );
}
}
if ( ! empty( $reply_to ) ) {
foreach( $reply_to as $address ) {
if ( isset( $address['name'] ) ) {
$this->php_mailer->addReplyTo( $address['email'], $address['name'] );
} else {
$this->php_mailer->addReplyTo( $address['email'] );
}
}
}
if ( ! empty( $headers['content-type'] ) && strpos( $headers['content-type'], 'text/html' ) !== false ) {
$this->php_mailer->isHTML( true );
} else {
$this->php_mailer->isHTML( false );
$this->php_mailer->ContentType = 'text/plain';
}
$this->php_mailer->CharSet = 'UTF-8';
$additional_headers = $this->get_filtered_message_headers();
if ( ! empty( $additional_headers ) ) {
foreach ( $additional_headers as $key => $value ) {
$this->php_mailer->addCustomHeader( $key, $value );
}
}
if ( (bool) $this->get_setting( self::SETTING_USE_RETURN_PATH, false ) ) {
$this->php_mailer->Sender = $this->php_mailer->From;
}
if ( $this->is_test_mode() ) {
$this->events->update( array( 'status' => 'sandboxed' ), $email );
$this->logger->log( $email, 'sandboxed', __( 'Email sandboxed.', 'gravitysmtp' ) );
return true;
}
try {
$debug_atts = compact( 'to', 'from', 'subject', 'headers', 'source', 'attachments', 'reply_to' );
$this->debug_logger->log_debug( $this->wrap_debug_with_details( __FUNCTION__, $email, 'Attempting send with the following attributes: ' . json_encode( $debug_atts ) ) );
$raw = $this->get_raw_message();
/**
* @var Microsoft_Oauth_Handler $oauth_handler
*/
$oauth_handler = Gravity_SMTP::container()->get( Connector_Service_Provider::MICROSOFT_OAUTH_HANDLER );
$token = $oauth_handler->get_access_token();
if ( is_wp_error( $token ) ) {
$this->events->update( array( 'status' => 'failed' ), $email );
$this->logger->log( $email, 'failed', $token->get_error_message() );
return $email;
}
$args = array(
'body' => $raw,
'headers' => array(
'content-type' => 'text/plain',
'Authorization' => 'Bearer ' . $token,
),
);
$request = wp_remote_post( 'https://graph.microsoft.com/v1.0/me/sendMail', $args );
$code = wp_remote_retrieve_response_code( $request );
if ( (int) $code === 202 ) {
$this->events->update( array( 'status' => 'sent' ), $email );
$this->logger->log( $email, 'sent', __( 'Email successfully sent.', 'gravitysmtp' ) );
return true;
}
$this->log_failure( $email, wp_remote_retrieve_body( $request ) );
return $email;
} catch ( \Exception $e ) {
$this->log_failure( $email, $e->getMessage() );
$this->debug_logger->log_fatal( $this->wrap_debug_with_details( __FUNCTION__, $email, 'Failed to send: ' . $e->getMessage() ) );
return $email;
}
}
private function log_failure( $email, $message ) {
$this->events->update( array( 'status' => 'failed' ), $email );
$this->logger->log( $email, 'failed', $message );
}
private function get_raw_message() {
$this->php_mailer->preSend();
$raw = $this->php_mailer->getSentMIMEMessage();
return base64_encode( $raw );
}
/**
* Connector data.
*
* @since 1.0
*
* @return array
*/
public function connector_data() {
return array(
self::SETTING_CLIENT_ID => $this->get_setting( self::SETTING_CLIENT_ID, '' ),
self::SETTING_CLIENT_SECRET => $this->get_setting( self::SETTING_CLIENT_SECRET, '' ),
self::SETTING_ACCESS_TOKEN => $this->get_setting( self::SETTING_ACCESS_TOKEN, '' ),
self::SETTING_FROM_EMAIL => $this->get_setting( self::SETTING_FROM_EMAIL, '' ),
self::SETTING_FORCE_FROM_EMAIL => $this->get_setting( self::SETTING_FORCE_FROM_EMAIL, false ),
self::SETTING_FROM_NAME => $this->get_setting( self::SETTING_FROM_NAME, '' ),
self::SETTING_FORCE_FROM_NAME => $this->get_setting( self::SETTING_FORCE_FROM_NAME, false ),
self::SETTING_REPLY_TO_EMAIL => $this->get_setting( self::SETTING_REPLY_TO_EMAIL, '' ),
self::SETTING_FORCE_REPLY_TO_EMAIL => $this->get_setting( self::SETTING_FORCE_REPLY_TO_EMAIL, false ),
self::SETTING_USE_RETURN_PATH => (bool) $this->get_setting( self::SETTING_USE_RETURN_PATH, false ),
'oauth_url' => 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize',
'oauth_params' => '&' . $this->get_oauth_params(),
);
}
protected function get_oauth_params() {
/**
* @var Microsoft_Oauth_Handler $oauth_handler
*/
$oauth_handler = Gravity_SMTP::container()->get( Connector_Service_Provider::MICROSOFT_OAUTH_HANDLER );
$params = array(
'response_type' => 'code',
'redirect_uri' => urldecode( $oauth_handler->get_return_url() ),
'response_mode' => 'query',
'scope' => $oauth_handler->get_scope(),
'state' => 1,
);
return http_build_query( $params );
}
/**
* Settings fields.
*
* @since 1.0
*
* @return array
*/
public function settings_fields() {
/**
* @var Microsoft_Oauth_Handler $oauth_handler
*/
$oauth_handler = Gravity_SMTP::container()->get( Connector_Service_Provider::MICROSOFT_OAUTH_HANDLER );
$oauth_handler->handle_response( $this->name );
$token = $oauth_handler->get_access_token( $this->name );
$has_token = $token && ! is_wp_error( $token );
$settings = array(
'title' => esc_html__( '365 / Outlook Settings', 'gravitysmtp' ),
'hide_save' => ( ! $has_token ),
'fields' => array(),
);
if ( ! $token || is_wp_error( $token ) ) {
$settings['fields'][] = array(
'component' => 'Alert',
'props' => array(
'customIconPrefix' => 'gravity-admin-icon',
'theme' => 'cosmos',
'type' => 'notice',
'spacing' => 3,
),
'fields' => array(
array(
'component' => 'Text',
'props' => array(
'content' => esc_html__( 'Please click the button below to initiate a connection with your Microsoft account. Remember to fill out both the Client ID and Client Secret fields before proceeding.', 'gravitysmtp' ),
'customClasses' => array( 'gform--display-block', 'gravitysmtp-integration__notice-message' ),
'size' => 'text-sm',
'spacing' => 4,
'tagName' => 'span',
),
),
array(
'component' => 'Link',
'props' => array(
'content' => esc_html__( 'Read our Microsoft 365 / Outlook documentation', 'gravitysmtp' ),
'customClasses' => array(
'gform-link--theme-cosmos',
'gravitysmtp-integration__notice-link',
'gform-button',
'gform-button--size-height-m',
'gform-button--white',
'gform-button--width-auto'
),
'href' => 'https://docs.gravitysmtp.com/category/integrations/microsoft/',
'target' => '_blank',
),
),
),
);
}
if ( isset( $_GET['code'] ) && ! $has_token ) {
$settings['fields'][] = array(
'component' => 'Alert',
'props' => array(
'customIconPrefix' => 'gravity-admin-icon',
'theme' => 'cosmos',
'type' => 'error',
'spacing' => 3,
),
'fields' => array(
array(
'component' => 'Text',
'props' => array(
'content' => esc_html__( 'Error Connecting to Microsoft. Check your credentials and try again.', 'gravitysmtp' ),
'weight' => 'medium',
'size' => 'text-sm',
'spacing' => 2,
'tagName' => 'span',
),
),
),
);
}
$settings['fields'][] = array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'Configuration', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => [ 4, 0, 3, 0 ],
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
);
if ( ! $token || is_wp_error( $token ) ) {
$settings['fields'][] = array(
'component' => 'LinkedHelpTextInput',
'external' => true,
'handle_change' => true,
'links' => array(
array(
'key' => 'link',
'props' => array(
'href' => 'https://portal.azure.com/',
'size' => 'text-xs',
'target' => '_blank',
),
),
),
'props' => array(
'helpTextAttributes' => array(
'content' => esc_html__( 'To obtain an Application ID from 365 / Outlook, login to your {{link}}Microsoft Azure{{link}} dashboard and generate an Application ID.', 'gravitysmtp' ),
'size' => 'text-xs',
'weight' => 'regular',
),
'labelAttributes' => array(
'label' => esc_html__( 'Application ID', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'medium',
),
'name' => self::SETTING_CLIENT_ID,
'spacing' => 4,
'size' => 'size-l',
'value' => $this->get_setting( self::SETTING_CLIENT_ID, '' ),
),
);
$settings['fields'][] = array(
'component' => 'LinkedHelpTextInput',
'external' => true,
'handle_change' => true,
'links' => array(
array(
'key' => 'link',
'props' => array(
'href' => 'https://portal.azure.com/',
'size' => 'text-xs',
'target' => '_blank',
),
),
),
'props' => array(
'helpTextAttributes' => array(
'content' => esc_html__( 'To obtain a Client Secret password, log in to your {{link}}Microsoft Azure{{link}} dashboard and generate a new client secret. Then, copy the secret value (not the secret ID) into this field.', 'gravitysmtp' ),
'size' => 'text-xs',
'weight' => 'regular',
),
'labelAttributes' => array(
'label' => esc_html__( 'Client Secret', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'medium',
),
'name' => self::SETTING_CLIENT_SECRET,
'spacing' => 6,
'size' => 'size-l',
'value' => $this->get_setting( self::SETTING_CLIENT_SECRET, '' ),
),
);
$settings['fields'][] = array(
'component' => 'CopyInput',
'external' => true,
'props' => array(
'actionButtonAttributes' => array(
'customAttributes' => array(
'type' => 'button',
),
'icon' => 'copy',
'iconPrefix' => 'gravity-admin-icon',
'label' => esc_html__( 'Copy', 'gravitysmtp' ),
),
'labelAttributes' => array(
'label' => esc_html__( 'Redirect URI (Personal Accounts)', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'medium',
),
'name' => self::VALUE_REDIRECT_URI,
'spacing' => 6,
'size' => 'size-l',
'customAttributes' => array(
'readOnly' => true,
),
'value' => urldecode( $oauth_handler->get_return_url( 'copy' ) ),
'helpTextAttributes' => array(
'content' => esc_html__( 'If your app is set up to support Personal Accounts, copy this URL and enter it as a Redirect URI in your App Settings.', 'gravitysmtp' ),
'size' => 'text-xs',
'weight' => 'regular',
),
),
);
$settings['fields'][] = array(
'component' => 'CopyInput',
'external' => true,
'props' => array(
'actionButtonAttributes' => array(
'customAttributes' => array(
'type' => 'button',
),
'icon' => 'copy',
'iconPrefix' => 'gravity-admin-icon',
'label' => esc_html__( 'Copy', 'gravitysmtp' ),
),
'labelAttributes' => array(
'label' => esc_html__( 'Redirect URI (School or Work Accounts)', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'medium',
),
'name' => self::VALUE_REDIRECT_URI_FULL,
'spacing' => 6,
'size' => 'size-l',
'customAttributes' => array(
'readOnly' => true,
),
'value' => urldecode( $oauth_handler->get_return_url( 'settings' ) ),
'helpTextAttributes' => array(
'content' => esc_html__( 'If your app is set up to support School and Work accounts, copy this URL and enter it as a Redirect URI in your App Settings.', 'gravitysmtp' ),
'size' => 'text-xs',
'weight' => 'regular',
),
),
);
$settings['fields'][] = array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'Authorization', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => [ 4, 0, 3, 0 ],
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
);
$settings['fields'][] = array(
'component' => 'BrandedButton',
'external' => true,
'props' => array(
'label' => esc_html__( 'Sign in with Microsoft', 'gravitysmtp' ),
'spacing' => 6,
'Svg' => 'MicrosoftAltLogo',
'type' => 'color',
),
);
} else {
$settings['fields'][] = array(
'component' => 'Box',
'props' => array(
'customClasses' => array( 'gravitysmtp-google-integration__connected-message' ),
'display' => 'flex',
'spacing' => 3,
),
'fields' => array(
array(
'component' => 'Icon',
'props' => array(
'customClasses' => array(
'gravitysmtp-google-integration__checkmark',
'gform-icon--preset-active',
'gform-icon-preset--status-correct',
'gform-alert__icon'
),
'icon' => 'checkmark-simple',
'iconPrefix' => 'gravity-admin-icon',
),
),
array(
'component' => 'Text',
'props' => array(
'asHtml' => true,
'content' => sprintf(
'%s <span class="gform-text gform-text--color-port gform-typography--size-text-sm gform-typography--weight-medium">%s</span>',
esc_html__( 'Connected with email account:', 'gravitysmtp' ),
esc_html( $oauth_handler->get_connection_details()['email'] )
),
'size' => 'text-sm',
'tagName' => 'span',
),
),
),
);
$disconnect_url = add_query_arg( '_wpnonce', wp_create_nonce( 'gsmtp_disconnect_microsoft' ), admin_url( 'admin-post.php?action=smtp_disconnect_microsoft' ) );
$settings['fields'][] = array(
'component' => 'Text',
'props' => array(
'content' => sprintf( '<a href="%s" class="%s"><span class="gravity-admin-icon gravity-admin-icon--x-circle gform-button__icon"></span>%s</a>', $disconnect_url, 'gform-link gform-link--theme-cosmos gform-button gform-button--size-height-m gform-button--white gform-button--width-auto gform-button--active-type-loader gform-button--loader-after gform-button--icon-leading', __( 'Disconnect from Microsoft', 'gravitysmtp' ) ),
'asHtml' => true,
'spacing' => 6,
),
);
$settings['fields'][] = array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'Configuration', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => [ 4, 0, 4, 0 ],
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
);
$settings['fields'][] = array(
'component' => 'Toggle',
'props' => array(
'helpTextAttributes' => array(
'content' => esc_html__( 'If Return Path is enabled this adds the return path to the email header which indicates where non-deliverable notifications should be sent. Bounce messages may be lost if not enabled.', 'gravitysmtp' ),
'size' => 'text-xs',
'weight' => 'regular',
'spacing' => [ 2, 0, 0, 0 ],
),
'helpTextWidth' => 'full',
'initialChecked' => (bool) $this->get_setting( self::SETTING_USE_RETURN_PATH, false ),
'labelAttributes' => array(
'label' => esc_html__( 'Return Path', 'gravitysmtp' ),
),
'labelPosition' => 'left',
'name' => self::SETTING_USE_RETURN_PATH,
'size' => 'size-m',
'spacing' => 5,
'width' => 'full',
),
);
$settings['fields'] = array_merge( $settings['fields'], $this->get_from_settings_fields(), $this->get_reply_to_settings_fields() );
}
return $settings;
}
protected function get_from_settings_fields() {
$fields = parent::get_from_settings_fields();
$fields[2]['component'] = 'Text';
$fields[2]['props']['size'] = 'text-sm';
$fields[2]['props']['weight'] = 'medium';
$fields[2]['props']['content'] = esc_html__( 'Force From Name', 'gravitysmtp' );
$fields[2]['props']['spacing'] = '2';
$fields[3]['component'] = 'Text';
$fields[3]['props']['content'] = esc_html__( 'Microsoft automatically forces the From Name associated with the From Email configured within their system.', 'gravitysmtp' );
return $fields;
}
public function is_configured() {
if ( Booliesh::get( $this->get_setting( 'access_token', false ) ) ) {
/**
* @var Microsoft_Oauth_Handler $oauth_handler
*/
$oauth_handler = Gravity_SMTP::container()->get( Connector_Service_Provider::MICROSOFT_OAUTH_HANDLER );
$token = $oauth_handler->get_access_token();
return $token;
}
return false;
}
/**
* Reset the PHPMailer instance to prevent carryover from previous send.
*
* @since 1.0
*
* @return void
*/
private function reset_phpmailer() {
$this->php_mailer->clearCustomHeaders();
$this->php_mailer->clearAddresses();
$this->php_mailer->clearBCCs();
$this->php_mailer->clearCCs();
$this->php_mailer->clearAllRecipients();
$this->php_mailer->clearReplyTos();
$this->php_mailer->clearAttachments();
}
}
@@ -0,0 +1,331 @@
<?php
namespace Gravity_Forms\Gravity_SMTP\Connectors\Types;
use Gravity_Forms\Gravity_SMTP\Connectors\Connector_Base;
use Gravity_Forms\Gravity_SMTP\Utils\Recipient;
/**
* Connector for Generic/Custom SMTP integration.
*
* @since 1.0
*/
class Connector_Phpmail extends Connector_Base {
const SETTING_USE_RETURN_PATH = 'use_return_path';
protected $name = 'phpmail';
protected $title = 'PHP Mail';
protected $disabled = false;
protected $description = '';
protected $logo = 'PHP';
protected $full_logo = 'PHPFull';
public function get_description() {
return __( "Use your server's default PHP Mailer to send email.", 'gravitysmtp' );
}
public function send() {
$to = $this->get_att( 'to', '' );
$subject = $this->get_att( 'subject', '' );
$message = $this->get_att( 'message', '' );
$headers = $this->get_parsed_headers( $this->get_att( 'headers', array() ) );
$attachments = $this->get_att( 'attachments', array() );
$from = $this->get_from( true );
$reply_to = $this->get_reply_to( true );
$source = $this->get_att( 'source' );
$params = $this->get_request_params();
$email = $this->email;
$this->reset_phpmailer();
if ( ! empty( $headers['content-type'] ) ) {
$headers['content-type'] = $this->get_att( 'content_type', $headers['content-type'] );
}
$this->set_email_log_data( $subject, $message, $to, empty( $from['name'] ) ? $from['email'] : sprintf( '%s <%s>', $from['name'], $from['email'] ), $headers, $attachments, $source, $params );
$this->logger->log( $email, 'started', __( 'Starting email send for PHPMail connector.', 'gravitysmtp' ) );
$this->php_mailer->setFrom( $from['email'], empty( $from['name'] ) ? '' : $from['name'] );
foreach( $to->recipients() as $recipient ) {
if ( ! empty( $recipient->name() ) ) {
$this->php_mailer->addAddress( $recipient->email(), $recipient->name() );
} else {
$this->php_mailer->addAddress( $recipient->email() );
}
}
$this->php_mailer->Subject = $subject;
$this->php_mailer->Body = $message;
if ( ! empty( $headers['cc'] ) ) {
foreach ( $headers['cc']->recipients() as $recipient ) {
if ( ! empty( $recipient->name() ) ) {
$this->php_mailer->addCC( $recipient->email(), $recipient->name() );
} else {
$this->php_mailer->addCC( $recipient->email() );
}
}
}
if ( ! empty( $headers['bcc'] ) ) {
foreach ( $headers['bcc']->recipients() as $recipient ) {
if ( ! empty( $recipient->name() ) ) {
$this->php_mailer->addBCC( $recipient->email(), $recipient->name() );
} else {
$this->php_mailer->addBCC( $recipient->email() );
}
}
}
if ( ! empty( $attachments ) ) {
foreach ( $attachments as $attachment ) {
$this->php_mailer->addAttachment( $attachment );
}
}
if ( ! empty( $reply_to ) ) {
foreach( $reply_to as $address ) {
if ( isset( $address['name'] ) ) {
$this->php_mailer->addReplyTo( $address['email'], $address['name'] );
} else {
$this->php_mailer->addReplyTo( $address['email'] );
}
}
}
if ( ! empty( $headers['content-type'] ) && strpos( $headers['content-type'], 'text/html' ) !== false ) {
$this->php_mailer->isHTML( true );
} else {
$this->php_mailer->isHTML( false );
$this->php_mailer->ContentType = 'text/plain';
}
$this->php_mailer->CharSet = 'UTF-8';
$additional_headers = $this->get_filtered_message_headers();
if ( ! empty( $additional_headers ) ) {
foreach ( $additional_headers as $key => $value ) {
$this->php_mailer->addCustomHeader( $key, $value );
}
}
if ( (bool) $this->get_setting( self::SETTING_USE_RETURN_PATH, false ) ) {
$this->php_mailer->Sender = $this->php_mailer->From;
}
if ( $this->is_test_mode() ) {
$this->events->update( array( 'status' => 'sandboxed' ), $email );
$this->logger->log( $email, 'sandboxed', __( 'Email sandboxed.', 'gravitysmtp' ) );
return true;
}
/**
* Fires after PHPMailer is initialized.
*
* @since 2.2.0
*
* @param PHPMailer $phpmailer The PHPMailer instance (passed by reference).
*/
do_action_ref_array( 'phpmailer_init', array( &$this->php_mailer ) );
$mail_data = compact( 'to', 'subject', 'message', 'headers', 'attachments' );
// Send!
try {
$debug_atts = compact( 'to', 'from', 'subject', 'headers', 'source', 'attachments', 'reply_to' );
$this->debug_logger->log_debug( $this->wrap_debug_with_details( __FUNCTION__, $email, 'Attempting send with the following attributes: ' . json_encode( $debug_atts ) ) );
$send = $this->php_mailer->send();
/**
* Fires after PHPMailer has successfully sent an email.
*
* The firing of this action does not necessarily mean that the recipient(s) received the
* email successfully. It only means that the `send` method above was able to
* process the request without any errors.
*
* @since 5.9.0
*
* @param array $mail_data {
* An array containing the email recipient(s), subject, message, headers, and attachments.
*
* @type string[] $to Email addresses to send message.
* @type string $subject Email subject.
* @type string $message Message contents.
* @type string[] $headers Additional headers.
* @type string[] $attachments Paths to files to attach.
* }
*/
do_action( 'wp_mail_succeeded', $mail_data );
$this->events->update( array( 'status' => 'sent' ), $this->email );
$this->logger->log( $this->email, 'sent', __( 'Email successfully sent.', 'gravitysmtp' ) );
return $send;
} catch ( \PHPMailer\PHPMailer\Exception $e ) {
$mail_data['phpmailer_exception_code'] = $e->getCode();
/**
* Fires after a PHPMailer\PHPMailer\Exception is caught.
*
* @since 4.4.0
*
* @param WP_Error $error A WP_Error object with the PHPMailer\PHPMailer\Exception message, and an array
* containing the mail recipient, subject, message, headers, and attachments.
*/
do_action( 'wp_mail_failed', new \WP_Error( 'wp_mail_failed', $e->getMessage(), $mail_data ) );
$this->log_failure( $this->email, $e->getMessage() );
return $this->email;
}
}
/**
* Get the request parameters for sending email through connector.
*
* @since 1.0
*
* @return array
*/
public function get_request_params() {
return array();
}
/**
* Connector data.
*
* @since 1.0
*
* @return array
*/
public function connector_data() {
return array(
self::SETTING_FROM_EMAIL => $this->get_setting( self::SETTING_FROM_EMAIL, '' ),
self::SETTING_FORCE_FROM_EMAIL => $this->get_setting( self::SETTING_FORCE_FROM_EMAIL, false ),
self::SETTING_FROM_NAME => $this->get_setting( self::SETTING_FROM_NAME, '' ),
self::SETTING_FORCE_FROM_NAME => $this->get_setting( self::SETTING_FORCE_FROM_NAME, false ),
self::SETTING_REPLY_TO_EMAIL => $this->get_setting( self::SETTING_REPLY_TO_EMAIL, '' ),
self::SETTING_FORCE_REPLY_TO_EMAIL => $this->get_setting( self::SETTING_FORCE_REPLY_TO_EMAIL, false ),
self::SETTING_USE_RETURN_PATH => (bool) $this->get_setting( self::SETTING_USE_RETURN_PATH, false ),
);
}
/**
* Settings fields.
*
* @since 1.0
*
* @return array
*/
public function settings_fields() {
return array(
'title' => esc_html__( 'PHP Mail Settings', 'gravitysmtp' ),
'description' => '',
'fields' => array_merge(
array(
array(
'component' => 'Alert',
'props' => array(
'customIconPrefix' => 'gravity-admin-icon',
'theme' => 'cosmos',
'type' => 'info',
'spacing' => 5,
),
'fields' => array(
array(
'component' => 'Text',
'props' => array(
'content' => esc_html__( 'When using PHP Mail, emails might not be delivered reliably. For optimal performance, we recommend using a dedicated email provider.', 'gravitysmtp' ),
'size' => 'text-sm',
'tagName' => 'span',
),
),
),
),
array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'General Settings', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => 4,
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
),
array(
'component' => 'Toggle',
'props' => array(
'helpTextAttributes' => array(
'content' => esc_html__( 'If Return Path is enabled this adds the return path to the email header which indicates where non-deliverable notifications should be sent. Bounce messages may be lost if not enabled.', 'gravitysmtp' ),
'size' => 'text-xs',
'weight' => 'regular',
'spacing' => [ 2, 0, 0, 0 ],
),
'helpTextWidth' => 'full',
'initialChecked' => (bool) $this->get_setting( self::SETTING_USE_RETURN_PATH, false ),
'labelAttributes' => array(
'label' => esc_html__( 'Return Path', 'gravitysmtp' ),
),
'labelPosition' => 'left',
'name' => self::SETTING_USE_RETURN_PATH,
'size' => 'size-m',
'spacing' => 5,
'width' => 'full',
),
),
),
$this->get_from_settings_fields(),
$this->get_reply_to_settings_fields(),
),
);
}
/**
* Determine if the SMTP credentials are configured correctly.
*
* @since 1.0
*
* @return bool|\WP_Error
*/
public function is_configured() {
global $phpmailer;
return ! empty( $phpmailer );
}
/**
* Logs an email send failure.
*
* @since 1.0
*
* @param string $email The email that failed.
* @param string $error_message The error message.
*/
private function log_failure( $email, $error_message ) {
$this->events->update( array( 'status' => 'failed' ), $email );
$this->logger->log( $email, 'failed', $error_message );
}
/**
* Reset the PHPMailer instance to prevent carryover from previous send.
*
* @since 1.0
*
* @return void
*/
private function reset_phpmailer() {
$this->php_mailer->clearCustomHeaders();
$this->php_mailer->clearAddresses();
$this->php_mailer->clearBCCs();
$this->php_mailer->clearCCs();
$this->php_mailer->clearAllRecipients();
$this->php_mailer->clearReplyTos();
$this->php_mailer->clearAttachments();
}
}
@@ -0,0 +1,358 @@
<?php
namespace Gravity_Forms\Gravity_SMTP\Connectors\Types;
use Gravity_Forms\Gravity_SMTP\Connectors\Connector_Base;
class Connector_Postmark extends Connector_Base {
const SETTING_SERVER_API_TOKEN = 'server_api_token';
protected $name = 'postmark';
protected $title = 'Postmark';
protected $description = '';
protected $logo = 'Postmark';
protected $full_logo = 'PostmarkFull';
protected $url = 'https://api.postmarkapp.com/email';
public function get_description() {
return esc_html__( 'Owned by ActiveCampaign, Postmark is a popular email-sending service with an impressive reputation for reliability and deliverability. Postmark offers a free plan that allows you to send up to 100 emails a month. Over 100, prices vary depending on the number of emails sent. For more information on how to get started with Postmark, read our documentation.', 'gravitysmtp' );
}
protected $sensitive_fields = array(
self::SETTING_SERVER_API_TOKEN,
);
/**
* Sending logic.
*
* @since 1.0
*
* @return bool
*/
public function send() {
try {
$atts = $this->get_send_atts();
$source = $this->get_att( 'source' );
$params = $this->get_request_params();
$email = $this->email;
$this->set_email_log_data( $atts['subject'], $atts['message'], $atts['to'], $atts['from'], $atts['headers'], $atts['attachments'], $source, $params );
$this->logger->log( $email, 'started', __( 'Starting email send for Postmark connector.', 'gravitysmtp' ) );
if ( $this->is_test_mode() ) {
$this->events->update( array( 'status' => 'sandboxed' ), $email );
$this->logger->log( $email, 'sandboxed', __( 'Email sandboxed.', 'gravitysmtp' ) );
return true;
}
$response = wp_safe_remote_post( $this->url, $params );
$response_code = (int) wp_remote_retrieve_response_code( $response );
$response_body = wp_remote_retrieve_body( $response );
if ( $response_code !== 200 ) {
$this->events->update( array( 'status' => 'failed' ), $email );
$this->logger->log( $email, 'failed', $response_body );
return $email;
}
$this->events->update( array( 'status' => 'sent' ), $email );
$decoded = json_decode( $response_body, true );
$api_message = isset( $decoded['Message'] ) ? trim( $decoded['Message'] ) : '';
if ( ! empty( $api_message ) && strcasecmp( $api_message, 'OK' ) !== 0 ) {
$sent_message = $api_message;
} elseif ( empty( $api_message ) && ! empty( $response_body ) && ! is_array( $decoded ) ) {
$sent_message = substr( $response_body, 0, 500 );
} else {
$sent_message = __( 'Email successfully sent.', 'gravitysmtp' );
}
$this->logger->log( $email, 'sent', $sent_message );
return true;
} catch ( \Exception $e ) {
$this->events->update( array( 'status' => 'failed' ), $email );
$this->logger->log( $email, 'failed', $e->getMessage() );
return $email;
}
}
/**
* Get the request parameters for sending email through connector.
*
* @since 1.0
*
* @return array
*/
public function get_request_params() {
$atts = $this->get_send_atts();
$api_key = $this->get_setting( self::SETTING_SERVER_API_TOKEN );
if ( ! empty( $atts['headers']['content-type'] ) && strpos( $atts['headers']['content-type'], 'text/html' ) !== false ) {
$html_body = $atts['message'];
} else {
$html_body = null;
}
$cc = isset( $atts['headers']['cc'] ) ? $atts['headers']['cc']->as_string( false ) : '';
$bcc = isset( $atts['headers']['bcc'] ) ? $atts['headers']['bcc']->as_string( false ) : '';
// Strip tags from plaintext
$text_body = wp_strip_all_tags( $atts['message'] );
// Remove leftover double-linebreaks from plaintext.
$text_body = preg_replace( "/([\r\n]{2,}|[\n]{2,}|[\r]{2,}|[\r\t]{2,}|[\n\t]{2,})/", "\n", $text_body );
$body = array(
'from' => $atts['from'],
'to' => $atts['to']->as_string( false ),
'subject' => $atts['subject'],
'textBody' => $text_body,
'htmlBody' => $html_body,
'Cc' => $cc,
'Bcc' => $bcc,
);
if ( ! empty( $atts['reply_to'] ) ) {
$body['ReplyTo'] = $atts['reply_to'];
}
if ( ! empty( $atts['attachments'] ) ) {
$body['Attachments'] = $this->get_attachments( $atts['attachments'] );
}
$additional_headers = $this->get_filtered_message_headers();
if ( ! empty( $additional_headers ) ) {
$body['Headers'] = array();
foreach ( $additional_headers as $key => $value ) {
$body['Headers'][] = array(
'Name' => $key,
'Value' => $value,
);
}
}
return array(
'body' => json_encode( $body ),
'headers' => $this->get_request_headers( $api_key ),
);
}
/**
* Get the attributes for sending email.
*
* @since 1.0
*
* @return array
*/
protected function get_send_atts() {
$headers = $this->get_parsed_headers( $this->get_att( 'headers', array() ) );
if ( ! empty( $headers['content-type'] ) ) {
$headers['content-type'] = $this->get_att( 'content_type', $headers['content-type'] );
}
return array(
'to' => $this->get_att( 'to', '' ),
'subject' => $this->get_att( 'subject', '' ),
'message' => $this->get_att( 'message', '' ),
'headers' => $headers,
'attachments' => $this->get_att( 'attachments', array() ),
'from' => $this->get_from(),
'reply_to' => $this->get_reply_to(),
);
}
protected function get_request_headers( $api_key ) {
return array(
'Content-Type' => 'application/json',
'Accept' => 'application/json',
'X-Postmark-Server-Token' => $api_key,
);
}
protected function get_attachments( $attachments ) {
$data = array();
foreach ( $attachments as $custom_name => $attachment ) {
$file = false;
try {
if ( is_file( $attachment ) && is_readable( $attachment ) ) {
$fileName = is_numeric( $custom_name ) ? basename( $attachment ) : $custom_name;
$contentId = wp_hash( $attachment );
$file = file_get_contents( $attachment );
$content = base64_encode( file_get_contents( $attachment ) );
$mimeType = mime_content_type( $attachment );
}
} catch ( \Exception $e ) {
$file = false;
}
if ( $file === false ) {
continue;
}
$data[] = array(
'ContentType' => $mimeType,
'Name' => $fileName,
'ContentId' => $contentId,
'Content' => $content,
);
}
return $data;
}
public function connector_data() {
return array(
self::SETTING_SERVER_API_TOKEN => $this->get_setting( self::SETTING_SERVER_API_TOKEN, '' ),
self::SETTING_FROM_EMAIL => $this->get_setting( self::SETTING_FROM_EMAIL, '' ),
self::SETTING_FORCE_FROM_EMAIL => $this->get_setting( self::SETTING_FORCE_FROM_EMAIL, false ),
self::SETTING_FROM_NAME => $this->get_setting( self::SETTING_FROM_NAME, '' ),
self::SETTING_FORCE_FROM_NAME => $this->get_setting( self::SETTING_FORCE_FROM_NAME, false ),
self::SETTING_REPLY_TO_EMAIL => $this->get_setting( self::SETTING_REPLY_TO_EMAIL, '' ),
self::SETTING_FORCE_REPLY_TO_EMAIL => $this->get_setting( self::SETTING_FORCE_REPLY_TO_EMAIL, false ),
);
}
public function settings_fields() {
return array(
'title' => esc_html__( 'Postmark Settings', 'gravitysmtp' ),
'description' => '',
'fields' => array_merge(
array(
array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'Configuration', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => [ 4, 0, 4, 0 ],
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
),
// array(
// 'component' => 'Toggle',
// 'props' => array(
// 'initialChecked' => (bool) $this->get_plugin_setting( 'primary' ) === $this->name,
// 'labelAttributes' => array(
// 'label' => esc_html__( 'If enabled, Postmark will be the default SMTP mailer.', 'gravitysmtp' ),
// ),
// 'labelPosition' => 'left',
// 'name' => 'default-mailer',
// 'size' => 'size-m',
// 'spacing' => 5,
// 'width' => 'full',
// ),
// ),
array(
'component' => 'Input',
'props' => array(
'labelAttributes' => array(
'label' => esc_html__( 'Server API Token', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'medium',
),
'helpTextAttributes' => array(
'asHtml' => true,
/* translators: 1: opening anchor tag, 2: closing anchor tag */
'content' => sprintf( __( 'To get a Server API Token, go to the %1$sAPI Token%2$s tab on your Postmark account page.', 'gravitysmtp' ), '<a class="gform-link gform-typography--size-text-xs" href="https://account.postmarkapp.com/api_tokens" target="_blank" rel="noopener noreferrer">', '</a>' ),
'size' => 'text-xs',
'weight' => 'regular',
),
'name' => self::SETTING_SERVER_API_TOKEN,
'size' => 'size-l',
'spacing' => 6,
'value' => $this->get_setting( self::SETTING_SERVER_API_TOKEN, '' ),
),
),
array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'General Settings', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => 4,
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
),
),
$this->get_from_settings_fields(),
$this->get_reply_to_settings_fields(),
),
);
}
public function migration_map() {
return array(
array(
'original_key' => 'gravityformsaddon_gravityformspostmark_settings',
'sub_key' => 'serverToken',
'new_key' => self::SETTING_SERVER_API_TOKEN,
),
);
}
/**
* Verify the API key with the API.
*
* @since 1.0
*
* @return true|\WP_Error
*/
private function verify_api_key() {
$api_key = $this->get_setting( self::SETTING_SERVER_API_TOKEN );
$url = str_replace( 'email', 'server', $this->url );
if ( empty( $api_key ) ) {
return new \WP_Error( 'missing_api_key', __( 'No API Key provided.', 'gravitysmtp' ) );
}
$response = wp_remote_get(
$url,
array(
'headers' => $this->get_request_headers( $api_key ),
)
);
if ( wp_remote_retrieve_response_code( $response ) != '200' ) {
return new \WP_Error( 'invalid_api_key', __( 'Invalid API Key provided.', 'gravitysmtp' ) );
}
return true;
}
/**
* Determine if the API credentials are configured correctly.
*
* @since 1.0
*
* @return bool|\WP_Error
*/
public function is_configured() {
$valid_api = $this->verify_api_key();
if ( is_wp_error( $valid_api ) ) {
self::$configured = $valid_api;
return $valid_api;
}
self::$configured = true;
return true;
}
}
@@ -0,0 +1,408 @@
<?php
namespace Gravity_Forms\Gravity_SMTP\Connectors\Types;
use Exception;
use Gravity_Forms\Gravity_SMTP\Connectors\Connector_Base;
use Gravity_Forms\Gravity_SMTP\Feature_Flags\Feature_Flag_Manager;
use WP_Error;
/**
* Connector for Resend
*
* @since 1.0
*/
class Connector_Resend extends Connector_Base {
const SETTING_API_KEY = 'api_key';
protected $name = 'resend';
protected $title = 'Resend';
protected $disabled = true;
protected $logo = 'Resend';
protected $full_logo = 'ResendFull';
protected $url = 'https://api.resend.com';
protected $sensitive_fields = array(
self::SETTING_API_KEY,
);
public function get_description() {
return esc_html__( 'Email for developers that makes it easy to write, format, and send emails. Deliver transactional and marketing emails at scale.', 'gravitysmtp' );
}
/**
* Sends email via Resend.
*
* @since 1.0
*
* @return int returns the email ID
*/
public function send() {
try {
$atts = $this->get_send_atts();
$source = $this->get_att( 'source' );
$params = $this->get_request_params();
$email = $this->email;
$this->set_email_log_data( $atts['subject'], $atts['message'], $atts['to'], $atts['from']['from'], $atts['headers'], $atts['attachments'], $source, $params );
$this->logger->log( $email, 'started', __( 'Starting email send for Resend connector.', 'gravitysmtp' ) );
if ( $this->is_test_mode() ) {
$this->events->update( array( 'status' => 'sandboxed' ), $email );
$this->logger->log( $email, 'sandboxed', __( 'Email sandboxed.', 'gravitysmtp' ) );
return true;
}
$response = wp_safe_remote_post( $this->url . '/emails', $params );
$is_success = in_array( (int) wp_remote_retrieve_response_code( $response ), array( 200, 201, 202 ) );
if ( ! $is_success ) {
$this->log_failure( $email, wp_remote_retrieve_body( $response ) );
return $email;
}
$this->events->update( array( 'status' => 'sent' ), $email );
$this->logger->log( $email, 'sent', __( 'Email successfully sent.', 'gravitysmtp' ) );
return true;
} catch ( Exception $e ) {
$this->log_failure( $email, $e->getMessage() );
return $email;
}
}
/**
* Get the request parameters for sending email through connector.
*
* @since 1.0
*
* @return array
*/
public function get_request_params() {
$atts = $this->get_send_atts();
$api_key = $this->get_setting( self::SETTING_API_KEY );
$message = array(
'to' => '',
'from' => ! empty( $atts['from']['name'] ) ? sprintf( '%s <%s>', $atts['from']['name'], $atts['from']['email'] ) : $atts['from']['email'],
'subject' => $atts['subject'],
'headers' => array(),
);
$to_value = array();
foreach ( $atts['to']->as_array() as $recipient ) {
if ( ! empty( $recipient['name'] ) ) {
$to_value[] = sprintf( '%s <%s>', $recipient['name'], $recipient['email'] );
} else {
$to_value[] = $recipient['email'];
}
}
$message['to'] = $to_value;
// Setting content
$is_html = ! empty( $atts['headers']['content-type'] ) && strpos( $atts['headers']['content-type'], 'text/html' ) !== false;
if ( $is_html ) {
$message['html'] = $atts['message'];
// Strip tags from plaintext
$text_body = wp_strip_all_tags( $atts['message'] );
// Remove leftover double-linebreaks from plaintext.
$text_body = preg_replace( "/([\r\n]{2,}|[\n]{2,}|[\r]{2,}|[\r\t]{2,}|[\n\t]{2,})/", "\n", $text_body );
$message['text'] = $text_body;
} else {
$message['text'] = $atts['message'];
}
// Setting cc
if ( ! empty( $atts['headers']['cc'] ) ) {
$cc_headers = array();
foreach ( $atts['headers']['cc']->as_array() as $cc_value ) {
$cc_headers[] = ! empty( $cc_value['name'] ) ? sprintf( '%s <%s>', $cc_value['name'], $cc_value['email'] ) : $cc_value['email'];
}
$message['cc'] = $cc_headers;
}
// Setting Bcc
if ( ! empty( $atts['headers']['bcc'] ) ) {
$bcc_headers = array();
foreach ( $atts['headers']['bcc']->as_array() as $bcc_value ) {
$bcc_headers[] = ! empty( $bcc_value['name'] ) ? sprintf( '%s <%s>', $bcc_value['name'], $bcc_value['email'] ) : $bcc_value['email'];
}
$message['bcc'] = $bcc_headers;
}
// Setting reply to
if ( ! empty( $atts['reply_to'] ) ) {
if ( isset( $atts['reply_to']['email'] ) ) {
$reply_to = $atts['reply_to'];
} else {
$reply_to = $atts['reply_to'][0];
}
$message['reply_to'] = $reply_to['email'];
}
// Setting attachments
if ( ! empty( $atts['attachments'] ) ) {
$message['attachments'] = $this->get_attachments( $atts['attachments'] );
}
return array(
'body' => json_encode( $message ),
'headers' => $this->get_request_headers( $api_key ),
);
}
/**
* Get the attributes for sending email.
*
* @since 1.0
*
* @return array
*/
protected function get_send_atts() {
$headers = $this->get_parsed_headers( $this->get_att( 'headers', array() ) );
if ( ! empty( $headers['content-type'] ) ) {
$headers['content-type'] = $this->get_att( 'content_type', $headers['content-type'] );
}
return array(
'to' => $this->get_att( 'to', '' ),
'subject' => $this->get_att( 'subject', '' ),
'message' => $this->get_att( 'message', '' ),
'headers' => $headers,
'attachments' => $this->get_att( 'attachments', array() ),
'from' => $this->get_from( true ),
'reply_to' => $this->get_reply_to( true ),
);
}
/**
* Gets a list of attachments, and returns them in a format that can be used by the API.
*
* @param array $attachments the list of attachments
*
* @since 1.0
*
* @return array Returns an array of attachments. Each item with a 'content' and 'name' property.
*/
protected function get_attachments( $attachments ) {
$data = array();
foreach ( $attachments as $custom_name => $attachment ) {
try {
if ( is_file( $attachment ) && is_readable( $attachment ) ) {
$fileName = is_numeric( $custom_name ) ? basename( $attachment ) : $custom_name;
$content = base64_encode( file_get_contents( $attachment ) );
$data[] = array(
'filename' => $fileName,
'content' => $content,
'content_type' => mime_content_type( $attachment ),
);
}
} catch ( Exception $e ) {
continue;
}
}
return $data;
}
/**
* Gets the headers to be used in the API request.
*
* @since 1.0
*
* @return array returns the header array to be passed to Resend's API
*/
protected function get_request_headers( $api_key ) {
return array(
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . $api_key,
);
}
/**
* Logs an email send failure.
*
* @since 1.0
*
* @param string $email the email that failed
* @param string $error_message the error message
*/
private function log_failure( $email, $error_message ) {
$this->events->update( array( 'status' => 'failed' ), $email );
$this->logger->log( $email, 'failed', $error_message );
}
/**
* Connector data.
*
* @since 1.0
*
* @return array
*/
public function connector_data() {
return array(
self::SETTING_API_KEY => $this->get_setting( self::SETTING_API_KEY, '' ),
self::SETTING_FROM_EMAIL => $this->get_setting( self::SETTING_FROM_EMAIL, '' ),
self::SETTING_FORCE_FROM_EMAIL => $this->get_setting( self::SETTING_FORCE_FROM_EMAIL, false ),
self::SETTING_FROM_NAME => $this->get_setting( self::SETTING_FROM_NAME, '' ),
self::SETTING_FORCE_FROM_NAME => $this->get_setting( self::SETTING_FORCE_FROM_NAME, false ),
self::SETTING_REPLY_TO_EMAIL => $this->get_setting( self::SETTING_REPLY_TO_EMAIL, '' ),
self::SETTING_FORCE_REPLY_TO_EMAIL => $this->get_setting( self::SETTING_FORCE_REPLY_TO_EMAIL, false ),
);
}
/**
* Settings fields.
*
* @since 1.0
*
* @return array
*/
public function settings_fields() {
return array(
'title' => esc_html__( 'Resend Settings', 'gravitysmtp' ),
'description' => '',
'fields' => array_merge(
array(
array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'Configuration', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => array( 4, 0, 4, 0 ),
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
),
array(
'component' => 'Input',
'props' => array(
'labelAttributes' => array(
'label' => esc_html__( 'API Key', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'medium',
),
'helpTextAttributes' => array(
'asHtml' => true,
/* translators: 1: opening anchor tag, 2: closing anchor tag */
'content' => __( 'To generate an API key from Resend, log in to your Resend dashboard and navigate to the Credentials section and then generate your API key.', 'gravitysmtp' ),
'size' => 'text-xs',
'weight' => 'regular',
),
'name' => self::SETTING_API_KEY,
'size' => 'size-l',
'spacing' => 6,
'value' => $this->get_setting( self::SETTING_API_KEY, '' ),
),
),
array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'General Settings', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => 4,
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
),
),
$this->get_from_settings_fields(),
$this->get_reply_to_settings_fields(),
),
);
}
/**
* Determine if the API credentials are configured correctly.
*
* @since 1.0
*
* @return bool|WP_Error returns true if configured, or a WP_Error object if not
*/
public function is_configured() {
$valid_api = $this->verify_api_key();
if ( is_wp_error( $valid_api ) ) {
self::$configured = $valid_api;
return $valid_api;
}
self::$configured = true;
return true;
}
/**
* Verify the API key with the API.
*
* @since 1.0
*
* @return true|WP_Error
*/
private function verify_api_key() {
$api_key = $this->get_setting( self::SETTING_API_KEY, '' );
$url = $this->url . '/domains';
if ( empty( $api_key ) ) {
return new WP_Error( 'missing_api_key', __( 'No API Key provided.', 'gravitysmtp' ) );
}
$response = wp_remote_get(
$url,
array(
'headers' => $this->get_request_headers( $api_key ),
)
);
$code = wp_remote_retrieve_response_code( $response );
$body = wp_remote_retrieve_body( $response );
// Some API keys are restricted to only sending; we can't verify them until a test send is attempted.
if ( strpos( $body, 'restricted_api_key' ) !== false ) {
return true;
}
if ( wp_remote_retrieve_response_code( $response ) != '200' ) {
return new WP_Error( 'invalid_api_key', __( 'Invalid API Key provided.', 'gravitysmtp' ) );
}
return true;
}
/**
* Get the unique data for this connector, merged with the default/common data for all
* connectors in the system.
*
* @since 1.0
*
* @return array
*/
protected function get_merged_data() {
$data = parent::get_merged_data();
$data['disabled'] = ! Feature_Flag_Manager::is_enabled( 'resend_integration' );
return $data;
}
}
@@ -0,0 +1,453 @@
<?php
namespace Gravity_Forms\Gravity_SMTP\Connectors\Types;
use Gravity_Forms\Gravity_SMTP\Connectors\Connector_Base;
class Connector_Sendgrid extends Connector_Base {
const SETTING_API_KEY = 'api_key';
const SETTING_REGION = 'region';
const SETTING_IP_POOL_NAME = 'ip_pool_name';
const OPTION_REGION_GLOBAL = 'global';
const OPTION_REGION_EU = 'eu';
const API_URL_GLOBAL = 'https://api.sendgrid.com/v3/mail/send';
const API_URL_EU = 'https://api.eu.sendgrid.com/v3/mail/send';
protected $name = 'sendgrid';
protected $title = 'SendGrid';
protected $description = '';
protected $logo = 'SendGrid';
protected $full_logo = 'SendGridFull';
public function get_description() {
return esc_html__( 'Send at scale with Twilio SendGrid, boasting an industry-leading 99% deliverability rate. SendGrid offers both a free-forever plan of 100 emails a day, and, if you need to exceed that limit, a selection of preset pricing plans, starting at $19.95 per month for up to 50,000 emails. For more information on how to get started with SendGrid, read our documentation.', 'gravitysmtp' );
}
protected $sensitive_fields = array(
self::SETTING_API_KEY,
);
/**
* Get the API URL based on the configured region.
*
* @since 2.2.0
*
* @return string
*/
protected function get_api_url() {
$region = $this->get_setting( self::SETTING_REGION, self::OPTION_REGION_GLOBAL );
return $region === self::OPTION_REGION_EU ? self::API_URL_EU : self::API_URL_GLOBAL;
}
public function send() {
try {
$atts = $this->get_send_atts();
$source = $this->get_att( 'source' );
$params = $this->get_request_params();
$email = $this->email;
$this->set_email_log_data( $atts['subject'], $atts['message'], $atts['to'], empty( $atts['from']['name'] ) ? $atts['from']['email'] : sprintf( '%s <%s>', $atts['from']['name'], $atts['from']['email'] ), $atts['headers'], $atts['attachments'], $source, $params );
$this->logger->log( $email, 'started', __( 'Starting email send for SendGrid connector.', 'gravitysmtp' ) );
if ( $this->is_test_mode() ) {
$this->events->update( array( 'status' => 'sandboxed' ), $email );
$this->logger->log( $email, 'sandboxed', __( 'Email sandboxed.', 'gravitysmtp' ) );
return true;
}
$response = wp_safe_remote_post( $this->get_api_url(), $params );
if ( (int) wp_remote_retrieve_response_code( $response ) > 299 ) {
$this->events->update( array( 'status' => 'failed' ), $email );
$this->logger->log( $email, 'failed', wp_remote_retrieve_body( $response ) );
return $email;
}
$this->events->update( array( 'status' => 'sent' ), $email );
$this->logger->log( $email, 'sent', __( 'Email successfully sent.', 'gravitysmtp' ) );
return true;
} catch ( \Exception $e ) {
$this->events->update( array( 'status' => 'failed' ), $email );
$this->logger->log( $email, 'failed', $e->getMessage() );
return $email;
}
}
/**
* Get the request parameters for sending email through connector.
*
* @since 1.0
*
* @return array
*/
public function get_request_params() {
$atts = $this->get_send_atts();
$api_key = $this->get_setting( self::SETTING_API_KEY );
if ( ! empty( $atts['headers']['content-type'] ) && strpos( $atts['headers']['content-type'], 'text/html' ) !== false ) {
$message_type = 'text/html';
} else {
$message_type = 'text/plain';
}
$body = array(
'from' => array( 'email' => $atts['from']['email'], 'name' => $atts['from']['name'] ),
'personalizations' => $this->get_recipients( $atts['to'], $atts['headers'] ),
'subject' => $atts['subject'],
'content' => array(
array(
'value' => $atts['message'],
'type' => $message_type,
),
),
);
if ( ! empty( $atts['reply_to'] ) ) {
$body['reply_to_list'] = $atts['reply_to'];
}
if ( ! empty( $atts['attachments'] ) ) {
$body['attachments'] = $this->get_attachments( $atts['attachments'] );
}
$additional_headers = $this->get_filtered_message_headers();
if ( ! empty( $additional_headers ) ) {
$body['headers'] = $additional_headers;
}
$ip_pool_name = trim( $this->get_setting( self::SETTING_IP_POOL_NAME, '' ) );
if ( ! empty( $ip_pool_name ) ) {
$body['ip_pool_name'] = $ip_pool_name;
}
return array(
'body' => json_encode( $body ),
'headers' => $this->get_request_headers( $api_key ),
);
}
/**
* Get the attributes for sending email.
*
* @since 1.0
*
* @return array
*/
protected function get_send_atts() {
$headers = $this->get_parsed_headers( $this->get_att( 'headers', array() ) );
if ( ! empty( $headers['content-type'] ) ) {
$headers['content-type'] = $this->get_att( 'content_type', $headers['content-type'] );
}
return array(
'to' => $this->get_att( 'to', '' ),
'subject' => $this->get_att( 'subject', '' ),
'message' => $this->get_att( 'message', '' ),
'headers' => $headers,
'attachments' => $this->get_att( 'attachments', array() ),
'from' => $this->get_from( true ),
'reply_to' => $this->get_reply_to( true ),
);
}
protected function get_recipients( $to, $headers ) {
$recipients = array(
'to' => $to->as_array(),
);
if ( ! empty( $headers['cc'] ) ) {
$recipients['cc'] = $headers['cc']->as_array();
}
if ( ! empty( $headers['bcc'] ) ) {
$recipients['bcc'] = $headers['bcc']->as_array();
}
$recipients = array_filter( $recipients );
return array( $recipients );
}
protected function get_request_headers( $api_key ) {
return array(
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . $api_key,
);
}
protected function get_attachments( $attachments ) {
$data = array();
foreach ( $attachments as $custom_name => $attachment ) {
$file = false;
try {
if ( is_file( $attachment ) && is_readable( $attachment ) ) {
$fileName = is_numeric( $custom_name ) ? basename( $attachment ) : $custom_name;
$contentId = wp_hash( $attachment );
$file = file_get_contents( $attachment );
$mimeType = mime_content_type( $attachment );
$filetype = str_replace( ';', '', trim( $mimeType ) );
}
} catch ( \Exception $e ) {
$file = false;
}
if ( $file === false ) {
continue;
}
$data[] = array(
'type' => $filetype,
'filename' => $fileName,
'disposition' => 'attachment',
'content_id' => $contentId,
'content' => base64_encode( $file ),
);
}
return $data;
}
public function connector_data() {
return array(
self::SETTING_API_KEY => $this->get_setting( self::SETTING_API_KEY, '' ),
self::SETTING_REGION => $this->get_setting( self::SETTING_REGION, self::OPTION_REGION_GLOBAL ),
self::SETTING_IP_POOL_NAME => $this->get_setting( self::SETTING_IP_POOL_NAME, '' ),
self::SETTING_FROM_EMAIL => $this->get_setting( self::SETTING_FROM_EMAIL, '' ),
self::SETTING_FORCE_FROM_EMAIL => $this->get_setting( self::SETTING_FORCE_FROM_EMAIL, false ),
self::SETTING_FROM_NAME => $this->get_setting( self::SETTING_FROM_NAME, '' ),
self::SETTING_FORCE_FROM_NAME => $this->get_setting( self::SETTING_FORCE_FROM_NAME, false ),
self::SETTING_REPLY_TO_EMAIL => $this->get_setting( self::SETTING_REPLY_TO_EMAIL, '' ),
self::SETTING_FORCE_REPLY_TO_EMAIL => $this->get_setting( self::SETTING_FORCE_REPLY_TO_EMAIL, false ),
);
}
public function settings_fields() {
return array(
'title' => esc_html__( 'SendGrid Settings', 'gravitysmtp' ),
'description' => '',
'fields' => array_merge(
array(
array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'Configuration', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => [ 4, 0, 4, 0 ],
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
),
array(
'component' => 'Input',
'props' => array(
'labelAttributes' => array(
'label' => esc_html__( 'SendGrid API Key', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'medium',
),
'helpTextAttributes' => array(
'asHtml' => true,
/* translators: 1: opening anchor tag, 2: closing anchor tag */
'content' => sprintf( __( 'To obtain an API key from SendGrid you will need to %1$sgenerate an API key%2$s. To send emails, the API key only requires \'Mail Send\' access.', 'gravitysmtp' ), '<a class="gform-link gform-typography--size-text-xs" href="https://app.sendgrid.com/settings/api_keys" target="_blank" rel="noopener noreferrer">', '</a>' ),
'size' => 'text-xs',
'weight' => 'regular',
),
'name' => self::SETTING_API_KEY,
'size' => 'size-l',
'spacing' => 6,
'value' => $this->get_setting( self::SETTING_API_KEY, '' ),
),
),
array(
'component' => 'Box',
'props' => array(
'display' => 'block',
'spacing' => 6,
),
'fields' => array(
array(
'component' => 'Label',
'props' => array(
'label' => __( 'Region', 'gravitysmtp' ),
'weight' => 'medium',
'size' => 'text-sm',
'spacing' => 2,
),
),
array(
'component' => 'InputGroup',
'props' => array(
'customAttributes' => array(
'style' => array(
'display' => 'flex',
),
),
'id' => self::SETTING_REGION . '_group',
'initialValue' => $this->get_setting( self::SETTING_REGION, self::OPTION_REGION_GLOBAL ),
'inputType' => 'radio',
'spacing' => 2,
'data' => array(
array(
'id' => self::SETTING_REGION . '_' . self::OPTION_REGION_GLOBAL,
'name' => self::SETTING_REGION,
'value' => self::OPTION_REGION_GLOBAL,
'size' => 'size-md',
'spacing' => array( 0, 4, 0, 0 ),
'labelAttributes' => array(
'label' => __( 'Global', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'regular',
),
),
array(
'id' => self::SETTING_REGION . '_' . self::OPTION_REGION_EU,
'name' => self::SETTING_REGION,
'value' => self::OPTION_REGION_EU,
'size' => 'size-md',
'spacing' => array( 0, 4, 0, 0 ),
'labelAttributes' => array(
'label' => __( 'EU', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'regular',
),
),
),
),
),
array(
'component' => 'Text',
'props' => array(
'asHtml' => true,
/* translators: 1: opening anchor tag, 2: closing anchor tag */
'content' => sprintf( __( 'Choose your SendGrid API region. Select EU for European data residency. Important: the EU region requires an API key created under an EU subuser. For more information, visit %1$sSendGrid Data Residency%2$s.', 'gravitysmtp' ), '<a class="gform-link gform-typography--size-text-xs" href="https://www.twilio.com/docs/sendgrid/data-residency" target="_blank" rel="noopener noreferrer">', '</a>' ),
'size' => 'text-xs',
'weight' => 'regular',
),
),
),
),
array(
'component' => 'Input',
'props' => array(
'labelAttributes' => array(
'label' => esc_html__( 'IP Pool Name', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'medium',
),
'helpTextAttributes' => array(
'asHtml' => true,
/* translators: 1: opening anchor tag, 2: closing anchor tag */
'content' => sprintf( __( 'Optional. Enter the name of a SendGrid IP pool to route emails through specific IPs (e.g. for EU/GDPR compliance). The pool must already exist in your SendGrid account. For more information, see %1$sSendGrid IP Pools%2$s.', 'gravitysmtp' ), '<a class="gform-link gform-typography--size-text-xs" href="https://docs.sendgrid.com/ui/account-and-settings/ip-pools" target="_blank" rel="noopener noreferrer">', '</a>' ),
'size' => 'text-xs',
'weight' => 'regular',
),
'name' => self::SETTING_IP_POOL_NAME,
'size' => 'size-l',
'spacing' => 6,
'value' => $this->get_setting( self::SETTING_IP_POOL_NAME, '' ),
),
),
array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'General Settings', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => 4,
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
),
),
$this->get_from_settings_fields(),
$this->get_reply_to_settings_fields(),
),
);
}
public function migration_map() {
return array(
array(
'original_key' => 'gravityformsaddon_gravityformssendgrid_settings',
'sub_key' => 'apiKey',
'new_key' => self::SETTING_API_KEY,
),
);
}
/**
* Verify the API key with the API.
*
* @since 1.0
*
* @return true|\WP_Error
*/
private function verify_api_key() {
$api_key = $this->get_setting( self::SETTING_API_KEY );
$url = str_replace( 'mail/send', 'scopes', $this->get_api_url() );
if ( empty( $api_key ) ) {
return new \WP_Error( 'missing_api_key', __( 'No API Key provided.', 'gravitysmtp' ) );
}
$response = wp_remote_get(
$url,
array(
'headers' => $this->get_request_headers( $api_key ),
)
);
$response_code = wp_remote_retrieve_response_code( $response );
if ( $response_code != '200' ) {
return new \WP_Error( 'invalid_api_key', __( 'Invalid API Key provided.', 'gravitysmtp' ) );
}
$scopes = json_decode( wp_remote_retrieve_body( $response ), true );
if ( ! in_array( 'mail.send', $scopes['scopes'] ) ) {
return new \WP_Error( 'insufficient_permissions', __( 'API Key does not have permission to send mail.', 'gravitysmtp' ) );
}
return true;
}
/**
* Determine if the API credentials are configured correctly.
*
* @since 1.0
*
* @return bool|\WP_Error
*/
public function is_configured() {
$valid_api = $this->verify_api_key();
if ( is_wp_error( $valid_api ) ) {
self::$configured = $valid_api;
return $valid_api;
}
self::$configured = true;
return true;
}
}
@@ -0,0 +1,393 @@
<?php
namespace Gravity_Forms\Gravity_SMTP\Connectors\Types;
use Exception;
use Gravity_Forms\Gravity_SMTP\Connectors\Connector_Base;
use Gravity_Forms\Gravity_SMTP\Feature_Flags\Feature_Flag_Manager;
use WP_Error;
/**
* Connector for SMTP2GO
*
* @since 1.0
*/
class Connector_Smtp2go extends Connector_Base {
const SETTING_API_KEY = 'api_key';
protected $name = 'smtp2go';
protected $title = 'SMTP2GO';
protected $logo = 'SMTP2GO';
protected $full_logo = 'SMTP2GOFull';
protected $url = 'https://api.smtp2go.com/v3/email/send';
protected $sensitive_fields = array(
self::SETTING_API_KEY,
);
public function get_description() {
return esc_html__( 'SMTP2GO is a cloud-based email delivery service that ensures reliable inbox placement with built-in analytics and automatic failover. Offering a free plan and scalable options, SMTP2GO helps businesses send transactional and marketing emails effortlessly.', 'gravitysmtp' );
}
/**
* Sends email via SMTP2GO.
*
* @since 1.0
*
* @return int returns the email ID
*/
public function send() {
try {
$atts = $this->get_send_atts();
$source = $this->get_att( 'source' );
$params = $this->get_request_params();
$email = $this->email;
$this->set_email_log_data( $atts['subject'], $atts['message'], $atts['to'], $atts['from']['from'], $atts['headers'], $atts['attachments'], $source, $params );
$this->logger->log( $email, 'started', __( 'Starting email send for SMTP2GO connector.', 'gravitysmtp' ) );
if ( $this->is_test_mode() ) {
$this->events->update( array( 'status' => 'sandboxed' ), $email );
$this->logger->log( $email, 'sandboxed', __( 'Email sandboxed.', 'gravitysmtp' ) );
return true;
}
$response = wp_safe_remote_post( $this->url, $params );
$is_success = in_array( (int) wp_remote_retrieve_response_code( $response ), array( 200, 201, 202 ) );
if ( ! $is_success ) {
$this->log_failure( $email, wp_remote_retrieve_body( $response ) );
return $email;
}
$this->events->update( array( 'status' => 'sent' ), $email );
$this->logger->log( $email, 'sent', __( 'Email successfully sent.', 'gravitysmtp' ) );
return true;
} catch ( Exception $e ) {
$this->log_failure( $email, $e->getMessage() );
return $email;
}
}
/**
* Get the request parameters for sending email through connector.
*
* @since 1.0
*
* @return array
*/
public function get_request_params() {
$atts = $this->get_send_atts();
$api_key = $this->get_setting( self::SETTING_API_KEY );
$from_value = isset( $atts['from']['name'] )
? sprintf( '"%s" <%s>', iconv_mime_encode( 'From', $atts['from']['name'], array( 'line-length' => apply_filters( 'gravitysmtp_smtp2go_from_name_line_length', 400 ) ) ), $atts['from']['email'] )
: $atts['from']['email'];
$from_value = str_replace( 'From: ', '', $from_value );
$body = array(
'sender' => $from_value,
'subject' => $atts['subject'],
'to' => array(),
);
foreach ( $atts['to']->as_array() as $to_value ) {
$email_value = isset( $to_value['name'] ) ? sprintf( '%s <%s>', $to_value['name'], $to_value['email'] ) : $to_value['email'];
$body['to'][] = $email_value;
}
// Setting content
$is_html = ! empty( $atts['headers']['content-type'] ) && strpos( $atts['headers']['content-type'], 'text/html' ) !== false;
if ( $is_html ) {
$body['html_body'] = $atts['message'];
} else {
$body['text_body'] = $atts['message'];
}
// Setting cc
if ( ! empty( $atts['headers']['cc'] ) ) {
$body['cc'] = array();
foreach ( $atts['headers']['cc']->as_array() as $cc_value ) {
$body['cc'][] = $cc_value['email'];
}
}
// Setting bcc
if ( ! empty( $atts['headers']['bcc'] ) ) {
$body['bcc'] = array();
foreach ( $atts['headers']['bcc']->as_array() as $bcc_value ) {
$body['bcc'][] = $bcc_value['email'];
}
}
$body['custom_headers'] = array();
// Setting reply to
if ( ! empty( $atts['reply_to'] ) ) {
if ( isset( $atts['reply_to']['email'] ) ) {
$reply_to = $atts['reply_to'];
} else {
$reply_to = $atts['reply_to'][0];
}
$body['custom_headers'][] = array(
'header' => 'Reply-To',
'value' => $reply_to['email'],
);
}
// Setting attachments
if ( ! empty( $atts['attachments'] ) ) {
$body['attachments'] = $this->get_attachments( $atts['attachments'] );
}
return array(
'body' => json_encode( $body ),
'headers' => $this->get_request_headers( $api_key ),
'timeout' => 10
);
}
/**
* Get the attributes for sending email.
*
* @since 1.0
*
* @return array
*/
protected function get_send_atts() {
$headers = $this->get_parsed_headers( $this->get_att( 'headers', array() ) );
if ( ! empty( $headers['content-type'] ) ) {
$headers['content-type'] = $this->get_att( 'content_type', $headers['content-type'] );
}
return array(
'to' => $this->get_att( 'to', '' ),
'subject' => $this->get_att( 'subject', '' ),
'message' => $this->get_att( 'message', '' ),
'headers' => $headers,
'attachments' => $this->get_att( 'attachments', array() ),
'from' => $this->get_from( true ),
'reply_to' => $this->get_reply_to( true ),
);
}
/**
* Gets a list of attachments, and returns them in a format that can be used by the API.
*
* @param array $attachments the list of attachments
*
* @since 1.0
*
* @return array Returns an array of attachments. Each item with a 'content' and 'name' property.
*/
protected function get_attachments( $attachments ) {
$data = array();
foreach ( $attachments as $custom_name => $attachment ) {
try {
if ( is_file( $attachment ) && is_readable( $attachment ) ) {
$fileName = is_numeric( $custom_name ) ? basename( $attachment ) : $custom_name;
$content = base64_encode( file_get_contents( $attachment ) );
$data[] = array(
'filename' => $fileName,
'fileblob' => $content,
'mimetype' => mime_content_type( $attachment ),
);
}
} catch ( Exception $e ) {
continue;
}
}
return $data;
}
/**
* Gets the headers to be used in the API request.
*
* @since 1.0
*
* @return array returns the header array to be passed to SMTP2GO's API
*/
protected function get_request_headers( $api_key ) {
return array(
'content-type' => 'application/json',
'accept' => 'application/json',
'X-Smtp2go-Api-Key' => $api_key,
);
}
/**
* Logs an email send failure.
*
* @since 1.0
*
* @param string $email the email that failed
* @param string $error_message the error message
*/
private function log_failure( $email, $error_message ) {
$this->events->update( array( 'status' => 'failed' ), $email );
$this->logger->log( $email, 'failed', $error_message );
}
/**
* Connector data.
*
* @since 1.0
*
* @return array
*/
public function connector_data() {
return array(
self::SETTING_API_KEY => $this->get_setting( self::SETTING_API_KEY, '' ),
self::SETTING_FROM_EMAIL => $this->get_setting( self::SETTING_FROM_EMAIL, '' ),
self::SETTING_FORCE_FROM_EMAIL => $this->get_setting( self::SETTING_FORCE_FROM_EMAIL, false ),
self::SETTING_FROM_NAME => $this->get_setting( self::SETTING_FROM_NAME, '' ),
self::SETTING_FORCE_FROM_NAME => $this->get_setting( self::SETTING_FORCE_FROM_NAME, false ),
self::SETTING_REPLY_TO_EMAIL => $this->get_setting( self::SETTING_REPLY_TO_EMAIL, '' ),
self::SETTING_FORCE_REPLY_TO_EMAIL => $this->get_setting( self::SETTING_FORCE_REPLY_TO_EMAIL, false ),
);
}
/**
* Settings fields.
*
* @since 1.0
*
* @return array
*/
public function settings_fields() {
return array(
'title' => esc_html__( 'SMTP2GO Settings', 'gravitysmtp' ),
'description' => '',
'fields' => array_merge(
array(
array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'Configuration', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => array( 4, 0, 4, 0 ),
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
),
array(
'component' => 'Input',
'props' => array(
'labelAttributes' => array(
'label' => esc_html__( 'API Key', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'medium',
),
'helpTextAttributes' => array(
'asHtml' => true,
/* translators: 1: opening anchor tag, 2: closing anchor tag */
'content' => sprintf( __( 'To generate an API key from SMTP2GO, log in to your SMTP2GO dashboard and navigate to the API section. %1$sCreate a new API key%2$s and ensure your %3$sverified senders%2$s are configured correctly.', 'gravitysmtp' ), '<a class="gform-link gform-typography--size-text-xs" href="https://docs.gravitysmtp.com/obtaining-your-smtp2go-api-key/" target="_blank" rel="noopener noreferrer">', '</a>', '<a class="gform-link gform-typography--size-text-xs" href="https://docs.gravitysmtp.com/adding-verified-senders-in-smtp2go/" target="_blank" rel="noopener noreferrer">' ),
'size' => 'text-xs',
'weight' => 'regular',
),
'name' => self::SETTING_API_KEY,
'size' => 'size-l',
'spacing' => 6,
'value' => $this->get_setting( self::SETTING_API_KEY, '' ),
),
),
array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'General Settings', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => 4,
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
),
),
$this->get_from_settings_fields(),
$this->get_reply_to_settings_fields(),
),
);
}
/**
* Determine if the API credentials are configured correctly.
*
* @since 1.0
*
* @return bool|WP_Error returns true if configured, or a WP_Error object if not
*/
public function is_configured() {
$valid_api = $this->verify_api_key();
if ( is_wp_error( $valid_api ) ) {
self::$configured = $valid_api;
return $valid_api;
}
self::$configured = true;
return true;
}
/**
* Verify the API key with the API.
*
* @since 1.0
*
* @return true|WP_Error
*/
private function verify_api_key() {
$api_key = $this->get_setting( self::SETTING_API_KEY );
$url = 'https://api.smtp2go.com/v3/stats/email_bounces';
if ( empty( $api_key ) ) {
return new WP_Error( 'missing_api_key', __( 'No API Key provided.', 'gravitysmtp' ) );
}
$response = wp_remote_post(
$url,
array(
'headers' => $this->get_request_headers( $api_key ),
)
);
if ( wp_remote_retrieve_response_code( $response ) != '200' ) {
return new WP_Error( 'invalid_api_key', __( 'Invalid API Key provided.', 'gravitysmtp' ) );
}
return true;
}
/**
* Get the unique data for this connector, merged with the default/common data for all
* connectors in the system.
*
* @since 1.0
*
* @return array
*/
protected function get_merged_data() {
$data = parent::get_merged_data();
$data['disabled'] = ! Feature_Flag_Manager::is_enabled( 'smtp2go_integration' );
return $data;
}
}
@@ -0,0 +1,550 @@
<?php
namespace Gravity_Forms\Gravity_SMTP\Connectors\Types;
use Exception;
use Gravity_Forms\Gravity_SMTP\Connectors\Connector_Base;
use Gravity_Forms\Gravity_SMTP\Feature_Flags\Feature_Flag_Manager;
use WP_Error;
/**
* Connector for SMTP.com
*
* @since 1.0
*/
class Connector_SMTPCom extends Connector_Base {
const SETTING_API_KEY = 'api_key';
const SETTING_CHANNEL = 'channel';
protected $name = 'smtpcom';
protected $title = 'SMTP.com';
protected $disabled = true;
protected $logo = 'SMTP';
protected $full_logo = 'SMTPFull';
protected $url = 'https://api.smtp.com/v4';
protected $sensitive_fields = array(
self::SETTING_API_KEY,
);
public function get_description() {
return esc_html__( 'Premium email delivery service with a powerful REST API for sending transactional and marketing emails at scale.', 'gravitysmtp' );
}
/**
* Sends email via SMTP.com.
*
* @since 1.0
*
* @return mixed
*/
public function send() {
try {
$atts = $this->get_send_atts();
$source = $this->get_att( 'source' );
$params = $this->get_request_params();
$email = $this->email;
$this->set_email_log_data( $atts['subject'], $atts['message'], $atts['to'], $atts['from']['from'], $atts['headers'], $atts['attachments'], $source, $params );
$this->logger->log( $email, 'started', __( 'Starting email send for SMTP.com connector.', 'gravitysmtp' ) );
if ( $this->is_test_mode() ) {
$this->events->update( array( 'status' => 'sandboxed' ), $email );
$this->logger->log( $email, 'sandboxed', __( 'Email sandboxed.', 'gravitysmtp' ) );
return true;
}
$response = wp_safe_remote_post( $this->url . '/messages', $params );
if ( is_wp_error( $response ) ) {
$this->log_failure( $email, $response->get_error_message() );
return $email;
}
$response_code = (int) wp_remote_retrieve_response_code( $response );
$response_body = wp_remote_retrieve_body( $response );
$response_data = json_decode( $response_body, true );
// Handle HTTP-level errors without valid JSON
if ( empty( $response_data ) || ! is_array( $response_data ) ) {
if ( $response_code !== 200 ) {
$this->log_failure( $email, __( 'Unexpected response from SMTP.com.', 'gravitysmtp' ) );
return $email;
}
}
// Handle HTTP status errors with specific messages
if ( $response_code === 401 ) {
$this->log_failure( $email, __( 'Invalid API key.', 'gravitysmtp' ) );
return $email;
}
if ( $response_code === 403 ) {
$this->log_failure( $email, __( 'Forbidden — check channel permissions.', 'gravitysmtp' ) );
return $email;
}
if ( $response_code === 429 ) {
$this->log_failure( $email, __( 'Rate limit exceeded.', 'gravitysmtp' ) );
return $email;
}
if ( $response_code >= 500 ) {
$this->log_failure( $email, __( 'SMTP.com server error.', 'gravitysmtp' ) );
return $email;
}
// Parse JSend response format
if ( isset( $response_data['status'] ) ) {
if ( $response_data['status'] === 'success' ) {
$this->events->update( array( 'status' => 'sent' ), $email );
$this->logger->log( $email, 'sent', __( 'Email successfully sent.', 'gravitysmtp' ) );
return true;
}
if ( $response_data['status'] === 'error' ) {
$error_message = isset( $response_data['message'] ) ? $response_data['message'] : __( 'Unknown error from SMTP.com.', 'gravitysmtp' );
$this->log_failure( $email, $error_message );
return $email;
}
if ( $response_data['status'] === 'fail' ) {
$error_message = isset( $response_data['data'] ) ? wp_json_encode( $response_data['data'] ) : __( 'Validation failed.', 'gravitysmtp' );
$this->log_failure( $email, $error_message );
return $email;
}
}
// Fallback for 2xx without a recognized JSend status key
if ( $response_code >= 200 && $response_code < 300 ) {
$this->events->update( array( 'status' => 'sent' ), $email );
$this->logger->log( $email, 'sent', __( 'Email accepted (HTTP 2xx) but response did not include a JSend status field.', 'gravitysmtp' ) );
return true;
}
$this->log_failure( $email, $response_body );
return $email;
} catch ( Exception $e ) {
$this->log_failure( $email, $e->getMessage() );
return $email;
}
}
/**
* Get the request parameters for sending email through connector.
*
* @since 1.0
*
* @return array
*/
public function get_request_params() {
$atts = $this->get_send_atts();
$api_key = $this->get_setting( self::SETTING_API_KEY );
$channel = $this->get_setting( self::SETTING_CHANNEL, '' );
$recipients = array( 'to' => $this->build_recipient_list( $atts['to']->as_array() ) );
if ( ! empty( $atts['headers']['cc'] ) ) {
$recipients['cc'] = $this->build_recipient_list( $atts['headers']['cc']->as_array() );
}
if ( ! empty( $atts['headers']['bcc'] ) ) {
$recipients['bcc'] = $this->build_recipient_list( $atts['headers']['bcc']->as_array() );
}
// Build originator
$from = array( 'address' => $atts['from']['email'] );
if ( ! empty( $atts['from']['name'] ) ) {
$from['name'] = $atts['from']['name'];
}
$originator = array( 'from' => $from );
// Build reply-to
if ( ! empty( $atts['reply_to'] ) ) {
if ( isset( $atts['reply_to']['email'] ) ) {
$reply_to_data = $atts['reply_to'];
} else {
$reply_to_data = $atts['reply_to'][0];
}
$reply_to_entry = array( 'address' => $reply_to_data['email'] );
if ( ! empty( $reply_to_data['name'] ) ) {
$reply_to_entry['name'] = $reply_to_data['name'];
}
$originator['reply_to'] = $reply_to_entry;
}
// Build body parts
$is_html = ! empty( $atts['headers']['content-type'] ) && strpos( $atts['headers']['content-type'], 'text/html' ) !== false;
$parts = array();
if ( $is_html ) {
$parts[] = array(
'type' => 'text/html',
'content' => base64_encode( $atts['message'] ),
'charset' => 'utf-8',
'encoding' => 'base64',
);
// Generate plain text fallback
$text_body = wp_strip_all_tags( $atts['message'] );
$text_body = preg_replace( "/([\r\n]{2,}|[\n]{2,}|[\r]{2,}|[\r\t]{2,}|[\n\t]{2,})/", "\n", $text_body );
$parts[] = array(
'type' => 'text/plain',
'content' => base64_encode( $text_body ),
'charset' => 'utf-8',
'encoding' => 'base64',
);
} else {
$parts[] = array(
'type' => 'text/plain',
'content' => base64_encode( $atts['message'] ),
'charset' => 'utf-8',
'encoding' => 'base64',
);
}
$body = array( 'parts' => $parts );
// Build attachments
if ( ! empty( $atts['attachments'] ) ) {
$attachment_data = $this->get_attachments( $atts['attachments'] );
if ( ! empty( $attachment_data ) ) {
$body['attachments'] = $attachment_data;
}
}
// Assemble the full message payload
$message = array(
'channel' => $channel,
'recipients' => $recipients,
'originator' => $originator,
'subject' => $atts['subject'],
'body' => $body,
);
// Build custom headers
$custom_headers = $this->get_filtered_message_headers();
if ( ! empty( $custom_headers ) ) {
$message['custom_headers'] = $custom_headers;
}
return array(
'body' => wp_json_encode( $message ),
'headers' => $this->get_request_headers( $api_key ),
);
}
/**
* Get the attributes for sending email.
*
* @since 1.0
*
* @return array
*/
protected function get_send_atts() {
$headers = $this->get_parsed_headers( $this->get_att( 'headers', array() ) );
if ( ! empty( $headers['content-type'] ) ) {
$headers['content-type'] = $this->get_att( 'content_type', $headers['content-type'] );
}
return array(
'to' => $this->get_att( 'to', '' ),
'subject' => $this->get_att( 'subject', '' ),
'message' => $this->get_att( 'message', '' ),
'headers' => $headers,
'attachments' => $this->get_att( 'attachments', array() ),
'from' => $this->get_from( true ),
'reply_to' => $this->get_reply_to( true ),
);
}
/**
* Gets a list of attachments, and returns them in a format that can be used by the SMTP.com API.
*
* @since 1.0
*
* @param array $attachments The list of attachments.
*
* @return array Returns an array of attachments formatted for SMTP.com.
*/
protected function get_attachments( $attachments ) {
$data = array();
foreach ( $attachments as $custom_name => $attachment ) {
try {
if ( is_file( $attachment ) && is_readable( $attachment ) ) {
$file_name = is_numeric( $custom_name ) ? basename( $attachment ) : $custom_name;
$content = base64_encode( file_get_contents( $attachment ) );
$data[] = array(
'filename' => $file_name,
'content' => $content,
'type' => mime_content_type( $attachment ),
'encoding' => 'base64',
);
}
} catch ( Exception $e ) {
continue;
}
}
return $data;
}
/**
* Gets the headers to be used in the API request.
*
* @since 1.0
*
* @param string $api_key The API key for authentication.
*
* @return array Returns the header array to be passed to SMTP.com's API.
*/
protected function get_request_headers( $api_key ) {
return array(
'Content-Type' => 'application/json',
'Accept' => 'application/json',
'Authorization' => 'Bearer ' . $api_key,
);
}
/**
* Logs an email send failure.
*
* @since 1.0
*
* @param string $email The email that failed.
* @param string $error_message The error message.
*/
private function log_failure( $email, $error_message ) {
$this->events->update( array( 'status' => 'failed' ), $email );
$this->logger->log( $email, 'failed', $error_message );
}
/**
* Builds a recipient list formatted for the SMTP.com API.
*
* @since 2.2.0
*
* @param array $recipients Raw recipient array with 'email' and optional 'name' keys.
*
* @return array Formatted recipient list for the API payload.
*/
private function build_recipient_list( $recipients ) {
$list = array();
foreach ( $recipients as $recipient ) {
$entry = array( 'address' => $recipient['email'] );
if ( ! empty( $recipient['name'] ) ) {
$entry['name'] = $recipient['name'];
}
$list[] = $entry;
}
return $list;
}
/**
* Connector data.
*
* @since 1.0
*
* @return array
*/
public function connector_data() {
return array(
self::SETTING_API_KEY => $this->get_setting( self::SETTING_API_KEY, '' ),
self::SETTING_CHANNEL => $this->get_setting( self::SETTING_CHANNEL, '' ),
self::SETTING_FROM_EMAIL => $this->get_setting( self::SETTING_FROM_EMAIL, '' ),
self::SETTING_FORCE_FROM_EMAIL => $this->get_setting( self::SETTING_FORCE_FROM_EMAIL, false ),
self::SETTING_FROM_NAME => $this->get_setting( self::SETTING_FROM_NAME, '' ),
self::SETTING_FORCE_FROM_NAME => $this->get_setting( self::SETTING_FORCE_FROM_NAME, false ),
self::SETTING_REPLY_TO_EMAIL => $this->get_setting( self::SETTING_REPLY_TO_EMAIL, '' ),
self::SETTING_FORCE_REPLY_TO_EMAIL => $this->get_setting( self::SETTING_FORCE_REPLY_TO_EMAIL, false ),
);
}
/**
* Settings fields.
*
* @since 1.0
*
* @return array
*/
public function settings_fields() {
return array(
'title' => esc_html__( 'SMTP.com Settings', 'gravitysmtp' ),
'description' => '',
'fields' => array_merge(
array(
array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'Configuration', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => array( 4, 0, 4, 0 ),
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
),
array(
'component' => 'Input',
'props' => array(
'labelAttributes' => array(
'label' => esc_html__( 'API Key', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'medium',
),
'helpTextAttributes' => array(
'asHtml' => true,
'content' => __( 'Enter your SMTP.com API key. You can find this in your SMTP.com dashboard under API Settings.', 'gravitysmtp' ),
'size' => 'text-xs',
'weight' => 'regular',
),
'name' => self::SETTING_API_KEY,
'size' => 'size-l',
'spacing' => 6,
'value' => $this->get_setting( self::SETTING_API_KEY, '' ),
),
),
array(
'component' => 'Input',
'props' => array(
'labelAttributes' => array(
'label' => esc_html__( 'Channel Name', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'medium',
),
'helpTextAttributes' => array(
'asHtml' => true,
'content' => __( 'Enter your SMTP.com channel name. You can find your channels in the SMTP.com dashboard under Channels.', 'gravitysmtp' ),
'size' => 'text-xs',
'weight' => 'regular',
),
'name' => self::SETTING_CHANNEL,
'size' => 'size-l',
'spacing' => 6,
'value' => $this->get_setting( self::SETTING_CHANNEL, '' ),
),
),
array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'General Settings', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => 4,
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
),
),
$this->get_from_settings_fields(),
$this->get_reply_to_settings_fields(),
),
);
}
/**
* Determine if the API credentials are configured correctly.
*
* @since 1.0
*
* @return bool|WP_Error Returns true if configured, or a WP_Error object if not.
*/
public function is_configured() {
$api_key = $this->get_setting( self::SETTING_API_KEY, '' );
if ( empty( $api_key ) ) {
$error = new WP_Error( 'missing_api_key', __( 'API key is required.', 'gravitysmtp' ) );
self::$configured = $error;
return $error;
}
$channel = $this->get_setting( self::SETTING_CHANNEL, '' );
if ( empty( $channel ) ) {
$error = new WP_Error( 'missing_channel', __( 'Channel name is required.', 'gravitysmtp' ) );
self::$configured = $error;
return $error;
}
$response = wp_remote_get(
$this->url . '/account',
array(
'headers' => $this->get_request_headers( $api_key ),
)
);
if ( is_wp_error( $response ) ) {
$error = new WP_Error( 'connection_error', $response->get_error_message() );
self::$configured = $error;
return $error;
}
$response_code = (int) wp_remote_retrieve_response_code( $response );
if ( $response_code === 401 ) {
$error = new WP_Error( 'invalid_api_key', __( 'Invalid API key.', 'gravitysmtp' ) );
self::$configured = $error;
return $error;
}
if ( $response_code !== 200 ) {
$error = new WP_Error( 'connection_error', __( 'Unable to connect to SMTP.com. Please check your credentials.', 'gravitysmtp' ) );
self::$configured = $error;
return $error;
}
self::$configured = true;
return true;
}
/**
* Get the unique data for this connector, merged with the default/common data for all
* connectors in the system.
*
* @since 1.0
*
* @return array
*/
protected function get_merged_data() {
$data = parent::get_merged_data();
$data['disabled'] = ! Feature_Flag_Manager::is_enabled( 'smtpcom_integration' );
return $data;
}
}
@@ -0,0 +1,464 @@
<?php
namespace Gravity_Forms\Gravity_SMTP\Connectors\Types;
use Exception;
use Gravity_Forms\Gravity_SMTP\Connectors\Connector_Base;
use Gravity_Forms\Gravity_SMTP\Feature_Flags\Feature_Flag_Manager;
use WP_Error;
/**
* Connector for SparkPost
*
* @since 1.0
*/
class Connector_Sparkpost extends Connector_Base {
const SETTING_API_KEY = 'api_key';
const SETTING_ACCOUNT_LOCATION = 'account_location';
const SETTING_USE_RETURN_PATH = 'use_return_path';
protected $name = 'sparkpost';
protected $title = 'SparkPost';
protected $disabled = true;
protected $logo = 'SparkPost';
protected $full_logo = 'SparkPostFull';
protected $url = 'https://api.sparkpost.com/api/v1';
protected $url_eu = 'https://api.eu.sparkpost.com/api/v1';
protected $sensitive_fields = array(
self::SETTING_API_KEY,
);
public function get_description() {
return esc_html__( 'SparkPost provides email delivery services and transactional email for developers at companies of all sizes.', 'gravitysmtp' );
}
/**
* Sends email via SparkPost.
*
* @since 1.0
*
* @return int returns the email ID
*/
public function send() {
try {
$atts = $this->get_send_atts();
$source = $this->get_att( 'source' );
$params = $this->get_request_params();
$email = $this->email;
$this->set_email_log_data( $atts['subject'], $atts['message'], $atts['to'], $atts['from']['from'], $atts['headers'], $atts['attachments'], $source, $params );
$this->logger->log( $email, 'started', __( 'Starting email send for SparkPost connector.', 'gravitysmtp' ) );
if ( $this->is_test_mode() ) {
$this->events->update( array( 'status' => 'sandboxed' ), $email );
$this->logger->log( $email, 'sandboxed', __( 'Email sandboxed.', 'gravitysmtp' ) );
return true;
}
$response = wp_safe_remote_post( $this->get_base_url() . '/transmissions/', $params );
$is_success = in_array( (int) wp_remote_retrieve_response_code( $response ), array( 200, 201, 202 ) );
if ( ! $is_success ) {
$this->log_failure( $email, wp_remote_retrieve_body( $response ) );
return $email;
}
$this->events->update( array( 'status' => 'sent' ), $email );
$this->logger->log( $email, 'sent', __( 'Email successfully sent.', 'gravitysmtp' ) );
return true;
} catch ( Exception $e ) {
$this->log_failure( $email, $e->getMessage() );
return $email;
}
}
protected function get_base_url() {
$location = $this->get_setting( self::SETTING_ACCOUNT_LOCATION, 'us' );
return $location !== 'us' ? $this->url_eu : $this->url;
}
/**
* Get the request parameters for sending email through connector.
*
* @since 1.0
*
* @return array
*/
public function get_request_params() {
$atts = $this->get_send_atts();
$api_key = $this->get_setting( self::SETTING_API_KEY );
$send_as_transactional = apply_filters( 'gravitysmtp_sparkpost_send_as_transactional', true );
$body = array(
'recipients' => array(),
'content' => array(
'from' => array(
'name' => $atts['from']['name'],
'email' => $atts['from']['email'],
),
'subject' => $atts['subject'],
'headers' => array(),
),
'options' => array(
'transactional' => $send_as_transactional,
),
);
foreach ( $atts['to']->as_array() as $recipient ) {
$body['recipients'][] = array( 'address' => $recipient );
}
// Setting content
$is_html = ! empty( $atts['headers']['content-type'] ) && strpos( $atts['headers']['content-type'], 'text/html' ) !== false;
if ( $is_html ) {
$body['content']['html'] = $atts['message'];
} else {
$body['content']['text'] = $atts['message'];
}
$main_recipient = $atts['to']->first()->as_array();
// Setting cc
if ( ! empty( $atts['headers']['cc'] ) ) {
$cc_headers = array();
foreach ( $atts['headers']['cc']->as_array() as $cc_value ) {
$values = array(
'email' => $cc_value['email'],
'name' => ! empty( $cc_value['name'] ) ? $cc_value['name'] : null,
'header_to' => $main_recipient['email'],
);
$body['recipients'][] = array( 'address' => array_filter( $values ) );
$cc_headers[] = $cc_value['email'];
}
$body['content']['headers']['CC'] = implode( ', ', $cc_headers );
}
// Setting bcc
if ( ! empty( $atts['headers']['bcc'] ) ) {
foreach ( $atts['headers']['bcc']->as_array() as $bcc_value ) {
$values = array(
'email' => $bcc_value['email'],
'name' => ! empty( $bcc_value['name'] ) ? $bcc_value['name'] : null,
'header_to' => $main_recipient['email'],
);
$body['recipients'][] = array( 'address' => array_filter( $values ) );
}
}
// Setting reply to
if ( ! empty( $atts['reply_to'] ) ) {
if ( isset( $atts['reply_to']['email'] ) ) {
$reply_to = $atts['reply_to'];
} else {
$reply_to = $atts['reply_to'][0];
}
$body['content']['reply_to'] = $reply_to['email'];
}
// Setting attachments
if ( ! empty( $atts['attachments'] ) ) {
$body['content']['attachments'] = $this->get_attachments( $atts['attachments'] );
}
if ( empty( $body['content']['headers'] ) ) {
unset( $body['content']['headers'] );
}
return array(
'body' => json_encode( $body ),
'headers' => $this->get_request_headers( $api_key ),
);
}
/**
* Get the attributes for sending email.
*
* @since 1.0
*
* @return array
*/
protected function get_send_atts() {
$headers = $this->get_parsed_headers( $this->get_att( 'headers', array() ) );
if ( ! empty( $headers['content-type'] ) ) {
$headers['content-type'] = $this->get_att( 'content_type', $headers['content-type'] );
}
return array(
'to' => $this->get_att( 'to', '' ),
'subject' => $this->get_att( 'subject', '' ),
'message' => $this->get_att( 'message', '' ),
'headers' => $headers,
'attachments' => $this->get_att( 'attachments', array() ),
'from' => $this->get_from( true ),
'reply_to' => $this->get_reply_to( true ),
);
}
/**
* Gets a list of attachments, and returns them in a format that can be used by the API.
*
* @param array $attachments the list of attachments
*
* @since 1.0
*
* @return array Returns an array of attachments. Each item with a 'content' and 'name' property.
*/
protected function get_attachments( $attachments ) {
$data = array();
foreach ( $attachments as $custom_name => $attachment ) {
try {
if ( is_file( $attachment ) && is_readable( $attachment ) ) {
$fileName = is_numeric( $custom_name ) ? basename( $attachment ) : $custom_name;
$content = base64_encode( file_get_contents( $attachment ) );
$data[] = array(
'name' => $fileName,
'data' => $content,
'type' => mime_content_type( $attachment ),
);
}
} catch ( Exception $e ) {
continue;
}
}
return $data;
}
/**
* Gets the headers to be used in the API request.
*
* @since 1.0
*
* @return array returns the header array to be passed to SparkPost's API
*/
protected function get_request_headers( $api_key ) {
return array(
'content-type' => 'application/json',
'accept' => 'application/json',
'Authorization' => $api_key,
);
}
/**
* Logs an email send failure.
*
* @since 1.0
*
* @param string $email the email that failed
* @param string $error_message the error message
*/
private function log_failure( $email, $error_message ) {
$this->events->update( array( 'status' => 'failed' ), $email );
$this->logger->log( $email, 'failed', $error_message );
}
/**
* Connector data.
*
* @since 1.0
*
* @return array
*/
public function connector_data() {
return array(
self::SETTING_API_KEY => $this->get_setting( self::SETTING_API_KEY, '' ),
self::SETTING_FROM_EMAIL => $this->get_setting( self::SETTING_FROM_EMAIL, '' ),
self::SETTING_FORCE_FROM_EMAIL => $this->get_setting( self::SETTING_FORCE_FROM_EMAIL, false ),
self::SETTING_FROM_NAME => $this->get_setting( self::SETTING_FROM_NAME, '' ),
self::SETTING_FORCE_FROM_NAME => $this->get_setting( self::SETTING_FORCE_FROM_NAME, false ),
self::SETTING_REPLY_TO_EMAIL => $this->get_setting( self::SETTING_REPLY_TO_EMAIL, '' ),
self::SETTING_FORCE_REPLY_TO_EMAIL => $this->get_setting( self::SETTING_FORCE_REPLY_TO_EMAIL, false ),
);
}
/**
* Settings fields.
*
* @since 1.0
*
* @return array
*/
public function settings_fields() {
return array(
'title' => esc_html__( 'SparkPost Settings', 'gravitysmtp' ),
'description' => '',
'fields' => array_merge(
array(
array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'Configuration', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => array( 4, 0, 4, 0 ),
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
),
array(
'component' => 'Select',
'props' => array(
'labelAttributes' => array(
'label' => esc_html__( 'Account Location', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'medium',
),
'name' => self::SETTING_ACCOUNT_LOCATION,
'size' => 'size-l',
'spacing' => 6,
'initialValue' => $this->get_setting( self::SETTING_ACCOUNT_LOCATION, 'us' ),
'options' => array(
array(
'label' => __( 'United States', 'gravitysmtp' ),
'value' => 'us',
),
array(
'label' => __( 'Europe', 'gravitysmtp' ),
'value' => 'eu',
),
),
),
),
array(
'component' => 'Input',
'props' => array(
'labelAttributes' => array(
'label' => esc_html__( 'API Key', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'medium',
),
'helpTextAttributes' => array(
'asHtml' => true,
/* translators: 1: opening anchor tag, 2: closing anchor tag */
'content' => sprintf( __( 'To generate an API key from SparkPost, log in to your SparkPost dashboard, navigate to the API Keys section, and %1$sgenerate your API key%2$s.', 'gravitysmtp' ), '<a class="gform-link gform-typography--size-text-xs" href="https://app.sparkpost.com/account/api-keys" target="_blank" rel="noopener noreferrer">', '</a>' ),
'size' => 'text-xs',
'weight' => 'regular',
),
'name' => self::SETTING_API_KEY,
'size' => 'size-l',
'spacing' => 6,
'value' => $this->get_setting( self::SETTING_API_KEY, '' ),
),
),
array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'General Settings', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => 4,
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
),
array(
'component' => 'Toggle',
'props' => array(
'helpTextAttributes' => array(
'content' => esc_html__( 'If Return Path is enabled this adds the return path to the email header which indicates where non-deliverable notifications should be sent. Bounce messages may be lost if not enabled.', 'gravitysmtp' ),
'size' => 'text-xs',
'weight' => 'regular',
'spacing' => array( 2, 0, 0, 0 ),
),
'helpTextWidth' => 'full',
'initialChecked' => (bool) $this->get_setting( self::SETTING_USE_RETURN_PATH, false ),
'labelAttributes' => array(
'label' => esc_html__( 'Return Path', 'gravitysmtp' ),
),
'labelPosition' => 'left',
'name' => self::SETTING_USE_RETURN_PATH,
'size' => 'size-m',
'spacing' => 5,
'width' => 'full',
),
),
),
$this->get_from_settings_fields(),
$this->get_reply_to_settings_fields(),
),
);
}
/**
* Determine if the API credentials are configured correctly.
*
* @since 1.0
*
* @return bool|WP_Error returns true if configured, or a WP_Error object if not
*/
public function is_configured() {
$valid_api = $this->verify_api_key();
if ( is_wp_error( $valid_api ) ) {
self::$configured = $valid_api;
return $valid_api;
}
self::$configured = true;
return true;
}
/**
* Verify the API key with the API.
*
* @since 1.0
*
* @return true|WP_Error
*/
private function verify_api_key() {
$api_key = $this->get_setting( self::SETTING_API_KEY );
$url = $this->get_base_url() . '/sending-domains?ownership_verified=true';
if ( empty( $api_key ) ) {
return new WP_Error( 'missing_api_key', __( 'No API Key provided.', 'gravitysmtp' ) );
}
$response = wp_remote_get(
$url,
array(
'headers' => $this->get_request_headers( $api_key ),
)
);
if ( wp_remote_retrieve_response_code( $response ) != '200' ) {
return new WP_Error( 'invalid_api_key', __( 'Invalid API Key provided.', 'gravitysmtp' ) );
}
return true;
}
/**
* Get the unique data for this connector, merged with the default/common data for all
* connectors in the system.
*
* @since 1.0
*
* @return array
*/
protected function get_merged_data() {
$data = parent::get_merged_data();
$data['disabled'] = ! Feature_Flag_Manager::is_enabled( 'sparkpost_integration' );
return $data;
}
}
@@ -0,0 +1,593 @@
<?php
namespace Gravity_Forms\Gravity_SMTP\Connectors\Types;
use Gravity_Forms\Gravity_SMTP\Connectors\Connector_Base;
use Gravity_Forms\Gravity_SMTP\Connectors\Connector_Service_Provider;
use Gravity_Forms\Gravity_SMTP\Connectors\Oauth\Zoho_Oauth_Handler;
use Gravity_Forms\Gravity_SMTP\Enums\Zoho_Datacenters_Enum;
use Gravity_Forms\Gravity_SMTP\Feature_Flags\Feature_Flag_Manager;
use Gravity_Forms\Gravity_SMTP\Gravity_SMTP;
use Gravity_Forms\Gravity_SMTP\Utils\Booliesh;
/**
* Connector for Zoho
*
* @since 1.0
*/
class Connector_Zoho extends Connector_Base {
const SETTING_ACCESS_TOKEN = 'access_token';
const SETTING_CLIENT_ID = 'client_id';
const SETTING_CLIENT_SECRET = 'client_secret';
const SETTING_DATA_CENTER_REGION = 'data_center_region';
const SETTING_ACCOUNT_ID = 'account_id';
const VALUE_REDIRECT_URI = 'redirect_uri';
const VALUE_REDIRECT_URI_FULL = 'redirect_uri_full';
protected $name = 'zoho';
protected $title = 'Zoho Mail';
protected $logo = 'Zoho';
protected $full_logo = 'ZohoFull';
protected $sensitive_fields = array(
self::SETTING_ACCESS_TOKEN,
self::SETTING_CLIENT_ID,
self::SETTING_CLIENT_SECRET,
);
/**
* Sending logic.
*
* @since 1.0
*
* @return bool
*/
public function send() {
$atts = $this->get_send_atts();
$email = $this->email;
$source = $this->get_att( 'source' );
$params = $this->get_request_body( $atts );
$this->set_email_log_data( $atts['subject'], $atts['message'], $atts['to'], $atts['from']['email'], $atts['headers'], $atts['attachments'], $source, $params );
$args = array(
'headers' => $this->get_request_headers(),
'body' => json_encode( $params ),
);
$this->set_email_log_data( $atts['subject'], $atts['message'], $atts['to'], $atts['from']['from'], $atts['headers'], $atts['attachments'], $source, $params );
$this->logger->log( $email, 'started', __( 'Starting email send for Zoho connector.', 'gravitysmtp' ) );
$this->debug_logger->log_debug( $this->wrap_debug_with_details( __FUNCTION__, $email, 'Starting email send with Zoho connector and the following params: ' . json_encode( $params ) ) );
if ( $this->is_test_mode() ) {
$this->events->update( array( 'status' => 'sandboxed' ), $email );
$this->logger->log( $email, 'sandboxed', __( 'Email sandboxed.', 'gravitysmtp' ) );
$this->debug_logger->log_debug( $this->wrap_debug_with_details( __FUNCTION__, $email, 'Test mode is enabled, sandboxing email.' ) );
return true;
}
$request_url = $this->get_api_url( sprintf( 'api/accounts/%s/messages', $this->get_setting( self::SETTING_ACCOUNT_ID, '' ) ) );
$request = wp_remote_post( $request_url, $args );
$response_code = wp_remote_retrieve_response_code( $request );
$body = wp_remote_retrieve_body( $request );
$decoded = json_decode( $body, true );
if ( $response_code >= 300 ) {
$this->log_failure( $email, $body );
$this->debug_logger->log_error( $this->wrap_debug_with_details( __FUNCTION__, $email, 'Email failed to send. Details: ' . $body ) );
return $email;
}
$this->debug_logger->log_debug( $this->wrap_debug_with_details( __FUNCTION__, $email, 'Received response of: ' . $body ) );
$this->events->update( array( 'status' => 'sent' ), $email );
$this->logger->log( $email, 'sent', __( 'Email successfully sent.', 'gravitysmtp' ) );
$this->debug_logger->log_debug( $this->wrap_debug_with_details( __FUNCTION__, $email, 'Email successfully sent.' ) );
return true;
}
private function get_request_headers() {
return array(
'Authorization' => 'Zoho-oauthtoken ' . $this->get_setting( self::SETTING_ACCESS_TOKEN ),
'Content-Type' => 'application/json',
);
}
private function get_request_body( $atts ) {
$body = array(
'fromAddress' => $atts['from']['email'],
'toAddress' => $atts['to']->first()->email,
'subject' => $atts['subject'],
'content' => $atts['message'],
'encoding' => 'UTF-8',
);
$is_html = ! empty( $atts['headers']['content-type'] ) && strpos( $atts['headers']['content-type'], 'text/html' ) !== false;
$body['mailFormat'] = $is_html ? 'html' : 'plaintext';
if ( ! empty( $atts['attachments'] ) ) {
$body['attachments'] = $this->handle_attachments( $atts['attachments'] );
}
return $body;
}
private function handle_attachments( $attachments ) {
$data = [];
$headers = $this->get_request_headers();
$headers['Content-Type'] = 'application/octet-stream';
foreach ( $attachments as $attachment ) {
if ( ! file_exists( $attachment ) ) {
continue;
}
$file = file_get_contents( $attachment );
if ( $file === false ) {
continue;
}
$file_name = basename( $attachment );
// Upload the attachment via Zoho API.
$url = add_query_arg(
'fileName',
$file_name,
$this->get_api_url( sprintf( 'api/accounts/%s/messages/attachments', $this->get_setting( self::SETTING_ACCOUNT_ID, '' ) ) )
);
$params = array(
'headers' => $headers,
'body' => $file,
);
$response = wp_safe_remote_post( $url, $params );
if ( is_wp_error( $response ) || wp_remote_retrieve_response_code( $response ) !== 200 ) {
continue;
}
$body = json_decode( wp_remote_retrieve_body( $response ), true );
if ( ! empty( $body['data'] ) ) {
$data[] = $body['data'];
}
}
return $data;
}
/**
* Get the attributes for sending email.
*
* @since 1.0
*
* @return array
*/
private function get_send_atts() {
$headers = $this->get_parsed_headers( $this->get_att( 'headers', array() ) );
if ( ! empty( $headers['content-type'] ) ) {
$headers['content-type'] = $this->get_att( 'content_type', $headers['content-type'] );
}
return array(
'to' => $this->get_att( 'to', '' ),
'subject' => $this->get_att( 'subject', '' ),
'message' => $this->get_att( 'message', '' ),
'headers' => $headers,
'attachments' => $this->get_att( 'attachments', array() ),
'from' => $this->get_from( true ),
'reply_to' => $this->get_reply_to( true ),
);
}
private function get_api_url( $endpoint ) {
$data_center_location = $this->get_setting( self::SETTING_DATA_CENTER_REGION, 'us' );
$base = Zoho_Datacenters_Enum::url_for_datacenter( $data_center_location );
return trailingslashit( $base ) . $endpoint;
}
public function get_description() {
return esc_html__( 'Reach inboxes when it matters most. Send email notifications to your contacts with Zoho Mail.', 'gravitysmtp' );
}
protected function get_merged_data() {
$data = parent::get_merged_data();
$data['disabled'] = ! Feature_Flag_Manager::is_enabled( 'zoho_integration' );
return $data;
}
/**
* Connector data.
*
* @since 1.0
*
* @return array
*/
public function connector_data() {
return array(
self::SETTING_CLIENT_ID => $this->get_setting( self::SETTING_CLIENT_ID, '' ),
self::SETTING_CLIENT_SECRET => $this->get_setting( self::SETTING_CLIENT_SECRET, '' ),
self::SETTING_ACCESS_TOKEN => $this->get_setting( self::SETTING_ACCESS_TOKEN, '' ),
self::SETTING_FROM_EMAIL => $this->get_setting( self::SETTING_FROM_EMAIL, '' ),
self::SETTING_FORCE_FROM_EMAIL => $this->get_setting( self::SETTING_FORCE_FROM_EMAIL, false ),
self::SETTING_FROM_NAME => $this->get_setting( self::SETTING_FROM_NAME, '' ),
self::SETTING_FORCE_FROM_NAME => $this->get_setting( self::SETTING_FORCE_FROM_NAME, false ),
self::SETTING_REPLY_TO_EMAIL => $this->get_setting( self::SETTING_REPLY_TO_EMAIL, '' ),
self::SETTING_FORCE_REPLY_TO_EMAIL => $this->get_setting( self::SETTING_FORCE_REPLY_TO_EMAIL, false ),
'oauth_url' => 'https://accounts.zoho.com/oauth/v2/auth',
'oauth_params' => '&' . $this->get_oauth_params(),
);
}
protected function get_oauth_params() {
/**
* @var Zoho_Oauth_Handler $oauth_handler
*/
$oauth_handler = Gravity_SMTP::container()->get( Connector_Service_Provider::ZOHO_OAUTH_HANDLER );
$params = array(
'response_type' => 'code',
'redirect_uri' => urldecode( $oauth_handler->get_return_url() ),
'scope' => $oauth_handler->get_scope(),
'state' => 1,
'access_type' => 'offline',
'prompt' => 'consent',
);
return http_build_query( $params );
}
public function is_configured() {
if ( Booliesh::get( $this->get_setting( 'access_token', false ) ) ) {
/**
* @var Zoho_Oauth_Handler $oauth_handler
*/
$oauth_handler = Gravity_SMTP::container()->get( Connector_Service_Provider::ZOHO_OAUTH_HANDLER );
$token = $oauth_handler->get_access_token();
return $token;
}
return false;
}
/**
* Settings fields.
*
* @since 1.0
*
* @return array
*/
public function settings_fields() {
/**
* @var Zoho_Oauth_Handler $oauth_handler
*/
$oauth_handler = Gravity_SMTP::container()->get( Connector_Service_Provider::ZOHO_OAUTH_HANDLER );
$oauth_handler->handle_response( $this->name );
$token = $oauth_handler->get_access_token( $this->name );
$has_token = $token && ! is_wp_error( $token );
$settings = array(
'title' => esc_html__( 'Zoho Mail Settings', 'gravitysmtp' ),
'hide_save' => ( ! $has_token ),
'fields' => array(),
);
if ( ! $token || is_wp_error( $token ) ) {
$settings['fields'][] = array(
'component' => 'Alert',
'props' => array(
'customIconPrefix' => 'gravity-admin-icon',
'theme' => 'cosmos',
'type' => 'notice',
'spacing' => 3,
),
'fields' => array(
array(
'component' => 'Text',
'props' => array(
'content' => esc_html__( 'Please click the button below to initiate a connection with your Zoho Mail account. Remember to fill out both the Client ID and Client Secret fields before proceeding.', 'gravitysmtp' ),
'customClasses' => array(
'gform--display-block',
'gravitysmtp-integration__notice-message'
),
'size' => 'text-sm',
'spacing' => 4,
'tagName' => 'span',
),
),
array(
'component' => 'Link',
'props' => array(
'content' => esc_html__( 'Read our Zoho Mail documentation', 'gravitysmtp' ),
'customClasses' => array(
'gform-link--theme-cosmos',
'gravitysmtp-integration__notice-link',
'gform-button',
'gform-button--size-height-m',
'gform-button--white',
'gform-button--width-auto'
),
'href' => 'https://docs.gravitysmtp.com/category/integrations/zoho/',
'target' => '_blank',
),
),
),
);
}
if ( isset( $_GET['code'] ) && ! $has_token ) {
$settings['fields'][] = array(
'component' => 'Alert',
'props' => array(
'customIconPrefix' => 'gravity-admin-icon',
'theme' => 'cosmos',
'type' => 'error',
'spacing' => 3,
),
'fields' => array(
array(
'component' => 'Text',
'props' => array(
'content' => esc_html__( 'Error Connecting to Zoho Mail. Check your credentials and try again.', 'gravitysmtp' ),
'weight' => 'medium',
'size' => 'text-sm',
'spacing' => 2,
'tagName' => 'span',
),
),
),
);
}
$settings['fields'][] = array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'Configuration', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => [ 4, 0, 3, 0 ],
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
);
$settings['fields'][] = array(
'component' => 'Select',
'props' => array(
'name' => self::SETTING_DATA_CENTER_REGION,
'size' => 'size-l',
'spacing' => [ 4, 0, 3, 0 ],
'helpTextAttributes' => array(
'content' => esc_html__( 'If you are unsure about your Datacenter location, check your Zoho account.', 'gravitysmtp' ),
'size' => 'text-xs',
'weight' => 'regular',
),
'labelAttributes' => array(
'label' => esc_html__( 'Datacenter Region', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'medium',
),
'options' => Zoho_Datacenters_Enum::select_component_options(),
'initialValue' => $this->get_setting( self::SETTING_DATA_CENTER_REGION, 'us' ),
),
);
if ( ! $token || is_wp_error( $token ) ) {
$settings['fields'][] = array(
'component' => 'LinkedHelpTextInput',
'external' => true,
'handle_change' => true,
'links' => array(
array(
'key' => 'link',
'props' => array(
'href' => 'https://api-console.zoho.com/',
'size' => 'text-xs',
'target' => '_blank',
),
),
),
'props' => array(
'helpTextAttributes' => array(
'content' => esc_html__( 'To obtain an Client ID from Zoho Mail, login to your {{link}}Zoho API{{link}} dashboard and generate a Client ID.', 'gravitysmtp' ),
'size' => 'text-xs',
'weight' => 'regular',
),
'labelAttributes' => array(
'label' => esc_html__( 'Client ID', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'medium',
),
'name' => self::SETTING_CLIENT_ID,
'spacing' => 4,
'size' => 'size-l',
'value' => $this->get_setting( self::SETTING_CLIENT_ID, '' ),
),
);
$settings['fields'][] = array(
'component' => 'LinkedHelpTextInput',
'external' => true,
'handle_change' => true,
'links' => array(
array(
'key' => 'link',
'props' => array(
'href' => 'https://api-console.zoho.com/',
'size' => 'text-xs',
'target' => '_blank',
),
),
),
'props' => array(
'helpTextAttributes' => array(
'content' => esc_html__( 'To obtain a Client Secret password, log in to your {{link}}Zoho API{{link}} dashboard and generate a new client secret. Then, copy the value into this field.', 'gravitysmtp' ),
'size' => 'text-xs',
'weight' => 'regular',
),
'labelAttributes' => array(
'label' => esc_html__( 'Client Secret', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'medium',
),
'name' => self::SETTING_CLIENT_SECRET,
'spacing' => 6,
'size' => 'size-l',
'value' => $this->get_setting( self::SETTING_CLIENT_SECRET, '' ),
),
);
$settings['fields'][] = array(
'component' => 'CopyInput',
'external' => true,
'props' => array(
'actionButtonAttributes' => array(
'customAttributes' => array(
'type' => 'button',
),
'icon' => 'copy',
'iconPrefix' => 'gravity-admin-icon',
'label' => esc_html__( 'Copy', 'gravitysmtp' ),
),
'labelAttributes' => array(
'label' => esc_html__( 'Redirect URI', 'gravitysmtp' ),
'size' => 'text-sm',
'weight' => 'medium',
),
'name' => self::VALUE_REDIRECT_URI,
'spacing' => 6,
'size' => 'size-l',
'customAttributes' => array(
'readOnly' => true,
),
'value' => urldecode( $oauth_handler->get_return_url( 'copy' ) . '?page=gravitysmtp-settings&integration=zoho&tab=integrations' ),
'helpTextAttributes' => array(
'content' => __( 'Copy this URL into the "Authorized redirect URIs" field of your Zoho Mail application.', 'gravitysmtp' ),
'size' => 'text-xs',
'weight' => 'regular',
),
),
);
$settings['fields'][] = array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'Authorization', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => [ 4, 0, 3, 0 ],
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
);
$settings['fields'][] = array(
'component' => 'BrandedButton',
'external' => true,
'props' => array(
'label' => esc_html__( 'Sign in with Zoho Mail', 'gravitysmtp' ),
'spacing' => 6,
'Svg' => 'ZohoAltLogo',
'type' => 'color',
),
);
} else {
$settings['fields'][] = array(
'component' => 'Box',
'props' => array(
'customClasses' => array( 'gravitysmtp-google-integration__connected-message' ),
'display' => 'flex',
'spacing' => 3,
),
'fields' => array(
array(
'component' => 'Icon',
'props' => array(
'customClasses' => array(
'gravitysmtp-google-integration__checkmark',
'gform-icon--preset-active',
'gform-icon-preset--status-correct',
'gform-alert__icon'
),
'icon' => 'checkmark-simple',
'iconPrefix' => 'gravity-admin-icon',
),
),
array(
'component' => 'Text',
'props' => array(
'asHtml' => true,
'content' => sprintf(
'%s <span class="gform-text gform-text--color-port gform-typography--size-text-sm gform-typography--weight-medium">%s</span>',
esc_html__( 'Connected to Zoho Mail with account:', 'gravitysmtp' ),
esc_html( $oauth_handler->get_connection_details()['account_id'] )
),
'size' => 'text-sm',
'tagName' => 'span',
),
),
),
);
$disconnect_url = add_query_arg( '_wpnonce', wp_create_nonce( 'gsmtp_disconnect_zoho' ), admin_url( 'admin-post.php?action=smtp_disconnect_zoho' ) );
$settings['fields'][] = array(
'component' => 'Text',
'props' => array(
'content' => sprintf( '<a href="%s" class="%s"><span class="gravity-admin-icon gravity-admin-icon--x-circle gform-button__icon"></span>%s</a>', $disconnect_url, 'gform-link gform-link--theme-cosmos gform-button gform-button--size-height-m gform-button--white gform-button--width-auto gform-button--active-type-loader gform-button--loader-after gform-button--icon-leading', __( 'Disconnect from Zoho Mail', 'gravitysmtp' ) ),
'asHtml' => true,
'spacing' => 6,
),
);
$settings['fields'][] = array(
'component' => 'Heading',
'props' => array(
'content' => esc_html__( 'Configuration', 'gravitysmtp' ),
'size' => 'text-sm',
'spacing' => [ 4, 0, 4, 0 ],
'tagName' => 'h3',
'type' => 'boxed',
'weight' => 'medium',
),
);
$settings['fields'] = array_merge( $settings['fields'], $this->get_from_settings_fields(), $this->get_reply_to_settings_fields() );
}
return $settings;
}
/**
* Logs an email send failure.
*
* @since 1.4.0
*
* @param string $email The email that failed.
* @param string $error_message The error message.
*/
private function log_failure( $email, $error_message ) {
$this->events->update( array( 'status' => 'failed' ), $email );
$this->logger->log( $email, 'failed', $error_message );
}
}