_key = $key; $this->_custom_fields_transient = 'api_custom_fields_' . $key; $this->set_custom_fields_mapping(); $this->set_custom_default_fields_mapping(); } /** * If the snake_case version of the function does not exist, attempt to call the camelCase version. * Can be deleted after we switch everything to snake_case. * * @param $method_name * @param $arguments * * @return mixed */ public function __call( $method_name, $arguments ) { $camel_case_method_name = tve_dash_to_camel_case( $method_name ); return method_exists( $this, $camel_case_method_name ) ? call_user_func_array( [ $this, $camel_case_method_name, ], $arguments ) : null; } /** * Same as above, but for static calls. * Can be deleted after we switch everything to snake_case. * * @param $method_name * @param $arguments * * @return mixed */ public static function __callStatic( $method_name, $arguments ) { $camel_case_method_name = tve_dash_to_camel_case( $method_name ); return method_exists( __CLASS__, $camel_case_method_name ) ? call_user_func_array( [ static::class, $camel_case_method_name, ], $arguments ) : null; } /** * Can be removed in 2-3 releases * * @return String * @deprecated */ public static function getType() { return static::get_type(); } /** * Returns the connection type * * @return String */ public static function get_type() { return 'autoresponder'; } /** * Can be removed in 2-3 releases * * @return mixed * @deprecated */ public static function getEmailMergeTag() { return static::get_email_merge_tag(); } /** * Return the connection email merge tag * * @return String */ public static function get_email_merge_tag() { return '[email]'; } /** * Called from TTW, do not delete. * * @return mixed * @deprecated */ public function getApi() { return $this->get_api(); } /** * get the API Connection code to use in calls * * @return mixed */ public function get_api() { if ( ! isset( $this->_api ) ) { $this->_api = $this->get_api_instance(); } return $this->_api; } /** * Can be removed in 2-3 releases * * @return array * @deprecated */ public function getCredentials() { return $this->get_credentials(); } /** * @return array */ public function get_credentials() { return $this->_credentials; } /** * @param array $connection_details * * @return Thrive_Dash_List_Connection_Abstract */ public function set_credentials( $connection_details ) { $this->_credentials = $connection_details; /* if we set new credentials, the previously saved API is no longer valid */ $this->_api = null; return $this; } /** * Can be removed in 2-3 releases * * @return string|null * @deprecated */ public function getKey() { return $this->get_key(); } /** * @return string */ public function get_key() { return $this->_key; } /** * Can be deleted in 2-3 releases * * @deprecated */ public function isConnected() { return $this->is_connected(); } /** * Whether this list is connected to the service (has been authenticated) * * @return bool */ public function is_connected() { return ! empty( $this->_credentials ); } /** * @return bool */ public function is_related() { return false; } /** * get connection parameter by name * * @param string $field * @param string $default * * @return mixed */ public function param( $field, $default = null ) { return isset( $this->_credentials[ $field ] ) ? $this->_credentials[ $field ] : $default; } /** * Used in TTW, do not delete. * * @param $field * @param $value * * @return $this * @deprecated */ public function setParam( $field, $value ) { return $this->set_param( $field, $value ); } /** * set connection parameter * * @param string $field * @param mixed $value * * @return $this */ public function set_param( $field, $value ) { $this->_credentials[ $field ] = $value; return $this; } /** * setup a global error message * * @param string $message * * @return Thrive_Dash_List_Connection_Abstract */ public function error( $message ) { if ( wp_doing_ajax() ) { return $message; } return $this->message( 'error', $message ); } /** * setup a global success message * * @param string $message * * @return Thrive_Dash_List_Connection_Abstract */ public function success( $message ) { if ( wp_doing_ajax() ) { return true; } return $this->message( 'success', $message ); } /** * save the connection details * * @return Thrive_Dash_List_Connection_Abstract * @see Thrive_Dash_List_Manager * */ public function save() { Thrive_Dash_List_Manager::save( $this ); return $this; } /** * disconnect (remove) this API connection */ public function disconnect() { $disconnect = apply_filters( 'tve_dash_disconnect_' . $this->get_key(), true ); if ( true === $disconnect ) { $this->before_disconnect(); $this->set_credentials( [] ); Thrive_Dash_List_Manager::save( $this ); } return $this; } /** * Actions to take before a disconnection (can be overwritten by any API connection for different purposes) * * @return $this */ public function before_disconnect() { delete_transient( $this->_custom_fields_transient ); return $this; } /** * get the last error message in communicating with the api * * @return string the error message */ public function get_api_error() { return $this->_error; } /** * Used in TTW, do not delete. * * @return string * @deprecated */ public function getTitle() { return $this->get_title(); } /** * @return string the API connection title */ abstract public function get_title(); /** * Can be deleted in 2-3 releases * * @deprecated */ public function outputSetupForm() { $this->output_setup_form(); } /** * output the setup form html * * @return void */ abstract public function output_setup_form(); /** * Can be removed in 2-3 releases * * @return mixed * @deprecated */ public function readCredentials() { return $this->read_credentials(); } /** * should handle: read data from post / get, test connection and save the details * on error return the error message * on success return true * * @return mixed */ abstract public function read_credentials(); /** * Used in TTW, do not delete. * * @return bool|string * @deprecated */ public function testConnection() { return $this->test_connection(); } /** * test if a connection can be made to the service using the stored credentials * * @return bool|string true for success or error message for failure */ abstract public function test_connection(); /** * Can be removed in 2-3 releases * * @param $list_identifier * @param $arguments * * @deprecated */ public function addSubscriber( $list_identifier, $arguments ) { $this->add_subscriber( $list_identifier, $arguments ); } /** * * @param mixed $list_identifier * @param array $arguments * * @return mixed */ abstract public function add_subscriber( $list_identifier, $arguments ); /** * delete a contact matching arguments * * @param string $email * @param array $arguments * * @return mixed */ public function delete_subscriber( $email, $arguments = [] ) { return false; } /** * Can be deleted in 2-3 releases * * @param bool $use_cache * * @return array|bool * @deprecated */ public function getLists( $use_cache = true ) { return $this->get_lists( $use_cache ); } /**` * get all Subscriber Lists from this API service * it will first check a local cache for the existing lists * * @param bool $use_cache if true, it will read the lists from a local cache (wp options) * * @return array|bool for error * @see self::_get_lists() */ public function get_lists( $use_cache = true ) { if ( ! $this->is_connected() ) { $this->_error = $this->get_title() . ' ' . __( 'is not connected', 'thrive-dash' ); return false; } $cache = get_option( 'thrive_auto_responder_lists', [] ); if ( ! $use_cache || ! isset( $cache[ $this->get_key() ] ) ) { $lists = $this->_get_lists(); if ( $lists !== false ) { $cache[ $this->get_key() ] = $lists; update_option( 'thrive_auto_responder_lists', $cache ); } } else { $lists = $cache[ $this->get_key() ]; } return $lists; } /** * if an API instance has a special way of designating the list, it should override this method * e.g. "Choose the mailing list you want your subscribers to be assigned to" * * @return string */ public function get_list_sub_title() { return ''; } /** * Get fields mapping specific to an API for add to autoresponder action * * @return string[][] */ public function get_automator_add_autoresponder_mapping_fields() { return array( 'autoresponder' => array( 'mailing_list', 'api_fields' ) ); } /** * Get fields mapping specific to an API for tag in autoresponder action * * @return string[][] */ public function get_automator_tag_autoresponder_mapping_fields() { return array( 'autoresponder' => array( 'tag_input' ) ); } /** * Enable custom subfields based on api * * @param $fields * @param $field * @param $action_id * @param $action_data * * @return mixed */ public function set_custom_autoresponder_fields( $fields, $field, $action_id, $action_data ) { return $fields; } /** * get an array of warning messages (e.g. The access token will expire in xxx days. Click here to renew it) * * @return array */ public function get_warnings() { return []; } /** * output any (possible) extra editor settings for this API * * @param array $params allow various different calls to this method * * @return array */ public function get_extra_settings( $params = [] ) { do_action( 'tvd_autoresponder_render_extra_editor_settings_' . $this->get_key() ); return []; } /** * Can be removed in 2-3 releases * * @param array $params * * @deprecated */ public function renderExtraEditorSettings( $params = [] ) { $this->render_extra_editor_settings( $params ); } /** * output any (possible) extra editor settings for this API * * @param array $params allow various different calls to this method */ public function render_extra_editor_settings( $params = [] ) { do_action( 'tvd_autoresponder_render_extra_editor_settings_' . $this->get_key() ); return false; } public function render_before_lists_settings( $params = [] ) { return false; } /** * @return string */ public function get_logo_url() { return TVE_DASH_URL . '/inc/auto-responder/views/images/' . $this->get_key() . '.png'; } /** * @return array */ public function prepare_json() { $properties = array( 'key' => $this->get_key(), 'connected' => $this->is_connected(), 'credentials' => $this->get_credentials(), 'title' => $this->get_title(), 'type' => $this->get_type(), 'logoUrl' => $this->get_logo_url(), 'success_message' => $this->custom_success_message(), 'can_test' => $this->can_test(), 'can_delete' => $this->can_delete(), 'can_edit' => $this->can_edit(), ); $properties['notification'] = TVE_Dash_InboxManager::instance()->get_by_slug( $this->get_key() ); return $properties; } /** * Custom message for success state * * @return string */ public function custom_success_message() { return ''; } /** * Can be overwritten by any api who needs custom data to be sent * * @param $params * @param bool $force * @param bool $get_all * * @return array|mixed */ public function get_api_custom_fields( $params, $force = false, $get_all = false ) { $cache_data = get_transient( $this->_custom_fields_transient ); return $cache_data ?: []; } /** * Get extra parameters or fields from apis. * * @param $func * @param $params * * @return mixed */ public function get_api_extra( $func, $params ) { $extra = []; if ( method_exists( $this, $func ) ) { $extra = call_user_func_array( array( $this, $func ), array( $params ) ); } return [ 'extra' => $extra, 'api_custom_fields' => $this->get_api_custom_fields( $params ), ]; } /** * Return an array with the lists, custom fields and extra settings * * @param array $params * @param int $force * * @return array */ public function get_api_data( $params = [], $force = false ) { if ( empty( $params ) ) { //in case it comes out empty string $params = []; } if ( $this->get_key() === 'email' ) { $force = true; } else { $transient = 'tve_api_data_' . $this->get_key(); $data = get_transient( $transient ); } if ( false === $force && tve_dash_is_debug_on() ) { $force = true; } if ( true === $force || false === $data ) { $data = array( 'lists' => $this->get_lists( false ), 'extra_settings' => $this->get_extra_settings( $params ), 'custom_fields' => $this->get_custom_fields( $params ), ); if ( $this->get_key() !== 'email' ) { set_transient( $transient, $data, MONTH_IN_SECONDS ); } } $data['api_custom_fields'] = $this->get_api_custom_fields( $params, $force ); return $data; } /** * Get API custom form fields. By default, we have only name and phone * * @param array $params * * @return array */ public function get_custom_fields( $params = [] ) { return [ [ 'id' => 'name', 'placeholder' => __( 'Name', 'thrive-dash' ) ], [ 'id' => 'phone', 'placeholder' => __( 'Phone', 'thrive-dash' ) ], ]; } /** * output directly the html for a connection form from views/setup * * @param string $filename * @param array $data allows passing variables to the view file */ protected function output_controls_html( $filename, $data = [] ) { include dirname( dirname( __DIR__ ) ) . '/views/setup/' . $filename . '.php'; } /** * @param $type * @param $message * * @return Thrive_Dash_List_Connection_Abstract */ protected function message( $type, $message ) { Thrive_Dash_List_Manager::message( $type, $message ); return $this; } /** * explode fullname into first name and last name * * @param string $full_name * * @return array */ protected function get_name_parts( $full_name ) { if ( empty( $full_name ) ) { return array( '', '' ); } $parts = explode( ' ', $full_name ); if ( count( $parts ) === 1 ) { return array( $parts[0], '', ); } $last_name = array_pop( $parts ); $first_name = implode( ' ', $parts ); return array( sanitize_text_field( $first_name ), sanitize_text_field( $last_name ), ); } /** * Compose name from email * * @param $email * * @return array */ protected function get_name_from_email( $email ) { if ( empty( $email ) || ! is_string( $email ) || false === strpos( $email, '@' ) ) { return array( '', '' ); } $email_name = str_replace( array( '.', '_', '-', '+', '=' ), ' ', strstr( $email, '@', true ) ); list( $first_name, $last_name ) = $this->get_name_parts( $email_name ); if ( empty( $first_name ) ) { $first_name = $email_name; } if ( empty( $last_name ) ) { $last_name = $first_name; } return [ $first_name, $last_name, ]; } /** * instantiate the API code required for this connection * * @return mixed */ protected abstract function get_api_instance(); /** * get all Subscriber Lists from this API service * * @return array|bool for error */ protected abstract function _get_lists(); /** * Whether the integration supports forms * * @return bool */ public function has_forms() { return false; } /** * @return array */ protected function _get_forms() { return []; } /** * @return array|bool */ public function get_forms() { if ( ! $this->is_connected() ) { $this->_error = $this->get_title() . ' ' . __( 'is not connected', 'thrive-dash' ); return false; } return $this->_get_forms(); } /** * Get API Videos URLs * * @return array */ public function get_api_video_urls() { $return = []; $transient = get_transient( 'ttw_api_urls' ); if ( ! empty( $transient ) && is_array( $transient ) ) { $return = (array) $transient; } return $return; } /** * Displays the video link with his html structure * * @return mixed|string */ public function display_video_link() { $api_slug = strtolower( str_replace( array( ' ', '-' ), '', $this->get_key() ) ); $video_urls = $this->get_api_video_urls(); if ( ! array_key_exists( $api_slug, $video_urls ) ) { return ''; } return include dirname( dirname( __DIR__ ) ) . '/views/includes/video-link.php'; } /** * A connection can work with different versions of API * * @return false|mixed */ public function get_version() { $credentials = (array) $this->get_credentials(); if ( ! empty( $credentials['version'] ) ) { return $credentials['version']; } return false; } /** * Defines a common field structure to work with * * @param array $data Field data from the API * * @return array */ protected function normalize_custom_field( $data ) { return [ 'id' => $data['id'], //unique identifier 'name' => $data['name'], //should be name="" attribute for an input 'type' => $data['type'], //type for e.g. [url, text] 'label' => $data['label'], //label to display for users ]; } /** * Get the value of from email for email delivery services * * @return string */ public function get_email_param() { return $this->param( 'email', get_option( 'admin_email' ) ); } /** * Insert message to API error log * * @param string|int $list_identifier * @param array $data * @param string $error * * @return bool */ public function api_log_error( $list_identifier, $data, $error ) { if ( ! $list_identifier || ! $data || ! $error ) { return false; } global $wpdb; $return = false; $api_log_table = $wpdb->prefix . 'tcb_api_error_log'; $table_exists = (bool) $wpdb->get_var( $wpdb->prepare( 'SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME=%s', $api_log_table ) ); if ( $table_exists ) { $log_data = array( 'date' => date( 'Y-m-d H:i:s' ), 'error_message' => tve_sanitize_data_recursive( $error ), 'api_data' => serialize( tve_sanitize_data_recursive( $data ) ), 'connection' => $this->get_key(), 'list_id' => maybe_serialize( tve_sanitize_data_recursive( $list_identifier ) ), ); $return = (bool) $wpdb->insert( $api_log_table, $log_data ); } return $return; } /** * Mapped custom fields setter */ protected function set_custom_fields_mapping() { $this->_mapped_custom_fields = apply_filters( 'tve_dash_mapped_custom_fields', array( array( 'id' => 'mapping_text', 'placeholder' => __( 'Text', 'thrive-dash' ), 'unique' => false, ), array( 'id' => 'mapping_url', 'placeholder' => __( 'URL', 'thrive-dash' ), 'unique' => false, ), array( 'id' => 'mapping_radio', 'placeholder' => __( 'Radio', 'thrive-dash' ), 'unique' => false, ), array( 'id' => 'mapping_select', 'placeholder' => __( 'Dropdown', 'thrive-dash' ), 'unique' => false, ), array( 'id' => 'mapping_checkbox', 'placeholder' => __( 'Checkbox', 'thrive-dash' ), 'unique' => false, ), array( 'id' => 'mapping_textarea', 'placeholder' => __( 'Textarea', 'thrive-dash' ), 'unique' => false, ), array( 'id' => 'mapping_file', 'placeholder' => __( 'File upload', 'thrive-dash' ), 'unique' => true, ), array( 'id' => 'mapping_avatar_picker', 'placeholder' => __( 'Avatar picker', 'thrive-dash' ), 'unique' => true, ), array( 'id' => 'mapping_hidden', 'placeholder' => __( 'Hidden', 'thrive-dash' ), 'unique' => false, ), array( 'id' => 'number', 'placeholder' => __( 'Number', 'thrive-dash' ), 'unique' => false, ), array( 'id' => 'country', 'placeholder' => __( 'Country', 'thrive-dash' ), 'unique' => false, ), array( 'id' => 'state', 'placeholder' => __( 'State', 'thrive-dash' ), 'unique' => false, ), array( 'id' => 'date', 'placeholder' => __( 'Date/Time', 'thrive-dash' ), 'unique' => false, ), ) ); } protected function set_custom_default_fields_mapping() { $this->_default_form_fields = apply_filters( 'tve_dash_mapped_default_fields', array( array( 'id' => 'email', 'placeholder' => __( 'Email', 'thrive-dash' ), 'unique' => true, 'mandatory' => true, ), array( 'id' => 'name', 'placeholder' => __( 'Name', 'thrive-dash' ), 'unique' => true, 'mandatory' => false, ), array( 'id' => 'phone', 'placeholder' => __( 'Phone', 'thrive-dash' ), 'unique' => true, 'mandatory' => false, ), ) ); } /** * Global getter for the hardcoded allowed custom fields type * * @return array */ public function get_custom_fields_mapping() { return $this->_mapped_custom_fields; } /** * Global getter for the default fields type * * @return array */ public function get_default_fields_mapper() { return $this->_default_form_fields; } /** * Custom fields cache getter * * @return mixed */ protected function get_cached_custom_fields() { return get_transient( $this->_custom_fields_transient ); } /** * Save custom fields * * @param array $custom_fields * * @return bool */ protected function _save_custom_fields( $custom_fields = [] ) { if ( empty( $custom_fields ) ) { return false; } $custom_fields = tve_sanitize_data_recursive( $custom_fields ); return set_transient( $this->_custom_fields_transient, $custom_fields, WEEK_IN_SECONDS ); } public function process_field( $field ) { if ( is_array( $field ) ) { $field = implode( ', ', $field ); } return stripslashes( $field ); } /** * Whether the integration supports tags * * @return bool */ public function has_tags() { return false; } /** * Whether the current integration can provide custom fields * * @return false */ public function has_custom_fields() { return false; } /** * Whether the current integration has multiple opt-in types * * @return false */ public function has_optin() { return false; } /** * @return string */ public function get_tags_key() { return $this->_key . '_tags'; } /** * @return string */ public function get_forms_key() { return $this->_key . '_form'; } /** * @return string */ public function get_optin_key() { return $this->_key . '_optin'; } /** * Get tags key for the api * * @return array */ protected function get_mapped_field_ids() { $mapped_fields = array_map( function ( $field ) { return $field['id']; }, $this->_mapped_custom_fields ); $mapped_fields[] = 'user_consent'; $mapped_fields[] = 'gdpr'; return $mapped_fields; } /** * Push external tags in $data, EX: adds tags from tqb * * @param array|string $tags * @param array $data * * @return array */ public function push_tags( $tags, $data = [] ) { if ( empty( $tags ) || ! $this->has_tags() ) { return $data; } if ( is_array( $tags ) ) { $tags = implode( ', ', $tags ); } else if ( ! is_string( $tags ) ) { $tags = ''; } $tag_key = $this->get_tags_key(); if ( empty( $data[ $tag_key ] ) ) { $tag_data = $tags; } else { $tag_data = $data[ $tag_key ] . ( empty( $tags ) ? '' : ', ' . $tags ); } $data[ $tag_key ] = trim( $tag_data ); return $data; } /** * Whether this connection can be edited * * @return bool */ public function can_edit() { return true; } /** * Whether this connection can be deleted * * @return bool */ public function can_delete() { return true; } /** * Whether this connection can be tested to validate that the stored credentials are correct * * @return bool */ public function can_test() { return true; } /** * Can be removed in 2-3 releases * * @return array * @deprecated */ public function getDataForSetup() { return $this->get_data_for_setup(); } /** * Get localization data needed for setting up this connection within a form * * @return array */ public function get_data_for_setup() { return []; } /** * get relevant data from webhook trigger * * @param $request WP_REST_Request * * @return array */ public function get_webhook_data( $request ) { return []; } /** * @param string $email * @param array $tags * @param array $extra * * @return int */ public function update_tags( $email, $tags = '', $extra = [] ) { $args = $this->get_args_for_tags_update( $email, $tags, $extra ); $list_identifier = ! empty( $args['list_identifier'] ) ? $args['list_identifier'] : null; unset( $args['list_identifier'] ); return $this->add_subscriber( $list_identifier, $args ); } /** * Prepare necessary arguments for adding a tag * * @param string $email * @param array|string $tags * @param array $extra * * @return array */ public function get_args_for_tags_update( $email, $tags = '', $extra = [] ) { $tags_key = $this->get_tags_key(); $return = [ 'email' => $email, $tags_key => $tags, ]; foreach ( $extra as $key => $value ) { $return[ $key ] = $value; } return $return; } /** * Add a custom field for a subscriber * This method should be overwritten in avery instance that deals with custom fields * Subscriber ID should be returned * * @param $email * @param array $custom_fields * @param array $extra * * @return int */ public function add_custom_fields( $email, $custom_fields = [], $extra = [] ) { return 0; } /** * * Each api expects custom fields data in a specific format * This method should be overwritten in every instance that deals with custom fields and prepare them as needed * * @param array $custom_fields * @param null $list_identifier * * @return array */ protected function prepare_custom_fields_for_api( $custom_fields = [], $list_identifier = null ) { return []; } /** * Get available custom fields for this api connection * * @param array $data * * @return array */ public function get_available_custom_fields( $data = [] ) { return method_exists( $this, 'get_all_custom_fields' ) ? $this->get_all_custom_fields( true ) : []; } /** * @param null $list * * @return array|mixed */ public function get_custom_fields_by_list( $list = null ) { $fields = $this->get_available_custom_fields(); if ( $list && isset( $fields[ $list ] ) ) { $fields = $fields[ $list ]; } return $fields; } /** * Get a sanitized value from post * * @param string $key * @param mixed $default * * @return array|mixed|null */ protected function post( $key, $default = null ) { if ( ! isset( $_POST[ $key ] ) ) { return $default; } return map_deep( $_POST[ $key ], 'sanitize_text_field' ); } /** * Build custom fields mapping for automations * * @param $automation_data * * @return array */ public function build_automation_custom_fields( $automation_data ) { return []; } }